Skip to content

Commit 30fdb8e

Browse files
fix(ui5-checkbox): make acc configurable (#11773)
1 parent 88617c7 commit 30fdb8e

File tree

9 files changed

+112
-8
lines changed

9 files changed

+112
-8
lines changed

packages/base/src/jsx-runtime.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ declare namespace JSX {
2222
export interface DOMAttributes<T extends EventTarget> extends _JSX.DOMAttributes<T> {}
2323
export interface SVGAttributes extends _JSX.SVGAttributes {}
2424
export type AriaRole = _JSX.AriaRole;
25+
export type AriaAttributes = _JSX.AriaAttributes;
2526
export type MouseEventHandler<T extends EventTarget> = _JSX.MouseEventHandler<T>;
2627
export type TargetedMouseEvent<Target extends EventTarget> = _JSX.TargetedMouseEvent<Target>;
2728
export type TargetedInputEvent<Target extends EventTarget> = _JSX.TargetedInputEvent<Target>;

packages/base/src/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ export type PassiveEventListenerObject = EventListenerObject & { passive: boolea
1919

2020
// Accessibility
2121
export type AriaRole = JSX.AriaRole;
22+
export type AriaDisabled = JSX.AriaAttributes["aria-disabled"];
23+
export type AriaChecked = JSX.AriaAttributes["aria-checked"];
24+
export type AriaReadonly = JSX.AriaAttributes["aria-readonly"];
2225
export type AriaHasPopup = "dialog" | "grid" | "listbox" | "menu" | "tree";
2326
export type AriaCurrent = "page" | "step" | "location" | "date" | "time" | "true" | "false" | boolean | undefined;
2427
export type AriaAutoComplete = "list" | "none" | "inline" | "both" | undefined;

packages/main/cypress/specs/MultiComboBox.cy.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,4 +582,46 @@ describe("Accessibility", () => {
582582
.find("input")
583583
.should("have.attr", "aria-label", label);
584584
});
585+
586+
it("checkbox should be presentational", () => {
587+
cy.mount(
588+
<MultiComboBox>
589+
<MultiComboBoxItem text="Algeria"></MultiComboBoxItem>
590+
<MultiComboBoxItem text="Bulgaria"></MultiComboBoxItem>
591+
<MultiComboBoxItem text="England"></MultiComboBoxItem>
592+
</MultiComboBox>
593+
);
594+
595+
cy.get("ui5-multi-combobox")
596+
.as("multiComboBox");
597+
598+
cy.get("@multiComboBox")
599+
.shadow()
600+
.find("ui5-icon")
601+
.as("icon");
602+
603+
cy.get("@icon")
604+
.click();
605+
606+
cy.get("ui5-mcb-item")
607+
.eq(0)
608+
.shadow()
609+
.find("ui5-checkbox")
610+
.as("checkbox");
611+
612+
cy.get("@checkbox")
613+
.shadow()
614+
.find("input[type='checkbox']")
615+
.should("not.exist");
616+
617+
cy.get("@checkbox")
618+
.shadow()
619+
.find(".ui5-checkbox-root")
620+
.should("have.attr", "role", "presentation");
621+
622+
cy.get("@checkbox")
623+
.shadow()
624+
.find(".ui5-checkbox-root")
625+
.should("not.have.attr", "tabindex");
626+
});
585627
});

packages/main/src/CheckBox.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ import { getEffectiveAriaLabelText } from "@ui5/webcomponents-base/dist/util/Acc
1111
import { isSpace, isEnter } from "@ui5/webcomponents-base/dist/Keys.js";
1212
import type { IFormInputElement } from "@ui5/webcomponents-base/dist/features/InputElementsFormSupport.js";
1313
import type WrappingType from "./types/WrappingType.js";
14+
import type {
15+
AriaRole,
16+
AriaChecked,
17+
AriaDisabled,
18+
AriaReadonly,
19+
} from "@ui5/webcomponents-base/dist/types.js";
1420
import {
1521
VALUE_STATE_ERROR,
1622
VALUE_STATE_WARNING,
@@ -27,6 +33,15 @@ import CheckBoxTemplate from "./CheckBoxTemplate.js";
2733
let isGlobalHandlerAttached = false;
2834
let activeCb: CheckBox;
2935

36+
type CheckBoxAccInfo = {
37+
role?: AriaRole,
38+
ariaChecked?: AriaChecked,
39+
ariaReadonly?: AriaReadonly,
40+
ariaDisabled?: AriaDisabled,
41+
ariaRequired?: boolean,
42+
tabindex?: number | undefined,
43+
}
44+
3045
/**
3146
* @class
3247
*
@@ -254,6 +269,13 @@ class CheckBox extends UI5Element implements IFormInputElement {
254269
@property({ type: Boolean })
255270
active = false;
256271

272+
/**
273+
* Defines custom aria implementation object.
274+
* @private
275+
*/
276+
@property({ type: Object })
277+
_accInfo?: CheckBoxAccInfo;
278+
257279
@i18n("@ui5/webcomponents")
258280
static i18nBundle: I18nBundle;
259281
_deactivate: () => void;
@@ -442,6 +464,17 @@ class CheckBox extends UI5Element implements IFormInputElement {
442464
get isDisplayOnly() {
443465
return this.displayOnly && !this.disabled;
444466
}
467+
468+
get accInfo() {
469+
return {
470+
"role": this._accInfo ? this._accInfo.role : "checkbox" as AriaRole,
471+
"ariaChecked": this._accInfo ? this._accInfo.ariaChecked : this.effectiveAriaChecked as AriaChecked,
472+
"ariaReadonly": this._accInfo ? this._accInfo.ariaReadonly : this.ariaReadonly as AriaReadonly,
473+
"ariaDisabled": this._accInfo ? this._accInfo.ariaDisabled : this.effectiveAriaDisabled as AriaDisabled,
474+
"ariaRequired": this._accInfo ? this._accInfo.ariaRequired : this.required,
475+
"tabindex": this._accInfo ? this._accInfo.tabindex : this.effectiveTabIndex,
476+
};
477+
}
445478
}
446479

447480
CheckBox.define();

packages/main/src/CheckBoxTemplate.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@ export default function CheckBoxTemplate(this: CheckBox) {
1515
"ui5-checkbox-root": true,
1616
"ui5-checkbox--hoverable": !this.disabled && !this.readonly && isDesktop(),
1717
}}
18-
role="checkbox"
18+
role={this.accInfo.role}
1919
part="root"
20-
aria-checked={this.effectiveAriaChecked}
21-
aria-readonly={this.ariaReadonly}
22-
aria-disabled={this.effectiveAriaDisabled}
20+
aria-checked={this.accInfo.ariaChecked}
21+
aria-readonly={this.accInfo.ariaReadonly}
22+
aria-disabled={this.accInfo.ariaDisabled}
2323
aria-label={this.ariaLabelText}
2424
aria-labelledby={this.ariaLabelledBy}
2525
aria-describedby={this.ariaDescribedBy}
26-
aria-required={this.required}
27-
tabindex={this.effectiveTabIndex}
26+
aria-required={this.accInfo.ariaRequired}
27+
tabindex={this.accInfo.tabindex}
2828
onMouseDown={this._onmousedown}
2929
onMouseUp={this._onmouseup}
3030
onKeyDown={this._onkeydown}
@@ -45,6 +45,7 @@ export default function CheckBoxTemplate(this: CheckBox) {
4545
</div>
4646
}
4747

48+
{this.accInfo.role === "checkbox" &&
4849
<input
4950
id={`${this._id}-CB`}
5051
type="checkbox"
@@ -56,6 +57,7 @@ export default function CheckBoxTemplate(this: CheckBox) {
5657
aria-hidden="true"
5758
data-sap-no-tab-ref
5859
/>
60+
}
5961

6062
{this.text &&
6163
<Label

packages/main/src/MultiComboBoxItem.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
import styles from "./generated/themes/MultiComboBoxItem.css.js";
1616
import MultiComboBoxItemTemplate from "./MultiComboBoxItemTemplate.js";
1717
import type { SelectionRequestEventDetail } from "./ListItem.js";
18+
import type { AriaRole } from "@ui5/webcomponents-base";
1819

1920
/**
2021
* @class
@@ -74,6 +75,12 @@ class MultiComboBoxItem extends ComboBoxItem implements IMultiComboBoxItem {
7475
get _accessibleName() {
7576
return MultiComboBoxItem.i18nBundle.getText(ARIA_LABEL_LIST_ITEM_CHECKBOX);
7677
}
78+
79+
get checkBoxAccInfo() {
80+
return {
81+
role: "presentation" as AriaRole,
82+
};
83+
}
7784
}
7885

7986
const isInstanceOfMultiComboBoxItem = (object: any): object is MultiComboBoxItem => {

packages/main/src/MultiComboBoxItemTemplate.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ function listItemContent(this: MultiComboBoxItem) {
1212
<CheckBox
1313
disabled={this._readonly}
1414
checked={this.selected}
15-
tabindex={-1}
16-
accessibleName={this._accessibleName}
15+
_accInfo = {this.checkBoxAccInfo}
1716
/>
1817
<div part="content" id="content" class="ui5-li-content">
1918
<div class="ui5-li-text-wrapper">

packages/main/test/pages/CheckBox.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@
6262
<ui5-checkbox value-state="Positive" text="Long long long text" indeterminate checked></ui5-checkbox>
6363
<ui5-checkbox value-state="Information" text="Long long long text" indeterminate checked></ui5-checkbox>
6464

65+
<ui5-title id="cb-label">ACC Test - custom acc</ui5-title>
66+
<ui5-checkbox id="accCustomAria"></ui5-checkbox>
67+
6568
<br />
6669
<ui5-checkbox id="checkboxChecked" checked></ui5-checkbox>
6770

@@ -101,8 +104,13 @@
101104
var checkBox2 = document.querySelector("#cb2");
102105
var displayOnlyCb = document.querySelector("#displayOnlyCb");
103106
var cbFormSubmitted = document.querySelector("#cbFormSubmitted");
107+
var cdCustomAria = document.querySelector("#accCustomAria");
104108
var counter = 0;
105109

110+
cdCustomAria._accInfo = {
111+
role: "presentation",
112+
};
113+
106114
[checkBox1, checkBox2, displayOnlyCb].forEach(function(el) {
107115
el.addEventListener("ui5-change", function(event) {
108116
counter += 1;

packages/main/test/specs/CheckBox.spec.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,15 @@ describe("CheckBox general interaction", () => {
9999
assert.strictEqual(await accNameRefCheckBox.getAttribute("aria-label"), EXPECTED_ARIA_LABEL_NAME_REF, "aria-label is set");
100100
});
101101

102+
it("tests _accInfo", async () => {
103+
const cbCustomAria = await browser.$("#accCustomAria").shadow$(".ui5-checkbox-root");
104+
105+
const EXPECTED_ROLE = "presentation";
106+
107+
assert.strictEqual(await cbCustomAria.getAttribute("role"), EXPECTED_ROLE, "aria role is custom set");
108+
assert.notOk(await cbCustomAria.getAttribute("aria-checked"), "aria-checked should not be rendered if not set in _accInfo");
109+
});
110+
102111
it("tests ui5-icon", async () => {
103112
const checkboxChecked = await browser.$("#checkboxChecked").shadow$(".ui5-checkbox-icon");
104113

0 commit comments

Comments
 (0)