Skip to content

Commit e2bb42f

Browse files
authored
refactor!: fire click on space for cells with text content (#10970)
1 parent 9eec7fa commit e2bb42f

File tree

3 files changed

+71
-46
lines changed

3 files changed

+71
-46
lines changed

packages/grid/src/vaadin-grid-active-item-mixin.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ export const ActiveItemMixin = (superClass) =>
7070
return (
7171
// Something has handled this click already, e. g., <vaadin-grid-sorter>
7272
e.defaultPrevented ||
73+
// Skip if click event explicitly requests to skip cell activation
74+
e.skipCellActivate ||
7375
// No clicked cell available
7476
!cell ||
7577
// Cell is a details cell

packages/grid/src/vaadin-grid-keyboard-navigation-mixin.js

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -766,19 +766,31 @@ export const KeyboardNavigationMixin = (superClass) =>
766766

767767
e.preventDefault();
768768

769+
// Fire click event on the first element in the cell, or the cell itself.
770+
// This serves two purposes:
771+
// - Activate any buttons or other clickable elements in the cell
772+
// - Fire a general click event for the cell, which can be used on the
773+
// application level to detect clicks on cells / items in the grid.
774+
// Used by the Flow component to implement the `item-click` event.
775+
//
776+
// When clicking an element in the cell, allow activating the cell since
777+
// that is excluded from the keydown handler above. Active item mixin
778+
// takes care of only activating the item under correct conditions.
779+
//
780+
// When clicking a cell with only text nodes, skip activating the cell
781+
// on click, since that is already handled on keydown.
769782
const cell = e.composedPath()[0];
770-
if (cell._content && cell._content.firstElementChild) {
771-
const wasNavigating = this.hasAttribute('navigating');
772-
cell._content.firstElementChild.dispatchEvent(
773-
new MouseEvent('click', {
774-
shiftKey: e.shiftKey,
775-
bubbles: true,
776-
composed: true,
777-
cancelable: true,
778-
}),
779-
);
780-
this.toggleAttribute('navigating', wasNavigating);
781-
}
783+
const target = (cell._content && cell._content.firstElementChild) || cell;
784+
const wasNavigating = this.hasAttribute('navigating');
785+
const clickEvent = new MouseEvent('click', {
786+
shiftKey: e.shiftKey,
787+
bubbles: true,
788+
composed: true,
789+
cancelable: true,
790+
});
791+
clickEvent.skipCellActivate = target === cell;
792+
target.dispatchEvent(clickEvent);
793+
this.toggleAttribute('navigating', wasNavigating);
782794
}
783795

784796
/**

packages/grid/test/keyboard-navigation.test.js

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ import '../all-imports.js';
2020
import {
2121
attributeRenderer,
2222
flushGrid,
23+
getBodyCell,
2324
getCell,
2425
getCellContent,
2526
getContainerCell,
2627
getFirstVisibleItem,
2728
getFocusedCellIndex,
2829
getFocusedRowIndex,
30+
getHeaderCell,
2931
getLastVisibleItem,
3032
getPhysicalItems,
3133
getRowCells,
@@ -101,6 +103,11 @@ function spaceUp(target) {
101103
keyUpOn(target || grid.shadowRoot.activeElement, 32, [], ' ');
102104
}
103105

106+
function spacePress(target) {
107+
spaceDown(target);
108+
spaceUp(target);
109+
}
110+
104111
function pageUp(target) {
105112
keyDownOn(target || grid.shadowRoot.activeElement, 33, [], 'PageUp');
106113
}
@@ -1399,7 +1406,7 @@ describe('keyboard navigation', () => {
13991406
it('should activate on space keydown', () => {
14001407
tabToBody();
14011408

1402-
spaceDown();
1409+
spacePress();
14031410

14041411
expect(grid.activeItem).to.equal('foo');
14051412
});
@@ -1409,7 +1416,7 @@ describe('keyboard navigation', () => {
14091416
clickItem(0);
14101417

14111418
down();
1412-
spaceDown();
1419+
spacePress();
14131420

14141421
expect(grid.activeItem).to.equal('bar');
14151422
});
@@ -1418,7 +1425,7 @@ describe('keyboard navigation', () => {
14181425
focusItem(0);
14191426
clickItem(0); // Activates first item on click
14201427

1421-
spaceDown();
1428+
spacePress();
14221429

14231430
expect(grid.activeItem).to.be.null;
14241431
});
@@ -1468,7 +1475,7 @@ describe('keyboard navigation', () => {
14681475
it('should not activate on space keydown click on a native input', () => {
14691476
const input = focusFirstBodyInput(0);
14701477
escape(input);
1471-
spaceDown();
1478+
spacePress();
14721479

14731480
expect(grid.activeItem).to.be.null;
14741481
});
@@ -1477,7 +1484,7 @@ describe('keyboard navigation', () => {
14771484
clickItem(0);
14781485

14791486
tabToHeader();
1480-
spaceDown();
1487+
spacePress();
14811488

14821489
expect(grid.activeItem).to.equal('foo');
14831490
});
@@ -1500,32 +1507,41 @@ describe('keyboard navigation', () => {
15001507
});
15011508

15021509
describe('space click shortcut', () => {
1503-
beforeEach(() => {
1504-
header.children[0].children[1].focus();
1505-
right();
1506-
});
1510+
function verifyClickOnSpace(cell) {
1511+
let composedPath;
1512+
const clickSpy = sinon.spy((e) => {
1513+
composedPath = e.composedPath();
1514+
});
1515+
grid.addEventListener('click', clickSpy);
15071516

1508-
it('should dispatch click event on first cell child on space keyup', () => {
1509-
const firstChild = getCellContent(header.children[0].children[2]).children[0];
1510-
const dispatchEventStub = sinon.stub(firstChild, 'dispatchEvent');
1517+
const firstElementChild = getCellContent(cell).firstElementChild;
15111518

1512-
spaceDown();
1513-
expect(dispatchEventStub.called).to.be.false;
1519+
spaceDown(cell);
1520+
expect(clickSpy.called).to.be.false;
15141521

1515-
spaceUp();
1516-
expect(dispatchEventStub.called).to.be.true;
1517-
expect(dispatchEventStub.args[0][0] instanceof MouseEvent).to.be.true;
1518-
expect(dispatchEventStub.args[0][0].type).to.equal('click');
1519-
});
1522+
spaceUp(cell);
1523+
expect(clickSpy.calledOnce).to.be.true;
1524+
1525+
const event = clickSpy.args[0][0];
1526+
const target = composedPath[0];
15201527

1521-
it('should not dispatch click event on other cell children on space keyup', () => {
1522-
const secondChild = getCellContent(header.children[0].children[2]).children[1];
1523-
const dispatchEventStub = sinon.stub(secondChild, 'dispatchEvent');
1528+
expect(event instanceof MouseEvent).to.be.true;
1529+
expect(event.type).to.equal('click');
1530+
expect(target).to.equal(firstElementChild || cell);
1531+
}
15241532

1525-
spaceDown();
1526-
spaceUp();
1533+
it('should dispatch click event on space keyup in header cells', () => {
1534+
// Verify for various cells with either text nodes or element children
1535+
grid.querySelectorAll('vaadin-grid-column').forEach((_, index) => {
1536+
verifyClickOnSpace(getHeaderCell(grid, 0, index));
1537+
});
1538+
});
15271539

1528-
expect(dispatchEventStub.called).to.be.false;
1540+
it('should dispatch click event on space keyup in body cells', () => {
1541+
// Verify for various cells with either text nodes or element children
1542+
grid.querySelectorAll('vaadin-grid-column').forEach((_, index) => {
1543+
verifyClickOnSpace(getBodyCell(grid, 0, index));
1544+
});
15291545
});
15301546

15311547
it('should prevent default keydown action when clicking on space', () => {
@@ -1537,21 +1553,16 @@ describe('keyboard navigation', () => {
15371553
});
15381554

15391555
it('should not activate if synthetic click has default prevented', () => {
1540-
const firstBodyRowFirstChild = getCellContent(getRows(body)[0].children[2]).children[0];
1556+
const bodyCell = getBodyCell(grid, 0, 2);
1557+
const firstChild = getCellContent(bodyCell).children[0];
15411558

15421559
const spy = sinon.spy();
1543-
listenOnce(firstBodyRowFirstChild, 'click', (e) => {
1560+
listenOnce(firstChild, 'click', (e) => {
15441561
spy();
15451562
e.preventDefault();
15461563
});
15471564

1548-
// Navigate to body, row 1, column 3
1549-
tabToBody();
1550-
right();
1551-
right();
1552-
1553-
spaceDown();
1554-
spaceUp();
1565+
spacePress(bodyCell);
15551566

15561567
expect(spy.called).to.be.true;
15571568
expect(grid.activeItem).to.be.null;

0 commit comments

Comments
 (0)