@linzjs/step-ag-grid 8.4.2 → 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 (51) 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/lui/TextAreaInput.d.ts +1 -0
  5. package/dist/src/lui/TextInputFormatted.d.ts +1 -0
  6. package/dist/src/react-menu3/components/FocusableItem.d.ts +1 -1
  7. package/dist/src/react-menu3/components/MenuItem.d.ts +3 -1
  8. package/dist/src/react-menu3/components/SubMenu.d.ts +1 -1
  9. package/dist/src/react-menu3/utils/withHovering.d.ts +2 -2
  10. package/dist/src/utils/textMatcher.d.ts +1 -1
  11. package/dist/src/utils/textMatcher.test.d.ts +1 -0
  12. package/dist/step-ag-grid.esm.js +502 -735
  13. package/dist/step-ag-grid.esm.js.map +1 -1
  14. package/package.json +42 -42
  15. package/src/components/Grid.tsx +29 -27
  16. package/src/components/gridForm/GridFormDropDown.tsx +12 -14
  17. package/src/components/gridForm/GridFormMultiSelect.tsx +31 -29
  18. package/src/components/gridForm/GridFormPopoverMenu.tsx +18 -22
  19. package/src/components/gridForm/GridFormSubComponentTextArea.tsx +1 -1
  20. package/src/components/gridForm/GridFormSubComponentTextInput.tsx +1 -1
  21. package/src/components/gridForm/GridFormTextArea.tsx +1 -1
  22. package/src/components/gridForm/GridFormTextInput.tsx +1 -1
  23. package/src/lui/TextAreaInput.tsx +3 -1
  24. package/src/lui/TextInputFormatted.tsx +3 -1
  25. package/src/react-menu3/components/ControlledMenu.tsx +19 -6
  26. package/src/react-menu3/components/MenuItem.tsx +1 -1
  27. package/src/react-menu3/utils/withHovering.tsx +3 -3
  28. package/src/stories/grid/GridKeyboardInteractions.stories.tsx +261 -0
  29. package/src/stories/grid/{GridPopoutBearing.stories.tsx → GridPopoverEditBearing.stories.tsx} +4 -4
  30. package/src/stories/grid/{GridPopoutEditDropDown.stories.tsx → GridPopoverEditDropDown.stories.tsx} +10 -2
  31. package/src/stories/grid/{GridPopoutEditMultiSelect.stories.tsx → GridPopoverEditMultiSelect.stories.tsx} +0 -0
  32. package/src/stories/grid/gridFormInteraction/GridFormDropDownInteraction.stories.tsx +137 -0
  33. package/src/stories/grid/gridFormInteraction/GridFormEditBearingCorrectionInteraction.stories.tsx +92 -0
  34. package/src/stories/grid/gridFormInteraction/GridFormEditBearingInteraction.stories.tsx +91 -0
  35. package/src/stories/grid/gridFormInteraction/GridFormMultiSelectInteraction.stories.tsx +153 -0
  36. package/src/stories/grid/gridFormInteraction/GridFormPopoverMenuInteraction.stories.tsx +171 -0
  37. package/src/stories/grid/gridFormInteraction/GridFormTextAreaInteraction.stories.tsx +78 -0
  38. package/src/stories/grid/gridFormInteraction/GridFormTextInputInteraction.stories.tsx +85 -0
  39. package/src/stories/grid/{gridForm → gridFormStatic}/GridFormDropDown.stories.tsx +1 -1
  40. package/src/stories/grid/{gridForm → gridFormStatic}/GridFormEditBearing.stories.tsx +1 -1
  41. package/src/stories/grid/{gridForm → gridFormStatic}/GridFormEditBearingCorrection.stories.tsx +1 -1
  42. package/src/stories/grid/{gridForm → gridFormStatic}/GridFormMessage.stories.tsx +1 -1
  43. package/src/stories/grid/{gridForm → gridFormStatic}/GridFormMultiSelect.stories.tsx +1 -1
  44. package/src/stories/grid/{gridForm → gridFormStatic}/GridFormPopoverMenu.stories.tsx +1 -1
  45. package/src/stories/grid/{gridForm → gridFormStatic}/GridFormTextArea.stories.tsx +1 -1
  46. package/src/stories/grid/{gridForm → gridFormStatic}/GridFormTextInput.stories.tsx +1 -1
  47. package/src/stories/react-menu/ReactMenu.stories.tsx +66 -3
  48. package/src/styles/Grid.scss +3 -0
  49. package/src/utils/testUtil.ts +1 -0
  50. package/src/utils/textMatcher.test.ts +38 -0
  51. package/src/utils/textMatcher.ts +9 -2
