From 7bfdb70fa1f67f9fccde786ce48880e915f1c26c Mon Sep 17 00:00:00 2001 From: Alexandr Isaev Date: Wed, 9 Oct 2024 20:00:11 +0300 Subject: [PATCH] feat(TreeSelect): added generic value depending on the multiple prop --- .../TableColumnSetup/TableColumnSetup.tsx | 4 +- .../withTableSettings/withTableSettings.tsx | 4 +- src/components/TreeSelect/TreeSelect.tsx | 45 ++++++++++++++++--- .../components/InfinityScrollExample.tsx | 2 +- .../components/WithDndListExample.tsx | 2 +- .../WithItemLinksAndActionsExample.tsx | 4 +- src/components/TreeSelect/index.ts | 2 +- src/components/TreeSelect/types.ts | 16 ++++--- .../useList/utils/getListItemClickHandler.ts | 8 ++-- src/unstable.ts | 2 + 10 files changed, 63 insertions(+), 26 deletions(-) diff --git a/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx b/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx index 840921b0a7..644e99d8c1 100644 --- a/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx +++ b/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx @@ -275,7 +275,7 @@ export interface TableColumnSetupProps { sortable?: boolean; onUpdate: (newSettings: TableSetting[]) => void; - popupWidth?: TreeSelectProps['popupWidth']; + popupWidth?: TreeSelectProps['popupWidth']; popupPlacement?: PopperPlacement; /** @@ -379,7 +379,7 @@ export const TableColumnSetup = (props: TableColumnSetupProps) => { const dndRenderItem = useDndRenderItem(sortingEnabled); - const renderControl: TreeSelectProps['renderControl'] = ({toggleOpen}) => { + const renderControl: TreeSelectProps['renderControl'] = ({toggleOpen}) => { const onKeyDown = createOnKeyDownHandler(toggleOpen); return ( diff --git a/src/components/Table/hoc/withTableSettings/withTableSettings.tsx b/src/components/Table/hoc/withTableSettings/withTableSettings.tsx index a9d1005a60..e273816945 100644 --- a/src/components/Table/hoc/withTableSettings/withTableSettings.tsx +++ b/src/components/Table/hoc/withTableSettings/withTableSettings.tsx @@ -110,7 +110,7 @@ export function getActualItems( } export interface WithTableSettingsOptions { - width?: TreeSelectProps['popupWidth']; + width?: TreeSelectProps['popupWidth']; sortable?: boolean; filterable?: boolean; } @@ -119,7 +119,7 @@ interface WithTableSettingsBaseProps { /** * @deprecated Use factory notation: "withTableSettings({width: })(Table)" */ - settingsPopupWidth?: TreeSelectProps['popupWidth']; + settingsPopupWidth?: TreeSelectProps['popupWidth']; settings: TableSettingsData; updateSettings: (data: TableSettingsData) => void; diff --git a/src/components/TreeSelect/TreeSelect.tsx b/src/components/TreeSelect/TreeSelect.tsx index 424d78ff32..7861394e69 100644 --- a/src/components/TreeSelect/TreeSelect.tsx +++ b/src/components/TreeSelect/TreeSelect.tsx @@ -17,7 +17,7 @@ import {block} from '../utils/cn'; import type {CnMods} from '../utils/cn'; import {useControlledValue} from './hooks/useControlledValue'; -import type {TreeSelectProps, TreeSelectRenderControlProps} from './types'; +import type {TreeSelectGetValue, TreeSelectProps, TreeSelectRenderControlProps} from './types'; import './TreeSelect.scss'; @@ -27,7 +27,11 @@ const defaultItemRenderer: TreeListRenderItem = (renderState) => { return ; }; -export const TreeSelect = React.forwardRef(function TreeSelect( +export const TreeSelect = React.forwardRef(function TreeSelect< + T, + P extends {} = {}, + M extends boolean = false, +>( { id, qa, @@ -68,7 +72,7 @@ export const TreeSelect = React.forwardRef(function TreeSelect, + }: TreeSelectProps, ref: React.Ref, ) { const mobile = useMobile(); @@ -103,10 +107,37 @@ export const TreeSelect = React.forwardRef(function TreeSelect { + if (onUpdate) { + if (multiple) { + onUpdate(nextValue as TreeSelectGetValue); + } else { + const [vl] = nextValue; + + onUpdate(vl as TreeSelectGetValue); + } + } + }, + [multiple, onUpdate], + ); + + const computedValue = React.useMemo(() => { + let result: string[] = []; + + if (Array.isArray(propsValue)) { + result = propsValue; + } else if (typeof propsValue !== 'undefined') { + result = [propsValue]; + } + + return result; + }, [propsValue]); + const {value, selectedById, setSelected} = useControlledValue({ - value: propsValue, + value: computedValue, defaultValue, - onUpdate, + onUpdate: handleUpdate, }); const list = useList({ @@ -264,6 +295,6 @@ export const TreeSelect = React.forwardRef(function TreeSelect ); -}) as ( - props: TreeSelectProps & {ref?: React.Ref}, +}) as ( + props: TreeSelectProps & {ref?: React.Ref}, ) => React.ReactElement; diff --git a/src/components/TreeSelect/__stories__/components/InfinityScrollExample.tsx b/src/components/TreeSelect/__stories__/components/InfinityScrollExample.tsx index 4695224a0b..62bf553b30 100644 --- a/src/components/TreeSelect/__stories__/components/InfinityScrollExample.tsx +++ b/src/components/TreeSelect/__stories__/components/InfinityScrollExample.tsx @@ -32,7 +32,7 @@ export const InfinityScrollExample = ({ itemsCount = 5, ...storyProps }: InfinityScrollExampleProps) => { - const [value, setValue] = React.useState([]); + const [value, setValue] = React.useState(); const { data: items = [], onFetchMore, diff --git a/src/components/TreeSelect/__stories__/components/WithDndListExample.tsx b/src/components/TreeSelect/__stories__/components/WithDndListExample.tsx index 39b1c9d821..de0859166f 100644 --- a/src/components/TreeSelect/__stories__/components/WithDndListExample.tsx +++ b/src/components/TreeSelect/__stories__/components/WithDndListExample.tsx @@ -38,7 +38,7 @@ type CustomDataType = {someRandomKey: string; id: string}; export interface WithDndListExampleProps extends Omit< TreeSelectProps, - 'value' | 'onUpdate' | 'items' | 'mapItemDataToContentProps' + 'value' | 'onUpdate' | 'items' | 'mapItemDataToContentProps' | 'multiple' > {} const randomItems: CustomDataType[] = createRandomizedData({ diff --git a/src/components/TreeSelect/__stories__/components/WithItemLinksAndActionsExample.tsx b/src/components/TreeSelect/__stories__/components/WithItemLinksAndActionsExample.tsx index c649526331..b7f5992368 100644 --- a/src/components/TreeSelect/__stories__/components/WithItemLinksAndActionsExample.tsx +++ b/src/components/TreeSelect/__stories__/components/WithItemLinksAndActionsExample.tsx @@ -28,14 +28,14 @@ export interface WithItemLinksAndActionsExampleProps > {} export const WithItemLinksAndActionsExample = (storyProps: WithItemLinksAndActionsExampleProps) => { - const [value, setValue] = React.useState([]); + const [value, setValue] = React.useState(); const [open, setOpen] = React.useState(true); const items = React.useMemo(() => createRandomizedData({num: 10, depth: 1}), []); const onItemClick = (id: ListItemId, list: UseListResult<{title: string}>) => { if (list.state.disabledById[id]) return; - setValue([id]); + setValue(id); list.state.setActiveItemId(id); diff --git a/src/components/TreeSelect/index.ts b/src/components/TreeSelect/index.ts index 6978ee396c..4e9304f23b 100644 --- a/src/components/TreeSelect/index.ts +++ b/src/components/TreeSelect/index.ts @@ -1,2 +1,2 @@ export {TreeSelect} from './TreeSelect'; -export type {TreeSelectProps, TreeSelectRenderItem} from './types'; +export type {TreeSelectProps, TreeSelectRenderItem, TreeSelectGetValue} from './types'; diff --git a/src/components/TreeSelect/types.ts b/src/components/TreeSelect/types.ts index 6038b64ae9..b4ed5142f5 100644 --- a/src/components/TreeSelect/types.ts +++ b/src/components/TreeSelect/types.ts @@ -45,12 +45,16 @@ export type TreeSelectRenderItem = TreeListRenderItem = TreeListContainerProps; export type TreeSelectRenderContainer = TreeListRenderContainer; -interface TreeSelectBehavioralProps extends UseListParsedStateProps { +interface TreeSelectBehavioralProps extends UseListParsedStateProps { withExpandedState?: boolean; - multiple?: boolean; + multiple?: M; } -export interface TreeSelectProps +export type TreeSelectGetValue = M extends true + ? ListItemId[] + : ListItemId | undefined; + +export interface TreeSelectProps extends Omit, 'list' | 'renderContainer' | 'multiple'>, Pick< TreeSelectRenderControlProps, @@ -63,8 +67,8 @@ export interface TreeSelectProps | 'errorMessage' >, UseOpenProps, - TreeSelectBehavioralProps { - value?: ListItemId[]; + TreeSelectBehavioralProps { + value?: TreeSelectGetValue; defaultValue?: ListItemId[] | undefined; popupClassName?: string; popupWidth?: SelectPopupProps['width']; @@ -82,7 +86,7 @@ export interface TreeSelectProps * In other situations use `renderContainer` method */ slotAfterListBody?: React.ReactNode; - onUpdate?(value: ListItemId[]): void; + onUpdate?(value: TreeSelectGetValue): void; /** * Ability to override custom toggler button */ diff --git a/src/components/useList/utils/getListItemClickHandler.ts b/src/components/useList/utils/getListItemClickHandler.ts index b311495ac4..f1cc38073e 100644 --- a/src/components/useList/utils/getListItemClickHandler.ts +++ b/src/components/useList/utils/getListItemClickHandler.ts @@ -1,14 +1,14 @@ import type {ListOnItemClick, UseListResult} from '../types'; -interface GetListItemClickHandlerProps { - multiple?: boolean; +interface GetListItemClickHandlerProps { + multiple?: M; list: UseListResult; } -export const getListItemClickHandler = ({ +export const getListItemClickHandler = ({ list, multiple, -}: GetListItemClickHandlerProps) => { +}: GetListItemClickHandlerProps) => { const onItemClick: ListOnItemClick = ({id}) => { if (list.state.disabledById[id]) return; diff --git a/src/unstable.ts b/src/unstable.ts index 18fdbba59f..25317292f5 100644 --- a/src/unstable.ts +++ b/src/unstable.ts @@ -23,6 +23,8 @@ export { export { TreeSelect as unstable_TreeSelect, type TreeSelectProps as unstable_TreeSelectProps, + type TreeSelectRenderItem as unstable_TreeSelectRenderItem, + type TreeSelectGetValue as unstable_TreeSelectGetValue, } from './components/TreeSelect'; export { TreeList as unstable_TreeList,