@linzjs/step-ag-grid 5.0.1 → 6.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.
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.0.1",
5
+ "version": "6.0.0",
6
6
  "keywords": [
7
7
  "aggrid",
8
8
  "ag-grid",
@@ -24,7 +24,7 @@ export const useGridPopoverHook = <RowType extends GridBaseRow>(props: GridPopov
24
24
 
25
25
  const triggerSave = useCallback(
26
26
  async (reason?: string) => {
27
- if (reason == "cancel") {
27
+ if (reason == CloseReason.CANCEL) {
28
28
  stopEditing();
29
29
  return;
30
30
  }
@@ -64,7 +64,7 @@ export const useGridPopoverHook = <RowType extends GridBaseRow>(props: GridPopov
64
64
  menuClassName={"step-ag-grid-react-menu"}
65
65
  onClose={(event: MenuCloseEvent) => {
66
66
  // Prevent menu from closing when modals are invoked
67
- if (event.reason === "blur") return;
67
+ if (event.reason === CloseReason.BLUR) return;
68
68
  triggerSave(event.reason).then();
69
69
  }}
70
70
  viewScroll={"auto"}
@@ -13,15 +13,15 @@ import { GridSubComponentContext } from "contexts/GridSubComponentContext";
13
13
  import { ClickEvent, MenuInstance } from "../../react-menu3/types";
14
14
  import { CloseReason } from "../../react-menu3/utils";
15
15
 
16
- export interface GridPopoutEditDropDownSelectedItem<RowType, ValueType> {
16
+ export interface GridPopoutEditDropDownSelectedItem<RowType> {
17
17
  // Note the row that was clicked on will be first
18
18
  selectedRows: RowType[];
19
- value: ValueType;
20
- subComponentValue?: ValueType;
19
+ value: any;
20
+ subComponentValue?: any;
21
21
  }
22
22
 
23
- interface FinalSelectOption<ValueType> {
24
- value: ValueType;
23
+ interface FinalSelectOption {
24
+ value: any;
25
25
  label?: JSX.Element | string;
26
26
  disabled?: boolean | string;
27
27
  subComponent?: (props: any, ref: any) => any;
@@ -35,9 +35,9 @@ export const MenuHeaderItem = (title: string) => {
35
35
  return { label: title, value: MenuHeaderString };
36
36
  };
37
37
 
38
- export type SelectOption<ValueType> = ValueType | FinalSelectOption<ValueType>;
38
+ export type SelectOption = null | string | FinalSelectOption;
39
39
 
40
- export interface GridFormPopoutDropDownProps<RowType extends GridBaseRow, ValueType> extends CellEditorCommon {
40
+ export interface GridFormPopoutDropDownProps<RowType extends GridBaseRow> extends CellEditorCommon {
41
41
  // This overrides CellEditorCommon to provide some common class options
42
42
  className?:
43
43
  | "GridPopoverEditDropDown-containerSmall"
@@ -49,11 +49,9 @@ export interface GridFormPopoutDropDownProps<RowType extends GridBaseRow, ValueT
49
49
  // local means the filter won't change if it's reloaded, reload means it does change
50
50
  filtered?: "local" | "reload";
51
51
  filterPlaceholder?: string;
52
- onSelectedItem?: (props: GridPopoutEditDropDownSelectedItem<RowType, ValueType>) => Promise<void>;
53
- onSelectFilter?: (props: GridPopoutEditDropDownSelectedItem<RowType, string>) => Promise<void>;
54
- options:
55
- | SelectOption<ValueType>[]
56
- | ((selectedRows: RowType[], filter?: string) => Promise<SelectOption<ValueType>[]> | SelectOption<ValueType>[]);
52
+ onSelectedItem?: (props: GridPopoutEditDropDownSelectedItem<RowType>) => Promise<void>;
53
+ onSelectFilter?: (props: GridPopoutEditDropDownSelectedItem<RowType>) => Promise<void>;
54
+ options: SelectOption[] | ((selectedRows: RowType[], filter?: string) => Promise<SelectOption[]> | SelectOption[]);
57
55
  optionsRequestCancel?: () => void;
58
56
  }
59
57
 
@@ -61,41 +59,44 @@ const fieldToString = (field: any) => {
61
59
  return typeof field == "symbol" ? field.toString() : `${field}`;
62
60
  };
63
61
 
64
- export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
65
- props: GridFormPopoutDropDownProps<RowType, ValueType>,
66
- ) => {
62
+ export const GridFormDropDown = <RowType extends GridBaseRow>(props: GridFormPopoutDropDownProps<RowType>) => {
67
63
  const { selectedRows, field, updateValue, data } = useGridPopoverContext<RowType>();
68
64
 
69
65
  // Save triggers during async action processing which triggers another selectItem(), this ref blocks that
70
- const hasSubmitted = useRef(false);
71
66
  const [filter, setFilter] = useState("");
72
67
  const [filteredValues, setFilteredValues] = useState<any[]>([]);
73
68
  const optionsInitialising = useRef(false);
74
- const [options, setOptions] = useState<FinalSelectOption<ValueType>[] | null>(null);
69
+ const [options, setOptions] = useState<FinalSelectOption[] | null>(null);
75
70
  const subComponentIsValid = useRef(false);
76
71
  const [subSelectedValue, setSubSelectedValue] = useState<any>();
77
- const [selectedSubComponent, setSelectedSubComponent] = useState<FinalSelectOption<any> | null>(null);
72
+ const [selectedSubComponent, setSelectedSubComponent] = useState<FinalSelectOption | null>(null);
78
73
 
79
74
  const selectItemHandler = useCallback(
80
- async (value: ValueType, subComponentValue?: ValueType, reason?: string): Promise<boolean> => {
81
- if (hasSubmitted.current || (subComponentValue !== undefined && !subComponentIsValid.current)) return false;
82
- hasSubmitted.current = true;
75
+ async (value: any, subComponentValue?: any): Promise<boolean> => {
76
+ const hasChanged = selectedRows.some((row) => row[field as keyof RowType] !== value);
77
+ if (hasChanged) {
78
+ if (props.onSelectedItem) {
79
+ await props.onSelectedItem({ selectedRows, value, subComponentValue });
80
+ } else {
81
+ selectedRows.forEach((row) => (row[field] = value as any));
82
+ }
83
+ }
84
+ return true;
85
+ },
86
+ [field, props, selectedRows],
87
+ );
88
+
89
+ const clickItemHandler = useCallback(
90
+ async (value: any, subComponentValue?: any, reason?: string): Promise<boolean> => {
91
+ if (subComponentValue !== undefined && !subComponentIsValid.current) return false;
83
92
  return updateValue(
84
- async (selectedRows) => {
85
- const hasChanged = selectedRows.some((row) => row[field as keyof RowType] !== value);
86
- if (hasChanged) {
87
- if (props.onSelectedItem) {
88
- await props.onSelectedItem({ selectedRows, value, subComponentValue });
89
- } else {
90
- selectedRows.forEach((row) => (row[field as keyof RowType] = value));
91
- }
92
- }
93
- return true;
93
+ async () => {
94
+ return await selectItemHandler(value, subComponentValue);
94
95
  },
95
96
  reason === CloseReason.TAB_FORWARD ? 1 : reason === CloseReason.TAB_BACKWARD ? -1 : 0,
96
97
  );
97
98
  },
98
- [field, props, updateValue],
99
+ [selectItemHandler, updateValue],
99
100
  );
100
101
 
101
102
  const selectFilterHandler = useCallback(
@@ -118,16 +119,15 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
118
119
  optionsConf = await optionsConf(selectedRows, filter);
119
120
  }
120
121
 
121
- const optionsList = optionsConf?.map((item) => {
122
- if (item == null || typeof item == "string" || typeof item == "number") {
123
- item = {
124
- value: item as unknown as ValueType,
125
- label: item,
126
- disabled: false,
127
- } as unknown as FinalSelectOption<ValueType>;
128
- }
129
- return item;
130
- }) as any as FinalSelectOption<ValueType>[];
122
+ const optionsList = optionsConf?.map((item) =>
123
+ item == null || typeof item == "string"
124
+ ? ({
125
+ value: item,
126
+ label: item,
127
+ disabled: false,
128
+ } as FinalSelectOption)
129
+ : item,
130
+ );
131
131
 
132
132
  if (props.filtered) {
133
133
  // This is needed otherwise when filter input is rendered and sets autofocus
@@ -177,6 +177,9 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
177
177
  }
178
178
  }, [filter, props, researchOnFilterChange]);
179
179
 
180
+ /**
181
+ * Saves are wrapped in updateValue and triggered by blur events
182
+ */
180
183
  const save = useCallback(async () => {
181
184
  if (!options) return true;
182
185
  const activeOptions = options.filter((option) => !filteredValues.includes(option.value));
@@ -188,7 +191,7 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
188
191
  // Handler for sub-selected value
189
192
  if (!selectedSubComponent) return true;
190
193
  if (selectedSubComponent.subComponent && !subComponentIsValid.current) return false;
191
- await selectItemHandler(selectedSubComponent.value as ValueType, subSelectedValue);
194
+ await selectItemHandler(selectedSubComponent.value, subSelectedValue);
192
195
  }
193
196
  return true;
194
197
  }, [
@@ -237,7 +240,7 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
237
240
  {options && options.length == filteredValues?.length && (
238
241
  <MenuItem key={`${fieldToString(field)}-empty`}>[Empty]</MenuItem>
239
242
  )}
240
- {options?.map((item: FinalSelectOption<ValueType | string>, index) =>
243
+ {options?.map((item: FinalSelectOption, index) =>
241
244
  item.value === MenuSeparatorString ? (
242
245
  <MenuDivider key={`$$divider_${index}`} />
243
246
  ) : item.value === MenuHeaderString ? (
@@ -261,8 +264,8 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
261
264
  }
262
265
  e.keepOpen = true;
263
266
  } else {
264
- selectItemHandler(
265
- item.value as ValueType,
267
+ clickItemHandler(
268
+ item.value,
266
269
  undefined,
267
270
  e.key === "Tab"
268
271
  ? e.shiftKey
@@ -7,7 +7,6 @@ import { CellEditorCommon } from "../GridCell";
7
7
  import { GridSubComponentContext } from "../../contexts/GridSubComponentContext";
8
8
  import { ClickEvent } from "../../react-menu3/types";
9
9
  import { useGridPopoverContext } from "../../contexts/GridPopoverContext";
10
- import { CloseReason } from "../../react-menu3/utils";
11
10
 
12
11
  export interface GridFormPopoutMenuProps<RowType extends GridBaseRow> extends CellEditorCommon {
13
12
  options: (selectedRows: RowType[]) => Promise<MenuOption<RowType>[]>;
@@ -43,8 +42,6 @@ export const GridFormPopoverMenu = <RowType extends GridBaseRow>(props: GridForm
43
42
  const optionsInitialising = useRef(false);
44
43
  const [options, setOptions] = useState<MenuOption<RowType>[]>();
45
44
 
46
- // Save triggers during async action processing which triggers another action(), this ref blocks that
47
- const actionProcessing = useRef(false);
48
45
  const [subComponentSelected, setSubComponentSelected] = useState<MenuOption<RowType> | null>(null);
49
46
  const subComponentIsValid = useRef(false);
50
47
  const [subSelectedValue, setSubSelectedValue] = useState<any>();
@@ -79,19 +76,12 @@ export const GridFormPopoverMenu = <RowType extends GridBaseRow>(props: GridForm
79
76
  }, [options, props.defaultAction, props.options, selectedRows]);
80
77
 
81
78
  const actionClick = useCallback(
82
- async (menuOption: MenuOption<RowType>, reason: string) => {
83
- actionProcessing.current = true;
84
- return updateValue(
85
- async () => {
86
- const result = { ...menuOption, subValue: subSelectedValue };
87
- await (menuOption.action ?? defaultAction)(selectedRows, result);
88
- actionProcessing.current = false;
89
- return true;
90
- },
91
- reason === CloseReason.TAB_FORWARD ? 1 : reason === CloseReason.TAB_BACKWARD ? -1 : 0,
92
- );
79
+ async (menuOption: MenuOption<RowType>) => {
80
+ const result = { ...menuOption, subValue: subSelectedValue };
81
+ await (menuOption.action ?? defaultAction)(selectedRows, result);
82
+ return true;
93
83
  },
94
- [defaultAction, selectedRows, subSelectedValue, updateValue],
84
+ [defaultAction, selectedRows, subSelectedValue],
95
85
  );
96
86
 
97
87
  const onMenuItemClick = useCallback(
@@ -102,23 +92,19 @@ export const GridFormPopoverMenu = <RowType extends GridBaseRow>(props: GridForm
102
92
  setSubComponentSelected(subComponentSelected === item ? null : item);
103
93
  e.keepOpen = true;
104
94
  } else {
105
- await actionClick(
106
- item,
107
- e.key === "Tab" ? (e.shiftKey ? CloseReason.TAB_BACKWARD : CloseReason.TAB_FORWARD) : CloseReason.CLICK,
108
- ).then();
95
+ await updateValue(async () => actionClick(item), e.key === "Tab" ? (e.shiftKey ? -1 : 1) : 0);
109
96
  }
110
97
  },
111
- [actionClick, subComponentSelected],
98
+ [actionClick, subComponentSelected, updateValue],
112
99
  );
113
100
 
114
101
  const save = useCallback(async () => {
115
102
  // if a subcomponent is open we assume that it's meant to be saved.
116
- if (!actionProcessing.current && subComponentSelected) {
103
+ if (subComponentSelected) {
117
104
  if (!subComponentIsValid.current) return false;
118
- await actionClick(subComponentSelected, "click");
119
- return true;
105
+ await actionClick(subComponentSelected);
120
106
  }
121
- // Otherwise assume it's a cancel, either way we close the menu
107
+ // Close the menu
122
108
  return true;
123
109
  }, [actionClick, subComponentSelected]);
124
110
 
@@ -3,9 +3,9 @@ import { GridBaseRow } from "../Grid";
3
3
  import { GridFormDropDown, GridFormPopoutDropDownProps } from "../gridForm/GridFormDropDown";
4
4
  import { GenericCellColDef } from "../gridRender/GridRenderGenericCell";
5
5
 
6
- export const GridPopoverEditDropDown = <RowType extends GridBaseRow, ValueType>(
6
+ export const GridPopoverEditDropDown = <RowType extends GridBaseRow>(
7
7
  colDef: GenericCellColDef<RowType>,
8
- props: GenericCellEditorProps<GridFormPopoutDropDownProps<RowType, ValueType>>,
8
+ props: GenericCellEditorProps<GridFormPopoutDropDownProps<RowType>>,
9
9
  ): ColDefT<RowType> =>
10
10
  GridCell(colDef, {
11
11
  editor: GridFormDropDown,
@@ -13,6 +13,6 @@ export const GridPopoverEditDropDown = <RowType extends GridBaseRow, ValueType>(
13
13
  editorParams: {
14
14
  // Defaults to medium size container
15
15
  className: "GridPopoverEditDropDown-containerMedium",
16
- ...(props.editorParams as GridFormPopoutDropDownProps<RowType, ValueType>),
16
+ ...(props.editorParams as GridFormPopoutDropDownProps<RowType>),
17
17
  },
18
18
  });
@@ -23,9 +23,9 @@ export interface GridContextType {
23
23
  props: { selectedRows: GridBaseRow[]; field?: string },
24
24
  fnUpdate: (selectedRows: any[]) => Promise<boolean>,
25
25
  setSaving?: (saving: boolean) => void,
26
+ tabDirection?: 1 | 0 | -1,
26
27
  ) => Promise<boolean>;
27
28
  redrawRows: (rowNodes?: RowNode[]) => void;
28
- selectNextCell: (tabDirection: -1 | 0 | 1) => void;
29
29
  }
30
30
 
31
31
  export const GridContext = createContext<GridContextType>({
@@ -88,7 +88,4 @@ export const GridContext = createContext<GridContextType>({
88
88
  redrawRows: () => {
89
89
  console.error("no context provider for redrawRows");
90
90
  },
91
- selectNextCell: () => {
92
- console.error("no context provider for selectNextCell");
93
- },
94
91
  });
@@ -234,10 +234,12 @@ export const GridContextProvider = (props: GridContextProps): ReactElement => {
234
234
  props: { selectedRows: GridBaseRow[]; field?: string },
235
235
  fnUpdate: (selectedRows: any[]) => Promise<boolean>,
236
236
  setSaving?: (saving: boolean) => void,
237
+ tabDirection?: 1 | 0 | -1,
237
238
  ): Promise<boolean> => {
238
239
  setSaving && setSaving(true);
239
-
240
240
  return await gridApiOp(async (gridApi) => {
241
+ const preOpCell = gridApi.getFocusedCell();
242
+
241
243
  const selectedRows = props.selectedRows;
242
244
 
243
245
  let ok = false;
@@ -265,6 +267,19 @@ export const GridContextProvider = (props: GridContextProps): ReactElement => {
265
267
  // Don't set saving if ok as the form has already closed
266
268
  setSaving && setSaving(false);
267
269
  }
270
+
271
+ // Only focus next cell if user hasn't already manually changed focus
272
+ const postOpCell = gridApi.getFocusedCell();
273
+ if (
274
+ tabDirection &&
275
+ preOpCell &&
276
+ postOpCell &&
277
+ preOpCell.rowIndex == postOpCell.rowIndex &&
278
+ preOpCell.column.getColId() == postOpCell.column.getColId()
279
+ ) {
280
+ selectNextCell(tabDirection);
281
+ }
282
+
268
283
  return ok;
269
284
  });
270
285
  };
@@ -303,7 +318,6 @@ export const GridContextProvider = (props: GridContextProps): ReactElement => {
303
318
  stopEditing,
304
319
  updatingCells,
305
320
  redrawRows,
306
- selectNextCell,
307
321
  }}
308
322
  >
309
323
  {props.children}
@@ -11,9 +11,10 @@ interface GridPopoverContextProps {
11
11
  }
12
12
 
13
13
  export const GridPopoverContextProvider = ({ props, children }: GridPopoverContextProps) => {
14
- const { getSelectedRows, updatingCells, stopEditing, selectNextCell } = useContext(GridContext);
14
+ const { getSelectedRows, updatingCells } = useContext(GridContext);
15
15
  const anchorRef = useRef<Element>(props.eGridCell);
16
16
 
17
+ const hasSaved = useRef(false);
17
18
  const [saving, setSaving] = useState(false);
18
19
 
19
20
  const { colDef } = props;
@@ -28,18 +29,13 @@ export const GridPopoverContextProvider = ({ props, children }: GridPopoverConte
28
29
 
29
30
  const updateValue = useCallback(
30
31
  async (saveFn: (selectedRows: any[]) => Promise<boolean>, tabDirection: 1 | 0 | -1): Promise<boolean> => {
31
- let result = false;
32
- if (!saving) {
33
- result = await updatingCells({ selectedRows, field }, saveFn, setSaving);
34
- if (result) {
35
- stopEditing();
36
- tabDirection && selectNextCell(tabDirection);
37
- }
38
- }
39
-
40
- return result;
32
+ if (hasSaved.current) return true;
33
+ hasSaved.current = true;
34
+ const r = saving ? false : await updatingCells({ selectedRows, field }, saveFn, setSaving, tabDirection);
35
+ //if (r) stopEditing();
36
+ return r;
41
37
  },
42
- [field, saving, selectNextCell, selectedRows, stopEditing, updatingCells],
38
+ [field, saving, selectedRows, updatingCells],
43
39
  );
44
40
 
45
41
  return (
@@ -82,8 +82,10 @@ export const ControlledMenuFr = (
82
82
  // the cell doesn't refresh during update if save is invoked from a native event
83
83
  // This doesn't happen in React18
84
84
  // To work around it, I invoke the save by clicking on a passed in invisible button ref
85
- if (saveButtonRef?.current) saveButtonRef.current.click();
86
- else safeCall(onClose, { reason: CloseReason.BLUR });
85
+ if (saveButtonRef?.current) {
86
+ saveButtonRef.current.setAttribute("data-reason", CloseReason.BLUR);
87
+ saveButtonRef.current.click();
88
+ } else safeCall(onClose, { reason: CloseReason.BLUR });
87
89
 
88
90
  // If a user clicks on the menu button when a menu is open, we need to close the menu.
89
91
  // However, a blur event will be fired prior to the click event on menu button,
@@ -221,8 +223,8 @@ export const ControlledMenuFr = (
221
223
  thisDocument.addEventListener("click", handleScreenEventForCancel, true);
222
224
  thisDocument.addEventListener("dblclick", handleScreenEventForCancel, true);
223
225
  return () => {
224
- thisDocument.addEventListener("keydown", handleKeydownTabAndEnter, true);
225
- thisDocument.addEventListener("keyup", handleKeyupTabAndEnter, true);
226
+ thisDocument.removeEventListener("keydown", handleKeydownTabAndEnter, true);
227
+ thisDocument.removeEventListener("keyup", handleKeyupTabAndEnter, true);
226
228
  thisDocument.removeEventListener("mousedown", handleScreenEventForSave, true);
227
229
  thisDocument.removeEventListener("mouseup", handleScreenEventForCancel, true);
228
230
  thisDocument.removeEventListener("click", handleScreenEventForCancel, true);
@@ -66,7 +66,7 @@ const GridEditDropDownTemplate: ComponentStory<typeof Grid> = (props: GridProps)
66
66
  "Scrum Master",
67
67
  "Tester",
68
68
  MenuSeparatorString,
69
- "(other)",
69
+ "Custom",
70
70
  ].filter((v) => (filter != null ? v != null && v.toLowerCase().indexOf(filter) === 0 : true));
71
71
  }, []);
72
72
 
@@ -86,20 +86,6 @@ const GridEditDropDownTemplate: ComponentStory<typeof Grid> = (props: GridProps)
86
86
  initialWidth: 65,
87
87
  maxWidth: 85,
88
88
  }),
89
- GridPopoverEditDropDown(
90
- {
91
- field: "position",
92
- initialWidth: 65,
93
- maxWidth: 150,
94
- headerName: "Position",
95
- },
96
- {
97
- multiEdit: false,
98
- editorParams: {
99
- options: ["Architect", "Developer", "Product Owner", "Scrum Master", "Tester", MenuSeparator, "(other)"],
100
- },
101
- },
102
- ),
103
89
  GridPopoverEditDropDown(
104
90
  {
105
91
  field: "position2",
@@ -133,7 +119,7 @@ const GridEditDropDownTemplate: ComponentStory<typeof Grid> = (props: GridProps)
133
119
  {
134
120
  multiEdit: true,
135
121
  editorParams: {
136
- options: [null, "Architect", "Developer", "Product Owner", "Scrum Master", "Tester", "(other)"],
122
+ options: [null, "Architect", "Developer", "Product Owner", "Scrum Master", "Tester"],
137
123
  onSelectedItem: async (selected) => {
138
124
  await wait(2000);
139
125
  selected.selectedRows.forEach((row) => {
@@ -178,7 +164,7 @@ const GridEditDropDownTemplate: ComponentStory<typeof Grid> = (props: GridProps)
178
164
  editorParams: {
179
165
  filtered: "local",
180
166
  filterPlaceholder: "Filter this",
181
- options: [null, "Architect", "Developer", "Product Owner", "Scrum Master", "Tester", "(other)"],
167
+ options: [null, "Architect", "Developer", "Product Owner", "Scrum Master", "Tester"],
182
168
  },
183
169
  },
184
170
  ),
@@ -201,33 +187,12 @@ const GridEditDropDownTemplate: ComponentStory<typeof Grid> = (props: GridProps)
201
187
  },
202
188
  },
203
189
  ),
204
- GridPopoverEditDropDown(
205
- {
206
- field: "code",
207
- initialWidth: 65,
208
- maxWidth: 150,
209
- headerName: "Max height",
210
- valueGetter: (params) => params.data.code,
211
- },
212
- {
213
- multiEdit: true,
214
- editorParams: {
215
- className: "GridPopoverEditDropDown-containerSmall",
216
- filtered: "local",
217
- filterPlaceholder: "Filter this",
218
- options: Array.from(Array(30).keys()).map((o) => {
219
- return { value: o, label: `${o}` };
220
- }),
221
- },
222
- },
223
- ),
224
190
  GridPopoverEditDropDown(
225
191
  {
226
192
  field: "code",
227
193
  initialWidth: 65,
228
194
  maxWidth: 150,
229
195
  headerName: "Filter Selectable",
230
- valueGetter: (params) => params.data.code,
231
196
  },
232
197
  {
233
198
  multiEdit: true,
@@ -291,7 +256,7 @@ const GridEditDropDownTemplate: ComponentStory<typeof Grid> = (props: GridProps)
291
256
  {},
292
257
  {
293
258
  editorParams: {
294
- options: async () => [{ label: "Hello", action: async () => {}, supportsMultiEdit: true }],
259
+ options: async () => [{ label: "Hello", action: async () => {} }],
295
260
  },
296
261
  },
297
262
  ),
@@ -63,7 +63,7 @@ const GridPopoutEditGenericTemplate: ComponentStory<typeof Grid> = (props: GridP
63
63
  required: true,
64
64
  maxLength: 12,
65
65
  placeholder: "Enter some text...",
66
- validate: (value: string) => {
66
+ invalid: (value: string) => {
67
67
  if (value === "never") return "The value 'never' is not allowed";
68
68
  return null;
69
69
  },
@@ -91,7 +91,7 @@ const GridPopoutEditGenericTemplate: ComponentStory<typeof Grid> = (props: GridP
91
91
  maxLength: 12,
92
92
  placeholder: "Enter distance...",
93
93
  units: "m",
94
- validate: (value: string) => {
94
+ invalid: (value: string) => {
95
95
  if (value.length && !isFloat(value)) return "Value must be a number";
96
96
  return null;
97
97
  },
@@ -118,7 +118,7 @@ const GridPopoutEditGenericTemplate: ComponentStory<typeof Grid> = (props: GridP
118
118
  maxLength: 32,
119
119
  placeholder: "Enter some text...",
120
120
 
121
- validate: (value: string) => {
121
+ invalid: (value: string) => {
122
122
  if (value === "never") return "The value 'never' is not allowed";
123
123
  return null;
124
124
  },
@@ -144,7 +144,6 @@ const GridPopoutEditGenericTemplate: ComponentStory<typeof Grid> = (props: GridP
144
144
  await wait(1500);
145
145
  setRowData(rowData.filter((data) => !selectedRows.some((row) => row.id == data.id)));
146
146
  },
147
- supportsMultiEdit: true,
148
147
  },
149
148
  ],
150
149
  },
@@ -139,7 +139,6 @@ const GridReadOnlyTemplate: ComponentStory<typeof Grid> = (props: GridProps) =>
139
139
  },
140
140
  {
141
141
  label: "Other (TextArea)",
142
- supportsMultiEdit: true,
143
142
  action: async (_, menuOptionResult) => {
144
143
  // eslint-disable-next-line no-console
145
144
  console.log(`Sub selected value was ${JSON.stringify(menuOptionResult.subValue)}`);
@@ -27,7 +27,7 @@ export const bearingNumberParser = (value: string): number | null => {
27
27
  const validMaskForDmsBearing = /^((-)?\d+)?(\.([0-5](\d([0-5](\d(\d+)?)?)?)?)?)?$/;
28
28
  export const bearingStringValidator = (
29
29
  value: string,
30
- customValidate?: (value: number | null) => string | null,
30
+ customInvalid?: (value: number | null) => string | null,
31
31
  ): string | null => {
32
32
  value = value.trim();
33
33
  if (value === "") return null;
@@ -39,7 +39,7 @@ export const bearingStringValidator = (
39
39
  }
40
40
 
41
41
  const bearing = parseFloat(value);
42
- return customValidate ? customValidate(bearing) : null;
42
+ return customInvalid ? customInvalid(bearing) : null;
43
43
  };
44
44
 
45
45
  // Decimal-ish degrees to Degrees Minutes Seconds converter
@@ -3,7 +3,8 @@ import { GridBaseRow } from "../components/Grid";
3
3
  export interface TextInputValidatorProps<RowType extends GridBaseRow> {
4
4
  required?: boolean;
5
5
  maxLength?: number;
6
- validate?: (value: string, data: RowType) => string | null;
6
+ maxBytes?: number;
7
+ invalid?: (value: string, data: RowType) => string | null;
7
8
  }
8
9
 
9
10
  export const TextInputValidator = <RowType extends GridBaseRow>(
@@ -23,8 +24,11 @@ export const TextInputValidator = <RowType extends GridBaseRow>(
23
24
  if (props.maxLength && value.length > props.maxLength) {
24
25
  return `Text must be no longer than ${props.maxLength} characters`;
25
26
  }
26
- if (props.validate) {
27
- return props.validate(value, data);
27
+ if (props.maxBytes && new TextEncoder().encode(value).length > props.maxBytes) {
28
+ return `Text must be no longer than ${props.maxLength} bytes`;
29
+ }
30
+ if (props.invalid) {
31
+ return props.invalid(value, data);
28
32
  }
29
33
  return null;
30
34
  };