@linzjs/step-ag-grid 29.11.4 → 29.13.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 (87) hide show
  1. package/dist/src/components/Grid.d.ts +7 -12
  2. package/dist/src/components/GridCell.d.ts +2 -19
  3. package/dist/src/components/GridCellFiller.d.ts +1 -1
  4. package/dist/src/components/GridCellMultiEditor.d.ts +1 -2
  5. package/dist/src/components/GridPopoverHook.d.ts +1 -1
  6. package/dist/src/components/gridFilter/GridFilterButtons.d.ts +1 -1
  7. package/dist/src/components/gridFilter/useGridFilter.d.ts +1 -1
  8. package/dist/src/components/gridForm/GridFormDropDown.d.ts +1 -1
  9. package/dist/src/components/gridForm/GridFormEditBearing.d.ts +1 -1
  10. package/dist/src/components/gridForm/GridFormMessage.d.ts +1 -1
  11. package/dist/src/components/gridForm/GridFormMultiSelect.d.ts +1 -1
  12. package/dist/src/components/gridForm/GridFormMultiSelectGrid.d.ts +1 -1
  13. package/dist/src/components/gridForm/GridFormPopoverMenu.d.ts +1 -1
  14. package/dist/src/components/gridForm/GridFormSubComponentTextArea.d.ts +1 -1
  15. package/dist/src/components/gridForm/GridFormSubComponentTextInput.d.ts +1 -1
  16. package/dist/src/components/gridForm/GridFormTextArea.d.ts +1 -1
  17. package/dist/src/components/gridForm/GridFormTextInput.d.ts +1 -1
  18. package/dist/src/components/gridHook/useGridContextMenu.d.ts +1 -1
  19. package/dist/src/components/gridPopoverEdit/GridButton.d.ts +1 -2
  20. package/dist/src/components/gridPopoverEdit/GridEditBoolean.d.ts +2 -2
  21. package/dist/src/components/gridPopoverEdit/GridPopoutEditMultiSelect.d.ts +2 -2
  22. package/dist/src/components/gridPopoverEdit/GridPopoutEditMultiSelectGrid.d.ts +2 -2
  23. package/dist/src/components/gridPopoverEdit/GridPopoverEditBearing.d.ts +2 -2
  24. package/dist/src/components/gridPopoverEdit/GridPopoverEditDropDown.d.ts +2 -2
  25. package/dist/src/components/gridPopoverEdit/GridPopoverMenu.d.ts +2 -2
  26. package/dist/src/components/gridPopoverEdit/GridPopoverMessage.d.ts +2 -2
  27. package/dist/src/components/gridPopoverEdit/GridPopoverTextArea.d.ts +2 -2
  28. package/dist/src/components/gridPopoverEdit/GridPopoverTextInput.d.ts +2 -2
  29. package/dist/src/components/gridRender/GridRenderGenericCell.d.ts +1 -2
  30. package/dist/src/components/index.d.ts +1 -0
  31. package/dist/src/components/types.d.ts +26 -0
  32. package/dist/src/contexts/GridContext.d.ts +12 -12
  33. package/dist/src/contexts/GridPopoverContext.d.ts +1 -1
  34. package/dist/src/utils/__tests__/storybookTestUtil.ts +28 -1
  35. package/dist/src/utils/__tests__/testQuick.ts +2 -0
  36. package/dist/src/utils/textValidator.d.ts +1 -1
  37. package/dist/src/utils/util.d.ts +1 -0
  38. package/dist/step-ag-grid.cjs +326 -214
  39. package/dist/step-ag-grid.cjs.map +1 -1
  40. package/dist/step-ag-grid.esm.js +327 -216
  41. package/dist/step-ag-grid.esm.js.map +1 -1
  42. package/package.json +5 -2
  43. package/src/components/Grid.tsx +130 -65
  44. package/src/components/GridCell.tsx +1 -25
  45. package/src/components/GridCellFiller.tsx +1 -1
  46. package/src/components/GridCellMultiEditor.tsx +2 -2
  47. package/src/components/GridPopoverHook.tsx +1 -1
  48. package/src/components/gridFilter/GridFilterButtons.tsx +1 -1
  49. package/src/components/gridFilter/useGridFilter.ts +1 -1
  50. package/src/components/gridForm/GridFormDropDown.tsx +1 -1
  51. package/src/components/gridForm/GridFormEditBearing.tsx +1 -1
  52. package/src/components/gridForm/GridFormMessage.tsx +1 -1
  53. package/src/components/gridForm/GridFormMultiSelect.tsx +1 -1
  54. package/src/components/gridForm/GridFormMultiSelectGrid.tsx +1 -1
  55. package/src/components/gridForm/GridFormPopoverMenu.tsx +1 -1
  56. package/src/components/gridForm/GridFormSubComponentTextArea.tsx +1 -1
  57. package/src/components/gridForm/GridFormSubComponentTextInput.tsx +1 -1
  58. package/src/components/gridForm/GridFormTextArea.tsx +1 -1
  59. package/src/components/gridForm/GridFormTextInput.tsx +1 -1
  60. package/src/components/gridHook/useGridContextMenu.tsx +1 -1
  61. package/src/components/gridPopoverEdit/GridButton.tsx +2 -2
  62. package/src/components/gridPopoverEdit/GridEditBoolean.tsx +2 -2
  63. package/src/components/gridPopoverEdit/GridPopoutEditMultiSelect.ts +2 -2
  64. package/src/components/gridPopoverEdit/GridPopoutEditMultiSelectGrid.ts +2 -2
  65. package/src/components/gridPopoverEdit/GridPopoverEditBearing.ts +2 -2
  66. package/src/components/gridPopoverEdit/GridPopoverEditDropDown.ts +2 -2
  67. package/src/components/gridPopoverEdit/GridPopoverMenu.tsx +2 -2
  68. package/src/components/gridPopoverEdit/GridPopoverMessage.ts +2 -2
  69. package/src/components/gridPopoverEdit/GridPopoverTextArea.ts +2 -2
  70. package/src/components/gridPopoverEdit/GridPopoverTextInput.ts +2 -2
  71. package/src/components/gridRender/GridRenderGenericCell.tsx +1 -2
  72. package/src/components/index.ts +1 -0
  73. package/src/components/types.ts +32 -0
  74. package/src/contexts/GridContext.tsx +55 -148
  75. package/src/contexts/GridContextProvider.tsx +61 -56
  76. package/src/contexts/GridPopoverContext.tsx +1 -1
  77. package/src/contexts/GridPopoverContextProvider.tsx +1 -1
  78. package/src/react-menu3/components/MenuButton.tsx +2 -2
  79. package/src/react-menu3/hooks/useItemState.ts +1 -1
  80. package/src/stories/grid/GridPopoverEditDropDown.stories.tsx +3 -3
  81. package/src/stories/grid/GridReadOnly.stories.tsx +3 -3
  82. package/src/stories/grid/GridSorting.stories.tsx +189 -0
  83. package/src/utils/__tests__/storybookTestUtil.ts +28 -1
  84. package/src/utils/__tests__/testQuick.ts +2 -0
  85. package/src/utils/textValidator.test.ts +1 -1
  86. package/src/utils/textValidator.ts +1 -1
  87. package/src/utils/util.ts +19 -0
