From dd736cc45c527c5b8630e89fef297c01ed763474 Mon Sep 17 00:00:00 2001 From: Alexandr Isaev Date: Mon, 18 Mar 2024 15:57:20 +0300 Subject: [PATCH] fix(withTableSettings): fix selected item issue --- .../Table/__stories__/Table.stories.tsx | 6 +- .../TableColumnSetup/TableColumnSetup.scss | 11 ---- .../TableColumnSetup/TableColumnSetup.tsx | 14 ++-- src/components/TreeList/TreeList.tsx | 20 +++--- .../__stories__/stories/DefaultStory.tsx | 4 +- .../stories/InfinityScrollStory.tsx | 8 +-- .../__stories__/stories/WithDndListStory.tsx | 8 +-- .../WithFiltrationAndControlsStory.tsx | 8 +-- .../WithGroupSelectionAndCustomIconStory.tsx | 5 +- .../stories/WithItemLinksAndActionsStory.tsx | 8 +-- src/components/TreeList/types.ts | 16 +++-- src/components/TreeSelect/TreeSelect.tsx | 65 ++++++++----------- .../__stories__/TreeSelect.stories.tsx | 7 +- .../components/InfinityScrollExample.tsx | 4 +- .../components/WithDndListExample.tsx | 8 +-- .../WithFiltrationAndControlsExample.tsx | 4 +- ...pSelectionControlledStateAndCustomIcon.tsx | 4 +- .../WithItemLinksAndActionsExample.tsx | 8 +-- src/components/TreeSelect/types.ts | 12 ++-- .../__stories__/components/FlattenList.tsx | 5 +- .../components/InfinityScrollList.tsx | 5 +- .../__stories__/components/ListWithDnd.tsx | 4 +- .../components/PopupWithTogglerList.tsx | 4 +- .../__stories__/components/RecursiveList.tsx | 9 +-- .../useList/__stories__/useList.mdx | 4 ++ src/components/useList/types.ts | 17 +---- .../useList/utils/getItemRenderState.tsx | 8 ++- 27 files changed, 129 insertions(+), 147 deletions(-) diff --git a/src/components/Table/__stories__/Table.stories.tsx b/src/components/Table/__stories__/Table.stories.tsx index 864fd65cfd..c6ec7b22de 100644 --- a/src/components/Table/__stories__/Table.stories.tsx +++ b/src/components/Table/__stories__/Table.stories.tsx @@ -145,7 +145,11 @@ const WithTableActionsTemplate: StoryFn> = (args) => { const items = ['action 1', 'action 2', 'action 3']; return ( - ({title})} /> + ({title})} + /> ); }} /> diff --git a/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.scss b/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.scss index 11820473db..a9a535280c 100644 --- a/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.scss +++ b/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.scss @@ -6,15 +6,4 @@ $block: '.#{variables.$ns}table-column-setup'; &__controls { margin: var(--g-spacing-1) var(--g-spacing-1) 0; } - - // to override this https://github.com/gravity-ui/uikit/blob/main/src/components/useList/components/ListItemView/ListItemView.scss#L25 - &__required-item { - background: inherit; - - &:hover { - /* stylelint-disable declaration-no-important */ - background: var(--g-color-base-simple-hover-solid) !important; - /* stylelint-enable declaration-no-important */ - } - } } diff --git a/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx b/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx index 3443ddd198..1af82a7d1d 100644 --- a/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx +++ b/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx @@ -25,15 +25,14 @@ import i18n from './i18n'; import './TableColumnSetup.scss'; -const b = block('table-column-setup'); -const tableColumnSetupCn = b(null); -const controlsCn = b('controls'); -const requiredDndItemCn = b('required-item'); - function identity(value: T): T { return value; } +const b = block('table-column-setup'); +const tableColumnSetupCn = b(null); +const controlsCn = b('controls'); + const reorderArray = (list: T[], startIndex: number, endIndex: number): T[] => { const result = [...list]; const [removed] = result.splice(startIndex, 1); @@ -50,8 +49,7 @@ const prepareDndItems = (tableColumnItems: TableColumnSetupItem[]) => { ...tableColumnItem, startSlot: tableColumnItem.isRequired ? : undefined, hasSelectionIcon, - // to overwrite select background effect - https://github.com/gravity-ui/uikit/blob/main/src/components/useList/components/ListItemView/ListItemView.tsx#L125 - className: hasSelectionIcon ? undefined : requiredDndItemCn, + selected: hasSelectionIcon ? tableColumnItem.isSelected : undefined, }; }); }; @@ -281,7 +279,7 @@ export const TableColumnSetup = (props: TableColumnSetupProps) => { return ( ({ multiple, setActiveItemId, containerRef: propsContainerRef, - getItemContent, + mapItemDataToProps, }: TreeListProps) => { const uniqId = useUniqId(); const treeListId = id ?? uniqId; @@ -47,7 +47,6 @@ export const TreeList = ({ onItemClick?.({ id: listItemId, data: listParsedState.itemsById[listItemId], - isGroup: listItemId in listParsedState.groupsState, disabled: disabledById ? Boolean(disabledById[listItemId]) : Boolean(listParsedState.initialState.disabledById[listItemId]), @@ -55,6 +54,8 @@ export const TreeList = ({ listParsedState.visibleFlattenIds[ listParsedState.visibleFlattenIds.length - 1 ] === listItemId, + groupState: listParsedState.groupsState[listItemId], + itemState: listParsedState.itemsState[listItemId], }); }, [ @@ -62,6 +63,7 @@ export const TreeList = ({ listParsedState.groupsState, listParsedState.initialState.disabledById, listParsedState.itemsById, + listParsedState.itemsState, listParsedState.visibleFlattenIds, onItemClick, ], @@ -84,6 +86,7 @@ export const TreeList = ({ const renderState = getItemRenderState({ id: itemId, size, + mapItemDataToProps, onItemClick: handleItemClick, ...listParsedState, expandedById, @@ -105,13 +108,7 @@ export const TreeList = ({ }); } - return ( - - ); + return ; }; // not JSX decl here is from weird `react-beautiful-dnd` render bug @@ -121,7 +118,10 @@ export const TreeList = ({ containerRef, className: b(null, className), ...listParsedState, - ...{expandedById, disabledById, activeItemId, selectedById}, + expandedById, + disabledById, + activeItemId, + selectedById, renderItem, }); }; diff --git a/src/components/TreeList/__stories__/stories/DefaultStory.tsx b/src/components/TreeList/__stories__/stories/DefaultStory.tsx index e28d20a275..fc4beea349 100644 --- a/src/components/TreeList/__stories__/stories/DefaultStory.tsx +++ b/src/components/TreeList/__stories__/stories/DefaultStory.tsx @@ -10,7 +10,7 @@ function identity(value: T): T { } export interface DefaultStoryProps - extends Omit, 'items' | 'getItemContent'> { + extends Omit, 'items' | 'mapItemDataToProps'> { itemsCount?: number; } @@ -19,7 +19,7 @@ export const DefaultStory = ({itemsCount = 5, ...props}: DefaultStoryProps) => { return ( - + ); }; diff --git a/src/components/TreeList/__stories__/stories/InfinityScrollStory.tsx b/src/components/TreeList/__stories__/stories/InfinityScrollStory.tsx index 9a28d47d3a..756cd694ac 100644 --- a/src/components/TreeList/__stories__/stories/InfinityScrollStory.tsx +++ b/src/components/TreeList/__stories__/stories/InfinityScrollStory.tsx @@ -17,7 +17,7 @@ function identity(value: T): T { export interface InfinityScrollStoryProps extends Omit< TreeListProps<{title: string}>, - 'value' | 'onUpdate' | 'items' | 'multiple' | 'size' | 'getItemContent' + 'value' | 'onUpdate' | 'items' | 'multiple' | 'size' | 'mapItemDataToProps' > { itemsCount?: number; } @@ -25,12 +25,12 @@ export interface InfinityScrollStoryProps export const InfinityScrollStory = ({itemsCount = 5, ...storyProps}: InfinityScrollStoryProps) => { const listState = useListState(); - const handleItemClick: TreeListOnItemClick<{title: string}> = ({id, isGroup, disabled}) => { + const handleItemClick: TreeListOnItemClick<{title: string}> = ({id, groupState, disabled}) => { if (disabled) return; listState.setActiveItemId(id); - if (isGroup) { + if (groupState) { listState.setExpanded((prevState) => ({ ...prevState, [id]: id in prevState ? !prevState[id] : false, @@ -56,7 +56,7 @@ export const InfinityScrollStory = ({itemsCount = 5, ...storyProps}: InfinityScr size="l" {...storyProps} {...listState} - getItemContent={identity} + mapItemDataToProps={identity} items={items} multiple onItemClick={handleItemClick} diff --git a/src/components/TreeList/__stories__/stories/WithDndListStory.tsx b/src/components/TreeList/__stories__/stories/WithDndListStory.tsx index f36186cbc6..b05f482f3c 100644 --- a/src/components/TreeList/__stories__/stories/WithDndListStory.tsx +++ b/src/components/TreeList/__stories__/stories/WithDndListStory.tsx @@ -41,7 +41,7 @@ const randomItems: CustomDataType[] = createRandomizedData({ }).map(({data}, idx) => ({someRandomKey: data, id: String(idx)})); export interface WithDndListStoryProps - extends Omit, 'items' | 'getItemContent' | 'getItemContent'> {} + extends Omit, 'items' | 'mapItemDataToProps'> {} export const WithDndListStory = (storyProps: WithDndListStoryProps) => { const [items, setItems] = React.useState(randomItems); @@ -146,11 +146,11 @@ export const WithDndListStory = (storyProps: WithDndListStoryProps) => { {...storyProps} items={items} {...listState} - getItemContent={({someRandomKey}) => ({title: someRandomKey})} + mapItemDataToProps={({someRandomKey}) => ({title: someRandomKey})} // you can omit this prop here. If prop `id` passed, TreeSelect would take it by default getId={({id}) => id} - onItemClick={({id, isGroup, disabled}) => { - if (!isGroup && !disabled) { + onItemClick={({id, groupState, disabled}) => { + if (!groupState && !disabled) { listState.setSelected((prevState) => ({ [id]: !prevState[id], })); diff --git a/src/components/TreeList/__stories__/stories/WithFiltrationAndControlsStory.tsx b/src/components/TreeList/__stories__/stories/WithFiltrationAndControlsStory.tsx index c1ba030988..534436d04d 100644 --- a/src/components/TreeList/__stories__/stories/WithFiltrationAndControlsStory.tsx +++ b/src/components/TreeList/__stories__/stories/WithFiltrationAndControlsStory.tsx @@ -13,7 +13,7 @@ import {RenderVirtualizedContainer} from '../components/RenderVirtualizedContain export interface WithFiltrationAndControlsStoryProps extends Omit< TreeListProps<{title: string}>, - 'value' | 'onUpdate' | 'items' | 'getItemContent' + 'value' | 'onUpdate' | 'items' | 'mapItemDataToProps' > { itemsCount?: number; } @@ -58,10 +58,10 @@ export const WithFiltrationAndControlsStory = ({ { + onItemClick={({id, groupState, disabled}) => { if (disabled) return; - if (isGroup) { + if (groupState) { listState.setExpanded((prevState) => ({ ...prevState, [id]: id in prevState ? !prevState[id] : false, @@ -81,7 +81,7 @@ export const WithFiltrationAndControlsStory = ({ listState.setActiveItemId(id); }} - getItemContent={(x) => x} + mapItemDataToProps={(x) => x} renderContainer={renderContainer} items={filterState.items} /> diff --git a/src/components/TreeList/__stories__/stories/WithGroupSelectionAndCustomIconStory.tsx b/src/components/TreeList/__stories__/stories/WithGroupSelectionAndCustomIconStory.tsx index ec438faf8c..5c42291911 100644 --- a/src/components/TreeList/__stories__/stories/WithGroupSelectionAndCustomIconStory.tsx +++ b/src/components/TreeList/__stories__/stories/WithGroupSelectionAndCustomIconStory.tsx @@ -21,7 +21,7 @@ interface CustomDataStructure { export interface WithGroupSelectionAndCustomIconStoryProps extends Omit< TreeListProps, - 'value' | 'onUpdate' | 'items' | 'multiple' | 'cantainerRef' | 'size' | 'getItemContent' + 'value' | 'onUpdate' | 'items' | 'multiple' | 'cantainerRef' | 'size' | 'mapItemDataToProps' > { itemsCount?: number; } @@ -43,7 +43,6 @@ export const WithGroupSelectionAndCustomIconStory = ({ const handleItemClick: TreeListOnItemClick = ({id, disabled}) => { if (disabled) return; - console.log(1); listState.setSelected((prevState) => ({ [id]: !prevState[id], @@ -57,7 +56,7 @@ export const WithGroupSelectionAndCustomIconStory = ({ (value: T): T { export interface WithItemLinksAndActionsStoryProps extends Omit< TreeListProps<{title: string}>, - 'items' | 'size' | 'multiple' | 'getItemContent' + 'items' | 'size' | 'multiple' | 'mapItemDataToProps' > {} export const WithItemLinksAndActionsStory = (props: WithItemLinksAndActionsStoryProps) => { @@ -30,11 +30,11 @@ export const WithItemLinksAndActionsStory = (props: WithItemLinksAndActionsStory { - if (!isGroup && !disabled) { + onItemClick={({id, groupState, disabled}) => { + if (!groupState && !disabled) { listState.setSelected((prevState) => ({[id]: !prevState[id]})); } }} diff --git a/src/components/TreeList/types.ts b/src/components/TreeList/types.ts index 1181d69df4..a6c07989c7 100644 --- a/src/components/TreeList/types.ts +++ b/src/components/TreeList/types.ts @@ -9,13 +9,13 @@ import type { ListParsedState, ListState, RenderItemContext, - RenderItemState, + RenderItemProps, } from '../useList'; export type TreeListRenderItem = (props: { data: T; // required item props to render - props: RenderItemState; + props: RenderItemProps; // internal list context props itemState: RenderItemContext; index: number; @@ -24,13 +24,17 @@ export type TreeListRenderItem = (props: { interface ItemClickContext { id: ListItemId; - isGroup: boolean; + /** + * Defined only if item is group + */ + groupState?: ListParsedState['groupsState'][number]; + itemState: ListParsedState['itemsState'][number]; isLastItem: boolean; disabled: boolean; data: T; } -export type TreeListOnItemClick = (ctx: ItemClickContext) => void; +export type TreeListOnItemClick = (ctx: ItemClickContext & R) => void; export type TreeListRenderContainerProps = ListParsedState & Partial & { @@ -52,7 +56,7 @@ export type TreeListRenderContainer = ( props: TreeListRenderContainerProps, ) => React.JSX.Element; -export type TreeListRenderContent = (item: T) => KnownItemStructure; +export type TreeListMapItemDataToProps = (item: T) => KnownItemStructure; export interface TreeListProps extends QAProps, Partial { /** @@ -77,7 +81,7 @@ export interface TreeListProps extends QAProps, Partial { * If you want to disable default behavior pass `disabled` as a value; */ onItemClick?: TreeListOnItemClick; - getItemContent: TreeListRenderContent; + mapItemDataToProps: TreeListMapItemDataToProps; /** * Required for keyboard correct work */ diff --git a/src/components/TreeSelect/TreeSelect.tsx b/src/components/TreeSelect/TreeSelect.tsx index 76d44a0885..1372871224 100644 --- a/src/components/TreeSelect/TreeSelect.tsx +++ b/src/components/TreeSelect/TreeSelect.tsx @@ -20,6 +20,10 @@ import './TreeSelect.scss'; const b = block('tree-select'); +const defaultItemRenderer: TreeListRenderItem = (renderState) => { + return ; +}; + export const TreeSelect = React.forwardRef(function TreeSelect( { id, @@ -50,11 +54,11 @@ export const TreeSelect = React.forwardRef(function TreeSelect( getId, onOpenChange, renderControl, - renderItem, + renderItem = defaultItemRenderer as TreeListRenderItem, renderContainer, onItemClick, setActiveItemId: propsSetActiveItemId, - getItemContent, + mapItemDataToProps, }: TreeSelectProps, ref: React.Ref, ) { @@ -110,14 +114,14 @@ export const TreeSelect = React.forwardRef(function TreeSelect( }); const handleItemClick = React.useCallback>( - ({id: listItemId, data, isGroup, isLastItem}) => { + ({id: listItemId, data, groupState, isLastItem, itemState}) => { const defaultHandleClick = () => { if (listState.disabledById[listItemId]) return; // always activate selected item setActiveItemId(listItemId); - if (isGroup && groupsBehavior === 'expandable') { + if (groupState && groupsBehavior === 'expandable') { listState.setExpanded((state) => ({ ...state, // toggle expanded state by id, by default all groups expanded @@ -136,7 +140,8 @@ export const TreeSelect = React.forwardRef(function TreeSelect( return onItemClick({ id: listItemId, data, - isGroup, + groupState, + itemState, isLastItem, disabled: listState.disabledById[listItemId], defaultClickCallback: defaultHandleClick, @@ -192,7 +197,7 @@ export const TreeSelect = React.forwardRef(function TreeSelect( getItemContent(listParsedState.itemsById[itemId]).title), + value.map((itemId) => mapItemDataToProps(listParsedState.itemsById[itemId]).title), ).join(', ')} view="normal" pin="round-round" @@ -211,18 +216,6 @@ export const TreeSelect = React.forwardRef(function TreeSelect( inlineStyles.width = width; } - const defaultItemRenderer: TreeListRenderItem = (renderState) => { - const itemData = renderState.data; - - return ( - - ); - }; - return ( ( {slotBeforeListBody} - {...{ - size, - className: containerClassName, - qa, - multiple, - id: `list-${treeSelectId}`, - containerRef, - getId, - disabledById: listState.disabledById, - selectedById: listState.selectedById, - expandedById: listState.expandedById, - activeItemId: listState.activeItemId, - setActiveItemId, - onItemClick: handleItemClick, - items, - renderContainer, - getItemContent, - renderItem: renderItem ?? defaultItemRenderer, - }} + size={size} + className={containerClassName} + qa={qa} + multiple={multiple} + id={`list-${treeSelectId}`} + containerRef={containerRef} + getId={getId} + disabledById={listState.disabledById} + selectedById={listState.selectedById} + expandedById={listState.expandedById} + activeItemId={listState.activeItemId} + setActiveItemId={setActiveItemId} + onItemClick={handleItemClick} + items={items} + renderContainer={renderContainer} + mapItemDataToProps={mapItemDataToProps} + renderItem={renderItem ?? defaultItemRenderer} /> {slotAfterListBody} diff --git a/src/components/TreeSelect/__stories__/TreeSelect.stories.tsx b/src/components/TreeSelect/__stories__/TreeSelect.stories.tsx index 8e93728eec..b34ed0066c 100644 --- a/src/components/TreeSelect/__stories__/TreeSelect.stories.tsx +++ b/src/components/TreeSelect/__stories__/TreeSelect.stories.tsx @@ -24,7 +24,10 @@ export default { } as Meta; const DefaultTemplate: StoryFn< - Omit, 'value' | 'onUpdate' | 'items' | 'getItemContent'> & { + Omit< + TreeSelectProps<{title: string}>, + 'value' | 'onUpdate' | 'items' | 'mapItemDataToProps' + > & { itemsCount?: number; } > = ({itemsCount = 5, ...props}) => { @@ -34,7 +37,7 @@ const DefaultTemplate: StoryFn< x} + mapItemDataToProps={(x) => x} items={items} onUpdate={(...args) => console.log('Uncontrolled `TreeSelect onUpdate args: `', ...args) diff --git a/src/components/TreeSelect/__stories__/components/InfinityScrollExample.tsx b/src/components/TreeSelect/__stories__/components/InfinityScrollExample.tsx index 80d9d0378f..138583ed83 100644 --- a/src/components/TreeSelect/__stories__/components/InfinityScrollExample.tsx +++ b/src/components/TreeSelect/__stories__/components/InfinityScrollExample.tsx @@ -17,7 +17,7 @@ function identity(value: T): T { export interface InfinityScrollExampleProps extends Omit< TreeSelectProps<{title: string}>, - 'value' | 'onUpdate' | 'items' | 'getItemContent' + 'value' | 'onUpdate' | 'items' | 'mapItemDataToProps' > { itemsCount?: number; } @@ -38,7 +38,7 @@ export const InfinityScrollExample = ({ {...storyProps} - getItemContent={identity} + mapItemDataToProps={identity} items={items} value={value} renderItem={({data, props, itemState: {isLastItem, groupState}}) => { diff --git a/src/components/TreeSelect/__stories__/components/WithDndListExample.tsx b/src/components/TreeSelect/__stories__/components/WithDndListExample.tsx index c08048d5bf..8d5303c5b2 100644 --- a/src/components/TreeSelect/__stories__/components/WithDndListExample.tsx +++ b/src/components/TreeSelect/__stories__/components/WithDndListExample.tsx @@ -39,7 +39,7 @@ type CustomDataType = {someRandomKey: string; id: string}; export interface WithDndListExampleProps extends Omit< TreeSelectProps, - 'value' | 'onUpdate' | 'items' | 'getItemContent' | 'getItemContent' + 'value' | 'onUpdate' | 'items' | 'mapItemDataToProps' > {} const randomItems: CustomDataType[] = createRandomizedData({ @@ -151,11 +151,11 @@ export const WithDndListExample = (storyProps: WithDndListExampleProps) => { setActiveItemId={setActiveItemId} // you can omit this prop here. If prop `id` passed, TreeSelect would take it by default getId={({id}) => id} - getItemContent={({someRandomKey}) => ({ + mapItemDataToProps={({someRandomKey}) => ({ title: someRandomKey, })} - onItemClick={({id, isGroup, disabled}) => { - if (!isGroup && !disabled) { + onItemClick={({id, groupState, disabled}) => { + if (!groupState && !disabled) { setValue([id]); setActiveItemId(id); } diff --git a/src/components/TreeSelect/__stories__/components/WithFiltrationAndControlsExample.tsx b/src/components/TreeSelect/__stories__/components/WithFiltrationAndControlsExample.tsx index 69227dce02..d406e64a4d 100644 --- a/src/components/TreeSelect/__stories__/components/WithFiltrationAndControlsExample.tsx +++ b/src/components/TreeSelect/__stories__/components/WithFiltrationAndControlsExample.tsx @@ -17,7 +17,7 @@ function identity(value: T): T { export interface WithFiltrationAndControlsExampleProps extends Omit< TreeSelectProps<{title: string}>, - 'value' | 'onUpdate' | 'items' | 'getItemContent' + 'value' | 'onUpdate' | 'items' | 'mapItemDataToProps' > { itemsCount?: number; } @@ -51,7 +51,7 @@ export const WithFiltrationAndControlsExample = ({ , - 'value' | 'onUpdate' | 'items' | 'getItemContent' | 'size' + 'value' | 'onUpdate' | 'items' | 'mapItemDataToProps' | 'size' > { itemsCount?: number; } @@ -50,7 +50,7 @@ export const WithGroupSelectionControlledStateAndCustomIconExample = ({ (value: T): T { export interface WithItemLinksAndActionsExampleProps extends Omit< TreeSelectProps<{title: string}>, - 'value' | 'onUpdate' | 'items' | 'getItemContent' | 'size' | 'open' | 'onOpenChange' + 'value' | 'onUpdate' | 'items' | 'mapItemDataToProps' | 'size' | 'open' | 'onOpenChange' > {} export const WithItemLinksAndActionsExample = (props: WithItemLinksAndActionsExampleProps) => { @@ -31,14 +31,14 @@ export const WithItemLinksAndActionsExample = (props: WithItemLinksAndActionsExa { - if (!isGroup && !disabled) { + onItemClick={({id, groupState, disabled}) => { + if (!groupState && !disabled) { setValue([id]); } diff --git a/src/components/TreeSelect/types.ts b/src/components/TreeSelect/types.ts index 0b86e27e42..351b81cc99 100644 --- a/src/components/TreeSelect/types.ts +++ b/src/components/TreeSelect/types.ts @@ -3,9 +3,10 @@ import type React from 'react'; import type {PopperPlacement} from '../../hooks/private'; import type {SelectPopupProps} from '../Select/components/SelectPopup/types'; import type { + TreeListMapItemDataToProps, + TreeListOnItemClick, TreeListRenderContainer, TreeListRenderContainerProps, - TreeListRenderContent, } from '../TreeList/types'; import type {QAProps} from '../types'; import type { @@ -13,9 +14,8 @@ import type { ListItemSize, ListItemType, ListState, - OverrideItemContext, RenderItemContext, - RenderItemState, + RenderItemProps, } from '../useList'; export type TreeSelectRenderControlProps = { @@ -32,7 +32,7 @@ export type TreeSelectRenderControlProps = { export type TreeSelectRenderItem = (props: { data: T; // required item props to render - props: RenderItemState; + props: RenderItemProps; // internal list context props itemState: RenderItemContext; index: number; @@ -98,10 +98,10 @@ export interface TreeSelectProps extends QAProps, Partial; - onItemClick?: (ctx: OverrideItemContext) => void; + onItemClick?: TreeListOnItemClick; /** * Map item data to view props */ - getItemContent: TreeListRenderContent; + mapItemDataToProps: TreeListMapItemDataToProps; setActiveItemId?(listItemId?: ListItemId): void; } diff --git a/src/components/useList/__stories__/components/FlattenList.tsx b/src/components/useList/__stories__/components/FlattenList.tsx index 225b24de25..2e46fb9f45 100644 --- a/src/components/useList/__stories__/components/FlattenList.tsx +++ b/src/components/useList/__stories__/components/FlattenList.tsx @@ -84,14 +84,15 @@ export const FlattenList = ({itemsCount, size}: FlattenListProps) => { } > {(id) => { - const {data, props} = getItemRenderState({ + const {props} = getItemRenderState({ id, size, onItemClick, + mapItemDataToProps: (x) => x, ...list, ...listState, }); - return ; + return ; }} diff --git a/src/components/useList/__stories__/components/InfinityScrollList.tsx b/src/components/useList/__stories__/components/InfinityScrollList.tsx index 7246c070ce..1a3fd6b3f6 100644 --- a/src/components/useList/__stories__/components/InfinityScrollList.tsx +++ b/src/components/useList/__stories__/components/InfinityScrollList.tsx @@ -92,14 +92,15 @@ export const InfinityScrollList = ({size}: InfinityScrollListProps) => { idToFlattenIndex={list.idToFlattenIndex} > {(id) => { - const {data, props, context} = getItemRenderState({ + const {props, context} = getItemRenderState({ id, size, onItemClick, + mapItemDataToProps: (x) => x, ...list, ...listState, }); - const node = ; + const node = ; if (context.isLastItem) { return ( diff --git a/src/components/useList/__stories__/components/ListWithDnd.tsx b/src/components/useList/__stories__/components/ListWithDnd.tsx index 306eff8388..49ad89f87c 100644 --- a/src/components/useList/__stories__/components/ListWithDnd.tsx +++ b/src/components/useList/__stories__/components/ListWithDnd.tsx @@ -90,10 +90,11 @@ export const ListWithDnd = ({size, itemsCount}: ListWithDndProps) => {
{list.visibleFlattenIds.map((id, index) => { - const {data, props} = getItemRenderState({ + const {props} = getItemRenderState({ id, size, onItemClick, + mapItemDataToProps: (x) => x, ...list, ...listState, }); @@ -110,7 +111,6 @@ export const ListWithDnd = ({size, itemsCount}: ListWithDndProps) => { ) => ( {(id) => { - const {data, props, context} = getItemRenderState({ + const {props, context} = getItemRenderState({ id, size, onItemClick, + mapItemDataToProps: (x) => x, ...list, ...listState, }); @@ -118,7 +119,6 @@ export const PopupWithTogglerList = ({size, itemsCount}: PopupWithTogglerListPro return ( ); diff --git a/src/components/useList/__stories__/components/RecursiveList.tsx b/src/components/useList/__stories__/components/RecursiveList.tsx index 66abfe7d03..e67dd17c04 100644 --- a/src/components/useList/__stories__/components/RecursiveList.tsx +++ b/src/components/useList/__stories__/components/RecursiveList.tsx @@ -82,20 +82,17 @@ export const RecursiveList = ({size, itemsCount}: RecursiveListProps) => { idToFlattenIndex={list.idToFlattenIndex} > {(id) => { - const {data, props, context} = getItemRenderState({ + const {props, context} = getItemRenderState({ id, size, onItemClick, + mapItemDataToProps: (x) => x, ...list, ...listState, }); return ( - + ); }} diff --git a/src/components/useList/__stories__/useList.mdx b/src/components/useList/__stories__/useList.mdx index 0e7afaf839..3b920e2f5a 100644 --- a/src/components/useList/__stories__/useList.mdx +++ b/src/components/useList/__stories__/useList.mdx @@ -73,6 +73,7 @@ function List() { context: _context, } = getItemRenderState({ id: String(i), + mapItemDataToProps: (title) => ({title}), onItemClick, ...list, ...listState, @@ -121,6 +122,7 @@ function List() { context: _context, } = getItemRenderState({ id: String(i), + mapItemDataToProps: (title) => ({title}), onItemClick, ...list, ...listState, @@ -496,6 +498,7 @@ item = T - `disabled` - is item disabled; - `selected` - is item selected; - `onClick` - on item click handle if exists; + - `mapItemDataToProps` - map item data to view render props with `KnownItemStructure` interface - item list context: - `itemState`: - `parentId?` - id of parant element; @@ -518,6 +521,7 @@ const handleItemClick = () => {}; id, size, // list size onItemClick: handleItemClick, + mapItemDataToProps, ...list, ...listState, }); diff --git a/src/components/useList/types.ts b/src/components/useList/types.ts index 5636a31d58..ca9d06c7e9 100644 --- a/src/components/useList/types.ts +++ b/src/components/useList/types.ts @@ -46,19 +46,6 @@ export type KnownItemStructure = { endSlot?: React.ReactNode; }; -export interface OverrideItemContext { - id: ListItemId; - isGroup: boolean; - disabled: boolean; - isLastItem: boolean; - data: T; - /** - * Use this callback if you don't wont to copy all selection logic (single|multiple variant). - * For example, you wont to add some additional logic on click - */ - defaultClickCallback: () => void; -} - export type RenderItemContext = { /** * optional, because ids may be skipped in the flatten order list, @@ -73,7 +60,7 @@ export type RenderItemContext = { isLastItem: boolean; }; -export type RenderItemState = { +export type RenderItemProps = { size: ListItemSize; id: ListItemId; onClick?(): void; @@ -83,7 +70,7 @@ export type RenderItemState = { active: boolean; indentation: number; hasSelectionIcon?: boolean; -}; +} & KnownItemStructure; export type ParsedState = { /** diff --git a/src/components/useList/utils/getItemRenderState.tsx b/src/components/useList/utils/getItemRenderState.tsx index 097092c10b..eb6ab2244e 100644 --- a/src/components/useList/utils/getItemRenderState.tsx +++ b/src/components/useList/utils/getItemRenderState.tsx @@ -1,17 +1,19 @@ /* eslint-disable valid-jsdoc */ import type { + KnownItemStructure, ListItemId, ListItemSize, ListParsedState, ListState, RenderItemContext, - RenderItemState, + RenderItemProps, } from '../types'; type ItemRendererProps = Partial & ListParsedState & { size?: ListItemSize; id: ListItemId; + mapItemDataToProps(data: T): KnownItemStructure; onItemClick?(id: ListItemId): void; }; @@ -25,6 +27,7 @@ export const getItemRenderState = ( expandedById, groupsState, onItemClick, + mapItemDataToProps, visibleFlattenIds, size = 'm', itemsState, @@ -49,7 +52,7 @@ export const getItemRenderState = ( expanded = expandedById[id] ?? defaultExpanded; } - const stateProps: RenderItemState = { + const stateProps: RenderItemProps = { id, size, expanded, @@ -58,6 +61,7 @@ export const getItemRenderState = ( disabled: Boolean(disabledById?.[id]), selected: Boolean(selectedById?.[id]), onClick: onItemClick ? () => onItemClick(id) : undefined, + ...mapItemDataToProps(itemsById[id]), }; return {data: itemsById[id], props: stateProps, context};