@pautena/react-design-system 0.2.1 → 0.3.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 (35) hide show
  1. package/dist/cjs/index.js +13 -4
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/components/value-displays/index.d.ts +1 -0
  4. package/dist/cjs/types/components/value-displays/value-datetime/index.d.ts +1 -0
  5. package/dist/cjs/types/components/value-displays/value-datetime/value-datetime.d.ts +18 -0
  6. package/dist/cjs/types/generators/generators.mock.d.ts +9 -5
  7. package/dist/cjs/types/generators/generators.model.d.ts +25 -1
  8. package/dist/esm/index.js +13 -4
  9. package/dist/esm/index.js.map +1 -1
  10. package/dist/esm/types/components/value-displays/index.d.ts +1 -0
  11. package/dist/esm/types/components/value-displays/value-datetime/index.d.ts +1 -0
  12. package/dist/esm/types/components/value-displays/value-datetime/value-datetime.d.ts +18 -0
  13. package/dist/esm/types/generators/generators.mock.d.ts +9 -5
  14. package/dist/esm/types/generators/generators.model.d.ts +25 -1
  15. package/dist/index.d.ts +45 -2
  16. package/package.json +6 -2
  17. package/src/components/value-displays/index.ts +1 -0
  18. package/src/components/value-displays/value-datetime/index.ts +1 -0
  19. package/src/components/value-displays/value-datetime/value-datetime.stories.tsx +21 -0
  20. package/src/components/value-displays/value-datetime/value-datetime.test.tsx +23 -0
  21. package/src/components/value-displays/value-datetime/value-datetime.tsx +40 -0
  22. package/src/components/value-displays/value-text/{value-test.test.tsx → value-text.test.tsx} +0 -0
  23. package/src/generators/generators.mock.ts +56 -17
  24. package/src/generators/generators.model.ts +39 -1
  25. package/src/generators/model-form/model-form.stories.tsx +2 -2
  26. package/src/generators/model-form/model-form.test.tsx +39 -22
  27. package/src/generators/model-form/model-form.tsx +220 -33
  28. package/src/generators/model-router/model-router.test.tsx +46 -52
  29. package/src/generators/model-router/stories/model-router.stories.tsx +6 -2
  30. package/src/generators/object-details/object-details.tsx +5 -4
  31. package/src/storybook.tsx +10 -0
  32. package/src/tests/actions.ts +43 -0
  33. package/src/tests/assertions.ts +70 -1
  34. package/src/tests/index.ts +1 -0
  35. package/src/tests/testing-library.tsx +5 -1
@@ -1,8 +1,69 @@
1
- import { Box, Button, Grid, Paper, TextField, Typography } from "@mui/material";
2
- import React, { ChangeEvent, FormEvent } from "react";
1
+ import {
2
+ Box,
3
+ Button,
4
+ Checkbox,
5
+ FormControl,
6
+ FormControlLabel,
7
+ Grid,
8
+ InputLabel,
9
+ ListItemText,
10
+ MenuItem,
11
+ Paper,
12
+ Select,
13
+ SelectChangeEvent,
14
+ TextField,
15
+ Typography,
16
+ } from "@mui/material";
17
+ import { DesktopDatePicker, TimePicker, DateTimePicker } from "@mui/x-date-pickers";
18
+ import React, { ChangeEvent, FormEvent, ReactElement, useMemo } from "react";
3
19
  import { useState } from "react";
4
20
  import { useGetDefaultThemeColor } from "../../utils/theme";
