@linzjs/step-ag-grid 8.4.3 → 9.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 (32) hide show
  1. package/README.md +4 -4
  2. package/dist/index.css +3 -0
  3. package/dist/src/components/gridForm/GridFormMultiSelect.d.ts +5 -4
  4. package/dist/src/utils/textMatcher.d.ts +1 -1
  5. package/dist/src/utils/textMatcher.test.d.ts +1 -0
  6. package/dist/step-ag-grid.esm.js +464 -689
  7. package/dist/step-ag-grid.esm.js.map +1 -1
  8. package/package.json +42 -42
  9. package/src/components/Grid.tsx +29 -27
  10. package/src/components/gridForm/GridFormDropDown.tsx +2 -2
  11. package/src/components/gridForm/GridFormMultiSelect.tsx +12 -6
  12. package/src/components/gridForm/GridFormSubComponentTextArea.tsx +0 -1
  13. package/src/components/gridForm/GridFormSubComponentTextInput.tsx +0 -1
  14. package/src/lui/TextAreaInput.tsx +1 -1
  15. package/src/lui/TextInputFormatted.tsx +1 -1
  16. package/src/react-menu3/components/ControlledMenu.tsx +19 -6
  17. package/src/stories/grid/GridKeyboardInteractions.stories.tsx +261 -0
  18. package/src/stories/grid/{GridPopoutBearing.stories.tsx → GridPopoverEditBearing.stories.tsx} +4 -4
  19. package/src/stories/grid/{GridPopoutEditDropDown.stories.tsx → GridPopoverEditDropDown.stories.tsx} +0 -0
  20. package/src/stories/grid/{GridPopoutEditMultiSelect.stories.tsx → GridPopoverEditMultiSelect.stories.tsx} +0 -0
  21. package/src/stories/grid/gridFormInteraction/GridFormDropDownInteraction.stories.tsx +10 -1
  22. package/src/stories/grid/gridFormInteraction/GridFormEditBearingCorrectionInteraction.stories.tsx +1 -1
  23. package/src/stories/grid/gridFormInteraction/GridFormEditBearingInteraction.stories.tsx +1 -1
  24. package/src/stories/grid/gridFormInteraction/GridFormMultiSelectInteraction.stories.tsx +153 -0
  25. package/src/stories/grid/gridFormInteraction/GridFormPopoverMenuInteraction.stories.tsx +1 -1
  26. package/src/stories/grid/gridFormInteraction/GridFormTextAreaInteraction.stories.tsx +1 -1
  27. package/src/stories/grid/gridFormInteraction/GridFormTextInputInteraction.stories.tsx +1 -1
  28. package/src/stories/react-menu/ReactMenu.stories.tsx +66 -3
  29. package/src/styles/Grid.scss +3 -0
  30. package/src/utils/testUtil.ts +1 -0
  31. package/src/utils/textMatcher.test.ts +38 -0
  32. package/src/utils/textMatcher.ts +9 -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": "8.4.3",
5
+ "version": "9.0.0",
6
6
  "keywords": [
7
7
  "aggrid",
8
8
  "ag-grid",
@@ -81,76 +81,76 @@
81
81
  ]
82
82
  },
