diff --git a/src/vaadin-grid.html b/src/vaadin-grid.html
index 96d76efd6..bfbac1e0e 100644
--- a/src/vaadin-grid.html
+++ b/src/vaadin-grid.html
@@ -342,6 +342,12 @@
static get properties() {
return {
+ /** @private */
+ _chrome: {
+ type: Boolean,
+ value: /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor)
+ },
+
/** @private */
_safari: {
type: Boolean,
@@ -607,13 +613,16 @@
// focusable slot wrapper, that is why cells are not focused with
// mousedown. Workaround: listen for mousedown and focus manually.
cellContent.addEventListener('mousedown', () => {
- if (window.chrome) {
+ if (this._chrome) {
// Chrome bug: focusing before mouseup prevents text selection, see http://crbug.com/771903
- const mouseUpListener = () => {
- if (!cellContent.contains(this.getRootNode().activeElement)) {
+ const mouseUpListener = (event) => {
+ // If focus is on element within the cell content — respect it, do not change
+ const contentContainsFocusedElement = cellContent.contains(this.getRootNode().activeElement);
+ // Only focus if mouse is released on cell content itself
+ const mouseUpWithinCell = event.composedPath().indexOf(cellContent) >= 0;
+ if (!contentContainsFocusedElement && mouseUpWithinCell) {
cell.focus();
}
- // If focus is in the cell content — respect it, do not change.
document.removeEventListener('mouseup', mouseUpListener, true);
};
document.addEventListener('mouseup', mouseUpListener, true);
diff --git a/test/keyboard-navigation.html b/test/keyboard-navigation.html
index 383697a02..230665cc1 100644
--- a/test/keyboard-navigation.html
+++ b/test/keyboard-navigation.html
@@ -230,6 +230,16 @@
MockInteractions.keyDownOn(target || grid.shadowRoot.activeElement, 113, [], 'F2');
}
+ function mouseDown(target) {
+ const event = new CustomEvent('mousedown', {bubbles: true, cancelable: true, composed: true});
+ target.dispatchEvent(event);
+ }
+
+ function mouseUp(target) {
+ const event = new CustomEvent('mouseup', {bubbles: true, cancelable: true, composed: true});
+ target.dispatchEvent(event);
+ }
+
function getFirstHeaderCell() {
return grid.$.header.children[0].children[0];
}
@@ -1914,6 +1924,63 @@
expect(spy.calledOnce).to.be.true;
});
+
+ // Separate test suite for Chrome, where we use a workaround to dispatch
+ // cell-focus on mouse up
+ const isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
+ (isChrome ? describe : describe.skip)('chrome', () => {
+ it('should dispatch cell-focus on mouse up on cell content', () => {
+ const spy = sinon.spy();
+ grid.addEventListener('cell-focus', spy);
+
+ // Mouse down and release on cell content element
+ const cell = getRowFirstCell(0);
+ mouseDown(cell._content);
+ mouseUp(cell._content);
+ expect(spy.calledOnce).to.be.true;
+ });
+
+ it('should dispatch cell-focus on mouse up on cell content, when grid is in shadow DOM', () => {
+ const spy = sinon.spy();
+ grid.addEventListener('cell-focus', spy);
+
+ // Move grid into a shadow DOM
+ const container = document.createElement('div');
+ document.body.appendChild(container);
+ container.attachShadow({mode: 'open'});
+ container.shadowRoot.appendChild(grid);
+
+ // Mouse down and release on cell content element
+ const cell = getRowFirstCell(0);
+ mouseDown(cell._content);
+ mouseUp(cell._content);
+ expect(spy.calledOnce).to.be.true;
+ });
+
+ it('should dispatch cell-focus on mouse up within cell content', () => {
+ const spy = sinon.spy();
+ grid.addEventListener('cell-focus', spy);
+
+ // Mouse down and release on cell content child
+ const cell = getRowFirstCell(0);
+ const contentSpan = document.createElement('span');
+ cell._content.appendChild(contentSpan);
+
+ mouseDown(contentSpan);
+ mouseUp(contentSpan);
+ expect(spy.calledOnce).to.be.true;
+ });
+
+ // Regression test for https://github.com/vaadin/flow-components/issues/2863
+ it('should not dispatch cell-focus on mouse up outside of cell', () => {
+ const spy = sinon.spy();
+ grid.addEventListener('cell-focus', spy);
+
+ mouseDown(getRowFirstCell(0)._content);
+ mouseUp(document.body);
+ expect(spy.calledOnce).to.be.false;
+ });
+ });
});
});