@@ -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({});
@@ -14,6 +14,7 @@ import { ColDefT, GridCell } from "../../components/GridCell";
14
14
  import { GridPopoverEditDropDown } from "../../components/gridPopoverEdit/GridPopoverEditDropDown";
15
15
  import { GridFormSubComponentTextInput } from "../../components/gridForm/GridFormSubComponentTextInput";
16
16
  import { GridPopoverMenu } from "../../components/gridPopoverEdit/GridPopoverMenu";
17
+ import { GridFormSubComponentTextArea } from "../../components/gridForm/GridFormSubComponentTextArea";
17
18
 
18
19
  export default {
19
20
  title: "Components / Grids",
@@ -234,9 +235,16 @@ const GridEditDropDownTemplate: ComponentStory<typeof Grid> = (props: GridProps)
234
235
  },
235
236
  {
236
237
  value: "oth",
237
- label: "Other",
238
+ label: "Other text input",
238
239
  subComponent: () => (
239
- <GridFormSubComponentTextInput placeholder={"Other..."} defaultValue={""} required={true} />
240
+ <GridFormSubComponentTextInput placeholder={"Other..."} defaultValue={"a"} required={true} />
241
+ ),
242
+ },
243
+ {
244
+ value: "oth",
245
+ label: "Other text area",
246
+ subComponent: () => (
247
+ <GridFormSubComponentTextArea placeholder={"Other..."} defaultValue={"b"} required={true} />
240
248
  ),
241
249
  },
242
250
  ];