83
83
  "devDependencies": {
84
- "@rollup/plugin-commonjs": "^23.0.2",
85
- "@rollup/plugin-json": "^5.0.1",
84
+ "@rollup/plugin-commonjs": "^24.0.1",
85
+ "@rollup/plugin-json": "^6.0.0",
86
86
  "@rollup/plugin-node-resolve": "^15.0.1",
87
- "@semantic-release/changelog": "^6.0.1",
87
+ "@semantic-release/changelog": "^6.0.2",
88
88
  "@semantic-release/git": "^10.0.1",
89
- "@storybook/addon-actions": "^6.5.13",
90
- "@storybook/addon-essentials": "^6.5.13",
91
- "@storybook/addon-interactions": "^6.5.13",
92
- "@storybook/addon-links": "^6.5.13",
89
+ "@storybook/addon-interactions": "^6.5.16",
90
+ "@storybook/addon-essentials": "^6.5.16",
91
+ "@storybook/builder-webpack5": "^6.5.16",
93
92
  "@storybook/jest": "^0.0.10",
94
- "@storybook/react": "6.5.13",
95
- "@storybook/test-runner": "^0.9.0",
93
+ "@storybook/manager-webpack5": "^6.5.16",
94
+ "@storybook/preset-create-react-app": "^4.1.2",
95
+ "@storybook/react": "^6.5.16",
96
+ "@storybook/test-runner": "^0.9.4",
96
97
  "@storybook/testing-library": "^0.0.13",
97
- "@testing-library/dom": "^8.19.0",
98
+ "@testing-library/dom": "^8.20.0",
98
99
  "@testing-library/jest-dom": "^5.16.5",
99
100
  "@testing-library/react": "^12.1.5",
100
101
  "@testing-library/user-event": "^13.5.0",
101
- "@types/debounce-promise": "^3.1.5",
102
- "@types/jest": "^29.2.2",
102
+ "@types/debounce-promise": "^3.1.6",
103
+ "@types/jest": "^29.4.0",
103
104
  "@types/lodash-es": "^4.17.6",
104
- "@types/node": "^18.11.9",
105
- "@types/react": "^17.0.52",
105
+ "@types/node": "^18.13.0",
106
+ "@types/react": "^17.0.53",
106
107
  "@types/react-dom": "^17.0.18",
107
- "@types/uuid": "^8.3.4",
108
- "@typescript-eslint/parser": "^5.42.0",
109
- "babel-jest": "^29.2.2",
108
+ "@types/uuid": "^9.0.0",
109
+ "@typescript-eslint/parser": "^5.51.0",
110
+ "babel-jest": "^29.4.2",
110
111
  "babel-preset-react-app": "^10.0.1",
111
- "chromatic": "^6.11.4",
112
+ "chromatic": "^6.17.0",
112
113
  "conventional-changelog-conventionalcommits": "^5.0.0",
113
- "eslint": "^8.27.0",
114
- "eslint-config-prettier": "^8.5.0",
114
+ "eslint": "^8.33.0",
115
+ "eslint-config-prettier": "^8.6.0",
115
116
  "eslint-config-react-app": "^7.0.1",
116
- "eslint-plugin-deprecation": "^1.3.2",
117
- "eslint-plugin-import": "^2.26.0",
118
- "eslint-plugin-jest": "^27.1.4",
119
- "eslint-plugin-jsx-a11y": "^6.6.1",
117
+ "eslint-plugin-deprecation": "^1.3.3",
118
+ "eslint-plugin-import": "^2.27.5",
119
+ "eslint-plugin-jest": "^27.2.1",
120
+ "eslint-plugin-jsx-a11y": "^6.7.1",
120
121
  "eslint-plugin-prettier": "^4.2.1",
121
- "eslint-plugin-react": "^7.31.10",
122
+ "eslint-plugin-react": "^7.32.2",
122
123
  "eslint-plugin-react-hooks": "^4.6.0",
123
- "eslint-plugin-testing-library": "^5.9.1",
124
- "jest": "^29.3.1",
124
+ "eslint-plugin-testing-library": "^5.10.1",
125
+ "jest": "^29.4.2",
125
126
  "jest-canvas-mock": "^2.4.0",
126
- "jest-environment-jsdom": "^29.3.1",
127
+ "jest-environment-jsdom": "^29.4.2",
127
128
  "jest-expect-message": "^1.1.3",
128
- "mkdirp": "^1.0.4",
129
+ "mkdirp": "^2.1.3",
129
130
  "npm-run-all": "^4.1.5",
130
- "postcss": "^8.4.18",
131
- "postcss-loader": "^7.0.1",
132
- "postcss-scss": "^4.0.5",
133
- "prettier": "^2.7.1",
131
+ "postcss": "^8.4.21",
132
+ "postcss-loader": "^7.0.2",
133
+ "postcss-scss": "^4.0.6",
134
+ "prettier": "^2.8.4",
134
135
  "react-app-polyfill": "^3.0.0",
135
136
  "react-scripts": "^5.0.1",
136
- "rollup": "^3.2.5",
137
+ "rollup": "^3.14.0",
137
138
  "rollup-plugin-copy": "^3.4.0",
138
139
  "rollup-plugin-peer-deps-external": "^2.2.4",
139
140
  "rollup-plugin-postcss": "^4.0.2",
140
141
  "rollup-plugin-typescript2": "^0.34.1",
141
- "sass": "^1.56.0",
142
- "sass-loader": "10.3.1",
142
+ "sass": "^1.58.0",
143
+ "sass-loader": "^13.2.0",
143
144
  "semantic-release": "^19.0.5",
144
- "storybook-addon-mock": "^2.4.1",
145
- "style-loader": "2.0.0",
146
- "stylelint": "^14.14.1",
147
- "stylelint-config-prettier": "^9.0.3",
145
+ "style-loader": "^3.3.1",
146
+ "stylelint": "^14.16.1",
147
+ "stylelint-config-prettier": "^9.0.4",
148
148
  "stylelint-config-recommended-scss": "^8.0.0",
149
149
  "stylelint-config-standard": "^29.0.0",
150
150
  "stylelint-prettier": "^2.0.0",
151
151
  "stylelint-scss": "^4.3.0",
152
152
  "tsconfig-paths-webpack-plugin": "^4.0.0",
153
- "typescript": "^4.8.4"
153
+ "typescript": "^4.9.5"
154
154
  },
