From d1228fae6c0fd7bfceef1e9a5eaad7486e3a0e6b Mon Sep 17 00:00:00 2001 From: yevhverb Date: Tue, 8 Nov 2022 20:27:37 +0200 Subject: [PATCH 1/2] [@mantine/hooks] use-toggle: Add handling for more than two values in an array --- .../src/use-toggle/use-toggle.test.ts | 19 ++++++++++++++ .../src/use-toggle/use-toggle.ts | 26 +++++++------------ src/mantine-hooks/src/utils/index.ts | 1 + .../src/utils/is-function/is-function.test.ts | 8 ++++++ .../src/utils/is-function/is-function.ts | 3 +++ 5 files changed, 40 insertions(+), 17 deletions(-) create mode 100644 src/mantine-hooks/src/utils/is-function/is-function.test.ts create mode 100644 src/mantine-hooks/src/utils/is-function/is-function.ts diff --git a/src/mantine-hooks/src/use-toggle/use-toggle.test.ts b/src/mantine-hooks/src/use-toggle/use-toggle.test.ts index 869073240f7..425f94910b6 100644 --- a/src/mantine-hooks/src/use-toggle/use-toggle.test.ts +++ b/src/mantine-hooks/src/use-toggle/use-toggle.test.ts @@ -17,6 +17,25 @@ describe('@mantine/hooks/use-toggle', () => { expect(hook.result.current[0]).toBe('dark'); }); + it('correctly toggles more than two values', () => { + const hook = renderHook(() => useToggle(['dark', 'light', 'normal'] as const)); + + act(() => hook.result.current[1]()); + expect(hook.result.current[0]).toBe('light'); + + act(() => hook.result.current[1]()); + expect(hook.result.current[0]).toBe('normal'); + + act(() => hook.result.current[1]()); + expect(hook.result.current[0]).toBe('dark'); + + act(() => hook.result.current[1]('normal')); + expect(hook.result.current[0]).toBe('normal'); + + act(() => hook.result.current[1]()); + expect(hook.result.current[0]).toBe('dark'); + }); + it('allows to set value', () => { const hook = renderHook(() => useToggle(['dark', 'light'] as const)); diff --git a/src/mantine-hooks/src/use-toggle/use-toggle.ts b/src/mantine-hooks/src/use-toggle/use-toggle.ts index 1cb2c79a76e..e6ae83bdc65 100644 --- a/src/mantine-hooks/src/use-toggle/use-toggle.ts +++ b/src/mantine-hooks/src/use-toggle/use-toggle.ts @@ -1,21 +1,13 @@ -import { useState } from 'react'; +import { useReducer } from 'react'; +import { isFunction } from '../utils'; -export function useToggle(options: readonly [T, T] = [false, true] as any) { - const [state, setState] = useState(options[0]); +export function useToggle(options: readonly T[] = [false, true] as any) { + const [[option], toggle] = useReducer((state: T[], action: React.SetStateAction) => { + const value = isFunction(action) ? action(state[0]) : action; + const index = Math.abs(state.indexOf(value)); - const toggle = (value?: React.SetStateAction) => { - if (typeof value !== 'undefined') { - setState(value); - } else { - setState((current) => { - if (current === options[0]) { - return options[1]; - } + return state.slice(index).concat(state.slice(0, index)); + }, options as T[]); - return options[0]; - }); - } - }; - - return [state, toggle] as const; + return [option, toggle as (value?: React.SetStateAction) => void] as const; } diff --git a/src/mantine-hooks/src/utils/index.ts b/src/mantine-hooks/src/utils/index.ts index ec5a2eb13a2..6fb0aaa1aa5 100644 --- a/src/mantine-hooks/src/utils/index.ts +++ b/src/mantine-hooks/src/utils/index.ts @@ -5,3 +5,4 @@ export { randomId } from './random-id/random-id'; export { range } from './range/range'; export { shallowEqual } from './shallow-equal/shallow-equal'; export { upperFirst } from './upper-first/upper-first'; +export { isFunction } from './is-function/is-function'; diff --git a/src/mantine-hooks/src/utils/is-function/is-function.test.ts b/src/mantine-hooks/src/utils/is-function/is-function.test.ts new file mode 100644 index 00000000000..7407d86c900 --- /dev/null +++ b/src/mantine-hooks/src/utils/is-function/is-function.test.ts @@ -0,0 +1,8 @@ +import { isFunction } from './is-function'; + +describe('@mantine/hooks/is-function', () => { + it('returns correct boolean value', () => { + expect(isFunction(/abc/)).toBe(false); + expect(isFunction(() => {})).toBe(true); + }); +}); diff --git a/src/mantine-hooks/src/utils/is-function/is-function.ts b/src/mantine-hooks/src/utils/is-function/is-function.ts new file mode 100644 index 00000000000..0d89dda579d --- /dev/null +++ b/src/mantine-hooks/src/utils/is-function/is-function.ts @@ -0,0 +1,3 @@ +export function isFunction(x: unknown): x is Function { + return typeof x === 'function'; +} From c171adc13a1e0d3a169d773b59d8dfbeaff056c0 Mon Sep 17 00:00:00 2001 From: yevhverb Date: Wed, 9 Nov 2022 13:11:01 +0200 Subject: [PATCH 2/2] [@mantine/hooks] use-toggle: Fixes after review --- src/mantine-hooks/src/use-toggle/use-toggle.ts | 3 +-- src/mantine-hooks/src/utils/index.ts | 1 - .../src/utils/is-function/is-function.test.ts | 8 -------- src/mantine-hooks/src/utils/is-function/is-function.ts | 3 --- 4 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 src/mantine-hooks/src/utils/is-function/is-function.test.ts delete mode 100644 src/mantine-hooks/src/utils/is-function/is-function.ts diff --git a/src/mantine-hooks/src/use-toggle/use-toggle.ts b/src/mantine-hooks/src/use-toggle/use-toggle.ts index e6ae83bdc65..3cfdf79c9d3 100644 --- a/src/mantine-hooks/src/use-toggle/use-toggle.ts +++ b/src/mantine-hooks/src/use-toggle/use-toggle.ts @@ -1,9 +1,8 @@ import { useReducer } from 'react'; -import { isFunction } from '../utils'; export function useToggle(options: readonly T[] = [false, true] as any) { const [[option], toggle] = useReducer((state: T[], action: React.SetStateAction) => { - const value = isFunction(action) ? action(state[0]) : action; + const value = action instanceof Function ? action(state[0]) : action; const index = Math.abs(state.indexOf(value)); return state.slice(index).concat(state.slice(0, index)); diff --git a/src/mantine-hooks/src/utils/index.ts b/src/mantine-hooks/src/utils/index.ts index 6fb0aaa1aa5..ec5a2eb13a2 100644 --- a/src/mantine-hooks/src/utils/index.ts +++ b/src/mantine-hooks/src/utils/index.ts @@ -5,4 +5,3 @@ export { randomId } from './random-id/random-id'; export { range } from './range/range'; export { shallowEqual } from './shallow-equal/shallow-equal'; export { upperFirst } from './upper-first/upper-first'; -export { isFunction } from './is-function/is-function'; diff --git a/src/mantine-hooks/src/utils/is-function/is-function.test.ts b/src/mantine-hooks/src/utils/is-function/is-function.test.ts deleted file mode 100644 index 7407d86c900..00000000000 --- a/src/mantine-hooks/src/utils/is-function/is-function.test.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { isFunction } from './is-function'; - -describe('@mantine/hooks/is-function', () => { - it('returns correct boolean value', () => { - expect(isFunction(/abc/)).toBe(false); - expect(isFunction(() => {})).toBe(true); - }); -}); diff --git a/src/mantine-hooks/src/utils/is-function/is-function.ts b/src/mantine-hooks/src/utils/is-function/is-function.ts deleted file mode 100644 index 0d89dda579d..00000000000 --- a/src/mantine-hooks/src/utils/is-function/is-function.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function isFunction(x: unknown): x is Function { - return typeof x === 'function'; -}