diff --git a/packages/components/tree-select/__test__/empty-options.test.js b/packages/components/tree-select/__test__/empty-options.test.js new file mode 100644 index 0000000000..5899e9f101 --- /dev/null +++ b/packages/components/tree-select/__test__/empty-options.test.js @@ -0,0 +1,77 @@ +import path from 'path'; +import simulate from 'miniprogram-simulate'; + +describe('TreeSelect empty options', () => { + const id = load(path.resolve(__dirname, '../tree-select')); + const options = [ + { label: 'First parent', value: 1, children: [{ label: 'First leaf', value: 11 }] }, + { label: 'Second parent', value: 2, children: [{ label: 'Second leaf', value: 22 }] }, + ]; + let component; + + function render(props = {}) { + component = simulate.render(id, { options, ...props }); + component.attach(document.createElement('parent-wrapper')); + return component; + } + + afterEach(() => component?.detach()); + + it.each([ + ['value', { value: [2, 22] }], + ['customValue', { value: [1, 11], customValue: [2, 22] }], + ['multiple', { value: [2, [22]], multiple: true }], + ['defaultValue', { defaultValue: [2, 22] }], + ])('removes rendered columns when %s options become empty', async (_, props) => { + render(props); + expect(component.dom.textContent).toContain('Second leaf'); + const selected = JSON.parse(JSON.stringify(component.data.innerValue)); + const change = jest.fn(); + component.addEventListener('change', change); + component.setData({ options: [] }); + await simulate.sleep(10); + expect(component.data.treeOptions).toEqual([]); + expect(component.dom.textContent).not.toContain('parent'); + expect(component.dom.textContent).not.toContain('leaf'); + expect(component.data.innerValue).toEqual(selected); + expect(change).not.toHaveBeenCalled(); + }); + + it('clears columns with custom keys', async () => { + render({ + options: [{ name: 'Named parent', id: 2, nodes: [{ name: 'Named leaf', id: 22 }] }], + keys: { label: 'name', value: 'id', children: 'nodes' }, + value: [2, 22], + }); + expect(component.dom.textContent).toContain('Named leaf'); + component.setData({ options: [] }); + await simulate.sleep(10); + expect(component.data.treeOptions).toEqual([]); + expect(component.dom.textContent).not.toContain('Named'); + }); + + it('renders no options when initially empty', () => { + render({ options: [], value: [2, 22] }); + expect(component.data.treeOptions || []).toEqual([]); + expect(component.dom.textContent.trim()).toBe(''); + }); + + it('restores the selected branch when options return', async () => { + render({ value: [2, 22] }); + component.setData({ options: [] }); + await simulate.sleep(10); + component.setData({ options }); + await simulate.sleep(10); + expect(component.data.innerValue).toEqual([2, 22]); + expect(component.dom.textContent).toContain('Second leaf'); + expect(component.dom.textContent).not.toContain('First leaf'); + }); + + it('renders asynchronously supplied options', async () => { + render({ options: [], value: [2, 22] }); + component.setData({ options }); + await simulate.sleep(10); + expect(component.data.innerValue).toEqual([2, 22]); + expect(component.dom.textContent).toContain('Second leaf'); + }); +}); diff --git a/packages/components/tree-select/tree-select.ts b/packages/components/tree-select/tree-select.ts index 01b5ac66fd..2b698aede3 100644 --- a/packages/components/tree-select/tree-select.ts +++ b/packages/components/tree-select/tree-select.ts @@ -56,7 +56,10 @@ export default class TreeSelect extends SuperComponent { buildTreeOptions() { const { options, value, customValue, multiple, keys } = this.data; - if (!options.length) return; + if (!options.length) { + this.setData({ treeOptions: [] }); + return; + } const treeOptions: TreeOptionData[][] = []; diff --git a/packages/uniapp-components/tree-select/tree-select.vue b/packages/uniapp-components/tree-select/tree-select.vue index 74bcd30898..0a9934da8d 100644 --- a/packages/uniapp-components/tree-select/tree-select.vue +++ b/packages/uniapp-components/tree-select/tree-select.vue @@ -205,7 +205,10 @@ export default { buildTreeOptions() { const { options, innerValue: value, customValue, multiple, keys } = this; - if (!options.length) return; + if (!options.length) { + this.treeOptions = []; + return; + } const treeOptions = [];