Skip to content

Commit 8ca950b

Browse files
fix: make conditional editing work with lazy columns (#11405) (#11410)
* fix: make conditional editing work with lazy columns * fix tests * handle cell editor navigation when using lazy column rendering * make getEventContext work with lazily rendered cells * stop editing when edited cell is scrolled out of view Co-authored-by: Sascha Ißbrücker <sissbruecker@vaadin.com>
1 parent dab1861 commit 8ca950b

4 files changed

Lines changed: 139 additions & 7 deletions

File tree

packages/grid-pro/src/vaadin-grid-pro-inline-editing-mixin.js

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,19 @@ export const InlineEditingMixin = (superClass) =>
325325
this._debouncerStopEdit = Debouncer.debounce(this._debouncerStopEdit, animationFrame, this._stopEdit.bind(this));
326326
}
327327

328+
/**
329+
* Override method from ScrollMixin to stop editing if the edited cell
330+
* is scrolled out of the view and removed from the DOM.
331+
* @private
332+
*/
333+
__updateColumnsBodyContentHidden() {
334+
super.__updateColumnsBodyContentHidden();
335+
336+
if (this.__edited && !this.__edited.cell.isConnected) {
337+
this._stopEdit(true, false);
338+
}
339+
}
340+
328341
/** @private */
329342
__shouldIgnoreFocusOut(event) {
330343
const edited = this.__edited;
@@ -357,9 +370,11 @@ export const InlineEditingMixin = (superClass) =>
357370
// Cancel debouncer enqueued on focusout
358371
this._cancelStopEdit();
359372

360-
this._scrollHorizontallyToCell(cell);
373+
// Scroll column into view synchronously, which also triggers lazy column
374+
// rendering to ensure cells for that column are in the DOM.
375+
this.scrollToColumn(column);
361376

362-
const model = this.__getRowModel(cell.parentElement);
377+
const model = this.__getRowModel(cell.__parentRow);
363378
this.__edited = { cell, column, model };
364379
column._startCellEdit(cell, model);
365380

@@ -505,7 +520,7 @@ export const InlineEditingMixin = (superClass) =>
505520
// Stop looking if the next cell is editable
506521
const nextRow = this._getRowByIndex(nextIndex);
507522
// eslint-disable-next-line @typescript-eslint/no-loop-func
508-
nextCell = nextRow && Array.from(nextRow.children).find((cell) => cell._column === nextColumn);
523+
nextCell = nextRow && Array.from(nextRow.__cells).find((cell) => cell._column === nextColumn);
509524
if (nextCell && this._isCellEditable(nextCell)) {
510525
break;
511526
}
@@ -537,7 +552,7 @@ export const InlineEditingMixin = (superClass) =>
537552
if (!this._isCellEditable(cell)) {
538553
// Cell is no longer editable, cancel edit
539554
this._stopEdit(true, true);
540-
} else if (cell.parentNode === row && item && this.getItemId(model.item) !== this.getItemId(item)) {
555+
} else if (cell.__parentRow === row && item && this.getItemId(model.item) !== this.getItemId(item)) {
541556
// Edited item identity has changed, stop edit
542557
this._stopEdit();
543558
}
@@ -573,7 +588,7 @@ export const InlineEditingMixin = (superClass) =>
573588
return true;
574589
}
575590
// Otherwise, check isCellEditable function
576-
const model = this.__getRowModel(cell.parentElement);
591+
const model = this.__getRowModel(cell.__parentRow);
577592
return column.isCellEditable(model);
578593
}
579594

