Skip to content
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

[@mantine/hooks] use-toggle: Add handling for more than two values in an array #2912

Merged
merged 2 commits into from Nov 12, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/mantine-hooks/src/use-toggle/use-toggle.test.ts
Expand Up @@ -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));

Expand Down
26 changes: 9 additions & 17 deletions 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<T = boolean>(options: readonly [T, T] = [false, true] as any) {
const [state, setState] = useState(options[0]);
export function useToggle<T = boolean>(options: readonly T[] = [false, true] as any) {
const [[option], toggle] = useReducer((state: T[], action: React.SetStateAction<T>) => {
const value = isFunction(action) ? action(state[0]) : action;
const index = Math.abs(state.indexOf(value));

const toggle = (value?: React.SetStateAction<T>) => {
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<T>) => void] as const;
}
1 change: 1 addition & 0 deletions src/mantine-hooks/src/utils/index.ts
Expand Up @@ -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';
8 changes: 8 additions & 0 deletions 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);
});
});
3 changes: 3 additions & 0 deletions src/mantine-hooks/src/utils/is-function/is-function.ts
@@ -0,0 +1,3 @@
export function isFunction(x: unknown): x is Function {
yverby marked this conversation as resolved.
Show resolved Hide resolved
return typeof x === 'function';
}