Skip to content

feat: useDisclosure 훅 구현 #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 6, 2024
Merged
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
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof useDisclosure>;

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

`<T>`: Type of data stored on local storage.
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -24,4 +25,5 @@ export {
useThrottle,
useDebounce,
useLocalStorage,
useDisclosure,
};
19 changes: 19 additions & 0 deletions src/stories/useDisclosure/Disclosure.stories.ts
Original file line number Diff line number Diff line change
@@ -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<typeof UseDisclosureExample>;

export default meta;

type Story = StoryObj<typeof meta>;

export const defaultStory: Story = {};
56 changes: 56 additions & 0 deletions src/stories/useDisclosure/Docs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Canvas, Meta, Description } from '@storybook/blocks';
import * as DisclosureStories from './Disclosure.stories';

<Meta of={DisclosureStories} />

# 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 (
<label className="switch">
<button onClick={handleOpenModal}>open</button>
<dialog open={modal.isOpen}>
Hello World
<button onClick={handleCloseModal}>close</button>
</dialog>
</label>
);
}
```

<Canvas of={DisclosureStories.defaultStory} />
23 changes: 23 additions & 0 deletions src/stories/useDisclosure/UseDisclosureExample.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<label className="switch">
<button onClick={handleOpenModal}>open</button>
<dialog open={modal.isOpen}>
Hello World
<button onClick={handleCloseModal}>close</button>
</dialog>
</label>
);
}
28 changes: 28 additions & 0 deletions src/useDisclosure/_useDisclosure.test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});
22 changes: 22 additions & 0 deletions src/useDisclosure/useDisclosure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import useBoolean from '../useBoolean/useBoolean';
import { useCallback } from 'react';

export type UseDisclosureReturn = ReturnType<typeof useDisclosure>;

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,
};
}
Loading