Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions packages/main/cypress/specs/Dialog.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,59 @@ describe("Acc", () => {
.and("have.attr", "aria-modal", "true");
});

it("tests header, content and footer regions", () => {
cy.mount(
<Dialog id="dialog-regions" headerText="Region Test">
<div>Some content</div>
<Button slot="footer">OK</Button>
</Dialog>
);

cy.get("#dialog-regions")
.invoke("prop", "open", true);

cy.get<Dialog>("#dialog-regions").ui5DialogOpened();

// Header region
cy.get("#dialog-regions")
.shadow()
.find(".ui5-popup-header-root")
.should("have.attr", "role", "region")
.and("have.attr", "aria-label", "Header");

// Content region
cy.get("#dialog-regions")
.shadow()
.find(".ui5-popup-content")
.should("have.attr", "role", "region")
.and("have.attr", "aria-label", "Content");

// Footer region
cy.get("#dialog-regions")
.shadow()
.find(".ui5-popup-footer-root")
.should("have.attr", "role", "region")
.and("have.attr", "aria-label", "Footer");
});

it("tests footer region is not rendered when no footer slot is provided", () => {
cy.mount(
<Dialog id="dialog-no-footer" headerText="No Footer">
<div>Some content</div>
</Dialog>
);

cy.get("#dialog-no-footer")
.invoke("prop", "open", true);

cy.get<Dialog>("#dialog-no-footer").ui5DialogOpened();

cy.get("#dialog-no-footer")
.shadow()
.find(".ui5-popup-footer-root")
.should("not.exist");
});

});

