@pautena/react-design-system 0.6.2 → 0.7.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 (80) hide show
  1. package/dist/cjs/index.js +4 -4
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/components/value-displays/group-value-card/group-value-card.d.ts +4 -13
  4. package/dist/cjs/types/components/value-displays/group-value-card/group-value-card.mock.d.ts +1 -1
  5. package/dist/cjs/types/components/value-displays/index.d.ts +1 -0
  6. package/dist/cjs/types/components/value-displays/value-base/value-displays.types.d.ts +5 -0
  7. package/dist/cjs/types/components/value-displays/value-boolean/value-boolean.d.ts +1 -1
  8. package/dist/cjs/types/components/value-displays/value-content/value-content.d.ts +6 -1
  9. package/dist/cjs/types/components/value-displays/value-datetime/value-datetime.d.ts +1 -1
  10. package/dist/cjs/types/components/value-displays/value-image/value-image.d.ts +1 -1
  11. package/dist/cjs/types/components/value-displays/value-item/index.d.ts +1 -0
  12. package/dist/cjs/types/components/value-displays/value-item/value-item.d.ts +13 -0
  13. package/dist/cjs/types/components/value-displays/value-rating/value-rating.d.ts +1 -1
  14. package/dist/cjs/types/components/value-displays/value-text/value-text.d.ts +1 -1
  15. package/dist/cjs/types/generators/generators.mock.d.ts +2 -0
  16. package/dist/cjs/types/generators/generators.model.d.ts +17 -4
  17. package/dist/cjs/types/generators/object-details/object-details.d.ts +2 -1
  18. package/dist/cjs/types/utils/breakpoints.d.ts +13 -0
  19. package/dist/esm/index.js +4 -4
  20. package/dist/esm/index.js.map +1 -1
  21. package/dist/esm/types/components/value-displays/group-value-card/group-value-card.d.ts +4 -13
  22. package/dist/esm/types/components/value-displays/group-value-card/group-value-card.mock.d.ts +1 -1
  23. package/dist/esm/types/components/value-displays/index.d.ts +1 -0
  24. package/dist/esm/types/components/value-displays/value-base/value-displays.types.d.ts +5 -0
  25. package/dist/esm/types/components/value-displays/value-boolean/value-boolean.d.ts +1 -1
  26. package/dist/esm/types/components/value-displays/value-content/value-content.d.ts +6 -1
  27. package/dist/esm/types/components/value-displays/value-datetime/value-datetime.d.ts +1 -1
  28. package/dist/esm/types/components/value-displays/value-image/value-image.d.ts +1 -1
  29. package/dist/esm/types/components/value-displays/value-item/index.d.ts +1 -0
  30. package/dist/esm/types/components/value-displays/value-item/value-item.d.ts +13 -0
  31. package/dist/esm/types/components/value-displays/value-rating/value-rating.d.ts +1 -1
  32. package/dist/esm/types/components/value-displays/value-text/value-text.d.ts +1 -1
  33. package/dist/esm/types/generators/generators.mock.d.ts +2 -0
  34. package/dist/esm/types/generators/generators.model.d.ts +17 -4
  35. package/dist/esm/types/generators/object-details/object-details.d.ts +2 -1
  36. package/dist/esm/types/utils/breakpoints.d.ts +13 -0
  37. package/dist/index.d.ts +42 -19
  38. package/package.json +1 -1
  39. package/src/components/alerts/expandable-alert/expandable-alert.stories.tsx +1 -1
  40. package/src/components/containers/center-container/center-container.stories.tsx +1 -1
  41. package/src/components/data-display/markdown/markdown.stories.tsx +0 -1
  42. package/src/components/dialogs/bootstrap-dialog/bootstrap-dialog.stories.tsx +1 -1
  43. package/src/components/drawers/drawer-item/drawer-item.stories.tsx +6 -11
  44. package/src/components/inputs/search-input/search-input.stories.tsx +0 -1
  45. package/src/components/navigation/tab/tab-card/tab-card.stories.tsx +0 -1
  46. package/src/components/value-displays/group-value-card/group-value-card.mock.tsx +28 -26
  47. package/src/components/value-displays/group-value-card/group-value-card.stories.tsx +14 -5
  48. package/src/components/value-displays/group-value-card/group-value-card.tsx +28 -40
  49. package/src/components/value-displays/index.ts +1 -0
  50. package/src/components/value-displays/value-base/value-displays.types.ts +6 -0
  51. package/src/components/value-displays/value-boolean/value-boolean.stories.tsx +8 -0
  52. package/src/components/value-displays/value-boolean/value-boolean.tsx +3 -2
  53. package/src/components/value-displays/value-content/value-content.stories.tsx +7 -2
  54. package/src/components/value-displays/value-content/value-content.tsx +16 -3
  55. package/src/components/value-displays/value-datetime/value-datetime.stories.tsx +9 -0
  56. package/src/components/value-displays/value-datetime/value-datetime.tsx +3 -2
  57. package/src/components/value-displays/value-displays.stories.mdx +1 -0
  58. package/src/components/value-displays/value-image/value-image.stories.tsx +8 -0
  59. package/src/components/value-displays/value-image/value-image.tsx +4 -2
  60. package/src/components/value-displays/value-item/index.ts +1 -0
  61. package/src/components/value-displays/value-item/value-item.stories.tsx +45 -0
  62. package/src/components/value-displays/value-item/value-item.test.tsx +20 -0
  63. package/src/components/value-displays/value-item/value-item.tsx +54 -0
  64. package/src/components/value-displays/value-rating/value-rating.stories.tsx +8 -0
  65. package/src/components/value-displays/value-rating/value-rating.tsx +4 -2
  66. package/src/components/value-displays/value-text/value-text.stories.tsx +8 -0
  67. package/src/components/value-displays/value-text/value-text.tsx +3 -2
  68. package/src/generators/generators.mock.ts +64 -0
  69. package/src/generators/generators.model.test.ts +7 -0
  70. package/src/generators/generators.model.ts +35 -13
  71. package/src/generators/model-form/model-form.test.tsx +5 -0
  72. package/src/generators/model-form/model-form.tsx +21 -3
  73. package/src/generators/model-router/model-router.test.tsx +10 -1
  74. package/src/generators/model-router/stories/templates.tsx +0 -1
  75. package/src/generators/object-details/object-details.stories.tsx +13 -3
  76. package/src/generators/object-details/object-details.tsx +96 -15
  77. package/src/providers/notification-center/notification-center.stories.tsx +1 -1
  78. package/src/tests/assertions.ts +10 -1
  79. package/src/utils/breakpoints.test.ts +42 -0
  80. package/src/utils/breakpoints.ts +62 -0
