@redsift/ds-mcp-server 12.4.0 → 12.5.0-muiv7
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/data/demos/patterns/_shared/StateDebugPanel.tsx +153 -0
- package/data/demos/patterns/_shared/columns.tsx +12 -3
- package/data/demos/patterns/_shared/defaults.ts +1 -1
- package/data/demos/patterns/_shared/filter-helpers.ts +1 -1
- package/data/demos/patterns/_shared/helpers.tsx +1 -1
- package/data/demos/patterns/_shared/server-logic.ts +1 -1
- package/data/demos/patterns/_shared/story-helpers.ts +578 -14
- package/data/demos/patterns/_shared/use-router-adapter.ts +51 -0
- package/data/demos/patterns/crossfiltered-datagrid-client-side/CrossfilteredDatagridClientSide.interaction.stories.tsx +2 -2
- package/data/demos/patterns/crossfiltered-datagrid-client-side/example.tsx +2 -2
- package/data/demos/patterns/crossfiltered-datagrid-server-side/CrossfilteredDatagridServerSide.interaction.stories.tsx +2 -2
- package/data/demos/patterns/crossfiltered-datagrid-server-side/example.tsx +6 -6
- package/data/demos/patterns/drilldowned-datagrid-client-side/DrilldownedDatagridClientSide.interaction.stories.tsx +2 -2
- package/data/demos/patterns/drilldowned-datagrid-client-side/example.tsx +1 -1
- package/data/demos/patterns/drilldowned-datagrid-server-side/DrilldownedDatagridServerSide.interaction.stories.tsx +2 -2
- package/data/demos/patterns/drilldowned-datagrid-server-side/example.tsx +1 -1
- package/data/demos/patterns/single-datagrid-client-side/SingleDatagridClientSide.interaction.stories.tsx +382 -169
- package/data/demos/patterns/single-datagrid-client-side/example.tsx +6 -6
- package/data/demos/patterns/single-datagrid-client-side/with-empty-state.tsx +1 -1
- package/data/demos/patterns/single-datagrid-client-side/with-error.tsx +1 -1
- package/data/demos/patterns/single-datagrid-client-side/with-loading.tsx +1 -1
- package/data/demos/patterns/single-datagrid-server-side/SingleDatagridServerSide.interaction.stories.tsx +426 -54
- package/data/demos/patterns/single-datagrid-server-side/example.tsx +6 -5
- package/data/demos/patterns/stateful-single-datagrid-client-side/StatefulSingleDatagridClientSide.interaction.stories.tsx +671 -0
- package/data/demos/patterns/stateful-single-datagrid-client-side/example.tsx +55 -0
- package/data/demos/patterns/stateful-single-datagrid-client-side/with-empty-state.tsx +27 -0
- package/data/demos/patterns/stateful-single-datagrid-client-side/with-error.tsx +39 -0
- package/data/demos/patterns/stateful-single-datagrid-client-side/with-loading.tsx +25 -0
- package/data/demos/patterns/stateful-single-datagrid-server-side/StatefulSingleDatagridServerSide.interaction.stories.tsx +692 -0
- package/data/demos/patterns/stateful-single-datagrid-server-side/example.tsx +108 -0
- package/data/demos/patterns/stateful-single-datagrid-server-side/with-empty-state.tsx +31 -0
- package/data/demos/patterns/stateful-single-datagrid-server-side/with-error.tsx +43 -0
- package/data/demos/patterns/stateful-single-datagrid-server-side/with-loading.tsx +29 -0
- package/data/demos/patterns/summary-dashboard/SummaryDashboard.interaction.stories.tsx +44 -24
- package/data/demos/patterns/summary-dashboard/example.tsx +66 -28
- package/data/demos/patterns/summary-dashboard/with-loading.tsx +12 -3
- package/data/demos/patterns/tabbed-datagrid-client-side/TabbedDatagridClientSide.interaction.stories.tsx +2 -2
- package/data/demos/patterns/tabbed-datagrid-server-side/TabbedDatagridServerSide.interaction.stories.tsx +2 -2
- package/data/demos/patterns/tabbed-datagrid-server-side/example.tsx +1 -1
- package/data/docs/components/dashboard/Dashboard.json +2 -2
- package/data/docs/components/table/DataGrid.json +30 -3
- package/data/docs/components/table/StatefulDataGrid.json +30 -3
- package/data/docs/components-index.json +2 -2
- package/data/docs/components.json +64 -10
- package/data/docs/llms-full.txt +799 -44
- package/data/docs/llms.txt +20 -6
- package/data/docs/patterns-catalog.md +25 -24
- package/data/docs/patterns.json +82 -8
- package/data/metadata.json +2 -2
- package/data/patterns/crossfiltered-datagrid-server-side.mdx +1 -1
- package/data/patterns/drilldowned-datagrid-client-side.mdx +1 -1
- package/data/patterns/drilldowned-datagrid-server-side.mdx +1 -1
- package/data/patterns/single-datagrid-client-side.mdx +9 -9
- package/data/patterns/single-datagrid-server-side.mdx +6 -6
- package/data/patterns/stateful-single-datagrid-client-side.mdx +304 -0
- package/data/patterns/stateful-single-datagrid-server-side.mdx +347 -0
- package/data/patterns/summary-dashboard.mdx +47 -7
- package/data/patterns/tabbed-datagrid-client-side.mdx +72 -2
- package/data/patterns/tabbed-datagrid-server-side.mdx +105 -2
- package/package.json +2 -2
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
3
|
import { within, userEvent } from '@storybook/testing-library';
|
|
4
4
|
import { expect } from '@storybook/jest';
|
|
5
|
-
import { GridFilterModel } from '@mui/x-data-grid-
|
|
5
|
+
import { GridFilterModel } from '@mui/x-data-grid-pro';
|
|
6
6
|
|
|
7
7
|
import Example from './example';
|
|
8
8
|
import WithLoadingExample from './with-loading';
|
|
@@ -16,7 +16,23 @@ import {
|
|
|
16
16
|
assertBulkActionBarVisible,
|
|
17
17
|
assertBulkActionBarHidden,
|
|
18
18
|
clickRowCheckbox,
|
|
19
|
+
clickHeaderCheckbox,
|
|
19
20
|
getRetryButton,
|
|
21
|
+
assertDisplayedRowsText,
|
|
22
|
+
clickNextPage,
|
|
23
|
+
clickPrevPage,
|
|
24
|
+
changePageSize,
|
|
25
|
+
openColumnsPanel,
|
|
26
|
+
toggleColumnInPanel,
|
|
27
|
+
closeColumnsPanel,
|
|
28
|
+
assertColumnVisible,
|
|
29
|
+
assertColumnHidden,
|
|
30
|
+
changeDensity,
|
|
31
|
+
assertDensity,
|
|
32
|
+
clickColumnHeader,
|
|
33
|
+
assertColumnSorted,
|
|
34
|
+
assertHeaderCheckboxState,
|
|
35
|
+
assertRowCheckboxChecked,
|
|
20
36
|
} from '../_shared/story-helpers';
|
|
21
37
|
import {
|
|
22
38
|
DEFAULT_FILTER_MODEL_COUNT,
|
|
@@ -35,25 +51,106 @@ import {
|
|
|
35
51
|
TAGS_HASANYOF_LOCAL_NEW,
|
|
36
52
|
} from '../_shared/expected-values';
|
|
37
53
|
|
|
38
|
-
const meta: Meta = {
|
|
54
|
+
const meta: Meta<typeof Example> = {
|
|
39
55
|
title: 'Patterns/Single Datagrid (Client)',
|
|
40
|
-
component: Example
|
|
56
|
+
component: Example,
|
|
41
57
|
};
|
|
42
58
|
export default meta;
|
|
43
59
|
type Story = StoryObj;
|
|
44
60
|
|
|
45
61
|
// ---------------------------------------------------------------------------
|
|
46
|
-
//
|
|
62
|
+
// Helper — creates a filter story
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
const makeStory = (filterModel: GridFilterModel, expectedCount: number): Story => ({
|
|
66
|
+
render: () => <Example initialFilterModel={filterModel} />,
|
|
67
|
+
play: async ({ canvasElement, step }) => {
|
|
68
|
+
const canvas = within(canvasElement);
|
|
69
|
+
await step('Wait for grid to load', async () => {
|
|
70
|
+
await waitForGridToLoad(canvas);
|
|
71
|
+
});
|
|
72
|
+
await step(`Assert filtered row count is ${expectedCount}`, async () => {
|
|
73
|
+
await assertRowCount(canvasElement, expectedCount);
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Items (string)
|
|
79
|
+
export const FilterItemsContainsBread: Story = makeStory(
|
|
80
|
+
{ items: [{ field: 'Items', operator: 'contains', value: 'Bread' }] },
|
|
81
|
+
ITEMS_CONTAINS_BREAD
|
|
82
|
+
);
|
|
83
|
+
export const FilterItemsEqualsCoffee: Story = makeStory(
|
|
84
|
+
{ items: [{ field: 'Items', operator: 'equals', value: 'Coffee' }] },
|
|
85
|
+
ITEMS_EQUALS_COFFEE
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// Category (singleSelect)
|
|
89
|
+
export const FilterCategoryIsBakery: Story = makeStory(
|
|
90
|
+
{ items: [{ field: 'Category', operator: 'is', value: 'Bakery' }] },
|
|
91
|
+
CATEGORY_IS_BAKERY
|
|
92
|
+
);
|
|
93
|
+
export const FilterCategoryIsAnyOfBakeryPastry: Story = makeStory(
|
|
94
|
+
{ items: [{ field: 'Category', operator: 'isAnyOf', value: ['Bakery', 'Pastry'] }] },
|
|
95
|
+
CATEGORY_ISANYOF_BAKERY_PASTRY
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// Paid (number)
|
|
99
|
+
export const FilterPaidGreaterThan3: Story = makeStory(
|
|
100
|
+
{ items: [{ field: 'Paid', operator: '>', value: 3 }] },
|
|
101
|
+
PAID_GT_3
|
|
102
|
+
);
|
|
103
|
+
export const FilterPaidLessThanOrEqual1_5: Story = makeStory(
|
|
104
|
+
{ items: [{ field: 'Paid', operator: '<=', value: 1.5 }] },
|
|
105
|
+
PAID_LTE_1_5
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
// InStock (boolean)
|
|
109
|
+
export const FilterInStockTrue: Story = makeStory(
|
|
110
|
+
{ items: [{ field: 'InStock', operator: 'is', value: 'true' }] },
|
|
111
|
+
INSTOCK_TRUE
|
|
112
|
+
);
|
|
113
|
+
export const FilterInStockFalse: Story = makeStory(
|
|
114
|
+
{ items: [{ field: 'InStock', operator: 'is', value: 'false' }] },
|
|
115
|
+
INSTOCK_FALSE
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// Allergens (multiSelect)
|
|
119
|
+
export const FilterAllergensHasAnyOfGluten: Story = makeStory(
|
|
120
|
+
{ items: [{ field: 'Allergens', operator: 'hasAnyOf', value: ['Gluten'] }] },
|
|
121
|
+
ALLERGENS_HASANYOF_GLUTEN
|
|
122
|
+
);
|
|
123
|
+
export const FilterAllergensHasAllOfGlutenDairy: Story = makeStory(
|
|
124
|
+
{ items: [{ field: 'Allergens', operator: 'hasAllOf', value: ['Gluten', 'Dairy'] }] },
|
|
125
|
+
ALLERGENS_HASALLOF_GLUTEN_DAIRY
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Tags (tags)
|
|
129
|
+
export const FilterTagsHasLocal: Story = makeStory(
|
|
130
|
+
{ items: [{ field: 'Tags', operator: 'has', value: 'Local' }] },
|
|
131
|
+
TAGS_HAS_LOCAL
|
|
132
|
+
);
|
|
133
|
+
export const FilterTagsHasAnyOfLocalNew: Story = makeStory(
|
|
134
|
+
{ items: [{ field: 'Tags', operator: 'hasAnyOf', value: ['Local', 'New'] }] },
|
|
135
|
+
TAGS_HASANYOF_LOCAL_NEW
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
// Default — renders with DEFAULT_FILTER_MODEL
|
|
47
140
|
// ---------------------------------------------------------------------------
|
|
48
141
|
|
|
49
142
|
export const Default: Story = {
|
|
50
143
|
render: () => <Example />,
|
|
51
|
-
play: async ({ canvasElement }) => {
|
|
144
|
+
play: async ({ canvasElement, step }) => {
|
|
52
145
|
const canvas = within(canvasElement);
|
|
53
|
-
await waitForGridToLoad(canvas);
|
|
54
146
|
|
|
55
|
-
|
|
56
|
-
|
|
147
|
+
await step('Wait for grid to load', async () => {
|
|
148
|
+
await waitForGridToLoad(canvas);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
await step('Assert default filter produces correct row count', async () => {
|
|
152
|
+
await assertRowCount(canvasElement, DEFAULT_FILTER_MODEL_COUNT);
|
|
153
|
+
});
|
|
57
154
|
},
|
|
58
155
|
};
|
|
59
156
|
|
|
@@ -63,221 +160,329 @@ export const Default: Story = {
|
|
|
63
160
|
|
|
64
161
|
export const NoFilter: Story = {
|
|
65
162
|
render: () => <Example initialFilterModel={{ items: [] }} />,
|
|
66
|
-
play: async ({ canvasElement }) => {
|
|
163
|
+
play: async ({ canvasElement, step }) => {
|
|
67
164
|
const canvas = within(canvasElement);
|
|
68
|
-
|
|
69
|
-
await
|
|
165
|
+
|
|
166
|
+
await step('Wait for grid to load', async () => {
|
|
167
|
+
await waitForGridToLoad(canvas);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
await step('Assert all rows visible (no filter)', async () => {
|
|
171
|
+
await assertRowCount(canvasElement, TOTAL);
|
|
172
|
+
});
|
|
70
173
|
},
|
|
71
174
|
};
|
|
72
175
|
|
|
73
176
|
// ---------------------------------------------------------------------------
|
|
74
|
-
//
|
|
177
|
+
// Checkbox selection — bulk action bar appears/disappears
|
|
75
178
|
// ---------------------------------------------------------------------------
|
|
76
179
|
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export const FilterItemsContainsBread: Story = {
|
|
82
|
-
render: () => <Example initialFilterModel={itemsContainsBread} />,
|
|
83
|
-
play: async ({ canvasElement }) => {
|
|
180
|
+
export const CheckboxSelectionBulkActions: Story = {
|
|
181
|
+
render: () => <Example initialFilterModel={{ items: [] }} />,
|
|
182
|
+
play: async ({ canvasElement, step }) => {
|
|
84
183
|
const canvas = within(canvasElement);
|
|
85
|
-
await waitForGridToLoad(canvas);
|
|
86
|
-
await assertRowCount(canvasElement, ITEMS_CONTAINS_BREAD);
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const itemsEqualsCoffee: GridFilterModel = {
|
|
91
|
-
items: [{ field: 'Items', operator: 'equals', value: 'Coffee' }],
|
|
92
|
-
};
|
|
93
184
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
await
|
|
99
|
-
|
|
185
|
+
await step('Wait for grid to load', async () => {
|
|
186
|
+
await waitForGridToLoad(canvas);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
await step('Verify no bulk action bar initially', async () => {
|
|
190
|
+
await assertBulkActionBarHidden(canvasElement);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
await step('Select first row — bulk bar shows 1 selected', async () => {
|
|
194
|
+
await clickRowCheckbox(canvasElement, 0);
|
|
195
|
+
await assertBulkActionBarVisible(canvasElement, 1);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
await step('Select second row — bulk bar shows 2 selected', async () => {
|
|
199
|
+
await clickRowCheckbox(canvasElement, 1);
|
|
200
|
+
await assertBulkActionBarVisible(canvasElement, 2);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
await step('Deselect first row — bulk bar shows 1 selected', async () => {
|
|
204
|
+
await clickRowCheckbox(canvasElement, 0);
|
|
205
|
+
await assertBulkActionBarVisible(canvasElement, 1);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
await step('Deselect last row — bulk bar disappears', async () => {
|
|
209
|
+
await clickRowCheckbox(canvasElement, 1);
|
|
210
|
+
await assertBulkActionBarHidden(canvasElement);
|
|
211
|
+
});
|
|
100
212
|
},
|
|
101
213
|
};
|
|
102
214
|
|
|
103
215
|
// ---------------------------------------------------------------------------
|
|
104
|
-
//
|
|
216
|
+
// Pagination — next/prev and page size
|
|
105
217
|
// ---------------------------------------------------------------------------
|
|
106
218
|
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export const FilterCategoryIsBakery: Story = {
|
|
112
|
-
render: () => <Example initialFilterModel={categoryIsBakery} />,
|
|
113
|
-
play: async ({ canvasElement }) => {
|
|
219
|
+
export const PaginationNextPrev: Story = {
|
|
220
|
+
render: () => <Example initialFilterModel={{ items: [] }} />,
|
|
221
|
+
play: async ({ canvasElement, step }) => {
|
|
114
222
|
const canvas = within(canvasElement);
|
|
115
|
-
await waitForGridToLoad(canvas);
|
|
116
|
-
await assertRowCount(canvasElement, CATEGORY_IS_BAKERY);
|
|
117
|
-
},
|
|
118
|
-
};
|
|
119
223
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
};
|
|
224
|
+
await step('Wait for grid to load', async () => {
|
|
225
|
+
await waitForGridToLoad(canvas);
|
|
226
|
+
});
|
|
123
227
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const canvas = within(canvasElement);
|
|
128
|
-
await waitForGridToLoad(canvas);
|
|
129
|
-
await assertRowCount(canvasElement, CATEGORY_ISANYOF_BAKERY_PASTRY);
|
|
130
|
-
},
|
|
131
|
-
};
|
|
228
|
+
await step('Verify initial page shows 1–10 of 14,075', async () => {
|
|
229
|
+
await assertDisplayedRowsText(canvasElement, '1–10 of 14,075');
|
|
230
|
+
});
|
|
132
231
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
232
|
+
await step('Navigate to next page — shows 11–20', async () => {
|
|
233
|
+
await clickNextPage(canvasElement);
|
|
234
|
+
await assertDisplayedRowsText(canvasElement, '11–20 of 14,075');
|
|
235
|
+
});
|
|
136
236
|
|
|
137
|
-
|
|
138
|
-
|
|
237
|
+
await step('Navigate back to first page — shows 1–10', async () => {
|
|
238
|
+
await clickPrevPage(canvasElement);
|
|
239
|
+
await assertDisplayedRowsText(canvasElement, '1–10 of 14,075');
|
|
240
|
+
});
|
|
241
|
+
},
|
|
139
242
|
};
|
|
140
243
|
|
|
141
|
-
export const
|
|
142
|
-
render: () => <Example initialFilterModel={
|
|
143
|
-
play: async ({ canvasElement }) => {
|
|
244
|
+
export const PaginationPageSizeChange: Story = {
|
|
245
|
+
render: () => <Example initialFilterModel={{ items: [] }} />,
|
|
246
|
+
play: async ({ canvasElement, step }) => {
|
|
144
247
|
const canvas = within(canvasElement);
|
|
145
|
-
await waitForGridToLoad(canvas);
|
|
146
|
-
await assertRowCount(canvasElement, PAID_GT_3);
|
|
147
|
-
},
|
|
148
|
-
};
|
|
149
248
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
};
|
|
249
|
+
await step('Wait for grid to load', async () => {
|
|
250
|
+
await waitForGridToLoad(canvas);
|
|
251
|
+
});
|
|
153
252
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
await
|
|
159
|
-
|
|
253
|
+
await step('Verify initial page shows 1–10 of 14,075', async () => {
|
|
254
|
+
await assertDisplayedRowsText(canvasElement, '1–10 of 14,075');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
await step('Change page size to 25 — shows 1–25', async () => {
|
|
258
|
+
await changePageSize(canvasElement, 25);
|
|
259
|
+
await assertDisplayedRowsText(canvasElement, '1–25 of 14,075');
|
|
260
|
+
});
|
|
160
261
|
},
|
|
161
262
|
};
|
|
162
263
|
|
|
163
264
|
// ---------------------------------------------------------------------------
|
|
164
|
-
//
|
|
265
|
+
// Selection + Pagination — selection persists across pages
|
|
165
266
|
// ---------------------------------------------------------------------------
|
|
166
267
|
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export const FilterInStockTrue: Story = {
|
|
172
|
-
render: () => <Example initialFilterModel={inStockTrue} />,
|
|
173
|
-
play: async ({ canvasElement }) => {
|
|
268
|
+
export const SelectionPersistsAcrossPages: Story = {
|
|
269
|
+
render: () => <Example initialFilterModel={{ items: [] }} />,
|
|
270
|
+
play: async ({ canvasElement, step }) => {
|
|
174
271
|
const canvas = within(canvasElement);
|
|
175
|
-
|
|
176
|
-
await
|
|
272
|
+
|
|
273
|
+
await step('Wait for grid to load', async () => {
|
|
274
|
+
await waitForGridToLoad(canvas);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
await step('Select rows 0 and 1 — verify checkboxes checked', async () => {
|
|
278
|
+
await assertHeaderCheckboxState(canvasElement, 'unchecked');
|
|
279
|
+
await clickRowCheckbox(canvasElement, 0);
|
|
280
|
+
await clickRowCheckbox(canvasElement, 1);
|
|
281
|
+
await assertBulkActionBarVisible(canvasElement, 2);
|
|
282
|
+
await assertRowCheckboxChecked(canvasElement, 0, true);
|
|
283
|
+
await assertRowCheckboxChecked(canvasElement, 1, true);
|
|
284
|
+
await assertHeaderCheckboxState(canvasElement, 'indeterminate');
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
await step('Navigate to next page — selection persists, page actually changed', async () => {
|
|
288
|
+
await clickNextPage(canvasElement);
|
|
289
|
+
await assertDisplayedRowsText(canvasElement, '11–20 of 14,075');
|
|
290
|
+
await assertBulkActionBarVisible(canvasElement, 2);
|
|
291
|
+
await assertHeaderCheckboxState(canvasElement, 'unchecked');
|
|
292
|
+
await assertRowCheckboxChecked(canvasElement, 0, false);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
await step('Navigate back — rows 0 and 1 still checked', async () => {
|
|
296
|
+
await clickPrevPage(canvasElement);
|
|
297
|
+
await assertDisplayedRowsText(canvasElement, '1–10 of 14,075');
|
|
298
|
+
await assertBulkActionBarVisible(canvasElement, 2);
|
|
299
|
+
await assertRowCheckboxChecked(canvasElement, 0, true);
|
|
300
|
+
await assertRowCheckboxChecked(canvasElement, 1, true);
|
|
301
|
+
await assertHeaderCheckboxState(canvasElement, 'indeterminate');
|
|
302
|
+
});
|
|
177
303
|
},
|
|
178
304
|
};
|
|
179
305
|
|
|
180
|
-
const
|
|
181
|
-
|
|
306
|
+
export const SelectAllOnPage: Story = {
|
|
307
|
+
render: () => <Example initialFilterModel={{ items: [] }} />,
|
|
308
|
+
play: async ({ canvasElement, step }) => {
|
|
309
|
+
const canvas = within(canvasElement);
|
|
310
|
+
|
|
311
|
+
await step('Wait for grid to load', async () => {
|
|
312
|
+
await waitForGridToLoad(canvas);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
await step('Verify header checkbox starts unchecked', async () => {
|
|
316
|
+
await assertHeaderCheckboxState(canvasElement, 'unchecked');
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
await step('Select all on first page — 10 selected, header checked', async () => {
|
|
320
|
+
await clickHeaderCheckbox(canvasElement);
|
|
321
|
+
await assertBulkActionBarVisible(canvasElement, 10);
|
|
322
|
+
await assertHeaderCheckboxState(canvasElement, 'checked');
|
|
323
|
+
await assertDisplayedRowsText(canvasElement, '1–10 of 14,075');
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
await step('Navigate to page 2 — verify page changed, header unchecked', async () => {
|
|
327
|
+
await clickNextPage(canvasElement);
|
|
328
|
+
await assertDisplayedRowsText(canvasElement, '11–20 of 14,075');
|
|
329
|
+
await assertBulkActionBarVisible(canvasElement, 10);
|
|
330
|
+
await assertHeaderCheckboxState(canvasElement, 'unchecked');
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
await step('Select all on page 2 — 20 selected, still on page 2', async () => {
|
|
334
|
+
await clickHeaderCheckbox(canvasElement);
|
|
335
|
+
await assertBulkActionBarVisible(canvasElement, 20);
|
|
336
|
+
await assertHeaderCheckboxState(canvasElement, 'checked');
|
|
337
|
+
await assertDisplayedRowsText(canvasElement, '11–20 of 14,075');
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
await step('Navigate back to page 1 — all rows still checked', async () => {
|
|
341
|
+
await clickPrevPage(canvasElement);
|
|
342
|
+
await assertDisplayedRowsText(canvasElement, '1–10 of 14,075');
|
|
343
|
+
await assertBulkActionBarVisible(canvasElement, 20);
|
|
344
|
+
await assertHeaderCheckboxState(canvasElement, 'checked');
|
|
345
|
+
await assertRowCheckboxChecked(canvasElement, 0, true);
|
|
346
|
+
});
|
|
347
|
+
},
|
|
182
348
|
};
|
|
183
349
|
|
|
184
|
-
export const
|
|
185
|
-
render: () => <Example initialFilterModel={
|
|
186
|
-
play: async ({ canvasElement }) => {
|
|
350
|
+
export const PartialDeselectAfterSelectAll: Story = {
|
|
351
|
+
render: () => <Example initialFilterModel={{ items: [] }} />,
|
|
352
|
+
play: async ({ canvasElement, step }) => {
|
|
187
353
|
const canvas = within(canvasElement);
|
|
188
|
-
|
|
189
|
-
await
|
|
354
|
+
|
|
355
|
+
await step('Wait for grid to load', async () => {
|
|
356
|
+
await waitForGridToLoad(canvas);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
await step('Select all on page — 10 selected, header checked', async () => {
|
|
360
|
+
await clickHeaderCheckbox(canvasElement);
|
|
361
|
+
await assertBulkActionBarVisible(canvasElement, 10);
|
|
362
|
+
await assertHeaderCheckboxState(canvasElement, 'checked');
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
await step('Deselect row 3 — 9 selected, header indeterminate', async () => {
|
|
366
|
+
await clickRowCheckbox(canvasElement, 3);
|
|
367
|
+
await assertBulkActionBarVisible(canvasElement, 9);
|
|
368
|
+
await assertHeaderCheckboxState(canvasElement, 'indeterminate');
|
|
369
|
+
await assertRowCheckboxChecked(canvasElement, 3, false);
|
|
370
|
+
await assertRowCheckboxChecked(canvasElement, 0, true);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
await step('Click header to re-select all — 10 selected', async () => {
|
|
374
|
+
await clickHeaderCheckbox(canvasElement);
|
|
375
|
+
await assertBulkActionBarVisible(canvasElement, 10);
|
|
376
|
+
await assertHeaderCheckboxState(canvasElement, 'checked');
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
await step('Click header to deselect all — 0 selected', async () => {
|
|
380
|
+
await clickHeaderCheckbox(canvasElement);
|
|
381
|
+
await assertBulkActionBarHidden(canvasElement);
|
|
382
|
+
await assertHeaderCheckboxState(canvasElement, 'unchecked');
|
|
383
|
+
});
|
|
190
384
|
},
|
|
191
385
|
};
|
|
192
386
|
|
|
193
387
|
// ---------------------------------------------------------------------------
|
|
194
|
-
//
|
|
388
|
+
// Column visibility — toggle columns on and off
|
|
195
389
|
// ---------------------------------------------------------------------------
|
|
196
390
|
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export const FilterAllergensHasAnyOfGluten: Story = {
|
|
202
|
-
render: () => <Example initialFilterModel={allergensHasAnyOfGluten} />,
|
|
203
|
-
play: async ({ canvasElement }) => {
|
|
391
|
+
export const ColumnVisibilityToggle: Story = {
|
|
392
|
+
render: () => <Example initialFilterModel={{ items: [] }} />,
|
|
393
|
+
play: async ({ canvasElement, step }) => {
|
|
204
394
|
const canvas = within(canvasElement);
|
|
205
|
-
await waitForGridToLoad(canvas);
|
|
206
|
-
await assertRowCount(canvasElement, ALLERGENS_HASANYOF_GLUTEN);
|
|
207
|
-
},
|
|
208
|
-
};
|
|
209
395
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
await
|
|
219
|
-
|
|
396
|
+
await step('Wait for grid to load', async () => {
|
|
397
|
+
await waitForGridToLoad(canvas);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
await step('Verify Date & Time column is visible', async () => {
|
|
401
|
+
await assertColumnVisible(canvasElement, 'Date & Time');
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
await step('Hide Date & Time column', async () => {
|
|
405
|
+
await openColumnsPanel(canvasElement);
|
|
406
|
+
await toggleColumnInPanel('Date & Time');
|
|
407
|
+
await closeColumnsPanel();
|
|
408
|
+
await assertColumnHidden(canvasElement, 'Date & Time');
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
await step('Show Date & Time column again', async () => {
|
|
412
|
+
await openColumnsPanel(canvasElement);
|
|
413
|
+
await toggleColumnInPanel('Date & Time');
|
|
414
|
+
await closeColumnsPanel();
|
|
415
|
+
await assertColumnVisible(canvasElement, 'Date & Time');
|
|
416
|
+
});
|
|
220
417
|
},
|
|
221
418
|
};
|
|
222
419
|
|
|
223
420
|
// ---------------------------------------------------------------------------
|
|
224
|
-
//
|
|
421
|
+
// Density — switch between compact, standard, comfortable
|
|
225
422
|
// ---------------------------------------------------------------------------
|
|
226
423
|
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
export const FilterTagsHasLocal: Story = {
|
|
232
|
-
render: () => <Example initialFilterModel={tagsHasLocal} />,
|
|
233
|
-
play: async ({ canvasElement }) => {
|
|
424
|
+
export const DensitySwitch: Story = {
|
|
425
|
+
render: () => <Example initialFilterModel={{ items: [] }} />,
|
|
426
|
+
play: async ({ canvasElement, step }) => {
|
|
234
427
|
const canvas = within(canvasElement);
|
|
235
|
-
await waitForGridToLoad(canvas);
|
|
236
|
-
await assertRowCount(canvasElement, TAGS_HAS_LOCAL);
|
|
237
|
-
},
|
|
238
|
-
};
|
|
239
428
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
};
|
|
429
|
+
await step('Wait for grid to load', async () => {
|
|
430
|
+
await waitForGridToLoad(canvas);
|
|
431
|
+
});
|
|
243
432
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
await
|
|
249
|
-
|
|
433
|
+
await step('Verify default density is Compact', async () => {
|
|
434
|
+
await assertDensity(canvasElement, 'Compact');
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
await step('Switch to Standard density', async () => {
|
|
438
|
+
await changeDensity(canvasElement, 'Standard');
|
|
439
|
+
await assertDensity(canvasElement, 'Standard');
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
await step('Switch to Comfortable density', async () => {
|
|
443
|
+
await changeDensity(canvasElement, 'Comfortable');
|
|
444
|
+
await assertDensity(canvasElement, 'Comfortable');
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
await step('Switch back to Compact density', async () => {
|
|
448
|
+
await changeDensity(canvasElement, 'Compact');
|
|
449
|
+
await assertDensity(canvasElement, 'Compact');
|
|
450
|
+
});
|
|
250
451
|
},
|
|
251
452
|
};
|
|
252
453
|
|
|
253
454
|
// ---------------------------------------------------------------------------
|
|
254
|
-
//
|
|
455
|
+
// Sorting — click column header to cycle sort direction
|
|
255
456
|
// ---------------------------------------------------------------------------
|
|
256
457
|
|
|
257
|
-
export const
|
|
458
|
+
export const SortingInteraction: Story = {
|
|
258
459
|
render: () => <Example initialFilterModel={{ items: [] }} />,
|
|
259
|
-
play: async ({ canvasElement }) => {
|
|
460
|
+
play: async ({ canvasElement, step }) => {
|
|
260
461
|
const canvas = within(canvasElement);
|
|
261
|
-
await waitForGridToLoad(canvas);
|
|
262
|
-
|
|
263
|
-
// Initially no bulk action bar
|
|
264
|
-
await assertBulkActionBarHidden(canvasElement);
|
|
265
|
-
|
|
266
|
-
// Select first row via checkbox
|
|
267
|
-
await clickRowCheckbox(canvasElement, 0);
|
|
268
|
-
await assertBulkActionBarVisible(canvasElement, 1);
|
|
269
462
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
463
|
+
await step('Wait for grid to load', async () => {
|
|
464
|
+
await waitForGridToLoad(canvas);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
await step('Verify default sort is DateTime descending', async () => {
|
|
468
|
+
await assertColumnSorted(canvasElement, 'Date & Time', 'descending');
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
await step('Click Item header — sort ascending', async () => {
|
|
472
|
+
await clickColumnHeader(canvasElement, 'Item');
|
|
473
|
+
await assertColumnSorted(canvasElement, 'Item', 'ascending');
|
|
474
|
+
await assertColumnSorted(canvasElement, 'Date & Time', 'none');
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
await step('Click Item header again — sort descending', async () => {
|
|
478
|
+
await clickColumnHeader(canvasElement, 'Item');
|
|
479
|
+
await assertColumnSorted(canvasElement, 'Item', 'descending');
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
await step('Click Item header again — unsorted', async () => {
|
|
483
|
+
await clickColumnHeader(canvasElement, 'Item');
|
|
484
|
+
await assertColumnSorted(canvasElement, 'Item', 'none');
|
|
485
|
+
});
|
|
281
486
|
},
|
|
282
487
|
};
|
|
283
488
|
|
|
@@ -287,10 +492,12 @@ export const CheckboxSelectionBulkActions: Story = {
|
|
|
287
492
|
|
|
288
493
|
export const WithLoading: Story = {
|
|
289
494
|
render: () => <WithLoadingExample />,
|
|
290
|
-
play: async ({ canvasElement }) => {
|
|
291
|
-
await
|
|
292
|
-
|
|
293
|
-
|
|
495
|
+
play: async ({ canvasElement, step }) => {
|
|
496
|
+
await step('Assert loading state is visible', async () => {
|
|
497
|
+
await assertLoadingVisible(canvasElement);
|
|
498
|
+
const grid = canvasElement.querySelector('.MuiDataGrid-root');
|
|
499
|
+
expect(grid).toBeTruthy();
|
|
500
|
+
});
|
|
294
501
|
},
|
|
295
502
|
};
|
|
296
503
|
|
|
@@ -308,12 +515,18 @@ export const WithEmptyState: Story = {
|
|
|
308
515
|
|
|
309
516
|
export const WithError: Story = {
|
|
310
517
|
render: () => <WithErrorExample />,
|
|
311
|
-
play: async ({ canvasElement }) => {
|
|
518
|
+
play: async ({ canvasElement, step }) => {
|
|
312
519
|
const canvas = within(canvasElement);
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
520
|
+
|
|
521
|
+
await step('Assert error banner is visible', async () => {
|
|
522
|
+
await assertErrorBannerVisible(canvas);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
await step('Click retry button — loading state appears', async () => {
|
|
526
|
+
const retryButton = getRetryButton(canvas);
|
|
527
|
+
expect(retryButton).toBeTruthy();
|
|
528
|
+
await userEvent.click(retryButton);
|
|
529
|
+
await assertLoadingVisible(canvasElement);
|
|
530
|
+
});
|
|
318
531
|
},
|
|
319
532
|
};
|