Skip to content

Commit

Permalink
fix(tree): fix checked logic when lazy load (#1976)
Browse files Browse the repository at this point in the history
* fix(tree): fix checked logic when lazy load

* fix(tree): fix treeNode.isChecked when lazyLoad and valueMode="onlyLeaf"

* chore(tree): add unit test for lazy and valueMode
  • Loading branch information
ylunwang authored Nov 8, 2024
1 parent bc9f1b4 commit 18d1638
Show file tree
Hide file tree
Showing 3 changed files with 293 additions and 31 deletions.
21 changes: 15 additions & 6 deletions js/tree-v1/tree-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -827,20 +827,29 @@ export class TreeNode {
*/
public isChecked(map?: TypeIdMap): boolean {
const { children, tree, value } = this;
const { checkStrictly } = tree.config;
const { checkStrictly, valueMode } = tree.config;
// 节点不在当前树上,视为未选中
if (!tree.nodeMap.get(value)) return false;
// 节点不可选,视为未选中
if (!this.isCheckable()) return false;
const checkedMap = map || tree.checkedMap;
// 严格模式,则已经可以判定选中状态
if (checkStrictly) {
return !!checkedMap.get(value);
}
let checked = false;
// 如果在 checkedMap 中,则直接为 true
if (checkedMap.get(value)) {
// 在 checkedMap 中,则根据 valueMode 的值进行判断
if (checkedMap.get(value)
&& (
// 如果 valueMode 为 all、parentFirst,则视为选中
valueMode !== 'onlyLeaf'
// 如果 valueMode 为 onlyLeaf 并且当前节点是叶子节点,则视为选中
|| this.isLeaf()
)
) {
return true;
}
// 严格模式,则已经可以判定选中状态
if (checkStrictly) return checked;
// 允许关联状态的情况下,需要进一步判断
// 如果 valueMode 为 onlyLeaf 并且当前节点是父节点,则进一步判断
if (Array.isArray(children) && children.length > 0) {
// 子节点全部选中,则当前节点选中
checked = children.every((node) => {
Expand Down
21 changes: 14 additions & 7 deletions js/tree/tree-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -864,22 +864,29 @@ export class TreeNode {
*/
public isChecked(map?: TypeIdMap): boolean {
const { children, tree, value } = this;
const { checkStrictly } = tree.config;
const { checkStrictly, valueMode } = tree.config;
// 节点不在当前树上,视为未选中
if (!tree.nodeMap.get(value)) return false;
// 节点不可选,视为未选中
if (!this.isCheckable()) return false;
const checkedMap = map || tree.checkedMap;
let checked = false;
// 如果在 checkedMap 中,则直接为 true
if (checkedMap.get(value)) {
return true;
}
// 严格模式,则已经可以判定选中状态
if (checkStrictly) {
return !!checkedMap.get(value);
}
// 允许关联状态的情况下,需要进一步判断
let checked = false;
// 在 checkedMap 中,则根据 valueMode 的值进行判断
if (checkedMap.get(value)
&& (
// 如果 valueMode 为 all、parentFirst,则视为选中
valueMode !== 'onlyLeaf'
// 如果 valueMode 为 onlyLeaf 并且当前节点是叶子节点,则视为选中
|| this.isLeaf()
)
) {
return true;
}
// 如果 valueMode 为 onlyLeaf 并且当前节点是父节点,则进一步判断
if (Array.isArray(children) && children.length > 0) {
// 子节点全部选中,则当前节点选中
checked = children.every((node) => {
Expand Down
282 changes: 264 additions & 18 deletions test/unit/tree/lazy.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import TreeStore from '../../../js/tree/tree-store';
import { delay } from './kit';

const lazyLoad = async (tree, value) => {
const promise = new Promise((resolve) => {
tree.emitter.on('load', resolve);
});
tree.getNode(value).setExpanded(true, {
directly: true,
});
await promise;
// promise 触发后,还要再等一个 reflow 事件
await delay(0);
};

// 节点延迟加载
describe('tree:lazy', () => {
describe('treeStore:lazy', () => {
Expand All @@ -22,16 +34,8 @@ describe('tree:lazy', () => {

let nodes = tree.getNodes();
expect(nodes.length).toBe(1);
const pm = new Promise((resolve) => {
tree.emitter.on('load', resolve);
});
tree.getNode('t1').setExpanded(true, {
directly: true,
});

await pm;
// promise 触发后,还要再等一个 reflow 事件
await delay(0);
await lazyLoad(tree, 't1');

nodes = tree.getNodes();
expect(nodes.length).toBe(2);
Expand Down Expand Up @@ -60,16 +64,8 @@ describe('tree:lazy', () => {

let nodes = tree.getNodes();
expect(nodes.length).toBe(1);
const pm = new Promise((resolve) => {
tree.emitter.on('load', resolve);
});
tree.getNode('t1').setExpanded(true, {
directly: true,
});

await pm;
// promise 触发后,还要再等一个 reflow 事件
await delay(0);
await lazyLoad(tree, 't1');

nodes = tree.getNodes();
expect(nodes.length).toBe(2);
Expand All @@ -79,4 +75,254 @@ describe('tree:lazy', () => {
expect(tree.getNode('t1.1').checked).toBe(true);
});
});

describe('treeStore:valueMode', () => {
it('valueMode 默认为 onlyLeaf 时,子节点未加载的情况下,无法选中父节点', async () => {
const tree = new TreeStore({
checkable: true,
lazy: true,
async load() {
await delay(0);
return [{
value: 't1.1'
}];
}
});
tree.append([{
value: 't1',
children: true,
}]);

const t1 = tree.getNode('t1');
t1.setChecked('t1', {
directly: true,
});
await delay(0);

expect(tree.getNodes().length).toBe(1);
expect(tree.getChecked().length).toBe(0);
expect(t1.checked).toBe(false);
});

it('valueMode 默认为 onlyLeaf 时,子节点全部选中的情况下,自动选中父节点', async () => {
const tree = new TreeStore({
checkable: true,
lazy: true,
async load() {
await delay(0);
return [{
value: 't1.1'
}, {
value: 't1.2'
}];
}
});
tree.append([{
value: 't1',
children: true,
}]);
const t1 = tree.getNode('t1');

await lazyLoad(tree, 't1');

expect(tree.getNodes().length).toBe(3);

const t1d1 = tree.getNode('t1.1');
const t1d2 = tree.getNode('t1.2');

t1d1.setChecked(true, {
directly: true,
});
t1d2.setChecked(true, {
directly: true,
});
expect(tree.getChecked().length).toBe(2);
expect(t1.checked).toBe(true);
expect(t1d1.checked).toBe(true);
expect(t1d2.checked).toBe(true);
});

it('valueMode 为 parentFirst', async () => {
const tree = new TreeStore({
checkable: true,
valueMode: 'parentFirst',
lazy: true,
async load() {
await delay(0);
return [{
value: 't1.1'
}, {
value: 't1.2'
}];
}
});
tree.append([{
value: 't1',
children: true,
}]);
const t1 = tree.getNode('t1');
t1.setChecked(true, {
directly: true,
});
await delay(0);

expect(tree.getNodes().length).toBe(1);
expect(tree.getChecked().length).toBe(1);
expect(t1.checked).toBe(true);

await lazyLoad(tree, 't1');

expect(tree.getNodes().length).toBe(3);
expect(tree.getChecked().length).toBe(1);
expect(t1.checked).toBe(true);
expect(tree.getNode('t1.1').checked).toBe(true);
expect(tree.getNode('t1.2').checked).toBe(true);
});

it('valueMode 为 parentFirst 的半选状态', async () => {
const tree = new TreeStore({
checkable: true,
valueMode: 'parentFirst',
lazy: true,
async load(node) {
await delay(0);
if (node.value === 't1') {
return [{
value: 't1.1',
children: true,
}];
}
if (node.value === 't1.1') {
return [{
value: 't1.1.1',
}, {
value: 't1.1.2'
}];
}
return [];
}
});
tree.append([{
value: 't1',
children: true,
}]);

// 展开 t1
await lazyLoad(tree, 't1');

expect(tree.getNodes().length).toBe(2);

// 展开 t1.1
await lazyLoad(tree, 't1.1');

expect(tree.getNodes().length).toBe(4);

tree.getNode('t1.1.1').setChecked(true, {
directly: true,
});

expect(tree.getChecked().length).toBe(1);

expect(tree.getNode('t1').checked).toBe(false);
expect(tree.getNode('t1').indeterminate).toBe(true);

expect(tree.getNode('t1.1').checked).toBe(false);
expect(tree.getNode('t1.1').indeterminate).toBe(true);

expect(tree.getNode('t1.1.1').checked).toBe(true);
expect(tree.getNode('t1.1.2').checked).toBe(false);
});

it('valueMode 为 all', async () => {
const tree = new TreeStore({
checkable: true,
valueMode: 'all',
lazy: true,
async load() {
await delay(0);
return [{
value: 't1.1'
}, {
value: 't1.2'
}];
}
});
tree.append([{
value: 't1',
children: true,
}]);

const t1 = tree.getNode('t1');
t1.setChecked(true, {
directly: true,
});
await delay(0);

expect(tree.getNodes().length).toBe(1);
expect(tree.getChecked().length).toBe(1);
expect(t1.checked).toBe(true);

await lazyLoad(tree, 't1');

expect(tree.getNodes().length).toBe(3);
expect(tree.getChecked().length).toBe(3);
expect(t1.checked).toBe(true);
expect(tree.getNode('t1.1').checked).toBe(true);
expect(tree.getNode('t1.2').checked).toBe(true);
});

it('valueMode 为 all 的半选状态', async () => {
const tree = new TreeStore({
checkable: true,
valueMode: 'all',
lazy: true,
async load(node) {
await delay(0);
if (node.value === 't1') {
return [{
value: 't1.1',
children: true,
}];
}
if (node.value === 't1.1') {
return [{
value: 't1.1.1',
}, {
value: 't1.1.2'
}];
}
return [];
}
});
tree.append([{
value: 't1',
children: true,
}]);

// 展开 t1
await lazyLoad(tree, 't1');

expect(tree.getNodes().length).toBe(2);

// 展开 t1.1
await lazyLoad(tree, 't1.1');

expect(tree.getNodes().length).toBe(4);

tree.getNode('t1.1.1').setChecked(true, {
directly: true,
});

expect(tree.getChecked().length).toBe(1);

expect(tree.getNode('t1').checked).toBe(false);
expect(tree.getNode('t1').indeterminate).toBe(true);

expect(tree.getNode('t1.1').checked).toBe(false);
expect(tree.getNode('t1.1').indeterminate).toBe(true);

expect(tree.getNode('t1.1.1').checked).toBe(true);
expect(tree.getNode('t1.1.2').checked).toBe(false);
});
});
});

0 comments on commit 18d1638

Please sign in to comment.