argent-grid 0.1.0 → 0.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.
Files changed (122) 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 +70 -27
  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/cell-renderers.spec.ts +152 -0
  28. package/e2e/debug-streaming.spec.ts +31 -0
  29. package/e2e/dnd.spec.ts +73 -0
  30. package/e2e/screenshots.spec.ts +52 -0
  31. package/e2e/theming.spec.ts +35 -0
  32. package/e2e/visual.spec.ts +112 -0
  33. package/e2e/visual.spec.ts-snapshots/checkbox-renderer-mixed.png +0 -0
  34. package/e2e/visual.spec.ts-snapshots/debug.png +0 -0
  35. package/e2e/visual.spec.ts-snapshots/grid-column-group-headers.png +0 -0
  36. package/e2e/visual.spec.ts-snapshots/grid-default.png +0 -0
  37. package/e2e/visual.spec.ts-snapshots/grid-empty-state.png +0 -0
  38. package/e2e/visual.spec.ts-snapshots/grid-filter-popup.png +0 -0
  39. package/e2e/visual.spec.ts-snapshots/grid-scroll-borders.png +0 -0
  40. package/e2e/visual.spec.ts-snapshots/grid-sidebar-buttons.png +0 -0
  41. package/e2e/visual.spec.ts-snapshots/grid-text-filter.png +0 -0
  42. package/e2e/visual.spec.ts-snapshots/grid-with-selection.png +0 -0
  43. package/e2e/visual.spec.ts-snapshots/rating-renderer-varied.png +0 -0
  44. package/package.json +21 -7
  45. package/plan.md +56 -28
  46. package/playwright.config.ts +38 -0
  47. package/setup-vitest.ts +10 -13
  48. package/src/lib/argent-grid.module.ts +10 -12
  49. package/src/lib/components/argent-grid.component.css +281 -321
  50. package/src/lib/components/argent-grid.component.html +295 -207
  51. package/src/lib/components/argent-grid.component.spec.ts +120 -160
  52. package/src/lib/components/argent-grid.component.ts +1193 -290
  53. package/src/lib/components/argent-grid.regressions.spec.ts +301 -0
  54. package/src/lib/components/argent-grid.selection.spec.ts +132 -0
  55. package/src/lib/components/set-filter/set-filter.component.spec.ts +191 -0
  56. package/src/lib/components/set-filter/set-filter.component.ts +307 -0
  57. package/src/lib/directives/ag-grid-compatibility.directive.ts +16 -26
  58. package/src/lib/directives/click-outside.directive.ts +19 -0
  59. package/src/lib/rendering/canvas-renderer.spec.ts +513 -0
  60. package/src/lib/rendering/canvas-renderer.ts +456 -452
  61. package/src/lib/rendering/live-data-handler.ts +110 -0
  62. package/src/lib/rendering/live-data-optimizations.ts +133 -0
  63. package/src/lib/rendering/render/blit.spec.ts +16 -27
  64. package/src/lib/rendering/render/blit.ts +48 -36
  65. package/src/lib/rendering/render/cells.spec.ts +132 -0
  66. package/src/lib/rendering/render/cells.ts +167 -28
  67. package/src/lib/rendering/render/column-utils.ts +95 -0
  68. package/src/lib/rendering/render/hit-test.ts +50 -0
  69. package/src/lib/rendering/render/index.ts +88 -76
  70. package/src/lib/rendering/render/lines.ts +53 -47
  71. package/src/lib/rendering/render/primitives.ts +423 -0
  72. package/src/lib/rendering/render/theme.spec.ts +8 -12
  73. package/src/lib/rendering/render/theme.ts +7 -10
  74. package/src/lib/rendering/render/types.ts +3 -2
  75. package/src/lib/rendering/render/walk.spec.ts +35 -38
  76. package/src/lib/rendering/render/walk.ts +94 -64
  77. package/src/lib/rendering/utils/damage-tracker.spec.ts +8 -7
  78. package/src/lib/rendering/utils/damage-tracker.ts +6 -18
  79. package/src/lib/rendering/utils/index.ts +1 -1
  80. package/src/lib/services/grid.service.set-filter.spec.ts +219 -0
  81. package/src/lib/services/grid.service.spec.ts +1241 -201
  82. package/src/lib/services/grid.service.ts +1204 -235
  83. package/src/lib/themes/parts/color-schemes.ts +132 -0
  84. package/src/lib/themes/parts/icon-sets.ts +258 -0
  85. package/src/lib/themes/theme-builder.ts +347 -0
  86. package/src/lib/themes/theme-quartz.ts +72 -0
  87. package/src/lib/themes/types.ts +238 -0
  88. package/src/lib/types/ag-grid-types.ts +573 -14
  89. package/src/public-api.ts +39 -9
  90. package/src/stories/Advanced.stories.ts +249 -0
  91. package/src/stories/ArgentGrid.stories.ts +301 -0
  92. package/src/stories/Benchmark.stories.ts +76 -0
  93. package/src/stories/CellRenderers.stories.ts +395 -0
  94. package/src/stories/Filtering.stories.ts +292 -0
  95. package/src/stories/Grouping.stories.ts +290 -0
  96. package/src/stories/Streaming.stories.ts +57 -0
  97. package/src/stories/Theming.stories.ts +137 -0
  98. package/src/stories/Tooltips.stories.ts +381 -0
  99. package/src/stories/benchmark-wrapper.component.ts +355 -0
  100. package/src/stories/story-utils.ts +88 -0
  101. package/src/stories/streaming-wrapper.component.ts +441 -0
  102. package/tsconfig.json +1 -0
  103. package/tsconfig.storybook.json +10 -0
  104. package/vitest.config.ts +9 -9
  105. package/demo-app/README.md +0 -70
  106. package/demo-app/angular.json +0 -78
  107. package/demo-app/e2e/benchmark.spec.ts +0 -53
  108. package/demo-app/e2e/demo-page.spec.ts +0 -77
  109. package/demo-app/e2e/grid-features.spec.ts +0 -269
  110. package/demo-app/package-lock.json +0 -14023
  111. package/demo-app/package.json +0 -36
  112. package/demo-app/playwright-test-menu.js +0 -19
  113. package/demo-app/playwright.config.ts +0 -23
  114. package/demo-app/src/app/app.component.ts +0 -10
  115. package/demo-app/src/app/app.config.ts +0 -13
  116. package/demo-app/src/app/app.routes.ts +0 -7
  117. package/demo-app/src/app/demo-page/demo-page.component.css +0 -313
  118. package/demo-app/src/app/demo-page/demo-page.component.html +0 -124
  119. package/demo-app/src/app/demo-page/demo-page.component.ts +0 -366
  120. package/demo-app/src/index.html +0 -19
  121. package/demo-app/src/main.ts +0 -6
  122. package/demo-app/tsconfig.json +0 -31
