@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.
Files changed (175) hide show
  1. package/package.json +1 -1
  2. package/src/components/app-bar/app-bar.stories.tsx +54 -0
  3. package/src/components/app-bar/app-bar.test.tsx +142 -0
  4. package/src/components/app-bar/app-bar.tsx +150 -0
  5. package/src/components/app-bar/app-bar.types.ts +17 -0
  6. package/src/components/app-bar/index.ts +3 -0
  7. package/src/components/app-bar/mini-app-bar/index.ts +1 -0
  8. package/src/components/app-bar/mini-app-bar/mini-app-bar.tsx +31 -0
  9. package/src/components/bullet/bullet.stories.tsx +43 -0
  10. package/src/components/bullet/bullet.test.tsx +24 -0
  11. package/src/components/bullet/bullet.tsx +30 -0
  12. package/src/components/bullet/index.ts +1 -0
  13. package/src/components/center-container/center-container.stories.tsx +50 -0
  14. package/src/components/center-container/center-container.test.tsx +16 -0
  15. package/src/components/center-container/center-container.tsx +32 -0
  16. package/src/components/center-container/index.ts +1 -0
  17. package/src/components/content/content.stories.tsx +23 -0
  18. package/src/components/content/content.test.tsx +26 -0
  19. package/src/components/content/content.tsx +11 -0
  20. package/src/components/content/content.types.ts +5 -0
  21. package/src/components/content/index.ts +2 -0
  22. package/src/components/drawer/__snapshots__/drawer.test.tsx.snap +20 -0
  23. package/src/components/drawer/drawer.context.ts +20 -0
  24. package/src/components/drawer/drawer.mixins.ts +24 -0
  25. package/src/components/drawer/drawer.mock.tsx +100 -0
  26. package/src/components/drawer/drawer.provider.tsx +23 -0
  27. package/src/components/drawer/drawer.test.tsx +97 -0
  28. package/src/components/drawer/drawer.tsx +30 -0
  29. package/src/components/drawer/drawer.types.ts +53 -0
  30. package/src/components/drawer/index.ts +5 -0
  31. package/src/components/drawer/mini-drawer/index.ts +1 -0
  32. package/src/components/drawer/mini-drawer/mini-drawer.stories.tsx +34 -0
  33. package/src/components/drawer/mini-drawer/mini-drawer.tsx +67 -0
  34. package/src/components/drawer-content/drawer-content.stories.tsx +29 -0
  35. package/src/components/drawer-content/drawer-content.test.tsx +34 -0
  36. package/src/components/drawer-content/drawer-content.tsx +18 -0
  37. package/src/components/drawer-content/index.ts +1 -0
  38. package/src/components/drawer-item/drawer-item.stories.tsx +62 -0
  39. package/src/components/drawer-item/drawer-item.test.tsx +119 -0
  40. package/src/components/drawer-item/drawer-item.tsx +71 -0
  41. package/src/components/drawer-item/index.ts +1 -0
  42. package/src/components/drawer-section/drawer-section.mock.tsx +39 -0
  43. package/src/components/drawer-section/drawer-section.stories.tsx +28 -0
  44. package/src/components/drawer-section/drawer-section.test.tsx +44 -0
  45. package/src/components/drawer-section/drawer-section.tsx +40 -0
  46. package/src/components/drawer-section/index.ts +1 -0
  47. package/src/components/header/header.dummy.ts +55 -0
  48. package/src/components/header/header.stories.tsx +116 -0
  49. package/src/components/header/header.test.tsx +169 -0
  50. package/src/components/header/header.tsx +121 -0
  51. package/src/components/header/header.types.ts +61 -0
  52. package/src/components/header/index.ts +2 -0
  53. package/src/components/index.ts +18 -0
  54. package/src/components/label/index.ts +1 -0
  55. package/src/components/label/label.stories.tsx +49 -0
  56. package/src/components/label/label.test.tsx +30 -0
  57. package/src/components/label/label.tsx +60 -0
  58. package/src/components/link/index.ts +1 -0
  59. package/src/components/link/link.tsx +17 -0
  60. package/src/components/loading-area/index.ts +1 -0
  61. package/src/components/loading-area/loading-area.stories.tsx +17 -0
  62. package/src/components/loading-area/loading-area.test.tsx +11 -0
  63. package/src/components/loading-area/loading-area.tsx +13 -0
  64. package/src/components/placeholder/index.ts +1 -0
  65. package/src/components/placeholder/placeholder.mock.ts +15 -0
  66. package/src/components/placeholder/placeholder.stories.tsx +44 -0
  67. package/src/components/placeholder/placeholder.test.tsx +76 -0
  68. package/src/components/placeholder/placeholder.tsx +75 -0
  69. package/src/components/query-container/index.ts +1 -0
  70. package/src/components/query-container/query-container.stories.tsx +68 -0
  71. package/src/components/query-container/query-container.test.tsx +95 -0
  72. package/src/components/query-container/query-container.tsx +71 -0
  73. package/src/components/sign-in/index.ts +1 -0
  74. package/src/components/sign-in/sign-in.stories.tsx +36 -0
  75. package/src/components/sign-in/sign-in.test.tsx +95 -0
  76. package/src/components/sign-in/sign-in.tsx +97 -0
  77. package/src/components/tab/index.ts +2 -0
  78. package/src/components/tab/tab-card/index.ts +1 -0
  79. package/src/components/tab/tab-card/tab-card.dummy.tsx +30 -0
  80. package/src/components/tab/tab-card/tab-card.stories.tsx +22 -0
  81. package/src/components/tab/tab-card/tab-card.test.tsx +53 -0
  82. package/src/components/tab/tab-card/tab-card.tsx +27 -0
  83. package/src/components/tab/tab-panel/index.ts +1 -0
  84. package/src/components/tab/tab-panel/tab-panel.test.tsx +26 -0
  85. package/src/components/tab/tab-panel/tab-panel.tsx +27 -0
  86. package/src/components/table/enhanced-remote-table/enhanced-remote-table.mock.tsx +27 -0
  87. package/src/components/table/enhanced-remote-table/enhanced-remote-table.stories.tsx +24 -0
  88. package/src/components/table/enhanced-remote-table/enhanced-remote-table.test.tsx +77 -0
  89. package/src/components/table/enhanced-remote-table/enhanced-remote-table.tsx +74 -0
  90. package/src/components/table/enhanced-remote-table/index.ts +1 -0
  91. package/src/components/table/enhanced-table/enhanced-table-head.tsx +58 -0
  92. package/src/components/table/enhanced-table/enhanced-table.mock.tsx +93 -0
  93. package/src/components/table/enhanced-table/enhanced-table.stories.tsx +21 -0
  94. package/src/components/table/enhanced-table/enhanced-table.test.tsx +107 -0
  95. package/src/components/table/enhanced-table/enhanced-table.tsx +136 -0
  96. package/src/components/table/enhanced-table/index.ts +2 -0
  97. package/src/components/table/index.ts +2 -0
  98. package/src/components/table-list/index.ts +1 -0
  99. package/src/components/table-list/table-list.stories.tsx +75 -0
  100. package/src/components/table-list/table-list.test.tsx +284 -0
  101. package/src/components/table-list/table-list.tsx +127 -0
  102. package/src/components/value-displays/group-value-card/group-value-card.mock.tsx +35 -0
  103. package/src/components/value-displays/group-value-card/group-value-card.stories.tsx +26 -0
  104. package/src/components/value-displays/group-value-card/group-value-card.test.tsx +58 -0
  105. package/src/components/value-displays/group-value-card/group-value-card.tsx +63 -0
  106. package/src/components/value-displays/group-value-card/index.ts +1 -0
  107. package/src/components/value-displays/index.ts +4 -0
  108. package/src/components/value-displays/value-boolean/index.ts +1 -0
  109. package/src/components/value-displays/value-boolean/value-boolean.stories.tsx +25 -0
  110. package/src/components/value-displays/value-boolean/value-boolean.test.tsx +27 -0
  111. package/src/components/value-displays/value-boolean/value-boolean.tsx +33 -0
  112. package/src/components/value-displays/value-card/index.ts +1 -0
  113. package/src/components/value-displays/value-card/value-card.stories.tsx +22 -0
  114. package/src/components/value-displays/value-card/value-card.test.tsx +18 -0
  115. package/src/components/value-displays/value-card/value-card.tsx +12 -0
  116. package/src/components/value-displays/value-text/index.ts +1 -0
  117. package/src/components/value-displays/value-text/value-test.test.tsx +21 -0
  118. package/src/components/value-displays/value-text/value-text.stories.tsx +26 -0
  119. package/src/components/value-displays/value-text/value-text.tsx +32 -0
  120. package/src/generators/generators.mock.ts +238 -0
  121. package/src/generators/generators.model.ts +46 -0
  122. package/src/generators/index.ts +4 -0
  123. package/src/generators/model-form/index.ts +1 -0
  124. package/src/generators/model-form/model-form.stories.tsx +30 -0
  125. package/src/generators/model-form/model-form.test.tsx +100 -0
  126. package/src/generators/model-form/model-form.tsx +97 -0
  127. package/src/generators/model-router/index.ts +1 -0
  128. package/src/generators/model-router/model-router.test.tsx +666 -0
  129. package/src/generators/model-router/model-router.tsx +29 -0
  130. package/src/generators/model-router/model-router.types.ts +14 -0
  131. package/src/generators/model-router/screens/add-screen.tsx +69 -0
  132. package/src/generators/model-router/screens/details-screen.tsx +62 -0
  133. package/src/generators/model-router/screens/index.ts +4 -0
  134. package/src/generators/model-router/screens/list-screen.tsx +110 -0
  135. package/src/generators/model-router/screens/screens.types.ts +13 -0
  136. package/src/generators/model-router/screens/update-screen.tsx +96 -0
  137. package/src/generators/model-router/stories/details-screen.stories.tsx +38 -0
  138. package/src/generators/model-router/stories/list-screen.stories.tsx +45 -0
  139. package/src/generators/model-router/stories/model-router.stories.tsx +164 -0
  140. package/src/generators/model-router/stories/templates.tsx +39 -0
  141. package/src/generators/object-details/index.ts +1 -0
  142. package/src/generators/object-details/object-details.stories.tsx +20 -0
  143. package/src/generators/object-details/object-details.test.tsx +21 -0
  144. package/src/generators/object-details/object-details.tsx +76 -0
  145. package/src/index.ts +4 -0
  146. package/src/layouts/app-bar-with-drawer-layout/app-bar-with-drawer-layout.stories.tsx +28 -0
  147. package/src/layouts/app-bar-with-drawer-layout/app-bar-with-drawer-layout.test.tsx +30 -0
  148. package/src/layouts/app-bar-with-drawer-layout/app-bar-with-drawer-layout.tsx +37 -0
  149. package/src/layouts/app-bar-with-drawer-layout/index.ts +1 -0
  150. package/src/layouts/header-layout/header-layout.stories.tsx +204 -0
  151. package/src/layouts/header-layout/header-layout.test.tsx +37 -0
  152. package/src/layouts/header-layout/header-layout.tsx +23 -0
  153. package/src/layouts/header-layout/index.ts +1 -0
  154. package/src/layouts/index.ts +2 -0
  155. package/src/providers/index.ts +2 -0
  156. package/src/providers/notification-center/index.ts +2 -0
  157. package/src/providers/notification-center/notification-center.context.ts +37 -0
  158. package/src/providers/notification-center/notification-center.provider.tsx +51 -0
  159. package/src/providers/notification-center/notification-center.stories.tsx +52 -0
  160. package/src/providers/notification-center/notification-center.test.tsx +112 -0
  161. package/src/providers/tab-provider/index.ts +2 -0
  162. package/src/providers/tab-provider/tab-provider.context.ts +8 -0
  163. package/src/providers/tab-provider/tab-provider.provider.tsx +13 -0
  164. package/src/storybook.tsx +90 -0
  165. package/src/tests/assertions.ts +76 -0
  166. package/src/tests/components.tsx +60 -0
  167. package/src/tests/content-placeholder.stories.tsx +16 -0
  168. package/src/tests/index.ts +3 -0
  169. package/src/tests/skeleton-card.stories.tsx +18 -0
  170. package/src/tests/testing-library.tsx +65 -0
  171. package/src/utils/arrays.test.ts +9 -0
  172. package/src/utils/arrays.ts +7 -0
  173. package/src/utils/index.ts +2 -0
  174. package/src/utils/theme.ts +11 -0
  175. package/.prettierrc.js +0 -5
