Skip to content

Commit

Permalink
fix(TreeSelect): fixed dnd examples with selected values (#1329)
Browse files Browse the repository at this point in the history
Co-authored-by: Alexandr Isaev <[email protected]>
  • Loading branch information
IsaevAlexandr and IsaevAlexandr authored Feb 12, 2024
1 parent 73fe484 commit 14d6ceb
Show file tree
Hide file tree
Showing 21 changed files with 173 additions and 96 deletions.
42 changes: 24 additions & 18 deletions src/components/TreeSelect/TreeSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import type {CnMods} from '../utils/cn';
import {TreeSelectItem} from './TreeSelectItem';
import {TreeListContainer} from './components/TreeListContainer/TreeListContainer';
import {useTreeSelectSelection, useValue} from './hooks/useTreeSelectSelection';
import type {RenderControlProps, TreeSelectProps} from './types';
import type {TreeSelectProps, TreeSelectRenderControlProps} from './types';

import './TreeSelect.scss';

Expand Down Expand Up @@ -135,15 +135,19 @@ export const TreeSelect = React.forwardRef(function TreeSelect<T>(
};

if (onItemClick) {
return onItemClick(defaultHandleClick, {
id,
isGroup: id in listParsedState.groupsState,
isLastItem:
listParsedState.visibleFlattenIds[
listParsedState.visibleFlattenIds.length - 1
] === id,
disabled: listState.disabledById[id],
});
return onItemClick(
listParsedState.itemsById[id],
{
id,
isGroup: id in listParsedState.groupsState,
isLastItem:
listParsedState.visibleFlattenIds[
listParsedState.visibleFlattenIds.length - 1
] === id,
disabled: listState.disabledById[id],
},
defaultHandleClick,
);
}

return defaultHandleClick();
Expand All @@ -152,6 +156,7 @@ export const TreeSelect = React.forwardRef(function TreeSelect<T>(
onItemClick,
listState,
listParsedState.groupsState,
listParsedState.itemsById,
listParsedState.visibleFlattenIds,
groupsBehavior,
multiple,
Expand Down Expand Up @@ -188,7 +193,7 @@ export const TreeSelect = React.forwardRef(function TreeSelect<T>(

const handleClose = React.useCallback(() => toggleOpen(false), [toggleOpen]);

const controlProps: RenderControlProps = {
const controlProps: TreeSelectRenderControlProps = {
open,
toggleOpen,
clearValue: handleClearValue,
Expand Down Expand Up @@ -263,7 +268,7 @@ export const TreeSelect = React.forwardRef(function TreeSelect<T>(
id={`list-${treeSelectId}`}
{...listParsedState}
{...listState}
renderItem={(id, renderContextProps) => {
renderItem={(id, index, renderContextProps) => {
const renderState = getItemRenderState({
id,
size,
Expand All @@ -277,12 +282,13 @@ export const TreeSelect = React.forwardRef(function TreeSelect<T>(
Boolean(multiple) && !renderState.context.groupState;

if (renderItem) {
return renderItem(
renderState.data,
renderState.props,
renderState.context,
renderContextProps,
);
return renderItem({
data: renderState.data,
props: renderState.props,
itemState: renderState.context,
index,
renderContext: renderContextProps,
});
}

const itemData = listParsedState.itemsById[id];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ export interface InfinityScrollExampleProps
itemsCount?: number;
}

export const InfinityScrollExample = ({itemsCount = 5, ...props}: InfinityScrollExampleProps) => {
export const InfinityScrollExample = ({
itemsCount = 5,
...storyProps
}: InfinityScrollExampleProps) => {
const [value, setValue] = React.useState<string[]>([]);
const {
data = [],
data: items = [],
onFetchMore,
canFetchMore,
isLoading,
Expand All @@ -30,14 +33,14 @@ export const InfinityScrollExample = ({itemsCount = 5, ...props}: InfinityScroll
return (
<Flex>
<TreeSelect<{title: string}>
{...props}
items={data}
{...storyProps}
items={items}
value={value}
renderItem={(item, state, {isLastItem, groupState}) => {
renderItem={({data, props, itemState: {isLastItem, groupState}}) => {
const node = (
<TreeSelectItem
{...state}
{...item}
{...props}
{...data}
endSlot={
groupState ? (
<Label>{groupState.childrenIds.length}</Label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';

import {ListContainerView, computeItemSize} from '../../../useList';
import {VirtualizedListContainer} from '../../../useList/__stories__/components/VirtualizedListContainer';
import type {RenderContainerProps} from '../../types';
import type {TreeSelectRenderContainerProps} from '../../types';

// custom container renderer example
export const RenderVirtualizedContainer = <T,>({
Expand All @@ -11,7 +11,7 @@ export const RenderVirtualizedContainer = <T,>({
visibleFlattenIds,
renderItem,
size,
}: RenderContainerProps<T>) => {
}: TreeSelectRenderContainerProps<T>) => {
return (
<ListContainerView fixedHeight id={id} ref={containerRef}>
<VirtualizedListContainer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,28 +34,42 @@ const DraggableListItem = ({
);
};

type CustomDataType = {someRandomKey: string; id: string};

export interface WithDndListExampleProps
extends Omit<TreeSelectProps<string>, 'value' | 'onUpdate' | 'items' | 'getItemContent'> {}
extends Omit<
TreeSelectProps<CustomDataType>,
'value' | 'onUpdate' | 'items' | 'getItemContent' | 'renderControlContent'
> {}

export const WithDndListExample = (props: WithDndListExampleProps) => {
const [items, setItems] = React.useState(() =>
createRandomizedData({num: 10, depth: 0, getData: (title) => title}),
);
const randomItems: CustomDataType[] = createRandomizedData({
num: 10,
depth: 0,
getData: (title) => title,
}).map(({data}, idx) => ({someRandomKey: data, id: String(idx)}));

export const WithDndListExample = (storyProps: WithDndListExampleProps) => {
const [items, setItems] = React.useState(randomItems);
const [value, setValue] = React.useState<string[]>([]);

const handleDrugEnd: OnDragEndResponder = ({destination, source}) => {
if (destination?.index && destination?.index !== source.index) {
if (typeof destination?.index === 'number' && destination.index !== source.index) {
setItems((items) => reorderArray(items, source.index, destination.index));
}
};

return (
<Flex>
<TreeSelect
{...props}
{...storyProps}
value={value}
items={items}
onItemClick={(_, {id, isGroup, disabled}) => {
// you can omit this prop here. If prop `id` passed, TreeSelect would take it by default
getId={({id}) => id}
renderControlContent={({someRandomKey}) => ({
title: someRandomKey,
})}
onItemClick={(_data, {id, isGroup, disabled}) => {
if (!isGroup && !disabled) {
setValue([id]);
}
Expand All @@ -70,10 +84,14 @@ export const WithDndListExample = (props: WithDndListExampleProps) => {
snapshot: DraggableStateSnapshot,
rubric: DraggableRubric,
) => {
return renderItem(visibleFlattenIds[rubric.source.index], {
provided,
active: snapshot.isDragging,
});
return renderItem(
visibleFlattenIds[rubric.source.index],
rubric.source.index,
{
provided,
active: snapshot.isDragging,
},
);
}}
>
{(droppableProvided: DroppableProvided) => (
Expand All @@ -82,7 +100,9 @@ export const WithDndListExample = (props: WithDndListExampleProps) => {
{...droppableProvided.droppableProps}
ref={droppableProvided.innerRef}
>
{visibleFlattenIds.map((id) => renderItem(id))}
{visibleFlattenIds.map((id, index) =>
renderItem(id, index),
)}
{droppableProvided.placeholder}
</div>
</ListContainerView>
Expand All @@ -91,28 +111,28 @@ export const WithDndListExample = (props: WithDndListExampleProps) => {
</DragDropContext>
);
}}
renderItem={(item, state, _listContext, renderContextProps) => {
renderItem={({data, props, index, renderContext: renderContextProps}) => {
const commonProps = {
...state,
title: item,
...props,
title: data.someRandomKey,
endSlot: <Icon data={Grip} size={16} />,
};

// here passed props from `renderContainer` method.
if (renderContextProps) {
return (
<DraggableListItem
key={`item-key-${state.id}`}
key={`item-key-${index}`}
{...commonProps}
{...renderContextProps}
/>
);
}
return (
<Draggable
draggableId={state.id}
index={Number(state.id)}
key={`item-key-${state.id}`}
draggableId={String(index)}
index={index}
key={`item-key-${index}`}
>
{(provided: DraggableProvided, snapshot: DraggableStateSnapshot) => (
<DraggableListItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {Flex, spacing} from '../../../layout';
import {useListFilter} from '../../../useList';
import {createRandomizedData} from '../../../useList/__stories__/utils/makeData';
import {TreeSelect} from '../../TreeSelect';
import type {RenderContainerProps, TreeSelectProps} from '../../types';
import type {TreeSelectProps, TreeSelectRenderContainerProps} from '../../types';

import {RenderVirtualizedContainer} from './RenderVirtualizedContainer';

Expand All @@ -21,11 +21,11 @@ export interface WithFiltrationAndControlsExampleProps

export const WithFiltrationAndControlsExample = ({
itemsCount = 5,
...props
...treeSelectProps
}: WithFiltrationAndControlsExampleProps) => {
const {items, renderContainer} = React.useMemo(() => {
const baseItems = createRandomizedData({num: itemsCount});
const containerRenderer = (props: RenderContainerProps<{title: string}>) => {
const containerRenderer = (props: TreeSelectRenderContainerProps<{title: string}>) => {
if (props.items.length === 0 && baseItems.length > 0) {
return (
<Flex centerContent className={spacing({p: 2})} height="300px">
Expand All @@ -47,13 +47,12 @@ export const WithFiltrationAndControlsExample = ({
return (
<Flex>
<TreeSelect
{...props}
{...treeSelectProps}
multiple
open={open}
onOpenChange={onOpenChange}
slotBeforeListBody={
<TextInput
autoFocus
hasClear
placeholder="Type for search..."
className={spacing({px: 2, py: 1})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,18 @@ export const WithGroupSelectionControlledStateAndCustomIconExample = ({
renderControlContent={mapCustomDataStructureToKnownProps}
expandedById={expandedById}
value={value}
renderItem={(
item,
{
renderItem={({
data,
props: {
expanded, // don't use default ListItemView expand icon
...state
},
{groupState},
) => {
itemState: {groupState},
}) => {
return (
<TreeSelectItem
{...state}
{...mapCustomDataStructureToKnownProps(item)}
{...mapCustomDataStructureToKnownProps(data)}
startSlot={
<Icon size={16} data={groupState ? Database : PlugConnection} />
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,22 @@ export const WithItemLinksAndActionsExample = (props: WithItemLinksAndActionsExa
setOpen((x) => !x);
}}
expandedById={expandedById}
renderItem={(
item,
{
renderItem={({
data,
props: {
expanded, // don't use build in expand icon ListItemView behavior
...state
},
{groupState},
) => {
itemState: {groupState},
}) => {
return (
// eslint-disable-next-line jsx-a11y/anchor-is-valid
<a
href="#"
style={{textDecoration: 'none', color: 'inherit', width: '100%'}}
>
<TreeSelectItem
{...item}
{...data}
{...state}
endSlot={
<DropdownMenu
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';

import type {RenderContainerProps} from 'src/components/TreeSelect/types';
import type {TreeSelectRenderContainerProps} from 'src/components/TreeSelect/types';

import {ListContainerView} from '../../../useList';
import {ListItemRecursiveRenderer} from '../../../useList/components/ListRecursiveRenderer/ListRecursiveRenderer';
Expand All @@ -12,12 +12,14 @@ export const TreeListContainer = <T,>({
expandedById,
renderItem,
className,
}: RenderContainerProps<T> & {className?: string}) => {
idToFlattenIndex,
}: TreeSelectRenderContainerProps<T> & {className?: string}) => {
return (
<ListContainerView ref={containerRef} className={className} id={id}>
{items.map((itemSchema, index) => (
<ListItemRecursiveRenderer
key={index}
idToFlattenIndex={idToFlattenIndex}
itemSchema={itemSchema}
index={index}
expandedById={expandedById}
Expand Down
2 changes: 1 addition & 1 deletion src/components/TreeSelect/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export {TreeSelect} from './TreeSelect';
export {TreeSelectItem, type TreeSelectItemProps} from './TreeSelectItem';
export type {TreeSelectProps, RenderItem} from './types';
export type {TreeSelectProps, TreeSelectRenderItem} from './types';
Loading

0 comments on commit 14d6ceb

Please sign in to comment.