woocommerce/plugins/woocommerce-blocks/packages/components/checkbox-list/index.tsx

183 lines
4.5 KiB
TypeScript

/**
* External dependencies
*/
import { __, _n, sprintf } from '@wordpress/i18n';
import { Fragment, useMemo, useState } from '@wordpress/element';
import classNames from 'classnames';
import { CheckboxControl } from '@woocommerce/blocks-checkout';
/**
* Internal dependencies
*/
import './style.scss';
interface CheckboxListOptions {
label: React.ReactNode;
value: string;
}
interface CheckboxListProps {
className?: string | undefined;
isLoading?: boolean | undefined;
isDisabled?: boolean | undefined;
limit?: number | undefined;
checked?: string[] | undefined;
onChange: ( value: string ) => void | undefined;
options?: CheckboxListOptions[] | undefined;
}
/**
* Component used to show a list of checkboxes in a group.
*
* @param {Object} props Incoming props for the component.
* @param {string} props.className CSS class used.
* @param {function(string):any} props.onChange Function called when inputs change.
* @param {Array} props.options Options for list.
* @param {Array} props.checked Which items are checked.
* @param {boolean} props.isLoading If loading or not.
* @param {boolean} props.isDisabled If inputs are disabled or not.
* @param {number} props.limit Whether to limit the number of inputs showing.
*/
const CheckboxList = ( {
className,
onChange,
options = [],
checked = [],
isLoading = false,
isDisabled = false,
limit = 10,
}: CheckboxListProps ): JSX.Element => {
const [ showExpanded, setShowExpanded ] = useState( false );
const placeholder = useMemo( () => {
return [ ...Array( 5 ) ].map( ( x, i ) => (
<li
key={ i }
style={ {
/* stylelint-disable */
width: Math.floor( Math.random() * 75 ) + 25 + '%',
} }
/>
) );
}, [] );
const renderedShowMore = useMemo( () => {
const optionCount = options.length;
const remainingOptionsCount = optionCount - limit;
return (
! showExpanded && (
<li key="show-more" className="show-more">
<button
onClick={ () => {
setShowExpanded( true );
} }
aria-expanded={ false }
aria-label={ sprintf(
/* translators: %s is referring the remaining count of options */
_n(
'Show %s more option',
'Show %s more options',
remainingOptionsCount,
'woo-gutenberg-products-block'
),
remainingOptionsCount
) }
>
{ sprintf(
/* translators: %s number of options to reveal. */
_n(
'Show %s more',
'Show %s more',
remainingOptionsCount,
'woo-gutenberg-products-block'
),
remainingOptionsCount
) }
</button>
</li>
)
);
}, [ options, limit, showExpanded ] );
const renderedShowLess = useMemo( () => {
return (
showExpanded && (
<li key="show-less" className="show-less">
<button
onClick={ () => {
setShowExpanded( false );
} }
aria-expanded={ true }
aria-label={ __(
'Show less options',
'woo-gutenberg-products-block'
) }
>
{ __( 'Show less', 'woo-gutenberg-products-block' ) }
</button>
</li>
)
);
}, [ showExpanded ] );
const renderedOptions = useMemo( () => {
// Truncate options if > the limit + 5.
const optionCount = options.length;
const shouldTruncateOptions = optionCount > limit + 5;
return (
<>
{ options.map( ( option, index ) => (
<Fragment key={ option.value }>
<li
{ ...( shouldTruncateOptions &&
! showExpanded &&
index >= limit && { hidden: true } ) }
>
<CheckboxControl
id={ option.value }
className="wc-block-checkbox-list__checkbox"
label={ option.label }
checked={ checked.includes( option.value ) }
onChange={ () => {
onChange( option.value );
} }
disabled={ isDisabled }
/>
</li>
{ shouldTruncateOptions &&
index === limit - 1 &&
renderedShowMore }
</Fragment>
) ) }
{ shouldTruncateOptions && renderedShowLess }
</>
);
}, [
options,
onChange,
checked,
showExpanded,
limit,
renderedShowLess,
renderedShowMore,
isDisabled,
] );
const classes = classNames(
'wc-block-checkbox-list',
'wc-block-components-checkbox-list',
{
'is-loading': isLoading,
},
className
);
return (
<ul className={ classes }>
{ isLoading ? placeholder : renderedOptions }
</ul>
);
};
export default CheckboxList;