@pautena/react-design-system 0.11.1 → 0.11.2

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 (26) 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 +7 -0
  7. package/dist/cjs/types/components/inputs/action/index.d.ts +1 -0
  8. package/dist/esm/index.js +4 -4
  9. package/dist/esm/index.js.map +1 -1
  10. package/dist/esm/types/components/data-display/header/header-title.d.ts +9 -0
  11. package/dist/esm/types/components/data-display/header/header.types.d.ts +10 -2
  12. package/dist/esm/types/components/data-display/header/index.d.ts +1 -0
  13. package/dist/esm/types/components/inputs/action/action-header.d.ts +7 -0
  14. package/dist/esm/types/components/inputs/action/index.d.ts +1 -0
  15. package/dist/index.d.ts +31 -7
  16. package/package.json +1 -1
  17. package/src/components/data-display/header/header-title.tsx +51 -0
  18. package/src/components/data-display/header/header.stories.tsx +40 -1
  19. package/src/components/data-display/header/header.test.tsx +62 -7
  20. package/src/components/data-display/header/header.tsx +7 -8
  21. package/src/components/data-display/header/header.types.ts +10 -2
  22. package/src/components/data-display/header/index.ts +1 -0
  23. package/src/components/inputs/action/action-header.test.tsx +11 -0
  24. package/src/components/inputs/action/action-header.tsx +30 -0
  25. package/src/components/inputs/action/action.tsx +3 -18
  26. 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,7 @@
1
+ type Variant = "primary" | "error" | "warning" | "success";
2
+ export interface ActionHeaderProps {
3
+ variant?: Variant;
4
+ title: string;
5
+ }
6
+ export declare const ActionHeader: ({ title, variant }: ActionHeaderProps) => JSX.Element;
7
+ export {};
@@ -1 +1,2 @@
1
1
  export * from "./action";
2
+ export * from "./action-header";
package/dist/index.d.ts CHANGED
@@ -11,7 +11,7 @@ import * as _emotion_styled from '@emotion/styled';
11
11
  import * as csstype from 'csstype';
12
12
  import * as _mui_material_OverridableComponent from '@mui/material/OverridableComponent';
13
13
  import { SelectInputProps } from '@mui/material/Select/SelectInput';
14
- import { Variant } from '@mui/material/styles/createTypography';
14
+ import { Variant as Variant$1 } from '@mui/material/styles/createTypography';
15
15
  import { LoremUnit } from 'lorem-ipsum/types/src/constants/units';
16
16
 
17
17
  interface ValueEditButtonsProps {
@@ -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[];
@@ -564,9 +581,9 @@ interface ActionProps {
564
581
  variant: ActionVariant;
565
582
  title: string;
566
583
  description?: string | ReactElement;
567
- descriptionVariant?: Variant;
584
+ descriptionVariant?: Variant$1;
568
585
  helperText?: string;
569
- helperTextVariant?: Variant;
586
+ helperTextVariant?: Variant$1;
570
587
  buttonText: string;
571
588
  confirmable?: boolean;
572
589
  confirmTitle?: string;
@@ -576,6 +593,13 @@ interface ActionProps {
576
593
  }
577
594
  declare const Action: ({ variant, title, description, descriptionVariant, buttonText, helperText, helperTextVariant, confirmable, passphrase, confirmTitle, confirmDescription, onAction, }: ActionProps) => JSX.Element;
578
595
 
596
+ type Variant = "primary" | "error" | "warning" | "success";
597
+ interface ActionHeaderProps {
598
+ variant?: Variant;
599
+ title: string;
600
+ }
601
+ declare const ActionHeader: ({ title, variant }: ActionHeaderProps) => JSX.Element;
602
+
579
603
  interface TabData {
580
604
  text: string;
581
605
  icon?: ReactElement;
@@ -683,7 +707,7 @@ declare const ContentPlaceholder: ({ size, children, p }: ContentPlaceholderProp
683
707
  interface LoremIpsumPlaceholderProps {
684
708
  count?: number;
685
709
  units?: LoremUnit;
686
- variant?: Variant;
710
+ variant?: Variant$1;
687
711
  }
688
712
  declare const LoremIpsumPlaceholder: ({ count, units, variant, }: LoremIpsumPlaceholderProps) => JSX.Element;
689
713
 
@@ -1084,4 +1108,4 @@ type TabProviderProps = PropsWithChildren<{
1084
1108
  }>;
1085
1109
  declare const TabProvider: ({ children, initialValue }: TabProviderProps) => JSX.Element;
1086
1110
 
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 };
1111
+ export { Action, ActionHeader, ActionHeaderProps, 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, 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.11.2",
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,30 @@
1
+ import { Typography, useTheme } from "@mui/material";
2
+ import React from "react";
3
+
4
+ type Variant = "primary" | "error" | "warning" | "success";
5
+ export interface ActionHeaderProps {
6
+ variant?: Variant;
7
+ title: string;
8
+ }
9
+
10
+ export const ActionHeader = ({ title, variant = "primary" }: ActionHeaderProps) => {
11
+ const { palette } = useTheme();
12
+
13
+ const titleColor: Record<Variant, string | undefined> = {
14
+ primary: undefined,
15
+ error: "error",
16
+ warning: palette.warning.main,
17
+ success: palette.success.main,
18
+ };
19
+ return (
20
+ <Typography
21
+ color={titleColor[variant]}
22
+ variant="h4"
23
+ pb={1}
24
+ borderBottom={1}
25
+ borderColor="grey.300"
26
+ >
27
+ {title}
28
+ </Typography>
29
+ );
30
+ };
@@ -1,7 +1,8 @@
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
 
@@ -35,7 +36,6 @@ export const Action = ({
35
36
  onAction,
36
37
  }: ActionProps) => {
37
38
  const { isOpen, open, close } = useDialog();
38
- const { palette } = useTheme();
39
39
 
40
40
  const handleClickActionButton = () => {
41
41
  if (confirmable) {
@@ -50,26 +50,11 @@ export const Action = ({
50
50
  close;
51
51
  };
52
52
 
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
53
  return (
61
54
  <>
62
55
  <Grid container spacing={1}>
63
56
  <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>
57
+ <ActionHeader title={title} />
73
58
  </Grid>
74
59
  {description && (
75
60
  <Grid item xs={12}>
@@ -1 +1,2 @@
1
1
  export * from "./action";
2
+ export * from "./action-header";