fix(ui): align toast swipe direction with viewport (#40980)

This commit is contained in:
yyh
2026-08-19 13:16:12 +00:00
committed by GitHub
parent 8e1c259a66
commit 98c2ffeec7
2 changed files with 77 additions and 1 deletions
@@ -88,6 +88,81 @@ describe('@langgenius/dify-ui/toast', () => {
)
})
it('should reject a downward swipe and dismiss upward from the top-right viewport', async () => {
const baseUIAnimationGlobal = globalThis as BaseUIAnimationGlobal
const animationState = baseUIAnimationGlobal.BASE_UI_ANIMATIONS_DISABLED
baseUIAnimationGlobal.BASE_UI_ANIMATIONS_DISABLED = false
try {
const screen = await render(
<>
<style>
{`
[role="dialog"]:not([data-ending-style]) {
transition: none !important;
}
[role="dialog"][data-ending-style] {
transition: opacity 10000s !important;
}
`}
</style>
<button
type="button"
aria-label="Swipe up destination"
style={{
position: 'fixed',
top: 0,
right: 200,
zIndex: 1000,
width: 20,
height: 20,
}}
/>
<button
type="button"
aria-label="Swipe down destination"
style={{
position: 'fixed',
top: 360,
right: 200,
zIndex: 1000,
width: 20,
height: 20,
}}
/>
<ToastHost timeout={0} offset={{ top: 120 }} />
</>,
)
toast('Directional notification')
const toastDialog = screen.getByRole('dialog', { name: 'Directional notification' })
await expect.element(toastDialog).toBeInTheDocument()
const toastElement = toastDialog.element()
const initialBounds = toastElement.getBoundingClientRect()
await userEvent.dragAndDrop(toastElement, screen.getByLabelText('Swipe down destination'), {
steps: 10,
})
await expect.element(toastDialog).toBeInTheDocument()
expect(toastElement).not.toHaveAttribute('data-ending-style')
expect(toastElement.getBoundingClientRect().top).toBeCloseTo(initialBounds.top, 0)
await userEvent.dragAndDrop(toastElement, screen.getByLabelText('Swipe up destination'), {
steps: 10,
})
await vi.waitFor(() => {
expect(toastElement).toHaveAttribute('data-ending-style')
expect(toastElement).toHaveAttribute('data-swipe-direction', 'up')
})
expect(toastElement.getBoundingClientRect().bottom).toBeLessThan(initialBounds.top)
} finally {
baseUIAnimationGlobal.BASE_UI_ANIMATIONS_DISABLED = animationState
}
})
it('should dismiss an expanded background toast from its current row when swiped right', async () => {
const baseUIAnimationGlobal = globalThis as BaseUIAnimationGlobal
const animationState = baseUIAnimationGlobal.BASE_UI_ANIMATIONS_DISABLED
+2 -1
View File
@@ -181,6 +181,7 @@ function ToastCard({ toast: toastItem }: { toast: ToastObject<ToastData> }) {
return (
<BaseToast.Root
toast={toastItem}
swipeDirection={['up', 'right']}
className={cn(
'pointer-events-auto absolute top-0 right-0 w-full origin-top cursor-default rounded-xl select-none focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden',
'[--toast-current-height:var(--toast-frontmost-height,var(--toast-height))] [--toast-expanded-offset-y:calc(var(--toast-offset-y)+var(--toast-swipe-movement-y)+(var(--toast-index)*var(--toast-gap)))] [--toast-gap:8px] [--toast-peek:5px] [--toast-scale:calc(1-(var(--toast-index)*0.0225))] [--toast-shrink:calc(1-var(--toast-scale))]',
@@ -189,7 +190,7 @@ function ToastCard({ toast: toastItem }: { toast: ToastObject<ToastData> }) {
'transform-[translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)+(var(--toast-index)*var(--toast-peek))+(var(--toast-shrink)*var(--toast-current-height))))_scale(var(--toast-scale))]',
'data-expanded:h-(--toast-height) data-expanded:transform-[translateX(var(--toast-swipe-movement-x))_translateY(var(--toast-expanded-offset-y))_scale(1)]',
'data-ending-style:pointer-events-none data-ending-style:transform-[translateY(-150%)] data-ending-style:opacity-0 data-ending-style:after:pointer-events-none',
'data-ending-style:data-[swipe-direction=down]:transform-[translateY(calc(var(--toast-swipe-movement-y)+150%))]',
'data-ending-style:data-[swipe-direction=up]:transform-[translateY(calc(var(--toast-swipe-movement-y)-150%))]',
'data-ending-style:data-[swipe-direction=right]:transform-[translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--toast-expanded-offset-y))]',
'data-limited:pointer-events-none data-limited:opacity-0 data-starting-style:transform-[translateY(-150%)] data-starting-style:opacity-0',
"after:pointer-events-auto after:absolute after:bottom-full after:left-0 after:h-[calc(var(--toast-gap)+1px)] after:w-full after:content-['']",