From b4a9f474bd229753a0bf94cfc1bfe16b7a2b1484 Mon Sep 17 00:00:00 2001 From: Georgi Damyanov Date: Wed, 30 Jul 2025 15:54:41 +0300 Subject: [PATCH 1/7] feat: focus first filter option on initial load --- packages/fiori/src/ViewSettingsDialog.ts | 19 ++++++++++++++++--- .../fiori/src/ViewSettingsDialogTemplate.tsx | 15 +++++++++------ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/packages/fiori/src/ViewSettingsDialog.ts b/packages/fiori/src/ViewSettingsDialog.ts index a95b34fc896d..b8c2f1f903ab 100644 --- a/packages/fiori/src/ViewSettingsDialog.ts +++ b/packages/fiori/src/ViewSettingsDialog.ts @@ -251,6 +251,7 @@ class ViewSettingsDialog extends UI5Element { _dialog?: Dialog; _sortOrder?: List; _sortBy?: List; + _focusedFilterOptionIndex: number = 0; @i18n("@ui5/webcomponents-fiori") static i18nBundle: I18nBundle; @@ -265,6 +266,20 @@ class ViewSettingsDialog extends UI5Element { } } + onAfterRendering() { + if (this._filterStepTwo) { + requestAnimationFrame(() => this._focusFirstFilterOptionItem()); + } + } + + _focusFirstFilterOptionItem() { + const filterList = this.shadowRoot?.querySelector("[ui5-list]:not([sort-by]):not([sort-order])"); + const firstItem = filterList?.getItems()[0]; + if (firstItem) { + firstItem.focus(); + } + } + onInvalidation(changeInfo: ChangeInfo) { if (changeInfo.type === "slot") { this._confirmedSettings = this._settings; @@ -531,6 +546,7 @@ class ViewSettingsDialog extends UI5Element { filter.filterOptions.forEach(option => { if (option.text === itemText) { option.selected = !option.selected; + option.focused = true } }); } @@ -568,9 +584,6 @@ class ViewSettingsDialog extends UI5Element { }); } - /** - * Sets focus on recently used control within the dialog. - */ _focusRecentlyUsedControl() { if (!this._recentlyFocused || !Object.keys(this._recentlyFocused).length) { return; diff --git a/packages/fiori/src/ViewSettingsDialogTemplate.tsx b/packages/fiori/src/ViewSettingsDialogTemplate.tsx index 31534bc6a7b1..3e2dea6c569b 100644 --- a/packages/fiori/src/ViewSettingsDialogTemplate.tsx +++ b/packages/fiori/src/ViewSettingsDialogTemplate.tsx @@ -110,16 +110,19 @@ function ViewSettingsDialogTemplateContent(this: ViewSettingsDialog) { {this._filterStepTwo ? ( - {this._currentSettings.filters.filter(item => item.selected).map(item => (<> - {item.filterOptions.map(option => ( + {this._currentSettings.filters + .filter(item => item.selected) + .flatMap(item => item.filterOptions.map((option, idx) => ( {option.text} - ))} - ))} + focused={option.focused} + > + {option.text} + + )))} ) : ( // else Date: Wed, 30 Jul 2025 16:32:04 +0300 Subject: [PATCH 2/7] feat: add focused property --- packages/fiori/src/ViewSettingsDialog.ts | 29 ++++++++++--------- .../fiori/src/ViewSettingsDialogTemplate.tsx | 2 +- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/fiori/src/ViewSettingsDialog.ts b/packages/fiori/src/ViewSettingsDialog.ts index b8c2f1f903ab..0c8f41eecc75 100644 --- a/packages/fiori/src/ViewSettingsDialog.ts +++ b/packages/fiori/src/ViewSettingsDialog.ts @@ -65,7 +65,7 @@ type ViewSettingsDialogCancelEventDetail = VSDSettings & { } // Common properties for several VSDInternalSettings fields -type VSDItem = {text?: string, selected: boolean} +type VSDItem = {text?: string, selected: boolean, focused?: boolean} // Used for sortOrder, sortBy and filterOptions // Used for the private properties _initialSettings, _confirmedSettings and _currentSettings type VSDInternalSettings = { @@ -266,19 +266,19 @@ class ViewSettingsDialog extends UI5Element { } } - onAfterRendering() { - if (this._filterStepTwo) { - requestAnimationFrame(() => this._focusFirstFilterOptionItem()); - } - } + // onAfterRendering() { + // if (this._filterStepTwo) { + // requestAnimationFrame(() => this._focusFirstFilterOptionItem()); + // } + // } - _focusFirstFilterOptionItem() { - const filterList = this.shadowRoot?.querySelector("[ui5-list]:not([sort-by]):not([sort-order])"); - const firstItem = filterList?.getItems()[0]; - if (firstItem) { - firstItem.focus(); - } - } + // _focusFirstFilterOptionItem() { + // const filterList = this.shadowRoot?.querySelector("[ui5-list]:not([sort-by]):not([sort-order])"); + // const firstItem = filterList?.getItems()[0]; + // if (firstItem) { + // firstItem.focus(); + // } + // } onInvalidation(changeInfo: ChangeInfo) { if (changeInfo.type === "slot") { @@ -544,9 +544,10 @@ class ViewSettingsDialog extends UI5Element { this._currentSettings.filters = this._currentSettings.filters.map(filter => { if (filter.selected) { filter.filterOptions.forEach(option => { + option.focused = false; if (option.text === itemText) { option.selected = !option.selected; - option.focused = true + option.focused = true; } }); } diff --git a/packages/fiori/src/ViewSettingsDialogTemplate.tsx b/packages/fiori/src/ViewSettingsDialogTemplate.tsx index 3e2dea6c569b..4bd88c51de0a 100644 --- a/packages/fiori/src/ViewSettingsDialogTemplate.tsx +++ b/packages/fiori/src/ViewSettingsDialogTemplate.tsx @@ -115,7 +115,7 @@ function ViewSettingsDialogTemplateContent(this: ViewSettingsDialog) { > {this._currentSettings.filters .filter(item => item.selected) - .flatMap(item => item.filterOptions.map((option, idx) => ( + .flatMap(item => item.filterOptions.map(option => ( Date: Mon, 4 Aug 2025 14:36:26 +0300 Subject: [PATCH 3/7] feat: focus first item in filter options --- .../cypress/specs/ViewSettingsDialog.cy.tsx | 37 +++++++++++++++++++ packages/fiori/src/ViewSettingsDialog.ts | 26 +++++-------- .../fiori/src/ViewSettingsDialogTemplate.tsx | 1 - 3 files changed, 46 insertions(+), 18 deletions(-) diff --git a/packages/fiori/cypress/specs/ViewSettingsDialog.cy.tsx b/packages/fiori/cypress/specs/ViewSettingsDialog.cy.tsx index 8878f18671e2..7f99c441e5e4 100644 --- a/packages/fiori/cypress/specs/ViewSettingsDialog.cy.tsx +++ b/packages/fiori/cypress/specs/ViewSettingsDialog.cy.tsx @@ -370,4 +370,41 @@ describe("ViewSettingsDialog Tests", () => { cy.get("@vsd") .invoke("prop", "open", false); }); + + it("should focus first item in filter options", () => { + cy.mount( + + + + + + + + + + + + + ); + + cy.get("#vsdFilter") + .as("vsd"); + + cy.get("@vsd") + .invoke("prop", "open", true); + + cy.get("@vsd") + .shadow() + .find("[ui5-li]") + .eq(0) + .shadow() + .find("span[part=title]") + .realClick(); + + cy.get("@vsd") + .shadow() + .find("[ui5-li]") + .eq(0) + .should("be.focused"); + }); }); diff --git a/packages/fiori/src/ViewSettingsDialog.ts b/packages/fiori/src/ViewSettingsDialog.ts index 0c8f41eecc75..c4bd307de5b8 100644 --- a/packages/fiori/src/ViewSettingsDialog.ts +++ b/packages/fiori/src/ViewSettingsDialog.ts @@ -13,6 +13,7 @@ import type List from "@ui5/webcomponents/dist/List.js"; import type { ListItemClickEventDetail, ListSelectionChangeEventDetail } from "@ui5/webcomponents/dist/List.js"; import announce from "@ui5/webcomponents-base/dist/util/InvisibleMessage.js"; import InvisibleMessageMode from "@ui5/webcomponents-base/dist/types/InvisibleMessageMode.js"; +import { renderFinished } from "@ui5/webcomponents-base/dist/Render.js"; import ViewSettingsDialogMode from "./types/ViewSettingsDialogMode.js"; import "@ui5/webcomponents-icons/dist/sort.js"; @@ -65,7 +66,7 @@ type ViewSettingsDialogCancelEventDetail = VSDSettings & { } // Common properties for several VSDInternalSettings fields -type VSDItem = {text?: string, selected: boolean, focused?: boolean} // Used for sortOrder, sortBy and filterOptions +type VSDItem = {text?: string, selected: boolean} // Used for sortOrder, sortBy and filterOptions // Used for the private properties _initialSettings, _confirmedSettings and _currentSettings type VSDInternalSettings = { @@ -266,19 +267,13 @@ class ViewSettingsDialog extends UI5Element { } } - // onAfterRendering() { - // if (this._filterStepTwo) { - // requestAnimationFrame(() => this._focusFirstFilterOptionItem()); - // } - // } - - // _focusFirstFilterOptionItem() { - // const filterList = this.shadowRoot?.querySelector("[ui5-list]:not([sort-by]):not([sort-order])"); - // const firstItem = filterList?.getItems()[0]; - // if (firstItem) { - // firstItem.focus(); - // } - // } + onAfterRendering() { + if (this.isModeFilter && this._filterStepTwo) { + renderFinished().then(() => { + this._dialog?.querySelector("[ui5-list]")?.focusFirstItem(); + }); + } + } onInvalidation(changeInfo: ChangeInfo) { if (changeInfo.type === "slot") { @@ -539,15 +534,12 @@ class ViewSettingsDialog extends UI5Element { _handleFilterValueItemClick(e: CustomEvent) { const itemText = e.detail.targetItem.innerText; - // Update the component state this._currentSettings.filters = this._currentSettings.filters.map(filter => { if (filter.selected) { filter.filterOptions.forEach(option => { - option.focused = false; if (option.text === itemText) { option.selected = !option.selected; - option.focused = true; } }); } diff --git a/packages/fiori/src/ViewSettingsDialogTemplate.tsx b/packages/fiori/src/ViewSettingsDialogTemplate.tsx index 4bd88c51de0a..031774340593 100644 --- a/packages/fiori/src/ViewSettingsDialogTemplate.tsx +++ b/packages/fiori/src/ViewSettingsDialogTemplate.tsx @@ -118,7 +118,6 @@ function ViewSettingsDialogTemplateContent(this: ViewSettingsDialog) { .flatMap(item => item.filterOptions.map(option => ( {option.text} From 4a07d68f30d0fd524e53b974b0ea533bbbb08b93 Mon Sep 17 00:00:00 2001 From: Georgi Damyanov Date: Mon, 4 Aug 2025 14:44:32 +0300 Subject: [PATCH 4/7] refactor: revert changes --- packages/fiori/src/ViewSettingsDialogTemplate.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/fiori/src/ViewSettingsDialogTemplate.tsx b/packages/fiori/src/ViewSettingsDialogTemplate.tsx index 031774340593..31534bc6a7b1 100644 --- a/packages/fiori/src/ViewSettingsDialogTemplate.tsx +++ b/packages/fiori/src/ViewSettingsDialogTemplate.tsx @@ -110,18 +110,16 @@ function ViewSettingsDialogTemplateContent(this: ViewSettingsDialog) { {this._filterStepTwo ? ( - {this._currentSettings.filters - .filter(item => item.selected) - .flatMap(item => item.filterOptions.map(option => ( + {this._currentSettings.filters.filter(item => item.selected).map(item => (<> + {item.filterOptions.map(option => ( - {option.text} - - )))} + >{option.text} + ))} + ))} ) : ( // else Date: Mon, 4 Aug 2025 14:47:24 +0300 Subject: [PATCH 5/7] refactor: revert not needed changes --- packages/fiori/src/ViewSettingsDialog.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/fiori/src/ViewSettingsDialog.ts b/packages/fiori/src/ViewSettingsDialog.ts index c4bd307de5b8..67bfc3c836e5 100644 --- a/packages/fiori/src/ViewSettingsDialog.ts +++ b/packages/fiori/src/ViewSettingsDialog.ts @@ -66,7 +66,7 @@ type ViewSettingsDialogCancelEventDetail = VSDSettings & { } // Common properties for several VSDInternalSettings fields -type VSDItem = {text?: string, selected: boolean} // Used for sortOrder, sortBy and filterOptions +type VSDItem = {text?: string, selected: boolean} // Used for the private properties _initialSettings, _confirmedSettings and _currentSettings type VSDInternalSettings = { @@ -252,7 +252,6 @@ class ViewSettingsDialog extends UI5Element { _dialog?: Dialog; _sortOrder?: List; _sortBy?: List; - _focusedFilterOptionIndex: number = 0; @i18n("@ui5/webcomponents-fiori") static i18nBundle: I18nBundle; @@ -534,6 +533,7 @@ class ViewSettingsDialog extends UI5Element { _handleFilterValueItemClick(e: CustomEvent) { const itemText = e.detail.targetItem.innerText; + // Update the component state this._currentSettings.filters = this._currentSettings.filters.map(filter => { if (filter.selected) { @@ -577,6 +577,9 @@ class ViewSettingsDialog extends UI5Element { }); } + /** + * Sets focus on recently used control within the dialog. + */ _focusRecentlyUsedControl() { if (!this._recentlyFocused || !Object.keys(this._recentlyFocused).length) { return; From 8c0247e2ea038cd1399c8d11399031433b2bfd12 Mon Sep 17 00:00:00 2001 From: Georgi Damyanov Date: Thu, 7 Aug 2025 14:13:52 +0300 Subject: [PATCH 6/7] fix: extract list in query --- packages/fiori/src/ViewSettingsDialog.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/fiori/src/ViewSettingsDialog.ts b/packages/fiori/src/ViewSettingsDialog.ts index 67bfc3c836e5..baf9526d27e7 100644 --- a/packages/fiori/src/ViewSettingsDialog.ts +++ b/packages/fiori/src/ViewSettingsDialog.ts @@ -3,6 +3,7 @@ import slot from "@ui5/webcomponents-base/dist/decorators/slot.js"; import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js"; import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js"; +import query from "@ui5/webcomponents-base/dist/decorators/query.js"; import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; import { isPhone } from "@ui5/webcomponents-base/dist/Device.js"; import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js"; @@ -249,6 +250,9 @@ class ViewSettingsDialog extends UI5Element { @slot() filterItems!: Array; + @query("[ui5-list]") + _list!: List; + _dialog?: Dialog; _sortOrder?: List; _sortBy?: List; @@ -267,9 +271,9 @@ class ViewSettingsDialog extends UI5Element { } onAfterRendering() { - if (this.isModeFilter && this._filterStepTwo) { + if (this.isModeFilter) { renderFinished().then(() => { - this._dialog?.querySelector("[ui5-list]")?.focusFirstItem(); + this._list?.focusFirstItem(); }); } } @@ -515,7 +519,7 @@ class ViewSettingsDialog extends UI5Element { } afterDialogOpen(): void { - this._dialog?.querySelector("[ui5-list]")?.focusFirstItem(); + this._list?.focusFirstItem(); this._focusRecentlyUsedControl(); From 4150520e33ab21f13519198da088a87fd4668384 Mon Sep 17 00:00:00 2001 From: Georgi Damyanov Date: Thu, 7 Aug 2025 14:17:43 +0300 Subject: [PATCH 7/7] fix: refactor tests --- packages/fiori/cypress/specs/ViewSettingsDialog.cy.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/fiori/cypress/specs/ViewSettingsDialog.cy.tsx b/packages/fiori/cypress/specs/ViewSettingsDialog.cy.tsx index 7f99c441e5e4..1cea9f293e9e 100644 --- a/packages/fiori/cypress/specs/ViewSettingsDialog.cy.tsx +++ b/packages/fiori/cypress/specs/ViewSettingsDialog.cy.tsx @@ -396,7 +396,7 @@ describe("ViewSettingsDialog Tests", () => { cy.get("@vsd") .shadow() .find("[ui5-li]") - .eq(0) + .first() .shadow() .find("span[part=title]") .realClick(); @@ -404,7 +404,7 @@ describe("ViewSettingsDialog Tests", () => { cy.get("@vsd") .shadow() .find("[ui5-li]") - .eq(0) + .first() .should("be.focused"); }); });