155
155
  "babel": {
156
156
  "presets": [
@@ -340,33 +340,35 @@ export const Grid = (params: GridProps): JSX.Element => {
340
340
  />
341
341
  </div>
342
342
  )}
343
- <AgGridReact
344
- animateRows={params.animateRows}
345
- rowClassRules={params.rowClassRules}
346
- defaultColDef={params.defaultColDef}
347
- getRowId={(params) => `${params.data.id}`}
348
- suppressRowClickSelection={true}
349
- rowSelection={params.rowSelection ?? "multiple"}
350
- suppressBrowserResizeObserver={true}
351
- colResizeDefault={"shift"}
352
- onFirstDataRendered={sizeColumnsToFit}
353
- onGridSizeChanged={sizeColumnsToFit}
354
- suppressClickEdit={true}
355
- onCellKeyPress={onCellKeyPress}
356
- onCellClicked={onCellClicked}
357
- onCellDoubleClicked={onCellDoubleClick}
358
- onCellEditingStarted={refreshSelectedRows}
359
- domLayout={params.domLayout}
360
- columnDefs={columnDefs}
361
- rowData={params.rowData}
362
- noRowsOverlayComponent={noRowsOverlayComponent}
363
- onGridReady={onGridReady}
364
- onSortChanged={ensureSelectedRowIsVisible}
365
- postSortRows={params.postSortRows ?? postSortRows}
366
- onSelectionChanged={synchroniseExternalStateToGridSelection}
367
- onColumnMoved={params.onColumnMoved}
368
- alwaysShowVerticalScroll={params.alwaysShowVerticalScroll}
369
- />
343
+ <div style={{ flex: 1 }}>
344
+ <AgGridReact
345
+ animateRows={params.animateRows}
346
+ rowClassRules={params.rowClassRules}
347
+ defaultColDef={params.defaultColDef}
348
+ getRowId={(params) => `${params.data.id}`}
349
+ suppressRowClickSelection={true}
350
+ rowSelection={params.rowSelection ?? "multiple"}
351
+ suppressBrowserResizeObserver={true}
352
+ colResizeDefault={"shift"}
353
+ onFirstDataRendered={sizeColumnsToFit}
354
+ onGridSizeChanged={sizeColumnsToFit}
355
+ suppressClickEdit={true}
356
+ onCellKeyPress={onCellKeyPress}
357
+ onCellClicked={onCellClicked}
358
+ onCellDoubleClicked={onCellDoubleClick}
359
+ onCellEditingStarted={refreshSelectedRows}
360
+ domLayout={params.domLayout}
361
+ columnDefs={columnDefs}
362
+ rowData={params.rowData}
363
+ noRowsOverlayComponent={noRowsOverlayComponent}
364
+ onGridReady={onGridReady}
365
+ onSortChanged={ensureSelectedRowIsVisible}
366
+ postSortRows={params.postSortRows ?? postSortRows}
367
+ onSelectionChanged={synchroniseExternalStateToGridSelection}
368
+ onColumnMoved={params.onColumnMoved}
369
+ alwaysShowVerticalScroll={params.alwaysShowVerticalScroll}
370
+ />
371
+ </div>
370
372
  </div>
371
373
  );
372
374
  };
