Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(a11y): enhance keyboard interaction in search mode #592

Merged
merged 25 commits into from
Nov 11, 2024
Merged
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e2112e9
feat(a11y): enhance keyboard interaction in search mode
aojunhao123 Nov 3, 2024
0411b7a
refactor(a11y): synchronize activeKey with search state
aojunhao123 Nov 4, 2024
481c983
fix: lint fix
aojunhao123 Nov 5, 2024
a9efbc7
feat: default active first item
aojunhao123 Nov 5, 2024
9b51e4a
chore: remove useless test case
aojunhao123 Nov 5, 2024
f4f76ab
feat: default active first item
aojunhao123 Nov 5, 2024
b99d2fb
refactor: merge active effect logic
aojunhao123 Nov 5, 2024
644bf9d
chore: adjust code style
aojunhao123 Nov 5, 2024
89900b8
fix: type fix
aojunhao123 Nov 5, 2024
2659bd1
feat: adjust active effect logic
aojunhao123 Nov 5, 2024
bf62a42
fix: lint fix
aojunhao123 Nov 5, 2024
e3ef3e5
chore: remove useless code
aojunhao123 Nov 5, 2024
516d0b8
feat: flatten tree to match first node
aojunhao123 Nov 5, 2024
750de00
chore: add .vscode to gitignore file
aojunhao123 Nov 5, 2024
945f128
feat: improve flatten treeData
aojunhao123 Nov 5, 2024
467b056
feat: improve flatten treeData
aojunhao123 Nov 5, 2024
37d7cfc
chore: adjust code style
aojunhao123 Nov 5, 2024
86cf480
perf: optimize tree node searching with flattened data
aojunhao123 Nov 5, 2024
134f4f4
chore: add comment
aojunhao123 Nov 6, 2024
5d29cc0
revert: restore recursive node search
aojunhao123 Nov 6, 2024
4640c4d
chore: remove unnecessary logic
aojunhao123 Nov 6, 2024
229f4f7
chore: remove unnecessary logic
aojunhao123 Nov 6, 2024
9c7ad27
chore: adjust logic
aojunhao123 Nov 6, 2024
6f5a84c
test: use testing-library
aojunhao123 Nov 7, 2024
97bde6c
fix: keep active matched item when search
aojunhao123 Nov 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
chore: remove unnecessary logic
  • Loading branch information
aojunhao123 committed Nov 6, 2024
commit 4640c4d76e9031938b1ea6b46a87740e795cf10c
8 changes: 8 additions & 0 deletions examples/basic.tsx
Original file line number Diff line number Diff line change
@@ -216,6 +216,7 @@ class Demo extends React.Component {
console.log('onPopupScroll:', evt.target);
}}
/>

<h2>single select (just select children)</h2>
<TreeSelect
style={{ width: 300 }}
@@ -232,6 +233,7 @@ class Demo extends React.Component {
filterTreeNode={false}
onChange={this.onChangeChildren}
/>

<h2>multiple select</h2>
<TreeSelect
style={{ width: 300 }}
@@ -247,6 +249,7 @@ class Demo extends React.Component {
onSelect={this.onSelect}
allowClear
/>

<h2>check select</h2>
<TreeSelect
open
@@ -278,6 +281,7 @@ class Demo extends React.Component {
return `${valueList.length} rest...`;
}}
/>

<h2>labelInValue & show path</h2>
<TreeSelect
style={{ width: 500 }}
@@ -295,6 +299,7 @@ class Demo extends React.Component {
filterTreeNode={false}
onChange={this.onChangeLV}
/>

<h2>use treeDataSimpleMode</h2>
<TreeSelect
style={{ width: 300 }}
@@ -318,6 +323,7 @@ class Demo extends React.Component {
this.onSelect(...args);
}}
/>

<h2>Testing in extreme conditions (Boundary conditions test) </h2>
<TreeSelect
style={{ width: 200 }}
@@ -341,6 +347,7 @@ class Demo extends React.Component {
]}
onChange={(val, ...args) => console.log(val, ...args)}
/>

