Skip to content

Commit

Permalink
Refactor all Tree related components. (#3769)
Browse files Browse the repository at this point in the history
* refactor: refactor CheckTree and CheckTreePicker components

* refactor: refactor Tree and TreePicker components

* refactor: refactor Tree and TreePicker components

* fix: fix accessibility

* feat: add support for `searchable` on `<Tree>` and `<CheckTree>`

* fix: fix bad tests

* chore: remove deprecated utility functions

* fix: remove inline parameter in usePickerRef

* fix: remove useFlattenTreeData

* test: add tests

* fix(CheckTree): fix checked error in deep nested data

* feat: use `treeHeight` instead of height

* fix: fix disabled option style issue

* fix: fix Tree not updated when loading data asynchronously

* fix: fix indent line not show in dark mode

* fix: fix cannot keep the expanded state

* test: add tests

* fix: use useFocusState in CheckTreePicker

* fix(CheckTreePicker): fix the flicker of the tree node checked state

* fix(CheckTreePicker): fix the flicker of the tree node checked state

* fix(Tree): fix the problem that tree nodes cannot be dragged

* fix(Tree,CheckTree): fix tree node cannot be interacted with by keyboard when virtualized

* fix: remove redundant code

* refactor: use CSS to add indent lines to tree nodes

* docs: add examples for uncheckable and disabled

* fix(TreePicker): fix accessibility issues

* fix: fix failed tests

* fix: `renderMenu` is deprecated and replaced by `reanderTree`

* docs: fix errors in docs
  • Loading branch information
simonguo committed May 15, 2024
1 parent a384bd0 commit 2650886
Show file tree
Hide file tree
Showing 146 changed files with 7,217 additions and 5,114 deletions.
14 changes: 14 additions & 0 deletions docs/pages/_common/types/tree-node.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
### `ts:TreeNode`

```ts
interface TreeNode {
/** The value of the option corresponds to the `valueKey` in the data. **/
value: string | number;

/** The content displayed by the option corresponds to the `labelKey` in the data. **/
label: React.ReactNode;

/** The data of the child option corresponds to the `childrenKey` in the data. */
children?: TreeNode[];
}
```
2 changes: 1 addition & 1 deletion docs/pages/components/check-picker/zh-CN/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
| menuMaxHeight | number `(320)` | 设置 Dropdown 的最大高度 |
| menuStyle | CSSProperties | 应用于菜单 DOM 节点的 style |
| onChange | (value: [ValueType][value] , event) => void | `value` 发生改变时的回调函数 |
| onClean | (event:SyntheticEvent) => void | 值清理时触发回调 |
| onClean | (event:SyntheticEvent) => void | 清空值时触发回调 |
| onClose | () => void | 关闭回调函数 |
| onEnter | () => void | 显示前动画过渡的回调函数 |
| onEntered | () => void | 显示后动画过渡的回调函数 |
Expand Down
137 changes: 77 additions & 60 deletions docs/pages/components/check-tree-picker/en-US/index.md

Large diffs are not rendered by default.

141 changes: 78 additions & 63 deletions docs/pages/components/check-tree-picker/zh-CN/index.md

Large diffs are not rendered by default.

102 changes: 73 additions & 29 deletions docs/pages/components/check-tree/en-US/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,44 +22,87 @@ The cascade attribute can set whether or not CheckTree can consider the cascade

<!--{include:`show-indent-line.md`}-->

### Custom options
### Custom Tree Node

<!--{include:`custom.md`}-->

### Async
### Virtualized

<!--{include:`virtualized.md`}-->

### Asynchronous Loading of Child Nodes

<!--{include:`async.md`}-->

### Searchable

<!--{include:`searchable.md`}-->

### Uncheckable Tree Node

<!--{include:`uncheckable.md`}-->

### Disabled Tree Node

<!--{include:`disabled.md`}-->

## Accessibility

### ARIA properties

**tree**

- CheckTree has role `tree`.
- CheckTree has the `aria-multiselectable=true` attribute to indicate that the tree is multi-selectable.

**treeitem**

- CheckTree node has role `treeitem`.
- Has the `aria-expanded` attribute to indicate whether the tree is open or not.
- Has the `aria-checked` attribute to indicate whether the tree node is checked or not.
- Has the `aria-level` attribute to indicate the level of the tree node.
- Has the `aria-disabled` attribute to indicate whether the tree node is disabled or not.

### Keyboard interactions

- <kbd>↓</kbd> - Move focus to the next tree node.
- <kbd>↑</kbd> - Move focus to the previous tree node.
- <kbd>→</kbd> - Expand the focused tree node if it is collapsed.
- <kbd>←</kbd> - Collapse the focused tree node if it is expanded.
- <kbd>Enter</kbd> - Select the focused tree node.

## Props

### `<CheckTree>`

| Property | Type `(Default)` | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- |
| cascade | boolean `(true)` | Whether cascade select |
| childKey | string `('children')` | Set childrenKey key in data |
| data \* | [ItemDataType][item][] | Tree data |
| defaultExpandAll | boolean | Expand all tree node |
| defaultExpandItemValues | any [] | Set the value of the default expanded node |
| defaultValue | string[] | Default values of the selected tree node |
| disabledItemValues | string[] | Values of disabled tree node |
| expandItemValues | any [] | Set the value of the expanded node (controlled) |
| getChildren | (item: [ItemDataType][item]) => Promise&lt;[ItemDataType][item]&gt; | load node children data asynchronously |
| height | number `(360px)` | height of menu. When `virtualize` is true, you can set the height of menu |
| labelKey | string `('label')` | Set label key in data |
| listProps | [ListProps][listprops] | Properties of virtualized lists. |
| onChange | (values:string[]) => void | Callback fired when value change |
| onExpand | (expandItemValues: any [], item: [ItemDataType][item], concat:(data, children) => Array) => void | callback fired when tree node expand state changed |
| onSelect | (item: [ItemDataType][item], value:any, event) => void | Callback fired when tree node is selected |
| renderTreeIcon | (item: [ItemDataType][item]) => ReactNode | Custom render the icon in tree node |
| renderTreeNode | (item: [ItemDataType][item]) => ReactNode | Custom render tree node |
| searchKeyword | string | searchKeyword (Controlled) |
| uncheckableItemValues | string[] | Set the option value for the check box not to be rendered |
| value | string[] | Specifies the values of the selected tree node (Controlled) |
| valueKey | string `('value')` | Set value key in data |
| virtualized | boolean | Whether using Virtualized List |

<!--{include:(_common/types/item-data-type.md)}-->
| Property | Type `(Default)` | Description |
| ----------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| cascade | boolean `(true)` | Whether to enable cascade selection |
| childrenKey | string `('children')` | Set the `key` of the child node of the tree node in `data` |
| data \* | [TreeNode][node][] | Data to render the tree |
| defaultExpandAll | boolean | Default expand all nodes |
| defaultExpandItemValues | string[] | Set the value of the default expanded node |
| defaultValue | string[] | Default selected Value |
| disabledItemValues | string[] | Disabled tree node values |
| expandItemValues | string[] | Set the value of the expanded node (controlled) |
| getChildren | (item: [TreeNode][node]) => Promise&lt;[TreeNode][node]&gt; | Load node children data asynchronously |
| height | number `(360px)` | The height of the tree |
| labelKey | string `('label')` | Set the tree node display content to the `key` in `data` |
| listProps | [ListProps][listprops] | Properties of virtualized lists |
| onChange | (values:string[]) => void | Called when the tree value changes |
| onExpand | (expandItemValues: string[], item: [TreeNode][node], concat:(data, children) => Array) => void | Called when the tree node expands the child node |
| onSearch | (keyword: string) => void | Called when the search box changes |
| onSelect | (item: [TreeNode][node], value:string, event) => void | Called when the tree node is selected |
| renderTreeIcon | (item: [TreeNode][node], expanded: boolean) => ReactNode | Custom render the icon in tree node |
| renderTreeNode | (item: [TreeNode][node]) => ReactNode | Custom render tree node |
| searchable | boolean | Whether to show the search box <br/>![][5.61.0] |
| searchKeyword | string | Set search keywords for the search box |
| uncheckableItemValues | string[] | Set the tree node values that do not display checkboxes |
| value | string[] | The value of the selected tree node |
| valueKey | string `('value')` | Set the `key` of the tree node value in `data` |
| virtualized | boolean | Whether to enable virtualized lists |

<!--{include:(_common/types/tree-node.md)}-->
<!--{include:(_common/types/list-props.md)}-->

## Related components
Expand All @@ -69,4 +112,5 @@ The cascade attribute can set whether or not CheckTree can consider the cascade
- [`<CheckTreePicker>`](/components/check-tree-picker)

[listprops]: #code-ts-list-props-code
[item]: #code-ts-item-data-type-code
[node]: #code-ts-tree-node-code
[5.61.0]: https://img.shields.io/badge/min-v5.61.0-blue
12 changes: 10 additions & 2 deletions docs/pages/components/check-tree/fragments/async.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ import { mockAsyncData } from './mock';
const [getNodes, fetchNodes] = mockAsyncData();
const data = getNodes(5);

const TreeNode = ({ children, ...rest }) => {
return (
<div {...rest} style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
{children}
</div>
);
};

const App = () => {
const [value, setValue] = React.useState([]);

Expand All @@ -21,9 +29,9 @@ const App = () => {
getChildren={fetchNodes}
renderTreeNode={node => {
return (
<>
<TreeNode>
{node.children ? <FolderFillIcon /> : <PageIcon />} {node.label}
</>
</TreeNode>
);
}}
/>
Expand Down
29 changes: 25 additions & 4 deletions docs/pages/components/check-tree/fragments/custom.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
import { CheckTree } from 'rsuite';
import PageIcon from '@rsuite/icons/Page';
import { mockTreeData } from './mock';
import {
MdOutlineKeyboardArrowDown,
MdOutlineKeyboardArrowRight,
MdFilePresent,
MdFolder
} from 'react-icons/md';

const data = mockTreeData({
limits: [3, 3, 4],
Expand All @@ -13,17 +19,32 @@ const data = mockTreeData({
}
});

const TreeNode = ({ children, ...rest }) => {
return (
<div {...rest} style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
{children}
</div>
);
};

const App = () => (
<CheckTree
data={data}
defaultExpandAll
renderTreeNode={nodeData => {
renderTreeNode={treeNode => {
return (
<span>
<PageIcon /> {nodeData.label}
</span>
<TreeNode>
{treeNode.children ? <MdFolder /> : <MdFilePresent />}
{treeNode.label}
</TreeNode>
);
}}
renderTreeIcon={(treeNode, expanded) => {
if (treeNode.children) {
return expanded ? <MdOutlineKeyboardArrowDown /> : <MdOutlineKeyboardArrowRight />;
}
return null;
}}
/>
);

Expand Down
54 changes: 54 additions & 0 deletions docs/pages/components/check-tree/fragments/disabled.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<!--start-code-->

```js
import { CheckTree } from 'rsuite';

const data = [
{
label: 'Node 1',
value: '1',
children: [
{
label: 'Node 1-1',
value: '1-1'
},
{
label: 'Node 1-2',
value: '1-2'
}
]
},
{
label: 'Node 2',
value: '2',
children: [
{
label: 'Node 2-1',
value: '2-1',
children: [
{
label: 'Node 2-1-1',
value: '2-1-1'
},
{
label: 'Node 2-1-2',
value: '2-1-2'
}
]
},
{
label: 'Node 2-2',
value: '2-2'
}
]
}
];

const App = () => (
<CheckTree data={data} disabledItemValues={['1', '2', '2-1-1', '2-1-2']} defaultExpandAll />
);

ReactDOM.render(<App />, document.getElementById('root'));
```

<!--end-code-->
21 changes: 21 additions & 0 deletions docs/pages/components/check-tree/fragments/searchable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!--start-code-->

```js
import { CheckTree } from 'rsuite';
import { mockTreeData } from './mock';

const data = mockTreeData({
limits: [3, 3, 4],
labels: (layer, value, faker) => {
const methodName = ['jobArea', 'jobType', 'firstName'];
return faker.person[methodName[layer]]();
}
});

const App = () => {
return <CheckTree data={data} searchable />;
};
ReactDOM.render(<App />, document.getElementById('root'));
```

<!--end-code-->
54 changes: 54 additions & 0 deletions docs/pages/components/check-tree/fragments/uncheckable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<!--start-code-->

```js
import { CheckTree } from 'rsuite';

const data = [
{
label: 'Node 1',
value: '1',
children: [
{
label: 'Node 1-1',
value: '1-1'
},
{
label: 'Node 1-2',
value: '1-2'
}
]
},
{
label: 'Node 2',
value: '2',
children: [
{
label: 'Node 2-1',
value: '2-1',
children: [
{
label: 'Node 2-1-1',
value: '2-1-1'
},
{
label: 'Node 2-1-2',
value: '2-1-2'
}
]
},
{
label: 'Node 2-2',
value: '2-2'
}
]
}
];

const App = () => (
<CheckTree data={data} uncheckableItemValues={['1', '2', '2-1-1', '2-1-2']} defaultExpandAll />
);

ReactDOM.render(<App />, document.getElementById('root'));
```

<!--end-code-->
Loading

0 comments on commit 2650886

Please sign in to comment.