Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,29 +56,29 @@
"@types/node": "^25.5.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@typescript/native-preview": "^7.0.0-dev.20260401.1",
"@typescript/native-preview": "^7.0.0-dev.20260407.1",
"@vitejs/plugin-react": "^6.0.1",
"@vitest/browser-playwright": "^4.1.2",
"@vitest/coverage-istanbul": "^4.1.2",
"@vitest/eslint-plugin": "^1.6.13",
"@vitest/browser-playwright": "^4.1.3",
"@vitest/coverage-istanbul": "^4.1.3",
"@vitest/eslint-plugin": "^1.6.14",
"clsx": "^2.1.1",
"ecij": "^0.4.1",
"eslint": "^10.0.3",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-sonarjs": "^4.0.2",
"jspdf": "^4.2.0",
"jspdf-autotable": "^5.0.7",
"oxfmt": "0.43.0",
"oxfmt": "0.44.0",
"playwright": "~1.59.0",
"postcss": "^8.5.2",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"tsdown": "^0.21.7",
"typescript": "~6.0.2",
"typescript-eslint": "^8.58.0",
"vite": "^8.0.3",
"vitest": "^4.1.2",
"vitest-browser-react": "^2.1.0"
"vite": "^8.0.6",
"vitest": "^4.1.3",
"vitest-browser-react": "^2.2.0"
},
"peerDependencies": {
"react": "^19.2",
Expand Down
8 changes: 4 additions & 4 deletions test/browser/column/cellClass.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ test('cellClass is a string', async () => {
await setup({ columns, rows });
const cell1 = page.getCell({ name: '0' });
const cell2 = page.getCell({ name: '1' });
await expect.element(cell1).toHaveClass(`${cellClassname} my-cell`, { exact: true });
await expect.element(cell2).toHaveClass(`${cellClassname} my-cell`, { exact: true });
await expect.element(cell1).toHaveClass(cellClassname, 'my-cell', { exact: true });
await expect.element(cell2).toHaveClass(cellClassname, 'my-cell', { exact: true });
});

test('cellClass returns a string', async () => {
Expand All @@ -50,8 +50,8 @@ test('cellClass returns a string', async () => {
await setup({ columns, rows });
const cell1 = page.getCell({ name: '0' });
const cell2 = page.getCell({ name: '1' });
await expect.element(cell1).toHaveClass(`${cellClassname} my-cell-0`, { exact: true });
await expect.element(cell2).toHaveClass(`${cellClassname} my-cell-1`, { exact: true });
await expect.element(cell1).toHaveClass(cellClassname, 'my-cell-0', { exact: true });
await expect.element(cell2).toHaveClass(cellClassname, 'my-cell-1', { exact: true });
});

test('cellClass returns undefined', async () => {
Expand Down
8 changes: 2 additions & 6 deletions test/browser/column/frozen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,8 @@ test('frozen column have a specific class, and are stable-sorted before non-froz
await expect.element(cell3).toHaveTextContent('col2');
await expect.element(cell4).toHaveTextContent('col4');

await expect
.element(cell1)
.toHaveClass(`${cellClassname} ${cellFrozenClassname}`, { exact: true });
await expect
.element(cell2)
.toHaveClass(`${cellClassname} ${cellFrozenClassname}`, { exact: true });
await expect.element(cell1).toHaveClass(cellClassname, cellFrozenClassname, { exact: true });
await expect.element(cell2).toHaveClass(cellClassname, cellFrozenClassname, { exact: true });
await expect.element(cell3).toHaveClass(cellClassname, { exact: true });
await expect.element(cell4).toHaveClass(cellClassname, { exact: true });
});
8 changes: 2 additions & 6 deletions test/browser/column/headerCellClass.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ test('headerCellClass is either nullish or a string', async () => {

await setup({ columns, rows: [] });
await expect.element(headerCells.nth(0)).toHaveClass(cellClassname, { exact: true });
await expect
.element(headerCells.nth(1))
.toHaveClass(`${cellClassname} my-header`, { exact: true });
await expect.element(headerCells.nth(1)).toHaveClass(cellClassname, 'my-header', { exact: true });
});

test('columnGroup.headerCellClass is either nullish or a string', async () => {
Expand All @@ -41,7 +39,5 @@ test('columnGroup.headerCellClass is either nullish or a string', async () => {

await setup({ columns, rows: [] });
await expect.element(headerCells.nth(0)).toHaveClass(cellClassname, { exact: true });
await expect
.element(headerCells.nth(1))
.toHaveClass(`${cellClassname} my-header`, { exact: true });
await expect.element(headerCells.nth(1)).toHaveClass(cellClassname, 'my-header', { exact: true });
});
18 changes: 13 additions & 5 deletions test/browser/column/renderEditCell.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useMemo, useState } from 'react';
import { createPortal } from 'react-dom';
import { page, userEvent } from 'vitest/browser';
import { page, server, userEvent } from 'vitest/browser';

import { DataGrid } from '../../../src';
import type { Column, DataGridProps } from '../../../src';
Expand Down Expand Up @@ -96,7 +96,7 @@ describe('Editor', () => {
await userEvent.click(getCellsAtRowIndex(0).nth(0));
const activeRowCells = getRowWithCell(page.getActiveCell()).getCell();
await testCount(activeRowCells, 2);
await scrollGrid({ top: 2001 });
scrollGrid({ top: 2001 });
await testCount(activeRowCells, 1);
await expect.element(col1Editor).not.toBeInTheDocument();
await expect.element(grid).toHaveProperty('scrollTop', 2001);
Expand Down Expand Up @@ -258,11 +258,19 @@ describe('Editor', () => {

await userEvent.dblClick(page.getCell({ name: 'name0' }));
await userEvent.keyboard('abc');

await scrollGrid({ top: 1500 });
if (server.browser === 'firefox') {
// When typing, Firefox scroll to the caret asynchronously,
// but does not to cancel the scroll task when calling `.scroll()` on the grid
// https://github.com/mozilla-firefox/firefox/blob/287c6cf323492ae0cc031e468c1d87f623413f50/dom/html/TextControlElement.cpp#L330
// https://github.com/mozilla-firefox/firefox/blob/287c6cf323492ae0cc031e468c1d87f623413f50/dom/base/Selection.cpp#L3828
await new Promise(requestAnimationFrame);
// Alternatively, configuring playwright's launchOptions.slowMo to 1 works,
// but slows down the tests.
}
scrollGrid({ top: 1500 });
await userEvent.click(page.getCell({ name: 'name43' }));
await expect.element(page.getActiveCell()).toHaveTextContent(/^name43$/);
await scrollGrid({ top: 0 });
scrollGrid({ top: 0 });
await expect.element(page.getCell({ name: 'name0abc' })).toBeVisible();
});

Expand Down
10 changes: 5 additions & 5 deletions test/browser/column/summaryCellClass.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ test('summaryCellClass is a string', async () => {
];
await setup({ columns, topSummaryRows, bottomSummaryRows, rows: [] });
for (const cell of cells.all()) {
await expect.element(cell).toHaveClass(`${cellClassname} my-cell`, { exact: true });
await expect.element(cell).toHaveClass(cellClassname, 'my-cell', { exact: true });
}
});

Expand All @@ -48,10 +48,10 @@ test('summaryCellClass returns a string', async () => {
}
];
await setup({ columns, topSummaryRows, bottomSummaryRows, rows: [] });
await expect.element(cells.nth(0)).toHaveClass(`${cellClassname} my-cell-0`, { exact: true });
await expect.element(cells.nth(1)).toHaveClass(`${cellClassname} my-cell-1`, { exact: true });
await expect.element(cells.nth(2)).toHaveClass(`${cellClassname} my-cell-2`, { exact: true });
await expect.element(cells.nth(3)).toHaveClass(`${cellClassname} my-cell-3`, { exact: true });
await expect.element(cells.nth(0)).toHaveClass(cellClassname, 'my-cell-0', { exact: true });
await expect.element(cells.nth(1)).toHaveClass(cellClassname, 'my-cell-1', { exact: true });
await expect.element(cells.nth(2)).toHaveClass(cellClassname, 'my-cell-2', { exact: true });
await expect.element(cells.nth(3)).toHaveClass(cellClassname, 'my-cell-3', { exact: true });
});

test('summaryCellClass returns undefined', async () => {
Expand Down
4 changes: 1 addition & 3 deletions test/browser/headerRowClass.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,5 @@ test('headerRowClass is a string', async () => {
rows,
headerRowClass: 'my-header-row'
});
await expect
.element(headerRow)
.toHaveClass(`${headerRowClassname} my-header-row`, { exact: true });
await expect.element(headerRow).toHaveClass(headerRowClassname, 'my-header-row', { exact: true });
});
8 changes: 4 additions & 4 deletions test/browser/keyboardNavigation.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {

const activeCell = page.getActiveCell();
const activeSelectAllCheckbox = activeCell.getSelectAllCheckbox();
const activeSelectCheckbox = activeCell.getByRole('checkbox', { name: 'Select', exact: true });
const activeSelectCheckbox = activeCell.getByRole('checkbox', { name: 'Select' });

type Row = undefined;

Expand Down Expand Up @@ -279,21 +279,21 @@ test('navigation when active cell not in the viewport', async () => {
await userEvent.keyboard('{Control>}{end}{/Control}{arrowup}{arrowup}');
await validateCellPosition(99, 100);
await expect.element(activeRowCells).not.toHaveLength(1);
await scrollGrid({ top: 0 });
scrollGrid({ top: 0 });
await testCount(activeRowCells, 1);
await userEvent.keyboard('{arrowup}');
await validateCellPosition(99, 99);
await expect.element(activeRowCells).not.toHaveLength(1);

await scrollGrid({ left: 0 });
scrollGrid({ left: 0 });
await userEvent.keyboard('{arrowdown}');
await validateCellPosition(99, 100);

await userEvent.keyboard(
'{home}{arrowright}{arrowright}{arrowright}{arrowright}{arrowright}{arrowright}{arrowright}'
);
await validateCellPosition(7, 100);
await scrollGrid({ left: 2000 });
scrollGrid({ left: 2000 });
await userEvent.keyboard('{arrowleft}');
await validateCellPosition(6, 100);
});
Expand Down
18 changes: 9 additions & 9 deletions test/browser/rowClass.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ test('rowClass is undefined', async () => {
rows: initialRows,
rowClass: undefined
});
await expect.element(rows.nth(0)).toHaveClass(`${rowClassname} rdg-row-even`, { exact: true });
await expect.element(rows.nth(1)).toHaveClass(`${rowClassname} rdg-row-odd`, { exact: true });
await expect.element(rows.nth(2)).toHaveClass(`${rowClassname} rdg-row-even`, { exact: true });
await expect.element(rows.nth(0)).toHaveClass(rowClassname, 'rdg-row-even', { exact: true });
await expect.element(rows.nth(1)).toHaveClass(rowClassname, 'rdg-row-odd', { exact: true });
await expect.element(rows.nth(2)).toHaveClass(rowClassname, 'rdg-row-even', { exact: true });
});

test('rowClass returns a string', async () => {
Expand All @@ -32,13 +32,13 @@ test('rowClass returns a string', async () => {
});
await expect
.element(rows.nth(0))
.toHaveClass(`${rowClassname} rdg-row-even my-row-0`, { exact: true });
.toHaveClass(rowClassname, 'rdg-row-even my-row-0', { exact: true });
await expect
.element(rows.nth(1))
.toHaveClass(`${rowClassname} rdg-row-odd my-row-1`, { exact: true });
.toHaveClass(rowClassname, 'rdg-row-odd my-row-1', { exact: true });
await expect
.element(rows.nth(2))
.toHaveClass(`${rowClassname} rdg-row-even my-row-2`, { exact: true });
.toHaveClass(rowClassname, 'rdg-row-even my-row-2', { exact: true });
});

test('rowClass returns undefined', async () => {
Expand All @@ -47,7 +47,7 @@ test('rowClass returns undefined', async () => {
rows: initialRows,
rowClass: () => undefined
});
await expect.element(rows.nth(0)).toHaveClass(`${rowClassname} rdg-row-even`, { exact: true });
await expect.element(rows.nth(1)).toHaveClass(`${rowClassname} rdg-row-odd`, { exact: true });
await expect.element(rows.nth(2)).toHaveClass(`${rowClassname} rdg-row-even`, { exact: true });
await expect.element(rows.nth(0)).toHaveClass(rowClassname, 'rdg-row-even', { exact: true });
await expect.element(rows.nth(1)).toHaveClass(rowClassname, 'rdg-row-odd', { exact: true });
await expect.element(rows.nth(2)).toHaveClass(rowClassname, 'rdg-row-even', { exact: true });
});
12 changes: 3 additions & 9 deletions test/browser/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,9 @@ export async function validateCellPosition(columnIdx: number, rowIdx: number) {
await expect.element(row).toHaveAttribute('aria-rowindex', `${rowIdx + 1}`);
}

export async function scrollGrid(options: ScrollToOptions) {
await new Promise((resolve) => {
// wait for browser state to stablize before scrolling, to avoid flaky scroll-related tests
requestAnimationFrame(() => {
const gridElement = page.getGrid().element();
gridElement.addEventListener('scrollend', resolve, { once: true });
gridElement.scroll(options);
});
});
export function scrollGrid(options: ScrollToOptions) {
const grid = page.getGrid().element();
grid.scroll(options);
}

export function testCount(locator: Locator, expectedCount: number) {
Expand Down
36 changes: 18 additions & 18 deletions test/browser/virtualization.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,50 +102,50 @@ test('virtualization is enabled', async () => {
await assertHeaderCells(18, 0, 17);
await assertRows(34, 0, 33);
await assertCells(0, 18, 0, 17);
await scrollGrid({ top: 244 });
scrollGrid({ top: 244 });
await assertRows(39, 2, 40);

await scrollGrid({ top: 245 });
scrollGrid({ top: 245 });
await assertRows(38, 3, 40);

await scrollGrid({ top: 419 });
scrollGrid({ top: 419 });
await assertRows(39, 7, 45);

await scrollGrid({ top: 420 });
scrollGrid({ top: 420 });
await assertRows(38, 8, 45);

await scrollGrid({ top: 524 });
scrollGrid({ top: 524 });
await assertRows(39, 10, 48);

await scrollGrid({ top: 525 });
scrollGrid({ top: 525 });
await assertRows(38, 11, 48);

await scrollGrid({ top: 1000 });
scrollGrid({ top: 1000 });
await assertRows(39, 24, 62);

// scroll height = header height + row height * row count
// max top = scroll height - grid height
await scrollGrid({ top: rowHeight + rowHeight * 100 - 1080 });
scrollGrid({ top: rowHeight + rowHeight * 100 - 1080 });
await assertRows(34, 66, 99);

await scrollGrid({ left: 92 });
scrollGrid({ left: 92 });
await assertHeaderCells(18, 0, 17);
await assertCells(66, 18, 0, 17);

await scrollGrid({ left: 93 });
scrollGrid({ left: 93 });
await assertHeaderCells(19, 0, 18);
await assertCells(66, 19, 0, 18);

await scrollGrid({ left: 209 });
scrollGrid({ left: 209 });
await assertHeaderCells(19, 0, 18);
await assertCells(66, 19, 0, 18);

await scrollGrid({ left: 210 });
scrollGrid({ left: 210 });
await assertHeaderCells(18, 1, 18);
await assertCells(66, 18, 1, 18);

// max left = row width - grid width
await scrollGrid({ left: 3600 - 1920 });
scrollGrid({ left: 3600 - 1920 });
await assertHeaderCells(17, 13, 29);
await assertCells(66, 17, 13, 29);
});
Expand All @@ -157,13 +157,13 @@ test('virtualization is enabled with 4 frozen columns', async () => {
await assertHeaderCellIndexes(indexes);
await assertCellIndexes(0, indexes);

await scrollGrid({ left: 1000 });
scrollGrid({ left: 1000 });
indexes = [0, 1, 2, 3, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25];
await assertHeaderCellIndexes(indexes);
await assertCellIndexes(0, indexes);

// max left = row width - grid width
await scrollGrid({ left: 3600 - 1920 });
scrollGrid({ left: 3600 - 1920 });
indexes = [0, 1, 2, 3, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29];
await assertHeaderCellIndexes(indexes);
await assertCellIndexes(0, indexes);
Expand All @@ -179,12 +179,12 @@ test('virtualization is enabled with all columns frozen', async () => {
await assertHeaderCellIndexes(indexes);
await assertCellIndexes(0, indexes);

await scrollGrid({ left: 1000 });
scrollGrid({ left: 1000 });
await assertHeaderCellIndexes(indexes);
await assertCellIndexes(0, indexes);

// max left = row width - grid width
await scrollGrid({ left: 3600 - 1920 });
scrollGrid({ left: 3600 - 1920 });
await assertHeaderCellIndexes(indexes);
await assertCellIndexes(0, indexes);
});
Expand All @@ -197,7 +197,7 @@ test('virtualization is enabled with 2 summary rows', async () => {
26, 27, 28, 29, 30, 31, 102, 103
]);

await scrollGrid({ top: 1000 });
scrollGrid({ top: 1000 });
await assertRowIndexes([
0, 1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 102, 103
Expand Down
Loading
Loading