diff --git a/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/block-error.js b/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/block-error.tsx similarity index 60% rename from plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/block-error.js rename to plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/block-error.tsx index 0b291e29835..49b5443de15 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/block-error.js +++ b/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/block-error.tsx @@ -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 (
{ 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; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/index.js b/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/index.js deleted file mode 100644 index f96c225f31c..00000000000 --- a/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/index.js +++ /dev/null @@ -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: ( - <> - { error.status }:  - { 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 ( - - ); - } - - 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; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/index.tsx b/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/index.tsx new file mode 100644 index 00000000000..47df319e96b --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/index.tsx @@ -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: ( + <> + { error.status }:  + { 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 ( + + ); + } + + return this.props.children; + } +} + +export default BlockErrorBoundary; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/types.ts b/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/types.ts new file mode 100644 index 00000000000..6680afeda32 --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/base/components/block-error-boundary/types.ts @@ -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; +} diff --git a/plugins/woocommerce-blocks/assets/js/base/components/checkbox-list/index.js b/plugins/woocommerce-blocks/assets/js/base/components/checkbox-list/index.tsx similarity index 90% rename from plugins/woocommerce-blocks/assets/js/base/components/checkbox-list/index.js rename to plugins/woocommerce-blocks/assets/js/base/components/checkbox-list/index.tsx index 0dbdd74f26c..a6843479e54 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/checkbox-list/index.js +++ b/plugins/woocommerce-blocks/assets/js/base/components/checkbox-list/index.tsx @@ -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; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/chip/chip.js b/plugins/woocommerce-blocks/assets/js/base/components/chip/chip.tsx similarity index 55% rename from plugins/woocommerce-blocks/assets/js/base/components/chip/chip.js rename to plugins/woocommerce-blocks/assets/js/base/components/chip/chip.tsx index d7c87e31db9..4485be31f14 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/chip/chip.js +++ b/plugins/woocommerce-blocks/assets/js/base/components/chip/chip.tsx @@ -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 ); }; - -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; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/chip/index.js b/plugins/woocommerce-blocks/assets/js/base/components/chip/index.ts similarity index 100% rename from plugins/woocommerce-blocks/assets/js/base/components/chip/index.js rename to plugins/woocommerce-blocks/assets/js/base/components/chip/index.ts diff --git a/plugins/woocommerce-blocks/assets/js/base/components/chip/removable-chip.js b/plugins/woocommerce-blocks/assets/js/base/components/chip/removable-chip.tsx similarity index 80% rename from plugins/woocommerce-blocks/assets/js/base/components/chip/removable-chip.js rename to plugins/woocommerce-blocks/assets/js/base/components/chip/removable-chip.tsx index 0bfe0334a04..f4cd0af3f40 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/chip/removable-chip.js +++ b/plugins/woocommerce-blocks/assets/js/base/components/chip/removable-chip.tsx @@ -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;