File: /var/www/vhosts/uyarreklam.com.tr/httpdocs/editor-components.tar
block-title/editor.scss 0000644 00000000557 15155071622 0011153 0 ustar 00 // Ensure textarea bg color is transparent for block titles.
// Some themes (e.g. Twenty Twenty) set a non-white background for the editor, and Gutenberg sets white background for text inputs, creating this issue.
// https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues/1204
.wc-block-editor-components-title {
background-color: transparent;
}
block-title/index.tsx 0000644 00000002034 15155071622 0010627 0 ustar 00 /**
* External dependencies
*/
import { PlainText } from '@wordpress/block-editor';
import { withInstanceId } from '@wordpress/compose';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import './editor.scss';
interface BlockTitleProps {
className: string;
headingLevel: number;
onChange: ( value: string ) => void;
heading: string;
instanceId: number;
}
const BlockTitle = ( {
className,
headingLevel,
onChange,
heading,
instanceId,
}: BlockTitleProps ) => {
const TagName = `h${ headingLevel }` as keyof JSX.IntrinsicElements;
return (
<TagName className={ className }>
<label
className="screen-reader-text"
htmlFor={ `block-title-${ instanceId }` }
>
{ __( 'Block title', 'woo-gutenberg-products-block' ) }
</label>
<PlainText
id={ `block-title-${ instanceId }` }
className="wc-block-editor-components-title"
value={ heading }
onChange={ onChange }
style={ { backgroundColor: 'transparent' } }
/>
</TagName>
);
};
export default withInstanceId( BlockTitle );
color-panel/index.tsx 0000644 00000010507 15155071622 0010635 0 ustar 00 /* eslint-disable @typescript-eslint/ban-ts-comment */
/**
* External dependencies
*/
import { useMemo } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import {
store as blockEditorStore,
getColorClassName,
InspectorControls,
useBlockEditContext,
// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
__experimentalColorGradientSettingsDropdown as ColorGradientSettingsDropdown,
// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
__experimentalUseMultipleOriginColorsAndGradients as useMultipleOriginColorsAndGradients,
} from '@wordpress/block-editor';
/**
* Internal dependencies
*/
import {
ColorSetting,
ColorAttributes,
ColorPaletteOption,
GradientPaletteOption,
ColorGradientOptionsItems,
CustomColorsMap,
} from './types';
const flattenColors = (
colorGradientOptions: ColorGradientOptionsItems
): ColorPaletteOption[] & GradientPaletteOption[] => {
const flattenedColors: ColorPaletteOption[] & GradientPaletteOption[] = [];
if ( colorGradientOptions.colors ) {
colorGradientOptions.colors.forEach( ( colorItem ) => {
flattenedColors.push( ...colorItem.colors );
} );
}
if ( colorGradientOptions.gradients ) {
colorGradientOptions.gradients.forEach( ( gradientItem ) => {
flattenedColors.push( ...gradientItem.gradients );
} );
}
return flattenedColors;
};
const getColorObject = (
colors: ColorPaletteOption[] & GradientPaletteOption[],
colorValue: string | undefined,
context: string
) => {
if ( ! colorValue ) {
return;
}
const colorObject =
( colors?.find( ( color ) => {
return color.color === colorValue || color.slug === colorValue;
} ) as {
color: string;
slug?: string | undefined;
class?: string | undefined;
} ) || {};
if ( ! colorObject?.color ) {
colorObject.color = colorValue;
}
colorObject.class = getColorClassName( context, colorObject?.slug );
return colorObject;
};
const createSetColor = (
colorName: string,
context: string,
colors: ColorPaletteOption[] & GradientPaletteOption[],
setAttributes: ( attributes: Record< string, unknown > ) => void
): ( ( colorValue?: string ) => void ) => {
return ( colorValue?: string ) => {
const colorObject = getColorObject( colors, colorValue, context ) || {};
setAttributes( {
[ colorName ]: colorObject,
} );
};
};
const createSettings = (
colorTypes: CustomColorsMap,
colors: ColorPaletteOption[] & GradientPaletteOption[],
attributes: ColorAttributes | undefined,
setAttributes: ( attributes: Record< string, unknown > ) => void
) => {
return Object.entries( colorTypes ).reduce(
( settingsAccumulator, [ colorAttributeName, colorAttribute ] ) => {
const colorSetter = createSetColor(
colorAttributeName,
colorAttribute.context,
colors,
setAttributes
);
const colorSetting = {
colorValue:
attributes?.[ colorAttributeName ]?.color ?? undefined,
label: colorAttribute.label,
onColorChange: colorSetter,
resetAllFilter: () => colorSetter(),
};
settingsAccumulator.push( colorSetting );
return settingsAccumulator;
},
[] as ColorSetting[]
);
};
export const ColorPanel = ( {
colorTypes,
}: {
colorTypes: CustomColorsMap;
} ) => {
const colorGradientOptions = useMultipleOriginColorsAndGradients();
const flattenedColors = flattenColors( colorGradientOptions );
const { clientId } = useBlockEditContext();
const attributes = useSelect(
( select ) => {
// @ts-ignore @wordpress/block-editor/store types not provided
const { getBlockAttributes } = select( blockEditorStore );
return getBlockAttributes( clientId ) || {};
},
[ clientId ]
);
// @ts-ignore @wordpress/block-editor/store types not provided
const { updateBlockAttributes } = useDispatch( blockEditorStore );
const settings = useMemo( () => {
return createSettings(
colorTypes,
flattenedColors,
attributes,
( newAttributes ) =>
updateBlockAttributes( clientId, newAttributes )
);
}, [
colorTypes,
flattenedColors,
updateBlockAttributes,
attributes,
clientId,
] );
return (
colorGradientOptions.hasColorsOrGradients && (
// @ts-ignore The dev package version doesn't have types for group.
<InspectorControls group="color">
<ColorGradientSettingsDropdown
__experimentalIsRenderedInSidebar
settings={ settings }
panelId={ clientId }
{ ...colorGradientOptions }
/>
</InspectorControls>
)
);
};
color-panel/types.ts 0000644 00000001543 15155071622 0010502 0 ustar 00 export interface ColorSetting {
colorValue: string | undefined;
onColorChange: ( value: string ) => void;
label: string;
resetAllFilter: () => void;
}
export interface ColorAttributes {
[ key: string ]: {
[ key: string ]: string;
};
}
export interface CustomColorsMap {
[ key: string ]: {
label: string;
context: string;
};
}
export interface ColorPaletteOption {
name: string;
slug: string | undefined;
color: string;
}
export interface GradientPaletteOption {
name: string;
gradient: string;
slug: string;
}
interface ColorGradientOptionsColorItem {
name: string;
colors: ColorPaletteOption[];
}
interface ColorGradientOptionsGradientItem {
name: string;
gradients: GradientPaletteOption[];
}
export interface ColorGradientOptionsItems {
colors: [ ColorGradientOptionsColorItem ];
gradients: [ ColorGradientOptionsGradientItem ];
}
default-notice/editor.scss 0000644 00000000415 15155071622 0011636 0 ustar 00 .wc-default-page-notice.is-dismissible {
margin: 0;
padding-right: 16px;
.components-notice__dismiss {
min-width: 24px;
}
.components-notice__content {
margin: 4px 0;
}
svg {
width: 16px;
height: 16px;
}
}
.wc-blocks-legacy-page-notice {
margin: 0;
}
default-notice/index.tsx 0000644 00000010130 15155071622 0011315 0 ustar 00 /**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { store as editorStore } from '@wordpress/editor';
import triggerFetch from '@wordpress/api-fetch';
import { store as coreStore } from '@wordpress/core-data';
import { Notice, Button } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { CHECKOUT_PAGE_ID, CART_PAGE_ID } from '@woocommerce/block-settings';
import { useCallback, useState } from '@wordpress/element';
/**
* Internal dependencies
*/
import './editor.scss';
export function DefaultNotice( { block }: { block: string } ) {
// To avoid having the same logic twice, we're going to handle both pages here.
const ORIGINAL_PAGE_ID =
block === 'checkout' ? CHECKOUT_PAGE_ID : CART_PAGE_ID;
const settingName =
block === 'checkout'
? 'woocommerce_checkout_page_id'
: 'woocommerce_cart_page_id';
const noticeContent =
block === 'checkout'
? __(
'If you would like to use this block as your default checkout, update your page settings',
'woo-gutenberg-products-block'
)
: __(
'If you would like to use this block as your default cart, update your page settings',
'woo-gutenberg-products-block'
);
// Everything below works the same for Cart/Checkout
const { saveEntityRecord } = useDispatch( coreStore );
const { editPost, savePost } = useDispatch( editorStore );
const { slug, isLoadingPage, postPublished, currentPostId } = useSelect(
( select ) => {
const { getEntityRecord, isResolving } = select( coreStore );
const { isCurrentPostPublished, getCurrentPostId } =
select( editorStore );
return {
slug:
getEntityRecord( 'postType', 'page', ORIGINAL_PAGE_ID )
?.slug || block,
isLoadingPage: isResolving( 'getEntityRecord', [
'postType',
'page',
ORIGINAL_PAGE_ID,
] ),
postPublished: isCurrentPostPublished(),
currentPostId: getCurrentPostId(),
};
},
[]
);
const [ settingStatus, setStatus ] = useState( 'pristine' );
const updatePage = useCallback( () => {
setStatus( 'updating' );
Promise.resolve()
.then( () =>
triggerFetch( {
path: `/wc/v3/settings/advanced/${ settingName }`,
method: 'GET',
} )
)
.catch( ( error ) => {
if ( error.code === 'rest_setting_setting_invalid' ) {
setStatus( 'error' );
}
} )
.then( () => {
if ( ! postPublished ) {
editPost( { status: 'publish' } );
return savePost();
}
} )
.then( () =>
// Make this page ID the default cart/checkout.
triggerFetch( {
path: `/wc/v3/settings/advanced/${ settingName }`,
method: 'POST',
data: {
value: currentPostId.toString(),
},
} )
)
// Append `-2` to the original link so we can use it here.
.then( () => {
if ( ORIGINAL_PAGE_ID !== 0 ) {
return saveEntityRecord( 'postType', 'page', {
id: ORIGINAL_PAGE_ID,
slug: `${ slug }-2`,
} );
}
} )
// Use the original link for this page.
.then( () => editPost( { slug } ) )
// Save page.
.then( () => savePost() )
.then( () => setStatus( 'updated' ) );
}, [
postPublished,
editPost,
savePost,
settingName,
currentPostId,
ORIGINAL_PAGE_ID,
saveEntityRecord,
slug,
] );
if ( currentPostId === ORIGINAL_PAGE_ID || settingStatus === 'dismissed' ) {
return null;
}
return (
<Notice
className="wc-default-page-notice"
status={ settingStatus === 'updated' ? 'success' : 'warning' }
onRemove={ () => setStatus( 'dismissed' ) }
spokenMessage={
settingStatus === 'updated'
? __(
'Page settings updated',
'woo-gutenberg-products-block'
)
: noticeContent
}
>
{ settingStatus === 'updated' ? (
__( 'Page settings updated', 'woo-gutenberg-products-block' )
) : (
<>
<p>{ noticeContent }</p>
<Button
onClick={ updatePage }
variant="secondary"
isBusy={ settingStatus === 'updating' }
disabled={ isLoadingPage }
isSmall={ true }
>
{ __(
'Update your page settings',
'woo-gutenberg-products-block'
) }
</Button>
</>
) }
</Notice>
);
}
edit-product-link/index.tsx 0000644 00000003011 15155071622 0011750 0 ustar 00 /**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Icon, external } from '@wordpress/icons';
import { ADMIN_URL } from '@woocommerce/settings';
import { InspectorControls } from '@wordpress/block-editor';
import { useProductDataContext } from '@woocommerce/shared-context';
interface EditProductLinkProps {
id?: number | undefined;
productId?: number | undefined;
}
/**
* Component to render an edit product link in the sidebar.
*
* @param {Object} props Component props.
*/
const EditProductLink = ( props: EditProductLinkProps ): JSX.Element | null => {
const productDataContext = useProductDataContext();
const product = productDataContext.product || {};
const productId = product.id || props.productId || 0;
if ( ! productId || productId === 1 ) {
return null;
}
return (
<InspectorControls>
<div className="wc-block-single-product__edit-card">
<div className="wc-block-single-product__edit-card-title">
<a
href={ `${ ADMIN_URL }post.php?post=${ productId }&action=edit` }
target="_blank"
rel="noopener noreferrer"
>
{ __(
"Edit this product's details",
'woo-gutenberg-products-block'
) }
<Icon icon={ external } size={ 16 } />
</a>
</div>
<div className="wc-block-single-product__edit-card-description">
{ __(
'Edit details such as title, price, description and more.',
'woo-gutenberg-products-block'
) }
</div>
</div>
</InspectorControls>
);
};
export default EditProductLink;
editable-button/index.tsx 0000644 00000001467 15155071622 0011511 0 ustar 00 /**
* External dependencies
*/
import Button, { ButtonProps } from '@woocommerce/base-components/button';
import { RichText } from '@wordpress/block-editor';
export interface EditableButtonProps
extends Omit< ButtonProps, 'onChange' | 'placeholder' | 'value' > {
/**
* On change callback.
*/
onChange: ( value: string ) => void;
/**
* The placeholder of the editable button.
*/
placeholder?: string;
/**
* The current value of the editable button.
*/
value: string;
}
const EditableButton = ( {
onChange,
placeholder,
value,
...props
}: EditableButtonProps ) => {
return (
<Button { ...props }>
<RichText
multiline={ false }
allowedFormats={ [] }
value={ value }
placeholder={ placeholder }
onChange={ onChange }
/>
</Button>
);
};
export default EditableButton;
error-placeholder/editor.scss 0000644 00000000361 15155071622 0012344 0 ustar 00 .wc-block-error-message {
margin-bottom: 16px;
margin-top: 8px;
}
.wc-block-api-error {
.components-placeholder__fieldset {
display: block;
}
.wc-block-error-message {
margin-top: 0;
}
.components-spinner {
float: none;
}
}
error-placeholder/error-message.tsx 0000644 00000002155 15155071622 0013477 0 ustar 00 /**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { escapeHTML } from '@wordpress/escape-html';
/**
* Internal dependencies
*/
import { ErrorObject } from '.';
export interface ErrorMessageProps {
/**
* The error object.
*/
error: ErrorObject;
}
const getErrorMessage = ( { message, type }: ErrorObject ) => {
if ( ! message ) {
return __(
'An error has prevented the block from being updated.',
'woo-gutenberg-products-block'
);
}
if ( type === 'general' ) {
return (
<span>
{ __(
'The following error was returned',
'woo-gutenberg-products-block'
) }
<br />
<code>{ escapeHTML( message ) }</code>
</span>
);
}
if ( type === 'api' ) {
return (
<span>
{ __(
'The following error was returned from the API',
'woo-gutenberg-products-block'
) }
<br />
<code>{ escapeHTML( message ) }</code>
</span>
);
}
return message;
};
const ErrorMessage = ( { error }: ErrorMessageProps ): JSX.Element => (
<div className="wc-block-error-message">{ getErrorMessage( error ) }</div>
);
export default ErrorMessage;
error-placeholder/index.tsx 0000644 00000003110 15155071622 0012023 0 ustar 00 /**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Icon, warning } from '@wordpress/icons';
import classNames from 'classnames';
import { Button, Placeholder, Spinner } from '@wordpress/components';
/**
* Internal dependencies
*/
import ErrorMessage from './error-message';
import './editor.scss';
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' | string;
}
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 icon={ warning } /> }
label={ __(
'Sorry, an error occurred',
'woo-gutenberg-products-block'
) }
className={ classNames( 'wc-block-api-error', className ) }
>
<ErrorMessage error={ error } />
{ onRetry && (
<>
{ isLoading ? (
<Spinner />
) : (
<Button isSecondary onClick={ onRetry }>
{ __( 'Retry', 'woo-gutenberg-products-block' ) }
</Button>
) }
</>
) }
</Placeholder>
);
export default ErrorPlaceholder;
error-placeholder/stories/error-message.tsx 0000644 00000001203 15155071622 0015160 0 ustar 00 /**
* External dependencies
*/
import type { 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',
},
};
error-placeholder/stories/error-placeholder.tsx 0000644 00000003000 15155071622 0016013 0 ustar 00 /**
* External dependencies
*/
import type { Story, Meta } from '@storybook/react';
import { useArgs } from '@storybook/client-api';
import { INTERACTION_TIMEOUT } from '@woocommerce/storybook-controls';
/**
* 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 } ),
INTERACTION_TIMEOUT
);
}
: 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',
},
};
expandable-search-list-item/expandable-search-list-item.tsx 0000644 00000002106 15155071622 0020035 0 ustar 00 /**
* External dependencies
*/
import { Spinner } from '@wordpress/components';
import { SearchListItem } from '@woocommerce/editor-components/search-list-control';
import { renderItemArgs } from '@woocommerce/editor-components/search-list-control/types';
import classNames from 'classnames';
interface ExpandableSearchListItemProps extends renderItemArgs {
isLoading: boolean;
}
const ExpandableSearchListItem = ( {
className,
item,
isSelected,
isLoading,
onSelect,
disabled,
...rest
}: ExpandableSearchListItemProps ): JSX.Element => {
return (
<>
<SearchListItem
{ ...rest }
key={ item.id }
className={ className }
isSelected={ isSelected }
item={ item }
onSelect={ onSelect }
disabled={ disabled }
/>
{ isSelected && isLoading && (
<div
key="loading"
className={ classNames(
'woocommerce-search-list__item',
'woocommerce-product-attributes__item',
'depth-1',
'is-loading',
'is-not-active'
) }
>
<Spinner />
</div>
) }
</>
);
};
export default ExpandableSearchListItem;
external-link-card/editor.scss 0000644 00000002003 15155071622 0012412 0 ustar 00 .wc-block-editor-components-external-link-card {
display: flex;
flex-direction: row;
text-decoration: none;
margin: $gap-large 0;
color: inherit;
align-items: flex-start;
+ .wc-block-editor-components-external-link-card {
margin-top: -($gap-large - $gap);
}
.wc-block-editor-components-external-link-card__content {
flex: 1 1 0;
padding-right: $gap;
}
.wc-block-editor-components-external-link-card__title {
font-weight: 500;
display: block;
}
.wc-block-editor-components-external-link-card__description {
color: $gray-700;
display: block;
@include font-size(small);
margin-top: 0.5em;
}
.wc-block-editor-components-external-link-card__icon {
flex: 0 0 24px;
margin: 0;
text-align: right;
color: inherit;
vertical-align: top;
}
.wc-block-editor-components-external-link-card__warning {
color: #cc1818;
display: flex;
align-items: flex-start;
font-size: 0.875em;
column-gap: 4px;
margin-top: 0.5em;
svg {
width: 18px;
height: 18px;
min-width: 18px;
}
}
}
external-link-card/index.tsx 0000644 00000003370 15155071622 0012106 0 ustar 00 /**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Icon, external } from '@wordpress/icons';
import { VisuallyHidden } from '@wordpress/components';
import { sanitizeHTML } from '@woocommerce/utils';
import { Alert } from '@woocommerce/icons';
/**
* Internal dependencies
*/
import './editor.scss';
export interface ExternalLinkCardProps {
href: string;
title: string;
description?: string;
warning?: string;
}
/**
* Show a link that displays a title, description, and an icon showing that the link is external.
* Links are opened in a new tab.
*/
const ExternalLinkCard = ( {
href,
title,
description,
warning,
}: ExternalLinkCardProps ): JSX.Element => {
return (
<a
href={ href }
className="wc-block-editor-components-external-link-card"
target="_blank"
rel="noreferrer"
>
<span className="wc-block-editor-components-external-link-card__content">
<strong className="wc-block-editor-components-external-link-card__title">
{ title }
</strong>
{ description && (
<span
className="wc-block-editor-components-external-link-card__description"
dangerouslySetInnerHTML={ {
__html: sanitizeHTML( description ),
} }
></span>
) }
{ warning ? (
<span className="wc-block-editor-components-external-link-card__warning">
<Icon icon={ <Alert status="error" /> } />
<span>{ warning }</span>
</span>
) : null }
</span>
<VisuallyHidden as="span">
{
/* translators: accessibility text */
__( '(opens in a new tab)', 'woo-gutenberg-products-block' )
}
</VisuallyHidden>
<Icon
icon={ external }
className="wc-block-editor-components-external-link-card__icon"
/>
</a>
);
};
export default ExternalLinkCard;
external-link-card/stories/index.tsx 0000644 00000001432 15155071622 0013573 0 ustar 00 /**
* External dependencies
*/
import type { Story, Meta } from '@storybook/react';
/**
* Internal dependencies
*/
import ExternalLinkCard, { ExternalLinkCardProps } from '..';
export default {
title: 'WooCommerce Blocks/editor-components/ExternalLinkCard',
component: ExternalLinkCard,
} as Meta< ExternalLinkCardProps >;
const Template: Story< ExternalLinkCardProps > = ( args ) => (
<ExternalLinkCard { ...args } />
);
export const Default = Template.bind( {} );
Default.args = {
description:
'This is the description of the link, perhaps a bit of a longer paragraph or a summary of a blog post, or whatever could give more context',
href: 'https://woocommerce.com/posts/seven-tips-to-extend-holiday-sales-momentum/',
title: 'Seven tips to extend holiday sales momentum',
};
feedback-prompt/index.tsx 0000644 00000006351 15155071622 0011467 0 ustar 00 /**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Icon, commentContent, external } from '@wordpress/icons';
import { useEffect, useState } from '@wordpress/element';
/**
* Internal dependencies
*/
import './style.scss';
interface FeedbackPromptProps {
text: string;
title?: string;
url?: string;
}
/**
* Component to render a Feedback prompt in the sidebar.
*
* @param {Object} props Incoming props for the component.
* @param {string} props.text
* @param {string} props.title
* @param {string} props.url
*/
const FeedbackPrompt = ( {
text,
title = __( 'Feedback?', 'woo-gutenberg-products-block' ),
url = 'https://ideas.woocommerce.com/forums/133476-woocommerce?category_id=384565',
}: FeedbackPromptProps ) => {
// By returning false we ensure that this component is not entered into the InspectorControls
// (which is a slot fill), children array on first render, on the second render when the state
// gets updated this component does get put into the InspectorControls children array but as the
// last item, ensuring it shows last in the sidebar.
const [ isVisible, setIsVisible ] = useState( false );
useEffect( () => {
setIsVisible( true );
}, [] );
return (
<>
{ isVisible && (
<div className="wc-block-feedback-prompt">
<Icon icon={ commentContent } />
<h2 className="wc-block-feedback-prompt__title">
{ title }
</h2>
<p className="wc-block-feedback-prompt__text">{ text }</p>
<a
href={ url }
className="wc-block-feedback-prompt__link"
rel="noreferrer noopener"
target="_blank"
>
{ __(
'Give us your feedback.',
'woo-gutenberg-products-block'
) }
<Icon icon={ external } size={ 16 } />
</a>
</div>
) }
</>
);
};
export default FeedbackPrompt;
export const CartCheckoutFeedbackPrompt = () => (
<FeedbackPrompt
text={ __(
'We are currently working on improving our cart and checkout blocks to provide merchants with the tools and customization options they need.',
'woo-gutenberg-products-block'
) }
url="https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues/new?template=--cart-checkout-feedback.md"
/>
);
export const LegacyFeedbackPrompt = () => (
<FeedbackPrompt
text={ __(
'We are working on a better editing experience that will replace classic blocks. Keep an eye out for updates!',
'woo-gutenberg-products-block'
) }
url="https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues/new?template=--classic-block-feedback.md"
/>
);
export const ProductQueryFeedbackPrompt = () => (
<FeedbackPrompt
text={ __(
'Thanks for trying out the Products block! Help us make it better by sharing your feedback.',
'woo-gutenberg-products-block'
) }
title={ __( 'Share your feedback!', 'woo-gutenberg-products-block' ) }
url={ 'https://airtable.com/shrFX5FAqmCY6hVYI' }
/>
);
export const ProductCollectionFeedbackPrompt = () => (
<FeedbackPrompt
text={ __(
'Thanks for trying out the Product Collection block! Help us make it better by sharing your feedback.',
'woo-gutenberg-products-block'
) }
title={ __( 'Share your feedback!', 'woo-gutenberg-products-block' ) }
url={ 'https://airtable.com/shrqsMSDPvAKoY99u' }
/>
);
feedback-prompt/style.scss 0000644 00000000513 15155071622 0011647 0 ustar 00 .wc-block-feedback-prompt {
background-color: #f7f7f7;
border-top: 1px solid $gray-200;
margin: 0 -16px 0;
padding: $gap-large;
text-align: center;
.wc-block-feedback-prompt__title {
margin: 0 0 $gap-small;
}
.wc-block-feedback-prompt__link {
color: inherit;
> .gridicon {
vertical-align: text-bottom;
}
}
}
grid-content-control/index.tsx 0000644 00000003701 15155071622 0012473 0 ustar 00 /**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { ToggleControl } from '@wordpress/components';
interface GridContentControlProps {
onChange: ( settings: GridContentSettings ) => void;
settings: GridContentSettings;
}
interface GridContentSettings {
image: boolean;
button: boolean;
price: boolean;
rating: boolean;
title: boolean;
}
/**
* A combination of toggle controls for content visibility in product grids.
*
* @param {Object} props Incoming props for the component.
* @param {function(any):any} props.onChange
* @param {Object} props.settings
*/
const GridContentControl = ( {
onChange,
settings,
}: GridContentControlProps ) => {
const { image, button, price, rating, title } = settings;
// If `image` is undefined, that might be because it's a block that was
// created before the `image` attribute existed, so we default to true.
const imageIsVisible = image !== false;
return (
<>
<ToggleControl
label={ __( 'Product image', 'woo-gutenberg-products-block' ) }
checked={ imageIsVisible }
onChange={ () =>
onChange( { ...settings, image: ! imageIsVisible } )
}
/>
<ToggleControl
label={ __( 'Product title', 'woo-gutenberg-products-block' ) }
checked={ title }
onChange={ () => onChange( { ...settings, title: ! title } ) }
/>
<ToggleControl
label={ __( 'Product price', 'woo-gutenberg-products-block' ) }
checked={ price }
onChange={ () => onChange( { ...settings, price: ! price } ) }
/>
<ToggleControl
label={ __( 'Product rating', 'woo-gutenberg-products-block' ) }
checked={ rating }
onChange={ () => onChange( { ...settings, rating: ! rating } ) }
/>
<ToggleControl
label={ __(
'Add to Cart button',
'woo-gutenberg-products-block'
) }
checked={ button }
onChange={ () => onChange( { ...settings, button: ! button } ) }
/>
</>
);
};
export default GridContentControl;
grid-layout-control/index.tsx 0000644 00000005336 15155071622 0012344 0 ustar 00 /**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { RangeControl, ToggleControl } from '@wordpress/components';
interface ClampProps {
( number: number, boundOne: number, boundTwo?: number ): number;
}
const clamp: ClampProps = ( number, boundOne, boundTwo ) => {
if ( ! boundTwo ) {
return Math.max( number, boundOne ) === boundOne ? number : boundOne;
} else if ( Math.min( number, boundOne ) === number ) {
return boundOne;
} else if ( Math.max( number, boundTwo ) === number ) {
return boundTwo;
}
return number;
};
interface GridLayoutControlProps {
columns: number;
rows: number;
setAttributes: ( attributes: Record< string, unknown > ) => void;
alignButtons: boolean;
minColumns?: number;
maxColumns?: number;
minRows?: number;
maxRows?: number;
}
/**
* A combination of range controls for product grid layout settings.
*
* @param {Object} props Incoming props for the component.
* @param {number} props.columns
* @param {number} props.rows
* @param {function(any):any} props.setAttributes Setter for block attributes.
* @param {boolean} props.alignButtons
* @param {number} props.minColumns
* @param {number} props.maxColumns
* @param {number} props.minRows
* @param {number} props.maxRows
*/
const GridLayoutControl = ( {
columns,
rows,
setAttributes,
alignButtons,
minColumns = 1,
maxColumns = 6,
minRows = 1,
maxRows = 6,
}: GridLayoutControlProps ) => {
return (
<>
<RangeControl
label={ __( 'Columns', 'woo-gutenberg-products-block' ) }
value={ columns }
onChange={ ( value: number ) => {
const newValue = clamp( value, minColumns, maxColumns );
setAttributes( {
columns: Number.isNaN( newValue ) ? '' : newValue,
} );
} }
min={ minColumns }
max={ maxColumns }
/>
<RangeControl
label={ __( 'Rows', 'woo-gutenberg-products-block' ) }
value={ rows }
onChange={ ( value: number ) => {
const newValue = clamp( value, minRows, maxRows );
setAttributes( {
rows: Number.isNaN( newValue ) ? '' : newValue,
} );
} }
min={ minRows }
max={ maxRows }
/>
<ToggleControl
label={ __(
'Align the last block to the bottom',
'woo-gutenberg-products-block'
) }
help={
alignButtons
? __(
'Align the last block to the bottom.',
'woo-gutenberg-products-block'
)
: __(
'The last inner block will follow other content.',
'woo-gutenberg-products-block'
)
}
checked={ alignButtons }
onChange={ () =>
setAttributes( { alignButtons: ! alignButtons } )
}
/>
</>
);
};
export default GridLayoutControl;
heading-toolbar/heading-level-icon.js 0000644 00000005040 15155071622 0013576 0 ustar 00 /**
* External dependencies
*/
import { Path, SVG } from '@wordpress/primitives';
export default function HeadingLevelIcon( { level } ) {
const levelToPath = {
1: 'M9 5h2v10H9v-4H5v4H3V5h2v4h4V5zm6.6 0c-.6.9-1.5 1.7-2.6 2v1h2v7h2V5h-1.4z',
2: 'M7 5h2v10H7v-4H3v4H1V5h2v4h4V5zm8 8c.5-.4.6-.6 1.1-1.1.4-.4.8-.8 1.2-1.3.3-.4.6-.8.9-1.3.2-.4.3-.8.3-1.3 0-.4-.1-.9-.3-1.3-.2-.4-.4-.7-.8-1-.3-.3-.7-.5-1.2-.6-.5-.2-1-.2-1.5-.2-.4 0-.7 0-1.1.1-.3.1-.7.2-1 .3-.3.1-.6.3-.9.5-.3.2-.6.4-.8.7l1.2 1.2c.3-.3.6-.5 1-.7.4-.2.7-.3 1.2-.3s.9.1 1.3.4c.3.3.5.7.5 1.1 0 .4-.1.8-.4 1.1-.3.5-.6.9-1 1.2-.4.4-1 .9-1.6 1.4-.6.5-1.4 1.1-2.2 1.6V15h8v-2H15z',
3: 'M12.1 12.2c.4.3.8.5 1.2.7.4.2.9.3 1.4.3.5 0 1-.1 1.4-.3.3-.1.5-.5.5-.8 0-.2 0-.4-.1-.6-.1-.2-.3-.3-.5-.4-.3-.1-.7-.2-1-.3-.5-.1-1-.1-1.5-.1V9.1c.7.1 1.5-.1 2.2-.4.4-.2.6-.5.6-.9 0-.3-.1-.6-.4-.8-.3-.2-.7-.3-1.1-.3-.4 0-.8.1-1.1.3-.4.2-.7.4-1.1.6l-1.2-1.4c.5-.4 1.1-.7 1.6-.9.5-.2 1.2-.3 1.8-.3.5 0 1 .1 1.6.2.4.1.8.3 1.2.5.3.2.6.5.8.8.2.3.3.7.3 1.1 0 .5-.2.9-.5 1.3-.4.4-.9.7-1.5.9v.1c.6.1 1.2.4 1.6.8.4.4.7.9.7 1.5 0 .4-.1.8-.3 1.2-.2.4-.5.7-.9.9-.4.3-.9.4-1.3.5-.5.1-1 .2-1.6.2-.8 0-1.6-.1-2.3-.4-.6-.2-1.1-.6-1.6-1l1.1-1.4zM7 9H3V5H1v10h2v-4h4v4h2V5H7v4z',
4: 'M9 15H7v-4H3v4H1V5h2v4h4V5h2v10zm10-2h-1v2h-2v-2h-5v-2l4-6h3v6h1v2zm-3-2V7l-2.8 4H16z',
5: 'M12.1 12.2c.4.3.7.5 1.1.7.4.2.9.3 1.3.3.5 0 1-.1 1.4-.4.4-.3.6-.7.6-1.1 0-.4-.2-.9-.6-1.1-.4-.3-.9-.4-1.4-.4H14c-.1 0-.3 0-.4.1l-.4.1-.5.2-1-.6.3-5h6.4v1.9h-4.3L14 8.8c.2-.1.5-.1.7-.2.2 0 .5-.1.7-.1.5 0 .9.1 1.4.2.4.1.8.3 1.1.6.3.2.6.6.8.9.2.4.3.9.3 1.4 0 .5-.1 1-.3 1.4-.2.4-.5.8-.9 1.1-.4.3-.8.5-1.3.7-.5.2-1 .3-1.5.3-.8 0-1.6-.1-2.3-.4-.6-.2-1.1-.6-1.6-1-.1-.1 1-1.5 1-1.5zM9 15H7v-4H3v4H1V5h2v4h4V5h2v10z',
6: 'M9 15H7v-4H3v4H1V5h2v4h4V5h2v10zm8.6-7.5c-.2-.2-.5-.4-.8-.5-.6-.2-1.3-.2-1.9 0-.3.1-.6.3-.8.5l-.6.9c-.2.5-.2.9-.2 1.4.4-.3.8-.6 1.2-.8.4-.2.8-.3 1.3-.3.4 0 .8 0 1.2.2.4.1.7.3 1 .6.3.3.5.6.7.9.2.4.3.8.3 1.3s-.1.9-.3 1.4c-.2.4-.5.7-.8 1-.4.3-.8.5-1.2.6-1 .3-2 .3-3 0-.5-.2-1-.5-1.4-.9-.4-.4-.8-.9-1-1.5-.2-.6-.3-1.3-.3-2.1s.1-1.6.4-2.3c.2-.6.6-1.2 1-1.6.4-.4.9-.7 1.4-.9.6-.3 1.1-.4 1.7-.4.7 0 1.4.1 2 .3.5.2 1 .5 1.4.8 0 .1-1.3 1.4-1.3 1.4zm-2.4 5.8c.2 0 .4 0 .6-.1.2 0 .4-.1.5-.2.1-.1.3-.3.4-.5.1-.2.1-.5.1-.7 0-.4-.1-.8-.4-1.1-.3-.2-.7-.3-1.1-.3-.3 0-.7.1-1 .2-.4.2-.7.4-1 .7 0 .3.1.7.3 1 .1.2.3.4.4.6.2.1.3.3.5.3.2.1.5.2.7.1z',
};
if ( ! levelToPath.hasOwnProperty( level ) ) {
return null;
}
return (
<SVG
width="20"
height="20"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<Path d={ levelToPath[ level ] } />
</SVG>
);
}
heading-toolbar/index.js 0000644 00000002415 15155071622 0011256 0 ustar 00 /**
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { Component } from '@wordpress/element';
import { ToolbarGroup } from '@wordpress/components';
/**
* Internal dependencies
*/
import HeadingLevelIcon from './heading-level-icon';
/**
* HeadingToolbar component.
*
* Allows the heading level to be chosen for a title block.
*/
class HeadingToolbar extends Component {
createLevelControl( targetLevel, selectedLevel, onChange ) {
const isActive = targetLevel === selectedLevel;
return {
icon: <HeadingLevelIcon level={ targetLevel } />,
title: sprintf(
/* translators: %s: heading level e.g: "2", "3", "4" */
__( 'Heading %d', 'woocommerce' ),
targetLevel
),
isActive,
onClick: () => onChange( targetLevel ),
};
}
render() {
const {
isCollapsed = true,
minLevel,
maxLevel,
selectedLevel,
onChange,
} = this.props;
const levels = Array.from(
{ length: maxLevel - minLevel + 1 },
( _, i ) => i + minLevel
);
return (
<ToolbarGroup
isCollapsed={ isCollapsed }
icon={ <HeadingLevelIcon level={ selectedLevel } /> }
controls={ levels.map( ( index ) =>
this.createLevelControl( index, selectedLevel, onChange )
) }
/>
);
}
}
export default HeadingToolbar;
incompatible-payment-gateways-notice/editor.scss 0000644 00000001326 15155071622 0016157 0 ustar 00 .wc-blocks-incompatible-extensions-notice.is-dismissible {
margin: 0;
padding-right: 16px;
.components-notice__dismiss {
min-width: 24px;
}
.components-notice__content {
margin: 4px 0;
}
svg {
width: 16px;
height: 16px;
}
.wc-blocks-incompatible-extensions-notice__content {
display: flex;
.wc-blocks-incompatible-extensions-notice__warning-icon {
width: 24px;
height: 24px;
margin-right: 6px;
min-width: max-content;
}
}
.wc-blocks-incompatible-extensions-notice__element {
display: flex;
align-items: center;
position: relative;
&::before {
content: "•";
position: absolute;
left: -13px;
font-size: 1.2rem;
}
}
}
.wc-blocks-legacy-page-notice {
margin: 0;
}
incompatible-payment-gateways-notice/index.tsx 0000644 00000004431 15155071622 0015643 0 ustar 00 /**
* External dependencies
*/
import { _n } from '@wordpress/i18n';
import { Notice, ExternalLink } from '@wordpress/components';
import { createInterpolateElement, useEffect } from '@wordpress/element';
import { Alert } from '@woocommerce/icons';
import { Icon } from '@wordpress/icons';
/**
* Internal dependencies
*/
import { useIncompatiblePaymentGatewaysNotice } from './use-incompatible-payment-gateways-notice';
import './editor.scss';
interface PaymentGatewaysNoticeProps {
toggleDismissedStatus: ( status: boolean ) => void;
block: 'woocommerce/cart' | 'woocommerce/checkout';
}
export function IncompatiblePaymentGatewaysNotice( {
toggleDismissedStatus,
block,
}: PaymentGatewaysNoticeProps ) {
const [
isVisible,
dismissNotice,
incompatiblePaymentMethods,
numberOfIncompatiblePaymentMethods,
] = useIncompatiblePaymentGatewaysNotice( block );
useEffect( () => {
toggleDismissedStatus( ! isVisible );
}, [ isVisible, toggleDismissedStatus ] );
if ( ! isVisible ) {
return null;
}
const noticeContent = createInterpolateElement(
_n(
'The following extension is incompatible with the block-based checkout. <a>Learn more</a>',
'The following extensions are incompatible with the block-based checkout. <a>Learn more</a>',
numberOfIncompatiblePaymentMethods,
'woo-gutenberg-products-block'
),
{
a: (
// Suppress the warning as this <a> will be interpolated into the string with content.
// eslint-disable-next-line jsx-a11y/anchor-has-content
<ExternalLink href="https://woocommerce.com/document/cart-checkout-blocks-support-status/" />
),
}
);
return (
<Notice
className="wc-blocks-incompatible-extensions-notice"
status={ 'warning' }
onRemove={ dismissNotice }
spokenMessage={ noticeContent }
>
<div className="wc-blocks-incompatible-extensions-notice__content">
<Icon
className="wc-blocks-incompatible-extensions-notice__warning-icon"
icon={ <Alert /> }
/>
<div>
<p>{ noticeContent }</p>
<ul>
{ Object.entries( incompatiblePaymentMethods ).map(
( [ id, title ] ) => (
<li
key={ id }
className="wc-blocks-incompatible-extensions-notice__element"
>
{ title }
</li>
)
) }
</ul>
</div>
</div>
</Notice>
);
}
incompatible-payment-gateways-notice/use-incompatible-payment-gateways-notice.ts 0000644 00000005462 15155071622 0024365 0 ustar 00 /**
* External dependencies
*/
import { useSelect } from '@wordpress/data';
import { useEffect, useState } from '@wordpress/element';
import { useLocalStorageState } from '@woocommerce/base-hooks';
/**
* Internal dependencies
*/
import { STORE_KEY as PAYMENT_STORE_KEY } from '../../data/payment/constants';
type StoredIncompatibleGateway = { [ k: string ]: string[] };
const initialDismissedNotices: React.SetStateAction<
StoredIncompatibleGateway[]
> = [];
const areEqual = ( array1: string[], array2: string[] ) => {
if ( array1.length !== array2.length ) {
return false;
}
const uniqueCollectionValues = new Set( [ ...array1, ...array2 ] );
return uniqueCollectionValues.size === array1.length;
};
export const useIncompatiblePaymentGatewaysNotice = (
blockName: string
): [ boolean, () => void, { [ k: string ]: string }, number ] => {
const [ dismissedNotices, setDismissedNotices ] = useLocalStorageState<
StoredIncompatibleGateway[]
>(
`wc-blocks_dismissed_incompatible_payment_gateways_notices`,
initialDismissedNotices
);
const [ isVisible, setIsVisible ] = useState( false );
const { incompatiblePaymentMethods } = useSelect( ( select ) => {
const { getIncompatiblePaymentMethods } = select( PAYMENT_STORE_KEY );
return {
incompatiblePaymentMethods: getIncompatiblePaymentMethods(),
};
}, [] );
const incompatiblePaymentMethodsIDs = Object.keys(
incompatiblePaymentMethods
);
const numberOfIncompatiblePaymentMethods =
incompatiblePaymentMethodsIDs.length;
const isDismissedNoticeUpToDate = dismissedNotices.some(
( notice ) =>
Object.keys( notice ).includes( blockName ) &&
areEqual(
notice[ blockName as keyof object ],
incompatiblePaymentMethodsIDs
)
);
const shouldBeDismissed =
numberOfIncompatiblePaymentMethods === 0 || isDismissedNoticeUpToDate;
const dismissNotice = () => {
const dismissedNoticesSet = new Set( dismissedNotices );
dismissedNoticesSet.add( {
[ blockName ]: incompatiblePaymentMethodsIDs,
} );
setDismissedNotices( [ ...dismissedNoticesSet ] );
};
// This ensures the modal is not loaded on first render. This is required so
// Gutenberg doesn't steal the focus from the Guide and focuses the block.
useEffect( () => {
setIsVisible( ! shouldBeDismissed );
if ( ! shouldBeDismissed && ! isDismissedNoticeUpToDate ) {
setDismissedNotices( ( previousDismissedNotices ) =>
previousDismissedNotices.reduce(
( acc: StoredIncompatibleGateway[], curr ) => {
if ( Object.keys( curr ).includes( blockName ) ) {
return acc;
}
acc.push( curr );
return acc;
},
[]
)
);
}
}, [
shouldBeDismissed,
isDismissedNoticeUpToDate,
setDismissedNotices,
blockName,
] );
return [
isVisible,
dismissNotice,
incompatiblePaymentMethods,
numberOfIncompatiblePaymentMethods,
];
};
no-payment-methods-notice/editor.scss 0000644 00000000151 15155071622 0013737 0 ustar 00 .wc-blocks-no-payment-methods-notice {
margin: 0;
.components-notice__content {
margin: 4px 0;
}
}
no-payment-methods-notice/index.tsx 0000644 00000001754 15155071622 0013435 0 ustar 00 /**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Notice, ExternalLink } from '@wordpress/components';
import { ADMIN_URL } from '@woocommerce/settings';
/**
* Internal dependencies
*/
import './editor.scss';
export function NoPaymentMethodsNotice() {
const noticeContent = __(
'Your store does not have any payment methods that support the Checkout block. Once you have configured a compatible payment method it will be displayed here.',
'woo-gutenberg-products-block'
);
return (
<Notice
className="wc-blocks-no-payment-methods-notice"
status={ 'warning' }
spokenMessage={ noticeContent }
isDismissible={ false }
>
<div className="wc-blocks-no-payment-methods-notice__content">
{ noticeContent }{ ' ' }
<ExternalLink
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=checkout` }
>
{ __(
'Configure Payment Methods',
'woo-gutenberg-products-block'
) }
</ExternalLink>
</div>
</Notice>
);
}
page-selector/index.js 0000644 00000002064 15155071622 0010751 0 ustar 00 /**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { PanelBody, SelectControl } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
/**
* Internal dependencies
*/
import { formatTitle } from '../utils';
const PageSelector = ( { setPageId, pageId, labels } ) => {
const pages =
useSelect( ( select ) => {
return select( 'core' ).getEntityRecords( 'postType', 'page', {
status: 'publish',
orderby: 'title',
order: 'asc',
per_page: 100,
} );
}, [] ) || null;
if ( pages ) {
return (
<PanelBody title={ labels.title }>
<SelectControl
label={ __( 'Link to', 'woocommerce' ) }
value={ pageId }
options={ [
{
label: labels.default,
value: 0,
},
...pages.map( ( page ) => {
return {
label: formatTitle( page, pages ),
value: parseInt( page.id, 10 ),
};
} ),
] }
onChange={ ( value ) => setPageId( parseInt( value, 10 ) ) }
/>
</PanelBody>
);
}
return null;
};
export default PageSelector;
product-attribute-term-control/index.tsx 0000644 00000013362 15155071622 0014530 0 ustar 00 /**
* External dependencies
*/
import classNames from 'classnames';
import { __, _n, sprintf } from '@wordpress/i18n';
import {
SearchListControl,
SearchListItem,
} from '@woocommerce/editor-components/search-list-control';
import { SelectControl } from '@wordpress/components';
import { withInstanceId } from '@wordpress/compose';
import useProductAttributes from '@woocommerce/base-context/hooks/use-product-attributes';
import ErrorMessage from '@woocommerce/editor-components/error-placeholder/error-message';
import ExpandableSearchListItem from '@woocommerce/editor-components/expandable-search-list-item/expandable-search-list-item';
import {
renderItemArgs,
SearchListControlProps,
SearchListItem as SearchListItemProps,
} from '@woocommerce/editor-components/search-list-control/types';
import { convertAttributeObjectToSearchItem } from '@woocommerce/utils';
/**
* Internal dependencies
*/
import './style.scss';
interface Props
extends Omit< SearchListControlProps, 'isSingle' | 'list' | 'selected' > {
instanceId?: string;
/**
* Callback to update the category operator. If not passed in, setting is not used.
*/
onOperatorChange?: () => void;
/**
* Setting for whether products should match all or any selected categories.
*/
operator: 'all' | 'any';
/**
* The list of currently selected attribute ids.
*/
selected: { id: number }[];
}
const ProductAttributeTermControl = ( {
onChange,
onOperatorChange,
instanceId,
isCompact = false,
messages = {},
operator = 'any',
selected,
type = 'text',
}: Props ) => {
const { errorLoadingAttributes, isLoadingAttributes, productsAttributes } =
useProductAttributes( true );
const renderItem = ( args: renderItemArgs ) => {
const { item, search, depth = 0 } = args;
const count = item.count || 0;
const classes = [
'woocommerce-product-attributes__item',
'woocommerce-search-list__item',
{
'is-searching': search.length > 0,
'is-skip-level': depth === 0 && item.parent !== 0,
},
];
if ( ! item.breadcrumbs.length ) {
return (
<ExpandableSearchListItem
{ ...args }
className={ classNames( classes ) }
item={ item }
isLoading={ isLoadingAttributes }
disabled={ item.count === 0 }
name={ `attributes-${ instanceId }` }
countLabel={ sprintf(
/* translators: %d is the count of terms. */
_n(
'%d term',
'%d terms',
count,
'woo-gutenberg-products-block'
),
count
) }
aria-label={ sprintf(
/* translators: %1$s is the item name, %2$d is the count of terms for the item. */
_n(
'%1$s, has %2$d term',
'%1$s, has %2$d terms',
count,
'woo-gutenberg-products-block'
),
item.name,
count
) }
/>
);
}
const itemName = `${ item.breadcrumbs[ 0 ] }: ${ item.name }`;
return (
<SearchListItem
{ ...args }
name={ `terms-${ instanceId }` }
className={ classNames( ...classes, 'has-count' ) }
countLabel={ sprintf(
/* translators: %d is the count of products. */
_n(
'%d product',
'%d products',
count,
'woo-gutenberg-products-block'
),
count
) }
aria-label={ sprintf(
/* translators: %1$s is the attribute name, %2$d is the count of products for that attribute. */
_n(
'%1$s, has %2$d product',
'%1$s, has %2$d products',
count,
'woo-gutenberg-products-block'
),
itemName,
count
) }
/>
);
};
const list = productsAttributes.reduce( ( acc, curr ) => {
const { terms, ...props } = curr;
return [
...acc,
convertAttributeObjectToSearchItem( props ),
...terms.map( convertAttributeObjectToSearchItem ),
];
}, [] as SearchListItemProps[] );
messages = {
clear: __(
'Clear all product attributes',
'woo-gutenberg-products-block'
),
noItems: __(
"Your store doesn't have any product attributes.",
'woo-gutenberg-products-block'
),
search: __(
'Search for product attributes',
'woo-gutenberg-products-block'
),
selected: ( n: number ) =>
sprintf(
/* translators: %d is the count of attributes selected. */
_n(
'%d attribute selected',
'%d attributes selected',
n,
'woo-gutenberg-products-block'
),
n
),
updated: __(
'Product attribute search results updated.',
'woo-gutenberg-products-block'
),
...messages,
};
if ( errorLoadingAttributes ) {
return <ErrorMessage error={ errorLoadingAttributes } />;
}
return (
<>
<SearchListControl
className="woocommerce-product-attributes"
isCompact={ isCompact }
isHierarchical
isLoading={ isLoadingAttributes }
isSingle={ false }
list={ list }
messages={ messages }
onChange={ onChange }
renderItem={ renderItem }
selected={
selected
.map( ( { id } ) =>
list.find( ( term ) => term.id === id )
)
.filter( Boolean ) as SearchListItemProps[]
}
type={ type }
/>
{ !! onOperatorChange && (
<div hidden={ selected.length < 2 }>
<SelectControl
className="woocommerce-product-attributes__operator"
label={ __(
'Display products matching',
'woo-gutenberg-products-block'
) }
help={ __(
'Pick at least two attributes to use this setting.',
'woo-gutenberg-products-block'
) }
value={ operator }
onChange={ onOperatorChange }
options={ [
{
label: __(
'Any selected attributes',
'woo-gutenberg-products-block'
),
value: 'any',
},
{
label: __(
'All selected attributes',
'woo-gutenberg-products-block'
),
value: 'all',
},
] }
/>
</div>
) }
</>
);
};
export default withInstanceId( ProductAttributeTermControl );
product-attribute-term-control/style.scss 0000644 00000001066 15155071622 0014714 0 ustar 00 .woocommerce-product-attributes__operator {
.components-base-control__help {
@include visually-hidden;
}
.components-base-control__label {
margin-bottom: 0;
margin-right: 0.5em;
}
}
.woocommerce-search-list__item.woocommerce-product-attributes__item {
&.is-searching,
&.is-skip-level {
.woocommerce-search-list__item-prefix::after {
content: ":";
}
}
&.is-not-active {
&:hover,
&:active,
&:focus {
background: $white;
}
}
&.is-loading {
justify-content: center;
.components-spinner {
margin-bottom: $gap-small;
}
}
}
product-category-control/index.js 0000644 00000012270 15155071622 0013170 0 ustar 00 /**
* External dependencies
*/
import { __, _n, sprintf } from '@wordpress/i18n';
import PropTypes from 'prop-types';
import {
SearchListControl,
SearchListItem,
} from '@woocommerce/editor-components/search-list-control';
import { SelectControl } from '@wordpress/components';
import { withCategories } from '@woocommerce/block-hocs';
import ErrorMessage from '@woocommerce/editor-components/error-placeholder/error-message';
import classNames from 'classnames';
/**
* Internal dependencies
*/
import './style.scss';
/**
* @param {Object} props
* @param {string=} props.categories
* @param {boolean=} props.isLoading
* @param {string=} props.error
* @param {Function} props.onChange
* @param {Function=} props.onOperatorChange
* @param {string=} props.operator
* @param {number[]} props.selected
* @param {boolean=} props.isCompact
* @param {boolean=} props.isSingle
* @param {boolean=} props.showReviewCount
*/
const ProductCategoryControl = ( {
categories,
error,
isLoading,
onChange,
onOperatorChange,
operator,
selected,
isCompact,
isSingle,
showReviewCount,
} ) => {
const renderItem = ( args ) => {
const { item, search, depth = 0 } = args;
const accessibleName = ! item.breadcrumbs.length
? item.name
: `${ item.breadcrumbs.join( ', ' ) }, ${ item.name }`;
const listItemAriaLabel = showReviewCount
? sprintf(
/* translators: %1$s is the item name, %2$d is the count of reviews for the item. */
_n(
'%1$s, has %2$d review',
'%1$s, has %2$d reviews',
item.review_count,
'woocommerce'
),
accessibleName,
item.review_count
)
: sprintf(
/* translators: %1$s is the item name, %2$d is the count of products for the item. */
_n(
'%1$s, has %2$d product',
'%1$s, has %2$d products',
item.count,
'woocommerce'
),
accessibleName,
item.count
);
const listItemCountLabel = showReviewCount
? sprintf(
/* translators: %d is the count of reviews. */
_n(
'%d review',
'%d reviews',
item.review_count,
'woocommerce'
),
item.review_count
)
: sprintf(
/* translators: %d is the count of products. */
_n(
'%d product',
'%d products',
item.count,
'woocommerce'
),
item.count
);
return (
<SearchListItem
className={ classNames(
'woocommerce-product-categories__item',
'has-count',
{
'is-searching': search.length > 0,
'is-skip-level': depth === 0 && item.parent !== 0,
}
) }
{ ...args }
countLabel={ listItemCountLabel }
aria-label={ listItemAriaLabel }
/>
);
};
const messages = {
clear: __(
'Clear all product categories',
'woocommerce'
),
list: __( 'Product Categories', 'woocommerce' ),
noItems: __(
"Your store doesn't have any product categories.",
'woocommerce'
),
search: __(
'Search for product categories',
'woocommerce'
),
selected: ( n ) =>
sprintf(
/* translators: %d is the count of selected categories. */
_n(
'%d category selected',
'%d categories selected',
n,
'woocommerce'
),
n
),
updated: __(
'Category search results updated.',
'woocommerce'
),
};
if ( error ) {
return <ErrorMessage error={ error } />;
}
return (
<>
<SearchListControl
className="woocommerce-product-categories"
list={ categories }
isLoading={ isLoading }
selected={ selected
.map( ( id ) =>
categories.find( ( category ) => category.id === id )
)
.filter( Boolean ) }
onChange={ onChange }
renderItem={ renderItem }
messages={ messages }
isCompact={ isCompact }
isHierarchical
isSingle={ isSingle }
/>
{ !! onOperatorChange && (
<div hidden={ selected.length < 2 }>
<SelectControl
className="woocommerce-product-categories__operator"
label={ __(
'Display products matching',
'woocommerce'
) }
help={ __(
'Pick at least two categories to use this setting.',
'woocommerce'
) }
value={ operator }
onChange={ onOperatorChange }
options={ [
{
label: __(
'Any selected categories',
'woocommerce'
),
value: 'any',
},
{
label: __(
'All selected categories',
'woocommerce'
),
value: 'all',
},
] }
/>
</div>
) }
</>
);
};
ProductCategoryControl.propTypes = {
/**
* Callback to update the selected product categories.
*/
onChange: PropTypes.func.isRequired,
/**
* Callback to update the category operator. If not passed in, setting is not used.
*/
onOperatorChange: PropTypes.func,
/**
* Setting for whether products should match all or any selected categories.
*/
operator: PropTypes.oneOf( [ 'all', 'any' ] ),
/**
* The list of currently selected category IDs.
*/
selected: PropTypes.array.isRequired,
isCompact: PropTypes.bool,
/**
* Allow only a single selection. Defaults to false.
*/
isSingle: PropTypes.bool,
};
ProductCategoryControl.defaultProps = {
operator: 'any',
isCompact: false,
isSingle: false,
};
export default withCategories( ProductCategoryControl );
product-category-control/style.scss 0000644 00000000301 15155071622 0013550 0 ustar 00 .woocommerce-product-categories__operator {
.components-base-control__help {
@include visually-hidden;
}
.components-base-control__label {
margin-bottom: 0;
margin-right: 0.5em;
}
}
product-control/index.tsx 0000644 00000013360 15155071622 0011560 0 ustar 00 /**
* External dependencies
*/
import { __, _n, sprintf } from '@wordpress/i18n';
import { isEmpty } from '@woocommerce/types';
import {
SearchListControl,
SearchListItem,
} from '@woocommerce/editor-components/search-list-control';
import type {
SearchListControlProps,
renderItemArgs,
} from '@woocommerce/editor-components/search-list-control/types';
import { withInstanceId } from '@wordpress/compose';
import {
withProductVariations,
withSearchedProducts,
withTransformSingleSelectToMultipleSelect,
} from '@woocommerce/block-hocs';
import type {
ProductResponseItem,
WithInjectedInstanceId,
WithInjectedProductVariations,
WithInjectedSearchedProducts,
} from '@woocommerce/types';
import { convertProductResponseItemToSearchItem } from '@woocommerce/utils';
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';
/**
* Internal dependencies
*/
import './style.scss';
interface ProductControlProps {
/**
* Callback to update the selected products.
*/
onChange: () => void;
isCompact?: boolean;
/**
* The ID of the currently expanded product.
*/
expandedProduct: number | null;
/**
* Callback to search products by their name.
*/
onSearch: () => void;
/**
* Callback to render each item in the selection list, allows any custom object-type rendering.
*/
renderItem: SearchListControlProps[ 'renderItem' ] | null;
/**
* The ID of the currently selected item (product or variation).
*/
selected: number[];
/**
* Whether to show variations in the list of items available.
*/
showVariations?: boolean;
}
const messages = {
list: __( 'Products', 'woo-gutenberg-products-block' ),
noItems: __(
"Your store doesn't have any products.",
'woo-gutenberg-products-block'
),
search: __(
'Search for a product to display',
'woo-gutenberg-products-block'
),
updated: __(
'Product search results updated.',
'woo-gutenberg-products-block'
),
};
const ProductControl = (
props: ProductControlProps &
WithInjectedSearchedProducts &
WithInjectedProductVariations &
WithInjectedInstanceId
) => {
const {
expandedProduct = null,
error,
instanceId,
isCompact = false,
isLoading,
onChange,
onSearch,
products,
renderItem,
selected = [],
showVariations = false,
variations,
variationsLoading,
} = props;
const renderItemWithVariations = (
args: renderItemArgs< ProductResponseItem >
) => {
const { item, search, depth = 0, isSelected, onSelect } = args;
const variationsCount =
item.details?.variations && Array.isArray( item.details.variations )
? item.details.variations.length
: 0;
const classes = classNames(
'woocommerce-search-product__item',
'woocommerce-search-list__item',
`depth-${ depth }`,
'has-count',
{
'is-searching': search.length > 0,
'is-skip-level': depth === 0 && item.parent !== 0,
'is-variable': variationsCount > 0,
}
);
// Top level items custom rendering based on SearchListItem.
if ( ! item.breadcrumbs.length ) {
const hasVariations =
item.details?.variations && item.details.variations.length > 0;
return (
<ExpandableSearchListItem
{ ...args }
className={ classNames( classes, {
'is-selected': isSelected,
} ) }
isSelected={ isSelected }
item={ item }
onSelect={ () => {
return () => {
onSelect( item )();
};
} }
isLoading={ isLoading || variationsLoading }
countLabel={
hasVariations
? sprintf(
/* translators: %1$d is the number of variations of a product product. */
__(
'%1$d variations',
'woo-gutenberg-products-block'
),
item.details?.variations.length
)
: null
}
name={ `products-${ instanceId }` }
aria-label={
hasVariations
? sprintf(
/* translators: %1$s is the product name, %2$d is the number of variations of that product. */
_n(
'%1$s, has %2$d variation',
'%1$s, has %2$d variations',
item.details?.variations
?.length as number,
'woo-gutenberg-products-block'
),
item.name,
item.details?.variations.length
)
: undefined
}
/>
);
}
const itemArgs = isEmpty( item.details?.variation )
? args
: {
...args,
item: {
...args.item,
name: item.details?.variation as string,
},
'aria-label': `${ item.breadcrumbs[ 0 ] }: ${ item.details?.variation }`,
};
return (
<SearchListItem
{ ...itemArgs }
className={ classes }
name={ `variations-${ instanceId }` }
/>
);
};
const getRenderItemFunc = () => {
if ( renderItem ) {
return renderItem;
} else if ( showVariations ) {
return renderItemWithVariations;
}
return () => null;
};
if ( error ) {
return <ErrorMessage error={ error } />;
}
const currentVariations =
variations && expandedProduct && variations[ expandedProduct ]
? variations[ expandedProduct ]
: [];
const currentList = [ ...products, ...currentVariations ].map(
convertProductResponseItemToSearchItem
);
return (
<SearchListControl
className="woocommerce-products"
list={ currentList }
isCompact={ isCompact }
isLoading={ isLoading }
isSingle
selected={ currentList.filter( ( { id } ) =>
selected.includes( Number( id ) )
) }
onChange={ onChange }
renderItem={ getRenderItemFunc() }
onSearch={ onSearch }
messages={ messages }
isHierarchical
/>
);
};
export default withTransformSingleSelectToMultipleSelect(
withSearchedProducts(
withProductVariations( withInstanceId( ProductControl ) )
)
);
product-control/style.scss 0000644 00000002220 15155071622 0011737 0 ustar 00 .woocommerce-search-product__item {
.woocommerce-search-list__item-name {
.description {
display: block;
}
}
&.is-searching,
&.is-skip-level {
.woocommerce-search-list__item-prefix::after {
content: ":";
}
}
&.is-not-active {
&:hover,
&:active,
&:focus {
background: $white;
}
}
&.is-loading {
justify-content: center;
.components-spinner {
margin-bottom: $gap-small;
}
}
&.depth-0.is-variable::after {
margin-left: $gap-smaller;
content: "";
height: $gap-large;
width: $gap-large;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z" fill="#{encode-color($gray-700)}" /></svg>');
background-repeat: no-repeat;
background-position: center right;
background-size: contain;
}
&.depth-0.is-variable.is-selected::after {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z" fill="#{encode-color($gray-700)}" /></svg>');
}
}
product-orderby-control/index.tsx 0000644 00000003260 15155071622 0013222 0 ustar 00 /**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { SelectControl } from '@wordpress/components';
/**
* Internal dependencies
*/
import type { ProductOrderbyControlProps } from './types';
/**
* A pre-configured SelectControl for product orderby settings.
*
* @param {Object} props Incoming props for the component.
* @param {string} props.value
* @param {function(any):any} props.setAttributes Setter for block attributes.
*/
const ProductOrderbyControl = ( {
value,
setAttributes,
}: ProductOrderbyControlProps ) => {
return (
<SelectControl
label={ __( 'Order products by', 'woo-gutenberg-products-block' ) }
value={ value }
options={ [
{
label: __(
'Newness - newest first',
'woo-gutenberg-products-block'
),
value: 'date',
},
{
label: __(
'Price - low to high',
'woo-gutenberg-products-block'
),
value: 'price_asc',
},
{
label: __(
'Price - high to low',
'woo-gutenberg-products-block'
),
value: 'price_desc',
},
{
label: __(
'Rating - highest first',
'woo-gutenberg-products-block'
),
value: 'rating',
},
{
label: __(
'Sales - most first',
'woo-gutenberg-products-block'
),
value: 'popularity',
},
{
label: __(
'Title - alphabetical',
'woo-gutenberg-products-block'
),
value: 'title',
},
{
label: __( 'Menu Order', 'woo-gutenberg-products-block' ),
value: 'menu_order',
},
] }
onChange={ ( orderby ) => setAttributes( { orderby } ) }
/>
);
};
export default ProductOrderbyControl;
product-orderby-control/types.ts 0000644 00000000203 15155071622 0013061 0 ustar 00 export interface ProductOrderbyControlProps {
value: string;
setAttributes: ( attributes: Record< string, unknown > ) => void;
}
product-stock-control/index.tsx 0000644 00000006101 15155071622 0012674 0 ustar 00 /**
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { getSetting } from '@woocommerce/settings';
import { useCallback, useState, useEffect } from '@wordpress/element';
import { ToggleControl } from '@wordpress/components';
export interface ProductStockControlProps {
value: Array< string >;
setAttributes: ( attributes: Record< string, unknown > ) => void;
}
// Look up whether or not out of stock items should be hidden globally.
const hideOutOfStockItems = getSetting( 'hideOutOfStockItems', false );
// Get the stock status options.
const allStockStatusOptions = getSetting( 'stockStatusOptions', {} );
/**
* A pre-configured SelectControl for product stock settings.
*/
const ProductStockControl = ( {
value,
setAttributes,
}: ProductStockControlProps ): JSX.Element => {
// Determine whether or not to use the out of stock status.
const { outofstock, ...otherStockStatusOptions } = allStockStatusOptions;
const stockStatusOptions = hideOutOfStockItems
? otherStockStatusOptions
: allStockStatusOptions;
/**
* Valid options must be in an array of [ 'value' : 'mystatus', 'label' : 'My label' ] format.
* stockStatusOptions are returned as [ 'mystatus' : 'My label' ].
* Formatting is corrected here.
*/
const displayOptions = Object.entries( stockStatusOptions )
.map( ( [ slug, name ] ) => ( { value: slug, label: name } ) )
.filter( ( status ) => !! status.label );
const defaultCheckedOptions = Object.keys( stockStatusOptions ).filter(
( key: string ) => !! key
);
// Set the initial state to the default or saved value.
const [ checkedOptions, setChecked ] = useState(
value || defaultCheckedOptions
);
/**
* Set attributes when checked items change.
* Note: The blank stock status prevents all results returning when all options are unchecked.
*/
useEffect( () => {
setAttributes( {
stockStatus: [ '', ...checkedOptions ],
} );
}, [ checkedOptions, setAttributes ] );
/**
* When a checkbox in the list changes, update state.
*/
const onChange = useCallback(
( checkedValue: string ) => {
const previouslyChecked = checkedOptions.includes( checkedValue );
const newChecked = checkedOptions.filter(
( filteredValue ) => filteredValue !== checkedValue
);
if ( ! previouslyChecked ) {
newChecked.push( checkedValue );
newChecked.sort();
}
setChecked( newChecked );
},
[ checkedOptions ]
);
return (
<>
{ displayOptions.map( ( option ) => {
const helpText = checkedOptions.includes( option.value )
? /* translators: %s stock status. */ __(
'Stock status "%s" visible.',
'woo-gutenberg-products-block'
)
: /* translators: %s stock status. */ __(
'Stock status "%s" hidden.',
'woo-gutenberg-products-block'
);
return (
<ToggleControl
label={ option.label }
key={ option.value }
help={ sprintf( helpText, option.label ) }
checked={ checkedOptions.includes( option.value ) }
onChange={ () => onChange( option.value ) }
/>
);
} ) }
</>
);
};
export default ProductStockControl;
product-tag-control/index.tsx 0000644 00000007114 15155071622 0012331 0 ustar 00 /**
* External dependencies
*/
import { __, _n, sprintf } from '@wordpress/i18n';
import { useState, useEffect, useCallback, useMemo } from '@wordpress/element';
import { SearchListControl } from '@woocommerce/editor-components/search-list-control';
import { SelectControl } from '@wordpress/components';
import { getSetting } from '@woocommerce/settings';
import { useDebouncedCallback } from 'use-debounce';
/**
* Internal dependencies
*/
import type { SearchListItem as SearchListItemProps } from '../search-list-control/types';
import ProductTagItem from './product-tag-item';
import type { ProductTagControlProps } from './types';
import { getProductTags } from '../utils';
import './style.scss';
/**
* Component to handle searching and selecting product tags.
*/
const ProductTagControl = ( {
isCompact = false,
onChange,
onOperatorChange,
operator = 'any',
selected,
}: ProductTagControlProps ): JSX.Element => {
const [ list, setList ] = useState< SearchListItemProps[] >( [] );
const [ loading, setLoading ] = useState( true );
const [ isMounted, setIsMounted ] = useState( false );
const limitTags = getSetting( 'limitTags', false );
const selectedTags = useMemo< SearchListItemProps[] >( () => {
return list.filter( ( item ) => selected.includes( item.id ) );
}, [ list, selected ] );
const onSearch = useCallback(
( search: string ) => {
setLoading( true );
getProductTags( { selected, search } )
.then( ( newList ) => {
setList( newList );
setLoading( false );
} )
.catch( () => {
setLoading( false );
} );
},
[ selected ]
);
// Load on mount.
useEffect( () => {
if ( isMounted ) {
return;
}
onSearch( '' );
setIsMounted( true );
}, [ onSearch, isMounted ] );
const debouncedOnSearch = useDebouncedCallback( onSearch, 400 );
const messages = {
clear: __( 'Clear all product tags', 'woo-gutenberg-products-block' ),
list: __( 'Product Tags', 'woo-gutenberg-products-block' ),
noItems: __(
'You have not set up any product tags on your store.',
'woo-gutenberg-products-block'
),
search: __( 'Search for product tags', 'woo-gutenberg-products-block' ),
selected: ( n: number ) =>
sprintf(
/* translators: %d is the count of selected tags. */
_n(
'%d tag selected',
'%d tags selected',
n,
'woo-gutenberg-products-block'
),
n
),
updated: __(
'Tag search results updated.',
'woo-gutenberg-products-block'
),
};
return (
<>
<SearchListControl
className="woocommerce-product-tags"
list={ list }
isLoading={ loading }
selected={ selectedTags }
onChange={ onChange }
onSearch={ limitTags ? debouncedOnSearch : undefined }
renderItem={ ProductTagItem }
messages={ messages }
isCompact={ isCompact }
isHierarchical
isSingle={ false }
/>
{ !! onOperatorChange && (
<div hidden={ selected.length < 2 }>
<SelectControl
className="woocommerce-product-tags__operator"
label={ __(
'Display products matching',
'woo-gutenberg-products-block'
) }
help={ __(
'Pick at least two tags to use this setting.',
'woo-gutenberg-products-block'
) }
value={ operator }
onChange={ onOperatorChange }
options={ [
{
label: __(
'Any selected tags',
'woo-gutenberg-products-block'
),
value: 'any',
},
{
label: __(
'All selected tags',
'woo-gutenberg-products-block'
),
value: 'all',
},
] }
/>
</div>
) }
</>
);
};
export default ProductTagControl;
product-tag-control/product-tag-item.tsx 0000644 00000002220 15155071622 0014400 0 ustar 00 /**
* External dependencies
*/
import { _n, sprintf } from '@wordpress/i18n';
import { SearchListItem } from '@woocommerce/editor-components/search-list-control';
import classNames from 'classnames';
/**
* Internal dependencies
*/
import type { renderItemArgs } from '../search-list-control/types';
export const ProductTagItem = ( {
item,
search,
depth = 0,
...rest
}: renderItemArgs ): JSX.Element => {
const accessibleName = ! item.breadcrumbs.length
? item.name
: `${ item.breadcrumbs.join( ', ' ) }, ${ item.name }`;
return (
<SearchListItem
className={ classNames(
'woocommerce-product-tags__item',
'has-count',
{
'is-searching': search.length > 0,
'is-skip-level': depth === 0 && item.parent !== 0,
}
) }
item={ item }
search={ search }
depth={ depth }
{ ...rest }
ariaLabel={ sprintf(
/* translators: %1$d is the count of products, %2$s is the name of the tag. */
_n(
'%1$d product tagged as %2$s',
'%1$d products tagged as %2$s',
item.count,
'woo-gutenberg-products-block'
),
item.count,
accessibleName
) }
/>
);
};
export default ProductTagItem;
product-tag-control/style.scss 0000644 00000000273 15155071622 0012516 0 ustar 00 .woocommerce-product-tags__operator {
.components-base-control__help {
@include visually-hidden;
}
.components-base-control__label {
margin-bottom: 0;
margin-right: 0.5em;
}
}
product-tag-control/types.ts 0000644 00000000563 15155071622 0012177 0 ustar 00 /**
* Internal dependencies
*/
import type { SearchListItem as SearchListItemProps } from '../search-list-control/types';
export type ProductTagControlProps = {
isCompact?: boolean;
onChange: ( selected: SearchListItemProps[] ) => void;
onOperatorChange?: ( operator: string ) => void;
operator?: string;
// Selected tag ids.
selected: ( number | string )[];
};
products-control/index.js 0000644 00000005326 15155071622 0011544 0 ustar 00 /**
* External dependencies
*/
import { __, _n, sprintf } from '@wordpress/i18n';
import { SearchListControl } from '@woocommerce/editor-components/search-list-control';
import PropTypes from 'prop-types';
import { withSearchedProducts } from '@woocommerce/block-hocs';
import ErrorMessage from '@woocommerce/editor-components/error-placeholder/error-message';
import { decodeEntities } from '@wordpress/html-entities';
/**
* The products control exposes a custom selector for searching and selecting
* products.
*
* @param {Object} props Component props.
* @param {string} props.error
* @param {Function} props.onChange Callback fired when the selected item changes
* @param {Function} props.onSearch Callback fired when a search is triggered
* @param {Array} props.selected An array of selected products.
* @param {Array} props.products An array of products to select from.
* @param {boolean} props.isLoading Whether or not the products are being loaded.
* @param {boolean} props.isCompact Whether or not the control should have compact styles.
*
* @return {Function} A functional component.
*/
const ProductsControl = ( {
error,
onChange,
onSearch,
selected,
products,
isLoading,
isCompact,
} ) => {
const messages = {
clear: __( 'Clear all products', 'woocommerce' ),
list: __( 'Products', 'woocommerce' ),
noItems: __(
"Your store doesn't have any products.",
'woocommerce'
),
search: __(
'Search for products to display',
'woocommerce'
),
selected: ( n ) =>
sprintf(
/* translators: %d is the number of selected products. */
_n(
'%d product selected',
'%d products selected',
n,
'woocommerce'
),
n
),
updated: __(
'Product search results updated.',
'woocommerce'
),
};
if ( error ) {
return <ErrorMessage error={ error } />;
}
return (
<SearchListControl
className="woocommerce-products"
list={ products.map( ( product ) => {
const formattedSku = product.sku
? ' (' + product.sku + ')'
: '';
return {
...product,
name: `${ decodeEntities(
product.name
) }${ formattedSku }`,
};
} ) }
isCompact={ isCompact }
isLoading={ isLoading }
selected={ products.filter( ( { id } ) =>
selected.includes( id )
) }
onSearch={ onSearch }
onChange={ onChange }
messages={ messages }
/>
);
};
ProductsControl.propTypes = {
onChange: PropTypes.func.isRequired,
onSearch: PropTypes.func,
selected: PropTypes.array,
products: PropTypes.array,
isCompact: PropTypes.bool,
isLoading: PropTypes.bool,
};
ProductsControl.defaultProps = {
selected: [],
products: [],
isCompact: false,
isLoading: true,
};
export default withSearchedProducts( ProductsControl );
search-list-control/index.ts 0000644 00000000077 15155071622 0012127 0 ustar 00 export * from './search-list-control';
export * from './item';
search-list-control/item.tsx 0000644 00000011677 15155071622 0012156 0 ustar 00 /**
* External dependencies
*/
import classNames from 'classnames';
import { CheckboxControl } from '@wordpress/components';
import { useCallback } from '@wordpress/element';
import { arrayDifferenceBy, arrayUnionBy } from '@woocommerce/utils';
import { decodeEntities } from '@wordpress/html-entities';
/**
* Internal dependencies
*/
import type {
renderItemArgs,
SearchListItem as SearchListItemProps,
} from './types';
import { getHighlightedName, getBreadcrumbsForDisplay } from './utils';
const Count = ( { label }: { label: string | React.ReactNode | number } ) => {
return (
<span className="woocommerce-search-list__item-count">{ label }</span>
);
};
const ItemLabel = ( props: { item: SearchListItemProps; search: string } ) => {
const { item, search } = props;
const hasBreadcrumbs = item.breadcrumbs && item.breadcrumbs.length;
return (
<span className="woocommerce-search-list__item-label">
{ hasBreadcrumbs ? (
<span className="woocommerce-search-list__item-prefix">
{ getBreadcrumbsForDisplay( item.breadcrumbs ) }
</span>
) : null }
<span className="woocommerce-search-list__item-name">
{ getHighlightedName( decodeEntities( item.name ), search ) }
</span>
</span>
);
};
export const SearchListItem = < T extends object = object >( {
countLabel,
className,
depth = 0,
controlId = '',
item,
isSelected,
isSingle,
onSelect,
search = '',
selected,
useExpandedPanelId,
...props
}: renderItemArgs< T > ): JSX.Element => {
const [ expandedPanelId, setExpandedPanelId ] = useExpandedPanelId;
const showCount =
countLabel !== undefined &&
countLabel !== null &&
item.count !== undefined &&
item.count !== null;
const hasBreadcrumbs = !! item.breadcrumbs?.length;
const hasChildren = !! item.children?.length;
const isExpanded = expandedPanelId === item.id;
const classes = classNames(
[ 'woocommerce-search-list__item', `depth-${ depth }`, className ],
{
'has-breadcrumbs': hasBreadcrumbs,
'has-children': hasChildren,
'has-count': showCount,
'is-expanded': isExpanded,
'is-radio-button': isSingle,
}
);
const name = props.name || `search-list-item-${ controlId }`;
const id = `${ name }-${ item.id }`;
const togglePanel = useCallback( () => {
setExpandedPanelId( isExpanded ? -1 : Number( item.id ) );
}, [ isExpanded, item.id, setExpandedPanelId ] );
return hasChildren ? (
<div
className={ classes }
onClick={ togglePanel }
onKeyDown={ ( e ) =>
e.key === 'Enter' || e.key === ' ' ? togglePanel() : null
}
role="treeitem"
tabIndex={ 0 }
>
{ isSingle ? (
<>
<input
type="radio"
id={ id }
name={ name }
value={ item.value }
onChange={ onSelect( item ) }
onClick={ ( e ) => e.stopPropagation() }
checked={ isSelected }
className="woocommerce-search-list__item-input"
{ ...props }
/>
<ItemLabel item={ item } search={ search } />
{ showCount ? (
<Count label={ countLabel || item.count } />
) : null }
</>
) : (
<>
<CheckboxControl
className="woocommerce-search-list__item-input"
checked={ isSelected }
{ ...( ! isSelected &&
// We know that `item.children` is not `undefined` because
// we are here only if `hasChildren` is `true`.
( item.children as SearchListItemProps[] ).some(
( child ) =>
selected.find(
( selectedItem ) =>
selectedItem.id === child.id
)
)
? { indeterminate: true }
: {} ) }
label={ getHighlightedName(
decodeEntities( item.name ),
search
) }
onChange={ () => {
if ( isSelected ) {
onSelect(
arrayDifferenceBy(
selected,
item.children as SearchListItemProps[],
'id'
)
)();
} else {
onSelect(
arrayUnionBy(
selected,
item.children as SearchListItemProps[],
'id'
)
)();
}
} }
onClick={ ( e ) => e.stopPropagation() }
/>
{ showCount ? (
<Count label={ countLabel || item.count } />
) : null }
</>
) }
</div>
) : (
<label htmlFor={ id } className={ classes }>
{ isSingle ? (
<>
<input
{ ...props }
type="radio"
id={ id }
name={ name }
value={ item.value }
onChange={ onSelect( item ) }
checked={ isSelected }
className="woocommerce-search-list__item-input"
></input>
<ItemLabel item={ item } search={ search } />
</>
) : (
<CheckboxControl
{ ...props }
id={ id }
name={ name }
className="woocommerce-search-list__item-input"
value={ decodeEntities( item.value ) }
label={ getHighlightedName(
decodeEntities( item.name ),
search
) }
onChange={ onSelect( item ) }
checked={ isSelected }
/>
) }
{ showCount ? <Count label={ countLabel || item.count } /> : null }
</label>
);
};
export default SearchListItem;
search-list-control/search-list-control.tsx 0000644 00000017723 15155071622 0015112 0 ustar 00 /**
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import {
Button,
FormTokenField,
Spinner,
TextControl,
withSpokenMessages,
} from '@wordpress/components';
import {
useState,
useMemo,
useEffect,
useCallback,
Fragment,
} from '@wordpress/element';
import { Icon, info } from '@wordpress/icons';
import classnames from 'classnames';
import { useInstanceId } from '@wordpress/compose';
/**
* Internal dependencies
*/
import { getFilteredList, defaultMessages } from './utils';
import SearchListItem from './item';
import Tag from '../tag';
import type {
SearchListItem as SearchListItemProps,
SearchListControlProps,
SearchListMessages,
renderItemArgs,
ListItemsProps,
SearchListItemsContainerProps,
} from './types';
import './style.scss';
const defaultRenderListItem = ( args: renderItemArgs ): JSX.Element => {
return <SearchListItem { ...args } />;
};
const ListItems = ( props: ListItemsProps ): JSX.Element | null => {
const {
list,
selected,
renderItem,
depth = 0,
onSelect,
instanceId,
isSingle,
search,
useExpandedPanelId,
} = props;
const [ expandedPanelId ] = useExpandedPanelId;
if ( ! list ) {
return null;
}
return (
<>
{ list.map( ( item ) => {
const isSelected =
item.children?.length && ! isSingle
? item.children.every( ( { id } ) =>
selected.find(
( selectedItem ) => selectedItem.id === id
)
)
: !! selected.find( ( { id } ) => id === item.id );
const isExpanded =
item.children?.length && expandedPanelId === item.id;
return (
<Fragment key={ item.id }>
<li>
{ renderItem( {
item,
isSelected,
onSelect,
isSingle,
selected,
search,
depth,
useExpandedPanelId,
controlId: instanceId,
} ) }
</li>
{ isExpanded ? (
<ListItems
{ ...props }
list={ item.children as SearchListItemProps[] }
depth={ depth + 1 }
/>
) : null }
</Fragment>
);
} ) }
</>
);
};
const SelectedListItems = < T extends object = object >( {
isLoading,
isSingle,
selected,
messages,
onChange,
onRemove,
}: SearchListControlProps< T > & {
messages: SearchListMessages;
onRemove: ( itemId: string | number ) => () => void;
} ) => {
if ( isLoading || isSingle || ! selected ) {
return null;
}
const selectedCount = selected.length;
return (
<div className="woocommerce-search-list__selected">
<div className="woocommerce-search-list__selected-header">
<strong>{ messages.selected( selectedCount ) }</strong>
{ selectedCount > 0 ? (
<Button
isLink
isDestructive
onClick={ () => onChange( [] ) }
aria-label={ messages.clear }
>
{ __( 'Clear all', 'woo-gutenberg-products-block' ) }
</Button>
) : null }
</div>
{ selectedCount > 0 ? (
<ul>
{ selected.map( ( item, i ) => (
<li key={ i }>
<Tag
label={ item.name }
id={ item.id }
remove={ onRemove }
/>
</li>
) ) }
</ul>
) : null }
</div>
);
};
const ListItemsContainer = < T extends object = object >( {
filteredList,
search,
onSelect,
instanceId,
useExpandedPanelId,
...props
}: SearchListItemsContainerProps< T > ) => {
const { messages, renderItem, selected, isSingle } = props;
const renderItemCallback = renderItem || defaultRenderListItem;
if ( filteredList.length === 0 ) {
return (
<div className="woocommerce-search-list__list is-not-found">
<span className="woocommerce-search-list__not-found-icon">
<Icon icon={ info } />
</span>
<span className="woocommerce-search-list__not-found-text">
{ search
? // eslint-disable-next-line @wordpress/valid-sprintf
sprintf( messages.noResults, search )
: messages.noItems }
</span>
</div>
);
}
return (
<ul className="woocommerce-search-list__list">
<ListItems
useExpandedPanelId={ useExpandedPanelId }
list={ filteredList }
selected={ selected }
renderItem={ renderItemCallback }
onSelect={ onSelect }
instanceId={ instanceId }
isSingle={ isSingle }
search={ search }
/>
</ul>
);
};
/**
* Component to display a searchable, selectable list of items.
*/
export const SearchListControl = < T extends object = object >(
props: SearchListControlProps< T >
) => {
const {
className = '',
isCompact,
isHierarchical,
isLoading,
isSingle,
list,
messages: customMessages = defaultMessages,
onChange,
onSearch,
selected,
type = 'text',
debouncedSpeak,
} = props;
const [ search, setSearch ] = useState( '' );
const useExpandedPanelId = useState< number >( -1 );
const instanceId = useInstanceId( SearchListControl );
const messages = useMemo(
() => ( { ...defaultMessages, ...customMessages } ),
[ customMessages ]
);
const filteredList = useMemo( () => {
return getFilteredList( list, search, isHierarchical );
}, [ list, search, isHierarchical ] );
useEffect( () => {
if ( debouncedSpeak ) {
debouncedSpeak( messages.updated );
}
}, [ debouncedSpeak, messages ] );
useEffect( () => {
if ( typeof onSearch === 'function' ) {
onSearch( search );
}
}, [ search, onSearch ] );
const onRemove = useCallback(
( itemId: string | number ) => () => {
if ( isSingle ) {
onChange( [] );
}
const i = selected.findIndex(
( { id: selectedId } ) => selectedId === itemId
);
onChange( [
...selected.slice( 0, i ),
...selected.slice( i + 1 ),
] );
},
[ isSingle, selected, onChange ]
);
const onSelect = useCallback(
( item: SearchListItemProps< T > | SearchListItemProps< T >[] ) =>
() => {
if ( Array.isArray( item ) ) {
onChange( item );
return;
}
if (
selected.findIndex( ( { id } ) => id === item.id ) !== -1
) {
onRemove( item.id )();
return;
}
if ( isSingle ) {
onChange( [ item ] );
} else {
onChange( [ ...selected, item ] );
}
},
[ isSingle, onRemove, onChange, selected ]
);
const onRemoveToken = useCallback(
( tokens: Array< SearchListItemProps & { value: string } > ) => {
const [ removedItem ] = selected.filter(
( item ) => ! tokens.find( ( token ) => item.id === token.id )
);
onRemove( removedItem.id )();
},
[ onRemove, selected ]
);
return (
<div
className={ classnames( 'woocommerce-search-list', className, {
'is-compact': isCompact,
'is-loading': isLoading,
'is-token': type === 'token',
} ) }
>
{ type === 'text' && (
<SelectedListItems
{ ...props }
onRemove={ onRemove }
messages={ messages }
/>
) }
<div className="woocommerce-search-list__search">
{ type === 'text' ? (
<TextControl
label={ messages.search }
type="search"
value={ search }
onChange={ ( value ) => setSearch( value ) }
/>
) : (
<FormTokenField
disabled={ isLoading }
label={ messages.search }
onChange={ onRemoveToken }
onInputChange={ ( value ) => setSearch( value ) }
suggestions={ [] }
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - Ignoring because `__experimentalValidateInput` is not yet in the type definitions.
__experimentalValidateInput={ () => false }
value={
isLoading
? [
__(
'Loading…',
'woo-gutenberg-products-block'
),
]
: selected.map( ( token ) => ( {
...token,
value: token.name,
} ) )
}
__experimentalShowHowTo={ false }
/>
) }
</div>
{ isLoading ? (
<div className="woocommerce-search-list__list">
<Spinner />
</div>
) : (
<ListItemsContainer
{ ...props }
search={ search }
filteredList={ filteredList }
messages={ messages }
onSelect={ onSelect }
instanceId={ instanceId }
useExpandedPanelId={ useExpandedPanelId }
/>
) }
</div>
);
};
export default withSpokenMessages( SearchListControl );
search-list-control/style.scss 0000644 00000013062 15155071622 0012503 0 ustar 00 .woocommerce-search-list {
width: 100%;
padding: 0 0 $gap;
text-align: left;
&.is-compact {
.woocommerce-search-list__selected {
margin: 0 0 $gap;
padding: 0;
border-top: none;
// 54px is the height of 1 row of tags in the sidebar.
min-height: 54px;
}
.woocommerce-search-list__search {
margin: 0 0 $gap;
padding: 0;
border-top: none;
&.is-token {
margin-bottom: 0;
}
}
}
&.is-loading {
.woocommerce-search-list__list {
padding: $gap-small 0;
text-align: center;
border: none;
}
.components-form-token-field__remove-token {
// We use a placeholder “Loading…” text when loading passed
// as a value to the `FormTokenField`, so we hide the “X”.
display: none;
}
}
&.is-token {
.woocommerce-search-list__list {
border-top: 0;
}
.woocommerce-search-list__search {
margin-bottom: 0;
.components-form-token-field__input-container {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
margin-bottom: 0;
}
}
}
}
.woocommerce-search-list__selected {
margin: $gap 0;
padding: $gap 0 0;
// 76px is the height of 1 row of tags.
min-height: 76px;
border-top: 1px solid $gray-100;
.woocommerce-search-list__selected-header {
margin-bottom: $gap-smaller;
button {
margin-left: $gap-small;
}
}
.woocommerce-tag__text {
max-width: 13em;
}
ul {
list-style: none;
margin: 0;
padding: 0;
li {
float: left;
}
}
}
.woocommerce-search-list__search {
margin: $gap 0;
padding: $gap 0 0;
border-top: 1px solid $gray-100;
.components-base-control__field {
margin-bottom: $gap;
}
}
.woocommerce-search-list__list {
border: 1px solid $gray-200;
margin: 0;
padding: 0;
max-height: 17em;
overflow-x: hidden;
overflow-y: auto;
li {
margin-bottom: 0;
}
&.is-not-found {
padding: $gap-small 0;
text-align: center;
border: none;
.woocommerce-search-list__not-found-icon,
.woocommerce-search-list__not-found-text {
display: inline-block;
}
.woocommerce-search-list__not-found-icon {
margin-right: $gap;
.gridicon {
vertical-align: top;
margin-top: -1px;
}
}
}
.components-spinner {
float: none;
margin: 0 auto;
}
.components-menu-group__label {
@include visually-hidden;
}
> [role="menu"] {
border: 1px solid $gray-100;
border-bottom: none;
}
.woocommerce-search-list__item {
display: flex;
align-items: center;
margin-bottom: 0;
padding: $gap-small $gap;
background: $studio-white;
// !important to keep the border around on hover
border-bottom: 1px solid $gray-100;
color: $gray-700;
&:hover,
&:active,
&:focus {
background: $gray-100;
}
&:active,
&:focus {
box-shadow: none;
}
&.has-children {
cursor: pointer;
&::after {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z" fill="#{encode-color($gray-700)}" /></svg>');
background-position: center right;
background-repeat: no-repeat;
background-size: contain;
content: "";
height: $gap-large;
margin-left: $gap-smaller;
width: $gap-large;
}
&[disabled]::after {
background: none;
margin-left: 0;
width: auto;
}
&.is-expanded::after {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z" fill="#{encode-color($gray-700)}" /></svg>');
}
}
.woocommerce-search-list__item-input {
margin: 0;
}
.woocommerce-search-list__item-input[type="radio"] {
margin-right: $gap-smaller;
}
.components-base-control__field {
margin: 0;
}
.woocommerce-search-list__item-label {
display: flex;
flex: 1;
}
&.depth-0 + .depth-1 {
// Hide the border on the preceding list item
margin-top: -1px;
}
&:not(.depth-0) {
border-bottom: 0 !important;
}
&:not(.depth-0) + .depth-0 {
border-top: 1px solid $gray-100;
}
// Anything deeper than 5 levels will use this fallback depth
&[class*="depth-"] .woocommerce-search-list__item-label::before {
margin-right: $gap-smallest;
content: str-repeat("— ", 5);
}
&.depth-0 .woocommerce-search-list__item-label::before {
margin-right: 0;
content: "";
}
@for $i from 1 to 5 {
&.depth-#{$i} {
padding-left: $gap * ( $i + 1 );
}
&.depth-#{$i} .woocommerce-search-list__item-label::before {
content: str-repeat("— ", $i);
}
}
.woocommerce-search-list__item-name {
display: inline-block;
}
.woocommerce-search-list__item-prefix {
display: none;
color: $gray-700;
}
&.is-searching,
&.is-skip-level {
.woocommerce-search-list__item-label {
// Un-flex the label, so the prefix (breadcrumbs) and name are aligned.
display: inline-block;
}
.woocommerce-search-list__item-prefix {
display: inline;
&::after {
margin-right: $gap-smallest;
content: " ›";
}
}
}
&.is-searching {
.woocommerce-search-list__item-name {
color: $gray-900;
}
}
&.has-count {
> .components-menu-item__item {
width: 100%;
}
}
.woocommerce-search-list__item-count {
flex: 0 1 auto;
padding: math.div($gap-smallest, 2) $gap-smaller;
border: 1px solid $gray-100;
border-radius: 12px;
font-size: 0.8em;
line-height: 1.4;
margin-left: auto;
color: $gray-700;
background: $studio-white;
white-space: nowrap;
}
}
li:last-child .woocommerce-search-list__item {
border-bottom: none;
}
}
search-list-control/test/__snapshots__/index.js.snap 0000644 00000551305 15155071622 0016657 0 ustar 00 // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SearchListControl should render a search box and list of hierarchical options 1`] = `
Object {
"asFragment": [Function],
"baseElement": <body>
<p
class="a11y-speak-intro-text"
hidden="hidden"
id="a11y-speak-intro-text"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
>
Notifications
</p>
<div
aria-atomic="true"
aria-live="assertive"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-assertive"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div
aria-atomic="true"
aria-live="polite"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-polite"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div>
<div
class="woocommerce-search-list is-compact"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-11"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-11"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<div
class="woocommerce-search-list__item depth-0 has-children"
role="treeitem"
tabindex="0"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="inspector-checkbox-control-54"
type="checkbox"
value="1"
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-54"
>
Apricots
</label>
</div>
</div>
</div>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-11-5"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-11-5"
name="search-list-item-11"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-55"
>
Lychee
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-11-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-11-6"
name="search-list-item-11"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-56"
>
Mulberry
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>
</body>,
"container": <div>
<div
class="woocommerce-search-list is-compact"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-11"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-11"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<div
class="woocommerce-search-list__item depth-0 has-children"
role="treeitem"
tabindex="0"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="inspector-checkbox-control-54"
type="checkbox"
value="1"
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-54"
>
Apricots
</label>
</div>
</div>
</div>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-11-5"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-11-5"
name="search-list-item-11"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-55"
>
Lychee
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-11-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-11-6"
name="search-list-item-11"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-56"
>
Mulberry
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;
exports[`SearchListControl should render a search box and list of options 1`] = `
Object {
"asFragment": [Function],
"baseElement": <body>
<p
class="a11y-speak-intro-text"
hidden="hidden"
id="a11y-speak-intro-text"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
>
Notifications
</p>
<div
aria-atomic="true"
aria-live="assertive"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-assertive"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div
aria-atomic="true"
aria-live="polite"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-polite"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-0"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-0"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-0-1"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-0-1"
name="search-list-item-0"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-0"
>
Apricots
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-0-2"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-0-2"
name="search-list-item-0"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-1"
>
Clementine
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-0-3"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-0-3"
name="search-list-item-0"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-2"
>
Elderberry
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-0-4"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-0-4"
name="search-list-item-0"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-3"
>
Guava
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-0-5"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-0-5"
name="search-list-item-0"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-4"
>
Lychee
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-0-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-0-6"
name="search-list-item-0"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-5"
>
Mulberry
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>
</body>,
"container": <div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-0"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-0"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-0-1"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-0-1"
name="search-list-item-0"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-0"
>
Apricots
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-0-2"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-0-2"
name="search-list-item-0"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-1"
>
Clementine
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-0-3"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-0-3"
name="search-list-item-0"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-2"
>
Elderberry
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-0-4"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-0-4"
name="search-list-item-0"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-3"
>
Guava
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-0-5"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-0-5"
name="search-list-item-0"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-4"
>
Lychee
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-0-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-0-6"
name="search-list-item-0"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-5"
>
Mulberry
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;
exports[`SearchListControl should render a search box and list of options with a custom className 1`] = `
Object {
"asFragment": [Function],
"baseElement": <body>
<p
class="a11y-speak-intro-text"
hidden="hidden"
id="a11y-speak-intro-text"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
>
Notifications
</p>
<div
aria-atomic="true"
aria-live="assertive"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-assertive"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div
aria-atomic="true"
aria-live="polite"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-polite"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div>
<div
class="woocommerce-search-list test-search"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-1"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-1"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-1-1"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-1-1"
name="search-list-item-1"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-6"
>
Apricots
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-1-2"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-1-2"
name="search-list-item-1"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-7"
>
Clementine
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-1-3"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-1-3"
name="search-list-item-1"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-8"
>
Elderberry
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-1-4"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-1-4"
name="search-list-item-1"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-9"
>
Guava
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-1-5"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-1-5"
name="search-list-item-1"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-10"
>
Lychee
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-1-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-1-6"
name="search-list-item-1"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-11"
>
Mulberry
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>
</body>,
"container": <div>
<div
class="woocommerce-search-list test-search"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-1"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-1"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-1-1"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-1-1"
name="search-list-item-1"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-6"
>
Apricots
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-1-2"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-1-2"
name="search-list-item-1"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-7"
>
Clementine
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-1-3"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-1-3"
name="search-list-item-1"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-8"
>
Elderberry
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-1-4"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-1-4"
name="search-list-item-1"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-9"
>
Guava
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-1-5"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-1-5"
name="search-list-item-1"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-10"
>
Lychee
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-1-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-1-6"
name="search-list-item-1"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-11"
>
Mulberry
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;
exports[`SearchListControl should render a search box and list of options, with a custom render callback for each item 1`] = `
Object {
"asFragment": [Function],
"baseElement": <body>
<p
class="a11y-speak-intro-text"
hidden="hidden"
id="a11y-speak-intro-text"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
>
Notifications
</p>
<div
aria-atomic="true"
aria-live="assertive"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-assertive"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div
aria-atomic="true"
aria-live="polite"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-polite"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-10"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-10"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<div>
Apricots
!
</div>
</li>
<li>
<div>
Clementine
!
</div>
</li>
<li>
<div>
Elderberry
!
</div>
</li>
<li>
<div>
Guava
!
</div>
</li>
<li>
<div>
Lychee
!
</div>
</li>
<li>
<div>
Mulberry
!
</div>
</li>
</ul>
</div>
</div>
</body>,
"container": <div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-10"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-10"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<div>
Apricots
!
</div>
</li>
<li>
<div>
Clementine
!
</div>
</li>
<li>
<div>
Elderberry
!
</div>
</li>
<li>
<div>
Guava
!
</div>
</li>
<li>
<div>
Lychee
!
</div>
</li>
<li>
<div>
Mulberry
!
</div>
</li>
</ul>
</div>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;
exports[`SearchListControl should render a search box and list of options, with a custom search input message 1`] = `
Object {
"asFragment": [Function],
"baseElement": <body>
<p
class="a11y-speak-intro-text"
hidden="hidden"
id="a11y-speak-intro-text"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
>
Notifications
</p>
<div
aria-atomic="true"
aria-live="assertive"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-assertive"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div
aria-atomic="true"
aria-live="polite"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-polite"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-9"
>
Testing search label
</label>
<input
class="components-text-control__input"
id="inspector-text-control-9"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-9-1"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-9-1"
name="search-list-item-9"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-48"
>
Apricots
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-9-2"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-9-2"
name="search-list-item-9"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-49"
>
Clementine
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-9-3"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-9-3"
name="search-list-item-9"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-50"
>
Elderberry
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-9-4"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-9-4"
name="search-list-item-9"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-51"
>
Guava
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-9-5"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-9-5"
name="search-list-item-9"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-52"
>
Lychee
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-9-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-9-6"
name="search-list-item-9"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-53"
>
Mulberry
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>
</body>,
"container": <div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-9"
>
Testing search label
</label>
<input
class="components-text-control__input"
id="inspector-text-control-9"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-9-1"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-9-1"
name="search-list-item-9"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-48"
>
Apricots
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-9-2"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-9-2"
name="search-list-item-9"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-49"
>
Clementine
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-9-3"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-9-3"
name="search-list-item-9"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-50"
>
Elderberry
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-9-4"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-9-4"
name="search-list-item-9"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-51"
>
Guava
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-9-5"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-9-5"
name="search-list-item-9"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-52"
>
Lychee
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-9-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-9-6"
name="search-list-item-9"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-53"
>
Mulberry
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;
exports[`SearchListControl should render a search box and no options 1`] = `
Object {
"asFragment": [Function],
"baseElement": <body>
<p
class="a11y-speak-intro-text"
hidden="hidden"
id="a11y-speak-intro-text"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
>
Notifications
</p>
<div
aria-atomic="true"
aria-live="assertive"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-assertive"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div
aria-atomic="true"
aria-live="polite"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-polite"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-4"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-4"
type="search"
value=""
/>
</div>
</div>
</div>
<div
class="woocommerce-search-list__list is-not-found"
>
<span
class="woocommerce-search-list__not-found-icon"
>
<svg
aria-hidden="true"
focusable="false"
height="24"
role="img"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 3.2c-4.8 0-8.8 3.9-8.8 8.8 0 4.8 3.9 8.8 8.8 8.8 4.8 0 8.8-3.9 8.8-8.8 0-4.8-4-8.8-8.8-8.8zm0 16c-4 0-7.2-3.3-7.2-7.2C4.8 8 8 4.8 12 4.8s7.2 3.3 7.2 7.2c0 4-3.2 7.2-7.2 7.2zM11 17h2v-6h-2v6zm0-8h2V7h-2v2z"
/>
</svg>
</span>
<span
class="woocommerce-search-list__not-found-text"
>
No items found.
</span>
</div>
</div>
</div>
</body>,
"container": <div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-4"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-4"
type="search"
value=""
/>
</div>
</div>
</div>
<div
class="woocommerce-search-list__list is-not-found"
>
<span
class="woocommerce-search-list__not-found-icon"
>
<svg
aria-hidden="true"
focusable="false"
height="24"
role="img"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 3.2c-4.8 0-8.8 3.9-8.8 8.8 0 4.8 3.9 8.8 8.8 8.8 4.8 0 8.8-3.9 8.8-8.8 0-4.8-4-8.8-8.8-8.8zm0 16c-4 0-7.2-3.3-7.2-7.2C4.8 8 8 4.8 12 4.8s7.2 3.3 7.2 7.2c0 4-3.2 7.2-7.2 7.2zM11 17h2v-6h-2v6zm0-8h2V7h-2v2z"
/>
</svg>
</span>
<span
class="woocommerce-search-list__not-found-text"
>
No items found.
</span>
</div>
</div>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;
exports[`SearchListControl should render a search box with a search term, and no matching options 1`] = `
Object {
"asFragment": [Function],
"baseElement": <body>
<p
class="a11y-speak-intro-text"
hidden="hidden"
id="a11y-speak-intro-text"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
>
Notifications
</p>
<div
aria-atomic="true"
aria-live="assertive"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-assertive"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div
aria-atomic="true"
aria-live="polite"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-polite"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-8"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-8"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-8-1"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-8-1"
name="search-list-item-8"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-42"
>
Apricots
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-8-2"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-8-2"
name="search-list-item-8"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-43"
>
Clementine
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-8-3"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-8-3"
name="search-list-item-8"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-44"
>
Elderberry
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-8-4"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-8-4"
name="search-list-item-8"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-45"
>
Guava
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-8-5"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-8-5"
name="search-list-item-8"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-46"
>
Lychee
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-8-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-8-6"
name="search-list-item-8"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-47"
>
Mulberry
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>
</body>,
"container": <div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-8"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-8"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-8-1"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-8-1"
name="search-list-item-8"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-42"
>
Apricots
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-8-2"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-8-2"
name="search-list-item-8"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-43"
>
Clementine
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-8-3"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-8-3"
name="search-list-item-8"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-44"
>
Elderberry
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-8-4"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-8-4"
name="search-list-item-8"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-45"
>
Guava
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-8-5"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-8-5"
name="search-list-item-8"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-46"
>
Lychee
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-8-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-8-6"
name="search-list-item-8"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-47"
>
Mulberry
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;
exports[`SearchListControl should render a search box with a search term, and only matching options 1`] = `
Object {
"asFragment": [Function],
"baseElement": <body>
<p
class="a11y-speak-intro-text"
hidden="hidden"
id="a11y-speak-intro-text"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
>
Notifications
</p>
<div
aria-atomic="true"
aria-live="assertive"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-assertive"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div
aria-atomic="true"
aria-live="polite"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-polite"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-5"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-5"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-5-1"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-5-1"
name="search-list-item-5"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-24"
>
Apricots
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-5-2"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-5-2"
name="search-list-item-5"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-25"
>
Clementine
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-5-3"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-5-3"
name="search-list-item-5"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-26"
>
Elderberry
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-5-4"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-5-4"
name="search-list-item-5"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-27"
>
Guava
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-5-5"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-5-5"
name="search-list-item-5"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-28"
>
Lychee
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-5-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-5-6"
name="search-list-item-5"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-29"
>
Mulberry
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>
</body>,
"container": <div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-5"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-5"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-5-1"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-5-1"
name="search-list-item-5"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-24"
>
Apricots
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-5-2"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-5-2"
name="search-list-item-5"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-25"
>
Clementine
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-5-3"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-5-3"
name="search-list-item-5"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-26"
>
Elderberry
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-5-4"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-5-4"
name="search-list-item-5"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-27"
>
Guava
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-5-5"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-5-5"
name="search-list-item-5"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-28"
>
Lychee
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-5-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-5-6"
name="search-list-item-5"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-29"
>
Mulberry
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;
exports[`SearchListControl should render a search box with a search term, and only matching options, regardless of case sensitivity 1`] = `
Object {
"asFragment": [Function],
"baseElement": <body>
<p
class="a11y-speak-intro-text"
hidden="hidden"
id="a11y-speak-intro-text"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
>
Notifications
</p>
<div
aria-atomic="true"
aria-live="assertive"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-assertive"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div
aria-atomic="true"
aria-live="polite"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-polite"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-6"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-6"
type="search"
value="BeRrY"
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-6-3"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-6-3"
name="search-list-item-6"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-32"
>
Elder
<strong>
berry
</strong>
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-6-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-6-6"
name="search-list-item-6"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-35"
>
Mul
<strong>
berry
</strong>
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>
</body>,
"container": <div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
0 items selected
</strong>
</div>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-6"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-6"
type="search"
value="BeRrY"
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-6-3"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-6-3"
name="search-list-item-6"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-32"
>
Elder
<strong>
berry
</strong>
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-6-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-6-6"
name="search-list-item-6"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-35"
>
Mul
<strong>
berry
</strong>
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;
exports[`SearchListControl should render a search box, a list of options, and 1 selected item 1`] = `
Object {
"asFragment": [Function],
"baseElement": <body>
<p
class="a11y-speak-intro-text"
hidden="hidden"
id="a11y-speak-intro-text"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
>
Notifications
</p>
<div
aria-atomic="true"
aria-live="assertive"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-assertive"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div
aria-atomic="true"
aria-live="polite"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-polite"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
1 item selected
</strong>
<button
aria-label="Clear all selected items"
class="components-button is-link is-destructive"
type="button"
>
Clear all
</button>
</div>
<ul>
<li>
<span
class="woocommerce-tag has-remove"
>
<span
class="woocommerce-tag__text"
id="woocommerce-tag__label-0"
>
<span
class="screen-reader-text"
>
Clementine
</span>
<span
aria-hidden="true"
>
Clementine
</span>
</span>
<button
aria-describedby="woocommerce-tag__label-0"
aria-label="Remove Clementine"
class="components-button woocommerce-tag__remove"
type="button"
>
<svg
aria-hidden="true"
class="clear-icon"
focusable="false"
height="20"
role="img"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21ZM15.5303 8.46967C15.8232 8.76256 15.8232 9.23744 15.5303 9.53033L13.0607 12L15.5303 14.4697C15.8232 14.7626 15.8232 15.2374 15.5303 15.5303C15.2374 15.8232 14.7626 15.8232 14.4697 15.5303L12 13.0607L9.53033 15.5303C9.23744 15.8232 8.76256 15.8232 8.46967 15.5303C8.17678 15.2374 8.17678 14.7626 8.46967 14.4697L10.9393 12L8.46967 9.53033C8.17678 9.23744 8.17678 8.76256 8.46967 8.46967C8.76256 8.17678 9.23744 8.17678 9.53033 8.46967L12 10.9393L14.4697 8.46967C14.7626 8.17678 15.2374 8.17678 15.5303 8.46967Z"
/>
</svg>
</button>
</span>
</li>
</ul>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-2"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-2"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-2-1"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-2-1"
name="search-list-item-2"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-12"
>
Apricots
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-2-2"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
checked=""
class="components-checkbox-control__input"
id="search-list-item-2-2"
name="search-list-item-2"
type="checkbox"
value=""
/>
<svg
aria-hidden="true"
class="components-checkbox-control__checked"
focusable="false"
height="24"
role="img"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.7 7.1l-6.3 8.5-3.3-2.5-.9 1.2 4.5 3.4L17.9 8z"
/>
</svg>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-13"
>
Clementine
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-2-3"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-2-3"
name="search-list-item-2"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-14"
>
Elderberry
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-2-4"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-2-4"
name="search-list-item-2"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-15"
>
Guava
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-2-5"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-2-5"
name="search-list-item-2"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-16"
>
Lychee
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-2-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-2-6"
name="search-list-item-2"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-17"
>
Mulberry
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>
</body>,
"container": <div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
1 item selected
</strong>
<button
aria-label="Clear all selected items"
class="components-button is-link is-destructive"
type="button"
>
Clear all
</button>
</div>
<ul>
<li>
<span
class="woocommerce-tag has-remove"
>
<span
class="woocommerce-tag__text"
id="woocommerce-tag__label-0"
>
<span
class="screen-reader-text"
>
Clementine
</span>
<span
aria-hidden="true"
>
Clementine
</span>
</span>
<button
aria-describedby="woocommerce-tag__label-0"
aria-label="Remove Clementine"
class="components-button woocommerce-tag__remove"
type="button"
>
<svg
aria-hidden="true"
class="clear-icon"
focusable="false"
height="20"
role="img"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21ZM15.5303 8.46967C15.8232 8.76256 15.8232 9.23744 15.5303 9.53033L13.0607 12L15.5303 14.4697C15.8232 14.7626 15.8232 15.2374 15.5303 15.5303C15.2374 15.8232 14.7626 15.8232 14.4697 15.5303L12 13.0607L9.53033 15.5303C9.23744 15.8232 8.76256 15.8232 8.46967 15.5303C8.17678 15.2374 8.17678 14.7626 8.46967 14.4697L10.9393 12L8.46967 9.53033C8.17678 9.23744 8.17678 8.76256 8.46967 8.46967C8.76256 8.17678 9.23744 8.17678 9.53033 8.46967L12 10.9393L14.4697 8.46967C14.7626 8.17678 15.2374 8.17678 15.5303 8.46967Z"
/>
</svg>
</button>
</span>
</li>
</ul>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-2"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-2"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-2-1"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-2-1"
name="search-list-item-2"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-12"
>
Apricots
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-2-2"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
checked=""
class="components-checkbox-control__input"
id="search-list-item-2-2"
name="search-list-item-2"
type="checkbox"
value=""
/>
<svg
aria-hidden="true"
class="components-checkbox-control__checked"
focusable="false"
height="24"
role="img"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.7 7.1l-6.3 8.5-3.3-2.5-.9 1.2 4.5 3.4L17.9 8z"
/>
</svg>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-13"
>
Clementine
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-2-3"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-2-3"
name="search-list-item-2"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-14"
>
Elderberry
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-2-4"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-2-4"
name="search-list-item-2"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-15"
>
Guava
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-2-5"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-2-5"
name="search-list-item-2"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-16"
>
Lychee
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-2-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-2-6"
name="search-list-item-2"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-17"
>
Mulberry
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;
exports[`SearchListControl should render a search box, a list of options, and 2 selected item 1`] = `
Object {
"asFragment": [Function],
"baseElement": <body>
<p
class="a11y-speak-intro-text"
hidden="hidden"
id="a11y-speak-intro-text"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
>
Notifications
</p>
<div
aria-atomic="true"
aria-live="assertive"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-assertive"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div
aria-atomic="true"
aria-live="polite"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-polite"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
2 items selected
</strong>
<button
aria-label="Clear all selected items"
class="components-button is-link is-destructive"
type="button"
>
Clear all
</button>
</div>
<ul>
<li>
<span
class="woocommerce-tag has-remove"
>
<span
class="woocommerce-tag__text"
id="woocommerce-tag__label-1"
>
<span
class="screen-reader-text"
>
Clementine
</span>
<span
aria-hidden="true"
>
Clementine
</span>
</span>
<button
aria-describedby="woocommerce-tag__label-1"
aria-label="Remove Clementine"
class="components-button woocommerce-tag__remove"
type="button"
>
<svg
aria-hidden="true"
class="clear-icon"
focusable="false"
height="20"
role="img"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21ZM15.5303 8.46967C15.8232 8.76256 15.8232 9.23744 15.5303 9.53033L13.0607 12L15.5303 14.4697C15.8232 14.7626 15.8232 15.2374 15.5303 15.5303C15.2374 15.8232 14.7626 15.8232 14.4697 15.5303L12 13.0607L9.53033 15.5303C9.23744 15.8232 8.76256 15.8232 8.46967 15.5303C8.17678 15.2374 8.17678 14.7626 8.46967 14.4697L10.9393 12L8.46967 9.53033C8.17678 9.23744 8.17678 8.76256 8.46967 8.46967C8.76256 8.17678 9.23744 8.17678 9.53033 8.46967L12 10.9393L14.4697 8.46967C14.7626 8.17678 15.2374 8.17678 15.5303 8.46967Z"
/>
</svg>
</button>
</span>
</li>
<li>
<span
class="woocommerce-tag has-remove"
>
<span
class="woocommerce-tag__text"
id="woocommerce-tag__label-2"
>
<span
class="screen-reader-text"
>
Guava
</span>
<span
aria-hidden="true"
>
Guava
</span>
</span>
<button
aria-describedby="woocommerce-tag__label-2"
aria-label="Remove Guava"
class="components-button woocommerce-tag__remove"
type="button"
>
<svg
aria-hidden="true"
class="clear-icon"
focusable="false"
height="20"
role="img"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21ZM15.5303 8.46967C15.8232 8.76256 15.8232 9.23744 15.5303 9.53033L13.0607 12L15.5303 14.4697C15.8232 14.7626 15.8232 15.2374 15.5303 15.5303C15.2374 15.8232 14.7626 15.8232 14.4697 15.5303L12 13.0607L9.53033 15.5303C9.23744 15.8232 8.76256 15.8232 8.46967 15.5303C8.17678 15.2374 8.17678 14.7626 8.46967 14.4697L10.9393 12L8.46967 9.53033C8.17678 9.23744 8.17678 8.76256 8.46967 8.46967C8.76256 8.17678 9.23744 8.17678 9.53033 8.46967L12 10.9393L14.4697 8.46967C14.7626 8.17678 15.2374 8.17678 15.5303 8.46967Z"
/>
</svg>
</button>
</span>
</li>
</ul>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-3"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-3"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-3-1"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-3-1"
name="search-list-item-3"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-18"
>
Apricots
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-3-2"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
checked=""
class="components-checkbox-control__input"
id="search-list-item-3-2"
name="search-list-item-3"
type="checkbox"
value=""
/>
<svg
aria-hidden="true"
class="components-checkbox-control__checked"
focusable="false"
height="24"
role="img"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.7 7.1l-6.3 8.5-3.3-2.5-.9 1.2 4.5 3.4L17.9 8z"
/>
</svg>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-19"
>
Clementine
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-3-3"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-3-3"
name="search-list-item-3"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-20"
>
Elderberry
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-3-4"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
checked=""
class="components-checkbox-control__input"
id="search-list-item-3-4"
name="search-list-item-3"
type="checkbox"
value=""
/>
<svg
aria-hidden="true"
class="components-checkbox-control__checked"
focusable="false"
height="24"
role="img"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.7 7.1l-6.3 8.5-3.3-2.5-.9 1.2 4.5 3.4L17.9 8z"
/>
</svg>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-21"
>
Guava
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-3-5"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-3-5"
name="search-list-item-3"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-22"
>
Lychee
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-3-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-3-6"
name="search-list-item-3"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-23"
>
Mulberry
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>
</body>,
"container": <div>
<div
class="woocommerce-search-list"
>
<div
class="woocommerce-search-list__selected"
>
<div
class="woocommerce-search-list__selected-header"
>
<strong>
2 items selected
</strong>
<button
aria-label="Clear all selected items"
class="components-button is-link is-destructive"
type="button"
>
Clear all
</button>
</div>
<ul>
<li>
<span
class="woocommerce-tag has-remove"
>
<span
class="woocommerce-tag__text"
id="woocommerce-tag__label-1"
>
<span
class="screen-reader-text"
>
Clementine
</span>
<span
aria-hidden="true"
>
Clementine
</span>
</span>
<button
aria-describedby="woocommerce-tag__label-1"
aria-label="Remove Clementine"
class="components-button woocommerce-tag__remove"
type="button"
>
<svg
aria-hidden="true"
class="clear-icon"
focusable="false"
height="20"
role="img"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21ZM15.5303 8.46967C15.8232 8.76256 15.8232 9.23744 15.5303 9.53033L13.0607 12L15.5303 14.4697C15.8232 14.7626 15.8232 15.2374 15.5303 15.5303C15.2374 15.8232 14.7626 15.8232 14.4697 15.5303L12 13.0607L9.53033 15.5303C9.23744 15.8232 8.76256 15.8232 8.46967 15.5303C8.17678 15.2374 8.17678 14.7626 8.46967 14.4697L10.9393 12L8.46967 9.53033C8.17678 9.23744 8.17678 8.76256 8.46967 8.46967C8.76256 8.17678 9.23744 8.17678 9.53033 8.46967L12 10.9393L14.4697 8.46967C14.7626 8.17678 15.2374 8.17678 15.5303 8.46967Z"
/>
</svg>
</button>
</span>
</li>
<li>
<span
class="woocommerce-tag has-remove"
>
<span
class="woocommerce-tag__text"
id="woocommerce-tag__label-2"
>
<span
class="screen-reader-text"
>
Guava
</span>
<span
aria-hidden="true"
>
Guava
</span>
</span>
<button
aria-describedby="woocommerce-tag__label-2"
aria-label="Remove Guava"
class="components-button woocommerce-tag__remove"
type="button"
>
<svg
aria-hidden="true"
class="clear-icon"
focusable="false"
height="20"
role="img"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21ZM15.5303 8.46967C15.8232 8.76256 15.8232 9.23744 15.5303 9.53033L13.0607 12L15.5303 14.4697C15.8232 14.7626 15.8232 15.2374 15.5303 15.5303C15.2374 15.8232 14.7626 15.8232 14.4697 15.5303L12 13.0607L9.53033 15.5303C9.23744 15.8232 8.76256 15.8232 8.46967 15.5303C8.17678 15.2374 8.17678 14.7626 8.46967 14.4697L10.9393 12L8.46967 9.53033C8.17678 9.23744 8.17678 8.76256 8.46967 8.46967C8.76256 8.17678 9.23744 8.17678 9.53033 8.46967L12 10.9393L14.4697 8.46967C14.7626 8.17678 15.2374 8.17678 15.5303 8.46967Z"
/>
</svg>
</button>
</span>
</li>
</ul>
</div>
<div
class="woocommerce-search-list__search"
>
<div
class="components-base-control css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<label
class="components-base-control__label css-13ck15n-StyledLabel e1puf3u1"
for="inspector-text-control-3"
>
Search for items
</label>
<input
class="components-text-control__input"
id="inspector-text-control-3"
type="search"
value=""
/>
</div>
</div>
</div>
<ul
class="woocommerce-search-list__list"
>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-3-1"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-3-1"
name="search-list-item-3"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-18"
>
Apricots
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-3-2"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
checked=""
class="components-checkbox-control__input"
id="search-list-item-3-2"
name="search-list-item-3"
type="checkbox"
value=""
/>
<svg
aria-hidden="true"
class="components-checkbox-control__checked"
focusable="false"
height="24"
role="img"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.7 7.1l-6.3 8.5-3.3-2.5-.9 1.2 4.5 3.4L17.9 8z"
/>
</svg>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-19"
>
Clementine
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-3-3"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-3-3"
name="search-list-item-3"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-20"
>
Elderberry
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-3-4"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
checked=""
class="components-checkbox-control__input"
id="search-list-item-3-4"
name="search-list-item-3"
type="checkbox"
value=""
/>
<svg
aria-hidden="true"
class="components-checkbox-control__checked"
focusable="false"
height="24"
role="img"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.7 7.1l-6.3 8.5-3.3-2.5-.9 1.2 4.5 3.4L17.9 8z"
/>
</svg>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-21"
>
Guava
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-3-5"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-3-5"
name="search-list-item-3"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-22"
>
Lychee
</label>
</div>
</div>
</label>
</li>
<li>
<label
class="woocommerce-search-list__item depth-0"
for="search-list-item-3-6"
>
<div
class="components-base-control components-checkbox-control woocommerce-search-list__item-input css-wdf2ti-Wrapper e1puf3u3"
>
<div
class="components-base-control__field css-igk9ll-StyledField e1puf3u2"
>
<span
class="components-checkbox-control__input-container"
>
<input
class="components-checkbox-control__input"
id="search-list-item-3-6"
name="search-list-item-3"
type="checkbox"
value=""
/>
</span>
<label
class="components-checkbox-control__label"
for="inspector-checkbox-control-23"
>
Mulberry
</label>
</div>
</div>
</label>
</li>
</ul>
</div>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;
search-list-control/test/hierarchy.js 0000644 00000007757 15155071622 0013757 0 ustar 00 /**
* Internal dependencies
*/
import { buildTermsTree } from '../utils';
const list = [
{ id: 1, name: 'Apricots', parent: 0 },
{ id: 2, name: 'Clementine', parent: 0 },
{ id: 3, name: 'Elderberry', parent: 2 },
{ id: 4, name: 'Guava', parent: 2 },
{ id: 5, name: 'Lychee', parent: 3 },
{ id: 6, name: 'Mulberry', parent: 0 },
{ id: 7, name: 'Tamarind', parent: 5 },
];
describe( 'buildTermsTree', () => {
test( 'should return an empty array on empty input', () => {
const tree = buildTermsTree( [] );
expect( tree ).toEqual( [] );
} );
test( 'should return a flat array when there are no parent relationships', () => {
const tree = buildTermsTree( [
{ id: 1, name: 'Apricots', parent: 0 },
{ id: 2, name: 'Clementine', parent: 0 },
] );
expect( tree ).toEqual( [
{
id: 1,
name: 'Apricots',
parent: 0,
breadcrumbs: [],
children: [],
},
{
id: 2,
name: 'Clementine',
parent: 0,
breadcrumbs: [],
children: [],
},
] );
} );
test( 'should return a tree of items', () => {
const tree = buildTermsTree( list );
expect( tree ).toEqual( [
{
id: 1,
name: 'Apricots',
parent: 0,
breadcrumbs: [],
children: [],
},
{
id: 2,
name: 'Clementine',
parent: 0,
breadcrumbs: [],
children: [
{
id: 3,
name: 'Elderberry',
parent: 2,
breadcrumbs: [ 'Clementine' ],
children: [
{
id: 5,
name: 'Lychee',
parent: 3,
breadcrumbs: [ 'Clementine', 'Elderberry' ],
children: [
{
id: 7,
name: 'Tamarind',
parent: 5,
breadcrumbs: [
'Clementine',
'Elderberry',
'Lychee',
],
children: [],
},
],
},
],
},
{
id: 4,
name: 'Guava',
parent: 2,
breadcrumbs: [ 'Clementine' ],
children: [],
},
],
},
{
id: 6,
name: 'Mulberry',
parent: 0,
breadcrumbs: [],
children: [],
},
] );
} );
test( 'should return a tree of items, with orphan categories appended to the end', () => {
const filteredList = [
{ id: 1, name: 'Apricots', parent: 0 },
{ id: 2, name: 'Clementine', parent: 0 },
{ id: 4, name: 'Guava', parent: 2 },
{ id: 5, name: 'Lychee', parent: 3 },
{ id: 6, name: 'Mulberry', parent: 0 },
];
const tree = buildTermsTree( filteredList, list );
expect( tree ).toEqual( [
{
id: 1,
name: 'Apricots',
parent: 0,
breadcrumbs: [],
children: [],
},
{
id: 2,
name: 'Clementine',
parent: 0,
breadcrumbs: [],
children: [
{
id: 4,
name: 'Guava',
parent: 2,
breadcrumbs: [ 'Clementine' ],
children: [],
},
],
},
{
id: 6,
name: 'Mulberry',
parent: 0,
breadcrumbs: [],
children: [],
},
{
id: 5,
name: 'Lychee',
parent: 3,
breadcrumbs: [ 'Clementine', 'Elderberry' ],
children: [],
},
] );
} );
test( 'should return a tree of items, with orphan categories appended to the end, with children of thier own', () => {
const filteredList = [
{ id: 1, name: 'Apricots', parent: 0 },
{ id: 3, name: 'Elderberry', parent: 2 },
{ id: 4, name: 'Guava', parent: 2 },
{ id: 5, name: 'Lychee', parent: 3 },
{ id: 6, name: 'Mulberry', parent: 0 },
];
const tree = buildTermsTree( filteredList, list );
expect( tree ).toEqual( [
{
id: 1,
name: 'Apricots',
parent: 0,
breadcrumbs: [],
children: [],
},
{
id: 6,
name: 'Mulberry',
parent: 0,
breadcrumbs: [],
children: [],
},
{
id: 3,
name: 'Elderberry',
parent: 2,
breadcrumbs: [ 'Clementine' ],
children: [
{
id: 5,
name: 'Lychee',
parent: 3,
breadcrumbs: [ 'Clementine', 'Elderberry' ],
children: [],
},
],
},
{
id: 4,
name: 'Guava',
parent: 2,
breadcrumbs: [ 'Clementine' ],
children: [],
},
] );
} );
} );
search-list-control/test/index.js 0000644 00000012423 15155071622 0013072 0 ustar 00 /**
* External dependencies
*/
import { fireEvent, render } from '@testing-library/react';
/**
* Internal dependencies
*/
import { SearchListControl } from '../';
const noop = () => {};
const SELECTORS = {
listItems: '.woocommerce-search-list__list > li',
searchInput: '.components-text-control__input[type="search"]',
};
const list = [
{ id: 1, name: 'Apricots' },
{ id: 2, name: 'Clementine' },
{ id: 3, name: 'Elderberry' },
{ id: 4, name: 'Guava' },
{ id: 5, name: 'Lychee' },
{ id: 6, name: 'Mulberry' },
];
const hierarchicalList = [
{ id: 1, name: 'Apricots', parent: 0 },
{ id: 2, name: 'Clementine', parent: 1 },
{ id: 3, name: 'Elderberry', parent: 1 },
{ id: 4, name: 'Guava', parent: 3 },
{ id: 5, name: 'Lychee', parent: 0 },
{ id: 6, name: 'Mulberry', parent: 0 },
];
describe( 'SearchListControl', () => {
test( 'should render a search box and list of options', () => {
const component = render(
<SearchListControl
instanceId={ 1 }
list={ list }
selected={ [] }
onChange={ noop }
/>
);
expect( component ).toMatchSnapshot();
} );
test( 'should render a search box and list of options with a custom className', () => {
const component = render(
<SearchListControl
instanceId={ 1 }
className="test-search"
list={ list }
selected={ [] }
onChange={ noop }
/>
);
expect( component ).toMatchSnapshot();
} );
test( 'should render a search box, a list of options, and 1 selected item', () => {
const component = render(
<SearchListControl
instanceId={ 1 }
list={ list }
selected={ [ list[ 1 ] ] }
onChange={ noop }
/>
);
expect( component ).toMatchSnapshot();
} );
test( 'should render a search box, a list of options, and 2 selected item', () => {
const component = render(
<SearchListControl
instanceId={ 1 }
list={ list }
selected={ [ list[ 1 ], list[ 3 ] ] }
onChange={ noop }
/>
);
expect( component ).toMatchSnapshot();
} );
test( 'should render a search box and no options', () => {
const component = render(
<SearchListControl
instanceId={ 1 }
list={ [] }
selected={ [] }
onChange={ noop }
/>
);
expect( component ).toMatchSnapshot();
} );
test( 'should render a search box with a search term, and only matching options', () => {
const component = render(
<SearchListControl
instanceId={ 1 }
list={ list }
search="berry"
selected={ [] }
onChange={ noop }
debouncedSpeak={ noop }
/>
);
expect( component ).toMatchSnapshot();
} );
test( 'should render a search box with a search term, and only matching options, regardless of case sensitivity', () => {
const component = render(
<SearchListControl
instanceId={ 1 }
list={ list }
selected={ [] }
onChange={ noop }
debouncedSpeak={ noop }
/>
);
fireEvent.change(
component.container.querySelector( SELECTORS.searchInput ),
{ target: { value: 'BeRrY' } }
);
expect( component ).toMatchSnapshot();
const $listItems = component.container.querySelectorAll(
SELECTORS.listItems
);
expect( $listItems ).toHaveLength( 2 );
} );
// @see https://github.com/woocommerce/woocommerce-blocks/issues/6524
test( "should render search results in their original case regardless of user's input case", () => {
const EXPECTED = [ 'Elderberry', 'Mulberry' ];
const component = render(
<SearchListControl
instanceId={ 1 }
list={ list }
selected={ [] }
onChange={ noop }
debouncedSpeak={ noop }
/>
);
fireEvent.change(
component.container.querySelector( SELECTORS.searchInput ),
{ target: { value: 'BeRrY' } }
);
const listItems = Array.from(
component.container.querySelectorAll( SELECTORS.listItems )
).map( ( $el ) => $el.textContent );
expect( listItems ).toEqual( expect.arrayContaining( EXPECTED ) );
} );
test( 'should render a search box with a search term, and no matching options', () => {
const component = render(
<SearchListControl
instanceId={ 1 }
list={ list }
search="no matches"
selected={ [] }
onChange={ noop }
debouncedSpeak={ noop }
/>
);
expect( component ).toMatchSnapshot();
} );
test( 'should render a search box and list of options, with a custom search input message', () => {
const messages = { search: 'Testing search label' };
const component = render(
<SearchListControl
instanceId={ 1 }
list={ list }
selected={ [] }
onChange={ noop }
messages={ messages }
/>
);
expect( component ).toMatchSnapshot();
} );
test( 'should render a search box and list of options, with a custom render callback for each item', () => {
const renderItem = ( { item } ) => (
<div key={ item.id }>{ item.name }!</div>
); // eslint-disable-line
const component = render(
<SearchListControl
instanceId={ 1 }
list={ list }
selected={ [] }
onChange={ noop }
renderItem={ renderItem }
/>
);
expect( component ).toMatchSnapshot();
} );
test( 'should render a search box and list of hierarchical options', () => {
const component = render(
<SearchListControl
isCompact
isHierarchical
instanceId={ 1 }
isSingle={ false }
list={ hierarchicalList }
onChange={ noop }
selected={ [] }
type={ 'text' }
/>
);
expect( component ).toMatchSnapshot();
} );
} );
search-list-control/types.ts 0000644 00000011743 15155071622 0012166 0 ustar 00 /**
* External dependencies
*/
import type { InputHTMLAttributes, ReactNode } from 'react';
import { Require } from '@woocommerce/types';
interface ItemProps< T extends object = object > {
// Depth, non-zero if the list is hierarchical.
depth?: number;
// Callback for selecting the item.
onSelect: (
item: SearchListItem< T > | SearchListItem< T >[]
) => () => void;
// Search string, used to highlight the substring in the item name.
search: string;
useExpandedPanelId: [
number,
React.Dispatch< React.SetStateAction< number > >
];
}
interface SearchListProps< T extends object = object > {
//Restrict selections to one item.
isSingle: boolean;
// A complete list of item objects, each with id, name properties. This is displayed as a clickable/keyboard-able list, and possibly filtered by the search term (searches name).
list: SearchListItem< T >[];
// Callback to render each item in the selection list, allows any custom object-type rendering.
renderItem?: ( args: renderItemArgs< T > ) => JSX.Element;
// The list of currently selected items.
selected: SearchListItem< T >[];
}
export interface ListItemsProps
extends Require< SearchListProps, 'renderItem' >,
ItemProps {
instanceId: string | number;
}
export type SearchListItem< T extends object = object > = {
breadcrumbs: string[];
children?: SearchListItem< T >[];
count?: number;
details?: T;
id: string | number;
name: string;
parent: number;
value: string;
};
export interface SearchListItemsContainerProps< T extends object = object >
extends SearchListControlProps,
ItemProps {
instanceId: string | number;
filteredList: SearchListItem< T >[];
messages: SearchListMessages;
}
export interface SearchListMessages {
// A more detailed label for the "Clear all" button, read to screen reader users.
clear: string;
// Message to display when the list is empty (implies nothing loaded from the server or parent component).
noItems: string;
// Message to display when no matching results are found. %s is the search term.
noResults: string;
// Label for the search input
search: string;
// Label for the selected items. This is actually a function, so that we can pass through the count of currently selected items.
selected: ( n: number ) => string;
// Label indicating that search results have changed, read to screen reader users.
updated: string;
}
export interface renderItemArgs< T extends object = object >
extends ItemProps,
Partial<
Omit<
InputHTMLAttributes< HTMLInputElement >,
'onChange' | 'onSelect'
>
> {
// Additional CSS classes.
className?: string;
// Unique id of the parent control.
controlId: string | number;
// Label to display in the count bubble. Takes preference over `item.count`.
countLabel?: ReactNode;
// Whether the item is disabled.
disabled?: boolean;
// Current item to display.
item: SearchListItem< T >;
// Whether this item is selected.
isSelected: boolean;
// Whether this should only display a single item (controls radio vs checkbox icon).
isSingle: boolean;
// The list of currently selected items.
selected: SearchListItem< T >[];
/**
* Name of the inputs. Used to group input controls together. See:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-name
* If not provided, a default name will be generated using the controlId.
*/
name?: string;
// Aria label for the input. If not provided, a default label will be generated using the item name.
ariaLabel?: string;
}
export interface SearchListControlProps< T extends object = object > {
// Additional CSS classes.
className?: string;
// Whether it should be displayed in a compact way, so it occupies less space.
isCompact: boolean;
// Whether the list of items is hierarchical or not. If true, each list item is expected to have a parent property.
isHierarchical?: boolean;
// Whether the list of items is still loading.
isLoading?: boolean;
//Restrict selections to one item.
isSingle: boolean;
// A complete list of item objects, each with id, name properties. This is displayed as a clickable/keyboard-able list, and possibly filtered by the search term (searches name).
list: SearchListItem< T >[];
// Messages displayed or read to the user. Configure these to reflect your object type.
messages?: Partial< SearchListMessages >;
// Callback fired when selected items change, whether added, cleared, or removed. Passed an array of item objects (as passed in via props.list).
onChange: ( search: SearchListItem< T >[] ) => void;
// Callback fired when the search field is used.
onSearch?: ( ( search: string ) => void ) | undefined;
// Callback to render each item in the selection list, allows any custom object-type rendering.
renderItem?:
| ( ( args: renderItemArgs< T > ) => JSX.Element | null )
| undefined;
// The list of currently selected items.
selected: SearchListItem< T >[];
// Whether to show a text field or a token field as search
// Defaults to `'text'`
type?: 'text' | 'token';
// from withSpokenMessages
debouncedSpeak?: ( message: string ) => void;
}
search-list-control/utils.tsx 0000644 00000007657 15155071622 0012363 0 ustar 00 /**
* External dependencies
*/
import { Fragment } from '@wordpress/element';
import { __, _n, sprintf } from '@wordpress/i18n';
import { keyBy } from '@woocommerce/base-utils';
/**
* Internal dependencies
*/
import type { SearchListItem } from './types';
export const defaultMessages = {
clear: __( 'Clear all selected items', 'woo-gutenberg-products-block' ),
noItems: __( 'No items found.', 'woo-gutenberg-products-block' ),
/* Translators: %s search term */
noResults: __( 'No results for %s', 'woo-gutenberg-products-block' ),
search: __( 'Search for items', 'woo-gutenberg-products-block' ),
selected: ( n: number ): string =>
sprintf(
/* translators: Number of items selected from list. */
_n(
'%d item selected',
'%d items selected',
n,
'woo-gutenberg-products-block'
),
n
),
updated: __( 'Search results updated.', 'woo-gutenberg-products-block' ),
};
/**
* Returns terms in a tree form.
*
* @param {Array} filteredList Array of terms, possibly a subset of all terms, in flat format.
* @param {Array} list Array of the full list of terms, defaults to the filteredList.
*
* @return {Array} Array of terms in tree format.
*/
export const buildTermsTree = (
filteredList: SearchListItem[],
list = filteredList
): SearchListItem[] | [] => {
const termsByParent = filteredList.reduce( ( acc, currentValue ) => {
const key = currentValue.parent || 0;
if ( ! acc[ key ] ) {
acc[ key ] = [];
}
acc[ key ].push( currentValue );
return acc;
}, {} as Record< string, SearchListItem[] > );
const listById = keyBy( list, 'id' );
const builtParents = [ '0' ];
const getParentsName = ( term = {} as SearchListItem ): string[] => {
if ( ! term.parent ) {
return term.name ? [ term.name ] : [];
}
const parentName = getParentsName( listById[ term.parent ] );
return [ ...parentName, term.name ];
};
const fillWithChildren = ( terms: SearchListItem[] ): SearchListItem[] => {
return terms.map( ( term ) => {
const children = termsByParent[ term.id ];
builtParents.push( '' + term.id );
return {
...term,
breadcrumbs: getParentsName( listById[ term.parent ] ),
children:
children && children.length
? fillWithChildren( children )
: [],
};
} );
};
const tree = fillWithChildren( termsByParent[ '0' ] || [] );
// Handle remaining items in termsByParent that have not been built (orphaned).
Object.entries( termsByParent ).forEach( ( [ termId, terms ] ) => {
if ( ! builtParents.includes( termId ) ) {
tree.push( ...fillWithChildren( terms || [] ) );
}
} );
return tree;
};
export const getFilteredList = (
list: SearchListItem[],
search: string,
isHierarchical?: boolean | undefined
) => {
if ( ! search ) {
return isHierarchical ? buildTermsTree( list ) : list;
}
const re = new RegExp(
search.replace( /[-\/\\^$*+?.()|[\]{}]/g, '\\$&' ),
'i'
);
const filteredList = list
.map( ( item ) => ( re.test( item.name ) ? item : false ) )
.filter( Boolean ) as SearchListItem[];
return isHierarchical ? buildTermsTree( filteredList, list ) : filteredList;
};
export const getHighlightedName = (
name: string,
search: string
): ( JSX.Element | string )[] | string => {
if ( ! search ) {
return name;
}
const re = new RegExp(
// Escaping.
`(${ search.replace( /[-\/\\^$*+?.()|[\]{}]/g, '\\$&' ) })`,
'ig'
);
const nameParts = name.split( re );
return nameParts.map( ( part, i ) => {
return re.test( part ) ? (
<strong key={ i }>{ part }</strong>
) : (
<Fragment key={ i }>{ part }</Fragment>
);
} );
};
export const getBreadcrumbsForDisplay = ( breadcrumbs: string[] ): string => {
if ( breadcrumbs.length === 1 ) {
return breadcrumbs.slice( 0, 1 ).toString();
}
if ( breadcrumbs.length === 2 ) {
return (
breadcrumbs.slice( 0, 1 ).toString() +
' › ' +
breadcrumbs.slice( -1 ).toString()
);
}
return (
breadcrumbs.slice( 0, 1 ).toString() +
' … ' +
breadcrumbs.slice( -1 ).toString()
);
};
sidebar-compatibility-notice/editor.scss 0000644 00000000421 15155071622 0014467 0 ustar 00 .wc-blocks-sidebar-compatibility-notice.is-dismissible {
margin: 0;
padding-right: 16px;
.components-notice__dismiss {
min-width: 24px;
}
.components-notice__content {
margin: 4px 0;
}
svg {
width: 16px;
height: 16px;
}
&.is-hidden {
display: none;
}
}
sidebar-compatibility-notice/index.tsx 0000644 00000002430 15155071622 0014155 0 ustar 00 /**
* External dependencies
*/
import { Notice, ExternalLink } from '@wordpress/components';
import { createInterpolateElement } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import classnames from 'classnames';
/**
* Internal dependencies
*/
import './editor.scss';
import { useCompatibilityNotice } from './use-compatibility-notice';
export const CartCheckoutSidebarCompatibilityNotice = ( {
block,
}: {
block: 'cart' | 'checkout';
} ) => {
const [ isVisible, dismissNotice ] = useCompatibilityNotice( block );
const noticeText = createInterpolateElement(
__(
'The Cart & Checkout Blocks are built to optimize for faster checkout. To make sure this feature is right for your store, <a>review the list of compatible extensions</a>.',
'woo-gutenberg-products-block'
),
{
a: (
// Suppress the warning as this <a> will be interpolated into the string with content.
// eslint-disable-next-line jsx-a11y/anchor-has-content
<ExternalLink href="https://woocommerce.com/document/cart-checkout-blocks-support-status/#section-3" />
),
}
);
return (
<Notice
onRemove={ dismissNotice }
className={ classnames( [
'wc-blocks-sidebar-compatibility-notice',
{ 'is-hidden': ! isVisible },
] ) }
>
{ noticeText }
</Notice>
);
};
sidebar-compatibility-notice/use-compatibility-notice.ts 0000644 00000002006 15155071622 0017577 0 ustar 00 /**
* External dependencies
*/
import { useEffect, useState } from '@wordpress/element';
import { useLocalStorageState } from '@woocommerce/base-hooks';
const initialDismissedNotices: string[] = [];
export const useCompatibilityNotice = (
blockName: string
): [ boolean, () => void ] => {
const [ dismissedNotices, setDismissedNotices ] = useLocalStorageState(
`wc-blocks_dismissed_sidebar_compatibility_notices`,
initialDismissedNotices
);
const [ isVisible, setIsVisible ] = useState( false );
const isDismissed = dismissedNotices.includes( blockName );
const dismissNotice = () => {
const dismissedNoticesSet = new Set( dismissedNotices );
dismissedNoticesSet.add( blockName );
setDismissedNotices( [ ...dismissedNoticesSet ] );
};
// This ensures the modal is not loaded on first render. This is required so
// Gutenberg doesn't steal the focus from the Guide and focuses the block.
useEffect( () => {
setIsVisible( ! isDismissed );
}, [ isDismissed ] );
return [ isVisible, dismissNotice ];
};
tag/editor.scss 0000644 00000001461 15155071622 0007510 0 ustar 00 .woocommerce-tag {
display: inline-flex;
margin: 1px 4px 1px 0;
overflow: hidden;
vertical-align: middle;
.woocommerce-tag__text,
.woocommerce-tag__remove {
display: inline-block;
line-height: 24px;
background: $gray-100;
transition: all 0.2s cubic-bezier(0.4, 1, 0.4, 1);
}
.woocommerce-tag__text {
align-self: center;
padding: 0 $gap-smaller;
border-radius: 12px;
color: $gray-700;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&.has-remove .woocommerce-tag__text {
padding: 0 $gap-smallest 0 $gap-smaller;
border-radius: 12px 0 0 12px;
}
.woocommerce-tag__remove {
cursor: pointer;
padding: 0 2px;
border-radius: 0 12px 12px 0;
color: $gray-700;
line-height: 10px;
text-indent: 0;
height: 24px;
&:hover {
color: $gray-900;
}
}
}
tag/index.tsx 0000644 00000005267 15155071622 0007204 0 ustar 00 /**
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import classnames from 'classnames';
import { Button, Popover } from '@wordpress/components';
import { Icon, cancelCircleFilled } from '@wordpress/icons';
import { decodeEntities } from '@wordpress/html-entities';
import { useInstanceId } from '@wordpress/compose';
import { useState } from '@wordpress/element';
/**
* Internal dependencies
*/
import './editor.scss';
/**
* This component can be used to show an item styled as a "tag", optionally with an `X` + "remove"
* or with a popover that is shown on click.
*/
const Tag = ( {
id,
label,
popoverContents,
remove,
screenReaderLabel,
className = '',
}: {
// Additional CSS classes.
className?: string;
// The ID for this item, used in the remove function.
id: string | number;
// The name for this item, displayed as the tag's text.
label: string;
// Contents to display on click in a popover
popoverContents?: JSX.Element;
// A function called when the remove X is clicked. If not used, no X icon will display.
remove?: ( id: string | number ) => () => void;
// A more descriptive label for screen reader users. Defaults to the `name` prop.
screenReaderLabel?: string;
} ): JSX.Element | null => {
const [ isVisible, setIsVisible ] = useState( false );
const instanceId = useInstanceId( Tag );
screenReaderLabel = screenReaderLabel || label;
if ( ! label ) {
// A null label probably means something went wrong
return null;
}
label = decodeEntities( label );
const classes = classnames( 'woocommerce-tag', className, {
'has-remove': !! remove,
} );
const labelId = `woocommerce-tag__label-${ instanceId }`;
const labelTextNode = (
<>
<span className="screen-reader-text">{ screenReaderLabel }</span>
<span aria-hidden="true">{ label }</span>
</>
);
return (
<span className={ classes }>
{ popoverContents ? (
<Button
className="woocommerce-tag__text"
id={ labelId }
onClick={ () => setIsVisible( true ) }
>
{ labelTextNode }
</Button>
) : (
<span className="woocommerce-tag__text" id={ labelId }>
{ labelTextNode }
</span>
) }
{ popoverContents && isVisible && (
<Popover onClose={ () => setIsVisible( false ) }>
{ popoverContents }
</Popover>
) }
{ remove && (
<Button
className="woocommerce-tag__remove"
onClick={ remove( id ) }
label={ sprintf(
// Translators: %s label.
__( 'Remove %s', 'woo-gutenberg-products-block' ),
label
) }
aria-describedby={ labelId }
>
<Icon
icon={ cancelCircleFilled }
size={ 20 }
className="clear-icon"
/>
</Button>
) }
</span>
);
};
export default Tag;
tag/test/__snapshots__/index.js.snap 0000644 00000042106 15155071622 0013530 0 ustar 00 // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Tag <Tag label="foo" /> should render a tag with the label foo 1`] = `
Object {
"asFragment": [Function],
"baseElement": <body>
<p
class="a11y-speak-intro-text"
hidden="hidden"
id="a11y-speak-intro-text"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
>
Notifications
</p>
<div
aria-atomic="true"
aria-live="assertive"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-assertive"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div
aria-atomic="true"
aria-live="polite"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-polite"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div>
<span
class="woocommerce-tag"
>
<span
class="woocommerce-tag__text"
id="woocommerce-tag__label-0"
>
<span
class="screen-reader-text"
>
foo
</span>
<span
aria-hidden="true"
>
foo
</span>
</span>
</span>
</div>
</body>,
"container": <div>
<span
class="woocommerce-tag"
>
<span
class="woocommerce-tag__text"
id="woocommerce-tag__label-0"
>
<span
class="screen-reader-text"
>
foo
</span>
<span
aria-hidden="true"
>
foo
</span>
</span>
</span>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;
exports[`Tag <Tag label="foo" popoverContents={ <p>This is a popover</p> } /> should render a tag with a popover 1`] = `
Object {
"asFragment": [Function],
"baseElement": <body>
<p
class="a11y-speak-intro-text"
hidden="hidden"
id="a11y-speak-intro-text"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
>
Notifications
</p>
<div
aria-atomic="true"
aria-live="assertive"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-assertive"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div
aria-atomic="true"
aria-live="polite"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-polite"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div>
<span
class="woocommerce-tag"
>
<button
class="components-button woocommerce-tag__text"
id="woocommerce-tag__label-2"
type="button"
>
<span
class="screen-reader-text"
>
foo
</span>
<span
aria-hidden="true"
>
foo
</span>
</button>
</span>
</div>
</body>,
"container": <div>
<span
class="woocommerce-tag"
>
<button
class="components-button woocommerce-tag__text"
id="woocommerce-tag__label-2"
type="button"
>
<span
class="screen-reader-text"
>
foo
</span>
<span
aria-hidden="true"
>
foo
</span>
</button>
</span>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;
exports[`Tag <Tag label="foo" remove={ noop } /> should render a tag with a close button 1`] = `
Object {
"asFragment": [Function],
"baseElement": <body>
<p
class="a11y-speak-intro-text"
hidden="hidden"
id="a11y-speak-intro-text"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
>
Notifications
</p>
<div
aria-atomic="true"
aria-live="assertive"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-assertive"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div
aria-atomic="true"
aria-live="polite"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-polite"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div>
<span
class="woocommerce-tag has-remove"
>
<span
class="woocommerce-tag__text"
id="woocommerce-tag__label-1"
>
<span
class="screen-reader-text"
>
foo
</span>
<span
aria-hidden="true"
>
foo
</span>
</span>
<button
aria-describedby="woocommerce-tag__label-1"
aria-label="Remove foo"
class="components-button woocommerce-tag__remove"
type="button"
>
<svg
aria-hidden="true"
class="clear-icon"
focusable="false"
height="20"
role="img"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21ZM15.5303 8.46967C15.8232 8.76256 15.8232 9.23744 15.5303 9.53033L13.0607 12L15.5303 14.4697C15.8232 14.7626 15.8232 15.2374 15.5303 15.5303C15.2374 15.8232 14.7626 15.8232 14.4697 15.5303L12 13.0607L9.53033 15.5303C9.23744 15.8232 8.76256 15.8232 8.46967 15.5303C8.17678 15.2374 8.17678 14.7626 8.46967 14.4697L10.9393 12L8.46967 9.53033C8.17678 9.23744 8.17678 8.76256 8.46967 8.46967C8.76256 8.17678 9.23744 8.17678 9.53033 8.46967L12 10.9393L14.4697 8.46967C14.7626 8.17678 15.2374 8.17678 15.5303 8.46967Z"
/>
</svg>
</button>
</span>
</div>
</body>,
"container": <div>
<span
class="woocommerce-tag has-remove"
>
<span
class="woocommerce-tag__text"
id="woocommerce-tag__label-1"
>
<span
class="screen-reader-text"
>
foo
</span>
<span
aria-hidden="true"
>
foo
</span>
</span>
<button
aria-describedby="woocommerce-tag__label-1"
aria-label="Remove foo"
class="components-button woocommerce-tag__remove"
type="button"
>
<svg
aria-hidden="true"
class="clear-icon"
focusable="false"
height="20"
role="img"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21ZM15.5303 8.46967C15.8232 8.76256 15.8232 9.23744 15.5303 9.53033L13.0607 12L15.5303 14.4697C15.8232 14.7626 15.8232 15.2374 15.5303 15.5303C15.2374 15.8232 14.7626 15.8232 14.4697 15.5303L12 13.0607L9.53033 15.5303C9.23744 15.8232 8.76256 15.8232 8.46967 15.5303C8.17678 15.2374 8.17678 14.7626 8.46967 14.4697L10.9393 12L8.46967 9.53033C8.17678 9.23744 8.17678 8.76256 8.46967 8.46967C8.76256 8.17678 9.23744 8.17678 9.53033 8.46967L12 10.9393L14.4697 8.46967C14.7626 8.17678 15.2374 8.17678 15.5303 8.46967Z"
/>
</svg>
</button>
</span>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;
exports[`Tag <Tag label="foo" screenReaderLabel="FooBar" /> should render a tag with a screen reader label 1`] = `
Object {
"asFragment": [Function],
"baseElement": <body>
<p
class="a11y-speak-intro-text"
hidden="hidden"
id="a11y-speak-intro-text"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
>
Notifications
</p>
<div
aria-atomic="true"
aria-live="assertive"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-assertive"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div
aria-atomic="true"
aria-live="polite"
aria-relevant="additions text"
class="a11y-speak-region"
id="a11y-speak-polite"
style="position: absolute;margin: -1px;padding: 0;height: 1px;width: 1px;overflow: hidden;clip: rect(1px, 1px, 1px, 1px);-webkit-clip-path: inset(50%);clip-path: inset(50%);border: 0;word-wrap: normal !important;"
/>
<div>
<span
class="woocommerce-tag"
>
<span
class="woocommerce-tag__text"
id="woocommerce-tag__label-3"
>
<span
class="screen-reader-text"
>
FooBar
</span>
<span
aria-hidden="true"
>
foo
</span>
</span>
</span>
</div>
</body>,
"container": <div>
<span
class="woocommerce-tag"
>
<span
class="woocommerce-tag__text"
id="woocommerce-tag__label-3"
>
<span
class="screen-reader-text"
>
FooBar
</span>
<span
aria-hidden="true"
>
foo
</span>
</span>
</span>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;
tag/test/index.js 0000644 00000002100 15155071622 0007740 0 ustar 00 /**
* External dependencies
*/
import { render } from '@testing-library/react';
/**
* Internal dependencies
*/
import Tag from '../';
const noop = () => {};
describe( 'Tag', () => {
test( '<Tag label="foo" /> should render a tag with the label foo', () => {
const component = render( <Tag label="foo" /> );
expect( component ).toMatchSnapshot();
} );
test( '<Tag label="foo" remove={ noop } /> should render a tag with a close button', () => {
const component = render( <Tag label="foo" remove={ noop } /> );
expect( component ).toMatchSnapshot();
} );
test( '<Tag label="foo" popoverContents={ <p>This is a popover</p> } /> should render a tag with a popover', () => {
const component = render(
<Tag label="foo" popoverContents={ <p>This is a popover</p> } />
);
expect( component ).toMatchSnapshot();
} );
test( '<Tag label="foo" screenReaderLabel="FooBar" /> should render a tag with a screen reader label', () => {
const component = render(
<Tag label="foo" screenReaderLabel="FooBar" />
);
expect( component ).toMatchSnapshot();
} );
} );
template-notice/editor.scss 0000644 00000000342 15155071622 0012024 0 ustar 00 .wc-default-template-notice.is-dismissible {
margin: 0;
padding-right: 16px;
.components-notice__dismiss {
min-width: 24px;
}
.components-notice__content {
margin: 4px 0;
}
svg {
width: 16px;
height: 16px;
}
}
template-notice/index.tsx 0000644 00000002712 15155071622 0011513 0 ustar 00 /**
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { Notice, Button } from '@wordpress/components';
import { useState } from '@wordpress/element';
import { isSiteEditorPage } from '@woocommerce/utils';
import { select } from '@wordpress/data';
import { getSetting } from '@woocommerce/settings';
/**
* Internal dependencies
*/
import './editor.scss';
export function TemplateNotice( { block }: { block: string } ) {
const [ settingStatus, setStatus ] = useState( 'pristine' );
const store = select( 'core/edit-site' );
if ( settingStatus === 'dismissed' || isSiteEditorPage( store ) ) {
return null;
}
const editUrl = `${ getSetting(
'adminUrl'
) }site-editor.php?postType=wp_template&postId=woocommerce%2Fwoocommerce%2F%2F${ block }`;
const noticeContent = sprintf(
// translators: %s: cart or checkout page name.
__(
'The default %s can be customized in the Site Editor',
'woo-gutenberg-products-block'
),
block === 'checkout'
? __( 'checkout', 'woo-gutenberg-products-block' )
: __( 'cart', 'woo-gutenberg-products-block' )
);
return (
<Notice
className="wc-default-template-notice"
status={ 'warning' }
onRemove={ () => setStatus( 'dismissed' ) }
spokenMessage={ noticeContent }
>
<>
<p>{ noticeContent }</p>
<Button href={ editUrl } variant="secondary" isSmall={ true }>
{ __( 'Edit template', 'woo-gutenberg-products-block' ) }
</Button>
</>
</Notice>
);
}
text-toolbar-button/index.js 0000644 00000000623 15155071622 0012153 0 ustar 00 /**
* External dependencies
*/
import { Button } from '@wordpress/components';
import classnames from 'classnames';
/**
* Internal dependencies
*/
import './style.scss';
function TextToolbarButton( { className = '', ...props } ) {
const classes = classnames( 'wc-block-text-toolbar-button', className );
return <Button className={ classes } { ...props } />;
}
export default TextToolbarButton;
text-toolbar-button/style.scss 0000644 00000000420 15155071622 0012536 0 ustar 00 .wc-block-text-toolbar-button {
align-items: center;
&.is-toggled,
&.is-toggled:focus {
background: $gray-700;
color: $white;
}
}
.block-editor-block-toolbar__slot {
// prevents text toolbar items shrinking to avoid other buttons overlapping.
flex-shrink: 0;
}
utils/index.js 0000644 00000014430 15155071622 0007357 0 ustar 00 /**
* External dependencies
*/
import { addQueryArgs } from '@wordpress/url';
import apiFetch from '@wordpress/api-fetch';
import { getSetting } from '@woocommerce/settings';
import { blocksConfig } from '@woocommerce/block-settings';
/**
* Get product query requests for the Store API.
*
* @param {Object} request A query object with the list of selected products and search term.
* @param {number[]} request.selected Currently selected products.
* @param {string=} request.search Search string.
* @param {(Record<string, unknown>)=} request.queryArgs Query args to pass in.
*/
const getProductsRequests = ( {
selected = [],
search = '',
queryArgs = {},
} ) => {
const isLargeCatalog = blocksConfig.productCount > 100;
const defaultArgs = {
per_page: isLargeCatalog ? 100 : 0,
catalog_visibility: 'any',
search,
orderby: 'title',
order: 'asc',
};
const requests = [
addQueryArgs( '/wc/store/v1/products', {
...defaultArgs,
...queryArgs,
} ),
];
// If we have a large catalog, we might not get all selected products in the first page.
if ( isLargeCatalog && selected.length ) {
requests.push(
addQueryArgs( '/wc/store/v1/products', {
catalog_visibility: 'any',
include: selected,
per_page: 0,
} )
);
}
return requests;
};
const uniqBy = ( array, iteratee ) => {
const seen = new Map();
return array.filter( ( item ) => {
const key = iteratee( item );
if ( ! seen.has( key ) ) {
seen.set( key, item );
return true;
}
return false;
} );
};
/**
* Get a promise that resolves to a list of products from the Store API.
*
* @param {Object} request A query object with the list of selected products and search term.
* @param {number[]} request.selected Currently selected products.
* @param {string=} request.search Search string.
* @param {(Record<string, unknown>)=} request.queryArgs Query args to pass in.
* @return {Promise<unknown>} Promise resolving to a Product list.
* @throws Exception if there is an error.
*/
export const getProducts = ( {
selected = [],
search = '',
queryArgs = {},
} ) => {
const requests = getProductsRequests( { selected, search, queryArgs } );
return Promise.all( requests.map( ( path ) => apiFetch( { path } ) ) )
.then( ( data ) => {
const flatData = data.flat();
const products = uniqBy( flatData, ( item ) => item.id );
const list = products.map( ( product ) => ( {
...product,
parent: 0,
} ) );
return list;
} )
.catch( ( e ) => {
throw e;
} );
};
/**
* Get a promise that resolves to a product object from the Store API.
*
* @param {number} productId Id of the product to retrieve.
*/
export const getProduct = ( productId ) => {
return apiFetch( {
path: `/wc/store/v1/products/${ productId }`,
} );
};
/**
* Get a promise that resolves to a list of attribute objects from the Store API.
*/
export const getAttributes = () => {
return apiFetch( {
path: `wc/store/v1/products/attributes`,
} );
};
/**
* Get a promise that resolves to a list of attribute term objects from the Store API.
*
* @param {number} attribute Id of the attribute to retrieve terms for.
*/
export const getTerms = ( attribute ) => {
return apiFetch( {
path: `wc/store/v1/products/attributes/${ attribute }/terms`,
} );
};
/**
* Get product tag query requests for the Store API.
*
* @param {Object} request A query object with the list of selected products and search term.
* @param {Array} request.selected Currently selected tags.
* @param {string} request.search Search string.
*/
const getProductTagsRequests = ( { selected = [], search } ) => {
const limitTags = getSetting( 'limitTags', false );
const requests = [
addQueryArgs( `wc/store/v1/products/tags`, {
per_page: limitTags ? 100 : 0,
orderby: limitTags ? 'count' : 'name',
order: limitTags ? 'desc' : 'asc',
search,
} ),
];
// If we have a large catalog, we might not get all selected products in the first page.
if ( limitTags && selected.length ) {
requests.push(
addQueryArgs( `wc/store/v1/products/tags`, {
include: selected,
} )
);
}
return requests;
};
/**
* Get a promise that resolves to a list of tags from the Store API.
*
* @param {Object} props A query object with the list of selected products and search term.
* @param {Array} props.selected
* @param {string} props.search
*/
export const getProductTags = ( { selected = [], search } ) => {
const requests = getProductTagsRequests( { selected, search } );
return Promise.all( requests.map( ( path ) => apiFetch( { path } ) ) ).then(
( data ) => {
const flatData = data.flat();
return uniqBy( flatData, ( item ) => item.id );
}
);
};
/**
* Get a promise that resolves to a list of category objects from the Store API.
*
* @param {Object} queryArgs Query args to pass in.
*/
export const getCategories = ( queryArgs ) => {
return apiFetch( {
path: addQueryArgs( `wc/store/v1/products/categories`, {
per_page: 0,
...queryArgs,
} ),
} );
};
/**
* Get a promise that resolves to a category object from the API.
*
* @param {number} categoryId Id of the product to retrieve.
*/
export const getCategory = ( categoryId ) => {
return apiFetch( {
path: `wc/store/v1/products/categories/${ categoryId }`,
} );
};
/**
* Get a promise that resolves to a list of variation objects from the Store API.
*
* @param {number} product Product ID.
*/
export const getProductVariations = ( product ) => {
return apiFetch( {
path: addQueryArgs( `wc/store/v1/products`, {
per_page: 0,
type: 'variation',
parent: product,
} ),
} );
};
/**
* Given a page object and an array of page, format the title.
*
* @param {Object} page Page object.
* @param {Object} page.title Page title object.
* @param {string} page.title.raw Page title.
* @param {string} page.slug Page slug.
* @param {Array} pages Array of all pages.
* @return {string} Formatted page title to display.
*/
export const formatTitle = ( page, pages ) => {
if ( ! page.title.raw ) {
return page.slug;
}
const isUnique =
pages.filter( ( p ) => p.title.raw === page.title.raw ).length === 1;
return page.title.raw + ( ! isUnique ? ` - ${ page.slug }` : '' );
};