From c33705c3c084a81636ae78811bc077de103ecaf2 Mon Sep 17 00:00:00 2001 From: tsanislavgatev Date: Mon, 9 Mar 2026 16:32:00 +0200 Subject: [PATCH 1/3] feat(ui5-banner): work in progress --- packages/fiori/cypress/specs/Banner.cy.tsx | 280 ++++++++++++++++++ packages/fiori/src/Banner.ts | 175 +++++++++++ packages/fiori/src/BannerTemplate.tsx | 51 ++++ packages/fiori/src/bundle.esm.ts | 1 + packages/fiori/src/themes/Banner.css | 125 ++++++++ .../src/themes/base/Banner-parameters.css | 8 + .../themes/sap_fiori_3/parameters-bundle.css | 1 + .../sap_fiori_3_dark/parameters-bundle.css | 1 + .../sap_fiori_3_hcb/parameters-bundle.css | 1 + .../sap_fiori_3_hcw/parameters-bundle.css | 1 + .../themes/sap_horizon/parameters-bundle.css | 1 + .../sap_horizon_dark/parameters-bundle.css | 1 + .../parameters-bundle.css | 1 + .../sap_horizon_exp/parameters-bundle.css | 1 + .../sap_horizon_hcb/parameters-bundle.css | 1 + .../sap_horizon_hcb_exp/parameters-bundle.css | 1 + .../sap_horizon_hcw/parameters-bundle.css | 1 + .../sap_horizon_hcw_exp/parameters-bundle.css | 1 + packages/fiori/src/types/BannerLayout.ts | 31 ++ packages/fiori/test/pages/Banner.html | 264 +++++++++++++++++ packages/fiori/test/pages/styles/Banner.css | 86 ++++++ 21 files changed, 1033 insertions(+) create mode 100644 packages/fiori/cypress/specs/Banner.cy.tsx create mode 100644 packages/fiori/src/Banner.ts create mode 100644 packages/fiori/src/BannerTemplate.tsx create mode 100644 packages/fiori/src/themes/Banner.css create mode 100644 packages/fiori/src/themes/base/Banner-parameters.css create mode 100644 packages/fiori/src/types/BannerLayout.ts create mode 100644 packages/fiori/test/pages/Banner.html create mode 100644 packages/fiori/test/pages/styles/Banner.css diff --git a/packages/fiori/cypress/specs/Banner.cy.tsx b/packages/fiori/cypress/specs/Banner.cy.tsx new file mode 100644 index 000000000000..7373a45cd46d --- /dev/null +++ b/packages/fiori/cypress/specs/Banner.cy.tsx @@ -0,0 +1,280 @@ +import Banner from "../../src/Banner.js"; + +describe("Banner", () => { + describe("Rendering", () => { + it("renders with default configuration", () => { + cy.mount( + + ); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-root") + .should("exist"); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-salutation") + .should("have.text", "Hello, John"); + }); + + it("renders salutation text", () => { + cy.mount( + + ); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-salutation") + .should("have.text", "Good Morning, Jane"); + }); + + it("renders date text when provided", () => { + cy.mount( + + ); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-date") + .should("have.text", "March 6, 2026"); + }); + + it("does not render date element when dateText is not set", () => { + cy.mount( + + ); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-date") + .should("not.exist"); + }); + + it("does not render salutation element when salutationText is not set", () => { + cy.mount( + + ); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-salutation") + .should("not.exist"); + }); + }); + + describe("Layout", () => { + it("applies FullWidth layout by default", () => { + cy.mount( + +
Start
+
+ ); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-layout-FullWidth") + .should("exist"); + }); + + it("applies HalfWidth layout", () => { + cy.mount( + +
Start
+
End
+
+ ); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-layout-HalfWidth") + .should("exist"); + }); + + it("applies TwoThirds layout", () => { + cy.mount( + +
Start
+
End
+
+ ); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-layout-TwoThirds") + .should("exist"); + }); + }); + + describe("Slots", () => { + it("renders startContent slot", () => { + cy.mount( + +
Start Content
+
+ ); + + cy.get("[ui5-banner]") + .find("#start-block") + .should("exist") + .and("have.text", "Start Content"); + }); + + it("renders endContent slot", () => { + cy.mount( + +
End Content
+
+ ); + + cy.get("[ui5-banner]") + .find("#end-block") + .should("exist") + .and("have.text", "End Content"); + }); + + it("renders default slot content", () => { + cy.mount( + +
Default Content
+
+ ); + + cy.get("[ui5-banner]") + .find("#default-content") + .should("exist") + .and("have.text", "Default Content"); + }); + + it("renders both startContent and endContent in HalfWidth layout", () => { + cy.mount( + +
Left
+
Right
+
+ ); + + cy.get("[ui5-banner]") + .find("#start") + .should("exist"); + + cy.get("[ui5-banner]") + .find("#end") + .should("exist"); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-block-start") + .should("exist"); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-block-end") + .should("exist"); + }); + }); + + describe("Background Image", () => { + it("applies background image when set", () => { + cy.mount( + + ); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-root") + .should("have.class", "ui5-banner--has-bg-image") + .and("have.attr", "style") + .and("include", "background-image"); + }); + + it("does not apply background image class when not set", () => { + cy.mount( + + ); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-root") + .should("not.have.class", "ui5-banner--has-bg-image"); + }); + }); + + describe("Height constraints", () => { + it("respects minimum height", () => { + cy.mount( + + ); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-root") + .invoke("css", "min-height") + .should("equal", "76px"); // 4.75rem = 76px at default font-size + }); + }); + + describe("Properties", () => { + it("updates salutationText dynamically", () => { + cy.mount( + + ); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-salutation") + .should("have.text", "Hello, John"); + + cy.get("[ui5-banner]") + .invoke("prop", "salutationText", "Hello, Jane"); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-salutation") + .should("have.text", "Hello, Jane"); + }); + + it("updates dateText dynamically", () => { + cy.mount( + + ); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-date") + .should("have.text", "March 6"); + + cy.get("[ui5-banner]") + .invoke("prop", "dateText", "March 7"); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-date") + .should("have.text", "March 7"); + }); + + it("updates layout dynamically", () => { + cy.mount( + +
Start
+
+ ); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-layout-FullWidth") + .should("exist"); + + cy.get("[ui5-banner]") + .invoke("prop", "layout", "HalfWidth"); + + cy.get("[ui5-banner]") + .shadow() + .find(".ui5-banner-layout-HalfWidth") + .should("exist"); + }); + }); +}); diff --git a/packages/fiori/src/Banner.ts b/packages/fiori/src/Banner.ts new file mode 100644 index 000000000000..01fe9393fb3f --- /dev/null +++ b/packages/fiori/src/Banner.ts @@ -0,0 +1,175 @@ +import UI5Element 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"; +import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js"; +import type { Slot, DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js"; + +import type BannerLayout from "./types/BannerLayout.js"; + +// Template +import BannerTemplate from "./BannerTemplate.js"; + +// Styles +import BannerCss from "./generated/themes/Banner.css.js"; + +/** + * @class + * + * ### Overview + * + * The `ui5-banner` is the main visual element that unifies all product homepages. + * It is a flexible card designed to accommodate various elements depending on product needs. + * The banner is a mandatory out-of-the-box element for product landing pages. + * + * ### Structure + * + * The Banner consists of the following building blocks: + * + * - **Banner Canvas** - the visual base with a background color, optional background image and shadow. + * - **Date** (optional) - contextual text at the top, typically showing the current date. + * - **Salutation** (mandatory) - the main greeting header below the date, e.g. "Hello, John". + * - **Free Blocks** (optional) - customizable content areas that can contain KPI cards, search components, text, buttons, etc. + * + * The banner scrolls away below the shell navigation when the user scrolls down the page. It is not sticky. + * + * ### Usage + * + * Use the `ui5-banner` at the top of a product homepage to provide a personalized greeting + * and quick access to key information or actions. + * + * The banner itself is non-interactive. However, interactive elements such as buttons, cards, + * or search fields can be placed inside the free content slots and will follow their own + * interactive states. + * + * ### Responsive Behavior + * + * The banner adapts to different screen sizes with responsive horizontal padding: + * - **XS/S** (up to 599px): 1.5rem padding + * - **M/L** (600px - 1439px): 2rem horizontal padding + * - **XL/MAX** (1440px - 2559px): 3rem horizontal padding + * - **4K** (2560px+): 4rem horizontal padding + * + * ### ES6 Module Import + * + * `import "@ui5/webcomponents-fiori/dist/Banner.js";` + * + * @constructor + * @extends UI5Element + * @public + * @since 2.12.0 + * @csspart canvas - Used to style the banner canvas container + * @csspart content - Used to style the content area of the banner + */ +@customElement({ + tag: "ui5-banner", + renderer: jsxRenderer, + styles: BannerCss, + template: BannerTemplate, +}) +class Banner extends UI5Element { + /** + * Defines the salutation text displayed in the banner. + * + * This is the main greeting header, typically a personalized message + * such as "Hello, John". + * + * @default undefined + * @public + */ + @property() + salutationText?: string; + + /** + * Defines the date or secondary text displayed above the salutation. + * + * Can be used to show the current date, a status message, + * or any other relevant contextual information. + * + * @default undefined + * @public + */ + @property() + dateText?: string; + + /** + * Defines the layout of the free content blocks inside the banner. + * + * Available options are: + * - `FullWidth` - Content spans the full width below the salutation. + * - `HalfWidth` - Content is split 50/50 side by side. + * - `TwoThirds` - First block takes 2/3 width, second takes 1/3. + * + * @default "FullWidth" + * @public + */ + @property() + layout: `${BannerLayout}` = "FullWidth"; + + /** + * Defines the URL of the background image for the banner canvas. + * + * When set, the image is displayed as a cover background on the banner. + * + * @default undefined + * @public + */ + @property() + backgroundImage?: string; + + /** + * Defines the first free content block of the banner. + * + * This slot can contain various content types defined by the product, + * including KPI cards, search components, text, and buttons. + * + * @public + */ + @slot() + startContent!: Slot; + + /** + * Defines the second free content block of the banner. + * + * Used alongside `startContent` in split layouts (`HalfWidth`, `TwoThirds`). + * Can contain cards, buttons, and other interactive elements. + * + * @public + */ + @slot() + endContent!: Slot; + + /** + * Defines the default content of the banner. + * + * Used for additional content that appears below the salutation + * and date area, spanning the full width of the banner. + * + * @public + */ + @slot({ type: HTMLElement, "default": true }) + content!: DefaultSlot; + + get _backgroundImageStyle(): Record | undefined { + if (this.backgroundImage) { + return { "background-image": `url('${this.backgroundImage}')` }; + } + return undefined; + } + + get _hasContent() { + return this.startContent.length > 0 || this.endContent.length > 0 || this.content.length > 0; + } + + get _hasStartContent() { + return this.startContent.length > 0; + } + + get _hasEndContent() { + return this.endContent.length > 0; + } +} + +Banner.define(); + +export default Banner; diff --git a/packages/fiori/src/BannerTemplate.tsx b/packages/fiori/src/BannerTemplate.tsx new file mode 100644 index 000000000000..e5e1b1ede766 --- /dev/null +++ b/packages/fiori/src/BannerTemplate.tsx @@ -0,0 +1,51 @@ +import type Banner from "./Banner.js"; + +export default function BannerTemplate(this: Banner) { + return ( +
+
+
+ {this.dateText && +
+ {this.dateText} +
+ } + + {this.salutationText && +
+ {this.salutationText} +
+ } +
+ + {this._hasContent && +
+ {this._hasStartContent && +
+ +
+ } + + {this._hasEndContent && +
+ +
+ } + + +
+ } +
+
+ ); +} diff --git a/packages/fiori/src/bundle.esm.ts b/packages/fiori/src/bundle.esm.ts index 9ccfbb26b1af..c3a71ce31ba8 100644 --- a/packages/fiori/src/bundle.esm.ts +++ b/packages/fiori/src/bundle.esm.ts @@ -9,6 +9,7 @@ import "./Assets.js"; import "./illustrations/AllIllustrations.js"; // FIORI components +import Banner from "./Banner.js"; import BarcodeScannerDialog from "./BarcodeScannerDialog.js"; import DynamicPage from "./DynamicPage.js"; import DynamicPageHeader from "./DynamicPageHeader.js"; diff --git a/packages/fiori/src/themes/Banner.css b/packages/fiori/src/themes/Banner.css new file mode 100644 index 000000000000..d8f528c6df3c --- /dev/null +++ b/packages/fiori/src/themes/Banner.css @@ -0,0 +1,125 @@ +/* Banner Component Styles */ + +:host(:not([hidden])) { + display: block; + width: 100%; + container-type: inline-size; +} + +/* Banner Canvas */ +.ui5-banner-root { + box-sizing: border-box; + min-height: 4.75rem; + max-height: 24rem; + border-radius: var(--_ui5_banner_border_radius); + background-color: var(--_ui5_banner_background); + background-image: var(--_ui5_banner_background_image); + background-size: cover; + background-position: center; + background-repeat: no-repeat; + box-shadow: var(--_ui5_banner_box_shadow); + border: var(--_ui5_banner_border); + overflow: hidden; + position: relative; +} + +/* Content area */ +.ui5-banner-content { + display: flex; + flex-direction: column; + height: 100%; + gap: 1rem; + box-sizing: border-box; + padding: 1.5rem; +} + +/* Header (Salutation + Date) */ +.ui5-banner-header { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.ui5-banner-salutation { + font-family: var(--sapFontHeaderFamily); + font-size: var(--sapFontHeader2Size); + font-weight: bold; + color: var(--_ui5_banner_text_color); + line-height: 1.4; +} + +.ui5-banner-date { + font-family: var(--sapFontFamily); + font-size: var(--sapFontSize); + color: var(--_ui5_banner_text_color); + line-height: 1.4; +} + +/* Free content blocks area */ +.ui5-banner-blocks { + display: flex; + flex-wrap: wrap; + gap: 1rem; + flex: 1; + min-height: 0; +} + +.ui5-banner-block { + box-sizing: border-box; + min-width: 0; +} + +/* Layout: FullWidth - blocks take full width */ +.ui5-banner-layout-FullWidth .ui5-banner-block { + flex: 1 1 100%; +} + +/* Layout: HalfWidth - 50/50 split */ +.ui5-banner-layout-HalfWidth .ui5-banner-block { + flex: 1 1 calc(50% - 0.5rem); +} + +/* Layout: TwoThirds - 2/3 + 1/3 split */ +.ui5-banner-layout-TwoThirds .ui5-banner-block-start { + flex: 2 1 calc(66.66% - 0.5rem); +} + +.ui5-banner-layout-TwoThirds .ui5-banner-block-end { + flex: 1 1 calc(33.33% - 0.5rem); +} + +/*** Responsive Paddings ***/ + +/* M Size (600px - 1439px) */ +@container (min-width: 600px) and (max-width: 1439px) { + .ui5-banner-content { + padding: 1.5rem 2rem; + } +} + +/* XL / MAX Size (1440px - 2559px) */ +@container (min-width: 1440px) and (max-width: 2559px) { + .ui5-banner-content { + padding: 1.5rem 3rem; + } +} + +/* 4K Size (2560px+) */ +@container (min-width: 2560px) { + .ui5-banner-content { + padding: 1.5rem 4rem; + } +} + +/* Stack blocks vertically on small screens */ +@container (max-width: 599px) { + .ui5-banner-blocks { + flex-direction: column; + } + + .ui5-banner-layout-HalfWidth .ui5-banner-block, + .ui5-banner-layout-TwoThirds .ui5-banner-block-start, + .ui5-banner-layout-TwoThirds .ui5-banner-block-end { + flex: 1 1 100%; + } +} diff --git a/packages/fiori/src/themes/base/Banner-parameters.css b/packages/fiori/src/themes/base/Banner-parameters.css new file mode 100644 index 000000000000..987a96f66801 --- /dev/null +++ b/packages/fiori/src/themes/base/Banner-parameters.css @@ -0,0 +1,8 @@ +:host { + --_ui5_banner_background: var(--sapShell_Banner_Background, var(--sapTile_Background)); + --_ui5_banner_background_image: var(--sapShell_Banner_BackgroundImage, none); + --_ui5_banner_text_color: var(--sapShell_Banner_TextColor, var(--sapTextColor)); + --_ui5_banner_border_radius: var(--sapTile_BorderCornerRadius); + --_ui5_banner_border: 0.0625rem solid var(--sapTile_BorderColor); + --_ui5_banner_box_shadow: var(--sapContent_Shadow0); +} diff --git a/packages/fiori/src/themes/sap_fiori_3/parameters-bundle.css b/packages/fiori/src/themes/sap_fiori_3/parameters-bundle.css index 4b9786a9cf35..dd58d818dddc 100644 --- a/packages/fiori/src/themes/sap_fiori_3/parameters-bundle.css +++ b/packages/fiori/src/themes/sap_fiori_3/parameters-bundle.css @@ -1,3 +1,4 @@ +@import "../base/Banner-parameters.css"; @import "../base/FlexibleColumnLayout-parameters.css"; @import "../base/IllustratedMessage-parameters.css"; @import "../base/MediaGallery-parameters.css"; diff --git a/packages/fiori/src/themes/sap_fiori_3_dark/parameters-bundle.css b/packages/fiori/src/themes/sap_fiori_3_dark/parameters-bundle.css index 2c3f6217b00c..2d206505c3dd 100644 --- a/packages/fiori/src/themes/sap_fiori_3_dark/parameters-bundle.css +++ b/packages/fiori/src/themes/sap_fiori_3_dark/parameters-bundle.css @@ -1,3 +1,4 @@ +@import "../base/Banner-parameters.css"; @import "../base/FlexibleColumnLayout-parameters.css"; @import "../base/IllustratedMessage-parameters.css"; @import "../base/MediaGallery-parameters.css"; diff --git a/packages/fiori/src/themes/sap_fiori_3_hcb/parameters-bundle.css b/packages/fiori/src/themes/sap_fiori_3_hcb/parameters-bundle.css index 2d01a0c072d1..8d2d07ce7d1b 100644 --- a/packages/fiori/src/themes/sap_fiori_3_hcb/parameters-bundle.css +++ b/packages/fiori/src/themes/sap_fiori_3_hcb/parameters-bundle.css @@ -1,3 +1,4 @@ +@import "../base/Banner-parameters.css"; @import "./FlexibleColumnLayout-parameters.css"; @import "../base/IllustratedMessage-parameters.css"; @import "./MediaGallery-parameters.css"; diff --git a/packages/fiori/src/themes/sap_fiori_3_hcw/parameters-bundle.css b/packages/fiori/src/themes/sap_fiori_3_hcw/parameters-bundle.css index 065f059b3581..dd9e50ac10d3 100644 --- a/packages/fiori/src/themes/sap_fiori_3_hcw/parameters-bundle.css +++ b/packages/fiori/src/themes/sap_fiori_3_hcw/parameters-bundle.css @@ -1,3 +1,4 @@ +@import "../base/Banner-parameters.css"; @import "./FlexibleColumnLayout-parameters.css"; @import "../base/IllustratedMessage-parameters.css"; @import "./MediaGallery-parameters.css"; diff --git a/packages/fiori/src/themes/sap_horizon/parameters-bundle.css b/packages/fiori/src/themes/sap_horizon/parameters-bundle.css index 538e15d26851..d17e39faf1c8 100644 --- a/packages/fiori/src/themes/sap_horizon/parameters-bundle.css +++ b/packages/fiori/src/themes/sap_horizon/parameters-bundle.css @@ -1,3 +1,4 @@ +@import "../base/Banner-parameters.css"; @import "../base/rtl-parameters.css"; @import "../base/FlexibleColumnLayout-parameters.css"; @import "../base/IllustratedMessage-parameters.css"; diff --git a/packages/fiori/src/themes/sap_horizon_dark/parameters-bundle.css b/packages/fiori/src/themes/sap_horizon_dark/parameters-bundle.css index acd952d5c2a8..93e4c3d74bfb 100644 --- a/packages/fiori/src/themes/sap_horizon_dark/parameters-bundle.css +++ b/packages/fiori/src/themes/sap_horizon_dark/parameters-bundle.css @@ -1,3 +1,4 @@ +@import "../base/Banner-parameters.css"; @import "../base/FlexibleColumnLayout-parameters.css"; @import "../base/IllustratedMessage-parameters.css"; @import "../base/MediaGallery-parameters.css"; diff --git a/packages/fiori/src/themes/sap_horizon_dark_exp/parameters-bundle.css b/packages/fiori/src/themes/sap_horizon_dark_exp/parameters-bundle.css index c6621d9b5d6a..fb9d1c4b39ed 100644 --- a/packages/fiori/src/themes/sap_horizon_dark_exp/parameters-bundle.css +++ b/packages/fiori/src/themes/sap_horizon_dark_exp/parameters-bundle.css @@ -1,3 +1,4 @@ +@import "../base/Banner-parameters.css"; @import "../base/FlexibleColumnLayout-parameters.css"; @import "../base/IllustratedMessage-parameters.css"; @import "../base/MediaGallery-parameters.css"; diff --git a/packages/fiori/src/themes/sap_horizon_exp/parameters-bundle.css b/packages/fiori/src/themes/sap_horizon_exp/parameters-bundle.css index 7c34f4b60abd..1d981cbbacf1 100644 --- a/packages/fiori/src/themes/sap_horizon_exp/parameters-bundle.css +++ b/packages/fiori/src/themes/sap_horizon_exp/parameters-bundle.css @@ -1,3 +1,4 @@ +@import "../base/Banner-parameters.css"; @import "../base/FlexibleColumnLayout-parameters.css"; @import "../base/IllustratedMessage-parameters.css"; @import "../base/MediaGallery-parameters.css"; diff --git a/packages/fiori/src/themes/sap_horizon_hcb/parameters-bundle.css b/packages/fiori/src/themes/sap_horizon_hcb/parameters-bundle.css index 7f8da0756e48..94071d30bf90 100644 --- a/packages/fiori/src/themes/sap_horizon_hcb/parameters-bundle.css +++ b/packages/fiori/src/themes/sap_horizon_hcb/parameters-bundle.css @@ -1,3 +1,4 @@ +@import "../base/Banner-parameters.css"; @import "./FlexibleColumnLayout-parameters.css"; @import "../base/IllustratedMessage-parameters.css"; @import "./MediaGallery-parameters.css"; diff --git a/packages/fiori/src/themes/sap_horizon_hcb_exp/parameters-bundle.css b/packages/fiori/src/themes/sap_horizon_hcb_exp/parameters-bundle.css index 3366cfe13917..4cb199256b56 100644 --- a/packages/fiori/src/themes/sap_horizon_hcb_exp/parameters-bundle.css +++ b/packages/fiori/src/themes/sap_horizon_hcb_exp/parameters-bundle.css @@ -1,3 +1,4 @@ +@import "../base/Banner-parameters.css"; @import "./FlexibleColumnLayout-parameters.css"; @import "../base/IllustratedMessage-parameters.css"; @import "./MediaGallery-parameters.css"; diff --git a/packages/fiori/src/themes/sap_horizon_hcw/parameters-bundle.css b/packages/fiori/src/themes/sap_horizon_hcw/parameters-bundle.css index fca7aa1b3e23..c6d286534cf3 100644 --- a/packages/fiori/src/themes/sap_horizon_hcw/parameters-bundle.css +++ b/packages/fiori/src/themes/sap_horizon_hcw/parameters-bundle.css @@ -1,3 +1,4 @@ +@import "../base/Banner-parameters.css"; @import "./FlexibleColumnLayout-parameters.css"; @import "../base/IllustratedMessage-parameters.css"; @import "./MediaGallery-parameters.css"; diff --git a/packages/fiori/src/themes/sap_horizon_hcw_exp/parameters-bundle.css b/packages/fiori/src/themes/sap_horizon_hcw_exp/parameters-bundle.css index bbc555b5dad1..e5d0ab45b3f8 100644 --- a/packages/fiori/src/themes/sap_horizon_hcw_exp/parameters-bundle.css +++ b/packages/fiori/src/themes/sap_horizon_hcw_exp/parameters-bundle.css @@ -1,3 +1,4 @@ +@import "../base/Banner-parameters.css"; @import "./FlexibleColumnLayout-parameters.css"; @import "../base/IllustratedMessage-parameters.css"; @import "./MediaGallery-parameters.css"; diff --git a/packages/fiori/src/types/BannerLayout.ts b/packages/fiori/src/types/BannerLayout.ts new file mode 100644 index 000000000000..b421807b2c1e --- /dev/null +++ b/packages/fiori/src/types/BannerLayout.ts @@ -0,0 +1,31 @@ +/** + * Available Banner layout options. + * + * Defines how the free content blocks are arranged within the banner. + * @public + * @since 2.12.0 + */ +enum BannerLayout { + /** + * Full width bottom-half free slot layout. + * The free content blocks span the entire width below the salutation. + * @public + */ + FullWidth = "FullWidth", + + /** + * 50/50 split layout. + * The free content blocks are split equally side by side. + * @public + */ + HalfWidth = "HalfWidth", + + /** + * 2/3 wide bottom-half layout. + * The first free block takes 2/3 width, the second takes 1/3. + * @public + */ + TwoThirds = "TwoThirds", +} + +export default BannerLayout; diff --git a/packages/fiori/test/pages/Banner.html b/packages/fiori/test/pages/Banner.html new file mode 100644 index 000000000000..ef80e2a1ba9b --- /dev/null +++ b/packages/fiori/test/pages/Banner.html @@ -0,0 +1,264 @@ + + + + + + Banner + + + + + + + +
+ + + + +
+

