From b00bcb9debb3f2f1749715bef7a1fb11a258e13a Mon Sep 17 00:00:00 2001 From: Sh031224 <1cktmdgh2@gmail.com> Date: Wed, 25 Mar 2026 17:45:09 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix(wds):=20toast=20hover=20=ED=8C=90?= =?UTF-8?q?=EB=8B=A8=20=EC=8B=9C=20pointer:=20fine=20=EB=AF=B8=EB=94=94?= =?UTF-8?q?=EC=96=B4=20=EC=BF=BC=EB=A6=AC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pointer: coarse 대신 pointer: fine을 사용하여 with-interaction 정책과 일관성 유지하고 pointer: none 디바이스도 올바르게 처리 Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/wds/src/components/toast/helpers.ts | 4 ++-- packages/wds/src/components/toast/hooks.ts | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) 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 From 5f0b1b1fa483a3e4c916c0591a83f12d6400b231 Mon Sep 17 00:00:00 2001 From: Sh031224 <1cktmdgh2@gmail.com> Date: Wed, 25 Mar 2026 17:52:33 +0900 Subject: [PATCH 2/2] =?UTF-8?q?test(wds):=20toast,=20snackbar=20hover=20?= =?UTF-8?q?=EC=9D=BC=EC=8B=9C=EC=A0=95=EC=A7=80=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EC=97=90=20matchMedia=20mock=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cursor device 여부에 따른 hover 동작을 명시적으로 검증하고 non-cursor device에서 토스트가 정상 닫히는 테스트 케이스 추가 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../wds/src/components/snackbar/index.test.tsx | 16 ++++++++++++++++ packages/wds/src/components/toast/index.test.tsx | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) 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/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', () => {