argent-grid 0.1.0 → 0.2.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.
Files changed (108) hide show
  1. package/.github/workflows/ci.yml +69 -0
  2. package/.github/workflows/pages.yml +6 -12
  3. package/.storybook/main.ts +20 -0
  4. package/.storybook/preview.ts +18 -0
  5. package/.storybook/tsconfig.json +24 -0
  6. package/AGENTS.md +2 -2
  7. package/README.md +51 -34
  8. package/angular.json +66 -0
  9. package/biome.json +66 -0
  10. package/demo-app/e2e/selection-screenshot.spec.ts +20 -0
  11. package/docs/AG-GRID-COMPARISON.md +725 -0
  12. package/docs/CELL-RENDERER-GUIDE.md +241 -0
  13. package/docs/CONTEXT-MENU-GUIDE.md +371 -0
  14. package/docs/LIVE-DATA-OPTIMIZATIONS.md +497 -0
  15. package/docs/PERFORMANCE-OPTIMIZATIONS-PHASE1.md +162 -0
  16. package/docs/PERFORMANCE-REVIEW.md +571 -0
  17. package/docs/RESEARCH-STATUS.md +234 -0
  18. package/docs/STATE-PERSISTENCE-GUIDE.md +370 -0
  19. package/docs/STORYBOOK-REFACTOR.md +215 -0
  20. package/docs/STORYBOOK-STATUS.md +156 -0
  21. package/docs/TEST-COVERAGE-REPORT.md +276 -0
  22. package/docs/THEME-API-GUIDE.md +445 -0
  23. package/docs/THEME-API-PLAN.md +364 -0
  24. package/e2e/advanced.spec.ts +109 -0
  25. package/e2e/argentgrid.spec.ts +65 -0
  26. package/e2e/benchmark.spec.ts +52 -0
  27. package/e2e/screenshots.spec.ts +52 -0
  28. package/e2e/theming.spec.ts +35 -0
  29. package/e2e/visual.spec.ts +91 -0
  30. package/e2e/visual.spec.ts-snapshots/grid-default.png +0 -0
  31. package/e2e/visual.spec.ts-snapshots/grid-empty-state.png +0 -0
  32. package/e2e/visual.spec.ts-snapshots/grid-filter-popup.png +0 -0
  33. package/e2e/visual.spec.ts-snapshots/grid-scroll-borders.png +0 -0
  34. package/e2e/visual.spec.ts-snapshots/grid-sidebar-buttons.png +0 -0
  35. package/e2e/visual.spec.ts-snapshots/grid-text-filter.png +0 -0
  36. package/e2e/visual.spec.ts-snapshots/grid-with-selection.png +0 -0
  37. package/package.json +20 -6
  38. package/plan.md +50 -18
  39. package/playwright.config.ts +38 -0
  40. package/setup-vitest.ts +10 -13
  41. package/src/lib/argent-grid.module.ts +10 -12
  42. package/src/lib/components/argent-grid.component.css +327 -76
  43. package/src/lib/components/argent-grid.component.html +186 -64
  44. package/src/lib/components/argent-grid.component.spec.ts +120 -160
  45. package/src/lib/components/argent-grid.component.ts +642 -189
  46. package/src/lib/components/argent-grid.selection.spec.ts +132 -0
  47. package/src/lib/components/set-filter/set-filter.component.ts +302 -0
  48. package/src/lib/directives/ag-grid-compatibility.directive.ts +16 -26
  49. package/src/lib/directives/click-outside.directive.ts +19 -0
  50. package/src/lib/rendering/canvas-renderer.spec.ts +366 -0
  51. package/src/lib/rendering/canvas-renderer.ts +418 -305
  52. package/src/lib/rendering/live-data-handler.ts +110 -0
  53. package/src/lib/rendering/live-data-optimizations.ts +133 -0
  54. package/src/lib/rendering/render/blit.spec.ts +16 -27
  55. package/src/lib/rendering/render/blit.ts +48 -36
  56. package/src/lib/rendering/render/cells.spec.ts +132 -0
  57. package/src/lib/rendering/render/cells.ts +46 -24
  58. package/src/lib/rendering/render/column-utils.ts +73 -0
  59. package/src/lib/rendering/render/hit-test.ts +55 -0
  60. package/src/lib/rendering/render/index.ts +79 -76
  61. package/src/lib/rendering/render/lines.ts +43 -43
  62. package/src/lib/rendering/render/primitives.ts +161 -0
  63. package/src/lib/rendering/render/theme.spec.ts +8 -12
  64. package/src/lib/rendering/render/theme.ts +7 -10
  65. package/src/lib/rendering/render/types.ts +2 -2
  66. package/src/lib/rendering/render/walk.spec.ts +35 -38
  67. package/src/lib/rendering/render/walk.ts +60 -50
  68. package/src/lib/rendering/utils/damage-tracker.spec.ts +8 -7
  69. package/src/lib/rendering/utils/damage-tracker.ts +6 -18
  70. package/src/lib/rendering/utils/index.ts +1 -1
  71. package/src/lib/services/grid.service.set-filter.spec.ts +219 -0
  72. package/src/lib/services/grid.service.spec.ts +1165 -201
  73. package/src/lib/services/grid.service.ts +819 -187
  74. package/src/lib/themes/parts/color-schemes.ts +132 -0
  75. package/src/lib/themes/parts/icon-sets.ts +258 -0
  76. package/src/lib/themes/theme-builder.ts +347 -0
  77. package/src/lib/themes/theme-quartz.ts +72 -0
  78. package/src/lib/themes/types.ts +238 -0
  79. package/src/lib/types/ag-grid-types.ts +73 -14
  80. package/src/public-api.ts +39 -9
  81. package/src/stories/Advanced.stories.ts +188 -0
  82. package/src/stories/ArgentGrid.stories.ts +277 -0
  83. package/src/stories/Benchmark.stories.ts +74 -0
  84. package/src/stories/CellRenderers.stories.ts +221 -0
  85. package/src/stories/Filtering.stories.ts +252 -0
  86. package/src/stories/Grouping.stories.ts +217 -0
  87. package/src/stories/Theming.stories.ts +124 -0
  88. package/src/stories/benchmark-wrapper.component.ts +315 -0
  89. package/tsconfig.storybook.json +10 -0
  90. package/vitest.config.ts +9 -9
  91. package/demo-app/README.md +0 -70
  92. package/demo-app/angular.json +0 -78
  93. package/demo-app/e2e/benchmark.spec.ts +0 -53
  94. package/demo-app/e2e/demo-page.spec.ts +0 -77
  95. package/demo-app/e2e/grid-features.spec.ts +0 -269
  96. package/demo-app/package-lock.json +0 -14023
  97. package/demo-app/package.json +0 -36
  98. package/demo-app/playwright-test-menu.js +0 -19
  99. package/demo-app/playwright.config.ts +0 -23
  100. package/demo-app/src/app/app.component.ts +0 -10
  101. package/demo-app/src/app/app.config.ts +0 -13
  102. package/demo-app/src/app/app.routes.ts +0 -7
  103. package/demo-app/src/app/demo-page/demo-page.component.css +0 -313
  104. package/demo-app/src/app/demo-page/demo-page.component.html +0 -124
  105. package/demo-app/src/app/demo-page/demo-page.component.ts +0 -366
  106. package/demo-app/src/index.html +0 -19
  107. package/demo-app/src/main.ts +0 -6
  108. package/demo-app/tsconfig.json +0 -31
