From 7f66d28d9e3b43ebe388b90b6bc8e2799962d0c0 Mon Sep 17 00:00:00 2001 From: Isaev Alexandr Date: Thu, 18 Apr 2024 15:16:17 +0300 Subject: [PATCH] feat(useList): added ability to define initial value to useListState (#1483) Co-authored-by: Alexandr Isaev --- src/components/TreeSelect/TreeSelect.tsx | 10 +-- ...pSelectionControlledStateAndCustomIcon.tsx | 13 ++-- .../useList/__stories__/useList.mdx | 64 +++++++++++++------ src/components/useList/hooks/useListState.ts | 42 ++++++++++-- 4 files changed, 95 insertions(+), 34 deletions(-) diff --git a/src/components/TreeSelect/TreeSelect.tsx b/src/components/TreeSelect/TreeSelect.tsx index 1c271ee78b..9a9f0d4cfe 100644 --- a/src/components/TreeSelect/TreeSelect.tsx +++ b/src/components/TreeSelect/TreeSelect.tsx @@ -80,10 +80,12 @@ export const TreeSelect = React.forwardRef(function TreeSelect( }); const listState = useListState({ - expandedById, - disabledById, - activeItemId, - selectedById: selected, + controlledValues: { + expandedById, + disabledById, + activeItemId, + selectedById: selected, + }, }); const setActiveItemId = propsSetActiveItemId ?? listState.setActiveItemId; diff --git a/src/components/TreeSelect/__stories__/components/WithGroupSelectionControlledStateAndCustomIcon.tsx b/src/components/TreeSelect/__stories__/components/WithGroupSelectionControlledStateAndCustomIcon.tsx index 651cc0f0b6..b08c1c0504 100644 --- a/src/components/TreeSelect/__stories__/components/WithGroupSelectionControlledStateAndCustomIcon.tsx +++ b/src/components/TreeSelect/__stories__/components/WithGroupSelectionControlledStateAndCustomIcon.tsx @@ -5,8 +5,8 @@ import {ChevronDown, ChevronUp, Database, PlugConnection} from '@gravity-ui/icon import {Button} from '../../../Button'; import {Icon} from '../../../Icon'; import {Flex, spacing} from '../../../layout'; -import {ListItemView, getListParsedState} from '../../../useList'; -import type {ListItemCommonProps, ListItemId} from '../../../useList'; +import {ListItemView, getListParsedState, useListState} from '../../../useList'; +import type {ListItemCommonProps} from '../../../useList'; import {createRandomizedData} from '../../../useList/__stories__/utils/makeData'; import {TreeSelect} from '../../TreeSelect'; import type {TreeSelectProps} from '../../types'; @@ -40,9 +40,12 @@ export const WithGroupSelectionControlledStateAndCustomIconExample = ({ ); const [value, setValue] = React.useState([]); - const [expandedById, setExpanded] = React.useState>( - () => getListParsedState(items).initialState.expandedById, - ); + + const {expandedById, setExpanded} = useListState({ + initialValues: { + expandedById: getListParsedState(items).initialState.expandedById, + }, + }); return ( diff --git a/src/components/useList/__stories__/useList.mdx b/src/components/useList/__stories__/useList.mdx index df4c58f5ca..189da940b7 100644 --- a/src/components/useList/__stories__/useList.mdx +++ b/src/components/useList/__stories__/useList.mdx @@ -380,6 +380,32 @@ const { } = useListState(); ``` +#### props: + +```tsx +interface UseListStateProps { + /** + * Initial state values + */ + initialValues?: Partial; + /** + * Ability to pass link to another state value + */ + controlledValues?: Partial; +} +``` + +##### controlledValues example: + +```tsx +const listState = useListState(); + +// inside your component +const innerListState = useListState({ + controlledValues: listState, +}); +``` + ## Components: ### ListItemView @@ -389,33 +415,33 @@ Use it even if the functionality of the `useList` hook seems redundant to you ```tsx import { - type unstable_ListItemType as ListItemType, - unstable_ListItemView as ListItemView, + type unstable_ListItemType as ListItemType, + unstable_ListItemView as ListItemView, } from '@gravity-ui/uikit/unstable'; type Entity = {title: stirng, subtitle: string, icon: React.ReactNode}; const items: ListItemType[] = [ - {title: 'some title 1', subtitle: 'some subtitle 1', icon: }, - {title: 'some title 2', subtitle: 'some subtitle 2', icon: }, + {title: 'some title 1', subtitle: 'some subtitle 1', icon: }, + {title: 'some title 2', subtitle: 'some subtitle 2', icon: }, ]; const List = () => { - return ( - <> - {items.map(item, i) => { - return ( - - ) - }} - - ) + return ( + <> + {items.map(item, i) => { + return ( + + ) + }} + + ) }; ``` diff --git a/src/components/useList/hooks/useListState.ts b/src/components/useList/hooks/useListState.ts index 5b3165abe9..2e51d0097e 100644 --- a/src/components/useList/hooks/useListState.ts +++ b/src/components/useList/hooks/useListState.ts @@ -3,7 +3,25 @@ import React from 'react'; import type {ListState} from '../types'; -interface UseListStateProps extends Partial {} +interface UseListStateProps { + /** + * Initial state values + */ + initialValues?: Partial; + /** + * Ability to pass link to another state value + * + * ```tsx + * const listState = useListState() + * + * // inside your component + * const innerListState = useListState({ + * controlledValues: listState + * }) + * ``` + */ + controlledValues?: Partial; +} function useControlledState(value: T, defaultValue: T) { const [state, setState] = React.useState(value || defaultValue); @@ -11,11 +29,23 @@ function useControlledState(value: T, defaultValue: T) { return [value || state, setState] as const; } -export const useListState = (props: UseListStateProps = {}) => { - const [disabledById, setDisabled] = useControlledState(props.disabledById!, {}); - const [selectedById, setSelected] = useControlledState(props.selectedById!, {}); - const [expandedById, setExpanded] = useControlledState(props.expandedById!, {}); - const [activeItemId, setActiveItemId] = useControlledState(props.activeItemId, undefined); +export const useListState = ({initialValues, controlledValues}: UseListStateProps = {}) => { + const [disabledById, setDisabled] = useControlledState( + controlledValues?.disabledById!, + initialValues?.disabledById || {}, + ); + const [selectedById, setSelected] = useControlledState( + controlledValues?.selectedById!, + initialValues?.selectedById || {}, + ); + const [expandedById, setExpanded] = useControlledState( + controlledValues?.expandedById!, + initialValues?.expandedById || {}, + ); + const [activeItemId, setActiveItemId] = useControlledState( + controlledValues?.activeItemId, + initialValues?.activeItemId, + ); return { disabledById,