@linzjs/step-ag-grid 29.1.4 → 29.1.6
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 +0 -1
- package/dist/src/contexts/GridContext.d.ts +5 -4
- package/dist/step-ag-grid.cjs +55 -65
- package/dist/step-ag-grid.cjs.map +1 -1
- package/dist/step-ag-grid.esm.js +56 -66
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Grid.tsx +13 -4
- package/src/components/GridCell.tsx +5 -24
- package/src/components/GridPopoverHook.tsx +0 -1
- package/src/components/gridForm/GridFormDropDown.tsx +44 -35
- package/src/contexts/GridContext.tsx +6 -1
- package/src/contexts/GridContextProvider.tsx +3 -5
- package/src/stories/grid/gridFormInteraction/GridFormEditBearingCorrectionInteraction.stories.tsx +0 -1
- package/src/stories/grid/interactions/GridKeyboardInteractions.stories.tsx +4 -6
package/package.json
CHANGED
package/src/components/Grid.tsx
CHANGED
|
@@ -142,6 +142,7 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
|
|
|
142
142
|
rowData,
|
|
143
143
|
rowHeight = theme === 'ag-theme-step-default' ? 40 : theme === 'ag-theme-step-compact' ? 36 : 40,
|
|
144
144
|
selectable,
|
|
145
|
+
onCellFocused: paramsOnCellFocused,
|
|
145
146
|
...params
|
|
146
147
|
}: GridProps<TData>): ReactElement => {
|
|
147
148
|
const {
|
|
@@ -450,7 +451,6 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
|
|
|
450
451
|
const invokeEditAction = (e: CellDoubleClickedEvent | CellKeyDownEvent): boolean => {
|
|
451
452
|
const editAction = e.colDef?.cellRendererParams?.editAction;
|
|
452
453
|
if (!editAction) return false;
|
|
453
|
-
|
|
454
454
|
const editable = fnOrVar(e.colDef?.editable, e);
|
|
455
455
|
if (editable) {
|
|
456
456
|
if (!e.node.isSelected()) {
|
|
@@ -625,7 +625,7 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
|
|
|
625
625
|
|
|
626
626
|
const onCellFocused = useCallback(
|
|
627
627
|
(event: CellFocusedEvent<TData>) => {
|
|
628
|
-
if (!
|
|
628
|
+
if (!paramsOnCellFocused || event.rowIndex == null) {
|
|
629
629
|
return;
|
|
630
630
|
}
|
|
631
631
|
const api = event.api;
|
|
@@ -639,9 +639,17 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
|
|
|
639
639
|
if (!colDef || typeof colDef === 'string') {
|
|
640
640
|
return;
|
|
641
641
|
}
|
|
642
|
-
|
|
642
|
+
// Prevent repeated callbacks to cell focus when it focus didn't change
|
|
643
|
+
const { sourceEvent } = event;
|
|
644
|
+
if (sourceEvent) {
|
|
645
|
+
if ((window as any).__stepaggrid_lastfocuseventtarget === sourceEvent.target) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
(window as any).__stepaggrid_lastfocuseventtarget = sourceEvent.target;
|
|
649
|
+
}
|
|
650
|
+
paramsOnCellFocused({ colDef, data });
|
|
643
651
|
},
|
|
644
|
-
[
|
|
652
|
+
[paramsOnCellFocused],
|
|
645
653
|
);
|
|
646
654
|
|
|
647
655
|
const onRowDragEnd = useCallback(
|
|
@@ -812,6 +820,7 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
|
|
|
812
820
|
pinnedBottomRowData={params.pinnedBottomRowData}
|
|
813
821
|
onRowClicked={params.onRowClicked}
|
|
814
822
|
onRowDoubleClicked={params.onRowDoubleClicked}
|
|
823
|
+
suppressStartEditOnTab={true}
|
|
815
824
|
/>
|
|
816
825
|
</div>
|
|
817
826
|
</div>
|
|
@@ -11,8 +11,7 @@ import {
|
|
|
11
11
|
ValueFormatterParams,
|
|
12
12
|
ValueGetterFunc,
|
|
13
13
|
} from 'ag-grid-community';
|
|
14
|
-
import {
|
|
15
|
-
import { forwardRef, ReactElement, useContext, useEffect } from 'react';
|
|
14
|
+
import { forwardRef, ReactElement, useContext } from 'react';
|
|
16
15
|
|
|
17
16
|
import { GridPopoverContextProvider } from '../contexts/GridPopoverContextProvider';
|
|
18
17
|
import { GridUpdatingContext } from '../contexts/GridUpdatingContext';
|
|
@@ -155,14 +154,10 @@ export const GridCell = <TData extends GridBaseRow, TValue = any, Props extends
|
|
|
155
154
|
resizable: true,
|
|
156
155
|
valueSetter: custom?.editor ? blockValueSetter : undefined,
|
|
157
156
|
editable: props.editable ?? !!custom?.editor,
|
|
158
|
-
...(custom?.editor
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
: {
|
|
164
|
-
cellEditor: CellEditorToBlockEditing,
|
|
165
|
-
}),
|
|
157
|
+
...(custom?.editor && {
|
|
158
|
+
cellClassRules: GridCellMultiSelectClassRules,
|
|
159
|
+
cellEditor: GenericCellEditorComponentWrapper(custom?.editor),
|
|
160
|
+
}),
|
|
166
161
|
suppressKeyboardEvent: suppressCellKeyboardEvents,
|
|
167
162
|
...(custom?.editorParams
|
|
168
163
|
? {
|
|
@@ -190,20 +185,6 @@ export const GridCell = <TData extends GridBaseRow, TValue = any, Props extends
|
|
|
190
185
|
};
|
|
191
186
|
};
|
|
192
187
|
|
|
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
|
-
|
|
207
188
|
export interface CellEditorCommon {
|
|
208
189
|
className?: string | undefined;
|
|
209
190
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import debounce from 'debounce-promise';
|
|
2
|
-
import { isEmpty } from 'lodash-es';
|
|
2
|
+
import { compact, isEmpty } from 'lodash-es';
|
|
3
3
|
import { Fragment, ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
|
|
5
5
|
import { useGridPopoverContext } from '../../contexts/GridPopoverContext';
|
|
@@ -90,7 +90,9 @@ export const GridFormDropDown = <TData extends GridBaseRow, TOptionValue>(
|
|
|
90
90
|
// Save triggers during async action processing which triggers another selectItem(), this ref blocks that
|
|
91
91
|
const [filter, setFilter] = useState(props.filterDefaultValue ?? '');
|
|
92
92
|
const [filteredValues, setFilteredValues] = useState<any[]>();
|
|
93
|
-
const [options, setOptions] = useState<FinalSelectOption<TOptionValue>[] | null>(
|
|
93
|
+
const [options, setOptions] = useState<FinalSelectOption<TOptionValue>[] | null>(
|
|
94
|
+
!!propOptions && typeof propOptions !== 'function' ? propOptions : null,
|
|
95
|
+
);
|
|
94
96
|
const subComponentIsValid = useRef(false);
|
|
95
97
|
const subComponentInitialValue = useRef<string | null>(null);
|
|
96
98
|
const [subSelectedValue, setSubSelectedValue] = useState<any>(null);
|
|
@@ -164,7 +166,7 @@ export const GridFormDropDown = <TData extends GridBaseRow, TOptionValue>(
|
|
|
164
166
|
subSelectedValue,
|
|
165
167
|
]);
|
|
166
168
|
|
|
167
|
-
const { popoverWrapper
|
|
169
|
+
const { popoverWrapper } = useGridPopoverHook({
|
|
168
170
|
className: props.className,
|
|
169
171
|
invalid: () => !options || !!(selectedItem && !subComponentIsValid.current),
|
|
170
172
|
save,
|
|
@@ -172,39 +174,49 @@ export const GridFormDropDown = <TData extends GridBaseRow, TOptionValue>(
|
|
|
172
174
|
});
|
|
173
175
|
|
|
174
176
|
// Load up options list if it's async function
|
|
175
|
-
const prevIsOpen = usePrevious(gridPopoverOpen);
|
|
176
|
-
const prevFilter = usePrevious(filter);
|
|
177
177
|
useEffect(() => {
|
|
178
|
-
|
|
179
|
-
|
|
178
|
+
// If options is null then we need to load/reload
|
|
179
|
+
// Options will be set to null during a reload based filter, or on open popup
|
|
180
|
+
if (options !== null) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
180
183
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
})();
|
|
184
|
+
// propOptions is a const list
|
|
185
|
+
if (typeof propOptions !== 'function') {
|
|
186
|
+
if (propOptions) {
|
|
187
|
+
setOptions(propOptions);
|
|
188
|
+
}
|
|
189
|
+
return;
|
|
189
190
|
}
|
|
190
|
-
|
|
191
|
+
|
|
192
|
+
// propOptions is function, probably loading from web
|
|
193
|
+
void (async () => {
|
|
194
|
+
const r = await propOptions(selectedRows, filter);
|
|
195
|
+
setOptions(r ?? []);
|
|
196
|
+
})();
|
|
197
|
+
}, [filter, options, propOptions, selectedRows]);
|
|
191
198
|
|
|
192
199
|
// Local filtering.
|
|
193
200
|
useEffect(() => {
|
|
194
|
-
if (props.filtered
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
console.error('Cannot filter non-string labels', option);
|
|
201
|
-
return undefined;
|
|
202
|
-
}
|
|
203
|
-
return textMatch((option.label as string) || '', filter) ? option : undefined;
|
|
204
|
-
})
|
|
205
|
-
.filter((r) => r !== undefined),
|
|
206
|
-
);
|
|
201
|
+
if (props.filtered !== 'local') {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (options == null) {
|
|
205
|
+
setFilteredValues([]);
|
|
206
|
+
return;
|
|
207
207
|
}
|
|
208
|
+
|
|
209
|
+
setFilteredValues(
|
|
210
|
+
compact(
|
|
211
|
+
options.map((option) => {
|
|
212
|
+
if (option.label != null && typeof option.label !== 'string') {
|
|
213
|
+
console.warn('GridFormDropDown: Cannot filter non-string labels', option);
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
return textMatch((option.label as string) || '', filter) ? option : undefined;
|
|
217
|
+
}),
|
|
218
|
+
),
|
|
219
|
+
);
|
|
208
220
|
}, [props.filtered, filter, options]);
|
|
209
221
|
|
|
210
222
|
const reSearchOnFilterChange = useMemo(
|
|
@@ -215,15 +227,12 @@ export const GridFormDropDown = <TData extends GridBaseRow, TOptionValue>(
|
|
|
215
227
|
[],
|
|
216
228
|
);
|
|
217
229
|
|
|
218
|
-
const previousFilter =
|
|
219
|
-
|
|
220
|
-
// Reload filtering.
|
|
230
|
+
const previousFilter = usePrevious(filter);
|
|
221
231
|
useEffect(() => {
|
|
222
|
-
if (previousFilter
|
|
223
|
-
previousFilter.current = filter;
|
|
232
|
+
if (previousFilter != null && previousFilter != filter && props.filtered === 'reload') {
|
|
224
233
|
void reSearchOnFilterChange();
|
|
225
234
|
}
|
|
226
|
-
}, [filter, props, reSearchOnFilterChange]);
|
|
235
|
+
}, [filter, previousFilter, props.filtered, reSearchOnFilterChange]);
|
|
227
236
|
|
|
228
237
|
let lastHeader: ReactElement | null = null;
|
|
229
238
|
let showHeader: ReactElement | null = null;
|
|
@@ -15,6 +15,11 @@ export interface AutoSizeColumnsProps {
|
|
|
15
15
|
|
|
16
16
|
export type AutoSizeColumnsResult = { width: number } | null;
|
|
17
17
|
|
|
18
|
+
export interface StartCellEditingProps {
|
|
19
|
+
rowId: number;
|
|
20
|
+
colId: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
18
23
|
export interface GridContextType<TData extends GridBaseRow> {
|
|
19
24
|
gridReady: boolean;
|
|
20
25
|
gridRenderState: () => null | 'empty' | 'rows-visible';
|
|
@@ -43,7 +48,7 @@ export interface GridContextType<TData extends GridBaseRow> {
|
|
|
43
48
|
getFirstRowId: () => number;
|
|
44
49
|
autoSizeColumns: (props?: AutoSizeColumnsProps) => AutoSizeColumnsResult;
|
|
45
50
|
sizeColumnsToFit: () => void;
|
|
46
|
-
startCellEditing: ({ rowId, colId }:
|
|
51
|
+
startCellEditing: ({ rowId, colId }: StartCellEditingProps) => Promise<void>;
|
|
47
52
|
// Restores the previous focus after cell editing
|
|
48
53
|
resetFocusedCellAfterCellEditing: () => void;
|
|
49
54
|
updatingCells: (
|
|
@@ -596,11 +596,9 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
|
|
|
596
596
|
const preRow = gridApi.getFocusedCell();
|
|
597
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
598
|
// we are managing it ourselves
|
|
599
|
-
gridApi.
|
|
600
|
-
if (
|
|
601
|
-
|
|
602
|
-
} else {
|
|
603
|
-
gridApi.tabToPreviousCell();
|
|
599
|
+
const didTab = tabDirection === 1 ? gridApi.tabToNextCell() : gridApi.tabToPreviousCell();
|
|
600
|
+
if (!didTab) {
|
|
601
|
+
break;
|
|
604
602
|
}
|
|
605
603
|
|
|
606
604
|
if (gridApi.isDestroyed()) {
|
|
@@ -275,13 +275,11 @@ GridKeyboardInteractions.play = async ({ canvasElement }) => {
|
|
|
275
275
|
await test(() => userEvent.keyboard('{Enter}'), '8', '2');
|
|
276
276
|
expect(multiEditAction).toHaveBeenCalled();
|
|
277
277
|
|
|
278
|
-
console.log('Open 2nd to last popup menu, tab to next disabled popup');
|
|
279
|
-
await test(() => userEvent.tab(), '
|
|
278
|
+
console.log('Open 2nd to last popup menu, tab to next disabled popup should fail');
|
|
279
|
+
await test(() => userEvent.tab(), '8', '2');
|
|
280
280
|
expect(bulkEditingCallback).toHaveBeenCalled();
|
|
281
281
|
bulkEditingCallback.mockClear();
|
|
282
282
|
|
|
283
|
-
console.log('Fail to edit last popup menu, tab back to 2nd to last popup menu');
|
|
284
|
-
await userEvent.tab({ shift: true });
|
|
285
283
|
console.log('Open 2nd to last popup menu, tab to desc cell');
|
|
286
284
|
await test(() => userEvent.tab({ shift: true }), '5', '2');
|
|
287
285
|
console.log('Cancel edit');
|
|
@@ -312,9 +310,9 @@ GridKeyboardInteractions.play = async ({ canvasElement }) => {
|
|
|
312
310
|
await userEvent.tab();
|
|
313
311
|
|
|
314
312
|
expect(eAction).not.toHaveBeenCalled();
|
|
315
|
-
|
|
313
|
+
await userEvent.keyboard('{Enter}');
|
|
316
314
|
await userEvent.keyboard('e');
|
|
317
315
|
await waitFor(() => {
|
|
318
316
|
expect(eAction).toHaveBeenCalled();
|
|
319
|
-
})
|
|
317
|
+
});
|
|
320
318
|
};
|