@linzjs/step-ag-grid 30.2.0 → 30.4.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/dist/src/components/gridFilter/GridFilterColumnsMultiSelect.d.ts +0 -1
- package/dist/src/components/gridForm/GridFormInlineTextInput.d.ts +5 -0
- package/dist/step-ag-grid.cjs +24 -6
- package/dist/step-ag-grid.cjs.map +1 -1
- package/dist/step-ag-grid.esm.js +24 -6
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/gridFilter/GridFilterColumnsMultiSelect.tsx +18 -5
- package/src/components/gridForm/GridFormInlineTextInput.tsx +9 -1
- package/src/stories/grid/GridInlineText.stories.tsx +99 -18
package/package.json
CHANGED
|
@@ -26,6 +26,8 @@ interface FilterUIProps {
|
|
|
26
26
|
|
|
27
27
|
const EMPTY_KEY = '__EMPTY__';
|
|
28
28
|
const DEFAULT_EMPTY_LABEL = '-';
|
|
29
|
+
const filterStateMap = new Map<string, boolean>();
|
|
30
|
+
const filterClickCountMap = new Map<string, number>();
|
|
29
31
|
|
|
30
32
|
const FilterUI: React.FC<FilterUIProps> = ({
|
|
31
33
|
allValues,
|
|
@@ -76,7 +78,6 @@ export class GridFilterColumnsMultiSelect implements IFilterComp {
|
|
|
76
78
|
private reactRoot: Root | null = null;
|
|
77
79
|
private filterButtonClickListener: ((e: Event) => void) | null = null;
|
|
78
80
|
private filterButton: Element | null = null;
|
|
79
|
-
private hasOpened = false;
|
|
80
81
|
|
|
81
82
|
private normalizeCellValue(value: unknown): string {
|
|
82
83
|
if (typeof value === 'string') return value.trim() === '' ? EMPTY_KEY : value;
|
|
@@ -134,13 +135,25 @@ export class GridFilterColumnsMultiSelect implements IFilterComp {
|
|
|
134
135
|
|
|
135
136
|
if (this.filterButton) {
|
|
136
137
|
this.filterButtonClickListener = (e: Event) => {
|
|
137
|
-
|
|
138
|
-
|
|
138
|
+
const clickCount = filterClickCountMap.get(columnId) || 0;
|
|
139
|
+
filterClickCountMap.set(columnId, clickCount + 1);
|
|
140
|
+
|
|
141
|
+
if (clickCount === 0) {
|
|
142
|
+
e.stopImmediatePropagation();
|
|
143
|
+
e.preventDefault();
|
|
144
|
+
this.params.api.hidePopupMenu();
|
|
145
|
+
filterStateMap.set(columnId, false);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const isOpen = filterStateMap.get(columnId) || false;
|
|
150
|
+
if (isOpen) {
|
|
151
|
+
e.stopImmediatePropagation();
|
|
139
152
|
e.preventDefault();
|
|
140
153
|
this.params.api.hidePopupMenu();
|
|
141
|
-
|
|
154
|
+
filterStateMap.set(columnId, false);
|
|
142
155
|
} else {
|
|
143
|
-
|
|
156
|
+
filterStateMap.set(columnId, true);
|
|
144
157
|
}
|
|
145
158
|
};
|
|
146
159
|
|
|
@@ -16,6 +16,7 @@ export interface GridFormInlineTextInput<TData extends GridBaseRow>
|
|
|
16
16
|
placeholder?: string;
|
|
17
17
|
units?: string;
|
|
18
18
|
onSave?: (props: { selectedRows: TData[]; selectedRowIds: TData['id'][]; value: string }) => MaybePromise<boolean>;
|
|
19
|
+
onChange?: (props: { selectedRows: TData[]; selectedRowIds: TData['id'][]; value: string }) => void;
|
|
19
20
|
helpText?: string;
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -103,7 +104,14 @@ export const GridFormInlineTextInput = <TData extends GridBaseRow>(props: GridFo
|
|
|
103
104
|
ref={inputRef}
|
|
104
105
|
type={'text'}
|
|
105
106
|
value={value}
|
|
106
|
-
onChange={(e) =>
|
|
107
|
+
onChange={(e) => {
|
|
108
|
+
setValue(e.target.value);
|
|
109
|
+
if (props.onChange) {
|
|
110
|
+
const selectedRows = getSelectedRows<TData>();
|
|
111
|
+
const selectedRowIds = selectedRows.map((data) => data.id);
|
|
112
|
+
props.onChange({ selectedRows, selectedRowIds, value: e.target.value });
|
|
113
|
+
}
|
|
114
|
+
}}
|
|
107
115
|
placeholder={props.placeholder ?? 'Type here'}
|
|
108
116
|
onBlur={() => {
|
|
109
117
|
void triggerSave(CloseReason.BLUR);
|
|
@@ -3,8 +3,9 @@ import '../../styles/index.scss';
|
|
|
3
3
|
import '@linzjs/lui/dist/scss/base.scss';
|
|
4
4
|
import '@linzjs/lui/dist/fonts';
|
|
5
5
|
|
|
6
|
-
import { Meta, StoryFn } from '@storybook/react-vite';
|
|
6
|
+
import type { Meta, StoryFn } from '@storybook/react-vite';
|
|
7
7
|
import { useCallback, useContext, useMemo, useState } from 'react';
|
|
8
|
+
import { expect, screen, userEvent, within } from 'storybook/test';
|
|
8
9
|
|
|
9
10
|
import {
|
|
10
11
|
ActionButton,
|
|
@@ -27,6 +28,11 @@ interface IFormTestRow {
|
|
|
27
28
|
text2: string | null;
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
const initialRows: IFormTestRow[] = [
|
|
32
|
+
{ id: 1000, text1: 'a text1', text2: 'a text2' },
|
|
33
|
+
{ id: 1001, text1: 'b text1', text2: 'b text2' },
|
|
34
|
+
];
|
|
35
|
+
|
|
30
36
|
export default {
|
|
31
37
|
title: 'Components / Grids',
|
|
32
38
|
component: Grid,
|
|
@@ -42,11 +48,6 @@ export default {
|
|
|
42
48
|
<Story />
|
|
43
49
|
</GridContextProvider>
|
|
44
50
|
</GridUpdatingContextProvider>
|
|
45
|
-
<GridUpdatingContextProvider>
|
|
46
|
-
<GridContextProvider>
|
|
47
|
-
<Story />
|
|
48
|
-
</GridContextProvider>
|
|
49
|
-
</GridUpdatingContextProvider>
|
|
50
51
|
</div>
|
|
51
52
|
),
|
|
52
53
|
],
|
|
@@ -55,18 +56,7 @@ export default {
|
|
|
55
56
|
const GridInlineTextTemplate: StoryFn<typeof Grid<IFormTestRow>> = (props: GridProps<IFormTestRow>) => {
|
|
56
57
|
const { selectRowsWithFlashDiff } = useContext(GridContext);
|
|
57
58
|
const [externalSelectedItems, setExternalSelectedItems] = useState<any[]>([]);
|
|
58
|
-
const [rowData, setRowData] = useState(
|
|
59
|
-
{
|
|
60
|
-
id: 1000,
|
|
61
|
-
text1: 'a text1',
|
|
62
|
-
text2: 'a text2',
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
id: 1001,
|
|
66
|
-
text1: 'b text1',
|
|
67
|
-
text2: 'b text2',
|
|
68
|
-
},
|
|
69
|
-
] as IFormTestRow[]);
|
|
59
|
+
const [rowData, setRowData] = useState<IFormTestRow[]>(initialRows);
|
|
70
60
|
|
|
71
61
|
const columnDefs: ColDefT<IFormTestRow>[] = useMemo(
|
|
72
62
|
() => [
|
|
@@ -183,3 +173,94 @@ const GridInlineTextTemplate: StoryFn<typeof Grid<IFormTestRow>> = (props: GridP
|
|
|
183
173
|
|
|
184
174
|
export const _EditInlineText = GridInlineTextTemplate.bind({});
|
|
185
175
|
_EditInlineText.play = waitForGridReady;
|
|
176
|
+
|
|
177
|
+
const _InlineText_ChangeEventsTemplate: StoryFn<typeof Grid<IFormTestRow>> = (props: GridProps<IFormTestRow>) => {
|
|
178
|
+
const [externalSelectedItems, setExternalSelectedItems] = useState<any[]>([]);
|
|
179
|
+
const [isDirty, setIsDirty] = useState(false);
|
|
180
|
+
const [rowData, setRowData] = useState<IFormTestRow[]>(initialRows);
|
|
181
|
+
|
|
182
|
+
// Use a named handler for onChange
|
|
183
|
+
const handleInlineEditChange = () => {
|
|
184
|
+
setIsDirty(true);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const columnDefs: ColDefT<IFormTestRow>[] = useMemo(
|
|
188
|
+
() => [
|
|
189
|
+
GridCell({
|
|
190
|
+
field: 'id',
|
|
191
|
+
headerName: 'Id',
|
|
192
|
+
}),
|
|
193
|
+
GridInlineTextInput(
|
|
194
|
+
{
|
|
195
|
+
field: 'text1',
|
|
196
|
+
headerName: 'Text input 1',
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
editorParams: {
|
|
200
|
+
placeholder: 'Enter some text...',
|
|
201
|
+
onChange: handleInlineEditChange,
|
|
202
|
+
onSave: ({ selectedRowIds, value }) => {
|
|
203
|
+
setRowData(rowData.map((data) => (selectedRowIds.includes(data.id) ? { ...data, text1: value } : data)));
|
|
204
|
+
return true;
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
),
|
|
209
|
+
GridInlineTextInput(
|
|
210
|
+
{
|
|
211
|
+
field: 'text2',
|
|
212
|
+
headerName: 'Text input 2',
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
editorParams: {
|
|
216
|
+
placeholder: 'Enter some text...',
|
|
217
|
+
onChange: handleInlineEditChange,
|
|
218
|
+
onSave: ({ selectedRowIds, value }) => {
|
|
219
|
+
setRowData(rowData.map((data) => (selectedRowIds.includes(data.id) ? { ...data, text2: value } : data)));
|
|
220
|
+
return true;
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
),
|
|
225
|
+
],
|
|
226
|
+
[rowData],
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
return (
|
|
230
|
+
<>
|
|
231
|
+
<Grid
|
|
232
|
+
{...props}
|
|
233
|
+
selectable={true}
|
|
234
|
+
hideSelectColumn={true}
|
|
235
|
+
externalSelectedItems={externalSelectedItems}
|
|
236
|
+
setExternalSelectedItems={setExternalSelectedItems}
|
|
237
|
+
columnDefs={columnDefs}
|
|
238
|
+
rowData={rowData}
|
|
239
|
+
domLayout={'autoHeight'}
|
|
240
|
+
defaultColDef={{ minWidth: 70 }}
|
|
241
|
+
sizeColumns={'auto'}
|
|
242
|
+
singleClickEdit={true}
|
|
243
|
+
enableMultilineBulkEdit={true}
|
|
244
|
+
/>
|
|
245
|
+
<div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: 8 }}>
|
|
246
|
+
<button type="button" data-testid="inline-edit-save" disabled={!isDirty} onClick={() => setIsDirty(false)}>
|
|
247
|
+
Save
|
|
248
|
+
</button>
|
|
249
|
+
</div>
|
|
250
|
+
</>
|
|
251
|
+
);
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
export const _InlineText_ChangeEvents = _InlineText_ChangeEventsTemplate.bind({});
|
|
255
|
+
_InlineText_ChangeEvents.play = async (context) => {
|
|
256
|
+
await waitForGridReady(context);
|
|
257
|
+
const canvas = within(context.canvasElement);
|
|
258
|
+
const saveButtons = canvas.getAllByTestId('inline-edit-save');
|
|
259
|
+
const saveButton = saveButtons[0];
|
|
260
|
+
await expect(saveButton).toBeDisabled();
|
|
261
|
+
const textCells = canvas.getAllByText('a text1');
|
|
262
|
+
await userEvent.click(textCells[0]);
|
|
263
|
+
const input = await screen.findByPlaceholderText('Enter some text...');
|
|
264
|
+
await userEvent.type(input, 'X');
|
|
265
|
+
await expect(saveButton).toBeEnabled();
|
|
266
|
+
};
|