@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.
- package/dist/cjs/index.js +4 -4
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/alerts/expandable-alert/expandable-alert.d.ts +4 -1
- package/dist/cjs/types/components/data-display/board/board.d.ts +1 -2
- package/dist/cjs/types/components/feedback/query-container/query-container.d.ts +7 -3
- package/dist/cjs/types/components/inputs/index.d.ts +1 -0
- package/dist/cjs/types/components/value-displays/group-value-card/group-value-card.mock.d.ts +3 -1
- package/dist/cjs/types/components/value-displays/value-base/value-edit.d.ts +7 -2
- package/dist/cjs/types/components/value-displays/value-content/value-content.d.ts +10 -1
- package/dist/esm/index.js +4 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/alerts/expandable-alert/expandable-alert.d.ts +4 -1
- package/dist/esm/types/components/data-display/board/board.d.ts +1 -2
- package/dist/esm/types/components/feedback/query-container/query-container.d.ts +7 -3
- package/dist/esm/types/components/inputs/index.d.ts +1 -0
- package/dist/esm/types/components/value-displays/group-value-card/group-value-card.mock.d.ts +3 -1
- package/dist/esm/types/components/value-displays/value-base/value-edit.d.ts +7 -2
- package/dist/esm/types/components/value-displays/value-content/value-content.d.ts +10 -1
- package/dist/index.d.ts +26 -9
- package/package.json +2 -1
- package/src/components/alerts/expandable-alert/expandable-alert.stories.tsx +23 -1
- package/src/components/alerts/expandable-alert/expandable-alert.tsx +11 -4
- package/src/components/data-display/board/board.test.tsx +60 -43
- package/src/components/data-display/board/board.tsx +13 -16
- package/src/components/feedback/query-container/query-container.stories.tsx +19 -6
- package/src/components/feedback/query-container/query-container.test.tsx +41 -17
- package/src/components/feedback/query-container/query-container.tsx +17 -5
- package/src/components/inputs/index.ts +1 -0
- package/src/components/value-displays/group-value-card/group-value-card.mock.tsx +28 -8
- package/src/components/value-displays/group-value-card/group-value-card.stories.tsx +19 -2
- package/src/components/value-displays/group-value-card/group-value-card.test.tsx +16 -18
- package/src/components/value-displays/value-base/value-edit.test.tsx +88 -0
- package/src/components/value-displays/value-base/value-edit.tsx +28 -6
- package/src/components/value-displays/value-boolean/value-boolean.stories.tsx +9 -0
- package/src/components/value-displays/value-boolean/value-boolean.test.tsx +29 -15
- package/src/components/value-displays/value-boolean/value-boolean.tsx +18 -11
- package/src/components/value-displays/value-content/value-content.test.tsx +20 -6
- package/src/components/value-displays/value-content/value-content.tsx +24 -10
- package/src/components/value-displays/value-datetime/value-datetime.stories.tsx +11 -0
- package/src/components/value-displays/value-datetime/value-datetime.test.tsx +9 -9
- package/src/components/value-displays/value-datetime/value-datetime.tsx +14 -10
- package/src/components/value-displays/value-rating/value-rating.stories.tsx +10 -0
- package/src/components/value-displays/value-rating/value-rating.test.tsx +11 -11
- package/src/components/value-displays/value-rating/value-rating.tsx +10 -8
- package/src/components/value-displays/value-text/value-text.stories.tsx +9 -0
- package/src/components/value-displays/value-text/value-text.test.tsx +20 -9
- package/src/components/value-displays/value-text/value-text.tsx +23 -10
- package/src/generators/model-form/model-form.test.tsx +1 -1
- package/src/generators/model-router/model-router.test.tsx +3 -3
- package/src/layouts/header-layout/header-layout.stories.tsx +2 -2
- package/src/layouts/header-layout/header-layout.tsx +1 -7
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Box,
|
|
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
|
|
39
|
-
|
|
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
|
|
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 = ({
|
|
8
|
+
const renderComponent = ({
|
|
9
|
+
tooltip,
|
|
10
|
+
hideLabel,
|
|
11
|
+
}: { tooltip?: string; hideLabel?: boolean } = {}) => {
|
|
9
12
|
render(
|
|
10
|
-
<ValueContent
|
|
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 })).
|
|
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 })).
|
|
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)).
|
|
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)).
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
108
|
+
await userEvent.click(screen.getByRole("button", { name: /edit/i }));
|
|
109
109
|
pickDatetime(screen.getByRole("textbox"), newValue, fmt);
|
|
110
|
-
await userEvent.click(screen.
|
|
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.
|
|
120
|
+
await userEvent.click(screen.getByRole("button", { name: /edit/i }));
|
|
121
121
|
pickDatetime(screen.getByRole("textbox"), NewValue, datetimeFormat);
|
|
122
|
-
await userEvent.click(screen.
|
|
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.
|
|
130
|
+
await userEvent.click(screen.getByRole("button", { name: /edit/i }));
|
|
131
131
|
pickDatetime(screen.getByRole("textbox"), NewValue, datetimeFormat);
|
|
132
|
-
await userEvent.click(screen.
|
|
133
|
-
await userEvent.click(screen.
|
|
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,
|
|
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
|
|
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
|
-
|
|
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>
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
77
|
-
await userEvent.click(screen.
|
|
78
|
-
await userEvent.click(screen.
|
|
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.
|
|
87
|
+
await userEvent.click(screen.getByRole("button", { name: /edit/i }));
|
|
88
88
|
await userEvent.click(screen.getAllByRole("radio")[3]);
|
|
89
|
-
await userEvent.click(screen.
|
|
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.
|
|
98
|
-
await userEvent.click(screen.
|
|
99
|
-
await userEvent.click(screen.
|
|
100
|
-
await userEvent.click(screen.
|
|
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={(
|
|
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
|
-
|
|
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
|
);
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
113
|
-
await userEvent.click(screen.
|
|
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 {
|
|
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:
|
|
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>
|
|
@@ -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";
|