@linzjs/step-ag-grid 28.5.0 → 29.0.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 (42) hide show
  1. package/dist/index.css +11 -7
  2. package/dist/src/components/Grid.d.ts +5 -1
  3. package/dist/src/contexts/GridContext.d.ts +3 -4
  4. package/dist/src/contexts/GridPopoverContext.d.ts +1 -0
  5. package/dist/src/contexts/GridUpdatingContext.d.ts +1 -0
  6. package/dist/src/lui/reactUtils.d.ts +4 -0
  7. package/dist/step-ag-grid.cjs +265 -247
  8. package/dist/step-ag-grid.cjs.map +1 -1
  9. package/dist/step-ag-grid.esm.js +266 -248
  10. package/dist/step-ag-grid.esm.js.map +1 -1
  11. package/package.json +21 -22
  12. package/src/components/Grid.tsx +118 -113
  13. package/src/components/GridPopoverHook.tsx +25 -20
  14. package/src/components/gridFilter/useGridFilter.ts +3 -3
  15. package/src/components/gridHook/useGridContextMenu.tsx +3 -3
  16. package/src/contexts/GridContext.tsx +11 -14
  17. package/src/contexts/GridContextProvider.tsx +164 -137
  18. package/src/contexts/GridPopoverContext.tsx +2 -0
  19. package/src/contexts/GridPopoverContextProvider.tsx +9 -5
  20. package/src/contexts/GridUpdatingContext.tsx +5 -0
  21. package/src/contexts/GridUpdatingContextProvider.tsx +35 -28
  22. package/src/lui/reactUtils.tsx +24 -0
  23. package/src/react-menu3/components/MenuList.tsx +3 -0
  24. package/src/stories/grid/GridFilterButtons.stories.tsx +1 -1
  25. package/src/stories/grid/GridNonEditableRow.stories.tsx +1 -1
  26. package/src/stories/grid/GridPinnedRow.stories.tsx +1 -1
  27. package/src/stories/grid/GridPopoutContextMenu.stories.tsx +3 -3
  28. package/src/stories/grid/GridPopoutEditGenericTextArea.stories.tsx +2 -2
  29. package/src/stories/grid/GridPopoverEditDropDown.stories.tsx +8 -4
  30. package/src/stories/grid/GridPopoverEditMultiSelect.stories.tsx +1 -0
  31. package/src/stories/grid/GridReadOnly.stories.tsx +1 -1
  32. package/src/stories/grid/GridViewList.stories.tsx +1 -1
  33. package/src/stories/grid/gridFormInteraction/GridFormDropDownInteraction.stories.tsx +5 -3
  34. package/src/stories/grid/gridFormInteraction/GridFormEditBearingCorrectionInteraction.stories.tsx +3 -2
  35. package/src/stories/grid/gridFormInteraction/GridFormEditBearingInteraction.stories.tsx +3 -2
  36. package/src/stories/grid/gridFormInteraction/GridFormMultiSelectGridInteraction.stories.tsx +3 -2
  37. package/src/stories/grid/gridFormInteraction/GridFormMultiSelectInteraction.stories.tsx +3 -2
  38. package/src/stories/grid/gridFormInteraction/GridFormPopoverMenuInteraction.stories.tsx +3 -2
  39. package/src/stories/grid/gridFormInteraction/GridFormTextAreaInteraction.stories.tsx +3 -2
  40. package/src/stories/grid/gridFormInteraction/GridFormTextInputInteraction.stories.tsx +3 -2
  41. package/src/stories/grid/gridFormStatic/GridFormDropDown.stories.tsx +2 -2
  42. package/src/stories/grid/interactions/GridKeyboardInteractions.stories.tsx +2 -2
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@linzjs/step-ag-grid",
3
3
  "repository": "github:linz/step-ag-grid.git",
4
4
  "license": "MIT",
5
- "version": "28.5.0",
5
+ "version": "29.0.0",
6
6
  "keywords": [
7
7
  "aggrid",
8
8
  "ag-grid",
@@ -28,8 +28,8 @@
28
28
  },
29
29
  "peerDependencies": {
30
30
  "@linzjs/lui": ">=21",
31
- "ag-grid-community": "^34.1.0",
32
- "ag-grid-react": "^34.1.0",
31
+ "ag-grid-community": "^34.2.0",
32
+ "ag-grid-react": "^34.2.0",
33
33
  "lodash-es": ">=4",
34
34
  "react": ">=18",
35
35
  "react-dom": ">=18"
@@ -76,21 +76,21 @@
76
76
  ]
