@linzjs/step-ag-grid 1.5.2 → 1.5.4

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": "1.5.2",
5
+ "version": "1.5.4",
6
6
  "main": "dist/index.js",
7
7
  "typings": "dist/index.d.ts",
8
8
  "module": "dist/step-ag-grid.esm.js",
@@ -4,7 +4,7 @@ import "./GridTheme.scss";
4
4
  import clsx from "clsx";
5
5
  import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
6
6
  import { AgGridReact } from "ag-grid-react";
7
- import { AgGridEvent, CellClickedEvent, ColDef } from "ag-grid-community";
7
+ import { CellClickedEvent, ColDef } from "ag-grid-community";
8
8
  import { CellEvent, GridReadyEvent, SelectionChangedEvent } from "ag-grid-community/dist/lib/events";
9
9
  import { GridOptions } from "ag-grid-community/dist/lib/entities/gridOptions";
10
10
  import { difference, last, xorBy } from "lodash-es";
@@ -28,7 +28,7 @@ export interface GridProps {
28
28
  externalSelectedItems?: any[];
29
29
  setExternalSelectedItems?: (items: any[]) => void;
30
30
  onGridReady?: GridOptions["onGridReady"];
31
- defaultColDef: GridOptions["defaultColDef"];
31
+ defaultColDef?: GridOptions["defaultColDef"];
32
32
  columnDefs: GridOptions["columnDefs"];
33
33
  rowData: GridOptions["rowData"];
34
34
  noRowsOverlayText?: string;
@@ -38,8 +38,15 @@ export interface GridProps {
38
38
  * Wrapper for AgGrid to add commonly used functionality.
39
39
  */
40
40
  export const Grid = (params: GridProps): JSX.Element => {
41
- const { gridReady, setGridApi, setQuickFilter, ensureRowVisible, selectRowsById, ensureSelectedRowIsVisible } =
42
- useContext(GridContext);
41
+ const {
42
+ gridReady,
43
+ setGridApi,
44
+ setQuickFilter,
45
+ ensureRowVisible,
46
+ selectRowsById,
47
+ ensureSelectedRowIsVisible,
48
+ sizeColumnsToFit,
49
+ } = useContext(GridContext);
43
50
  const { checkUpdating } = useContext(UpdatingContext);
44
51
 
45
52
  const [internalQuickFilter, setInternalQuickFilter] = useState("");
@@ -156,15 +163,6 @@ export const Grid = (params: GridProps): JSX.Element => {
156
163
  [params.noRowsOverlayText],
157
164
  );
158
165
 
159
- const sizeColumnsToFit = useCallback((event: AgGridEvent) => {
160
- // @ts-ignore (gridBodyCtrl is private)
161
- if (event.api.gridBodyCtrl == undefined) {
162
- return;
163
- // this is here because ag grid was throwing api undefined error when closing POPPED OUT panel
164
- }
165
- event.api.sizeColumnsToFit();
166
- }, []);
167
-
168
166
  const refreshSelectedRows = useCallback((event: CellEvent): void => {
169
167
  // Force-refresh all selected rows to re-run class function, to update selection highlighting
170
168
  event.api.refreshCells({
@@ -227,6 +225,13 @@ export const Grid = (params: GridProps): JSX.Element => {
227
225
  [refreshSelectedRows],
228
226
  );
229
227
 
228
+ // When rows added or removed then resize columns
229
+ useEffect(() => {
230
+ if (columnDefs?.length) {
231
+ sizeColumnsToFit();
232
+ }
233
+ }, [columnDefs?.length, sizeColumnsToFit]);
234
+
230
235
  return (
231
236
  <div
232
237
  data-testid={params.dataTestId}
@@ -72,8 +72,9 @@ export const GridCell = <RowType extends GridBaseRow, FormProps extends GenericC
72
72
  }),
73
73
  // Default value formatter, otherwise react freaks out on objects
74
74
  valueFormatter: (params: ValueFormatterParams) => {
75
- const types = ["number", "undefined", "boolean", "string"];
76
- if (types.includes(typeof params.value)) return params.value;
75
+ if (params.value == null) return "-";
76
+ const types = ["number", "boolean", "string"];
77
+ if (types.includes(typeof params.value)) return `${params.value}`;
77
78
  else return JSON.stringify(params.value);
78
79
  },
79
80
  ...props,
@@ -130,6 +131,7 @@ export const GenericCellEditorComponentFr = <RowType extends GridBaseRow, FormPr
130
131
  updateValue={updateValue}
131
132
  saving={saving}
132
133
  formProps={formProps}
134
+ data={data}
133
135
  value={value}
134
136
  field={field}
135
137
  selectedRows={selectedRows}
@@ -8,17 +8,15 @@ import { delay, fromPairs } from "lodash-es";
8
8
  import { LuiCheckboxInput } from "@linzjs/lui";
9
9
  import { GenericCellEditorParams, GridFormProps } from "../GridCell";
10
10
  import { useGridPopoverHook } from "../GridPopoverHook";
11
+ import { MenuSeparatorString } from "@components/gridForm/GridFormDropDown";
11
12
 
12
- interface FinalSelectOption<ValueType> {
13
+ interface MultiFinalSelectOption<ValueType> {
13
14
  value: ValueType;
14
15
  label?: JSX.Element | string;
15
16
  subComponent?: (props: any, ref: any) => any;
16
17
  }
17
18
 
18
- export const MenuSeparatorString = "_____MENU_SEPARATOR_____";
19
- export const MenuSeparator = Object.freeze({ value: MenuSeparatorString });
20
-
21
- export type SelectOption<ValueType> = ValueType | FinalSelectOption<ValueType>;
19
+ export type MultiSelectOption<ValueType> = ValueType | MultiFinalSelectOption<ValueType>;
22
20
 
23
21
  export interface MultiSelectResult<RowType> {
24
22
  selectedRows: RowType[];
@@ -31,8 +29,8 @@ export interface GridFormMultiSelectProps<RowType extends GridBaseRow, ValueType
31
29
  filterPlaceholder?: string;
32
30
  onSave?: (props: MultiSelectResult<RowType>) => Promise<boolean>;
33
31
  options:
34
- | SelectOption<ValueType>[]
35
- | ((selectedRows: RowType[]) => Promise<SelectOption<ValueType>[]> | SelectOption<ValueType>[]);
32
+ | MultiSelectOption<ValueType>[]
33
+ | ((selectedRows: RowType[]) => Promise<MultiSelectOption<ValueType>[]> | MultiSelectOption<ValueType>[]);
36
34
  initialSelectedValues?: (selectedRows: RowType[]) => any[];
37
35
  }
38
36
 
@@ -42,7 +40,7 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(prop
42
40
  const [filter, setFilter] = useState("");
43
41
  const [filteredValues, setFilteredValues] = useState<any[]>([]);
44
42
  const optionsInitialising = useRef(false);
45
- const [options, setOptions] = useState<FinalSelectOption<ValueType>[]>();
43
+ const [options, setOptions] = useState<MultiFinalSelectOption<ValueType>[]>();
46
44
  const subSelectedValues = useRef<Record<string, any>>({});
47
45
  const [selectedValues, setSelectedValues] = useState<any[]>(() =>
48
46
  formProps.initialSelectedValues ? formProps.initialSelectedValues(props.selectedRows) : [],
@@ -75,10 +73,10 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(prop
75
73
 
76
74
  const optionsList = optionsConf?.map((item) => {
77
75
  if (item == null || typeof item == "string" || typeof item == "number") {
78
- item = { value: item as ValueType, label: item } as FinalSelectOption<ValueType>;
76
+ item = { value: item as ValueType, label: item } as MultiFinalSelectOption<ValueType>;
79
77
  }
80
78
  return item;
81
- }) as any as FinalSelectOption<ValueType>[];
79
+ }) as any as MultiFinalSelectOption<ValueType>[];
82
80
 
83
81
  if (formProps.filtered) {
84
82
  // This is needed otherwise when filter input is rendered and sets autofocus
@@ -11,7 +11,7 @@ export interface GridFormPopoutMenuProps<RowType extends GridBaseRow> extends Ge
11
11
  }
12
12
 
13
13
  /** Menu configuration types **/
14
- export const MenuSeparator = Object.freeze({ __isMenuSeparator__: true });
14
+ export const PopoutMenuSeparator = Object.freeze({ __isMenuSeparator__: true });
15
15
 
16
16
  interface MenuSeparatorType {
17
17
  __isMenuSeparator__: boolean;
@@ -66,7 +66,7 @@ export const GridFormPopoutMenu = <RowType extends GridBaseRow>(props: GridFormP
66
66
  const selectedRowCount = props.selectedRows.length;
67
67
 
68
68
  const filteredOptions = options?.filter((menuOption) => {
69
- return menuOption.label === MenuSeparator || selectedRowCount === 1 || menuOption.supportsMultiEdit;
69
+ return menuOption.label === PopoutMenuSeparator || selectedRowCount === 1 || menuOption.supportsMultiEdit;
70
70
  });
71
71
 
72
72
  const { popoverWrapper } = useGridPopoverHook(props);
@@ -74,7 +74,7 @@ export const GridFormPopoutMenu = <RowType extends GridBaseRow>(props: GridFormP
74
74
  <ComponentLoadingWrapper loading={!filteredOptions}>
75
75
  <div className={"Grid-popoverContainerList"}>
76
76
  {options?.map((item, index) =>
77
- item.label === MenuSeparator ? (
77
+ item.label === PopoutMenuSeparator ? (
78
78
  <MenuDivider key={`$$divider_${index}`} />
79
79
  ) : (
80
80
  !item.hidden && (
@@ -6,26 +6,30 @@ import { GridBaseRow } from "../Grid";
6
6
 
7
7
  export interface GridFormTextInputProps<RowType extends GridBaseRow> extends GenericCellEditorParams<RowType> {
8
8
  placeholder?: string;
9
+ units?: string;
9
10
  required?: boolean;
10
11
  maxlength?: number;
11
12
  width?: string | number;
12
- validate?: (value: string) => string | null;
13
+ // Return null for ok, otherwise an error string
14
+ validate?: (value: string, data: RowType) => string | null;
13
15
  onSave?: (selectedRows: RowType[], value: string) => Promise<boolean>;
14
16
  }
15
17
 
16
18
  export const GridFormTextInput = <RowType extends GridBaseRow>(props: GridFormProps<RowType>) => {
17
19
  const formProps = props.formProps as GridFormTextInputProps<RowType>;
18
- const [value, setValue] = useState(props.value ?? "");
20
+ const initValue = props.value == null ? "" : `${props.value}`;
21
+ const [value, setValue] = useState(initValue);
19
22
 
20
23
  const invalid = useCallback(() => {
21
- if (formProps.required && value.length == 0) {
24
+ const trimmedValue = value.trim();
25
+ if (formProps.required && trimmedValue.length == 0) {
22
26
  return `Some text is required`;
23
27
  }
24
- if (formProps.maxlength && value.length > formProps.maxlength) {
28
+ if (formProps.maxlength && trimmedValue.length > formProps.maxlength) {
25
29
  return `Text must be no longer than ${formProps.maxlength} characters`;
26
30
  }
27
31
  if (formProps.validate) {
28
- return formProps.validate(value);
32
+ return formProps.validate(trimmedValue, props.data);
29
33
  }
30
34
  return null;
31
35
  }, [formProps, value]);
@@ -33,11 +37,11 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(props: GridFormPr
33
37
  const save = useCallback(
34
38
  async (selectedRows: any[]): Promise<boolean> => {
35
39
  if (invalid()) return false;
36
-
37
- if (props.value === (value ?? "")) return true;
40
+ const trimmedValue = value.trim();
41
+ if (initValue === trimmedValue) return true;
38
42
 
39
43
  if (formProps.onSave) {
40
- return await formProps.onSave(selectedRows, value);
44
+ return await formProps.onSave(selectedRows, trimmedValue);
41
45
  }
42
46
 
43
47
  const field = props.field;
@@ -45,10 +49,10 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(props: GridFormPr
45
49
  console.error("ColDef has no field set");
46
50
  return false;
47
51
  }
48
- selectedRows.forEach((row) => (row[field] = value));
52
+ selectedRows.forEach((row) => (row[field] = trimmedValue));
49
53
  return true;
50
54
  },
51
- [invalid, props.value, props.field, value, formProps],
55
+ [invalid, value, initValue, formProps, props.field],
52
56
  );
53
57
  const { popoverWrapper, triggerSave } = useGridPopoverHook(props, save);
54
58
 
@@ -58,7 +62,7 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(props: GridFormPr
58
62
  value={value}
59
63
  onChange={(e) => setValue(e.target.value)}
60
64
  error={invalid()}
61
- formatted={""}
65
+ formatted={formProps.units}
62
66
  inputProps={{
63
67
  style: { width: "100%" },
64
68
  placeholder: formProps.placeholder,
@@ -17,6 +17,7 @@ export interface GridContextType {
17
17
  flashRowsDiff: (updateFn: () => Promise<any>) => Promise<void>;
18
18
  ensureRowVisible: (id: number) => void;
19
19
  ensureSelectedRowIsVisible: () => void;
20
+ sizeColumnsToFit: () => void;
20
21
  stopEditing: () => void;
21
22
  updatingCells: (
22
23
  props: { selectedRows: GridBaseRow[]; field?: string },
@@ -68,6 +69,9 @@ export const GridContext = createContext<GridContextType>({
68
69
  ensureSelectedRowIsVisible: () => {
69
70
  console.error("no context provider for ensureSelectedRowIsVisible");
70
71
  },
72
+ sizeColumnsToFit: () => {
73
+ console.error("no context provider for sizeColumnsToFit");
74
+ },
71
75
  editingCells: () => {
72
76
  console.error("no context provider for editingCells");
73
77
  return false;
@@ -220,6 +220,15 @@ export const GridContextProvider = (props: GridContextProps): ReactElement => {
220
220
  });
221
221
  };
222
222
 
223
+ /**
224
+ * Resize columns to fit container
225
+ */
226
+ const sizeColumnsToFit = (): void => {
227
+ gridApiOp((gridApi) => {
228
+ gridApi.sizeColumnsToFit();
229
+ });
230
+ };
231
+
223
232
  const stopEditing = (): void => gridApiOp((gridApi) => gridApi.stopEditing());
224
233
 
225
234
  const updatingCells = async (
@@ -283,6 +292,7 @@ export const GridContextProvider = (props: GridContextProps): ReactElement => {
283
292
  editingCells,
284
293
  ensureRowVisible,
285
294
  ensureSelectedRowIsVisible,
295
+ sizeColumnsToFit,
286
296
  stopEditing,
287
297
  updatingCells,
288
298
  }}
@@ -14,9 +14,8 @@ export interface LuiTextInputProps {
14
14
  className?: string;
15
15
  value: string;
16
16
 
17
- icon?: JSX.Element;
18
17
  placeholder?: string;
19
- formatted: string;
18
+ formatted?: string;
20
19
  }
21
20
 
22
21
  export const TextInputFormatted = (props: LuiTextInputProps): JSX.Element => {
@@ -39,7 +38,6 @@ export const TextInputFormatted = (props: LuiTextInputProps): JSX.Element => {
39
38
  {...props.inputProps}
40
39
  />
41
40
  <span className={"LuiTextInput-formatted"}>{props.formatted}</span>
42
- {props.icon}
43
41
  </span>
44
42
 
45
43
  {props.error && (
@@ -13,6 +13,7 @@ export interface IFormTestRow {
13
13
  nameType: string;
14
14
  numba: string;
15
15
  plan: string;
16
+ distance: number | null;
16
17
  }
17
18
 
18
19
  export const FormTest = <RowType extends GridBaseRow>(props: GridFormProps<RowType>): JSX.Element => {
@@ -9,7 +9,7 @@ import { Grid, GridProps } from "@components/Grid";
9
9
  import { useMemo, useState } from "react";
10
10
  import { GridCell } from "@components/GridCell";
11
11
  import { IFormTestRow } from "./FormTest";
12
- import { wait } from "@utils/util";
12
+ import { isFloat, wait } from "@utils/util";
13
13
  import { GridPopoverTextArea } from "@components/gridPopoverEdit/GridPopoverTextArea";
14
14
  import { GridPopoverTextInput } from "@components/gridPopoverEdit/GridPopoverTextInput";
15
15
 
@@ -63,6 +63,30 @@ const GridPopoutEditGenericTemplate: ComponentStory<typeof Grid> = (props: GridP
63
63
  },
64
64
  },
65
65
  }),
66
+ GridPopoverTextInput<IFormTestRow>({
67
+ field: "distance",
68
+ headerName: "Number input",
69
+ maxWidth: 140,
70
+ valueFormatter: (params) => {
71
+ const v = params.data.distance;
72
+ return v != null ? `${v}${params.colDef.cellEditorParams.units}` : v;
73
+ },
74
+ cellEditorParams: {
75
+ maxlength: 12,
76
+ placeholder: "Enter distance...",
77
+ units: "m",
78
+ validate: (value: string) => {
79
+ if (value.length && !isFloat(value)) return "Value must be a number";
80
+ return null;
81
+ },
82
+ multiEdit: false,
83
+ onSave: async (selectedRows, value) => {
84
+ await wait(1000);
85
+ selectedRows.forEach((selectedRow) => (selectedRow["distance"] = value.length ? parseFloat(value) : null));
86
+ return true;
87
+ },
88
+ },
89
+ }),
66
90
  GridPopoverTextArea<IFormTestRow>({
67
91
  field: "plan",
68
92
  headerName: "Text area",
@@ -90,8 +114,8 @@ const GridPopoutEditGenericTemplate: ComponentStory<typeof Grid> = (props: GridP
90
114
  const rowData = useMemo(
91
115
  () =>
92
116
  [
93
- { id: 1000, name: "IS IS DP12345", nameType: "IS", numba: "IX", plan: "DP 12345" },
94
- { id: 1001, name: "PEG V SD523", nameType: "PEG", numba: "V", plan: "SD 523" },
117
+ { id: 1000, name: "IS IS DP12345", nameType: "IS", numba: "IX", plan: "DP 12345", distance: 10 },
118
+ { id: 1001, name: "PEG V SD523", nameType: "PEG", numba: "V", plan: "SD 523", distance: null },
95
119
  ] as IFormTestRow[],
96
120
  [],
97
121
  );