From 06563081496c431add533a10e6dc8736236e40aa Mon Sep 17 00:00:00 2001 From: uros Date: Thu, 22 Aug 2024 12:56:47 +0200 Subject: [PATCH 1/3] fix: Listbox: Preserve option groups while filtering. chore: Add unit tests fixes #6233 --- packages/primevue/src/listbox/Listbox.spec.js | 65 +++++++++++++++++++ packages/primevue/src/listbox/Listbox.vue | 22 ++++++- 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/packages/primevue/src/listbox/Listbox.spec.js b/packages/primevue/src/listbox/Listbox.spec.js index 23084674c..4bf70bdd7 100644 --- a/packages/primevue/src/listbox/Listbox.spec.js +++ b/packages/primevue/src/listbox/Listbox.spec.js @@ -47,5 +47,70 @@ describe('Listbox.vue', () => { expect(icon.classes()).toContain('pi-discord'); }); + + it('should correctly filter', async () => { + await wrapper.setProps({ + filter: true + }); + + const filterInput = wrapper.find('input.p-listbox-filter'); + + expect(filterInput.exists()).toBe(true); + + await filterInput.setValue('is'); + + const options = wrapper.findAll('.p-listbox-option'); + + expect(options.length).toBe(2); + expect(options[0].text()).toBe('Istanbul'); + }); + + it('should correctly filter groups', async () => { + await wrapper.setProps({ + filter: true, + optionGroupLabel: 'label', + optionLabel: 'label', + optionGroupChildren: 'items', + options: [ + { + label: 'Germany', + code: 'DE', + items: [ + { label: 'Berlin', value: 'Berlin' }, + { label: 'Frankfurt', value: 'Frankfurt' }, + { label: 'Hamburg', value: 'Hamburg' }, + { label: 'Munich', value: 'Munich' } + ] + }, + { + label: 'USA', + code: 'US', + items: [ + { label: 'Chicago', value: 'Chicago' }, + { label: 'Los Angeles', value: 'Los Angeles' }, + { label: 'New York', value: 'New York' }, + { label: 'San Francisco', value: 'San Francisco' } + ] + } + ] + }); + + const filterInput = wrapper.find('input.p-listbox-filter'); + + expect(filterInput.exists()).toBe(true); + + await filterInput.setValue('ch'); + + const optionGroups = wrapper.findAll('.p-listbox-option-group'); + const options = wrapper.findAll('.p-listbox-option'); + + expect(optionGroups.length).toBe(2); + expect(optionGroups[0].text()).toBe('Germany'); + expect(optionGroups[1].text()).toBe('USA'); + + expect(options.length).toBe(2); + expect(options[0].text()).toBe('Munich'); + expect(options[1].text()).toBe('Chicago'); + }); }); }); diff --git a/packages/primevue/src/listbox/Listbox.vue b/packages/primevue/src/listbox/Listbox.vue index 04cc524cc..ebad55ec9 100755 --- a/packages/primevue/src/listbox/Listbox.vue +++ b/packages/primevue/src/listbox/Listbox.vue @@ -726,7 +726,27 @@ export default { visibleOptions() { const options = this.optionGroupLabel ? this.flatOptions(this.options) : this.options || []; - return this.filterValue ? FilterService.filter(options, this.searchFields, this.filterValue, this.filterMatchMode, this.filterLocale) : options; + if (this.filterValue) { + const filteredOptions = FilterService.filter(options, this.searchFields, this.filterValue, this.filterMatchMode, this.filterLocale); + + if (this.optionGroupLabel) { + const optionGroups = this.options || []; + const filtered = []; + + optionGroups.forEach((group) => { + const groupChildren = this.getOptionGroupChildren(group); + const filteredItems = groupChildren.filter((item) => filteredOptions.includes(item)); + + if (filteredItems.length > 0) filtered.push({ ...group, [typeof this.optionGroupChildren === 'string' ? this.optionGroupChildren : 'items']: [...filteredItems] }); + }); + + return this.flatOptions(filtered); + } + + return filteredOptions; + } + + return options; }, hasSelectedOption() { return isNotEmpty(this.modelValue); From e5a49eb55145b0f8efefba68cd5b626b347d8729 Mon Sep 17 00:00:00 2001 From: uros Date: Sun, 15 Sep 2024 18:09:58 +0200 Subject: [PATCH 2/3] - optimize the options display --- packages/primevue/src/listbox/Listbox.vue | 47 ++++++++--------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/packages/primevue/src/listbox/Listbox.vue b/packages/primevue/src/listbox/Listbox.vue index 248d0010e..d637b4802 100755 --- a/packages/primevue/src/listbox/Listbox.vue +++ b/packages/primevue/src/listbox/Listbox.vue @@ -703,17 +703,6 @@ export default { this.$emit('update:modelValue', value); this.$emit('change', { originalEvent: event, value }); }, - flatOptions(options) { - return (options || []).reduce((result, option, index) => { - result.push({ optionGroup: option, group: true, index }); - - const optionGroupChildren = this.getOptionGroupChildren(option); - - optionGroupChildren && optionGroupChildren.forEach((o) => result.push(o)); - - return result; - }, []); - }, listRef(el, contentRef) { this.list = el; contentRef && contentRef(el); // For VirtualScroller @@ -723,30 +712,26 @@ export default { } }, computed: { - visibleOptions() { - const options = this.optionGroupLabel ? this.flatOptions(this.options) : this.options || []; + optionsListFlat() { + return this.filterValue ? FilterService.filter(this.options, this.searchFields, this.filterValue, this.filterMatchMode, this.filterLocale) : this.options; + }, + optionsListGroup() { + const filteredOptions = []; - if (this.filterValue) { - const filteredOptions = FilterService.filter(options, this.searchFields, this.filterValue, this.filterMatchMode, this.filterLocale); + (this.options || []).forEach((optionGroup) => { + const optionGroupChildren = this.getOptionGroupChildren(optionGroup) || []; + const filteredChildren = this.filterValue ? FilterService.filter(optionGroupChildren, this.searchFields, this.filterValue, this.filterMatchMode, this.filterLocale) : optionGroupChildren; - if (this.optionGroupLabel) { - const optionGroups = this.options || []; - const filtered = []; - - optionGroups.forEach((group) => { - const groupChildren = this.getOptionGroupChildren(group); - const filteredItems = groupChildren.filter((item) => filteredOptions.includes(item)); - - if (filteredItems.length > 0) filtered.push({ ...group, [typeof this.optionGroupChildren === 'string' ? this.optionGroupChildren : 'items']: [...filteredItems] }); - }); - - return this.flatOptions(filtered); + if (filteredChildren && filteredChildren.length) { + filteredOptions.push({ optionGroup, group: true }); + filteredOptions.push(...filteredChildren); } + }); - return filteredOptions; - } - - return options; + return filteredOptions; + }, + visibleOptions() { + return this.optionGroupLabel ? this.optionsListGroup : this.optionsListFlat; }, hasSelectedOption() { return isNotEmpty(this.modelValue); From d4e44f3bd04d900863b0e7ae621c4a31bf616c26 Mon Sep 17 00:00:00 2001 From: uros Date: Sun, 15 Sep 2024 18:14:29 +0200 Subject: [PATCH 3/3] - optimize optionsListGroup --- packages/primevue/src/listbox/Listbox.vue | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/primevue/src/listbox/Listbox.vue b/packages/primevue/src/listbox/Listbox.vue index d637b4802..c044f20c4 100755 --- a/packages/primevue/src/listbox/Listbox.vue +++ b/packages/primevue/src/listbox/Listbox.vue @@ -722,9 +722,8 @@ export default { const optionGroupChildren = this.getOptionGroupChildren(optionGroup) || []; const filteredChildren = this.filterValue ? FilterService.filter(optionGroupChildren, this.searchFields, this.filterValue, this.filterMatchMode, this.filterLocale) : optionGroupChildren; - if (filteredChildren && filteredChildren.length) { - filteredOptions.push({ optionGroup, group: true }); - filteredOptions.push(...filteredChildren); + if (filteredChildren?.length) { + filteredOptions.push({ optionGroup, group: true }, ...filteredChildren); } });