Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(ui5-list): update focus handling #9073

Merged
merged 8 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/compat/src/themes/GrowingButton.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
box-sizing: border-box;
}

[growing-button-inner]:focus {
[growing-button-inner]:focus-visible {
outline: var(--_ui5_load_more_outline_width) var(--sapContent_FocusStyle) var(--sapContent_FocusColor);
outline-offset: -0.125rem;
border-color: transparent;
Expand Down
1 change: 0 additions & 1 deletion packages/fiori/src/NotificationListGroupItem.hbs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<li
class="ui5-nli-group-root ui5-nli-focusable"
@focusin="{{_onfocusin}}"
@focusout="{{_onfocusout}}"
@keydown="{{_onkeydown}}"
tabindex="{{forcedTabIndex}}"
aria-labelledby="{{ariaLabelledBy}}"
Expand Down
3 changes: 2 additions & 1 deletion packages/fiori/src/NotificationListGroupItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ class NotificationListGroupItem extends NotificationListItemBase {
}

async _onkeydown(e: KeyboardEvent) {
if (!this.focused) {
const isFocused = this.matches(":focus");
if (!isFocused) {
return;
}

Expand Down
1 change: 0 additions & 1 deletion packages/fiori/src/NotificationListItem.hbs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<li
class="{{itemClasses}}"
@focusin="{{_onfocusin}}"
@focusout="{{_onfocusout}}"
@keydown="{{_onkeydown}}"
@keyup="{{_onkeyup}}"
@click="{{_onclick}}"
Expand Down
15 changes: 8 additions & 7 deletions packages/fiori/src/NotificationListItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,12 @@ const ICON_PER_STATUS_DESIGN = {
*/
@event<NotificationListItemCloseEventDetail>("close", {
detail: {
/**
* @public
*/
item: {
type: HTMLElement,
},
/**
* @public
*/
item: {
type: HTMLElement,
},
},
})

Expand Down Expand Up @@ -497,7 +497,8 @@ class NotificationListItem extends NotificationListItemBase {
return;
}

if (this.focused || (!isUp(e) && !isDown(e))) {
const isFocusWithin = this.matches(":focus-within");
if (!isFocusWithin || (!isUp(e) && !isDown(e))) {
return;
}

Expand Down
12 changes: 8 additions & 4 deletions packages/fiori/src/NotificationListItemBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { getTabbableElements } from "@ui5/webcomponents-base/dist/util/TabbableE
import getActiveElement from "@ui5/webcomponents-base/dist/util/getActiveElement.js";
import ListItemBase from "@ui5/webcomponents/dist/ListItemBase.js";
import Integer from "@ui5/webcomponents-base/dist/types/Integer.js";
import { getFirstFocusableElement } from "@ui5/webcomponents-base/dist/util/FocusableElements.js";
import { getEventMark } from "@ui5/webcomponents-base/dist/MarkedEvents.js";
import { getFirstFocusableElement } from "@ui5/webcomponents-base/dist/util/FocusableElements.js";

/**
* @class
Expand Down Expand Up @@ -74,11 +74,15 @@ class NotificationListItemBase extends ListItemBase {

if (isF2(e)) {
e.stopImmediatePropagation();

const activeElement = getActiveElement();
const focusDomRef = this.getHeaderDomRef()!;
if (this.focused) {
(await getFirstFocusableElement(focusDomRef))?.focus(); // start content editing

if (activeElement === focusDomRef) {
const firstFocusable = await getFirstFocusableElement(focusDomRef);
firstFocusable?.focus();
} else {
focusDomRef.focus(); // stop content editing
focusDomRef.focus();
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/fiori/src/themes/NotificationListGroupItem.css
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
}

/* The focus is on the whole group but should be visualized on the Group Header */
:host([focused]) .ui5-nli-focusable.ui5-nli-group-root .ui5-nli-group-header::before {
.ui5-nli-focusable.ui5-nli-group-root:focus .ui5-nli-group-header::before {
content: "";
border: var(--sapContent_FocusWidth) var(--sapContent_FocusStyle) var(--sapContent_FocusColor);
position: absolute;
Expand Down
4 changes: 2 additions & 2 deletions packages/fiori/src/themes/NotificationListItem.css
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
border-radius: var(--_ui5-notification_item-border-radius);
}

:host([focused]) .ui5-nli-root:active {
.ui5-nli-root:focus:active {
background-color: var(--_ui5-notification_item-background-color-active);
border-radius: var(--_ui5-notification_item-border-radius);
border: var(--_ui5-notification_item-border-active);
Expand Down Expand Up @@ -180,7 +180,7 @@
margin-inline-end: 0.125rem;
}

:host([focused]) .ui5-nli-focusable:not(ui5-nli-group-root)::after {
.ui5-nli-focusable:focus:not(.ui5-nli-group-root)::after {
border-radius: var(--_ui5-notification_item-border-radius);
top: var(--_ui5-notification_item-focus-offset);
right: var(--_ui5-notification_item-focus-offset);
Expand Down
4 changes: 2 additions & 2 deletions packages/fiori/src/themes/NotificationListItemBase.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
}

/*TODO after li is implemented*/
:host([focused]) .ui5-nli-focusable {
.ui5-nli-focusable:focus {
outline: none;
}

:host([focused]) .ui5-nli-focusable:not(.ui5-nli-group-root)::after {
.ui5-nli-focusable:focus:not(.ui5-nli-group-root)::after {
content: "";
border: var(--sapContent_FocusWidth) var(--sapContent_FocusStyle) var(--sapContent_FocusColor);
position: absolute;
Expand Down
6 changes: 4 additions & 2 deletions packages/main/src/CustomListItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ class CustomListItem extends ListItem {

async _onkeydown(e: KeyboardEvent) {
const isTab = isTabNext(e) || isTabPrevious(e);
const isFocused = this.matches(":focus");

if (!isTab && !this.focused && !isF2(e)) {
if (!isTab && !isFocused && !isF2(e)) {
return;
}

Expand All @@ -64,8 +65,9 @@ class CustomListItem extends ListItem {

_onkeyup(e: KeyboardEvent) {
const isTab = isTabNext(e) || isTabPrevious(e);
const isFocused = this.matches(":focus");

if (!isTab && !this.focused) {
if (!isTab && !isFocused && !isF2(e)) {
return;
}

Expand Down
22 changes: 15 additions & 7 deletions packages/main/src/ListItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { getEventMark } from "@ui5/webcomponents-base/dist/MarkedEvents.js";
import {
isSpace, isEnter, isDelete, isF2,
} from "@ui5/webcomponents-base/dist/Keys.js";
import { getFirstFocusableElement } from "@ui5/webcomponents-base/dist/util/FocusableElements.js";
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
import { getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
import { isDesktop } from "@ui5/webcomponents-base/dist/Device.js";
import getActiveElement from "@ui5/webcomponents-base/dist/util/getActiveElement.js";
import { getFirstFocusableElement } from "@ui5/webcomponents-base/dist/util/FocusableElements.js";
import type { AccessibilityAttributes, PassiveEventListenerObject } from "@ui5/webcomponents-base/dist/types.js";
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import event from "@ui5/webcomponents-base/dist/decorators/event.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";
Expand Down Expand Up @@ -233,6 +235,10 @@ abstract class ListItem extends ListItemBase {
document.addEventListener("mouseup", this.deactivate);
document.addEventListener("touchend", this.deactivate);
document.addEventListener("keyup", this.deactivateByKey);

if (isDesktop()) {
this.setAttribute("desktop", "");
}
}

onExitDOM() {
Expand All @@ -252,11 +258,14 @@ abstract class ListItem extends ListItemBase {
}

if (isF2(e)) {
const activeElement = getActiveElement();
const focusDomRef = this.getFocusDomRef()!;
if (this.focused) {
(await getFirstFocusableElement(focusDomRef))?.focus(); // start content editing

if (activeElement === focusDomRef) {
const firstFocusable = await getFirstFocusableElement(focusDomRef);
firstFocusable?.focus();
} else {
focusDomRef.focus(); // stop content editing
focusDomRef.focus();
}
}
}
Expand Down Expand Up @@ -292,7 +301,6 @@ abstract class ListItem extends ListItemBase {
}

_onfocusout() {
super._onfocusout();
this.deactivate();
}

Expand All @@ -308,7 +316,7 @@ abstract class ListItem extends ListItemBase {
}
}

/*
/**
* Called when selection components in Single (ui5-radio-button)
* and Multi (ui5-checkbox) selection modes are used.
*/
Expand Down
1 change: 0 additions & 1 deletion packages/main/src/ListItemBase.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
tabindex="{{_effectiveTabIndex}}"
class="{{classes.main}}"
@focusin="{{_onfocusin}}"
@focusout="{{_onfocusout}}"
@keyup="{{_onkeyup}}"
@keydown="{{_onkeydown}}"
draggable="{{movable}}"
Expand Down
19 changes: 7 additions & 12 deletions packages/main/src/ListItemBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,9 @@ class ListItemBase extends UI5Element implements ITabbable {
return;
}

this.focused = true;
this.fireEvent("_focused", e);
}

_onfocusout() {
this.focused = false;
}

_onkeydown(e: KeyboardEvent) {
if (isTabNext(e)) {
return this._handleTabNext(e);
Expand Down Expand Up @@ -175,19 +170,19 @@ class ListItemBase extends UI5Element implements ITabbable {
}
}

/*
* Determines if th current list item either has no tabbable content or
* [Tab] is performed onto the last tabbale content item.
*/
/**
* 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());
}

/*
* Determines if the current list item is target of [SHIFT+TAB].
*/
/**
* Determines if the current list item is target of [SHIFT+TAB].
*/
shouldForwardTabBefore(target: HTMLElement) {
return this.getFocusDomRef() === target;
}
Expand Down
1 change: 0 additions & 1 deletion packages/main/src/ListItemGroupHeader.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
tabindex="{{forcedTabIndex}}"
class="ui5-ghli-root {{classes.main}}"
@focusin="{{_onfocusin}}"
@focusout="{{_onfocusout}}"
@keydown="{{_onkeydown}}"
aria-label="{{ariaLabelText}}"
aria-roledescription="{{groupHeaderText}}"
Expand Down
7 changes: 7 additions & 0 deletions packages/main/src/ListItemGroupHeader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
import { getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
import { isDesktop } from "@ui5/webcomponents-base/dist/Device.js";
import ListItemBase from "./ListItemBase.js";

import { GROUP_HEADER_TEXT } from "./generated/i18n/i18n-defaults.js";
Expand Down Expand Up @@ -60,6 +61,12 @@ class ListItemGroupHeader extends ListItemBase {
static async onDefine() {
ListItemGroupHeader.i18nBundle = await getI18nBundle("@ui5/webcomponents");
}

onEnterDOM() {
if (isDesktop()) {
this.setAttribute("desktop", "");
}
}
}

ListItemGroupHeader.define();
Expand Down
6 changes: 4 additions & 2 deletions packages/main/src/TreeItemCustom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ class TreeItemCustom extends TreeItemBase {

async _onkeydown(e: KeyboardEvent) {
const isTab = isTabNext(e) || isTabPrevious(e);
const isFocused = this.matches(":focus");

if (!isTab && !this.focused && !isF2(e)) {
if (!isTab && !isFocused && !isF2(e)) {
return;
}

Expand All @@ -62,8 +63,9 @@ class TreeItemCustom extends TreeItemBase {

_onkeyup(e: KeyboardEvent) {
const isTab = isTabNext(e) || isTabPrevious(e);
const isFocused = this.matches(":focus");

if (!isTab && !this.focused) {
if (!isTab && !isFocused && !isF2(e)) {
return;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/main/src/themes/GrowingButton.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
box-sizing: border-box;
}

[growing-button-inner]:focus {
[growing-button-inner]:focus-visible {
outline: var(--_ui5_load_more_outline_width) var(--sapContent_FocusStyle) var(--sapContent_FocusColor);
outline-offset: -0.125rem;
border-color: transparent;
Expand Down
12 changes: 6 additions & 6 deletions packages/main/src/themes/ListItem.css
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,12 @@
/* highlight */
.ui5-li-highlight {
position: absolute;
width: 0.375rem;
bottom: 0;
left: 0;
top: 0;
border-inline-end: 0.0625rem solid var(--ui5-listitem-background-color);
box-sizing: border-box;
width: 0.375rem;
bottom: 0;
left: 0;
top: 0;
border-inline-end: 0.0625rem solid var(--ui5-listitem-background-color);
box-sizing: border-box;
}

:host([highlight="Negative"]) .ui5-li-highlight {
Expand Down
Loading