@linzjs/step-ag-grid 31.2.5 → 32.1.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/index.css +5 -1
- package/dist/src/components/GridCell.d.ts +1 -1
- package/dist/src/components/GridLoadableCell.d.ts +1 -1
- package/dist/src/components/GridNoRowsOverlay.d.ts +1 -1
- package/dist/src/components/GridPopoverHook.d.ts +1 -1
- package/dist/src/components/gridFilter/GridFilterDownloadCsvButton.d.ts +1 -1
- package/dist/src/components/gridFilter/GridFilterQuick.d.ts +1 -1
- package/dist/src/components/gridFilter/GridFilters.d.ts +1 -1
- package/dist/src/components/gridForm/GridFormDropDown.d.ts +1 -1
- package/dist/src/components/gridForm/GridFormEditBearing.d.ts +1 -1
- package/dist/src/components/gridForm/GridFormInlineTextInput.d.ts +1 -1
- package/dist/src/components/gridForm/GridFormMultiSelect.d.ts +2 -2
- package/dist/src/components/gridForm/GridFormPopoverMenu.d.ts +1 -1
- package/dist/src/components/gridForm/GridFormTextInput.d.ts +1 -1
- package/dist/src/components/gridHeader/GridHeaderSelect.d.ts +1 -1
- package/dist/src/components/gridHook/useGridContextMenu.d.ts +1 -1
- package/dist/src/components/gridHook/useGridCopy.d.ts +1 -1
- package/dist/src/components/gridHook/useGridRangeSelection.d.ts +1 -1
- package/dist/src/components/gridRender/GridRenderPopoutMenuCell.d.ts +1 -1
- package/dist/src/components/types.d.ts +2 -0
- package/dist/src/contexts/GridPopoverContextProvider.d.ts +1 -1
- package/dist/src/contexts/GridUpdatingContextProvider.d.ts +1 -1
- package/dist/src/lui/FormError.d.ts +1 -1
- package/dist/src/lui/TextAreaInput.d.ts +1 -1
- package/dist/src/react-menu3/components/ControlledMenu.d.ts +1 -1
- package/dist/src/react-menu3/components/FocusableItem.d.ts +1 -1
- package/dist/src/react-menu3/components/Menu.d.ts +1 -1
- package/dist/src/react-menu3/components/MenuGroup.d.ts +1 -1
- package/dist/src/react-menu3/components/MenuHeader.d.ts +1 -1
- package/dist/src/react-menu3/components/MenuList.d.ts +1 -1
- package/dist/src/react-menu3/components/MenuRadioGroup.d.ts +1 -1
- package/dist/src/react-menu3/components/SubMenu.d.ts +1 -1
- package/dist/src/utils/useSharedInterval.d.ts +5 -0
- package/dist/step-ag-grid.cjs +259 -105
- package/dist/step-ag-grid.cjs.map +1 -1
- package/dist/step-ag-grid.esm.js +260 -106
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +29 -30
- package/src/components/Grid.tsx +6 -3
- package/src/components/gridForm/GridFormPopoverMenu.tsx +6 -2
- package/src/components/gridHook/useGridRangeSelection.ts +1 -1
- package/src/components/gridPopoverEdit/GridEditBoolean.tsx +64 -26
- package/src/components/gridPopoverEdit/GridPopoverMenu.tsx +3 -0
- package/src/components/types.ts +2 -0
- package/src/contexts/GridContextProvider.tsx +18 -5
- package/src/react-app-env.d.ts +4 -1
- package/src/stories/grid/GridFilterColumnsMultiSelect.stories.tsx +1 -1
- package/src/stories/grid/GridNonEditableRow.stories.tsx +2 -2
- package/src/stories/grid/GridPopoutEditBoolean.stories.tsx +48 -9
- package/src/stories/grid/GridReadOnly.stories.tsx +69 -0
- package/src/stories/grid/gridFormInteraction/GridFormDropDownInteraction.stories.tsx +1 -1
- package/src/stories/grid/gridFormInteraction/GridFormEditBearingCorrectionInteraction.stories.tsx +1 -1
- package/src/stories/grid/gridFormInteraction/GridFormEditBearingInteraction.stories.tsx +1 -1
- package/src/stories/grid/gridFormInteraction/GridFormMultiSelectGridInteraction.stories.tsx +1 -1
- package/src/stories/grid/gridFormInteraction/GridFormMultiSelectInteraction.stories.tsx +1 -1
- package/src/stories/grid/gridFormInteraction/GridFormPopoverMenuInteraction.stories.tsx +48 -1
- package/src/stories/grid/gridFormInteraction/GridFormTextAreaInteraction.stories.tsx +1 -1
- package/src/stories/grid/gridFormInteraction/GridFormTextInputInteraction.stories.tsx +1 -1
- package/src/stories/grid/gridFormStatic/GridFormDropDown.stories.tsx +1 -1
- package/src/stories/grid/gridFormStatic/GridFormEditBearing.stories.tsx +1 -1
- package/src/stories/grid/gridFormStatic/GridFormEditBearingCorrection.stories.tsx +1 -1
- package/src/stories/grid/gridFormStatic/GridFormMessage.stories.tsx +1 -1
- package/src/stories/grid/gridFormStatic/GridFormMultiSelect.stories.tsx +1 -1
- package/src/stories/grid/gridFormStatic/GridFormPopoverMenu.stories.tsx +1 -1
- package/src/stories/grid/gridFormStatic/GridFormTextArea.stories.tsx +1 -1
- package/src/stories/grid/gridFormStatic/GridFormTextInput.stories.tsx +1 -1
- package/src/styles/GridLoadableCell.scss +5 -1
- package/src/utils/useSharedInterval.ts +57 -0
- package/src/utils/util.ts +4 -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": "
|
|
5
|
+
"version": "32.1.0",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"aggrid",
|
|
8
8
|
"ag-grid",
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
"ag-grid-react": "34.3.1",
|
|
38
38
|
"clsx": "^2.1.1",
|
|
39
39
|
"debounce-promise": "^3.1.2",
|
|
40
|
-
"matcher": "^
|
|
40
|
+
"matcher": "^6.0.0",
|
|
41
41
|
"natsort": "^2.0.3",
|
|
42
|
-
"react-transition-state": "^2.
|
|
42
|
+
"react-transition-state": "^2.4.0",
|
|
43
43
|
"usehooks-ts": "^3.1.1"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
@@ -75,51 +75,50 @@
|
|
|
75
75
|
]
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
|
-
"@chromatic-com/storybook": "^
|
|
79
|
-
"@linzjs/lui": "^24.
|
|
80
|
-
"@linzjs/windows": "^
|
|
81
|
-
"@rollup/plugin-commonjs": "^
|
|
78
|
+
"@chromatic-com/storybook": "^5.2.1",
|
|
79
|
+
"@linzjs/lui": "^24.17.0",
|
|
80
|
+
"@linzjs/windows": "^11.2.0",
|
|
81
|
+
"@rollup/plugin-commonjs": "^29.0.3",
|
|
82
82
|
"@rollup/plugin-json": "^6.1.0",
|
|
83
83
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
84
|
-
"@storybook/addon-docs": "^
|
|
85
|
-
"@storybook/addon-links": "^
|
|
86
|
-
"@storybook/react": "^
|
|
87
|
-
"@storybook/react-vite": "^
|
|
84
|
+
"@storybook/addon-docs": "^10.4.6",
|
|
85
|
+
"@storybook/addon-links": "^10.4.6",
|
|
86
|
+
"@storybook/react": "^10.4.6",
|
|
87
|
+
"@storybook/react-vite": "^10.4.6",
|
|
88
88
|
"@testing-library/dom": "^10.4.1",
|
|
89
|
-
"@testing-library/react": "^16.3.
|
|
89
|
+
"@testing-library/react": "^16.3.2",
|
|
90
90
|
"@testing-library/user-event": "^14.6.1",
|
|
91
91
|
"@types/debounce-promise": "^3.1.9",
|
|
92
92
|
"@types/jest": "^30.0.0",
|
|
93
|
-
"@types/jsdom": "^28.0.
|
|
93
|
+
"@types/jsdom": "^28.0.3",
|
|
94
94
|
"@types/lodash-es": "^4.17.12",
|
|
95
|
-
"@types/node": "^
|
|
95
|
+
"@types/node": "^24.13.2",
|
|
96
96
|
"@types/react": "^18.3.27",
|
|
97
97
|
"@types/react-dom": "^18.3.7",
|
|
98
|
-
"@vitejs/plugin-react
|
|
99
|
-
"@vitest/ui": "^
|
|
100
|
-
"chromatic": "^
|
|
98
|
+
"@vitejs/plugin-react": "^6.0.3",
|
|
99
|
+
"@vitest/ui": "^4.1.9",
|
|
100
|
+
"chromatic": "^17.6.0",
|
|
101
101
|
"cross-env": "^10.1.0",
|
|
102
102
|
"husky": "^9.1.7",
|
|
103
|
-
"jsdom": "
|
|
103
|
+
"jsdom": "29.1.1",
|
|
104
104
|
"lodash-es": "^4.18.1",
|
|
105
105
|
"npm-run-all": "^4.1.5",
|
|
106
|
-
"oxfmt": "^0.
|
|
107
|
-
"oxlint": "^1.
|
|
108
|
-
"postcss": "^8.5.
|
|
106
|
+
"oxfmt": "^0.56.0",
|
|
107
|
+
"oxlint": "^1.71.0",
|
|
108
|
+
"postcss": "^8.5.15",
|
|
109
109
|
"postcss-scss": "^4.0.9",
|
|
110
|
-
"react": ">=
|
|
111
|
-
"react-dom": "^
|
|
112
|
-
"rollup": "^4.
|
|
110
|
+
"react": ">=19",
|
|
111
|
+
"react-dom": "^19.2.7",
|
|
112
|
+
"rollup": "^4.62.2",
|
|
113
113
|
"rollup-plugin-copy": "^3.5.0",
|
|
114
114
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
115
115
|
"rollup-plugin-postcss": "^4.0.2",
|
|
116
116
|
"rollup-plugin-typescript2": "^0.37.0",
|
|
117
|
-
"sass": "^1.
|
|
118
|
-
"storybook": "^
|
|
119
|
-
"typescript": "^
|
|
120
|
-
"vite": "^
|
|
121
|
-
"
|
|
122
|
-
"vitest": "^3.2.4"
|
|
117
|
+
"sass": "^1.101.0",
|
|
118
|
+
"storybook": "^10.4.6",
|
|
119
|
+
"typescript": "^6.0.3",
|
|
120
|
+
"vite": "^8.1.0",
|
|
121
|
+
"vitest": "^4.1.9"
|
|
123
122
|
},
|
|
124
123
|
"husky": {
|
|
125
124
|
"hooks": {
|
package/src/components/Grid.tsx
CHANGED
|
@@ -44,7 +44,7 @@ import { useGridCopy } from './gridHook/useGridCopy';
|
|
|
44
44
|
import { CellLocation, useGridRangeSelection } from './gridHook/useGridRangeSelection';
|
|
45
45
|
import { GridNoRowsOverlay } from './GridNoRowsOverlay';
|
|
46
46
|
import { usePostSortRowsHook } from './PostSortRowsHook';
|
|
47
|
-
import { GridBaseRow, GridOnRowDragEndProps } from './types';
|
|
47
|
+
import { ColDefT, GridBaseRow, GridOnRowDragEndProps } from './types';
|
|
48
48
|
|
|
49
49
|
ModuleRegistry.registerModules([AllCommunityModule]);
|
|
50
50
|
|
|
@@ -471,13 +471,16 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
|
|
|
471
471
|
};
|
|
472
472
|
} else {
|
|
473
473
|
const colDefEditable = colDef.editable;
|
|
474
|
+
const ignoreGridReadOnly = (colDef as ColDefT<TData>).ignoreGridReadOnly === true;
|
|
474
475
|
const editable = combineEditables(
|
|
475
|
-
params.loading !== true
|
|
476
|
+
params.loading !== true,
|
|
477
|
+
ignoreGridReadOnly ? true : params.readOnly !== true,
|
|
476
478
|
params.defaultColDef?.editable,
|
|
477
479
|
colDefEditable,
|
|
478
480
|
);
|
|
479
481
|
return {
|
|
480
|
-
|
|
482
|
+
// ag-grid warns on unrecognised colDef properties, so it can't be left on the returned colDef
|
|
483
|
+
...omit(colDef, 'ignoreGridReadOnly'),
|
|
481
484
|
editable,
|
|
482
485
|
cellClassRules: {
|
|
483
486
|
...colDef.cellClassRules,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { isEmpty } from 'lodash-es';
|
|
2
1
|
import { Fragment, ReactElement, useCallback, useEffect, useRef, useState } from 'react';
|
|
3
2
|
|
|
4
3
|
import { useGridPopoverContext } from '../../contexts/GridPopoverContext';
|
|
@@ -126,10 +125,15 @@ export const GridFormPopoverMenu = <TData extends GridBaseRow>(props: GridFormPo
|
|
|
126
125
|
save,
|
|
127
126
|
});
|
|
128
127
|
|
|
128
|
+
// isEmpty(options) alone isn't enough: options can be a non-empty array where every item is
|
|
129
|
+
// hidden (e.g. all actions unavailable in a given mode), which must also show "No actions"
|
|
130
|
+
// rather than rendering an empty menu body.
|
|
131
|
+
const hasVisibleOption = options?.some((item) => !item.hidden && item.label !== __isMenuSeparator__);
|
|
132
|
+
|
|
129
133
|
return popoverWrapper(
|
|
130
134
|
<ComponentLoadingWrapper loading={!options} className={'GridFormPopupMenu'}>
|
|
131
135
|
<>
|
|
132
|
-
{
|
|
136
|
+
{!hasVisibleOption ? (
|
|
133
137
|
<MenuItem key={`GridPopoverMenu-empty`} className={'GridPopoverMenu-noOptions'} disabled={true}>
|
|
134
138
|
No actions
|
|
135
139
|
</MenuItem>
|
|
@@ -24,7 +24,7 @@ export const useGridRangeSelection = <TData extends GridBaseRow>({
|
|
|
24
24
|
rangeSortedNodesRef,
|
|
25
25
|
}: {
|
|
26
26
|
enableRangeSelection: boolean;
|
|
27
|
-
gridDivRef: RefObject<HTMLDivElement>;
|
|
27
|
+
gridDivRef: RefObject<HTMLDivElement | null>;
|
|
28
28
|
rangeStartRef: MutableRefObject<CellLocation | null>;
|
|
29
29
|
rangeEndRef: MutableRefObject<CellLocation | null>;
|
|
30
30
|
hasSelectedMoreThanOneCellRef: MutableRefObject<boolean>;
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
import { CellFocusedEvent } from 'ag-grid-community';
|
|
1
|
+
import { CellFocusedEvent, IRowNode } from 'ag-grid-community';
|
|
2
2
|
import { CustomCellEditorProps } from 'ag-grid-react';
|
|
3
3
|
import clsx from 'clsx';
|
|
4
|
-
import { useEffect, useRef } from 'react';
|
|
4
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
5
5
|
|
|
6
|
+
import { useGridContext } from '../../contexts/GridContext';
|
|
6
7
|
import { fnOrVar } from '../../utils/util';
|
|
7
8
|
import { clickInputWhenContainingCellClicked } from '../clickInputWhenContainingCellClicked';
|
|
8
9
|
import { CellEditorCommon, GridCell } from '../GridCell';
|
|
9
10
|
import { GenericCellColDef } from '../gridRender';
|
|
10
11
|
import { ColDefT, GridBaseRow } from '../types';
|
|
12
|
+
import { useSharedInterval } from '../../utils/useSharedInterval';
|
|
11
13
|
|
|
12
14
|
const BooleanCellRenderer = (props: CustomCellEditorProps) => {
|
|
13
|
-
const { onValueChange, value, api, node, column, colDef, data } = props;
|
|
15
|
+
const { onValueChange, value, api, node, column, colDef, data, eGridCell } = props;
|
|
14
16
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
17
|
+
const { updatingCells, redrawRows, resetFocusedCellAfterCellEditing, prePopupOps } = useGridContext();
|
|
15
18
|
|
|
16
19
|
useEffect(() => {
|
|
17
20
|
const checkFocus = (event: CellFocusedEvent) => {
|
|
@@ -20,16 +23,67 @@ const BooleanCellRenderer = (props: CustomCellEditorProps) => {
|
|
|
20
23
|
}
|
|
21
24
|
};
|
|
22
25
|
api.addEventListener('cellFocused', checkFocus);
|
|
23
|
-
return () =>
|
|
24
|
-
api.removeEventListener('cellFocused', checkFocus);
|
|
25
|
-
};
|
|
26
|
+
return () => void api.removeEventListener('cellFocused', checkFocus);
|
|
26
27
|
}, [api, column, node.rowIndex]);
|
|
27
28
|
|
|
28
29
|
const isDisabled = !fnOrVar(colDef?.editable, props);
|
|
29
30
|
|
|
31
|
+
const toggleCheckbox = useCallback(() => {
|
|
32
|
+
if (!onValueChange) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const params = props?.colDef?.cellEditorParams as GridEditBooleanEditorProps<any> | undefined;
|
|
37
|
+
if (!params) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const nodes: IRowNode[] = [];
|
|
42
|
+
api.forEachNode((n) => {
|
|
43
|
+
if (n.data.id === data.id) {
|
|
44
|
+
nodes.push(n);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
prePopupOps();
|
|
49
|
+
|
|
50
|
+
const checked = !value;
|
|
51
|
+
onValueChange(checked);
|
|
52
|
+
const field = props.colDef?.field ?? props.colDef?.colId ?? '';
|
|
53
|
+
const selectedRows = nodes.map((node) => node.data);
|
|
54
|
+
void updatingCells({ selectedRows, field }, async () => {
|
|
55
|
+
redrawRows(nodes);
|
|
56
|
+
return params.onClick({ selectedRows, selectedRowIds: selectedRows.map((r) => r.id), checked });
|
|
57
|
+
}).finally(() => {
|
|
58
|
+
resetFocusedCellAfterCellEditing();
|
|
59
|
+
});
|
|
60
|
+
}, [
|
|
61
|
+
api,
|
|
62
|
+
data.id,
|
|
63
|
+
onValueChange,
|
|
64
|
+
prePopupOps,
|
|
65
|
+
props.colDef?.cellEditorParams,
|
|
66
|
+
props.colDef?.colId,
|
|
67
|
+
props.colDef?.field,
|
|
68
|
+
redrawRows,
|
|
69
|
+
resetFocusedCellAfterCellEditing,
|
|
70
|
+
updatingCells,
|
|
71
|
+
value,
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
useSharedInterval(() => {
|
|
75
|
+
if (isDisabled) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const cell: HTMLElement | null = eGridCell?.querySelector('.ag-cell-focus .grid-edit-boolean input.ag-checkbox-input:not(:disabled)');
|
|
79
|
+
if (cell && cell.ownerDocument.activeElement !== cell) {
|
|
80
|
+
cell.focus();
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
30
84
|
return (
|
|
31
85
|
<div
|
|
32
|
-
className={clsx('ag-wrapper ag-input-wrapper ag-checkbox-input-wrapper', {
|
|
86
|
+
className={clsx('grid-edit-boolean ag-wrapper ag-input-wrapper ag-checkbox-input-wrapper', {
|
|
33
87
|
'ag-checked': props.value,
|
|
34
88
|
'ag-disabled': isDisabled,
|
|
35
89
|
})}
|
|
@@ -43,23 +97,7 @@ const BooleanCellRenderer = (props: CustomCellEditorProps) => {
|
|
|
43
97
|
onChange={() => {}}
|
|
44
98
|
onClick={(e) => {
|
|
45
99
|
e.stopPropagation();
|
|
46
|
-
|
|
47
|
-
// if in non-edit mode clickInputWhenContainingCellClicked will click to put it in edit mode
|
|
48
|
-
if (!onValueChange) return;
|
|
49
|
-
const params = props?.colDef?.cellEditorParams as GridEditBooleanEditorProps<any> | undefined;
|
|
50
|
-
if (!params) return;
|
|
51
|
-
// The data cannot be relied upon if grid changed whilst editing, data will be stale
|
|
52
|
-
// So I get the data from the node itself which will be up to date.
|
|
53
|
-
const selectedRows: { id: string | number }[] = [];
|
|
54
|
-
api.forEachNode((n) => {
|
|
55
|
-
if (n.data.id === data.id) {
|
|
56
|
-
selectedRows.push(n.data);
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
const checked = !value;
|
|
61
|
-
onValueChange(checked);
|
|
62
|
-
void params.onClick({ selectedRows, selectedRowIds: selectedRows.map((r) => r.id), checked });
|
|
100
|
+
toggleCheckbox();
|
|
63
101
|
}}
|
|
64
102
|
/>
|
|
65
103
|
</div>
|
|
@@ -78,7 +116,7 @@ export const GridEditBoolean = <TData extends GridBaseRow>(
|
|
|
78
116
|
colDef: GenericCellColDef<TData, boolean>,
|
|
79
117
|
editorProps: GridEditBooleanEditorProps<TData>,
|
|
80
118
|
): ColDefT<TData> => {
|
|
81
|
-
return GridCell({
|
|
119
|
+
return GridCell<TData>({
|
|
82
120
|
minWidth: 64,
|
|
83
121
|
maxWidth: 64,
|
|
84
122
|
cellRenderer: BooleanCellRenderer as any,
|
|
@@ -88,7 +126,7 @@ export const GridEditBoolean = <TData extends GridBaseRow>(
|
|
|
88
126
|
singleClickEdit: true,
|
|
89
127
|
resizable: false,
|
|
90
128
|
editable: true,
|
|
91
|
-
cellClass: 'GridCellAlignCenter',
|
|
129
|
+
cellClass: 'GridCellAlignCenter GridCellEditableWithNoPopup',
|
|
92
130
|
headerClass: 'GridHeaderAlignCenter',
|
|
93
131
|
...colDef,
|
|
94
132
|
});
|
|
@@ -20,6 +20,9 @@ export const GridPopoverMenu = <TData extends GridBaseRow>(
|
|
|
20
20
|
editable: colDef.editable != null ? colDef.editable : true,
|
|
21
21
|
exportable: false,
|
|
22
22
|
cellStyle: { flex: 1, justifyContent: 'center' },
|
|
23
|
+
suppressMovable: true,
|
|
24
|
+
lockPosition: 'right',
|
|
25
|
+
resizable: false,
|
|
23
26
|
cellRenderer: GridRenderPopoutMenuCell,
|
|
24
27
|
// Menus open on single click, this parameter is picked up in Grid.tsx
|
|
25
28
|
singleClickEdit: true,
|
package/src/components/types.ts
CHANGED
|
@@ -20,6 +20,8 @@ export interface GridOnRowDragEndProps<TData extends GridBaseRow> {
|
|
|
20
20
|
// This is so that typescript retains the row type to pass to the GridCells
|
|
21
21
|
export interface ColDefT<TData extends GridBaseRow, ValueType = any> extends ColDef<TData, ValueType> {
|
|
22
22
|
editable?: boolean | EditableCallback<TData, ValueType>;
|
|
23
|
+
/** When true, this column's editable state ignores the grid-level `readOnly` prop. */
|
|
24
|
+
ignoreGridReadOnly?: boolean;
|
|
23
25
|
valueGetter?: string | ValueGetterFunc<TData, ValueType>;
|
|
24
26
|
valueFormatter?: string | ValueFormatterFunc<TData, ValueType>;
|
|
25
27
|
cellRenderer?:
|
|
@@ -646,17 +646,21 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
|
|
|
646
646
|
return true;
|
|
647
647
|
}
|
|
648
648
|
|
|
649
|
-
|
|
649
|
+
/**
|
|
650
|
+
* @returns true if editable with a popup, false if editable without popup, null if not editable
|
|
651
|
+
*/
|
|
652
|
+
const focusedCellIsEditable = (): true | false | null => {
|
|
650
653
|
const focusedCell = gridApi.isDestroyed() ? null : gridApi.getFocusedCell();
|
|
651
654
|
const nextColumn = focusedCell?.column;
|
|
652
655
|
const nextColDef = nextColumn?.getColDef();
|
|
653
656
|
const rowNode = focusedCell && gridApi.getDisplayedRowAtIndex(focusedCell?.rowIndex);
|
|
657
|
+
const popupable = String(nextColDef?.cellClass).indexOf("GridCellEditableWithNoPopup") === -1
|
|
654
658
|
return (
|
|
655
659
|
!!(rowNode && nextColumn && nextColDef) &&
|
|
656
660
|
nextColumn.isCellEditable(rowNode) &&
|
|
657
661
|
!nextColDef.cellEditorParams?.preventAutoEdit &&
|
|
658
662
|
!nextColDef.cellRendererParams?.editAction
|
|
659
|
-
)
|
|
663
|
+
) ? popupable : null
|
|
660
664
|
};
|
|
661
665
|
|
|
662
666
|
// Just in case I've missed something, we don't want the loop to hang everything
|
|
@@ -692,7 +696,10 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
|
|
|
692
696
|
}
|
|
693
697
|
}
|
|
694
698
|
|
|
695
|
-
|
|
699
|
+
// checkbox cells are editable but don't have a popup
|
|
700
|
+
// we need to end bulk editing and just select the cell
|
|
701
|
+
const editable = focusedCellIsEditable()
|
|
702
|
+
if (editable !== null) {
|
|
696
703
|
const focusedCell = gridApi.getFocusedCell();
|
|
697
704
|
if (focusedCell) {
|
|
698
705
|
const rowNode = gridApi.getDisplayedRowAtIndex(focusedCell.rowIndex);
|
|
@@ -700,8 +707,14 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
|
|
|
700
707
|
if (rowId == null) {
|
|
701
708
|
return false;
|
|
702
709
|
}
|
|
703
|
-
|
|
704
|
-
|
|
710
|
+
if (editable) {
|
|
711
|
+
await startCellEditing({rowId, colId: focusedCell.column.getColId()});
|
|
712
|
+
// Continue bulk edit
|
|
713
|
+
return true;
|
|
714
|
+
} else {
|
|
715
|
+
// This will terminate bulk editing as there was no popup editor
|
|
716
|
+
return false;
|
|
717
|
+
}
|
|
705
718
|
}
|
|
706
719
|
}
|
|
707
720
|
}
|
package/src/react-app-env.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { Meta, StoryFn } from '@storybook/react-vite';
|
|
|
7
7
|
import {
|
|
8
8
|
createCheckboxMultiFilterParams,
|
|
9
9
|
GridFilterColumnsMultiSelect,
|
|
10
|
-
} from 'components/gridFilter/GridFilterColumnsMultiSelect';
|
|
10
|
+
} from '../../components/gridFilter/GridFilterColumnsMultiSelect';
|
|
11
11
|
import { useMemo, useState } from 'react';
|
|
12
12
|
import { expect, userEvent, within } from 'storybook/test';
|
|
13
13
|
|
|
@@ -4,8 +4,8 @@ import '@linzjs/lui/dist/scss/base.scss';
|
|
|
4
4
|
import '@linzjs/lui/dist/fonts';
|
|
5
5
|
|
|
6
6
|
import { Meta, StoryFn } from '@storybook/react-vite';
|
|
7
|
-
import { GridPopoverEditDropDown } from 'components/gridPopoverEdit/GridPopoverEditDropDown';
|
|
8
|
-
import { GridPopoverTextArea } from 'components/gridPopoverEdit/GridPopoverTextArea';
|
|
7
|
+
import { GridPopoverEditDropDown } from '../../components/gridPopoverEdit/GridPopoverEditDropDown';
|
|
8
|
+
import { GridPopoverTextArea } from '../../components/gridPopoverEdit/GridPopoverTextArea';
|
|
9
9
|
import { useMemo, useState } from 'react';
|
|
10
10
|
|
|
11
11
|
import {
|
|
@@ -13,7 +13,10 @@ import {
|
|
|
13
13
|
GridCell,
|
|
14
14
|
GridContextProvider,
|
|
15
15
|
GridEditBoolean,
|
|
16
|
+
GridPopoverEditDropDown,
|
|
16
17
|
GridUpdatingContextProvider,
|
|
18
|
+
primitiveToSelectOption,
|
|
19
|
+
wait,
|
|
17
20
|
} from '../..';
|
|
18
21
|
import { waitForGridReady } from '../../utils/__tests__/storybookTestUtil';
|
|
19
22
|
import { IFormTestRow } from './FormTest';
|
|
@@ -63,7 +66,40 @@ const GridPopoutEditBooleanTemplate: StoryFn<typeof Grid> = () => {
|
|
|
63
66
|
field: 'bold',
|
|
64
67
|
},
|
|
65
68
|
{
|
|
66
|
-
onClick: ({ selectedRowIds, checked }) => {
|
|
69
|
+
onClick: async ({ selectedRowIds, checked }) => {
|
|
70
|
+
await wait(1000);
|
|
71
|
+
setRowData((rowData) => {
|
|
72
|
+
// eslint-disable-next-line no-console
|
|
73
|
+
console.log('onchange', selectedRowIds, checked);
|
|
74
|
+
return rowData.map((row) => (selectedRowIds.includes(row.id) ? { ...row, bold: checked } : row));
|
|
75
|
+
});
|
|
76
|
+
return true;
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
),
|
|
80
|
+
GridPopoverEditDropDown(
|
|
81
|
+
{
|
|
82
|
+
field: 'name',
|
|
83
|
+
headerName: 'Name',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
multiEdit: false,
|
|
87
|
+
editorParams: {
|
|
88
|
+
filtered: 'local',
|
|
89
|
+
filterPlaceholder: 'Filter this',
|
|
90
|
+
options: [null, 'Architect', 'Developer', 'Product Owner', 'Scrum Master', 'Tester'].map(
|
|
91
|
+
primitiveToSelectOption,
|
|
92
|
+
),
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
),
|
|
96
|
+
GridEditBoolean(
|
|
97
|
+
{
|
|
98
|
+
colId: 'bold2',
|
|
99
|
+
field: 'bold',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
onClick: async ({ selectedRowIds, checked }) => {
|
|
67
103
|
setRowData((rowData) => {
|
|
68
104
|
// eslint-disable-next-line no-console
|
|
69
105
|
console.log('onchange', selectedRowIds, checked);
|
|
@@ -78,14 +114,17 @@ const GridPopoutEditBooleanTemplate: StoryFn<typeof Grid> = () => {
|
|
|
78
114
|
);
|
|
79
115
|
|
|
80
116
|
return (
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
117
|
+
<>
|
|
118
|
+
<Grid
|
|
119
|
+
theme={'ag-theme-step-view-list-default'}
|
|
120
|
+
columnDefs={columnDefs}
|
|
121
|
+
rowData={rowData}
|
|
122
|
+
selectable={false}
|
|
123
|
+
singleClickEdit={true}
|
|
124
|
+
rowSelection="single"
|
|
125
|
+
domLayout={'autoHeight'}
|
|
126
|
+
/>
|
|
127
|
+
</>
|
|
89
128
|
);
|
|
90
129
|
};
|
|
91
130
|
|
|
@@ -5,6 +5,7 @@ import '@linzjs/lui/dist/fonts';
|
|
|
5
5
|
|
|
6
6
|
import { Meta, StoryFn } from '@storybook/react-vite';
|
|
7
7
|
import { ReactElement, useCallback, useMemo, useState } from 'react';
|
|
8
|
+
import { expect, fn, screen, userEvent } from 'storybook/test';
|
|
8
9
|
|
|
9
10
|
import {
|
|
10
11
|
ColDefT,
|
|
@@ -294,3 +295,71 @@ const GridFilterLessThan = (props: {
|
|
|
294
295
|
|
|
295
296
|
export const ReadOnlySingleSelection = GridReadOnlyTemplate.bind({});
|
|
296
297
|
ReadOnlySingleSelection.play = waitForGridReady;
|
|
298
|
+
|
|
299
|
+
interface IIgnoreReadOnlyRow {
|
|
300
|
+
id: number;
|
|
301
|
+
name: string;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const disabledMenuItemAction = fn();
|
|
305
|
+
|
|
306
|
+
const IgnoreGridReadOnlyTemplate: StoryFn<typeof Grid<IIgnoreReadOnlyRow>> = () => {
|
|
307
|
+
const columnDefs: ColDefT<IIgnoreReadOnlyRow>[] = useMemo(
|
|
308
|
+
() => [
|
|
309
|
+
GridCell<IIgnoreReadOnlyRow, IIgnoreReadOnlyRow['name']>({ field: 'name', headerName: 'Name' }),
|
|
310
|
+
GridPopoverMenu(
|
|
311
|
+
{ colId: 'blockedActions', headerName: 'Blocked actions' },
|
|
312
|
+
{
|
|
313
|
+
multiEdit: true,
|
|
314
|
+
editorParams: {
|
|
315
|
+
options: async () => [{ label: 'Should not open', action: async () => {} }],
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
),
|
|
319
|
+
GridPopoverMenu(
|
|
320
|
+
{ colId: 'alwaysEnabledActions', headerName: 'Always enabled actions', ignoreGridReadOnly: true },
|
|
321
|
+
{
|
|
322
|
+
multiEdit: true,
|
|
323
|
+
editorParams: {
|
|
324
|
+
options: async () => [
|
|
325
|
+
{ label: 'Locate feature', action: async () => {} },
|
|
326
|
+
{ label: 'Other action', action: disabledMenuItemAction, disabled: 'Not available in read-only mode' },
|
|
327
|
+
],
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
),
|
|
331
|
+
],
|
|
332
|
+
[],
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
const [rowData] = useState<IIgnoreReadOnlyRow[]>([{ id: 1, name: 'Row 1' }]);
|
|
336
|
+
|
|
337
|
+
return (
|
|
338
|
+
<GridWrapper maxHeight={200}>
|
|
339
|
+
<Grid data-testid={'readonly-ignore-flag'} readOnly columnDefs={columnDefs} rowData={rowData} />
|
|
340
|
+
</GridWrapper>
|
|
341
|
+
);
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
export const ReadOnlyIgnoreGridReadOnlyColumn = IgnoreGridReadOnlyTemplate.bind({});
|
|
345
|
+
ReadOnlyIgnoreGridReadOnlyColumn.play = async (context) => {
|
|
346
|
+
await waitForGridReady(context);
|
|
347
|
+
const { canvasElement } = context;
|
|
348
|
+
|
|
349
|
+
const alwaysEnabledCell = canvasElement.querySelector('.ag-cell[col-id="alwaysEnabledActions"]');
|
|
350
|
+
await expect(alwaysEnabledCell).toBeInTheDocument();
|
|
351
|
+
await userEvent.click(alwaysEnabledCell as HTMLElement);
|
|
352
|
+
await expect(await screen.findByRole('menuitem', { name: 'Locate feature' })).toBeInTheDocument();
|
|
353
|
+
|
|
354
|
+
const disabledItem = await screen.findByRole('menuitem', { name: 'Other action' });
|
|
355
|
+
await expect(disabledItem).toHaveAttribute('aria-disabled', 'true');
|
|
356
|
+
await userEvent.click(disabledItem);
|
|
357
|
+
await expect(disabledMenuItemAction).not.toHaveBeenCalled();
|
|
358
|
+
|
|
359
|
+
await userEvent.keyboard('{Escape}');
|
|
360
|
+
|
|
361
|
+
const blockedCell = canvasElement.querySelector('.ag-cell[col-id="blockedActions"]');
|
|
362
|
+
await expect(blockedCell).toBeInTheDocument();
|
|
363
|
+
await userEvent.click(blockedCell as HTMLElement);
|
|
364
|
+
await expect(screen.queryByRole('menuitem', { name: 'Should not open' })).not.toBeInTheDocument();
|
|
365
|
+
};
|
|
@@ -4,7 +4,7 @@ import '@linzjs/lui/dist/scss/base.scss';
|
|
|
4
4
|
import '@linzjs/lui/dist/fonts';
|
|
5
5
|
|
|
6
6
|
import { StoryFn } from '@storybook/react-vite';
|
|
7
|
-
import { GridPopoverContext } from 'contexts/GridPopoverContext';
|
|
7
|
+
import { GridPopoverContext } from '../../../contexts/GridPopoverContext';
|
|
8
8
|
import { useRef } from 'react';
|
|
9
9
|
import { expect, fn, userEvent, within } from 'storybook/test';
|
|
10
10
|
|
package/src/stories/grid/gridFormInteraction/GridFormEditBearingCorrectionInteraction.stories.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import '@linzjs/lui/dist/scss/base.scss';
|
|
|
4
4
|
import '@linzjs/lui/dist/fonts';
|
|
5
5
|
|
|
6
6
|
import { StoryFn } from '@storybook/react-vite';
|
|
7
|
-
import { GridPopoverContext } from 'contexts/GridPopoverContext';
|
|
7
|
+
import { GridPopoverContext } from '../../../contexts/GridPopoverContext';
|
|
8
8
|
import { useRef } from 'react';
|
|
9
9
|
import { expect, fn, userEvent, within } from 'storybook/test';
|
|
10
10
|
|
|
@@ -4,7 +4,7 @@ import '@linzjs/lui/dist/scss/base.scss';
|
|
|
4
4
|
import '@linzjs/lui/dist/fonts';
|
|
5
5
|
|
|
6
6
|
import { StoryFn } from '@storybook/react-vite';
|
|
7
|
-
import { GridPopoverContext } from 'contexts/GridPopoverContext';
|
|
7
|
+
import { GridPopoverContext } from '../../../contexts/GridPopoverContext';
|
|
8
8
|
import { useRef } from 'react';
|
|
9
9
|
import { expect, fn, userEvent, within } from 'storybook/test';
|
|
10
10
|
|
|
@@ -4,7 +4,7 @@ import '@linzjs/lui/dist/scss/base.scss';
|
|
|
4
4
|
import '@linzjs/lui/dist/fonts';
|
|
5
5
|
|
|
6
6
|
import { StoryFn } from '@storybook/react-vite';
|
|
7
|
-
import { GridPopoverContext } from 'contexts/GridPopoverContext';
|
|
7
|
+
import { GridPopoverContext } from '../../../contexts/GridPopoverContext';
|
|
8
8
|
import { useRef } from 'react';
|
|
9
9
|
import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
|
|
10
10
|
|
|
@@ -4,7 +4,7 @@ import '@linzjs/lui/dist/scss/base.scss';
|
|
|
4
4
|
import '@linzjs/lui/dist/fonts';
|
|
5
5
|
|
|
6
6
|
import { StoryFn } from '@storybook/react-vite';
|
|
7
|
-
import { GridPopoverContext } from 'contexts/GridPopoverContext';
|
|
7
|
+
import { GridPopoverContext } from '../../../contexts/GridPopoverContext';
|
|
8
8
|
import { useRef } from 'react';
|
|
9
9
|
import { expect, fn, userEvent, within } from 'storybook/test';
|
|
10
10
|
|