@@ -71,7 +71,7 @@ interface ITestRow {
71
71
  }
72
72
 
73
73
  const GridReadOnlyTemplate: StoryFn<typeof Grid<ITestRow>> = (props: GridProps<ITestRow>) => {
74
- const [externalSelectedItems, setExternalSelectedItems] = useState<any[]>([]);
74
+ const [externalSelectedIds, setExternalSelectedIds] = useState<number[]>([]);
75
75
  const columnDefs: ColDefT<ITestRow>[] = useMemo(
76
76
  () => [
77
77
  GridCell({
@@ -248,8 +248,8 @@ const GridReadOnlyTemplate: StoryFn<typeof Grid<ITestRow>> = (props: GridProps<I
248
248
  enableClickSelection={true}
249
249
  enableSelectionWithoutKeys={true}
250
250
  autoSelectFirstRow={true}
251
- externalSelectedItems={externalSelectedItems}
252
- setExternalSelectedItems={setExternalSelectedItems}
251
+ externalSelectedIds={externalSelectedIds}
252
+ setExternalSelectedIds={setExternalSelectedIds}
253
253
  columnDefs={columnDefs}
254
254
  rowData={rowData}
255
255
  />
@@ -0,0 +1,189 @@
1
+ import '../../styles/GridTheme.scss';
2
+ import '../../styles/index.scss';
3
+ import '@linzjs/lui/dist/scss/base.scss';
4
+ import '@linzjs/lui/dist/fonts';
5
+
6
+ import { Meta, StoryFn } from '@storybook/react-vite';
7
+ import { useMemo, useState } from 'react';
8
+ import { expect } from 'storybook/test';
9
+
10
+ import {
11
+ ColDefT,
12
+ compareNaturalInsensitive,
13
+ Grid,
14
+ GridCell,
15
+ GridContextProvider,
16
+ GridProps,
17
+ GridUpdatingContextProvider,
18
+ GridWrapper,
19
+ wait,
20
+ } from '../..';
21
+ import {
22
+ clickColumnHeaderToSort,
23
+ gridColumnValues,
24
+ waitForGridReady,
25
+ waitForGridRows,
26
+ } from '../../utils/__tests__/storybookTestUtil';
27
+
28
+ export default {
29
+ title: 'Components / Grids',
30
+ component: Grid,
31
+ args: {
32
+ quickFilter: true,
33
+ quickFilterValue: '',
34
+ quickFilterPlaceholder: 'Quick filter...',
35
+ selectable: false,
36
+ rowSelection: 'single',
37
+ }, // Storybook hangs otherwise
38
+ parameters: {
39
+ docs: {
40
+ source: {
41
+ type: 'code',
42
+ },
43
+ },
44
+ },
45
+ decorators: [
46
+ (Story) => (
47
+ <div style={{ maxWidth: 1024, height: 400, display: 'flex', flexDirection: 'column' }}>
48
+ <GridUpdatingContextProvider>
49
+ <GridContextProvider>
50
+ <Story />
51
+ </GridContextProvider>
52
+ </GridUpdatingContextProvider>
53
+ </div>
54
+ ),
55
+ ],
56
+ } as Meta<typeof Grid>;
57
+
58
+ interface ITestRow {
59
+ id: number;
60
+ numeric?: number;
61
+ text?: string;
62
+ }
63
+
64
+ const GridReadOnlyTemplate: StoryFn<typeof Grid<ITestRow>> = (props: GridProps<ITestRow>) => {
65
+ const [externalSelectedItems, setExternalSelectedItems] = useState<any[]>([]);
66
+ const columnDefs: ColDefT<ITestRow>[] = useMemo(
67
+ () => [
68
+ GridCell({
69
+ field: 'id',
70
+ headerName: 'Id',
71
+ lockVisible: true,
72
+ }),
73
+ GridCell({
74
+ field: 'numeric',
75
+ headerName: 'Numeric',
76
+ }),
77
+ GridCell({
78
+ field: 'text',
79
+ headerName: 'Text',
80
+ }),
81
+ GridCell({
82
+ colId: 'customComparatorText',
83
+ headerName: 'Cust. comp. id as text',
84
+ valueFormatter: ({ data }) => data?.text ?? '',
85
+ comparator: (_v1, _v2, n1, n2) => compareNaturalInsensitive(String(n1.data?.id), String(n2.data?.id)) ?? 0,
86
+ }),
87
+ GridCell({
88
+ colId: 'customComparatorNumeric',
89
+ headerName: 'Cust. comp. abs number',
90
+ valueFormatter: ({ data }) => String(data?.numeric ?? ''),
91
+ comparator: (_v1, _v2, n1, n2) => {
92
+ return compareNaturalInsensitive(n1.data?.numeric, n2.data?.numeric) ?? 0;
93
+ },
94
+ }),
95
+ ],
96
+ [],
97
+ );
98
+
99
+ const [rowData] = useState<ITestRow[]>([
100
+ { id: 1009, numeric: -1.1, text: 'ade' },
101
+ { id: 1000, numeric: 1, text: 'ade' },
102
+ { id: 1001, numeric: 11, text: 'cdc' },
103
+ { id: 1002, numeric: 21, text: '2' },
104
+ { id: 1003, numeric: 2.1, text: '10' },
105
+ { id: 1004, numeric: 2.01, text: 'b' },
106
+ { id: 1005, numeric: 2.21, text: 'b' },
107
+ { id: 1006, numeric: 3, text: undefined },
108
+ { id: 1008, numeric: 3, text: 'e' },
109
+ { id: 1007, numeric: undefined, text: 'e' },
110
+ ]);
111
+
112
+ return (
113
+ <GridWrapper maxHeight={400}>
114
+ <Grid
115
+ data-testid={'readonly'}
116
+ {...props}
117
+ selectable={true}
118
+ enableClickSelection={true}
119
+ enableSelectionWithoutKeys={true}
120
+ autoSelectFirstRow={true}
121
+ externalSelectedItems={externalSelectedItems}
122
+ setExternalSelectedItems={setExternalSelectedItems}
123
+ columnDefs={columnDefs}
124
+ rowData={rowData}
125
+ />
126
+ </GridWrapper>
127
+ );
128
+ };
129
+
130
+ export const Sorting = GridReadOnlyTemplate.bind({});
131
+ Sorting.play = async (context) => {
132
+ const { canvasElement } = context;
133
+ await waitForGridReady(context);
134
+ await waitForGridRows({ grid: canvasElement });
135
+
136
+ const test = async (colId: string | null, expected: string[] | null, idExpected?: string[]) => {
137
+ if (colId !== null) {
138
+ await clickColumnHeaderToSort(colId, canvasElement);
139
+ await wait(500);
140
+ expected && expect(gridColumnValues(colId, canvasElement)).toEqual(expected);
141
+ }
142
+ idExpected && expect(gridColumnValues('id', canvasElement)).toEqual(idExpected);
143
+ };
144
+
145
+ // Default sort order
146
+ await test(null, null, ['1009', '1000', '1001', '1002', '1003', '1004', '1005', '1006', '1008', '1007']);
147
+
148
+ await test('numeric', ['–', '-1.1', '1', '2.01', '2.1', '2.21', '3', '3', '11', '21']);
149
+ await test('numeric', ['21', '11', '3', '3', '2.21', '2.1', '2.01', '1', '-1.1', '–']);
150
+
151
+ await test(
152
+ 'text',
153
+ ['–', '2', '10', 'ade', 'ade', 'b', 'b', 'cdc', 'e', 'e'],
154
+ ['1006', '1002', '1003', '1000', '1009', '1004', '1005', '1001', '1007', '1008'],
155
+ );
156
+ await test(
157
+ 'text',
158
+ ['e', 'e', 'cdc', 'b', 'b', 'ade', 'ade', '10', '2', '–'],
159
+ ['1008', '1007', '1001', '1005', '1004', '1009', '1000', '1003', '1002', '1006'],
160
+ );
161
+
162
+ await test('customComparatorText', null, [
163
+ '1000',
164
+ '1001',
165
+ '1002',
166
+ '1003',
167
+ '1004',
168
+ '1005',
169
+ '1006',
170
+ '1007',
171
+ '1008',
172
+ '1009',
173
+ ]);
174
+ await test('customComparatorText', null, [
175
+ '1009',
176
+ '1008',
177
+ '1007',
178
+ '1006',
179
+ '1005',
180
+ '1004',
181
+ '1003',
182
+ '1002',
183
+ '1001',
184
+ '1000',
185
+ ]);
186
+
187
+ await test('customComparatorNumeric', ['–', '-1.1', '1', '2.01', '2.1', '2.21', '3', '3', '11', '21']);
188
+ await test('customComparatorNumeric', ['21', '11', '3', '3', '2.21', '2.1', '2.01', '1', '-1.1', '–']);
189
+ };
@@ -1,4 +1,31 @@
1
- import { expect, waitFor } from 'storybook/test';
1
+ import { sortBy } from 'lodash-es';
2
+ import { expect, userEvent, waitFor } from 'storybook/test';
3
+
4
+ import { findQuick, getAllQuick } from './testQuick';
2
5
 
3
6
  export const waitForGridReady = ({ canvasElement }: { canvasElement: HTMLElement }) =>
4
7
  waitFor(() => expect(canvasElement.querySelector('.Grid-ready')).toBeInTheDocument(), { timeout: 5000 });
8
+
9
+ export const waitForGridRows = async (props?: { grid?: HTMLElement; timeout?: number }) =>
10
+ waitFor(() => expect(getAllQuick({ classes: '.ag-row' }, props?.grid).length > 0).toBe(true), {
11
+ timeout: props?.timeout ?? 4000,
12
+ });
13
+
14
+ export const clickColumnHeaderToSort = async (colId: string, within: HTMLElement): Promise<void> => {
15
+ const user = userEvent.setup({
16
+ delay: 100,
17
+ });
18
+ const header = await findQuick({ selector: `.ag-header-cell[col-id="${colId}"]` }, within);
19
+ await expect(header).toBeInTheDocument();
20
+ // For some reason clicks in storybook don't trigger ag-grid, but manual clicks do
21
+ await user.click(header);
22
+ await user.keyboard('{Enter}');
23
+ };
24
+
25
+ export const gridColumnValues = (colId: string, within: HTMLElement): string[] => {
26
+ return sortBy(
27
+ getAllQuick({ tagName: `[col-id="${colId}"]`, classes: `.ag-cell` }, within),
28
+ // @ts-expect-error css style type
29
+ (el) => el.parentElement?.attributeStyleMap.get('transform')?.[0]?.y.value,
30
+ ).map((cell) => cell.innerText);
31
+ };
@@ -25,6 +25,7 @@ export interface IQueryQuick {
25
25
  role?: string;
26
26
  classes?: string;
27
27
  child?: IQueryQuick;
28
+ selector?: string;
28
29
  }
29
30
 
30
31
  const escapeSelectorParam = (param: string): string => param.replace(/["\\]/g, '\\$&');
@@ -51,6 +52,7 @@ const quickSelector = <T extends HTMLElement>(
51
52
  let lastIQueryQuick = props;
52
53
  for (let loop: IQueryQuick | undefined = props; loop; loop = loop.child) {
53
54
  lastIQueryQuick = loop;
55
+ loop.selector && (selector += loop.selector);
54
56
  loop.tagName && (selector += loop.tagName);
55
57
  loop.ariaLabel && (selector += `[aria-label='${escapeSelectorParam(loop.ariaLabel)}']`);
56
58
  loop.role && (selector += `[role="${escapeSelectorParam(loop.role)}"]`);
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, test, vi } from 'vitest';
2
2
 
3
- import { GridBaseRow } from '../components/Grid';
3
+ import { GridBaseRow } from '../components/types';
4
4
  import { TextInputValidator, TextInputValidatorProps } from './textValidator';
5
5
 
6
6
  describe('TextInputValidator', () => {
@@ -1,6 +1,6 @@
1
1
  import { ReactElement } from 'react';
2
2
 
3
- import { GridBaseRow } from '../components/Grid';
3
+ import { GridBaseRow } from '../components/types';
4
4
  import { isFloat, stringByteLengthIsInvalid } from './util';
5
5
 
6
6
  export interface TextInputValidatorProps<TData extends GridBaseRow> {
package/src/utils/util.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { isEmpty, negate } from 'lodash-es';
2
+ import natsort from 'natsort';
2
3
 
3
4
  export const isNotEmpty = negate(isEmpty);
4
5
 
@@ -58,3 +59,21 @@ export const sanitiseFileName = (filename: string): string => {
58
59
  if (!fileExt) return valid.slice().slice(0, 64);
59
60
  return valid.slice(0, -fileExt.length - 1).slice(0, 64) + '.' + fileExt;
60
61
  };
62
+
63
+ const naturalSortInsensitive = natsort({ insensitive: true });
64
+ const santitizeNaturalValue = (v: string | number | null | undefined): string => {
65
+ if (v === '–' || v === '-' || v == null) {
66
+ return '';
67
+ }
68
+ return String(v);
69
+ };
70
+ export const compareNaturalInsensitive = (
71
+ a?: object | string | number | null,
72
+ b?: object | string | number | null,
73
+ ): number => {
74
+ if ((a !== null && typeof a === 'object') || (b !== null && typeof b === 'object')) {
75
+ // We can't compare objects
76
+ return 0;
77
+ }
78
+ return naturalSortInsensitive(santitizeNaturalValue(a), santitizeNaturalValue(b));
79
+ };