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
4 changes: 4 additions & 0 deletions dev/ag-grid-angular/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ if (isDevMode()) {
{
path: 'infinite-selection',
loadComponent: async () => import('./tests/infinite-selection.ng').then((m) => m.DevInfiniteSelection)
},
{
path: 'row-group',
loadComponent: async () => import('./tests/row-group').then((m) => m.DevRowGroup)
}
]
};
Expand Down
2 changes: 0 additions & 2 deletions dev/ag-grid-angular/src/overview.ng.ts
Original file line number Diff line number Diff line change
Expand Up @@ -620,8 +620,6 @@ export class DevOverview {
const { api } = event;

this.gridApi = api;

this.gridApi.setColumnWidths([{ key: 'ag-Grid-SelectionColumn', newWidth: 36 }]);
}

onFirstDataRendered(event: FirstDataRenderedEvent): void {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions dev/ag-grid-angular/src/tests/column-state.playwright-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,16 @@ test.describe('KbqAgGridColumnState', () => {
.locator('.ag-header-cell[col-id="athlete"]')
.evaluate((el: Element) => Math.round(el.getBoundingClientRect().width));

// The store persists to the URL asynchronously (router navigation) and isn't awaited
// by the resize handler — wait for it to land before reloading, or the reload can race
// ahead of the navigation and pick up the pre-resize width.
await expect
.poll(async () => {
const state = await getColumnStateFromUrl(page);
return state?.find((s) => s.colId === 'athlete')?.width;
})
.toBe(widthBeforeReload);

await page.reload();

await expect
Expand Down
726 changes: 726 additions & 0 deletions dev/ag-grid-angular/src/tests/row-group.playwright-spec.ts

Large diffs are not rendered by default.

192 changes: 192 additions & 0 deletions dev/ag-grid-angular/src/tests/row-group.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import { ChangeDetectionStrategy, Component, computed, input, model, signal, viewChild } from '@angular/core';
import { FormsModule } from '@angular/forms';
import {
KbqAgGridRowGroup,
KbqAgGridRowGroupCellContent,
kbqAgGridRowGroupColOptionsProvider,
KbqAgGridRowGroupInfo,
KbqAgGridRowGroupRowId,
KbqAgGridThemeModule
} from '@koobiq/ag-grid-angular-theme';
import { AgGridModule } from 'ag-grid-angular';
import { AllCommunityModule, ColDef, ModuleRegistry, SelectionChangedEvent } from 'ag-grid-community';
import { devInjectRowData } from '../row-data';

ModuleRegistry.registerModules([AllCommunityModule]);

const COLUMN_DEFS: ColDef[] = [
{ field: 'athlete', headerName: 'Athlete' },
{ field: 'country', headerName: 'Country' },
{ field: 'sport', headerName: 'Sport' },
{ field: 'year', headerName: 'Year' },
{ field: 'gold', headerName: 'Gold' },
{ field: 'silver', headerName: 'Silver' },
{ field: 'bronze', headerName: 'Bronze' },
{ field: 'total', headerName: 'Total' }
];

const DEFAULT_COL_DEF: ColDef = {
resizable: true,
minWidth: 80
};

// Custom `kbqAgGridRowGroupCellContent` demo — replaces the default key markup with a
// differently-styled label; the toggle button/icon/click handling stays whatever the directive renders.
@Component({
standalone: true,
selector: 'dev-row-group-custom-cell-content',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<strong>{{ group().key }}</strong>
<span class="dev-row-group-custom-cell-content__count">{{ group().count }}</span>
`,
styles: `
:host {
display: flex;
gap: var(--kbq-size-xxs);
}

.dev-row-group-custom-cell-content__count {
color: var(--kbq-foreground-contrast-secondary);
}
`
})
class DevRowGroupCustomCellContent implements KbqAgGridRowGroupCellContent {
readonly group = input.required<KbqAgGridRowGroupInfo>();
}

@Component({
standalone: true,
imports: [AgGridModule, KbqAgGridThemeModule, FormsModule],
selector: 'dev-row-group',
template: `
<div>
Group by:
@for (col of columnDefs; track col.field) {
<label>
<input
type="checkbox"
[checked]="groupCols().includes(col.field!)"
(change)="onToggle(col.field!, $event)"
/>
@let index = groupCols().indexOf(col.field!);
{{ col.headerName }} {{ index >= 0 ? index : '' }}
</label>
}
</div>

<div>
Selected rows:
<span data-testid="selectedCount">{{ selectedRows().length }}</span>
</div>

<div>
<button type="button" data-testid="expandAllBtn" (click)="expandAll()">Expand all</button>
<button type="button" data-testid="collapseAllBtn" (click)="collapseAll()">Collapse all</button>
<button type="button" data-testid="expandRussiaDivingBtn" (click)="expandRussiaDiving()">
Expand Russia &gt; Diving
</button>
<button type="button" data-testid="collapseRussiaDivingBtn" (click)="collapseRussiaDiving()">
Collapse Russia &gt; Diving
</button>
<button type="button" data-testid="selectIlyaZakharovBtn" (click)="setIlyaZakharovSelected(true)">
Select Ilya Zakharov
</button>
<button type="button" data-testid="deselectIlyaZakharovBtn" (click)="setIlyaZakharovSelected(false)">
Deselect Ilya Zakharov
</button>
<button type="button" data-testid="sortGroupColAscBtn" (click)="sortGroupColAsc()">
Sort group column asc
</button>
<button type="button" data-testid="clearGroupColSortBtn" (click)="clearGroupColSort()">
Clear group column sort
</button>
<label>
<input type="checkbox" data-testid="useCustomCellContentCheckbox" [(ngModel)]="useCustomCellContent" />
Use custom group cell content
</label>
</div>

<ag-grid-angular
data-testid="e2eScreenshotTarget"
kbqAgGridTheme
kbqAgGridThemeDisableCellFocusStyles
kbqAgGridRowGroup
[kbqAgGridRowGroupRowData]="rowData()"
[kbqAgGridRowGroupRowId]="rowId"
[columnDefs]="columnDefs"
[(kbqAgGridRowGroupCols)]="groupCols"
[kbqAgGridRowGroupCellContent]="cellContent()"
[defaultColDef]="defaultColDef"
[rowSelection]="rowSelection"
[animateRows]="false"
(selectionChanged)="onSelectionChanged($event)"
(kbqAgGridRowGroupSelectionChanged)="onRowSelectionChanged($event)"
/>
`,
providers: [kbqAgGridRowGroupColOptionsProvider({ headerName: 'Group' })],
styles: `
ag-grid-angular {
height: 500px;
}
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DevRowGroup {
readonly rowData = devInjectRowData();
readonly columnDefs = COLUMN_DEFS;
readonly defaultColDef = DEFAULT_COL_DEF;
protected readonly useCustomCellContent = model(false);
protected readonly cellContent = computed(() =>
this.useCustomCellContent() ? DevRowGroupCustomCellContent : undefined
);
protected readonly rowId: KbqAgGridRowGroupRowId = (row) => String(row.id);
protected readonly groupCols = signal<string[]>(['country', 'sport']);
protected readonly rowSelection = { mode: 'multiRow', checkboxes: true } as const;
protected readonly selectedRows = signal<Record<string, unknown>[]>([]);
private readonly group = viewChild.required(KbqAgGridRowGroup);

protected expandAll(): void {
this.group().expandAll();
}

protected collapseAll(): void {
this.group().collapseAll();
}

protected expandRussiaDiving(): void {
this.group().setExpanded(['Russia', 'Diving'], true);
}

protected collapseRussiaDiving(): void {
this.group().setExpanded(['Russia', 'Diving'], false);
}

protected setIlyaZakharovSelected(selected: boolean): void {
const row = this.rowData().find((r) => r.athlete === 'Ilya Zakharov');
if (row) this.group().setRowSelected(row.id, selected);
}

protected sortGroupColAsc(): void {
this.group().setGroupColSort('asc');
}

protected clearGroupColSort(): void {
this.group().setGroupColSort(null);
}

protected onToggle(field: string, event: Event): void {
const { target } = event;
if (!(target instanceof HTMLInputElement)) return;
const { checked } = target;
this.groupCols.update((cols) => (checked ? [...cols, field] : cols.filter((f) => f !== field)));
}

protected onSelectionChanged(event: SelectionChangedEvent): void {
console.debug('SelectionChangedEvent: ', event);
}

protected onRowSelectionChanged(rows: Record<string, unknown>[]): void {
this.selectedRows.set(rows);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@ test.describe('DevSkeletonCellRenderer', () => {
test('data rows appear after block loads', async ({ page }) => {
await page.goto('/e2e/skeleton-cell-renderer');
await expect(getDataRows(page).first()).toBeVisible();
// Global check fails — next unloaded block already renders skeleton cells.
// Scope to loaded rows only: skeleton cells inside rows with row-index must be gone.
await expect(page.locator('.ag-row[row-index] .kbq-ag-grid-skeleton-cell')).toHaveCount(0, { timeout: 5_000 });
// The grid's viewport + row buffer renders more rows than a single cache block (9 rows),
// so later blocks' stub rows also carry `row-index` before their data arrives.
// Scope the check to block 0's rows (row-index 0-8), which are guaranteed loaded by now.
const firstBlockSkeletons = Array.from(
{ length: 9 },
(_, rowIndex) => `.ag-row[row-index="${rowIndex}"] .kbq-ag-grid-skeleton-cell`
).join(', ');
await expect(page.locator(firstBlockSkeletons)).toHaveCount(0, { timeout: 5_000 });
});

test('subsequent blocks show skeletons while scrolling', async ({ page }) => {
Expand Down
7 changes: 1 addition & 6 deletions dev/ag-grid-angular/src/tests/theme.ng.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { KbqAgGridThemeModule } from '@koobiq/ag-grid-angular-theme';
import { AgGridModule } from 'ag-grid-angular';
import { AllCommunityModule, ColDef, GridReadyEvent, ModuleRegistry, RowSelectionOptions } from 'ag-grid-community';
import { AllCommunityModule, ColDef, ModuleRegistry, RowSelectionOptions } from 'ag-grid-community';
import { devInjectRowData } from '../row-data';

ModuleRegistry.registerModules([AllCommunityModule]);
Expand Down Expand Up @@ -35,7 +35,6 @@ const DEFAULT_COL_DEF: ColDef = {
[rowSelection]="rowSelection"
[defaultColDef]="defaultColDef"
[pagination]="false"
(gridReady)="onGridReady($event)"
/>
`,
styles: `
Expand Down Expand Up @@ -69,8 +68,4 @@ export class DevTheme {
{ field: 'bronze', headerName: 'Bronze' },
{ field: 'total', headerName: 'Total' }
];

onGridReady({ api }: GridReadyEvent): void {
api.setColumnWidths([{ key: 'ag-Grid-SelectionColumn', newWidth: 36 }]);
}
}
1 change: 1 addition & 0 deletions packages/ag-grid-angular-theme/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export * from './src/loading-overlay.ng';
export * from './src/module.ng';
export * from './src/quick-filter-state.ng';
export * from './src/row-actions.ng';
export * from './src/row-group.ng';
export * from './src/select-all-rows-by-ctrl-a.ng';
export * from './src/select-rows-by-ctrl-click.ng';
export * from './src/select-rows-by-shift-arrow.ng';
Expand Down
4 changes: 3 additions & 1 deletion packages/ag-grid-angular-theme/src/module.ng.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { KbqAgGridInfiniteSelection } from './infinite-selection.ng';
import { KbqAgGridLoadingOverlay } from './loading-overlay.ng';
import { KbqAgGridQuickFilterState } from './quick-filter-state.ng';
import { KbqAgGridRowActions } from './row-actions.ng';
import { KbqAgGridRowGroup } from './row-group.ng';
import { KbqAgGridSelectAllRowsByCtrlA } from './select-all-rows-by-ctrl-a.ng';
import { KbqAgGridSelectRowsByCtrlClick } from './select-rows-by-ctrl-click.ng';
import { KbqAgGridSelectRowsByShiftArrow } from './select-rows-by-shift-arrow.ng';
Expand All @@ -34,7 +35,8 @@ const COMPONENTS = [
KbqAgGridFilterState,
KbqAgGridQuickFilterState,
KbqAgGridExternalFilterState,
KbqAgGridLoadingOverlay
KbqAgGridLoadingOverlay,
KbqAgGridRowGroup
];

@NgModule({
Expand Down
Loading