From 41d5fed4841e958e1d512c8cc7292105c6b02664 Mon Sep 17 00:00:00 2001 From: Svilen Darvenyashki Date: Fri, 20 Mar 2026 12:03:48 +0200 Subject: [PATCH 1/4] chore(ui5-user-settings-dialog): Group skipping is not correct --- packages/fiori/src/UserSettingsDialog.ts | 61 +++++++++++++++++++ .../fiori/src/UserSettingsDialogTemplate.tsx | 8 +-- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/packages/fiori/src/UserSettingsDialog.ts b/packages/fiori/src/UserSettingsDialog.ts index 6f8ee7ed6c7b..682fa03c728d 100644 --- a/packages/fiori/src/UserSettingsDialog.ts +++ b/packages/fiori/src/UserSettingsDialog.ts @@ -13,6 +13,8 @@ import type ListItemBase from "@ui5/webcomponents/dist/ListItemBase.js"; import type { PopupBeforeCloseEventDetail } from "@ui5/webcomponents/dist/Popup.js"; import { isPhone, isTablet, isCombi } from "@ui5/webcomponents-base/dist/Device.js"; import MediaRange from "@ui5/webcomponents-base/dist/MediaRange.js"; +import { isF6Next, isF6Previous } from "@ui5/webcomponents-base/dist/Keys.js"; +import { getFirstFocusableElement } from "@ui5/webcomponents-base/dist/util/FocusableElements.js"; import UserSettingsDialogTemplate from "./UserSettingsDialogTemplate.js"; import type UserSettingsItem from "./UserSettingsItem.js"; import UserSettingsDialogCss from "./generated/themes/UserSettingsDialog.css.js"; @@ -193,6 +195,21 @@ class UserSettingsDialog extends UI5Element { @property({ type: String }) _mediaRange?: any; + _boundHandleF6: (e: KeyboardEvent) => void; + + constructor() { + super(); + this._boundHandleF6 = this._handleF6.bind(this); + } + + onEnterDOM() { + this.addEventListener("keydown", this._boundHandleF6 as EventListener); + } + + onExitDOM() { + this.removeEventListener("keydown", this._boundHandleF6 as EventListener); + } + onBeforeRendering() { this._mediaRange = MediaRange.getCurrentRange(MediaRange.RANGESETS.RANGE_4STEPS); const searchValue = this._searchValue.toLowerCase(); @@ -310,6 +327,50 @@ class UserSettingsDialog extends UI5Element { this._searchValue = (e.target as Input).value; } + async _handleF6(e: KeyboardEvent) { + const forward = isF6Next(e); + const backward = isF6Previous(e); + + if (!forward && !backward) { + return; + } + + e.preventDefault(); + e.stopPropagation(); + + const zones = this._getVisibleF6Zones(); + if (zones.length === 0) { + return; + } + + const currentZone = e.composedPath().find(node => zones.includes(node as HTMLElement)) as HTMLElement | undefined; + const currentIndex = currentZone ? zones.indexOf(currentZone) : -1; + + let nextIndex: number; + if (forward) { + nextIndex = currentIndex + 1; + if (nextIndex >= zones.length) { + nextIndex = 0; + } + } else { + nextIndex = currentIndex - 1; + if (nextIndex < 0) { + nextIndex = zones.length - 1; + } + } + + const elementToFocus = await getFirstFocusableElement(zones[nextIndex]); + elementToFocus?.focus(); + } + + _getVisibleF6Zones(): HTMLElement[] { + return Array.from(this.shadowRoot!.querySelectorAll("[data-sap-ui-fastnavgroup='true']")) + .filter(el => { + const style = getComputedStyle(el); + return style.display !== "none" && style.visibility !== "hidden"; + }); + } + captureRef(ref: HTMLElement & { associatedSettingItem?: UI5Element} | null) { if (ref) { ref.associatedSettingItem = this; diff --git a/packages/fiori/src/UserSettingsDialogTemplate.tsx b/packages/fiori/src/UserSettingsDialogTemplate.tsx index 43d87df4ab8e..a3e1e3c1a34b 100644 --- a/packages/fiori/src/UserSettingsDialogTemplate.tsx +++ b/packages/fiori/src/UserSettingsDialogTemplate.tsx @@ -25,7 +25,7 @@ export default function UserSettingsDialogTemplate(this: UserSettingsDialog) { initialFocus={`setting-${this._selectedSetting?._id}`} >