package/src/public-api.ts CHANGED
@@ -3,20 +3,50 @@
3
3
  * A free, high-performance alternative to AG Grid Enterprise
4
4
  */
5
5
 
6
- // Core types - AG Grid compatible
7
- export * from './lib/types/ag-grid-types';
6
+ // Modules
7
+ export { ArgentGridModule } from './lib/argent-grid.module';
8
8
 
9
9
  // Main grid component
10
10
  export { ArgentGridComponent } from './lib/components/argent-grid.component';
11
-
12
- // Canvas renderer
13
- export { CanvasRenderer } from './lib/rendering/canvas-renderer';
11
+ // Components
12
+ export { SetFilterComponent } from './lib/components/set-filter/set-filter.component';
14
13
 
15
14
  // Directives
16
15
  export { AgGridCompatibilityDirective } from './lib/directives/ag-grid-compatibility.directive';
17
-
16
+ export { ClickOutsideDirective } from './lib/directives/click-outside.directive';
17
+ // Canvas renderer
18
+ export { CanvasRenderer } from './lib/rendering/canvas-renderer';
19
+ // Live Data Optimizations
20
+ export { LiveDataOptimizations } from './lib/rendering/live-data-optimizations';
18
21
  // Services
19
22
  export { GridService } from './lib/services/grid.service';