@@ -11,6 +11,7 @@ import { GridSubComponentContext } from "../../contexts/GridSubComponentContext"
11
11
  import { ClickEvent } from "../../react-menu3/types";
12
12
  import { FormError } from "../../lui/FormError";
13
13
  import { isNotEmpty } from "../../utils/util";
14
+ import { textMatch } from "../../utils/textMatcher";
14
15
 
15
16
  export interface GridPopoutEditDropDownSelectedItem<RowType> {
16
17
  // Note the row that was clicked on will be first
@@ -136,8 +137,7 @@ export const GridFormDropDown = <RowType extends GridBaseRow>(props: GridFormDro
136
137
  console.error("Cannot filter non-string labels", option);
137
138
  return undefined;
138
139
  }
139
- const str = (option.label as string) || "";
140
- return str.toLowerCase().indexOf(filter.toLowerCase()) !== -1 ? option : undefined;
140
+ return textMatch((option.label as string) || "", filter) ? option : undefined;
141
141
  })
142
142
  .filter((r) => r !== undefined),
143
143
  );
@@ -41,6 +41,11 @@ export interface GridFormMultiSelectGroup {
41
41
  filter?: string;
42
42
  }
43
43
 
44
+ export interface GridFormMultiSelectSaveProps<RowType extends GridBaseRow> {
45
+ selectedRows: RowType[];
46
+ selectedOptions: MultiSelectOption[];
47
+ }
48
+
44
49
  export interface GridFormMultiSelectProps<RowType extends GridBaseRow> extends CellEditorCommon {
45
50
  className?:
46
51
  | "GridMultiSelect-containerSmall"
@@ -54,7 +59,7 @@ export interface GridFormMultiSelectProps<RowType extends GridBaseRow> extends C
54
59
  filterHelpText?: string | ((filter: string, options: MultiSelectOption[]) => string | undefined);
55
60
  noOptionsMessage?: string;
56
61
  onSelectFilter?: (props: { filter: string; options: MultiSelectOption[] }) => void;
57
- onSave?: (props: { selectedRows: RowType[]; selectedOptions: MultiSelectOption[] }) => Promise<boolean>;
62
+ onSave?: (props: GridFormMultiSelectSaveProps<RowType>) => Promise<boolean>;
58
63
  headers?: GridFormMultiSelectGroup[];
59
64
  options: MultiSelectOption[] | ((selectedRows: RowType[]) => Promise<MultiSelectOption[]> | MultiSelectOption[]);
60
65
  invalid?: (selectedRows: RowType[], selectedOptions: MultiSelectOption[]) => boolean;
@@ -164,11 +169,12 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow>(props: GridForm
164
169
  filterPlaceholder={props.filterPlaceholder}
165
170
  />
166
171
  )}
