@pautena/react-design-system 0.1.2 → 0.1.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/package.json +1 -1
- package/src/components/app-bar/app-bar.stories.tsx +54 -0
- package/src/components/app-bar/app-bar.test.tsx +142 -0
- package/src/components/app-bar/app-bar.tsx +150 -0
- package/src/components/app-bar/app-bar.types.ts +17 -0
- package/src/components/app-bar/index.ts +3 -0
- package/src/components/app-bar/mini-app-bar/index.ts +1 -0
- package/src/components/app-bar/mini-app-bar/mini-app-bar.tsx +31 -0
- package/src/components/bullet/bullet.stories.tsx +43 -0
- package/src/components/bullet/bullet.test.tsx +24 -0
- package/src/components/bullet/bullet.tsx +30 -0
- package/src/components/bullet/index.ts +1 -0
- package/src/components/center-container/center-container.stories.tsx +50 -0
- package/src/components/center-container/center-container.test.tsx +16 -0
- package/src/components/center-container/center-container.tsx +32 -0
- package/src/components/center-container/index.ts +1 -0
- package/src/components/content/content.stories.tsx +23 -0
- package/src/components/content/content.test.tsx +26 -0
- package/src/components/content/content.tsx +11 -0
- package/src/components/content/content.types.ts +5 -0
- package/src/components/content/index.ts +2 -0
- package/src/components/drawer/__snapshots__/drawer.test.tsx.snap +20 -0
- package/src/components/drawer/drawer.context.ts +20 -0
- package/src/components/drawer/drawer.mixins.ts +24 -0
- package/src/components/drawer/drawer.mock.tsx +100 -0
- package/src/components/drawer/drawer.provider.tsx +23 -0
- package/src/components/drawer/drawer.test.tsx +97 -0
- package/src/components/drawer/drawer.tsx +30 -0
- package/src/components/drawer/drawer.types.ts +53 -0
- package/src/components/drawer/index.ts +5 -0
- package/src/components/drawer/mini-drawer/index.ts +1 -0
- package/src/components/drawer/mini-drawer/mini-drawer.stories.tsx +34 -0
- package/src/components/drawer/mini-drawer/mini-drawer.tsx +67 -0
- package/src/components/drawer-content/drawer-content.stories.tsx +29 -0
- package/src/components/drawer-content/drawer-content.test.tsx +34 -0
- package/src/components/drawer-content/drawer-content.tsx +18 -0
- package/src/components/drawer-content/index.ts +1 -0
- package/src/components/drawer-item/drawer-item.stories.tsx +62 -0
- package/src/components/drawer-item/drawer-item.test.tsx +119 -0
- package/src/components/drawer-item/drawer-item.tsx +71 -0
- package/src/components/drawer-item/index.ts +1 -0
- package/src/components/drawer-section/drawer-section.mock.tsx +39 -0
- package/src/components/drawer-section/drawer-section.stories.tsx +28 -0
- package/src/components/drawer-section/drawer-section.test.tsx +44 -0
- package/src/components/drawer-section/drawer-section.tsx +40 -0
- package/src/components/drawer-section/index.ts +1 -0
- package/src/components/header/header.dummy.ts +55 -0
- package/src/components/header/header.stories.tsx +116 -0
- package/src/components/header/header.test.tsx +169 -0
- package/src/components/header/header.tsx +121 -0
- package/src/components/header/header.types.ts +61 -0
- package/src/components/header/index.ts +2 -0
- package/src/components/index.ts +18 -0
- package/src/components/label/index.ts +1 -0
- package/src/components/label/label.stories.tsx +49 -0
- package/src/components/label/label.test.tsx +30 -0
- package/src/components/label/label.tsx +60 -0
- package/src/components/link/index.ts +1 -0
- package/src/components/link/link.tsx +17 -0
- package/src/components/loading-area/index.ts +1 -0
- package/src/components/loading-area/loading-area.stories.tsx +17 -0
- package/src/components/loading-area/loading-area.test.tsx +11 -0
- package/src/components/loading-area/loading-area.tsx +13 -0
- package/src/components/placeholder/index.ts +1 -0
- package/src/components/placeholder/placeholder.mock.ts +15 -0
- package/src/components/placeholder/placeholder.stories.tsx +44 -0
- package/src/components/placeholder/placeholder.test.tsx +76 -0
- package/src/components/placeholder/placeholder.tsx +75 -0
- package/src/components/query-container/index.ts +1 -0
- package/src/components/query-container/query-container.stories.tsx +68 -0
- package/src/components/query-container/query-container.test.tsx +95 -0
- package/src/components/query-container/query-container.tsx +71 -0
- package/src/components/sign-in/index.ts +1 -0
- package/src/components/sign-in/sign-in.stories.tsx +36 -0
- package/src/components/sign-in/sign-in.test.tsx +95 -0
- package/src/components/sign-in/sign-in.tsx +97 -0
- package/src/components/tab/index.ts +2 -0
- package/src/components/tab/tab-card/index.ts +1 -0
- package/src/components/tab/tab-card/tab-card.dummy.tsx +30 -0
- package/src/components/tab/tab-card/tab-card.stories.tsx +22 -0
- package/src/components/tab/tab-card/tab-card.test.tsx +53 -0
- package/src/components/tab/tab-card/tab-card.tsx +27 -0
- package/src/components/tab/tab-panel/index.ts +1 -0
- package/src/components/tab/tab-panel/tab-panel.test.tsx +26 -0
- package/src/components/tab/tab-panel/tab-panel.tsx +27 -0
- package/src/components/table/enhanced-remote-table/enhanced-remote-table.mock.tsx +27 -0
- package/src/components/table/enhanced-remote-table/enhanced-remote-table.stories.tsx +24 -0
- package/src/components/table/enhanced-remote-table/enhanced-remote-table.test.tsx +77 -0
- package/src/components/table/enhanced-remote-table/enhanced-remote-table.tsx +74 -0
- package/src/components/table/enhanced-remote-table/index.ts +1 -0
- package/src/components/table/enhanced-table/enhanced-table-head.tsx +58 -0
- package/src/components/table/enhanced-table/enhanced-table.mock.tsx +93 -0
- package/src/components/table/enhanced-table/enhanced-table.stories.tsx +21 -0
- package/src/components/table/enhanced-table/enhanced-table.test.tsx +107 -0
- package/src/components/table/enhanced-table/enhanced-table.tsx +136 -0
- package/src/components/table/enhanced-table/index.ts +2 -0
- package/src/components/table/index.ts +2 -0
- package/src/components/table-list/index.ts +1 -0
- package/src/components/table-list/table-list.stories.tsx +75 -0
- package/src/components/table-list/table-list.test.tsx +284 -0
- package/src/components/table-list/table-list.tsx +127 -0
- package/src/components/value-displays/group-value-card/group-value-card.mock.tsx +35 -0
- package/src/components/value-displays/group-value-card/group-value-card.stories.tsx +26 -0
- package/src/components/value-displays/group-value-card/group-value-card.test.tsx +58 -0
- package/src/components/value-displays/group-value-card/group-value-card.tsx +63 -0
- package/src/components/value-displays/group-value-card/index.ts +1 -0
- package/src/components/value-displays/index.ts +4 -0
- package/src/components/value-displays/value-boolean/index.ts +1 -0
- package/src/components/value-displays/value-boolean/value-boolean.stories.tsx +25 -0
- package/src/components/value-displays/value-boolean/value-boolean.test.tsx +27 -0
- package/src/components/value-displays/value-boolean/value-boolean.tsx +33 -0
- package/src/components/value-displays/value-card/index.ts +1 -0
- package/src/components/value-displays/value-card/value-card.stories.tsx +22 -0
- package/src/components/value-displays/value-card/value-card.test.tsx +18 -0
- package/src/components/value-displays/value-card/value-card.tsx +12 -0
- package/src/components/value-displays/value-text/index.ts +1 -0
- package/src/components/value-displays/value-text/value-test.test.tsx +21 -0
- package/src/components/value-displays/value-text/value-text.stories.tsx +26 -0
- package/src/components/value-displays/value-text/value-text.tsx +32 -0
- package/src/generators/generators.mock.ts +238 -0
- package/src/generators/generators.model.ts +46 -0
- package/src/generators/index.ts +4 -0
- package/src/generators/model-form/index.ts +1 -0
- package/src/generators/model-form/model-form.stories.tsx +30 -0
- package/src/generators/model-form/model-form.test.tsx +100 -0
- package/src/generators/model-form/model-form.tsx +97 -0
- package/src/generators/model-router/index.ts +1 -0
- package/src/generators/model-router/model-router.test.tsx +666 -0
- package/src/generators/model-router/model-router.tsx +29 -0
- package/src/generators/model-router/model-router.types.ts +14 -0
- package/src/generators/model-router/screens/add-screen.tsx +69 -0
- package/src/generators/model-router/screens/details-screen.tsx +62 -0
- package/src/generators/model-router/screens/index.ts +4 -0
- package/src/generators/model-router/screens/list-screen.tsx +110 -0
- package/src/generators/model-router/screens/screens.types.ts +13 -0
- package/src/generators/model-router/screens/update-screen.tsx +96 -0
- package/src/generators/model-router/stories/details-screen.stories.tsx +38 -0
- package/src/generators/model-router/stories/list-screen.stories.tsx +45 -0
- package/src/generators/model-router/stories/model-router.stories.tsx +164 -0
- package/src/generators/model-router/stories/templates.tsx +39 -0
- package/src/generators/object-details/index.ts +1 -0
- package/src/generators/object-details/object-details.stories.tsx +20 -0
- package/src/generators/object-details/object-details.test.tsx +21 -0
- package/src/generators/object-details/object-details.tsx +76 -0
- package/src/index.ts +4 -0
- package/src/layouts/app-bar-with-drawer-layout/app-bar-with-drawer-layout.stories.tsx +28 -0
- package/src/layouts/app-bar-with-drawer-layout/app-bar-with-drawer-layout.test.tsx +30 -0
- package/src/layouts/app-bar-with-drawer-layout/app-bar-with-drawer-layout.tsx +37 -0
- package/src/layouts/app-bar-with-drawer-layout/index.ts +1 -0
- package/src/layouts/header-layout/header-layout.stories.tsx +204 -0
- package/src/layouts/header-layout/header-layout.test.tsx +37 -0
- package/src/layouts/header-layout/header-layout.tsx +23 -0
- package/src/layouts/header-layout/index.ts +1 -0
- package/src/layouts/index.ts +2 -0
- package/src/providers/index.ts +2 -0
- package/src/providers/notification-center/index.ts +2 -0
- package/src/providers/notification-center/notification-center.context.ts +37 -0
- package/src/providers/notification-center/notification-center.provider.tsx +51 -0
- package/src/providers/notification-center/notification-center.stories.tsx +52 -0
- package/src/providers/notification-center/notification-center.test.tsx +112 -0
- package/src/providers/tab-provider/index.ts +2 -0
- package/src/providers/tab-provider/tab-provider.context.ts +8 -0
- package/src/providers/tab-provider/tab-provider.provider.tsx +13 -0
- package/src/storybook.tsx +90 -0
- package/src/tests/assertions.ts +76 -0
- package/src/tests/components.tsx +60 -0
- package/src/tests/content-placeholder.stories.tsx +16 -0
- package/src/tests/index.ts +3 -0
- package/src/tests/skeleton-card.stories.tsx +18 -0
- package/src/tests/testing-library.tsx +65 -0
- package/src/utils/arrays.test.ts +9 -0
- package/src/utils/arrays.ts +7 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/theme.ts +11 -0
- package/.prettierrc.js +0 -5
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
Breadcrumbs,
|
|
4
|
+
Typography,
|
|
5
|
+
Container,
|
|
6
|
+
useTheme,
|
|
7
|
+
Box,
|
|
8
|
+
Tabs,
|
|
9
|
+
Tab,
|
|
10
|
+
Button,
|
|
11
|
+
} from "@mui/material";
|
|
12
|
+
import { Link } from "../link";
|
|
13
|
+
import { useGetDefaultThemeColor } from "../../utils";
|
|
14
|
+
import { HeaderComponent, HeaderPreset, HeaderProps } from "./header.types";
|
|
15
|
+
import { useTab } from "~/providers";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Section used to explain give basic information about the page
|
|
19
|
+
* and put the main actions
|
|
20
|
+
*/
|
|
21
|
+
export const Header: HeaderComponent = ({
|
|
22
|
+
title,
|
|
23
|
+
subtitle,
|
|
24
|
+
preset = "default",
|
|
25
|
+
actionsVariant = "outlined",
|
|
26
|
+
breadcrumbs,
|
|
27
|
+
actions,
|
|
28
|
+
tabs,
|
|
29
|
+
}: HeaderProps) => {
|
|
30
|
+
const { palette } = useTheme();
|
|
31
|
+
const defaultColor = useGetDefaultThemeColor();
|
|
32
|
+
const [selectedTab, setSelectedTab] = useTab();
|
|
33
|
+
|
|
34
|
+
const bgColorPresets: Record<HeaderPreset, string> = {
|
|
35
|
+
default: defaultColor,
|
|
36
|
+
primary: palette.primary.main,
|
|
37
|
+
secondary: palette.secondary.main,
|
|
38
|
+
inherit: "inherit",
|
|
39
|
+
transparent: "transparent",
|
|
40
|
+
};
|
|
41
|
+
const bgColor = bgColorPresets[preset];
|
|
42
|
+
const textColorPresets: Record<HeaderPreset, string> = {
|
|
43
|
+
default: palette.getContrastText(bgColorPresets.default),
|
|
44
|
+
primary: palette.primary.contrastText,
|
|
45
|
+
secondary: palette.secondary.contrastText,
|
|
46
|
+
inherit: "inherit",
|
|
47
|
+
transparent: palette.text.primary,
|
|
48
|
+
};
|
|
49
|
+
const textColor = textColorPresets[preset];
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<Box bgcolor={bgColor} color={textColor}>
|
|
53
|
+
<Container>
|
|
54
|
+
<Box sx={{ py: 3, display: "flex", flexDirection: "row", justifyContent: "space-between" }}>
|
|
55
|
+
<Box>
|
|
56
|
+
{breadcrumbs?.length && (
|
|
57
|
+
<Breadcrumbs
|
|
58
|
+
color="inherit"
|
|
59
|
+
separator="›"
|
|
60
|
+
aria-label="breadcrumb"
|
|
61
|
+
sx={{ marginTop: 1 }}
|
|
62
|
+
>
|
|
63
|
+
{breadcrumbs.map(({ id, link, text }) => (
|
|
64
|
+
<Link
|
|
65
|
+
key={id}
|
|
66
|
+
underline="hover"
|
|
67
|
+
color="inherit"
|
|
68
|
+
href={link}
|
|
69
|
+
variant="body2"
|
|
70
|
+
role="link"
|
|
71
|
+
>
|
|
72
|
+
{text}
|
|
73
|
+
</Link>
|
|
74
|
+
))}
|
|
75
|
+
</Breadcrumbs>
|
|
76
|
+
)}
|
|
77
|
+
<Typography variant="h4" role="heading" aria-level={1}>
|
|
78
|
+
{title}
|
|
79
|
+
</Typography>
|
|
80
|
+
{subtitle && (
|
|
81
|
+
<Typography variant="body1" role="heading" aria-level={2}>
|
|
82
|
+
{subtitle}
|
|
83
|
+
</Typography>
|
|
84
|
+
)}
|
|
85
|
+
</Box>
|
|
86
|
+
{actions && (
|
|
87
|
+
<Box>
|
|
88
|
+
{actions.map(({ disabled, id, href, onClick, text }, i) => (
|
|
89
|
+
<Button
|
|
90
|
+
component={href ? Link : "button"}
|
|
91
|
+
role="button"
|
|
92
|
+
color="inherit"
|
|
93
|
+
disabled={disabled}
|
|
94
|
+
key={id}
|
|
95
|
+
variant={actionsVariant}
|
|
96
|
+
size="small"
|
|
97
|
+
href={href}
|
|
98
|
+
onClick={onClick}
|
|
99
|
+
sx={{ mr: i != actions.length - 1 ? 1 : 0 }}
|
|
100
|
+
>
|
|
101
|
+
{text}
|
|
102
|
+
</Button>
|
|
103
|
+
))}
|
|
104
|
+
</Box>
|
|
105
|
+
)}
|
|
106
|
+
</Box>
|
|
107
|
+
{tabs && (
|
|
108
|
+
<Tabs
|
|
109
|
+
value={selectedTab}
|
|
110
|
+
textColor="inherit"
|
|
111
|
+
onChange={(_, index) => setSelectedTab(index)}
|
|
112
|
+
>
|
|
113
|
+
{tabs.map(({ id, label, disabled }) => (
|
|
114
|
+
<Tab key={id} label={label} disabled={disabled} />
|
|
115
|
+
))}
|
|
116
|
+
</Tabs>
|
|
117
|
+
)}
|
|
118
|
+
</Container>
|
|
119
|
+
</Box>
|
|
120
|
+
);
|
|
121
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { PropTypes } from "@mui/material";
|
|
2
|
+
import { FunctionComponent, ReactElement } from "react";
|
|
3
|
+
|
|
4
|
+
export type HeaderPreset = PropTypes.Color | "transparent";
|
|
5
|
+
export type HeaderActionVariant = "text" | "outlined" | "contained";
|
|
6
|
+
|
|
7
|
+
export type HeaderAction = {
|
|
8
|
+
id: string;
|
|
9
|
+
text: string;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
href?: string;
|
|
12
|
+
onClick?: () => void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export interface HeaderBreadcrumb {
|
|
16
|
+
id: string;
|
|
17
|
+
text: string;
|
|
18
|
+
link: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface HeaderTab {
|
|
22
|
+
id: string;
|
|
23
|
+
label: string;
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type HeaderProps = {
|
|
28
|
+
/**
|
|
29
|
+
* Title of the header
|
|
30
|
+
*/
|
|
31
|
+
title: string;
|
|
32
|
+
/**
|
|
33
|
+
* Subtitle of the header
|
|
34
|
+
*/
|
|
35
|
+
subtitle?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Color palete used to render the component
|
|
38
|
+
*/
|
|
39
|
+
preset?: HeaderPreset;
|
|
40
|
+
/**
|
|
41
|
+
* List of breadcumbs to represent the path to reach
|
|
42
|
+
* the page that we are
|
|
43
|
+
*/
|
|
44
|
+
breadcrumbs?: HeaderBreadcrumb[];
|
|
45
|
+
/**
|
|
46
|
+
* List of actions that can be performed by the user.
|
|
47
|
+
* Each action will be a button in the header.
|
|
48
|
+
*/
|
|
49
|
+
actions?: HeaderAction[];
|
|
50
|
+
/**
|
|
51
|
+
* Variant used to render the actions
|
|
52
|
+
*/
|
|
53
|
+
actionsVariant?: HeaderActionVariant;
|
|
54
|
+
/**
|
|
55
|
+
* If is set, a list of tabs is dispayed at the bottom
|
|
56
|
+
*/
|
|
57
|
+
tabs?: HeaderTab[];
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export type HeaderComponent = FunctionComponent<HeaderProps>;
|
|
61
|
+
export type HeaderElement = ReactElement<HeaderProps, HeaderComponent>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export * from "./header";
|
|
2
|
+
export * from "./link";
|
|
3
|
+
export * from "./query-container";
|
|
4
|
+
export * from "./sign-in";
|
|
5
|
+
export * from "./tab";
|
|
6
|
+
export * from "./table";
|
|
7
|
+
export * from "./app-bar";
|
|
8
|
+
export * from "./drawer";
|
|
9
|
+
export * from "./table-list";
|
|
10
|
+
export * from "./placeholder";
|
|
11
|
+
export * from "./label";
|
|
12
|
+
export * from "./bullet";
|
|
13
|
+
export * from "./drawer-content";
|
|
14
|
+
export * from "./drawer-section";
|
|
15
|
+
export * from "./drawer-item";
|
|
16
|
+
export * from "./center-container";
|
|
17
|
+
export * from "./value-displays";
|
|
18
|
+
export * from "./content";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./label";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ComponentMeta } from "@storybook/react";
|
|
2
|
+
import { createTemplate } from "../../storybook";
|
|
3
|
+
import { Label } from "./label";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: "Data Display/Label",
|
|
7
|
+
component: Label,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: "centered",
|
|
10
|
+
},
|
|
11
|
+
} as ComponentMeta<typeof Label>;
|
|
12
|
+
|
|
13
|
+
const Template = createTemplate(Label);
|
|
14
|
+
|
|
15
|
+
export const Default = Template.bind({});
|
|
16
|
+
Default.args = {
|
|
17
|
+
text: "lorem",
|
|
18
|
+
variant: "default",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const Primary = Template.bind({});
|
|
22
|
+
Primary.args = {
|
|
23
|
+
text: "lorem",
|
|
24
|
+
variant: "primary",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const Secondary = Template.bind({});
|
|
28
|
+
Secondary.args = {
|
|
29
|
+
text: "lorem",
|
|
30
|
+
variant: "secondary",
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const Info = Template.bind({});
|
|
34
|
+
Info.args = {
|
|
35
|
+
text: "lorem",
|
|
36
|
+
variant: "info",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const Warning = Template.bind({});
|
|
40
|
+
Warning.args = {
|
|
41
|
+
text: "lorem",
|
|
42
|
+
variant: "warning",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const Error = Template.bind({});
|
|
46
|
+
Error.args = {
|
|
47
|
+
text: "lorem",
|
|
48
|
+
variant: "error",
|
|
49
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Label, LabelVariant } from "./label";
|
|
3
|
+
import { render, screen } from "../../tests";
|
|
4
|
+
|
|
5
|
+
describe("Label", () => {
|
|
6
|
+
const renderComponent = (variant: LabelVariant | undefined = undefined) => {
|
|
7
|
+
return render(<Label variant={variant} text="lorem ipsum" />);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
it("renders the label text", () => {
|
|
11
|
+
renderComponent();
|
|
12
|
+
|
|
13
|
+
expect(screen.getByText("LOREM IPSUM")).toBeInTheDocument();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("renders as default without a variant", () => {
|
|
17
|
+
renderComponent(undefined);
|
|
18
|
+
|
|
19
|
+
expect(screen.getByRole("label")).toHaveAttribute("aria-describedby", "default");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it.each([["primary"], ["secondary"], ["default"], ["info"], ["warning"], ["error"]])(
|
|
23
|
+
"renders correctly with variant %s",
|
|
24
|
+
(variant: string) => {
|
|
25
|
+
renderComponent(variant as LabelVariant);
|
|
26
|
+
|
|
27
|
+
expect(screen.getByRole("label")).toHaveAttribute("aria-describedby", variant);
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Box, Typography, useTheme } from "@mui/material";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
export type LabelVariant = "primary" | "secondary" | "default" | "info" | "warning" | "error";
|
|
5
|
+
|
|
6
|
+
export const labelClasses = {
|
|
7
|
+
root: "RdsLabel-root",
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export interface LabelProps {
|
|
11
|
+
/**
|
|
12
|
+
* Content of the component
|
|
13
|
+
*/
|
|
14
|
+
text: string;
|
|
15
|
+
/**
|
|
16
|
+
* Color palette used to draw the component
|
|
17
|
+
*/
|
|
18
|
+
variant?: LabelVariant;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Compact element to represent a text
|
|
23
|
+
*/
|
|
24
|
+
export const Label = ({ text, variant = "default" }: LabelProps) => {
|
|
25
|
+
const { palette } = useTheme();
|
|
26
|
+
|
|
27
|
+
const backgroundColor: Record<LabelVariant, string> = {
|
|
28
|
+
default: palette.mode === "light" ? palette.grey[100] : palette.grey[900],
|
|
29
|
+
primary: palette.primary.main,
|
|
30
|
+
secondary: palette.secondary.main,
|
|
31
|
+
info: palette.info.main,
|
|
32
|
+
warning: palette.warning.main,
|
|
33
|
+
error: palette.error.main,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const textColor: Record<LabelVariant, string> = {
|
|
37
|
+
default: palette.getContrastText(backgroundColor.default),
|
|
38
|
+
primary: palette.primary.contrastText,
|
|
39
|
+
secondary: palette.secondary.contrastText,
|
|
40
|
+
info: palette.info.contrastText,
|
|
41
|
+
warning: palette.warning.contrastText,
|
|
42
|
+
error: palette.error.contrastText,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Box
|
|
47
|
+
px={1}
|
|
48
|
+
sx={{ backgroundColor: backgroundColor[variant] }}
|
|
49
|
+
borderRadius={1}
|
|
50
|
+
color={textColor[variant]}
|
|
51
|
+
className={labelClasses.root}
|
|
52
|
+
role="label"
|
|
53
|
+
aria-describedby={variant}
|
|
54
|
+
>
|
|
55
|
+
<Typography variant="caption" fontWeight={700}>
|
|
56
|
+
{text.toUpperCase()}
|
|
57
|
+
</Typography>
|
|
58
|
+
</Box>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./link";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Link as RouterLink, LinkProps as RouterLinkProps } from "react-router-dom";
|
|
3
|
+
import { forwardRef } from "react";
|
|
4
|
+
import { LinkProps, Link as MuiLink } from "@mui/material";
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line react/display-name, @typescript-eslint/no-explicit-any
|
|
7
|
+
export const LinkBehaviour = forwardRef<
|
|
8
|
+
any,
|
|
9
|
+
Omit<RouterLinkProps, "to"> & { href: RouterLinkProps["to"] }
|
|
10
|
+
>((props, ref) => {
|
|
11
|
+
const { href, ...other } = props;
|
|
12
|
+
return <RouterLink ref={ref} to={href} {...other} />;
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const Link = forwardRef<any, LinkProps>((props, _) => {
|
|
16
|
+
return <MuiLink {...props} component={LinkBehaviour} />;
|
|
17
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./loading-area";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ComponentMeta } from "@storybook/react";
|
|
2
|
+
import { createTemplate, withContainer } from "../../storybook";
|
|
3
|
+
import { LoadingArea } from "./loading-area";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: "Components/LoadingArea",
|
|
7
|
+
component: LoadingArea,
|
|
8
|
+
decorators: [withContainer({ height: 300 })],
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: "fullscreen",
|
|
11
|
+
},
|
|
12
|
+
} as ComponentMeta<typeof LoadingArea>;
|
|
13
|
+
|
|
14
|
+
const Template = createTemplate(LoadingArea);
|
|
15
|
+
|
|
16
|
+
export const Default = Template.bind({});
|
|
17
|
+
Default.args = {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render, expectProgressIndicator } from "../../tests";
|
|
3
|
+
import { LoadingArea } from "./loading-area";
|
|
4
|
+
|
|
5
|
+
describe("LoadingArea", () => {
|
|
6
|
+
it("would render a loading icon", () => {
|
|
7
|
+
render(<LoadingArea />);
|
|
8
|
+
|
|
9
|
+
expectProgressIndicator();
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CircularProgress, Box } from "@mui/material";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Displays a centered loading indicator
|
|
6
|
+
*/
|
|
7
|
+
export const LoadingArea = () => {
|
|
8
|
+
return (
|
|
9
|
+
<Box width={1} height={1} display="flex" justifyContent="center" alignItems="center">
|
|
10
|
+
<CircularProgress />
|
|
11
|
+
</Box>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./placeholder";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PlaceholderAction } from "./placeholder";
|
|
2
|
+
import { action } from "@storybook/addon-actions";
|
|
3
|
+
|
|
4
|
+
export const actions: PlaceholderAction[] = [
|
|
5
|
+
{
|
|
6
|
+
id: "add",
|
|
7
|
+
text: "Add",
|
|
8
|
+
href: "/placeholders/add",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
id: "edit",
|
|
12
|
+
text: "Edit",
|
|
13
|
+
onClick: action("on click edit action"),
|
|
14
|
+
},
|
|
15
|
+
];
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ComponentMeta } from "@storybook/react";
|
|
3
|
+
import { createTemplate } from "../../storybook";
|
|
4
|
+
import { Placeholder, PlaceholderIconArgs } from "./placeholder";
|
|
5
|
+
import SearchIcon from "@mui/icons-material/Search";
|
|
6
|
+
import { actions } from "./placeholder.mock";
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
title: "Components/Placeholder",
|
|
10
|
+
component: Placeholder,
|
|
11
|
+
parameters: {
|
|
12
|
+
layout: "fullscreen",
|
|
13
|
+
},
|
|
14
|
+
} as ComponentMeta<typeof Placeholder>;
|
|
15
|
+
|
|
16
|
+
const Template = createTemplate(Placeholder);
|
|
17
|
+
|
|
18
|
+
export const Default = Template.bind({});
|
|
19
|
+
Default.args = {
|
|
20
|
+
title: "Lorem ipsum dolor sit amet",
|
|
21
|
+
subtitle:
|
|
22
|
+
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eleifend at libero in tristique. Pellentesque bibendum arcu eget augue commodo, non convallis eros porttitor",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const WithIcon = Template.bind({});
|
|
26
|
+
WithIcon.args = {
|
|
27
|
+
icon: ({ size, color }: PlaceholderIconArgs) => (
|
|
28
|
+
<SearchIcon color={color} sx={{ fontSize: size }} />
|
|
29
|
+
),
|
|
30
|
+
title: "Lorem ipsum dolor sit amet",
|
|
31
|
+
subtitle:
|
|
32
|
+
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eleifend at libero in tristique. Pellentesque bibendum arcu eget augue commodo, non convallis eros porttitor",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const WithActions = Template.bind({});
|
|
36
|
+
WithActions.args = {
|
|
37
|
+
icon: ({ size, color }: PlaceholderIconArgs) => (
|
|
38
|
+
<SearchIcon color={color} sx={{ fontSize: size }} />
|
|
39
|
+
),
|
|
40
|
+
title: "Lorem ipsum dolor sit amet",
|
|
41
|
+
subtitle:
|
|
42
|
+
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eleifend at libero in tristique. Pellentesque bibendum arcu eget augue commodo, non convallis eros porttitor",
|
|
43
|
+
actions,
|
|
44
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React, { ReactElement } from "react";
|
|
2
|
+
import { render, screen } from "../../tests";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { Placeholder, PlaceholderAction } from "./placeholder";
|
|
5
|
+
import { actions as actionData } from "./placeholder.mock";
|
|
6
|
+
import SearchIcon from "@mui/icons-material/Search";
|
|
7
|
+
|
|
8
|
+
const actions = actionData.map((a) => ({ ...a, onClick: jest.fn() }));
|
|
9
|
+
|
|
10
|
+
describe("Placeholder", () => {
|
|
11
|
+
const renderComponent = ({
|
|
12
|
+
actions = undefined,
|
|
13
|
+
icon = undefined,
|
|
14
|
+
}: { actions?: PlaceholderAction[]; icon?: ReactElement } = {}) => {
|
|
15
|
+
return render(
|
|
16
|
+
<Placeholder
|
|
17
|
+
title="Lorem ipsum"
|
|
18
|
+
subtitle="Lorem ipsum sit amet"
|
|
19
|
+
actions={actions}
|
|
20
|
+
icon={() => icon}
|
|
21
|
+
/>,
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
it("would render the title", () => {
|
|
26
|
+
renderComponent();
|
|
27
|
+
|
|
28
|
+
expect(screen.getByRole("heading", { name: /lorem ipsum/i, level: 1 })).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("would render the subtitle", () => {
|
|
32
|
+
renderComponent();
|
|
33
|
+
|
|
34
|
+
expect(
|
|
35
|
+
screen.getByRole("heading", { name: /lorem ipsum sit amet/i, level: 2 }),
|
|
36
|
+
).toBeInTheDocument();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe("icon", () => {
|
|
40
|
+
it("wouldn't render an icon if it's not set", () => {
|
|
41
|
+
renderComponent({ icon: undefined });
|
|
42
|
+
|
|
43
|
+
expect(screen.queryByTestId("SearchIcon")).not.toBeInTheDocument();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("would render an icon if it's set", () => {
|
|
47
|
+
renderComponent({ icon: <SearchIcon /> });
|
|
48
|
+
|
|
49
|
+
expect(screen.queryByTestId("SearchIcon")).toBeInTheDocument();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe("actions", () => {
|
|
54
|
+
it("wouldn't render an action if they are not set", () => {
|
|
55
|
+
renderComponent({ actions: undefined });
|
|
56
|
+
|
|
57
|
+
expect(screen.queryByRole("button")).not.toBeInTheDocument();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("would render a button for each action", () => {
|
|
61
|
+
renderComponent({ actions });
|
|
62
|
+
|
|
63
|
+
expect(screen.queryAllByRole("button")).toHaveLength(actions.length);
|
|
64
|
+
expect(screen.queryByRole("button", { name: /add/i })).toBeInTheDocument();
|
|
65
|
+
expect(screen.queryByRole("button", { name: /edit/i })).toBeInTheDocument();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("would call onClick if a button is clicked", async () => {
|
|
69
|
+
renderComponent({ actions });
|
|
70
|
+
|
|
71
|
+
await userEvent.click(screen.getByRole("button", { name: /edit/i }));
|
|
72
|
+
|
|
73
|
+
expect(actions[1].onClick).toHaveBeenCalledTimes(1);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React, { ReactElement } from "react";
|
|
2
|
+
import { Box, Typography, Button } from "@mui/material";
|
|
3
|
+
|
|
4
|
+
type IconColor =
|
|
5
|
+
| "inherit"
|
|
6
|
+
| "action"
|
|
7
|
+
| "disabled"
|
|
8
|
+
| "primary"
|
|
9
|
+
| "secondary"
|
|
10
|
+
| "error"
|
|
11
|
+
| "info"
|
|
12
|
+
| "success"
|
|
13
|
+
| "warning";
|
|
14
|
+
|
|
15
|
+
export interface PlaceholderAction {
|
|
16
|
+
id: string;
|
|
17
|
+
text: string;
|
|
18
|
+
href?: string;
|
|
19
|
+
onClick?: () => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface PlaceholderIconArgs {
|
|
23
|
+
size: number;
|
|
24
|
+
color: IconColor;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface PlaceholderProps {
|
|
28
|
+
title: string;
|
|
29
|
+
subtitle: string;
|
|
30
|
+
iconSize?: number;
|
|
31
|
+
icon?: ({ size, color }: PlaceholderIconArgs) => ReactElement;
|
|
32
|
+
actions?: PlaceholderAction[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const Placeholder = ({
|
|
36
|
+
title,
|
|
37
|
+
subtitle,
|
|
38
|
+
icon,
|
|
39
|
+
iconSize = 200,
|
|
40
|
+
actions,
|
|
41
|
+
}: PlaceholderProps) => {
|
|
42
|
+
return (
|
|
43
|
+
<Box
|
|
44
|
+
display="flex"
|
|
45
|
+
flexDirection="column"
|
|
46
|
+
justifyContent="center"
|
|
47
|
+
alignItems="center"
|
|
48
|
+
textAlign="center"
|
|
49
|
+
>
|
|
50
|
+
{icon && icon({ size: iconSize, color: "primary" })}
|
|
51
|
+
<Typography variant="h4" role="heading" aria-level={1}>
|
|
52
|
+
{title}
|
|
53
|
+
</Typography>
|
|
54
|
+
<Typography variant="subtitle1" role="heading" aria-level={2} sx={{ mt: 2 }}>
|
|
55
|
+
{subtitle}
|
|
56
|
+
</Typography>
|
|
57
|
+
{actions && (
|
|
58
|
+
<Box sx={{ pt: 2 }}>
|
|
59
|
+
{actions.map(({ id, text, href, onClick }, index) => (
|
|
60
|
+
<Button
|
|
61
|
+
key={id}
|
|
62
|
+
role="button"
|
|
63
|
+
variant="contained"
|
|
64
|
+
href={href}
|
|
65
|
+
onClick={onClick}
|
|
66
|
+
sx={{ mr: index < actions.length - 1 ? 2 : 0 }}
|
|
67
|
+
>
|
|
68
|
+
{text}
|
|
69
|
+
</Button>
|
|
70
|
+
))}
|
|
71
|
+
</Box>
|
|
72
|
+
)}
|
|
73
|
+
</Box>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./query-container";
|