From 717deb1de866401636a9b14b575fa7c4a22ff9d4 Mon Sep 17 00:00:00 2001 From: Leonid Fedorov Date: Sat, 26 Oct 2024 11:59:10 +0300 Subject: [PATCH] feat(Select): add OnOpenChange options --- .../__tests__/Select.base-actions.test.tsx | 6 +++--- .../useSelect/__tests__/useOpenState.test.tsx | 19 ++++++++++++++++--- src/hooks/useSelect/types.ts | 8 ++++++-- src/hooks/useSelect/useOpenState.ts | 10 +++++----- src/hooks/useSelect/useSelect.ts | 2 +- 5 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/components/Select/__tests__/Select.base-actions.test.tsx b/src/components/Select/__tests__/Select.base-actions.test.tsx index 5cd3a2f4fe..3303d1bfef 100644 --- a/src/components/Select/__tests__/Select.base-actions.test.tsx +++ b/src/components/Select/__tests__/Select.base-actions.test.tsx @@ -134,9 +134,9 @@ describe('Select base actions', () => { setup({onOpenChange}); await toggleSelectPopup(); - expect(onOpenChange).toHaveBeenCalledWith(true); + expect(onOpenChange).toHaveBeenCalledWith(true, {reason: 'outside-click'}); await toggleSelectPopup(); - expect(onOpenChange).toHaveBeenCalledWith(false); + expect(onOpenChange).toHaveBeenCalledWith(false, {reason: 'outside-click'}); expect(onOpenChange).toHaveBeenCalledTimes(2); }); test('should call onOpenChange whith controlled open', async () => { @@ -145,7 +145,7 @@ describe('Select base actions', () => { await toggleSelectPopup(); - expect(onOpenChange).toHaveBeenCalledWith(false); + expect(onOpenChange).toHaveBeenCalledWith(false, {reason: 'outside-click'}); expect(onOpenChange).toHaveBeenCalledTimes(1); }); }); diff --git a/src/hooks/useSelect/__tests__/useOpenState.test.tsx b/src/hooks/useSelect/__tests__/useOpenState.test.tsx index 74b3171df9..cc8c2baab7 100644 --- a/src/hooks/useSelect/__tests__/useOpenState.test.tsx +++ b/src/hooks/useSelect/__tests__/useOpenState.test.tsx @@ -71,14 +71,14 @@ describe('useOpenState', () => { }); rerender(props); - expect(onOpenChange).toHaveBeenCalledWith(true); + expect(onOpenChange).toHaveBeenCalledWith(true, {reason: 'outside-click'}); act(() => { result.current.toggleOpen(); }); rerender(props); - expect(onOpenChange).toHaveBeenCalledWith(false); + expect(onOpenChange).toHaveBeenCalledWith(false, {reason: 'outside-click'}); }); test('if the same value is set, the onOpenChange will not be called', () => { const onOpenChange = jest.fn(); @@ -150,7 +150,20 @@ describe('useOpenState', () => { }); rerender(props); - expect(onOpenChange).toHaveBeenCalledWith(false); + expect(onOpenChange).toHaveBeenCalledWith(false, {reason: 'outside-click'}); + }); + + test('onOpenChange is called with reason', () => { + const onOpenChange = jest.fn(); + const props = {open: true, onOpenChange}; + const {result, rerender} = renderUseOpenStateHook(props); + + act(() => { + result.current.toggleOpen(false, {reason: 'selection'}); + }); + rerender(props); + + expect(onOpenChange).toHaveBeenCalledWith(false, {reason: 'selection'}); }); test('if the same value is set, the onOpenChange will not be called', () => { diff --git a/src/hooks/useSelect/types.ts b/src/hooks/useSelect/types.ts index 9e7b203514..690d94e913 100644 --- a/src/hooks/useSelect/types.ts +++ b/src/hooks/useSelect/types.ts @@ -1,10 +1,14 @@ export type UseSelectOption = T & {value: string}; +export type OnOpenChangeOptions = { + reason: 'selection' | 'outside-click'; +}; + export type UseOpenProps = { defaultOpen?: boolean; open?: boolean; onClose?: () => void; - onOpenChange?: (open: boolean) => void; + onOpenChange?: (open: boolean, options: OnOpenChangeOptions) => void; }; export type UseSelectProps = { @@ -22,6 +26,6 @@ export type UseSelectResult = { handleSelection: (option: UseSelectOption) => void; handleClearValue: () => void; setValue: (value: string[]) => void; - toggleOpen: (val?: boolean | undefined) => void; + toggleOpen: (val?: boolean, options?: OnOpenChangeOptions) => void; setActiveIndex: React.Dispatch>; }; diff --git a/src/hooks/useSelect/useOpenState.ts b/src/hooks/useSelect/useOpenState.ts index 78fb7398c3..56e9c4e017 100644 --- a/src/hooks/useSelect/useOpenState.ts +++ b/src/hooks/useSelect/useOpenState.ts @@ -2,13 +2,13 @@ import React from 'react'; import {useControlledState} from '../useControlledState/useControlledState'; -import type {UseOpenProps} from './types'; +import type {OnOpenChangeOptions, UseOpenProps} from './types'; export const useOpenState = (props: UseOpenProps) => { const {onOpenChange, onClose} = props; const handleOpenChange = React.useCallback( - (newOpen: boolean) => { - onOpenChange?.(newOpen); + (newOpen: boolean, options: OnOpenChangeOptions = {reason: 'outside-click'}) => { + onOpenChange?.(newOpen, options); if (newOpen === false && onClose) { onClose(); } @@ -23,9 +23,9 @@ export const useOpenState = (props: UseOpenProps) => { ); const toggleOpen = React.useCallback( - (val?: boolean) => { + (val?: boolean, options?: OnOpenChangeOptions) => { const newOpen = typeof val === 'boolean' ? val : !open; - setOpenState(newOpen); + setOpenState(newOpen, options); }, [open, setOpenState], ); diff --git a/src/hooks/useSelect/useSelect.ts b/src/hooks/useSelect/useSelect.ts index cce9826692..8c8c25e6e5 100644 --- a/src/hooks/useSelect/useSelect.ts +++ b/src/hooks/useSelect/useSelect.ts @@ -41,7 +41,7 @@ export const useSelect = ({ setValue(nextValue); } - toggleOpen(false); + toggleOpen(false, {reason: 'selection'}); }, [value, setValue, toggleOpen], );