@@ -0,0 +1,137 @@
1
+ import "@linzjs/lui/dist/scss/base.scss";
2
+ import "@linzjs/lui/dist/fonts";
3
+
4
+ import { useRef } from "react";
5
+ import { ComponentMeta, ComponentStory } from "@storybook/react/dist/ts3.9/client/preview/types-6-3";
6
+ import {
7
+ GridFormDropDown,
8
+ GridFormDropDownProps,
9
+ GridPopoutEditDropDownSelectedItem,
10
+ } from "../../../components/gridForm/GridFormDropDown";
11
+ import { GridContextProvider } from "../../../contexts/GridContextProvider";
12
+ import { GridPopoverContext, GridPopoverContextType } from "contexts/GridPopoverContext";
13
+ import { GridFormSubComponentTextInput } from "../../../components/gridForm/GridFormSubComponentTextInput";
14
+ import { userEvent, within } from "@storybook/testing-library";
15
+ import { expect, jest } from "@storybook/jest";
16
+
17
+ export default {
18
+ title: "GridForm / Interactions",
19
+ component: GridFormDropDown,
20
+ args: {},
21
+ } as ComponentMeta<typeof GridFormDropDown>;
22
+
23
+ const updateValue = jest
24
+ .fn<void, [saveFn: (selectedRows: any[]) => Promise<boolean>, _tabDirection: 1 | 0 | -1]>()
25
+ .mockImplementation((saveFn: (selectedRows: any[]) => Promise<boolean>, _tabDirection: 1 | 0 | -1) => saveFn([]));
26
+
27
+ const onSelectedItem = jest
28
+ .fn<Promise<void>, [GridPopoutEditDropDownSelectedItem<any>]>()
29
+ .mockImplementation(async () => undefined);
30
+
31
+ const Template: ComponentStory<typeof GridFormDropDown> = (props) => {
32
+ const config: GridFormDropDownProps<any> = {
33
+ filtered: "local",
34
+ onSelectedItem,
35
+ options: [
36
+ { label: "Enabled", value: 1 },
37
+ { label: "Disabled", value: 0, disabled: true },
38
+ {
39
+ label: "Sub menu",
40
+ value: 0,
41
+ subComponent: () => (
42
+ <GridFormSubComponentTextInput placeholder={"Text input"} maxLength={5} required defaultValue={""} />
43
+ ),
44
+ },
45
+ ],
46
+ };
47
+ const anchorRef = useRef<HTMLHeadingElement>(null);
48
+
49
+ return (
50
+ <div className={"react-menu-inline-test"}>
51
+ <GridContextProvider>
52
+ <div>
53
+ <h6 ref={anchorRef}>Interaction test</h6>
54
+ <GridPopoverContext.Provider
55
+ value={
56
+ {
57
+ anchorRef: anchorRef,
58
+ updateValue,
59
+ data: { value: "" },
60
+ value: "",
61
+ field: "value",
62
+ selectedRows: [],
63
+ } as any as GridPopoverContextType<any>
64
+ }
65
+ >
66
+ <GridFormDropDown {...props} {...config} />
67
+ </GridPopoverContext.Provider>
68
+ </div>
69
+ </GridContextProvider>
70
+ </div>
71
+ );
72
+ };
73
+
74
+ export const GridFormDropDownInteractions_ = Template.bind({});
75
+ GridFormDropDownInteractions_.play = async ({ canvasElement }) => {
76
+ const canvas = within(canvasElement);
77
+
78
+ const getOption = (name: string) => canvas.findByRole("menuitem", { name });
79
+
80
+ // Check enabled menu handles click
81
+ const enabledMenuOption = await getOption("Enabled");
82
+ expect(enabledMenuOption).toBeInTheDocument();
83
+
84
+ userEvent.click(enabledMenuOption);
85
+ expect(updateValue).toHaveBeenCalled();
86
+ expect(onSelectedItem).toHaveBeenCalled();
87
+
88
+ // Check disabled menu ignores click
89
+ updateValue.mockClear();
90
+ onSelectedItem.mockClear();
91
+ const disabledMenuOption = await getOption("Disabled");
92
+ userEvent.click(disabledMenuOption);
93
+ expect(updateValue).not.toHaveBeenCalled();
94
+ expect(onSelectedItem).not.toHaveBeenCalled();
95
+
96
+ // Check sub menu works
97
+ const subTextInput = await getOption("Sub menu...");
98
+ expect(subTextInput).toBeInTheDocument();
99
+
100
+ expect(canvas.queryByPlaceholderText("Text input")).not.toBeInTheDocument();
101
+
102
+ userEvent.click(subTextInput);
103
+ const textInput = await canvas.findByPlaceholderText("Text input");
104
+ expect(textInput).toBeInTheDocument();
105
+ expect(await canvas.findByText("Must not be empty")).toBeInTheDocument();
106
+
107
+ userEvent.type(textInput, "Hello");
108
+ expect(await canvas.findByText("Press enter or tab to save")).toBeInTheDocument();
109
+
110
+ // Test tab to save
111
+ updateValue.mockClear();
112
+ userEvent.tab();
113
+ expect(updateValue).toHaveBeenCalledWith(expect.anything(), 1); // 1 = Tab
114
+
115
+ // Test shift+tab to save
116
+ updateValue.mockClear();
117
+ userEvent.tab({ shift: true });
118
+ expect(updateValue).toHaveBeenCalledWith(expect.anything(), -1); // -1 = Shift + tab
119
+
120
+ // Test escape to not save
121
+ updateValue.mockClear();
122
+ userEvent.type(textInput, "{Escape}");
123
+ expect(updateValue).not.toHaveBeenCalled();
124
+
125
+ // Test invalid value doesn't save
126
+ updateValue.mockClear();
127
+ userEvent.clear(textInput);
128
+ userEvent.type(textInput, "{Enter}");
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();
137
+ };
@@ -0,0 +1,92 @@
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 { GridFormEditBearing } from "../../../components/gridForm/GridFormEditBearing";
6
+ import { GridContextProvider } from "../../../contexts/GridContextProvider";
7
+ import { GridPopoverContext, GridPopoverContextType } from "contexts/GridPopoverContext";
8
+ import { useRef } from "react";
9
+ import { GridPopoverEditBearingCorrectionEditorParams } from "../../../components/gridPopoverEdit/GridPopoverEditBearing";
10
+ import { userEvent, within } from "@storybook/testing-library";
11
+ import { expect, jest } from "@storybook/jest";
12
+
13
+ export default {
14
+ title: "GridForm / Interactions",
15
+ component: GridFormEditBearing,
16
+ args: {},
17
+ } as ComponentMeta<typeof GridFormEditBearing>;
18
+
19
+ const updateValue = jest.fn();
20
+
21
+ const Template: ComponentStory<typeof GridFormEditBearing> = (props) => {
22
+ const anchorRef = useRef<HTMLHeadingElement>(null);
23
+
24
+ return (
25
+ <div className={"react-menu-inline-test"}>
26
+ <GridContextProvider>
27
+ <h6 ref={anchorRef}>Interaction Test</h6>
28
+ <GridPopoverContext.Provider
29
+ value={
30
+ {
31
+ anchorRef,
32
+ value: null,
33
+ updateValue,
34
+ } as any as GridPopoverContextType<any>
35
+ }
36
+ >
37
+ <GridFormEditBearing {...props} {...GridPopoverEditBearingCorrectionEditorParams} />
38
+ </GridPopoverContext.Provider>
39
+ </GridContextProvider>
40
+ </div>
41
+ );
42
+ };
43
+
44
+ export const GridFormEditBearingCorrectionInteractions_ = Template.bind({});
45
+ GridFormEditBearingCorrectionInteractions_.play = async ({ canvasElement }) => {
46
+ const canvas = within(canvasElement);
47
+
48
+ expect(await canvas.findByText("Press enter or tab to save")).toBeInTheDocument();
49
+
50
+ const inputField = canvas.getByPlaceholderText("Enter bearing correction");
51
+
52
+ // Test formatting null
53
+ expect(await canvas.findByText("–")).toBeInTheDocument();
54
+
55
+ // Test formatting a bearing
56
+ expect(inputField).toBeInTheDocument();
57
+ userEvent.type(inputField, "1.2345");
58
+ expect(await canvas.findByText("+1° 23' 45.0\"")).toBeInTheDocument();
59
+
60
+ // Test enter to save
61
+ updateValue.mockClear();
62
+ userEvent.type(inputField, "{Enter}");
63
+ expect(updateValue).toHaveBeenCalledWith(expect.anything(), 0); // 0 = Enter
64
+
65
+ // Test tab to save
66
+ updateValue.mockClear();
67
+ userEvent.tab();
68
+ expect(updateValue).toHaveBeenCalledWith(expect.anything(), 1); // 1 = Tab
69
+
70
+ // Test shift+tab to save
71
+ updateValue.mockClear();
72
+ userEvent.tab({ shift: true });
73
+ expect(updateValue).toHaveBeenCalledWith(expect.anything(), -1); // -1 = Shift + tab
74
+
75
+ // Test escape not to save
76
+ updateValue.mockClear();
77
+ userEvent.type(inputField, "{Escape}");
78
+ expect(updateValue).not.toHaveBeenCalled();
79
+
80
+ // Test invalid value doesn't save
81
+ updateValue.mockClear();
82
+ userEvent.type(inputField, "xxx");
83
+ expect(await canvas.findByText("?")).toBeInTheDocument();
84
+
85
+ expect(canvas.getByText("Bearing must be a number in D.MMSSS format")).toBeInTheDocument();
86
+ userEvent.type(inputField, "{Enter}");
87
+ expect(updateValue).not.toHaveBeenCalled();
88
+ userEvent.tab();
89
+ expect(updateValue).not.toHaveBeenCalled();
90
+ userEvent.tab({ shift: true });
91
+ expect(updateValue).not.toHaveBeenCalled();
92
+ };
@@ -0,0 +1,91 @@
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 { GridFormEditBearing } from "../../../components/gridForm/GridFormEditBearing";
6
+ import { GridContextProvider } from "../../../contexts/GridContextProvider";
7
+ import { GridPopoverContext, GridPopoverContextType } from "contexts/GridPopoverContext";
8
+ import { useRef } from "react";
9
+ import { GridPopoverEditBearingEditorParams } from "../../../components/gridPopoverEdit/GridPopoverEditBearing";
10
+ import { userEvent, within } from "@storybook/testing-library";
11
+ import { expect, jest } from "@storybook/jest";
12
+
13
+ export default {
14
+ title: "GridForm / Interactions",
15
+ component: GridFormEditBearing,
16
+ args: {},
17
+ } as ComponentMeta<typeof GridFormEditBearing>;
18
+
19
+ const updateValue = jest.fn();
20
+
21
+ const Template: ComponentStory<typeof GridFormEditBearing> = (props) => {
22
+ const anchorRef = useRef<HTMLHeadingElement>(null);
23
+
24
+ return (
25
+ <div className={"react-menu-inline-test"}>
26
+ <GridContextProvider>
27
+ <h6 ref={anchorRef}>Interaction Test</h6>
28
+ <GridPopoverContext.Provider
29
+ value={
30
+ {
31
+ anchorRef,
32
+ value: null,
33
+ updateValue,
34
+ } as any as GridPopoverContextType<any>
35
+ }
36
+ >
37
+ <GridFormEditBearing {...props} {...GridPopoverEditBearingEditorParams} />
38
+ </GridPopoverContext.Provider>
39
+ </GridContextProvider>
40
+ </div>
41
+ );
42
+ };
43
+
44
+ export const GridFormEditBearingInteractions_ = Template.bind({});
45
+ GridFormEditBearingInteractions_.play = async ({ canvasElement }) => {
46
+ const canvas = within(canvasElement);
47
+
48
+ expect(await canvas.findByText("Press enter or tab to save")).toBeInTheDocument();
49
+
50
+ const inputField = canvas.getByPlaceholderText("Enter bearing");
51
+
52
+ // Test formatting null
53
+ expect(await canvas.findByText("–")).toBeInTheDocument();
54
+
55
+ // Test formatting a bearing
56
+ expect(inputField).toBeInTheDocument();
57
+ userEvent.type(inputField, "1.2345");
58
+ expect(await canvas.findByText("1° 23' 45\"")).toBeInTheDocument();
59
+
60
+ // Test enter to save
61
+ updateValue.mockClear();
62
+ userEvent.type(inputField, "{Enter}");
63
+ expect(updateValue).toHaveBeenCalledWith(expect.anything(), 0); // 0 = Enter
64
+
65
+ // Test tab to save
66
+ updateValue.mockClear();
67
+ userEvent.tab();
68
+ expect(updateValue).toHaveBeenCalledWith(expect.anything(), 1); // 1 = Tab
69
+
70
+ // Test shift+tab to save
71
+ updateValue.mockClear();
72
+ userEvent.tab({ shift: true });
73
+ expect(updateValue).toHaveBeenCalledWith(expect.anything(), -1); // -1 = Shift + tab
74
+
75
+ // Test escape not to save
76
+ updateValue.mockClear();
77
+ userEvent.type(inputField, "{Escape}");
78
+ expect(updateValue).not.toHaveBeenCalled();
79
+
80
+ // Test invalid value doesn't save
81
+ updateValue.mockClear();
82
+ userEvent.type(inputField, "xxx");
83
+ expect(await canvas.findByText("?")).toBeInTheDocument();
84
+ expect(canvas.getByText("Bearing must be a number in D.MMSSS format")).toBeInTheDocument();
85
+ userEvent.type(inputField, "{Enter}");
86
+ expect(updateValue).not.toHaveBeenCalled();
87
+ userEvent.tab();
88
+ expect(updateValue).not.toHaveBeenCalled();
89
+ userEvent.tab({ shift: true });
90
+ expect(updateValue).not.toHaveBeenCalled();
91
+ };