diff --git a/package-lock.json b/package-lock.json index 6169b51af..764099ae6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52610,6 +52610,25 @@ } } }, + "@testing-library/react-hooks": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-8.0.0.tgz", + "integrity": "sha512-uZqcgtcUUtw7Z9N32W13qQhVAD+Xki2hxbTR461MKax8T6Jr8nsUvZB+vcBTkzY2nFvsUet434CsgF0ncW2yFw==", + "requires": { + "@babel/runtime": "^7.12.5", + "react-error-boundary": "^3.1.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.3.tgz", + "integrity": "sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + } + } + }, "@testing-library/user-event": { "version": "12.8.3", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-12.8.3.tgz", @@ -76668,6 +76687,24 @@ } } }, + "react-error-boundary": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz", + "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==", + "requires": { + "@babel/runtime": "^7.12.5" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.3.tgz", + "integrity": "sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + } + } + }, "react-error-overlay": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-3.0.0.tgz", diff --git a/package.json b/package.json index 2ff83e745..83d3743dd 100644 --- a/package.json +++ b/package.json @@ -114,6 +114,7 @@ "dependencies": { "@datepicker-react/hooks": "^2.3.1", "@styled-system/theme-get": "^5.1.2", + "@testing-library/react-hooks": "^8.0.0", "@types/react-select": "^3.0.13", "@types/styled-system": "^5.1.9", "date-fns": "^2.11.1", diff --git a/src/hooks/index.ts b/src/hooks/index.ts new file mode 100644 index 000000000..320c57177 --- /dev/null +++ b/src/hooks/index.ts @@ -0,0 +1 @@ +export { useToggle } from './useToggle'; diff --git a/src/hooks/tests/useToggle.spec.ts b/src/hooks/tests/useToggle.spec.ts new file mode 100644 index 000000000..a01932b52 --- /dev/null +++ b/src/hooks/tests/useToggle.spec.ts @@ -0,0 +1,52 @@ +import { act, cleanup, renderHook } from '@testing-library/react-hooks'; + +import { useToggle } from '../useToggle'; + +describe('useToggle hook', () => { + afterEach(cleanup); + + it('should return boolean and a function inside the Array', () => { + const { result } = renderHook(() => useToggle()); + const [value, toggle] = result.current; + + expect(typeof value).toBe('boolean'); + expect(typeof toggle).toBe('function'); + }); + + it('should toggle the value', () => { + const { result } = renderHook(() => useToggle()); + const [, toggle] = result.current; + + expect(result.current[0]).toBe(false); + + act(() => { + toggle(); + }); + expect(result.current[0]).toBe(true); + + act(() => { + toggle(); + }); + expect(result.current[0]).toBe(false); + }); + + it('should set the boolean passed as argument', () => { + const { result } = renderHook(() => useToggle()); + const [, toggle] = result.current; + + act(() => { + toggle(true); + }); + expect(result.current[0]).toBe(true); + + act(() => { + toggle(true); + }); + expect(result.current[0]).toBe(true); + + act(() => { + toggle(false); + }); + expect(result.current[0]).toBe(false); + }); +}); diff --git a/src/hooks/useToggle.tsx b/src/hooks/useToggle.tsx new file mode 100644 index 000000000..1ba1300b2 --- /dev/null +++ b/src/hooks/useToggle.tsx @@ -0,0 +1,15 @@ +import { useReducer } from 'react'; + +export type DispatchWithOptionalAction = (_arg?: Type | unknown) => void; + +export type UseToggleType = [Type, DispatchWithOptionalAction]; + +function toggle(currentValue: boolean, newValue: boolean | undefined) { + return typeof newValue === 'boolean' ? newValue : !currentValue; +} + +function useToggle(initialValue = false): UseToggleType { + return useReducer(toggle, initialValue); +} + +export { useToggle }; diff --git a/src/index.ts b/src/index.ts index 47ee19677..116f18a85 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,4 +3,5 @@ import './polyfills'; export * from './components'; export * from './essentials'; export * from './icons'; +export * from './hooks'; export { get as themeGet } from './utils/themeGet'; diff --git a/src/utils/hooks/index.ts b/src/utils/hooks/index.ts new file mode 100644 index 000000000..11978eda9 --- /dev/null +++ b/src/utils/hooks/index.ts @@ -0,0 +1,3 @@ +export { useControlledState } from './useControlledState'; +export { useGeneratedId } from './useGeneratedId'; +export { useIsEscKeyPressed } from './useIsEscKeyPressed';