Skip to content

Commit 6fbc8b3

Browse files
feat(useModal): Add useModal hook (#7)
* Add use modal * Add typesafe-actions to devDependencies
1 parent 50df051 commit 6fbc8b3

File tree

6 files changed

+128
-2
lines changed

6 files changed

+128
-2
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
"@patternfly/react-tokens": "^4.11.2",
3333
"axios": "^0.21.1",
3434
"react": "^17.0.2",
35-
"react-dom": "^17.0.2"
35+
"react-dom": "^17.0.2",
36+
"typesafe-actions": "^5.1.0"
3637
},
3738
"devDependencies": {
3839
"@babel/core": "^7.13.16",
@@ -83,6 +84,7 @@
8384
"semantic-release": "^17.4.2",
8485
"ts-jest": "^26.5.5",
8586
"tslib": "^2.2.0",
87+
"typesafe-actions": "^5.1.0",
8688
"typescript": "^4.2.4"
8789
},
8890
"config": {

src/hooks/useModal/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { useModal } from './useModal';
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { renderHook, act } from '@testing-library/react-hooks';
2+
import { useModal } from './useModal';
3+
4+
describe('useModal', () => {
5+
it('onOpen: without data', () => {
6+
const { result } = renderHook(() => useModal());
7+
8+
// Open modal
9+
const { open } = result.current;
10+
act(() => open());
11+
expect(result.current.isOpen).toEqual(true);
12+
expect(result.current.data).toBeUndefined();
13+
});
14+
15+
it('onOpen: with data', () => {
16+
const ENTITY = 'hello';
17+
18+
const { result } = renderHook(() => useModal<string>());
19+
20+
// Open modal
21+
const { open } = result.current;
22+
act(() => open(ENTITY));
23+
24+
expect(result.current.isOpen).toEqual(true);
25+
expect(result.current.data).toEqual(ENTITY);
26+
});
27+
28+
it('Close modal with data', () => {
29+
const ENTITY = 'hello';
30+
31+
const { result } = renderHook(() => useModal<string>());
32+
const { open, close } = result.current;
33+
34+
// Open modal
35+
act(() => open(ENTITY));
36+
37+
expect(result.current.isOpen).toEqual(true);
38+
expect(result.current.data).toEqual(ENTITY);
39+
40+
// Close modal
41+
act(() => close());
42+
43+
expect(result.current.isOpen).toEqual(false);
44+
expect(result.current.data).toBeUndefined();
45+
});
46+
});

src/hooks/useModal/useModal.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { useCallback, useReducer } from 'react';
2+
import { ActionType, createAction, getType } from 'typesafe-actions';
3+
4+
const openModal = createAction('useModal/action/openModalWithData')<any>();
5+
const closeModal = createAction('useModal/action/startClose')();
6+
7+
// State
8+
type State = Readonly<{
9+
data: any;
10+
isOpen: boolean;
11+
}>;
12+
13+
const defaultState: State = {
14+
data: undefined,
15+
isOpen: false,
16+
};
17+
18+
// Reducer
19+
20+
type Action = ActionType<typeof openModal | typeof closeModal>;
21+
22+
const reducer = (state: State, action: Action): State => {
23+
switch (action.type) {
24+
case getType(openModal):
25+
return {
26+
...state,
27+
data: action.payload,
28+
isOpen: true,
29+
};
30+
case getType(closeModal):
31+
return {
32+
...state,
33+
data: undefined,
34+
isOpen: false,
35+
};
36+
default:
37+
return state;
38+
}
39+
};
40+
41+
// Hook
42+
43+
interface HookState<T> {
44+
data?: T;
45+
isOpen: boolean;
46+
open: (data?: T) => void;
47+
close: () => void;
48+
}
49+
50+
export const useModal = <T>(): HookState<T> => {
51+
const [state, dispatch] = useReducer(reducer, {
52+
...defaultState,
53+
});
54+
55+
const openHandler = useCallback((entity?: T) => {
56+
dispatch(openModal(entity));
57+
}, []);
58+
59+
const closeHandler = useCallback(() => {
60+
dispatch(closeModal());
61+
}, []);
62+
63+
return {
64+
data: state.data,
65+
isOpen: state.isOpen,
66+
open: openHandler,
67+
close: closeHandler,
68+
};
69+
};
70+
71+
export default useModal;

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './components/StatusIcon';
22

3-
export * from './hooks/useDelete';
3+
export * from './hooks/useDelete';
4+
export * from './hooks/useModal';

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14481,6 +14481,11 @@ typedarray@^0.0.6:
1448114481
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
1448214482
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
1448314483

14484+
typesafe-actions@^5.1.0:
14485+
version "5.1.0"
14486+
resolved "https://registry.yarnpkg.com/typesafe-actions/-/typesafe-actions-5.1.0.tgz#9afe8b1e6a323af1fd59e6a57b11b7dd6623d2f1"
14487+
integrity sha512-bna6Yi1pRznoo6Bz1cE6btB/Yy8Xywytyfrzu/wc+NFW3ZF0I+2iCGImhBsoYYCOWuICtRO4yHcnDlzgo1AdNg==
14488+
1448414489
typescript@^4.2.4, typescript@^4.4.3:
1448514490
version "4.4.4"
1448614491
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c"

0 commit comments

Comments
 (0)