@linzjs/step-ag-grid 4.0.2 → 4.0.3

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": "4.0.2",
5
+ "version": "4.0.3",
6
6
  "keywords": [
7
7
  "aggrid",
8
8
  "ag-grid",
@@ -7,6 +7,7 @@ import { MenuCloseEvent } from "../react-menu3/types";
7
7
 
8
8
  export interface GridPopoverHookProps<RowType> {
9
9
  className: string | undefined;
10
+ invalid?: () => Promise<boolean | string | null> | boolean | string | null;
10
11
  save?: (selectedRows: RowType[]) => Promise<boolean>;
11
12
  }
12
13
 
@@ -22,12 +23,25 @@ export const useGridPopoverHook = <RowType extends GridBaseRow>(props: GridPopov
22
23
 
23
24
  const triggerSave = useCallback(
24
25
  async (reason?: string) => {
25
- if (reason == "cancel" || !props.save || (updateValue && (await updateValue(props.save)))) {
26
- setOpen(false);
26
+ if (reason == "cancel") {
27
27
  stopEditing();
28
+ return;
29
+ }
30
+ if (props.invalid && props.invalid()) {
31
+ return;
32
+ }
33
+
34
+ if (!props.save) {
35
+ stopEditing();
36
+ } else if (props.save) {
37
+ // forms that don't provide an invalid fn must wait until they have saved to close
38
+ if (props.invalid) stopEditing();
39
+ if (await updateValue(props.save)) {
40
+ if (!props.invalid) stopEditing();
41
+ }
28
42
  }
29
43
  },
30
- [props.save, stopEditing, updateValue],
44
+ [props, stopEditing, updateValue],
31
45
  );
32
46
 
33
47
  const onlyInputKeyboardEventHandlers: {
@@ -139,7 +153,13 @@ export const useGridPopoverHook = <RowType extends GridBaseRow>(props: GridPopov
139
153
  />
140
154
  )}
141
155
  {children}
142
- <button ref={saveButtonRef} onClick={() => triggerSave().then()} style={{ display: "none" }} />
156
+ <button
157
+ ref={saveButtonRef}
158
+ onClick={() => {
159
+ triggerSave().then();
160
+ }}
161
+ style={{ display: "none" }}
162
+ />
143
163
  </ControlledMenu>
144
164
  )}
145
165
  </>
@@ -67,17 +67,20 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
67
67
  const { selectedRows, field, updateValue, data } = useGridPopoverContext<RowType>();
68
68
  const { stopEditing } = useContext(GridContext);
69
69
 
70
+ // Save triggers during async action processing which triggers another selectItem(), this ref blocks that
71
+ const hasSubmitted = useRef(false);
70
72
  const [filter, setFilter] = useState("");
71
73
  const [filteredValues, setFilteredValues] = useState<any[]>([]);
72
74
  const optionsInitialising = useRef(false);
73
75
  const [options, setOptions] = useState<FinalSelectOption<ValueType>[] | null>(null);
74
76
  const subComponentIsValid = useRef(false);
75
77
  const [subSelectedValue, setSubSelectedValue] = useState<any>();
76
- const [selectedSubComponent, setSelectedSubComponent] = useState<any>();
78
+ const [selectedSubComponent, setSelectedSubComponent] = useState<FinalSelectOption<any> | null>(null);
77
79
 
78
80
  const selectItemHandler = useCallback(
79
81
  async (value: ValueType, subComponentValue?: ValueType): Promise<boolean> => {
80
- if (subComponentValue !== undefined && !subComponentIsValid.current) return false;
82
+ if (hasSubmitted.current || (subComponentValue !== undefined && !subComponentIsValid.current)) return false;
83
+ hasSubmitted.current = true;
81
84
 
82
85
  return updateValue(async (selectedRows) => {
83
86
  const hasChanged = selectedRows.some((row) => row[field as keyof RowType] !== value);
@@ -196,12 +199,16 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
196
199
  const save = useCallback(async () => {
197
200
  // Handler for sub-selected value
198
201
  if (!selectedSubComponent) return true;
199
- if (!subComponentIsValid.current) return false;
202
+ if (selectedSubComponent.subComponent && !subComponentIsValid.current) return false;
200
203
  await selectItemHandler(selectedSubComponent.value as ValueType, subSelectedValue);
201
204
  return true;
202
205
  }, [selectItemHandler, selectedSubComponent, subSelectedValue]);
203
206
 
204
- const { popoverWrapper } = useGridPopoverHook({ className: props.className, save });
207
+ const { popoverWrapper } = useGridPopoverHook({
208
+ className: props.className,
209
+ invalid: () => !!(selectedSubComponent && !subComponentIsValid.current),
210
+ save,
211
+ });
205
212
  return popoverWrapper(
206
213
  <>
207
214
  {props.filtered && (
@@ -247,9 +254,11 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
247
254
  onClick={(e: ClickEvent) => {
248
255
  if (item.subComponent) {
249
256
  if (selectedSubComponent === item) {
257
+ // toggle selection off
250
258
  setSelectedSubComponent(null);
251
259
  subComponentIsValid.current = true;
252
260
  } else {
261
+ // toggle selection on
253
262
  setSelectedSubComponent(item);
254
263
  }
255
264
  e.keepOpen = true;
@@ -259,6 +268,7 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
259
268
  }}
260
269
  >
261
270
  {item.label ?? (item.value == null ? `<${item.value}>` : `${item.value}`)}
271
+ {item.subComponent ? "..." : ""}
262
272
  </MenuItem>
263
273
 
264
274
  {item.subComponent && selectedSubComponent === item && (
@@ -1,6 +1,6 @@
1
1
  import "../../styles/GridFormEditBearing.scss";
2
2
 
3
- import { useCallback, useState } from "react";
3
+ import { useCallback, useMemo, useState } from "react";
4
4
  import { GridBaseRow } from "../Grid";
5
5
  import { TextInputFormatted } from "../../lui/TextInputFormatted";
6
6
  import { bearingNumberParser, bearingStringValidator, convertDDToDMS } from "../../utils/bearing";
@@ -17,14 +17,21 @@ export interface GridFormEditBearingProps<RowType extends GridBaseRow> extends C
17
17
  export const GridFormEditBearing = <RowType extends GridBaseRow>(props: GridFormEditBearingProps<RowType>) => {
18
18
  const { field, value: initialValue } = useGridPopoverContext<RowType>();
19
19
 
20
- const [value, setValue] = useState<string>(`${initialValue ?? ""}`);
20
+ // This clears out any scientific precision
21
+ const defaultValue = useMemo(
22
+ () => (initialValue == null ? "" : parseFloat(parseFloat(initialValue).toFixed(10)).toString()),
23
+ [initialValue],
24
+ );
25
+
26
+ const [value, setValue] = useState<string>(defaultValue);
27
+
28
+ const invalid = useCallback(() => bearingStringValidator(value, props.range), [props.range, value]);
21
29
 
22
30
  const save = useCallback(
23
31
  async (selectedRows: RowType[]): Promise<boolean> => {
24
- if (bearingStringValidator(value)) return false;
25
32
  const parsedValue = bearingNumberParser(value);
26
33
  // Value didn't change so don't save just cancel
27
- if (parsedValue === initialValue) {
34
+ if (parsedValue === bearingNumberParser(defaultValue)) {
28
35
  return true;
29
36
  }
30
37
 
@@ -41,17 +48,18 @@ export const GridFormEditBearing = <RowType extends GridBaseRow>(props: GridForm
41
48
  }
42
49
  return true;
43
50
  },
44
- [field, initialValue, props, value],
51
+ [defaultValue, field, props, value],
45
52
  );
46
53
  const { popoverWrapper, onlyInputKeyboardEventHandlers } = useGridPopoverHook({
47
54
  className: props.className,
55
+ invalid,
48
56
  save,
49
57
  });
50
58
 
51
59
  return popoverWrapper(
52
60
  <div className={"GridFormEditBearing-input Grid-popoverContainer"}>
53
61
  <TextInputFormatted
54
- value={value ?? ""}
62
+ value={defaultValue}
55
63
  onChange={(e) => {
56
64
  setValue(e.target.value.trim());
57
65
  }}
@@ -60,6 +68,7 @@ export const GridFormEditBearing = <RowType extends GridBaseRow>(props: GridForm
60
68
  {...onlyInputKeyboardEventHandlers}
61
69
  formatted={bearingStringValidator(value, props.range) ? "?" : convertDDToDMS(bearingNumberParser(value))}
62
70
  error={bearingStringValidator(value, props.range)}
71
+ helpText={"Press enter or tab to save"}
63
72
  />
64
73
  </div>,
65
74
  );
@@ -25,8 +25,6 @@ export const GridFormTextArea = <RowType extends GridBaseRow>(props: GridFormTex
25
25
 
26
26
  const save = useCallback(
27
27
  async (selectedRows: RowType[]): Promise<boolean> => {
28
- if (invalid()) return false;
29
-
30
28
  if (initialVale === (value ?? "")) return true;
31
29
 
32
30
  if (props.onSave) {
@@ -42,9 +40,13 @@ export const GridFormTextArea = <RowType extends GridBaseRow>(props: GridFormTex
42
40
  });
43
41
  return true;
44
42
  },
45
- [invalid, initialVale, value, props, field],
43
+ [initialVale, value, props, field],
46
44
  );
47
- const { popoverWrapper, onlyInputKeyboardEventHandlers } = useGridPopoverHook({ className: props.className, save });
45
+ const { popoverWrapper, onlyInputKeyboardEventHandlers } = useGridPopoverHook({
46
+ className: props.className,
47
+ invalid,
48
+ save,
49
+ });
48
50
  return popoverWrapper(
49
51
  <div style={{ display: "flex", flexDirection: "row", width: props.width ?? 240 }}>
50
52
  <TextAreaInput
@@ -1,10 +1,11 @@
1
- import { useCallback, useState } from "react";
1
+ import { useCallback, useContext, useMemo, useState } from "react";
2
2
  import { TextInputFormatted } from "../../lui/TextInputFormatted";
3
3
  import { useGridPopoverHook } from "../GridPopoverHook";
4
4
  import { GridBaseRow } from "../Grid";
5
5
  import { CellEditorCommon } from "../GridCell";
6
6
  import { useGridPopoverContext } from "../../contexts/GridPopoverContext";
7
7
  import { TextInputValidator, TextInputValidatorProps } from "../../utils/textValidator";
8
+ import { GridContext } from "../../contexts/GridContext";
8
9
 
9
10
  export interface GridFormTextInputProps<RowType extends GridBaseRow>
10
11
  extends TextInputValidatorProps<RowType>,
@@ -17,11 +18,12 @@ export interface GridFormTextInputProps<RowType extends GridBaseRow>
17
18
  }
18
19
 
19
20
  export const GridFormTextInput = <RowType extends GridBaseRow>(props: GridFormTextInputProps<RowType>) => {
21
+ const { stopEditing } = useContext(GridContext);
20
22
  const { field, value: initialVale, data } = useGridPopoverContext<RowType>();
21
23
 
22
24
  const helpText = props.helpText ?? "Press enter or tab to save";
23
25
 
24
- const initValue = initialVale == null ? "" : `${initialVale}`;
26
+ const initValue = useMemo(() => (initialVale == null ? "" : `${initialVale}`), [initialVale]);
25
27
  const [value, setValue] = useState(initValue);
26
28
 
27
29
  const invalid = useCallback(() => TextInputValidator<RowType>(props, value, data), [data, props, value]);
@@ -29,6 +31,7 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(props: GridFormTe
29
31
  const save = useCallback(
30
32
  async (selectedRows: RowType[]): Promise<boolean> => {
31
33
  if (invalid()) return false;
34
+ stopEditing();
32
35
  const trimmedValue = value.trim();
33
36
  if (initValue === trimmedValue) return true;
34
37
 
@@ -45,9 +48,13 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(props: GridFormTe
45
48
  });
46
49
  return true;
47
50
  },
48
- [invalid, value, initValue, props, field],
51
+ [invalid, stopEditing, value, initValue, props, field],
49
52
  );
50
- const { popoverWrapper, onlyInputKeyboardEventHandlers } = useGridPopoverHook({ className: props.className, save });
53
+ const { popoverWrapper, onlyInputKeyboardEventHandlers } = useGridPopoverHook({
54
+ className: props.className,
55
+ invalid,
56
+ save,
57
+ });
51
58
 
52
59
  return popoverWrapper(
53
60
  <div style={{ display: "flex", flexDirection: "row", width: props.width ?? 240 }} className={"FormTest"}>
@@ -39,10 +39,12 @@ export const FormTest = (props: CellEditorCommon): JSX.Element => {
39
39
 
40
40
  const [showModal, setShowModal] = useState<boolean>(false);
41
41
 
42
- const { popoverWrapper, firstInputKeyboardEventHandlers, lastInputKeyboardEventHandlers } = useGridPopoverHook({
43
- className: props.className,
44
- save,
45
- });
42
+ const { popoverWrapper, firstInputKeyboardEventHandlers, lastInputKeyboardEventHandlers, triggerSave } =
43
+ useGridPopoverHook({
44
+ className: props.className,
45
+ invalid: () => numba.length < 3,
46
+ save,
47
+ });
46
48
 
47
49
  return popoverWrapper(
48
50
  <>
@@ -50,7 +52,7 @@ export const FormTest = (props: CellEditorCommon): JSX.Element => {
50
52
  <LuiAlertModal
51
53
  data-testid="WarningAlertWithButtons-modal"
52
54
  level="warning"
53
- // If panel is popped out, append modal to poppped out window DOM, otherwise use default
55
+ // If panel is popped out, append modal to popped out window DOM, otherwise use default
54
56
  //appendToElement={() => (poppedOut && popoutElement) || document.body}
55
57
  >
56
58
  <h2>Header</h2>
@@ -73,6 +75,7 @@ export const FormTest = (props: CellEditorCommon): JSX.Element => {
73
75
  level="primary"
74
76
  onClick={() => {
75
77
  setShowModal(false);
78
+ triggerSave().then();
76
79
  }}
77
80
  data-testid="WarningAlertWithButtons-ok"
78
81
  >
@@ -37,7 +37,7 @@ export default {
37
37
 
38
38
  interface ITestRow {
39
39
  id: number;
40
- bearing1: number | null;
40
+ bearing1: string | number | null;
41
41
  bearingCorrection: number | null;
42
42
  }
43
43
 
@@ -85,8 +85,9 @@ const GridReadOnlyTemplate: ComponentStory<typeof Grid> = (props: GridProps) =>
85
85
 
86
86
  const [rowData] = useState([
87
87
  { id: 1000, bearing1: 1.234, bearingCorrection: 90 },
88
- { id: 1001, bearing1: 1.565, bearingCorrection: 240 },
88
+ { id: 1001, bearing1: "0E-12", bearingCorrection: 240 },
89
89
  { id: 1002, bearing1: null, bearingCorrection: 355.1 },
90
+ { id: 1003, bearing1: null, bearingCorrection: 0 },
90
91
  ] as ITestRow[]);
91
92
 
92
93
  return (
@@ -1,7 +1,7 @@
1
1
  import { ValueFormatterParams } from "ag-grid-community/dist/lib/entities/colDef";
2
2
 
3
3
  export const bearingValueFormatter = (params: ValueFormatterParams): string => {
4
- const value = params.value;
4
+ const value = typeof params.value == "string" ? parseFloat(params.value) : params.value;
5
5
  if (value == null) {
6
6
  return "-";
7
7
  }