@linzjs/step-ag-grid 29.2.3 → 29.3.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/README.md CHANGED
@@ -275,7 +275,6 @@ test("click Delete menu option removes row from the table", async () => {
275
275
  If your grid has a data-testid a global will be exposed in window with the helper scrollRowIntoViewById.
276
276
  This will throw an exception if the row id is not found.
277
277
 
278
-
279
278
  ```tsx
280
279
  window.__stepAgGrid.grids[dataTestId].scrollRowIntoViewById("1000")
281
280
  ```
@@ -61,6 +61,10 @@ export interface GridProps<TData extends GridBaseRow = GridBaseRow> {
61
61
  * If you want to stretch to container width if width is greater than the container add a flex column.
62
62
  */
63
63
  sizeColumns?: 'fit' | 'auto' | 'auto-skip-headers' | 'none';
64
+ /**
65
+ * On first don't return a content size larger than this.
66
+ */
67
+ maxInitialWidth?: number;
64
68
  /**
65
69
  * When pressing tab whilst editing the grid will select and edit the next cell if available.
66
70
  * Once the last cell to edit closes this callback is called.
@@ -88,4 +92,4 @@ export interface GridProps<TData extends GridBaseRow = GridBaseRow> {
88
92
  /**
89
93
  * Wrapper for AgGrid to add commonly used functionality.
90
94
  */
91
- export declare const Grid: <TData extends GridBaseRow = GridBaseRow>({ "data-testid": dataTestId, defaultPostSort, rowSelection, suppressColumnVirtualization, theme, sizeColumns, selectColumnPinned, contextMenuSelectRow, singleClickEdit, rowData, rowHeight, selectable, onCellFocused: paramsOnCellFocused, ...params }: GridProps<TData>) => ReactElement;
95
+ export declare const Grid: <TData extends GridBaseRow = GridBaseRow>({ "data-testid": dataTestId, defaultPostSort, rowSelection, suppressColumnVirtualization, theme, sizeColumns, selectColumnPinned, contextMenuSelectRow, singleClickEdit, rowData, rowHeight, selectable, onCellFocused: paramsOnCellFocused, maxInitialWidth, ...params }: GridProps<TData>) => ReactElement;
@@ -1,5 +1,7 @@
1
- import { PropsWithChildren } from 'react';
2
1
  export interface GridWrapperProps {
2
+ className?: string | undefined;
3
3
  maxHeight?: number | string;
4
4
  }
5
- export declare const GridWrapper: ({ children, maxHeight }: PropsWithChildren<GridWrapperProps>) => import("react/jsx-runtime").JSX.Element;
5
+ export declare const GridWrapper: import("react").ForwardRefExoticComponent<GridWrapperProps & {
6
+ children?: import("react").ReactNode | undefined;
7
+ } & import("react").RefAttributes<HTMLDivElement>>;
@@ -1,4 +1,4 @@
1
- import { ColDef, GridApi, IRowNode } from 'ag-grid-community';
1
+ import { ColDef, GridApi, IRowNode, ISizeColumnsToFitParams } from 'ag-grid-community';
2
2
  import { CsvExportParams } from 'ag-grid-community';
3
3
  import { ColDefT, GridBaseRow } from '../components';
4
4
  export type GridFilterExternal<TData extends GridBaseRow> = (data: TData, rowNode: IRowNode) => boolean;
@@ -40,7 +40,7 @@ export interface GridContextType<TData extends GridBaseRow> {
40
40
  ensureSelectedRowIsVisible: () => void;
41
41
  getFirstRowId: () => number;
42
42
  autoSizeColumns: (props?: AutoSizeColumnsProps) => AutoSizeColumnsResult;
43
- sizeColumnsToFit: () => void;
43
+ sizeColumnsToFit: (paramsOrGridWidth?: ISizeColumnsToFitParams) => void;
44
44
  startCellEditing: ({ rowId, colId }: StartCellEditingProps) => Promise<void>;
45
45
  resetFocusedCellAfterCellEditing: () => void;
46
46
  updatingCells: (props: {
@@ -2762,7 +2762,7 @@ agGridCommunity.ModuleRegistry.registerModules([agGridCommunity.AllCommunityModu
2762
2762
  /**
2763
2763
  * Wrapper for AgGrid to add commonly used functionality.
2764
2764
  */
2765
- const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection = 'multiple', suppressColumnVirtualization = true, theme = 'ag-theme-step-default', sizeColumns = 'auto', selectColumnPinned = 'left', contextMenuSelectRow = false, singleClickEdit = false, rowData, rowHeight = theme === 'ag-theme-step-default' ? 40 : theme === 'ag-theme-step-compact' ? 36 : 40, selectable, onCellFocused: paramsOnCellFocused, ...params }) => {
2765
+ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection = 'multiple', suppressColumnVirtualization = true, theme = 'ag-theme-step-default', sizeColumns = 'auto', selectColumnPinned = 'left', contextMenuSelectRow = false, singleClickEdit = false, rowData, rowHeight = theme === 'ag-theme-step-default' ? 40 : theme === 'ag-theme-step-compact' ? 36 : 40, selectable, onCellFocused: paramsOnCellFocused, maxInitialWidth, ...params }) => {
2766
2766
  const { gridReady, gridRenderState, setApis, ensureRowVisible, getFirstRowId, selectRowsById, focusByRowById, ensureSelectedRowIsVisible, autoSizeColumns, sizeColumnsToFit, externallySelectedItemsAreInSync, setExternallySelectedItemsAreInSync, isExternalFilterPresent, doesExternalFilterPass, setOnBulkEditingComplete, getColDef, showNoRowsOverlay, prePopupOps, startCellEditing, } = useGridContext();
2767
2767
  const { updatedDep, updatingCols } = React.useContext(GridUpdatingContext);
2768
2768
  const gridDivRef = React.useRef(null);
@@ -2779,6 +2779,8 @@ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection
2779
2779
  const hasSetContentSizeEmpty = React.useRef(false);
2780
2780
  const needsAutoSize = React.useRef(true);
2781
2781
  const lastFullResize = React.useRef();
2782
+ const autoSizeResultRef = React.useRef(null);
2783
+ const prevRowsVisibleRef = React.useRef(false);
2782
2784
  const setInitialContentSize = React.useCallback(() => {
2783
2785
  if (!gridDivRef.current?.clientWidth || rowData == null) {
2784
2786
  // Don't resize grids if they are offscreen as it doesn't work.
@@ -2791,33 +2793,50 @@ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection
2791
2793
  needsAutoSize.current = true;
2792
2794
  return;
2793
2795
  }
2794
- const skipHeader = sizeColumns === 'auto-skip-headers' && gridRendered === 'rows-visible';
2795
- if (sizeColumns === 'auto' || skipHeader) {
2796
- const result = autoSizeColumns({
2797
- skipHeader,
2798
- userSizedColIds: new Set(userSizedColIds.current.keys()),
2799
- includeFlex: true,
2800
- });
2801
- if (!result) {
2796
+ // 1. First we autosize to get the size of the columns on an infinite grid.
2797
+ if (sizeColumns === 'auto' || sizeColumns === 'auto-skip-headers') {
2798
+ // You can't skip headers until the grid has content
2799
+ const rowsVisible = gridRendered === 'rows-visible';
2800
+ const skipHeader = sizeColumns === 'auto-skip-headers' && rowsVisible;
2801
+ // If grid was empty and now has content we need to autosize
2802
+ if (rowsVisible !== prevRowsVisibleRef.current && rowsVisible) {
2803
+ prevRowsVisibleRef.current = rowsVisible;
2804
+ autoSizeResultRef.current = null;
2805
+ }
2806
+ const autoSizeResult = autoSizeResultRef.current ??
2807
+ autoSizeColumns({
2808
+ skipHeader,
2809
+ userSizedColIds: new Set(userSizedColIds.current.keys()),
2810
+ });
2811
+ // Auto-size failed retry later
2812
+ if (!autoSizeResult) {
2802
2813
  needsAutoSize.current = true;
2803
2814
  return;
2804
2815
  }
2816
+ autoSizeResultRef.current = autoSizeResult;
2817
+ // Calculate the auto-sized width, limit it to maxInitialWidth
2818
+ autoSizeResult.width = maxInitialWidth ? Math.min(autoSizeResult.width, maxInitialWidth) : autoSizeResult.width;
2805
2819
  if (gridRendered === 'empty') {
2820
+ // If the grid is empty we still do an onContentSize callback, we will do another callback when grid has data
2821
+ // We don't do this callback if we have previously had row data, or have already called back for empty
2806
2822
  if (!hasSetContentSizeEmpty.current && !hasSetContentSize.current) {
2807
2823
  hasSetContentSizeEmpty.current = true;
2808
- params.onContentSize?.(result);
2824
+ params.onContentSize?.(autoSizeResult);
2809
2825
  }
2810
2826
  }
2811
2827
  else if (gridRendered === 'rows-visible') {
2828
+ // we have rows now so callback grid size
2812
2829
  if (!hasSetContentSize.current) {
2813
- if (lastFullResize.current === result.width) {
2830
+ // Only callback if grid size has settled
2831
+ if (lastFullResize.current === autoSizeResult.width) {
2814
2832
  hasSetContentSize.current = true;
2815
- params.onContentSize?.(result);
2833
+ params.onContentSize?.(autoSizeResult);
2816
2834
  }
2817
2835
  else {
2818
- needsAutoSize.current = true;
2836
+ // Need to retry callback when size has settelled
2837
+ lastFullResize.current = autoSizeResult.width;
2838
+ return;
2819
2839
  }
2820
- lastFullResize.current = result.width;
2821
2840
  }
2822
2841
  }
2823
2842
  else {
@@ -2825,12 +2844,13 @@ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection
2825
2844
  console.error('Unknown value returned from hasGridRendered');
2826
2845
  }
2827
2846
  }
2847
+ // 2. Now we size columns to fit the grid width
2828
2848
  if (sizeColumns !== 'none') {
2829
2849
  sizeColumnsToFit();
2830
2850
  }
2831
2851
  setAutoSized(true);
2832
2852
  needsAutoSize.current = false;
2833
- }, [autoSizeColumns, gridRenderState, params, rowData, sizeColumns, sizeColumnsToFit]);
2853
+ }, [autoSizeColumns, gridRenderState, maxInitialWidth, params, rowData, sizeColumns, sizeColumnsToFit]);
2834
2854
  const lastOwnerDocumentRef = React.useRef();
2835
2855
  const wasVisibleRef = React.useRef(false);
2836
2856
  /**
@@ -2982,6 +3002,8 @@ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection
2982
3002
  const onRowDataChanged = React.useCallback(() => {
2983
3003
  const length = rowData?.length ?? 0;
2984
3004
  if (previousRowDataLength.current !== length) {
3005
+ // We need to autosize all cells again
3006
+ autoSizeResultRef.current = null;
2985
3007
  setInitialContentSize();
2986
3008
  previousRowDataLength.current = length;
2987
3009
  }
@@ -3124,12 +3146,15 @@ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection
3124
3146
  /**
3125
3147
  * Resize columns to fit if required on window/container resize
3126
3148
  */
3127
- const onGridSizeChanged = React.useCallback((event) => {
3149
+ const onGridResize = React.useCallback((event) => {
3128
3150
  if (sizeColumns !== 'none') {
3151
+ // Flex columns can expand to fit after resize, but they cannot shrink less than use resized value
3152
+ // Double click column resize handle to reset this behaviour
3129
3153
  const columnLimits = [
3130
3154
  ...userSizedColIds.current.entries().map(([c, w]) => ({
3131
3155
  key: c,
3132
3156
  minWidth: w,
3157
+ maxWidth: w,
3133
3158
  })),
3134
3159
  ];
3135
3160
  event.api.sizeColumnsToFit({ columnLimits });
@@ -3154,12 +3179,17 @@ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection
3154
3179
  switch (e.source) {
3155
3180
  case 'uiColumnResized':
3156
3181
  userSizedColIds.current.set(colId, width);
3182
+ const colDef = e.column?.getColDef();
3183
+ if (!colDef?.flex) {
3184
+ onGridResize(e);
3185
+ }
3157
3186
  break;
3158
3187
  case 'autosizeColumns':
3159
3188
  userSizedColIds.current.delete(colId);
3189
+ onGridResize(e);
3160
3190
  break;
3161
3191
  }
3162
- }, []);
3192
+ }, [onGridResize]);
3163
3193
  const gridContextMenu = useGridContextMenu({ contextMenu: params.contextMenu, contextMenuSelectRow });
3164
3194
  const startDragYRef = React.useRef(null);
3165
3195
  const clearHighlightRowClasses = React.useCallback(() => {
@@ -3256,6 +3286,7 @@ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection
3256
3286
  return (jsxRuntime.jsx(GridNoRowsOverlay, { loading: !rowData || params.loading === true, rowCount: rowCount, headerRowHeight: headerRowCount * rowHeight, filteredRowCount: event.api.getDisplayedRowCount(), noRowsOverlayText: params.noRowsOverlayText, noRowsMatchingOverlayText: params.noRowsMatchingOverlayText }));
3257
3287
  }, [columnDefs, params.loading, params.noRowsMatchingOverlayText, params.noRowsOverlayText, rowData, rowHeight]);
3258
3288
  const selectionColumnDef = React.useMemo(() => {
3289
+ // Note this has to be 1 for hidden otherwise ag-grid does crazy things whilst resizing
3259
3290
  const selectWidth = params.hideSelectColumn ? 0 : selectable && params.onRowDragEnd ? 76 : 48;
3260
3291
  return {
3261
3292
  suppressNavigable: params.hideSelectColumn,
@@ -3301,7 +3332,7 @@ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection
3301
3332
  enableClickSelection: params.enableClickSelection ?? false,
3302
3333
  mode: rowSelection == 'single' ? 'singleRow' : 'multiRow',
3303
3334
  }
3304
- : undefined, rowHeight: rowHeight, animateRows: params.animateRows ?? false, rowClassRules: params.rowClassRules, getRowId: getRowId, onGridSizeChanged: onGridSizeChanged, suppressColumnVirtualisation: suppressColumnVirtualization, suppressClickEdit: true, onColumnVisible: setInitialContentSize, onRowDataUpdated: onRowDataChanged, onCellFocused: onCellFocused, onCellKeyDown: onCellKeyPress, onCellClicked: onCellClicked, onCellDoubleClicked: onCellDoubleClick, domLayout: params.domLayout, onColumnResized: onColumnResized, defaultColDef: defaultColDef, columnDefs: columnDefsAdjusted, selectionColumnDef: selectionColumnDef, rowData: rowData, postSortRows: params.onRowDragEnd || !defaultPostSort ? undefined : postSortRows, onModelUpdated: onModelUpdated, onGridReady: onGridReady, onSortChanged: ensureSelectedRowIsVisible, quickFilterParser: quickFilterParser, onSelectionChanged: synchroniseExternalStateToGridSelection, onColumnMoved: params.onColumnMoved, noRowsOverlayComponent: noRowsOverlayComponent, alwaysShowVerticalScroll: params.alwaysShowVerticalScroll, isExternalFilterPresent: isExternalFilterPresent, doesExternalFilterPass: doesExternalFilterPass, maintainColumnOrder: true, preventDefaultOnContextMenu: true, onCellContextMenu: gridContextMenu.cellContextMenu, rowDragText: params.rowDragText, onRowDragCancel: clearHighlightRowClasses, onRowDragMove: onRowDragMove, onRowDragEnd: onRowDragEnd, suppressCellFocus: params.suppressCellFocus, pinnedTopRowData: params.pinnedTopRowData, pinnedBottomRowData: params.pinnedBottomRowData, onRowClicked: params.onRowClicked, onRowDoubleClicked: params.onRowDoubleClicked, suppressStartEditOnTab: true }) })] }));
3335
+ : undefined, rowHeight: rowHeight, animateRows: params.animateRows ?? false, rowClassRules: params.rowClassRules, getRowId: getRowId, onGridSizeChanged: onGridResize, suppressColumnVirtualisation: suppressColumnVirtualization, suppressClickEdit: true, onColumnVisible: setInitialContentSize, onRowDataUpdated: onRowDataChanged, onCellFocused: onCellFocused, onCellKeyDown: onCellKeyPress, onCellClicked: onCellClicked, onCellDoubleClicked: onCellDoubleClick, domLayout: params.domLayout, onColumnResized: onColumnResized, defaultColDef: defaultColDef, columnDefs: columnDefsAdjusted, selectionColumnDef: selectionColumnDef, rowData: rowData, postSortRows: params.onRowDragEnd || !defaultPostSort ? undefined : postSortRows, onModelUpdated: onModelUpdated, onGridReady: onGridReady, onSortChanged: ensureSelectedRowIsVisible, quickFilterParser: quickFilterParser, onSelectionChanged: synchroniseExternalStateToGridSelection, onColumnMoved: params.onColumnMoved, noRowsOverlayComponent: noRowsOverlayComponent, alwaysShowVerticalScroll: params.alwaysShowVerticalScroll, isExternalFilterPresent: isExternalFilterPresent, doesExternalFilterPass: doesExternalFilterPass, maintainColumnOrder: true, preventDefaultOnContextMenu: true, onCellContextMenu: gridContextMenu.cellContextMenu, rowDragText: params.rowDragText, onRowDragCancel: clearHighlightRowClasses, onRowDragMove: onRowDragMove, onRowDragEnd: onRowDragEnd, suppressCellFocus: params.suppressCellFocus, pinnedTopRowData: params.pinnedTopRowData, pinnedBottomRowData: params.pinnedBottomRowData, onRowClicked: params.onRowClicked, onRowDoubleClicked: params.onRowDoubleClicked, suppressStartEditOnTab: true }) })] }));
3305
3336
  };
