Skip to content

Commit bdb43dc

Browse files
authored
feat(ui5-timeline): introduce header-bar slot (#13155)
## Overview Introducing a `header-bar` slot to `ui5-timeline` with a new `ui5-timeline-header-bar` component that provides search, filter, and sort controls. The Timeline itself remains a pure renderer — filtering and sorting are the application's responsibility via events. ## What We Did - Added `ui5-timeline-header-bar` component with search input, sort toggle, and filter dialog - Added `ui5-timeline-filter-option` component for declarative filter choices - Added `TimelineSortOrder` enum (`Ascending` / `Descending`) - Added `headerBar` slot to `Timeline` and forwarded `search`, `filter`, `sort` events - Updated `Timeline.css` to use flexbox layout for the header-bar + content split - Updated existing Cypress tests to use attribute selectors (`[ui5-timeline-item]`) and `realClick()` - Added new Cypress tests covering search, filter, sort, and header-bar rendering - Added three website samples: WithHeaderBar, WithSearch, WithFilter - Registered new components in ReactPlayground and Monaco types - Added i18n keys for all accessible names, tooltips, and dialog labels ## What This Adds - **Search** — text input fires `search` event on every keystroke; app filters items in the DOM - **Filter** — dialog with multi-select list fires `filter` event with selected option texts; empty selection = show all - **Sort** — toggle button fires `sort` event with `Ascending` or `Descending`; sorting is always active - **Accessibility** — all controls have `accessibleName`/tooltips via i18n;
1 parent 506e4e8 commit bdb43dc

33 files changed

Lines changed: 4899 additions & 48 deletions

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

Lines changed: 331 additions & 30 deletions
Large diffs are not rendered by default.

packages/fiori/src/Timeline.ts

Lines changed: 95 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
2-
import type { DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js";
2+
import type { DefaultSlot, Slot } from "@ui5/webcomponents-base/dist/UI5Element.js";
33
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
44
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
55
import slot from "@ui5/webcomponents-base/dist/decorators/slot-strict.js";
@@ -21,7 +21,10 @@ import type ToggleButton from "@ui5/webcomponents/dist/ToggleButton.js";
2121
import "./TimelineItem.js";
2222
import ItemNavigation from "@ui5/webcomponents-base/dist/delegate/ItemNavigation.js";
2323
import NavigationMode from "@ui5/webcomponents-base/dist/types/NavigationMode.js";
24-
import { TIMELINE_ARIA_LABEL, TIMELINE_LOAD_MORE_BUTTON_TEXT } from "./generated/i18n/i18n-defaults.js";
24+
import {
25+
TIMELINE_ARIA_LABEL,
26+
TIMELINE_LOAD_MORE_BUTTON_TEXT,
27+
} from "./generated/i18n/i18n-defaults.js";
2528
import TimelineTemplate from "./TimelineTemplate.js";
2629
import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
2730
import debounce from "@ui5/webcomponents-base/dist/util/debounce.js";
@@ -35,6 +38,8 @@ import TimelineLayout from "./types/TimelineLayout.js";
3538
import TimelineGrowingMode from "./types/TimelineGrowingMode.js";
3639
import { getFirstFocusableElement } from "@ui5/webcomponents-base/dist/util/FocusableElements.js";
3740
import getActiveElement from "@ui5/webcomponents-base/dist/util/getActiveElement.js";
41+
import type TimelineHeaderBar from "./TimelineHeaderBar.js";
42+
import type { TimelineHeaderBarSearchEventDetail, TimelineHeaderBarFilterEventDetail, TimelineHeaderBarSortEventDetail } from "./TimelineHeaderBar.js";
3843

3944
/**
4045
* Interface for components that may be slotted inside `ui5-timeline` as items
@@ -54,8 +59,17 @@ interface ITimelineItem extends UI5Element, ITabbable {
5459
isNextItemGroup?: boolean;
5560
firstItemInTimeline?: boolean;
5661
effectiveRole?: string;
62+
titleText?: string;
63+
name?: string;
64+
subtitleText?: string;
5765
}
5866

67+
type TimelineSearchEventDetail = TimelineHeaderBarSearchEventDetail;
68+
69+
type TimelineFilterEventDetail = TimelineHeaderBarFilterEventDetail;
70+
71+
type TimelineSortEventDetail = TimelineHeaderBarSortEventDetail;
72+
5973
const SHORT_LINE_WIDTH = "ShortLineWidth";
6074
const LARGE_LINE_WIDTH = "LargeLineWidth";
6175
const GROWING_WITH_SCROLL_DEBOUNCE_RATE = 250; // ms
@@ -70,6 +84,15 @@ const GROWING_WITH_SCROLL_DEBOUNCE_RATE = 250; // ms
7084
* These entries can be generated by the system (for example, value XY changed from A to B), or added manually.
7185
* There are two distinct variants of the timeline: basic and social. The basic timeline is read-only,
7286
* while the social timeline offers a high level of interaction and collaboration, and is integrated within SAP Jam.
87+
*
88+
* ### Header Bar
89+
*
90+
* The Timeline supports a `header-bar` slot for search, filter, and sort functionality.
91+
* Use the `ui5-timeline-header-bar` component in this slot.
92+
* The Timeline fires `search`, `filter`, and `sort` events that the application should handle
93+
* by adding, removing, or reordering items in the DOM. The Timeline itself does not perform
94+
* filtering or sorting — it renders whatever items are provided in the default slot.
95+
*
7396
* @constructor
7497
* @extends UI5Element
7598
* @public
@@ -94,9 +117,55 @@ const GROWING_WITH_SCROLL_DEBOUNCE_RATE = 250; // ms
94117
bubbles: true,
95118
})
96119

120+
/**
121+
* Fired when the user performs a search in the header bar.
122+
*
123+
* **Note:** The Timeline does not perform filtering. The application should handle
124+
* this event and add/remove items from the DOM to reflect the search results.
125+
*
126+
* @param {string} value The search value entered by the user.
127+
* @public
128+
* @since 2.22.0
129+
*/
130+
@event("search", {
131+
bubbles: true,
132+
})
133+
134+
/**
135+
* Fired when the user changes filter selection in the header bar.
136+
*
137+
* **Note:** The Timeline does not perform filtering. The application should handle
138+
* this event and add/remove items from the DOM to reflect the filter selection.
139+
*
140+
* @param {string} filterBy The filter category.
141+
* @param {string[]} selectedOptions The selected filter option texts.
142+
* @public
143+
* @since 2.22.0
144+
*/
145+
@event("filter", {
146+
bubbles: true,
147+
})
148+
149+
/**
150+
* Fired when the user changes sort order in the header bar.
151+
*
152+
* **Note:** The Timeline does not perform sorting. The application should handle
153+
* this event and reorder the items in the DOM accordingly.
154+
*
155+
* @param {string} sortOrder The sort order ("Ascending" or "Descending").
156+
* @public
157+
* @since 2.22.0
158+
*/
159+
@event("sort", {
160+
bubbles: true,
161+
})
162+
97163
class Timeline extends UI5Element {
98164
eventDetails!: {
99165
"load-more": void,
166+
"search": TimelineSearchEventDetail,
167+
"filter": TimelineFilterEventDetail,
168+
"sort": TimelineSortEventDetail,
100169
}
101170
/**
102171
* Defines the items orientation.
@@ -167,6 +236,19 @@ class Timeline extends UI5Element {
167236
@slot({ type: HTMLElement, individualSlots: true, "default": true })
168237
items!: DefaultSlot<ITimelineItem>;
169238

239+
/**
240+
* Defines the header bar of the timeline.
241+
* Use `ui5-timeline-header-bar` for filtering, sorting, and search functionality.
242+
*
243+
* **Note:** The Timeline fires `search`, `filter`, and `sort` events when the user interacts
244+
* with the header bar. The application should handle these events to filter/sort the items.
245+
*
246+
* @public
247+
* @since 2.22.0
248+
*/
249+
@slot()
250+
headerBar!: Slot<TimelineHeaderBar>;
251+
170252
@query(".ui5-timeline-end-marker")
171253
timelineEndMarker!: HTMLElement;
172254

@@ -215,6 +297,14 @@ class Timeline extends UI5Element {
215297
return this.growing === TimelineGrowingMode.Button;
216298
}
217299

300+
get _hasHeaderBar(): boolean {
301+
return this.headerBar.length > 0;
302+
}
303+
304+
onExitDOM() {
305+
this.unobserveTimelineEnd();
306+
}
307+
218308
onAfterRendering() {
219309
if (this.growsOnScroll) {
220310
this.observeTimelineEnd();
@@ -225,10 +315,6 @@ class Timeline extends UI5Element {
225315
this.growingIntersectionObserver = this.getIntersectionObserver();
226316
}
227317

228-
onExitDOM() {
229-
this.unobserveTimelineEnd();
230-
}
231-
232318
async observeTimelineEnd() {
233319
if (!this.timeLineEndObserved) {
234320
await renderFinished();
@@ -473,4 +559,7 @@ Timeline.define();
473559
export default Timeline;
474560
export type {
475561
ITimelineItem,
562+
TimelineSearchEventDetail,
563+
TimelineFilterEventDetail,
564+
TimelineSortEventDetail,
476565
};
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
2+
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
3+
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
4+
5+
/**
6+
* @class
7+
*
8+
* ### Overview
9+
*
10+
* The `ui5-timeline-filter-option` component defines individual filter values within a `ui5-timeline-header-bar`.
11+
* It represents a single selectable option that users can choose to filter timeline items.
12+
*
13+
* ### Usage
14+
*
15+
* The `ui5-timeline-filter-option` is used as a child component within `ui5-timeline-header-bar`.
16+
* Each option represents a specific value that can be used for filtering.
17+
*
18+
* ### ES6 Module Import
19+
*
20+
* `import "@ui5/webcomponents-fiori/dist/TimelineFilterOption.js";`
21+
* @constructor
22+
* @extends UI5Element
23+
* @since 2.22.0
24+
* @public
25+
*/
26+
@customElement("ui5-timeline-filter-option")
27+
class TimelineFilterOption extends UI5Element {
28+
/**
29+
* Defines the text of the filter option.
30+
* @default ""
31+
* @public
32+
*/
33+
@property()
34+
text = "";
35+
36+
/**
37+
* Defines if the filter option is selected.
38+
* @default false
39+
* @public
40+
*/
41+
@property({ type: Boolean })
42+
selected = false;
43+
}
44+
45+
TimelineFilterOption.define();
46+
47+
export default TimelineFilterOption;

0 commit comments

Comments
 (0)