Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7,799 changes: 4,372 additions & 3,427 deletions package-lock.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ sourceLink: https://github.com/patternfly/react-component-groups/blob/main/packa
---

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

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`.

## Examples

### Basic error state

To provide users with error details, a basic error state should contain an appropriate and informative `titleText` and `bodyText`.
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.

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

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

```

### Customizations using EmptyState props

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.

```js file="./ErrorStateExtraProps.tsx"

```
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import ErrorState from "@patternfly/react-component-groups/dist/dynamic/ErrorState";
import { PathMissingIcon } from '@patternfly/react-icons/dist/dynamic/icons/path-missing-icon';

export const BasicExample: React.FunctionComponent = () => (
<ErrorState
titleText='Sample error title'
bodyText='Sample error description'
headingLevel='h2'
icon={PathMissingIcon}
status="none"
customFooter="Any other details in a custom footer."
/>
);
24 changes: 20 additions & 4 deletions packages/module/src/ErrorBoundary/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ export interface ErrorBoundaryProps {
children?: React.ReactNode;
/** Custom OUIA ID */
ouiaId?: string | number;
/** The heading level to use on the header title, default is h1 */
headerTitleHeadingLevel?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
/** The heading level to use on the error title, default is h2 */
errorTitleHeadingLevel?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
}

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

