@pautena/react-design-system 0.11.1 → 0.12.0

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 (28) hide show
  1. package/dist/cjs/index.js +4 -4
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/components/data-display/header/header-title.d.ts +9 -0
  4. package/dist/cjs/types/components/data-display/header/header.types.d.ts +10 -2
  5. package/dist/cjs/types/components/data-display/header/index.d.ts +1 -0
  6. package/dist/cjs/types/components/inputs/action/action-header.d.ts +9 -0
  7. package/dist/cjs/types/components/inputs/action/action.d.ts +2 -1
  8. package/dist/cjs/types/components/inputs/action/index.d.ts +1 -0
  9. package/dist/esm/index.js +4 -4
  10. package/dist/esm/index.js.map +1 -1
  11. package/dist/esm/types/components/data-display/header/header-title.d.ts +9 -0
  12. package/dist/esm/types/components/data-display/header/header.types.d.ts +10 -2
  13. package/dist/esm/types/components/data-display/header/index.d.ts +1 -0
  14. package/dist/esm/types/components/inputs/action/action-header.d.ts +9 -0
  15. package/dist/esm/types/components/inputs/action/action.d.ts +2 -1
  16. package/dist/esm/types/components/inputs/action/index.d.ts +1 -0
  17. package/dist/index.d.ts +32 -6
  18. package/package.json +1 -1
  19. package/src/components/data-display/header/header-title.tsx +51 -0
  20. package/src/components/data-display/header/header.stories.tsx +40 -1
  21. package/src/components/data-display/header/header.test.tsx +62 -7
  22. package/src/components/data-display/header/header.tsx +7 -8
  23. package/src/components/data-display/header/header.types.ts +10 -2
  24. package/src/components/data-display/header/index.ts +1 -0
  25. package/src/components/inputs/action/action-header.test.tsx +11 -0
  26. package/src/components/inputs/action/action-header.tsx +37 -0
  27. package/src/components/inputs/action/action.tsx +5 -18
  28. package/src/components/inputs/action/index.ts +1 -0
