Skip to content

Commit 55ad4ae

Browse files
GDamyanovilhan007
authored andcommitted
feat(ui5-timeline): improve keyboard handling (#12021)
ui5-timeline keyboard handling improvements: - press F2 on focused timeline item will move the focus to the first interactive element inside the timeline item - press F2 on focused interactive element inside timeline item will move the focus to the parent timeline item - press Tab on interactive element inside timeline item will move the focus to the next interactive element inside the same timeline item if such element exists otherwise will move the focus to closest interactive element in timeline chain
1 parent c61c67a commit 55ad4ae

File tree

2 files changed

+133
-33
lines changed

2 files changed

+133
-33
lines changed

packages/fiori/cypress/specs/Timeline.cy.tsx

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import messageInformation from "@ui5/webcomponents-icons/dist/message-informatio
77
import Label from "@ui5/webcomponents/dist/Label.js";
88
import Avatar from "@ui5/webcomponents/dist/Avatar.js";
99
import UI5Element from "@ui5/webcomponents-base";
10+
import Button from "@ui5/webcomponents/dist/Button.js";
11+
import Input from "@ui5/webcomponents/dist/Input.js";
1012

1113
function Sample() {
1214
return <Timeline layout="Vertical" accessibleName="vertical" id="timelineAccName">
@@ -133,7 +135,9 @@ describe("Timeline with group items interactions", () => {
133135
cy.get("@currentGroupButton")
134136
.realClick();
135137

136-
cy.realPress("Tab");
138+
cy.realPress("F2");
139+
140+
cy.realPress("ArrowDown");
137141

138142
cy.get("[ui5-timeline]")
139143
.find("[ui5-timeline-group-item][group-name='Meetings']")
@@ -263,6 +267,113 @@ describe("Timeline with growing mode", () => {
263267
});
264268
});
265269

270+
describe("Keyboard interactions", () => {
271+
it("F2 should move the focus to interactive items and then should revert the focus to parent timeline item", () => {
272+
cy.mount(
273+
<Timeline>
274+
<TimelineItem titleText="first item" subtitleText="20.02.2017 11:30" >
275+
<Button id="button" title="Click me"></Button>
276+
</TimelineItem>
277+
<TimelineItem titleText="coming up" subtitleText="20.02.2017 11:30"></TimelineItem>
278+
<TimelineItem titleText="coming up" subtitleText="20.02.2017 11:30" ></TimelineItem>
279+
</Timeline>
280+
);
281+
282+
cy.get("[ui5-timeline]")
283+
.as("timeline");
284+
285+
cy.get("@timeline")
286+
.find("ui5-timeline-item")
287+
.first()
288+
.as("firstItem")
289+
.realClick();
290+
291+
cy.get("@firstItem")
292+
.should("be.focused");
293+
294+
cy.realPress("F2");
295+
296+
cy.get("#button")
297+
.should("be.focused");
298+
299+
cy.realPress("F2");
300+
301+
cy.get("@firstItem")
302+
.should("be.focused");
303+
});
304+
305+
it("should move the focus to the next interactive item inside timeline item when Tab is pressed", () => {
306+
cy.mount(
307+
<Timeline>
308+
<TimelineItem titleText="first item" subtitleText="20.02.2017 11:30" >
309+
<Button id="button1" title="Click me"></Button>
310+
<Button id="button2" title="Click me"></Button>
311+
</TimelineItem>
312+
<TimelineItem titleText="coming up" subtitleText="20.02.2017 11:30"></TimelineItem>
313+
<TimelineItem titleText="coming up" subtitleText="20.02.2017 11:30" ></TimelineItem>
314+
</Timeline>
315+
);
316+
317+
cy.get("[ui5-timeline]")
318+
.as("timeline");
319+
320+
cy.get("@timeline")
321+
.find("ui5-timeline-item")
322+
.first()
323+
.as("firstItem")
324+
.realClick();
325+
326+
cy.get("@firstItem")
327+
.should("be.focused");
328+
329+
cy.realPress("F2");
330+
331+
cy.get("#button1")
332+
.should("be.focused");
333+
334+
cy.realPress("Tab");
335+
336+
cy.get("#button2")
337+
.should("be.focused");
338+
});
339+
340+
it("should move the focus to interactive element inside next timeline item with interactive elements when pressing Tab", () => {
341+
cy.mount(
342+
<Timeline>
343+
<TimelineItem titleText="first item" subtitleText="20.02.2017 11:30" >
344+
<Button id="button1" title="Click me"></Button>
345+
</TimelineItem>
346+
<TimelineItem titleText="coming up" subtitleText="20.02.2017 11:30"></TimelineItem>
347+
<TimelineItem titleText="coming up" subtitleText="20.02.2017 11:30">
348+
<Button id="button2" title="Click me"></Button>
349+
</TimelineItem>
350+
</Timeline>
351+
);
352+
353+
cy.get("[ui5-timeline]")
354+
.as("timeline");
355+
356+
cy.get("@timeline")
357+
.find("ui5-timeline-item")
358+
.first()
359+
.as("firstItem")
360+
.realClick();
361+
362+
cy.get("@firstItem")
363+
.should("be.focused");
364+
365+
cy.realPress("F2");
366+
367+
cy.get("#button1")
368+
.should("be.focused");
369+
370+
cy.realPress("Tab");
371+
372+
cy.get("#button2")
373+
.should("be.focused");
374+
});
375+
});
376+
266377
describe("Accessibility", () => {
267378
beforeEach(() => {
268379
cy.mount(

packages/fiori/src/Timeline.ts

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js";
77
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
88
import { renderFinished } from "@ui5/webcomponents-base/dist/Render.js";
99
import {
10-
isTabNext,
11-
isTabPrevious,
1210
isSpace,
1311
isEnter,
1412
isUp,
1513
isDown,
1614
isLeft,
1715
isRight,
16+
isF2,
1817
} from "@ui5/webcomponents-base/dist/Keys.js";
1918
import type { ITabbable } from "@ui5/webcomponents-base/dist/delegate/ItemNavigation.js";
2019
import type ToggleButton from "@ui5/webcomponents/dist/ToggleButton.js";
@@ -33,6 +32,8 @@ import TimelineCss from "./generated/themes/Timeline.css.js";
3332
import TimelineLayout from "./types/TimelineLayout.js";
3433
// Mode
3534
import TimelineGrowingMode from "./types/TimelineGrowingMode.js";
35+
import { getFirstFocusableElement } from "@ui5/webcomponents-base/dist/util/FocusableElements.js";
36+
import getActiveElement from "@ui5/webcomponents-base/dist/util/getActiveElement.js";
3637

3738
/**
3839
* Interface for components that may be slotted inside `ui5-timeline` as items
@@ -353,8 +354,10 @@ class Timeline extends UI5Element {
353354
}
354355
}
355356

356-
_onkeydown(e: KeyboardEvent) {
357-
const target = e.target as ITimelineItem;
357+
async _onkeydown(e: KeyboardEvent) {
358+
const target = e.target as ITimelineItem,
359+
targetfocusDomRef = target?.getFocusDomRef(),
360+
shouldHandleCustomArrowNavigation = targetfocusDomRef === this.getFocusDomRef() || target === this.growingButton;
358361

359362
if (isDown(e) || isRight(e)) {
360363
this._handleDown();
@@ -368,36 +371,22 @@ class Timeline extends UI5Element {
368371
return;
369372
}
370373

371-
if (target.nameClickable && !target.getFocusDomRef()!.matches(":has(:focus-within)")) {
372-
return;
373-
}
374-
375-
if (isTabNext(e)) {
376-
this._handleNextOrPreviousItem(e, true);
377-
} else if (isTabPrevious(e)) {
378-
this._handleNextOrPreviousItem(e);
379-
}
380-
}
381-
382-
_handleNextOrPreviousItem(e: KeyboardEvent, isNext?: boolean) {
383-
const target = e.target as ITimelineItem | ToggleButton;
384-
let updatedTarget = target;
385-
386-
if ((target as ITimelineItem).isGroupItem) {
387-
updatedTarget = target.shadowRoot!.querySelector<ToggleButton>("[ui5-toggle-button]")!;
388-
}
389-
390-
const nextTargetIndex = isNext ? this._navigableItems.indexOf(updatedTarget) + 1 : this._navigableItems.indexOf(updatedTarget) - 1;
391-
const nextTarget = this._navigableItems[nextTargetIndex];
374+
if (isF2(e)) {
375+
e.stopImmediatePropagation();
376+
const activeElement = getActiveElement();
377+
const focusDomRef = this.getFocusDomRef();
392378

393-
if (!nextTarget) {
394-
return;
395-
}
379+
if (!focusDomRef) {
380+
return;
381+
}
396382

397-
if (nextTarget) {
398-
e.preventDefault();
399-
nextTarget.focus();
400-
this._itemNavigation.setCurrentItem(nextTarget);
383+
if (activeElement === focusDomRef) {
384+
const firstFocusable = await getFirstFocusableElement(focusDomRef);
385+
firstFocusable?.focus();
386+
} else {
387+
const parentItem = (e.target as HTMLElement)?.closest("ui5-timeline-item") as HTMLElement;
388+
parentItem?.focus();
389+
}
401390
}
402391
}
403392

0 commit comments

Comments
 (0)