Skip to content
Open
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
59 changes: 30 additions & 29 deletions packages/main/cypress/specs/Dialog.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -580,11 +580,10 @@ describe("Dialog general interaction", () => {
const initialTop = parseInt(dialog.css("top"));
const initialLeft = parseInt(dialog.css("left"));

// Act - Move dialog up using keyboard
cy.get("#header-slot").realClick();

cy.get("#header-slot").focused().realPress("{uparrow}");
cy.get("#header-slot").focused().realPress("{uparrow}");
// Act - Tab to the drag/resize handle and move dialog up
cy.realPress("Tab");
cy.realPress("{uparrow}");
cy.realPress("{uparrow}");

// Assert - Top position changes, left remains the same

Expand All @@ -599,10 +598,8 @@ describe("Dialog general interaction", () => {
})

// Act - Move dialog left using keyboard
cy.get("#header-slot").realClick();

cy.get("#header-slot").focused().realPress("{leftarrow}");
cy.get("#header-slot").focused().realPress("{leftarrow}");
cy.realPress("{leftarrow}");
cy.realPress("{leftarrow}");

// Assert - Left position changes, top remains the same
cy.get("#draggable-dialog")
Expand Down Expand Up @@ -776,9 +773,9 @@ describe("Dialog general interaction", () => {
const initialTop = parseInt(dialog.css("top"));
const initialLeft = parseInt(dialog.css("left"));

// Act - Resize height using keyboard
cy.get("#resizable-dialog").shadow().find(".ui5-popup-resize-handle").click();
cy.get("#resizable-dialog").realPress(["Shift", "ArrowDown"]);
// Act - Tab to the drag/resize handle and resize height
cy.realPress("Tab");
cy.realPress(["Shift", "ArrowDown"]);

// Assert - Height changes, width and position remain the same
cy.get("#resizable-dialog").then(dialogAfterResizeHeight => {
Expand All @@ -791,8 +788,7 @@ describe("Dialog general interaction", () => {
expect(leftAfterResizeHeight).to.equal(initialLeft);

// Act - Resize width using keyboard
cy.get("#resizable-dialog").shadow().find(".ui5-popup-resize-handle").click();
cy.get("#resizable-dialog").realPress(["Shift", "ArrowRight"]);
cy.realPress(["Shift", "ArrowRight"]);

// Assert - Width changes, height and position remain the same
cy.get("#resizable-dialog").then(dialogAfterResizeWidth => {
Expand Down Expand Up @@ -1078,30 +1074,33 @@ describe("Acc", () => {
cy.get("#draggable-dialog").invoke("attr", "open", true);
cy.get<Dialog>("#draggable-dialog").ui5DialogOpened();

// Assert aria-labelledby and aria attributes
// Assert aria-label on the dialog root
cy.get("#draggable-dialog")
.shadow()
.find(".ui5-popup-root")
.should("have.attr", "aria-label", "Draggable");

// Assert aria-describedby is on the drag/resize handle, not the header
cy.get("#draggable-dialog")
.shadow()
.find(".ui5-popup-header-root")
.find(".ui5-popup-drag-resize-handler")
.should("have.attr", "aria-describedby");

// Assert hidden text contains keyboard instructions
cy.get("#draggable-dialog")
.shadow()
.find(".ui5-hidden-text")
.should("exist")
.then(hiddenText => {
const valueOfTheHiddenText = hiddenText.text();
const valueOfTheHiddenText = hiddenText.first().text();
cy.wrap(valueOfTheHiddenText).should("equal", "Use Arrow keys to move");
});

// Assert aria-roledescription on the drag/resize handle
cy.get("#draggable-dialog")
.shadow()
.find(".ui5-popup-header-root")
.should("have.attr", "aria-roledescription", "Interactive Header");
.find(".ui5-popup-drag-resize-handler")
.should("have.attr", "aria-roledescription", "Handle");
});

it("tests aria-describedby for default header", () => {
Expand All @@ -1119,26 +1118,27 @@ describe("Acc", () => {
cy.get("#resizable-dialog").invoke("attr", "open", true);
cy.get<Dialog>("#resizable-dialog").ui5DialogOpened();

// Assert aria-describedby and aria-roledescription attributes
// Assert aria-describedby is on the drag/resize handle
cy.get("#resizable-dialog")
.shadow()
.find(".ui5-popup-header-root")
.find(".ui5-popup-drag-resize-handler")
.should("have.attr", "aria-describedby")
.then($el => {
cy.get("#resizable-dialog")
.shadow()
.find(".ui5-hidden-text")
.should("exist")
.then(hiddenText => {
const valueOfTheHiddenText = hiddenText.text();
const valueOfTheHiddenText = hiddenText.first().text();
cy.wrap(valueOfTheHiddenText).should("equal", "Use Shift+Arrow keys to resize");
});
});

// Assert aria-roledescription on the drag/resize handle
cy.get("#resizable-dialog")
.shadow()
.find(".ui5-popup-header-root")
.should("have.attr", "aria-roledescription", "Interactive Header");
.find(".ui5-popup-drag-resize-handler")
.should("have.attr", "aria-roledescription", "Handle");

});

Expand All @@ -1157,26 +1157,27 @@ describe("Acc", () => {
cy.get("#resizable-dialog-custom-header").invoke("attr", "open", true);
cy.get<Dialog>("#resizable-dialog-custom-header").ui5DialogOpened();

// Assert aria-describedby and aria-roledescription attributes
// Assert aria-describedby is on the drag/resize handle
cy.get("#resizable-dialog-custom-header")
.shadow()
.find(".ui5-popup-header-root")
.find(".ui5-popup-drag-resize-handler")
.should("have.attr", "aria-describedby")
.then($el => {
cy.get("#resizable-dialog-custom-header")
.shadow()
.find(".ui5-hidden-text")
.should("exist")
.then(hiddenText => {
const valueOfTheHiddenText = hiddenText.text();
const valueOfTheHiddenText = hiddenText.first().text();
cy.wrap(valueOfTheHiddenText).should("equal", "Use Shift+Arrow keys to resize");
});
});

// Assert aria-roledescription on the drag/resize handle
cy.get("#resizable-dialog-custom-header")
.shadow()
.find(".ui5-popup-header-root")
.should("have.attr", "aria-roledescription", "Interactive Header");
.find(".ui5-popup-drag-resize-handler")
.should("have.attr", "aria-roledescription", "Handle");
});

it("tests accessibleName-ref", () => {
Expand Down
79 changes: 73 additions & 6 deletions packages/main/src/Dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,17 @@ import "@ui5/webcomponents-icons/dist/sys-enter-2.js";
import "@ui5/webcomponents-icons/dist/information.js";

import {
DIALOG_HEADER_ARIA_ROLE_DESCRIPTION,
DIALOG_HEADER_ARIA_DESCRIBEDBY_RESIZABLE,
DIALOG_HEADER_ARIA_DESCRIBEDBY_DRAGGABLE,
DIALOG_HEADER_ARIA_DESCRIBEDBY_DRAGGABLE_RESIZABLE,
DIALOG_ARIA_DESCRIBEDBY_DRAGGABLE_RESIZABLE,
DIALOG_ARIA_DESCRIBEDBY_DRAGGABLE,
DIALOG_ARIA_DESCRIBEDBY_RESIZABLE,
DIALOG_RESIZE_HANDLE_TOOLTIP,
DIALOG_DRAG_AND_RESIZE_HANDLE_ARIA_LABEL,
DIALOG_DRAG_HANDLE_ARIA_LABEL,
DIALOG_RESIZE_HANDLE_ARIA_LABEL,
DIALOG_HANDLE_ARIA_ROLEDESCRIPTION,
} from "./generated/i18n/i18n-defaults.js";

// Template
Expand Down Expand Up @@ -252,12 +259,33 @@ class Dialog extends Popup {
return ariaLabelledById;
}

get ariaRoleDescriptionHeaderText() {
return (this.resizable || this.draggable) ? Dialog.i18nBundle.getText(DIALOG_HEADER_ARIA_ROLE_DESCRIPTION) : undefined;
get effectiveAriaDescribedBy() {
return this._movable ? `${this._id}-dialog-descr` : undefined;
}

get effectiveAriaDescribedBy() {
return (this.resizable || this.draggable) ? `${this._id}-descr` : undefined;
get ariaDescribedByIds() {
return [
this.ariaDescriptionTextId,
this.effectiveAriaDescribedBy,
].filter(Boolean).join(" ");
}

get dialogAriaDescribedByText() {
if (!this._movable) {
return "";
}

if (this.resizable && this.draggable) {
return Dialog.i18nBundle.getText(DIALOG_ARIA_DESCRIBEDBY_DRAGGABLE_RESIZABLE);
}
if (this.draggable) {
return Dialog.i18nBundle.getText(DIALOG_ARIA_DESCRIBEDBY_DRAGGABLE);
}
if (this.resizable) {
return Dialog.i18nBundle.getText(DIALOG_ARIA_DESCRIBEDBY_RESIZABLE);
}

return "";
}

get ariaDescribedByHeaderTextResizable() {
Expand All @@ -284,13 +312,47 @@ class Dialog extends Popup {
}

get _headerTabIndex() {
return undefined;
}

get _dragResizeHandleTabIndex() {
return this._movable ? 0 : undefined;
}

get _dragResizeHandleAriaLabel() {
if (!this._movable) {
return "";
}

if (this.resizable && this.draggable) {
return Dialog.i18nBundle.getText(DIALOG_DRAG_AND_RESIZE_HANDLE_ARIA_LABEL);
}
if (this.draggable) {
return Dialog.i18nBundle.getText(DIALOG_DRAG_HANDLE_ARIA_LABEL);
}
if (this.resizable) {
return Dialog.i18nBundle.getText(DIALOG_RESIZE_HANDLE_ARIA_LABEL);
}

return "";
}

get _dragResizeHandleAriaRoleDescription() {
return this._movable ? Dialog.i18nBundle.getText(DIALOG_HANDLE_ARIA_ROLEDESCRIPTION) : undefined;
}

get _dragResizeHandleAriaDescribedBy() {
return this._movable ? `${this._id}-descr` : undefined;
}

get _showResizeHandle() {
return this.resizable && this.onDesktop;
}

get _resizeHandleTooltip() {
return this._showResizeHandle ? Dialog.i18nBundle.getText(DIALOG_RESIZE_HANDLE_TOOLTIP) : undefined;
}

get _minHeight() {
let minHeight = Number.parseInt(window.getComputedStyle(this.contentDOM).minHeight);

Expand Down Expand Up @@ -470,7 +532,12 @@ class Dialog extends Popup {
}

_onDragOrResizeKeyDown(e: KeyboardEvent) {
if (!this._movable || !Dialog._isHeader(e.target as HTMLElement)) {
if (!this._movable) {
return;
}

const target = e.target as HTMLElement;
if (!target || target.id !== `${this._id}-dragResizeHandler`) {
return;
}

Expand Down
42 changes: 27 additions & 15 deletions packages/main/src/DialogTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ function beforeContent(this: Dialog) {
<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}
Expand All @@ -35,16 +30,6 @@ function beforeContent(this: Dialog) {
:
<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.draggable &&
<span id={`${this._id}-descr`} aria-hidden="true" class="ui5-hidden-text">{this.ariaDescribedByHeaderTextDraggable}</span>
}
</div>
</header>
}
Expand All @@ -62,9 +47,36 @@ function afterContent(this: Dialog) {
<div
class="ui5-popup-resize-handle"
onMouseDown={this._onResizeMouseDown}
title={this._resizeHandleTooltip}
>
<Icon name={resizeCorner}></Icon>
</div>
}
{this._movable &&
<>
<span
id={`${this._id}-dragResizeHandler`}
class="ui5-popup-drag-resize-handler"
tabIndex={this._dragResizeHandleTabIndex}
role="img"
aria-roledescription={this._dragResizeHandleAriaRoleDescription}
aria-label={this._dragResizeHandleAriaLabel}
aria-describedby={this._dragResizeHandleAriaDescribedBy}
onKeyDown={this._onDragOrResizeKeyDown}
></span>
{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.draggable &&
<span id={`${this._id}-descr`} aria-hidden="true" class="ui5-hidden-text">{this.ariaDescribedByHeaderTextDraggable}</span>
}
{this.dialogAriaDescribedByText &&
<span id={`${this._id}-dialog-descr`} aria-hidden="true" class="ui5-hidden-text">{this.dialogAriaDescribedByText}</span>
}
</>
}
</>);
}
25 changes: 25 additions & 0 deletions packages/main/src/i18n/messagebundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,31 @@ 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 announcement for describedby attribute informing users how to reach drag and resize handle
DIALOG_ARIA_DESCRIBEDBY_DRAGGABLE_RESIZABLE=Tab to the end of dialog to reach drag and resize handle.

#XACT: ARIA announcement for describedby attribute informing users how to reach drag handle
DIALOG_ARIA_DESCRIBEDBY_DRAGGABLE=Tab to the end of dialog to reach drag handle.

#XACT: ARIA announcement for describedby attribute informing users how to reach resize handle
DIALOG_ARIA_DESCRIBEDBY_RESIZABLE=Tab to the end of dialog to reach resize handle.

#XACT: Tooltip for the resize handle icon in the Dialog
DIALOG_RESIZE_HANDLE_TOOLTIP=Drag handle to resize (Shift+Arrow)

#XACT: ARIA label for drag and resize handle
DIALOG_DRAG_AND_RESIZE_HANDLE_ARIA_LABEL=Drag and resize dialog

#XACT: ARIA label for drag handle
DIALOG_DRAG_HANDLE_ARIA_LABEL=Drag dialog

#XACT: ARIA label for resize handle
DIALOG_RESIZE_HANDLE_ARIA_LABEL=Resize dialog

#XACT: ARIA role description for drag/resize handle
DIALOG_HANDLE_ARIA_ROLEDESCRIPTION=Handle

#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
Loading