1. Basic Banner (Salutation Only)

+

The simplest banner with only the mandatory salutation text.

+ + +
+ + + + +
+

2. Banner with Date

+

A banner combining salutation and date text.

+ + +
+ + + + +
+

3. FullWidth Layout

+

Banner with a full-width search field below the salutation.

+ + +
+ + + + +
+

4. HalfWidth Layout (50/50 Split)

+

Two KPI cards side by side in a 50/50 layout.

+ + +
+ + + + +
+

5. TwoThirds Layout (2/3 + 1/3)

+

Primary KPI card taking 2/3 of the width with a secondary card at 1/3.

+ + +
+ + + + +
+

6. Banner with Background Image

+

A banner with a custom background image applied to the canvas.

+ + +
+ + + + +
+

7. Banner with Interactive Elements

+

The banner itself is non-interactive, but slotted content like buttons and cards follow their own interactive states.

+ + +
+ + + + +
+

8. Scroll-Away Behavior

+

The banner scrolls away with the page content. It is not sticky. Scroll the container below to see the behavior.

+ +
+ + +
+

↓ Scroll down to see the banner scroll away ↓

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

+

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

+

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

+

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

+

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+
+
+
+ + + + +
+

9. Interactive Controls

+

Change the banner properties dynamically.

+ +
+ Change Salutation + Toggle Date + FullWidth + HalfWidth + TwoThirds +
+ + +
+ +
+ + + + diff --git a/packages/fiori/test/pages/styles/Banner.css b/packages/fiori/test/pages/styles/Banner.css new file mode 100644 index 000000000000..2ea8d5a6dacd --- /dev/null +++ b/packages/fiori/test/pages/styles/Banner.css @@ -0,0 +1,86 @@ +html, body { + height: 100%; + padding: 0; + margin: 0; + background-color: var(--sapBackgroundColor); + font-family: var(--sapFontFamily); +} + +.showcase { + max-width: 1440px; + margin: 0 auto; + padding: 2rem; + display: flex; + flex-direction: column; + gap: 2rem; +} + +.showcase h2 { + font-family: var(--sapFontHeaderFamily); + color: var(--sapTextColor); + font-size: var(--sapFontHeader3Size); + margin: 0.5rem 0; + border-bottom: 1px solid var(--sapGroup_TitleBorderColor); + padding-bottom: 0.5rem; +} + +.showcase p { + font-size: var(--sapFontSize); + color: var(--sapContent_LabelColor); + margin: 0 0 1rem 0; +} + +/* KPI card styling for demo */ +.kpi-card { + background: var(--sapTile_Background); + border-radius: 0.75rem; + padding: 1rem; + box-shadow: var(--sapContent_Shadow0); + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.kpi-card .kpi-label { + font-size: var(--sapFontSmallSize); + color: var(--sapContent_LabelColor); +} + +.kpi-card .kpi-value { + font-family: var(--sapFontHeaderFamily); + font-size: var(--sapFontHeader2Size); + font-weight: bold; + color: var(--sapTextColor); +} + +.kpi-card .kpi-trend { + font-size: var(--sapFontSmallSize); + color: var(--sapPositiveColor); +} + +.kpi-card .kpi-trend.negative { + color: var(--sapNegativeColor); +} + +.controls-bar { + display: flex; + gap: 0.5rem; + flex-wrap: wrap; + padding: 0.5rem 0; +} + +/* Scrollable content for scroll-away demo */ +.scroll-container { + height: 500px; + overflow-y: auto; + border: 1px solid var(--sapGroup_TitleBorderColor); + border-radius: 0.5rem; + padding: 1rem; +} + +.filler-content { + padding: 1rem 0; + color: var(--sapContent_LabelColor); + font-size: var(--sapFontSize); + line-height: 1.6; +} From 566f79d0176483293e221de11b821f3cea17af68 Mon Sep 17 00:00:00 2001 From: tsanislavgatev Date: Thu, 12 Mar 2026 15:07:17 +0200 Subject: [PATCH 2/3] feat(ui5-banner): add theming and samples --- packages/fiori/src/themes/Banner.css | 4 ++-- packages/fiori/src/themes/base/Banner-parameters.css | 4 ++-- packages/fiori/src/themes/sap_horizon/Banner-parameters.css | 4 ++++ packages/fiori/src/themes/sap_horizon/parameters-bundle.css | 1 + .../fiori/src/themes/sap_horizon_exp/parameters-bundle.css | 1 + packages/fiori/test/pages/Banner.html | 3 ++- 6 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 packages/fiori/src/themes/sap_horizon/Banner-parameters.css diff --git a/packages/fiori/src/themes/Banner.css b/packages/fiori/src/themes/Banner.css index d8f528c6df3c..3ed44a14bf43 100644 --- a/packages/fiori/src/themes/Banner.css +++ b/packages/fiori/src/themes/Banner.css @@ -14,9 +14,9 @@ border-radius: var(--_ui5_banner_border_radius); background-color: var(--_ui5_banner_background); background-image: var(--_ui5_banner_background_image); - background-size: cover; - background-position: center; background-repeat: no-repeat; + background-position: top; + background-size: cover; box-shadow: var(--_ui5_banner_box_shadow); border: var(--_ui5_banner_border); overflow: hidden; diff --git a/packages/fiori/src/themes/base/Banner-parameters.css b/packages/fiori/src/themes/base/Banner-parameters.css index 987a96f66801..59650a6b07f1 100644 --- a/packages/fiori/src/themes/base/Banner-parameters.css +++ b/packages/fiori/src/themes/base/Banner-parameters.css @@ -1,7 +1,7 @@ :host { - --_ui5_banner_background: var(--sapShell_Banner_Background, var(--sapTile_Background)); + --_ui5_banner_background: var(--sapShell_Banner_Background, var(--sapShellColor)); --_ui5_banner_background_image: var(--sapShell_Banner_BackgroundImage, none); - --_ui5_banner_text_color: var(--sapShell_Banner_TextColor, var(--sapTextColor)); + --_ui5_banner_text_color: var(--sapShell_Banner_TextColor, var(--sapShell_TextColor)); --_ui5_banner_border_radius: var(--sapTile_BorderCornerRadius); --_ui5_banner_border: 0.0625rem solid var(--sapTile_BorderColor); --_ui5_banner_box_shadow: var(--sapContent_Shadow0); diff --git a/packages/fiori/src/themes/sap_horizon/Banner-parameters.css b/packages/fiori/src/themes/sap_horizon/Banner-parameters.css new file mode 100644 index 000000000000..14e14b8002ff --- /dev/null +++ b/packages/fiori/src/themes/sap_horizon/Banner-parameters.css @@ -0,0 +1,4 @@ +:host { + --_ui5_banner_background: var(--sapShell_Banner_Background, var(--sapHighlightColor)); + --_ui5_banner_text_color: var(--sapShell_Banner_TextColor, var(--sapContent_ContrastTextColor)); +} diff --git a/packages/fiori/src/themes/sap_horizon/parameters-bundle.css b/packages/fiori/src/themes/sap_horizon/parameters-bundle.css index d17e39faf1c8..ee5a5714bf27 100644 --- a/packages/fiori/src/themes/sap_horizon/parameters-bundle.css +++ b/packages/fiori/src/themes/sap_horizon/parameters-bundle.css @@ -1,4 +1,5 @@ @import "../base/Banner-parameters.css"; +@import "./Banner-parameters.css"; @import "../base/rtl-parameters.css"; @import "../base/FlexibleColumnLayout-parameters.css"; @import "../base/IllustratedMessage-parameters.css"; diff --git a/packages/fiori/src/themes/sap_horizon_exp/parameters-bundle.css b/packages/fiori/src/themes/sap_horizon_exp/parameters-bundle.css index 1d981cbbacf1..0819ff5c55ab 100644 --- a/packages/fiori/src/themes/sap_horizon_exp/parameters-bundle.css +++ b/packages/fiori/src/themes/sap_horizon_exp/parameters-bundle.css @@ -1,4 +1,5 @@ @import "../base/Banner-parameters.css"; +@import "../sap_horizon/Banner-parameters.css"; @import "../base/FlexibleColumnLayout-parameters.css"; @import "../base/IllustratedMessage-parameters.css"; @import "../base/MediaGallery-parameters.css"; diff --git a/packages/fiori/test/pages/Banner.html b/packages/fiori/test/pages/Banner.html index ef80e2a1ba9b..97eedee02626 100644 --- a/packages/fiori/test/pages/Banner.html +++ b/packages/fiori/test/pages/Banner.html @@ -33,6 +33,7 @@

2. Banner with Date

A banner combining salutation and date text.

@@ -133,7 +134,7 @@

6. Banner with Background Image

7. Banner with Interactive Elements

The banner itself is non-interactive, but slotted content like buttons and cards follow their own interactive states.

-