@linzjs/step-ag-grid 29.1.2 → 29.1.4
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/Grid.d.ts +1 -1
- package/dist/src/components/GridPopoverHook.d.ts +2 -0
- package/dist/src/contexts/GridContext.d.ts +2 -2
- package/dist/src/utils/waitForCondition.d.ts +1 -1
- package/dist/step-ag-grid.cjs +147 -114
- package/dist/step-ag-grid.cjs.map +1 -1
- package/dist/step-ag-grid.esm.js +147 -115
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Grid.tsx +1 -1
- package/src/components/GridCell.tsx +34 -14
- package/src/components/GridPopoverHook.tsx +10 -15
- package/src/components/gridForm/GridFormDropDown.tsx +73 -49
- package/src/components/gridForm/GridFormMultiSelect.tsx +12 -4
- package/src/components/gridPopoverEdit/GridPopoverMessage.ts +1 -0
- package/src/contexts/GridContext.tsx +2 -2
- package/src/contexts/GridContextProvider.tsx +31 -23
- package/src/stories/grid/GridPopoutEditGeneric.stories.tsx +1 -1
- package/src/stories/grid/GridPopoverEditMultiSelect.stories.tsx +1 -1
- package/src/stories/grid/GridPopoverEditMultiSelectGrid.stories.tsx +1 -1
- package/src/stories/grid/gridFormInteraction/GridFormDropDownInteraction.stories.tsx +1 -1
- package/src/stories/grid/gridFormInteraction/GridFormEditBearingCorrectionInteraction.stories.tsx +2 -1
- package/src/stories/grid/gridFormInteraction/GridFormEditBearingInteraction.stories.tsx +1 -1
- package/src/stories/grid/gridFormInteraction/GridFormMultiSelectInteraction.stories.tsx +2 -1
- package/src/stories/grid/gridFormInteraction/GridFormPopoverMenuInteraction.stories.tsx +2 -2
- package/src/stories/grid/gridFormInteraction/GridFormTextAreaInteraction.stories.tsx +1 -1
- package/src/stories/grid/gridFormInteraction/GridFormTextInputInteraction.stories.tsx +1 -1
- package/src/stories/grid/interactions/GridKeyboardInteractions.stories.tsx +56 -13
- package/src/utils/waitForCondition.tsx +6 -2
package/package.json
CHANGED
package/src/components/Grid.tsx
CHANGED
|
@@ -101,7 +101,7 @@ export interface GridProps<TData extends GridBaseRow = GridBaseRow> {
|
|
|
101
101
|
* When pressing tab whilst editing the grid will select and edit the next cell if available.
|
|
102
102
|
* Once the last cell to edit closes this callback is called.
|
|
103
103
|
*/
|
|
104
|
-
onBulkEditingComplete?: () => void;
|
|
104
|
+
onBulkEditingComplete?: () => Promise<void> | void;
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
107
|
* Context menu definition if required.
|
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
ValueFormatterParams,
|
|
12
12
|
ValueGetterFunc,
|
|
13
13
|
} from 'ag-grid-community';
|
|
14
|
-
import {
|
|
14
|
+
import { defer } from 'lodash-es';
|
|
15
|
+
import { forwardRef, ReactElement, useContext, useEffect } from 'react';
|
|
15
16
|
|
|
16
17
|
import { GridPopoverContextProvider } from '../contexts/GridPopoverContextProvider';
|
|
17
18
|
import { GridUpdatingContext } from '../contexts/GridUpdatingContext';
|
|
@@ -153,20 +154,25 @@ export const GridCell = <TData extends GridBaseRow, TValue = any, Props extends
|
|
|
153
154
|
sortable: true,
|
|
154
155
|
resizable: true,
|
|
155
156
|
valueSetter: custom?.editor ? blockValueSetter : undefined,
|
|
156
|
-
editable: props.editable ??
|
|
157
|
-
...(custom?.editor
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
157
|
+
editable: props.editable ?? !!custom?.editor,
|
|
158
|
+
...(custom?.editor
|
|
159
|
+
? {
|
|
160
|
+
cellClassRules: GridCellMultiSelectClassRules,
|
|
161
|
+
cellEditor: GenericCellEditorComponentWrapper(custom?.editor),
|
|
162
|
+
}
|
|
163
|
+
: {
|
|
164
|
+
cellEditor: CellEditorToBlockEditing,
|
|
165
|
+
}),
|
|
162
166
|
suppressKeyboardEvent: suppressCellKeyboardEvents,
|
|
163
|
-
...(custom?.editorParams
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
...(custom?.editorParams
|
|
168
|
+
? {
|
|
169
|
+
cellEditorParams: {
|
|
170
|
+
...custom.editorParams,
|
|
171
|
+
multiEdit: custom.multiEdit,
|
|
172
|
+
preventAutoEdit: custom.preventAutoEdit ?? !custom?.editor,
|
|
173
|
+
},
|
|
174
|
+
}
|
|
175
|
+
: { cellEditorParams: { preventAutoEdit: !custom?.editor } }),
|
|
170
176
|
// If there's a valueFormatter and no filterValueGetter then create a filterValueGetter
|
|
171
177
|
getQuickFilterText: filterValueGetter as any,
|
|
172
178
|
// Default value formatter, otherwise react freaks out on objects
|
|
@@ -184,6 +190,20 @@ export const GridCell = <TData extends GridBaseRow, TValue = any, Props extends
|
|
|
184
190
|
};
|
|
185
191
|
};
|
|
186
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Ag-grid will start its own editor if editable is true and there is no cell editor
|
|
195
|
+
* like in the case of a cell that is editable because it triggers a modal.
|
|
196
|
+
* This will block that editor.
|
|
197
|
+
*/
|
|
198
|
+
const CellEditorToBlockEditing = ({ stopEditing }: { stopEditing: () => void }) => {
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
defer(() => {
|
|
201
|
+
stopEditing();
|
|
202
|
+
});
|
|
203
|
+
}, [stopEditing]);
|
|
204
|
+
return <></>;
|
|
205
|
+
};
|
|
206
|
+
|
|
187
207
|
export interface CellEditorCommon {
|
|
188
208
|
className?: string | undefined;
|
|
189
209
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ReactElement, useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
|
|
3
|
-
import { useGridContext } from '../contexts/GridContext';
|
|
4
3
|
import { useGridPopoverContext } from '../contexts/GridPopoverContext';
|
|
5
4
|
import { ControlledMenu } from '../react-menu3';
|
|
6
5
|
import { MenuCloseEvent } from '../react-menu3/types';
|
|
@@ -20,13 +19,14 @@ export interface GridPopoverHookProps<TData> {
|
|
|
20
19
|
dontSaveOnExternalClick?: boolean;
|
|
21
20
|
}
|
|
22
21
|
|
|
22
|
+
export const CancelPromise = () => Promise.resolve(true);
|
|
23
|
+
|
|
23
24
|
export const useGridPopoverHook = <TData extends GridBaseRow>({
|
|
24
25
|
className,
|
|
25
26
|
save,
|
|
26
27
|
invalid,
|
|
27
28
|
dontSaveOnExternalClick,
|
|
28
29
|
}: GridPopoverHookProps<TData>) => {
|
|
29
|
-
const { onBulkEditingComplete, redrawRows } = useGridContext<TData>();
|
|
30
30
|
const { anchorRef, saving, updateValue, stopEditing } = useGridPopoverContext<TData>();
|
|
31
31
|
const saveButtonRef = useRef<HTMLButtonElement>(null);
|
|
32
32
|
const [isOpen, setOpen] = useState(false);
|
|
@@ -38,9 +38,9 @@ export const useGridPopoverHook = <TData extends GridBaseRow>({
|
|
|
38
38
|
const triggerSave = useCallback(
|
|
39
39
|
async (reason?: string) => {
|
|
40
40
|
if (reason == CloseReason.CANCEL) {
|
|
41
|
+
await updateValue(CancelPromise, 0);
|
|
41
42
|
stopEditing();
|
|
42
|
-
|
|
43
|
-
redrawRows();
|
|
43
|
+
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
46
|
if (invalid?.()) {
|
|
@@ -48,22 +48,16 @@ export const useGridPopoverHook = <TData extends GridBaseRow>({
|
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
if (!save) {
|
|
52
|
-
// No save method so just close
|
|
53
|
-
stopEditing();
|
|
54
|
-
onBulkEditingComplete();
|
|
55
|
-
redrawRows();
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
51
|
if (
|
|
60
|
-
await updateValue(
|
|
52
|
+
await updateValue(
|
|
53
|
+
save ?? (() => Promise.resolve(true)),
|
|
54
|
+
reason === CloseReason.TAB_FORWARD ? 1 : reason === CloseReason.TAB_BACKWARD ? -1 : 0,
|
|
55
|
+
)
|
|
61
56
|
) {
|
|
62
57
|
stopEditing();
|
|
63
|
-
redrawRows();
|
|
64
58
|
}
|
|
65
59
|
},
|
|
66
|
-
[invalid,
|
|
60
|
+
[invalid, save, stopEditing, updateValue],
|
|
67
61
|
);
|
|
68
62
|
|
|
69
63
|
const popoverWrapper = useCallback(
|
|
@@ -116,5 +110,6 @@ export const useGridPopoverHook = <TData extends GridBaseRow>({
|
|
|
116
110
|
return {
|
|
117
111
|
popoverWrapper,
|
|
118
112
|
triggerSave,
|
|
113
|
+
gridPopoverOpen: isOpen,
|
|
119
114
|
};
|
|
120
115
|
};
|
|
@@ -5,6 +5,7 @@ import { Fragment, ReactElement, useCallback, useEffect, useMemo, useRef, useSta
|
|
|
5
5
|
import { useGridPopoverContext } from '../../contexts/GridPopoverContext';
|
|
6
6
|
import { GridSubComponentContext } from '../../contexts/GridSubComponentContext';
|
|
7
7
|
import { FormError } from '../../lui/FormError';
|
|
8
|
+
import { usePrevious } from '../../lui/reactUtils';
|
|
8
9
|
import { FocusableItem, MenuDivider, MenuHeader, MenuItem } from '../../react-menu3';
|
|
9
10
|
import { ClickEvent } from '../../react-menu3/types';
|
|
10
11
|
import { textMatch } from '../../utils/textMatcher';
|
|
@@ -32,7 +33,7 @@ interface FinalSelectOption<TOptionValue> {
|
|
|
32
33
|
export const primitiveToSelectOption = <T,>(value: T): SelectOption<T> => {
|
|
33
34
|
return {
|
|
34
35
|
value: value,
|
|
35
|
-
label: value ? String(value) : '',
|
|
36
|
+
label: value ? String(value) : '-',
|
|
36
37
|
};
|
|
37
38
|
};
|
|
38
39
|
|
|
@@ -84,6 +85,7 @@ export const GridFormDropDown = <TData extends GridBaseRow, TOptionValue>(
|
|
|
84
85
|
props: GridFormDropDownProps<TData, TOptionValue>,
|
|
85
86
|
) => {
|
|
86
87
|
const { selectedRows, field, data } = useGridPopoverContext<TData>();
|
|
88
|
+
const { onSelectFilter, options: propOptions, onSelectedItem } = props;
|
|
87
89
|
|
|
88
90
|
// Save triggers during async action processing which triggers another selectItem(), this ref blocks that
|
|
89
91
|
const [filter, setFilter] = useState(props.filterDefaultValue ?? '');
|
|
@@ -101,8 +103,8 @@ export const GridFormDropDown = <TData extends GridBaseRow, TOptionValue>(
|
|
|
101
103
|
selectedRows.some((row) => row[field] !== value) ||
|
|
102
104
|
(subComponentValue !== undefined && subComponentInitialValue.current !== JSON.stringify(subComponentValue));
|
|
103
105
|
if (hasChanged) {
|
|
104
|
-
if (
|
|
105
|
-
await
|
|
106
|
+
if (onSelectedItem) {
|
|
107
|
+
await onSelectedItem({
|
|
106
108
|
selectedRows,
|
|
107
109
|
selectedRowIds: selectedRows.map((row) => row.id),
|
|
108
110
|
value,
|
|
@@ -114,23 +116,78 @@ export const GridFormDropDown = <TData extends GridBaseRow, TOptionValue>(
|
|
|
114
116
|
}
|
|
115
117
|
return true;
|
|
116
118
|
},
|
|
117
|
-
[field,
|
|
119
|
+
[field, onSelectedItem, selectedRows],
|
|
118
120
|
);
|
|
119
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Saves are wrapped in updateValue and triggered by blur events
|
|
124
|
+
*/
|
|
125
|
+
const save = useCallback(async () => {
|
|
126
|
+
if (!options) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Filter saved
|
|
131
|
+
if (selectedItem === null) {
|
|
132
|
+
if (onSelectFilter) {
|
|
133
|
+
if (!isEmpty(filter)) {
|
|
134
|
+
await onSelectFilter({
|
|
135
|
+
selectedRows,
|
|
136
|
+
selectedRowIds: selectedRows.map((row) => row.id),
|
|
137
|
+
value: filter as any,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return true;
|
|
142
|
+
} else {
|
|
143
|
+
if (filteredValues && filteredValues.length === 1) {
|
|
144
|
+
if (filteredValues[0].subComponent) return false;
|
|
145
|
+
return await selectItemHandler(filteredValues[0].value, null);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
if (selectedItem.subComponent && !subComponentIsValid.current) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
await selectItemHandler(selectedItem.value, subSelectedValue);
|
|
154
|
+
|
|
155
|
+
return true;
|
|
156
|
+
}, [
|
|
157
|
+
filter,
|
|
158
|
+
filteredValues,
|
|
159
|
+
onSelectFilter,
|
|
160
|
+
options,
|
|
161
|
+
selectItemHandler,
|
|
162
|
+
selectedItem,
|
|
163
|
+
selectedRows,
|
|
164
|
+
subSelectedValue,
|
|
165
|
+
]);
|
|
166
|
+
|
|
167
|
+
const { popoverWrapper, gridPopoverOpen } = useGridPopoverHook({
|
|
168
|
+
className: props.className,
|
|
169
|
+
invalid: () => !options || !!(selectedItem && !subComponentIsValid.current),
|
|
170
|
+
save,
|
|
171
|
+
dontSaveOnExternalClick: true,
|
|
172
|
+
});
|
|
173
|
+
|
|
120
174
|
// Load up options list if it's async function
|
|
175
|
+
const prevIsOpen = usePrevious(gridPopoverOpen);
|
|
176
|
+
const prevFilter = usePrevious(filter);
|
|
121
177
|
useEffect(() => {
|
|
122
|
-
if (
|
|
123
|
-
|
|
178
|
+
if ((gridPopoverOpen !== prevIsOpen || filter !== prevFilter) && gridPopoverOpen) {
|
|
179
|
+
let optionsConf = propOptions;
|
|
124
180
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
181
|
+
void (async () => {
|
|
182
|
+
if (typeof optionsConf === 'function') {
|
|
183
|
+
optionsConf = await optionsConf(selectedRows, filter);
|
|
184
|
+
}
|
|
185
|
+
if (optionsConf !== undefined) {
|
|
186
|
+
setOptions(optionsConf);
|
|
187
|
+
}
|
|
188
|
+
})();
|
|
189
|
+
}
|
|
190
|
+
}, [filter, gridPopoverOpen, options, prevFilter, prevIsOpen, propOptions, selectedRows]);
|
|
134
191
|
|
|
135
192
|
// Local filtering.
|
|
136
193
|
useEffect(() => {
|
|
@@ -168,39 +225,6 @@ export const GridFormDropDown = <TData extends GridBaseRow, TOptionValue>(
|
|
|
168
225
|
}
|
|
169
226
|
}, [filter, props, reSearchOnFilterChange]);
|
|
170
227
|
|
|
171
|
-
/**
|
|
172
|
-
* Saves are wrapped in updateValue and triggered by blur events
|
|
173
|
-
*/
|
|
174
|
-
const save = useCallback(async () => {
|
|
175
|
-
if (!options) return true;
|
|
176
|
-
|
|
177
|
-
// Filter saved
|
|
178
|
-
if (selectedItem === null) {
|
|
179
|
-
if (props.onSelectFilter) {
|
|
180
|
-
const { onSelectFilter } = props;
|
|
181
|
-
await onSelectFilter({ selectedRows, selectedRowIds: selectedRows.map((row) => row.id), value: filter as any });
|
|
182
|
-
return true;
|
|
183
|
-
} else {
|
|
184
|
-
if (filteredValues && filteredValues.length === 1) {
|
|
185
|
-
if (filteredValues[0].subComponent) return false;
|
|
186
|
-
return await selectItemHandler(filteredValues[0].value, null);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
return false;
|
|
190
|
-
}
|
|
191
|
-
if (selectedItem.subComponent && !subComponentIsValid.current) return false;
|
|
192
|
-
await selectItemHandler(selectedItem.value, subSelectedValue);
|
|
193
|
-
|
|
194
|
-
return true;
|
|
195
|
-
}, [filter, filteredValues, options, props, selectItemHandler, selectedItem, selectedRows, subSelectedValue]);
|
|
196
|
-
|
|
197
|
-
const { popoverWrapper } = useGridPopoverHook({
|
|
198
|
-
className: props.className,
|
|
199
|
-
invalid: () => !options || !!(selectedItem && !subComponentIsValid.current),
|
|
200
|
-
save,
|
|
201
|
-
dontSaveOnExternalClick: true,
|
|
202
|
-
});
|
|
203
|
-
|
|
204
228
|
let lastHeader: ReactElement | null = null;
|
|
205
229
|
let showHeader: ReactElement | null = null;
|
|
206
230
|
|
|
@@ -230,7 +254,7 @@ export const GridFormDropDown = <TData extends GridBaseRow, TOptionValue>(
|
|
|
230
254
|
!props.onSelectFilter &&
|
|
231
255
|
!(filteredValues && filteredValues.length === 1 && !filteredValues[0].subComponent)
|
|
232
256
|
}
|
|
233
|
-
onChange={(e) => setFilter(e.target.value)}
|
|
257
|
+
onChange={(e) => setFilter(e.target.value.trim())}
|
|
234
258
|
/>
|
|
235
259
|
{props.filterHelpText && isNotEmpty(filter) && (
|
|
236
260
|
<FormError error={null} helpText={props.filterHelpText} />
|
|
@@ -98,10 +98,14 @@ export const GridFormMultiSelect = <TData extends GridBaseRow>(props: GridFormMu
|
|
|
98
98
|
|
|
99
99
|
const save = useCallback(
|
|
100
100
|
async (selectedRows: TData[]): Promise<boolean> => {
|
|
101
|
-
if (!options || !props.onSave)
|
|
101
|
+
if (!options || !props.onSave) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
102
104
|
|
|
103
105
|
// Any changes to save?
|
|
104
|
-
if (initialValues === JSON.stringify(options))
|
|
106
|
+
if (initialValues === JSON.stringify(options)) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
105
109
|
|
|
106
110
|
return props.onSave({
|
|
107
111
|
selectedRows,
|
|
@@ -278,7 +282,9 @@ const FilterInput = (props: {
|
|
|
278
282
|
}, [filter, headerGroups, options, setOptions]);
|
|
279
283
|
|
|
280
284
|
const addCustomFilterValue = useCallback(() => {
|
|
281
|
-
if (!options || !onSelectFilter)
|
|
285
|
+
if (!options || !onSelectFilter) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
282
288
|
|
|
283
289
|
const filterTrimmed = filter.trim();
|
|
284
290
|
if (isEmpty(filterTrimmed)) {
|
|
@@ -289,7 +295,9 @@ const FilterInput = (props: {
|
|
|
289
295
|
const preFilterOptions = JSON.stringify(options);
|
|
290
296
|
onSelectFilter({ filter: filterTrimmed, options });
|
|
291
297
|
// Detect if options list changed and update
|
|
292
|
-
if (preFilterOptions === JSON.stringify(options))
|
|
298
|
+
if (preFilterOptions === JSON.stringify(options)) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
293
301
|
|
|
294
302
|
setOptions([...options]);
|
|
295
303
|
setFilter('');
|
|
@@ -63,8 +63,8 @@ export interface GridContextType<TData extends GridBaseRow> {
|
|
|
63
63
|
invisibleColumnIds: string[] | undefined;
|
|
64
64
|
setInvisibleColumnIds: (colIds: string[]) => void;
|
|
65
65
|
downloadCsv: (csvExportParams?: CsvExportParams) => void;
|
|
66
|
-
onBulkEditingComplete: () => void;
|
|
67
|
-
setOnBulkEditingComplete: (callback: (() => void) | undefined) => void;
|
|
66
|
+
onBulkEditingComplete: () => Promise<void> | void;
|
|
67
|
+
setOnBulkEditingComplete: (callback: (() => Promise<void> | void) | undefined) => void;
|
|
68
68
|
showNoRowsOverlay: () => void;
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -502,8 +502,13 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
|
|
|
502
502
|
|
|
503
503
|
try {
|
|
504
504
|
// Edit in progress so don't edit until finished, timeout waiting after 5s
|
|
505
|
-
if (
|
|
506
|
-
|
|
505
|
+
if (
|
|
506
|
+
!(await waitForCondition(
|
|
507
|
+
'startCellEditing failed as update still in progress, waited for 15 seconds',
|
|
508
|
+
() => !anyUpdating(),
|
|
509
|
+
15000,
|
|
510
|
+
))
|
|
511
|
+
) {
|
|
507
512
|
return;
|
|
508
513
|
}
|
|
509
514
|
|
|
@@ -529,11 +534,12 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
|
|
|
529
534
|
const rowIndex = rowNode.rowIndex;
|
|
530
535
|
if (rowIndex != null) {
|
|
531
536
|
defer(() => {
|
|
532
|
-
!gridApi.isDestroyed()
|
|
537
|
+
if (!gridApi.isDestroyed()) {
|
|
533
538
|
gridApi.startEditingCell({
|
|
534
539
|
rowIndex,
|
|
535
540
|
colKey: colId,
|
|
536
541
|
});
|
|
542
|
+
}
|
|
537
543
|
});
|
|
538
544
|
}
|
|
539
545
|
} finally {
|
|
@@ -543,15 +549,18 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
|
|
|
543
549
|
[anyUpdating, gridApi, prePopupOps, waitForExternallySelectedItemsToBeInSync],
|
|
544
550
|
);
|
|
545
551
|
|
|
546
|
-
const bulkEditingCompleteCallbackRef = useRef<() => void>();
|
|
547
|
-
const onBulkEditingComplete = useCallback(() => {
|
|
552
|
+
const bulkEditingCompleteCallbackRef = useRef<() => Promise<void> | void>();
|
|
553
|
+
const onBulkEditingComplete = useCallback(async () => {
|
|
548
554
|
resetFocusedCellAfterCellEditing();
|
|
549
|
-
bulkEditingCompleteCallbackRef.current?.();
|
|
555
|
+
await bulkEditingCompleteCallbackRef.current?.();
|
|
550
556
|
}, [resetFocusedCellAfterCellEditing]);
|
|
551
557
|
|
|
552
|
-
const setOnBulkEditingComplete = useCallback(
|
|
553
|
-
|
|
554
|
-
|
|
558
|
+
const setOnBulkEditingComplete = useCallback(
|
|
559
|
+
(cellEditingCompleteCallback: (() => Promise<void> | void) | undefined) => {
|
|
560
|
+
bulkEditingCompleteCallbackRef.current = cellEditingCompleteCallback;
|
|
561
|
+
},
|
|
562
|
+
[],
|
|
563
|
+
);
|
|
555
564
|
|
|
556
565
|
/**
|
|
557
566
|
* Returns true if an editable cell on same row was selected, else false.
|
|
@@ -585,8 +594,10 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
|
|
|
585
594
|
prePopupFocusedCell.current = undefined;
|
|
586
595
|
|
|
587
596
|
const preRow = gridApi.getFocusedCell();
|
|
597
|
+
// If we don't do this ag-grid will do its own continuation of an edit on tab, we don't want that as
|
|
598
|
+
// we are managing it ourselves
|
|
599
|
+
gridApi.stopEditing();
|
|
588
600
|
if (tabDirection === 1) {
|
|
589
|
-
gridApi.stopEditing();
|
|
590
601
|
gridApi.tabToNextCell();
|
|
591
602
|
} else {
|
|
592
603
|
gridApi.tabToPreviousCell();
|
|
@@ -629,6 +640,7 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
|
|
|
629
640
|
): Promise<boolean> => {
|
|
630
641
|
try {
|
|
631
642
|
setSaving?.(true);
|
|
643
|
+
|
|
632
644
|
return await gridApiOp(async (gridApi) => {
|
|
633
645
|
const selectedRows = props.selectedRows;
|
|
634
646
|
|
|
@@ -638,10 +650,8 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
|
|
|
638
650
|
props.field ?? '',
|
|
639
651
|
selectedRows.map((data) => data.id),
|
|
640
652
|
async () => {
|
|
641
|
-
// MATT Disabled I don't believe these are needed anymore
|
|
642
|
-
// I've left them here just in case they are
|
|
643
653
|
// Need to refresh to get spinners to work on all rows
|
|
644
|
-
|
|
654
|
+
gridApi.refreshCells({ rowNodes: props.selectedRows as RowNode[], force: true });
|
|
645
655
|
ok = await fnUpdate(selectedRows).catch((ex) => {
|
|
646
656
|
console.error('Exception during modifyUpdating', ex);
|
|
647
657
|
return false;
|
|
@@ -649,23 +659,21 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
|
|
|
649
659
|
},
|
|
650
660
|
);
|
|
651
661
|
|
|
652
|
-
// MATT Disabled I don't believe these are needed anymore
|
|
653
|
-
// I've left them here just in case they are
|
|
654
662
|
// async processes need to refresh their own rows
|
|
655
|
-
|
|
663
|
+
gridApi.refreshCells({ rowNodes: selectedRows as RowNode[], force: true });
|
|
656
664
|
|
|
657
665
|
if (gridApi.isDestroyed()) {
|
|
658
666
|
return ok;
|
|
659
667
|
}
|
|
660
668
|
|
|
661
669
|
if (ok) {
|
|
662
|
-
const cell = gridApi.getFocusedCell();
|
|
663
|
-
if (cell && gridApi.getFocusedCell() == null) {
|
|
664
|
-
|
|
665
|
-
}
|
|
670
|
+
//const cell = gridApi.getFocusedCell();
|
|
671
|
+
//if (cell && gridApi.getFocusedCell() == null) {
|
|
672
|
+
// !gridApi.isDestroyed && gridApi.setFocusedCell(cell.rowIndex, cell.column);
|
|
673
|
+
// }
|
|
666
674
|
|
|
667
675
|
// This is needed to trigger postSortRowsHook
|
|
668
|
-
gridApi.refreshClientSideRowModel();
|
|
676
|
+
!gridApi.isDestroyed && gridApi.refreshClientSideRowModel();
|
|
669
677
|
}
|
|
670
678
|
|
|
671
679
|
void (async () => {
|
|
@@ -678,10 +686,10 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
|
|
|
678
686
|
prePopupFocusedCell.current.column.getColId() == postPopupFocusedCell.column.getColId()
|
|
679
687
|
) {
|
|
680
688
|
if (!tabDirection || !(await selectNextEditableCell(tabDirection))) {
|
|
681
|
-
onBulkEditingComplete();
|
|
689
|
+
await onBulkEditingComplete();
|
|
682
690
|
}
|
|
683
691
|
} else {
|
|
684
|
-
onBulkEditingComplete();
|
|
692
|
+
await onBulkEditingComplete();
|
|
685
693
|
}
|
|
686
694
|
})();
|
|
687
695
|
|
|
@@ -30,7 +30,7 @@ export default {
|
|
|
30
30
|
],
|
|
31
31
|
} as Meta<typeof Grid>;
|
|
32
32
|
|
|
33
|
-
const GridPopoutEditGenericTemplate: StoryFn<typeof Grid
|
|
33
|
+
const GridPopoutEditGenericTemplate: StoryFn<typeof Grid<IFormTestRow>> = (props: GridProps<IFormTestRow>) => {
|
|
34
34
|
const [externalSelectedItems, setExternalSelectedItems] = useState<any[]>([]);
|
|
35
35
|
const columnDefs: ColDefT<IFormTestRow>[] = useMemo(
|
|
36
36
|
() => [
|
|
@@ -49,7 +49,7 @@ interface ITestRow {
|
|
|
49
49
|
position3: string | null;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
const GridEditMultiSelectTemplate: StoryFn<typeof Grid
|
|
52
|
+
const GridEditMultiSelectTemplate: StoryFn<typeof Grid<ITestRow>> = (props: GridProps<ITestRow>) => {
|
|
53
53
|
const [externalSelectedItems, setExternalSelectedItems] = useState<any[]>([]);
|
|
54
54
|
|
|
55
55
|
const columnDefs: ColDefT<ITestRow>[] = useMemo(() => {
|
|
@@ -39,7 +39,7 @@ interface ITestRow {
|
|
|
39
39
|
position2: string | null;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
const GridEditMultiSelectGridTemplate: StoryFn<typeof Grid
|
|
42
|
+
const GridEditMultiSelectGridTemplate: StoryFn<typeof Grid<ITestRow>> = (props: GridProps<ITestRow>) => {
|
|
43
43
|
const [externalSelectedItems, setExternalSelectedItems] = useState<any[]>([]);
|
|
44
44
|
|
|
45
45
|
const columnDefs: ColDefT<ITestRow>[] = useMemo(() => {
|
|
@@ -123,7 +123,7 @@ GridFormDropDownInteractions_.play = async ({ canvasElement }) => {
|
|
|
123
123
|
// Test escape to not save
|
|
124
124
|
updateValue.mockClear();
|
|
125
125
|
await userEvent.type(textInput, '{Escape}');
|
|
126
|
-
expect(updateValue).
|
|
126
|
+
expect(updateValue).toHaveBeenCalledWith(expect.anything(), 0);
|
|
127
127
|
|
|
128
128
|
// Test invalid value doesn't save
|
|
129
129
|
updateValue.mockClear();
|
package/src/stories/grid/gridFormInteraction/GridFormEditBearingCorrectionInteraction.stories.tsx
CHANGED
|
@@ -9,6 +9,7 @@ import { useRef } from 'react';
|
|
|
9
9
|
import { expect, fn, userEvent, within } from 'storybook/test';
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
|
+
CancelPromise,
|
|
12
13
|
GridContextProvider,
|
|
13
14
|
GridFormEditBearing,
|
|
14
15
|
GridFormEditBearingProps,
|
|
@@ -86,7 +87,7 @@ GridFormEditBearingCorrectionInteractions_.play = async ({ canvasElement }) => {
|
|
|
86
87
|
// Test escape not to save
|
|
87
88
|
updateValue.mockClear();
|
|
88
89
|
await userEvent.type(inputField, '{Escape}');
|
|
89
|
-
expect(updateValue).
|
|
90
|
+
expect(updateValue).toHaveBeenCalledWith(expect.anything(), 0);
|
|
90
91
|
|
|
91
92
|
// Test invalid value doesn't save
|
|
92
93
|
updateValue.mockClear();
|
|
@@ -86,7 +86,7 @@ GridFormEditBearingInteractions_.play = async ({ canvasElement }) => {
|
|
|
86
86
|
// Test escape not to save
|
|
87
87
|
updateValue.mockClear();
|
|
88
88
|
await userEvent.type(inputField, '{Escape}');
|
|
89
|
-
expect(updateValue).
|
|
89
|
+
expect(updateValue).toHaveBeenCalledWith(expect.anything(), 0);
|
|
90
90
|
|
|
91
91
|
// Test invalid value doesn't save
|
|
92
92
|
updateValue.mockClear();
|
|
@@ -135,7 +135,8 @@ GridFormMultiSelectInteractions_.play = async ({ canvasElement }) => {
|
|
|
135
135
|
updateValue.mockClear();
|
|
136
136
|
onSave.mockClear();
|
|
137
137
|
await userEvent.type(textInput, '{Escape}');
|
|
138
|
-
expect(updateValue).not.toHaveBeenCalled();
|
|
138
|
+
// expect(updateValue).not.toHaveBeenCalled();
|
|
139
|
+
expect(updateValue).toHaveBeenCalledWith(expect.anything(), 0);
|
|
139
140
|
expect(onSave).not.toHaveBeenCalled();
|
|
140
141
|
|
|
141
142
|
// Test invalid value doesn't save
|
|
@@ -131,7 +131,7 @@ GridFormPopoverMenuInteractions_.play = async ({ canvasElement }) => {
|
|
|
131
131
|
// Test escape to not save
|
|
132
132
|
updateValue.mockClear();
|
|
133
133
|
await userEvent.type(textInput, '{Escape}');
|
|
134
|
-
expect(updateValue).
|
|
134
|
+
expect(updateValue).toHaveBeenCalledWith(expect.anything(), 0);
|
|
135
135
|
|
|
136
136
|
// Test invalid value doesn't save
|
|
137
137
|
updateValue.mockClear();
|
|
@@ -163,7 +163,7 @@ GridFormPopoverMenuInteractions_.play = async ({ canvasElement }) => {
|
|
|
163
163
|
// Test escape to not save
|
|
164
164
|
updateValue.mockClear();
|
|
165
165
|
await userEvent.type(textArea, '{Escape}');
|
|
166
|
-
expect(updateValue).
|
|
166
|
+
expect(updateValue).toHaveBeenCalledWith(expect.anything(), 0);
|
|
167
167
|
|
|
168
168
|
// Test invalid value doesn't save
|
|
169
169
|
updateValue.mockClear();
|
|
@@ -71,7 +71,7 @@ GridFormTextAreaInteractions_.play = async ({ canvasElement }) => {
|
|
|
71
71
|
// Test escape not to save
|
|
72
72
|
updateValue.mockClear();
|
|
73
73
|
await userEvent.type(inputField, '{Escape}');
|
|
74
|
-
expect(updateValue).
|
|
74
|
+
expect(updateValue).toHaveBeenCalledWith(expect.anything(), 0);
|
|
75
75
|
|
|
76
76
|
// Test invalid value doesn't save
|
|
77
77
|
updateValue.mockClear();
|
|
@@ -76,7 +76,7 @@ GridFormTextInputInteractions_.play = async ({ canvasElement }) => {
|
|
|
76
76
|
// Test escape not to save
|
|
77
77
|
updateValue.mockClear();
|
|
78
78
|
await userEvent.type(inputField, '{Escape}');
|
|
79
|
-
expect(updateValue).
|
|
79
|
+
expect(updateValue).toHaveBeenCalledWith(expect.anything(), 0);
|
|
80
80
|
|
|
81
81
|
// Test invalid value doesn't save
|
|
82
82
|
updateValue.mockClear();
|