5
- import { Model, ModelField, BasicModelInstance } from "../generators.model";
21
+ import { Model, ModelField, BasicModelInstance, ModelFieldTypes } from "../generators.model";
22
+
23
+ const InitialStateZeroValue: Record<string, any> = {
24
+ string: undefined,
25
+ number: undefined,
26
+ boolean: false,
27
+ enum: "",
28
+ multienum: [],
29
+ date: "01/01/1970",
30
+ group: {},
31
+ };
32
+
33
+ const getFieldInitialState = <T extends BasicModelInstance>(
34
+ field: ModelField,
35
+ initialValues: T | undefined,
36
+ ) => {
37
+ return initialValues ? initialValues[field.id] : InitialStateZeroValue[field.type];
38
+ };
39
+
40
+ const getValuesInitialState = <T extends BasicModelInstance>(
41
+ model: Model,
42
+ initialValues: T | undefined,
43
+ ): T => {
44
+ const obj = {} as any;
45
+
46
+ model.fields.forEach((field) => {
47
+ let value: any;
48
+ if (field.type === "group") {
49
+ value = {};
50
+ field.value.forEach((groupField) => {
51
+ value[groupField.id] = getFieldInitialState(
52
+ groupField,
53
+ initialValues && initialValues[field.id],
54
+ );
55
+ });
56
+ } else if (field.type === "date" || field.type === "time") {
57
+ value = (initialValues && initialValues[field.id]) || field.default;
58
+ } else {
59
+ value = getFieldInitialState(field, initialValues);
60
+ }
61
+
62
+ obj[field.id] = value;
63
+ });
64
+
65
+ return obj;
66
+ };
6
67
 
