@pautena/react-design-system 0.7.2 → 0.7.3

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/dist/cjs/index.js +4 -4
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/components/alerts/expandable-alert/expandable-alert.d.ts +4 -1
  4. package/dist/cjs/types/components/data-display/board/board.d.ts +1 -2
  5. package/dist/cjs/types/components/feedback/query-container/query-container.d.ts +7 -3
  6. package/dist/cjs/types/components/inputs/index.d.ts +1 -0
  7. package/dist/cjs/types/components/value-displays/group-value-card/group-value-card.mock.d.ts +3 -1
  8. package/dist/cjs/types/components/value-displays/value-base/value-edit.d.ts +7 -2
  9. package/dist/cjs/types/components/value-displays/value-content/value-content.d.ts +10 -1
  10. package/dist/esm/index.js +4 -4
  11. package/dist/esm/index.js.map +1 -1
  12. package/dist/esm/types/components/alerts/expandable-alert/expandable-alert.d.ts +4 -1
  13. package/dist/esm/types/components/data-display/board/board.d.ts +1 -2
  14. package/dist/esm/types/components/feedback/query-container/query-container.d.ts +7 -3
  15. package/dist/esm/types/components/inputs/index.d.ts +1 -0
  16. package/dist/esm/types/components/value-displays/group-value-card/group-value-card.mock.d.ts +3 -1
  17. package/dist/esm/types/components/value-displays/value-base/value-edit.d.ts +7 -2
  18. package/dist/esm/types/components/value-displays/value-content/value-content.d.ts +10 -1
  19. package/dist/index.d.ts +26 -9
  20. package/package.json +2 -1
  21. package/src/components/alerts/expandable-alert/expandable-alert.stories.tsx +23 -1
  22. package/src/components/alerts/expandable-alert/expandable-alert.tsx +11 -4
  23. package/src/components/data-display/board/board.test.tsx +60 -43
  24. package/src/components/data-display/board/board.tsx +13 -16
  25. package/src/components/feedback/query-container/query-container.stories.tsx +19 -6
  26. package/src/components/feedback/query-container/query-container.test.tsx +41 -17
  27. package/src/components/feedback/query-container/query-container.tsx +17 -5
  28. package/src/components/inputs/index.ts +1 -0
  29. package/src/components/value-displays/group-value-card/group-value-card.mock.tsx +28 -8
  30. package/src/components/value-displays/group-value-card/group-value-card.stories.tsx +19 -2
  31. package/src/components/value-displays/group-value-card/group-value-card.test.tsx +16 -18
  32. package/src/components/value-displays/value-base/value-edit.test.tsx +88 -0
  33. package/src/components/value-displays/value-base/value-edit.tsx +28 -6
  34. package/src/components/value-displays/value-boolean/value-boolean.stories.tsx +9 -0
  35. package/src/components/value-displays/value-boolean/value-boolean.test.tsx +29 -15
  36. package/src/components/value-displays/value-boolean/value-boolean.tsx +18 -11
  37. package/src/components/value-displays/value-content/value-content.test.tsx +20 -6
  38. package/src/components/value-displays/value-content/value-content.tsx +24 -10
  39. package/src/components/value-displays/value-datetime/value-datetime.stories.tsx +11 -0
  40. package/src/components/value-displays/value-datetime/value-datetime.test.tsx +9 -9
  41. package/src/components/value-displays/value-datetime/value-datetime.tsx +14 -10
  42. package/src/components/value-displays/value-rating/value-rating.stories.tsx +10 -0
  43. package/src/components/value-displays/value-rating/value-rating.test.tsx +11 -11
  44. package/src/components/value-displays/value-rating/value-rating.tsx +10 -8
  45. package/src/components/value-displays/value-text/value-text.stories.tsx +9 -0
  46. package/src/components/value-displays/value-text/value-text.test.tsx +20 -9
  47. package/src/components/value-displays/value-text/value-text.tsx +23 -10
  48. package/src/generators/model-form/model-form.test.tsx +1 -1
  49. package/src/generators/model-router/model-router.test.tsx +3 -3
  50. package/src/layouts/header-layout/header-layout.stories.tsx +2 -2
  51. package/src/layouts/header-layout/header-layout.tsx +1 -7
@@ -1,4 +1,4 @@
1
- import { Box, IconButton, Switch, Typography, useTheme } from "@mui/material";
1
+ import { Box, Switch, Typography, useTheme } from "@mui/material";
2
2
  import React from "react";