<h2>use TreeNode Component (not recommend)</h2>
<TreeSelect
style={{ width: 200 }}
@@ -377,6 +384,7 @@ class Demo extends React.Component {
</TreeNode>
<TreeNode value="same value3" title="same title" key="0-3" />
</TreeSelect>

<h2>title render</h2>
<TreeSelect<{ label: string }>
open
55 changes: 20 additions & 35 deletions src/OptionList.tsx
Original file line number Diff line number Diff line change
@@ -10,7 +10,6 @@ import LegacyContext from './LegacyContext';
import TreeSelectContext from './TreeSelectContext';
import type { Key, SafeKey } from './interface';
import { getAllKeys, isCheckDisabled } from './utils/valueUtil';
import { flattenTreeData } from 'rc-tree/lib/utils/treeUtil';

const HIDDEN_STYLE = {
width: 0,
@@ -77,10 +76,6 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
(prev, next) => next[0] && prev[1] !== next[1],
);

// ========================== Active Key ==========================
const [activeKey, setActiveKey] = React.useState<Key>(null);
const activeEntity = keyEntities[activeKey as SafeKey];

// ========================== Values ==========================
const mergedCheckedKeys = React.useMemo(() => {
if (!checkable) {
@@ -93,7 +88,7 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
};
}, [checkable, checkedKeys, halfCheckedKeys]);

// ========================== Scroll Effect ==========================
// ========================== Scroll ==========================
React.useEffect(() => {
// Single mode should scroll to current key
if (open && !multiple && checkedKeys.length) {
@@ -123,15 +118,6 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
}
};

// ========================== Search ==========================
const lowerSearchValue = String(searchValue).toLowerCase();
const filterTreeNode = (treeNode: EventDataNode<any>) => {
if (!lowerSearchValue) {
return false;
}
return String(treeNode[treeNodeFilterProp]).toLowerCase().includes(lowerSearchValue);
};

// =========================== Keys ===========================
const [expandedKeys, setExpandedKeys] = React.useState<Key[]>(treeDefaultExpandedKeys);
const [searchExpandedKeys, setSearchExpandedKeys] = React.useState<Key[]>(null);
@@ -152,7 +138,15 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
}
};

// ========================== Search Effect ==========================
// ========================== Search ==========================
const lowerSearchValue = String(searchValue).toLowerCase();
const filterTreeNode = (treeNode: EventDataNode<any>) => {
if (!lowerSearchValue) {
return false;
}
return String(treeNode[treeNodeFilterProp]).toLowerCase().includes(lowerSearchValue);
};

React.useEffect(() => {
if (searchValue) {
setSearchExpandedKeys(getAllKeys(treeData, fieldNames));
@@ -161,25 +155,14 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
}, [searchValue]);

// ========================== Get First Selectable Node ==========================
const getFirstMatchingNode = (
nodes: EventDataNode<any>[],
searchVal?: string,
): EventDataNode<any> | null => {
const getFirstMatchingNode = (nodes: EventDataNode<any>[]): EventDataNode<any> | null => {
for (const node of nodes) {
if (node.disabled || node.selectable === false) {
continue;
}

if (searchVal) {
if (filterTreeNode(node)) {
return node;
}
} else {
if (!node.disabled && node.selectable !== false) {
return node;
}

if (node[fieldNames.children]) {
const matchInChildren = getFirstMatchingNode(node[fieldNames.children], searchVal);
const matchInChildren = getFirstMatchingNode(node[fieldNames.children]);
zombieJ marked this conversation as resolved.
Show resolved Hide resolved
if (matchInChildren) {
return matchInChildren;
}
@@ -188,16 +171,18 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
return null;
};

// ========================== Active Key Effect ==========================
// ========================== Active ==========================
const [activeKey, setActiveKey] = React.useState<Key>(null);
const activeEntity = keyEntities[activeKey as SafeKey];

React.useEffect(() => {
if (!open) {
setActiveKey(null);
return;
}

// Prioritize activating the searched node
// // Prioritize activating the searched node
if (searchValue) {
const firstNode = getFirstMatchingNode(treeData, searchValue);
const firstNode = getFirstMatchingNode(memoTreeData);
setActiveKey(firstNode ? firstNode[fieldNames.value] : null);
return;
}
@@ -209,7 +194,7 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
}

// If no search value and no checked nodes, activate the first node
const firstNode = getFirstMatchingNode(treeData);
const firstNode = getFirstMatchingNode(memoTreeData);
setActiveKey(firstNode ? firstNode[fieldNames.value] : null);
}, [open, searchValue]);