More ts conversions (https://github.com/woocommerce/woocommerce-blocks/pull/5263)
* WIP * Converted block-error-boundary to TS * Convert CheckboxList to TS * Converted Chip and Removable Chip components to TS * Change type to React.FC<T> * Fix tests * Implement Lucio feedback
This commit is contained in:
parent
48a29c5bba
commit
0a8415e3a6
|
@ -2,9 +2,12 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import PropTypes from 'prop-types';
|
||||
import { WC_BLOCKS_IMAGE_URL } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import type { BlockErrorProps } from './types';
|
||||
const BlockError = ( {
|
||||
imageUrl = `${ WC_BLOCKS_IMAGE_URL }/block-error.svg`,
|
||||
header = __( 'Oops!', 'woo-gutenberg-products-block' ),
|
||||
|
@ -15,7 +18,7 @@ const BlockError = ( {
|
|||
errorMessage,
|
||||
errorMessagePrefix = __( 'Error:', 'woo-gutenberg-products-block' ),
|
||||
button,
|
||||
} ) => {
|
||||
}: BlockErrorProps ): JSX.Element => {
|
||||
return (
|
||||
<div className="wc-block-error wc-block-components-error">
|
||||
{ imageUrl && (
|
||||
|
@ -52,37 +55,4 @@ const BlockError = ( {
|
|||
);
|
||||
};
|
||||
|
||||
BlockError.propTypes = {
|
||||
/**
|
||||
* Error message to display below the content.
|
||||
*/
|
||||
errorMessage: PropTypes.node,
|
||||
/**
|
||||
* Text to display as the heading of the error block.
|
||||
* If it's `null` or an empty string, no header will be displayed.
|
||||
* If it's not defined, the default header will be used.
|
||||
*/
|
||||
header: PropTypes.string,
|
||||
/**
|
||||
* URL of the image to display.
|
||||
* If it's `null` or an empty string, no image will be displayed.
|
||||
* If it's not defined, the default image will be used.
|
||||
*/
|
||||
imageUrl: PropTypes.string,
|
||||
/**
|
||||
* Text to display in the error block below the header.
|
||||
* If it's `null` or an empty string, nothing will be displayed.
|
||||
* If it's not defined, the default text will be used.
|
||||
*/
|
||||
text: PropTypes.node,
|
||||
/**
|
||||
* Text preceeding the error message.
|
||||
*/
|
||||
errorMessagePrefix: PropTypes.string,
|
||||
/**
|
||||
* Button cta.
|
||||
*/
|
||||
button: PropTypes.node,
|
||||
};
|
||||
|
||||
export default BlockError;
|
|
@ -1,104 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import PropTypes from 'prop-types';
|
||||
import { Component } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import BlockError from './block-error';
|
||||
import './style.scss';
|
||||
|
||||
class BlockErrorBoundary extends Component {
|
||||
state = { errorMessage: '', hasError: false };
|
||||
|
||||
static getDerivedStateFromError( error ) {
|
||||
if (
|
||||
typeof error.statusText !== 'undefined' &&
|
||||
typeof error.status !== 'undefined'
|
||||
) {
|
||||
return {
|
||||
errorMessage: (
|
||||
<>
|
||||
<strong>{ error.status }</strong>:
|
||||
{ error.statusText }
|
||||
</>
|
||||
),
|
||||
hasError: true,
|
||||
};
|
||||
}
|
||||
|
||||
return { errorMessage: error.message, hasError: true };
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
header,
|
||||
imageUrl,
|
||||
showErrorMessage,
|
||||
text,
|
||||
errorMessagePrefix,
|
||||
renderError,
|
||||
button,
|
||||
} = this.props;
|
||||
const { errorMessage, hasError } = this.state;
|
||||
|
||||
if ( hasError ) {
|
||||
if ( typeof renderError === 'function' ) {
|
||||
return renderError( { errorMessage } );
|
||||
}
|
||||
return (
|
||||
<BlockError
|
||||
errorMessage={ showErrorMessage ? errorMessage : null }
|
||||
header={ header }
|
||||
imageUrl={ imageUrl }
|
||||
text={ text }
|
||||
errorMessagePrefix={ errorMessagePrefix }
|
||||
button={ button }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
BlockErrorBoundary.propTypes = {
|
||||
/**
|
||||
* Text to display as the heading of the error block.
|
||||
* If it's `null` or an empty string, no header will be displayed.
|
||||
* If it's not defined, the default header will be used.
|
||||
*/
|
||||
header: PropTypes.string,
|
||||
/**
|
||||
* URL of the image to display.
|
||||
* If it's `null` or an empty string, no image will be displayed.
|
||||
* If it's not defined, the default image will be used.
|
||||
*/
|
||||
imageUrl: PropTypes.string,
|
||||
/**
|
||||
* Whether to display the JS error message.
|
||||
*/
|
||||
showErrorMessage: PropTypes.bool,
|
||||
/**
|
||||
* Text to display in the error block below the header.
|
||||
* If it's `null` or an empty string, nothing will be displayed.
|
||||
* If it's not defined, the default text will be used.
|
||||
*/
|
||||
text: PropTypes.node,
|
||||
/**
|
||||
* Text preceeding the error message.
|
||||
*/
|
||||
errorMessagePrefix: PropTypes.string,
|
||||
/**
|
||||
* Render function to show a custom error component.
|
||||
*/
|
||||
renderError: PropTypes.func,
|
||||
};
|
||||
|
||||
BlockErrorBoundary.defaultProps = {
|
||||
showErrorMessage: true,
|
||||
};
|
||||
|
||||
export default BlockErrorBoundary;
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Component } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import BlockError from './block-error';
|
||||
import './style.scss';
|
||||
|
||||
import type {
|
||||
DerivedStateReturn,
|
||||
ReactError,
|
||||
BlockErrorBoundaryProps,
|
||||
} from './types';
|
||||
|
||||
class BlockErrorBoundary extends Component< BlockErrorBoundaryProps > {
|
||||
state = { errorMessage: '', hasError: false };
|
||||
|
||||
static getDerivedStateFromError( error: ReactError ): DerivedStateReturn {
|
||||
if (
|
||||
typeof error.statusText !== 'undefined' &&
|
||||
typeof error.status !== 'undefined'
|
||||
) {
|
||||
return {
|
||||
errorMessage: (
|
||||
<>
|
||||
<strong>{ error.status }</strong>:
|
||||
{ error.statusText }
|
||||
</>
|
||||
),
|
||||
hasError: true,
|
||||
};
|
||||
}
|
||||
|
||||
return { errorMessage: error.message, hasError: true };
|
||||
}
|
||||
|
||||
render(): JSX.Element | React.ReactNode {
|
||||
const {
|
||||
header,
|
||||
imageUrl,
|
||||
showErrorMessage = true,
|
||||
text,
|
||||
errorMessagePrefix,
|
||||
renderError,
|
||||
button,
|
||||
} = this.props;
|
||||
const { errorMessage, hasError } = this.state;
|
||||
|
||||
if ( hasError ) {
|
||||
if ( typeof renderError === 'function' ) {
|
||||
return renderError( { errorMessage } );
|
||||
}
|
||||
return (
|
||||
<BlockError
|
||||
errorMessage={ showErrorMessage ? errorMessage : null }
|
||||
header={ header }
|
||||
imageUrl={ imageUrl }
|
||||
text={ text }
|
||||
errorMessagePrefix={ errorMessagePrefix }
|
||||
button={ button }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
export default BlockErrorBoundary;
|
|
@ -0,0 +1,57 @@
|
|||
interface BlockErrorBase {
|
||||
/**
|
||||
* URL of the image to display.
|
||||
* If it's `null` or an empty string, no image will be displayed.
|
||||
* If it's not defined, the default image will be used.
|
||||
*/
|
||||
imageUrl?: string;
|
||||
/**
|
||||
* Text to display as the heading of the error block.
|
||||
* If it's `null` or an empty string, no header will be displayed.
|
||||
* If it's not defined, the default header will be used.
|
||||
*/
|
||||
header?: string;
|
||||
/**
|
||||
* Text to display in the error block below the header.
|
||||
* If it's `null` or an empty string, nothing will be displayed.
|
||||
* If it's not defined, the default text will be used.
|
||||
*/
|
||||
text: React.ReactNode;
|
||||
/**
|
||||
* Text preceeding the error message.
|
||||
*/
|
||||
errorMessagePrefix?: string;
|
||||
/**
|
||||
* Button cta.
|
||||
*/
|
||||
button: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface BlockErrorProps extends BlockErrorBase {
|
||||
/**
|
||||
* Error message to display below the content.
|
||||
*/
|
||||
errorMessage: React.ReactNode;
|
||||
}
|
||||
|
||||
type RenderErrorProps = {
|
||||
errorMessage: React.ReactNode;
|
||||
};
|
||||
|
||||
export interface BlockErrorBoundaryProps extends BlockErrorBase {
|
||||
/**
|
||||
* Override the default error with a function that takes the error message and returns a React component
|
||||
*/
|
||||
renderError: ( props: RenderErrorProps ) => React.ReactNode;
|
||||
}
|
||||
|
||||
export interface DerivedStateReturn {
|
||||
errorMessage: JSX.Element | string;
|
||||
hasError: boolean;
|
||||
}
|
||||
|
||||
export interface ReactError {
|
||||
status: string;
|
||||
statusText: string;
|
||||
message: string;
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { __, _n, sprintf } from '@wordpress/i18n';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Fragment, useMemo, useState } from '@wordpress/element';
|
||||
import classNames from 'classnames';
|
||||
|
||||
|
@ -11,6 +10,21 @@ import classNames from 'classnames';
|
|||
*/
|
||||
import './style.scss';
|
||||
|
||||
interface CheckboxListOptions {
|
||||
label: React.ReactNode;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface CheckboxListProps {
|
||||
className?: string;
|
||||
isLoading?: boolean;
|
||||
isDisabled?: boolean;
|
||||
limit?: number;
|
||||
checked?: string[];
|
||||
onChange?: ( value: string ) => void;
|
||||
options?: CheckboxListOptions[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Component used to show a list of checkboxes in a group.
|
||||
*
|
||||
|
@ -25,13 +39,13 @@ import './style.scss';
|
|||
*/
|
||||
const CheckboxList = ( {
|
||||
className,
|
||||
onChange = () => {},
|
||||
onChange = () => void 0,
|
||||
options = [],
|
||||
checked = [],
|
||||
isLoading = false,
|
||||
isDisabled = false,
|
||||
limit = 10,
|
||||
} ) => {
|
||||
}: CheckboxListProps ): JSX.Element => {
|
||||
const [ showExpanded, setShowExpanded ] = useState( false );
|
||||
|
||||
const placeholder = useMemo( () => {
|
||||
|
@ -167,19 +181,4 @@ const CheckboxList = ( {
|
|||
);
|
||||
};
|
||||
|
||||
CheckboxList.propTypes = {
|
||||
onChange: PropTypes.func,
|
||||
options: PropTypes.arrayOf(
|
||||
PropTypes.shape( {
|
||||
label: PropTypes.node.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
} )
|
||||
),
|
||||
checked: PropTypes.array,
|
||||
className: PropTypes.string,
|
||||
isLoading: PropTypes.bool,
|
||||
isDisabled: PropTypes.bool,
|
||||
limit: PropTypes.number,
|
||||
};
|
||||
|
||||
export default CheckboxList;
|
|
@ -1,7 +1,6 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
/**
|
||||
|
@ -9,7 +8,32 @@ import classNames from 'classnames';
|
|||
*/
|
||||
import './style.scss';
|
||||
|
||||
/** @typedef {import('react')} React */
|
||||
export interface ChipProps {
|
||||
/**
|
||||
* Text for chip content.
|
||||
*/
|
||||
text: string;
|
||||
/**
|
||||
* Screenreader text for the content.
|
||||
*/
|
||||
screenReaderText?: string;
|
||||
/**
|
||||
* The element type for the chip. Default 'li'.
|
||||
*/
|
||||
element?: string;
|
||||
/**
|
||||
* CSS class used.
|
||||
*/
|
||||
className?: string;
|
||||
/**
|
||||
* React children.
|
||||
*/
|
||||
children?: React.ReactNode | React.ReactNode[];
|
||||
/**
|
||||
* Radius size.
|
||||
*/
|
||||
radius?: 'none' | 'small' | 'medium' | 'large';
|
||||
}
|
||||
|
||||
/**
|
||||
* Component used to render a "chip" -- a list item containing some text.
|
||||
|
@ -17,16 +41,8 @@ import './style.scss';
|
|||
* Each chip defaults to a list element but this can be customized by providing
|
||||
* a wrapperElement.
|
||||
*
|
||||
* @param {Object} props Incoming props for the component.
|
||||
* @param {string} props.text Text for chip content.
|
||||
* @param {string} props.screenReaderText Screenreader text for the content.
|
||||
* @param {string} props.element The element type for the chip.
|
||||
* @param {string} props.className CSS class used.
|
||||
* @param {string} props.radius Radius size.
|
||||
* @param {React.ReactChildren|null} props.children React children.
|
||||
* @param {Object} props.props Rest of props passed through to component.
|
||||
*/
|
||||
const Chip = ( {
|
||||
const Chip: React.FC< ChipProps > = ( {
|
||||
text,
|
||||
screenReaderText = '',
|
||||
element = 'li',
|
||||
|
@ -47,7 +63,6 @@ const Chip = ( {
|
|||
);
|
||||
|
||||
return (
|
||||
// @ts-ignore
|
||||
<Wrapper className={ wrapperClassName } { ...props }>
|
||||
<span
|
||||
aria-hidden={ showScreenReaderText }
|
||||
|
@ -62,13 +77,4 @@ const Chip = ( {
|
|||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
Chip.propTypes = {
|
||||
text: PropTypes.node.isRequired,
|
||||
screenReaderText: PropTypes.string,
|
||||
element: PropTypes.elementType,
|
||||
className: PropTypes.string,
|
||||
radius: PropTypes.oneOf( [ 'none', 'small', 'medium', 'large' ] ),
|
||||
};
|
||||
|
||||
export default Chip;
|
|
@ -1,7 +1,6 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { Icon, noAlt } from '@woocommerce/icons';
|
||||
|
@ -9,7 +8,30 @@ import { Icon, noAlt } from '@woocommerce/icons';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Chip from './chip.js';
|
||||
import Chip, { ChipProps } from './chip';
|
||||
|
||||
interface RemovableChipProps extends ChipProps {
|
||||
/**
|
||||
* Aria label content.
|
||||
*/
|
||||
ariaLabel?: string;
|
||||
/**
|
||||
* CSS class used.
|
||||
*/
|
||||
className?: string;
|
||||
/**
|
||||
* Whether action is disabled or not.
|
||||
*/
|
||||
disabled?: boolean;
|
||||
/**
|
||||
* Function to call when remove event is fired.
|
||||
*/
|
||||
onRemove?: () => void;
|
||||
/**
|
||||
* Whether to expand click area for remove event.
|
||||
*/
|
||||
removeOnAnyClick?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component used to render a "chip" -- an item containing some text with
|
||||
|
@ -25,11 +47,11 @@ import Chip from './chip.js';
|
|||
* @param {string} props.screenReaderText The screen reader text for the chip.
|
||||
* @param {Object} props.props Rest of props passed into component.
|
||||
*/
|
||||
const RemovableChip = ( {
|
||||
const RemovableChip: React.FC< RemovableChipProps > = ( {
|
||||
ariaLabel = '',
|
||||
className = '',
|
||||
disabled = false,
|
||||
onRemove = () => void null,
|
||||
onRemove = () => void 0,
|
||||
removeOnAnyClick = false,
|
||||
text,
|
||||
screenReaderText = '',
|
||||
|
@ -57,7 +79,7 @@ const RemovableChip = ( {
|
|||
'aria-label': ariaLabel,
|
||||
disabled,
|
||||
onClick: onRemove,
|
||||
onKeyDown: ( e ) => {
|
||||
onKeyDown: ( e: React.KeyboardEvent ) => {
|
||||
if ( e.key === 'Backspace' || e.key === 'Delete' ) {
|
||||
onRemove();
|
||||
}
|
||||
|
@ -92,14 +114,4 @@ const RemovableChip = ( {
|
|||
);
|
||||
};
|
||||
|
||||
RemovableChip.propTypes = {
|
||||
text: PropTypes.node.isRequired,
|
||||
ariaLabel: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
onRemove: PropTypes.func,
|
||||
removeOnAnyClick: PropTypes.bool,
|
||||
screenReaderText: PropTypes.string,
|
||||
};
|
||||
|
||||
export default RemovableChip;
|
Loading…
Reference in New Issue