@linzjs/step-ag-grid 2.1.3 → 2.2.1

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 (35) hide show
  1. package/dist/index.js +123 -20
  2. package/dist/index.js.map +1 -1
  3. package/dist/src/index.d.ts +4 -0
  4. package/dist/src/lui/ActionButton.d.ts +15 -0
  5. package/dist/src/lui/reactUtils.d.ts +6 -0
  6. package/dist/src/lui/reactUtils.test.d.ts +1 -0
  7. package/dist/src/lui/stateDeferredHook.d.ts +4 -0
  8. package/dist/src/lui/timeoutHook.d.ts +8 -0
  9. package/dist/src/lui/timeoutHook.test.d.ts +1 -0
  10. package/dist/src/react-menu3/components/ControlledMenu.d.ts +2 -2
  11. package/dist/src/react-menu3/types.d.ts +4 -0
  12. package/dist/step-ag-grid.esm.js +116 -22
  13. package/dist/step-ag-grid.esm.js.map +1 -1
  14. package/package.json +4 -3
  15. package/src/components/GridPopoverHook.tsx +1 -0
  16. package/src/index.ts +5 -0
  17. package/src/lui/ActionButton.scss +32 -0
  18. package/src/lui/ActionButton.tsx +76 -0
  19. package/src/lui/reactUtils.test.tsx +47 -0
  20. package/src/lui/reactUtils.tsx +14 -0
  21. package/src/lui/stateDeferredHook.tsx +29 -0
  22. package/src/lui/timeoutHook.test.tsx +38 -0
  23. package/src/lui/timeoutHook.tsx +40 -0
  24. package/src/react-menu3/components/ControlledMenu.tsx +7 -4
  25. package/src/react-menu3/types.ts +5 -0
  26. package/src/stories/components/ActionButton.stories.tsx +33 -0
  27. package/src/stories/{components → grid}/FormTest.scss +0 -0
  28. package/src/stories/{components → grid}/FormTest.tsx +0 -0
  29. package/src/stories/{components → grid}/GridPopoutBearing.stories.tsx +7 -10
  30. package/src/stories/{components → grid}/GridPopoutEditDropDown.stories.tsx +20 -23
  31. package/src/stories/{components → grid}/GridPopoutEditGeneric.stories.tsx +6 -9
  32. package/src/stories/{components → grid}/GridPopoutEditGenericTextArea.stories.tsx +57 -18
  33. package/src/stories/{components → grid}/GridPopoutEditMultiSelect.stories.tsx +6 -9
  34. package/src/stories/{components → grid}/GridReadOnly.stories.tsx +6 -8
  35. /package/src/stories/{components → react-menu}/ReactMenu.stories.tsx +0 -0
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": "2.1.3",
5
+ "version": "2.2.1",
6
6
  "keywords": [
7
7
  "aggrid",
8
8
  "ag-grid",
@@ -91,9 +91,10 @@
91
91
  "@storybook/react": "6.5.13",
92
92
  "@storybook/test-runner": "^0.9.0",
93
93
  "@storybook/testing-library": "^0.0.13",
94
+ "@testing-library/dom": "^8.19.0",
94
95
  "@testing-library/jest-dom": "^5.16.5",
95
- "@testing-library/react": "^13.4.0",
96
- "@testing-library/user-event": "^14.4.3",
96
+ "@testing-library/react": "^12.1.5",
97
+ "@testing-library/user-event": "^13.5.0",
97
98
  "@types/debounce-promise": "^3.1.5",
98
99
  "@types/jest": "^29.2.2",
99
100
  "@types/lodash-es": "^4.17.6",
@@ -49,6 +49,7 @@ export const useGridPopoverHook = <RowType extends GridBaseRow>(props: GridPopov
49
49
  viewScroll={"auto"}
50
50
  dontShrinkIfDirectionIsTop={true}
51
51
  className={props.className}
52
+ closeMenuExclusionClassName={"ReactModal__Content"}
52
53
  >
53
54
  {saving && ( // This is the overlay that prevents editing when the editor is saving
54
55
  <div
package/src/index.ts CHANGED
@@ -27,6 +27,9 @@ export { GridPopoverEditDropDown } from "./components/gridPopoverEdit/GridPopove
27
27
  export { GridPopoverMessage } from "./components/gridPopoverEdit/GridPopoverMessage";
28
28
  export { GridPopoverTextArea } from "./components/gridPopoverEdit/GridPopoverTextArea";
29
29
  export { GridPopoverTextInput } from "./components/gridPopoverEdit/GridPopoverTextInput";
30
+ export * from "./components/gridForm/GridFormDropDown";
31
+ export * from "./components/gridForm/GridFormMultiSelect";
32
+ export * from "./components/gridForm/GridFormPopoutMenu";
30
33
 
31
34
  export { GridHeaderSelect } from "./components/gridHeader/GridHeaderSelect";
32
35
 
@@ -34,5 +37,7 @@ export { TextAreaInput } from "./lui/TextAreaInput";
34
37
  export { TextInputFormatted } from "./lui/TextInputFormatted";
35
38
  export { GridSubComponentTextArea } from "./components/GridSubComponentTextArea";
36
39
 
40
+ export * from "./lui/ActionButton";
41
+
37
42
  export * from "./utils/bearing";
38
43
  export * from "./utils/util";
@@ -0,0 +1,32 @@
1
+ @use "../../node_modules/@linzjs/lui/dist/scss/Foundation/Variables/ColorVars" as colors;
2
+
3
+ .ActionButton {
4
+ display: flex;
5
+ align-items: center;
6
+ }
7
+
8
+ .ActionButton .LuiIcon {
9
+ margin: 0 4px !important;
10
+ }
11
+
12
+ .ActionButton-minimalArea {
13
+ position: relative;
14
+ }
15
+
16
+ .ActionButton-minimalAreaDisplay {
17
+ position: absolute;
18
+ }
19
+
20
+ .ActionButton-minimalAreaExpand {
21
+ visibility: hidden;
22
+ }
23
+
24
+ .ActionButton-inProgress {
25
+ cursor: progress;
26
+ color: #007198 !important;
27
+ background-color: colors.$polar !important;
28
+
29
+ svg * {
30
+ fill: transparent !important;
31
+ }
32
+ }
@@ -0,0 +1,76 @@
1
+ import "./ActionButton.scss";
2
+
3
+ import clsx from "clsx";
4
+ import { useEffect } from "react";
5
+ import { LuiButton, LuiIcon, LuiMiniSpinner } from "@linzjs/lui";
6
+ import { IconName } from "@linzjs/lui/dist/components/LuiIcon/LuiIcon";
7
+ import { usePrevious } from "./reactUtils";
8
+ import { useStateDeferred } from "./stateDeferredHook";
9
+
10
+ export interface ActionButtonProps {
11
+ icon: IconName;
12
+ name: string;
13
+ inProgressName?: string;
14
+ title?: string;
15
+ dataTestId?: string;
16
+ size?: "xs" | "sm" | "md" | "lg" | "xl" | "ns";
17
+ className?: string;
18
+ onClick?: () => void;
19
+ inProgress?: boolean;
20
+ }
21
+
22
+ // Kept this less than one second, so I don't have issues with waitFor as it defaults to 1s
23
+ const minimumInProgressTimeMs = 950;
24
+
25
+ export const ActionButton = ({
26
+ icon,
27
+ name,
28
+ inProgressName,
29
+ dataTestId,
30
+ inProgress,
31
+ className,
32
+ title,
33
+ onClick,
34
+ size = "sm",
35
+ }: ActionButtonProps): JSX.Element => {
36
+ const lastInProgress = usePrevious(inProgress ?? false);
37
+ const [localInProgress, setLocalInProgress, setLocalInProgressDeferred] = useStateDeferred<boolean>(!!inProgress);
38
+
39
+ useEffect(() => {
40
+ if (inProgress == lastInProgress) return;
41
+ inProgress ? setLocalInProgress(true) : setLocalInProgressDeferred(false, minimumInProgressTimeMs);
42
+ }, [inProgress, lastInProgress, setLocalInProgress, setLocalInProgressDeferred]);
43
+
44
+ return (
45
+ <LuiButton
46
+ data-testid={dataTestId}
47
+ type={"button"}
48
+ level={"tertiary"}
49
+ title={title ?? name}
50
+ aria-label={name}
51
+ className={clsx("lui-button-icon", "ActionButton", className, localInProgress && "ActionButton-inProgress")}
52
+ size={"lg"}
53
+ onClick={onClick}
54
+ disabled={localInProgress}
55
+ >
56
+ {localInProgress ? (
57
+ <LuiMiniSpinner
58
+ size={16}
59
+ divProps={{
60
+ "data-testid": "loading-spinner",
61
+ style: { padding: 0, margin: 0, paddingRight: 8 },
62
+ role: "status",
63
+ ["aria-label"]: "Loading",
64
+ }}
65
+ />
66
+ ) : (
67
+ <LuiIcon name={icon} alt={name} size={size} />
68
+ )}
69
+ <span className={"ActionButton-minimalArea"}>
70
+ <span className={"ActionButton-minimalAreaDisplay"}>{(localInProgress ? inProgressName : name) ?? name}</span>
71
+ {/* This makes sure the button expands to fill maximum length text at all times */}
72
+ <span className={"ActionButton-minimalAreaExpand"}>{name}</span>
73
+ </span>
74
+ </LuiButton>
75
+ );
76
+ };
@@ -0,0 +1,47 @@
1
+ import { usePrevious } from "./reactUtils";
2
+ import { useEffect, useState } from "react";
3
+ import { act, render } from "@testing-library/react";
4
+ import { screen } from "@testing-library/dom";
5
+
6
+ interface WrapperProps {
7
+ value: boolean;
8
+ }
9
+
10
+ let extPrevious: boolean | undefined = undefined;
11
+
12
+ const UsePreviousWrapper = ({ value }: WrapperProps): JSX.Element => {
13
+ const previous = usePrevious<boolean>(value);
14
+ useEffect(() => {
15
+ extPrevious = previous;
16
+ }, [previous]);
17
+
18
+ return <div />;
19
+ };
20
+
21
+ const TestComponent = (): JSX.Element => {
22
+ const [value, setValue] = useState(false);
23
+
24
+ const toggleValue = () => setValue((value) => !value);
25
+
26
+ return (
27
+ <>
28
+ <button onClick={toggleValue}>Change value</button>
29
+ <UsePreviousWrapper value={value} />
30
+ </>
31
+ );
32
+ };
33
+
34
+ describe("usePrevious", () => {
35
+ test("usePrevious", async () => {
36
+ render(<TestComponent />);
37
+ const button = await screen.findByText("Change value");
38
+
39
+ expect(extPrevious).toBeUndefined();
40
+
41
+ act(() => button.click());
42
+ expect(extPrevious).toBe(false);
43
+
44
+ act(() => button.click());
45
+ expect(extPrevious).toBe(true);
46
+ });
47
+ });
@@ -0,0 +1,14 @@
1
+ import { useEffect, useRef } from "react";
2
+
3
+ /**
4
+ * Track previous values of states.
5
+ *
6
+ * @param value Value to track.
7
+ */
8
+ export const usePrevious = <T extends unknown>(value: T): T | undefined => {
9
+ const ref = useRef<T>();
10
+ useEffect(() => {
11
+ ref.current = value;
12
+ }, [value]);
13
+ return ref.current;
14
+ };
@@ -0,0 +1,29 @@
1
+ import { useCallback, useRef, useState } from "react";
2
+ import { useTimeoutHook } from "./timeoutHook";
3
+
4
+ /**
5
+ * Defers state change up to a minimum time since last state change.
6
+ */
7
+ export const useStateDeferred = <T extends unknown>(
8
+ initialValue: T,
9
+ ): [T, (v: T) => void, (v: T, w: number) => void] => {
10
+ const startTime = useRef<number>(0);
11
+ const timeoutHook = useTimeoutHook();
12
+
13
+ const [value, _setValue] = useState(initialValue);
14
+
15
+ const setValue = useCallback((newValue: T) => {
16
+ startTime.current = Date.now();
17
+ _setValue(newValue);
18
+ }, []);
19
+
20
+ const setValueDeferred = useCallback(
21
+ (newValue: T, minimumWaitTimeMs: number) => {
22
+ const waitTimeMs = Math.max(minimumWaitTimeMs - (Date.now() - startTime.current), 0);
23
+ timeoutHook(() => _setValue(newValue), waitTimeMs);
24
+ },
25
+ [timeoutHook],
26
+ );
27
+
28
+ return [value, setValue, setValueDeferred];
29
+ };
@@ -0,0 +1,38 @@
1
+ import { useTimeoutHook } from "./timeoutHook";
2
+ import { render } from "@testing-library/react";
3
+
4
+ describe("useTimeoutHook", () => {
5
+ beforeEach(() => {
6
+ jest.useFakeTimers();
7
+ });
8
+
9
+ afterEach(() => {
10
+ jest.useRealTimers();
11
+ });
12
+
13
+ const TestComponent = (props: { callback: () => void; repeat?: number }) => {
14
+ const timeoutHook = useTimeoutHook();
15
+ for (let i = 0; i < (props.repeat ?? 1); i++) {
16
+ timeoutHook(() => props.callback(), 1000);
17
+ }
18
+ return <div />;
19
+ };
20
+
21
+ test("invokes on timeout", async () => {
22
+ const callback = jest.fn();
23
+
24
+ render(<TestComponent callback={callback} />);
25
+ expect(callback).toHaveBeenCalledTimes(0);
26
+ jest.runAllTimers();
27
+ expect(callback).toHaveBeenCalledTimes(1);
28
+ });
29
+
30
+ test("invokes once on double invocation", async () => {
31
+ const callback = jest.fn();
32
+
33
+ render(<TestComponent callback={callback} repeat={2} />);
34
+ expect(callback).toHaveBeenCalledTimes(0);
35
+ jest.runAllTimers();
36
+ expect(callback).toHaveBeenCalledTimes(1);
37
+ });
38
+ });
@@ -0,0 +1,40 @@
1
+ import { useCallback, useEffect, useRef } from "react";
2
+
3
+ /**
4
+ * Cancels timeouts on scope being destroyed.
5
+ *
6
+ * This could almost be a debounce, but debounce tracks by function reference, this tracks by hook reference.
7
+ * This could have been implemented using debounce if the callers function was wrapped in useCallback,
8
+ * but there's no way to enforce that, so it would lead to bugs.
9
+ */
10
+ export const useTimeoutHook = () => {
11
+ const timeout = useRef<ReturnType<typeof setTimeout>>();
12
+
13
+ /**
14
+ * Clear any pending timeouts.
15
+ */
16
+ const clearTimeouts = () => {
17
+ if (timeout.current) {
18
+ const tc = timeout.current;
19
+ timeout.current = undefined;
20
+ clearTimeout(tc);
21
+ }
22
+ };
23
+
24
+ /**
25
+ * Call this when your action has completed.
26
+ */
27
+ const invoke = useCallback((fn: () => void, waitTimeMs: number) => {
28
+ clearTimeouts();
29
+ timeout.current = setTimeout(fn, waitTimeMs);
30
+ }, []);
31
+
32
+ /**
33
+ * Clear timeout on loss of scope.
34
+ */
35
+ useEffect(() => {
36
+ return () => clearTimeouts();
37
+ }, []);
38
+
39
+ return invoke;
40
+ };
@@ -40,6 +40,7 @@ export const ControlledMenuFr = (
40
40
  onItemClick,
41
41
  onClose,
42
42
  saveButtonRef,
43
+ closeMenuExclusionClassName,
43
44
  ...restProps
44
45
  }: ControlledMenuProps & { saveButtonRef?: MutableRefObject<HTMLButtonElement | null> },
45
46
  externalRef: ForwardedRef<HTMLUListElement>,
@@ -75,14 +76,16 @@ export const ControlledMenuFr = (
75
76
  ],
76
77
  );
77
78
 
78
- const clickIsWithinMenu = useCallback((ev: MouseEvent) => {
79
- return hasParentClass("szh-menu--state-open", ev.target as Node);
80
- }, []);
79
+ const clickIsWithinMenu = useCallback(
80
+ (ev: MouseEvent) =>
81
+ hasParentClass("szh-menu--state-open", ev.target as Node) ||
82
+ (closeMenuExclusionClassName && hasParentClass(closeMenuExclusionClassName, ev.target as Node)),
83
+ [closeMenuExclusionClassName],
84
+ );
81
85
 
82
86
  const handleScreenEventForSave = useCallback(
83
87
  (ev: MouseEvent) => {
84
88
  if (!clickIsWithinMenu(ev)) {
85
- //!ev.currentTarget.contains(ev.relatedTarget || document.activeElement)) {
86
89
  ev.preventDefault();
87
90
  ev.stopPropagation();
88
91
  // FIXME There's an issue in React17
@@ -439,4 +439,9 @@ export interface ControlledMenuProps extends RootMenuProps, ExtraMenuProps {
439
439
  * Event fired when menu is about to close.
440
440
  */
441
441
  onClose?: EventHandler<MenuCloseEvent>;
442
+
443
+ /**
444
+ * When clicking outside the menu to close, anything with this class will not cause a close.
445
+ */
446
+ closeMenuExclusionClassName?: string;
442
447
  }
@@ -0,0 +1,33 @@
1
+ import "@linzjs/lui/dist/scss/base.scss";
2
+ import "@linzjs/lui/dist/fonts";
3
+
4
+ import { ComponentMeta, ComponentStory } from "@storybook/react/dist/ts3.9/client/preview/types-6-3";
5
+ import { ActionButton } from "../../lui/ActionButton";
6
+ import { useCallback, useState } from "react";
7
+ import { wait } from "../../utils/util";
8
+
9
+ export default {
10
+ title: "Components / ButtonTextWithIcon",
11
+ component: ActionButton,
12
+ args: {},
13
+ } as ComponentMeta<typeof ActionButton>;
14
+
15
+ const ActionButtonTemplate: ComponentStory<typeof ActionButton> = () => {
16
+ const [inProgress, setInProgress] = useState(false);
17
+ const performAction = useCallback(async () => {
18
+ setInProgress(true);
19
+ await wait(1000);
20
+ setInProgress(false);
21
+ }, []);
22
+ return (
23
+ <ActionButton
24
+ icon={"ic_add"}
25
+ name={"Add new row"}
26
+ inProgressName={"Adding..."}
27
+ inProgress={inProgress}
28
+ onClick={performAction}
29
+ />
30
+ );
31
+ };
32
+
33
+ export const ActionButtonSimple = ActionButtonTemplate.bind({});
File without changes
File without changes
@@ -24,7 +24,7 @@ export default {
24
24
  },
25
25
  decorators: [
26
26
  (Story) => (
27
- <div style={{ width: 1200, height: 400, display: "flex" }}>
27
+ <div style={{ width: 1024, height: 400 }}>
28
28
  <GridUpdatingContextProvider>
29
29
  <GridContextProvider>
30
30
  <Story />
@@ -83,15 +83,11 @@ const GridReadOnlyTemplate: ComponentStory<typeof Grid> = (props: GridProps) =>
83
83
  [],
84
84
  );
85
85
 
86
- const rowData = useMemo(
87
- () =>
88
- [
89
- { id: 1000, bearing1: 1.234, bearingCorrection: 90 },
90
- { id: 1001, bearing1: 1.565, bearingCorrection: 240 },
91
- { id: 1002, bearing1: null, bearingCorrection: 355.1 },
92
- ] as ITestRow[],
93
- [],
94
- );
86
+ const [rowData, setRowData] = useState([
87
+ { id: 1000, bearing1: 1.234, bearingCorrection: 90 },
88
+ { id: 1001, bearing1: 1.565, bearingCorrection: 240 },
89
+ { id: 1002, bearing1: null, bearingCorrection: 355.1 },
90
+ ] as ITestRow[]);
95
91
 
96
92
  return (
97
93
  <Grid
@@ -100,6 +96,7 @@ const GridReadOnlyTemplate: ComponentStory<typeof Grid> = (props: GridProps) =>
100
96
  setExternalSelectedItems={setExternalSelectedItems}
101
97
  columnDefs={columnDefs}
102
98
  rowData={rowData}
99
+ domLayout={"autoHeight"}
103
100
  />
104
101
  );
105
102
  };
@@ -22,7 +22,7 @@ export default {
22
22
  },
23
23
  decorators: [
24
24
  (Story) => (
25
- <div style={{ width: 1200, height: 400, display: "flex" }}>
25
+ <div style={{ width: 1024, height: 400 }}>
26
26
  <GridUpdatingContextProvider>
27
27
  <GridContextProvider>
28
28
  <Story />
@@ -245,28 +245,24 @@ const GridEditDropDownTemplate: ComponentStory<typeof Grid> = (props: GridProps)
245
245
  [optionsFn, optionsObjects],
246
246
  );
247
247
 
248
- const rowData = useMemo(
249
- () =>
250
- [
251
- {
252
- id: 1000,
253
- position: "Tester",
254
- position2: "1",
255
- position3: "Tester",
256
- position4: { code: "O1", desc: "Object One" },
257
- code: "O1",
258
- },
259
- {
260
- id: 1001,
261
- position: "Developer",
262
- position2: "2",
263
- position3: "Developer",
264
- position4: { code: "O2", desc: "Object Two" },
265
- code: "O2",
266
- },
267
- ] as ITestRow[],
268
- [],
269
- );
248
+ const [rowData, setRowData] = useState([
249
+ {
250
+ id: 1000,
251
+ position: "Tester",
252
+ position2: "1",
253
+ position3: "Tester",
254
+ position4: { code: "O1", desc: "Object One" },
255
+ code: "O1",
256
+ },
257
+ {
258
+ id: 1001,
259
+ position: "Developer",
260
+ position2: "2",
261
+ position3: "Developer",
262
+ position4: { code: "O2", desc: "Object Two" },
263
+ code: "O2",
264
+ },
265
+ ] as ITestRow[]);
270
266
 
271
267
  return (
272
268
  <Grid
@@ -275,6 +271,7 @@ const GridEditDropDownTemplate: ComponentStory<typeof Grid> = (props: GridProps)
275
271
  setExternalSelectedItems={setExternalSelectedItems}
276
272
  columnDefs={columnDefs}
277
273
  rowData={rowData}
274
+ domLayout={"autoHeight"}
278
275
  />
279
276
  );
280
277
  };
@@ -20,7 +20,7 @@ export default {
20
20
  },
21
21
  decorators: [
22
22
  (Story) => (
23
- <div style={{ width: 1200, height: 400, display: "flex" }}>
23
+ <div style={{ width: 1024, height: 400 }}>
24
24
  <GridUpdatingContextProvider>
25
25
  <GridContextProvider>
26
26
  <Story />
@@ -57,14 +57,10 @@ const GridPopoutEditGenericTemplate: ComponentStory<typeof Grid> = (props: GridP
57
57
  [],
58
58
  );
59
59
 
60
- const rowData = useMemo(
61
- () =>
62
- [
63
- { id: 1000, name: "IS IS DP12345", nameType: "IS", numba: "IX", plan: "DP 12345" },
64
- { id: 1001, name: "PEG V SD523", nameType: "PEG", numba: "V", plan: "SD 523" },
65
- ] as IFormTestRow[],
66
- [],
67
- );
60
+ const [rowData, setRowData] = useState([
61
+ { id: 1000, name: "IS IS DP12345", nameType: "IS", numba: "IX", plan: "DP 12345" },
62
+ { id: 1001, name: "PEG V SD523", nameType: "PEG", numba: "V", plan: "SD 523" },
63
+ ] as IFormTestRow[]);
68
64
 
69
65
  return (
70
66
  <Grid
@@ -73,6 +69,7 @@ const GridPopoutEditGenericTemplate: ComponentStory<typeof Grid> = (props: GridP
73
69
  setExternalSelectedItems={setExternalSelectedItems}
74
70
  columnDefs={columnDefs}
75
71
  rowData={rowData}
72
+ domLayout={"autoHeight"}
76
73
  />
77
74
  );
78
75
  };
@@ -7,12 +7,14 @@ import { ComponentMeta, ComponentStory } from "@storybook/react/dist/ts3.9/clien
7
7
  import { GridUpdatingContextProvider } from "../../contexts/GridUpdatingContextProvider";
8
8
  import { GridContextProvider } from "../../contexts/GridContextProvider";
9
9
  import { Grid, GridProps } from "../../components/Grid";
10
- import { useMemo, useState } from "react";
10
+ import { useCallback, useMemo, useState } from "react";
11
11
  import { ColDefT, GridCell } from "../../components/GridCell";
12
12
  import { IFormTestRow } from "./FormTest";
13
13
  import { isFloat, wait } from "../../utils/util";
14
14
  import { GridPopoverTextArea } from "../../components/gridPopoverEdit/GridPopoverTextArea";
15
15
  import { GridPopoverTextInput } from "../../components/gridPopoverEdit/GridPopoverTextInput";
16
+ import { ActionButton } from "../../lui/ActionButton";
17
+ import { GridPopoverMenu } from "../../components/gridPopoverEdit/GridPopoverMenu";
16
18
 
17
19
  export default {
18
20
  title: "Components / Grids",
@@ -23,7 +25,7 @@ export default {
23
25
  },
24
26
  decorators: [
25
27
  (Story) => (
26
- <div style={{ width: 1200, height: 400, display: "flex" }}>
28
+ <div style={{ width: 1024, height: 400 }}>
27
29
  <GridUpdatingContextProvider>
28
30
  <GridContextProvider>
29
31
  <Story />
@@ -36,6 +38,11 @@ export default {
36
38
 
37
39
  const GridPopoutEditGenericTemplate: ComponentStory<typeof Grid> = (props: GridProps) => {
38
40
  const [externalSelectedItems, setExternalSelectedItems] = useState<any[]>([]);
41
+ const [rowData, setRowData] = useState([
42
+ { id: 1000, name: "IS IS DP12345", nameType: "IS", numba: "IX", plan: "DP 12345", distance: 10 },
43
+ { id: 1001, name: "PEG V SD523", nameType: "PEG", numba: "V", plan: "SD 523", distance: null },
44
+ ] as IFormTestRow[]);
45
+
39
46
  const columnDefs: ColDefT<IFormTestRow>[] = useMemo(
40
47
  () => [
41
48
  GridCell({
@@ -123,27 +130,59 @@ const GridPopoutEditGenericTemplate: ComponentStory<typeof Grid> = (props: GridP
123
130
  },
124
131
  },
125
132
  ),
133
+ GridPopoverMenu(
134
+ {
135
+ headerName: "Delete menu",
136
+ },
137
+ {
138
+ multiEdit: true,
139
+ editorParams: {
140
+ options: async (_) => [
141
+ {
142
+ label: "Delete",
143
+ action: async (selectedRows) => {
144
+ await wait(1500);
145
+ setRowData(rowData.filter((data) => !selectedRows.some((row) => row.id == data.id)));
146
+ return true;
147
+ },
148
+ supportsMultiEdit: true,
149
+ },
150
+ ],
151
+ },
152
+ },
153
+ ),
126
154
  ],
127
- [],
155
+ [rowData],
128
156
  );
129
157
 
130
- const rowData = useMemo(
131
- () =>
132
- [
133
- { id: 1000, name: "IS IS DP12345", nameType: "IS", numba: "IX", plan: "DP 12345", distance: 10 },
134
- { id: 1001, name: "PEG V SD523", nameType: "PEG", numba: "V", plan: "SD 523", distance: null },
135
- ] as IFormTestRow[],
136
- [],
137
- );
158
+ const [inProgress, setInProgress] = useState(false);
159
+
160
+ const addRowAction = useCallback(async () => {
161
+ setInProgress(true);
162
+ await wait(1000);
163
+ const lastRow = rowData[rowData.length - 1];
164
+ setRowData([...rowData, { id: lastRow.id + 1, name: "?", nameType: "?", numba: "?", plan: "", distance: null }]);
165
+ setInProgress(false);
166
+ }, [rowData]);
138
167
 
139
168
  return (
140
- <Grid
141
- {...props}
142
- externalSelectedItems={externalSelectedItems}
143
- setExternalSelectedItems={setExternalSelectedItems}
144
- columnDefs={columnDefs}
145
- rowData={rowData}
146
- />
169
+ <>
170
+ <Grid
171
+ {...props}
172
+ externalSelectedItems={externalSelectedItems}
173
+ setExternalSelectedItems={setExternalSelectedItems}
174
+ columnDefs={columnDefs}
175
+ rowData={rowData}
176
+ domLayout={"autoHeight"}
177
+ />
178
+ <ActionButton
179
+ icon={"ic_add"}
180
+ name={"Add new row"}
181
+ inProgressName={"Adding..."}
182
+ inProgress={inProgress}
183
+ onClick={addRowAction}
184
+ />
185
+ </>
147
186
  );
148
187
  };
149
188