7
68
  export interface ModelFormProps<T extends BasicModelInstance> {
8
69
  model: Model;
@@ -17,28 +78,63 @@ export const ModelForm = <T extends BasicModelInstance>({
17
78
  onSubmit,
18
79
  initialValues,
19
80
  }: ModelFormProps<T>) => {
20
- const [values, setValues] = useState<T>(initialValues || ({} as T));
21
-
22
- const handleInputChange = (e: ChangeEvent<any>, key: string | undefined) => {
23
- e.preventDefault();
24
-
25
- e.target;
81
+ const valuesInitialState = useMemo(
82
+ () => getValuesInitialState<T>(model, initialValues),
83
+ [model, initialValues],
84
+ );
85
+ const [values, setValues] = useState<T>(valuesInitialState);
26
86
 
87
+ const setKeyValue = (name: string, key: string | undefined, value: any) => {
27
88
  setValues((v) => {
28
89
  const n: Record<string, object> = {};
29
90
  if (key) {
30
91
  n[key] = {
31
92
  ...v[key],
32
- [e.target.name]: e.target.value,
93
+ [name]: value,
33
94
  };
34
95
  } else {
35
- n[e.target.name] = e.target.value;
96
+ n[name] = value;
36
97
  }
37
98
 
38
99
  return { ...v, ...n };
39
100
  });
40
101
  };
41
102
 
103
+ const handleCheckboxChange = (e: ChangeEvent<any>, key: string | undefined) => {
104
+ e.preventDefault();
105
+ setKeyValue(e.target.name, key, e.target.checked);
106
+ };
107
+
108
+ const handleSelectChange = (e: SelectChangeEvent<any>, key: string | undefined) => {
109
+ e.preventDefault();
110
+ setKeyValue(e.target.name, key, e.target.value);
111
+ };
112
+
113
+ const handleMultiSelectChange = (e: SelectChangeEvent<any>, key: string | undefined) => {
114
+ e.preventDefault();
115
+ const { value } = e.target;
116
+ const newValue = typeof value === "string" ? value.split(",") : value;
117
+ setKeyValue(e.target.name, key, newValue);
118
+ };
119
+
120
+ const handleInputChange = (
121
+ e: ChangeEvent<any>,
122
+ key: string | undefined,
123
+ type: ModelFieldTypes,
124
+ ) => {
125
+ e.preventDefault();
126
+
127
+ let value = e.target.value;
128
+ if (type === "number") {
129
+ value = parseInt(e.target.value);
130
+ }
131
+ setKeyValue(e.target.name, key, value);
132
+ };
133
+
134
+ const handleDateChange = (value: any, key: string | undefined, id: string) => {
135
+ setKeyValue(id, key, value);
136
+ };
137
+
42
138
  const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
43
139
  e.preventDefault();
44
140
  onSubmit(values);
@@ -48,28 +144,113 @@ export const ModelForm = <T extends BasicModelInstance>({
48
144
  const defaultColor = useGetDefaultThemeColor({ lightWeight: 200, darkWeight: 800 });
49
145
 
50
146
  const { id, type, name, description, xs, sm, md, lg, xl } = field;
147
+
148
+ let fieldInput: ReactElement;
149
+ const value = key ? values[key][id] : values[id];
51
150
  if (type === "group") {
52
- return (
53
- <Grid item key={id} xs={xs} sm={sm} md={md} lg={lg} xl={xl}>
54
- <Paper>
55
- <Box bgcolor={defaultColor} px={2} py={1} mb={2}>
56
- <Typography variant="h6" role="heading" aria-level={1}>
57
- {name}
58
- </Typography>
59
- <Typography variant="body2" role="heading" aria-level={2}>
60
- {description}
61
- </Typography>
62
- </Box>
63
- <Grid container spacing={2} sx={{ p: 2 }}>
64
- {field.value.map((f) => renderField(f, id))}
65
- </Grid>
66
- </Paper>
67
- </Grid>
151
+ fieldInput = (
152
+ <Paper>
153
+ <Box bgcolor={defaultColor} px={2} py={1} mb={2}>
154
+ <Typography variant="h6" role="heading" aria-level={1}>
155
+ {name}
156
+ </Typography>
157
+ <Typography variant="body2" role="heading" aria-level={2}>
158
+ {description}
159
+ </Typography>
160
+ </Box>
161
+ <Grid container spacing={2} sx={{ p: 2 }}>
162
+ {field.value.map((f) => renderField(f, id))}
163
+ </Grid>
164
+ </Paper>
68
165
  );
69
- }
70
-
71
- return (
72
- <Grid item key={id} xs={xs} sm={sm} md={md} lg={lg} xl={xl}>
166
+ } else if (type === "boolean") {
167
+ fieldInput = (
168
+ <Box sx={{ height: 1, display: "flex", alignItems: "center" }}>
169
+ <FormControlLabel
170
+ control={
171
+ <Checkbox name={id} onChange={(e) => handleCheckboxChange(e, key)} checked={value} />
172
+ }
173
+ label={name}
174
+ />
175
+ </Box>
176
+ );
177
+ } else if (type === "enum") {
178
+ fieldInput = (
179
+ <FormControl fullWidth>
180
+ <InputLabel id={`${id}-select-label`}>{name}</InputLabel>
181
+ <Select
182
+ labelId={`${id}-select-label`}
183
+ id={`${id}-select`}
184
+ value={value}
185
+ label={name}
186
+ name={id}
187
+ onChange={(e) => handleSelectChange(e, key)}
188
+ required
189
+ >
190
+ {field.value.map((fieldValue) => (
191
+ <MenuItem key={fieldValue} value={fieldValue}>
192
+ {fieldValue}
193
+ </MenuItem>
194
+ ))}
195
+ </Select>
196
+ </FormControl>
197
+ );
198
+ } else if (type === "multienum") {
199
+ fieldInput = (
200
+ <FormControl fullWidth>
201
+ <InputLabel id={`${id}-select-label`}>{name}</InputLabel>
202
+ <Select
203
+ labelId={`${id}-select-label`}
204
+ id={`${id}-select`}
205
+ value={value || []}
206
+ renderValue={(selected) => selected.join(", ")}
207
+ label={name}
208
+ name={id}
209
+ onChange={(e) => handleMultiSelectChange(e, key)}
210
+ required
211
+ multiple
212
+ >
213
+ {field.value.map((fieldValue) => (
214
+ <MenuItem key={fieldValue} value={fieldValue}>
215
+ <Checkbox checked={(value || []).includes(fieldValue)} />
216
+ <ListItemText primary={fieldValue} />
217
+ </MenuItem>
218
+ ))}
219
+ </Select>
220
+ </FormControl>
221
+ );
222
+ } else if (type === "date") {
223
+ fieldInput = (
224
+ <DesktopDatePicker
225
+ label={name}
226
+ inputFormat={field.format}
227
+ value={value}
228
+ onChange={(value) => handleDateChange(value, key, id)}
229
+ renderInput={(params: any) => <TextField {...params} />}
230
+ />
231
+ );
232
+ } else if (type === "time") {
233
+ fieldInput = (
234
+ <TimePicker
235
+ label={name}
236
+ inputFormat={field.format}
237
+ value={value}
238
+ onChange={(value) => handleDateChange(value, key, id)}
239
+ renderInput={(params: any) => <TextField {...params} />}
240
+ />
241
+ );
242
+ } else if (type === "datetime") {
243
+ fieldInput = (
244
+ <DateTimePicker
245
+ label={name}
246
+ inputFormat={field.format}
247
+ value={value}
248
+ onChange={(value) => handleDateChange(value, key, id)}
249
+ renderInput={(params: any) => <TextField {...params} />}
250
+ />
251
+ );
252
+ } else {
253
+ fieldInput = (
73
254
  <TextField
74
255
  required
75
256
  type={type}
@@ -77,9 +258,15 @@ export const ModelForm = <T extends BasicModelInstance>({
77
258
  name={id}
78
259
  variant="outlined"
79
260
  fullWidth
80
- value={key && key in values ? values[key][id] : values[id]}
81
- onChange={(e) => handleInputChange(e, key)}
261
+ value={value}
262
+ onChange={(e) => handleInputChange(e, key, type)}
82
263
  />
264
+ );
265
+ }
266
+
267
+ return (
268
+ <Grid item key={id} xs={xs} sm={sm} md={md} lg={lg} xl={xl}>
269
+ {fieldInput}
83
270
  </Grid>
84
271
  );
85
272
  };
@@ -9,16 +9,31 @@ import {
9
9
  TestRouter,
10
10
  expectModelFieldValue,
11
11
  expectModelFieldInputValue,
12
+ selectOption,
12
13
  } from "~/tests";
13
14
  import { data as mockData } from "./stories/templates";
14
15
  import userEvent from "@testing-library/user-event";
15
16
  import { getRandomItem } from "../../utils";
16
17
  import { Model } from "../generators.model";
17
- import { createModelInstance, MockInstance, mockModel } from "../generators.mock";
18
+ import {
19
+ BirthDateFormat,
20
+ createModelInstance,
21
+ MockInstance,
22
+ mockModel,
23
+ ReturnTimeFormat,
24
+ TradeDateFormat,
25
+ } from "../generators.mock";
18
26
  import { NotificationCenterProvider } from "../../providers";
19
27
  import { Box } from "@mui/system";
20
28
  import { Button } from "@mui/material";
21
- import { Route, Routes, useNavigate } from "react-router-dom";
29
+ import { useNavigate } from "react-router-dom";
30
+ import {
31
+ clearCheckbox,
32
+ clearMultiSelect,
33
+ expectToHaveBeenCalledOnceWithMockInstance,
34
+ pickDatetime,
35
+ selectOptions,
36
+ } from "../../tests";
22
37
 
23
38
  const REQUEST_TIMEOUT = 20;
24
39
 
@@ -82,30 +97,6 @@ describe("ModelRouter", () => {
82
97
  expectNotToHaveMenuOption: ({ id }: { id: string }) => {
83
98
  expect(screen.queryByTestId(`options-${id}`)).not.toBeInTheDocument();
84
99
  },
85
- expectSubmitInstanceCall: (mockFn: jest.Mock, instance: MockInstance) => {
86
- expect(mockFn).toHaveBeenCalledTimes(1);
87
- expect(mockFn).toHaveBeenCalledWith({
88
- id: instance.id,
89
- firstName: instance.firstName,
90
- middleName: instance.middleName,
91
- lastName: instance.lastName,
92
- gender: instance.gender,
93
- age: instance.age.toString(),
94
- birthDate: instance.birthDate,
95
- car: {
96
- model: instance.car.model,
97
- manufacturer: instance.car.manufacturer,
98
- color: instance.car.color,
99
- type: instance.car.type,
100
- vin: instance.car.vin,
101
- vrm: instance.car.vrm,
102
- },
103
- quantity: instance.quantity.toString(),
104
- available: instance.available.toString(),
105
- currency: instance.currency,
106
- tradeDate: instance.tradeDate,
107
- });
108
- },
109
100
  };
110
101
 
111
102
  const actions = {
@@ -149,17 +140,18 @@ describe("ModelRouter", () => {
149
140
  const firstNameElement = screen.getByRole("textbox", { name: /first name/i });
150
141
  const middleNameElement = screen.getByRole("textbox", { name: /middle name/i });
151
142
  const lastNameElement = screen.getByRole("textbox", { name: /last name/i });
152
- const genderElement = screen.getByRole("textbox", { name: /gender/i });
143
+ const genderElement = screen.getByRole("button", { name: /gender/i });
153
144
  const ageElement = screen.getByRole("spinbutton", { name: /age/i });
154
145
  const birthDateElement = screen.getByRole("textbox", { name: /birth date/i });
155
- const manufacturerElement = screen.getByRole("textbox", { name: /manufacturer/i });
156
- const modelElement = screen.getByRole("textbox", { name: /model/i });
146
+ const manufacturerElement = screen.getByRole("button", { name: /manufacturer/i });
147
+ const modelElement = screen.getByRole("button", { name: /model/i });
157
148
  const colorElement = screen.getByRole("textbox", { name: /color/i });
158
- const typeElement = screen.getByRole("textbox", { name: /type/i });
149
+ const typeElement = screen.getByRole("button", { name: /type/i });
159
150
  const vinElement = screen.getByRole("textbox", { name: /vin/i });
160
151
  const vrmElement = screen.getByRole("textbox", { name: /vrm/i });
152
+ const timeReturnElement = screen.getByRole("textbox", { name: /return time/i });
161
153
  const quantityElement = screen.getByRole("spinbutton", { name: /q/i });
162
- const availableElement = screen.getByRole("textbox", { name: /available/i });
154
+ const availableElement = screen.getByRole("checkbox", { name: /available/i });
163
155
  const currencyElement = screen.getByRole("textbox", { name: /currency/i });
164
156
  const tradeDateElement = screen.getByRole("textbox", { name: /trade date/i });
165
157
 
@@ -168,37 +160,39 @@ describe("ModelRouter", () => {
168
160
  await userEvent.clear(firstNameElement);
169
161
  await userEvent.clear(middleNameElement);
170
162
  await userEvent.clear(lastNameElement);
171
- await userEvent.clear(genderElement);
172
163
  await userEvent.clear(ageElement);
173
164
  await userEvent.clear(birthDateElement);
174
- await userEvent.clear(manufacturerElement);
175
- await userEvent.clear(modelElement);
176
165
  await userEvent.clear(colorElement);
177
- await userEvent.clear(typeElement);
178
166
  await userEvent.clear(vinElement);
179
167
  await userEvent.clear(vrmElement);
180
168
  await userEvent.clear(quantityElement);
181
169
  await userEvent.clear(availableElement);
182
170
  await userEvent.clear(currencyElement);
183
171
  await userEvent.clear(tradeDateElement);
172
+ await userEvent.clear(timeReturnElement);
173
+ await clearCheckbox(availableElement);
174
+ await clearMultiSelect(typeElement);
184
175
  }
185
176
  await userEvent.type(idElement, instance.id);
186
177
  await userEvent.type(firstNameElement, instance.firstName);
187
178
  await userEvent.type(middleNameElement, instance.middleName);
188
179
  await userEvent.type(lastNameElement, instance.lastName);
189
- await userEvent.type(genderElement, instance.gender);
180
+ await selectOption(genderElement, instance.gender);
190
181
  await userEvent.type(ageElement, instance.age.toString());
191
- await userEvent.type(birthDateElement, instance.birthDate);
192
- await userEvent.type(modelElement, instance.car.model);
193
- await userEvent.type(manufacturerElement, instance.car.manufacturer);
182
+ pickDatetime(birthDateElement, instance.birthDate, BirthDateFormat);
183
+ await selectOption(modelElement, instance.car.model);
184
+ await selectOption(manufacturerElement, instance.car.manufacturer);
194
185
  await userEvent.type(colorElement, instance.car.color);
195
- await userEvent.type(typeElement, instance.car.type);
186
+ await selectOptions(typeElement, instance.car.type);
196
187
  await userEvent.type(vinElement, instance.car.vin);
197
188
  await userEvent.type(vrmElement, instance.car.vrm);
189
+ pickDatetime(timeReturnElement, instance.car.returnTime, ReturnTimeFormat);
198
190
  await userEvent.type(quantityElement, instance.quantity.toString());
199
- await userEvent.type(availableElement, instance.available.toString());
191
+ if (instance.available) {
192
+ await userEvent.click(availableElement);
193
+ }
200
194
  await userEvent.type(currencyElement, instance.currency);
201
- await userEvent.type(tradeDateElement, instance.tradeDate);
195
+ pickDatetime(tradeDateElement, instance.tradeDate, TradeDateFormat);
202
196
 
203
197
  submit && (await userEvent.click(screen.getByRole("button", { name: /save/i })));
204
198
 
@@ -544,7 +538,7 @@ describe("ModelRouter", () => {
544
538
 
545
539
  const newInstance = await actions.fullfillModelForm({ model, submit: true });
546
540
 
547
- assertions.expectSubmitInstanceCall(onSubmitNewItem, newInstance);
541
+ expectToHaveBeenCalledOnceWithMockInstance(onSubmitNewItem, newInstance);
548
542
  });
549
543
 
550
544
  it("would show a loading indicator when the request is in progress", async () => {
@@ -707,12 +701,12 @@ describe("ModelRouter", () => {
707
701
  });
708
702
 
709
703
  it("would make a request with the new values when the form is submitted", async () => {
710
- const { model } = await renderComponent({ screen: "update" });
704
+ const { model, onSubmitUpdate } = await renderComponent({ screen: "update" });
711
705
 
712
706
  await waitForProgressIndicatorToBeRemoved();
713
- const newInstance = await actions.fullfillModelForm({ model, clear: true });
707
+ const newInstance = await actions.fullfillModelForm({ model, clear: true, submit: true });
714
708
 
715
- expectModelFieldInputValue(model.fields, newInstance);
709
+ expectToHaveBeenCalledOnceWithMockInstance(onSubmitUpdate, newInstance);
716
710
  });
717
711
 
718
712
  it("would show a loading indicator while the submit request is in progress", async () => {
@@ -724,14 +718,14 @@ describe("ModelRouter", () => {
724
718
  expectProgressIndicator();
725
719
  });
726
720
 
727
- it("would navigate to the list screen when the submit request finish", async () => {
728
- const { model, onSubmitUpdate } = await renderComponent({ screen: "update" });
721
+ // it("would navigate to the list screen when the submit request finish", async () => {
722
+ // const { model } = await renderComponent({ screen: "update" });
729
723
 
730
- await waitForProgressIndicatorToBeRemoved();
731
- const newInstance = await actions.fullfillModelForm({ model, submit: true, clear: true });
724
+ // await waitForProgressIndicatorToBeRemoved();
725
+ // await actions.fullfillModelForm({ model, submit: true, clear: true });
732
726
 
733
- assertions.expectSubmitInstanceCall(onSubmitUpdate, newInstance);
734
- });
727
+ // await assertions.expectListScreen();
728
+ // });
735
729
  });
736
730
 
737
731
  describe("delete item", () => {
@@ -1,6 +1,10 @@
1
1
  import React from "react";
2
2
  import { ComponentMeta } from "@storybook/react";
3
- import { withMemoryRouter, withNotificationCenter } from "../../../storybook";
3
+ import {
4
+ withLocalizationProvider,
5
+ withMemoryRouter,
6
+ withNotificationCenter,
7
+ } from "../../../storybook";
4
8
  import { ModelRouter } from "../model-router";
5
9
  import { IdleRequest } from "../model-router.types";
6
10
  import { MockInstance, mockModel } from "../../generators.mock";
@@ -216,7 +220,7 @@ export const InternalModelRouter = () => {
216
220
  export default {
217
221
  title: "Generators/ModelRouter",
218
222
  component: DummyModelRouter,
219
- decorators: [withMemoryRouter(), withNotificationCenter],
223
+ decorators: [withMemoryRouter(), withNotificationCenter, withLocalizationProvider],
220
224
  parameters: {
221
225
  layout: "fullscreen",
222
226
  },
@@ -6,16 +6,17 @@ import {
6
6
  ValueBoolean,
7
7
  ValueCard,
8
8
  ValueText,
9
+ ValueDatetime,
9
10
  } from "../../components";
10
11
  import { ModelField, GroupField, Model, BasicModelInstance } from "../generators.model";
11
12
 
12
- const singleDetailValueFactory = <T extends BasicModelInstance>(
13
- { id, name, type }: ModelField,
14
- instance: T,
15
- ) => {
13
+ const singleDetailValueFactory = <T extends BasicModelInstance>(field: ModelField, instance: T) => {
14
+ const { id, name, type } = field;
16
15
  const value = instance[id];
17
16
  if (type === "boolean") {
18
17
  return <ValueBoolean label={name} value={value} />;
18
+ } else if (type === "date" || type === "time" || type === "datetime") {
19
+ return <ValueDatetime label={name} value={value} format={field.format} />;
19
20
  }
20
21
  return <ValueText label={name} value={value?.toString()} />;
21
22
  };
package/src/storybook.tsx CHANGED
@@ -6,6 +6,8 @@ import { Box } from "@mui/material";
6
6
  import { MemoryRouter, Router, Navigator, Route, Routes } from "react-router-dom";
7
7
  import { NotificationCenterProvider } from "./providers";
8
8
  import { action } from "@storybook/addon-actions";
9
+ import { LocalizationProvider } from "@mui/x-date-pickers/";
10
+ import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns";
9
11
 
10
12
  export function createTemplate<P>(
11
13
  C: JSXElementConstructor<P>,
@@ -88,3 +90,11 @@ export const withPadding =
88
90
  </Box>
89
91
  );
90
92
  };
93
+
94
+ export const withLocalizationProvider = (Story: FunctionComponent) => {
95
+ return (
96
+ <LocalizationProvider dateAdapter={AdapterDateFns}>
97
+ <Story />
98
+ </LocalizationProvider>
99
+ );
100
+ };
@@ -0,0 +1,43 @@
1
+ import userEvent from "@testing-library/user-event";
2
+ import { screen, fireEvent } from "./testing-library";
3
+ import { format } from "date-fns";
4
+
5
+ export const selectOption = async (element: HTMLElement, option: string) => {
6
+ await userEvent.click(element);
7
+ await userEvent.click(screen.getByRole("option", { name: option }));
8
+ };
9
+
10
+ export const selectOptions = async (element: HTMLElement, options: string[]) => {
11
+ await userEvent.click(element);
12
+
13
+ for (const option of options) {
14
+ const optionElement = screen.getByRole("option", { name: option });
15
+ await userEvent.click(optionElement);
16
+ }
17
+
18
+ const presentation = screen.getByRole("presentation").firstChild;
19
+ presentation && fireEvent.click(presentation);
20
+ };
21
+
22
+ export const clearMultiSelect = async (element: HTMLElement) => {
23
+ await userEvent.click(element);
24
+
25
+ const options = screen.queryAllByRole("option", { selected: true });
26
+
27
+ for (const option of options) {
28
+ await userEvent.click(option);
29
+ }
30
+
31
+ const presentation = screen.getByRole("presentation").firstChild;
32
+ presentation && fireEvent.click(presentation);
33
+ };
34
+
35
+ export const clearCheckbox = async (element: HTMLInputElement) => {
36
+ if (element.checked) {
37
+ await userEvent.click(element);
38
+ }
39
+ };
40
+
41
+ export const pickDatetime = (element: HTMLInputElement, value: Date, fmt: string) => {
42
+ fireEvent.change(element, { target: { value: format(value, fmt) } });
43
+ };