167
- {headerGroups && (isEmpty(headerGroups) || !toPairs(headerGroups).some(([_, options]) => !isEmpty(options))) && (
168
- <MenuItem key={"noOptions"} className={"GridMultiSelect-noOptions"} disabled={true}>
169
- {props.noOptionsMessage ?? "No Options"}
170
- </MenuItem>
171
- )}
172
+ {headerGroups &&
173
+ (isEmpty(headerGroups) || !toPairs(headerGroups).some(([_, options]) => !isEmpty(options))) && (
174
+ <MenuItem key={"noOptions"} className={"GridMultiSelect-noOptions"} disabled={true}>
175
+ {props.noOptionsMessage ?? "No Options"}
176
+ </MenuItem>
177
+ )}
172
178
  {headerGroups && !isEmpty(headerGroups) && (
173
179
  <div className={"GridFormMultiSelect-options"}>
174
180
  {headers.map((header, index) => {
@@ -42,7 +42,6 @@ export const GridFormSubComponentTextArea = <RowType extends GridBaseRow>(
42
42
  onChange={(e) => setValue(e.target.value)}
43
43
  error={invalid()}
44
44
  helpText={helpText}
45
- autoFocus={true}
46
45
  placeholder={props.placeholder}
47
46
  allowTabToSave={true}
48
47
  />
@@ -38,7 +38,6 @@ export const GridFormSubComponentTextInput = <RowType extends GridBaseRow>(
38
38
  error={invalid()}
39
39
  onChange={(e) => setValue(e.target.value)}
40
40
  helpText={helpText}
41
- autoFocus={true}
42
41
  placeholder={props.placeholder}
43
42
  style={{ width: "100%" }}
44
43
  allowTabToSave={true}
@@ -40,7 +40,7 @@ export const TextAreaInput = (props: LuiTextAreaInputProps) => {
40
40
  {/* wrapper div used for error styling */}
41
41
  <textarea
42
42
  rows={5}
43
- {...omit(props, ["error", "value", "helpText", "formatted", "className"])}
43
+ {...omit(props, ["error", "value", "helpText", "formatted", "className", "allowTabToSave"])}
44
44
  id={id}
45
45
  value={props.value ?? ""}
46
46
  spellCheck={true}
@@ -26,7 +26,7 @@ export const TextInputFormatted = (props: LuiTextInputProps): JSX.Element => {
26
26
  type={"text"}
27
27
  spellCheck={true}
28
28
  defaultValue={props.value}
29
- {...omit(props, ["error", "value", "helpText", "formatted", "className"])}
29
+ {...omit(props, ["error", "value", "helpText", "formatted", "className", "allowTabToSave"])}
30
30
  className={"LuiTextInput-input"}
31
31
  onMouseEnter={(e) => {
32
32
  e.currentTarget.focus();
@@ -127,6 +127,25 @@ export const ControlledMenuFr = (
127
127
  return;
128
128
  }
129
129
 
130
+ const invokeSave = (reason: string) => {
131
+ if (!saveButtonRef?.current) return;
132
+ saveButtonRef.current?.setAttribute("data-reason", reason);
133
+ saveButtonRef?.current?.click();
134
+ };
135
+
136
+ const allowTabToSave = activeElement.getAttribute("data-allowtabtosave") == "true";
137
+ if (allowTabToSave) {
138
+ if (isDown) {
139
+ ev.preventDefault();
140
+ ev.stopPropagation();
141
+ lastTabDownEl.current = activeElement;
142
+ } else {
143
+ lastTabDownEl.current == activeElement &&
144
+ invokeSave(ev.shiftKey ? CloseReason.TAB_BACKWARD : CloseReason.TAB_FORWARD);
145
+ }
146
+ return;
147
+ }
148
+
130
149
  const inputElsIterator = thisDocument.querySelectorAll<HTMLElement>(".szh-menu--state-open input,textarea");
131
150
  let inputEls: HTMLElement[] = [];
132
151
  inputElsIterator.forEach((el) => inputEls.push(el));
@@ -138,12 +157,6 @@ export const ControlledMenuFr = (
138
157
 
139
158
  const isTextArea = activeElement.nodeName === "TEXTAREA";
140
159
  const suppressEnterAutoSave = activeElement.getAttribute("data-disableenterautosave") == "true" || isTextArea;
141
- const allowTabToSave = activeElement.getAttribute("data-allowtabtosave") == "true";
142
- const invokeSave = (reason: string) => {
143
- if (!saveButtonRef?.current) return;
144
- saveButtonRef.current?.setAttribute("data-reason", reason);
145
- saveButtonRef?.current?.click();
146
- };
147
160
 
148
161
  switch (activeElement.nodeName) {
149
162
  case "TEXTAREA":
@@ -0,0 +1,261 @@
1
+ import "@linzjs/lui/dist/scss/base.scss";
2
+ import "@linzjs/lui/dist/fonts";
3
+ import "../../styles/index.scss";
4
+ import "../../styles/GridTheme.scss";
5
+
6
+ import { ComponentMeta, ComponentStory } from "@storybook/react/dist/ts3.9/client/preview/types-6-3";
7
+ import { GridUpdatingContextProvider } from "../../contexts/GridUpdatingContextProvider";
8
+ import { GridContextProvider } from "../../contexts/GridContextProvider";
9
+ import { Grid, GridProps } from "../../components/Grid";
10
+ import { useMemo, useState } from "react";
11
+ import { wait } from "../../utils/util";
12
+ import { GridPopoverMenu } from "../../components/gridPopoverEdit/GridPopoverMenu";
13
+ import { ColDefT, GridCell } from "../../components/GridCell";
14
+ import { GridPopoverMessage } from "../../components/gridPopoverEdit/GridPopoverMessage";
15
+ import { MenuOption } from "../../components/gridForm/GridFormPopoverMenu";
16
+ import { GridFormSubComponentTextInput } from "../../components/gridForm/GridFormSubComponentTextInput";
17
+ import { GridFormSubComponentTextArea } from "../../components/gridForm/GridFormSubComponentTextArea";
18
+ import { GridIcon } from "../../components/GridIcon";
19
+ import { userEvent, waitFor } from "@storybook/testing-library";
20
+ import { expect, jest } from "@storybook/jest";
21
+
22
+ export default {
23
+ title: "Components / Grids",
24
+ component: Grid,
25
+ args: {
26
+ quickFilter: true,
27
+ quickFilterValue: "",
28
+ quickFilterPlaceholder: "Quick filter...",
29
+ selectable: false,
30
+ rowSelection: "single",
31
+ },
32
+ decorators: [
33
+ (Story) => (
34
+ <div style={{ width: 1024, height: 400 }}>
35
+ <GridUpdatingContextProvider>
36
+ <GridContextProvider>
37
+ <Story />
38
+ </GridContextProvider>
39
+ </GridUpdatingContextProvider>
40
+ </div>
41
+ ),
42
+ ],
43
+ } as ComponentMeta<typeof Grid>;
44
+
45
+ interface ITestRow {
46
+ id: number;
47
+ position: string;
48
+ age: number;
49
+ desc: string;
50
+ dd: string;
51
+ }
52
+
53
+ const multiEditAction = jest.fn().mockImplementation(async () => {
54
+ await wait(500);
55
+ });
56
+
57
+ const eAction = jest.fn<boolean, []>().mockReturnValue(true);
58
+
59
+ const GridKeyboardInteractionsTemplate: ComponentStory<typeof Grid> = (props: GridProps) => {
60
+ const [externalSelectedItems, setExternalSelectedItems] = useState<any[]>([]);
61
+ const columnDefs: ColDefT<ITestRow>[] = useMemo(
62
+ () => [
63
+ GridCell({
64
+ field: "id",
65
+ headerName: "Id",
66
+ initialWidth: 65,
67
+ maxWidth: 85,
68
+ }),
69
+ GridCell({
70
+ field: "position",
71
+ headerName: "Position",
72
+ initialWidth: 65,
73
+ maxWidth: 150,
74
+ cellRendererParams: {
75
+ warning: (props) => props.value === "Tester" && "Testers are testing",
76
+ info: (props) => props.value === "Developer" && "Developers are awesome",
77
+ },
78
+ }),
79
+ GridCell({
80
+ field: "age",
81
+ headerName: "Age",
82
+ initialWidth: 65,
83
+ maxWidth: 85,
84
+ }),
85
+ GridCell({
86
+ field: "desc",
87
+ headerName: "Description",
88
+ initialWidth: 150,
89
+ maxWidth: 200,
90
+ }),
91
+ GridPopoverMessage(
92
+ {
93
+ headerName: "Popout message",
94
+ maxWidth: 150,
95
+ cellRenderer: () => <>Single Click me!</>,
96
+ },
97
+ {
98
+ multiEdit: true,
99
+ editorParams: {
100
+ message: async (selectedRows): Promise<string> => {
101
+ await wait(1000);
102
+ return `There are ${selectedRows.length} row(s) selected`;
103
+ },
104
+ },
105
+ },
106
+ ),
107
+ GridCell({
108
+ headerName: "Custom edit",
109
+ maxWidth: 100,
110
+ editable: true,
111
+ valueFormatter: () => "Press E",
112
+ cellRendererParams: {
113
+ rightHoverElement: (
114
+ <GridIcon icon={"ic_launch_modal"} title={"Title text"} className={"GridCell-editableIcon"} />
115
+ ),
116
+ editAction: () => {
117
+ //
118
+ },
119
+ shortcutKeys: {
120
+ e: eAction,
121
+ },
122
+ },
123
+ }),
124
+ GridPopoverMenu(
125
+ {},
126
+ {
127
+ multiEdit: true,
128
+ editorParams: {
129
+ defaultAction: async ({ menuOption }) => {
130
+ // eslint-disable-next-line no-console
131
+ console.log("clicked", { menuOption });
132
+ },
133
+ options: async (selectedItems) => {
134
+ // Just doing a timeout here to demonstrate deferred loading
135
+ await wait(500);
136
+ return [
137
+ {
138
+ label: "Single edit only",
139
+ action: async () => {
140
+ //
141
+ },
142
+ disabled: selectedItems.length > 1,
143
+ },
144
+ {
145
+ label: "Multi-edit",
146
+ action: multiEditAction,
147
+ },
148
+ {
149
+ label: "Disabled item",
150
+ disabled: "Disabled for test",
151
+ },
152
+ {
153
+ label: "Developer Only",
154
+ hidden: selectedItems.some((x) => x.position != "Developer"),
155
+ },
156
+ {
157
+ label: "Other (TextInput)",
158
+ action: async () => {
159
+ //
160
+ },
161
+ subComponent: () => (
162
+ <GridFormSubComponentTextInput placeholder={"Other"} maxLength={5} required defaultValue={""} />
163
+ ),
164
+ },
165
+ {
166
+ label: "Other (TextArea)",
167
+ action: async ({ menuOption }) => {
168
+ // eslint-disable-next-line no-console
169
+ console.log(`Sub selected value was ${JSON.stringify(menuOption.subValue)}`);
170
+ await wait(500);
171
+ },
172
+ subComponent: () => (
173
+ <GridFormSubComponentTextArea placeholder={"Other"} maxLength={5} required defaultValue={""} />
174
+ ),
175
+ },
176
+ ] as MenuOption<ITestRow>[];
177
+ },
178
+ },
179
+ },
180
+ ),
181
+ GridPopoverMenu(
182
+ {
183
+ editable: () => false,
184
+ },
185
+ {
186
+ editorParams: {
187
+ options: async () => {
188
+ return [];
189
+ },
190
+ },
191
+ },
192
+ ),
193
+ ],
194
+ [],
195
+ );
196
+
197
+ const [rowData] = useState([
198
+ { id: 1000, position: "Tester", age: 30, desc: "Tests application", dd: "1" },
199
+ { id: 1001, position: "Developer", age: 12, desc: "Develops application", dd: "2" },
200
+ { id: 1002, position: "Manager", age: 65, desc: "Manages", dd: "3" },
201
+ ]);
202
+
203
+ return (
204
+ <Grid
205
+ {...props}
206
+ selectable={true}
207
+ externalSelectedItems={externalSelectedItems}
208
+ setExternalSelectedItems={setExternalSelectedItems}
209
+ columnDefs={columnDefs}
210
+ rowData={rowData}
211
+ domLayout={"autoHeight"}
212
+ autoSelectFirstRow={true}
213
+ />
214
+ );
215
+ };
216
+
217
+ export const GridKeyboardInteractions = GridKeyboardInteractionsTemplate.bind({});
218
+ GridKeyboardInteractions.play = async ({ canvasElement }) => {
219
+ // Ensure first row/cell is selected on render
220
+ await waitFor(async () => {
221
+ const activeCell = canvasElement.ownerDocument.activeElement;
222
+ expect(activeCell).toHaveClass("ag-cell-focus");
223
+ expect(activeCell).toHaveAttribute("aria-colindex", "1");
224
+ expect(activeCell?.parentElement).toHaveAttribute("row-index", "0");
225
+ });
226
+ userEvent.keyboard("{arrowdown}{arrowdown}");
227
+ userEvent.keyboard("{arrowright}{arrowright}{arrowright}{arrowright}{arrowright}{arrowright}{arrowright}");
228
+
229
+ // Test enter post focus
230
+ const test = async (fn: () => any, colId: string, rowId: string) => {
231
+ userEvent.keyboard("{Enter}");
232
+ await wait(1000);
233
+ userEvent.keyboard("{arrowdown}{arrowdown}");
234
+ fn();
235
+ await waitFor(async () => {
236
+ expect(multiEditAction).toHaveBeenCalled();
237
+ });
238
+
239
+ await waitFor(async () => {
240
+ const activeCell = canvasElement.ownerDocument.activeElement;
241
+ expect(activeCell).toHaveClass("ag-cell-focus");
242
+ expect(activeCell).toHaveAttribute("aria-colindex", colId);
243
+ expect(activeCell?.parentElement).toHaveAttribute("row-index", rowId);
244
+ });
245
+ await wait(1000);
246
+ };
247
+
248
+ await test(() => userEvent.keyboard("{Enter}"), "8", "2");
249
+ await test(() => userEvent.tab(), "9", "2");
250
+ userEvent.tab({ shift: true });
251
+ await test(() => userEvent.tab({ shift: true }), "7", "2");
252
+
253
+ userEvent.keyboard("{Enter}");
254
+ await wait(250);
255
+ expect(eAction).not.toHaveBeenCalled();
256
+
257
+ userEvent.keyboard("e");
258
+ await waitFor(async () => {
259
+ expect(eAction).toHaveBeenCalled();
260
+ });
261
+ };
@@ -42,7 +42,7 @@ interface ITestRow {
42
42
  bearing: string | number | null;
43
43
  }
44
44
 
45
- const GridReadOnlyTemplate: ComponentStory<typeof Grid> = (props: GridProps) => {
45
+ const GridPopoverEditBearingTemplate: ComponentStory<typeof Grid> = (props: GridProps) => {
46
46
  const [externalSelectedItems, setExternalSelectedItems] = useState<any[]>([]);
47
47
  const columnDefs: ColDefT<ITestRow>[] = useMemo(
48
48
  () => [
@@ -57,8 +57,8 @@ const GridReadOnlyTemplate: ComponentStory<typeof Grid> = (props: GridProps) =>
57
57
  field: "bearingCorrection",
58
58
  headerName: "Bearing correction",
59
59
  cellRendererParams: {
60
- warning: (props) => props.data.id == 1002 && "Testers are testing",
61
- info: (props) => props.data.id == 1001 && "Developers are developing",
60
+ warning: ({ data }) => data.id == 1002 && "Testers are testing",
61
+ info: ({ data }) => data.id == 1001 && "Developers are developing",
62
62
  },
63
63
  },
64
64
  {
@@ -108,4 +108,4 @@ const GridReadOnlyTemplate: ComponentStory<typeof Grid> = (props: GridProps) =>
108
108
  );
109
109
  };
110
110
 
111
- export const Bearings = GridReadOnlyTemplate.bind({});
111
+ export const _GridPopoverEditBearing = GridPopoverEditBearingTemplate.bind({});
@@ -24,7 +24,9 @@ const updateValue = jest
24
24
  .fn<void, [saveFn: (selectedRows: any[]) => Promise<boolean>, _tabDirection: 1 | 0 | -1]>()
25
25
  .mockImplementation((saveFn: (selectedRows: any[]) => Promise<boolean>, _tabDirection: 1 | 0 | -1) => saveFn([]));
26
26
 
27
- const onSelectedItem = jest.fn<Promise<void>, [GridPopoutEditDropDownSelectedItem<any>]>().mockResolvedValue(undefined);
27
+ const onSelectedItem = jest
28
+ .fn<Promise<void>, [GridPopoutEditDropDownSelectedItem<any>]>()
29
+ .mockImplementation(async () => undefined);
28
30
 
29
31
  const Template: ComponentStory<typeof GridFormDropDown> = (props) => {
30
32
  const config: GridFormDropDownProps<any> = {
@@ -125,4 +127,11 @@ GridFormDropDownInteractions_.play = async ({ canvasElement }) => {
125
127
  userEvent.clear(textInput);
126
128
  userEvent.type(textInput, "{Enter}");
127
129
  expect(updateValue).not.toHaveBeenCalled();
130
+
131
+ // Test filter
132
+ const filterText = await canvas.findByPlaceholderText("Filter...");
133
+ userEvent.type(filterText, "ena");
134
+ expect(canvas.queryByText("Enabled")).toBeInTheDocument();
135
+ expect(canvas.queryByText("Disabled")).not.toBeInTheDocument();
136
+ expect(canvas.queryByText("Sub menu...")).not.toBeInTheDocument();
128
137
  };
@@ -24,7 +24,7 @@ const Template: ComponentStory<typeof GridFormEditBearing> = (props) => {
24
24
  return (
25
25
  <div className={"react-menu-inline-test"}>
26
26
  <GridContextProvider>
27
- <h6 ref={anchorRef}>Test</h6>
27
+ <h6 ref={anchorRef}>Interaction Test</h6>
28
28
  <GridPopoverContext.Provider
29
29
  value={
30
30
  {