Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,18 @@ const Toast = (props: ToastProps) => {
setSwiping(false);
setSwipeDirection(null);
}}
onPointerCancel={() => {
// The browser fires this instead of pointerup when it takes the gesture
// over, so the swipe has to be unwound here too.
if (swipeOut || !dismissible) return;

pointerStartRef.current = null;
toastRef.current?.style.setProperty('--swipe-amount-x', '0px');
toastRef.current?.style.setProperty('--swipe-amount-y', '0px');
setIsSwiped(false);
setSwiping(false);
setSwipeDirection(null);
}}
onPointerMove={(event) => {
if (!pointerStartRef.current || !dismissible) return;

Expand Down Expand Up @@ -855,6 +867,7 @@ const Toaster = React.forwardRef<HTMLElement, ToasterProps>(function Toaster(pro
setInteracting(true);
}}
onPointerUp={() => setInteracting(false)}
onPointerCancel={() => setInteracting(false)}
>
{filteredToasts
.filter((toast) => (!toast.position && index === 0) || toast.position === position)
Expand Down
22 changes: 22 additions & 0 deletions test/tests/basic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,28 @@ test.describe('Basic functionality', () => {
await expect(toast).toHaveCount(1);
});

test('a cancelled swipe returns the toast to rest', async ({ page }) => {
await page.goto('/?position=top-center');
await page.getByTestId('default-button').click();

const toast = page.locator('[data-sonner-toast]').first();
const box = await toast.boundingBox();
if (!box) throw new Error('Toast not rendered');

await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
await page.mouse.down();
await page.mouse.move(box.x + box.width / 2 + 40, box.y + box.height / 2, { steps: 5 });

await expect(toast).toHaveAttribute('data-swiping', 'true');

await toast.dispatchEvent('pointercancel', { bubbles: true });

await expect(toast).toHaveAttribute('data-swiping', 'false');
await expect
.poll(() => toast.evaluate((el: HTMLElement) => el.style.getPropertyValue('--swipe-amount-x')))
.toBe('0px');
});

test('toast() clears the loading state of a toast with the same id', async ({ page }) => {
await page.getByTestId('loading-toast-fixed-id').click();
const toast = page.locator('[data-sonner-toast]');
Expand Down