From 1dbbbf942f5fa88373def86080b3e1a64b32830f Mon Sep 17 00:00:00 2001 From: Alexandr Isaev Date: Wed, 27 Mar 2024 11:33:32 +0300 Subject: [PATCH] fix: more docs fixes --- .../TreeList/__stories__/TreeList.mdx | 145 ++++++++++++++---- .../WithGroupSelectionAndCustomIconStory.tsx | 4 +- src/components/TreeList/types.ts | 4 +- ...pSelectionControlledStateAndCustomIcon.tsx | 4 +- .../useList/__stories__/useList.mdx | 136 ++++++++++++---- .../components/ListItemView/ListItemView.tsx | 8 +- src/components/useList/types.ts | 4 +- .../useList/utils/getItemRenderState.tsx | 4 +- 8 files changed, 234 insertions(+), 75 deletions(-) diff --git a/src/components/TreeList/__stories__/TreeList.mdx b/src/components/TreeList/__stories__/TreeList.mdx index 4eed8e98f8..b7d734cea4 100644 --- a/src/components/TreeList/__stories__/TreeList.mdx +++ b/src/components/TreeList/__stories__/TreeList.mdx @@ -199,7 +199,7 @@ Control the default expanded state of items' groups. Default - `true`. ### renderItem -The ability to completely redefine the rendering of a list item. For example, add dividers between list items or wrap an item in a link component. As a view component to display a list item, use [ListItemView](/docs/unstable-uselist--docs#listitemview); +Redefine the rendering of a list item. For example, add dividers between list items or wrap an item in a link component. As a view component to display a list item, use [ListItemView](/docs/unstable-uselist--docs#listitemview); ```tsx Important! Absolutely all the props for [ListItemView](/docs/unstable-uselist--docs#listitemview) can be redefined in the renderItem method. This is the preferred method for changing the view of the sheet elements. +> Important! Absolutely all the props for [ListItemView](/docs/unstable-uselist--docs#listitemview) can be redefined in the renderItem method. This is the preferred method for changing the view of the list elements. ### renderContainer -Ability to override default list container `ListContainerView`; +Render custom list container. ```tsx = (props: OnItemClickProps) => void; + +interface OnItemClickProps { + /** + * `id` of the current element; + */ + id: string; + /** + * access to the original payload (`T`) list item; + */ + data: T; + /** + * the ordinal index of the element, taking into account that with a tree-like data structure, the list elements have a flatten representation; + */ + index: number; + /** + * whether the item is selected or not; + */ + selected: boolean; + /** + * is the element disabled; + */ + disabled: boolean; + /** + * are nested child elements hidden; + */ + expanded: boolean; + /** + * useful information about the current list item: + */ + context: { + // meta info about item + itemState: { + // integer number, representing nested list level + indentation: number; + // `id` of parent list item if it exists + parentId?: string; + }; + // An optional parameter. If the list item is also the first item of the nested list + groupState: { + // array of `id` of nested list items; + childrenIds: string[]; + }; + // is the current item the last one in the list + isLastItem: boolean; + }; +} +``` diff --git a/src/components/TreeList/__stories__/stories/WithGroupSelectionAndCustomIconStory.tsx b/src/components/TreeList/__stories__/stories/WithGroupSelectionAndCustomIconStory.tsx index 0c5d2c274c..80769a6a18 100644 --- a/src/components/TreeList/__stories__/stories/WithGroupSelectionAndCustomIconStory.tsx +++ b/src/components/TreeList/__stories__/stories/WithGroupSelectionAndCustomIconStory.tsx @@ -6,7 +6,7 @@ import {Button} from '../../../Button'; import {Icon} from '../../../Icon'; import {Flex, spacing} from '../../../layout'; import {ListItemView, useListState} from '../../../useList'; -import type {KnownItemStructure} from '../../../useList'; +import type {ListItemCommonProps} from '../../../useList'; import {createRandomizedData} from '../../../useList/__stories__/utils/makeData'; import {TreeList} from '../../TreeList'; import type {TreeListOnItemClick, TreeListProps} from '../../types'; @@ -29,7 +29,7 @@ export interface WithGroupSelectionAndCustomIconStoryProps itemsCount?: number; } -const mapCustomDataStructureToKnownProps = (props: CustomDataStructure): KnownItemStructure => ({ +const mapCustomDataStructureToKnownProps = (props: CustomDataStructure): ListItemCommonProps => ({ title: props.a, }); diff --git a/src/components/TreeList/types.ts b/src/components/TreeList/types.ts index a8e80af135..ebddd93be3 100644 --- a/src/components/TreeList/types.ts +++ b/src/components/TreeList/types.ts @@ -2,7 +2,7 @@ import type React from 'react'; import type {QAProps} from '../types'; import type { - KnownItemStructure, + ListItemCommonProps, ListItemId, ListItemSize, ListItemType, @@ -67,7 +67,7 @@ export type TreeListRenderContainer = ( props: TreeListRenderContainerProps, ) => React.JSX.Element; -export type TreeListMapItemDataToProps = (item: T) => KnownItemStructure; +export type TreeListMapItemDataToProps = (item: T) => ListItemCommonProps; export interface TreeListProps extends QAProps, Partial { /** diff --git a/src/components/TreeSelect/__stories__/components/WithGroupSelectionControlledStateAndCustomIcon.tsx b/src/components/TreeSelect/__stories__/components/WithGroupSelectionControlledStateAndCustomIcon.tsx index d8e3805fea..651cc0f0b6 100644 --- a/src/components/TreeSelect/__stories__/components/WithGroupSelectionControlledStateAndCustomIcon.tsx +++ b/src/components/TreeSelect/__stories__/components/WithGroupSelectionControlledStateAndCustomIcon.tsx @@ -6,7 +6,7 @@ import {Button} from '../../../Button'; import {Icon} from '../../../Icon'; import {Flex, spacing} from '../../../layout'; import {ListItemView, getListParsedState} from '../../../useList'; -import type {KnownItemStructure, ListItemId} from '../../../useList'; +import type {ListItemCommonProps, ListItemId} from '../../../useList'; import {createRandomizedData} from '../../../useList/__stories__/utils/makeData'; import {TreeSelect} from '../../TreeSelect'; import type {TreeSelectProps} from '../../types'; @@ -26,7 +26,7 @@ export interface WithGroupSelectionControlledStateAndCustomIconExampleProps itemsCount?: number; } -const mapCustomDataStructureToKnownProps = (props: CustomDataStructure): KnownItemStructure => ({ +const mapCustomDataStructureToKnownProps = (props: CustomDataStructure): ListItemCommonProps => ({ title: props.a, }); diff --git a/src/components/useList/__stories__/useList.mdx b/src/components/useList/__stories__/useList.mdx index 6a8a9ced4e..b817ee07da 100644 --- a/src/components/useList/__stories__/useList.mdx +++ b/src/components/useList/__stories__/useList.mdx @@ -148,7 +148,7 @@ The main hook for creating a stateless version of the sheet. - `items` - `ListItemType[]` - a flat or tree-like data structure, with `List` declaration: - ##### items supported data structure: + ##### Item structure variants ```ts const simple: ListItemType[] = ['one', 'two', 'free', 'four', 'five']; @@ -182,8 +182,6 @@ The main hook for creating a stateless version of the sheet. ]; ``` - > It is **not recommended** to control the state of a list through an array of its elements, recreating it every time the state changes. Use `useListState` hook to control list state - ##### Object decl reserved propeties: ```tsx @@ -378,17 +376,12 @@ Use it even if the functionality of the `useList` hook seems redundant to you - `id` - required prop. Set `[data-list-item="${id}"]` data attribute. By this it core list engine finds elements to scroll to. - `title` - base required prop to use. If passed string, applas default component styles according desig system. Pass you own componnet if you wont custom behaviour; - `as` - if needed, override `html` tag. By default - `li`; -- `size` - the size of the element. This also affects the rounding radius of the list element . By default, `m`. - ##### Size available options: - - `s`, - - `m`, - - `l`, - - `xl`. +- `size` - the size of the element. This also affects the rounding radius of the list element . By default, `m`. Available sizes are `s`, `m`, `l` and `xl`; - `height` - the height of the element in pixels. By default, it is calculated depending on the `size` parameter and the presence of the `subtitle` parameter; - `selected` - the selected state of the component; affects the `activeOnHover` if value if the value is different from `undefined`; - `active` - the state when the element is in the user's focus, but not selected. It can also be used when you drag an element; - `disabled` - the disabled state. It also prevents clicking on an element; -- `activeOnHover`- directly control on hover behaviour; +- `activeOnHover`- directly control hover behaviour; - `indentation` - affects the visual indentation of the element content; - `hasSelectionIcon` - show selected icon if selected and reserve space for this icon; - `onClick` - on item click callback. !Note: if passed this and `disabled` option is `true` click will not be appear; @@ -520,16 +513,57 @@ React.useLayoutEffect(() => { Map list state to ListItemView render props; +```tsx +const list = useList(); +const listState = useListState(); + +const {data, props, context} = getItemRenderState({ + qa: 'some-qa-id', + id, + multiple: false, + size, // list size + onItemClick: (id: ListItemId) => {}, + mapItemDataToProps: (item) => ({title: item.title}), + ...list, + ...listState, +}); + +return ; +``` + #### Props: -- `mapItemDataToProps` - map item data to view render props with `KnownItemStructure` interface; -- `size` - list item size; -- `multiple` - Affects the view of the selected items; -- `defaultExpanded` - Group expanded initial state. Default value `true` -- `id` - of list item; -- `onItemClick` - item click handler; -- `[...useList]` - result of hook `useList`; -- `[...useListState]` - retult of hook `useListState`; +```tsx +type ListItemSize = 's' | 'm' | 'l' | 'xl'; +type ListItemid = string; + +type GetItemRenderStateProps = { + /** + * `id` of list item; + */ + id: ListItemid; + /** + * map item data to view render props with `ListItemCommonProps` interface + */ + mapItemDataToProps(data: T): { + title: React.ReactNode; + endIcon?: React.ReactNode; + startIcon: React.ReactNode; + subtitle?: React.ReactNode; + }; + size: ListItemSize; + /** + * Affects the view of the selected items; + */ + multiple?: boolean; + /** + * Group expanded initial state. Default value `true` + */ + defaultExpanded?: boolean; + onItemClick?(id: ListItemid): void; +} & ReturnType & + ReturnType; +``` #### Returns: @@ -546,19 +580,59 @@ item = T ##### item state props: - - `id` - item id; - - `qa` - qa attribute for tests; - - `size` - item size; - - `expanded` - expanded state if item group; - - `active` - is item active; - - `indentation` - item nest level; - - `disabled` - is item disabled; - - `selected` - is item selected; - - `onClick` - on item click handle if exists; - - `hasSelectionIcon` - affects the view of the selected items; +```tsx +type ListItemSize = 's' | 'm' | 'l' | 'xl'; + +interface ItemRenderProps { + // item id; + id: string; + // qa attribute for tests + qa?: string; + // item size; + size: ListItemSize; + // expanded state if item group; + expanded?: boolean; + // is item active + active: boolean; + // item nest level; + indentation: number; + // is item disabled; + disabled: boolean; + // is item selected; + selected?: boolean; + // on item click handle if exists; + onClick?(): void; + // affects the view of the selected items; + hasSelectionIcon?: boolean; + // one required field of result `mapItemDataToProps` function work; + title: React.ReactNode; +} +``` ##### item list context: +```tsx +/** + * useful information about the current list item: + */ +interface ItemContext { + // meta info about item + itemState: { + // integer number, representing nested list level + indentation: number; + // `id` of parent list item if it exists + parentId?: string; + }; + // An optional parameter. If the list item is also the first item of the nested list + groupState: { + // array of `id` of nested list items; + childrenIds: string[]; + }; + // is the current item the last one in the list + isLastItem: boolean; +} +``` + - `itemState`: - `parentId?` - id of parant element; - `indentation` - item nest level; @@ -605,9 +679,9 @@ const [expandedById, setExpanded] = React.useState( ### getListItemQa -Функция используется для генерации `qa` атрибутов в элементах списка; -Также используйте эту функцию в тестах для составления уникального data атрибута для доступа к конкретному элементу списка +Function is used to generate `qa` attributes in list items; +Also use this function in tests to create a unique data attribute for accessing a specific list item. ```ts -await locator.getByTestId(getListItemQa('some-list-qa', '0')); // выбрать первый элемент списка, если не используются автосгенеренные id +await locator.getByTestId(getListItemQa('some-list-qa', '0')); // select the first item in the list if auto-generated `id` are used ``` diff --git a/src/components/useList/components/ListItemView/ListItemView.tsx b/src/components/useList/components/ListItemView/ListItemView.tsx index a960b34846..029a9c0ca8 100644 --- a/src/components/useList/components/ListItemView/ListItemView.tsx +++ b/src/components/useList/components/ListItemView/ListItemView.tsx @@ -10,13 +10,13 @@ import type {FlexProps} from '../../../layout'; import type {QAProps} from '../../../types'; import {block} from '../../../utils/cn'; import {LIST_ITEM_DATA_ATR, modToHeight} from '../../constants'; -import type {ListItemId, ListItemSize} from '../../types'; +import type {ListItemCommonProps, ListItemId, ListItemSize} from '../../types'; import './ListItemView.scss'; const b = block('list-item-view'); -export interface ListItemViewProps extends QAProps { +export interface ListItemViewProps extends QAProps, ListItemCommonProps { /** * Ability to override default html tag */ @@ -46,10 +46,6 @@ export interface ListItemViewProps extends QAProps { */ onClick?(): void; style?: React.CSSProperties; - title: React.ReactNode; - subtitle?: React.ReactNode; - startSlot?: React.ReactNode; - endSlot?: React.ReactNode; className?: string; role?: React.AriaRole; expanded?: boolean; diff --git a/src/components/useList/types.ts b/src/components/useList/types.ts index 53e8fc171e..75be6b1531 100644 --- a/src/components/useList/types.ts +++ b/src/components/useList/types.ts @@ -41,7 +41,7 @@ export type ItemState = { indentation: number; }; -export type KnownItemStructure = { +export type ListItemCommonProps = { title: React.ReactNode; subtitle?: React.ReactNode; startSlot?: React.ReactNode; @@ -67,7 +67,7 @@ export type RenderItemProps = { active: boolean; indentation: number; hasSelectionIcon?: boolean; -} & KnownItemStructure & +} & ListItemCommonProps & QAProps; export type ParsedState = { diff --git a/src/components/useList/utils/getItemRenderState.tsx b/src/components/useList/utils/getItemRenderState.tsx index d07c4ca63a..3d1df14850 100644 --- a/src/components/useList/utils/getItemRenderState.tsx +++ b/src/components/useList/utils/getItemRenderState.tsx @@ -1,7 +1,7 @@ /* eslint-disable valid-jsdoc */ import type {QAProps} from '../../types'; import type { - KnownItemStructure, + ListItemCommonProps, ListItemId, ListItemSize, ListParsedState, @@ -27,7 +27,7 @@ type ItemRendererProps = Partial & */ defaultExpanded?: boolean; id: ListItemId; - mapItemDataToProps(data: T): KnownItemStructure; + mapItemDataToProps(data: T): ListItemCommonProps; onItemClick?(id: ListItemId): void; };