argent-grid 0.2.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.
- package/AGENTS.md +70 -27
- package/e2e/advanced.spec.ts +1 -1
- package/e2e/benchmark.spec.ts +7 -7
- package/e2e/cell-renderers.spec.ts +152 -0
- package/e2e/debug-streaming.spec.ts +31 -0
- package/e2e/dnd.spec.ts +73 -0
- package/e2e/screenshots.spec.ts +1 -1
- package/e2e/visual.spec.ts +30 -9
- package/e2e/visual.spec.ts-snapshots/checkbox-renderer-mixed.png +0 -0
- package/e2e/visual.spec.ts-snapshots/debug.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-column-group-headers.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-default.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-empty-state.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-filter-popup.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-scroll-borders.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-sidebar-buttons.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-text-filter.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-with-selection.png +0 -0
- package/e2e/visual.spec.ts-snapshots/rating-renderer-varied.png +0 -0
- package/package.json +5 -5
- package/plan.md +30 -34
- package/src/lib/components/argent-grid.component.css +258 -549
- package/src/lib/components/argent-grid.component.html +272 -306
- package/src/lib/components/argent-grid.component.ts +585 -135
- package/src/lib/components/argent-grid.regressions.spec.ts +301 -0
- package/src/lib/components/argent-grid.selection.spec.ts +2 -2
- package/src/lib/components/set-filter/set-filter.component.spec.ts +191 -0
- package/src/lib/components/set-filter/set-filter.component.ts +7 -2
- package/src/lib/rendering/canvas-renderer.spec.ts +148 -1
- package/src/lib/rendering/canvas-renderer.ts +177 -286
- package/src/lib/rendering/render/cells.ts +122 -5
- package/src/lib/rendering/render/column-utils.ts +27 -5
- package/src/lib/rendering/render/hit-test.ts +6 -11
- package/src/lib/rendering/render/index.ts +15 -6
- package/src/lib/rendering/render/lines.ts +12 -6
- package/src/lib/rendering/render/primitives.ts +269 -7
- package/src/lib/rendering/render/types.ts +2 -1
- package/src/lib/rendering/render/walk.ts +39 -19
- package/src/lib/services/grid.service.spec.ts +76 -0
- package/src/lib/services/grid.service.ts +451 -114
- package/src/lib/themes/theme-quartz.ts +2 -2
- package/src/lib/types/ag-grid-types.ts +500 -0
- package/src/stories/Advanced.stories.ts +78 -17
- package/src/stories/ArgentGrid.stories.ts +50 -26
- package/src/stories/Benchmark.stories.ts +17 -15
- package/src/stories/CellRenderers.stories.ts +205 -31
- package/src/stories/Filtering.stories.ts +56 -16
- package/src/stories/Grouping.stories.ts +86 -13
- package/src/stories/Streaming.stories.ts +57 -0
- package/src/stories/Theming.stories.ts +23 -10
- package/src/stories/Tooltips.stories.ts +381 -0
- package/src/stories/benchmark-wrapper.component.ts +69 -29
- package/src/stories/story-utils.ts +88 -0
- package/src/stories/streaming-wrapper.component.ts +441 -0
- package/tsconfig.json +1 -0
|
@@ -2,6 +2,14 @@ import { BrowserModule } from '@angular/platform-browser';
|
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/angular';
|
|
3
3
|
import { moduleMetadata } from '@storybook/angular';
|
|
4
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';
|
|
5
13
|
|
|
6
14
|
interface Employee {
|
|
7
15
|
id: number;
|
|
@@ -30,9 +38,9 @@ export default meta;
|
|
|
30
38
|
type Story = StoryObj<ArgentGridComponent<Employee>>;
|
|
31
39
|
|
|
32
40
|
function generateStaticData(count: number): Employee[] {
|
|
33
|
-
const departments =
|
|
34
|
-
const roles =
|
|
35
|
-
const locations =
|
|
41
|
+
const departments = STORY_DEPARTMENTS;
|
|
42
|
+
const roles = STORY_ROLES;
|
|
43
|
+
const locations = STORY_LOCATIONS;
|
|
36
44
|
|
|
37
45
|
return Array.from({ length: count }, (_, i) => ({
|
|
38
46
|
id: i + 1,
|
|
@@ -50,14 +58,32 @@ export const SideBar: Story = {
|
|
|
50
58
|
columnDefs: [
|
|
51
59
|
{ field: 'id', headerName: 'ID', width: 80, filter: true },
|
|
52
60
|
{ field: 'name', headerName: 'Name', width: 200, filter: true },
|
|
53
|
-
{
|
|
54
|
-
|
|
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
|
+
},
|
|
55
75
|
{ field: 'salary', headerName: 'Salary', width: 120, filter: 'number' },
|
|
56
|
-
{
|
|
76
|
+
{
|
|
77
|
+
field: 'location',
|
|
78
|
+
headerName: 'Location',
|
|
79
|
+
width: 150,
|
|
80
|
+
filter: 'set',
|
|
81
|
+
valueFormatter: locationValueFormatter,
|
|
82
|
+
},
|
|
57
83
|
{ field: 'performance', headerName: 'Performance', width: 120, filter: 'number' },
|
|
58
84
|
],
|
|
59
85
|
rowData: generateStaticData(50),
|
|
60
|
-
height: '
|
|
86
|
+
height: 'calc(100vh - 60px)',
|
|
61
87
|
width: '100%',
|
|
62
88
|
theme: themeQuartz,
|
|
63
89
|
gridOptions: {
|
|
@@ -96,12 +122,24 @@ export const SideBarDefault: Story = {
|
|
|
96
122
|
columnDefs: [
|
|
97
123
|
{ field: 'id', headerName: 'ID', width: 80, filter: true },
|
|
98
124
|
{ field: 'name', headerName: 'Name', width: 200, filter: true },
|
|
99
|
-
{
|
|
100
|
-
|
|
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
|
+
},
|
|
101
139
|
{ field: 'salary', headerName: 'Salary', width: 120, filter: 'number' },
|
|
102
140
|
],
|
|
103
141
|
rowData: generateStaticData(50),
|
|
104
|
-
height: '
|
|
142
|
+
height: 'calc(100vh - 60px)',
|
|
105
143
|
width: '100%',
|
|
106
144
|
theme: themeQuartz,
|
|
107
145
|
gridOptions: {
|
|
@@ -122,13 +160,23 @@ export const RangeSelection: Story = {
|
|
|
122
160
|
columnDefs: [
|
|
123
161
|
{ field: 'id', headerName: 'ID', width: 80 },
|
|
124
162
|
{ field: 'name', headerName: 'Name', width: 200 },
|
|
125
|
-
{
|
|
126
|
-
|
|
163
|
+
{
|
|
164
|
+
field: 'department',
|
|
165
|
+
headerName: 'Department',
|
|
166
|
+
width: 180,
|
|
167
|
+
valueFormatter: departmentValueFormatter,
|
|
168
|
+
},
|
|
169
|
+
{ field: 'role', headerName: 'Role', width: 250, valueFormatter: roleValueFormatter },
|
|
127
170
|
{ field: 'salary', headerName: 'Salary', width: 120 },
|
|
128
|
-
{
|
|
171
|
+
{
|
|
172
|
+
field: 'location',
|
|
173
|
+
headerName: 'Location',
|
|
174
|
+
width: 150,
|
|
175
|
+
valueFormatter: locationValueFormatter,
|
|
176
|
+
},
|
|
129
177
|
],
|
|
130
178
|
rowData: generateStaticData(50),
|
|
131
|
-
height: '
|
|
179
|
+
height: 'calc(100vh - 60px)',
|
|
132
180
|
width: '100%',
|
|
133
181
|
theme: themeQuartz,
|
|
134
182
|
gridOptions: {
|
|
@@ -157,13 +205,26 @@ export const FullFeatures: Story = {
|
|
|
157
205
|
filter: 'set',
|
|
158
206
|
sortable: true,
|
|
159
207
|
rowGroup: true,
|
|
208
|
+
valueFormatter: departmentValueFormatter,
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
field: 'role',
|
|
212
|
+
headerName: 'Role',
|
|
213
|
+
width: 250,
|
|
214
|
+
filter: true,
|
|
215
|
+
valueFormatter: roleValueFormatter,
|
|
160
216
|
},
|
|
161
|
-
{ field: 'role', headerName: 'Role', width: 250, filter: true },
|
|
162
217
|
{ field: 'salary', headerName: 'Salary', width: 120, filter: 'number', sortable: true },
|
|
163
|
-
{
|
|
218
|
+
{
|
|
219
|
+
field: 'location',
|
|
220
|
+
headerName: 'Location',
|
|
221
|
+
width: 150,
|
|
222
|
+
filter: 'set',
|
|
223
|
+
valueFormatter: locationValueFormatter,
|
|
224
|
+
},
|
|
164
225
|
],
|
|
165
226
|
rowData: generateStaticData(100),
|
|
166
|
-
height: '
|
|
227
|
+
height: 'calc(100vh - 60px)',
|
|
167
228
|
width: '100%',
|
|
168
229
|
theme: themeQuartz,
|
|
169
230
|
gridOptions: {
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/angular';
|
|
2
2
|
import { moduleMetadata } from '@storybook/angular';
|
|
3
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';
|
|
4
12
|
|
|
5
13
|
interface Employee {
|
|
6
14
|
id: number;
|
|
@@ -35,9 +43,9 @@ export default meta;
|
|
|
35
43
|
type Story = StoryObj<ArgentGridComponent<Employee>>;
|
|
36
44
|
|
|
37
45
|
function generateStaticData(count: number): Employee[] {
|
|
38
|
-
const departments =
|
|
39
|
-
const roles =
|
|
40
|
-
const locations =
|
|
46
|
+
const departments = STORY_DEPARTMENTS;
|
|
47
|
+
const roles = STORY_ROLES;
|
|
48
|
+
const locations = STORY_LOCATIONS;
|
|
41
49
|
|
|
42
50
|
return Array.from({ length: count }, (_, i) => ({
|
|
43
51
|
id: i + 1,
|
|
@@ -56,15 +64,25 @@ export const Default: Story = {
|
|
|
56
64
|
columnDefs: [
|
|
57
65
|
{ field: 'id', headerName: 'ID', width: 80 },
|
|
58
66
|
{ field: 'name', headerName: 'Name', width: 200 },
|
|
59
|
-
{
|
|
60
|
-
|
|
67
|
+
{
|
|
68
|
+
field: 'department',
|
|
69
|
+
headerName: 'Department',
|
|
70
|
+
width: 180,
|
|
71
|
+
valueFormatter: departmentValueFormatter,
|
|
72
|
+
},
|
|
73
|
+
{ field: 'role', headerName: 'Role', width: 250, valueFormatter: roleValueFormatter },
|
|
61
74
|
{ field: 'salary', headerName: 'Salary', width: 120 },
|
|
62
|
-
{
|
|
75
|
+
{
|
|
76
|
+
field: 'location',
|
|
77
|
+
headerName: 'Location',
|
|
78
|
+
width: 180,
|
|
79
|
+
valueFormatter: locationValueFormatter,
|
|
80
|
+
},
|
|
63
81
|
{ field: 'startDate', headerName: 'Start Date', width: 130 },
|
|
64
82
|
{ field: 'performance', headerName: 'Performance', width: 120 },
|
|
65
83
|
],
|
|
66
84
|
rowData: generateStaticData(100),
|
|
67
|
-
height: '
|
|
85
|
+
height: 'calc(100vh - 60px)',
|
|
68
86
|
width: '100%',
|
|
69
87
|
theme: themeQuartz,
|
|
70
88
|
},
|
|
@@ -82,12 +100,17 @@ export const LargeDataset: Story = {
|
|
|
82
100
|
columnDefs: [
|
|
83
101
|
{ field: 'id', headerName: 'ID', width: 80 },
|
|
84
102
|
{ field: 'name', headerName: 'Name', width: 200 },
|
|
85
|
-
{
|
|
86
|
-
|
|
103
|
+
{
|
|
104
|
+
field: 'department',
|
|
105
|
+
headerName: 'Department',
|
|
106
|
+
width: 180,
|
|
107
|
+
valueFormatter: departmentValueFormatter,
|
|
108
|
+
},
|
|
109
|
+
{ field: 'role', headerName: 'Role', width: 250, valueFormatter: roleValueFormatter },
|
|
87
110
|
{ field: 'salary', headerName: 'Salary', width: 120 },
|
|
88
111
|
],
|
|
89
112
|
rowData: generateStaticData(100000),
|
|
90
|
-
height: '
|
|
113
|
+
height: 'calc(100vh - 60px)',
|
|
91
114
|
width: '100%',
|
|
92
115
|
theme: themeQuartz,
|
|
93
116
|
},
|
|
@@ -106,7 +129,7 @@ export const WithSorting: Story = {
|
|
|
106
129
|
columnDefs: [
|
|
107
130
|
{
|
|
108
131
|
field: 'id',
|
|
109
|
-
headerName: 'ID
|
|
132
|
+
headerName: 'ID',
|
|
110
133
|
width: 80,
|
|
111
134
|
sortable: true,
|
|
112
135
|
headerComponentParams: { sortIcon: '↕️' },
|
|
@@ -134,7 +157,7 @@ export const WithSorting: Story = {
|
|
|
134
157
|
},
|
|
135
158
|
],
|
|
136
159
|
rowData: generateStaticData(50),
|
|
137
|
-
height: '
|
|
160
|
+
height: 'calc(100vh - 60px)',
|
|
138
161
|
width: '100%',
|
|
139
162
|
theme: themeQuartz,
|
|
140
163
|
},
|
|
@@ -151,20 +174,19 @@ export const WithSorting: Story = {
|
|
|
151
174
|
export const WithSelection: Story = {
|
|
152
175
|
args: {
|
|
153
176
|
columnDefs: [
|
|
177
|
+
{ field: 'id', headerName: 'ID', width: 80 },
|
|
178
|
+
{ field: 'name', headerName: 'Name', width: 200 },
|
|
154
179
|
{
|
|
155
|
-
field: '
|
|
156
|
-
headerName: '
|
|
157
|
-
width:
|
|
158
|
-
|
|
159
|
-
headerComponentParams: { selectionIcon: '☑️' },
|
|
180
|
+
field: 'department',
|
|
181
|
+
headerName: 'Department',
|
|
182
|
+
width: 180,
|
|
183
|
+
valueFormatter: departmentValueFormatter,
|
|
160
184
|
},
|
|
161
|
-
{ field: '
|
|
162
|
-
{ field: 'department', headerName: 'Department', width: 180 },
|
|
163
|
-
{ field: 'role', headerName: 'Role', width: 250 },
|
|
185
|
+
{ field: 'role', headerName: 'Role', width: 250, valueFormatter: roleValueFormatter },
|
|
164
186
|
],
|
|
165
187
|
rowData: generateStaticData(50),
|
|
166
188
|
rowSelection: 'multiple',
|
|
167
|
-
height: '
|
|
189
|
+
height: 'calc(100vh - 60px)',
|
|
168
190
|
width: '100%',
|
|
169
191
|
theme: themeQuartz,
|
|
170
192
|
},
|
|
@@ -172,7 +194,7 @@ export const WithSelection: Story = {
|
|
|
172
194
|
docs: {
|
|
173
195
|
description: {
|
|
174
196
|
story:
|
|
175
|
-
'**Row selection with checkboxes**.
|
|
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.',
|
|
176
198
|
},
|
|
177
199
|
},
|
|
178
200
|
},
|
|
@@ -183,7 +205,7 @@ export const WithFiltering: Story = {
|
|
|
183
205
|
columnDefs: [
|
|
184
206
|
{
|
|
185
207
|
field: 'id',
|
|
186
|
-
headerName: 'ID
|
|
208
|
+
headerName: 'ID',
|
|
187
209
|
width: 80,
|
|
188
210
|
filter: 'number',
|
|
189
211
|
floatingFilter: true,
|
|
@@ -204,6 +226,7 @@ export const WithFiltering: Story = {
|
|
|
204
226
|
filter: 'set',
|
|
205
227
|
floatingFilter: true,
|
|
206
228
|
headerComponentParams: { filterIcon: '☑️' },
|
|
229
|
+
valueFormatter: departmentValueFormatter,
|
|
207
230
|
},
|
|
208
231
|
{
|
|
209
232
|
field: 'role',
|
|
@@ -212,10 +235,11 @@ export const WithFiltering: Story = {
|
|
|
212
235
|
filter: 'text',
|
|
213
236
|
floatingFilter: true,
|
|
214
237
|
headerComponentParams: { filterIcon: '🔤' },
|
|
238
|
+
valueFormatter: roleValueFormatter,
|
|
215
239
|
},
|
|
216
240
|
],
|
|
217
241
|
rowData: generateStaticData(50),
|
|
218
|
-
height: '
|
|
242
|
+
height: 'calc(100vh - 60px)',
|
|
219
243
|
width: '100%',
|
|
220
244
|
theme: themeQuartz,
|
|
221
245
|
},
|
|
@@ -236,7 +260,7 @@ export const Empty: Story = {
|
|
|
236
260
|
{ field: 'name', headerName: 'Name', width: 200 },
|
|
237
261
|
],
|
|
238
262
|
rowData: [],
|
|
239
|
-
height: '
|
|
263
|
+
height: 'calc(100vh - 60px)',
|
|
240
264
|
width: '100%',
|
|
241
265
|
theme: themeQuartz,
|
|
242
266
|
},
|
|
@@ -258,7 +282,7 @@ export const WithCustomTheme: Story = {
|
|
|
258
282
|
{ field: 'salary', headerName: 'Salary', width: 120 },
|
|
259
283
|
],
|
|
260
284
|
rowData: generateStaticData(50),
|
|
261
|
-
height: '
|
|
285
|
+
height: 'calc(100vh - 60px)',
|
|
262
286
|
width: '100%',
|
|
263
287
|
theme: themeQuartz.withParams({
|
|
264
288
|
accentColor: '#ff5722', // Orange accent
|
|
@@ -18,56 +18,58 @@ const meta: Meta<BenchmarkWrapperComponent> = {
|
|
|
18
18
|
export default meta;
|
|
19
19
|
type Story = StoryObj<BenchmarkWrapperComponent>;
|
|
20
20
|
|
|
21
|
-
export const
|
|
22
|
-
args: {
|
|
23
|
-
// Default 10K rows
|
|
24
|
-
},
|
|
21
|
+
export const Benchmark100K: Story = {
|
|
22
|
+
args: {},
|
|
25
23
|
render: (args) => ({
|
|
26
|
-
props:
|
|
27
|
-
|
|
24
|
+
props: {
|
|
25
|
+
...args,
|
|
26
|
+
rowCount: 100000,
|
|
27
|
+
} as any,
|
|
28
|
+
template: `<app-benchmark-wrapper [rowCount]="100000" />`,
|
|
28
29
|
}),
|
|
29
30
|
parameters: {
|
|
30
31
|
docs: {
|
|
31
32
|
description: {
|
|
32
33
|
story:
|
|
33
|
-
'Benchmark with ~
|
|
34
|
+
'Benchmark with ~100,000 rows. Tests virtual scrolling performance with a large dataset.',
|
|
34
35
|
},
|
|
35
36
|
},
|
|
36
37
|
},
|
|
37
38
|
};
|
|
38
39
|
|
|
39
|
-
export const
|
|
40
|
+
export const Benchmark500K: Story = {
|
|
40
41
|
args: {},
|
|
41
42
|
render: (args) => ({
|
|
42
43
|
props: {
|
|
43
44
|
...args,
|
|
44
|
-
rowCount:
|
|
45
|
+
rowCount: 500000,
|
|
45
46
|
} as any,
|
|
46
|
-
template: `<app-benchmark-wrapper [rowCount]="
|
|
47
|
+
template: `<app-benchmark-wrapper [rowCount]="500000" />`,
|
|
47
48
|
}),
|
|
48
49
|
parameters: {
|
|
49
50
|
docs: {
|
|
50
51
|
description: {
|
|
51
|
-
story:
|
|
52
|
+
story:
|
|
53
|
+
'Benchmark with ~500,000 rows. Heavy stress test for virtual scrolling and canvas rendering.',
|
|
52
54
|
},
|
|
53
55
|
},
|
|
54
56
|
},
|
|
55
57
|
};
|
|
56
58
|
|
|
57
|
-
export const
|
|
59
|
+
export const Benchmark1M: Story = {
|
|
58
60
|
args: {},
|
|
59
61
|
render: (args) => ({
|
|
60
62
|
props: {
|
|
61
63
|
...args,
|
|
62
|
-
rowCount:
|
|
64
|
+
rowCount: 1000000,
|
|
63
65
|
} as any,
|
|
64
|
-
template: `<app-benchmark-wrapper [rowCount]="
|
|
66
|
+
template: `<app-benchmark-wrapper [rowCount]="1000000" />`,
|
|
65
67
|
}),
|
|
66
68
|
parameters: {
|
|
67
69
|
docs: {
|
|
68
70
|
description: {
|
|
69
71
|
story:
|
|
70
|
-
'Benchmark with ~
|
|
72
|
+
'Benchmark with ~1,000,000 rows. Extreme stress test. Validates canvas renderer at maximum scale.',
|
|
71
73
|
},
|
|
72
74
|
},
|
|
73
75
|
},
|