@skyux/ag-grid 15.2.0 → 15.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  import { UnitTestElement } from '@angular/cdk/testing/testbed';
2
2
  import { SkyComponentHarness } from '@skyux/core/testing';
3
+ import { SkyInlineDeleteHarness } from '@skyux/layout/testing';
3
4
  import { getGridApi } from 'ag-grid-community';
4
5
  import { SkyAgGridService } from '@skyux/ag-grid';
5
6
  import * as i0 from '@angular/core';
@@ -61,6 +62,7 @@ class SkyAgGridWrapperHarness extends SkyComponentHarness {
61
62
  * @internal
62
63
  */
63
64
  static { this.hostSelector = 'sky-ag-grid-wrapper'; }
65
+ #getGrid = this.locatorFor('div.sky-ag-grid');
64
66
  /**
65
67
  * Gets a `HarnessPredicate` that can be used to search for a
66
68
  * `SkyAgGridWrapperHarness` that meets certain criteria
@@ -95,6 +97,23 @@ class SkyAgGridWrapperHarness extends SkyComponentHarness {
95
97
  .map((col) => col.getColDef().headerName || ''))
96
98
  .catch(() => Promise.reject('Unable to retrieve displayed column header names.'));
97
99
  }
100
+ /**
101
+ * Gets the inline delete confirmation shown for a row by the
102
+ * `skyAgGridRowDelete` directive, or `null` if the row is not showing one.
103
+ * @param rowId The ID of the row.
104
+ */
105
+ async getRowInlineDelete(rowId) {
106
+ await this.waitUntilRendered();
107
+ // The row delete overlay updates after the grid renders, outside the
108
+ // Angular zone, so let its change detection run before querying it.
109
+ await new Promise((resolve) => setTimeout(resolve));
110
+ await this.forceStabilize();
111
+ // The overlay renders outside the grid, attached to the document body, and
112
+ // is tagged with the grid's ID so rows from other grids are not matched.
113
+ const gridId = await (await this.#getGrid()).getAttribute('id');
114
+ const rowLoader = await this.documentRootLocatorFactory().harnessLoaderForOptional(`[data-sky-ag-grid-id="${gridId}"] [id="row-delete-ref-${rowId}"]`);
115
+ return (await rowLoader?.getHarnessOrNull(SkyInlineDeleteHarness)) ?? null;
116
+ }
98
117
  /**
99
118
  * @internal
100
119
  */
@@ -1 +1 @@
1
- {"version":3,"file":"skyux-ag-grid-testing.mjs","sources":["../../../../../libs/components/ag-grid/testing/src/modules/ag-grid/ag-grid-render-tracking.ts","../../../../../libs/components/ag-grid/testing/src/modules/ag-grid-wrapper/ag-grid-wrapper-harness.ts","../../../../../libs/components/ag-grid/testing/src/modules/ag-grid/ag-grid-testing.service.ts","../../../../../libs/components/ag-grid/testing/src/modules/ag-grid/provide-ag-grid-testing.ts","../../../../../libs/components/ag-grid/testing/src/skyux-ag-grid-testing.ts"],"sourcesContent":["import { GridApi } from 'ag-grid-community';\n\ninterface RenderState {\n count: number;\n lastRenderedAt: number;\n}\n\nconst renderStates = new WeakMap<GridApi, RenderState>();\n\n/**\n * @internal\n * Records that AG Grid finished a row-model render pass for the given grid\n * (initial load, sort, filter, or pagination all trigger `modelUpdated`).\n */\nexport function trackModelUpdate(api: GridApi): void {\n const previous = renderStates.get(api);\n renderStates.set(api, {\n count: (previous?.count ?? 0) + 1,\n lastRenderedAt: Date.now(),\n });\n}\n\n/**\n * @internal\n * The number of `modelUpdated` render passes recorded for the given grid.\n */\nexport function getRenderCount(api: GridApi): number {\n return renderStates.get(api)?.count ?? 0;\n}\n\n/**\n * @internal\n * Milliseconds since the last recorded render pass, or `Infinity` if none\n * has been recorded yet. A grid that loads data asynchronously (e.g. an\n * Angular `resource()`) can render an empty pass before its real data\n * arrives, each firing its own `modelUpdated` - this lets callers wait for\n * the render count to stop moving, not just for it to move once.\n */\nexport function getMsSinceLastRender(api: GridApi): number {\n const state = renderStates.get(api);\n return state ? Date.now() - state.lastRenderedAt : Infinity;\n}\n\n/**\n * @internal\n * Whether AG Grid is currently opted out of Angular's test zone (i.e.\n * `provideSkyAgGridTesting()` is active), meaning render counts are actually\n * being recorded and it's safe to wait on them. Consumers of\n * `SkyAgGridWrapperHarness` that don't use `provideSkyAgGridTesting()` get no\n * tracking at all, so waiting on a render count that will never move would\n * just burn the full poll timeout on every read.\n */\nexport function isRenderTrackingActive(): boolean {\n return (window as any).AG_GRID_UNDER_TEST === false;\n}\n","import { HarnessPredicate } from '@angular/cdk/testing';\nimport { UnitTestElement } from '@angular/cdk/testing/testbed';\nimport { SkyComponentHarness } from '@skyux/core/testing';\n\nimport { GridApi, getGridApi } from 'ag-grid-community';\n\nimport {\n getMsSinceLastRender,\n getRenderCount,\n isRenderTrackingActive,\n} from '../ag-grid/ag-grid-render-tracking';\n\nimport { SkyAgGridWrapperHarnessFilters } from './ag-grid-wrapper-harness.filters';\n\n/**\n * Harness for interacting with SKY UX AG Grid components in tests.\n * Add `provideSkyAgGridTesting()` to the spec's providers so the harness\n * can wait for the grid to finish rendering; without it, render-readiness\n * waits are skipped.\n */\nexport class SkyAgGridWrapperHarness extends SkyComponentHarness {\n /**\n * @internal\n */\n public static hostSelector = 'sky-ag-grid-wrapper';\n\n /**\n * Gets a `HarnessPredicate` that can be used to search for a\n * `SkyAgGridWrapperHarness` that meets certain criteria\n */\n public static with(\n filters: SkyAgGridWrapperHarnessFilters,\n ): HarnessPredicate<SkyAgGridWrapperHarness> {\n return SkyAgGridWrapperHarness.getDataSkyIdPredicate(filters);\n }\n\n /**\n * Checks whether the grid is ready.\n */\n public async isGridReady(): Promise<boolean> {\n const gridReady = this.locatorFactory.locatorFor(\n '.ag-root.ag-unselectable',\n );\n\n return await gridReady()\n .then((el) => !!el)\n .catch(() => false);\n }\n\n /**\n * Retrieves the IDs of the currently displayed columns.\n */\n public async getDisplayedColumnIds(): Promise<string[]> {\n return await this.getGridApi()\n .then((api) => api.getAllDisplayedColumns().map((col) => col.getColId()))\n .catch(() => Promise.reject('Unable to retrieve displayed column IDs.'));\n }\n\n /**\n * Retrieves the header names of the currently displayed columns.\n */\n public async getDisplayedColumnHeaderNames(): Promise<string[]> {\n return await this.getGridApi()\n .then((api) =>\n api\n .getAllDisplayedColumns()\n .map((col) => col.getColDef().headerName || ''),\n )\n .catch(() =>\n Promise.reject('Unable to retrieve displayed column header names.'),\n );\n }\n\n /**\n * @internal\n */\n public async getGridApi(): Promise<GridApi> {\n const api = await this.#locateGridApi();\n if (isRenderTrackingActive()) {\n await this.#waitForRenderCount(api, (count) => count > 0);\n }\n return api;\n }\n\n /**\n * Retrieves the formatted values of a column for the rows the grid currently\n * displays, in display order.\n * @param columnId The ID of the column.\n */\n public async getDisplayedCellValues(columnId: string): Promise<string[]> {\n const api = await this.getGridApi();\n if (!api.getColumn(columnId)) {\n throw new Error(`Unable to find column \"${columnId}\".`);\n }\n\n return api\n .getRenderedNodes()\n .map((rowNode) =>\n String(\n api.getCellValue({ rowNode, colKey: columnId, useFormatter: true }) ??\n '',\n ),\n );\n }\n\n /**\n * Waits until the grid finishes rendering. Use this instead of\n * `fixture.whenStable()`, since AG Grid renders outside the Angular zone.\n * Requires `provideSkyAgGridTesting()`; without it, this resolves without waiting.\n * @param action An action that causes the grid to render again, such as\n * entering search text. When provided, the action runs first, and this waits\n * for the render it causes.\n */\n public async waitUntilRendered(\n action?: () => Promise<void> | void,\n ): Promise<void> {\n if (!isRenderTrackingActive()) {\n await action?.();\n return;\n }\n const api = await this.#locateGridApi();\n const renderCountBeforeAction = action ? getRenderCount(api) : 0;\n await action?.();\n await this.#waitForRenderCount(\n api,\n (count) => count > renderCountBeforeAction,\n );\n }\n\n async #locateGridApi(): Promise<GridApi> {\n // Query the `.ag-root` element rather than `ag-grid-angular` itself, since\n // `skyViewkeeper`'s shadow element is inserted as `ag-grid-angular`'s\n // first child and `getGridApi()` locates the grid by walking up from the\n // queried element's first element child.\n const locator = this.locatorFactory.locatorFor('ag-grid-angular .ag-root');\n return await locator().then((grid) => {\n if (grid instanceof UnitTestElement) {\n const api = getGridApi(grid.element);\n if (api) {\n return api;\n }\n }\n // If this harness were used in an environment that did not provide UnitTestElement.\n /* istanbul ignore next */\n throw new Error('Unable to get GridApi from AgGridAngular component.');\n });\n }\n\n // A grid that loads data asynchronously (e.g. an Angular `resource()`) can\n // render an empty pass before its real data arrives, each firing its own\n // `modelUpdated`. Waiting for the predicate to become true isn't enough -\n // this also waits for the render count to stop moving for `settleMs`, so a\n // read doesn't land on that intermediate empty pass.\n async #waitForRenderCount(\n api: GridApi,\n predicate: (count: number) => boolean,\n timeoutMs = 2000,\n settleMs = 100,\n ): Promise<void> {\n const deadline = Date.now() + timeoutMs;\n while (\n !predicate(getRenderCount(api)) ||\n getMsSinceLastRender(api) < settleMs\n ) {\n if (Date.now() >= deadline) {\n return;\n }\n await new Promise((resolve) => setTimeout(resolve, 25));\n }\n }\n}\n","import { DestroyRef, Injectable, inject } from '@angular/core';\nimport { SkyAgGridService, SkyGetGridOptionsArgs } from '@skyux/ag-grid';\n\nimport { GridOptions, ModelUpdatedEvent } from 'ag-grid-community';\n\nimport { trackModelUpdate } from './ag-grid-render-tracking';\n\n/**\n * `GridOptions` applied to every grid built through `SkyAgGridService` while\n * `provideSkyAgGridTesting()` is in effect. AG Grid recommends disabling\n * row/column virtualization in tests so the whole grid is DOM-queryable\n * regardless of a consumer's own layout/scroll settings.\n */\nconst SKY_AG_GRID_TESTING_OPTIONS: Partial<GridOptions> = {\n suppressColumnVirtualisation: true,\n suppressRowVirtualisation: true,\n};\n\n/**\n * Records every `modelUpdated` render pass so `SkyAgGridWrapperHarness` can\n * poll for a real, non-zone-dependent readiness signal instead of relying on\n * `whenStable()`, which no longer waits for AG Grid once it runs outside the\n * Angular zone (see `provideSkyAgGridTesting()`).\n */\nfunction withRenderTracking<T>(options: GridOptions<T>): GridOptions<T> {\n const onModelUpdated = options.onModelUpdated;\n return {\n ...options,\n onModelUpdated: (event: ModelUpdatedEvent<T>): void => {\n trackModelUpdate(event.api);\n onModelUpdated?.(event);\n },\n };\n}\n\n/**\n * Tracks how many live `SkyAgGridTestingService` instances currently want\n * `AG_GRID_UNDER_TEST` forced to `false`, and the value it held before the\n * first of them activated it. `provideSkyAgGridTesting()` can be added to\n * `TestBed.configureTestingModule()`'s providers *and/or* to one or more\n * component `providers` arrays within the same spec, so multiple instances\n * can be alive at once and destroyed in any order - the flag must stay forced\n * while any instance is alive, and the pre-existing value must only be\n * restored once the last one is destroyed.\n */\nlet activeInstanceCount = 0;\nlet restoreValue: unknown;\n\n/**\n * @internal\n * Configures every grid with disabled row/column virtualization and opts out\n * of AG Grid's Angular test-zone detection. Specs that create and destroy many\n * grids can otherwise see AG Grid's own internal timers and animation frames\n * occasionally leave a `requestAnimationFrame` callback pending indefinitely,\n * which surfaces as a `whenStable()` timeout unrelated to the code under test.\n * Without zone opt-out, AG Grid schedules its internal scroll/viewport work\n * inside the Angular zone under Karma, which can keep the zone from ever\n * reaching stable. The flag is restored to its prior value once the last live\n * instance is destroyed, so specs that don't opt in aren't affected by ones\n * that do.\n *\n * Note: `provideSkyAgGridTesting()` can be added to `TestBed.configureTestingModule()`'s\n * providers *or* to a component's own `providers` array, so this can't rely on\n * an environment-injector-only mechanism (like `ENVIRONMENT_INITIALIZER`) to\n * set/restore the flag — a service constructor works at either injector level,\n * and `DestroyRef` fires correctly for either a TestBed module teardown or a\n * component destroy.\n */\n@Injectable()\nexport class SkyAgGridTestingService extends SkyAgGridService {\n constructor() {\n super();\n if (activeInstanceCount === 0) {\n restoreValue = (window as any).AG_GRID_UNDER_TEST;\n }\n activeInstanceCount++;\n (window as any).AG_GRID_UNDER_TEST = false;\n inject(DestroyRef).onDestroy(() => {\n activeInstanceCount--;\n if (activeInstanceCount === 0) {\n (window as any).AG_GRID_UNDER_TEST = restoreValue;\n }\n });\n }\n\n public override getGridOptions<T = any>(\n args: SkyGetGridOptionsArgs<T>,\n ): GridOptions<T> {\n return withRenderTracking({\n ...super.getGridOptions(args),\n ...SKY_AG_GRID_TESTING_OPTIONS,\n });\n }\n\n public override getEditableGridOptions<T = any>(\n args: SkyGetGridOptionsArgs<T>,\n ): GridOptions<T> {\n return withRenderTracking({\n ...super.getEditableGridOptions(args),\n ...SKY_AG_GRID_TESTING_OPTIONS,\n });\n }\n}\n","import { Provider } from '@angular/core';\nimport { SkyAgGridService } from '@skyux/ag-grid';\n\nimport { SkyAgGridTestingService } from './ag-grid-testing.service';\n\n/**\n * Configures every grid built through `SkyAgGridService` to disable AG Grid's\n * row/column virtualization and to opt out of AG Grid's Angular test-zone\n * detection while the test is running, so grid work completes without hanging\n * `whenStable()`. The previous test-zone setting is restored when the test's\n * injector is destroyed, so specs that don't opt in aren't affected by ones\n * that do. Add to the spec's `TestBed.configureTestingModule` providers.\n *\n * @example\n * ```typescript\n * TestBed.configureTestingModule({\n * providers: [provideSkyAgGridTesting()],\n * });\n * ```\n * @preview\n */\nexport function provideSkyAgGridTesting(): Provider[] {\n return [{ provide: SkyAgGridService, useClass: SkyAgGridTestingService }];\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;AAOA,MAAM,YAAY,GAAG,IAAI,OAAO,EAAwB;AAExD;;;;AAIG;AACG,SAAU,gBAAgB,CAAC,GAAY,EAAA;IAC3C,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC;AACtC,IAAA,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE;QACpB,KAAK,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,CAAC,IAAI,CAAC;AACjC,QAAA,cAAc,EAAE,IAAI,CAAC,GAAG,EAAE;AAC3B,KAAA,CAAC;AACJ;AAEA;;;AAGG;AACG,SAAU,cAAc,CAAC,GAAY,EAAA;IACzC,OAAO,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC;AAC1C;AAEA;;;;;;;AAOG;AACG,SAAU,oBAAoB,CAAC,GAAY,EAAA;IAC/C,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC;AACnC,IAAA,OAAO,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,cAAc,GAAG,QAAQ;AAC7D;AAEA;;;;;;;;AAQG;SACa,sBAAsB,GAAA;AACpC,IAAA,OAAQ,MAAc,CAAC,kBAAkB,KAAK,KAAK;AACrD;;ACxCA;;;;;AAKG;AACG,MAAO,uBAAwB,SAAQ,mBAAmB,CAAA;AAC9D;;AAEG;aACW,IAAA,CAAA,YAAY,GAAG,qBAAqB,CAAC;AAEnD;;;AAGG;IACI,OAAO,IAAI,CAChB,OAAuC,EAAA;AAEvC,QAAA,OAAO,uBAAuB,CAAC,qBAAqB,CAAC,OAAO,CAAC;IAC/D;AAEA;;AAEG;AACI,IAAA,MAAM,WAAW,GAAA;QACtB,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,CAC9C,0BAA0B,CAC3B;QAED,OAAO,MAAM,SAAS;aACnB,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE;AACjB,aAAA,KAAK,CAAC,MAAM,KAAK,CAAC;IACvB;AAEA;;AAEG;AACI,IAAA,MAAM,qBAAqB,GAAA;AAChC,QAAA,OAAO,MAAM,IAAI,CAAC,UAAU;aACzB,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,sBAAsB,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,QAAQ,EAAE,CAAC;aACvE,KAAK,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,0CAA0C,CAAC,CAAC;IAC5E;AAEA;;AAEG;AACI,IAAA,MAAM,6BAA6B,GAAA;AACxC,QAAA,OAAO,MAAM,IAAI,CAAC,UAAU;AACzB,aAAA,IAAI,CAAC,CAAC,GAAG,KACR;AACG,aAAA,sBAAsB;AACtB,aAAA,GAAG,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,SAAS,EAAE,CAAC,UAAU,IAAI,EAAE,CAAC;aAElD,KAAK,CAAC,MACL,OAAO,CAAC,MAAM,CAAC,mDAAmD,CAAC,CACpE;IACL;AAEA;;AAEG;AACI,IAAA,MAAM,UAAU,GAAA;AACrB,QAAA,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE;QACvC,IAAI,sBAAsB,EAAE,EAAE;AAC5B,YAAA,MAAM,IAAI,CAAC,mBAAmB,CAAC,GAAG,EAAE,CAAC,KAAK,KAAK,KAAK,GAAG,CAAC,CAAC;QAC3D;AACA,QAAA,OAAO,GAAG;IACZ;AAEA;;;;AAIG;IACI,MAAM,sBAAsB,CAAC,QAAgB,EAAA;AAClD,QAAA,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE;QACnC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE;AAC5B,YAAA,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,CAAA,EAAA,CAAI,CAAC;QACzD;AAEA,QAAA,OAAO;AACJ,aAAA,gBAAgB;aAChB,GAAG,CAAC,CAAC,OAAO,KACX,MAAM,CACJ,GAAG,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;YACjE,EAAE,CACL,CACF;IACL;AAEA;;;;;;;AAOG;IACI,MAAM,iBAAiB,CAC5B,MAAmC,EAAA;AAEnC,QAAA,IAAI,CAAC,sBAAsB,EAAE,EAAE;YAC7B,MAAM,MAAM,IAAI;YAChB;QACF;AACA,QAAA,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE;AACvC,QAAA,MAAM,uBAAuB,GAAG,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC;QAChE,MAAM,MAAM,IAAI;AAChB,QAAA,MAAM,IAAI,CAAC,mBAAmB,CAC5B,GAAG,EACH,CAAC,KAAK,KAAK,KAAK,GAAG,uBAAuB,CAC3C;IACH;AAEA,IAAA,MAAM,cAAc,GAAA;;;;;QAKlB,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,0BAA0B,CAAC;QAC1E,OAAO,MAAM,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,KAAI;AACnC,YAAA,IAAI,IAAI,YAAY,eAAe,EAAE;gBACnC,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;gBACpC,IAAI,GAAG,EAAE;AACP,oBAAA,OAAO,GAAG;gBACZ;YACF;;;AAGA,YAAA,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC;AACxE,QAAA,CAAC,CAAC;IACJ;;;;;;AAOA,IAAA,MAAM,mBAAmB,CACvB,GAAY,EACZ,SAAqC,EACrC,SAAS,GAAG,IAAI,EAChB,QAAQ,GAAG,GAAG,EAAA;QAEd,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;AACvC,QAAA,OACE,CAAC,SAAS,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;AAC/B,YAAA,oBAAoB,CAAC,GAAG,CAAC,GAAG,QAAQ,EACpC;AACA,YAAA,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ,EAAE;gBAC1B;YACF;AACA,YAAA,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACzD;IACF;;;AClKF;;;;;AAKG;AACH,MAAM,2BAA2B,GAAyB;AACxD,IAAA,4BAA4B,EAAE,IAAI;AAClC,IAAA,yBAAyB,EAAE,IAAI;CAChC;AAED;;;;;AAKG;AACH,SAAS,kBAAkB,CAAI,OAAuB,EAAA;AACpD,IAAA,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc;IAC7C,OAAO;AACL,QAAA,GAAG,OAAO;AACV,QAAA,cAAc,EAAE,CAAC,KAA2B,KAAU;AACpD,YAAA,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC;AAC3B,YAAA,cAAc,GAAG,KAAK,CAAC;QACzB,CAAC;KACF;AACH;AAEA;;;;;;;;;AASG;AACH,IAAI,mBAAmB,GAAG,CAAC;AAC3B,IAAI,YAAqB;AAEzB;;;;;;;;;;;;;;;;;;;AAmBG;AAEG,MAAO,uBAAwB,SAAQ,gBAAgB,CAAA;AAC3D,IAAA,WAAA,GAAA;AACE,QAAA,KAAK,EAAE;AACP,QAAA,IAAI,mBAAmB,KAAK,CAAC,EAAE;AAC7B,YAAA,YAAY,GAAI,MAAc,CAAC,kBAAkB;QACnD;AACA,QAAA,mBAAmB,EAAE;AACpB,QAAA,MAAc,CAAC,kBAAkB,GAAG,KAAK;AAC1C,QAAA,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,MAAK;AAChC,YAAA,mBAAmB,EAAE;AACrB,YAAA,IAAI,mBAAmB,KAAK,CAAC,EAAE;AAC5B,gBAAA,MAAc,CAAC,kBAAkB,GAAG,YAAY;YACnD;AACF,QAAA,CAAC,CAAC;IACJ;AAEgB,IAAA,cAAc,CAC5B,IAA8B,EAAA;AAE9B,QAAA,OAAO,kBAAkB,CAAC;AACxB,YAAA,GAAG,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC;AAC7B,YAAA,GAAG,2BAA2B;AAC/B,SAAA,CAAC;IACJ;AAEgB,IAAA,sBAAsB,CACpC,IAA8B,EAAA;AAE9B,QAAA,OAAO,kBAAkB,CAAC;AACxB,YAAA,GAAG,KAAK,CAAC,sBAAsB,CAAC,IAAI,CAAC;AACrC,YAAA,GAAG,2BAA2B;AAC/B,SAAA,CAAC;IACJ;8GAhCW,uBAAuB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA,CAAA;kHAAvB,uBAAuB,EAAA,CAAA,CAAA;;2FAAvB,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBADnC;;;AC/DD;;;;;;;;;;;;;;;AAeG;SACa,uBAAuB,GAAA;IACrC,OAAO,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,uBAAuB,EAAE,CAAC;AAC3E;;ACvBA;;AAEG;;;;"}
1
+ {"version":3,"file":"skyux-ag-grid-testing.mjs","sources":["../../../../../libs/components/ag-grid/testing/src/modules/ag-grid/ag-grid-render-tracking.ts","../../../../../libs/components/ag-grid/testing/src/modules/ag-grid-wrapper/ag-grid-wrapper-harness.ts","../../../../../libs/components/ag-grid/testing/src/modules/ag-grid/ag-grid-testing.service.ts","../../../../../libs/components/ag-grid/testing/src/modules/ag-grid/provide-ag-grid-testing.ts","../../../../../libs/components/ag-grid/testing/src/skyux-ag-grid-testing.ts"],"sourcesContent":["import { GridApi } from 'ag-grid-community';\n\ninterface RenderState {\n count: number;\n lastRenderedAt: number;\n}\n\nconst renderStates = new WeakMap<GridApi, RenderState>();\n\n/**\n * @internal\n * Records that AG Grid finished a row-model render pass for the given grid\n * (initial load, sort, filter, or pagination all trigger `modelUpdated`).\n */\nexport function trackModelUpdate(api: GridApi): void {\n const previous = renderStates.get(api);\n renderStates.set(api, {\n count: (previous?.count ?? 0) + 1,\n lastRenderedAt: Date.now(),\n });\n}\n\n/**\n * @internal\n * The number of `modelUpdated` render passes recorded for the given grid.\n */\nexport function getRenderCount(api: GridApi): number {\n return renderStates.get(api)?.count ?? 0;\n}\n\n/**\n * @internal\n * Milliseconds since the last recorded render pass, or `Infinity` if none\n * has been recorded yet. A grid that loads data asynchronously (e.g. an\n * Angular `resource()`) can render an empty pass before its real data\n * arrives, each firing its own `modelUpdated` - this lets callers wait for\n * the render count to stop moving, not just for it to move once.\n */\nexport function getMsSinceLastRender(api: GridApi): number {\n const state = renderStates.get(api);\n return state ? Date.now() - state.lastRenderedAt : Infinity;\n}\n\n/**\n * @internal\n * Whether AG Grid is currently opted out of Angular's test zone (i.e.\n * `provideSkyAgGridTesting()` is active), meaning render counts are actually\n * being recorded and it's safe to wait on them. Consumers of\n * `SkyAgGridWrapperHarness` that don't use `provideSkyAgGridTesting()` get no\n * tracking at all, so waiting on a render count that will never move would\n * just burn the full poll timeout on every read.\n */\nexport function isRenderTrackingActive(): boolean {\n return (window as any).AG_GRID_UNDER_TEST === false;\n}\n","import { HarnessPredicate } from '@angular/cdk/testing';\nimport { UnitTestElement } from '@angular/cdk/testing/testbed';\nimport { SkyComponentHarness } from '@skyux/core/testing';\nimport { SkyInlineDeleteHarness } from '@skyux/layout/testing';\n\nimport { GridApi, getGridApi } from 'ag-grid-community';\n\nimport {\n getMsSinceLastRender,\n getRenderCount,\n isRenderTrackingActive,\n} from '../ag-grid/ag-grid-render-tracking';\n\nimport { SkyAgGridWrapperHarnessFilters } from './ag-grid-wrapper-harness.filters';\n\n/**\n * Harness for interacting with SKY UX AG Grid components in tests.\n * Add `provideSkyAgGridTesting()` to the spec's providers so the harness\n * can wait for the grid to finish rendering; without it, render-readiness\n * waits are skipped.\n */\nexport class SkyAgGridWrapperHarness extends SkyComponentHarness {\n /**\n * @internal\n */\n public static hostSelector = 'sky-ag-grid-wrapper';\n\n #getGrid = this.locatorFor('div.sky-ag-grid');\n\n /**\n * Gets a `HarnessPredicate` that can be used to search for a\n * `SkyAgGridWrapperHarness` that meets certain criteria\n */\n public static with(\n filters: SkyAgGridWrapperHarnessFilters,\n ): HarnessPredicate<SkyAgGridWrapperHarness> {\n return SkyAgGridWrapperHarness.getDataSkyIdPredicate(filters);\n }\n\n /**\n * Checks whether the grid is ready.\n */\n public async isGridReady(): Promise<boolean> {\n const gridReady = this.locatorFactory.locatorFor(\n '.ag-root.ag-unselectable',\n );\n\n return await gridReady()\n .then((el) => !!el)\n .catch(() => false);\n }\n\n /**\n * Retrieves the IDs of the currently displayed columns.\n */\n public async getDisplayedColumnIds(): Promise<string[]> {\n return await this.getGridApi()\n .then((api) => api.getAllDisplayedColumns().map((col) => col.getColId()))\n .catch(() => Promise.reject('Unable to retrieve displayed column IDs.'));\n }\n\n /**\n * Retrieves the header names of the currently displayed columns.\n */\n public async getDisplayedColumnHeaderNames(): Promise<string[]> {\n return await this.getGridApi()\n .then((api) =>\n api\n .getAllDisplayedColumns()\n .map((col) => col.getColDef().headerName || ''),\n )\n .catch(() =>\n Promise.reject('Unable to retrieve displayed column header names.'),\n );\n }\n\n /**\n * Gets the inline delete confirmation shown for a row by the\n * `skyAgGridRowDelete` directive, or `null` if the row is not showing one.\n * @param rowId The ID of the row.\n */\n public async getRowInlineDelete(\n rowId: string,\n ): Promise<SkyInlineDeleteHarness | null> {\n await this.waitUntilRendered();\n // The row delete overlay updates after the grid renders, outside the\n // Angular zone, so let its change detection run before querying it.\n await new Promise((resolve) => setTimeout(resolve));\n await this.forceStabilize();\n // The overlay renders outside the grid, attached to the document body, and\n // is tagged with the grid's ID so rows from other grids are not matched.\n const gridId = await (await this.#getGrid()).getAttribute('id');\n const rowLoader =\n await this.documentRootLocatorFactory().harnessLoaderForOptional(\n `[data-sky-ag-grid-id=\"${gridId}\"] [id=\"row-delete-ref-${rowId}\"]`,\n );\n return (await rowLoader?.getHarnessOrNull(SkyInlineDeleteHarness)) ?? null;\n }\n\n /**\n * @internal\n */\n public async getGridApi(): Promise<GridApi> {\n const api = await this.#locateGridApi();\n if (isRenderTrackingActive()) {\n await this.#waitForRenderCount(api, (count) => count > 0);\n }\n return api;\n }\n\n /**\n * Retrieves the formatted values of a column for the rows the grid currently\n * displays, in display order.\n * @param columnId The ID of the column.\n */\n public async getDisplayedCellValues(columnId: string): Promise<string[]> {\n const api = await this.getGridApi();\n if (!api.getColumn(columnId)) {\n throw new Error(`Unable to find column \"${columnId}\".`);\n }\n\n return api\n .getRenderedNodes()\n .map((rowNode) =>\n String(\n api.getCellValue({ rowNode, colKey: columnId, useFormatter: true }) ??\n '',\n ),\n );\n }\n\n /**\n * Waits until the grid finishes rendering. Use this instead of\n * `fixture.whenStable()`, since AG Grid renders outside the Angular zone.\n * Requires `provideSkyAgGridTesting()`; without it, this resolves without waiting.\n * @param action An action that causes the grid to render again, such as\n * entering search text. When provided, the action runs first, and this waits\n * for the render it causes.\n */\n public async waitUntilRendered(\n action?: () => Promise<void> | void,\n ): Promise<void> {\n if (!isRenderTrackingActive()) {\n await action?.();\n return;\n }\n const api = await this.#locateGridApi();\n const renderCountBeforeAction = action ? getRenderCount(api) : 0;\n await action?.();\n await this.#waitForRenderCount(\n api,\n (count) => count > renderCountBeforeAction,\n );\n }\n\n async #locateGridApi(): Promise<GridApi> {\n // Query the `.ag-root` element rather than `ag-grid-angular` itself, since\n // `skyViewkeeper`'s shadow element is inserted as `ag-grid-angular`'s\n // first child and `getGridApi()` locates the grid by walking up from the\n // queried element's first element child.\n const locator = this.locatorFactory.locatorFor('ag-grid-angular .ag-root');\n return await locator().then((grid) => {\n if (grid instanceof UnitTestElement) {\n const api = getGridApi(grid.element);\n if (api) {\n return api;\n }\n }\n // If this harness were used in an environment that did not provide UnitTestElement.\n /* istanbul ignore next */\n throw new Error('Unable to get GridApi from AgGridAngular component.');\n });\n }\n\n // A grid that loads data asynchronously (e.g. an Angular `resource()`) can\n // render an empty pass before its real data arrives, each firing its own\n // `modelUpdated`. Waiting for the predicate to become true isn't enough -\n // this also waits for the render count to stop moving for `settleMs`, so a\n // read doesn't land on that intermediate empty pass.\n async #waitForRenderCount(\n api: GridApi,\n predicate: (count: number) => boolean,\n timeoutMs = 2000,\n settleMs = 100,\n ): Promise<void> {\n const deadline = Date.now() + timeoutMs;\n while (\n !predicate(getRenderCount(api)) ||\n getMsSinceLastRender(api) < settleMs\n ) {\n if (Date.now() >= deadline) {\n return;\n }\n await new Promise((resolve) => setTimeout(resolve, 25));\n }\n }\n}\n","import { DestroyRef, Injectable, inject } from '@angular/core';\nimport { SkyAgGridService, SkyGetGridOptionsArgs } from '@skyux/ag-grid';\n\nimport { GridOptions, ModelUpdatedEvent } from 'ag-grid-community';\n\nimport { trackModelUpdate } from './ag-grid-render-tracking';\n\n/**\n * `GridOptions` applied to every grid built through `SkyAgGridService` while\n * `provideSkyAgGridTesting()` is in effect. AG Grid recommends disabling\n * row/column virtualization in tests so the whole grid is DOM-queryable\n * regardless of a consumer's own layout/scroll settings.\n */\nconst SKY_AG_GRID_TESTING_OPTIONS: Partial<GridOptions> = {\n suppressColumnVirtualisation: true,\n suppressRowVirtualisation: true,\n};\n\n/**\n * Records every `modelUpdated` render pass so `SkyAgGridWrapperHarness` can\n * poll for a real, non-zone-dependent readiness signal instead of relying on\n * `whenStable()`, which no longer waits for AG Grid once it runs outside the\n * Angular zone (see `provideSkyAgGridTesting()`).\n */\nfunction withRenderTracking<T>(options: GridOptions<T>): GridOptions<T> {\n const onModelUpdated = options.onModelUpdated;\n return {\n ...options,\n onModelUpdated: (event: ModelUpdatedEvent<T>): void => {\n trackModelUpdate(event.api);\n onModelUpdated?.(event);\n },\n };\n}\n\n/**\n * Tracks how many live `SkyAgGridTestingService` instances currently want\n * `AG_GRID_UNDER_TEST` forced to `false`, and the value it held before the\n * first of them activated it. `provideSkyAgGridTesting()` can be added to\n * `TestBed.configureTestingModule()`'s providers *and/or* to one or more\n * component `providers` arrays within the same spec, so multiple instances\n * can be alive at once and destroyed in any order - the flag must stay forced\n * while any instance is alive, and the pre-existing value must only be\n * restored once the last one is destroyed.\n */\nlet activeInstanceCount = 0;\nlet restoreValue: unknown;\n\n/**\n * @internal\n * Configures every grid with disabled row/column virtualization and opts out\n * of AG Grid's Angular test-zone detection. Specs that create and destroy many\n * grids can otherwise see AG Grid's own internal timers and animation frames\n * occasionally leave a `requestAnimationFrame` callback pending indefinitely,\n * which surfaces as a `whenStable()` timeout unrelated to the code under test.\n * Without zone opt-out, AG Grid schedules its internal scroll/viewport work\n * inside the Angular zone under Karma, which can keep the zone from ever\n * reaching stable. The flag is restored to its prior value once the last live\n * instance is destroyed, so specs that don't opt in aren't affected by ones\n * that do.\n *\n * Note: `provideSkyAgGridTesting()` can be added to `TestBed.configureTestingModule()`'s\n * providers *or* to a component's own `providers` array, so this can't rely on\n * an environment-injector-only mechanism (like `ENVIRONMENT_INITIALIZER`) to\n * set/restore the flag — a service constructor works at either injector level,\n * and `DestroyRef` fires correctly for either a TestBed module teardown or a\n * component destroy.\n */\n@Injectable()\nexport class SkyAgGridTestingService extends SkyAgGridService {\n constructor() {\n super();\n if (activeInstanceCount === 0) {\n restoreValue = (window as any).AG_GRID_UNDER_TEST;\n }\n activeInstanceCount++;\n (window as any).AG_GRID_UNDER_TEST = false;\n inject(DestroyRef).onDestroy(() => {\n activeInstanceCount--;\n if (activeInstanceCount === 0) {\n (window as any).AG_GRID_UNDER_TEST = restoreValue;\n }\n });\n }\n\n public override getGridOptions<T = any>(\n args: SkyGetGridOptionsArgs<T>,\n ): GridOptions<T> {\n return withRenderTracking({\n ...super.getGridOptions(args),\n ...SKY_AG_GRID_TESTING_OPTIONS,\n });\n }\n\n public override getEditableGridOptions<T = any>(\n args: SkyGetGridOptionsArgs<T>,\n ): GridOptions<T> {\n return withRenderTracking({\n ...super.getEditableGridOptions(args),\n ...SKY_AG_GRID_TESTING_OPTIONS,\n });\n }\n}\n","import { Provider } from '@angular/core';\nimport { SkyAgGridService } from '@skyux/ag-grid';\n\nimport { SkyAgGridTestingService } from './ag-grid-testing.service';\n\n/**\n * Configures every grid built through `SkyAgGridService` to disable AG Grid's\n * row/column virtualization and to opt out of AG Grid's Angular test-zone\n * detection while the test is running, so grid work completes without hanging\n * `whenStable()`. The previous test-zone setting is restored when the test's\n * injector is destroyed, so specs that don't opt in aren't affected by ones\n * that do. Add to the spec's `TestBed.configureTestingModule` providers.\n *\n * @example\n * ```typescript\n * TestBed.configureTestingModule({\n * providers: [provideSkyAgGridTesting()],\n * });\n * ```\n * @preview\n */\nexport function provideSkyAgGridTesting(): Provider[] {\n return [{ provide: SkyAgGridService, useClass: SkyAgGridTestingService }];\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { NgClass, CommonModule, NgTemplateOutlet, AsyncPipe } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { NgModule, inject, Injectable, input, booleanAttribute, numberAttribute, signal, linkedSignal, contentChild, viewChild, computed, DestroyRef, ElementRef, effect, ChangeDetectionStrategy, Component, contentChildren, ChangeDetectorRef, untracked, Directive, InjectionToken, model, output, EnvironmentInjector, afterNextRender, HostListener, ViewEncapsulation, HostBinding, isSignal, Input, RendererFactory2, CSP_NONCE, DOCUMENT } from '@angular/core';
3
+ import { NgModule, inject, Injectable, input, booleanAttribute, numberAttribute, linkedSignal, contentChild, viewChild, computed, DestroyRef, ElementRef, signal, effect, ChangeDetectionStrategy, Component, contentChildren, ChangeDetectorRef, untracked, Directive, InjectionToken, model, output, EnvironmentInjector, afterNextRender, HostListener, ViewEncapsulation, HostBinding, isSignal, Input, RendererFactory2, CSP_NONCE, DOCUMENT } from '@angular/core';
4
4
  import { AgGridAngular } from 'ag-grid-angular';
5
5
  import * as i3 from '@skyux/i18n';
6
6
  import { SkyLibResourcesService, SkyI18nModule, SkyAppLocaleProvider } from '@skyux/i18n';
@@ -534,12 +534,13 @@ class SkyAgGridWrapperComponent {
534
534
  this.beforeAnchorId = 'sky-ag-grid-nav-anchor-before-' + this.#idIndex;
535
535
  this.gridId = 'sky-ag-grid-' + this.#idIndex;
536
536
  /**
537
- * Set by SkyAgGridDataManagerAdapterDirective to hand sticky-header duty
538
- * to the data manager's own viewkeeper. Survives grid recreation, unlike
539
- * a direct write to `viewkeeperClasses`.
537
+ * Whether to stop keeping the column headers visible while the page scrolls,
538
+ * for a host that keeps them visible itself. This is applied when the grid
539
+ * initializes.
540
540
  * @internal
541
541
  */
542
- this.viewkeeperSuppressed = signal(false, /* @ts-ignore */
542
+ this.viewkeeperDisabled = input(false, { ...(ngDevMode ? { debugName: "viewkeeperDisabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
543
+ this.viewkeeperSuppressed = linkedSignal(() => this.viewkeeperDisabled(), /* @ts-ignore */
543
544
  ...(ngDevMode ? [{ debugName: "viewkeeperSuppressed" }] : /* istanbul ignore next */ []));
544
545
  /**
545
546
  * @internal
@@ -815,14 +816,14 @@ class SkyAgGridWrapperComponent {
815
816
  }
816
817
  }
817
818
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: SkyAgGridWrapperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
818
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.2", type: SkyAgGridWrapperComponent, isStandalone: true, selector: "sky-ag-grid-wrapper", inputs: { compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.sky-ag-grid-layout-normal": "isNormalLayout()" } }, queries: [{ propertyName: "agGrid", first: true, predicate: AgGridAngular, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "skyAgGridDiv", first: true, predicate: ["skyAgGridDiv"], descendants: true, isSignal: true }], ngImport: i0, template: "<span\n class=\"nav-anchor\"\n tabindex=\"0\"\n [id]=\"beforeAnchorId\"\n (focusin)=\"onAnchorFocus($event)\"\n>\n</span>\n<div\n #skyAgGridDiv\n class=\"sky-ag-grid\"\n skyViewkeeperOmitShadow=\".ag-body-horizontal-scroll\"\n tabindex=\"0\"\n [class.sky-ag-grid-top-scrollbar]=\"agGridContext()?.['enableTopScroll']\"\n [ngClass]=\"wrapperClasses()\"\n [id]=\"gridId\"\n [skyViewkeeper]=\"viewkeeperClasses()\"\n (keydown)=\"onGridKeydown($event)\"\n (keyup.escape)=\"onKeyUpEscape($event)\"\n>\n <ng-content />\n</div>\n<span\n tabindex=\"0\"\n class=\"nav-anchor\"\n [id]=\"afterAnchorId\"\n (focusin)=\"onAnchorFocus($event)\"\n>\n</span>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: SkyIdModule }, { kind: "ngmodule", type: SkyViewkeeperModule }, { kind: "directive", type: i1.λ3, selector: "[skyViewkeeper]", inputs: ["skyViewkeeper", "skyViewkeeperOmitShadow"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
819
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.2", type: SkyAgGridWrapperComponent, isStandalone: true, selector: "sky-ag-grid-wrapper", inputs: { compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, viewkeeperDisabled: { classPropertyName: "viewkeeperDisabled", publicName: "viewkeeperDisabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.sky-ag-grid-layout-normal": "isNormalLayout()" } }, queries: [{ propertyName: "agGrid", first: true, predicate: AgGridAngular, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "skyAgGridDiv", first: true, predicate: ["skyAgGridDiv"], descendants: true, isSignal: true }], ngImport: i0, template: "<span\n class=\"nav-anchor\"\n tabindex=\"0\"\n [id]=\"beforeAnchorId\"\n (focusin)=\"onAnchorFocus($event)\"\n>\n</span>\n<div\n #skyAgGridDiv\n class=\"sky-ag-grid\"\n skyViewkeeperOmitShadow=\".ag-body-horizontal-scroll\"\n tabindex=\"0\"\n [class.sky-ag-grid-top-scrollbar]=\"agGridContext()?.['enableTopScroll']\"\n [ngClass]=\"wrapperClasses()\"\n [id]=\"gridId\"\n [skyViewkeeper]=\"viewkeeperClasses()\"\n (keydown)=\"onGridKeydown($event)\"\n (keyup.escape)=\"onKeyUpEscape($event)\"\n>\n <ng-content />\n</div>\n<span\n tabindex=\"0\"\n class=\"nav-anchor\"\n [id]=\"afterAnchorId\"\n (focusin)=\"onAnchorFocus($event)\"\n>\n</span>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: SkyIdModule }, { kind: "ngmodule", type: SkyViewkeeperModule }, { kind: "directive", type: i1.λ3, selector: "[skyViewkeeper]", inputs: ["skyViewkeeper", "skyViewkeeperOmitShadow"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
819
820
  }
820
821
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: SkyAgGridWrapperComponent, decorators: [{
821
822
  type: Component,
822
823
  args: [{ selector: 'sky-ag-grid-wrapper', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgClass, SkyIdModule, SkyViewkeeperModule], host: {
823
824
  '[class.sky-ag-grid-layout-normal]': 'isNormalLayout()',
824
825
  }, template: "<span\n class=\"nav-anchor\"\n tabindex=\"0\"\n [id]=\"beforeAnchorId\"\n (focusin)=\"onAnchorFocus($event)\"\n>\n</span>\n<div\n #skyAgGridDiv\n class=\"sky-ag-grid\"\n skyViewkeeperOmitShadow=\".ag-body-horizontal-scroll\"\n tabindex=\"0\"\n [class.sky-ag-grid-top-scrollbar]=\"agGridContext()?.['enableTopScroll']\"\n [ngClass]=\"wrapperClasses()\"\n [id]=\"gridId\"\n [skyViewkeeper]=\"viewkeeperClasses()\"\n (keydown)=\"onGridKeydown($event)\"\n (keyup.escape)=\"onKeyUpEscape($event)\"\n>\n <ng-content />\n</div>\n<span\n tabindex=\"0\"\n class=\"nav-anchor\"\n [id]=\"afterAnchorId\"\n (focusin)=\"onAnchorFocus($event)\"\n>\n</span>\n" }]
825
- }], ctorParameters: () => [], propDecorators: { compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], minHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "minHeight", required: false }] }], agGrid: [{ type: i0.ContentChild, args: [i0.forwardRef(() => AgGridAngular), { isSignal: true }] }], skyAgGridDiv: [{ type: i0.ViewChild, args: ['skyAgGridDiv', { isSignal: true }] }] } });
826
+ }], ctorParameters: () => [], propDecorators: { compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], minHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "minHeight", required: false }] }], viewkeeperDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewkeeperDisabled", required: false }] }], agGrid: [{ type: i0.ContentChild, args: [i0.forwardRef(() => AgGridAngular), { isSignal: true }] }], skyAgGridDiv: [{ type: i0.ViewChild, args: ['skyAgGridDiv', { isSignal: true }] }] } });
826
827
 
827
828
  function toColumnWidthName(breakpoint) {
828
829
  return breakpoint === 'xs' ? 'xs' : 'sm';
@@ -1490,6 +1491,13 @@ class SkyAgGridRowDeleteDirective {
1490
1491
  useValue: this.#rowDeleteSvc,
1491
1492
  },
1492
1493
  ]);
1494
+ // The overlay renders outside the grid, so tag it with the grid's ID to
1495
+ // let the grid's test harness tell its inline deletes apart from those
1496
+ // of another grid with the same row IDs.
1497
+ const gridId = this.#elementRef.nativeElement.querySelector('div.sky-ag-grid')?.id;
1498
+ if (gridId) {
1499
+ this.#overlay.componentRef.location.nativeElement.setAttribute('data-sky-ag-grid-id', gridId);
1500
+ }
1493
1501
  this.#zIndex
1494
1502
  .pipe(takeUntilDestroyed(this.#destroyRef), takeUntil(this.#overlay.closed), takeUntil(agGridDestroyed), distinctUntilChanged())
1495
1503
  .subscribe((zIndex) => {