Skip to content

Commit

Permalink
Scheduler - a11y - make overflow indicator accessible (DevExpress#27925)
Browse files Browse the repository at this point in the history
  • Loading branch information
pomahtri authored Sep 23, 2024
1 parent 3b2cfa5 commit 2bf2bbf
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 21 deletions.
83 changes: 83 additions & 0 deletions e2e/testcafe-devextreme/tests/scheduler/a11y/appointment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,86 @@ fixture.disablePageReloads`a11y - appointment`
});
});
});

test('appointments & collector buttons can be navigated', async (t) => {
const scheduler = new Scheduler('#container');

// forward
await t.click(scheduler.workSpace);
await t.pressKey('tab');
await t.expect(
scheduler.getAppointment('App 1').element.focused,
).ok();

await t.pressKey('tab');
await t.expect(
scheduler.collectors.get(0).isFocused,
).ok();

await t.pressKey('tab');
await t.expect(
scheduler.getAppointment('App 2').element.focused,
).ok();

await t.pressKey('tab');
await t.expect(
scheduler.getAppointment('App 4').element.focused,
).ok();

// backward

await t.pressKey('shift+tab');
await t.expect(
scheduler.getAppointment('App 2').element.focused,
).ok();

await t.pressKey('shift+tab');
await t.expect(
scheduler.collectors.get(0).isFocused,
).ok();

await t.pressKey('shift+tab');
await t.expect(
scheduler.getAppointment('App 1').element.focused,
).ok();

// open list
await t.pressKey('tab');
await t.expect(
scheduler.collectors.get(0).isFocused,
).ok();

await t.pressKey('enter');
await t.expect(
scheduler.appointmentTooltip.element.count,
).eql(1);
}).before(async () => {
await createWidget('dxScheduler', {
dataSource: [
{
text: 'App 1',
startDate: new Date(2021, 1, 1),
endDate: new Date(2021, 1, 1),
},
{
text: 'App 2',
startDate: new Date(2021, 1, 2),
endDate: new Date(2021, 1, 2),
},
{
text: 'App 3',
startDate: new Date(2021, 1, 2),
endDate: new Date(2021, 1, 2),
},
{
text: 'App 4',
startDate: new Date(2021, 1, 3),
endDate: new Date(2021, 1, 3),
},
],
allDayPanelMode: 'hidden',
currentView: 'month',
maxAppointmentsPerCell: 1,
currentDate: new Date(2021, 1, 1),
});
});
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
/* eslint-disable spellcheck/spell-checker */
import { locate, move } from '@js/animation/translator';
import registerComponent from '@js/core/component_registrator';
import domAdapter from '@js/core/dom_adapter';
import { getPublicElement } from '@js/core/element';
import { data as elementData } from '@js/core/element_data';
import type { dxElementWrapper } from '@js/core/renderer';
import $ from '@js/core/renderer';
import { wrapToArray } from '@js/core/utils/array';
// @ts-expect-error
Expand Down Expand Up @@ -105,17 +107,17 @@ class SchedulerAppointments extends CollectionWidget {
const parent = super._supportedKeys();

const tabHandler = function (e) {
const appointments = this._getAccessAppointments();
const focusedAppointment = appointments.filter('.dx-state-focused');
let index = focusedAppointment.data(APPOINTMENT_SETTINGS_KEY).sortedIndex;
const lastIndex = appointments.length - 1;
const navigatableItems = this._getNavigatableItems();
const focusedItem = navigatableItems.filter('.dx-state-focused');
let index = focusedItem.data(APPOINTMENT_SETTINGS_KEY).sortedIndex;
const lastIndex = navigatableItems.length - 1;

if ((index > 0 && e.shiftKey) || (index < lastIndex && !e.shiftKey)) {
e.preventDefault();

e.shiftKey ? index-- : index++;

const $nextAppointment = this._getAppointmentByIndex(index);
const $nextAppointment = this._getNavigatableItemByIndex(index);
this._resetTabIndex($nextAppointment);
// @ts-expect-error
eventsEngine.trigger($nextAppointment, 'focus');
Expand Down Expand Up @@ -145,14 +147,20 @@ class SchedulerAppointments extends CollectionWidget {
});
}

_getAppointmentByIndex(sortedIndex) {
const appointments = this._getAccessAppointments();

return appointments.filter((_, $item) => elementData($item, APPOINTMENT_SETTINGS_KEY).sortedIndex === sortedIndex).eq(0);
private _getNavigatableItemByIndex(sortedIndex) {
const appointments = this._getNavigatableItems();
return appointments.filter(
// @ts-expect-error
(_, $item) => elementData($item, APPOINTMENT_SETTINGS_KEY).sortedIndex === sortedIndex,
).eq(0);
}

_getAccessAppointments() {
return (this as any)._itemElements().filter(':visible').not('.dx-state-disabled');
private _getNavigatableItems(): dxElementWrapper {
// @ts-expect-error
const appts = this._itemElements().filter(':visible').not('.dx-state-disabled');
// @ts-expect-error
const apptCollectors = this.$element().find('.dx-scheduler-appointment-collector');
return appts.add(apptCollectors);
}

_resetTabIndex($appointment) {
Expand All @@ -163,11 +171,11 @@ class SchedulerAppointments extends CollectionWidget {
_moveFocus() {}

_focusTarget() {
return (this as any)._itemElements();
return (this as any)._getNavigatableItems();
}

_renderFocusTarget() {
const $appointment = this._getAppointmentByIndex(0);
const $appointment = this._getNavigatableItemByIndex(0);

this._resetTabIndex($appointment);
}
Expand All @@ -179,7 +187,7 @@ class SchedulerAppointments extends CollectionWidget {
}

_focusOutHandler(e) {
const $appointment = this._getAppointmentByIndex(0);
const $appointment = this._getNavigatableItemByIndex(0);
this.option('focusedElement', getPublicElement($appointment));
super._focusOutHandler(e);
}
Expand Down Expand Up @@ -898,6 +906,7 @@ class SchedulerAppointments extends CollectionWidget {
items: { data: [], colors: [], settings: [] },
isAllDay: !!virtualAppointment.isAllDay,
buttonColor: color,
sortedIndex: appointmentSetting.sortedIndex,
};
}

Expand Down Expand Up @@ -935,6 +944,7 @@ class SchedulerAppointments extends CollectionWidget {
},
items: virtualItems,
buttonColor: virtualGroup.buttonColor,
sortedIndex: virtualGroup.sortedIndex,
width: buttonWidth - this.option('_collectorOffset'),
height: buttonHeight,
onAppointmentClick: this.option('onItemClick'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,12 @@ class BaseRenderingStrategy {

const createItem = (currentItem, index?) => {
const currentIndex = index || 0;
const skipSortIndex = this._skipSortedIndex(currentIndex);

if (skipSortIndex) {
stack.shouldShiftAfterSkip = true;
}

return {
index: currentIndex,
i: currentItem.i,
Expand All @@ -472,7 +478,7 @@ class BaseRenderingStrategy {
top: currentItem.top,
bottom: currentItem.bottom,
allDay: currentItem.allDay,
sortedIndex: this._skipSortedIndex(currentIndex) ? null : sortedIndex++,
sortedIndex: skipSortIndex ? stack.startSortedIndex : sortedIndex++,
};
};

Expand All @@ -483,17 +489,24 @@ class BaseRenderingStrategy {
stack.top = currentItem.top;
stack.bottom = currentItem.bottom;
stack.allDay = currentItem.allDay;
stack.startSortedIndex = stack.items[0].sortedIndex;
};
const pushItemsInResult = (items) => {
items.forEach((item) => {
const pushItemsInResult = (stack) => {
stack.items.forEach((item) => {
result.push({
index: item.index,
count: maxIndexInStack + 1,
i: item.i,
j: item.j,
sortedIndex: item.sortedIndex,
sortedIndex: stack.shouldShiftAfterSkip && !this._skipSortedIndex(item.index)
? item.sortedIndex + 1
: item.sortedIndex,
});
});

if (stack.shouldShiftAfterSkip) {
sortedIndex += 1;
}
};

for (i = 0; i < sortedArray.length; i++) {
Expand All @@ -518,14 +531,14 @@ class BaseRenderingStrategy {
stack.bottom = Math.max(stack.bottom, currentItem.bottom);
stack.allDay = currentItem.allDay;
} else {
pushItemsInResult(stack.items);
pushItemsInResult(stack);
stack = {};
startNewStack(currentItem);
maxIndexInStack = 0;
}
}
if (stack.items) {
pushItemsInResult(stack.items);
pushItemsInResult(stack);
}

return result.sort((a, b) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,16 @@ export class CompactAppointmentsHelper {
});
}

_createCompactButtonElement({ isCompact, $container, coordinates }) {
_createCompactButtonElement({
isCompact, $container, coordinates, sortedIndex,
}) {
const result = $('<div>')
.addClass(APPOINTMENT_COLLECTOR_CLASS)
.toggleClass(COMPACT_APPOINTMENT_COLLECTOR_CLASS, isCompact)
.appendTo($container);

result.data('dxAppointmentSettings', { sortedIndex });

this._setPosition(result, coordinates);

return result;
Expand Down

0 comments on commit 2bf2bbf

Please sign in to comment.