Skip to content

Commit b3d57b7

Browse files
vaadin-botvursen
andauthored
fix: force scroller update after revealing a date (#8217) (#8219)
Co-authored-by: Sergey Vinogradov <mr.vursen@gmail.com>
1 parent 3f8e157 commit b3d57b7

14 files changed

+87
-66
lines changed

integration/tests/context-menu-date-picker.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import '@vaadin/context-menu';
44
import '@vaadin/date-picker';
55
import { isTouch } from '@vaadin/component-base/src/browser-utils';
66
import { getMenuItems, openMenu } from '@vaadin/context-menu/test/helpers';
7-
import { getFocusedCell, open } from '@vaadin/date-picker/test/helpers';
7+
import { getFocusableCell, open } from '@vaadin/date-picker/test/helpers';
88

99
describe('date picker in context menu', () => {
1010
let menu;
@@ -27,7 +27,7 @@ describe('date picker in context menu', () => {
2727
const datePicker = getMenuItems(menu)[0];
2828
await open(datePicker);
2929

30-
const date = getFocusedCell(datePicker._overlayContent);
30+
const date = getFocusableCell(datePicker);
3131
date.click();
3232
await nextFrame();
3333

integration/tests/dialog-date-picker.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ describe('date-picker in dialog', () => {
4141
await sendKeys({ press: 'Tab' });
4242

4343
await nextRender();
44-
await waitForScrollToFinish(overlayContent);
44+
await waitForScrollToFinish(datePicker);
4545

4646
// Focus the Today button
4747
await sendKeys({ press: 'Tab' });
@@ -63,7 +63,7 @@ describe('date-picker in dialog', () => {
6363
await sendKeys({ press: 'Tab' });
6464

6565
await nextRender();
66-
await waitForScrollToFinish(overlayContent);
66+
await waitForScrollToFinish(datePicker);
6767

6868
const spy = sinon.spy(datePicker.inputElement, 'focus');
6969

packages/date-picker/src/vaadin-date-picker-overlay-content-mixin.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ export const DatePickerOverlayContentMixin = (superClass) =>
571571

572572
if (!animate) {
573573
this._monthScroller.position = targetPosition;
574+
this._monthScroller.forceUpdate();
574575
this._targetPosition = undefined;
575576
this._repositionYearScroller();
576577
this.__tryFocusDate();
@@ -626,6 +627,7 @@ export const DatePickerOverlayContentMixin = (superClass) =>
626627
);
627628

628629
this._monthScroller.position = this._targetPosition;
630+
this._monthScroller.forceUpdate();
629631
this._targetPosition = undefined;
630632

631633
revealResolve();

packages/date-picker/src/vaadin-infinite-scroller.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Copyright (c) 2016 - 2024 Vaadin Ltd.
44
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
55
*/
6+
import { flush } from '@polymer/polymer/lib/utils/flush.js';
67
import { timeOut } from '@vaadin/component-base/src/async.js';
78
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
89
import { generateUniqueId } from '@vaadin/component-base/src/unique-id-utils.js';
@@ -205,11 +206,17 @@ export class InfiniteScroller extends HTMLElement {
205206
* waiting for the debouncer to resolve.
206207
*/
207208
forceUpdate() {
209+
if (this._debouncerScrollFinish) {
210+
this._debouncerScrollFinish.flush();
211+
}
212+
208213
if (this._debouncerUpdateClones) {
209214
this._buffers[0].updated = this._buffers[1].updated = false;
210215
this._updateClones();
211216
this._debouncerUpdateClones.cancel();
212217
}
218+
219+
flush();
213220
}
214221

215222
/**

packages/date-picker/test/custom-input.test.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,8 @@ describe('custom input', () => {
6868
});
6969

7070
it('should propagate theme attribute to month calendar', async () => {
71-
const overlayContent = datePicker._overlayContent;
72-
await waitForScrollToFinish(overlayContent);
73-
const monthCalendar = overlayContent.querySelector('vaadin-month-calendar');
71+
await waitForScrollToFinish(datePicker);
72+
const monthCalendar = datePicker._overlayContent.querySelector('vaadin-month-calendar');
7473
expect(monthCalendar.getAttribute('theme')).to.equal('foo');
7574
});
7675
});

packages/date-picker/test/dropdown.common.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
} from '@vaadin/testing-helpers';
1313
import { sendKeys } from '@web/test-runner-commands';
1414
import sinon from 'sinon';
15-
import { getFocusedCell, monthsEqual, open, waitForOverlayRender } from './helpers.js';
15+
import { getFocusableCell, monthsEqual, open, waitForOverlayRender } from './helpers.js';
1616

1717
describe('dropdown', () => {
1818
let datePicker, input, overlay;
@@ -235,7 +235,7 @@ describe('dropdown', () => {
235235

236236
describe('date tap', () => {
237237
function dateTap() {
238-
const date = getFocusedCell(datePicker._overlayContent);
238+
const date = getFocusableCell(datePicker);
239239
mousedown(date);
240240
date.focus();
241241
date.click();

packages/date-picker/test/events.common.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe('events', () => {
2727

2828
it('should not be fired on programmatic value change when having user input', async () => {
2929
await sendKeys({ type: '1/2/2000' });
30-
await waitForScrollToFinish(datePicker._overlayContent);
30+
await waitForScrollToFinish(datePicker);
3131
datePicker.value = '2000-01-01';
3232
await close(datePicker);
3333
expect(changeSpy.called).to.be.false;
@@ -52,7 +52,7 @@ describe('events', () => {
5252
describe('with user input', () => {
5353
beforeEach(async () => {
5454
await sendKeys({ type: '1/1/2022' });
55-
await waitForScrollToFinish(datePicker._overlayContent);
55+
await waitForScrollToFinish(datePicker);
5656
hasInputValueChangedSpy.resetHistory();
5757
});
5858

@@ -86,7 +86,7 @@ describe('events', () => {
8686
describe('with value', () => {
8787
beforeEach(async () => {
8888
await sendKeys({ type: '1/1/2022' });
89-
await waitForScrollToFinish(datePicker._overlayContent);
89+
await waitForScrollToFinish(datePicker);
9090
await sendKeys({ press: 'Enter' });
9191
valueChangedSpy.resetHistory();
9292
hasInputValueChangedSpy.resetHistory();

packages/date-picker/test/fullscreen.common.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { expect } from '@esm-bundle/chai';
22
import { aTimeout, fixtureSync, nextRender, nextUpdate, outsideClick, tabKeyDown, tap } from '@vaadin/testing-helpers';
33
import { sendKeys, setViewport } from '@web/test-runner-commands';
44
import sinon from 'sinon';
5-
import { getFocusedCell, open, touchTap, waitForOverlayRender } from './helpers.js';
5+
import { getFocusableCell, open, touchTap, waitForOverlayRender } from './helpers.js';
66

77
describe('fullscreen mode', () => {
88
let datePicker, input, overlay, width, height;
@@ -74,7 +74,7 @@ describe('fullscreen mode', () => {
7474

7575
it('should focus date element when opening overlay', async () => {
7676
await open(datePicker);
77-
const cell = getFocusedCell(datePicker._overlayContent);
77+
const cell = getFocusableCell(datePicker);
7878
expect(cell).to.be.instanceOf(HTMLTableCellElement);
7979
expect(cell.getAttribute('part')).to.include('today');
8080
});
@@ -166,7 +166,7 @@ describe('fullscreen mode', () => {
166166
});
167167

168168
it('should move focus to date cell button on Cancel button Tab', async () => {
169-
const cell = getFocusedCell(overlayContent);
169+
const cell = getFocusableCell(datePicker);
170170
const spy = sinon.spy(cell, 'focus');
171171

172172
// Move focus to Cancel button

packages/date-picker/test/helpers.js

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { fire, listenOnce, makeSoloTouchEvent, nextRender } from '@vaadin/testing-helpers';
22
import { flush } from '@polymer/polymer/lib/utils/flush.js';
33
import { afterNextRender } from '@polymer/polymer/lib/utils/render-status.js';
4+
import { isElementFocused } from '@vaadin/a11y-base/src/focus-utils.js';
45

56
export function activateScroller(scroller) {
67
scroller.active = true;
@@ -108,24 +109,38 @@ export function getFirstVisibleItem(scroller, bufferOffset = 0) {
108109
});
109110
}
110111

111-
export function getFocusedMonth(overlayContent) {
112-
const months = [...overlayContent.querySelectorAll('vaadin-month-calendar')];
113-
return months.find((month) => {
114-
return !!month.shadowRoot.querySelector('[part~="focused"]');
112+
/**
113+
* @param {HTMLElement} root vaadin-date-picker or vaadin-date-picker-overlay-content
114+
*/
115+
export function getFocusableCell(root) {
116+
const overlayContent = root._overlayContent ?? root;
117+
const focusableMonth = [...overlayContent.querySelectorAll('vaadin-month-calendar')].find((month) => {
118+
return !!month.shadowRoot.querySelector('[tabindex="0"]');
115119
});
120+
121+
if (focusableMonth) {
122+
return focusableMonth.shadowRoot.querySelector('[tabindex="0"]');
123+
}
116124
}
117125

118-
export function getFocusedCell(overlayContent) {
119-
const focusedMonth = getFocusedMonth(overlayContent);
120-
return focusedMonth.shadowRoot.querySelector('[part~="focused"]');
126+
/**
127+
* @param {HTMLElement} root vaadin-date-picker or vaadin-date-picker-overlay-content
128+
*/
129+
export function getFocusedCell(root) {
130+
const focusableCell = getFocusableCell(root);
131+
if (focusableCell && isElementFocused(focusableCell)) {
132+
return focusableCell;
133+
}
121134
}
122135

123136
/**
124137
* Waits for the scroll to finish in the date-picker overlay content.
125138
*
126-
* @param {HTMLElement} overlayContent
139+
* @param {HTMLElement} root vaadin-date-picker or vaadin-date-picker-overlay-content
127140
*/
128-
export async function waitForScrollToFinish(overlayContent) {
141+
export async function waitForScrollToFinish(root) {
142+
const overlayContent = root._overlayContent ?? root;
143+
129144
if (overlayContent._revealPromise) {
130145
// The overlay content is scrolling.
131146
await overlayContent._revealPromise;

packages/date-picker/test/keyboard-input.common.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ describe('keyboard', () => {
6565
// FIXME: flaky test often failing locally due to scroll animation
6666
it.skip('should display focused date while overlay focused', async () => {
6767
await sendKeys({ type: '1/2/2000' });
68-
const content = datePicker._overlayContent;
69-
await waitForScrollToFinish(content);
68+
await waitForScrollToFinish(datePicker);
7069

7170
// Move focus to the calendar
7271
await sendKeys({ press: 'Tab' });
@@ -212,7 +211,7 @@ describe('keyboard', () => {
212211
// Move focus to the calendar
213212
await sendKeys({ press: 'Tab' });
214213
await waitForOverlayRender();
215-
const cell = getFocusedCell(overlayContent);
214+
const cell = getFocusedCell(datePicker);
216215
const spy = sinon.spy(input, 'focus');
217216
tap(cell);
218217
expect(spy.calledOnce).to.be.true;
@@ -284,9 +283,9 @@ describe('keyboard', () => {
284283
// Move focus to the calendar
285284
await sendKeys({ press: 'Tab' });
286285

287-
await waitForScrollToFinish(overlayContent);
286+
await waitForScrollToFinish(datePicker);
288287

289-
const cell = getFocusedCell(overlayContent);
288+
const cell = getFocusedCell(datePicker);
290289
expect(cell).to.be.instanceOf(HTMLTableCellElement);
291290
expect(cell.getAttribute('part')).to.contain('today');
292291
});
@@ -300,9 +299,9 @@ describe('keyboard', () => {
300299
await sendKeys({ press: 'Tab' });
301300
await sendKeys({ up: 'Shift' });
302301

303-
await waitForScrollToFinish(overlayContent);
302+
await waitForScrollToFinish(datePicker);
304303

305-
const cell = getFocusedCell(overlayContent);
304+
const cell = getFocusedCell(datePicker);
306305
expect(cell).to.be.instanceOf(HTMLTableCellElement);
307306
expect(cell.getAttribute('part')).to.contain('today');
308307
});

packages/date-picker/test/keyboard-navigation.common.js

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ describe('keyboard navigation', () => {
3131
// Move focus to the calendar
3232
await sendKeys({ press: 'Tab' });
3333

34-
const cell = getFocusedCell(datePicker._overlayContent);
34+
const cell = getFocusedCell(datePicker);
3535
expect(cell.date).to.eql(new Date(today.getFullYear(), today.getMonth(), today.getDate()));
3636
});
3737

@@ -47,7 +47,7 @@ describe('keyboard navigation', () => {
4747
// Move focus to the calendar
4848
await sendKeys({ press: 'Tab' });
4949

50-
const cell = getFocusedCell(datePicker._overlayContent);
50+
const cell = getFocusedCell(datePicker);
5151
expect(cell.date).to.eql(new Date(today.getFullYear(), today.getMonth(), today.getDate()));
5252
});
5353
});
@@ -66,7 +66,7 @@ describe('keyboard navigation', () => {
6666
// Move focus to the calendar
6767
await sendKeys({ press: 'Tab' });
6868

69-
const cell = getFocusedCell(datePicker._overlayContent);
69+
const cell = getFocusedCell(datePicker);
7070
expect(cell.date).to.eql(new Date(2001, 0, 1));
7171
});
7272

@@ -101,7 +101,7 @@ describe('keyboard navigation', () => {
101101
// Move focus to the calendar
102102
await sendKeys({ press: 'Tab' });
103103

104-
const cell = getFocusedCell(datePicker._overlayContent);
104+
const cell = getFocusedCell(datePicker);
105105
expect(cell.date).to.eql(new Date(2001, 0, 1));
106106
});
107107

@@ -115,7 +115,7 @@ describe('keyboard navigation', () => {
115115
// Move focus to the calendar
116116
await sendKeys({ press: 'Tab' });
117117

118-
const cell = getFocusedCell(datePicker._overlayContent);
118+
const cell = getFocusedCell(datePicker);
119119
expect(cell.date).to.eql(new Date(2001, 0, 1));
120120
});
121121
});
@@ -142,20 +142,20 @@ describe('keyboard navigation', () => {
142142

143143
// Move focus inside the dropdown to the typed date.
144144
await sendKeys({ press: 'ArrowDown' });
145-
await waitForScrollToFinish(datePicker._overlayContent);
145+
await waitForScrollToFinish(datePicker);
146146

147147
// Move focus to the previous week and it should instead move to the min date
148148
await sendKeys({ press: 'ArrowUp' });
149-
await waitForScrollToFinish(datePicker._overlayContent);
149+
await waitForScrollToFinish(datePicker);
150150

151-
let cell = getFocusedCell(datePicker._overlayContent);
151+
let cell = getFocusedCell(datePicker);
152152
expect(cell.date).to.eql(new Date(2010, 0, 1));
153153

154154
// Attempt to move focus to the previous week and it should stay on the min date
155155
await sendKeys({ press: 'ArrowUp' });
156-
await waitForScrollToFinish(datePicker._overlayContent);
156+
await waitForScrollToFinish(datePicker);
157157

158-
cell = getFocusedCell(datePicker._overlayContent);
158+
cell = getFocusedCell(datePicker);
159159
expect(cell.date).to.eql(new Date(2010, 0, 1));
160160
});
161161

@@ -164,20 +164,20 @@ describe('keyboard navigation', () => {
164164

165165
// Move focus inside the dropdown to the typed date.
166166
await sendKeys({ press: 'ArrowDown' });
167-
await waitForScrollToFinish(datePicker._overlayContent);
167+
await waitForScrollToFinish(datePicker);
168168

169169
// Move focus to the next week and it should instead move to the max date
170170
await sendKeys({ press: 'ArrowDown' });
171-
await waitForScrollToFinish(datePicker._overlayContent);
171+
await waitForScrollToFinish(datePicker);
172172

173-
let cell = getFocusedCell(datePicker._overlayContent);
173+
let cell = getFocusedCell(datePicker);
174174
expect(cell.date).to.eql(new Date(2010, 0, 31));
175175

176176
// Attempt to move focus to the next week and it should stay on the max date
177177
await sendKeys({ press: 'ArrowDown' });
178-
await waitForScrollToFinish(datePicker._overlayContent);
178+
await waitForScrollToFinish(datePicker);
179179

180-
cell = getFocusedCell(datePicker._overlayContent);
180+
cell = getFocusedCell(datePicker);
181181
expect(cell.date).to.eql(new Date(2010, 0, 31));
182182
});
183183

@@ -186,13 +186,13 @@ describe('keyboard navigation', () => {
186186

187187
// Move focus inside the dropdown to the typed date.
188188
await sendKeys({ press: 'ArrowDown' });
189-
await waitForScrollToFinish(datePicker._overlayContent);
189+
await waitForScrollToFinish(datePicker);
190190

191191
// Move focus to a disabled date
192192
await sendKeys({ press: 'ArrowRight' });
193-
await waitForScrollToFinish(datePicker._overlayContent);
193+
await waitForScrollToFinish(datePicker);
194194

195-
const cell = getFocusedCell(datePicker._overlayContent);
195+
const cell = getFocusedCell(datePicker);
196196
expect(cell.date).to.eql(new Date(2010, 0, 29));
197197
});
198198

@@ -202,7 +202,7 @@ describe('keyboard navigation', () => {
202202
// Move focus to a disabled date
203203
await sendKeys({ press: 'ArrowDown' });
204204
await sendKeys({ press: 'ArrowRight' });
205-
await waitForScrollToFinish(datePicker._overlayContent);
205+
await waitForScrollToFinish(datePicker);
206206

207207
await sendKeys({ press: 'Enter' });
208208

packages/date-picker/test/theme-propagation.common.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,8 @@ describe('theme attribute', () => {
3030
});
3131

3232
it('should propagate theme attribute to month calendar', async () => {
33-
const overlayContent = datePicker._overlayContent;
34-
await waitForScrollToFinish(overlayContent);
35-
const monthCalendar = overlayContent.querySelector('vaadin-month-calendar');
33+
await waitForScrollToFinish(datePicker);
34+
const monthCalendar = datePicker._overlayContent.querySelector('vaadin-month-calendar');
3635
expect(monthCalendar.getAttribute('theme')).to.equal('foo');
3736
});
3837
});

0 commit comments

Comments
 (0)