20
-
21
- // Modules
22
- export { ArgentGridModule } from './lib/argent-grid.module';
23
+ export {
24
+ COLOR_SCHEMES,
25
+ colorSchemeAuto,
26
+ colorSchemeDark,
27
+ colorSchemeLight,
28
+ getColorScheme,
29
+ } from './lib/themes/parts/color-schemes';
30
+ export {
31
+ getIconSet,
32
+ getIconSvg,
33
+ ICON_SETS,
34
+ iconSetMaterial,
35
+ iconSetMinimal,
36
+ iconSetQuartz,
37
+ } from './lib/themes/parts/icon-sets';
38
+ export {
39
+ applyTheme,
40
+ applyThemeCSSVariables,
41
+ convertThemeToGridTheme,
42
+ createTheme,
43
+ extendTheme,
44
+ getThemeCSSVariables,
45
+ mergeThemes,
46
+ removeTheme,
47
+ } from './lib/themes/theme-builder';
48
+ export { themeQuartz } from './lib/themes/theme-quartz';
49
+ // Theme System - New Theming API (AG Grid v32.2+ compatible)
50
+ export * from './lib/themes/types';
51
+ // Core types - AG Grid compatible
52
+ export * from './lib/types/ag-grid-types';
@@ -0,0 +1,249 @@
1
+ import { BrowserModule } from '@angular/platform-browser';
2
+ import type { Meta, StoryObj } from '@storybook/angular';
3
+ import { moduleMetadata } from '@storybook/angular';
4
+ import { ArgentGridComponent, ArgentGridModule, themeQuartz } from '../public-api';
5
+ import {
6
+ departmentValueFormatter,
7
+ locationValueFormatter,
8
+ roleValueFormatter,
9
+ STORY_DEPARTMENTS,
10
+ STORY_LOCATIONS,
11
+ STORY_ROLES,
12
+ } from './story-utils';
13
+
14
+ interface Employee {
15
+ id: number;
16
+ name: string;
17
+ department: string;
18
+ role: string;
19
+ salary: number;
20
+ location: string;
21
+ performance: number;
22
+ }
23
+
24
+ const meta: Meta<ArgentGridComponent<Employee>> = {
25
+ title: 'Features/Advanced',
26
+ component: ArgentGridComponent,
27
+ decorators: [
28
+ moduleMetadata({
29
+ imports: [ArgentGridModule, BrowserModule],
30
+ }),
31
+ ],
32
+ parameters: {
33
+ layout: 'fullscreen',
34
+ },
35
+ };
36
+
37
+ export default meta;
38
+ type Story = StoryObj<ArgentGridComponent<Employee>>;
39
+
40
+ function generateStaticData(count: number): Employee[] {
41
+ const departments = STORY_DEPARTMENTS;
42
+ const roles = STORY_ROLES;
43
+ const locations = STORY_LOCATIONS;
44
+
45
+ return Array.from({ length: count }, (_, i) => ({
46
+ id: i + 1,
47
+ name: `Employee ${i + 1}`,
48
+ department: departments[i % departments.length],
49
+ role: roles[i % roles.length],
50
+ salary: 50000 + i * 1000,
51
+ location: locations[i % locations.length],
52
+ performance: 80,
53
+ }));
54
+ }
55
+
56
+ export const SideBar: Story = {
57
+ args: {
58
+ columnDefs: [
59
+ { field: 'id', headerName: 'ID', width: 80, filter: true },
60
+ { field: 'name', headerName: 'Name', width: 200, filter: true },
61
+ {
62
+ field: 'department',
63
+ headerName: 'Department',
64
+ width: 180,
65
+ filter: 'set',
66
+ valueFormatter: departmentValueFormatter,
67
+ },
68
+ {
69
+ field: 'role',
70
+ headerName: 'Role',
71
+ width: 250,
72
+ filter: true,
73
+ valueFormatter: roleValueFormatter,
74
+ },
75
+ { field: 'salary', headerName: 'Salary', width: 120, filter: 'number' },
76
+ {
77
+ field: 'location',
78
+ headerName: 'Location',
79
+ width: 150,
80
+ filter: 'set',
81
+ valueFormatter: locationValueFormatter,
82
+ },
83
+ { field: 'performance', headerName: 'Performance', width: 120, filter: 'number' },
84
+ ],
85
+ rowData: generateStaticData(50),
86
+ height: 'calc(100vh - 60px)',
87
+ width: '100%',
88
+ theme: themeQuartz,
89
+ gridOptions: {
90
+ sideBar: {
91
+ toolPanels: [
92
+ {
93
+ id: 'columns',
94
+ labelDefault: 'Columns',
95
+ labelKey: 'columns',
96
+ iconKey: 'columns',
97
+ toolPanel: 'agColumnsToolPanel',
98
+ },
99
+ {
100
+ id: 'filters',
101
+ labelDefault: 'Filters',
102
+ labelKey: 'filters',
103
+ iconKey: 'filter',
104
+ toolPanel: 'agFiltersToolPanel',
105
+ },
106
+ ],
107
+ },
108
+ },
109
+ },
110
+ parameters: {
111
+ docs: {
112
+ description: {
113
+ story:
114
+ 'Side bar with Columns and Filters panels. Click the sidebar toggle button in the grid toolbar to show/hide.',
115
+ },
116
+ },
117
+ },
118
+ };
119
+
120
+ export const SideBarDefault: Story = {
121
+ args: {
122
+ columnDefs: [
123
+ { field: 'id', headerName: 'ID', width: 80, filter: true },
124
+ { field: 'name', headerName: 'Name', width: 200, filter: true },
125
+ {
126
+ field: 'department',
127
+ headerName: 'Department',
128
+ width: 180,
129
+ filter: 'set',
130
+ valueFormatter: departmentValueFormatter,
131
+ },
132
+ {
133
+ field: 'role',
134
+ headerName: 'Role',
135
+ width: 250,
136
+ filter: true,
137
+ valueFormatter: roleValueFormatter,
138
+ },
139
+ { field: 'salary', headerName: 'Salary', width: 120, filter: 'number' },
140
+ ],
141
+ rowData: generateStaticData(50),
142
+ height: 'calc(100vh - 60px)',
143
+ width: '100%',
144
+ theme: themeQuartz,
145
+ gridOptions: {
146
+ sideBar: true,
147
+ },
148
+ },
149
+ parameters: {
150
+ docs: {
151
+ description: {
152
+ story: 'Side bar with default configuration (Columns and Filters tool panels).',
153
+ },
154
+ },
155
+ },
156
+ };
157
+
158
+ export const RangeSelection: Story = {
159
+ args: {
160
+ columnDefs: [
161
+ { field: 'id', headerName: 'ID', width: 80 },
162
+ { field: 'name', headerName: 'Name', width: 200 },
163
+ {
164
+ field: 'department',
165
+ headerName: 'Department',
166
+ width: 180,
167
+ valueFormatter: departmentValueFormatter,
168
+ },
169
+ { field: 'role', headerName: 'Role', width: 250, valueFormatter: roleValueFormatter },
170
+ { field: 'salary', headerName: 'Salary', width: 120 },
171
+ {
172
+ field: 'location',
173
+ headerName: 'Location',
174
+ width: 150,
175
+ valueFormatter: locationValueFormatter,
176
+ },
177
+ ],
178
+ rowData: generateStaticData(50),
179
+ height: 'calc(100vh - 60px)',
180
+ width: '100%',
181
+ theme: themeQuartz,
182
+ gridOptions: {
183
+ enableRangeSelection: true,
184
+ },
185
+ },
186
+ parameters: {
187
+ docs: {
188
+ description: {
189
+ story:
190
+ 'Enable range selection to select multiple cells by clicking and dragging. Use Ctrl+C to copy selected range.',
191
+ },
192
+ },
193
+ },
194
+ };
195
+
196
+ export const FullFeatures: Story = {
197
+ args: {
198
+ columnDefs: [
199
+ { field: 'id', headerName: 'ID', width: 80, filter: true, sortable: true },
200
+ { field: 'name', headerName: 'Name', width: 200, filter: true, sortable: true },
201
+ {
202
+ field: 'department',
203
+ headerName: 'Department',
204
+ width: 180,
205
+ filter: 'set',
206
+ sortable: true,
207
+ rowGroup: true,
208
+ valueFormatter: departmentValueFormatter,
209
+ },
210
+ {
211
+ field: 'role',
212
+ headerName: 'Role',
213
+ width: 250,
214
+ filter: true,
215
+ valueFormatter: roleValueFormatter,
216
+ },
217
+ { field: 'salary', headerName: 'Salary', width: 120, filter: 'number', sortable: true },
218
+ {
219
+ field: 'location',
220
+ headerName: 'Location',
221
+ width: 150,
222
+ filter: 'set',
223
+ valueFormatter: locationValueFormatter,
224
+ },
225
+ ],
226
+ rowData: generateStaticData(100),
227
+ height: 'calc(100vh - 60px)',
228
+ width: '100%',
229
+ theme: themeQuartz,
230
+ gridOptions: {
231
+ floatingFilter: true,
232
+ enableRangeSelection: true,
233
+ sideBar: true,
234
+ autoGroupColumnDef: {
235
+ headerName: 'Organization',
236
+ width: 250,
237
+ pinned: 'left',
238
+ },
239
+ },
240
+ },
241
+ parameters: {
242
+ docs: {
243
+ description: {
244
+ story:
245
+ 'Full-featured grid with sorting, filtering, floating filters, range selection, side bar, and row grouping.',
246
+ },
247
+ },
248
+ },
249
+ };
@@ -0,0 +1,301 @@
1
+ import type { Meta, StoryObj } from '@storybook/angular';
2
+ import { moduleMetadata } from '@storybook/angular';
3
+ import { ArgentGridComponent, ArgentGridModule, themeQuartz } from '../public-api';
4
+ import {
5
+ departmentValueFormatter,
6
+ locationValueFormatter,
7
+ roleValueFormatter,
8
+ STORY_DEPARTMENTS,
9
+ STORY_LOCATIONS,
10
+ STORY_ROLES,
11
+ } from './story-utils';
12
+
13
+ interface Employee {
14
+ id: number;
15
+ name: string;
16
+ department: string;
17
+ role: string;
18
+ salary: number;
19
+ location: string;
20
+ startDate: string;
21
+ performance: number;
22
+ }
23
+
24
+ const meta: Meta<ArgentGridComponent<Employee>> = {
25
+ title: 'Components/ArgentGrid',
26
+ component: ArgentGridComponent,
27
+ decorators: [
28
+ moduleMetadata({
29
+ imports: [ArgentGridModule],
30
+ }),
31
+ ],
32
+ parameters: {
33
+ layout: 'fullscreen',
34
+ },
35
+ argTypes: {
36
+ height: { control: 'text' },
37
+ width: { control: 'text' },
38
+ rowHeight: { control: 'number' },
39
+ },
40
+ };
41
+
42
+ export default meta;
43
+ type Story = StoryObj<ArgentGridComponent<Employee>>;
44
+
45
+ function generateStaticData(count: number): Employee[] {
46
+ const departments = STORY_DEPARTMENTS;
47
+ const roles = STORY_ROLES;
48
+ const locations = STORY_LOCATIONS;
49
+
50
+ return Array.from({ length: count }, (_, i) => ({
51
+ id: i + 1,
52
+ name: `Employee ${i + 1}`,
53
+ department: departments[i % departments.length],
54
+ role: roles[i % roles.length],
55
+ salary: 50000 + i * 1000,
56
+ location: locations[i % locations.length],
57
+ startDate: '2020-01-01',
58
+ performance: 80,
59
+ }));
60
+ }
61
+
62
+ export const Default: Story = {
63
+ args: {
64
+ columnDefs: [
65
+ { field: 'id', headerName: 'ID', width: 80 },
66
+ { field: 'name', headerName: 'Name', width: 200 },
67
+ {
68
+ field: 'department',
69
+ headerName: 'Department',
70
+ width: 180,
71
+ valueFormatter: departmentValueFormatter,
72
+ },
73
+ { field: 'role', headerName: 'Role', width: 250, valueFormatter: roleValueFormatter },
74
+ { field: 'salary', headerName: 'Salary', width: 120 },
75
+ {
76
+ field: 'location',
77
+ headerName: 'Location',
78
+ width: 180,
79
+ valueFormatter: locationValueFormatter,
80
+ },
81
+ { field: 'startDate', headerName: 'Start Date', width: 130 },
82
+ { field: 'performance', headerName: 'Performance', width: 120 },
83
+ ],
84
+ rowData: generateStaticData(100),
85
+ height: 'calc(100vh - 60px)',
86
+ width: '100%',
87
+ theme: themeQuartz,
88
+ },
89
+ parameters: {
90
+ docs: {
91
+ description: {
92
+ story: '**Basic grid** with 100 rows. Default theme (Quartz). No special features enabled.',
93
+ },
94
+ },
95
+ },
96
+ };
97
+
98
+ export const LargeDataset: Story = {
99
+ args: {
100
+ columnDefs: [
101
+ { field: 'id', headerName: 'ID', width: 80 },
102
+ { field: 'name', headerName: 'Name', width: 200 },
103
+ {
104
+ field: 'department',
105
+ headerName: 'Department',
106
+ width: 180,
107
+ valueFormatter: departmentValueFormatter,
108
+ },
109
+ { field: 'role', headerName: 'Role', width: 250, valueFormatter: roleValueFormatter },
110
+ { field: 'salary', headerName: 'Salary', width: 120 },
111
+ ],
112
+ rowData: generateStaticData(100000),
113
+ height: 'calc(100vh - 60px)',
114
+ width: '100%',
115
+ theme: themeQuartz,
116
+ },
117
+ parameters: {
118
+ docs: {
119
+ description: {
120
+ story:
121
+ '**Performance demo with 100K rows**. Scroll smoothly at 60fps thanks to canvas rendering and virtual scrolling. Try scrolling to the bottom!',
122
+ },
123
+ },
124
+ },
125
+ };
126
+
127
+ export const WithSorting: Story = {
128
+ args: {
129
+ columnDefs: [
130
+ {
131
+ field: 'id',
132
+ headerName: 'ID',
133
+ width: 80,
134
+ sortable: true,
135
+ headerComponentParams: { sortIcon: '↕️' },
136
+ },
137
+ {
138
+ field: 'name',
139
+ headerName: 'Name ↕️',
140
+ width: 200,
141
+ sortable: true,
142
+ headerComponentParams: { sortIcon: '↕️' },
143
+ },
144
+ {
145
+ field: 'department',
146
+ headerName: 'Department ↕️',
147
+ width: 180,
148
+ sortable: true,
149
+ headerComponentParams: { sortIcon: '↕️' },
150
+ },
151
+ {
152
+ field: 'salary',
153
+ headerName: 'Salary ↕️',
154
+ width: 120,
155
+ sortable: true,
156
+ headerComponentParams: { sortIcon: '↕️' },
157
+ },
158
+ ],
159
+ rowData: generateStaticData(50),
160
+ height: 'calc(100vh - 60px)',
161
+ width: '100%',
162
+ theme: themeQuartz,
163
+ },
164
+ parameters: {
165
+ docs: {
166
+ description: {
167
+ story:
168
+ '**Sortable columns** with ↕️ indicators. **Click column headers** to sort ascending/descending. Look for the **▲/▼ arrows** that appear when sorted.',
169
+ },
170
+ },
171
+ },
172
+ };
173
+
174
+ export const WithSelection: Story = {
175
+ args: {
176
+ columnDefs: [
177
+ { field: 'id', headerName: 'ID', width: 80 },
178
+ { field: 'name', headerName: 'Name', width: 200 },
179
+ {
180
+ field: 'department',
181
+ headerName: 'Department',
182
+ width: 180,
183
+ valueFormatter: departmentValueFormatter,
184
+ },
185
+ { field: 'role', headerName: 'Role', width: 250, valueFormatter: roleValueFormatter },
186
+ ],
187
+ rowData: generateStaticData(50),
188
+ rowSelection: 'multiple',
189
+ height: 'calc(100vh - 60px)',
190
+ width: '100%',
191
+ theme: themeQuartz,
192
+ },
193
+ parameters: {
194
+ docs: {
195
+ description: {
196
+ story:
197
+ '**Row selection with checkboxes**. Enabling `rowSelection` automatically adds a dedicated checkbox selection column. **Click checkboxes** to select/deselect rows. **Header checkbox** selects/deselects all visible rows.',
198
+ },
199
+ },
200
+ },
201
+ };
202
+
203
+ export const WithFiltering: Story = {
204
+ args: {
205
+ columnDefs: [
206
+ {
207
+ field: 'id',
208
+ headerName: 'ID',
209
+ width: 80,
210
+ filter: 'number',
211
+ floatingFilter: true,
212
+ headerComponentParams: { filterIcon: '🔢' },
213
+ },
214
+ {
215
+ field: 'name',
216
+ headerName: 'Name 🔤',
217
+ width: 200,
218
+ filter: 'text',
219
+ floatingFilter: true,
220
+ headerComponentParams: { filterIcon: '🔤' },
221
+ },
222
+ {
223
+ field: 'department',
224
+ headerName: 'Department ☑️',
225
+ width: 180,
226
+ filter: 'set',
227
+ floatingFilter: true,
228
+ headerComponentParams: { filterIcon: '☑️' },
229
+ valueFormatter: departmentValueFormatter,
230
+ },
231
+ {
232
+ field: 'role',
233
+ headerName: 'Role 🔤',
234
+ width: 250,
235
+ filter: 'text',
236
+ floatingFilter: true,
237
+ headerComponentParams: { filterIcon: '🔤' },
238
+ valueFormatter: roleValueFormatter,
239
+ },
240
+ ],
241
+ rowData: generateStaticData(50),
242
+ height: 'calc(100vh - 60px)',
243
+ width: '100%',
244
+ theme: themeQuartz,
245
+ },
246
+ parameters: {
247
+ docs: {
248
+ description: {
249
+ story:
250
+ '**Filtering with visible filter inputs**. Each filterable column shows an icon (🔢 Number, 🔤 Text, ☑️ Set). **Filter inputs are visible in the header row** - type to filter. Department uses a set filter (dropdown with checkboxes).',
251
+ },
252
+ },
253
+ },
254
+ };
255
+
256
+ export const Empty: Story = {
257
+ args: {
258
+ columnDefs: [
259
+ { field: 'id', headerName: 'ID', width: 80 },
260
+ { field: 'name', headerName: 'Name', width: 200 },
261
+ ],
262
+ rowData: [],
263
+ height: 'calc(100vh - 60px)',
264
+ width: '100%',
265
+ theme: themeQuartz,
266
+ },
267
+ parameters: {
268
+ docs: {
269
+ description: {
270
+ story: '**Empty grid** with no rows. Shows overlay message "No rows to show".',
271
+ },
272
+ },
273
+ },
274
+ };
275
+
276
+ export const WithCustomTheme: Story = {
277
+ args: {
278
+ columnDefs: [
279
+ { field: 'id', headerName: 'ID', width: 80 },
280
+ { field: 'name', headerName: 'Name', width: 200 },
281
+ { field: 'department', headerName: 'Department', width: 180 },
282
+ { field: 'salary', headerName: 'Salary', width: 120 },
283
+ ],
284
+ rowData: generateStaticData(50),
285
+ height: 'calc(100vh - 60px)',
286
+ width: '100%',
287
+ theme: themeQuartz.withParams({
288
+ accentColor: '#ff5722', // Orange accent
289
+ rowHeight: 48,
290
+ fontSize: 14,
291
+ }),
292
+ },
293
+ parameters: {
294
+ docs: {
295
+ description: {
296
+ story:
297
+ '**Custom theme** with orange accent color, larger row height (48px), and larger font (14px). See Theming stories for more theme options.',
298
+ },
299
+ },
300
+ },
301
+ };
@@ -0,0 +1,76 @@
1
+ import type { Meta, StoryObj } from '@storybook/angular';
2
+ import { moduleMetadata } from '@storybook/angular';
3
+ import { BenchmarkWrapperComponent } from './benchmark-wrapper.component';
4
+
5
+ const meta: Meta<BenchmarkWrapperComponent> = {
6
+ title: 'Features/Benchmark',
7
+ component: BenchmarkWrapperComponent,
8
+ decorators: [
9
+ moduleMetadata({
10
+ imports: [BenchmarkWrapperComponent],
11
+ }),
12
+ ],
13
+ parameters: {
14
+ layout: 'fullscreen',
15
+ },
16
+ };
17
+
18
+ export default meta;
19
+ type Story = StoryObj<BenchmarkWrapperComponent>;
20
+
21
+ export const Benchmark100K: Story = {
22
+ args: {},
23
+ render: (args) => ({
24
+ props: {
25
+ ...args,
26
+ rowCount: 100000,
27
+ } as any,
28
+ template: `<app-benchmark-wrapper [rowCount]="100000" />`,
29
+ }),
30
+ parameters: {
31
+ docs: {
32
+ description: {
33
+ story:
34
+ 'Benchmark with ~100,000 rows. Tests virtual scrolling performance with a large dataset.',
35
+ },
36
+ },
37
+ },
38
+ };
39
+
40
+ export const Benchmark500K: Story = {
41
+ args: {},
42
+ render: (args) => ({
43
+ props: {
44
+ ...args,
45
+ rowCount: 500000,
46
+ } as any,
47
+ template: `<app-benchmark-wrapper [rowCount]="500000" />`,
48
+ }),
49
+ parameters: {
50
+ docs: {
51
+ description: {
52
+ story:
53
+ 'Benchmark with ~500,000 rows. Heavy stress test for virtual scrolling and canvas rendering.',
54
+ },
55
+ },
56
+ },
57
+ };
58
+
59
+ export const Benchmark1M: Story = {
60
+ args: {},
61
+ render: (args) => ({
62
+ props: {
63
+ ...args,
64
+ rowCount: 1000000,
65
+ } as any,
66
+ template: `<app-benchmark-wrapper [rowCount]="1000000" />`,
67
+ }),
68
+ parameters: {
69
+ docs: {
70
+ description: {
71
+ story:
72
+ 'Benchmark with ~1,000,000 rows. Extreme stress test. Validates canvas renderer at maximum scale.',
73
+ },
74
+ },
75
+ },
76
+ };