@pautena/react-design-system 0.7.2 → 0.7.4
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 +24 -17
- package/src/components/feedback/query-container/query-container.stories.tsx +19 -6
- package/src/components/feedback/query-container/query-container.test.tsx +65 -17
- package/src/components/feedback/query-container/query-container.tsx +20 -5
- package/src/components/inputs/autocomplete/autocomplete.stories.tsx +8 -0
- package/src/components/inputs/index.ts +1 -0
- package/src/components/inputs/text-field/text-field.stories.tsx +8 -0
- package/src/components/inputs/text-field/text-field.tsx +2 -2
- 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
|
@@ -6,47 +6,95 @@ import { expectProgressIndicator } from "~/tests/assertions";
|
|
|
6
6
|
|
|
7
7
|
describe("QueryContainer", () => {
|
|
8
8
|
const renderComponent = ({
|
|
9
|
-
|
|
9
|
+
fetching = false,
|
|
10
|
+
loading = false,
|
|
10
11
|
error = undefined,
|
|
11
12
|
success = undefined,
|
|
12
13
|
}: {
|
|
13
|
-
|
|
14
|
+
fetching?: boolean | boolean[];
|
|
15
|
+
loading?: boolean | boolean[];
|
|
14
16
|
error?: QueryContainerError;
|
|
15
17
|
success?: QueryContainerSuccess;
|
|
16
18
|
} = {}) => {
|
|
17
19
|
return render(
|
|
18
|
-
<QueryContainer
|
|
20
|
+
<QueryContainer fetching={fetching} loading={loading} error={error} success={success}>
|
|
19
21
|
<Typography>children content</Typography>
|
|
20
22
|
</QueryContainer>,
|
|
21
23
|
);
|
|
22
24
|
};
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
describe("fetching", () => {
|
|
27
|
+
it("should render a loading indicator if fetching=true", () => {
|
|
28
|
+
renderComponent({ fetching: true });
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
expectProgressIndicator();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should render a loading indicator if fetching is an array with a true value", () => {
|
|
34
|
+
renderComponent({ fetching: [false, false, true, false, false] });
|
|
35
|
+
|
|
36
|
+
expectProgressIndicator();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should render the children if fetching=true", () => {
|
|
40
|
+
renderComponent({ fetching: true });
|
|
41
|
+
|
|
42
|
+
expect(screen.getByText(/children content/i)).toBeVisible();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should render the children if fetching is an array with a true value", () => {
|
|
46
|
+
renderComponent({ fetching: [false, false, true, false, false] });
|
|
47
|
+
|
|
48
|
+
expect(screen.getByText(/children content/i)).toBeVisible();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("loading", () => {
|
|
53
|
+
it("should render a loading indicator if loading=true", () => {
|
|
54
|
+
renderComponent({ loading: true });
|
|
55
|
+
|
|
56
|
+
expectProgressIndicator();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should render a loading indicator if loading is an array with a true value", () => {
|
|
60
|
+
renderComponent({ loading: [false, false, true, false, false] });
|
|
61
|
+
|
|
62
|
+
expectProgressIndicator();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("shouldn't render the children", () => {
|
|
66
|
+
renderComponent({ loading: true });
|
|
67
|
+
|
|
68
|
+
expect(screen.queryByText(/children content/i)).not.toBeInTheDocument();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("shouldn't render the children if fetching is an array with a true value", () => {
|
|
72
|
+
renderComponent({ loading: [false, false, true, false, false] });
|
|
73
|
+
|
|
74
|
+
expect(screen.queryByText(/children content/i)).not.toBeInTheDocument();
|
|
75
|
+
});
|
|
28
76
|
});
|
|
29
77
|
|
|
30
78
|
describe("error", () => {
|
|
31
|
-
it("
|
|
79
|
+
it("should render an error alert", () => {
|
|
32
80
|
renderComponent({ error: { message: "Internal error" } });
|
|
33
81
|
|
|
34
82
|
expect(screen.getByRole("alert")).toHaveAttribute("aria-describedby", "error");
|
|
35
83
|
});
|
|
36
84
|
|
|
37
|
-
it("
|
|
85
|
+
it("should render the error message if error is defined", () => {
|
|
38
86
|
renderComponent({ error: { message: "Internal error" } });
|
|
39
87
|
|
|
40
88
|
expect(screen.getByText(/internal error/i)).toBeInTheDocument();
|
|
41
89
|
});
|
|
42
90
|
|
|
43
|
-
it("
|
|
91
|
+
it("should render the error title if error.name is defined", () => {
|
|
44
92
|
renderComponent({ error: { name: "Internal error", message: "There was an error" } });
|
|
45
93
|
|
|
46
94
|
expect(screen.getByRole("heading", { name: /internal error/i })).toBeInTheDocument();
|
|
47
95
|
});
|
|
48
96
|
|
|
49
|
-
it("
|
|
97
|
+
it("shouldn't render the error title if error is defined but the error.name not", () => {
|
|
50
98
|
renderComponent({ error: { message: "Internal error" } });
|
|
51
99
|
|
|
52
100
|
expect(screen.queryByRole("heading")).not.toBeInTheDocument();
|
|
@@ -54,19 +102,19 @@ describe("QueryContainer", () => {
|
|
|
54
102
|
});
|
|
55
103
|
|
|
56
104
|
describe("success", () => {
|
|
57
|
-
it("
|
|
105
|
+
it("should render a success alert", () => {
|
|
58
106
|
renderComponent({ success: { message: "Item added" } });
|
|
59
107
|
|
|
60
108
|
expect(screen.getByRole("alert")).toHaveAttribute("aria-describedby", "success");
|
|
61
109
|
});
|
|
62
110
|
|
|
63
|
-
it("
|
|
111
|
+
it("should render the success message if success is defined", () => {
|
|
64
112
|
renderComponent({ success: { message: "Item added" } });
|
|
65
113
|
|
|
66
114
|
expect(screen.getByText(/item added/i)).toBeInTheDocument();
|
|
67
115
|
});
|
|
68
116
|
|
|
69
|
-
it("
|
|
117
|
+
it("should render the success title if success.name is defined", () => {
|
|
70
118
|
renderComponent({
|
|
71
119
|
success: { name: "Item added", message: "The item has been added successfully" },
|
|
72
120
|
});
|
|
@@ -74,22 +122,22 @@ describe("QueryContainer", () => {
|
|
|
74
122
|
expect(screen.getByRole("heading", { name: /item added/i })).toBeInTheDocument();
|
|
75
123
|
});
|
|
76
124
|
|
|
77
|
-
it("
|
|
125
|
+
it("shouldn't render the error title if success is defined but the success.name not", () => {
|
|
78
126
|
renderComponent({ success: { message: "Item added" } });
|
|
79
127
|
|
|
80
128
|
expect(screen.queryByRole("heading")).not.toBeInTheDocument();
|
|
81
129
|
});
|
|
82
130
|
//TODO validate the severity of the alert
|
|
83
131
|
|
|
84
|
-
it("
|
|
132
|
+
it("should render the content", () => {
|
|
85
133
|
renderComponent({ success: { message: "Item added" } });
|
|
86
134
|
|
|
87
135
|
expect(screen.getByText(/children content/i)).toBeInTheDocument();
|
|
88
136
|
});
|
|
89
137
|
});
|
|
90
138
|
|
|
91
|
-
it("
|
|
92
|
-
renderComponent({
|
|
139
|
+
it("should render the children if isFetching is false and there is no defined error", () => {
|
|
140
|
+
renderComponent({ fetching: false, error: undefined });
|
|
93
141
|
|
|
94
142
|
expect(screen.getByText(/children content/i)).toBeInTheDocument();
|
|
95
143
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Alert, AlertTitle, Box } from "@mui/material";
|
|
1
|
+
import { Alert, AlertTitle, Box, LinearProgress } from "@mui/material";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { PropsWithChildren } from "react";
|
|
4
4
|
import { LoadingArea } from "../loading-area";
|
|
@@ -27,9 +27,14 @@ export interface QueryContainerSuccess {
|
|
|
27
27
|
|
|
28
28
|
export type QueryContainerProps = PropsWithChildren<{
|
|
29
29
|
/**
|
|
30
|
-
* There is a query in progress
|
|
30
|
+
* There is a query in progress and we have available data
|
|
31
31
|
*/
|
|
32
|
-
|
|
32
|
+
fetching?: boolean | boolean[];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* There is a query in progress and we don't have available data
|
|
36
|
+
*/
|
|
37
|
+
loading?: boolean | boolean[];
|
|
33
38
|
/**
|
|
34
39
|
* The query has returned an error
|
|
35
40
|
*/
|
|
@@ -43,8 +48,17 @@ export type QueryContainerProps = PropsWithChildren<{
|
|
|
43
48
|
/**
|
|
44
49
|
* Component to show different indicators based on the usual api query statuses
|
|
45
50
|
*/
|
|
46
|
-
export function QueryContainer({
|
|
47
|
-
|
|
51
|
+
export function QueryContainer({
|
|
52
|
+
fetching: fetchingProp = false,
|
|
53
|
+
loading: loadingProp = false,
|
|
54
|
+
error,
|
|
55
|
+
success,
|
|
56
|
+
children,
|
|
57
|
+
}: QueryContainerProps) {
|
|
58
|
+
const fetching = Array.isArray(fetchingProp) ? fetchingProp.some((f) => f) : fetchingProp;
|
|
59
|
+
const loading = Array.isArray(loadingProp) ? loadingProp.some((f) => f) : loadingProp;
|
|
60
|
+
|
|
61
|
+
if (loading) {
|
|
48
62
|
return <LoadingArea />;
|
|
49
63
|
}
|
|
50
64
|
|
|
@@ -65,6 +79,7 @@ export function QueryContainer({ isFetching, error, success, children }: QueryCo
|
|
|
65
79
|
{success.message}
|
|
66
80
|
</Alert>
|
|
67
81
|
)}
|
|
82
|
+
{fetching && <LinearProgress sx={{ width: 1, mb: 1 }} />}
|
|
68
83
|
{children}
|
|
69
84
|
</Box>
|
|
70
85
|
);
|
|
@@ -53,6 +53,14 @@ export const Fetching: Story = {
|
|
|
53
53
|
},
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
+
export const LoadingAndFetching: Story = {
|
|
57
|
+
args: {
|
|
58
|
+
...Default.args,
|
|
59
|
+
fetching: true,
|
|
60
|
+
loading: true,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
56
64
|
export const FetchingWithEndAdornment: Story = {
|
|
57
65
|
args: {
|
|
58
66
|
...Default.args,
|
|
@@ -83,13 +83,13 @@ export const TextField = ({
|
|
|
83
83
|
endAdornment={
|
|
84
84
|
<InputAdornment position="end">
|
|
85
85
|
{InputProps?.endAdornment}
|
|
86
|
-
{
|
|
86
|
+
{loading ? <CircularProgress color="inherit" size={20} sx={{ ml: 1 }} /> : null}
|
|
87
87
|
</InputAdornment>
|
|
88
88
|
}
|
|
89
89
|
sx={sx}
|
|
90
90
|
{...(rest as any)}
|
|
91
91
|
/>
|
|
92
|
-
{loading && (
|
|
92
|
+
{fetching && !loading && (
|
|
93
93
|
<LinearProgress
|
|
94
94
|
color="inherit"
|
|
95
95
|
sx={{ position: "absolute", left: 0, right: 0, bottom: 0 }}
|
|
@@ -4,37 +4,57 @@ import { ValueRating } from "../value-rating";
|
|
|
4
4
|
import { ValueText } from "../value-text";
|
|
5
5
|
import { GroupValueCard, GroupValueCardProps } from "./group-value-card";
|
|
6
6
|
import { ValueItem } from "../value-item";
|
|
7
|
+
import { ValueDatetime } from "../value-datetime";
|
|
7
8
|
|
|
8
|
-
export const GroupValueCardDummy = ({
|
|
9
|
+
export const GroupValueCardDummy = ({
|
|
10
|
+
dense,
|
|
11
|
+
editable,
|
|
12
|
+
...rest
|
|
13
|
+
}: GroupValueCardProps & { editable?: boolean }) => {
|
|
9
14
|
return (
|
|
10
15
|
<GroupValueCard {...rest} dense={dense}>
|
|
11
16
|
<ValueItem xs={12} sm={6} md={4}>
|
|
12
|
-
<ValueText
|
|
17
|
+
<ValueText
|
|
18
|
+
editable={editable}
|
|
19
|
+
dense={dense}
|
|
20
|
+
label="Hello world"
|
|
21
|
+
value="Lorem ipsum sit amet"
|
|
22
|
+
/>
|
|
13
23
|
</ValueItem>
|
|
14
24
|
<ValueItem xs={12} sm={6} md={2}>
|
|
15
|
-
<ValueBoolean dense={dense} label="Enabled" value />
|
|
25
|
+
<ValueBoolean editable={editable} dense={dense} label="Enabled" value />
|
|
16
26
|
</ValueItem>
|
|
17
27
|
<ValueItem xs={12} sm={6} md={3}>
|
|
18
|
-
<ValueText dense={dense} label="Quantity" value="1200" />
|
|
28
|
+
<ValueText editable={editable} dense={dense} label="Quantity" value="1200" />
|
|
19
29
|
</ValueItem>
|
|
20
30
|
<ValueItem xs={12} sm={6} md={3}>
|
|
21
|
-
<ValueText dense={dense} label="Currency" value="EUR" />
|
|
31
|
+
<ValueText editable={editable} dense={dense} label="Currency" value="EUR" />
|
|
22
32
|
</ValueItem>
|
|
23
33
|
<ValueItem xs={12} sm={6} md={6}>
|
|
24
34
|
<ValueText
|
|
35
|
+
editable={editable}
|
|
25
36
|
dense={dense}
|
|
26
37
|
label="I am Batman"
|
|
27
38
|
value=" Does it come in black? It's ends here. Hero can be anyone"
|
|
28
39
|
/>
|
|
29
40
|
</ValueItem>
|
|
30
41
|
<ValueItem xs={12} sm={6} md={3}>
|
|
31
|
-
<ValueText dense={dense} label="Status" value="Open" />
|
|
42
|
+
<ValueText editable={editable} dense={dense} label="Status" value="Open" />
|
|
32
43
|
</ValueItem>
|
|
33
44
|
<ValueItem xs={12} sm={6} md={3}>
|
|
34
|
-
<ValueText dense={dense} label="Level" value="2144" />
|
|
45
|
+
<ValueText editable={editable} dense={dense} label="Level" value="2144" />
|
|
35
46
|
</ValueItem>
|
|
36
47
|
<ValueItem xs={12} sm={6} md={3}>
|
|
37
|
-
<ValueRating dense={dense} label="Rating" value={3} />
|
|
48
|
+
<ValueRating editable={editable} dense={dense} label="Rating" value={3} />
|
|
49
|
+
</ValueItem>
|
|
50
|
+
<ValueItem xs={12} sm={6} md={3}>
|
|
51
|
+
<ValueDatetime
|
|
52
|
+
editable={editable}
|
|
53
|
+
dense={dense}
|
|
54
|
+
label="Created at"
|
|
55
|
+
value={new Date(2022, 5, 1)}
|
|
56
|
+
format="yyyy-MM-dd"
|
|
57
|
+
/>
|
|
38
58
|
</ValueItem>
|
|
39
59
|
</GroupValueCard>
|
|
40
60
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Meta, StoryObj } from "@storybook/react";
|
|
2
2
|
import { GroupValueCard } from "./group-value-card";
|
|
3
|
-
import { withPadding } from "../../../storybook";
|
|
3
|
+
import { withPadding, withLocalizationProvider } from "../../../storybook";
|
|
4
4
|
import { GroupValueCardDummy } from "./group-value-card.mock";
|
|
5
5
|
import { ValueImage } from "../value-image";
|
|
6
6
|
import workInProgressImg from "../../../stories/assets/work-in-progress.jpg";
|
|
@@ -12,7 +12,7 @@ import { ValueItem } from "../value-item";
|
|
|
12
12
|
export default {
|
|
13
13
|
title: "Components/Value displays/GroupValueCard",
|
|
14
14
|
component: GroupValueCardDummy,
|
|
15
|
-
decorators: [withPadding(2)],
|
|
15
|
+
decorators: [withPadding(2), withLocalizationProvider],
|
|
16
16
|
parameters: {
|
|
17
17
|
layout: "fullscreen",
|
|
18
18
|
},
|
|
@@ -34,6 +34,23 @@ export const Dense: Story = {
|
|
|
34
34
|
},
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
+
export const EditableDense: Story = {
|
|
38
|
+
args: {
|
|
39
|
+
title: "Hello world",
|
|
40
|
+
subtitle: "Lorem ipsum sit amet",
|
|
41
|
+
dense: true,
|
|
42
|
+
editable: true,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const Editable: Story = {
|
|
47
|
+
args: {
|
|
48
|
+
title: "Hello world",
|
|
49
|
+
subtitle: "Lorem ipsum sit amet",
|
|
50
|
+
editable: true,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
37
54
|
export const WihtoutSubtitle: Story = {
|
|
38
55
|
args: {
|
|
39
56
|
title: "Hello world",
|
|
@@ -17,7 +17,7 @@ describe("GroupValueCard", () => {
|
|
|
17
17
|
it("would render a title", () => {
|
|
18
18
|
renderComponent({ title: "Hello world" });
|
|
19
19
|
|
|
20
|
-
expect(screen.getByRole("heading", { level: 1 })).
|
|
20
|
+
expect(screen.getByRole("heading", { level: 1 })).toBeVisible();
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
describe("subtitle", () => {
|
|
@@ -30,34 +30,32 @@ describe("GroupValueCard", () => {
|
|
|
30
30
|
it("would render a subtitle if it's provided", () => {
|
|
31
31
|
renderComponent({ subtitle: "Hi. I am batman" });
|
|
32
32
|
|
|
33
|
-
expect(
|
|
34
|
-
screen.getByRole("heading", { level: 2, name: /hi. i am batman/i }),
|
|
35
|
-
).toBeInTheDocument();
|
|
33
|
+
expect(screen.getByRole("heading", { level: 2, name: /hi. i am batman/i })).toBeVisible();
|
|
36
34
|
});
|
|
37
35
|
});
|
|
38
36
|
|
|
39
37
|
it("would render all values", () => {
|
|
40
38
|
renderComponent();
|
|
41
39
|
|
|
42
|
-
expect(screen.getByRole("label", { name: /hello world/i })).
|
|
43
|
-
expect(screen.getByText(/lorem ipsum sit amet/i)).
|
|
40
|
+
expect(screen.getByRole("label", { name: /hello world/i })).toBeVisible();
|
|
41
|
+
expect(screen.getByText(/lorem ipsum sit amet/i)).toBeVisible();
|
|
44
42
|
|
|
45
|
-
expect(screen.getByRole("label", { name: /enabled/i })).
|
|
46
|
-
expect(screen.
|
|
43
|
+
expect(screen.getByRole("label", { name: /enabled/i })).toBeVisible();
|
|
44
|
+
expect(screen.getByLabelText(/enabled/i)).toBeChecked();
|
|
47
45
|
|
|
48
|
-
expect(screen.getByRole("label", { name: /quantity/i })).
|
|
49
|
-
expect(screen.getByText(/1200/i)).
|
|
46
|
+
expect(screen.getByRole("label", { name: /quantity/i })).toBeVisible();
|
|
47
|
+
expect(screen.getByText(/1200/i)).toBeVisible();
|
|
50
48
|
|
|
51
|
-
expect(screen.getByRole("label", { name: /currency/i })).
|
|
52
|
-
expect(screen.getByText(/eur/i)).
|
|
49
|
+
expect(screen.getByRole("label", { name: /currency/i })).toBeVisible();
|
|
50
|
+
expect(screen.getByText(/eur/i)).toBeVisible();
|
|
53
51
|
|
|
54
|
-
expect(screen.getByRole("label", { name: /i am batman/i })).
|
|
55
|
-
expect(screen.getByText(/does it come in black/i)).
|
|
52
|
+
expect(screen.getByRole("label", { name: /i am batman/i })).toBeVisible();
|
|
53
|
+
expect(screen.getByText(/does it come in black/i)).toBeVisible();
|
|
56
54
|
|
|
57
|
-
expect(screen.getByRole("label", { name: /status/i })).
|
|
58
|
-
expect(screen.getByText(/open/i)).
|
|
55
|
+
expect(screen.getByRole("label", { name: /status/i })).toBeVisible();
|
|
56
|
+
expect(screen.getByText(/open/i)).toBeVisible();
|
|
59
57
|
|
|
60
|
-
expect(screen.getByRole("label", { name: /level/i })).
|
|
61
|
-
expect(screen.getByText(/2144/i)).
|
|
58
|
+
expect(screen.getByRole("label", { name: /level/i })).toBeVisible();
|
|
59
|
+
expect(screen.getByText(/2144/i)).toBeVisible();
|
|
62
60
|
});
|
|
63
61
|
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { render, screen } from "~/tests/testing-library";
|
|
2
|
+
import { ValueEditButton, ValueEditButtons } from "./value-edit";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import userEvent from "@testing-library/user-event";
|
|
5
|
+
import { vi } from "vitest";
|
|
6
|
+
|
|
7
|
+
describe("ValueEditButton", () => {
|
|
8
|
+
const renderComponent = () => {
|
|
9
|
+
const onClick = vi.fn();
|
|
10
|
+
render(<ValueEditButton onClick={onClick} />);
|
|
11
|
+
|
|
12
|
+
return { onClick };
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
it("should render a edit icon", () => {
|
|
16
|
+
renderComponent();
|
|
17
|
+
|
|
18
|
+
expect(screen.getByTestId("EditIcon")).toBeVisible();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("should be accessible", () => {
|
|
22
|
+
renderComponent();
|
|
23
|
+
|
|
24
|
+
expect(screen.getByRole("button", { name: /edit button/i })).toBeVisible();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should call onClick when the button is clicked", async () => {
|
|
28
|
+
const { onClick } = renderComponent();
|
|
29
|
+
|
|
30
|
+
await userEvent.click(screen.getByRole("button", { name: /edit button/i }));
|
|
31
|
+
|
|
32
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("ValueEditButtons", () => {
|
|
37
|
+
const renderComponent = () => {
|
|
38
|
+
const onClickCancel = vi.fn();
|
|
39
|
+
const onClickSubmit = vi.fn();
|
|
40
|
+
render(<ValueEditButtons onClickCancel={onClickCancel} onClickSubmit={onClickSubmit} />);
|
|
41
|
+
|
|
42
|
+
return { onClickCancel, onClickSubmit };
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
describe("cancel button", () => {
|
|
46
|
+
it("should render a cancel icon", () => {
|
|
47
|
+
renderComponent();
|
|
48
|
+
|
|
49
|
+
expect(screen.getByTestId("ClearIcon")).toBeVisible();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should be accessible", () => {
|
|
53
|
+
renderComponent();
|
|
54
|
+
|
|
55
|
+
expect(screen.getByRole("button", { name: /cancel button/i })).toBeVisible();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("should call onClickCancel when the button is clicked", async () => {
|
|
59
|
+
const { onClickCancel } = renderComponent();
|
|
60
|
+
|
|
61
|
+
await userEvent.click(screen.getByRole("button", { name: /cancel button/i }));
|
|
62
|
+
|
|
63
|
+
expect(onClickCancel).toHaveBeenCalledTimes(1);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe("submit button", () => {
|
|
68
|
+
it("should render a submit icon", () => {
|
|
69
|
+
renderComponent();
|
|
70
|
+
|
|
71
|
+
expect(screen.getByTestId("CheckIcon")).toBeVisible();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("should be accessible", () => {
|
|
75
|
+
renderComponent();
|
|
76
|
+
|
|
77
|
+
expect(screen.getByRole("button", { name: /submit button/i })).toBeVisible();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("should call onClickCancel when the button is clicked", async () => {
|
|
81
|
+
const { onClickSubmit } = renderComponent();
|
|
82
|
+
|
|
83
|
+
await userEvent.click(screen.getByRole("button", { name: /submit button/i }));
|
|
84
|
+
|
|
85
|
+
expect(onClickSubmit).toHaveBeenCalledTimes(1);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
2
|
import CheckIcon from "@mui/icons-material/Check";
|
|
3
3
|
import ClearIcon from "@mui/icons-material/Clear";
|
|
4
|
-
import { Button, InputAdornment, SxProps, Theme } from "@mui/material";
|
|
4
|
+
import { Button, IconButton, InputAdornment, SxProps, Theme, useTheme } from "@mui/material";
|
|
5
|
+
import EditIcon from "@mui/icons-material/Edit";
|
|
5
6
|
|
|
6
7
|
export interface ValueEditButtonsProps {
|
|
7
8
|
onClickCancel: () => void;
|
|
8
|
-
|
|
9
|
+
onClickSubmit: () => void;
|
|
9
10
|
sx?: SxProps<Theme>;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
export const ValueEditButtons = ({ onClickCancel,
|
|
13
|
+
export const ValueEditButtons = ({ onClickCancel, onClickSubmit, sx }: ValueEditButtonsProps) => {
|
|
13
14
|
return (
|
|
14
15
|
<InputAdornment position="end" sx={sx}>
|
|
15
16
|
<Button
|
|
16
17
|
variant="contained"
|
|
17
18
|
size="small"
|
|
18
19
|
color="error"
|
|
19
|
-
|
|
20
|
+
aria-label="cancel button"
|
|
21
|
+
startIcon={<ClearIcon sx={{ fontSize: 12 }} />}
|
|
20
22
|
onClick={onClickCancel}
|
|
21
23
|
sx={{ paddingRight: 0, minWidth: 0, marginRight: 1 }}
|
|
22
24
|
/>
|
|
@@ -24,8 +26,9 @@ export const ValueEditButtons = ({ onClickCancel, onSubmitEdit, sx }: ValueEditB
|
|
|
24
26
|
variant="contained"
|
|
25
27
|
size="small"
|
|
26
28
|
color="primary"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
aria-label="submit button"
|
|
30
|
+
startIcon={<CheckIcon sx={{ fontSize: 12 }} />}
|
|
31
|
+
onClick={onClickSubmit}
|
|
29
32
|
sx={{ paddingRight: 0, minWidth: 0 }}
|
|
30
33
|
/>
|
|
31
34
|
</InputAdornment>
|
|
@@ -57,3 +60,22 @@ export const useEditableValueDisplay = <T,>(
|
|
|
57
60
|
|
|
58
61
|
return { isEditing, cancelEdit, editValue, setEditValue, startEdit, submitEdit };
|
|
59
62
|
};
|
|
63
|
+
|
|
64
|
+
export interface ValueEditButtonProps {
|
|
65
|
+
dense?: boolean;
|
|
66
|
+
onClick: () => void;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const ValueEditButton = ({ dense, onClick }: ValueEditButtonProps) => {
|
|
70
|
+
const { typography } = useTheme();
|
|
71
|
+
return (
|
|
72
|
+
<IconButton
|
|
73
|
+
size="small"
|
|
74
|
+
onClick={onClick}
|
|
75
|
+
sx={{ ml: dense ? 0.5 : 1 }}
|
|
76
|
+
aria-label="edit button"
|
|
77
|
+
>
|
|
78
|
+
<EditIcon sx={{ fontSize: typography.pxToRem(dense ? 18 : 24) }} />
|
|
79
|
+
</IconButton>
|
|
80
|
+
);
|
|
81
|
+
};
|
|
@@ -31,6 +31,15 @@ export const Editable: Story = {
|
|
|
31
31
|
},
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
+
export const DenseEditable: Story = {
|
|
35
|
+
args: {
|
|
36
|
+
label: "Lorem ipsum",
|
|
37
|
+
value: false,
|
|
38
|
+
dense: true,
|
|
39
|
+
editable: true,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
34
43
|
export const Dense: Story = {
|
|
35
44
|
args: {
|
|
36
45
|
label: "Lorem ipsum",
|