describe("Page scrolling", () => {
Expand Down
19 changes: 19 additions & 0 deletions packages/main/src/Dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import {
DIALOG_HEADER_ARIA_DESCRIBEDBY_RESIZABLE,
DIALOG_HEADER_ARIA_DESCRIBEDBY_DRAGGABLE,
DIALOG_HEADER_ARIA_DESCRIBEDBY_DRAGGABLE_RESIZABLE,
DIALOG_HEADER_ARIA_LABEL,
DIALOG_CONTENT_ARIA_LABEL,
DIALOG_FOOTER_ARIA_LABEL,
} from "./generated/i18n/i18n-defaults.js";

// Template
Expand Down Expand Up @@ -327,6 +330,22 @@ class Dialog extends Popup {
return toLowercaseEnumValue(this.accessibleRole);
}

get _contentRole(): "region" {
return "region";
}

get _headerAriaLabel() {
return Dialog.i18nBundle.getText(DIALOG_HEADER_ARIA_LABEL);
}

get _contentAriaLabel() {
return Dialog.i18nBundle.getText(DIALOG_CONTENT_ARIA_LABEL);
}

get _footerAriaLabel() {
return Dialog.i18nBundle.getText(DIALOG_FOOTER_ARIA_LABEL);
}

_show() {
super._show();
this._center();
Expand Down
70 changes: 37 additions & 33 deletions packages/main/src/DialogTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,49 +14,53 @@ export default function DialogTemplate(this: Dialog) {
function beforeContent(this: Dialog) {
return (<>
{!!this._displayHeader &&
<header>
<div
class="ui5-popup-header-root"
id="ui5-popup-header"
role="group"
aria-describedby={this.effectiveAriaDescribedBy}
aria-roledescription={this.ariaRoleDescriptionHeaderText}
tabIndex={this._headerTabIndex}
onKeyDown={this._onDragOrResizeKeyDown}
onMouseDown={this._onDragMouseDown}
part="header"
// state={this.state}
>
{this.hasValueState &&
<Icon class="ui5-dialog-value-state-icon" name={this._dialogStateIcon}></Icon>
}
{this.header.length ?
<slot name="header"></slot>
:
<Title level="H1" id="ui5-popup-header-text" class="ui5-popup-header-text">{this.headerText}</Title>
}
<div
class="ui5-popup-header-root"
id="ui5-popup-header"
role="region"
aria-label={this._headerAriaLabel}
aria-describedby={this.effectiveAriaDescribedBy}
aria-roledescription={this.ariaRoleDescriptionHeaderText}
tabIndex={this._headerTabIndex}
onKeyDown={this._onDragOrResizeKeyDown}
onMouseDown={this._onDragMouseDown}
part="header"
// state={this.state}
>
{this.hasValueState &&
<Icon class="ui5-dialog-value-state-icon" name={this._dialogStateIcon}></Icon>
}
{this.header.length ?
<slot name="header"></slot>
:
<Title level="H1" id="ui5-popup-header-text" class="ui5-popup-header-text">{this.headerText}</Title>
}

{this.resizable ?
this.draggable ?
<span id={`${this._id}-descr`} aria-hidden="true" class="ui5-hidden-text">{this.ariaDescribedByHeaderTextDraggableAndResizable}</span>
:
<span id={`${this._id}-descr`} aria-hidden="true" class="ui5-hidden-text">{this.ariaDescribedByHeaderTextResizable}</span>
{this.resizable ?
this.draggable ?
<span id={`${this._id}-descr`} aria-hidden="true" class="ui5-hidden-text">{this.ariaDescribedByHeaderTextDraggableAndResizable}</span>
:
this.draggable &&
<span id={`${this._id}-descr`} aria-hidden="true" class="ui5-hidden-text">{this.ariaDescribedByHeaderTextDraggable}</span>
}
</div>
</header>
<span id={`${this._id}-descr`} aria-hidden="true" class="ui5-hidden-text">{this.ariaDescribedByHeaderTextResizable}</span>
:
this.draggable &&
<span id={`${this._id}-descr`} aria-hidden="true" class="ui5-hidden-text">{this.ariaDescribedByHeaderTextDraggable}</span>
}
</div>
}
</>);
}

function afterContent(this: Dialog) {
return (<>
{!!this.footer.length &&
<footer class="ui5-popup-footer-root" part="footer">
<div
class="ui5-popup-footer-root"
role="region"
aria-label={this._footerAriaLabel}
part="footer"
>
<slot name="footer"></slot>
</footer>
</div>
}
{this._showResizeHandle &&
<div
Expand Down
10 changes: 9 additions & 1 deletion packages/main/src/Popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { renderFinished } from "@ui5/webcomponents-base/dist/Render.js";
import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot-strict.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import type { ClassMap } from "@ui5/webcomponents-base/dist/types.js";
import type { ClassMap, AriaRole } from "@ui5/webcomponents-base/dist/types.js";
import jsxRender from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js";
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import type { DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js";
Expand Down Expand Up @@ -706,6 +706,14 @@ abstract class Popup extends UI5Element {
return (this.accessibleRole === PopupAccessibleRole.None) ? undefined : toLowercaseEnumValue(this.accessibleRole);
}

get _contentRole(): AriaRole | undefined {
return undefined;
}

get _contentAriaLabel(): string | undefined {
return undefined;
}

get _ariaModal(): "true" | undefined {
return this.accessibleRole === PopupAccessibleRole.None ? undefined : "true";
}
Expand Down
9 changes: 8 additions & 1 deletion packages/main/src/PopupTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,14 @@ export default function PopupTemplate(this: Popup, hooks?: {

{(hooks?.beforeContent || beforeContent).call(this)}

<div style={this.styles.content} class={this.classes.content} onScroll={this._scroll} part="content">
<div
style={this.styles.content}
class={this.classes.content}
role={this._contentRole}
aria-label={this._contentAriaLabel}
onScroll={this._scroll}
part="content"
>
<slot></slot>
</div>

Expand Down
9 changes: 9 additions & 0 deletions packages/main/src/i18n/messagebundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,15 @@ DIALOG_HEADER_ARIA_DESCRIBEDBY_DRAGGABLE=Use Arrow keys to move

#XACT: ARIA announcement for describedby attribute of draggable and resizable Dialog header
DIALOG_HEADER_ARIA_DESCRIBEDBY_DRAGGABLE_RESIZABLE=Use Arrow keys to move, Shift+Arrow keys to resize

#XACT: ARIA label for the Dialog header region
DIALOG_HEADER_ARIA_LABEL=Header

#XACT: ARIA label for the Dialog content region
DIALOG_CONTENT_ARIA_LABEL=Content

#XACT: ARIA label for the Dialog footer region
DIALOG_FOOTER_ARIA_LABEL=Footer
#XFLD: A colon to separate the "label" from an input. In some languages there might be a different symbol used for such a colon
LABEL_COLON=:

Expand Down
Loading