@@ -0,0 +1,9 @@
1
+ import { PropsWithChildren } from "react";
2
+ export type HeaderTitleProps = PropsWithChildren<{
3
+ loading?: boolean;
4
+ }>;
5
+ export declare const HeaderTitle: ({ loading, children }: HeaderTitleProps) => JSX.Element;
6
+ export type HeaderSubtitleProps = PropsWithChildren<{
7
+ loading?: boolean;
8
+ }>;
9
+ export declare const HeaderSubtitle: ({ loading, children }: HeaderSubtitleProps) => JSX.Element;
@@ -38,11 +38,19 @@ export type HeaderProps = {
38
38
  /**
39
39
  * Title of the header
40
40
  */
41
- title: string;
41
+ title?: string | ReactElement;
42
+ /**
43
+ * Show a loading indicator in the title position
44
+ */
45
+ loadingTitle?: boolean;
42
46
  /**
43
47
  * Subtitle of the header
44
48
  */
45
- subtitle?: string;
49
+ subtitle?: string | ReactElement;
50
+ /**
51
+ * Show a loading indicator in the subtitle position
52
+ */
53
+ loadingSubtitle?: boolean;
46
54
  /**
47
55
  * Color palete used to render the component
48
56
  */
@@ -1,2 +1,3 @@
1
1
  export * from "./header";
2
2
  export * from "./header.types";
3
+ export * from "./header-title";
@@ -0,0 +1,9 @@
1
+ import { Variant } from "@mui/material/styles/createTypography";
2
+ type ActionVariant = "primary" | "error" | "warning" | "success";
3
+ export interface ActionHeaderProps {
4
+ variant?: ActionVariant;
5
+ title: string;
6
+ titleVariant?: Extract<Variant, "h4" | "h5" | "h6">;
7
+ }
8
+ export declare const ActionHeader: ({ title, titleVariant, variant, }: ActionHeaderProps) => JSX.Element;
9
+ export {};
@@ -4,6 +4,7 @@ export type ActionVariant = "primary" | "error" | "warning" | "success";
4
4
  export interface ActionProps {
5
5
  variant: ActionVariant;
6
6
  title: string;
7
+ titleVariant?: Extract<Variant, "h4" | "h5" | "h6">;
7
8
  description?: string | ReactElement;
8
9
  descriptionVariant?: Variant;
9
10
  helperText?: string;
@@ -15,4 +16,4 @@ export interface ActionProps {
15
16
  passphrase?: string;
16
17
  onAction: () => void;
17
18
  }
18
- export declare const Action: ({ variant, title, description, descriptionVariant, buttonText, helperText, helperTextVariant, confirmable, passphrase, confirmTitle, confirmDescription, onAction, }: ActionProps) => JSX.Element;
19
+ export declare const Action: ({ variant, title, titleVariant, description, descriptionVariant, buttonText, helperText, helperTextVariant, confirmable, passphrase, confirmTitle, confirmDescription, onAction, }: ActionProps) => JSX.Element;
@@ -1 +1,2 @@
1
1
  export * from "./action";
2
+ export * from "./action-header";
package/dist/index.d.ts CHANGED
@@ -270,11 +270,19 @@ type HeaderProps = {
270
270
  /**
271
271
  * Title of the header
272
272
  */
273
- title: string;
273
+ title?: string | ReactElement;
274
+ /**
275
+ * Show a loading indicator in the title position
276
+ */
277
+ loadingTitle?: boolean;
274
278
  /**
275
279
  * Subtitle of the header
276
280
  */
277
- subtitle?: string;
281
+ subtitle?: string | ReactElement;
282
+ /**
283
+ * Show a loading indicator in the subtitle position
284
+ */
285
+ loadingSubtitle?: boolean;
278
286
  /**
279
287
  * Color palete used to render the component
280
288
  */
@@ -318,6 +326,15 @@ type HeaderElement = ReactElement<HeaderProps, HeaderComponent>;
318
326
  */
319
327
  declare const Header: HeaderComponent;
320
328
 
329
+ type HeaderTitleProps = PropsWithChildren<{
330
+ loading?: boolean;
331
+ }>;
332
+ declare const HeaderTitle: ({ loading, children }: HeaderTitleProps) => JSX.Element;
333
+ type HeaderSubtitleProps = PropsWithChildren<{
334
+ loading?: boolean;
335
+ }>;
336
+ declare const HeaderSubtitle: ({ loading, children }: HeaderSubtitleProps) => JSX.Element;
337
+
321
338
  type BoardProps = PropsWithChildren<{
322
339
  markdown?: string;
323
340
  content?: string | string[];
@@ -559,10 +576,11 @@ type TextFieldProps = TextFieldProps$1 & {
559
576
  };
560
577
  declare const TextField: ({ id: overrideId, label, InputLabelProps, InputProps, fetching, loading, helperText, hexColor, size, fullWidth, sx, ...rest }: TextFieldProps) => JSX.Element;
561
578
 
562
- type ActionVariant = "primary" | "error" | "warning" | "success";
579
+ type ActionVariant$1 = "primary" | "error" | "warning" | "success";
563
580
  interface ActionProps {
564
- variant: ActionVariant;
581
+ variant: ActionVariant$1;
565
582
  title: string;
583
+ titleVariant?: Extract<Variant, "h4" | "h5" | "h6">;
566
584
  description?: string | ReactElement;
567
585
  descriptionVariant?: Variant;
568
586
  helperText?: string;
@@ -574,7 +592,15 @@ interface ActionProps {
574
592
  passphrase?: string;
575
593
  onAction: () => void;
576
594
  }
577
- declare const Action: ({ variant, title, description, descriptionVariant, buttonText, helperText, helperTextVariant, confirmable, passphrase, confirmTitle, confirmDescription, onAction, }: ActionProps) => JSX.Element;
595
+ declare const Action: ({ variant, title, titleVariant, description, descriptionVariant, buttonText, helperText, helperTextVariant, confirmable, passphrase, confirmTitle, confirmDescription, onAction, }: ActionProps) => JSX.Element;
596
+
597
+ type ActionVariant = "primary" | "error" | "warning" | "success";
598
+ interface ActionHeaderProps {
599
+ variant?: ActionVariant;
600
+ title: string;
601
+ titleVariant?: Extract<Variant, "h4" | "h5" | "h6">;
602
+ }
603
+ declare const ActionHeader: ({ title, titleVariant, variant, }: ActionHeaderProps) => JSX.Element;
578
604
 
579
605
  interface TabData {
580
606
  text: string;
@@ -1084,4 +1110,4 @@ type TabProviderProps = PropsWithChildren<{
1084
1110
  }>;
1085
1111
  declare const TabProvider: ({ children, initialValue }: TabProviderProps) => JSX.Element;
1086
1112
 
1087
- export { Action, ActionProps, ActionVariant, ArrayFieldType, ArrayGroupField, ArrayInstanceType, Autocomplete, AutocompleteProps, BaseFieldType, BaseValueProps, BasicModelInstance, Board, BoardProps, BootstrapDialog, BootstrapDialogDialogProps, Bullet, BulletProps, BulletVariant, CenterContainer, CenterContainerProps, ConfirmDialog, ConfirmDialogProps, Content, ContentComponent, ContentElement, ContentPlaceholder, ContentPlaceholderProps, ContentProps, DateRangeCalendar, DateRangeCalendarProps, DateRangePicker, DateRangePickerProps, DefaultPlaceholder, DialogAction, Drawer, DrawerAppBar, DrawerAppBarComponent, DrawerAppBarElement, DrawerAppBarProps, DrawerComponent, DrawerContent, DrawerContentComponent, DrawerContentElement, DrawerContentProps, DrawerContext, DrawerContextProps, DrawerElement, DrawerHeader, DrawerItemAvatar, DrawerItemBullet, DrawerItemLabel, DrawerLayout, DrawerLayoutProps, DrawerMain, DrawerMainProps, DrawerNavigation, DrawerNavigationItem, DrawerNavigationItemCollapsable, DrawerNavigationItemLink, DrawerNavigationSection, DrawerProps, DrawerProvider, DrawerProviderProps, DrawerSection, DrawerSectionProps, DrawerSize, DrawerState, DrawerSubheader, DrawerSubheaderProps, DrawerVariant, EditInputType, EditableValueProps, EnhancedRemoteTable, EnhancedRemoteTableProps, EnhancedTable, EnhancedTableHead, ExpandableAlert, ExpandableAlertProps, FieldType, FormDialog, FormDialogProps, GroupField, GroupInstanceType, GroupValueCard, GroupValueCardProps, HeadCell, Header, HeaderAction, HeaderActionVariant, HeaderBreadcrumb, HeaderComponent, HeaderElement, HeaderLayout, HeaderLayoutError, HeaderLayoutProps, HeaderNavigationButton, HeaderPreset, HeaderProps, HeaderTab, IdleRequest, Label, LabelProps, LabelVariant, ListPanel, ListPanelContext, ListPanelContextProvider, ListPanelItem, ListPanelProps, LoadingArea, LoadingRequest, LoremIpsumPlaceholder, LoremIpsumPlaceholderProps, Markdown, MarkdownProps, Model, ModelField, ModelFieldTypes, ModelForm, ModelFormProps, ModelRouter, ModelRouterProps, Notification, NotificationCenterContext, NotificationCenterProps, NotificationCenterProvider, NotificationCenterProviderProps, NotificationCenterProviderUndefinedError, NotifyWhenValueChangesOptions, ObjectDetails, ObjectDetailsProps, Order, Placeholder, PlaceholderAction, PlaceholderIcon, PlaceholderIconArgs, PlaceholderProps, QueryContainer, QueryContainerError, QueryContainerProps, QueryContainerSuccess, RequestState, Select, SelectProps, SelectSize, SignIn, SignInProps, SingleFieldType, SkeletonCard, SkeletonCardProps, SkeletonGrid, SkeletonGridProps, SuccessRequest, TabCard, TabCardPanel, TabCardPanelProps, TabCardProps, TabContext, TabContextProvider, TabData, TabPanel, TabProvider, TableList, TableListProps, TableRowOption, TextField, TextFieldProps, UndefinedProvider, ValueBoolean, ValueBooleanProps, ValueCard, ValueCardProps, ValueDatetime, ValueDatetimeProps, ValueEditButton, ValueEditButtonProps, ValueEditButtons, ValueEditButtonsProps, ValueItem, ValueItemComponent, ValueItemElement, ValueItemProps, ValueLabel, ValueLabelProps, ValueRating, ValueRatingProps, ValueText, ValueTextProps, bulletClasses, getDrawerItemColors, getFormData, getRandomItem, labelClasses, markdownDefaultOptions, newArrayWithSize, newBreakpointsCounter, newInstanceFromValuesOrZeroValue, useDialog, useDrawer, useEditableValueDisplay, useGetDefaultThemeColor, useListPanel, useNotificationCenter, useNotifyWhenValueChanges, useTab, valueItemClasses };
1113
+ export { Action, ActionHeader, ActionHeaderProps, ActionProps, ActionVariant$1 as ActionVariant, ArrayFieldType, ArrayGroupField, ArrayInstanceType, Autocomplete, AutocompleteProps, BaseFieldType, BaseValueProps, BasicModelInstance, Board, BoardProps, BootstrapDialog, BootstrapDialogDialogProps, Bullet, BulletProps, BulletVariant, CenterContainer, CenterContainerProps, ConfirmDialog, ConfirmDialogProps, Content, ContentComponent, ContentElement, ContentPlaceholder, ContentPlaceholderProps, ContentProps, DateRangeCalendar, DateRangeCalendarProps, DateRangePicker, DateRangePickerProps, DefaultPlaceholder, DialogAction, Drawer, DrawerAppBar, DrawerAppBarComponent, DrawerAppBarElement, DrawerAppBarProps, DrawerComponent, DrawerContent, DrawerContentComponent, DrawerContentElement, DrawerContentProps, DrawerContext, DrawerContextProps, DrawerElement, DrawerHeader, DrawerItemAvatar, DrawerItemBullet, DrawerItemLabel, DrawerLayout, DrawerLayoutProps, DrawerMain, DrawerMainProps, DrawerNavigation, DrawerNavigationItem, DrawerNavigationItemCollapsable, DrawerNavigationItemLink, DrawerNavigationSection, DrawerProps, DrawerProvider, DrawerProviderProps, DrawerSection, DrawerSectionProps, DrawerSize, DrawerState, DrawerSubheader, DrawerSubheaderProps, DrawerVariant, EditInputType, EditableValueProps, EnhancedRemoteTable, EnhancedRemoteTableProps, EnhancedTable, EnhancedTableHead, ExpandableAlert, ExpandableAlertProps, FieldType, FormDialog, FormDialogProps, GroupField, GroupInstanceType, GroupValueCard, GroupValueCardProps, HeadCell, Header, HeaderAction, HeaderActionVariant, HeaderBreadcrumb, HeaderComponent, HeaderElement, HeaderLayout, HeaderLayoutError, HeaderLayoutProps, HeaderNavigationButton, HeaderPreset, HeaderProps, HeaderSubtitle, HeaderSubtitleProps, HeaderTab, HeaderTitle, HeaderTitleProps, IdleRequest, Label, LabelProps, LabelVariant, ListPanel, ListPanelContext, ListPanelContextProvider, ListPanelItem, ListPanelProps, LoadingArea, LoadingRequest, LoremIpsumPlaceholder, LoremIpsumPlaceholderProps, Markdown, MarkdownProps, Model, ModelField, ModelFieldTypes, ModelForm, ModelFormProps, ModelRouter, ModelRouterProps, Notification, NotificationCenterContext, NotificationCenterProps, NotificationCenterProvider, NotificationCenterProviderProps, NotificationCenterProviderUndefinedError, NotifyWhenValueChangesOptions, ObjectDetails, ObjectDetailsProps, Order, Placeholder, PlaceholderAction, PlaceholderIcon, PlaceholderIconArgs, PlaceholderProps, QueryContainer, QueryContainerError, QueryContainerProps, QueryContainerSuccess, RequestState, Select, SelectProps, SelectSize, SignIn, SignInProps, SingleFieldType, SkeletonCard, SkeletonCardProps, SkeletonGrid, SkeletonGridProps, SuccessRequest, TabCard, TabCardPanel, TabCardPanelProps, TabCardProps, TabContext, TabContextProvider, TabData, TabPanel, TabProvider, TableList, TableListProps, TableRowOption, TextField, TextFieldProps, UndefinedProvider, ValueBoolean, ValueBooleanProps, ValueCard, ValueCardProps, ValueDatetime, ValueDatetimeProps, ValueEditButton, ValueEditButtonProps, ValueEditButtons, ValueEditButtonsProps, ValueItem, ValueItemComponent, ValueItemElement, ValueItemProps, ValueLabel, ValueLabelProps, ValueRating, ValueRatingProps, ValueText, ValueTextProps, bulletClasses, getDrawerItemColors, getFormData, getRandomItem, labelClasses, markdownDefaultOptions, newArrayWithSize, newBreakpointsCounter, newInstanceFromValuesOrZeroValue, useDialog, useDrawer, useEditableValueDisplay, useGetDefaultThemeColor, useListPanel, useNotificationCenter, useNotifyWhenValueChanges, useTab, valueItemClasses };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pautena/react-design-system",
3
- "version": "0.11.1",
3
+ "version": "0.12.0",
4
4
  "description": "My custom design system on top of MUI",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -0,0 +1,51 @@
1
+ import { CircularProgress, Typography, useTheme } from "@mui/material";
2
+ import React from "react";
3
+ import { PropsWithChildren } from "react";
4
+
5
+ export type HeaderTitleProps = PropsWithChildren<{ loading?: boolean }>;
6
+
7
+ export const HeaderTitle = ({ loading, children }: HeaderTitleProps) => {
8
+ const { typography } = useTheme();
9
+
10
+ if (loading) {
11
+ return (
12
+ <CircularProgress color="inherit" size={typography.h4.fontSize} aria-label="title loading" />
13
+ );
14
+ }
15
+
16
+ if (typeof children === "string") {
17
+ return (
18
+ <Typography variant="h4" role="heading" aria-level={1}>
19
+ {children}
20
+ </Typography>
21
+ );
22
+ }
23
+
24
+ return <>{children}</>;
25
+ };
26
+
27
+ export type HeaderSubtitleProps = PropsWithChildren<{ loading?: boolean }>;
28
+
29
+ export const HeaderSubtitle = ({ loading, children }: HeaderSubtitleProps) => {
30
+ const { typography } = useTheme();
31
+
32
+ if (loading) {
33
+ return (
34
+ <CircularProgress
35
+ color="inherit"
36
+ size={typography.body1.fontSize}
37
+ aria-label="subtitle loading"
38
+ />
39
+ );
40
+ }
41
+
42
+ if (typeof children === "string") {
43
+ return (
44
+ <Typography variant="body1" role="heading" aria-level={2}>
45
+ {children}
46
+ </Typography>
47
+ );
48
+ }
49
+
50
+ return <>{children}</>;
51
+ };
@@ -9,6 +9,7 @@ import { Content } from "~/components/containers";
9
9
  import { Box, Typography } from "@mui/material";
10
10
  import { TabPanel } from "~/components/navigation/tab-panel";
11
11
  import { Route, Routes, useLocation } from "react-router-dom";
12
+ import { Label } from "../label";
12
13
 
13
14
  export default {
14
15
  title: "Components/Data Display/Header",
@@ -24,7 +25,7 @@ export const Default: Story = {
24
25
  args: {
25
26
  title: "Lorem ipsum",
26
27
  subtitle: "Dolor sit amet",
27
- preset: "inherit",
28
+ preset: "default",
28
29
  breadcrumbs,
29
30
  actions,
30
31
  },
@@ -36,6 +37,44 @@ export const OnlyTitle: Story = {
36
37
  },
37
38
  };
38
39
 
40
+ export const CustomTitle: Story = {
41
+ args: {
42
+ ...Default.args,
43
+ title: (
44
+ <Box display="flex" flexDirection="row" alignItems="center">
45
+ <Typography variant="h6">Lorem ipsum</Typography>
46
+ <Label variant="primary" text="4 items" sx={{ ml: 1 }} />
47
+ </Box>
48
+ ),
49
+ },
50
+ };
51
+
52
+ export const TitleLoading: Story = {
53
+ args: {
54
+ ...Default.args,
55
+ loadingTitle: true,
56
+ },
57
+ };
58
+
59
+ export const CustomSubtitle: Story = {
60
+ args: {
61
+ ...Default.args,
62
+ subtitle: (
63
+ <Box display="flex" flexDirection="row" alignItems="center">
64
+ <Typography variant="body2">Dolor sit amet</Typography>
65
+ <Label variant="error" text="since yesterday" sx={{ ml: 1 }} />
66
+ </Box>
67
+ ),
68
+ },
69
+ };
70
+
71
+ export const SubtitleLoading: Story = {
72
+ args: {
73
+ ...Default.args,
74
+ loadingSubtitle: true,
75
+ },
76
+ };
77
+
39
78
  export const ColorInherit: Story = {
40
79
  args: {
41
80
  title: "Lorem ipsum",
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { ReactElement } from "react";
2
2
  import { render, screen } from "~/tests/testing-library";
3
3
  import userEvent from "@testing-library/user-event";
4
4
  import { Header } from "./header";
@@ -13,12 +13,17 @@ import {
13
13
  import { breadcrumbs, actions as actionsData, tabs, linkedTabs } from "./header.dummy";
14
14
  import { TabProvider } from "../../../providers";
15
15
  import { WithLinkedTabs, WithPanelTabs } from "./header.stories";
16
+ import { Typography } from "@mui/material";
17
+ import { Box } from "@mui/system";
18
+ import { Label } from "../label";
16
19
 
17
20
  const actions = actionsData.map((a) => ({ ...a, onClick: a.onClick && vi.fn() }));
18
21
 
19
22
  const renderInstance = ({
20
23
  title = "Lorem ipsum",
24
+ loadingTitle,
21
25
  subtitle,
26
+ loadingSubtitle,
22
27
  preset = "default",
23
28
  breadcrumbs,
24
29
  actions,
@@ -27,8 +32,10 @@ const renderInstance = ({
27
32
  selectedTab,
28
33
  navigationButton,
29
34
  }: {
30
- title?: string;
31
- subtitle?: string | undefined;
35
+ title?: string | ReactElement;
36
+ subtitle?: string | ReactElement;
37
+ loadingTitle?: boolean;
38
+ loadingSubtitle?: boolean;
32
39
  preset?: HeaderPreset;
33
40
  breadcrumbs?: HeaderBreadcrumb[];
34
41
  actions?: HeaderAction[];
@@ -41,7 +48,9 @@ const renderInstance = ({
41
48
  <TabProvider initialValue={selectedTab}>
42
49
  <Header
43
50
  title={title}
51
+ loadingTitle={loadingTitle}
44
52
  subtitle={subtitle}
53
+ loadingSubtitle={loadingSubtitle}
45
54
  preset={preset}
46
55
  breadcrumbs={breadcrumbs}
47
56
  actions={actions}
@@ -56,14 +65,39 @@ const renderInstance = ({
56
65
  };
57
66
 
58
67
  describe("Header", () => {
59
- it("renders the title", () => {
60
- renderInstance({ title: "Lorem ipsum" });
68
+ describe("title", () => {
69
+ it("should render the title", () => {
70
+ renderInstance({ title: "Lorem ipsum" });
61
71
 
62
- expect(screen.getByRole("heading", { level: 1, name: /lorem ipsum/i })).toBeInTheDocument();
72
+ expect(screen.getByRole("heading", { level: 1, name: /lorem ipsum/i })).toBeInTheDocument();
73
+ });
74
+
75
+ it("should render the custom title", () => {
76
+ renderInstance({
77
+ title: (
78
+ <Box display="flex" flexDirection="row" alignItems="center">
79
+ <Typography variant="h6">custom title</Typography>
80
+ <Label variant="primary" text="4 items" sx={{ ml: 1 }} />
81
+ </Box>
82
+ ),
83
+ });
84
+
85
+ expect(screen.getByRole("heading", { level: 6, name: /custom title/i })).toBeVisible();
86
+ expect(screen.getByText(/4 items/i)).toBeVisible();
87
+ });
88
+
89
+ it("should render a loading indicator if title is loading", () => {
90
+ renderInstance({ title: "Lorem ipsum", loadingTitle: true });
91
+
92
+ expect(
93
+ screen.queryByRole("heading", { level: 1, name: /lorem ipsum/i }),
94
+ ).not.toBeInTheDocument();
95
+ expect(screen.queryByRole("progressbar", { name: /title loading/i })).toBeInTheDocument();
96
+ });
63
97
  });
64
98
 
65
99
  describe("subtitle", () => {
66
- it("should renders when is set", () => {
100
+ it("should render when is set", () => {
67
101
  renderInstance({ subtitle: "sit amet" });
68
102
 
69
103
  expect(screen.queryByRole("heading", { level: 2, name: /sit amet/i })).toBeInTheDocument();
@@ -74,6 +108,27 @@ describe("Header", () => {
74
108
 
75
109
  expect(screen.queryByRole("heading", { level: 2 })).not.toBeInTheDocument();
76
110
  });
111
+
112
+ it("should render the custom title", () => {
113
+ renderInstance({
114
+ subtitle: (
115
+ <Box display="flex" flexDirection="row" alignItems="center">
116
+ <Typography variant="body2">Dolor sit amet</Typography>
117
+ <Label variant="error" text="since yesterday" sx={{ ml: 1 }} />
118
+ </Box>
119
+ ),
120
+ });
121
+
122
+ expect(screen.getByText(/dolor sit amet/i)).toBeVisible();
123
+ expect(screen.getByText(/since yesterday/i)).toBeVisible();
124
+ });
125
+
126
+ it("should render a loading indicator if title is loading", () => {
127
+ renderInstance({ subtitle: "sit amet", loadingSubtitle: true });
128
+
129
+ expect(screen.queryByRole("heading", { level: 2 })).not.toBeInTheDocument();
130
+ expect(screen.queryByRole("progressbar", { name: /subtitle loading/i })).toBeInTheDocument();
131
+ });
77
132
  });
78
133
 
79
134
  describe("breadcrumbs", () => {
@@ -14,14 +14,17 @@ import { useGetDefaultThemeColor } from "../../../utils";
14
14
  import { HeaderComponent, HeaderPreset, HeaderProps } from "./header.types";
15
15
  import { useTab } from "~/providers";
16
16
  import { useLocation } from "react-router-dom";
17
+ import { HeaderSubtitle, HeaderTitle } from "./header-title";
17
18
 
18
19
  /**
19
20
  * Section used to explain give basic information about the page
20
21
  * and put the main actions
21
22
  */
22
23
  export const Header: HeaderComponent = ({
23
- title,
24
+ title = "",
25
+ loadingTitle,
24
26
  subtitle,
27
+ loadingSubtitle,
25
28
  preset = "default",
26
29
  actionsVariant = "outlined",
27
30
  breadcrumbs,
@@ -93,13 +96,9 @@ export const Header: HeaderComponent = ({
93
96
  ))}
94
97
  </Breadcrumbs>
95
98
  )}
96
- <Typography variant="h4" role="heading" aria-level={1}>
97
- {title}
98
- </Typography>
99
- {subtitle && (
100
- <Typography variant="body1" role="heading" aria-level={2}>
101
- {subtitle}
102
- </Typography>
99
+ <HeaderTitle loading={loadingTitle}>{title}</HeaderTitle>
100
+ {(subtitle || loadingSubtitle) && (
101
+ <HeaderSubtitle loading={loadingSubtitle}>{subtitle}</HeaderSubtitle>
103
102
  )}
104
103
  </Box>
105
104
  {actions && (
@@ -44,11 +44,19 @@ export type HeaderProps = {
44
44
  /**
45
45
  * Title of the header
46
46
  */
47
- title: string;
47
+ title?: string | ReactElement;
48
+ /**
49
+ * Show a loading indicator in the title position
50
+ */
51
+ loadingTitle?: boolean;
48
52
  /**
49
53
  * Subtitle of the header
50
54
  */
51
- subtitle?: string;
55
+ subtitle?: string | ReactElement;
56
+ /**
57
+ * Show a loading indicator in the subtitle position
58
+ */
59
+ loadingSubtitle?: boolean;
52
60
  /**
53
61
  * Color palete used to render the component
54
62
  */
@@ -1,2 +1,3 @@
1
1
  export * from "./header";
2
2
  export * from "./header.types";
3
+ export * from "./header-title";
@@ -0,0 +1,11 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import { ActionHeader } from "./action-header";
3
+ import React from "react";
4
+
5
+ describe("ActionHeader", () => {
6
+ it("should render the title", () => {
7
+ render(<ActionHeader title="Lorem ipsum" />);
8
+
9
+ expect(screen.getByRole("heading", { name: /lorem ipsum/i, level: 4 }));
10
+ });
11
+ });
@@ -0,0 +1,37 @@
1
+ import { Typography, useTheme } from "@mui/material";
2
+ import { Variant } from "@mui/material/styles/createTypography";
3
+ import React from "react";
4
+
5
+ type ActionVariant = "primary" | "error" | "warning" | "success";
6
+
7
+ export interface ActionHeaderProps {
8
+ variant?: ActionVariant;
9
+ title: string;
10
+ titleVariant?: Extract<Variant, "h4" | "h5" | "h6">;
11
+ }
12
+
13
+ export const ActionHeader = ({
14
+ title,
15
+ titleVariant = "h4",
16
+ variant = "primary",
17
+ }: ActionHeaderProps) => {
18
+ const { palette } = useTheme();
19
+
20
+ const titleColor: Record<ActionVariant, string | undefined> = {
21
+ primary: undefined,
22
+ error: "error",
23
+ warning: palette.warning.main,
24
+ success: palette.success.main,
25
+ };
26
+ return (
27
+ <Typography
28
+ color={titleColor[variant]}
29
+ variant={titleVariant}
30
+ pb={1}
31
+ borderBottom={1}
32
+ borderColor="grey.300"
33
+ >
34
+ {title}
35
+ </Typography>
36
+ );
37
+ };
@@ -1,13 +1,15 @@
1
- import { Grid, Button, Typography, useTheme, DialogContentText } from "@mui/material";
1
+ import { Grid, Button, Typography, DialogContentText } from "@mui/material";
2
2
  import { Variant } from "@mui/material/styles/createTypography";
3
3
  import React, { ReactElement } from "react";
4
4
  import { ConfirmDialog, useDialog } from "~/components/dialogs";
5
+ import { ActionHeader } from "./action-header";
5
6
 
6
7
  export type ActionVariant = "primary" | "error" | "warning" | "success";
7
8
 
8
9
  export interface ActionProps {
9
10
  variant: ActionVariant;
10
11
  title: string;
12
+ titleVariant?: Extract<Variant, "h4" | "h5" | "h6">;
11
13
  description?: string | ReactElement;
12
14
  descriptionVariant?: Variant;
13
15
  helperText?: string;
@@ -23,6 +25,7 @@ export interface ActionProps {
23
25
  export const Action = ({
24
26
  variant = "primary",
25
27
  title,
28
+ titleVariant = "h4",
26
29
  description,
27
30
  descriptionVariant = "body2",
28
31
  buttonText,
@@ -35,7 +38,6 @@ export const Action = ({
35
38
  onAction,
36
39
  }: ActionProps) => {
37
40
  const { isOpen, open, close } = useDialog();
38
- const { palette } = useTheme();
39
41
 
40
42
  const handleClickActionButton = () => {
41
43
  if (confirmable) {
@@ -50,26 +52,11 @@ export const Action = ({
50
52
  close;
51
53
  };
52
54
 
53
- const titleColor: Record<ActionVariant, string | undefined> = {
54
- primary: undefined,
55
- error: "error",
56
- warning: palette.warning.main,
57
- success: palette.success.main,
58
- };
59
-
60
55
  return (
61
56
  <>
62
57
  <Grid container spacing={1}>
63
58
  <Grid item xs={12} mb={2}>
64
- <Typography
65
- color={titleColor[variant]}
66
- variant="h4"
67
- pb={1}
68
- borderBottom={1}
69
- borderColor="grey.300"
70
- >
71
- {title}
72
- </Typography>
59
+ <ActionHeader title={title} titleVariant={titleVariant} />
73
60
  </Grid>
74
61
  {description && (
75
62
  <Grid item xs={12}>
@@ -1 +1,2 @@
1
1
  export * from "./action";
2
+ export * from "./action-header";