@@ -0,0 +1,76 @@
1
+ import React from "react";
2
+ import { Grid } from "@mui/material";
3
+ import {
4
+ GroupValueCard,
5
+ GroupValueItem,
6
+ ValueBoolean,
7
+ ValueCard,
8
+ ValueText,
9
+ } from "../../components";
10
+ import { ModelField, GroupField, Model, BasicModelInstance } from "../generators.model";
11
+
12
+ const singleDetailValueFactory = <T extends BasicModelInstance>(
13
+ { id, name, type }: ModelField,
14
+ instance: T,
15
+ ) => {
16
+ const value = instance[id];
17
+ if (type === "boolean") {
18
+ return <ValueBoolean label={name} value={value} />;
19
+ }
20
+ return <ValueText label={name} value={value?.toString()} />;
21
+ };
22
+
23
+ interface ObjectDetailGroupProps<T extends BasicModelInstance> {
24
+ field: GroupField;
25
+ instance: T;
26
+ }
27
+
28
+ const ObjectDetailGroup = <T extends BasicModelInstance>({
29
+ field: { name, description, value },
30
+ instance,
31
+ }: ObjectDetailGroupProps<T>) => {
32
+ return (
33
+ <GroupValueCard title={name} subtitle={description}>
34
+ {value.map((field) => {
35
+ const { id, xs, sm, md, lg, xl } = field;
36
+ return (
37
+ <GroupValueItem key={id} xs={xs} sm={sm} md={md} lg={lg} xl={xl}>
38
+ {singleDetailValueFactory(field, instance)}
39
+ </GroupValueItem>
40
+ );
41
+ })}
42
+ </GroupValueCard>
43
+ );
44
+ };
45
+
46
+ export interface ObjectDetailsProps<T extends BasicModelInstance> {
47
+ model: Model;
48
+ instance: T;
49
+ }
50
+
51
+ export const ObjectDetails = <T extends BasicModelInstance>({
52
+ model,
53
+ instance,
54
+ }: ObjectDetailsProps<T>) => {
55
+ return (
56
+ <Grid container spacing={2}>
57
+ {model.fields.map((field) => {
58
+ const { id, type, xs = 3, sm, md, lg, xl } = field;
59
+
60
+ if (type === "group") {
61
+ return (
62
+ <Grid item key={id} xs={12}>
63
+ <ObjectDetailGroup field={field} instance={instance[id]} />
64
+ </Grid>
65
+ );
66
+ }
67
+
68
+ return (
69
+ <Grid item key={id} xs={xs} sm={sm} md={md} lg={lg} xl={xl}>
70
+ <ValueCard>{singleDetailValueFactory(field, instance)}</ValueCard>
71
+ </Grid>
72
+ );
73
+ })}
74
+ </Grid>
75
+ );
76
+ };
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./components";
2
+ export * from "./utils";
3
+ export * from "./layouts";
4
+ export * from "./generators";
@@ -0,0 +1,28 @@
1
+ import React from "react";
2
+ import { ComponentMeta } from "@storybook/react";
3
+ import { AppBarWithDrawerLayout } from "./app-bar-with-drawer-layout";
4
+ import { ContentPlaceholder } from "../../tests";
5
+ import { mockNav } from "../../components/drawer/drawer.mock";
6
+ import { MiniAppBar } from "../../components/app-bar";
7
+ import { DrawerContent, MiniDrawer } from "../../components";
8
+ import { withRouter } from "storybook-addon-react-router-v6";
9
+
10
+ export default {
11
+ title: "Layouts/AppBarWithDrawer",
12
+ component: AppBarWithDrawerLayout,
13
+ decorators: [withRouter],
14
+ parameters: {
15
+ layout: "fullscreen",
16
+ },
17
+ } as ComponentMeta<typeof AppBarWithDrawerLayout>;
18
+
19
+ export const MiniDrawerStory = () => (
20
+ <AppBarWithDrawerLayout>
21
+ <MiniDrawer>
22
+ <DrawerContent nav={mockNav} />
23
+ </MiniDrawer>
24
+ <MiniAppBar title="Lorem ipsum" onClickSignOut={() => null} />
25
+ <ContentPlaceholder />
26
+ </AppBarWithDrawerLayout>
27
+ );
28
+ MiniDrawerStory.storyName = "Mini drawer";
@@ -0,0 +1,30 @@
1
+ import React from "react";
2
+ import { MiniDrawerStory } from "./app-bar-with-drawer-layout.stories";
3
+ import { expectContentPlaceholder, render, screen } from "../../tests";
4
+ import userEvent from "@testing-library/user-event";
5
+
6
+ describe("AppBarWithDrawerLayout", () => {
7
+ const renderComponent = () => {
8
+ return render(<MiniDrawerStory />);
9
+ };
10
+
11
+ it("would render a drawer", async () => {
12
+ renderComponent();
13
+
14
+ await userEvent.click(screen.getByTestId("MenuIcon"));
15
+
16
+ expect(screen.getByRole("button", { name: /item 1.1/i })).toBeInTheDocument();
17
+ });
18
+
19
+ it("would render an appbar", () => {
20
+ renderComponent();
21
+
22
+ expect(screen.getByRole("heading", { level: 1, name: /lorem ipsum/i })).toBeInTheDocument();
23
+ });
24
+
25
+ it("would render the content", async () => {
26
+ renderComponent();
27
+
28
+ await expectContentPlaceholder();
29
+ });
30
+ });
@@ -0,0 +1,37 @@
1
+ import React, { ReactNode } from "react";
2
+ import { styled } from "@mui/material/styles";
3
+ import { AppBarElement } from "../../components/app-bar";
4
+ import { DrawerElement } from "../../components/drawer";
5
+ import { Box } from "@mui/material";
6
+ import { DrawerProvider } from "../../components/drawer/drawer.provider";
7
+
8
+ const DrawerHeader = styled("div")(({ theme }) => ({
9
+ display: "flex",
10
+ alignItems: "center",
11
+ justifyContent: "flex-end",
12
+ padding: theme.spacing(0, 1),
13
+ ...theme.mixins.toolbar,
14
+ }));
15
+
16
+ export interface AppBarWithDrawerLayoutProps {
17
+ children: [DrawerElement, AppBarElement, ReactNode];
18
+ }
19
+
20
+ export const AppBarWithDrawerLayout = ({
21
+ children: childrenProps,
22
+ }: AppBarWithDrawerLayoutProps) => {
23
+ const [appBar, drawer, children] = childrenProps;
24
+
25
+ return (
26
+ <Box sx={{ display: "flex" }}>
27
+ <DrawerProvider>
28
+ {appBar}
29
+ {drawer}
30
+ <Box sx={{ flexGrow: 1, p: 3 }}>
31
+ <DrawerHeader />
32
+ {children}
33
+ </Box>
34
+ </DrawerProvider>
35
+ </Box>
36
+ );
37
+ };
@@ -0,0 +1 @@
1
+ export * from "./app-bar-with-drawer-layout";
@@ -0,0 +1,204 @@
1
+ import React, { ReactElement } from "react";
2
+ import { ComponentMeta } from "@storybook/react";
3
+ import { createTemplate } from "../../storybook";
4
+ import { HeaderLayout } from "./header-layout";
5
+ import { withRouter } from "storybook-addon-react-router-v6";
6
+ import { SkeletonGrid } from "../../tests";
7
+ import { withFullHeight } from "../../storybook";
8
+ import { Content, Header, HeaderProps, HeaderTab, TableList, TabPanel } from "../../components";
9
+ import { Box, Typography } from "@mui/material";
10
+ import { useDemoData } from "@mui/x-data-grid-generator";
11
+ import { ModelForm, ObjectDetails } from "../../generators";
12
+ import { mockModel, createModelInstance } from "../../generators/generators.mock";
13
+ import { action } from "@storybook/addon-actions";
14
+ import { DataGrid } from "@mui/x-data-grid";
15
+
16
+ const breadcrumbs = [
17
+ {
18
+ id: "list",
19
+ text: "Items",
20
+ link: "/items",
21
+ },
22
+ {
23
+ id: "item",
24
+ text: "Item 1",
25
+ link: "/items/1",
26
+ },
27
+ ];
28
+
29
+ const actions = [
30
+ {
31
+ id: "new",
32
+ text: "Add",
33
+ },
34
+ ];
35
+
36
+ const tabs: HeaderTab[] = [
37
+ {
38
+ id: "tab1",
39
+ label: "Tab 1",
40
+ },
41
+ {
42
+ id: "tab2",
43
+ label: "Tab 2",
44
+ disabled: true,
45
+ },
46
+ {
47
+ id: "tab3",
48
+ label: "Tab 3",
49
+ },
50
+ ];
51
+
52
+ export default {
53
+ title: "Layouts/HeaderLayout",
54
+ component: HeaderLayout,
55
+ decorators: [withRouter, withFullHeight],
56
+ parameters: {
57
+ layout: "fullscreen",
58
+ },
59
+ } as ComponentMeta<typeof HeaderLayout>;
60
+
61
+ interface HeaderLayoutStoryProps {
62
+ headerProps: HeaderProps;
63
+ contentChildren: ReactElement;
64
+ }
65
+
66
+ const Template = createTemplate(
67
+ ({ headerProps, contentChildren, ...rest }: HeaderLayoutStoryProps) => {
68
+ return (
69
+ <HeaderLayout {...rest}>
70
+ <Header {...headerProps} />
71
+ <Content>{contentChildren}</Content>
72
+ </HeaderLayout>
73
+ );
74
+ },
75
+ );
76
+
77
+ export const Skeleton = Template.bind({});
78
+ Skeleton.args = {
79
+ headerProps: {
80
+ title: "Lorem ipsum",
81
+ subtitle: "Dolor sit amet",
82
+ breadcrumbs,
83
+ actions,
84
+ },
85
+ contentChildren: <SkeletonGrid />,
86
+ };
87
+
88
+ const ListContent = () => {
89
+ const { data } = useDemoData({
90
+ dataSet: "Commodity",
91
+ rowLength: 100,
92
+ maxColumns: 3,
93
+ editable: true,
94
+ });
95
+
96
+ const { rows } = data;
97
+ const columns = data.columns.map(({ field, headerName }) => ({
98
+ id: field,
99
+ label: headerName || "",
100
+ sort: true,
101
+ disablePadding: false,
102
+ numeric: false,
103
+ }));
104
+
105
+ return <TableList data={rows} columns={columns} defaultSort={columns[0].id} defaultOrder="asc" />;
106
+ };
107
+
108
+ export const List = Template.bind({});
109
+ List.args = {
110
+ headerProps: {
111
+ title: "Lorem ipsum",
112
+ subtitle: "Dolor sit amet",
113
+ breadcrumbs,
114
+ actions,
115
+ },
116
+ contentChildren: <ListContent />,
117
+ };
118
+
119
+ export const Details = Template.bind({});
120
+ Details.args = {
121
+ headerProps: {
122
+ title: "Lorem ipsum",
123
+ subtitle: "Dolor sit amet",
124
+ breadcrumbs,
125
+ actions,
126
+ },
127
+ contentChildren: <ObjectDetails model={mockModel} instance={createModelInstance(mockModel)} />,
128
+ };
129
+
130
+ export const Form = Template.bind({});
131
+ Form.args = {
132
+ headerProps: {
133
+ title: "Lorem ipsum",
134
+ subtitle: "Dolor sit amet",
135
+ breadcrumbs,
136
+ actions,
137
+ },
138
+ contentChildren: (
139
+ <ModelForm
140
+ model={mockModel}
141
+ initialValues={createModelInstance(mockModel)}
142
+ saveButtonText="Save"
143
+ onSubmit={action("Save form data")}
144
+ />
145
+ ),
146
+ };
147
+
148
+ const DataTableContent = () => {
149
+ const { data } = useDemoData({
150
+ dataSet: "Commodity",
151
+ rowLength: 100,
152
+ maxColumns: 7,
153
+ editable: true,
154
+ });
155
+
156
+ return <DataGrid rows={data.rows} columns={data.columns} pagination sx={{ height: 400 }} />;
157
+ };
158
+
159
+ export const DataTable = Template.bind({});
160
+ DataTable.args = {
161
+ headerProps: {
162
+ title: "Lorem ipsum",
163
+ subtitle: "Dolor sit amet",
164
+ breadcrumbs,
165
+ actions,
166
+ },
167
+ contentChildren: <DataTableContent />,
168
+ };
169
+
170
+ export const Tabs = Template.bind({});
171
+ Tabs.args = {
172
+ headerProps: {
173
+ title: "Lorem ipsum",
174
+ subtitle: "Dolor sit amet",
175
+ breadcrumbs,
176
+ actions,
177
+ tabs,
178
+ },
179
+ contentChildren: (
180
+ <Box>
181
+ <TabPanel index={0}>
182
+ <Typography>Panel 1</Typography>
183
+ </TabPanel>
184
+ <TabPanel index={[1, 2]}>
185
+ <Typography>Panel 2</Typography>
186
+ </TabPanel>
187
+ <TabPanel index={3}>
188
+ <Typography>Panel 3</Typography>
189
+ </TabPanel>
190
+ </Box>
191
+ ),
192
+ };
193
+
194
+ export const Loading = Template.bind({});
195
+ Loading.args = {
196
+ loading: true,
197
+ headerProps: {
198
+ title: "Lorem ipsum",
199
+ subtitle: "Dolor sit amet",
200
+ breadcrumbs,
201
+ actions,
202
+ },
203
+ contentChildren: <SkeletonGrid />,
204
+ };
@@ -0,0 +1,37 @@
1
+ import React from "react";
2
+ import { HeaderLayout } from "./header-layout";
3
+ import { render, screen } from "../../tests";
4
+ import { Content, Header } from "../../components";
5
+ import { Typography } from "@mui/material";
6
+
7
+ describe("HeaderLayout", () => {
8
+ const renderComponent = () => {
9
+ return render(
10
+ <HeaderLayout>
11
+ <Header title="Lorem ipsum" subtitle="Dolor sit amet" />
12
+ <Content>
13
+ <Typography>Test content</Typography>
14
+ </Content>
15
+ </HeaderLayout>,
16
+ );
17
+ };
18
+
19
+ it("would render the header", () => {
20
+ renderComponent();
21
+
22
+ expect(screen.getByRole("heading", { level: 1, name: /lorem ipsum/i })).toBeInTheDocument();
23
+ expect(screen.getByRole("heading", { level: 2, name: /dolor sit amet/i })).toBeInTheDocument();
24
+ });
25
+
26
+ it("would render a main element", () => {
27
+ renderComponent();
28
+
29
+ expect(screen.getByRole("main")).toBeInTheDocument();
30
+ });
31
+
32
+ it("would render the content", () => {
33
+ renderComponent();
34
+
35
+ expect(screen.getByText(/test content/i)).toBeInTheDocument();
36
+ });
37
+ });
@@ -0,0 +1,23 @@
1
+ import { Box } from "@mui/material";
2
+ import React from "react";
3
+ import { ContentElement, HeaderElement } from "../../components";
4
+ import { LoadingArea } from "../../components/loading-area";
5
+ import { TabProvider } from "../../providers";
6
+
7
+ interface HeaderLayoutProps {
8
+ loading?: boolean;
9
+ children: [HeaderElement, ContentElement];
10
+ }
11
+
12
+ export const HeaderLayout = ({ loading, children }: HeaderLayoutProps) => {
13
+ const [headerElement, contentElement] = children;
14
+
15
+ return (
16
+ <TabProvider>
17
+ <Box display="flex" flexDirection="column" height={1}>
18
+ {headerElement}
19
+ {loading ? <LoadingArea /> : contentElement}
20
+ </Box>
21
+ </TabProvider>
22
+ );
23
+ };
@@ -0,0 +1 @@
1
+ export * from "./header-layout";
@@ -0,0 +1,2 @@
1
+ export * from "./app-bar-with-drawer-layout";
2
+ export * from "./header-layout";
@@ -0,0 +1,2 @@
1
+ export * from "./notification-center";
2
+ export * from "./tab-provider";
@@ -0,0 +1,2 @@
1
+ export * from "./notification-center.provider";
2
+ export * from "./notification-center.context";
@@ -0,0 +1,37 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { AlertColor } from "@mui/material";
3
+ import React from "react";
4
+
5
+ export const NotificationCenterProviderUndefinedError = new Error(
6
+ "NotificationCenterContext.Provider is required and was undefined",
7
+ );
8
+ export type SnackbarContentType =
9
+ | React.ReactElement<any, any>
10
+ | ((key: string) => React.ReactElement<any, any>);
11
+
12
+ export type SnackbarActionType = React.ReactNode | ((key: string) => React.ReactNode);
13
+
14
+ export interface Notification {
15
+ severity: AlertColor;
16
+ title?: string;
17
+ message: string;
18
+ }
19
+
20
+ export interface NotificationCenterProps {
21
+ show(notification: Notification): void;
22
+ hide(): void;
23
+ }
24
+
25
+ export const NotificationCenterContext = React.createContext<NotificationCenterProps | undefined>(
26
+ undefined,
27
+ );
28
+
29
+ export const useNotificationCenter = () => {
30
+ const context = React.useContext(NotificationCenterContext);
31
+
32
+ if (context === undefined) {
33
+ throw NotificationCenterProviderUndefinedError;
34
+ }
35
+
36
+ return context;
37
+ };
@@ -0,0 +1,51 @@
1
+ import { useState } from "react";
2
+ import { Snackbar, Alert, AlertTitle } from "@mui/material";
3
+ import React, { PropsWithChildren } from "react";
4
+ import { Notification, NotificationCenterContext } from "./notification-center.context";
5
+
6
+ export type NotificationCenterProviderProps = PropsWithChildren<{
7
+ autoHideDuration?: number;
8
+ }>;
9
+
10
+ export const NotificationCenterProvider = ({
11
+ children,
12
+ autoHideDuration = 6000,
13
+ }: NotificationCenterProviderProps) => {
14
+ const [notification, setNotification] = useState<Notification | undefined>(undefined);
15
+ const [open, setOpen] = useState(false);
16
+ const show = (notification: Notification) => {
17
+ setNotification(notification);
18
+ setOpen(true);
19
+ };
20
+
21
+ const hide = () => {
22
+ setOpen(false);
23
+ };
24
+
25
+ return (
26
+ <NotificationCenterContext.Provider
27
+ value={{
28
+ show,
29
+ hide,
30
+ }}
31
+ >
32
+ <Snackbar
33
+ open={open}
34
+ autoHideDuration={autoHideDuration}
35
+ onClose={hide}
36
+ anchorOrigin={{ vertical: "top", horizontal: "right" }}
37
+ >
38
+ <Alert
39
+ onClose={hide}
40
+ severity={notification?.severity}
41
+ aria-label={notification?.severity}
42
+ sx={{ width: "100%" }}
43
+ >
44
+ {notification?.title && <AlertTitle>{notification?.title}</AlertTitle>}
45
+ {notification?.message}
46
+ </Alert>
47
+ </Snackbar>
48
+ {children}
49
+ </NotificationCenterContext.Provider>
50
+ );
51
+ };
@@ -0,0 +1,52 @@
1
+ import React from "react";
2
+ import { ComponentMeta } from "@storybook/react";
3
+ import { NotificationCenterProvider } from "./notification-center.provider";
4
+ import { useNotificationCenter } from "./notification-center.context";
5
+ import { ContentPlaceholder } from "../../tests";
6
+ import { Button, Box } from "@mui/material";
7
+
8
+ const DummyError = {
9
+ title: "Internal Server error",
10
+ message: "Unable to save the item",
11
+ };
12
+
13
+ export default {
14
+ title: "Providers/NotificationCenter",
15
+ component: ContentPlaceholder,
16
+ decorators: [
17
+ (Story) => (
18
+ <NotificationCenterProvider>
19
+ <Story />
20
+ </NotificationCenterProvider>
21
+ ),
22
+ ],
23
+ parameters: {
24
+ layout: "fullscreen",
25
+ },
26
+ } as ComponentMeta<typeof ContentPlaceholder>;
27
+
28
+ export const Default = () => {
29
+ const { show, hide } = useNotificationCenter();
30
+
31
+ return (
32
+ <ContentPlaceholder size={3} p={2}>
33
+ <Box pb={2}>
34
+ <Button onClick={() => show({ ...DummyError, severity: "info" })} variant="contained">
35
+ Show info
36
+ </Button>
37
+ <Button onClick={() => show({ ...DummyError, severity: "success" })} variant="contained">
38
+ Show success
39
+ </Button>
40
+ <Button onClick={() => show({ ...DummyError, severity: "warning" })} variant="contained">
41
+ Show warning
42
+ </Button>
43
+ <Button onClick={() => show({ ...DummyError, severity: "error" })} variant="contained">
44
+ Show error
45
+ </Button>
46
+ <Button onClick={hide} variant="contained">
47
+ hide
48
+ </Button>
49
+ </Box>
50
+ </ContentPlaceholder>
51
+ );
52
+ };