Skip to content

Commit

Permalink
feat(useList): added ability to define initial value to useListState (#…
Browse files Browse the repository at this point in the history
…1483)

Co-authored-by: Alexandr Isaev <[email protected]>
  • Loading branch information
IsaevAlexandr and IsaevAlexandr authored Apr 18, 2024
1 parent 634429a commit 7f66d28
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 34 deletions.
10 changes: 6 additions & 4 deletions src/components/TreeSelect/TreeSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,12 @@ export const TreeSelect = React.forwardRef(function TreeSelect<T>(
});

const listState = useListState({
expandedById,
disabledById,
activeItemId,
selectedById: selected,
controlledValues: {
expandedById,
disabledById,
activeItemId,
selectedById: selected,
},
});

const setActiveItemId = propsSetActiveItemId ?? listState.setActiveItemId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -40,9 +40,12 @@ export const WithGroupSelectionControlledStateAndCustomIconExample = ({
);

const [value, setValue] = React.useState<string[]>([]);
const [expandedById, setExpanded] = React.useState<Record<ListItemId, boolean>>(
() => getListParsedState(items).initialState.expandedById,
);

const {expandedById, setExpanded} = useListState({
initialValues: {
expandedById: getListParsedState(items).initialState.expandedById,
},
});

return (
<Flex>
Expand Down
64 changes: 45 additions & 19 deletions src/components/useList/__stories__/useList.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,32 @@ const {
} = useListState();
```

#### props:

```tsx
interface UseListStateProps {
/**
* Initial state values
*/
initialValues?: Partial<ListState>;
/**
* Ability to pass link to another state value
*/
controlledValues?: Partial<ListState>;
}
```

##### controlledValues example:

```tsx
const listState = useListState();

// inside your component
const innerListState = useListState({
controlledValues: listState,
});
```

## Components:

### ListItemView
Expand All @@ -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<Entity>[] = [
{title: 'some title 1', subtitle: 'some subtitle 1', icon: <Icon data={Grip} size={16} />},
{title: 'some title 2', subtitle: 'some subtitle 2', icon: <Icon data={Grip} size={16} />},
{title: 'some title 1', subtitle: 'some subtitle 1', icon: <Icon data={Grip} size={16} />},
{title: 'some title 2', subtitle: 'some subtitle 2', icon: <Icon data={Grip} size={16} />},
];

const List = () => {
return (
<>
{items.map(item, i) => {
return (
<ListItemView
key={i}
id={String(i)}
title={item.title}
subtitle={item.subtitle}
endSlot={item.icon}
/>
)
}}
</>
)
return (
<>
{items.map(item, i) => {
return (
<ListItemView
key={i}
id={String(i)}
title={item.title}
subtitle={item.subtitle}
endSlot={item.icon}
/>
)
}}
</>
)
};
```

Expand Down
42 changes: 36 additions & 6 deletions src/components/useList/hooks/useListState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,49 @@ import React from 'react';

import type {ListState} from '../types';

interface UseListStateProps extends Partial<ListState> {}
interface UseListStateProps {
/**
* Initial state values
*/
initialValues?: Partial<ListState>;
/**
* Ability to pass link to another state value
*
* ```tsx
* const listState = useListState()
*
* // inside your component
* const innerListState = useListState({
* controlledValues: listState
* })
* ```
*/
controlledValues?: Partial<ListState>;
}

function useControlledState<T>(value: T, defaultValue: T) {
const [state, setState] = React.useState(value || defaultValue);

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,
Expand Down

0 comments on commit 7f66d28

Please sign in to comment.