Skip to content

Commit

Permalink
feat(ui5-timeline-item): introduce state property (#10277)
Browse files Browse the repository at this point in the history
Introducing **`state`** property into the `<ui5-timeline-item>` web component.

The `state` property allows you to set the semantic state of the icon using the following values: `Information`, `Positive`, `Critical`, `Negative` or `None`.
Each state automatically changes the icon's color to visually represent the corresponding state, improving clarity and consistency.
The `state` is also accessible. Screen readers will read the text, ensuring that users with assistive technologies can understand the semantic meaning.
  • Loading branch information
hinzzx authored Jan 20, 2025
1 parent f6d6c67 commit b378604
Show file tree
Hide file tree
Showing 13 changed files with 216 additions and 15 deletions.
55 changes: 55 additions & 0 deletions packages/fiori/cypress/specs/Timeline.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { html } from "lit";
import "../../src/Timeline.js";
import "../../src/TimelineItem.js";
import "@ui5/webcomponents-icons/dist/accept.js";
import "@ui5/webcomponents-icons/dist/message-information.js";
import "@ui5/webcomponents-icons/dist/decline.js";
import "@ui5/webcomponents-icons/dist/message-warning.js";

describe("Accessibility", () => {
beforeEach(() => {
cy.mount(html`
<ui5-timeline id="test-timeline">
<ui5-timeline-group-item group-name="Build">
<ui5-timeline-item
id="item1"
title="Compile"
subtitle="Testing suite A"
icon="sap-icon://accept"
name="Testing suite A"
state="Positive"
>
Compilation succeeded.
</ui5-timeline-item>
<ui5-timeline-item
id="item2"
title="Lint"
subtitle="Testing suite B"
icon="sap-icon://message-information"
name="Testing suite B"
>
Lint completed with minor issues.
</ui5-timeline-item>
</ui5-timeline-group-item>
</ui5-timeline>
`);

cy.get("#test-timeline").as("timeline");
});

it("item with state attribute has aria-description, item without state does not", () => {
cy.get(`ui5-timeline-item[state="Positive"]`).each($itemWithState => {
cy.wrap($itemWithState)
.shadow()
.find(".ui5-tli-bubble")
.should("have.attr", "aria-description");
});

cy.get(`ui5-timeline-item:not([state="Positive"])`).each($itemWithoutState => {
cy.wrap($itemWithoutState)
.shadow()
.find(".ui5-tli-bubble")
.should("not.have.attr", "aria-description");
});
});
});
11 changes: 1 addition & 10 deletions packages/fiori/src/TimelineGroupItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";
import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js";
import TimelineLayout from "./types/TimelineLayout.js";
import type TimelineLayout from "./types/TimelineLayout.js";
import type { ITimelineItem } from "./Timeline.js";

import TimelineGroupItemTemplate from "./TimelineGroupItemTemplate.js";

// Styles
Expand Down Expand Up @@ -162,14 +161,6 @@ class TimelineGroupItem extends UI5Element implements ITimelineItem {
get _groupName() {
return this.groupName;
}

get _groupItemIcon() {
if (this.layout === TimelineLayout.Vertical) {
return this.collapsed ? "slim-arrow-left" : "slim-arrow-down";
}

return this.collapsed ? "slim-arrow-up" : "slim-arrow-right";
}
}

TimelineGroupItem.define();
Expand Down
15 changes: 14 additions & 1 deletion packages/fiori/src/TimelineGroupItemTemplate.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import type TimelineGroupItem from "./TimelineGroupItem.js";
import TimelineLayout from "./types/TimelineLayout.js";
import ToggleButton from "@ui5/webcomponents/dist/ToggleButton.js";
import slimArrowleft from "@ui5/webcomponents-icons/dist/slim-arrow-left.js";
import slimArrowRight from "@ui5/webcomponents-icons/dist/slim-arrow-right.js";
import slimArrowDown from "@ui5/webcomponents-icons/dist/slim-arrow-down.js";
import slimArrowup from "@ui5/webcomponents-icons/dist/slim-arrow-up.js";

export default function TimelineGroupItemTemplate(this: TimelineGroupItem) {
return (
Expand All @@ -15,7 +20,7 @@ export default function TimelineGroupItemTemplate(this: TimelineGroupItem) {

<ToggleButton
class="ui5-tlgi-btn"
icon={this._groupItemIcon}
icon={getEffectiveGroupIcon.call(this, this.layout, this.collapsed)}
pressed={this.collapsed}
onClick={this.onGroupItemClick}
>
Expand All @@ -32,3 +37,11 @@ export default function TimelineGroupItemTemplate(this: TimelineGroupItem) {
</div>
);
}

function getEffectiveGroupIcon(layout: `${TimelineLayout}`, collapsed: boolean): string {
if (layout === TimelineLayout.Vertical) {
return collapsed ? slimArrowleft : slimArrowDown;
}

return collapsed ? slimArrowup : slimArrowRight;
}
37 changes: 37 additions & 0 deletions packages/fiori/src/TimelineItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,23 @@ import customElement from "@ui5/webcomponents-base/dist/decorators/customElement
import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";
import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js";
import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js";
import type Link from "@ui5/webcomponents/dist/Link.js";
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
import type { I18nText } from "@ui5/webcomponents-base/dist/i18nBundle.js";
import type { ITimelineItem } from "./Timeline.js";
import type ValueState from "@ui5/webcomponents-base/dist/types/ValueState.js";
import TimelineItemTemplate from "./TimelineItemTemplate.js";
import type TimelineLayout from "./types/TimelineLayout.js";

import {
TIMELINE_ITEM_INFORMATION_STATE_TEXT,
TIMELINE_ITEM_POSITIVE_STATE_TEXT,
TIMELINE_ITEM_NEGATIVE_STATE_TEXT,
TIMELINE_ITEM_CRITICAL_STATE_TEXT,
} from "./generated/i18n/i18n-defaults.js";

// Styles
import TimelineItemCss from "./generated/themes/TimelineItem.css.js";

Expand Down Expand Up @@ -86,6 +98,15 @@ class TimelineItem extends UI5Element implements ITimelineItem {
@property()
subtitleText?: string;

/**
* Defines the state of the icon displayed in the `ui5-timeline-item`.
* @default "None"
* @public
* @since 2.7.0
*/
@property()
state: `${ValueState}` = "None";

/**
* Defines the content of the `ui5-timeline-item`.
* @public
Expand Down Expand Up @@ -149,6 +170,9 @@ class TimelineItem extends UI5Element implements ITimelineItem {
@property({ type: Number })
positionInGroup?: number;

@i18n("@ui5/webcomponents")
static i18nBundle: I18nBundle;

constructor() {
super();
}
Expand All @@ -164,6 +188,19 @@ class TimelineItem extends UI5Element implements ITimelineItem {
this.shadowRoot!.querySelector<Link>("[ui5-link]")?.focus();
}

static typeTextMappings(): Record<string, I18nText> {
return {
"Information": TIMELINE_ITEM_INFORMATION_STATE_TEXT,
"Positive": TIMELINE_ITEM_POSITIVE_STATE_TEXT,
"Negative": TIMELINE_ITEM_NEGATIVE_STATE_TEXT,
"Critical": TIMELINE_ITEM_CRITICAL_STATE_TEXT,
};
}

get timelineItemStateText() {
return this.state !== "None" ? TimelineItem.i18nBundle.getText(TimelineItem.typeTextMappings()[this.state]) : undefined;
}

get isGroupItem() {
return false;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/fiori/src/TimelineItemTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default function TimelineItemTemplate(this: TimelineItem) {
<div class="ui5-tli-icon-outer">
{
this.icon ?
<Icon class="ui5-tli-icon" name={this.icon}/>
<Icon class="ui5-tli-icon" name={this.icon} mode="Decorative"/>
:
<div class="ui5-tli-dummy-icon-container"></div>
}
Expand All @@ -28,6 +28,7 @@ export default function TimelineItemTemplate(this: TimelineItem) {
data-sap-focus-ref
class="ui5-tli-bubble"
tabindex={parseInt(this.forcedTabIndex)}
aria-description={this.timelineItemStateText}
>
<div class="ui5-tli-title">
{ this.name && name.call(this) }
Expand Down
6 changes: 6 additions & 0 deletions packages/fiori/src/i18n/messagebundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ NOTIFICATION_LIST_GROUP_EXPANDED=Expanded
#XACT: ARIA announcement for timeline label
TIMELINE_ARIA_LABEL=Timeline

#XACT: ARIA announcement for the TimelineItem state
TIMELINE_ITEM_INFORMATION_STATE_TEXT=Information State
TIMELINE_ITEM_POSITIVE_STATE_TEXT=Positive State
TIMELINE_ITEM_NEGATIVE_STATE_TEXT=Negative State
TIMELINE_ITEM_CRITICAL_STATE_TEXT=Critical State

#XBUT: Button text for cancel button in the UploadCollectionItem
UPLOADCOLLECTIONITEM_CANCELBUTTON_TEXT=Cancel

Expand Down
16 changes: 16 additions & 0 deletions packages/fiori/src/themes/TimelineItem.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,22 @@
inset-inline-start: 50%;
}

:host([state="Positive"]) .ui5-tli-icon {
color: var(--sapPositiveElementColor);
}

:host([state="Negative"]) .ui5-tli-icon {
color: var(--sapNegativeElementColor);
}

:host([state="Critical"]) .ui5-tli-icon {
color: var(--sapCriticalElementColor);
}

:host([state="Information"]) .ui5-tli-icon {
color: var(--sapInformativeElementColor);
}

:host(:not([icon])[layout="Vertical"]) .ui5-tli-indicator::before {
inset-block-start: 1.75rem;
}
Expand Down
27 changes: 27 additions & 0 deletions packages/fiori/test/pages/Timeline.html
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,33 @@ <h2>Advanced Timeline - Horizontal With Groups and Diverse Components</h2>
</ui5-timeline>
</div>
</section>

<section style="width: 100%;">
<h2>Timeline with Various Timeline Item States</h2>
<div style="width: 50%; margin: 1rem;">
<ui5-timeline id="test-timeline">
<ui5-timeline-group-item group-name="Build">
<ui5-timeline-item title="Compile" subtitle="Testing suite A" icon="sap-icon://accept" name="Testing suite A" state="Positive">
Compilation succeeded.
</ui5-timeline-item>
<ui5-timeline-item title="Lint" subtitle="Testing suite B" icon="sap-icon://message-information" name="Testing suite B" state="Information">
Lint completed with minor issues.
</ui5-timeline-item>
</ui5-timeline-group-item>
<ui5-timeline-group-item group-name="Test">
<ui5-timeline-item title="Unit Test" subtitle="Testing suite C" icon="sap-icon://decline" name="Testing suite C" state="Negative">
Unit tests failed.
</ui5-timeline-item>
<ui5-timeline-item title="Integration Test" subtitle="Testing suite D" icon="sap-icon://message-warning" name="Testing suite D" state="Critical">
Integration tests have warnings.
</ui5-timeline-item>
<ui5-timeline-item title="E2E Test" subtitle="Testing suite E" icon="sap-icon://accept" name="Testing suite E" state="Positive">
End-to-end tests passed.
</ui5-timeline-item>
</ui5-timeline-group-item>
</ui5-timeline>
</div>
</section>
</div>

<script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Basic from "../../../_samples/fiori/Timeline/Basic/Basic.md";
import Horizontal from "../../../_samples/fiori/Timeline/Horizontal/Horizontal.md";
import InCard from "../../../_samples/fiori/Timeline/InCard/InCard.md";
import WithGroups from "../../../_samples/fiori/Timeline/WithGroups/WithGroups.md";
import WithState from "../../../_samples/fiori/Timeline/WithState/WithState.md";

<%COMPONENT_OVERVIEW%>

Expand All @@ -15,6 +16,9 @@ import WithGroups from "../../../_samples/fiori/Timeline/WithGroups/WithGroups.m
### Horizontal layout
<Horizontal />

### Timeline with State

<WithState />

### Timeline in Card

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,4 @@ import "@ui5/webcomponents-fiori/dist/TimelineItem.js";
import "@ui5/webcomponents/dist/Avatar.js";
import "@ui5/webcomponents/dist/Label.js";
import "@ui5/webcomponents-icons/dist/phone.js";
import "@ui5/webcomponents-icons/dist/calendar.js";
import "@ui5/webcomponents-icons/dist/slim-arrow-down.js";
import "@ui5/webcomponents-icons/dist/slim-arrow-left.js";
import "@ui5/webcomponents-icons/dist/calendar.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import html from '!!raw-loader!./sample.html';
import js from '!!raw-loader!./main.js';

<Editor html={html} js={js} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import "@ui5/webcomponents/dist/Label.js";

import "@ui5/webcomponents-fiori/dist/Timeline.js";
import "@ui5/webcomponents-fiori/dist/TimelineItem.js";

import "@ui5/webcomponents-icons/dist/message-information.js";
import "@ui5/webcomponents-icons/dist/decline.js";
import "@ui5/webcomponents-icons/dist/message-warning.js";
import "@ui5/webcomponents-icons/dist/accept.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!-- playground-fold -->
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sample</title>
</head>

<body style="background-color: var(--sapBackgroundColor); padding: 2rem;">
<!-- playground-fold-end -->

<ui5-timeline id="test-timeline">
<ui5-timeline-group-item group-name="Build">
<ui5-timeline-item title="Compile" subtitle="Testing suite A" icon="sap-icon://accept" name="Testing suite A" state="Positive">
Compilation succeeded.
</ui5-timeline-item>
<ui5-timeline-item title="Lint" subtitle="Testing suite B" icon="sap-icon://message-information" name="Testing suite B" state="Information">
Lint completed with minor issues.
</ui5-timeline-item>
</ui5-timeline-group-item>
<ui5-timeline-group-item group-name="Test">
<ui5-timeline-item title="Unit Test" subtitle="Testing suite C" icon="sap-icon://decline" name="Testing suite C" state="Negative">
Unit tests failed.
</ui5-timeline-item>
<ui5-timeline-item title="Integration Test" subtitle="Testing suite D" icon="sap-icon://message-warning" name="Testing suite D" state="Critical">
Integration tests have warnings.
</ui5-timeline-item>
<ui5-timeline-item title="E2E Test" subtitle="Testing suite E" icon="sap-icon://accept" name="Testing suite E" state="Positive">
End-to-end tests passed.
</ui5-timeline-item>
</ui5-timeline-group-item>
</ui5-timeline>
<!-- playground-fold -->
<script type="module" src="main.js"></script>
</body>

</html>
<!-- playground-fold-end -->

0 comments on commit b378604

Please sign in to comment.