diff --git a/packages/wds/src/components/snackbar/index.test.tsx b/packages/wds/src/components/snackbar/index.test.tsx index a654819a1..eb1126938 100644 --- a/packages/wds/src/components/snackbar/index.test.tsx +++ b/packages/wds/src/components/snackbar/index.test.tsx @@ -78,6 +78,8 @@ describe('when given snackbar component', () => { }); it('should not close snackbar if mouse is over the toast container after open', () => { + window.matchMedia = vi.fn().mockReturnValue({ matches: true }); + expect(screen.getByTestId('snackbar')).toBeInTheDocument(); act(() => vi.advanceTimersByTime(DEFAULT_DURATION / 2)); @@ -95,6 +97,20 @@ describe('when given snackbar component', () => { expect(screen.queryByTestId('snackbar')).not.toBeInTheDocument(); }); + it('should close snackbar even if mouse is over the toast container on non-cursor device', () => { + window.matchMedia = vi.fn().mockReturnValue({ matches: false }); + + expect(screen.getByTestId('snackbar')).toBeInTheDocument(); + + act(() => vi.advanceTimersByTime(DEFAULT_DURATION / 2)); + + fireEvent.mouseEnter(screen.getByTestId('snackbar')); + + act(() => vi.advanceTimersByTime(DEFAULT_DURATION / 2)); + + expect(screen.queryByTestId('snackbar')).not.toBeInTheDocument(); + }); + it('should pass accessibility test', async () => { expect(await axe(screen.getByTestId('snackbar'))).toHaveNoViolations(); expect( diff --git a/packages/wds/src/components/toast/helpers.ts b/packages/wds/src/components/toast/helpers.ts index 5d360c1a0..a9cd1fb14 100644 --- a/packages/wds/src/components/toast/helpers.ts +++ b/packages/wds/src/components/toast/helpers.ts @@ -1,7 +1,7 @@ import type { CSSProperties } from 'react'; -export const isNotPointerDevice = () => - window.matchMedia('(pointer: coarse)').matches; +export const isCursorDevice = () => + window.matchMedia('(pointer: fine)').matches; export const makeTransitionStyle = ({ open, diff --git a/packages/wds/src/components/toast/hooks.ts b/packages/wds/src/components/toast/hooks.ts index 4f20c9dd2..3c113fec6 100644 --- a/packages/wds/src/components/toast/hooks.ts +++ b/packages/wds/src/components/toast/hooks.ts @@ -1,5 +1,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; +import { isCursorDevice } from './helpers'; + import type { CSSProperties } from 'react'; import type { RegionToastItem } from '../../stores/region-store'; @@ -78,6 +80,7 @@ export const useToastAnimation = ({ const handleMouseEnter = () => { if ( + isCursorDevice() && timeoutRef.current && startTimeRef.current && remainingTimeRef.current diff --git a/packages/wds/src/components/toast/index.test.tsx b/packages/wds/src/components/toast/index.test.tsx index 9f7f56482..bc94eff60 100644 --- a/packages/wds/src/components/toast/index.test.tsx +++ b/packages/wds/src/components/toast/index.test.tsx @@ -52,6 +52,8 @@ describe('when given toast component', () => { }); it('should not close toast if mouse is over the toast container after open', () => { + window.matchMedia = vi.fn().mockReturnValue({ matches: true }); + expect(screen.getByTestId('toast-container')).toBeInTheDocument(); act(() => vi.advanceTimersByTime(DEFAULT_DURATION / 2)); @@ -68,6 +70,20 @@ describe('when given toast component', () => { expect(screen.queryByTestId('toast-container')).not.toBeInTheDocument(); }); + + it('should close toast even if mouse is over the toast container on non-cursor device', () => { + window.matchMedia = vi.fn().mockReturnValue({ matches: false }); + + expect(screen.getByTestId('toast-container')).toBeInTheDocument(); + + act(() => vi.advanceTimersByTime(DEFAULT_DURATION / 2)); + + fireEvent.mouseEnter(screen.getByTestId('toast-container')); + + act(() => vi.advanceTimersByTime(DEFAULT_DURATION / 2)); + + expect(screen.queryByTestId('toast-container')).not.toBeInTheDocument(); + }); }); describe('when given toast component with each variant', () => {