@@ -0,0 +1,366 @@
1
+ import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { GridApi } from '../types/ag-grid-types';
3
+ import { CanvasRenderer } from './canvas-renderer';
4
+
5
+ // Mock canvas context with ALL required methods
6
+ const mockCanvasContext = {
7
+ fillRect: vi.fn(),
8
+ clearRect: vi.fn(),
9
+ fillText: vi.fn(),
10
+ measureText: vi.fn(() => ({ width: 100 })),
11
+ stroke: vi.fn(),
12
+ beginPath: vi.fn(),
13
+ moveTo: vi.fn(),
14
+ lineTo: vi.fn(),
15
+ arc: vi.fn(),
16
+ fill: vi.fn(),
17
+ clip: vi.fn(),
18
+ save: vi.fn(),
19
+ restore: vi.fn(),
20
+ scale: vi.fn(),
21
+ setTransform: vi.fn(),
22
+ drawImage: vi.fn(),
23
+ createPattern: vi.fn(),
24
+ translate: vi.fn(),
25
+ rotate: vi.fn(),
26
+ transform: vi.fn(),
27
+ isPointInPath: vi.fn(),
28
+ strokeRect: vi.fn(),
29
+ strokeText: vi.fn(),
30
+ rect: vi.fn(),
31
+ lineCap: 'butt',
32
+ lineJoin: 'miter',
33
+ lineWidth: 1,
34
+ miterLimit: 10,
35
+ fillStyle: '#000000',
36
+ strokeStyle: '#000000',
37
+ font: '13px sans-serif',
38
+ textAlign: 'start',
39
+ textBaseline: 'alphabetic',
40
+ } as unknown as CanvasRenderingContext2D;
41
+
42
+ // Mock GridApi
43
+ const createMockGridApi = (): GridApi => {
44
+ return {
45
+ getGridId: vi.fn(() => 'test-grid'),
46
+ getColumnDefs: vi.fn(() => []),
47
+ setColumnDefs: vi.fn(),
48
+ getColumn: vi.fn(),
49
+ getAllColumns: vi.fn(() => []),
50
+ getDisplayedRowAtIndex: vi.fn(),
51
+ getDisplayedRowCount: vi.fn(() => 100),
52
+ getFirstRenderedRow: vi.fn(() => 0),
53
+ getLastRenderedRow: vi.fn(() => 100),
54
+ getFirstRenderedColumn: vi.fn(),
55
+ getLastRenderedColumn: vi.fn(),
56
+ forEachNode: vi.fn(),
57
+ forEachNodeAfterFilter: vi.fn(),
58
+ forEachNodeAfterFilterAndSort: vi.fn(),
59
+ getRowData: vi.fn(() => []),
60
+ setRowData: vi.fn(),
61
+ addItems: vi.fn(),
62
+ updateRowData: vi.fn(),
63
+ applyTransaction: vi.fn(),
64
+ selectIndex: vi.fn(),
65
+ selectNodes: vi.fn(),
66
+ deselectAll: vi.fn(),
67
+ getSelectedNodes: vi.fn(() => []),
68
+ getSelectedRows: vi.fn(() => []),
69
+ isRowSelected: vi.fn(() => false),
70
+ getFilterInstance: vi.fn(),
71
+ getFilterModel: vi.fn(() => ({})),
72
+ setFilterModel: vi.fn(),
73
+ onFilterChanged: vi.fn(),
74
+ getSortModel: vi.fn(() => []),
75
+ setSortModel: vi.fn(),
76
+ onSortChanged: vi.fn(),
77
+ sizeColumnsToFit: vi.fn(),
78
+ autoSizeColumns: vi.fn(),
79
+ setColumnWidth: vi.fn(),
80
+ getColumnState: vi.fn(() => []),
81
+ setColumnState: vi.fn(),
82
+ resetColumnState: vi.fn(),
83
+ exportDataAsCsv: vi.fn(),
84
+ exportDataAsExcel: vi.fn(),
85
+ copySelectedRowsToClipboard: vi.fn(),
86
+ copySelectedRangeToClipboard: vi.fn(),
87
+ copyRangeToClipboard: vi.fn(),
88
+ pasteFromClipboard: vi.fn(),
89
+ enableBrowserTooltips: vi.fn(),
90
+ showLoadingOverlay: vi.fn(),
91
+ hideOverlay: vi.fn(),
92
+ getFocusedCell: vi.fn(),
93
+ startEditingCell: vi.fn(),
94
+ stopEditingCell: vi.fn(),
95
+ getEditingCells: vi.fn(() => []),
96
+ refreshCells: vi.fn(),
97
+ refreshRows: vi.fn(),
98
+ redrawRows: vi.fn(),
99
+ refreshHeader: vi.fn(),
100
+ refreshFooter: vi.fn(),
101
+ refreshPivot: vi.fn(),
102
+ resetRowGroupColumns: vi.fn(),
103
+ addRowGroupColumn: vi.fn(),
104
+ removeRowGroupColumn: vi.fn(),
105
+ setRowGroupColumns: vi.fn(),
106
+ addPivotColumn: vi.fn(),
107
+ removePivotColumn: vi.fn(),
108
+ setPivotColumns: vi.fn(),
109
+ addValueColumn: vi.fn(),
110
+ removeValueColumn: vi.fn(),
111
+ setValueColumns: vi.fn(),
112
+ getRowGroupColumns: vi.fn(() => []),
113
+ getPivotColumns: vi.fn(() => []),
114
+ getValueColumns: vi.fn(() => []),
115
+ isPivotMode: vi.fn(() => false),
116
+ getPivotMode: vi.fn(() => false),
117
+ setPivotMode: vi.fn(),
118
+ getGroupDisplayType: vi.fn(() => 'singleColumn'),
119
+ getGroupRowRenderer: vi.fn(),
120
+ getRowHeight: vi.fn(() => 32),
121
+ getTotalRowHeight: vi.fn(() => 3200),
122
+ getScrollPosition: vi.fn(() => ({ top: 0, left: 0 })),
123
+ ensureIndexVisible: vi.fn(),
124
+ ensureColumnVisible: vi.fn(),
125
+ ensureColumnIndexVisible: vi.fn(),
126
+ flashCells: vi.fn(),
127
+ flashRows: vi.fn(),
128
+ getCellEditorFactory: vi.fn(),
129
+ registerCellRenderer: vi.fn(),
130
+ getRenderer: vi.fn(),
131
+ getEditor: vi.fn(),
132
+ getContextMenuItems: vi.fn(() => []),
133
+ getMainMenuItems: vi.fn(() => []),
134
+ showToolPanel: vi.fn(),
135
+ hideToolPanel: vi.fn(),
136
+ isToolPanelShowing: vi.fn(() => false),
137
+ setToolPanel: vi.fn(),
138
+ getToolPanel: vi.fn(),
139
+ destroy: vi.fn(),
140
+ addGlobalListener: vi.fn(),
141
+ removeGlobalListener: vi.fn(),
142
+ dispatchEvent: vi.fn(),
143
+ getEventPath: vi.fn(() => []),
144
+ getApi: vi.fn(),
145
+ getColumnApi: vi.fn(),
146
+ getLocaleText: vi.fn(),
147
+ getDocument: vi.fn(() => document),
148
+ getGridOptions: vi.fn(),
149
+ getRowPinned: vi.fn(),
150
+ getTopLevelNodes: vi.fn(() => []),
151
+ getRootNode: vi.fn(),
152
+ getRowNode: vi.fn(),
153
+ getModel: vi.fn(),
154
+ getRowModel: vi.fn(),
155
+ getPaginationPageSize: vi.fn(),
156
+ setPaginationPageSize: vi.fn(),
157
+ paginationGetPageSize: vi.fn(),
158
+ paginationSetPageSize: vi.fn(),
159
+ paginationGetRowCount: vi.fn(() => 100),
160
+ paginationGetCurrentPage: vi.fn(() => 0),
161
+ paginationGetTotalPages: vi.fn(() => 10),
162
+ paginationGoToFirstPage: vi.fn(),
163
+ paginationGoToLastPage: vi.fn(),
164
+ paginationGoToNextPage: vi.fn(),
165
+ paginationGoToPreviousPage: vi.fn(),
166
+ paginationGoToPage: vi.fn(),
167
+ isPaginationEnabled: vi.fn(() => false),
168
+ getCellRanges: vi.fn(() => null),
169
+ getRowAtY: vi.fn((y: number) => Math.floor(y / 32)),
170
+ getRowY: vi.fn((rowIndex: number) => rowIndex * 32),
171
+ } as any;
172
+ };
173
+
174
+ describe('CanvasRenderer', () => {
175
+ let renderer: CanvasRenderer;
176
+ let mockApi: GridApi;
177
+ let mockContainer: HTMLDivElement;
178
+ let mockCanvas: HTMLCanvasElement;
179
+
180
+ beforeAll(() => {
181
+ vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockImplementation(
182
+ () => mockCanvasContext as any
183
+ );
184
+ vi.spyOn(console, 'error').mockImplementation(() => {});
185
+ });
186
+
187
+ afterAll(() => {
188
+ vi.restoreAllMocks();
189
+ });
190
+
191
+ beforeEach(() => {
192
+ // Create a real canvas element for proper DOM behavior
193
+ mockCanvas = document.createElement('canvas');
194
+ mockCanvas.width = 800;
195
+ mockCanvas.height = 600;
196
+
197
+ // Mock scrollHeight and clientHeight using Object.defineProperty
198
+ Object.defineProperty(mockCanvas, 'scrollHeight', {
199
+ value: 3200,
200
+ writable: true,
201
+ configurable: true,
202
+ });
203
+ Object.defineProperty(mockCanvas, 'clientHeight', {
204
+ value: 600,
205
+ writable: true,
206
+ configurable: true,
207
+ });
208
+
209
+ mockApi = createMockGridApi();
210
+ mockContainer = document.createElement('div');
211
+ mockContainer.style.height = '600px';
212
+ mockContainer.style.overflow = 'auto';
213
+ mockContainer.appendChild(mockCanvas);
214
+
215
+ vi.spyOn(mockCanvas, 'parentElement', 'get').mockReturnValue(mockContainer);
216
+ vi.spyOn(mockContainer, 'scrollHeight', 'get').mockReturnValue(3200);
217
+ vi.spyOn(mockContainer, 'clientHeight', 'get').mockReturnValue(600);
218
+
219
+ renderer = new CanvasRenderer(mockCanvas, mockApi, 32);
220
+ });
221
+
222
+ it('should create', () => {
223
+ expect(renderer).toBeTruthy();
224
+ });
225
+
226
+ it('should initialize with correct row height', () => {
227
+ // Renderer should be initialized
228
+ expect(mockCanvas).toBeTruthy();
229
+ });
230
+
231
+ it('should resize canvas', () => {
232
+ const resizeSpy = vi.spyOn(renderer as any, 'resize');
233
+ window.dispatchEvent(new Event('resize'));
234
+ vi.advanceTimersByTime(200);
235
+ expect(resizeSpy).toHaveBeenCalled();
236
+ });
237
+
238
+ it('should render frame', () => {
239
+ expect(() => renderer.renderFrame()).not.toThrow();
240
+ });
241
+
242
+ it('should scrollToBottom', () => {
243
+ const container = mockCanvas.parentElement!;
244
+ const scrollTopSpy = vi.spyOn(container, 'scrollTop', 'set');
245
+ renderer.scrollToBottom();
246
+ expect(scrollTopSpy).toHaveBeenCalled();
247
+ });
248
+
249
+ it('should handle context menu events', () => {
250
+ const event = new MouseEvent('contextmenu', { bubbles: true });
251
+ mockCanvas.dispatchEvent(event);
252
+ expect(event.defaultPrevented).toBe(false);
253
+ });
254
+
255
+ it('should get column at position', () => {
256
+ const column = renderer.getColumnAtPosition(100);
257
+ expect(typeof column).toBe('number');
258
+ });
259
+
260
+ it('should get row at position', () => {
261
+ const row = renderer.getRowAtPosition(100);
262
+ expect(typeof row).toBe('number');
263
+ });
264
+
265
+ it('should handle viewport changes', () => {
266
+ const container = mockCanvas.parentElement!;
267
+ Object.defineProperty(container, 'clientHeight', {
268
+ value: 800,
269
+ writable: true,
270
+ configurable: true,
271
+ });
272
+ window.dispatchEvent(new Event('resize'));
273
+ expect(container).toBeTruthy();
274
+ });
275
+
276
+ it('should handle render with damage tracking', () => {
277
+ expect(() => renderer.render()).not.toThrow();
278
+ });
279
+
280
+ it('should handle multiple invalidations', () => {
281
+ renderer.render();
282
+ renderer.render();
283
+ renderer.render();
284
+ expect(renderer).toBeTruthy();
285
+ });
286
+
287
+ it('should handle event listener cleanup', () => {
288
+ const removeSpy = vi.spyOn(mockCanvas, 'removeEventListener');
289
+ renderer.destroy();
290
+ expect(removeSpy).toHaveBeenCalled();
291
+ removeSpy.mockRestore();
292
+ });
293
+
294
+ it('should render checkbox for dedicated selection column', () => {
295
+ const mockColumn = {
296
+ colId: 'ag-Grid-SelectionColumn',
297
+ width: 50,
298
+ visible: true,
299
+ pinned: 'left',
300
+ } as any;
301
+
302
+ const mockRowNode = {
303
+ data: { id: 1, name: 'Test' },
304
+ selected: true,
305
+ rowIndex: 0,
306
+ displayedRowIndex: 0,
307
+ group: false,
308
+ master: false,
309
+ level: 0,
310
+ expanded: false,
311
+ detail: false,
312
+ } as any;
313
+
314
+ vi.spyOn(mockApi, 'getAllColumns').mockReturnValue([mockColumn]);
315
+ vi.spyOn(mockApi, 'getDisplayedRowAtIndex').mockReturnValue(mockRowNode);
316
+ vi.spyOn(mockApi, 'getDisplayedRowCount').mockReturnValue(1);
317
+
318
+ // Force a render
319
+ renderer.renderFrame();
320
+
321
+ // Verify strokeRect was called (for checkbox border)
322
+ expect(mockCanvasContext.strokeRect).toHaveBeenCalled();
323
+ });
324
+
325
+ it('should toggle row selection when clicking dedicated selection column', () => {
326
+ const mockColumn = {
327
+ colId: 'ag-Grid-SelectionColumn',
328
+ width: 50,
329
+ visible: true,
330
+ pinned: 'left',
331
+ } as any;
332
+
333
+ const mockRowNode = {
334
+ data: { id: 1, name: 'Test' },
335
+ selected: false,
336
+ rowIndex: 0,
337
+ displayedRowIndex: 0,
338
+ setSelected: vi.fn(function (this: any, val) {
339
+ this.selected = val;
340
+ }),
341
+ group: false,
342
+ master: false,
343
+ level: 0,
344
+ expanded: false,
345
+ detail: false,
346
+ } as any;
347
+
348
+ vi.spyOn(mockApi, 'getAllColumns').mockReturnValue([mockColumn]);
349
+ vi.spyOn(mockApi, 'getDisplayedRowAtIndex').mockReturnValue(mockRowNode);
350
+ vi.spyOn(mockApi, 'getDisplayedRowCount').mockReturnValue(1);
351
+
352
+ // Simulate click on checkbox area
353
+ const rect = mockCanvas.getBoundingClientRect();
354
+ const clickEvent = new MouseEvent('click', {
355
+ clientX: rect.left + 25, // Center of checkbox column
356
+ clientY: rect.top + 16, // Center of first row
357
+ bubbles: true,
358
+ });
359
+
360
+ mockCanvas.dispatchEvent(clickEvent);
361
+
362
+ // setSelected should have been called
363
+ expect(mockRowNode.setSelected).toHaveBeenCalledWith(true);
364
+ expect(mockRowNode.selected).toBe(true);
365
+ });
366
+ });