77
77
  },
78
78
  "optionalDependencies": {
79
- "@rollup/rollup-linux-x64-gnu": "^4.46.2",
80
- "@swc/core-linux-x64-gnu": "^1.13.3"
79
+ "@rollup/rollup-linux-x64-gnu": "^4.52.3",
80
+ "@swc/core-linux-x64-gnu": "^1.13.20"
81
81
  },
82
82
  "devDependencies": {
83
- "@chromatic-com/storybook": "^4.0.1",
83
+ "@chromatic-com/storybook": "^4.1.1",
84
84
  "@linzjs/lui": "^23.11.1",
85
85
  "@linzjs/style": "^5.4.0",
86
86
  "@rollup/plugin-commonjs": "^28.0.6",
87
87
  "@rollup/plugin-json": "^6.1.0",
88
88
  "@rollup/plugin-node-resolve": "^16.0.1",
89
89
  "@semantic-release/git": "^10.0.1",
90
- "@storybook/addon-docs": "^9.1.0",
91
- "@storybook/addon-links": "^9.1.0",
92
- "@storybook/react": "^9.1.0",
93
- "@storybook/react-vite": "^9.1.0",
90
+ "@storybook/addon-docs": "^9.1.8",
91
+ "@storybook/addon-links": "^9.1.8",
92
+ "@storybook/react": "^9.1.8",
93
+ "@storybook/react-vite": "^9.1.8",
94
94
  "@testing-library/dom": "^10.4.1",
95
95
  "@testing-library/react": "^16.3.0",
96
96
  "@testing-library/user-event": "^14.6.1",
@@ -100,42 +100,41 @@
100
100
  "@types/node": "^22.17.0",
101
101
  "@types/react": "^18.3.23",
102
102
  "@types/react-dom": "^18.3.7",
103
- "@types/uuid": "^10.0.0",
104
103
  "@vitejs/plugin-react-swc": "^3.11.0",
105
104
  "@vitest/ui": "^3.2.4",
106
- "ag-grid-community": "^34.1.0",
107
- "ag-grid-react": "^34.1.0",
105
+ "ag-grid-community": "^34.2.0",
106
+ "ag-grid-react": "^34.2.0",
108
107
  "babel-preset-react-app": "^10.1.0",
109
108
  "canvas": "^3.2.0",
110
- "chromatic": "^13.1.3",
111
- "cross-env": "^10.0.0",
109
+ "chromatic": "^13.3.0",
110
+ "cross-env": "^10.1.0",
112
111
  "css-loader": "^7.1.2",
113
112
  "eslint-plugin-react": "^7.37.5",
114
- "eslint-plugin-storybook": "^9.1.0",
113
+ "eslint-plugin-storybook": "^9.1.8",
115
114
  "jsdom": "^26.1.0",
116
115
  "lodash-es": "^4.17.21",
117
116
  "mkdirp": "^3.0.1",
118
117
  "npm-run-all": "^4.1.5",
119
118
  "postcss": "^8.5.6",
120
- "postcss-loader": "^8.1.1",
119
+ "postcss-loader": "^8.2.0",
121
120
  "postcss-scss": "^4.0.9",
122
121
  "react": ">=18",
123
122
  "react-app-polyfill": "^3.0.0",
124
123
  "react-dom": "^18.3.1",
125
124
  "rimraf": "^6.0.1",
126
- "rollup": "^4.46.2",
125
+ "rollup": "^4.52.3",
127
126
  "rollup-plugin-copy": "^3.5.0",
128
127
  "rollup-plugin-peer-deps-external": "^2.2.4",
129
128
  "rollup-plugin-postcss": "^4.0.2",
130
129
  "rollup-plugin-typescript2": "^0.36.0",
131
- "sass": "^1.90.0",
130
+ "sass": "^1.93.2",
132
131
  "sass-loader": "^16.0.5",
133
- "semantic-release": "^24.2.7",
134
- "storybook": "^9.1.0",
132
+ "semantic-release": "^24.2.9",
133
+ "storybook": "^9.1.8",
135
134
  "storybook-css-modules-preset": "^1.1.1",
136
135
  "style-loader": "^4.0.0",
137
136
  "typescript": "^5.9.2",
138
- "vite": "^6.3.5",
137
+ "vite": "^7.1.7",
139
138
  "vite-plugin-html": "^3.2.2",
140
139
  "vite-tsconfig-paths": "^5.1.4",
141
140
  "vitest": "^3.2.4"
@@ -4,13 +4,14 @@ import {
4
4
  CellClassParams,
5
5
  CellClickedEvent,
6
6
  CellDoubleClickedEvent,
7
- CellEditingStartedEvent,
7
+ CellFocusedEvent,
8
8
  CellKeyDownEvent,
9
9
  ColDef,
10
10
  ColGroupDef,
11
11
  ColumnResizedEvent,
12
12
  EditableCallback,
13
13
  EditableCallbackParams,
14
+ GetRowIdParams,
14
15
  GridOptions,
15
16
  GridReadyEvent,
16
17
  ModelUpdatedEvent,
@@ -18,6 +19,7 @@ import {
18
19
  RowDragEndEvent,
19
20
  RowDragMoveEvent,
20
21
  SelectionChangedEvent,
22
+ SelectionColumnDef,
21
23
  } from 'ag-grid-community';
22
24
  import { AgGridReact } from 'ag-grid-react';
23
25
  import clsx from 'clsx';
@@ -25,7 +27,7 @@ import { defer, difference, isEmpty, last, omit, xorBy } from 'lodash-es';
25
27
  import { ReactElement, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
26
28
  import { useInterval } from 'usehooks-ts';
27
29
 
28
- import { GridContext, useGridContext } from '../contexts/GridContext';
30
+ import { useGridContext } from '../contexts/GridContext';
29
31
  import { GridUpdatingContext } from '../contexts/GridUpdatingContext';
30
32
  import { fnOrVar, isNotEmpty } from '../utils/util';
31
33
  import { clickInputWhenContainingCellClicked } from './clickInputWhenContainingCellClicked';
@@ -36,36 +38,6 @@ import { usePostSortRowsHook } from './PostSortRowsHook';
36
38
 
37
39
  ModuleRegistry.registerModules([AllCommunityModule]);
38
40
 
39
- let timeOfLastSingleClick = 0;
40
- let lastClickColId: unknown;
41
- let lastClickRowIndex: unknown;
42
-
43
- const resetClickDebounce = () => {
44
- timeOfLastSingleClick = 0;
45
- lastClickColId = '';
46
- lastClickRowIndex = -1;
47
- };
48
-
49
- /**
50
- * If click is more than 200ms since last click return true.
51
- */
52
- const clickDebounceSkipClick = (colId: unknown, rowIndex: unknown): boolean => {
53
- const doubleClickMs = 200;
54
-
55
- if (
56
- Date.now() - timeOfLastSingleClick < doubleClickMs &&
57
- lastClickColId === colId &&
58
- lastClickRowIndex === rowIndex
59
- ) {
60
- // Skipping double click due to single click edit
61
- return true;
62
- }
63
- timeOfLastSingleClick = Date.now();
64
- lastClickColId = colId;
65
- lastClickRowIndex = rowIndex;
66
- return false;
67
- };
68
-
69
41
  export interface GridBaseRow {
70
42
  id: string | number;
71
43
  }
@@ -99,6 +71,7 @@ export interface GridProps<TData extends GridBaseRow = GridBaseRow> {
99
71
  rowClassRules?: GridOptions['rowClassRules'];
100
72
  rowSelection?: 'single' | 'multiple';
101
73
  autoSelectFirstRow?: boolean;
74
+ onCellFocused?: (props: { colDef: ColDef<TData>; data: TData }) => void;
102
75
  onColumnMoved?: GridOptions['onColumnMoved'];
103
76
  rowDragText?: GridOptions['rowDragText'];
104
77
  onRowDragEnd?: (props: GridOnRowDragEndProps<TData>) => Promise<void> | void;
@@ -126,7 +99,7 @@ export interface GridProps<TData extends GridBaseRow = GridBaseRow> {
126
99
  * When pressing tab whilst editing the grid will select and edit the next cell if available.
127
100
  * Once the last cell to edit closes this callback is called.
128
101
  */
129
- onCellEditingComplete?: () => void;
102
+ onBulkEditingComplete?: () => void;
130
103
 
131
104
  /**
132
105
  * Context menu definition if required.
@@ -182,13 +155,13 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
182
155
  setExternallySelectedItemsAreInSync,
183
156
  isExternalFilterPresent,
184
157
  doesExternalFilterPass,
185
- setOnCellEditingComplete,
158
+ setOnBulkEditingComplete,
186
159
  getColDef,
187
160
  showNoRowsOverlay,
188
161
  prePopupOps,
189
- stopEditing,
190
- } = useContext(GridContext);
191
- const { startCellEditing } = useGridContext();
162
+ startCellEditing,
163
+ } = useGridContext<TData>();
164
+
192
165
  const { updatedDep, updatingCols } = useContext(GridUpdatingContext);
193
166
 
194
167
  const gridDivRef = useRef<HTMLDivElement>(null);
@@ -273,7 +246,10 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
273
246
  needsAutoSize.current = true;
274
247
  }
275
248
  }
276
- if (needsAutoSize.current || (!hasSetContentSize.current && sizeColumns === 'auto')) {
249
+ if (
250
+ needsAutoSize.current ||
251
+ (!hasSetContentSize.current && (sizeColumns === 'auto' || sizeColumns === 'auto-skip-headers'))
252
+ ) {
277
253
  needsAutoSize.current = false;
278
254
  setInitialContentSize();
279
255
  }
@@ -438,28 +414,11 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
438
414
  event.api.showNoRowsOverlay();
439
415
  }, []);
440
416
 
441
- /**
442
- * Force-refresh all selected rows to re-run class function, to update selection highlighting
443
- */
444
- const refreshSelectedRows = useCallback((_event: CellEditingStartedEvent): void => {
445
- // MATT Disabled I don't believe these are needed anymore
446
- // I've left them here just in case they are
447
- /*event.api.refreshCells({
448
- force: true,
449
- rowNodes: event.api.getSelectedNodes(),
450
- });*/
451
- }, []);
452
-
453
417
  /**
454
418
  * Handle double click edit
455
419
  */
456
420
  const onCellDoubleClick = useCallback(
457
421
  (event: CellDoubleClickedEvent) => {
458
- if (clickDebounceSkipClick(event.colDef.colId, event.rowIndex)) {
459
- // the next click will be a single click, we want it to pass
460
- resetClickDebounce();
461
- return;
462
- }
463
422
  const editable = fnOrVar(event.colDef?.editable, event);
464
423
  if (editable && !invokeEditAction(event)) {
465
424
  void startCellEditing({ rowId: event.data.id, colId: event.column.getColId() });
@@ -475,9 +434,6 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
475
434
  (event: CellClickedEvent) => {
476
435
  const editable = fnOrVar(event.colDef?.editable, event);
477
436
  if ((editable && event.colDef.singleClickEdit) ?? singleClickEdit) {
478
- if (clickDebounceSkipClick(event.colDef.colId, event.rowIndex)) {
479
- return;
480
- }
481
437
  void startCellEditing({ rowId: event.data.id, colId: event.column.getColId() });
482
438
  }
483
439
  },
@@ -587,11 +543,10 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
587
543
  useEffect(() => {
588
544
  const newLoading = !rowData || params.loading === true;
589
545
  if (newLoading && !prevLoading.current) {
590
- stopEditing();
591
546
  showNoRowsOverlay();
592
547
  }
593
548
  prevLoading.current = newLoading;
594
- }, [params.loading, rowData, showNoRowsOverlay, stopEditing]);
549
+ }, [params.loading, rowData, showNoRowsOverlay]);
595
550
 
596
551
  /**
597
552
  * Resize columns to fit if required on window/container resize
@@ -664,6 +619,27 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
664
619
  [clearHighlightRowClasses],
665
620
  );
666
621
 
622
+ const onCellFocused = useCallback(
623
+ (event: CellFocusedEvent<TData>) => {
624
+ if (!params.onCellFocused || event.rowIndex == null) {
625
+ return;
626
+ }
627
+ const api = event.api;
628
+ const rowNode = api.getDisplayedRowAtIndex(event.rowIndex);
629
+ const data = rowNode?.data;
630
+ const column = event.column;
631
+ if (!data || !column || typeof column === 'string') {
632
+ return;
633
+ }
634
+ const colDef = column.getColDef();
635
+ if (!colDef || typeof colDef === 'string') {
636
+ return;
637
+ }
638
+ params.onCellFocused({ colDef, data });
639
+ },
640
+ [params],
641
+ );
642
+
667
643
  const onRowDragEnd = useCallback(
668
644
  (event: RowDragEndEvent<TData>) => {
669
645
  clearHighlightRowClasses();
@@ -695,10 +671,77 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
695
671
  }, [params.setExternalSelectedItems, selectable]);
696
672
 
697
673
  // This is setting a ref in the GridContext so won't be triggering an update loop
698
- setOnCellEditingComplete(params.onCellEditingComplete);
674
+ setOnBulkEditingComplete(params.onBulkEditingComplete);
675
+
676
+ const getRowId = useCallback((params: GetRowIdParams<TData, unknown>) => `${params.data.id}`, []);
677
+ const defaultColDef = useMemo(
678
+ (): ColDef<TData, unknown> => ({
679
+ minWidth: 48,
680
+ ...omit(params.defaultColDef, ['editable', 'field', 'colId', 'tooltipField']),
681
+ }),
682
+ [params.defaultColDef],
683
+ );
684
+
685
+ const noRowsOverlayComponent = useCallback(
686
+ (event: AgGridEvent) => {
687
+ const headerRowCount = columnDefs.some((c) => (c as any).children) ? 2 : 1;
688
+
689
+ let rowCount = 0;
690
+ event.api.forEachNode(() => rowCount++);
691
+ return (
692
+ <GridNoRowsOverlay
693
+ loading={!rowData || params.loading === true}
694
+ rowCount={rowCount}
695
+ headerRowHeight={headerRowCount * rowHeight}
696
+ filteredRowCount={event.api.getDisplayedRowCount()}
697
+ noRowsOverlayText={params.noRowsOverlayText}
698
+ noRowsMatchingOverlayText={params.noRowsMatchingOverlayText}
699
+ />
700
+ );
701
+ },
702
+ [columnDefs, params.loading, params.noRowsMatchingOverlayText, params.noRowsOverlayText, rowData, rowHeight],
703
+ );
704
+
705
+ const selectionColumnDef = useMemo((): SelectionColumnDef => {
706
+ const selectWidth = params.hideSelectColumn ? 0 : selectable && params.onRowDragEnd ? 76 : 48;
707
+ return {
708
+ suppressNavigable: params.hideSelectColumn,
709
+ rowDrag: !!params.onRowDragEnd,
710
+ minWidth: selectWidth,
711
+ maxWidth: selectWidth,
712
+ pinned: selectColumnPinned,
713
+ headerComponentParams: {
714
+ exportable: false,
715
+ },
716
+ headerClass: clsx('ag-header-hide-default-select', params.onRowDragEnd && 'ag-header-select-draggable'),
717
+ headerComponent: rowSelection == 'multiple' ? GridHeaderSelect : undefined,
718
+ suppressHeaderKeyboardEvent: (e) => {
719
+ if (!selectable) return false;
720
+ if ((e.event.key === 'Enter' || e.event.key === ' ') && !e.event.repeat) {
721
+ if (isEmpty(e.api.getSelectedRows())) {
722
+ e.api.selectAll('filtered');
723
+ } else {
724
+ e.api.deselectAll();
725
+ }
726
+ return true;
727
+ }
728
+ return false;
729
+ },
730
+ onCellClicked:
731
+ params.enableSelectionWithoutKeys || params.enableClickSelection
732
+ ? undefined
733
+ : clickInputWhenContainingCellClicked,
734
+ };
735
+ }, [
736
+ params.enableClickSelection,
737
+ params.enableSelectionWithoutKeys,
738
+ params.hideSelectColumn,
739
+ params.onRowDragEnd,
740
+ rowSelection,
741
+ selectColumnPinned,
742
+ selectable,
743
+ ]);
699
744
 
700
- const selectWidth = params.hideSelectColumn ? 0 : selectable && params.onRowDragEnd ? 76 : 48;
701
- const headerRowCount = columnDefs.some((c) => (c as any).children) ? 2 : 1;
702
745
  return (
703
746
  <div
704
747
  data-testid={dataTestId}
@@ -726,45 +769,30 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
726
769
  rowHeight={rowHeight}
727
770
  animateRows={params.animateRows ?? false}
728
771
  rowClassRules={params.rowClassRules}
729
- getRowId={(params) => `${params.data.id}`}
772
+ getRowId={getRowId}
730
773
  onGridSizeChanged={onGridSizeChanged}
731
774
  suppressColumnVirtualisation={suppressColumnVirtualization}
732
775
  suppressClickEdit={true}
733
776
  onColumnVisible={setInitialContentSize}
734
777
  onRowDataUpdated={onRowDataChanged}
778
+ onCellFocused={onCellFocused}
735
779
  onCellKeyDown={onCellKeyPress}
736
780
  onCellClicked={onCellClicked}
737
781
  onCellDoubleClicked={onCellDoubleClick}
738
- onCellEditingStarted={refreshSelectedRows}
739
782
  domLayout={params.domLayout}
740
783
  onColumnResized={onColumnResized}
741
- defaultColDef={{ minWidth: 48, ...omit(params.defaultColDef, ['editable']) }}
784
+ defaultColDef={defaultColDef}
742
785
  columnDefs={columnDefsAdjusted}
786
+ selectionColumnDef={selectionColumnDef}
743
787
  rowData={rowData}
744
- noRowsOverlayComponent={(event: AgGridEvent) => {
745
- let rowCount = 0;
746
- event.api.forEachNode(() => rowCount++);
747
- return (
748
- <GridNoRowsOverlay
749
- loading={!rowData || params.loading === true}
750
- rowCount={rowCount}
751
- headerRowHeight={headerRowCount * rowHeight}
752
- filteredRowCount={event.api.getDisplayedRowCount()}
753
- noRowsOverlayText={params.noRowsOverlayText}
754
- noRowsMatchingOverlayText={params.noRowsMatchingOverlayText}
755
- />
756
- );
757
- }}
758
- quickFilterParser={(filterStr) => {
759
- // filter is exact matches exactly groups separated by commas
760
- return filterStr.split(',').map((str) => str.trim());
761
- }}
788
+ postSortRows={params.onRowDragEnd || !defaultPostSort ? undefined : postSortRows}
762
789
  onModelUpdated={onModelUpdated}
763
790
  onGridReady={onGridReady}
764
791
  onSortChanged={ensureSelectedRowIsVisible}
765
- postSortRows={params.onRowDragEnd || !defaultPostSort ? undefined : postSortRows}
792
+ quickFilterParser={quickFilterParser}
766
793
  onSelectionChanged={synchroniseExternalStateToGridSelection}
767
794
  onColumnMoved={params.onColumnMoved}
795
+ noRowsOverlayComponent={noRowsOverlayComponent}
768
796
  alwaysShowVerticalScroll={params.alwaysShowVerticalScroll}
769
797
  isExternalFilterPresent={isExternalFilterPresent}
770
798
  doesExternalFilterPass={doesExternalFilterPass}
@@ -778,36 +806,13 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
778
806
  suppressCellFocus={params.suppressCellFocus}
779
807
  pinnedTopRowData={params.pinnedTopRowData}
780
808
  pinnedBottomRowData={params.pinnedBottomRowData}
781
- selectionColumnDef={{
782
- suppressNavigable: params.hideSelectColumn,
783
- rowDrag: !!params.onRowDragEnd,
784
- minWidth: selectWidth,
785
- maxWidth: selectWidth,
786
- pinned: selectColumnPinned,
787
- headerComponentParams: {
788
- exportable: false,
789
- },
790
- headerClass: clsx('ag-header-hide-default-select', params.onRowDragEnd && 'ag-header-select-draggable'),
791
- headerComponent: rowSelection == 'multiple' ? GridHeaderSelect : undefined,
792
- suppressHeaderKeyboardEvent: (e) => {
793
- if (!selectable) return false;
794
- if ((e.event.key === 'Enter' || e.event.key === ' ') && !e.event.repeat) {
795
- if (isEmpty(e.api.getSelectedRows())) {
796
- e.api.selectAll('filtered');
797
- } else {
798
- e.api.deselectAll();
799
- }
800
- return true;
801
- }
802
- return false;
803
- },
804
- onCellClicked:
805
- params.enableSelectionWithoutKeys || params.enableClickSelection
806
- ? undefined
807
- : clickInputWhenContainingCellClicked,
808
- }}
809
809
  />
810
810
  </div>
811
811
  </div>
812
812
  );
813
813
  };
814
+
815
+ const quickFilterParser = (filterStr: string) => {
816
+ // filter is exact matches exactly groups separated by commas
817
+ return filterStr.split(',').map((str) => str.trim());
818
+ };
@@ -1,6 +1,6 @@
1
- import { ReactElement, useCallback, useContext, useEffect, useRef, useState } from 'react';
1
+ import { ReactElement, useCallback, useEffect, useRef, useState } from 'react';
2
2
 
3
- import { GridContext } from '../contexts/GridContext';
3
+ import { useGridContext } from '../contexts/GridContext';
4
4
  import { useGridPopoverContext } from '../contexts/GridPopoverContext';
5
5
  import { ControlledMenu } from '../react-menu3';
6
6
  import { MenuCloseEvent } from '../react-menu3/types';
@@ -21,8 +21,8 @@ export interface GridPopoverHookProps<TData> {
21
21
  }
22
22
 
23
23
  export const useGridPopoverHook = <TData extends GridBaseRow>(props: GridPopoverHookProps<TData>) => {
24
- const { stopEditing, cancelEdit } = useContext(GridContext);
25
- const { anchorRef, saving, updateValue } = useGridPopoverContext<TData>();
24
+ const { onBulkEditingComplete } = useGridContext<TData>();
25
+ const { anchorRef, saving, updateValue, stopEditing } = useGridPopoverContext<TData>();
26
26
  const saveButtonRef = useRef<HTMLButtonElement>(null);
27
27
  const [isOpen, setOpen] = useState(false);
28
28
 
@@ -33,29 +33,32 @@ export const useGridPopoverHook = <TData extends GridBaseRow>(props: GridPopover
33
33
  const triggerSave = useCallback(
34
34
  async (reason?: string) => {
35
35
  if (reason == CloseReason.CANCEL) {
36
- cancelEdit();
36
+ stopEditing();
37
+ onBulkEditingComplete();
37
38
  return;
38
39
  }
39
- if (props.invalid && props.invalid()) {
40
+ if (props?.invalid?.()) {
41
+ // Don't close, don't do anything it's invalid
40
42
  return;
41
43
  }
42
44
 
43
45
  if (!props.save) {
44
- cancelEdit();
45
- } else if (props.save) {
46
- // forms that don't provide an invalid fn must wait until they have saved to close
47
- if (props.invalid) stopEditing();
48
- if (
49
- await updateValue(
50
- props.save,
51
- reason === CloseReason.TAB_FORWARD ? 1 : reason === CloseReason.TAB_BACKWARD ? -1 : 0,
52
- )
53
- ) {
54
- if (!props.invalid) stopEditing();
55
- }
46
+ // No save method so just close
47
+ stopEditing();
48
+ onBulkEditingComplete();
49
+ return;
50
+ }
51
+
52
+ if (
53
+ await updateValue(
54
+ props.save,
55
+ reason === CloseReason.TAB_FORWARD ? 1 : reason === CloseReason.TAB_BACKWARD ? -1 : 0,
56
+ )
57
+ ) {
58
+ stopEditing();
56
59
  }
57
60
  },
58
- [cancelEdit, props, stopEditing, updateValue],
61
+ [onBulkEditingComplete, props, stopEditing, updateValue],
59
62
  );
60
63
 
61
64
  const popoverWrapper = useCallback(
@@ -72,7 +75,9 @@ export const useGridPopoverHook = <TData extends GridBaseRow>(props: GridPopover
72
75
  menuClassName={'step-ag-grid-react-menu'}
73
76
  onClose={(event: MenuCloseEvent) => {
74
77
  // Prevent menu from closing when modals are invoked
75
- if (event.reason === CloseReason.BLUR) return;
78
+ if (event.reason === CloseReason.BLUR) {
79
+ return;
80
+ }
76
81
  void triggerSave(event.reason);
77
82
  }}
78
83
  viewScroll={'auto'}
@@ -1,10 +1,10 @@
1
- import { useContext, useEffect } from 'react';
1
+ import { useEffect } from 'react';
2
2
 
3
- import { GridContext, GridFilterExternal } from '../../contexts/GridContext';
3
+ import { GridFilterExternal, useGridContext } from '../../contexts/GridContext';
4
4
  import { GridBaseRow } from '../Grid';
5
5
 
6
6
  export const useGridFilter = <TData extends GridBaseRow>(filter: GridFilterExternal<TData> | undefined) => {
7
- const { addExternalFilter, removeExternalFilter } = useContext(GridContext);
7
+ const { addExternalFilter, removeExternalFilter } = useGridContext<TData>();
8
8
 
9
9
  useEffect(() => {
10
10
  const thisFilter = filter;
@@ -24,7 +24,7 @@ export const useGridContextMenu = <TData extends GridBaseRow>({
24
24
  contextMenuSelectRow: boolean;
25
25
  contextMenu?: GridContextMenuComponent<TData>;
26
26
  }) => {
27
- const { redrawRows, prePopupOps, postPopupOps, getSelectedRows } = useContext(GridContext);
27
+ const { redrawRows, prePopupOps, resetFocusedCellAfterCellEditing, getSelectedRows } = useContext(GridContext);
28
28
 
29
29
  const [isOpen, setOpen] = useState(false);
30
30
  const [anchorPoint, setAnchorPoint] = useState({ x: 0, y: 0 });
@@ -47,8 +47,8 @@ export const useGridContextMenu = <TData extends GridBaseRow>({
47
47
  const closeMenu = useCallback(() => {
48
48
  setOpen(false);
49
49
  redrawRows();
50
- postPopupOps();
51
- }, [postPopupOps, redrawRows]);
50
+ resetFocusedCellAfterCellEditing();
51
+ }, [resetFocusedCellAfterCellEditing, redrawRows]);
52
52
 
53
53
  const cellContextMenu = useCallback(
54
54
  (event: CellContextMenuEvent) => {
@@ -25,7 +25,6 @@ export interface GridContextType<TData extends GridBaseRow> {
25
25
  getColumnIds: (filter?: keyof ColDef | ((r: ColDef) => boolean | undefined | null | number | string)) => string[];
26
26
  setApis: (gridApi: GridApi | undefined, hasExternallySelectedItems: boolean, dataTestId?: string) => void;
27
27
  prePopupOps: () => void;
28
- postPopupOps: () => void;
29
28
  setQuickFilter: (quickFilter: string) => void;
30
29
  editingCells: () => boolean;
31
30
  getSelectedRows: <T extends GridBaseRow>() => T[];
@@ -44,9 +43,9 @@ export interface GridContextType<TData extends GridBaseRow> {
44
43
  getFirstRowId: () => number;
45
44
  autoSizeColumns: (props?: AutoSizeColumnsProps) => AutoSizeColumnsResult;
46
45
  sizeColumnsToFit: () => void;
47
- cancelEdit: () => void;
48
46
  startCellEditing: ({ rowId, colId }: { rowId: number; colId: string }) => Promise<void>;
49
- stopEditing: () => void;
47
+ // Restores the previous focus after cell editing
48
+ resetFocusedCellAfterCellEditing: () => void;
50
49
  updatingCells: (
51
50
  props: { selectedRows: GridBaseRow[]; field?: string },
52
51
  fnUpdate: (selectedRows: any[]) => Promise<boolean>,
@@ -64,7 +63,8 @@ export interface GridContextType<TData extends GridBaseRow> {
64
63
  invisibleColumnIds: string[] | undefined;
65
64
  setInvisibleColumnIds: (colIds: string[]) => void;
66
65
  downloadCsv: (csvExportParams?: CsvExportParams) => void;
67
- setOnCellEditingComplete: (callback: (() => void) | undefined) => void;
66
+ onBulkEditingComplete: () => void;
67
+ setOnBulkEditingComplete: (callback: (() => void) | undefined) => void;
68
68
  showNoRowsOverlay: () => void;
69
69
  }
70
70
 
@@ -90,9 +90,6 @@ export const GridContext = createContext<GridContextType<any>>({
90
90
  prePopupOps: () => {
91
91
  console.error('no context provider for prePopupOps');
92
92
  },
93
- postPopupOps: () => {
94
- console.error('no context provider for prePopupOps');
95
- },
96
93
  externallySelectedItemsAreInSync: false,
97
94
  setApis: () => {
98
95
  console.error('no context provider for setApis');
@@ -164,15 +161,12 @@ export const GridContext = createContext<GridContextType<any>>({
164
161
  console.error('no context provider for editingCells');
165
162
  return false;
166
163
  },
167
- cancelEdit: () => {
168
- console.error('no context provider for cancelEdit');
169
- },
170
164
  // eslint-disable-next-line @typescript-eslint/require-await
171
165
  startCellEditing: async () => {
172
166
  console.error('no context provider for startCellEditing');
173
167
  },
174
- stopEditing: () => {
175
- console.error('no context provider for stopEditing');
168
+ resetFocusedCellAfterCellEditing: () => {
169
+ console.error('no context provider for resetFocusedCellAfterCellEditing');
176
170
  },
177
171
  // eslint-disable-next-line @typescript-eslint/require-await
178
172
  updatingCells: async () => {
@@ -206,8 +200,11 @@ export const GridContext = createContext<GridContextType<any>>({
206
200
  downloadCsv: () => {
207
201
  console.error('no context provider for downloadCsv');
208
202
  },
209
- setOnCellEditingComplete: () => {
210
- console.error('no context provider for setOnCellEditingComplete');
203
+ onBulkEditingComplete: () => {
204
+ console.error('no context provider for onBulkEditingComplete');
205
+ },
206
+ setOnBulkEditingComplete: () => {
207
+ console.error('no context provider for setOnBulkEditingComplete');
211
208
  },
212
209
  showNoRowsOverlay: () => {
213
210
  console.error('no context provider for showLoadingOverlay');