diff --git a/packages/grid/src/vaadin-grid-mixin.js b/packages/grid/src/vaadin-grid-mixin.js
index 85d4df21fb..99bf814997 100644
--- a/packages/grid/src/vaadin-grid-mixin.js
+++ b/packages/grid/src/vaadin-grid-mixin.js
@@ -600,8 +600,8 @@ export const GridMixin = (superClass) =>
// Patch `focus()` to use the button
cell._focusButton = div;
- cell.focus = function () {
- cell._focusButton.focus();
+ cell.focus = function (options) {
+ cell._focusButton.focus(options);
};
div.appendChild(slot);
@@ -624,7 +624,7 @@ export const GridMixin = (superClass) =>
// Only focus if mouse is released on cell content itself
const mouseUpWithinCell = event.composedPath().includes(cellContent);
if (!contentContainsFocusedElement && mouseUpWithinCell) {
- cell.focus();
+ cell.focus({ preventScroll: true });
}
document.removeEventListener('mouseup', mouseUpListener, true);
};
@@ -634,7 +634,7 @@ export const GridMixin = (superClass) =>
// Watch out sync focus removal issue, only async focus works here.
setTimeout(() => {
if (!cellContent.contains(this.getRootNode().activeElement)) {
- cell.focus();
+ cell.focus({ preventScroll: true });
}
});
}
diff --git a/packages/grid/src/vaadin-grid-scroll-mixin.js b/packages/grid/src/vaadin-grid-scroll-mixin.js
index 01b01f4ce4..5725f6305e 100644
--- a/packages/grid/src/vaadin-grid-scroll-mixin.js
+++ b/packages/grid/src/vaadin-grid-scroll-mixin.js
@@ -126,7 +126,10 @@ export const ScrollMixin = (superClass) =>
if (this._rowWithFocusedElement) {
// Make sure the row with the focused element is fully inside the visible viewport
- this.__scrollIntoViewport(this._rowWithFocusedElement.index);
+ // Don't change scroll position if the user is interacting with the mouse
+ if (!this._isMousedown) {
+ this.__scrollIntoViewport(this._rowWithFocusedElement.index);
+ }
if (!this.$.table.contains(e.relatedTarget)) {
// Virtualizer can't catch the event because if orginates from the light DOM.
diff --git a/packages/grid/test/scroll-into-view-lit.test.js b/packages/grid/test/scroll-into-view-lit.test.js
new file mode 100644
index 0000000000..3d10732035
--- /dev/null
+++ b/packages/grid/test/scroll-into-view-lit.test.js
@@ -0,0 +1,3 @@
+import '../theme/lumo/lit-all-imports.js';
+import '../src/lit-all-imports.js';
+import './scroll-into-view.common.js';
diff --git a/packages/grid/test/scroll-into-view-polymer.test.js b/packages/grid/test/scroll-into-view-polymer.test.js
new file mode 100644
index 0000000000..7866627fed
--- /dev/null
+++ b/packages/grid/test/scroll-into-view-polymer.test.js
@@ -0,0 +1,2 @@
+import '../vaadin-grid.js';
+import './scroll-into-view.common.js';
diff --git a/packages/grid/test/scroll-into-view.common.js b/packages/grid/test/scroll-into-view.common.js
new file mode 100644
index 0000000000..084d99e7dc
--- /dev/null
+++ b/packages/grid/test/scroll-into-view.common.js
@@ -0,0 +1,90 @@
+import { expect } from '@esm-bundle/chai';
+import { fixtureSync, nextFrame } from '@vaadin/testing-helpers';
+import { sendKeys, sendMouse } from '@web/test-runner-commands';
+import { flushGrid, getContainerCell, getLastVisibleItem, getPhysicalItems } from './helpers.js';
+
+describe('scroll into view', () => {
+ let grid, firstCell, secondCell, secondDetailsCell;
+
+ function verifyRowFullyVisible(grid, rowIndex) {
+ const scrollerBounds = grid.$.scroller.getBoundingClientRect();
+ const row = getPhysicalItems(grid)[rowIndex];
+ const rowBounds = row.getBoundingClientRect();
+ expect(rowBounds.top).to.be.greaterThanOrEqual(scrollerBounds.top);
+ expect(rowBounds.bottom).to.be.lessThanOrEqual(scrollerBounds.bottom);
+ }
+
+ function verifyRowNotFullyVisible(grid, rowIndex) {
+ const scrollerBounds = grid.$.scroller.getBoundingClientRect();
+ const row = getPhysicalItems(grid)[rowIndex];
+ const rowBounds = row.getBoundingClientRect();
+ const isTopInvisible = rowBounds.top < scrollerBounds.top;
+ const isBottomInvisible = rowBounds.bottom > scrollerBounds.bottom;
+ expect(isTopInvisible || isBottomInvisible).to.be.true;
+ }
+
+ beforeEach(async () => {
+ grid = fixtureSync(`
+