@@ -5,6 +5,8 @@
5
5
  export type ModelFieldTypes =
6
6
  | "string"
7
7
  | "number"
8
+ | "string[]"
9
+ | "number[]"
8
10
  | "boolean"
9
11
  | "enum"
10
12
  | "multienum"
@@ -31,10 +33,18 @@ type StringField = {
31
33
  type: "string";
32
34
  };
33
35
 
36
+ type StringArrayField = {
37
+ type: "string[]";
38
+ };
39
+
34
40
  type NumberField = {
35
41
  type: "number";
36
42
  };
37
43
 
44
+ type NumberArrayField = {
45
+ type: "number[]";
46
+ };
47
+
38
48
  type BooleanField = {
39
49
  type: "boolean";
40
50
  };
@@ -70,6 +80,8 @@ type DatetimeField = {
70
80
  type SingleFields =
71
81
  | StringField
72
82
  | NumberField
83
+ | StringArrayField
84
+ | NumberArrayField
73
85
  | BooleanField
74
86
  | EnumField
75
87
  | MultiEnumField
@@ -82,7 +94,12 @@ export type GroupField = {
82
94
  value: (Base & Breakpoints & SingleFields)[];
83
95
  } & Base;
84
96
 
85
- type Fields = SingleFields | GroupField;
97
+ export type ArrayGroupField = {
98
+ type: "group[]";
99
+ value: (Base & Breakpoints & SingleFields)[];
100
+ } & Base;
101
+
102
+ type Fields = SingleFields | GroupField | ArrayGroupField;
86
103
  export type ModelField = Base & Breakpoints & Fields;
87
104
 
88
105
  export type Model = {
@@ -94,9 +111,10 @@ export type Model = {
94
111
  * Types used to represent an instance of a model specification
95
112
  */
96
113
  export type BaseFieldType = string | number | boolean | Date;
97
- export type ArrayFieldType = string[];
114
+ export type ArrayFieldType = string[] | number[];
98
115
  export type SingleFieldType = BaseFieldType | ArrayFieldType;
99
116
  export type GroupInstanceType = { [key: string]: SingleFieldType };
117
+ export type ArrayInstanceType = { [key: string]: SingleFieldType }[];
100
118
  export type FieldType = SingleFieldType | GroupInstanceType;
101
119
 
102
120
  export interface BasicModelInstance {
@@ -108,17 +126,21 @@ export interface BasicModelInstance {
108
126
  * UTILITIES
109
127
  * Some functions used in several places to help to manage models
110
128
  */
111
- const InitialStateZeroValue: Record<ModelFieldTypes | "group", FieldType | undefined> = {
112
- string: "",
113
- number: 0,
114
- boolean: false,
115
- enum: "",
116
- multienum: [],
117
- date: new Date(1970, 0, 1, 0, 0),
118
- time: new Date(1970, 0, 1, 0, 0),
119
- datetime: new Date(1970, 0, 1, 0, 0),
120
- group: {},
121
- };
129
+ const InitialStateZeroValue: Record<ModelFieldTypes | "group" | "group[]", FieldType | undefined> =
130
+ {
131
+ string: "",
132
+ number: 0,
133
+ boolean: false,
134
+ enum: "",
135
+ multienum: [],
136
+ date: new Date(1970, 0, 1, 0, 0),
137
+ time: new Date(1970, 0, 1, 0, 0),
138
+ datetime: new Date(1970, 0, 1, 0, 0),
139
+ group: {},
140
+ "group[]": [],
141
+ "string[]": [],
142
+ "number[]": [],
143
+ };
122
144
 
123
145
  const getFieldValueOrZero = (
124
146
  field: ModelField,
@@ -86,6 +86,8 @@ describe("ModelForm", () => {
86
86
  await userEvent.click(screen.getByRole("checkbox", { name: /available/i }));
87
87
  await userEvent.type(screen.getByRole("textbox", { name: /currency/i }), "mxn");
88
88
  pickDatetime(screen.getByRole("textbox", { name: /trade date/i }), tradeDate, TradeDateFormat);
89
+ await userEvent.type(screen.getByRole("textbox", { name: /codes/i }), "foo,bar");
90
+ await userEvent.type(screen.getByRole("textbox", { name: /identifiers/i }), "1,2,3");
89
91
 
90
92
  await userEvent.click(screen.getByRole("button", { name: /save/i }));
91
93
 
@@ -110,6 +112,9 @@ describe("ModelForm", () => {
110
112
  available: true,
111
113
  currency: "mxn",
112
114
  tradeDate,
115
+ codes: ["foo", "bar"],
116
+ identifiers: ["1", "2", "3"],
117
+ carsHistory: [],
113
118
  });
114
119
  });
115
120
  });
@@ -88,9 +88,11 @@ export const ModelForm = <T extends BasicModelInstance>({
88
88
  ) => {
89
89
  e.preventDefault();
90
90
 
91
- let value: string | number = e.target.value;
91
+ let value: string | number | string[] | number[] = e.target.value;
92
92
  if (type === "number" && typeof value === "string") {
93
- value = parseInt(e.target.value);
93
+ value = parseInt(e.target.value, 10);
94
+ } else if (type.includes("[]")) {
95
+ value = e.target.value.split(",");
94
96
  }
95
97
  setKeyValue(e.target.name, key, value);
96
98
  };
@@ -180,7 +182,7 @@ export const ModelForm = <T extends BasicModelInstance>({
180
182
  >
181
183
  {field.value.map((fieldValue) => (
182
184
  <MenuItem key={fieldValue} value={fieldValue}>
183
- <Checkbox checked={((value as ArrayFieldType) || []).includes(fieldValue)} />
185
+ <Checkbox checked={((value as any[]) || []).includes(fieldValue)} />
184
186
  <ListItemText primary={fieldValue} />
185
187
  </MenuItem>
186
188
  ))}
@@ -214,6 +216,22 @@ export const ModelForm = <T extends BasicModelInstance>({
214
216
  onChange={(value) => handleDateChange(value, key, id)}
215
217
  />
216
218
  );
219
+ } else if (type === "group[]") {
220
+ return null;
221
+ } else if (type.includes("[]")) {
222
+ fieldInput = (
223
+ <TextField
224
+ required
225
+ type="text"
226
+ label={name}
227
+ name={id}
228
+ variant="outlined"
229
+ helperText="Use comas to separate multiple values"
230
+ fullWidth
231
+ value={(value as any[]).join(",")}
232
+ onChange={(e) => handleInputChange(e, key, type)}
233
+ />
234
+ );
217
235
  } else {
218
236
  fieldInput = (
219
237
  <TextField
@@ -167,6 +167,8 @@ describe("ModelRouter", () => {
167
167
  const tradeDateElement = screen.getByRole<HTMLInputElement>("textbox", {
168
168
  name: /trade date/i,
169
169
  });
170
+ const codesElement = screen.getByRole("textbox", { name: /codes/i });
171
+ const identifiersElement = screen.getByRole("textbox", { name: /identifiers/i });
170
172
 
171
173
  if (clear) {
172
174
  await userEvent.clear(idElement);
@@ -181,6 +183,8 @@ describe("ModelRouter", () => {
181
183
  await userEvent.clear(currencyElement);
182
184
  await userEvent.clear(tradeDateElement);
183
185
  await userEvent.clear(timeReturnElement);
186
+ await userEvent.clear(codesElement);
187
+ await userEvent.clear(identifiersElement);
184
188
  await clearCheckbox(availableElement);
185
189
  await clearMultiSelect(typeElement);
186
190
  }
@@ -204,6 +208,8 @@ describe("ModelRouter", () => {
204
208
  }
205
209
  await userEvent.type(currencyElement, instance.currency);
206
210
  pickDatetime(tradeDateElement, instance.tradeDate, TradeDateFormat);
211
+ await userEvent.type(codesElement, instance.codes.join(","));
212
+ await userEvent.type(identifiersElement, instance.identifiers.join(","));
207
213
 
208
214
  submit && (await userEvent.click(screen.getByRole("button", { name: /save/i })));
209
215
 
@@ -712,7 +718,10 @@ describe("ModelRouter", () => {
712
718
 
713
719
  const newInstance = await actions.fullfillModelForm({ model, submit: true });
714
720
 
715
- expectToHaveBeenCalledOnceWithMockInstance(onSubmitNewItem, newInstance);
721
+ expectToHaveBeenCalledOnceWithMockInstance(onSubmitNewItem, {
722
+ ...newInstance,
723
+ carsHistory: [],
724
+ });
716
725
  });
717
726
 
718
727
  it("would show a loading indicator when the request is in progress", async () => {
@@ -1,5 +1,4 @@
1
1
  import { action } from "@storybook/addon-actions";
2
- import { ModelRouter } from "../model-router";
3
2
  import { createModelInstance, MockInstance, mockModel } from "../../generators.mock";
4
3
  import { IdleRequest } from "../model-router.types";
5
4
 
@@ -1,4 +1,3 @@
1
- import React from "react";
2
1
  import { Meta } from "@storybook/react";
3
2
  import { ObjectDetails } from "./object-details";
4
3
  import { withPadding } from "../../storybook";
@@ -15,6 +14,17 @@ export default {
15
14
  },
16
15
  } satisfies Meta<typeof ObjectDetails>;
17
16
 
18
- export const Default = () => {
19
- return <ObjectDetails model={mockModel} instance={instance} />;
17
+ export const Default = {
18
+ args: {
19
+ model: mockModel,
20
+ instance: instance,
21
+ },
22
+ };
23
+
24
+ export const Dense = {
25
+ args: {
26
+ model: mockModel,
27
+ instance: instance,
28
+ dense: true,
29
+ },
20
30
  };
@@ -2,9 +2,8 @@ import React from "react";
2
2
  import { Grid } from "@mui/material";
3
3
  import {
4
4
  GroupValueCard,
5
- GroupValueItem,
5
+ ValueItem,
6
6
  ValueBoolean,
7
- ValueCard,
8
7
  ValueText,
9
8
  ValueDatetime,
10
9
  } from "../../components";
@@ -14,39 +13,103 @@ import {
14
13
  Model,
15
14
  BasicModelInstance,
16
15
  GroupInstanceType,
16
+ ArrayGroupField,
17
+ ArrayInstanceType,
17
18
  } from "../generators.model";
19
+ import { newBreakpointsCounter } from "~/utils/breakpoints";
20
+ import { DataGrid, GridColDef } from "@mui/x-data-grid";
21
+ import { ValueContent } from "~/components/value-displays/value-content";
22
+ import { ArrayFieldType } from "../generators.model";
23
+
24
+ interface SingleDetailValueFactoryOptions {
25
+ dense?: boolean;
26
+ }
18
27
 
19
28
  const singleDetailValueFactory = <T extends BasicModelInstance>(
20
29
  field: ModelField,
21
30
  instance: T | GroupInstanceType,
31
+ { dense }: SingleDetailValueFactoryOptions = {},
22
32
  ) => {
23
33
  const { id, name, type } = field;
24
34
  const value = instance[id];
25
35
  if (type === "boolean") {
26
- return <ValueBoolean label={name} value={value as boolean} />;
36
+ return <ValueBoolean dense={dense} label={name} value={value as boolean} />;
27
37
  } else if (type === "date" || type === "time" || type === "datetime") {
28
- return <ValueDatetime label={name} value={value as Date} format={field.format} />;
38
+ return <ValueDatetime dense={dense} label={name} value={value as Date} format={field.format} />;
29
39
  }
30
- return <ValueText label={name} value={value?.toString()} />;
40
+ return <ValueText dense={dense} label={name} value={value?.toString()} />;
41
+ };
42
+
43
+ interface ObjectArrayGroupProps {
44
+ field: ArrayGroupField;
45
+ instance: ArrayInstanceType;
46
+ dense?: boolean;
47
+ }
48
+
49
+ const ObjectArrayGroup = ({
50
+ field: { name, description, value },
51
+ instance,
52
+ dense,
53
+ }: ObjectArrayGroupProps) => {
54
+ const columns: GridColDef[] = [{ field: "id", headerName: "ID", width: 70 }];
55
+
56
+ value.forEach((column) => {
57
+ columns.push({
58
+ field: column.id,
59
+ headerName: column.name,
60
+ });
61
+ });
62
+
63
+ const rows = instance.map((f, id) => ({
64
+ id,
65
+ ...f,
66
+ }));
67
+
68
+ return (
69
+ <GroupValueCard title={name} subtitle={description} dense={dense}>
70
+ <Grid item xs={12}>
71
+ <DataGrid
72
+ rows={rows}
73
+ columns={columns}
74
+ density={dense ? "compact" : "standard"}
75
+ disableRowSelectionOnClick
76
+ pageSizeOptions={[5]}
77
+ initialState={{
78
+ pagination: {
79
+ paginationModel: {
80
+ pageSize: 5,
81
+ },
82
+ },
83
+ }}
84
+ sx={{ height: 400 }}
85
+ />
86
+ </Grid>
87
+ </GroupValueCard>
88
+ );
31
89
  };
32
90
 
33
91
  interface ObjectDetailGroupProps {
34
92
  field: GroupField;
35
93
  instance: GroupInstanceType;
94
+ dense?: boolean;
36
95
  }
37
96
 
38
97
  const ObjectDetailGroup = ({
39
98
  field: { name, description, value },
40
99
  instance,
100
+ dense,
41
101
  }: ObjectDetailGroupProps) => {
102
+ const breakpointsCounter = newBreakpointsCounter();
103
+
42
104
  return (
43
- <GroupValueCard title={name} subtitle={description}>
105
+ <GroupValueCard title={name} subtitle={description} dense={dense}>
44
106
  {value.map((field) => {
45
107
  const { id, xs, sm, md, lg, xl } = field;
108
+ const bordered = breakpointsCounter.increment(field);
46
109
  return (
47
- <GroupValueItem key={id} xs={xs} sm={sm} md={md} lg={lg} xl={xl}>
48
- {singleDetailValueFactory(field, instance)}
49
- </GroupValueItem>
110
+ <ValueItem key={id} xs={xs} sm={sm} md={md} lg={lg} xl={xl} bordered={bordered}>
111
+ {singleDetailValueFactory(field, instance, { dense })}
112
+ </ValueItem>
50
113
  );
51
114
  })}
52
115
  </GroupValueCard>
@@ -55,30 +118,48 @@ const ObjectDetailGroup = ({
55
118
 
56
119
  export interface ObjectDetailsProps<T extends BasicModelInstance> {
57
120
  model: Model;
121
+ dense?: boolean;
58
122
  instance: T;
59
123
  }
60
124
 
61
125
  export const ObjectDetails = <T extends BasicModelInstance>({
62
126
  model,
63
127
  instance,
128
+ dense,
64
129
  }: ObjectDetailsProps<T>) => {
130
+ const breakpointsCounter = newBreakpointsCounter();
65
131
  return (
66
- <Grid container spacing={2}>
132
+ <Grid container spacing={dense ? 1 : 2}>
67
133
  {model.fields.map((field) => {
68
- const { id, type, xs = 3, sm, md, lg, xl } = field;
134
+ const { id, type, xs = 3, sm = 0, md = 0, lg = 0, xl = 0 } = field;
69
135
 
70
136
  if (type === "group") {
137
+ breakpointsCounter.increment({ xs: 12 });
138
+ return (
139
+ <Grid item key={id} xs={12}>
140
+ <ObjectDetailGroup
141
+ field={field}
142
+ instance={instance[id] as GroupInstanceType}
143
+ dense={dense}
144
+ />
145
+ </Grid>
146
+ );
147
+ }
148
+
149
+ if (type === "group[]") {
150
+ breakpointsCounter.increment({ xs: 12 });
71
151
  return (
72
152
  <Grid item key={id} xs={12}>
73
- <ObjectDetailGroup field={field} instance={instance[id] as GroupInstanceType} />
153
+ <ObjectArrayGroup field={field} instance={instance[id] as any} dense={dense} />
74
154
  </Grid>
75
155
  );
76
156
  }
77
157
 
158
+ const bordered = breakpointsCounter.increment(field);
78
159
  return (
79
- <Grid item key={id} xs={xs} sm={sm} md={md} lg={lg} xl={xl}>
80
- <ValueCard>{singleDetailValueFactory(field, instance)}</ValueCard>
81
- </Grid>
160
+ <ValueItem key={id} xs={xs} sm={sm} md={md} lg={lg} xl={xl} bordered={bordered}>
161
+ {singleDetailValueFactory(field, instance, { dense })}
162
+ </ValueItem>
82
163
  );
83
164
  })}
84
165
  </Grid>
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { Meta, StoryObj } from "@storybook/react";
2
+ import { Meta } from "@storybook/react";
3
3
  import { NotificationCenterProvider } from "./notification-center.provider";
4
4
  import { useNotificationCenter } from "./notification-center.context";
5
5
  import { Button, Box } from "@mui/material";
@@ -32,7 +32,7 @@ export const expectModelFieldInputExist = (fields: ModelField[]) => {
32
32
  expect(
33
33
  screen.getByRole("button", { name: new RegExp(field.name.toLowerCase(), "i") }),
34
34
  ).toBeInTheDocument();
35
- } else {
35
+ } else if (field.type !== "group[]") {
36
36
  expect(screen.getByRole("textbox", { name: field.name })).toBeInTheDocument();
37
37
  }
38
38
  });
@@ -58,6 +58,12 @@ export const expectModelFieldInputValue = (
58
58
  fmt: field.format,
59
59
  addSpaces: true,
60
60
  });
61
+ } else if (field.type === "group[]") {
62
+ // Ignore group[] cases
63
+ } else if (field.type.includes("[]")) {
64
+ expect(screen.getByRole("textbox", { name: field.name })).toHaveValue(
65
+ (value as any[]).join(","),
66
+ );
61
67
  } else {
62
68
  expect(screen.getByDisplayValue(value.toString())).toBeInTheDocument();
63
69
  }
@@ -166,6 +172,9 @@ export const expectToHaveBeenCalledOnceWithMockInstance = (
166
172
  calledTradeDate.getSeconds(),
167
173
  calledTradeDate.getMilliseconds(),
168
174
  ),
175
+ codes: instance.codes,
176
+ identifiers: instance.identifiers.map((i) => i.toString()),
177
+ carsHistory: instance.carsHistory,
169
178
  });
170
179
  };
171
180
 
@@ -0,0 +1,42 @@
1
+ import { newBreakpointsCounter } from "./breakpoints";
2
+
3
+ describe("newBreakpointsCounter", () => {
4
+ //TODO Write some tests to be able to debug/develop faster
5
+ it("should create a new instance with the correct initial values", () => {
6
+ const breakpointsCounter = newBreakpointsCounter();
7
+
8
+ expect(breakpointsCounter.xs).toBe(0);
9
+ expect(breakpointsCounter.sm).toBe(0);
10
+ expect(breakpointsCounter.md).toBe(0);
11
+ expect(breakpointsCounter.lg).toBe(0);
12
+ expect(breakpointsCounter.xl).toBe(0);
13
+ });
14
+
15
+ describe.each([["xs"], ["sm"], ["md"], ["lg"], ["xl"]])("breakpoint %s", (breakpoint: string) => {
16
+ it.each([
17
+ [false, [2, 3, 3, 4, 3]],
18
+ [false, [2, 3, 3, 4, 12, 3]],
19
+ [false, [6, 6, 6, 6, 6, 12, 3]],
20
+ [false, [4, 4, 4, 4, 4, 3, 8]],
21
+ [false, [4, 4, 4, 4, 4, 3, 8]],
22
+ [true, [1, 2]],
23
+ [true, [12, 6, 3]],
24
+ [true, [2, 6, 3]],
25
+ ])(
26
+ "should have border=%s if apply %s increments",
27
+ (expectedBorder: boolean, increments: number[]) => {
28
+ const breakpointsCounter = newBreakpointsCounter();
29
+
30
+ let border: Record<string, boolean> = {};
31
+ increments.forEach((increment) => {
32
+ border = breakpointsCounter.increment({ [breakpoint]: increment }) as Record<
33
+ string,
34
+ boolean
35
+ >;
36
+ });
37
+
38
+ expect(border[breakpoint]).toBe(expectedBorder);
39
+ },
40
+ );
41
+ });
42
+ });
@@ -0,0 +1,62 @@
1
+ import { ResponsiveStyleValue } from "@mui/system";
2
+
3
+ interface Breakpoints {
4
+ xs: number;
5
+ sm: number;
6
+ md: number;
7
+ lg: number;
8
+ xl: number;
9
+ }
10
+
11
+ interface BreakpointsCounter extends Breakpoints {
12
+ increment(breakpoints: Partial<Breakpoints>): ResponsiveStyleValue<boolean>;
13
+ }
14
+
15
+ export const newBreakpointsCounter = (breakpoint = 12): BreakpointsCounter => {
16
+ const calculateBreakpointIncrement = (
17
+ breakpointsCounter: BreakpointsCounter,
18
+ key: keyof Breakpoints,
19
+ value: number,
20
+ ): boolean => {
21
+ const initialValue = breakpointsCounter[key];
22
+ breakpointsCounter[key] += value;
23
+ if (breakpointsCounter[key] > breakpoint) {
24
+ breakpointsCounter[key] = value;
25
+ return false;
26
+ }
27
+
28
+ if (breakpointsCounter[key] == breakpoint) {
29
+ breakpointsCounter[key] = 0;
30
+ }
31
+
32
+ return initialValue > 0;
33
+ };
34
+
35
+ return {
36
+ xs: 0,
37
+ sm: 0,
38
+ md: 0,
39
+ lg: 0,
40
+ xl: 0,
41
+ increment: function ({
42
+ xs = 0,
43
+ sm = 0,
44
+ md = 0,
45
+ lg = 0,
46
+ xl = 0,
47
+ }): ResponsiveStyleValue<boolean> {
48
+ const smInc = sm || xs;
49
+ const mdInc = md || smInc;
50
+ const lgInc = lg || mdInc;
51
+ const xlInc = xl || lgInc;
52
+
53
+ return {
54
+ xs: calculateBreakpointIncrement(this, "xs", xs),
55
+ sm: calculateBreakpointIncrement(this, "sm", smInc),
56
+ md: calculateBreakpointIncrement(this, "md", mdInc),
57
+ lg: calculateBreakpointIncrement(this, "lg", lgInc),
58
+ xl: calculateBreakpointIncrement(this, "xl", xlInc),
59
+ };
60
+ },
61
+ };
62
+ };