3
3
  import CheckIcon from "@mui/icons-material/Check";
4
4
  import CloseIcon from "@mui/icons-material/Close";
@@ -7,10 +7,10 @@ import {
7
7
  DefaultPlaceholder,
8
8
  EditableValueProps,
9
9
  useEditableValueDisplay,
10
+ ValueEditButton,
10
11
  ValueEditButtons,
11
12
  } from "../value-base";
12
- import { ValueContent } from "../value-content";
13
- import EditIcon from "@mui/icons-material/Edit";
13
+ import { ValueContent, getValueContentLabelId } from "../value-content";
14
14
 
15
15
  export type ValueBooleanProps = BaseValueProps<boolean> & EditableValueProps<boolean>;
16
16
 
@@ -25,6 +25,7 @@ export const ValueBoolean = ({
25
25
  dense,
26
26
  onEdit = () => null,
27
27
  }: ValueBooleanProps) => {
28
+ const id = getValueContentLabelId(label);
28
29
  const { typography } = useTheme();
29
30
  const { isEditing, editValue, startEdit, cancelEdit, setEditValue, submitEdit } =
30
31
  useEditableValueDisplay(value, onEdit);
@@ -35,11 +36,21 @@ export const ValueBoolean = ({
35
36
  <ValueContent label={label} dense={dense}>
36
37
  {isEditing ? (
37
38
  <Box display="flex" alignItems="center">
38
- <Switch checked={editValue} onChange={(e) => setEditValue(e.target.checked)} />
39
- <ValueEditButtons onClickCancel={cancelEdit} onSubmitEdit={submitEdit} />
39
+ <Switch
40
+ size={dense ? "small" : "medium"}
41
+ checked={editValue}
42
+ onChange={(e) => setEditValue(e.target.checked)}
43
+ />
44
+ <ValueEditButtons onClickCancel={cancelEdit} onClickSubmit={submitEdit} />
40
45
  </Box>
41
46
  ) : (
42
- <Box display="flex" alignItems="center">
47
+ <Box
48
+ display="flex"
49
+ alignItems="center"
50
+ aria-labelledby={id}
51
+ role="checkbox"
52
+ aria-checked={value}
53
+ >
43
54
  {value === undefined ? (
44
55
  <Typography variant="h5">{placeholder}</Typography>
45
56
  ) : value ? (
@@ -47,11 +58,7 @@ export const ValueBoolean = ({
47
58
  ) : (
48
59
  <CloseIcon color="error" sx={iconSx} />
49
60
  )}
50
- {editable && (
51
- <IconButton size="small" onClick={startEdit} sx={{ ml: 1 }}>
52
- <EditIcon />
53
- </IconButton>
54
- )}
61
+ {editable && <ValueEditButton dense={dense} onClick={startEdit} />}
55
62
  </Box>
56
63
  )}
57
64
  </ValueContent>
@@ -5,9 +5,17 @@ import { ValueContent } from "./value-content";
5
5
  import userEvent from "@testing-library/user-event";
6
6
 
7
7
  describe("ValueContent", () => {
8
- const renderComponent = ({ tooltip }: { tooltip?: string } = {}) => {
8
+ const renderComponent = ({
9
+ tooltip,
10
+ hideLabel,
11
+ }: { tooltip?: string; hideLabel?: boolean } = {}) => {
9
12
  render(
10
- <ValueContent label="lorem ipsum" tooltip={tooltip} tooltipEnterDelay={0}>
13
+ <ValueContent
14
+ label="lorem ipsum"
15
+ tooltip={tooltip}
16
+ tooltipEnterDelay={0}
17
+ hideLabel={hideLabel}
18
+ >
11
19
  <Typography>Test content</Typography>
12
20
  </ValueContent>,
13
21
  );
@@ -16,7 +24,13 @@ describe("ValueContent", () => {
16
24
  it("should render a label", () => {
17
25
  renderComponent();
18
26
 
19
- expect(screen.getByRole("label", { name: /lorem ipsum/i })).toBeInTheDocument();
27
+ expect(screen.getByRole("label", { name: /lorem ipsum/i })).toBeVisible();
28
+ });
29
+
30
+ it("shouldn't render a label if hideLabel=true", () => {
31
+ renderComponent({ hideLabel: true });
32
+
33
+ expect(screen.queryByRole("label", { name: /lorem ipsum/i })).not.toBeInTheDocument();
20
34
  });
21
35
 
22
36
  describe("tooltip", () => {
@@ -25,7 +39,7 @@ describe("ValueContent", () => {
25
39
 
26
40
  await userEvent.hover(screen.getByText(/test content/i));
27
41
 
28
- expect(await screen.findByRole("tooltip", { name: /dolor sit amet/i })).toBeInTheDocument();
42
+ expect(await screen.findByRole("tooltip", { name: /dolor sit amet/i })).toBeVisible();
29
43
  });
30
44
 
31
45
  it("shouldn't render a tooltip if it's not defined", () => {
@@ -39,12 +53,12 @@ describe("ValueContent", () => {
39
53
  it("should render the children if tooltip is defined", () => {
40
54
  renderComponent({ tooltip: "dolor sit amet" });
41
55
 
42
- expect(screen.getByText(/test content/i)).toBeInTheDocument();
56
+ expect(screen.getByText(/test content/i)).toBeVisible();
43
57
  });
44
58
  it("should render the children if tooltip is undefined", () => {
45
59
  renderComponent({ tooltip: undefined });
46
60
 
47
- expect(screen.getByText(/test content/i)).toBeInTheDocument();
61
+ expect(screen.getByText(/test content/i)).toBeVisible();
48
62
  });
49
63
  });
50
64
  });
@@ -1,4 +1,4 @@
1
- import { Box, Tooltip, Typography, useTheme } from "@mui/material";
1
+ import { Box, SxProps, Theme, Tooltip, Typography, useTheme } from "@mui/material";
2
2
  import React from "react";
3
3
 
4
4
  export const getValueContentLabelId = (label: string): string =>
@@ -10,6 +10,11 @@ export interface ValueContentProps {
10
10
  */
11
11
  label: string;
12
12
 
13
+ /**
14
+ * If true, the label will not be shown
15
+ */
16
+ hideLabel?: boolean;
17
+
13
18
  /**
14
19
  * If defined, a tooltip is going to be added arround the children;
15
20
  */
@@ -30,6 +35,11 @@ export interface ValueContentProps {
30
35
  * False by default
31
36
  */
32
37
  dense?: boolean;
38
+
39
+ /**
40
+ * Custom styles for the root component
41
+ */
42
+ sx?: SxProps<Theme>;
33
43
  }
34
44
 
35
45
  /**
@@ -37,24 +47,28 @@ export interface ValueContentProps {
37
47
  */
38
48
  export const ValueContent = ({
39
49
  label,
50
+ hideLabel,
40
51
  tooltip,
41
52
  tooltipEnterDelay = 2000,
42
53
  children,
43
54
  dense,
55
+ sx,
44
56
  }: ValueContentProps) => {
45
57
  const { typography } = useTheme();
46
58
  const id = getValueContentLabelId(label);
47
59
 
48
60
  return (
49
- <Box width={1} lineHeight={dense ? 0 : undefined}>
50
- <Typography
51
- variant={dense ? "caption" : "subtitle2"}
52
- role="label"
53
- id={id}
54
- lineHeight={dense ? typography.pxToRem(15) : undefined}
55
- >
56
- {label}
57
- </Typography>
61
+ <Box width={1} lineHeight={dense ? 0 : undefined} sx={sx}>
62
+ {!hideLabel && (
63
+ <Typography
64
+ variant={dense ? "caption" : "subtitle2"}
65
+ role="label"
66
+ id={id}
67
+ lineHeight={dense ? typography.pxToRem(15) : undefined}
68
+ >
69
+ {label}
70
+ </Typography>
71
+ )}
58
72
  {tooltip ? (
59
73
  <Tooltip title={tooltip} placement="top" enterDelay={tooltipEnterDelay}>
60
74
  {children}
@@ -73,3 +73,14 @@ export const EditableDate: Story = {
73
73
  editInputType: "date",
74
74
  },
75
75
  };
76
+
77
+ export const DenseEditable: Story = {
78
+ args: {
79
+ label: "Lorem",
80
+ value: new Date(2022, 8, 22),
81
+ format: "yyyy/MM/dd",
82
+ editable: true,
83
+ editInputType: "date",
84
+ dense: true,
85
+ },
86
+ };
@@ -77,13 +77,13 @@ describe("ValueDatetime", () => {
77
77
  it("should render an option to edit if editable is true", () => {
78
78
  renderComponent({ value: DummyValue, editable: true });
79
79
 
80
- expect(screen.getByTestId("EditIcon")).toBeVisible();
80
+ expect(screen.getByRole("button", { name: /edit/i })).toBeVisible();
81
81
  });
82
82
 
83
83
  it("should render an input with the value if the edit button is clicked", async () => {
84
84
  renderComponent({ value: DummyValue, editable: true });
85
85
 
86
- await userEvent.click(screen.getByTestId("EditIcon"));
86
+ await userEvent.click(screen.getByRole("button", { name: /edit/i }));
87
87
 
88
88
  assertDatetimeInputValue(screen.getByRole("textbox"), {
89
89
  value: DummyValue,
@@ -105,9 +105,9 @@ describe("ValueDatetime", () => {
105
105
  fmt,
106
106
  });
107
107
 
108
- await userEvent.click(screen.getByTestId("EditIcon"));
108
+ await userEvent.click(screen.getByRole("button", { name: /edit/i }));
109
109
  pickDatetime(screen.getByRole("textbox"), newValue, fmt);
110
- await userEvent.click(screen.getByTestId("CheckIcon"));
110
+ await userEvent.click(screen.getByRole("button", { name: /submit/i }));
111
111
 
112
112
  expect(onEdit).toHaveBeenCalledTimes(1);
113
113
  expect(onEdit).toHaveBeenCalledWith(expectedDate);
@@ -117,9 +117,9 @@ describe("ValueDatetime", () => {
117
117
  it("should not call onEdit if the edition is cancelled", async () => {
118
118
  const { onEdit } = renderComponent({ value: DummyValue, editable: true });
119
119
 
120
- await userEvent.click(screen.getByTestId("EditIcon"));
120
+ await userEvent.click(screen.getByRole("button", { name: /edit/i }));
121
121
  pickDatetime(screen.getByRole("textbox"), NewValue, datetimeFormat);
122
- await userEvent.click(screen.getByTestId("ClearIcon"));
122
+ await userEvent.click(screen.getByRole("button", { name: /cancel/i }));
123
123
 
124
124
  expect(onEdit).not.toHaveBeenCalled();
125
125
  });
@@ -127,10 +127,10 @@ describe("ValueDatetime", () => {
127
127
  it("should have the original value if is edited again after clear a change", async () => {
128
128
  renderComponent({ value: DummyValue, editable: true });
129
129
 
130
- await userEvent.click(screen.getByTestId("EditIcon"));
130
+ await userEvent.click(screen.getByRole("button", { name: /edit/i }));
131
131
  pickDatetime(screen.getByRole("textbox"), NewValue, datetimeFormat);
132
- await userEvent.click(screen.getByTestId("ClearIcon"));
133
- await userEvent.click(screen.getByTestId("EditIcon"));
132
+ await userEvent.click(screen.getByRole("button", { name: /cancel/i }));
133
+ await userEvent.click(screen.getByRole("button", { name: /edit/i }));
134
134
 
135
135
  assertDatetimeInputValue(screen.getByRole("textbox"), {
136
136
  value: DummyValue,
@@ -1,4 +1,4 @@
1
- import { Box, IconButton, TextField, Typography } from "@mui/material";
1
+ import { Box, TextField, Typography } from "@mui/material";
2
2
  import React from "react";
3
3
  import { format } from "date-fns";
4
4
  import {
@@ -7,8 +7,7 @@ import {
7
7
  EditableValueProps,
8
8
  } from "../value-base/value-displays.types";
9
9
  import { getValueContentLabelId, ValueContent } from "../value-content";
10
- import { useEditableValueDisplay, ValueEditButtons } from "../value-base";
11
- import EditIcon from "@mui/icons-material/Edit";
10
+ import { useEditableValueDisplay, ValueEditButton, ValueEditButtons } from "../value-base";
12
11
  import { DatePicker, DateTimePicker, TimePicker } from "@mui/x-date-pickers";
13
12
 
14
13
  export type EditInputType = "datetime" | "date" | "time";
@@ -51,16 +50,24 @@ export const ValueDatetime = ({
51
50
  : DatePicker;
52
51
 
53
52
  return (
54
- <ValueContent label={label} tooltip={value} dense={dense}>
53
+ <ValueContent
54
+ label={label}
55
+ hideLabel={isEditing}
56
+ tooltip={value}
57
+ dense={dense}
58
+ sx={{ display: "flex", flexDirection: "column" }}
59
+ >
55
60
  {isEditing ? (
56
61
  <EditPickerComponent
57
62
  value={editValue}
58
63
  format={fmt}
64
+ label={label}
59
65
  onChange={(newValue) => setEditValue(newValue ? newValue : undefined)}
60
66
  slots={{
61
67
  textField: (params) => (
62
68
  <TextField
63
69
  {...params}
70
+ size="small"
64
71
  InputProps={{
65
72
  ...params.InputProps,
66
73
  endAdornment: (
@@ -68,11 +75,12 @@ export const ValueDatetime = ({
68
75
  {params.InputProps?.endAdornment}
69
76
  <ValueEditButtons
70
77
  onClickCancel={cancelEdit}
71
- onSubmitEdit={submitEdit}
78
+ onClickSubmit={submitEdit}
72
79
  sx={{ ml: 2 }}
73
80
  />
74
81
  </>
75
82
  ),
83
+ sx: { marginY: !dense ? 1 : 0.2 },
76
84
  }}
77
85
  />
78
86
  ),
@@ -83,11 +91,7 @@ export const ValueDatetime = ({
83
91
  <Typography variant={dense ? "body1" : "h5"} noWrap aria-labelledby={id}>
84
92
  {value}
85
93
  </Typography>
86
- {editable && (
87
- <IconButton size="small" onClick={startEdit} sx={{ ml: 1 }}>
88
- <EditIcon />
89
- </IconButton>
90
- )}
94
+ {editable && <ValueEditButton dense={dense} onClick={startEdit} />}
91
95
  </Box>
92
96
  )}
93
97
  </ValueContent>
@@ -43,3 +43,13 @@ export const Editable: Story = {
43
43
  editable: true,
44
44
  },
45
45
  };
46
+
47
+ export const DenseEditable: Story = {
48
+ args: {
49
+ label: "Lorem",
50
+ value: 4,
51
+ maxRating: 7,
52
+ dense: true,
53
+ editable: true,
54
+ },
55
+ };
@@ -59,13 +59,13 @@ describe("ValueRating", () => {
59
59
  it("should render an option to edit if editable is true", () => {
60
60
  renderComponent({ editable: true });
61
61
 
62
- expect(screen.getByTestId("EditIcon")).toBeVisible();
62
+ expect(screen.getByRole("button", { name: /edit/i })).toBeVisible();
63
63
  });
64
64
 
65
65
  it("should render an input for each star + 1 for the zero value if the edit button is clicked", async () => {
66
66
  renderComponent({ editable: true });
67
67
 
68
- await userEvent.click(screen.getByTestId("EditIcon"));
68
+ await userEvent.click(screen.getByRole("button", { name: /edit/i }));
69
69
 
70
70
  expect(screen.getAllByRole("radio")).toHaveLength(6);
71
71
  });
@@ -73,9 +73,9 @@ describe("ValueRating", () => {
73
73
  it("should submit the new value if is edited", async () => {
74
74
  const { onEdit } = renderComponent({ value: 2, editable: true });
75
75
 
76
- await userEvent.click(screen.getByTestId("EditIcon"));
77
- await userEvent.click(screen.getAllByRole("radio")[3]); // Click the fourth star
78
- await userEvent.click(screen.getByTestId("CheckIcon"));
76
+ await userEvent.click(screen.getByRole("button", { name: /edit/i }));
77
+ await userEvent.click(screen.getByRole("radio", { name: /4 stars/i }));
78
+ await userEvent.click(screen.getByRole("button", { name: /submit/i }));
79
79
 
80
80
  expect(onEdit).toHaveBeenCalledTimes(1);
81
81
  expect(onEdit).toHaveBeenCalledWith(4);
@@ -84,9 +84,9 @@ describe("ValueRating", () => {
84
84
  it("should not call onEdit if the edition is cancelled", async () => {
85
85
  const { onEdit } = renderComponent({ editable: true });
86
86
 
87
- await userEvent.click(screen.getByTestId("EditIcon"));
87
+ await userEvent.click(screen.getByRole("button", { name: /edit/i }));
88
88
  await userEvent.click(screen.getAllByRole("radio")[3]);
89
- await userEvent.click(screen.getByTestId("ClearIcon"));
89
+ await userEvent.click(screen.getByRole("button", { name: /cancel/i }));
90
90
 
91
91
  expect(onEdit).not.toHaveBeenCalled();
92
92
  });
@@ -94,10 +94,10 @@ describe("ValueRating", () => {
94
94
  it("should have the original value if is edited again after clear a change", async () => {
95
95
  renderComponent({ editable: true });
96
96
 
97
- await userEvent.click(screen.getByTestId("EditIcon"));
98
- await userEvent.click(screen.getAllByRole("radio")[3]);
99
- await userEvent.click(screen.getByTestId("ClearIcon"));
100
- await userEvent.click(screen.getByTestId("EditIcon"));
97
+ await userEvent.click(screen.getByRole("button", { name: /edit/i }));
98
+ await userEvent.click(screen.getByLabelText(/3 stars/i));
99
+ await userEvent.click(screen.getByRole("button", { name: /cancel/i }));
100
+ await userEvent.click(screen.getByRole("button", { name: /edit/i }));
101
101
 
102
102
  expect(screen.getAllByTestId("StarIcon")).toHaveLength(3);
103
103
  });
@@ -1,6 +1,6 @@
1
1
  import { Box, IconButton, Rating } from "@mui/material";
2
2
  import React from "react";
3
- import { useEditableValueDisplay, ValueEditButtons } from "../value-base";
3
+ import { useEditableValueDisplay, ValueEditButton, ValueEditButtons } from "../value-base";
4
4
  import { BaseValueProps, EditableValueProps } from "../value-base/value-displays.types";
5
5
  import { getValueContentLabelId, ValueContent } from "../value-content";
6
6
  import EditIcon from "@mui/icons-material/Edit";
@@ -33,14 +33,16 @@ export const ValueRating = ({
33
33
  max={maxRating}
34
34
  size={dense ? "small" : "medium"}
35
35
  value={isEditing ? editValue : value}
36
- onChange={(_, newValue) => newValue && setEditValue(newValue)}
36
+ onChange={(e, newValue) => {
37
+ if (Number.isNaN(newValue) && (e.currentTarget as any).value) {
38
+ setEditValue(parseInt((e.currentTarget as any).value, 10));
39
+ } else if (newValue) {
40
+ setEditValue(newValue);
41
+ }
42
+ }}
37
43
  />
38
- {editable && !isEditing && (
39
- <IconButton size="small" onClick={startEdit} sx={{ ml: 1 }}>
40
- <EditIcon />
41
- </IconButton>
42
- )}
43
- {isEditing && <ValueEditButtons onClickCancel={cancelEdit} onSubmitEdit={submitEdit} />}
44
+ {editable && !isEditing && <ValueEditButton dense={dense} onClick={startEdit} />}
45
+ {isEditing && <ValueEditButtons onClickCancel={cancelEdit} onClickSubmit={submitEdit} />}
44
46
  </Box>
45
47
  </ValueContent>
46
48
  );
@@ -61,3 +61,12 @@ export const Editable: Story = {
61
61
  editable: true,
62
62
  },
63
63
  };
64
+
65
+ export const DenseEditable: Story = {
66
+ args: {
67
+ label: "Lorem",
68
+ value: "lorem ipsum",
69
+ dense: true,
70
+ editable: true,
71
+ },
72
+ };
@@ -69,13 +69,13 @@ describe("ValueText", () => {
69
69
  it("should render an option to edit if editable is true", () => {
70
70
  renderComponent({ value: DummyValue, editable: true });
71
71
 
72
- expect(screen.getByTestId("EditIcon")).toBeVisible();
72
+ expect(screen.getByRole("button", { name: /edit/i })).toBeVisible();
73
73
  });
74
74
 
75
75
  it("should render an input with the value if the edit button is clicked", async () => {
76
76
  renderComponent({ value: DummyValue, editable: true });
77
77
 
78
- await userEvent.click(screen.getByTestId("EditIcon"));
78
+ await userEvent.click(screen.getByRole("button", { name: /edit/i }));
79
79
 
80
80
  expect(screen.getByRole("textbox")).toHaveValue(DummyValue);
81
81
  });
@@ -83,10 +83,10 @@ describe("ValueText", () => {
83
83
  it("should submit the new value if is edited", async () => {
84
84
  const { onEdit } = renderComponent({ value: DummyValue, editable: true });
85
85
 
86
- await userEvent.click(screen.getByTestId("EditIcon"));
86
+ await userEvent.click(screen.getByRole("button", { name: /edit/i }));
87
87
  await userEvent.clear(screen.getByRole("textbox"));
88
88
  await userEvent.type(screen.getByRole("textbox"), "new value");
89
- await userEvent.click(screen.getByTestId("CheckIcon"));
89
+ await userEvent.click(screen.getByRole("button", { name: /submit/i }));
90
90
 
91
91
  expect(onEdit).toHaveBeenCalledTimes(1);
92
92
  expect(onEdit).toHaveBeenCalledWith("new value");
@@ -95,10 +95,10 @@ describe("ValueText", () => {
95
95
  it("should not call onEdit if the edition is cancelled", async () => {
96
96
  const { onEdit } = renderComponent({ value: DummyValue, editable: true });
97
97
 
98
- await userEvent.click(screen.getByTestId("EditIcon"));
98
+ await userEvent.click(screen.getByRole("button", { name: /edit/i }));
99
99
  await userEvent.clear(screen.getByRole("textbox"));
100
100
  await userEvent.type(screen.getByRole("textbox"), "new value");
101
- await userEvent.click(screen.getByTestId("ClearIcon"));
101
+ await userEvent.click(screen.getByRole("button", { name: /cancel/i }));
102
102
 
103
103
  expect(onEdit).not.toHaveBeenCalled();
104
104
  });
@@ -106,13 +106,24 @@ describe("ValueText", () => {
106
106
  it("should have the original value if is edited again after clear a change", async () => {
107
107
  renderComponent({ value: DummyValue, editable: true });
108
108
 
109
- await userEvent.click(screen.getByTestId("EditIcon"));
109
+ await userEvent.click(screen.getByRole("button", { name: /edit/i }));
110
110
  await userEvent.clear(screen.getByRole("textbox"));
111
111
  await userEvent.type(screen.getByRole("textbox"), "new value");
112
- await userEvent.click(screen.getByTestId("ClearIcon"));
113
- await userEvent.click(screen.getByTestId("EditIcon"));
112
+ await userEvent.click(screen.getByRole("button", { name: /cancel/i }));
113
+ await userEvent.click(screen.getByRole("button", { name: /edit/i }));
114
114
 
115
115
  expect(screen.getByRole("textbox")).toHaveValue(DummyValue);
116
116
  });
117
+
118
+ it("should call onEdit if the enter button is pressed", async () => {
119
+ const { onEdit } = renderComponent({ value: DummyValue, editable: true });
120
+
121
+ await userEvent.click(screen.getByRole("button", { name: /edit/i }));
122
+ await userEvent.clear(screen.getByRole("textbox"));
123
+ await userEvent.type(screen.getByRole("textbox"), "new value{enter}");
124
+
125
+ expect(onEdit).toHaveBeenCalledTimes(1);
126
+ expect(onEdit).toHaveBeenCalledWith("new value");
127
+ });
117
128
  });
118
129
  });
@@ -1,14 +1,14 @@
1
- import { IconButton, TextField, Typography } from "@mui/material";
2
- import React from "react";
1
+ import { Typography, TextField } from "@mui/material";
2
+ import React, { useEffect, useRef } from "react";
3
3
  import {
4
4
  BaseValueProps,
5
5
  DefaultPlaceholder,
6
6
  EditableValueProps,
7
7
  useEditableValueDisplay,
8
+ ValueEditButton,
8
9
  ValueEditButtons,
9
10
  } from "../value-base";
10
11
  import { getValueContentLabelId, ValueContent } from "../value-content";
11
- import EditIcon from "@mui/icons-material/Edit";
12
12
 
13
13
  export type ValueTextProps = BaseValueProps<string | number> & EditableValueProps<string>;
14
14
 
@@ -23,30 +23,43 @@ export const ValueText = ({
23
23
  dense,
24
24
  onEdit = () => null,
25
25
  }: ValueTextProps) => {
26
+ const editInputRef = useRef<HTMLInputElement>(null);
26
27
  const { isEditing, editValue, startEdit, cancelEdit, setEditValue, submitEdit } =
27
28
  useEditableValueDisplay(valueProp?.toString(), onEdit);
28
29
  const id = getValueContentLabelId(label);
29
30
  const value = valueProp?.toString() || placeholder;
30
31
 
32
+ const editKeyPressListener = (e: KeyboardEvent) => {
33
+ if (e.key === "Enter") {
34
+ onEdit((e.target as any).value);
35
+ }
36
+ };
37
+
38
+ useEffect(() => {
39
+ editInputRef.current?.addEventListener("keypress", editKeyPressListener);
40
+ return () => editInputRef.current?.removeEventListener("keypress", editKeyPressListener);
41
+ }, [editInputRef.current]);
42
+
31
43
  return (
32
- <ValueContent label={label} tooltip={value} dense={dense}>
44
+ <ValueContent hideLabel={isEditing} label={label} tooltip={value} dense={dense}>
33
45
  {isEditing ? (
34
46
  <TextField
47
+ inputRef={editInputRef}
35
48
  value={editValue}
49
+ label={label}
36
50
  size="small"
37
51
  onChange={(e) => setEditValue(e.target.value)}
38
52
  InputProps={{
39
- endAdornment: <ValueEditButtons onClickCancel={cancelEdit} onSubmitEdit={submitEdit} />,
53
+ endAdornment: (
54
+ <ValueEditButtons onClickCancel={cancelEdit} onClickSubmit={submitEdit} />
55
+ ),
40
56
  }}
57
+ sx={{ marginY: !dense ? 1 : 0 }}
41
58
  />
42
59
  ) : (
43
60
  <Typography variant={dense ? "body1" : "h5"} noWrap aria-labelledby={id}>
44
61
  {value}
45
- {editable && (
46
- <IconButton size="small" onClick={startEdit} sx={{ ml: 1 }}>
47
- <EditIcon />
48
- </IconButton>
49
- )}
62
+ {editable && <ValueEditButton dense={dense} onClick={startEdit} />}
50
63
  </Typography>
51
64
  )}
52
65
  </ValueContent>
@@ -115,5 +115,5 @@ describe("ModelForm", () => {
115
115
  codes: ["foo", "bar"],
116
116
  identifiers: ["1", "2", "3"],
117
117
  });
118
- });
118
+ }, 20000);
119
119
  });
@@ -711,7 +711,7 @@ describe("ModelRouter", () => {
711
711
  const newInstance = await actions.fullfillModelForm({ model, submit: false });
712
712
 
713
713
  expectModelFieldInputValue(model.fields, newInstance);
714
- });
714
+ }, 20000);
715
715
 
716
716
  it("would make a request when the form is submitted", async () => {
717
717
  const { onSubmitNewItem, model } = await renderComponent({ screen: "add" });
@@ -721,7 +721,7 @@ describe("ModelRouter", () => {
721
721
  expectToHaveBeenCalledOnceWithMockInstance(onSubmitNewItem, {
722
722
  ...newInstance,
723
723
  });
724
- });
724
+ }, 20000);
725
725
 
726
726
  it("would show a loading indicator when the request is in progress", async () => {
727
727
  const { model } = await renderComponent({ screen: "add" });
@@ -729,7 +729,7 @@ describe("ModelRouter", () => {
729
729
  await actions.fullfillModelForm({ model, submit: true });
730
730
 
731
731
  expectProgressIndicator();
732
- });
732
+ }, 20000);
733
733
 
734
734
  it("would show a success message if the request finish with a success", async () => {
735
735
  renderAddScreen();
@@ -1,7 +1,7 @@
1
1
  import React, { ReactElement } from "react";
2
2
  import { Meta, StoryObj } from "@storybook/react";
3
3
  import { HeaderLayout, HeaderLayoutProps } from "./header-layout";
4
- import { withMemoryRouter } from "~/storybook";
4
+ import { withLocalizationProvider, withMemoryRouter } from "~/storybook";
5
5
  import { withFullHeight } from "../../storybook";
6
6
  import { Content, Header, HeaderProps, HeaderTab, SkeletonGrid, TabPanel } from "../../components";
7
7
  import { Box, Typography } from "@mui/material";
@@ -64,7 +64,7 @@ const DummyHeaderLayout = ({ headerProps, contentChildren, ...rest }: HeaderLayo
64
64
  export default {
65
65
  title: "Layouts/HeaderLayout",
66
66
  component: DummyHeaderLayout,
67
- decorators: [withMemoryRouter(), withFullHeight],
67
+ decorators: [withMemoryRouter(), withFullHeight, withLocalizationProvider],
68
68
  parameters: {
69
69
  layout: "fullscreen",
70
70
  },
@@ -1,12 +1,6 @@
1
1
  import { Box, LinearProgress } from "@mui/material";
2
2
  import React from "react";
3
- import {
4
- ContentElement,
5
- HeaderElement,
6
- Placeholder,
7
- PlaceholderIcon,
8
- PlaceholderIconArgs,
9
- } from "../../components";
3
+ import { ContentElement, HeaderElement, Placeholder, PlaceholderIcon } from "../../components";
10
4
  import { LoadingArea } from "../../components/feedback/loading-area";
11
5
  import { TabProvider } from "../../providers";
12
6
  import ReportProblemIcon from "@mui/icons-material/ReportProblem";