From 37fa20d181df4bef068df363321a88507c4581e2 Mon Sep 17 00:00:00 2001
From: Stoyan
Date: Tue, 19 May 2026 10:28:18 +0300
Subject: [PATCH 1/3] feat(ui5-timeline): [PoC]introduce header and info-bar
slots
---
packages/fiori/cypress/specs/Timeline.cy.tsx | 152 +++++
packages/fiori/src/Timeline.ts | 95 ++-
packages/fiori/src/TimelineTemplate.tsx | 11 +-
packages/fiori/src/themes/Timeline.css | 57 +-
packages/fiori/test/pages/Timeline.html | 178 ++++++
.../test/pages/TimelineHeaderInfoBar.html | 540 ++++++++++++++++++
packages/main/datetimepicker-investigation.md | 207 +++++++
.../fiori/Timeline/Timeline.mdx | 13 +-
.../fiori/Timeline/WithFilter/WithFilter.md | 5 +
.../fiori/Timeline/WithFilter/main.js | 88 +++
.../fiori/Timeline/WithFilter/sample.html | 52 ++
.../fiori/Timeline/WithFilter/sample.tsx | 153 +++++
12 files changed, 1541 insertions(+), 10 deletions(-)
create mode 100644 packages/fiori/test/pages/TimelineHeaderInfoBar.html
create mode 100644 packages/main/datetimepicker-investigation.md
create mode 100644 packages/website/docs/_samples/fiori/Timeline/WithFilter/WithFilter.md
create mode 100644 packages/website/docs/_samples/fiori/Timeline/WithFilter/main.js
create mode 100644 packages/website/docs/_samples/fiori/Timeline/WithFilter/sample.html
create mode 100644 packages/website/docs/_samples/fiori/Timeline/WithFilter/sample.tsx
diff --git a/packages/fiori/cypress/specs/Timeline.cy.tsx b/packages/fiori/cypress/specs/Timeline.cy.tsx
index 0e19b814380b..2d0b536a6736 100644
--- a/packages/fiori/cypress/specs/Timeline.cy.tsx
+++ b/packages/fiori/cypress/specs/Timeline.cy.tsx
@@ -581,4 +581,156 @@ describe("TimelineItem iconTooltip", () => {
});
});
+describe("Timeline header and info-bar slots", () => {
+ it("renders the header slot when content is provided", () => {
+ cy.mount(
+
+
+
+
+ );
+
+ cy.get("[ui5-timeline]")
+ .shadow()
+ .find(".ui5-timeline-header")
+ .should("exist");
+
+ cy.get("#header-content").should("be.visible");
+ });
+
+ it("renders the info-bar slot when content is provided", () => {
+ cy.mount(
+
+ Active filters: 2
+
+
+ );
+
+ cy.get("[ui5-timeline]")
+ .shadow()
+ .find(".ui5-timeline-info-bar")
+ .should("exist");
+
+ cy.get("#info-bar-content").should("be.visible");
+ });
+
+ it("does not render header or info-bar wrappers when slots are empty", () => {
+ cy.mount(
+
+
+
+ );
+
+ cy.get("[ui5-timeline]")
+ .shadow()
+ .find(".ui5-timeline-header")
+ .should("not.exist");
+
+ cy.get("[ui5-timeline]")
+ .shadow()
+ .find(".ui5-timeline-info-bar")
+ .should("not.exist");
+ });
+
+ it("renders both slots side by side when both are provided", () => {
+ cy.mount(
+
+ Search/Filter/Sort
+ Status: 3 items
+
+
+ );
+
+ cy.get("[ui5-timeline]")
+ .shadow()
+ .find(".ui5-timeline-header")
+ .should("exist");
+
+ cy.get("[ui5-timeline]")
+ .shadow()
+ .find(".ui5-timeline-info-bar")
+ .should("exist");
+
+ cy.get("#hdr").should("be.visible");
+ cy.get("#ifb").should("be.visible");
+ });
+
+ it("reflects stickyHeader as the [sticky-header] host attribute and applies sticky positioning", () => {
+ cy.mount(
+
+ Header
+
+
+ );
+
+ cy.get("[ui5-timeline]")
+ .should("have.attr", "sticky-header");
+
+ cy.get("[ui5-timeline]")
+ .shadow()
+ .find(".ui5-timeline-header")
+ .should("have.css", "position", "sticky");
+ });
+
+ it("reflects stickyInfoBar as the [sticky-info-bar] host attribute and applies sticky positioning", () => {
+ cy.mount(
+
+ Info
+
+
+ );
+
+ cy.get("[ui5-timeline]")
+ .should("have.attr", "sticky-info-bar");
+
+ cy.get("[ui5-timeline]")
+ .shadow()
+ .find(".ui5-timeline-info-bar")
+ .should("have.css", "position", "sticky");
+ });
+
+ it("does not apply sticky positioning when stickyHeader is false (default)", () => {
+ cy.mount(
+
+ Header
+
+
+ );
+
+ cy.get("[ui5-timeline]")
+ .should("not.have.attr", "sticky-header");
+
+ cy.get("[ui5-timeline]")
+ .shadow()
+ .find(".ui5-timeline-header")
+ .should("not.have.css", "position", "sticky");
+ });
+
+ it("scrollable defaults to true and the host reflects the [scrollable] attribute", () => {
+ cy.mount(
+
+
+
+ );
+
+ cy.get("[ui5-timeline]")
+ .should("have.attr", "scrollable");
+ });
+
+ it("removes overflow:auto from the scroll container when scrollable is set to false", () => {
+ cy.mount(
+
+
+
+ );
+
+ cy.get("[ui5-timeline]")
+ .should("not.have.attr", "scrollable");
+
+ cy.get("[ui5-timeline]")
+ .shadow()
+ .find(".ui5-timeline-scroll-container")
+ .should("not.have.css", "overflow-y", "auto");
+ });
+});
diff --git a/packages/fiori/src/Timeline.ts b/packages/fiori/src/Timeline.ts
index 77aed3ff5e68..82290010dd49 100644
--- a/packages/fiori/src/Timeline.ts
+++ b/packages/fiori/src/Timeline.ts
@@ -1,5 +1,5 @@
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
-import type { DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js";
+import type { DefaultSlot, Slot } from "@ui5/webcomponents-base/dist/UI5Element.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot-strict.js";
@@ -70,6 +70,23 @@ const GROWING_WITH_SCROLL_DEBOUNCE_RATE = 250; // ms
* These entries can be generated by the system (for example, value XY changed from A to B), or added manually.
* There are two distinct variants of the timeline: basic and social. The basic timeline is read-only,
* while the social timeline offers a high level of interaction and collaboration, and is integrated within SAP Jam.
+ *
+ * ### Header and Info Bar Slots
+ *
+ * The Timeline exposes two named slots above the items area:
+ *
+ * - `header` — for a controls bar (search field, filter trigger, sort toggle, etc.).
+ * The most common pattern is to place a `ui5-bar` containing a search input and buttons that open
+ * a filter dialog or toggle sort direction. The Timeline itself performs no filtering, sorting, or
+ * searching — the application listens for events from its own controls and reorders, hides, or
+ * adds items in the default slot accordingly.
+ *
+ * - `infoBar` — for a status bar that reflects the result of the controls (active filters,
+ * applied sort, current search query). Typically contains tokens, labels, or a `ui5-bar`.
+ *
+ * Either slot can be made sticky using `stickyHeader` and `stickyInfoBar`. Sticky behavior
+ * applies relative to the Timeline's internal scroll container when `scrollable` is set, and
+ * relative to the nearest ancestor scroll container when `scrollable={false}`.
* @constructor
* @extends UI5Element
* @public
@@ -153,6 +170,46 @@ class Timeline extends UI5Element {
@property()
growing: `${TimelineGrowingMode}` = "None";
+ /**
+ * Defines whether the Timeline provides its own scroll container for the items area.
+ *
+ * When `true` (default), the Timeline scrolls internally and any sticky header or info bar
+ * sticks to the top of the Timeline. When `false`, the Timeline does not clip or scroll its
+ * content — the application is expected to provide a scroll container on an ancestor element,
+ * and sticky slots will stick to that ancestor instead.
+ *
+ * **Note:** When the layout is `Horizontal`, items scroll horizontally inside the Timeline
+ * by default. Setting `scrollable={false}` in horizontal layout means the application must
+ * also provide horizontal scrolling on an ancestor; otherwise items will overflow without a
+ * scrollbar.
+ *
+ * @default true
+ * @public
+ * @since 2.22.0
+ */
+ @property({ type: Boolean })
+ scrollable = true;
+
+ /**
+ * Defines whether the content of the `header` slot remains visible when the user scrolls the Timeline.
+ *
+ * @default false
+ * @public
+ * @since 2.22.0
+ */
+ @property({ type: Boolean })
+ stickyHeader = false;
+
+ /**
+ * Defines whether the content of the `infoBar` slot remains visible when the user scrolls the Timeline.
+ *
+ * @default false
+ * @public
+ * @since 2.22.0
+ */
+ @property({ type: Boolean })
+ stickyInfoBar = false;
+
/**
* Defines the active state of the `More` button.
* @private
@@ -167,6 +224,34 @@ class Timeline extends UI5Element {
@slot({ type: HTMLElement, individualSlots: true, "default": true })
items!: DefaultSlot;
+ /**
+ * Defines the content of the Timeline's header area, displayed above the items.
+ *
+ * The most common use case is a controls bar with a search field, sort toggle,
+ * and a filter trigger. Typically a `ui5-bar` is placed in this slot. The Timeline
+ * itself does not filter, sort, or search — the application listens for events from
+ * its own controls and updates the items in the default slot accordingly.
+ *
+ * @public
+ * @since 2.22.0
+ */
+ @slot()
+ header!: Slot;
+
+ /**
+ * Defines the content of the Timeline's info bar area, displayed below the header
+ * and above the items.
+ *
+ * Use this slot to surface state derived from the header controls — for example,
+ * a list of currently applied filters, the active sort direction, or the current
+ * search query.
+ *
+ * @public
+ * @since 2.22.0
+ */
+ @slot()
+ infoBar!: Slot;
+
@query(".ui5-timeline-end-marker")
timelineEndMarker!: HTMLElement;
@@ -195,6 +280,14 @@ class Timeline extends UI5Element {
: Timeline.i18nBundle.getText(TIMELINE_ARIA_LABEL);
}
+ get _hasHeader(): boolean {
+ return this.header.length > 0;
+ }
+
+ get _hasInfoBar(): boolean {
+ return this.infoBar.length > 0;
+ }
+
get showBusyIndicatorOverlay() {
return !this.growsWithButton && this.loading;
}
diff --git a/packages/fiori/src/TimelineTemplate.tsx b/packages/fiori/src/TimelineTemplate.tsx
index 7818da88a07a..113fece566c6 100644
--- a/packages/fiori/src/TimelineTemplate.tsx
+++ b/packages/fiori/src/TimelineTemplate.tsx
@@ -21,7 +21,16 @@ export default function TimelineTemplate(this: Timeline) {
class="ui5-timeline-busy-indicator"
>
+
+
+
+
+
+
+
+
+
+
+
+
+ Sort: Newest
+ Filter
+
+
+
+
+
+ Showing 0 of 0
+
+
+
+
+ Initial alignment with all stakeholders.
+ Visual design walk-through and feedback.
+ Backlog refinement and capacity planning.
+ Authentication endpoint signature changed.
+ Re-prioritised the next two sprints.
+ v1.2.4 hotfix to production.
+ Demo of new dashboard.
+ What went well, what to improve.
+ Search latency up by 40%.
+ Scope and timing for the next release.
+ Reviewing PR #482.
+ v1.3.0 rollout.
+ Roundtable with three pilot customers.
+ CVE-2017-XXXX mitigation.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 8 of 8 activities
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Header sticks to the ancestor
+ Add row
+
+
+ 6 rows
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Type
+
+ Meeting
+ Review
+ Deploy
+ Incident
+
+
+
+
+ Status
+
+ Open
+ In Progress
+ Done
+
+
+
+
+ Author
+
+ Stanislava Baltova
+ Sarah Kerrigan
+ John Smith
+
+
+
+
+ Priority
+
+ Any
+ Low
+ Medium
+ High
+
+
+
+
+ Date range
+
+
+
+
+
+ Apply
+ Clear all
+ Cancel
+
+
+
+
+
+
diff --git a/packages/fiori/test/pages/TimelineHeaderInfoBar.html b/packages/fiori/test/pages/TimelineHeaderInfoBar.html
new file mode 100644
index 000000000000..60ff0e16b1f2
--- /dev/null
+++ b/packages/fiori/test/pages/TimelineHeaderInfoBar.html
@@ -0,0 +1,540 @@
+
+
+
+