Skip to content

Commit 65f296b

Browse files
authored
feat(ErrorState): add some customization capabilities (#448)
* feat(ErrorState): add some customization capabilities * fix linting and add headingLevel overrides to relevant components
1 parent 374d47d commit 65f296b

File tree

11 files changed

+4456
-3442
lines changed

11 files changed

+4456
-3442
lines changed

package-lock.json

Lines changed: 4372 additions & 3427 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/module/patternfly-docs/content/extensions/component-groups/examples/ErrorState/ErrorState.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@ sourceLink: https://github.com/patternfly/react-component-groups/blob/main/packa
1515
---
1616

1717
import ErrorState from "@patternfly/react-component-groups/dist/dynamic/ErrorState";
18+
import { PathMissingIcon } from '@patternfly/react-icons/dist/dynamic/icons/path-missing-icon';
1819

1920
The **error state** component repurposes the `EmptyState` component to display an error to users. To further customize this component, you can also utilize all properties of the [empty state component](/components/empty-state), with the `exception` of `children`.
2021

2122
## Examples
2223

2324
### Basic error state
2425

25-
To provide users with error details, a basic error state should contain an appropriate and informative `titleText` and `bodyText`.
26+
To provide users with error details, a basic error state should contain an appropriate and informative `titleText` and `bodyText`. Error state provides a default action to navigate back to the previous page, or the home page in the empty state's footer.
2627

2728
```js file="./ErrorStateExample.tsx"
2829

@@ -35,3 +36,11 @@ To override the default action button, specify your own using `customFooter`.
3536
```js file="./ErrorStateFooterExample.tsx"
3637

3738
```
39+
40+
### Customizations using EmptyState props
41+
42+
All properties of the [empty state component](/components/empty-state) are spread to the error state component group. Passing `status='none'` to the error state will cause the icon color to be grey.
43+
44+
```js file="./ErrorStateExtraProps.tsx"
45+
46+
```
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react';
2+
import ErrorState from "@patternfly/react-component-groups/dist/dynamic/ErrorState";
3+
import { PathMissingIcon } from '@patternfly/react-icons/dist/dynamic/icons/path-missing-icon';
4+
5+
export const BasicExample: React.FunctionComponent = () => (
6+
<ErrorState
7+
titleText='Sample error title'
8+
bodyText='Sample error description'
9+
headingLevel='h2'
10+
icon={PathMissingIcon}
11+
status="none"
12+
customFooter="Any other details in a custom footer."
13+
/>
14+
);

packages/module/src/ErrorBoundary/ErrorBoundary.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ export interface ErrorBoundaryProps {
2929
children?: React.ReactNode;
3030
/** Custom OUIA ID */
3131
ouiaId?: string | number;
32+
/** The heading level to use on the header title, default is h1 */
33+
headerTitleHeadingLevel?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
34+
/** The heading level to use on the error title, default is h2 */
35+
errorTitleHeadingLevel?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
3236
}
3337

3438
export interface ErrorBoundaryState {
@@ -45,9 +49,20 @@ interface ErrorPageProps extends ErrorBoundaryProps {
4549
classes: Record<string | number | symbol, string>;
4650
}
4751

48-
export const ErrorBoundary: React.FunctionComponent<ErrorBoundaryProps> = (props: ErrorBoundaryProps) => {
52+
export const ErrorBoundary: React.FunctionComponent<ErrorBoundaryProps> = ({
53+
headerTitleHeadingLevel = "h1",
54+
errorTitleHeadingLevel = "h2",
55+
...props
56+
}: ErrorBoundaryProps) => {
4957
const classes = useStyles();
50-
return <ErrorBoundaryContent classes={classes} {...props} />
58+
return (
59+
<ErrorBoundaryContent
60+
classes={classes}
61+
headerTitleHeadingLevel={headerTitleHeadingLevel}
62+
errorTitleHeadingLevel={errorTitleHeadingLevel}
63+
{...props}
64+
/>
65+
);
5166
}
5267

5368
// As of time of writing, React only supports error boundaries in class components
@@ -82,7 +97,7 @@ class ErrorBoundaryContent extends React.Component<ErrorPageProps, ErrorBoundary
8297
}
8398

8499
render() {
85-
const { ouiaId = 'ErrorBoundary', ...props } = this.props;
100+
const { ouiaId = 'ErrorBoundary', errorTitleHeadingLevel, headerTitleHeadingLevel, ...props } = this.props;
86101

87102
if (this.state.hasError) {
88103
if (this.props.silent) {
@@ -91,9 +106,10 @@ class ErrorBoundaryContent extends React.Component<ErrorPageProps, ErrorBoundary
91106

92107
return (
93108
<div data-ouia-component-id={ouiaId}>
94-
{props?.headerTitle && <Title headingLevel="h1" size="2xl" ouiaId={`${ouiaId}-title`}>{props.headerTitle}</Title>}
109+
{props?.headerTitle && <Title headingLevel={headerTitleHeadingLevel || 'h1'} size="2xl" ouiaId={`${ouiaId}-title`}>{props.headerTitle}</Title>}
95110
<ErrorState
96111
titleText={props.errorTitle}
112+
headingLevel={errorTitleHeadingLevel}
97113
bodyText={
98114
<>
99115
<span>{props.errorDescription}</span>

packages/module/src/ErrorState/ErrorState.test.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,11 @@ describe('ErrorState component', () => {
2020
expect(screen.getByText('Go to home page')).toBeVisible();
2121
});
2222

23+
it('should spread empty state props', () => {
24+
render(<ErrorState headingLevel="h2" variant="xs" data-testid="test"/>);
25+
26+
expect(screen.getByRole('heading', { level: 2 })).toBeInTheDocument();
27+
expect(screen.getByTestId('test')).toHaveClass('pf-m-xs');
28+
});
29+
2330
});

packages/module/src/ErrorState/ErrorState.tsx

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,13 @@ import { createUseStyles } from 'react-jss'
1313
import React from 'react';
1414

1515
const useStyles = createUseStyles({
16-
errorIcon: {
17-
fill: 'var(--pf-t--global--color--status--danger--default)',
18-
},
1916
errorDescription: {
2017
margin: 'auto'
2118
}
2219
})
2320

2421
/** extends EmptyStateProps */
25-
export interface ErrorStateProps extends Omit<EmptyStateProps, 'children' | 'titleText'> {
22+
export interface ErrorStateProps extends Omit<EmptyStateProps, 'children' | 'titleText' | 'status'> {
2623
/** Title of the error. */
2724
titleText?: string;
2825
/** A description of the error, if no body text is provided then it will be set to the defaultBodyText. */
@@ -33,12 +30,31 @@ export interface ErrorStateProps extends Omit<EmptyStateProps, 'children' | 'tit
3330
customFooter?: React.ReactNode;
3431
/** ErrorState OUIA ID */
3532
ouiaId?: string | number;
33+
/** Status of the error message. */
34+
status?: 'danger' | 'warning' | 'success' | 'info' | 'custom' | 'none';
3635
}
3736

38-
const ErrorState: React.FunctionComponent<ErrorStateProps> = ({ titleText = 'Something went wrong', bodyText, defaultBodyText, customFooter, ouiaId = "ErrorState", ...props }: ErrorStateProps) => {
37+
const ErrorState: React.FunctionComponent<ErrorStateProps> = ({
38+
titleText = 'Something went wrong',
39+
bodyText,
40+
defaultBodyText,
41+
customFooter,
42+
ouiaId = "ErrorState",
43+
headingLevel = "h4",
44+
status = EmptyStateStatus.danger,
45+
variant = EmptyStateVariant.lg,
46+
...props
47+
}: ErrorStateProps) => {
3948
const classes = useStyles();
4049
return (
41-
<EmptyState headingLevel="h4" status={EmptyStateStatus.danger} variant={EmptyStateVariant.lg} titleText={titleText} data-ouia-component-id={ouiaId} {...props}>
50+
<EmptyState
51+
headingLevel={headingLevel}
52+
{...(status !== 'none' && { status } )}
53+
variant={variant}
54+
titleText={titleText}
55+
data-ouia-component-id={ouiaId}
56+
{...props}
57+
>
4258
<EmptyStateBody data-ouia-component-id={`${ouiaId}-body`}>
4359
<Stack>
4460
{bodyText ? <StackItem className={classes.errorDescription}>{bodyText}</StackItem> : defaultBodyText}

packages/module/src/Maintenance/Maintenance.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const Maintenance: React.FunctionComponent<MaintenanceProps> = ({
3636
redirectLinkText = 'status.redhat.com.',
3737
customFooter = 'For more information please visit',
3838
ouiaId = 'Maintenance',
39+
headingLevel = 'h5',
3940
...props
4041
}: MaintenanceProps) => {
4142
let formattedBodyText = bodyText;
@@ -44,7 +45,7 @@ const Maintenance: React.FunctionComponent<MaintenanceProps> = ({
4445
}
4546

4647
return (
47-
<EmptyState headingLevel="h5" status={EmptyStateStatus.danger} icon={HourglassHalfIcon} titleText={titleText} variant={EmptyStateVariant.lg} data-ouia-component-id={ouiaId} {...props}>
48+
<EmptyState headingLevel={headingLevel} status={EmptyStateStatus.danger} icon={HourglassHalfIcon} titleText={titleText} variant={EmptyStateVariant.lg} data-ouia-component-id={ouiaId} {...props}>
4849
<EmptyStateBody data-ouia-component-id={`${ouiaId}-body`}>
4950
{bodyText ? formattedBodyText : defaultBodyText}
5051
</EmptyStateBody>

packages/module/src/MissingPage/MissingPage.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ export const MissingPage: React.FunctionComponent<MissingPageProps> = ({
2222
titleText = 'We lost that page',
2323
bodyText = "Let's find you a new one. Try a new search or return home.",
2424
ouiaId = "MissingPage",
25+
headingLevel = 'h1',
2526
...props
2627
}: MissingPageProps) => (
27-
<EmptyState headingLevel='h1' icon={NotFoundIcon} variant={EmptyStateVariant.full} data-ouia-component-id={ouiaId} {...props} titleText={titleText}>
28+
<EmptyState headingLevel={headingLevel} icon={NotFoundIcon} variant={EmptyStateVariant.full} data-ouia-component-id={ouiaId} {...props} titleText={titleText}>
2829
<EmptyStateBody data-ouia-component-id={`${ouiaId}-body`}>{bodyText}</EmptyStateBody>
2930
<EmptyStateFooter data-ouia-component-id={`${ouiaId}-footer`}>
3031
<Button variant="link" component="a" href={toHomePageUrl} ouiaId={`${ouiaId}-home-button`}>

packages/module/src/UnauthorizedAccess/UnauthorizedAccess.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ const UnauthorizedAccess: React.FunctionComponent<UnauthorizedAccessProps> = ({
4343
showReturnButton = true,
4444
className,
4545
ouiaId = 'UnauthorizedAccess',
46+
headingLevel = 'h5',
4647
...props
4748
}: UnauthorizedAccessProps) => (
48-
<EmptyState headingLevel="h5" icon={Icon} variant={EmptyStateVariant.full} className={className} data-ouia-component-id={ouiaId} {...props} titleText={titleText}>
49+
<EmptyState headingLevel={headingLevel} icon={Icon} variant={EmptyStateVariant.full} className={className} data-ouia-component-id={ouiaId} {...props} titleText={titleText}>
4950
<EmptyStateBody data-ouia-component-id={`${ouiaId}-body`}>{bodyText}</EmptyStateBody>
5051
<EmptyStateFooter data-ouia-component-id={`${ouiaId}-footer`}>
5152
{primaryAction ? <EmptyStateActions>{primaryAction}</EmptyStateActions> : null}

packages/module/src/UnavailableContent/UnavailableContent.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ const UnavailableContent: React.FunctionComponent<UnavailableContentProps> = ({
2222
titleText = 'This page is temporarily unavailable',
2323
bodyText = 'Try refreshing the page. If the problem persists, contact your organization administrator or visit our status page for known outages.',
2424
ouiaId = 'UnavailableContent',
25+
headingLevel = "h5",
2526
...props
2627
}: UnavailableContentProps) => (
27-
<EmptyState headingLevel="h5" status={EmptyStateStatus.danger} icon={ExclamationCircleIcon} titleText={titleText} variant={EmptyStateVariant.lg} data-ouia-component-id={ouiaId} {...props}>
28+
<EmptyState headingLevel={headingLevel} status={EmptyStateStatus.danger} icon={ExclamationCircleIcon} titleText={titleText} variant={EmptyStateVariant.lg} data-ouia-component-id={ouiaId} {...props}>
2829
<EmptyStateBody data-ouia-component-id={`${ouiaId}-body`}>
2930
{bodyText}
3031
</EmptyStateBody>

0 commit comments

Comments
 (0)