packages/grid-pro/test/edit-column.test.js

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
import { expect } from '@vaadin/chai-plugins';
22
import { sendKeys } from '@vaadin/test-runner-commands';
3-
import { enter, esc, fixtureSync, focusin, focusout, nextFrame } from '@vaadin/testing-helpers';
3+
import {
4+
aTimeout,
5+
enter,
6+
esc,
7+
fixtureSync,
8+
focusin,
9+
focusout,
10+
isFirefox,
11+
nextFrame,
12+
oneEvent,
13+
} from '@vaadin/testing-helpers';
414
import sinon from 'sinon';
515
import './not-animated-styles.js';
616
import '../src/vaadin-grid-pro.js';
@@ -10,6 +20,7 @@ import {
1020
dblclick,
1121
flatMap,
1222
flushGrid,
23+
getCellContent,
1324
getCellEditor,
1425
getContainerCell,
1526
getRowCells,
@@ -570,6 +581,111 @@ describe('edit column', () => {
570581
});
571582
});
572583
});
584+
585+
describe('lazy column rendering', () => {
586+
const UPDATE_CONTENT_VISIBILITY_DEBOUNCER_TIMEOUT = 100;
587+
588+
async function scrollHorizontally(delta) {
589+
grid.$.table.scrollLeft += delta;
590+
await oneEvent(grid.$.table, 'scroll');
591+
await aTimeout(UPDATE_CONTENT_VISIBILITY_DEBOUNCER_TIMEOUT);
592+
}
593+
594+
function getCellByColumnPath(columnPath) {
595+
const row = getRows(grid.$.items)[0];
596+
const cells = getRowCells(row);
597+
const cell = cells.find((c) => c._column.path === columnPath);
598+
expect(cell, `Could not find cell for column with path ${columnPath}`).to.be.ok;
599+
600+
return cell;
601+
}
602+
603+
beforeEach(() => {
604+
grid = fixtureSync(`<vaadin-grid-pro style="width: 400px;"></vaadin-grid-pro>`);
605+
606+
const columns = [];
607+
for (let i = 0; i < 8; i++) {
608+
const column = document.createElement('vaadin-grid-pro-edit-column');
609+
column.path = `col${i}`;
610+
column.header = `Col ${i}`;
611+
column.width = '100px';
612+
column.flexGrow = 0;
613+
columns.push(column);
614+
grid.appendChild(column);
615+
}
616+
617+
grid.items = [
618+
{ col0: 'a0', col1: 'a1', col2: 'a2', col3: 'a3', col4: 'a4', col5: 'a5', col6: 'a6', col7: 'a7' },
619+
{ col0: 'b0', col1: 'b1', col2: 'b2', col3: 'b3', col4: 'b4', col5: 'b5', col6: 'b6', col7: 'b7' },
620+
];
621+
622+
// Make odd-indexed columns conditionally editable
623+
columns.forEach((column, i) => {
624+
column.isCellEditable = () => i % 2 === 1;
625+
});
626+
627+
grid.columnRendering = 'lazy';
628+
flushGrid(grid);
629+
});
630+
631+
it('should mark initially visible cells as non-editable based on isCellEditable', () => {
632+
expect(hasEditablePart(0, 0)).to.be.false;
633+
expect(hasEditablePart(0, 1)).to.be.true;
634+
});
635+
636+
it('should mark lazily rendered cells as non-editable based on isCellEditable after scrolling', async () => {
637+
// Scroll far enough to reveal the last columns
638+
await scrollHorizontally(400);
639+
640+
const cells = getRowCells(getRows(grid.$.items)[0]);
641+
642+
// Verify we are testing correct cells
643+
expect(getCellContent(cells[cells.length - 2]).textContent).to.equal('a6');
644+
expect(getCellContent(cells[cells.length - 1]).textContent).to.equal('a7');
645+
646+
expect(hasEditablePart(0, cells.length - 2)).to.be.false;
647+
expect(hasEditablePart(0, cells.length - 1)).to.be.true;
648+
});
649+
650+
// Focus button mode that is active on MacOS causes issues with Tab key navigation in Firefox when run with Playwright
651+
(isFirefox && isMac ? it.skip : it)('should navigate through editable cells with Tab', async () => {
652+
let cell = getCellByColumnPath('col1');
653+
cell.focus();
654+
await sendKeys({ press: 'Enter' });
655+
expect(getCellEditor(cell)).to.be.ok;
656+
657+
await sendKeys({ press: 'Tab' });
658+
cell = getCellByColumnPath('col3');
659+
expect(getCellEditor(cell)).to.be.ok;
660+
661+
await sendKeys({ press: 'Tab' });
662+
cell = getCellByColumnPath('col5');
663+
expect(getCellEditor(cell)).to.be.ok;
664+
665+
await sendKeys({ press: 'Tab' });
666+
cell = getCellByColumnPath('col7');
667+
expect(getCellEditor(cell)).to.be.ok;
668+
});
669+
670+
it('should edit cell again after it was temporarily removed due to scrolling', async () => {
671+
const cell = getCellByColumnPath('col1');
672+
cell.focus();
673+
await sendKeys({ press: 'Enter' });
674+
expect(getCellEditor(cell)).to.be.ok;
675+
676+
await scrollHorizontally(500);
677+
678+
expect(cell.isConnected).to.be.false;
679+
680+
await scrollHorizontally(-500);
681+
682+
expect(getCellEditor(cell)).to.not.be.ok;
683+
684+
cell.focus();
685+
await sendKeys({ press: 'Enter' });
686+
expect(getCellEditor(cell)).to.be.ok;
687+
});
688+
});
573689
});
574690

575691
describe('vertical scrolling', () => {

packages/grid/src/vaadin-grid-event-context-mixin.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export const EventContextMixin = (superClass) =>
4949
}
5050

5151
if (context.section === 'body' || context.section === 'details') {
52-
Object.assign(context, this.__getRowModel(cell.parentElement));
52+
Object.assign(context, this.__getRowModel(cell.__parentRow));
5353
}
5454

5555
return context;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,7 @@ export const GridMixin = (superClass) =>
536536
contentsFragment.appendChild(detailsCell._content);
537537
}
538538
this._configureDetailsCell(detailsCell);
539+
detailsCell.__parentRow = row;
539540
row.appendChild(detailsCell);
540541
// Cache the details cell reference
541542
row.__detailsCell = detailsCell;

0 commit comments

Comments
 (0)