diff --git a/integration/tests/context-menu-date-picker.test.js b/integration/tests/context-menu-date-picker.test.js index 24f81b3acd..9060668a4d 100644 --- a/integration/tests/context-menu-date-picker.test.js +++ b/integration/tests/context-menu-date-picker.test.js @@ -4,7 +4,7 @@ import '@vaadin/context-menu'; import '@vaadin/date-picker'; import { isTouch } from '@vaadin/component-base/src/browser-utils'; import { getMenuItems, openMenu } from '@vaadin/context-menu/test/helpers'; -import { getFocusedCell, open } from '@vaadin/date-picker/test/helpers'; +import { getFocusableCell, open } from '@vaadin/date-picker/test/helpers'; describe('date picker in context menu', () => { let menu; @@ -27,7 +27,7 @@ describe('date picker in context menu', () => { const datePicker = getMenuItems(menu)[0]; await open(datePicker); - const date = getFocusedCell(datePicker._overlayContent); + const date = getFocusableCell(datePicker); date.click(); await nextFrame(); diff --git a/integration/tests/dialog-date-picker.test.js b/integration/tests/dialog-date-picker.test.js index 5c43e65832..4e36958892 100644 --- a/integration/tests/dialog-date-picker.test.js +++ b/integration/tests/dialog-date-picker.test.js @@ -41,7 +41,7 @@ describe('date-picker in dialog', () => { await sendKeys({ press: 'Tab' }); await nextRender(); - await waitForScrollToFinish(overlayContent); + await waitForScrollToFinish(datePicker); // Focus the Today button await sendKeys({ press: 'Tab' }); @@ -63,7 +63,7 @@ describe('date-picker in dialog', () => { await sendKeys({ press: 'Tab' }); await nextRender(); - await waitForScrollToFinish(overlayContent); + await waitForScrollToFinish(datePicker); const spy = sinon.spy(datePicker.inputElement, 'focus'); diff --git a/packages/date-picker/src/vaadin-date-picker-overlay-content-mixin.js b/packages/date-picker/src/vaadin-date-picker-overlay-content-mixin.js index 028a679378..24f1653fbf 100644 --- a/packages/date-picker/src/vaadin-date-picker-overlay-content-mixin.js +++ b/packages/date-picker/src/vaadin-date-picker-overlay-content-mixin.js @@ -571,6 +571,7 @@ export const DatePickerOverlayContentMixin = (superClass) => if (!animate) { this._monthScroller.position = targetPosition; + this._monthScroller.forceUpdate(); this._targetPosition = undefined; this._repositionYearScroller(); this.__tryFocusDate(); @@ -626,6 +627,7 @@ export const DatePickerOverlayContentMixin = (superClass) => ); this._monthScroller.position = this._targetPosition; + this._monthScroller.forceUpdate(); this._targetPosition = undefined; revealResolve(); diff --git a/packages/date-picker/src/vaadin-infinite-scroller.js b/packages/date-picker/src/vaadin-infinite-scroller.js index d35d2ba332..1bd340f2a5 100644 --- a/packages/date-picker/src/vaadin-infinite-scroller.js +++ b/packages/date-picker/src/vaadin-infinite-scroller.js @@ -3,6 +3,7 @@ * Copyright (c) 2016 - 2024 Vaadin Ltd. * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ */ +import { flush } from '@polymer/polymer/lib/utils/flush.js'; import { timeOut } from '@vaadin/component-base/src/async.js'; import { Debouncer } from '@vaadin/component-base/src/debounce.js'; import { generateUniqueId } from '@vaadin/component-base/src/unique-id-utils.js'; @@ -205,11 +206,17 @@ export class InfiniteScroller extends HTMLElement { * waiting for the debouncer to resolve. */ forceUpdate() { + if (this._debouncerScrollFinish) { + this._debouncerScrollFinish.flush(); + } + if (this._debouncerUpdateClones) { this._buffers[0].updated = this._buffers[1].updated = false; this._updateClones(); this._debouncerUpdateClones.cancel(); } + + flush(); } /** diff --git a/packages/date-picker/test/custom-input.test.js b/packages/date-picker/test/custom-input.test.js index 3760eb9356..bd37d8f656 100644 --- a/packages/date-picker/test/custom-input.test.js +++ b/packages/date-picker/test/custom-input.test.js @@ -68,9 +68,8 @@ describe('custom input', () => { }); it('should propagate theme attribute to month calendar', async () => { - const overlayContent = datePicker._overlayContent; - await waitForScrollToFinish(overlayContent); - const monthCalendar = overlayContent.querySelector('vaadin-month-calendar'); + await waitForScrollToFinish(datePicker); + const monthCalendar = datePicker._overlayContent.querySelector('vaadin-month-calendar'); expect(monthCalendar.getAttribute('theme')).to.equal('foo'); }); }); diff --git a/packages/date-picker/test/dropdown.common.js b/packages/date-picker/test/dropdown.common.js index bf9e79ed66..9784f06295 100644 --- a/packages/date-picker/test/dropdown.common.js +++ b/packages/date-picker/test/dropdown.common.js @@ -12,7 +12,7 @@ import { } from '@vaadin/testing-helpers'; import { sendKeys } from '@web/test-runner-commands'; import sinon from 'sinon'; -import { getFocusedCell, monthsEqual, open, waitForOverlayRender } from './helpers.js'; +import { getFocusableCell, monthsEqual, open, waitForOverlayRender } from './helpers.js'; describe('dropdown', () => { let datePicker, input, overlay; @@ -235,7 +235,7 @@ describe('dropdown', () => { describe('date tap', () => { function dateTap() { - const date = getFocusedCell(datePicker._overlayContent); + const date = getFocusableCell(datePicker); mousedown(date); date.focus(); date.click(); diff --git a/packages/date-picker/test/events.common.js b/packages/date-picker/test/events.common.js index 9daaa460ad..6f164c4f05 100644 --- a/packages/date-picker/test/events.common.js +++ b/packages/date-picker/test/events.common.js @@ -27,7 +27,7 @@ describe('events', () => { it('should not be fired on programmatic value change when having user input', async () => { await sendKeys({ type: '1/2/2000' }); - await waitForScrollToFinish(datePicker._overlayContent); + await waitForScrollToFinish(datePicker); datePicker.value = '2000-01-01'; await close(datePicker); expect(changeSpy.called).to.be.false; @@ -52,7 +52,7 @@ describe('events', () => { describe('with user input', () => { beforeEach(async () => { await sendKeys({ type: '1/1/2022' }); - await waitForScrollToFinish(datePicker._overlayContent); + await waitForScrollToFinish(datePicker); hasInputValueChangedSpy.resetHistory(); }); @@ -86,7 +86,7 @@ describe('events', () => { describe('with value', () => { beforeEach(async () => { await sendKeys({ type: '1/1/2022' }); - await waitForScrollToFinish(datePicker._overlayContent); + await waitForScrollToFinish(datePicker); await sendKeys({ press: 'Enter' }); valueChangedSpy.resetHistory(); hasInputValueChangedSpy.resetHistory(); diff --git a/packages/date-picker/test/fullscreen.common.js b/packages/date-picker/test/fullscreen.common.js index 8e8105531f..6261a1ab6a 100644 --- a/packages/date-picker/test/fullscreen.common.js +++ b/packages/date-picker/test/fullscreen.common.js @@ -2,7 +2,7 @@ import { expect } from '@esm-bundle/chai'; import { aTimeout, fixtureSync, nextRender, nextUpdate, outsideClick, tabKeyDown, tap } from '@vaadin/testing-helpers'; import { sendKeys, setViewport } from '@web/test-runner-commands'; import sinon from 'sinon'; -import { getFocusedCell, open, touchTap, waitForOverlayRender } from './helpers.js'; +import { getFocusableCell, open, touchTap, waitForOverlayRender } from './helpers.js'; describe('fullscreen mode', () => { let datePicker, input, overlay, width, height; @@ -74,7 +74,7 @@ describe('fullscreen mode', () => { it('should focus date element when opening overlay', async () => { await open(datePicker); - const cell = getFocusedCell(datePicker._overlayContent); + const cell = getFocusableCell(datePicker); expect(cell).to.be.instanceOf(HTMLTableCellElement); expect(cell.getAttribute('part')).to.include('today'); }); @@ -166,7 +166,7 @@ describe('fullscreen mode', () => { }); it('should move focus to date cell button on Cancel button Tab', async () => { - const cell = getFocusedCell(overlayContent); + const cell = getFocusableCell(datePicker); const spy = sinon.spy(cell, 'focus'); // Move focus to Cancel button diff --git a/packages/date-picker/test/helpers.js b/packages/date-picker/test/helpers.js index 144c96d9b8..128e6ed0ec 100644 --- a/packages/date-picker/test/helpers.js +++ b/packages/date-picker/test/helpers.js @@ -1,6 +1,7 @@ import { fire, listenOnce, makeSoloTouchEvent, nextRender } from '@vaadin/testing-helpers'; import { flush } from '@polymer/polymer/lib/utils/flush.js'; import { afterNextRender } from '@polymer/polymer/lib/utils/render-status.js'; +import { isElementFocused } from '@vaadin/a11y-base/src/focus-utils.js'; export function activateScroller(scroller) { scroller.active = true; @@ -108,24 +109,38 @@ export function getFirstVisibleItem(scroller, bufferOffset = 0) { }); } -export function getFocusedMonth(overlayContent) { - const months = [...overlayContent.querySelectorAll('vaadin-month-calendar')]; - return months.find((month) => { - return !!month.shadowRoot.querySelector('[part~="focused"]'); +/** + * @param {HTMLElement} root vaadin-date-picker or vaadin-date-picker-overlay-content + */ +export function getFocusableCell(root) { + const overlayContent = root._overlayContent ?? root; + const focusableMonth = [...overlayContent.querySelectorAll('vaadin-month-calendar')].find((month) => { + return !!month.shadowRoot.querySelector('[tabindex="0"]'); }); + + if (focusableMonth) { + return focusableMonth.shadowRoot.querySelector('[tabindex="0"]'); + } } -export function getFocusedCell(overlayContent) { - const focusedMonth = getFocusedMonth(overlayContent); - return focusedMonth.shadowRoot.querySelector('[part~="focused"]'); +/** + * @param {HTMLElement} root vaadin-date-picker or vaadin-date-picker-overlay-content + */ +export function getFocusedCell(root) { + const focusableCell = getFocusableCell(root); + if (focusableCell && isElementFocused(focusableCell)) { + return focusableCell; + } } /** * Waits for the scroll to finish in the date-picker overlay content. * - * @param {HTMLElement} overlayContent + * @param {HTMLElement} root vaadin-date-picker or vaadin-date-picker-overlay-content */ -export async function waitForScrollToFinish(overlayContent) { +export async function waitForScrollToFinish(root) { + const overlayContent = root._overlayContent ?? root; + if (overlayContent._revealPromise) { // The overlay content is scrolling. await overlayContent._revealPromise; diff --git a/packages/date-picker/test/keyboard-input.common.js b/packages/date-picker/test/keyboard-input.common.js index ac2c6e8464..f630bb8e56 100644 --- a/packages/date-picker/test/keyboard-input.common.js +++ b/packages/date-picker/test/keyboard-input.common.js @@ -65,8 +65,7 @@ describe('keyboard', () => { // FIXME: flaky test often failing locally due to scroll animation it.skip('should display focused date while overlay focused', async () => { await sendKeys({ type: '1/2/2000' }); - const content = datePicker._overlayContent; - await waitForScrollToFinish(content); + await waitForScrollToFinish(datePicker); // Move focus to the calendar await sendKeys({ press: 'Tab' }); @@ -212,7 +211,7 @@ describe('keyboard', () => { // Move focus to the calendar await sendKeys({ press: 'Tab' }); await waitForOverlayRender(); - const cell = getFocusedCell(overlayContent); + const cell = getFocusedCell(datePicker); const spy = sinon.spy(input, 'focus'); tap(cell); expect(spy.calledOnce).to.be.true; @@ -284,9 +283,9 @@ describe('keyboard', () => { // Move focus to the calendar await sendKeys({ press: 'Tab' }); - await waitForScrollToFinish(overlayContent); + await waitForScrollToFinish(datePicker); - const cell = getFocusedCell(overlayContent); + const cell = getFocusedCell(datePicker); expect(cell).to.be.instanceOf(HTMLTableCellElement); expect(cell.getAttribute('part')).to.contain('today'); }); @@ -300,9 +299,9 @@ describe('keyboard', () => { await sendKeys({ press: 'Tab' }); await sendKeys({ up: 'Shift' }); - await waitForScrollToFinish(overlayContent); + await waitForScrollToFinish(datePicker); - const cell = getFocusedCell(overlayContent); + const cell = getFocusedCell(datePicker); expect(cell).to.be.instanceOf(HTMLTableCellElement); expect(cell.getAttribute('part')).to.contain('today'); }); diff --git a/packages/date-picker/test/keyboard-navigation.common.js b/packages/date-picker/test/keyboard-navigation.common.js index f805314730..48cf497847 100644 --- a/packages/date-picker/test/keyboard-navigation.common.js +++ b/packages/date-picker/test/keyboard-navigation.common.js @@ -31,7 +31,7 @@ describe('keyboard navigation', () => { // Move focus to the calendar await sendKeys({ press: 'Tab' }); - const cell = getFocusedCell(datePicker._overlayContent); + const cell = getFocusedCell(datePicker); expect(cell.date).to.eql(new Date(today.getFullYear(), today.getMonth(), today.getDate())); }); @@ -47,7 +47,7 @@ describe('keyboard navigation', () => { // Move focus to the calendar await sendKeys({ press: 'Tab' }); - const cell = getFocusedCell(datePicker._overlayContent); + const cell = getFocusedCell(datePicker); expect(cell.date).to.eql(new Date(today.getFullYear(), today.getMonth(), today.getDate())); }); }); @@ -66,7 +66,7 @@ describe('keyboard navigation', () => { // Move focus to the calendar await sendKeys({ press: 'Tab' }); - const cell = getFocusedCell(datePicker._overlayContent); + const cell = getFocusedCell(datePicker); expect(cell.date).to.eql(new Date(2001, 0, 1)); }); @@ -101,7 +101,7 @@ describe('keyboard navigation', () => { // Move focus to the calendar await sendKeys({ press: 'Tab' }); - const cell = getFocusedCell(datePicker._overlayContent); + const cell = getFocusedCell(datePicker); expect(cell.date).to.eql(new Date(2001, 0, 1)); }); @@ -115,7 +115,7 @@ describe('keyboard navigation', () => { // Move focus to the calendar await sendKeys({ press: 'Tab' }); - const cell = getFocusedCell(datePicker._overlayContent); + const cell = getFocusedCell(datePicker); expect(cell.date).to.eql(new Date(2001, 0, 1)); }); }); @@ -142,20 +142,20 @@ describe('keyboard navigation', () => { // Move focus inside the dropdown to the typed date. await sendKeys({ press: 'ArrowDown' }); - await waitForScrollToFinish(datePicker._overlayContent); + await waitForScrollToFinish(datePicker); // Move focus to the previous week and it should instead move to the min date await sendKeys({ press: 'ArrowUp' }); - await waitForScrollToFinish(datePicker._overlayContent); + await waitForScrollToFinish(datePicker); - let cell = getFocusedCell(datePicker._overlayContent); + let cell = getFocusedCell(datePicker); expect(cell.date).to.eql(new Date(2010, 0, 1)); // Attempt to move focus to the previous week and it should stay on the min date await sendKeys({ press: 'ArrowUp' }); - await waitForScrollToFinish(datePicker._overlayContent); + await waitForScrollToFinish(datePicker); - cell = getFocusedCell(datePicker._overlayContent); + cell = getFocusedCell(datePicker); expect(cell.date).to.eql(new Date(2010, 0, 1)); }); @@ -164,20 +164,20 @@ describe('keyboard navigation', () => { // Move focus inside the dropdown to the typed date. await sendKeys({ press: 'ArrowDown' }); - await waitForScrollToFinish(datePicker._overlayContent); + await waitForScrollToFinish(datePicker); // Move focus to the next week and it should instead move to the max date await sendKeys({ press: 'ArrowDown' }); - await waitForScrollToFinish(datePicker._overlayContent); + await waitForScrollToFinish(datePicker); - let cell = getFocusedCell(datePicker._overlayContent); + let cell = getFocusedCell(datePicker); expect(cell.date).to.eql(new Date(2010, 0, 31)); // Attempt to move focus to the next week and it should stay on the max date await sendKeys({ press: 'ArrowDown' }); - await waitForScrollToFinish(datePicker._overlayContent); + await waitForScrollToFinish(datePicker); - cell = getFocusedCell(datePicker._overlayContent); + cell = getFocusedCell(datePicker); expect(cell.date).to.eql(new Date(2010, 0, 31)); }); @@ -186,13 +186,13 @@ describe('keyboard navigation', () => { // Move focus inside the dropdown to the typed date. await sendKeys({ press: 'ArrowDown' }); - await waitForScrollToFinish(datePicker._overlayContent); + await waitForScrollToFinish(datePicker); // Move focus to a disabled date await sendKeys({ press: 'ArrowRight' }); - await waitForScrollToFinish(datePicker._overlayContent); + await waitForScrollToFinish(datePicker); - const cell = getFocusedCell(datePicker._overlayContent); + const cell = getFocusedCell(datePicker); expect(cell.date).to.eql(new Date(2010, 0, 29)); }); @@ -202,7 +202,7 @@ describe('keyboard navigation', () => { // Move focus to a disabled date await sendKeys({ press: 'ArrowDown' }); await sendKeys({ press: 'ArrowRight' }); - await waitForScrollToFinish(datePicker._overlayContent); + await waitForScrollToFinish(datePicker); await sendKeys({ press: 'Enter' }); diff --git a/packages/date-picker/test/theme-propagation.common.js b/packages/date-picker/test/theme-propagation.common.js index dde776041d..9d4fecfbb1 100644 --- a/packages/date-picker/test/theme-propagation.common.js +++ b/packages/date-picker/test/theme-propagation.common.js @@ -30,9 +30,8 @@ describe('theme attribute', () => { }); it('should propagate theme attribute to month calendar', async () => { - const overlayContent = datePicker._overlayContent; - await waitForScrollToFinish(overlayContent); - const monthCalendar = overlayContent.querySelector('vaadin-month-calendar'); + await waitForScrollToFinish(datePicker); + const monthCalendar = datePicker._overlayContent.querySelector('vaadin-month-calendar'); expect(monthCalendar.getAttribute('theme')).to.equal('foo'); }); }); diff --git a/packages/date-picker/test/value-commit.common.js b/packages/date-picker/test/value-commit.common.js index df0cd4b263..4d35e882b7 100644 --- a/packages/date-picker/test/value-commit.common.js +++ b/packages/date-picker/test/value-commit.common.js @@ -255,7 +255,7 @@ describe('value commit', () => { await waitForOverlayRender(); // Focus yesterday's date. await sendKeys({ press: 'ArrowLeft' }); - await waitForScrollToFinish(datePicker._overlayContent); + await waitForScrollToFinish(datePicker); }); it('should commit on focused date selection with click', () => { @@ -315,7 +315,7 @@ describe('value commit', () => { beforeEach(async () => { // Focus yesterday's date. await sendKeys({ press: 'ArrowLeft' }); - await waitForScrollToFinish(datePicker._overlayContent); + await waitForScrollToFinish(datePicker); }); it('should commit on focused date selection with click', () => { diff --git a/packages/grid/test/column-rendering.common.js b/packages/grid/test/column-rendering.common.js index 4b3606246f..3da3fc44b3 100644 --- a/packages/grid/test/column-rendering.common.js +++ b/packages/grid/test/column-rendering.common.js @@ -541,7 +541,7 @@ import { flushGrid, getCellContent, getHeaderCellContent, onceResized } from './ keyDownOn(grid.shadowRoot.activeElement, 9, 'shift', 'Tab'); } - function getFocusedCellText() { + function getFocusableCellText() { const focusedCell = grid.shadowRoot.activeElement; return getCellContent(focusedCell).textContent; } @@ -556,24 +556,24 @@ import { flushGrid, getCellContent, getHeaderCellContent, onceResized } from './ const lastVisibleCell = getBodyCell(0, lastVisibleIndex); lastVisibleCell.focus(); backward(); - expect(getFocusedCellText()).to.equal(`cell ${lastVisibleIndex - 1}`); + expect(getFocusableCellText()).to.equal(`cell ${lastVisibleIndex - 1}`); forward(); - expect(getFocusedCellText()).to.equal(`cell ${lastVisibleIndex}`); + expect(getFocusableCellText()).to.equal(`cell ${lastVisibleIndex}`); forward(); - expect(getFocusedCellText()).to.equal(`cell ${lastVisibleIndex + 1}`); + expect(getFocusableCellText()).to.equal(`cell ${lastVisibleIndex + 1}`); forward(); - expect(getFocusedCellText()).to.equal(`cell ${lastVisibleIndex + 2}`); + expect(getFocusableCellText()).to.equal(`cell ${lastVisibleIndex + 2}`); }); it('should focus the last cell on the row', () => { end(); - expect(getFocusedCellText()).to.equal(`cell ${columns.length - 1}`); + expect(getFocusableCellText()).to.equal(`cell ${columns.length - 1}`); }); it('should focus the first cell on the row', () => { end(); home(); - expect(getFocusedCellText()).to.equal('cell 0'); + expect(getFocusableCellText()).to.equal('cell 0'); }); it('should focus the frozen to end cell without scrolling', async () => { @@ -597,7 +597,7 @@ import { flushGrid, getCellContent, getHeaderCellContent, onceResized } from './ columns.at(-1).frozenToEnd = true; end(); backward(); - expect(getFocusedCellText()).to.equal(`cell ${columns.length - 2}`); + expect(getFocusableCellText()).to.equal(`cell ${columns.length - 2}`); }); it('should focus the cell after frozen cell', () => { @@ -605,7 +605,7 @@ import { flushGrid, getCellContent, getHeaderCellContent, onceResized } from './ end(); home(); forward(); - expect(getFocusedCellText()).to.equal('cell 1'); + expect(getFocusableCellText()).to.equal('cell 1'); }); it('should have a focusable body cell after scrolling', async () => { @@ -628,7 +628,7 @@ import { flushGrid, getCellContent, getHeaderCellContent, onceResized } from './ it('should not change the focusable body cell after scrolling', async () => { forward(); - const focusedCellText = getFocusedCellText(); + const focusedCellText = getFocusableCellText(); // Tab to header shiftTab(); await nextFrame(); @@ -641,7 +641,7 @@ import { flushGrid, getCellContent, getHeaderCellContent, onceResized } from './ await nextFrame(); // Expect the focused element to be the same cell - expect(getFocusedCellText()).to.equal(focusedCellText); + expect(getFocusableCellText()).to.equal(focusedCellText); }); }); });