diff --git a/packages/fiori/cypress/specs/DynamicPage.cy.tsx b/packages/fiori/cypress/specs/DynamicPage.cy.tsx
index 6e02f410df0c..ad17d50ada99 100644
--- a/packages/fiori/cypress/specs/DynamicPage.cy.tsx
+++ b/packages/fiori/cypress/specs/DynamicPage.cy.tsx
@@ -165,7 +165,7 @@ describe("DynamicPage", () => {
cy.get("[ui5-dynamic-page]")
.shadow()
- .find("header.ui5-dynamic-page-title-header-wrapper > slot[name=headerArea]")
+ .find("div.ui5-dynamic-page-title-header-wrapper > slot[name=headerArea]")
.should("not.exist");
cy.get("[ui5-dynamic-page]")
@@ -178,7 +178,7 @@ describe("DynamicPage", () => {
cy.get("[ui5-dynamic-page]")
.shadow()
- .find("header.ui5-dynamic-page-title-header-wrapper > slot[name=headerArea]")
+ .find("div.ui5-dynamic-page-title-header-wrapper > slot[name=headerArea]")
.should("exist");
cy.get("[ui5-dynamic-page]")
@@ -1276,4 +1276,100 @@ describe("ARIA attributes", () => {
.find(".ui5-dynamic-page-header-root")
.should("have.attr", "aria-label", "Header Expanded");
});
+
+ it("supports customizing header role and label via accessibilityAttributes", () => {
+ cy.mount(
+
+
+ Page Title
+
+
+ Header Content
+
+ Content
+
+ );
+
+ cy.get("[ui5-dynamic-page]").invoke("prop", "accessibilityAttributes", {
+ header: { role: "none", name: "Custom Header" },
+ });
+
+ cy.get("[ui5-dynamic-page]")
+ .shadow()
+ .find(".ui5-dynamic-page-title-header-wrapper")
+ .should("have.attr", "role", "none")
+ .should("have.attr", "aria-label", "Custom Header");
+ });
+
+ it("supports customizing headerContent label via accessibleName on DynamicPageHeader", () => {
+ cy.mount(
+
+
+ Page Title
+
+
+ Header Content
+
+ Content
+
+ );
+
+ cy.get("[ui5-dynamic-page-header]")
+ .shadow()
+ .find(".ui5-dynamic-page-header-root")
+ .should("have.attr", "aria-label", "Custom Region Label");
+ });
+
+ it("renders default banner role when only header.name is set", () => {
+ cy.mount(
+
+
+ Page Title
+
+
+ Header Content
+
+ Content
+
+ );
+
+ cy.get("[ui5-dynamic-page]").invoke("prop", "accessibilityAttributes", {
+ header: { name: "Custom Header Label" },
+ });
+
+ cy.get("[ui5-dynamic-page]")
+ .shadow()
+ .find("div.ui5-dynamic-page-title-header-wrapper")
+ .should("exist")
+ .should("have.attr", "role", "banner")
+ .should("have.attr", "aria-label", "Custom Header Label");
+ });
+
+ it("supports customizing content and footer roles via accessibilityAttributes", () => {
+ cy.mount(
+
+
+ Page Title
+
+ Content
+
+ );
+
+ cy.get("[ui5-dynamic-page]").invoke("prop", "accessibilityAttributes", {
+ content: { role: "main", name: "Page Content" },
+ footer: { role: "contentinfo", name: "Page Footer" },
+ });
+
+ cy.get("[ui5-dynamic-page]")
+ .shadow()
+ .find(".ui5-dynamic-page-content")
+ .should("have.attr", "role", "main")
+ .should("have.attr", "aria-label", "Page Content");
+
+ cy.get("[ui5-dynamic-page]")
+ .shadow()
+ .find(".ui5-dynamic-page-footer")
+ .should("have.attr", "role", "contentinfo")
+ .should("have.attr", "aria-label", "Page Footer");
+ });
});
\ No newline at end of file
diff --git a/packages/fiori/src/DynamicPage.ts b/packages/fiori/src/DynamicPage.ts
index 224436a813f9..39a57a36786f 100644
--- a/packages/fiori/src/DynamicPage.ts
+++ b/packages/fiori/src/DynamicPage.ts
@@ -10,6 +10,7 @@ import { renderFinished } from "@ui5/webcomponents-base/dist/Render.js";
import announce from "@ui5/webcomponents-base/dist/util/InvisibleMessage.js";
import InvisibleMessageMode from "@ui5/webcomponents-base/dist/types/InvisibleMessageMode.js";
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
+import type { AriaLandmarkRole } from "@ui5/webcomponents-base";
import { isPhone } from "@ui5/webcomponents-base/dist/Device.js";
import debounce from "@ui5/webcomponents-base/dist/util/debounce.js";
@@ -32,6 +33,29 @@ import {
import type { Slot, DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js";
+type DynamicPageHeaderRoles = Extract;
+type DynamicPageContentRoles = Extract;
+type DynamicPageFooterRoles = Extract;
+type DynamicPageRootRoles = Extract;
+type DynamicPageAccessibilityAttributes = {
+ root?: {
+ role?: DynamicPageRootRoles,
+ name?: string,
+ },
+ header?: {
+ role?: DynamicPageHeaderRoles,
+ name?: string,
+ },
+ content?: {
+ role?: DynamicPageContentRoles,
+ name?: string,
+ },
+ footer?: {
+ role?: DynamicPageFooterRoles,
+ name?: string,
+ },
+};
+
const SCROLL_DEBOUNCE_RATE = 5; // ms
const SCROLL_THRESHOLD = 10; // px
/**
@@ -184,6 +208,36 @@ class DynamicPage extends UI5Element {
@slot({ type: HTMLElement })
footerArea!: Slot;
+ /**
+ * Defines additional accessibility attributes on different areas of the component.
+ *
+ * The accessibilityAttributes object has the following fields,
+ * where each field is an object supporting one or more accessibility attributes:
+ *
+ * - **root**: `root.role` and `root.name`.
+ * - **header**: `header.role` and `header.name`.
+ * - **content**: `content.role` and `content.name`.
+ * - **footer**: `footer.role` and `footer.name`.
+ *
+ * The accessibility attributes support the following values:
+ *
+ * - **role**: Defines the accessible ARIA landmark role of the area.
+ * Accepts the following values per section:
+ * `root` — `none`, `main`, `region`;
+ * `header` — `none`, `banner`, `region`;
+ * `content` — `none`, `main`, `region`, `form`;
+ * `footer` — `none`, `contentinfo`, `region`.
+ *
+ * - **name**: Defines the accessible ARIA name of the area.
+ * Accepts any string.
+ *
+ * @default {}
+ * @public
+ * @since 2.23.0
+ */
+ @property({ type: Object })
+ accessibilityAttributes: DynamicPageAccessibilityAttributes = {};
+
@i18n("@ui5/webcomponents-fiori")
static i18nBundle: I18nBundle;
@@ -289,9 +343,17 @@ class DynamicPage extends UI5Element {
}
get headerAriaLabel() {
- return this.hasHeading ? this._headerLabel : undefined;
+ return this.accessibilityAttributes.header?.name || (this.hasHeading ? this._headerLabel : undefined);
}
+ get _headerRole() { return this.accessibilityAttributes.header?.role; }
+ get _rootRole() { return this.accessibilityAttributes.root?.role; }
+ get _rootAriaLabel() { return this.accessibilityAttributes.root?.name; }
+ get _contentRole() { return this.accessibilityAttributes.content?.role; }
+ get _contentAriaLabel() { return this.accessibilityAttributes.content?.name; }
+ get _footerRole() { return this.accessibilityAttributes.footer?.role; }
+ get _footerAriaLabel() { return this.accessibilityAttributes.footer?.name; }
+
get _hidePinButton() {
return this.hidePinButton || isPhone();
}
@@ -489,3 +551,5 @@ class DynamicPage extends UI5Element {
DynamicPage.define();
export default DynamicPage;
+
+export type { DynamicPageAccessibilityAttributes };
diff --git a/packages/fiori/src/DynamicPageHeader.ts b/packages/fiori/src/DynamicPageHeader.ts
index 0bb6b4fc1e47..b43e315d0dc6 100644
--- a/packages/fiori/src/DynamicPageHeader.ts
+++ b/packages/fiori/src/DynamicPageHeader.ts
@@ -75,6 +75,16 @@ class DynamicPageHeader extends UI5Element {
@property({ type: Boolean })
_snapped = false;
+ /**
+ * Defines the accessible ARIA label for the header region.
+ * Overrides the default "Header Expanded" / "Header Snapped" text.
+ * @public
+ * @default undefined
+ * @since 2.23.0
+ */
+ @property()
+ accessibleName?: string;
+
@i18n("@ui5/webcomponents-fiori")
static i18nBundle: I18nBundle;
@@ -83,6 +93,9 @@ class DynamicPageHeader extends UI5Element {
* @internal
*/
get _headerRegionAriaLabel(): string {
+ if (this.accessibleName) {
+ return this.accessibleName;
+ }
const defaultText = this._snapped
? DYNAMIC_PAGE_ARIA_LABEL_SNAPPED_HEADER
: DYNAMIC_PAGE_ARIA_LABEL_EXPANDED_HEADER;
diff --git a/packages/fiori/src/DynamicPageTemplate.tsx b/packages/fiori/src/DynamicPageTemplate.tsx
index 91c1e81f320c..6ae5e33c3187 100644
--- a/packages/fiori/src/DynamicPageTemplate.tsx
+++ b/packages/fiori/src/DynamicPageTemplate.tsx
@@ -3,13 +3,14 @@ import DynamicPageHeaderActions from "./DynamicPageHeaderActions.js";
export default function DynamicPageTemplate(this: DynamicPage) {
return (
-
+
-
+
{this.headerInContent &&
@@ -48,7 +50,7 @@ export default function DynamicPageTemplate(this: DynamicPage) {
-
diff --git a/packages/fiori/test/pages/DynamicPage.html b/packages/fiori/test/pages/DynamicPage.html
index 7012c33409dd..d819a3f62a1e 100644
--- a/packages/fiori/test/pages/DynamicPage.html
+++ b/packages/fiori/test/pages/DynamicPage.html
@@ -183,6 +183,7 @@
const cancelEdit = document.querySelector("#cancel-edit");
const saveEdit = document.querySelector("#save-edit");
+
editButton.addEventListener("click", () => {
dynamicPage.setAttribute("show-footer", true);
});