From 1559db7917215887314b8f0180b3c83ece32e344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gromit=20=28=EC=A0=84=EB=AF=BC=EC=9E=AC=29?= <64779472+ssi02014@users.noreply.github.com> Date: Thu, 11 Jul 2024 17:45:45 +0900 Subject: [PATCH] =?UTF-8?q?feat(react):=20usePreferredColocScheme=20?= =?UTF-8?q?=EC=8B=A0=EA=B7=9C=20=ED=9B=85=20=EC=B6=94=EA=B0=80=20(#322)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(react): usePreferredColocScheme 신규 훅 추가 * docs(react): usePreferredColorScheme 문서 수정 * docs: 문서 수정 * docs: 문서 수정 --- .changeset/tidy-eagles-kiss.md | 5 ++ docs/docs/react/hooks/useMediaQuery.mdx | 10 ++- .../react/hooks/usePreferredColorScheme.mdx | 66 +++++++++++++++++++ packages/react/src/hooks/index.ts | 1 + .../react/src/hooks/useMediaQuery/index.ts | 10 +-- .../hooks/useMediaQuery/useMediaQuery.spec.ts | 6 +- .../hooks/usePreferredColorScheme/index.ts | 7 ++ .../usePreferredColorScheme.spec.ts | 28 ++++++++ 8 files changed, 120 insertions(+), 13 deletions(-) create mode 100644 .changeset/tidy-eagles-kiss.md create mode 100644 docs/docs/react/hooks/usePreferredColorScheme.mdx create mode 100644 packages/react/src/hooks/usePreferredColorScheme/index.ts create mode 100644 packages/react/src/hooks/usePreferredColorScheme/usePreferredColorScheme.spec.ts diff --git a/.changeset/tidy-eagles-kiss.md b/.changeset/tidy-eagles-kiss.md new file mode 100644 index 000000000..c084c4fde --- /dev/null +++ b/.changeset/tidy-eagles-kiss.md @@ -0,0 +1,5 @@ +--- +'@modern-kit/react': minor +--- + +feat(react): usePreferredColocScheme 신규 훅 추가 - @ssi02014 diff --git a/docs/docs/react/hooks/useMediaQuery.mdx b/docs/docs/react/hooks/useMediaQuery.mdx index 381389e95..21ca8ee68 100644 --- a/docs/docs/react/hooks/useMediaQuery.mdx +++ b/docs/docs/react/hooks/useMediaQuery.mdx @@ -2,7 +2,7 @@ import { useMediaQuery } from '@modern-kit/react'; # useMediaQuery -Resize 시에도 동작하며, 미디어 쿼리 문자열의 분석 결과를 쉽게 확인 할 수 있는 커스텀 훅입니다. +미디어 쿼리 문자열의 분석 결과를 쉽게 확인 할 수 있는 커스텀 훅입니다.
@@ -11,9 +11,7 @@ Resize 시에도 동작하며, 미디어 쿼리 문자열의 분석 결과를 ## Interface ```ts title="typescript" -const useMediaQuery: (query: string) => { - isMatch: boolean; -} +const useMediaQuery: (query: string, defaultValue?: boolean) => boolean ``` ## Usage @@ -21,7 +19,7 @@ const useMediaQuery: (query: string) => { import { useMediaQuery } from '@modern-kit/react'; const Example = () => { - const { isMatch } = useMediaQuery('(min-width: 768px)'); + const isMatch = useMediaQuery('(min-width: 768px)'); return (
@@ -36,7 +34,7 @@ const Example = () => { ``` export const Example = () => { - const { isMatch } = useMediaQuery('(min-width: 768px)'); + const isMatch = useMediaQuery('(min-width: 768px)'); return (

브라우저 너비를 수정해보세요!

diff --git a/docs/docs/react/hooks/usePreferredColorScheme.mdx b/docs/docs/react/hooks/usePreferredColorScheme.mdx new file mode 100644 index 000000000..d06d68a58 --- /dev/null +++ b/docs/docs/react/hooks/usePreferredColorScheme.mdx @@ -0,0 +1,66 @@ +import { usePreferredColorScheme } from '@modern-kit/react'; + +# usePreferredColorScheme + +사용자의 색상 스킴 선호도(**[prefers-coloc-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)**) 에 따라 `dark`, `light`, 또는 `no-preference`를 반환합니다. + +- ligth: 사용자가 시스템에 `ligth` 테마를 사용하는 것을 선호하거나 선호하는 테마를 알리지 않았을 때 반환합니다. +- dark: 사용자가 시스템에 `dark` 테마를 사용하는 것을 선호한다고 알렸을 때 반환합니다. + +사용자는 `운영체제 설정`이나 `사용자 에이전트 설정`에서 선호하는 테마를 지정 할 수 있습니다. + +
+ +## Code +[🔗 실제 구현 코드 확인](https://github.com/modern-agile-team/modern-kit/blob/main/packages/react/src/hooks/usePreferredColorScheme/index.ts) + +## Interface + +```ts title="typescript" +const usePreferredColorScheme: () => "dark" | "light" +``` + +## Usage +```tsx title="typescript" +import { usePreferredColorScheme } from '@modern-kit/react'; + +const Example = () => { + const colorScheme = usePreferredColorScheme(); + + return ( +
+

OS 시스템 설정에서 테마를 변경해보세요.

+

ColorScheme: {colorScheme}

+
+ ); +}; +``` + +## Example + +export const Example = () => { + const colorScheme = usePreferredColorScheme(); + + return ( +
+

OS 시스템 설정에서 테마를 변경해보세요.

+

ColorScheme: {colorScheme}

+
+ ); +}; + + + +
+ +## Image +### 1. Light Theme +![스크린샷 2024-07-10 오후 8 09 33](https://github.com/modern-agile-team/modern-kit/assets/64779472/79c6298b-72f1-4f50-b644-93762d4d59e1) + +
+ +### 2. Dark Theme +![스크린샷 2024-07-10 오후 8 09 39](https://github.com/modern-agile-team/modern-kit/assets/64779472/ab735906-9f8c-4e8e-9688-f6cde1786ec9) + +## Note +- [Prefers Color Scheme - MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) \ No newline at end of file diff --git a/packages/react/src/hooks/index.ts b/packages/react/src/hooks/index.ts index 5e72b0318..7320c16c1 100644 --- a/packages/react/src/hooks/index.ts +++ b/packages/react/src/hooks/index.ts @@ -19,6 +19,7 @@ export * from './useMergeRefs'; export * from './useMouse'; export * from './useNetwork'; export * from './useOnClickOutside'; +export * from './usePreferredColorScheme'; export * from './usePreservedCallback'; export * from './usePreservedState'; export * from './usePrevious'; diff --git a/packages/react/src/hooks/useMediaQuery/index.ts b/packages/react/src/hooks/useMediaQuery/index.ts index ffd885203..680cbd0dc 100644 --- a/packages/react/src/hooks/useMediaQuery/index.ts +++ b/packages/react/src/hooks/useMediaQuery/index.ts @@ -1,15 +1,17 @@ import { isClient } from '@modern-kit/utils'; import { useCallback, useEffect, useState } from 'react'; -const getMatchMedia = (query: string) => { +const getMatchMedia = (query: string, defaultValue?: boolean) => { + if (defaultValue != null) return defaultValue; + if (isClient()) { return window.matchMedia(query).matches; } return false; }; -export const useMediaQuery = (query: string) => { - const [isMatch, setIsMatch] = useState(getMatchMedia(query)); +export const useMediaQuery = (query: string, defaultValue?: boolean) => { + const [isMatch, setIsMatch] = useState(getMatchMedia(query, defaultValue)); const handleChange = useCallback(() => { setIsMatch(getMatchMedia(query)); @@ -23,5 +25,5 @@ export const useMediaQuery = (query: string) => { return () => matchMedia.removeEventListener('change', handleChange); }, [query, handleChange]); - return { isMatch }; + return isMatch; }; diff --git a/packages/react/src/hooks/useMediaQuery/useMediaQuery.spec.ts b/packages/react/src/hooks/useMediaQuery/useMediaQuery.spec.ts index c538f0eed..8f528cb88 100644 --- a/packages/react/src/hooks/useMediaQuery/useMediaQuery.spec.ts +++ b/packages/react/src/hooks/useMediaQuery/useMediaQuery.spec.ts @@ -21,13 +21,13 @@ describe('useMediaQuery', () => { it('should return true for matches when query matches', () => { const { result } = renderHook(() => useMediaQuery('(min-width: 600px)')); - expect(result.current.isMatch).toBe(true); + expect(result.current).toBe(true); }); it('should return false when query does not match', () => { const { result } = renderHook(() => useMediaQuery('(min-width: 599px)')); - expect(result.current.isMatch).toBe(false); + expect(result.current).toBe(false); }); it('should return false for isMatch when not in a client environment', () => { @@ -35,6 +35,6 @@ describe('useMediaQuery', () => { const { result } = renderHook(() => useMediaQuery('(min-width: 600px)')); - expect(result.current.isMatch).toBe(false); + expect(result.current).toBe(false); }); }); diff --git a/packages/react/src/hooks/usePreferredColorScheme/index.ts b/packages/react/src/hooks/usePreferredColorScheme/index.ts new file mode 100644 index 000000000..713c1a677 --- /dev/null +++ b/packages/react/src/hooks/usePreferredColorScheme/index.ts @@ -0,0 +1,7 @@ +import { useMediaQuery } from '../useMediaQuery'; + +export const usePreferredColorScheme = () => { + const isDark = useMediaQuery('(prefers-color-scheme: dark)'); + + return isDark ? 'dark' : 'light'; +}; diff --git a/packages/react/src/hooks/usePreferredColorScheme/usePreferredColorScheme.spec.ts b/packages/react/src/hooks/usePreferredColorScheme/usePreferredColorScheme.spec.ts new file mode 100644 index 000000000..5529afecf --- /dev/null +++ b/packages/react/src/hooks/usePreferredColorScheme/usePreferredColorScheme.spec.ts @@ -0,0 +1,28 @@ +import { renderHook } from '@testing-library/react'; +import { usePreferredColorScheme } from '.'; +import { useMediaQuery } from '../useMediaQuery'; +import { Mock } from 'vitest'; + +vi.mock('../useMediaQuery', () => ({ + useMediaQuery: vi.fn(), +})); + +describe('usePreferredColorScheme', () => { + it('should return "dark" when prefers-color-scheme is dark', () => { + (useMediaQuery as Mock).mockImplementation((query: string) => { + return query === '(prefers-color-scheme: dark)'; + }); + + const { result } = renderHook(() => usePreferredColorScheme()); + expect(result.current).toBe('dark'); + }); + + it('should return "light" when prefers-color-scheme is light', () => { + (useMediaQuery as Mock).mockImplementation((query: string) => { + return query === '(prefers-color-scheme: light)'; + }); + + const { result } = renderHook(() => usePreferredColorScheme()); + expect(result.current).toBe('light'); + }); +});