Skip to content

Commit

Permalink
Storybook and TS migration of ErrorPlaceholder component (woocommer…
Browse files Browse the repository at this point in the history
…ce#5294)

* Convert `ErrorPlaceholder` and `ErrorMessage` to TypeScript

* Add stories for `ErrorPlaceholder` and `ErrorMessage` (woocommerce#5255)

Stories include:

* Default generic error
* API error
* Unknown error
* Error without possibility to retry
* Base Error atom

Where applicable, the **Retry** button will not only trigger the appropriate
action, but also simulate the loading state of the error component.

* Update references to `ErrorMessage` component to leave the file extension out

Fix woocommerce#5255
Refs woocommerce#5249
  • Loading branch information
sunyatasattva authored and jonny-bull committed Dec 16, 2021
1 parent cf24e88 commit 90ab25e
Show file tree
Hide file tree
Showing 11 changed files with 485 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import PropTypes from 'prop-types';
import { escapeHTML } from '@wordpress/escape-html';

const getErrorMessage = ( { message, type } ) => {
/**
* Internal dependencies
*/
import { ErrorObject } from '.';

export interface ErrorMessageProps {
/**
* The error object.
*/
error: ErrorObject;
}

const getErrorMessage = ( { message, type }: ErrorObject ) => {
if ( ! message ) {
return __(
'An unknown error occurred which prevented the block from being updated.',
Expand Down Expand Up @@ -42,24 +53,8 @@ const getErrorMessage = ( { message, type } ) => {
return message;
};

const ErrorMessage = ( { error } ) => (
const ErrorMessage = ( { error }: ErrorMessageProps ): JSX.Element => (
<div className="wc-block-error-message">{ getErrorMessage( error ) }</div>
);

ErrorMessage.propTypes = {
/**
* The error object.
*/
error: PropTypes.shape( {
/**
* Human-readable error message to display.
*/
message: PropTypes.node,
/**
* Context in which the error was triggered. That will determine how the error is displayed to the user.
*/
type: PropTypes.oneOf( [ 'api', 'general' ] ),
} ),
};

export default ErrorMessage;
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,53 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import PropTypes from 'prop-types';
import { Icon, notice } from '@woocommerce/icons';
import classNames from 'classnames';
import { Button, Placeholder, Spinner } from '@wordpress/components';

/**
* Internal dependencies
*/
import ErrorMessage from './error-message.js';
import ErrorMessage from './error-message';
import './editor.scss';

const ErrorPlaceholder = ( { className, error, isLoading, onRetry } ) => (
export interface ErrorObject {
/**
* Human-readable error message to display.
*/
message: string;
/**
* Context in which the error was triggered. That will determine how the error is displayed to the user.
*/
type: 'api' | 'general';
}

export interface ErrorPlaceholderProps {
/**
* Classname to add to placeholder in addition to the defaults.
*/
className?: string;
/**
* The error object.
*/
error: ErrorObject;
/**
* Whether there is a request running, so the 'Retry' button is hidden and
* a spinner is shown instead.
*/
isLoading: boolean;
/**
* Callback to retry an action.
*/
onRetry?: () => void;
}

const ErrorPlaceholder = ( {
className,
error,
isLoading = false,
onRetry,
}: ErrorPlaceholderProps ): JSX.Element => (
<Placeholder
icon={ <Icon srcElement={ notice } /> }
label={ __(
Expand All @@ -37,33 +72,4 @@ const ErrorPlaceholder = ( { className, error, isLoading, onRetry } ) => (
</Placeholder>
);

ErrorPlaceholder.propTypes = {
/**
* Classname to add to placeholder in addition to the defaults.
*/
className: PropTypes.string,
/**
* The error object.
*/
error: PropTypes.shape( {
/**
* Human-readable error message to display.
*/
message: PropTypes.node,
/**
* Context in which the error was triggered. That will determine how the error is displayed to the user.
*/
type: PropTypes.oneOf( [ 'api', 'general' ] ),
} ),
/**
* Whether there is a request running, so the 'Retry' button is hidden and
* a spinner is shown instead.
*/
isLoading: PropTypes.bool,
/**
* Callback to retry an action.
*/
onRetry: PropTypes.func,
};

export default ErrorPlaceholder;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* External dependencies
*/
import { Story, Meta } from '@storybook/react';

/**
* Internal dependencies
*/
import ErrorMessage, { ErrorMessageProps } from '../error-message';

export default {
title: 'WooCommerce Blocks/editor-components/Errors/Base Error Atom',
component: ErrorMessage,
} as Meta< ErrorMessageProps >;

const Template: Story< ErrorMessageProps > = ( args ) => (
<ErrorMessage { ...args } />
);

export const BaseErrorAtom = Template.bind( {} );
BaseErrorAtom.args = {
error: {
message:
'A very generic and unhelpful error. Please try again later. Or contact support. Or not.',
type: 'general',
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* External dependencies
*/
import { Story, Meta } from '@storybook/react';
import { useArgs } from '@storybook/client-api';

/**
* Internal dependencies
*/
import ErrorPlaceholder, { ErrorPlaceholderProps } from '..';

export default {
title: 'WooCommerce Blocks/editor-components/Errors',
component: ErrorPlaceholder,
} as Meta< ErrorPlaceholderProps >;

const Template: Story< ErrorPlaceholderProps > = ( args ) => {
const [ { isLoading }, setArgs ] = useArgs();

const onRetry = args.onRetry
? () => {
setArgs( { isLoading: true } );

setTimeout( () => setArgs( { isLoading: false } ), 3500 );
}
: undefined;

return (
<ErrorPlaceholder
{ ...args }
onRetry={ onRetry }
isLoading={ isLoading }
/>
);
};

export const Default = Template.bind( {} );
Default.args = {
error: {
message:
'A very generic and unhelpful error. Please try again later. Or contact support. Or not.',
type: 'general',
},
};

export const APIError = Template.bind( {} );
APIError.args = {
error: {
message: 'Server refuses to comply. It is a teapot.',
type: 'api',
},
};

export const UnknownError = Template.bind( {} );
UnknownError.args = {
error: {
message: '',
type: 'general',
},
};

export const NoRetry: Story< ErrorPlaceholderProps > = ( args ) => {
return <ErrorPlaceholder { ...args } onRetry={ undefined } />;
};
NoRetry.args = {
error: {
message: '',
type: 'general',
},
};
19 changes: 0 additions & 19 deletions assets/js/editor-components/error-placeholder/stories/index.js

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { SearchListControl, SearchListItem } from '@woocommerce/components';
import { SelectControl } from '@wordpress/components';
import { withInstanceId } from '@wordpress/compose';
import { withAttributes } from '@woocommerce/block-hocs';
import ErrorMessage from '@woocommerce/editor-components/error-placeholder/error-message.js';
import ErrorMessage from '@woocommerce/editor-components/error-placeholder/error-message';
import classNames from 'classnames';
import ExpandableSearchListItem from '@woocommerce/editor-components/expandable-search-list-item/expandable-search-list-item.tsx';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
import { SearchListControl, SearchListItem } from '@woocommerce/components';
import { SelectControl } from '@wordpress/components';
import { withCategories } from '@woocommerce/block-hocs';
import ErrorMessage from '@woocommerce/editor-components/error-placeholder/error-message.js';
import ErrorMessage from '@woocommerce/editor-components/error-placeholder/error-message';
import classNames from 'classnames';

/**
Expand Down
2 changes: 1 addition & 1 deletion assets/js/editor-components/product-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
withSearchedProducts,
withTransformSingleSelectToMultipleSelect,
} from '@woocommerce/block-hocs';
import ErrorMessage from '@woocommerce/editor-components/error-placeholder/error-message.js';
import ErrorMessage from '@woocommerce/editor-components/error-placeholder/error-message';
import classNames from 'classnames';
import ExpandableSearchListItem from '@woocommerce/editor-components/expandable-search-list-item/expandable-search-list-item.tsx';

Expand Down
2 changes: 1 addition & 1 deletion assets/js/editor-components/products-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { __, _n, sprintf } from '@wordpress/i18n';
import { SearchListControl } from '@woocommerce/components';
import PropTypes from 'prop-types';
import { withSearchedProducts } from '@woocommerce/block-hocs';
import ErrorMessage from '@woocommerce/editor-components/error-placeholder/error-message.js';
import ErrorMessage from '@woocommerce/editor-components/error-placeholder/error-message';

/**
* The products control exposes a custom selector for searching and selecting
Expand Down
Loading

0 comments on commit 90ab25e

Please sign in to comment.