export const ErrorBoundary: React.FunctionComponent<ErrorBoundaryProps> = (props: ErrorBoundaryProps) => {
export const ErrorBoundary: React.FunctionComponent<ErrorBoundaryProps> = ({
headerTitleHeadingLevel = "h1",
errorTitleHeadingLevel = "h2",
...props
}: ErrorBoundaryProps) => {
const classes = useStyles();
return <ErrorBoundaryContent classes={classes} {...props} />
return (
<ErrorBoundaryContent
classes={classes}
headerTitleHeadingLevel={headerTitleHeadingLevel}
errorTitleHeadingLevel={errorTitleHeadingLevel}
{...props}
/>
);
}

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

render() {
const { ouiaId = 'ErrorBoundary', ...props } = this.props;
const { ouiaId = 'ErrorBoundary', errorTitleHeadingLevel, headerTitleHeadingLevel, ...props } = this.props;

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

return (
<div data-ouia-component-id={ouiaId}>
{props?.headerTitle && <Title headingLevel="h1" size="2xl" ouiaId={`${ouiaId}-title`}>{props.headerTitle}</Title>}
{props?.headerTitle && <Title headingLevel={headerTitleHeadingLevel || 'h1'} size="2xl" ouiaId={`${ouiaId}-title`}>{props.headerTitle}</Title>}
<ErrorState
titleText={props.errorTitle}
headingLevel={errorTitleHeadingLevel}
bodyText={
<>
<span>{props.errorDescription}</span>
Expand Down
7 changes: 7 additions & 0 deletions packages/module/src/ErrorState/ErrorState.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,11 @@ describe('ErrorState component', () => {
expect(screen.getByText('Go to home page')).toBeVisible();
});

it('should spread empty state props', () => {
render(<ErrorState headingLevel="h2" variant="xs" data-testid="test"/>);

expect(screen.getByRole('heading', { level: 2 })).toBeInTheDocument();
expect(screen.getByTestId('test')).toHaveClass('pf-m-xs');
});

});
28 changes: 22 additions & 6 deletions packages/module/src/ErrorState/ErrorState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,13 @@ import { createUseStyles } from 'react-jss'
import React from 'react';

const useStyles = createUseStyles({
errorIcon: {
fill: 'var(--pf-t--global--color--status--danger--default)',
},
errorDescription: {
margin: 'auto'
}
})

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

const ErrorState: React.FunctionComponent<ErrorStateProps> = ({ titleText = 'Something went wrong', bodyText, defaultBodyText, customFooter, ouiaId = "ErrorState", ...props }: ErrorStateProps) => {
const ErrorState: React.FunctionComponent<ErrorStateProps> = ({
titleText = 'Something went wrong',
bodyText,
defaultBodyText,
customFooter,
ouiaId = "ErrorState",
headingLevel = "h4",
status = EmptyStateStatus.danger,
variant = EmptyStateVariant.lg,
...props
}: ErrorStateProps) => {
const classes = useStyles();
return (
<EmptyState headingLevel="h4" status={EmptyStateStatus.danger} variant={EmptyStateVariant.lg} titleText={titleText} data-ouia-component-id={ouiaId} {...props}>
<EmptyState
headingLevel={headingLevel}
{...(status !== 'none' && { status } )}
variant={variant}
titleText={titleText}
data-ouia-component-id={ouiaId}
{...props}
>
<EmptyStateBody data-ouia-component-id={`${ouiaId}-body`}>
<Stack>
{bodyText ? <StackItem className={classes.errorDescription}>{bodyText}</StackItem> : defaultBodyText}
Expand Down
3 changes: 2 additions & 1 deletion packages/module/src/Maintenance/Maintenance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const Maintenance: React.FunctionComponent<MaintenanceProps> = ({
redirectLinkText = 'status.redhat.com.',
customFooter = 'For more information please visit',
ouiaId = 'Maintenance',
headingLevel = 'h5',
...props
}: MaintenanceProps) => {
let formattedBodyText = bodyText;
Expand All @@ -44,7 +45,7 @@ const Maintenance: React.FunctionComponent<MaintenanceProps> = ({
}

return (
<EmptyState headingLevel="h5" status={EmptyStateStatus.danger} icon={HourglassHalfIcon} titleText={titleText} variant={EmptyStateVariant.lg} data-ouia-component-id={ouiaId} {...props}>
<EmptyState headingLevel={headingLevel} status={EmptyStateStatus.danger} icon={HourglassHalfIcon} titleText={titleText} variant={EmptyStateVariant.lg} data-ouia-component-id={ouiaId} {...props}>
<EmptyStateBody data-ouia-component-id={`${ouiaId}-body`}>
{bodyText ? formattedBodyText : defaultBodyText}
</EmptyStateBody>
Expand Down
3 changes: 2 additions & 1 deletion packages/module/src/MissingPage/MissingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ export const MissingPage: React.FunctionComponent<MissingPageProps> = ({
titleText = 'We lost that page',
bodyText = "Let's find you a new one. Try a new search or return home.",
ouiaId = "MissingPage",
headingLevel = 'h1',
...props
}: MissingPageProps) => (
<EmptyState headingLevel='h1' icon={NotFoundIcon} variant={EmptyStateVariant.full} data-ouia-component-id={ouiaId} {...props} titleText={titleText}>
<EmptyState headingLevel={headingLevel} icon={NotFoundIcon} variant={EmptyStateVariant.full} data-ouia-component-id={ouiaId} {...props} titleText={titleText}>
<EmptyStateBody data-ouia-component-id={`${ouiaId}-body`}>{bodyText}</EmptyStateBody>
<EmptyStateFooter data-ouia-component-id={`${ouiaId}-footer`}>
<Button variant="link" component="a" href={toHomePageUrl} ouiaId={`${ouiaId}-home-button`}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ const UnauthorizedAccess: React.FunctionComponent<UnauthorizedAccessProps> = ({
showReturnButton = true,
className,
ouiaId = 'UnauthorizedAccess',
headingLevel = 'h5',
...props
}: UnauthorizedAccessProps) => (
<EmptyState headingLevel="h5" icon={Icon} variant={EmptyStateVariant.full} className={className} data-ouia-component-id={ouiaId} {...props} titleText={titleText}>
<EmptyState headingLevel={headingLevel} icon={Icon} variant={EmptyStateVariant.full} className={className} data-ouia-component-id={ouiaId} {...props} titleText={titleText}>
<EmptyStateBody data-ouia-component-id={`${ouiaId}-body`}>{bodyText}</EmptyStateBody>
<EmptyStateFooter data-ouia-component-id={`${ouiaId}-footer`}>
{primaryAction ? <EmptyStateActions>{primaryAction}</EmptyStateActions> : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ const UnavailableContent: React.FunctionComponent<UnavailableContentProps> = ({
titleText = 'This page is temporarily unavailable',
bodyText = 'Try refreshing the page. If the problem persists, contact your organization administrator or visit our status page for known outages.',
ouiaId = 'UnavailableContent',
headingLevel = "h5",
...props
}: UnavailableContentProps) => (
<EmptyState headingLevel="h5" status={EmptyStateStatus.danger} icon={ExclamationCircleIcon} titleText={titleText} variant={EmptyStateVariant.lg} data-ouia-component-id={ouiaId} {...props}>
<EmptyState headingLevel={headingLevel} status={EmptyStateStatus.danger} icon={ExclamationCircleIcon} titleText={titleText} variant={EmptyStateVariant.lg} data-ouia-component-id={ouiaId} {...props}>
<EmptyStateBody data-ouia-component-id={`${ouiaId}-body`}>
{bodyText}
</EmptyStateBody>
Expand Down
3 changes: 3 additions & 0 deletions packages/module/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ export * from './MultiContentCard';
export { default as MissingPage } from './MissingPage';
export * from './MissingPage';

export { default as Maintenance } from './Maintenance';
export * from './Maintenance';

export { default as LogSnippet } from './LogSnippet';
export * from './LogSnippet';

Expand Down