Skip to content

Commit 312da62

Browse files
authored
Scheduler - Virtual Scrolling - Support keyboard navigation (#32743)
Signed-off-by: Eldar Iusupzhanov <84278206+Tucchhaa@users.noreply.github.com>
1 parent f371641 commit 312da62

16 files changed

Lines changed: 698 additions & 376 deletions

File tree

8 Bytes
Loading
Lines changed: 255 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,278 @@
11
import Scheduler from 'devextreme-testcafe-models/scheduler';
2-
import { ClientFunction } from 'testcafe';
32
import url from '../../../../helpers/getPageUrl';
43
import { createWidget } from '../../../../helpers/createWidget';
5-
import { getDocumentScrollTop } from '../../../../helpers/domUtils';
4+
import { generateAppointmentsWithResources, resources } from '../../helpers/generateAppointmentsWithResources';
5+
import { insertStylesheetRulesToPage } from '../../../../helpers/domUtils';
66

77
fixture.disablePageReloads`KeyboardNavigation.Appointments`
88
.page(url(__dirname, '../../../container.html'));
99

1010
const SCHEDULER_SELECTOR = '#container';
1111

12-
test('Document should not scroll on \'End\' press when appointment is focused', async (t) => {
13-
const scheduler = new Scheduler(SCHEDULER_SELECTOR);
12+
const resourceCount = 30;
1413

15-
await t.click(scheduler.getAppointment('Appointment 1').element);
14+
const dataSource = generateAppointmentsWithResources({
15+
startDay: new Date(2021, 1, 1),
16+
endDay: new Date(2021, 1, 6),
17+
startDayHour: 8,
18+
endDayHour: 20,
19+
resourceCount,
20+
});
1621

17-
const expectedScrollTop = await getDocumentScrollTop();
22+
const appointmentCount = dataSource.length;
1823

19-
await t
20-
.pressKey('End')
21-
.expect(getDocumentScrollTop()).eql(expectedScrollTop);
22-
}).before(async () => {
23-
await ClientFunction(() => {
24-
document.body.style.height = '2000px';
25-
})();
26-
27-
await createWidget('dxScheduler', {
28-
dataSource: [
29-
{
30-
text: 'Appointment 1',
31-
startDate: new Date(2015, 1, 9, 8),
32-
endDate: new Date(2015, 1, 9, 9),
33-
},
34-
{
35-
text: 'Appointment 2',
36-
startDate: new Date(2015, 1, 9, 10),
37-
endDate: new Date(2015, 1, 9, 11),
38-
},
39-
{
40-
text: 'Appointment 3',
41-
startDate: new Date(2015, 1, 9, 12),
42-
endDate: new Date(2015, 1, 9, 13),
43-
},
44-
],
45-
height: 300,
46-
currentView: 'day',
47-
currentDate: new Date(2015, 1, 9),
24+
const getConfig = () => ({
25+
views: [
26+
{
27+
type: 'timelineWorkWeek',
28+
name: 'Timeline',
29+
groupOrientation: 'vertical',
30+
},
31+
'week',
32+
],
33+
dataSource,
34+
resources: [
35+
{ fieldExpr: 'resourceId', label: 'Resource', dataSource: resources },
36+
],
37+
groups: ['resourceId'],
38+
scrolling: {
39+
mode: 'virtual',
40+
},
41+
height: 600,
42+
cellDuration: 60,
43+
startDayHour: 8,
44+
endDayHour: 20,
45+
showAllDayPanel: false,
46+
currentView: 'Timeline',
47+
currentDate: new Date(2021, 1, 2),
48+
});
49+
50+
const cellStyles = '#container .dx-scheduler-cell-sizes-vertical { height: 100px; } #container .dx-scheduler-cell-sizes-horizontal { width: 150px; }';
51+
52+
['virtual', 'standard'].forEach((scrollingMode) => {
53+
test(`focus next appointment on single tab (${scrollingMode} scrolling)`, async (t) => {
54+
const scheduler = new Scheduler(SCHEDULER_SELECTOR);
55+
56+
await t
57+
.click(scheduler.getAppointment('[Appointment 1]').element)
58+
.pressKey('tab');
59+
60+
await t
61+
.expect(scheduler.getAppointment('[Appointment 2]').isFocused).ok();
62+
}).before(async () => {
63+
await insertStylesheetRulesToPage(cellStyles);
64+
await createWidget('dxScheduler', { ...getConfig(), scrolling: { mode: scrollingMode } });
65+
});
66+
67+
test(`focus next appointment on 5 tab (${scrollingMode} scrolling)`, async (t) => {
68+
const scheduler = new Scheduler(SCHEDULER_SELECTOR);
69+
70+
await t
71+
.click(scheduler.getAppointment('[Appointment 1]').element)
72+
.pressKey('tab')
73+
.pressKey('tab')
74+
.pressKey('tab')
75+
.pressKey('tab')
76+
.pressKey('tab');
77+
78+
await t
79+
.expect(scheduler.getAppointment('[Appointment 6]').isFocused).ok();
80+
}).before(async () => {
81+
await insertStylesheetRulesToPage(cellStyles);
82+
await createWidget('dxScheduler', { ...getConfig(), scrolling: { mode: scrollingMode } });
83+
});
84+
85+
test(`focus prev appointment on single shift+tab (${scrollingMode} scrolling)`, async (t) => {
86+
const scheduler = new Scheduler(SCHEDULER_SELECTOR);
87+
88+
const lastAppointmentText = `[Appointment ${appointmentCount}]`;
89+
const prevAppointmentText = `[Appointment ${appointmentCount - 1}]`;
90+
91+
await scheduler.scrollTo(new Date(2021, 1, 5), { resourceId: resourceCount });
92+
93+
await t
94+
.click(scheduler.getAppointment(lastAppointmentText).element)
95+
.pressKey('shift+tab');
96+
97+
await t
98+
.expect(scheduler.getAppointment(prevAppointmentText).isFocused).ok();
99+
}).before(async () => {
100+
await insertStylesheetRulesToPage(cellStyles);
101+
await createWidget('dxScheduler', { ...getConfig(), scrolling: { mode: scrollingMode } });
102+
});
103+
104+
test(`focus prev appointment on 5 shift+tab (${scrollingMode} scrolling)`, async (t) => {
105+
const scheduler = new Scheduler(SCHEDULER_SELECTOR);
106+
107+
const lastAppointmentText = `[Appointment ${appointmentCount}]`;
108+
const prevAppointmentText = `[Appointment ${appointmentCount - 5}]`;
109+
110+
await scheduler.scrollTo(new Date(2021, 1, 5), { resourceId: resourceCount });
111+
112+
await t
113+
.click(scheduler.getAppointment(lastAppointmentText).element)
114+
.pressKey('shift+tab')
115+
.pressKey('shift+tab')
116+
.pressKey('shift+tab')
117+
.pressKey('shift+tab')
118+
.pressKey('shift+tab');
119+
120+
await t
121+
.expect(scheduler.getAppointment(prevAppointmentText).isFocused).ok();
122+
}).before(async () => {
123+
await insertStylesheetRulesToPage(cellStyles);
124+
await createWidget('dxScheduler', { ...getConfig(), scrolling: { mode: scrollingMode } });
125+
});
126+
127+
test(`focus last appointment on End (${scrollingMode} scrolling)`, async (t) => {
128+
const scheduler = new Scheduler(SCHEDULER_SELECTOR);
129+
130+
await t
131+
.click(scheduler.getAppointment('[Appointment 1]').element)
132+
.pressKey('end');
133+
134+
await t
135+
.expect(scheduler.getAppointment(`[Appointment ${appointmentCount}]`).isFocused).ok();
136+
}).before(async () => {
137+
await insertStylesheetRulesToPage(cellStyles);
138+
await createWidget('dxScheduler', { ...getConfig(), scrolling: { mode: scrollingMode } });
139+
});
140+
141+
test(`focus first appointment on Home (${scrollingMode} scrolling)`, async (t) => {
142+
const scheduler = new Scheduler(SCHEDULER_SELECTOR);
143+
144+
await scheduler.scrollTo(new Date(2021, 1, 5), { resourceId: resourceCount });
145+
146+
await t
147+
.click(scheduler.getAppointment(`[Appointment ${appointmentCount}]`).element)
148+
.pressKey('home');
149+
150+
await t
151+
.expect(scheduler.getAppointment('[Appointment 1]').isFocused).ok();
152+
}).before(async () => {
153+
await insertStylesheetRulesToPage(cellStyles);
154+
await createWidget('dxScheduler', { ...getConfig(), scrolling: { mode: scrollingMode } });
155+
});
156+
157+
test(`focus first appointment in the next group by tab (${scrollingMode} scrolling)`, async (t) => {
158+
const scheduler = new Scheduler(SCHEDULER_SELECTOR);
159+
160+
await scheduler.scrollTo(new Date(2021, 1, 5), { resourceId: 1 });
161+
162+
await t
163+
.click(scheduler.getAppointment('[Appointment 14]').element)
164+
.pressKey('tab');
165+
166+
await t
167+
.expect(scheduler.getAppointment('[Appointment 15]').isFocused).ok();
168+
}).before(async () => {
169+
await insertStylesheetRulesToPage(cellStyles);
170+
await createWidget('dxScheduler', { ...getConfig(), scrolling: { mode: scrollingMode } });
171+
});
172+
173+
test(`focus last appointment in the prev group by shift+tab (${scrollingMode} scrolling)`, async (t) => {
174+
const scheduler = new Scheduler(SCHEDULER_SELECTOR);
175+
176+
await t
177+
.click(scheduler.getAppointment('[Appointment 15]').element)
178+
.pressKey('shift+tab');
179+
await t
180+
.expect(scheduler.getAppointment('[Appointment 14]').isFocused).ok();
181+
}).before(async () => {
182+
await insertStylesheetRulesToPage(cellStyles);
183+
await createWidget('dxScheduler', { ...getConfig(), scrolling: { mode: scrollingMode } });
184+
});
185+
186+
test(`should focus appointment after close edit popup (${scrollingMode} scrolling)`, async (t) => {
187+
const scheduler = new Scheduler(SCHEDULER_SELECTOR);
188+
189+
await t
190+
.click(scheduler.getAppointment('[Appointment 1]').element)
191+
.pressKey('tab')
192+
.pressKey('enter')
193+
.pressKey('esc');
194+
195+
await t
196+
.expect(scheduler.getAppointment('[Appointment 2]').isFocused).ok();
197+
}).before(async () => {
198+
await insertStylesheetRulesToPage(cellStyles);
199+
await createWidget('dxScheduler', { ...getConfig(), scrolling: { mode: scrollingMode } });
200+
});
201+
202+
test(`first appointment should be focusable when navigating by tab second time (${scrollingMode} scrolling)`, async (t) => {
203+
const scheduler = new Scheduler(SCHEDULER_SELECTOR);
204+
205+
await t
206+
.click(scheduler.getAppointment('[Appointment 1]').element)
207+
.pressKey('tab')
208+
.click(scheduler.toolbar.viewSwitcher.element)
209+
.pressKey('tab')
210+
.pressKey('tab');
211+
212+
await t
213+
.expect(scheduler.getAppointment('[Appointment 1]').isFocused).ok();
214+
}).before(async () => {
215+
await insertStylesheetRulesToPage(cellStyles);
216+
await createWidget('dxScheduler', { ...getConfig(), scrolling: { mode: scrollingMode } });
217+
});
218+
219+
test(`should not reset scroll after appointment focus and scrolling down (${scrollingMode} scrolling)`, async (t) => {
220+
const scheduler = new Scheduler(SCHEDULER_SELECTOR);
221+
222+
await t
223+
.click(scheduler.getAppointment('[Appointment 1]').element)
224+
.pressKey('tab')
225+
.scroll(scheduler.workspaceScrollable, 0, 1000);
226+
227+
await t.expect(scheduler.workspaceScrollable.scrollTop).eql(1000);
228+
}).before(async () => {
229+
await insertStylesheetRulesToPage(cellStyles);
230+
await createWidget('dxScheduler', { ...getConfig(), scrolling: { mode: scrollingMode } });
231+
});
232+
233+
test(`should focus next appointment on tab after any appointment was clicked (${scrollingMode} scrolling)`, async (t) => {
234+
const scheduler = new Scheduler(SCHEDULER_SELECTOR);
235+
236+
await t
237+
.click(scheduler.getAppointment('[Appointment 15]').element)
238+
.pressKey('tab');
239+
240+
await t
241+
.expect(scheduler.getAppointment('[Appointment 16]').isFocused).ok();
242+
}).before(async () => {
243+
await insertStylesheetRulesToPage(cellStyles);
244+
await createWidget('dxScheduler', { ...getConfig(), scrolling: { mode: scrollingMode } });
48245
});
49-
}).after(async () => {
50-
await ClientFunction(() => {
51-
document.body.style.height = '';
52-
})();
53246
});
54247

55-
test('Document should not scroll on \'Home\' press when appointment is focused', async (t) => {
248+
test('should focus first visible appointment on tab (virtual scrolling)', async (t) => {
56249
const scheduler = new Scheduler(SCHEDULER_SELECTOR);
57250

58251
await t
59-
.scroll(0, 100)
60-
.click(scheduler.getAppointment('Appointment 1').element);
252+
.scroll(scheduler.workspaceScrollable, 0, 1000)
253+
.click(scheduler.toolbar.viewSwitcher.element)
254+
.pressKey('tab')
255+
.pressKey('tab');
61256

62-
const expectedScrollTop = await getDocumentScrollTop();
257+
await t
258+
.expect(scheduler.getAppointment('[Appointment 135]').isFocused).ok();
259+
}).before(async () => {
260+
await insertStylesheetRulesToPage(cellStyles);
261+
await createWidget('dxScheduler', { ...getConfig(), scrolling: { mode: 'virtual' } });
262+
});
263+
264+
test('should focus first rendered appointment on tab (standard scrolling)', async (t) => {
265+
const scheduler = new Scheduler(SCHEDULER_SELECTOR);
266+
267+
await t
268+
.scroll(scheduler.workspaceScrollable, 0, 1000)
269+
.click(scheduler.toolbar.viewSwitcher.element)
270+
.pressKey('tab')
271+
.pressKey('tab');
63272

64273
await t
65-
.pressKey('Home')
66-
.expect(getDocumentScrollTop()).eql(expectedScrollTop);
274+
.expect(scheduler.getAppointment('[Appointment 1]').isFocused).ok();
67275
}).before(async () => {
68-
await ClientFunction(() => {
69-
document.body.style.height = '2000px';
70-
})();
71-
72-
await createWidget('dxScheduler', {
73-
dataSource: [
74-
{
75-
text: 'Appointment 1',
76-
startDate: new Date(2015, 1, 9, 8),
77-
endDate: new Date(2015, 1, 9, 9),
78-
},
79-
{
80-
text: 'Appointment 2',
81-
startDate: new Date(2015, 1, 9, 10),
82-
endDate: new Date(2015, 1, 9, 11),
83-
},
84-
{
85-
text: 'Appointment 3',
86-
startDate: new Date(2015, 1, 9, 12),
87-
endDate: new Date(2015, 1, 9, 13),
88-
},
89-
],
90-
height: 300,
91-
currentView: 'day',
92-
currentDate: new Date(2015, 1, 9),
93-
});
94-
}).after(async () => {
95-
await ClientFunction(() => {
96-
document.body.style.height = '';
97-
})();
276+
await insertStylesheetRulesToPage(cellStyles);
277+
await createWidget('dxScheduler', { ...getConfig(), scrolling: { mode: 'standard' } });
98278
});

0 commit comments

Comments
 (0)