diff --git a/README.md b/README.md index 5e1ea83..d1e27dd 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,35 @@ function App() { } ``` +### useDisclosure + +`useDisclosure` is a custom hook for managing the open/closed state of a component. It initializes the state and provides open and close functions to control the component's visibility. This allows for easy and efficient control of the modal, disclosure, ...etc's open/closed state. + +```tsx +import useBoolean from '../useBoolean/useBoolean'; +import { useCallback } from 'react'; + +export type UseDisclosureReturn = ReturnType; + +export default function useDisclosure(defaultValue: boolean = false) { + const [isOpen, setIsOpen] = useBoolean(defaultValue); + + const open = useCallback(() => { + setIsOpen(true); + }, [setIsOpen]); + + const close = useCallback(() => { + setIsOpen(false); + }, [setIsOpen]); + + return { + isOpen, + open, + close, + }; +} +``` + #### Generic ``: Type of data stored on local storage. diff --git a/src/index.ts b/src/index.ts index e147401..621c9bd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,7 @@ import useRadio from './useRadio/useRadio'; import useThrottle from './useThrottle/useThrottle'; import useDebounce from './useDebounce/useDebounce'; import useLocalStorage from './useLocalStorage/useLocalStorage'; +import useDisclosure from './useDisclosure/useDisclosure'; export { useInput, @@ -24,4 +25,5 @@ export { useThrottle, useDebounce, useLocalStorage, + useDisclosure, }; diff --git a/src/stories/useDisclosure/Disclosure.stories.ts b/src/stories/useDisclosure/Disclosure.stories.ts new file mode 100644 index 0000000..982d6bb --- /dev/null +++ b/src/stories/useDisclosure/Disclosure.stories.ts @@ -0,0 +1,19 @@ +import { Meta, StoryObj } from '@storybook/react'; +import UseDisclosureExample from './UseDisclosureExample'; + +const meta = { + title: 'hooks/useDisclosure', + component: UseDisclosureExample, + parameters: { + layout: 'centered', + docs: { + canvas: {}, + }, + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const defaultStory: Story = {}; diff --git a/src/stories/useDisclosure/Docs.mdx b/src/stories/useDisclosure/Docs.mdx new file mode 100644 index 0000000..9896f45 --- /dev/null +++ b/src/stories/useDisclosure/Docs.mdx @@ -0,0 +1,56 @@ +import { Canvas, Meta, Description } from '@storybook/blocks'; +import * as DisclosureStories from './Disclosure.stories'; + + + +# useDisclosure + +useDisclosure를 통해 선언적으로 Modal, Disclosure등의 컴포넌트를 관리할 수 있습니다. + +## 함수인자 + +defaultValue를 통해 초기 요소의 열림/닫힘 상태를 결정할 수 있습니다. + +## 반환값 + +### `isOpen` (default `false`) + +요소의 열림 상태를 나타냅니다. 요소가 열려있으면 `true`, 아니면 `false`입니다. + +### `open` + +요소를 열림 상태로 전환합니다. + +### `close` + +요소를 닫힘 상태로 전환합니다. + +## 기본 사용 예시 + +```typescript +import useDisclosure from '../../useDisclosure/useDisclosure'; +import React from 'react'; + +export default function UseDisclosureExample() { + const modal = useDisclosure(false); + + const handleOpenModal = () => { + modal.open(); + }; + const handleCloseModal = () => { + modal.close(); + }; + + return ( + + ); +} +``` + + diff --git a/src/stories/useDisclosure/UseDisclosureExample.tsx b/src/stories/useDisclosure/UseDisclosureExample.tsx new file mode 100644 index 0000000..d54fa81 --- /dev/null +++ b/src/stories/useDisclosure/UseDisclosureExample.tsx @@ -0,0 +1,23 @@ +import useDisclosure from '../../useDisclosure/useDisclosure'; +import React from 'react'; + +export default function UseDisclosureExample() { + const modal = useDisclosure(false); + + const handleOpenModal = () => { + modal.open(); + }; + const handleCloseModal = () => { + modal.close(); + }; + + return ( + + ); +} diff --git a/src/useDisclosure/_useDisclosure.test.ts b/src/useDisclosure/_useDisclosure.test.ts new file mode 100644 index 0000000..6e4f45a --- /dev/null +++ b/src/useDisclosure/_useDisclosure.test.ts @@ -0,0 +1,28 @@ +import useDisclosure from './useDisclosure'; +import { renderHook, act } from '@testing-library/react'; + +describe('useDisclosure 기능테스트', () => { + it('useDisclosure는 modal, disclosure와 같이 컴포넌트의 열림과 닫힘 상태를 조절할 수 있는 기능들을 반환한다.', () => { + const { result } = renderHook(() => useDisclosure(false)); + + expect(result.current.isOpen).toBe(false); + act(() => { + result.current.open(); + }); + expect(result.current.isOpen).toBe(true); + act(() => { + result.current.close(); + }); + expect(result.current.isOpen).toBe(false); + }); + + it('useDisclosure는 초기값을 파라미터로 받는다.', () => { + const { result: resultFalse } = renderHook(() => useDisclosure(false)); + const { result: resultTrue } = renderHook(() => useDisclosure(true)); + const maybeFalse = resultFalse.current.isOpen; + const maybeTrue = resultTrue.current.isOpen; + + expect(maybeFalse).toBe(false); + expect(maybeTrue).toBe(true); + }); +}); diff --git a/src/useDisclosure/useDisclosure.ts b/src/useDisclosure/useDisclosure.ts new file mode 100644 index 0000000..7682c6e --- /dev/null +++ b/src/useDisclosure/useDisclosure.ts @@ -0,0 +1,22 @@ +import useBoolean from '../useBoolean/useBoolean'; +import { useCallback } from 'react'; + +export type UseDisclosureReturn = ReturnType; + +export default function useDisclosure(defaultValue: boolean = false) { + const [isOpen, setIsOpen] = useBoolean(defaultValue); + + const open = useCallback(() => { + setIsOpen(true); + }, [setIsOpen]); + + const close = useCallback(() => { + setIsOpen(false); + }, [setIsOpen]); + + return { + isOpen, + open, + close, + }; +}