Skip to content

Commit dbfe7c5

Browse files
authored
feat(ui5-form, ui5-form-group): add accessibleNameRef (#12595)
Adds acessibleNameRef to both ui5-form, ui5-form-group web components.
1 parent b4e8489 commit dbfe7c5

File tree

3 files changed

+134
-32
lines changed

3 files changed

+134
-32
lines changed

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

Lines changed: 106 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,35 @@ describe("Accessibility", () => {
845845
.should("have.attr", "aria-label", "basic form");
846846
});
847847

848+
it("tests 'aria-label' via 'accessibleNameRef'", () => {
849+
cy.mount(
850+
<>
851+
<span id="lbl">basic form</span>
852+
<Form headerText="Form header text" accessibleNameRef="lbl">
853+
<FormItem>
854+
<Label>Name:</Label>
855+
<Text>Red Point Stores</Text>
856+
</FormItem>
857+
<FormItem>
858+
<Label>Twitter:</Label>
859+
<Text>@sap</Text>
860+
</FormItem>
861+
<FormItem>
862+
<Label>Name:</Label>
863+
<Text>Red Point Stores</Text>
864+
</FormItem>
865+
</Form>
866+
</>);
867+
868+
cy.get("[ui5-form]")
869+
.as("form");
870+
871+
cy.get("@form")
872+
.shadow()
873+
.find(".ui5-form-root")
874+
.should("have.attr", "aria-label", "basic form");
875+
});
876+
848877
describe("FormGroup accessibility", () => {
849878
it("tests 'aria-label' default", () => {
850879
cy.mount(<Form>
@@ -866,7 +895,7 @@ describe("Accessibility", () => {
866895
.should("have.attr", "aria-label", Form.i18nBundle.getText(FORM_GROUP_ACCESSIBLE_NAME, "1"));
867896
});
868897

869-
it("tests 'aria-label' via accessible-name", () => {
898+
it("tests 'aria-label', 'aria-labelledby' via 'accessibleName'", () => {
870899
const EXPECTED_LABEL = "Custom group label";
871900
cy.mount(<Form>
872901
<FormGroup accessibleName={EXPECTED_LABEL}>
@@ -884,10 +913,38 @@ describe("Accessibility", () => {
884913
.shadow()
885914
.find(".ui5-form-group-layout")
886915
.eq(0)
887-
.should("have.attr", "aria-label", EXPECTED_LABEL);
916+
// 'aria-label' is rendered in Shadow DOM when accessibleName or accessibleNameRef is set
917+
.should("have.attr", "aria-label", EXPECTED_LABEL)
918+
.should("not.have.attr", "aria-labelledby");
919+
});
920+
921+
it("tests 'aria-label', 'aria-labelledby' via 'accessibleNameRef'", () => {
922+
const EXPECTED_LABEL = "Custom group label";
923+
cy.mount(<>
924+
<span id="lbl">{EXPECTED_LABEL}</span>
925+
<Form>
926+
<FormGroup accessibleNameRef="lbl">
927+
<FormItem>
928+
<Label>Name:</Label>
929+
<Text>Red Point Stores</Text>
930+
</FormItem>
931+
</FormGroup>
932+
</Form>
933+
</>);
934+
935+
cy.get("[ui5-form]")
936+
.as("form");
937+
938+
cy.get("@form")
939+
.shadow()
940+
.find(".ui5-form-group-layout")
941+
.eq(0)
942+
// 'aria-label' is rendered in Shadow DOM when accessibleName or accessibleNameRef is set
943+
.should("have.attr", "aria-label", EXPECTED_LABEL)
944+
.should("not.have.attr", "aria-labelledby");
888945
});
889946

890-
it("tests 'aria-labelledby' via header-text", () => {
947+
it("tests 'aria-label', 'aria-labelledby' when 'headerText' present", () => {
891948
cy.mount(<Form>
892949
<FormGroup headerText="Custom header text">
893950
<FormItem>
@@ -914,48 +971,70 @@ describe("Accessibility", () => {
914971
.find(".ui5-form-group-heading [ui5-title]")
915972
.invoke("attr", "id")
916973
.should(id => {
974+
// aria-labelledby is used pointing to the header title ID
917975
expect(ariaLabelledBy).to.equal(id);
918976
});
919977
});
920978

979+
// no 'aria-label' when headerText is present, aria-labelledby is used instead
921980
cy.get("@group")
922981
.should("not.have.attr", "aria-label");
923982
});
924983

925-
it("tests 'aria-label' via accessible-name and header-text", () => {
984+
it("tests 'aria-label', 'aria-labelledby' via 'accessibleName' when 'headerText' present", () => {
926985
const EXPECTED_LABEL = "Custom group header";
927-
cy.mount(<Form>
928-
<FormGroup headerText="Custom header text" accessibleName={EXPECTED_LABEL}>
929-
<FormItem>
930-
<Label>Name:</Label>
931-
<Text>Red Point Stores</Text>
932-
</FormItem>
933-
</FormGroup>
934-
</Form>);
986+
cy.mount(
987+
<Form>
988+
<FormGroup headerText="Custom header text" accessibleName={EXPECTED_LABEL}>
989+
<FormItem>
990+
<Label>Name:</Label>
991+
<Text>Red Point Stores</Text>
992+
</FormItem>
993+
</FormGroup>
994+
</Form>
995+
);
935996

936997
cy.get("[ui5-form]")
937998
.as("form");
938999

1000+
// accessibleName has higher priority than headerText
9391001
cy.get("@form")
9401002
.shadow()
9411003
.find(".ui5-form-group-layout")
9421004
.eq(0)
943-
.as("group")
944-
.invoke("attr", "aria-labelledby")
945-
.then(ariaLabelledBy => {
946-
cy.get("@form")
947-
.shadow()
948-
.find(".ui5-form-group")
949-
.eq(0)
950-
.find(".ui5-form-group-heading [ui5-title]")
951-
.invoke("attr", "id")
952-
.should(id => {
953-
expect(ariaLabelledBy).to.equal(id);
954-
});
955-
});
1005+
// 'aria-label' is rendered in Shadow DOM when accessibleName or accessibleNameRef is set
1006+
.should("have.attr", "aria-label", EXPECTED_LABEL)
1007+
.should("not.have.attr", "aria-labelledby");
1008+
});
9561009

957-
cy.get("@group")
958-
.should("have.attr", "aria-label", EXPECTED_LABEL);
1010+
it("tests 'aria-label', 'aria-labelledby' via 'accessibleNameRef' when 'headerText' present", () => {
1011+
const EXPECTED_LABEL = "Custom group header";
1012+
cy.mount(
1013+
<>
1014+
<span id="lbl">{EXPECTED_LABEL}</span>
1015+
<Form>
1016+
<FormGroup headerText="Custom header text" accessibleNameRef="lbl">
1017+
1018+
<FormItem>
1019+
<Label>Name:</Label>
1020+
<Text>Red Point Stores</Text>
1021+
</FormItem>
1022+
</FormGroup>
1023+
</Form>
1024+
</>
1025+
);
1026+
1027+
cy.get("[ui5-form]")
1028+
.as("form");
1029+
1030+
// accessibleNameReg has higher priority than headerText
1031+
cy.get("@form")
1032+
.shadow()
1033+
.find(".ui5-form-group-layout")
1034+
.eq(0)
1035+
// 'aria-label' is rendered in Shadow DOM when accessibleName or accessibleNameRef is set
1036+
.should("have.attr", "aria-label", EXPECTED_LABEL)
1037+
.should("not.have.attr", "aria-labelledby");
9591038
});
9601039
});
9611040

packages/main/src/Form.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";
77
import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js";
88
import type { AriaRole } from "@ui5/webcomponents-base";
99
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
10+
import { getEffectiveAriaLabelText } from "@ui5/webcomponents-base/dist/util/AccessibilityTextsHelper.js";
1011

1112
// Template
1213
import FormTemplate from "./FormTemplate.js";
@@ -229,6 +230,15 @@ class Form extends UI5Element {
229230
@property()
230231
accessibleName?: string;
231232

233+
/**
234+
* Defines id (or many ids) of the element (or elements) that label the component.
235+
* @default undefined
236+
* @public
237+
* @since 2.16.0
238+
*/
239+
@property()
240+
accessibleNameRef?: string;
241+
232242
/**
233243
* Defines the accessibility mode of the component in "edit" and "display" use-cases.
234244
*
@@ -569,15 +579,15 @@ class Form extends UI5Element {
569579
}
570580

571581
get effectiveAccessibleName() {
572-
if (this.accessibleName) {
573-
return this.accessibleName;
582+
if (this.accessibleName || this.accessibleNameRef) {
583+
return getEffectiveAriaLabelText(this);
574584
}
575585

576586
return this.hasHeader ? undefined : Form.i18nBundle.getText(FORM_ACCESSIBLE_NAME);
577587
}
578588

579589
get effectiveАccessibleNameRef(): string | undefined {
580-
if (this.accessibleName) {
590+
if (this.accessibleName || this.accessibleNameRef) {
581591
return;
582592
}
583593

packages/main/src/FormGroup.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
44
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
55
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
66
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";
7+
import { getEffectiveAriaLabelText } from "@ui5/webcomponents-base/dist/util/AccessibilityTextsHelper.js";
78

89
import type FormItem from "./FormItem.js";
910
import type { IFormItem } from "./Form.js";
@@ -81,6 +82,15 @@ class FormGroup extends UI5Element implements IFormItem {
8182
@property()
8283
accessibleName?: string;
8384

85+
/**
86+
* Defines id (or many ids) of the element (or elements) that label the component.
87+
* @default undefined
88+
* @public
89+
* @since 2.16.0
90+
*/
91+
@property()
92+
accessibleNameRef?: string;
93+
8494
/**
8595
* Defines the items of the component.
8696
* @public
@@ -123,8 +133,8 @@ class FormGroup extends UI5Element implements IFormItem {
123133
}
124134

125135
getEffectiveAccessibleName(index: number) {
126-
if (this.accessibleName) {
127-
return this.accessibleName;
136+
if (this.accessibleName || this.accessibleNameRef) {
137+
return getEffectiveAriaLabelText(this);
128138
}
129139

130140
if (this.headerText) {
@@ -135,6 +145,9 @@ class FormGroup extends UI5Element implements IFormItem {
135145
}
136146

137147
get effectiveAccessibleNameRef() {
148+
if (this.accessibleName || this.accessibleNameRef) {
149+
return undefined;
150+
}
138151
return this.headerText ? `${this._id}-group-header-text` : undefined;
139152
}
140153

0 commit comments

Comments
 (0)