From c0b629b90da868f9c80391376279b67d3cbea548 Mon Sep 17 00:00:00 2001 From: Dobrin Dimchev Date: Thu, 19 Sep 2024 13:23:12 +0300 Subject: [PATCH 1/5] fix(ui5-list): enable forms mode navigation --- .../fiori/src/NotificationListItemBase.ts | 7 --- packages/main/src/List.ts | 48 ++++++++----------- packages/main/src/ListItem.ts | 1 + packages/main/src/ListItemBase.ts | 24 ++-------- 4 files changed, 23 insertions(+), 57 deletions(-) diff --git a/packages/fiori/src/NotificationListItemBase.ts b/packages/fiori/src/NotificationListItemBase.ts index 6b456c42e409..753bb41700ca 100644 --- a/packages/fiori/src/NotificationListItemBase.ts +++ b/packages/fiori/src/NotificationListItemBase.ts @@ -2,7 +2,6 @@ import { isSpace, isF2 } from "@ui5/webcomponents-base/dist/Keys.js"; import property from "@ui5/webcomponents-base/dist/decorators/property.js"; import { getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js"; import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; -import { getTabbableElements } from "@ui5/webcomponents-base/dist/util/TabbableElements.js"; import getActiveElement from "@ui5/webcomponents-base/dist/util/getActiveElement.js"; import ListItemBase from "@ui5/webcomponents/dist/ListItemBase.js"; import { getEventMark } from "@ui5/webcomponents-base/dist/MarkedEvents.js"; @@ -99,12 +98,6 @@ class NotificationListItemBase extends ListItemBase { return this.getFocusDomRef(); } - shouldForwardTabAfter() { - const aContent = getTabbableElements(this.getHeaderDomRef()!); - - return aContent.length === 0 || (aContent[aContent.length - 1] === getActiveElement()); - } - static async onDefine() { NotificationListItemBase.i18nFioriBundle = await getI18nBundle("@ui5/webcomponents-fiori"); } diff --git a/packages/main/src/List.ts b/packages/main/src/List.ts index 0f77f780f458..28cbfeac6980 100644 --- a/packages/main/src/List.ts +++ b/packages/main/src/List.ts @@ -896,9 +896,9 @@ class List extends UI5Element { return; } - if (isTabNext(e)) { - this._handleTabNext(e); - } + // if (isTabNext(e)) { + // this._handleTabNext(e); + // } } _moveItem(item: ListItemBase, e: KeyboardEvent) { @@ -995,31 +995,6 @@ class List extends UI5Element { this.fireEvent("load-more"); } - /* - * KEYBOARD SUPPORT - */ - _handleTabNext(e: KeyboardEvent) { - let lastTabbableEl; - const target = getNormalizedTarget(e.target as HTMLElement); - - if (!lastTabbableEl) { - return; - } - - if (lastTabbableEl === target) { - if (this.getFirstItem(x => x.selected && x._focusable)) { - this.focusFirstSelectedItem(); - } else if (this.getPreviouslyFocusedItem()) { - this.focusPreviouslyFocusedItem(); - } else { - this.focusFirstItem(); - } - - e.stopImmediatePropagation(); - e.preventDefault(); - } - } - _onfocusin(e: FocusEvent) { const target = getNormalizedTarget(e.target as HTMLElement); // If the focusin event does not origin from one of the 'triggers' - ignore it. @@ -1218,13 +1193,28 @@ class List extends UI5Element { } onForwardBefore(e: CustomEvent) { + const item = e.detail.item as ListItemBase; + const target = getNormalizedTarget(e.target as HTMLElement); + this.setPreviouslyFocusedItem(e.target as ListItemBase); + + if (target !== item.getFocusDomRef()) { + return; + } + this.focusBeforeElement(); e.stopPropagation(); } onForwardAfter(e: CustomEvent) { - this.setPreviouslyFocusedItem(e.target as ListItemBase); + const item = e.detail.item as ListItemBase; + const target = getNormalizedTarget(e.target as HTMLElement); + + this.setPreviouslyFocusedItem(item); + + if (target !== item.getFocusDomRef()) { + return; + } if (!this.growsWithButton) { this.focusAfterElement(); diff --git a/packages/main/src/ListItem.ts b/packages/main/src/ListItem.ts index 1b348a430e0a..bd7c4bff03f3 100644 --- a/packages/main/src/ListItem.ts +++ b/packages/main/src/ListItem.ts @@ -266,6 +266,7 @@ abstract class ListItem extends ListItemBase { const activeElement = getActiveElement(); const focusDomRef = this.getFocusDomRef()!; + console.error(activeElement, focusDomRef); if (activeElement === focusDomRef) { const firstFocusable = await getFirstFocusableElement(focusDomRef); firstFocusable?.focus(); diff --git a/packages/main/src/ListItemBase.ts b/packages/main/src/ListItemBase.ts index 2ec1438a3950..d66da0c404dd 100644 --- a/packages/main/src/ListItemBase.ts +++ b/packages/main/src/ListItemBase.ts @@ -5,7 +5,6 @@ import property from "@ui5/webcomponents-base/dist/decorators/property.js"; import event from "@ui5/webcomponents-base/dist/decorators/event.js"; import type { ITabbable } from "@ui5/webcomponents-base/dist/delegate/ItemNavigation.js"; import type { ClassMap } from "@ui5/webcomponents-base/dist/types.js"; -import { getTabbableElements } from "@ui5/webcomponents-base/dist/util/TabbableElements.js"; import { isDesktop } from "@ui5/webcomponents-base/dist/Device.js"; import { isEnter, @@ -13,7 +12,6 @@ import { isTabNext, isTabPrevious, } from "@ui5/webcomponents-base/dist/Keys.js"; -import getActiveElement from "@ui5/webcomponents-base/dist/util/getActiveElement.js"; import { getEventMark } from "@ui5/webcomponents-base/dist/MarkedEvents.js"; // Styles @@ -165,29 +163,13 @@ class ListItemBase extends UI5Element implements ITabbable { } _handleTabNext(e: KeyboardEvent) { - if (this.shouldForwardTabAfter()) { - if (!this.fireEvent("_forward-after", {}, true)) { - e.preventDefault(); - } + if (!this.fireEvent("_forward-after", { item: this, target: e.target }, true)) { + e.preventDefault(); } } _handleTabPrevious(e: KeyboardEvent) { - const target = e.target as HTMLElement; - - if (this.shouldForwardTabBefore(target)) { - this.fireEvent("_forward-before"); - } - } - - /** - * Determines if th current list item either has no tabbable content or - * [Tab] is performed onto the last tabbale content item. - */ - shouldForwardTabAfter() { - const aContent = getTabbableElements(this.getFocusDomRef()!); - - return aContent.length === 0 || (aContent[aContent.length - 1] === getActiveElement()); + this.fireEvent("_forward-before", { item: this, target: e.target }); } /** From 0915a16cd1c242a9d2282bd1714fcb2ba8adad62 Mon Sep 17 00:00:00 2001 From: Dobrin Dimchev Date: Thu, 19 Sep 2024 13:27:22 +0300 Subject: [PATCH 2/5] fix: add f7 as well --- packages/main/src/ListItem.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/main/src/ListItem.ts b/packages/main/src/ListItem.ts index bd7c4bff03f3..8ae86645e159 100644 --- a/packages/main/src/ListItem.ts +++ b/packages/main/src/ListItem.ts @@ -1,7 +1,7 @@ import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import { getEventMark } from "@ui5/webcomponents-base/dist/MarkedEvents.js"; import { - isSpace, isEnter, isDelete, isF2, + isSpace, isEnter, isDelete, isF2, isF7, } from "@ui5/webcomponents-base/dist/Keys.js"; import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; import { getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js"; @@ -262,11 +262,10 @@ abstract class ListItem extends ListItemBase { this.activate(); } - if (isF2(e)) { + if (isF2(e) || isF7(e)) { const activeElement = getActiveElement(); const focusDomRef = this.getFocusDomRef()!; - console.error(activeElement, focusDomRef); if (activeElement === focusDomRef) { const firstFocusable = await getFirstFocusableElement(focusDomRef); firstFocusable?.focus(); From 7f5167f83b06ebb4d66a74de308451ec8ac51af5 Mon Sep 17 00:00:00 2001 From: Dobrin Dimchev Date: Thu, 19 Sep 2024 13:28:40 +0300 Subject: [PATCH 3/5] fix: remove test code --- packages/main/src/List.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/main/src/List.ts b/packages/main/src/List.ts index 28cbfeac6980..75e263d74f42 100644 --- a/packages/main/src/List.ts +++ b/packages/main/src/List.ts @@ -893,12 +893,7 @@ class List extends UI5Element { _onkeydown(e: KeyboardEvent) { if (isCtrl(e)) { this._moveItem(e.target as ListItemBase, e); - return; } - - // if (isTabNext(e)) { - // this._handleTabNext(e); - // } } _moveItem(item: ListItemBase, e: KeyboardEvent) { From 71b8e53bee009539e1bfe255170b5db4dab3f217 Mon Sep 17 00:00:00 2001 From: Dobrin Dimchev Date: Thu, 19 Sep 2024 13:41:46 +0300 Subject: [PATCH 4/5] fix: remove effective tab index --- packages/main/src/List.ts | 2 -- packages/main/src/ListItemBase.ts | 8 +------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/main/src/List.ts b/packages/main/src/List.ts index 75e263d74f42..7c76e5fb096f 100644 --- a/packages/main/src/List.ts +++ b/packages/main/src/List.ts @@ -546,8 +546,6 @@ class List extends UI5Element { this._handleResize = this.checkListInViewport.bind(this); - this._handleResize = this.checkListInViewport.bind(this); - // Indicates the List bottom most part has been detected by the IntersectionObserver // for the first time. this.initialIntersection = true; diff --git a/packages/main/src/ListItemBase.ts b/packages/main/src/ListItemBase.ts index d66da0c404dd..49d444b3ec77 100644 --- a/packages/main/src/ListItemBase.ts +++ b/packages/main/src/ListItemBase.ts @@ -205,13 +205,7 @@ class ListItemBase extends UI5Element implements ITabbable { } get _effectiveTabIndex() { - if (!this._focusable) { - return -1; - } - if (this.selected) { - return 0; - } - return this.forcedTabIndex; + return -1; } } From a93b89590cccb59ec460f9cde27ff753aba8492f Mon Sep 17 00:00:00 2001 From: Dobrin Dimchev Date: Thu, 19 Sep 2024 15:50:49 +0300 Subject: [PATCH 5/5] fix: allow tab "forms" navigation --- packages/main/src/List.ts | 20 +- packages/main/test/pages/List.html | 663 +---------------------------- 2 files changed, 20 insertions(+), 663 deletions(-) diff --git a/packages/main/src/List.ts b/packages/main/src/List.ts index 7c76e5fb096f..7ea19665be15 100644 --- a/packages/main/src/List.ts +++ b/packages/main/src/List.ts @@ -28,6 +28,8 @@ import debounce from "@ui5/webcomponents-base/dist/util/debounce.js"; import isElementInView from "@ui5/webcomponents-base/dist/util/isElementInView.js"; import Orientation from "@ui5/webcomponents-base/dist/types/Orientation.js"; import MovePlacement from "@ui5/webcomponents-base/dist/types/MovePlacement.js"; +import { getTabbableElements } from "@ui5/webcomponents-base/dist/util/TabbableElements.js"; +import getActiveElement from "@ui5/webcomponents-base/dist/util/getActiveElement.js"; import ListSelectionMode from "./types/ListSelectionMode.js"; import ListGrowingMode from "./types/ListGrowingMode.js"; import type ListAccessibleRole from "./types/ListAccessibleRole.js"; @@ -1186,26 +1188,28 @@ class List extends UI5Element { } onForwardBefore(e: CustomEvent) { + const activeElement = getActiveElement() as HTMLElement; const item = e.detail.item as ListItemBase; - const target = getNormalizedTarget(e.target as HTMLElement); + const isFirstItem = this.getItems().indexOf(item) === 0; + const isFirstTabbable = getTabbableElements(item).shift() === getActiveElement(); + const isItemFocused = item.getFocusDomRef() === activeElement; this.setPreviouslyFocusedItem(e.target as ListItemBase); - if (target !== item.getFocusDomRef()) { - return; + if (isItemFocused && (isFirstItem && !isFirstTabbable)) { + this.focusBeforeElement(); + e.stopPropagation(); } - - this.focusBeforeElement(); - e.stopPropagation(); } onForwardAfter(e: CustomEvent) { const item = e.detail.item as ListItemBase; - const target = getNormalizedTarget(e.target as HTMLElement); + const isLastItem = this.getItems().indexOf(item) === this.getItems().length - 1; + const isLastTabbable = isLastItem && getTabbableElements(item).pop() === getActiveElement(); this.setPreviouslyFocusedItem(item); - if (target !== item.getFocusDomRef()) { + if (!isLastTabbable) { return; } diff --git a/packages/main/test/pages/List.html b/packages/main/test/pages/List.html index 339f1499df55..1629425f7c8b 100644 --- a/packages/main/test/pages/List.html +++ b/packages/main/test/pages/List.html @@ -20,674 +20,27 @@ - List growing="Scroll" - - - - - - Voluptate do eu cupidatat elit est culpa. Reprehenderit eiusmod voluptate ex est dolor nostrud Lorem Lorem do nisi laborum veniam. Sint do non culpa aute occaecat labore ipsum veniam minim tempor est. Duis pariatur aute culpa irure ad excepteur pariatur culpa culpa ea duis occaecat aute irure. Ipsum velit culpa non exercitation ex laboris deserunt in eu non officia in. Laborum sunt aliqua labore cupidatat sunt labore. - - Woman image - - - Laptop Lenovo - - Woman image - - - IPhone 3 - - Woman image - - - - - - HP Monitor 24 - - Woman image - - - Audio cabel - - Woman image - - - DVD set - - Woman image - - - - - - HP Monitor 24 - - Woman image - - - Audio cabel - - Woman image - - - DVD set - - Woman image - - - - - -

- -

List growing="Button" and growing-button-text property used

- - - Voluptate do eu cupidatat elit est culpa. Reprehenderit eiusmod voluptate ex est dolor nostrud Lorem Lorem do nisi laborum veniam. Sint do non culpa aute occaecat labore ipsum veniam minim tempor est. Duis pariatur aute culpa irure ad excepteur pariatur culpa culpa ea duis occaecat aute irure. Ipsum velit culpa non exercitation ex laboris deserunt in eu non officia in. Laborum sunt aliqua labore cupidatat sunt labore. - - Woman image - - - Laptop Lenovo - - Woman image - - - IPhone 3 - - Woman image - - - - - - -

- -

List with img tag

- - - Voluptate do eu cupidatat elit est culpa. Reprehenderit eiusmod voluptate ex est dolor nostrud Lorem Lorem do nisi laborum veniam. Sint do non culpa aute occaecat labore ipsum veniam minim tempor est. Duis pariatur aute culpa irure ad excepteur pariatur culpa culpa ea duis occaecat aute irure. Ipsum velit culpa non exercitation ex laboris deserunt in eu non officia in. Laborum sunt aliqua labore cupidatat sunt labore. - Woman image - - Laptop Lenovo - Woman image - - IPhone 3 - Woman image - - - - -

- -

ui5-list

- - - - Voluptate do eu cupidatat elit est culpa. Reprehenderit eiusmod voluptate ex est dolor nostrud Lorem Lorem do nisi laborum veniam. Sint do non culpa aute occaecat labore ipsum veniam minim tempor est. Duis pariatur aute culpa irure ad excepteur pariatur culpa culpa ea duis occaecat aute irure. Ipsum velit culpa non exercitation ex laboris deserunt in eu non officia in. Laborum sunt aliqua labore cupidatat sunt labore. - - Woman image - - - Laptop Lenovo - - Woman image - - - IPhone 3 - - Woman image - - - - - - HP Monitor 24 - - Woman image - - - Audio cabel - - Woman image - - - DVD set - - Woman image - - - - - - HP Monitor 24 - - Woman image - - - Audio cabel - - Woman image - - - DVD set - - Woman image - - - - - -

- - - Option 1 - - -

-
-

- - - Laptop HP - - Woman image - - - laptop Lenovo - - Woman image - - - IPhone 3 - - Woman image - - - - -

- - - Avatar inside image slot -
- -
-
- Avatar inside image slot -
- - Woman image - -
-
-
- -

- - - Active item - press - Active item - press - Inactive item - Inactive item - Detail item - - -

- - - Inactive item - Inactive item - - -

- - - Inactive item - Inactive item - - -

- - - Navigation item - Navigation item - -
- -

- - - Navigated item - Navigated item - -
- -

- - - Argentina - Bulgaria - China - - -

- - [prevented Event] itemClick :: n/a - - - Argentina - Bulgaria - China - - -

- - [Event] itemPress :: n/a -

- [Event] itemClick :: n/a -

- [Event] selectionChange :: n/a - - Argentina - Bulgaria - China - Italy - Vietnam - - -

- - - Dolor dolor ipsum culpa proident esse quis quis incididunt. Sunt duis eu ad deserunt dolor sunt Lorem incididunt est irure qui dolore minim consectetur. Voluptate minim veniam aliqua aute et consectetur magna commodo sit. Duis fugiat esse culpa ea velit sit excepteur duis deserunt aliquip minim laborum. Amet aliquip excepteur esse aute ut dolore labore. - Bulgaria - China - - -

- - [Event] itemPress :: n/a -

- [Event] selectionChange :: n/a - - Argentina - Bulgaria - China - - -

-
-

Items 3/3

- Reset List -
- - - Argentina - Bulgaria - China - Detail item - Denmark -
- Custom Delete Button -
-
-
- -

- - - Laptop HP - - Woman image - - - laptop Lenovo - - Woman image - - - IPhone 3 - - Woman image - - - - -

- - - Laptop HP - - Woman image - - - laptop Lenovo - - Woman image - - - IPhone 3 - - Woman image - - - DVD set - - Woman image - - - - -

- - - +
- Press me - Go to SAP + Press me + Go to SAP
- Press me - Go to SAP + Press me + Go to SAP
- Press me - Go to SAP + Press me + Go to SAP
- - - Option 1 - Option 2 - Option 3 - -

- - Option 1 - Option 2 - Option 3 - - - - -
- Export -
- - - Argentina - - Click me - UI5 link - - Click me - - China - - - -
- - - Header button - Option 1 - Option 2 - Option 3 - - -
- Change selectionMode: - - None - Single - SingleStart - SingleEnd - Multiple - Delete - - - -
- Press me - Go to SAP -
-
- Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. - - Woman image - - - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. - - Woman image - - - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. - - Woman image - - - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. - - Woman image - - -
- -
- -
- -
- - + \ No newline at end of file