Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: A11Y: Radiogroup and Checkbox (voice over + better structure) #6937

Merged
merged 14 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
ca1f63c
work for the https://github.com/surveyjs/survey-library/issues/6859
dmitry-kurmanov Sep 7, 2023
33ab548
Merge branch 'master' into bug/6859-a11y-radiogroup-checkbox-voice-over
dmitry-kurmanov Sep 7, 2023
7d7f80d
work for the https://github.com/surveyjs/survey-library/issues/6859 (…
dmitry-kurmanov Sep 8, 2023
549fd9b
Merge branch 'master' into bug/6859-a11y-radiogroup-checkbox-voice-over
dmitry-kurmanov Sep 8, 2023
fdeba83
work for the https://github.com/surveyjs/survey-library/issues/6859 (…
dmitry-kurmanov Sep 8, 2023
44ee73a
work for the https://github.com/surveyjs/survey-library/issues/6859 (…
dmitry-kurmanov Sep 8, 2023
c2adcde
work for the https://github.com/surveyjs/survey-library/issues/6859 (…
dmitry-kurmanov Sep 11, 2023
26b721e
work for the https://github.com/surveyjs/survey-library/issues/6859 (…
dmitry-kurmanov Sep 11, 2023
d92d85e
Merge branch 'master' into bug/6859-a11y-radiogroup-checkbox-voice-over
dmitry-kurmanov Sep 11, 2023
e54cac3
work for the https://github.com/surveyjs/survey-library/issues/6859 (…
dmitry-kurmanov Sep 11, 2023
e5bdd88
Merge branch 'master' into bug/6859-a11y-radiogroup-checkbox-voice-over
dmitry-kurmanov Sep 12, 2023
f924a72
work for the https://github.com/surveyjs/survey-library/issues/6859 (…
dmitry-kurmanov Sep 12, 2023
a6f0415
Merge branch 'master' into bug/6859-a11y-radiogroup-checkbox-voice-over
dmitry-kurmanov Sep 13, 2023
7e401c3
work for the https://github.com/surveyjs/survey-library/issues/6859 (…
dmitry-kurmanov Sep 13, 2023
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<textarea *ngIf="!question.isReadOnlyRenderDiv()" [id]="otherId" [attr.maxlength]="question.getOthersMaxLength()" [attr.aria-required]="question.ariaRequired" [attr.aria-label]="question.ariaLabel" [attr.placeholder]="otherPlaceholder"
<textarea *ngIf="!question.isReadOnlyRenderDiv()" [id]="otherId" [attr.maxlength]="question.getOthersMaxLength()" [attr.aria-required]="question.ariaRequired || question.a11y_input_ariaRequired" [attr.aria-label]="question.ariaLabel || question.a11y_input_ariaLabel" [attr.placeholder]="otherPlaceholder"
[value]="otherValue"
dmitry-kurmanov marked this conversation as resolved.
Show resolved Hide resolved
[style.resize]="question.resizeStyle"
[disabled]="question.isInputReadOnly"
Expand Down
2 changes: 1 addition & 1 deletion packages/survey-angular-ui/src/comment.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<textarea *ngIf="!question.isReadOnlyRenderDiv()" [id]="question.commentId" [attr.maxlength]="question.getOthersMaxLength()" [attr.aria-required]="question.ariaRequired" [attr.aria-label]="question.ariaLabel" [attr.placeholder]="question.renderedCommentPlaceholder"
<textarea *ngIf="!question.isReadOnlyRenderDiv()" [id]="question.commentId" [attr.maxlength]="question.getOthersMaxLength()" [attr.aria-required]="question.ariaRequired || question.a11y_input_ariaRequired" [attr.aria-label]="question.ariaLabel || question.a11y_input_ariaLabel" [attr.placeholder]="question.renderedCommentPlaceholder"
[value]="comment"
[style.resize]="question.resizeStyle"
[disabled]="question.isInputReadOnly"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<input *ngIf="model == question.selectAllItem" role="option" type="checkbox" [name]="question.name" [id]="question.getItemId(model)" [attr.aria-describedby]="question.ariaDescribedBy" [class]="question.cssClasses.itemControl"
<input *ngIf="model == question.selectAllItem" type="checkbox" [name]="question.name" [id]="question.getItemId(model)" [class]="question.cssClasses.itemControl"
[disabled]="!question.getItemEnabled(model)" [checked]="question.isAllSelected" [value]="''" (change)="onSelectAllChange($event)"/>
<input *ngIf="model != question.selectAllItem" role="option" type="checkbox" [name]="question.name" [id]="question.getItemId(model)" [attr.aria-describedby]="question.ariaDescribedBy" [class]="question.cssClasses.itemControl"
<input *ngIf="model != question.selectAllItem" type="checkbox" [name]="question.name" [id]="question.getItemId(model)" [class]="question.cssClasses.itemControl"
[disabled]="!question.getItemEnabled(model)" [checked]="question.isItemSelected(model)" [value]="model.value" (change)="onChange($event)"/>
<ng-content></ng-content>
4 changes: 2 additions & 2 deletions packages/survey-angular-ui/src/questions/selectbase-item.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<ng-template #template>
<div role="presentation" [class]="question.getItemClass(model)">
<ng-container [ngSwitch]="inputType">
<label *ngSwitchCase="'checkbox'" [class]="question.getLabelClass(model)" [attr.aria-label]="question.getAriaItemLabel(model)" [model]="model" [question]="question" sv-ng-checkbox-item>
<label *ngSwitchCase="'checkbox'" [class]="question.getLabelClass(model)" [model]="model" [question]="question" sv-ng-checkbox-item>
<ng-container *ngTemplateOutlet="itemDecorator"></ng-container>
</label>
<label *ngSwitchCase="'radio'" (mousedown)="question.onMouseDown()" [class]="question.getLabelClass(model)" [attr.aria-label]="question.getAriaItemLabel(model)" [model]="model" [question]="question" sv-ng-radiogroup-item>
<label *ngSwitchCase="'radio'" (mousedown)="question.onMouseDown()" [class]="question.getLabelClass(model)" [model]="model" [question]="question" sv-ng-radiogroup-item>
<ng-container *ngTemplateOutlet="itemDecorator"></ng-container>
</label>
</ng-container>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
<fieldset [class]="model.getSelectBaseRootCss()" role="presentation" #contentElement>
<fieldset [class]="model.getSelectBaseRootCss()" #contentElement
[attr.role]="model.a11y_input_ariaRole"
[attr.aria-required]="model.a11y_input_ariaRequired"
[attr.aria-label]="model.a11y_input_ariaLabel"
[attr.aria-labelledby]="model.a11y_input_ariaLabelledBy"
[attr.aria-invalid]="model.a11y_input_ariaInvalid"
[attr.aria-describedby]="model.a11y_input_ariaDescribedBy"
>
<legend *ngIf="showLegend" role="presentation" class="sv-hidden"></legend>
<ng-container *ngIf="model.hasHeadItems">
<ng-container *ngFor="let item of model.headItems; trackBy: trackItemBy">
Expand Down
9 changes: 8 additions & 1 deletion packages/survey-vue3-ui/src/Checkbox.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
<template>
<fieldset :class="question.getSelectBaseRootCss()" role="presentation">
<fieldset :class="question.getSelectBaseRootCss()"
:role="question.a11y_input_ariaRole"
:aria-required="question.a11y_input_ariaRequired"
:aria-label="question.a11y_input_ariaLabel"
:aria-labelledby="question.a11y_input_ariaLabelledBy"
:aria-invalid="question.a11y_input_ariaInvalid"
:aria-describedby="question.a11y_input_ariaDescribedBy"
>
<legend role="presentation" class="sv-hidden"></legend>
<template v-if="question.hasHeadItems">
<survey-checkbox-item
Expand Down
5 changes: 0 additions & 5 deletions packages/survey-vue3-ui/src/CheckboxItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,24 @@
<div role="presentation">
<label
:class="question.getLabelClass(item)"
:aria-label="question.getAriaItemLabel(item)"
>
<input
v-if="item == question.selectAllItem"
role="option"
type="checkbox"
:name="question.name"
:value="isAllSelected"
v-model="isAllSelected"
:id="question.getItemId(item)"
:disabled="!question.getItemEnabled(item)"
:aria-describedby="question.ariaDescribedBy"
:class="question.cssClasses.itemControl"
/><input
v-if="item != question.selectAllItem"
role="option"
type="checkbox"
:name="question.name"
:value="item.value"
v-model="question.renderedValue"
:id="question.getItemId(item)"
:disabled="!question.getItemEnabled(item)"
:aria-describedby="question.ariaDescribedBy"
:class="question.cssClasses.itemControl"
/><span
v-if="question.cssClasses.materialDecorator"
Expand Down
4 changes: 2 additions & 2 deletions packages/survey-vue3-ui/src/QuestionComment.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
:value="question.comment"
:maxlength="question.getOthersMaxLength()"
:placeholder="question.renderedCommentPlaceholder"
:aria-label="question.ariaLabel"
:aria-required="question.ariaRequired"
:aria-label="question.ariaLabel || question.a11y_input_ariaLabel"
dmitry-kurmanov marked this conversation as resolved.
Show resolved Hide resolved
:aria-required="question.ariaRequired || question.a11y_input_ariaRequired"
v-bind:style="{ resize: question.resizeStyle }"
@change="
(e) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/survey-vue3-ui/src/QuestionOther.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
:value="question.otherValue"
:maxlength="question.getOthersMaxLength()"
:placeholder="question.otherPlaceholder"
:aria-label="question.ariaLabel"
:aria-required="question.ariaRequired"
:aria-label="question.ariaLabel || question.a11y_input_ariaLabel"
:aria-required="question.ariaRequired || question.a11y_input_ariaRequired"
v-bind:style="{ resize: question.resizeStyle }"
@change="
(e) => {
Expand Down
7 changes: 6 additions & 1 deletion packages/survey-vue3-ui/src/Radiogroup.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
<template>
<fieldset
role="presentation"
:class="question.getSelectBaseRootCss()"
ref="root"
:role="question.a11y_input_ariaRole"
:aria-required="question.a11y_input_ariaRequired"
:aria-label="question.a11y_input_ariaLabel"
:aria-labelledby="question.a11y_input_ariaLabelledBy"
:aria-invalid="question.a11y_input_ariaInvalid"
:aria-describedby="question.a11y_input_ariaDescribedBy"
>
<survey-radiogroup-item
v-if="!question.hasColumns && !question.blockedRow"
Expand Down
1 change: 0 additions & 1 deletion packages/survey-vue3-ui/src/RadiogroupItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
<label
@mousedown="question.onMouseDown()"
:class="getLabelClass(item)"
:aria-label="question.getAriaItemLabel(item)"
>
<input
type="radio"
Expand Down
4 changes: 2 additions & 2 deletions src/knockout/templates/comment.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script type="text/html" id="survey-comment">
<!--ko if: !question.isReadOnlyRenderDiv() -->
<textarea data-bind="attr: { id: question.commentId, maxLength: question.getOthersMaxLength(), 'aria-required': question.ariaRequired, 'aria-label': question.ariaLabel, placeholder: question.renderedCommentPlaceholder },
<textarea data-bind="attr: { id: question.commentId, maxLength: question.getOthersMaxLength(), 'aria-required': question.ariaRequired || question.a11y_input_ariaRequired, 'aria-label': question.ariaLabel || question.a11y_input_ariaLabel, placeholder: question.renderedCommentPlaceholder },
event: { input: function(s, e) { $data.question.onCommentInput(s, e); } },
value: $data.question.comment,
visible: $data.visible,
Expand All @@ -15,7 +15,7 @@
</script>
<script type="text/html" id="survey-other">
<!--ko if: !question.isReadOnlyRenderDiv() -->
<textarea data-bind="attr: { id: question.otherId, maxLength: question.getOthersMaxLength(), 'aria-required': question.ariaRequired, 'aria-label': question.ariaLabel, placeholder: question.otherPlaceholder },
<textarea data-bind="attr: { id: question.otherId, maxLength: question.getOthersMaxLength(), 'aria-required': question.ariaRequired || question.a11y_input_ariaRequired, 'aria-label': question.ariaLabel || question.a11y_input_ariaLabel, placeholder: question.otherPlaceholder },
event: { input: function(s, e) { $data.question.onOtherValueInput(s, e); } },
value: $data.question.otherValue,
visible: $data.visible,
Expand Down
8 changes: 4 additions & 4 deletions src/knockout/templates/question-checkbox.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script type="text/html" id="survey-question-checkbox">
<fieldset data-bind="css: question.getSelectBaseRootCss()" role="presentation">
<fieldset data-bind="css: question.getSelectBaseRootCss(), attr: { 'role': question.a11y_input_ariaRole, 'aria-required': question.a11y_input_ariaRequired, 'aria-labelledby': question.a11y_input_ariaLabelledBy, 'aria-invalid': question.a11y_input_ariaInvalid, 'aria-describedby': question.a11y_input_ariaDescribedBy, 'aria-label': question.a11y_input_ariaLabel }">
<legend role="presentation" class="sv-hidden"></legend>
<!-- ko if: question.hasHeadItems -->
<!-- ko foreach: { data: question.headItems, as: 'item', afterRender: question.koAfterRender } -->
Expand Down Expand Up @@ -48,12 +48,12 @@
</script>
<script type="text/html" id="survey-checkbox-item">
<div role="presentation" data-bind="css: question.getItemClass($data)">
<label data-bind="css: question.getLabelClass($data), attr: { 'aria-label': question.getAriaItemLabel($data) }">
<label data-bind="css: question.getLabelClass($data)">
<!-- ko if: $data == question.selectAllItem -->
<input role="option" type="checkbox" value="" data-bind="attr: {name: question.name, id: question.getItemId($data), 'aria-describedby': question.ariaDescribedBy }, checked: question.koAllSelected, enable: question.getItemEnabled($data), css: question.cssClasses.itemControl"/>
<input type="checkbox" value="" data-bind="attr: {name: question.name, id: question.getItemId($data)}, checked: question.koAllSelected, enable: question.getItemEnabled($data), css: question.cssClasses.itemControl"/>
<!-- /ko -->
<!-- ko if: $data != question.selectAllItem -->
<input role="option" type="checkbox" data-bind="attr: {name: question.name, id: question.getItemId($data), 'aria-describedby': question.ariaDescribedBy }, checkedValue: $data.value, checked: question.koValue, enable: question.getItemEnabled($data), css: question.cssClasses.itemControl"/>
<input type="checkbox" data-bind="attr: {name: question.name, id: question.getItemId($data)}, checkedValue: $data.value, checked: question.koValue, enable: question.getItemEnabled($data), css: question.cssClasses.itemControl"/>
<!-- /ko -->
<!-- ko if: question.cssClasses.materialDecorator -->
<span data-bind="css: question.cssClasses.materialDecorator">
Expand Down
4 changes: 2 additions & 2 deletions src/knockout/templates/question-radiogroup.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script type="text/html" id="survey-question-radiogroup">

<fieldset data-bind="css: question.getSelectBaseRootCss()" role="presentation">
<fieldset data-bind="css: question.getSelectBaseRootCss(), attr: { 'role': question.a11y_input_ariaRole, 'aria-required': question.a11y_input_ariaRequired, 'aria-labelledby': question.a11y_input_ariaLabelledBy, 'aria-invalid': question.a11y_input_ariaInvalid, 'aria-describedby': question.a11y_input_ariaDescribedBy, 'aria-label': question.a11y_input_ariaLabel }">
<!-- ko ifnot: question.hasColumns -->
<!-- ko if: question.blockedRow -->
<div data-bind="css: question.cssClasses.rootRow">
Expand Down Expand Up @@ -52,7 +52,7 @@
</script>
<script type="text/html" id="survey-radiogroup-item">
<div role="presentation" data-bind="css: question.getItemClass($data)">
<label data-bind="css: question.getLabelClass($data), attr: { 'aria-label': question.getAriaItemLabel($data) }, event: { mousedown: question.koMouseDown }">
<label data-bind="css: question.getLabelClass($data), event: { mousedown: question.koMouseDown }">
<input
type="radio"
data-bind="attr: { name: question.questionName, id: question.getItemId($data), 'aria-describedby': question.ariaDescribedBy }, checkedValue: $data.value, checked: question.renderedValue, enable: question.getItemEnabled($data), css: question.cssClasses.itemControl"
Expand Down
94 changes: 72 additions & 22 deletions src/question.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,25 +233,6 @@ export class Question extends SurveyElement<Question>
public get isReady(): boolean {
return this.isReadyValue;
}
public get ariaRequired() {
return this.isRequired ? "true" : "false";
}
public get ariaInvalid() {
return this.errors.length > 0 ? "true" : "false";
}
public get ariaLabelledBy(): string {
if (this.hasTitle) {
return this.ariaTitleId;
} else {
return null;
}
}
public get ariaExpanded(): string {
return null;
}
public get ariaDescribedBy(): string {
return this.errors.length > 0 ? this.id + "_errors" : null;
}

public choicesLoaded(): void { }
/**
Expand Down Expand Up @@ -1163,9 +1144,6 @@ export class Question extends SurveyElement<Question>
public get commentId(): string {
return this.id + "_comment";
}
public get ariaRole(): string {
return "textbox";
}
/**
* Specifies whether to display the "Other" choice item. Incompatible with the `showCommentArea` property.
*
Expand Down Expand Up @@ -2349,6 +2327,78 @@ export class Question extends SurveyElement<Question>
this.dependedQuestions[i].resetDependedQuestion();
}
}

dmitry-kurmanov marked this conversation as resolved.
Show resolved Hide resolved
//a11y
public get isNewA11yStructure(): boolean {
return false;
}
public get ariaLabel(): string {
if (this.isNewA11yStructure) return null;

return this.locTitle.renderedHtml;
}
public get ariaRole(): string {
if (this.isNewA11yStructure) return null;

return "textbox";
}
public get ariaRequired() {
if (this.isNewA11yStructure) return null;

return this.isRequired ? "true" : "false";
}
public get ariaInvalid() {
if (this.isNewA11yStructure) return null;

return this.errors.length > 0 ? "true" : "false";
}
public get ariaLabelledBy(): string {
if (this.isNewA11yStructure) return null;

if (this.hasTitle) {
return this.ariaTitleId;
} else {
return null;
}
}
public get ariaExpanded(): string {
return null;
}
public get ariaDescribedBy(): string {
if (this.isNewA11yStructure) return null;

return this.errors.length > 0 ? this.id + "_errors" : null;
}
//EO a11y

//new a11y
public get a11y_input_ariaRole(): string {
return null;
}
public get a11y_input_ariaRequired(): "true" | "false" {
return this.isRequired ? "true" : "false";
}
public get a11y_input_ariaInvalid(): "true" | "false" {
return this.errors.length > 0 ? "true" : "false";
}
public get a11y_input_ariaLabel(): string {
if (this.hasTitle && !this.parentQuestion) {
return null;
} else {
return this.locTitle.renderedHtml;
}
}
public get a11y_input_ariaLabelledBy(): string {
if (this.hasTitle && !this.parentQuestion) {
return this.ariaTitleId;
} else {
return null;
}
}
public get a11y_input_ariaDescribedBy(): string {
return this.errors.length > 0 ? this.id + "_errors" : null;
}
//EO new a11y
}
function makeNameValid(str: string): string {
if(!str) return str;
Expand Down
12 changes: 9 additions & 3 deletions src/question_checkbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,6 @@ export class QuestionCheckboxModel extends QuestionCheckboxBase {
protected getDefaultItemComponent(): string {
return "survey-checkbox-item";
}
public get ariaRole(): string {
return "listbox";
}
public getType(): string {
return "checkbox";
}
Expand Down Expand Up @@ -580,6 +577,15 @@ export class QuestionCheckboxModel extends QuestionCheckboxBase {
public get checkBoxSvgPath(): string {
return "M5,13l2-2l3,3l7-7l2,2l-9,9L5,13z";
}

//a11y
public get isNewA11yStructure(): boolean {
return true;
}
public get a11y_input_ariaRole(): string {
return "listbox";
}
// EO a11y
}
Serializer.addClass(
"checkbox",
Expand Down
15 changes: 9 additions & 6 deletions src/question_radiogroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,6 @@ export class QuestionRadiogroupModel extends QuestionCheckboxBase {
return "radiogroup";
}

public get ariaRole(): string {
return "radiogroup";
}
public get titleAriaLabel(): string | null {
return null;
}
protected getFirstInputElementId(): string {
return this.inputId + "_0";
}
Expand Down Expand Up @@ -83,6 +77,15 @@ export class QuestionRadiogroupModel extends QuestionCheckboxBase {
}
return actions;
}

//a11y
public get isNewA11yStructure(): boolean {
return true;
}
public get a11y_input_ariaRole(): string {
return "radiogroup";
}
// EO a11y
}

Serializer.addClass(
Expand Down
6 changes: 6 additions & 0 deletions src/question_ranking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,12 @@ export class QuestionRankingModel extends QuestionCheckboxModel {
public set useFullItemSizeForShortcut(val: boolean) {
this.setPropertyValue("useFullItemSizeForShortcut", val);
}

//a11y
public get isNewA11yStructure(): boolean {
return false;
}
// EO a11y
}

Serializer.addClass(
Expand Down
6 changes: 6 additions & 0 deletions src/question_tagbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ export class QuestionTagboxModel extends QuestionCheckboxModel {
super.clearValue();
this.dropdownListModel.clear();
}

//a11y
public get isNewA11yStructure(): boolean {
return false;
}
// EO a11y
}

Serializer.addClass(
Expand Down
Loading
Loading