3306
3337
  const quickFilterParser = (filterStr) => {
3307
3338
  // filter is exact matches exactly groups separated by commas
@@ -5136,7 +5167,9 @@ const GridPopoverTextInput = (colDef, params) => GridCell(colDef, {
5136
5167
  ...params,
5137
5168
  });
5138
5169
 
5139
- const GridWrapper = ({ children, maxHeight }) => (jsxRuntime.jsx("div", { className: 'Grid-wrapper', style: { maxHeight }, children: children }));
5170
+ const GridWrapper = React.forwardRef(function GridWrapperFr({ children, maxHeight, className }, ref) {
5171
+ return (jsxRuntime.jsx("div", { className: clsx('Grid-wrapper', className), style: { maxHeight }, ref: ref, children: children }));
5172
+ });
5140
5173
 
5141
5174
  const getColId = (colDef) => colDef.colId ?? '';
5142
5175
  const isFlexColumn = (colDef) => !!colDef.flex && !isGridCellFiller(colDef);
@@ -5456,9 +5489,9 @@ const GridContextProvider = (props) => {
5456
5489
  /**
5457
5490
  * Resize columns to fit container
5458
5491
  */
5459
- const sizeColumnsToFit = React.useCallback(() => {
5492
+ const sizeColumnsToFit = React.useCallback((paramsOrGridWidth) => {
5460
5493
  if (gridApi && !gridApi?.isDestroyed()) {
5461
- gridApi.sizeColumnsToFit();
5494
+ gridApi.sizeColumnsToFit(paramsOrGridWidth);
5462
5495
  }
5463
5496
  }, [gridApi]);
5464
5497
  /**