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

Scheduler a11y - fixes #28470

Merged
merged 9 commits into from
Dec 4, 2024
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
29 changes: 29 additions & 0 deletions e2e/testcafe-devextreme/tests/scheduler/a11y/appointment.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import Scheduler from 'devextreme-testcafe-models/scheduler';
import { a11yCheck } from '../../../helpers/accessibility/utils';
import { createWidget } from '../../../helpers/createWidget';
import url from '../../../helpers/getPageUrl';
import { checkOptions } from './axe_options';

fixture.disablePageReloads`a11y - appointment`
.page(url(__dirname, '../../container.html'));
Expand All @@ -14,6 +16,8 @@ fixture.disablePageReloads`a11y - appointment`
scheduler.getAppointment('App 1').element.attributes['aria-label'],
)
.eql(undefined);

await a11yCheck(t, checkOptions, '#container');
}).before(async () => {
await createWidget('dxScheduler', {
dataSource: [{
Expand All @@ -34,6 +38,8 @@ fixture.disablePageReloads`a11y - appointment`
await t
.expect(attrs['aria-roledescription'])
.eql('February 1, 2021, Group: resource1, ');

await a11yCheck(t, checkOptions, '#container');
}).before(async () => {
await createWidget('dxScheduler', {
dataSource: [{
Expand Down Expand Up @@ -65,6 +71,8 @@ fixture.disablePageReloads`a11y - appointment`
await t
.expect(attrs['aria-roledescription'])
.eql('February 1, 2021, Group: resource11, resource21, ');

await a11yCheck(t, checkOptions, '#container');
}).before(async () => {
await createWidget('dxScheduler', {
dataSource: [{
Expand Down Expand Up @@ -103,6 +111,8 @@ fixture.disablePageReloads`a11y - appointment`
await t
.expect(recurrenceIcon.getAttribute('aria-label'))
.eql('Recurring appointment');

await a11yCheck(t, checkOptions, '#container');
}).before(async () => {
await createWidget('dxScheduler', {
timeZone: 'America/Los_Angeles',
Expand All @@ -123,14 +133,21 @@ fixture.disablePageReloads`a11y - appointment`
test('appointments should have right role', async (t) => {
const scheduler = new Scheduler('#container');
const appt = scheduler.getAppointment('Website Re-Design Plan');
const contentId = await appt.element.find('.dx-scheduler-appointment-content').getAttribute('id');

await t
.expect(appt.element.getAttribute('role'))
.eql('application');

await t
.expect(appt.element.getAttribute('aria-describedby'))
.eql(contentId);

await t
.expect(appt.element.getAttribute('aria-activedescendant'))
.eql(null);

await a11yCheck(t, checkOptions, '#container');
}).before(async () => {
await createWidget('dxScheduler', {
timeZone: 'America/Los_Angeles',
Expand Down Expand Up @@ -186,6 +203,8 @@ fixture.disablePageReloads`a11y - appointment`
await t.expect(apptLabels[0]).eql(labels[0]);
await t.expect(apptLabels[1]).eql(labels[1]);
await t.expect(apptLabels[2]).eql(labels[2]);

await a11yCheck(t, checkOptions, '#container');
}).before(async () => {
await createWidget('dxScheduler', {
dataSource: [{
Expand Down Expand Up @@ -252,6 +271,8 @@ test('appointments & collector buttons can be navigated', async (t) => {
await t.expect(
scheduler.appointmentTooltip.element.count,
).eql(1);

await a11yCheck(t, checkOptions, '#container');
}).before(async () => {
await createWidget('dxScheduler', {
dataSource: [
Expand Down Expand Up @@ -301,6 +322,8 @@ test('Scheduler a11y: Disabled time ranges are not supported', async (t) => {
.eql(expectedAriaLabels.prev)
.expect(actualNextAriaLabel)
.eql(expectedAriaLabels.next);

await a11yCheck(t, checkOptions, '#container');
}).before(async () => {
await createWidget('dxScheduler', {
dataSource: [
Expand All @@ -322,6 +345,8 @@ test('Scheduler a11y: appointments does not have info about reccurence', async (
await t
.expect(recurrenceIcon.getAttribute('aria-label'))
.eql('Recurring appointment');

await a11yCheck(t, checkOptions, '#container');
}).before(async () => {
await createWidget('dxScheduler', {
timeZone: 'America/Los_Angeles',
Expand Down Expand Up @@ -350,6 +375,8 @@ test('Scheduler a11y: Appointment collector button doesn\'t have info about date
.ok()
.expect(schedulerCollector.element().getAttribute('aria-roledescription'))
.contains(dateText);

await a11yCheck(t, checkOptions, '#container');
}).before(async () => {
await createWidget('dxScheduler', {
timeZone: 'America/Los_Angeles',
Expand Down Expand Up @@ -387,6 +414,8 @@ test('appointment aria label should contain date with right timezone', async (t)
await t
.expect(appt.element.getAttribute('aria-roledescription'))
.eql('March 29, 2021, ');

await a11yCheck(t, checkOptions, '#container');
}).before(async () => {
await createWidget('dxScheduler', {
timeZone: 'America/Los_Angeles',
Expand Down
5 changes: 5 additions & 0 deletions e2e/testcafe-devextreme/tests/scheduler/a11y/axe_options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const checkOptions = {
rules: {
'color-contrast': { enabled: false },
},
};
11 changes: 5 additions & 6 deletions e2e/testcafe-devextreme/tests/scheduler/a11y/scheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@ import { a11yCheck } from '../../../helpers/accessibility/utils';
import { createWidget } from '../../../helpers/createWidget';
import url from '../../../helpers/getPageUrl';
import { changeTheme } from '../../../helpers/changeTheme';
import { checkOptions } from './axe_options';

fixture.disablePageReloads`a11y - appointment`
.page(url(__dirname, '../../container.html'));

const checkOptions = {
rules: {
'color-contrast': { enabled: false },
},
};

test('Scheduler should have right aria attributes', async (t) => {
const scheduler = new Scheduler('#container');

Expand All @@ -29,6 +24,8 @@ test('Scheduler should have right aria attributes', async (t) => {
await t.expect(
scheduler.element.getAttribute('aria-label'),
).eql('Scheduler. Week view');

await a11yCheck(t, checkOptions, '#container');
}).before(async () => {
await createWidget('dxScheduler', {
dataSource: [],
Expand All @@ -47,6 +44,8 @@ test('Scheduler table elements have right aria attributes', async (t) => {
tables.nth(i).getAttribute('aria-hidden'),
).eql('true');
}

await a11yCheck(t, checkOptions, '#container');
}).before(async () => {
await createWidget('dxScheduler', {
dataSource: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { move } from '@js/animation/translator';
import registerComponent from '@js/core/component_registrator';
import DOMComponent from '@js/core/dom_component';
import Guid from '@js/core/guid';
import type { dxElementWrapper } from '@js/core/renderer';
import $ from '@js/core/renderer';
import { Deferred } from '@js/core/utils/deferred';
Expand Down Expand Up @@ -212,6 +213,11 @@ export class Appointment extends DOMComponent {
.filter((label) => !!label)
.join(', ');
$element.attr('aria-roledescription', `${ariaLabel}, `);

const id = `dx-${new Guid()}`;

$element.attr('aria-describedby', id);
$element.find('.dx-item-content').attr('id', id);
}

_renderAppointmentGeometry() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,10 @@ class SchedulerAppointments extends CollectionWidget {
const $target = $(e.currentTarget);
const data = (this as any)._getItemData($target);

if ($target.is('.dx-scheduler-appointment-collector')) {
return;
}

if (e.type === 'keydown' || isFakeClickEvent(e)) {
this.notifyObserver('showEditAppointmentPopup', { data, target: $target });
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ export const createAppointmentLayout = (formatText, config) => {
$('<div>').addClass(APPOINTMENT_CONTENT_CLASSES.APPOINTMENT_DATE).text(formatText.formatDate).appendTo($contentDetails);

config.isRecurrence
&& $('<span>').addClass(`${APPOINTMENT_CONTENT_CLASSES.RECURRING_ICON} dx-icon-repeat`).attr('aria-label', recurringText)
&& $('<span>')
.addClass(`${APPOINTMENT_CONTENT_CLASSES.RECURRING_ICON} dx-icon-repeat`)
.attr('aria-label', recurringText)
.attr('role', 'img')
.appendTo(result);

config.isAllDay
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export class CompactAppointmentsHelper {
dragBehavior: options.allowDrag && this._createTooltipDragBehavior($appointmentCollector).bind(this),
dropDownAppointmentTemplate: this.instance.option().dropDownAppointmentTemplate, // TODO deprecated option
isButtonClick: true,
_loopFocus: true,
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import messageLocalization from '@js/localization/message';
import supportUtils from '@ts/core/utils/m_support';
import Tooltip from '@ts/ui/m_tooltip';

Expand Down Expand Up @@ -40,16 +41,24 @@ export class DesktopTooltipStrategy extends TooltipStrategyBase {
}

_createTooltip(target, dataList) {
const tooltip = this._createTooltipElement(APPOINTMENT_TOOLTIP_WRAPPER_CLASS);
const tooltipElement = this._createTooltipElement(APPOINTMENT_TOOLTIP_WRAPPER_CLASS);

return this._options.createComponent(tooltip, Tooltip, {
const tooltip = this._options.createComponent(tooltipElement, Tooltip, {
target,
maxHeight: MAX_TOOLTIP_HEIGHT,
rtlEnabled: this._extraOptions.rtlEnabled,
onShown: this._onShown.bind(this),
contentTemplate: this._getContentTemplate(dataList),
wrapperAttr: { class: APPOINTMENT_TOOLTIP_WRAPPER_CLASS },
_loopFocus: this._extraOptions._loopFocus,
});

tooltip.setAria({
role: 'dialog',
label: messageLocalization.format('dxScheduler-appointmentListAriaLabel'),
});

return tooltip;
}

_onListRender(e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import $ from '@js/core/renderer';
import { FunctionTemplate } from '@js/core/templates/function_template';
import { isRenderer } from '@js/core/utils/type';
import Button from '@js/ui/button';
import { createPromise } from '@ts/core/utils/promise';
import List from '@ts/ui/list/m_list.edit';
Expand Down Expand Up @@ -40,6 +41,8 @@ export class TooltipStrategyBase {
}

_showCore(target, dataList) {
const describedByValue = isRenderer(target) && target.attr('aria-describedby') as string;

if (!this._tooltip) {
this._tooltip = this._createTooltip(target, dataList);
} else {
Expand All @@ -49,6 +52,8 @@ export class TooltipStrategyBase {

this._prepareBeforeVisibleChanged(dataList);
this._tooltip.option('visible', true);

describedByValue && target.attr('aria-describedby', describedByValue);
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand All @@ -61,6 +66,10 @@ export class TooltipStrategyBase {
const listElement = $('<div>');
$(container).append(listElement);
this._list = this._createList(listElement, dataList);
this._list.registerKeyHandler?.('escape', () => {
this.hide();
this._tooltip.option('target').focus();
});
};
}

Expand Down
3 changes: 3 additions & 0 deletions packages/devextreme/js/localization/messages/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@
"dxScheduler-appointmentAriaLabel-group": "Group: {0}",
"dxScheduler-appointmentAriaLabel-recurring": "Recurring appointment",

"dxScheduler-appointmentListAriaLabel": "Appointment list",


"dxScheduler-editorLabelTitle": "الموضوع",
"dxScheduler-editorLabelStartDate": "تاريخ البدء",
"dxScheduler-editorLabelEndDate": "تاريخ الانتهاء",
Expand Down
3 changes: 3 additions & 0 deletions packages/devextreme/js/localization/messages/ca.json
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@
"dxScheduler-appointmentAriaLabel-group": "Group: {0}",
"dxScheduler-appointmentAriaLabel-recurring": "Recurring appointment",

"dxScheduler-appointmentListAriaLabel": "Appointment list",


"dxScheduler-editorLabelTitle": "Assignatura",
"dxScheduler-editorLabelStartDate": "Data d'inici",
"dxScheduler-editorLabelEndDate": "Data de finalització",
Expand Down
3 changes: 3 additions & 0 deletions packages/devextreme/js/localization/messages/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@
"dxScheduler-appointmentAriaLabel-group": "Group: {0}",
"dxScheduler-appointmentAriaLabel-recurring": "Recurring appointment",

"dxScheduler-appointmentListAriaLabel": "Appointment list",


"dxScheduler-editorLabelTitle": "Předmět",
"dxScheduler-editorLabelStartDate": "Počáteční datum",
"dxScheduler-editorLabelEndDate": "Koncové datum",
Expand Down
3 changes: 3 additions & 0 deletions packages/devextreme/js/localization/messages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@
"dxScheduler-appointmentAriaLabel-group": "Group: {0}",
"dxScheduler-appointmentAriaLabel-recurring": "Recurring appointment",

"dxScheduler-appointmentListAriaLabel": "Appointment list",


"dxScheduler-editorLabelTitle": "Betreff",
"dxScheduler-editorLabelStartDate": "Anfangszeit",
"dxScheduler-editorLabelEndDate": "Endzeit",
Expand Down
3 changes: 3 additions & 0 deletions packages/devextreme/js/localization/messages/el.json
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@
"dxScheduler-appointmentAriaLabel-group": "Group: {0}",
"dxScheduler-appointmentAriaLabel-recurring": "Recurring appointment",

"dxScheduler-appointmentListAriaLabel": "Appointment list",


"dxScheduler-editorLabelTitle": "Θέμα",
"dxScheduler-editorLabelStartDate": "Ημερομηνία έναρξης",
"dxScheduler-editorLabelEndDate": "Ημερομηνία λήξης",
Expand Down
3 changes: 3 additions & 0 deletions packages/devextreme/js/localization/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@
"dxScheduler-appointmentAriaLabel-group": "Group: {0}",
"dxScheduler-appointmentAriaLabel-recurring": "Recurring appointment",

"dxScheduler-appointmentListAriaLabel": "Appointment list",


"dxScheduler-editorLabelTitle": "Subject",
"dxScheduler-editorLabelStartDate": "Start Date",
"dxScheduler-editorLabelEndDate": "End Date",
Expand Down
3 changes: 3 additions & 0 deletions packages/devextreme/js/localization/messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@
"dxScheduler-appointmentAriaLabel-group": "Group: {0}",
"dxScheduler-appointmentAriaLabel-recurring": "Recurring appointment",

"dxScheduler-appointmentListAriaLabel": "Appointment list",


"dxScheduler-editorLabelTitle": "Asunto",
"dxScheduler-editorLabelStartDate": "Fecha inicial",
"dxScheduler-editorLabelEndDate": "Fecha final",
Expand Down
3 changes: 3 additions & 0 deletions packages/devextreme/js/localization/messages/fa.json
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@
"dxScheduler-appointmentAriaLabel-group": "Group: {0}",
"dxScheduler-appointmentAriaLabel-recurring": "Recurring appointment",

"dxScheduler-appointmentListAriaLabel": "Appointment list",


"dxScheduler-editorLabelTitle": "موضوع",
"dxScheduler-editorLabelStartDate": "تاریخ شروع",
"dxScheduler-editorLabelEndDate": "تاریخ پایان",
Expand Down
3 changes: 3 additions & 0 deletions packages/devextreme/js/localization/messages/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@
"dxScheduler-appointmentAriaLabel-group": "Group: {0}",
"dxScheduler-appointmentAriaLabel-recurring": "Recurring appointment",

"dxScheduler-appointmentListAriaLabel": "Appointment list",


"dxScheduler-editorLabelTitle": "Aihe",
"dxScheduler-editorLabelStartDate": "Alkamispäivä",
"dxScheduler-editorLabelEndDate": "Päättymispäivä",
Expand Down
3 changes: 3 additions & 0 deletions packages/devextreme/js/localization/messages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@
"dxScheduler-appointmentAriaLabel-group": "Group: {0}",
"dxScheduler-appointmentAriaLabel-recurring": "Recurring appointment",

"dxScheduler-appointmentListAriaLabel": "Appointment list",


"dxScheduler-editorLabelTitle": "Titre",
"dxScheduler-editorLabelStartDate": "Date de début",
"dxScheduler-editorLabelEndDate": "Date de fin",
Expand Down
3 changes: 3 additions & 0 deletions packages/devextreme/js/localization/messages/hu.json
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@
"dxScheduler-appointmentAriaLabel-group": "Group: {0}",
"dxScheduler-appointmentAriaLabel-recurring": "Recurring appointment",

"dxScheduler-appointmentListAriaLabel": "Appointment list",


"dxScheduler-editorLabelTitle": "Tárgy",
"dxScheduler-editorLabelStartDate": "Kezdés dátuma",
"dxScheduler-editorLabelEndDate": "Befejezés dátuma",
Expand Down
Loading
Loading