mirror of
https://github.com/langgenius/dify.git
synced 2026-06-12 19:53:38 +08:00
fix: align toast stack with Base UI (#37382)
This commit is contained in:
parent
e0c6ca9930
commit
e5d5931fec
@ -69,6 +69,28 @@ describe('@langgenius/dify-ui/toast', () => {
|
||||
dispatchToastMouseOut(viewport)
|
||||
})
|
||||
|
||||
it('should clamp varying-height toasts to the frontmost stack height when collapsed', async () => {
|
||||
const screen = await render(<ToastHost />)
|
||||
|
||||
toast.info('Long background toast', {
|
||||
description: 'This longer toast intentionally spans multiple lines so it would overflow the collapsed stack without matching the frontmost toast height.',
|
||||
})
|
||||
toast.success('Short front toast', {
|
||||
description: 'Short message.',
|
||||
})
|
||||
|
||||
await expect.element(screen.getByText('Short front toast')).toBeInTheDocument()
|
||||
await expect.element(screen.getByText('Long background toast')).toBeInTheDocument()
|
||||
await expect.element(screen.getByRole('region', { name: 'Notifications' })).toHaveAttribute('aria-live', 'polite')
|
||||
await expect.element(screen.getByRole('dialog', { name: 'Short front toast' })).toBeInTheDocument()
|
||||
await expect.element(screen.getByRole('dialog', { name: 'Long background toast' })).toBeInTheDocument()
|
||||
|
||||
const longToastContent = screen.getByText('Long background toast').element().closest('[class*="transition-opacity"]')
|
||||
expect(longToastContent).toHaveAttribute('data-behind')
|
||||
expect(longToastContent).toHaveClass('h-full')
|
||||
expect(longToastContent?.parentElement).toHaveClass('h-full')
|
||||
})
|
||||
|
||||
it('should render a neutral toast when called directly', async () => {
|
||||
const screen = await render(<ToastHost />)
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import type { ReactNode } from 'react'
|
||||
import { useRef } from 'react'
|
||||
import { toast, ToastHost } from '.'
|
||||
|
||||
const buttonClassName = 'rounded-lg border border-divider-subtle bg-components-button-secondary-bg px-3 py-2 text-sm text-text-secondary shadow-xs transition-colors hover:bg-state-base-hover'
|
||||
@ -117,6 +118,15 @@ const StackExamples = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const createVaryingHeightStack = () => {
|
||||
toast.info('Long background toast', {
|
||||
description: 'This longer toast intentionally spans multiple lines so the collapsed stack can be checked against the shorter frontmost toast height without panel overflow.',
|
||||
})
|
||||
toast.success('Short front toast', {
|
||||
description: 'Short message.',
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<ExampleCard
|
||||
eyebrow="Stack"
|
||||
@ -129,6 +139,9 @@ const StackExamples = () => {
|
||||
<button type="button" className={buttonClassName} onClick={createBurst}>
|
||||
Stress the stack
|
||||
</button>
|
||||
<button type="button" className={buttonClassName} onClick={createVaryingHeightStack}>
|
||||
Varying heights
|
||||
</button>
|
||||
</ExampleCard>
|
||||
)
|
||||
}
|
||||
@ -192,11 +205,14 @@ const PromiseExamples = () => {
|
||||
|
||||
const ActionExamples = () => {
|
||||
const createActionToast = () => {
|
||||
toast.warning('Project archived', {
|
||||
let archivedToastId = ''
|
||||
archivedToastId = toast.warning('Project archived', {
|
||||
description: 'You can restore it from workspace settings for the next 30 days.',
|
||||
timeout: 10000,
|
||||
actionProps: {
|
||||
children: 'Undo',
|
||||
onClick: () => {
|
||||
toast.dismiss(archivedToastId)
|
||||
toast.success('Project restored', {
|
||||
description: 'The workspace is active again.',
|
||||
})
|
||||
@ -233,6 +249,32 @@ const ActionExamples = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const DeduplicateExamples = () => {
|
||||
const saveCountRef = useRef(0)
|
||||
|
||||
const saveDraft = () => {
|
||||
saveCountRef.current += 1
|
||||
toast.success('Draft saved', {
|
||||
id: 'draft-save-status',
|
||||
description: saveCountRef.current === 1
|
||||
? 'Click again while this toast is visible to update the same mounted toast.'
|
||||
: `Same toast updated ${saveCountRef.current} times.`,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<ExampleCard
|
||||
eyebrow="Deduplicated"
|
||||
title="Same-id upsert"
|
||||
description="Matches the Base UI deduplicated toast example: repeated triggers use a stable id, so the visible toast is updated instead of adding another stack item."
|
||||
>
|
||||
<button type="button" className={buttonClassName} onClick={saveDraft}>
|
||||
Save draft
|
||||
</button>
|
||||
</ExampleCard>
|
||||
)
|
||||
}
|
||||
|
||||
const UpdateExamples = () => {
|
||||
const createUpdatableToast = () => {
|
||||
const toastId = toast.info('Import started', {
|
||||
@ -292,6 +334,7 @@ const ToastDocsDemo = () => {
|
||||
<StackExamples />
|
||||
<PromiseExamples />
|
||||
<ActionExamples />
|
||||
<DeduplicateExamples />
|
||||
<UpdateExamples />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -166,12 +166,12 @@ function ToastCard({
|
||||
'after:pointer-events-auto after:absolute after:top-full after:left-0 after:h-[calc(var(--toast-gap)+1px)] after:w-full after:content-[\'\']',
|
||||
)}
|
||||
>
|
||||
<div className="relative overflow-hidden rounded-xl border border-components-panel-border bg-components-panel-bg-blur shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px]">
|
||||
<div className="relative h-full overflow-hidden rounded-xl border border-components-panel-border bg-components-panel-bg-blur shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px]">
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className={cn('absolute -inset-px bg-linear-to-r opacity-40', getToneGradientClasses(toastType))}
|
||||
/>
|
||||
<BaseToast.Content className="relative flex items-start gap-1 overflow-hidden p-3 transition-opacity duration-200 data-behind:opacity-0 data-expanded:opacity-100 motion-reduce:transition-none">
|
||||
<BaseToast.Content className="relative flex h-full items-start gap-1 overflow-hidden p-3 transition-opacity duration-200 data-behind:opacity-0 data-expanded:opacity-100 motion-reduce:transition-none">
|
||||
<div className="flex shrink-0 items-center justify-center p-0.5">
|
||||
<ToastIcon type={toastType} />
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user