Add dropdown version of Filter by Stock Status (https://github.com/woocommerce/woocommerce-blocks/pull/7831)
* Extend Filter by Stock Editor options with dropdown and single/multiple choice * Add dropdown implementation for Filter by Stock Status * Adjust font-sizes to the rest of the filters * Add tests to Filter by Stock: dropdown and list variants * Change test file extension from .js to .tsx, so it handles types as well * Add E2E test to Filter by Stock checking if display style can be toggled * When typing in Filter by Stock dropdown, handle the space so it highlights the suggestions * Change the name of the filter blocks in the test files * Remove unnecessary waiting step in E2E test for Filter by Stock toMatchElement waits for an element for 30s by itself, hence waitForSelector usage was removed * Improve the STOCK_STATUS_OPTIONS type handling * Extract onDropdownChange function instead of inline arrow function * Fix overlaping dropdown content with the wrapper when Filter by Stock was set to single
This commit is contained in:
parent
6c40524dfe
commit
6aa8a72f8e
|
@ -16,7 +16,7 @@ jest.mock( '@woocommerce/base-context/hooks', () => ( {
|
|||
...jest.requireActual( '@woocommerce/base-context/hooks' ),
|
||||
} ) );
|
||||
|
||||
const setWindowUrl = ( { url }: SetWindowUrlParams ) => {
|
||||
const setWindowUrl = ( { url }: { url: string } ) => {
|
||||
global.window = Object.create( window );
|
||||
Object.defineProperty( window, 'location', {
|
||||
value: {
|
||||
|
|
|
@ -39,6 +39,14 @@
|
|||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"displayStyle": {
|
||||
"type": "string",
|
||||
"default": "list"
|
||||
},
|
||||
"selectType": {
|
||||
"type": "string",
|
||||
"default": "multiple"
|
||||
},
|
||||
"isPreview": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
|
|
|
@ -3,7 +3,12 @@
|
|||
*/
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { speak } from '@wordpress/a11y';
|
||||
import { usePrevious, useShallowEqual } from '@woocommerce/base-hooks';
|
||||
import { Icon, chevronDown } from '@wordpress/icons';
|
||||
import {
|
||||
usePrevious,
|
||||
useShallowEqual,
|
||||
useBorderProps,
|
||||
} from '@woocommerce/base-hooks';
|
||||
import {
|
||||
useQueryStateByKey,
|
||||
useQueryStateByContext,
|
||||
|
@ -22,11 +27,13 @@ import FilterSubmitButton from '@woocommerce/base-components/filter-submit-butto
|
|||
import FilterResetButton from '@woocommerce/base-components/filter-reset-button';
|
||||
import FilterTitlePlaceholder from '@woocommerce/base-components/filter-placeholder';
|
||||
import Label from '@woocommerce/base-components/filter-element-label';
|
||||
import FormTokenField from '@woocommerce/base-components/form-token-field';
|
||||
import isShallowEqual from '@wordpress/is-shallow-equal';
|
||||
import { decodeEntities } from '@wordpress/html-entities';
|
||||
import { isBoolean, objectHasProp } from '@woocommerce/types';
|
||||
import { addQueryArgs, removeQueryArgs } from '@wordpress/url';
|
||||
import { changeUrl, PREFIX_QUERY_ARG_FILTER_TYPE } from '@woocommerce/utils';
|
||||
import { difference } from 'lodash';
|
||||
import classnames from 'classnames';
|
||||
|
||||
/**
|
||||
|
@ -34,8 +41,8 @@ import classnames from 'classnames';
|
|||
*/
|
||||
import { previewOptions } from './preview';
|
||||
import './style.scss';
|
||||
import { getActiveFilters } from './utils';
|
||||
import { Attributes, DisplayOption } from './types';
|
||||
import { formatSlug, getActiveFilters, generateUniqueId } from './utils';
|
||||
import { Attributes, DisplayOption, Current } from './types';
|
||||
import { useSetWraperVisibility } from '../filter-wrapper/context';
|
||||
|
||||
export const QUERY_PARAM_KEY = PREFIX_QUERY_ARG_FILTER_TYPE + 'stock_status';
|
||||
|
@ -74,7 +81,7 @@ const StockStatusFilterBlock = ( {
|
|||
? []
|
||||
: getSettingWithCoercion( 'product_ids', [], Array.isArray );
|
||||
|
||||
const STOCK_STATUS_OPTIONS = useRef(
|
||||
const STOCK_STATUS_OPTIONS: { current: Current } = useRef(
|
||||
getSetting( 'hideOutOfStockItems', false )
|
||||
? otherStockStatusOptions
|
||||
: { outofstock, ...otherStockStatusOptions }
|
||||
|
@ -127,6 +134,15 @@ const StockStatusFilterBlock = ( {
|
|||
[ filteredCounts ]
|
||||
);
|
||||
|
||||
/*
|
||||
FormTokenField forces the dropdown to reopen on reset, so we create a unique ID to use as the components key.
|
||||
This will force the component to remount on reset when we change this value.
|
||||
More info: https://github.com/woocommerce/woocommerce-blocks/pull/6920#issuecomment-1222402482
|
||||
*/
|
||||
const [ remountKey, setRemountKey ] = useState( generateUniqueId() );
|
||||
|
||||
const borderProps = useBorderProps( blockAttributes );
|
||||
|
||||
/**
|
||||
* Compare intersection of all stock statuses and filtered counts to get a list of options to display.
|
||||
*/
|
||||
|
@ -173,11 +189,15 @@ const StockStatusFilterBlock = ( {
|
|||
count={ blockAttributes.showCounts ? count : null }
|
||||
/>
|
||||
),
|
||||
textLabel: blockAttributes.showCounts
|
||||
? `${ decodeEntities( status.name ) } (${ count })`
|
||||
: decodeEntities( status.name ),
|
||||
};
|
||||
} )
|
||||
.filter( ( option ): option is DisplayOption => !! option );
|
||||
|
||||
setDisplayedOptions( newOptions );
|
||||
setRemountKey( generateUniqueId() );
|
||||
}, [
|
||||
blockAttributes.showCounts,
|
||||
blockAttributes.isPreview,
|
||||
|
@ -221,6 +241,8 @@ const StockStatusFilterBlock = ( {
|
|||
changeUrl( newUrl );
|
||||
};
|
||||
|
||||
const allowsMultipleOptions = blockAttributes.selectType !== 'single';
|
||||
|
||||
const onSubmit = useCallback(
|
||||
( checkedOptions ) => {
|
||||
if ( isEditor ) {
|
||||
|
@ -327,23 +349,59 @@ const StockStatusFilterBlock = ( {
|
|||
|
||||
const previouslyChecked = checked.includes( checkedValue );
|
||||
|
||||
const newChecked = checked.filter(
|
||||
( value ) => value !== checkedValue
|
||||
);
|
||||
|
||||
if ( ! previouslyChecked ) {
|
||||
newChecked.push( checkedValue );
|
||||
newChecked.sort();
|
||||
announceFilterChange( { filterAdded: checkedValue } );
|
||||
} else {
|
||||
announceFilterChange( { filterRemoved: checkedValue } );
|
||||
if ( ! allowsMultipleOptions ) {
|
||||
const newChecked = previouslyChecked ? [] : [ checkedValue ];
|
||||
announceFilterChange(
|
||||
previouslyChecked
|
||||
? { filterRemoved: checkedValue }
|
||||
: { filterAdded: checkedValue }
|
||||
);
|
||||
setChecked( newChecked );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( previouslyChecked ) {
|
||||
const newChecked = checked.filter(
|
||||
( value ) => value !== checkedValue
|
||||
);
|
||||
|
||||
announceFilterChange( { filterRemoved: checkedValue } );
|
||||
setChecked( newChecked );
|
||||
return;
|
||||
}
|
||||
|
||||
const newChecked = [ ...checked, checkedValue ].sort();
|
||||
announceFilterChange( { filterAdded: checkedValue } );
|
||||
setChecked( newChecked );
|
||||
},
|
||||
[ checked, displayedOptions ]
|
||||
[ checked, allowsMultipleOptions, displayedOptions ]
|
||||
);
|
||||
|
||||
const onDropdownChange = ( tokens: string[] ) => {
|
||||
if ( ! allowsMultipleOptions && tokens.length > 1 ) {
|
||||
tokens = tokens.slice( -1 );
|
||||
}
|
||||
|
||||
tokens = tokens.map( ( token ) => {
|
||||
const displayOption = displayedOptions.find(
|
||||
( option ) => option.value === token
|
||||
);
|
||||
|
||||
return displayOption ? displayOption.value : token;
|
||||
} );
|
||||
|
||||
const added = difference( tokens, checked );
|
||||
|
||||
if ( added.length === 1 ) {
|
||||
return onChange( added[ 0 ] );
|
||||
}
|
||||
|
||||
const removed = difference( checked, tokens );
|
||||
if ( removed.length === 1 ) {
|
||||
onChange( removed[ 0 ] );
|
||||
}
|
||||
};
|
||||
|
||||
if ( ! filteredCountsLoading && displayedOptions.length === 0 ) {
|
||||
setWrapperVisibility( false );
|
||||
return null;
|
||||
|
@ -385,18 +443,76 @@ const StockStatusFilterBlock = ( {
|
|||
<>
|
||||
{ ! isEditor && blockAttributes.heading && filterHeading }
|
||||
<div
|
||||
className={ classnames( 'wc-block-stock-filter', {
|
||||
'is-loading': isLoading,
|
||||
} ) }
|
||||
className={ classnames(
|
||||
'wc-block-stock-filter',
|
||||
`style-${ blockAttributes.displayStyle }`,
|
||||
{
|
||||
'is-loading': isLoading,
|
||||
}
|
||||
) }
|
||||
>
|
||||
<CheckboxList
|
||||
className={ 'wc-block-stock-filter-list' }
|
||||
options={ displayedOptions }
|
||||
checked={ checked }
|
||||
onChange={ onChange }
|
||||
isLoading={ isLoading }
|
||||
isDisabled={ isDisabled }
|
||||
/>
|
||||
{ blockAttributes.displayStyle === 'dropdown' ? (
|
||||
<>
|
||||
<FormTokenField
|
||||
key={ remountKey }
|
||||
className={ classnames( borderProps.className, {
|
||||
'single-selection': ! allowsMultipleOptions,
|
||||
'is-loading': isLoading,
|
||||
} ) }
|
||||
style={ { ...borderProps.style } }
|
||||
suggestions={ displayedOptions
|
||||
.filter(
|
||||
( option ) =>
|
||||
! checked.includes( option.value )
|
||||
)
|
||||
.map( ( option ) => option.value ) }
|
||||
disabled={ isLoading }
|
||||
placeholder={ __(
|
||||
'Select stock status',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
onChange={ onDropdownChange }
|
||||
value={ checked }
|
||||
displayTransform={ ( value: string ) => {
|
||||
const result = displayedOptions.find(
|
||||
( option ) => option.value === value
|
||||
);
|
||||
return result ? result.textLabel : value;
|
||||
} }
|
||||
saveTransform={ formatSlug }
|
||||
messages={ {
|
||||
added: __(
|
||||
'Stock filter added.',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
removed: __(
|
||||
'Stock filter removed.',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
remove: __(
|
||||
'Remove stock filter.',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
__experimentalInvalid: __(
|
||||
'Invalid stock filter.',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
} }
|
||||
/>
|
||||
{ allowsMultipleOptions && (
|
||||
<Icon icon={ chevronDown } size={ 30 } />
|
||||
) }
|
||||
</>
|
||||
) : (
|
||||
<CheckboxList
|
||||
className={ 'wc-block-stock-filter-list' }
|
||||
options={ displayedOptions }
|
||||
checked={ checked }
|
||||
onChange={ onChange }
|
||||
isLoading={ isLoading }
|
||||
isDisabled={ isDisabled }
|
||||
/>
|
||||
) }
|
||||
</div>
|
||||
{
|
||||
<div className="wc-block-stock-filter__actions">
|
||||
|
|
|
@ -4,14 +4,18 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import classnames from 'classnames';
|
||||
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
|
||||
import BlockTitle from '@woocommerce/editor-components/block-title';
|
||||
import type { BlockEditProps } from '@wordpress/blocks';
|
||||
import {
|
||||
Disabled,
|
||||
PanelBody,
|
||||
ToggleControl,
|
||||
withSpokenMessages,
|
||||
// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
|
||||
__experimentalToggleGroupControl as ToggleGroupControl,
|
||||
// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
|
||||
__experimentalToggleGroupControlOption as ToggleGroupControlOption,
|
||||
} from '@wordpress/components';
|
||||
import BlockTitle from '@woocommerce/editor-components/block-title';
|
||||
import type { BlockEditProps } from '@wordpress/blocks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -26,8 +30,15 @@ const Edit = ( {
|
|||
attributes,
|
||||
setAttributes,
|
||||
}: BlockEditProps< Attributes > ) => {
|
||||
const { className, heading, headingLevel, showCounts, showFilterButton } =
|
||||
attributes;
|
||||
const {
|
||||
className,
|
||||
heading,
|
||||
headingLevel,
|
||||
showCounts,
|
||||
showFilterButton,
|
||||
selectType,
|
||||
displayStyle,
|
||||
} = attributes;
|
||||
|
||||
const blockProps = useBlockProps( {
|
||||
className: classnames( 'wc-block-stock-filter', className ),
|
||||
|
@ -37,7 +48,10 @@ const Edit = ( {
|
|||
return (
|
||||
<InspectorControls key="inspector">
|
||||
<PanelBody
|
||||
title={ __( 'Content', 'woo-gutenberg-products-block' ) }
|
||||
title={ __(
|
||||
'Display Settings',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
>
|
||||
<ToggleControl
|
||||
label={ __(
|
||||
|
@ -51,10 +65,62 @@ const Edit = ( {
|
|||
} )
|
||||
}
|
||||
/>
|
||||
</PanelBody>
|
||||
<PanelBody
|
||||
title={ __( 'Settings', 'woo-gutenberg-products-block' ) }
|
||||
>
|
||||
<ToggleGroupControl
|
||||
label={ __(
|
||||
'Allow selecting multiple options?',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
value={ selectType || 'multiple' }
|
||||
onChange={ ( value: string ) =>
|
||||
setAttributes( {
|
||||
selectType: value,
|
||||
} )
|
||||
}
|
||||
className="wc-block-attribute-filter__multiple-toggle"
|
||||
>
|
||||
<ToggleGroupControlOption
|
||||
value="multiple"
|
||||
label={ __(
|
||||
'Multiple',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
/>
|
||||
<ToggleGroupControlOption
|
||||
value="single"
|
||||
label={ __(
|
||||
'Single',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
/>
|
||||
</ToggleGroupControl>
|
||||
<ToggleGroupControl
|
||||
label={ __(
|
||||
'Display Style',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
value={ displayStyle }
|
||||
onChange={ ( value ) =>
|
||||
setAttributes( {
|
||||
displayStyle: value,
|
||||
} )
|
||||
}
|
||||
className="wc-block-attribute-filter__display-toggle"
|
||||
>
|
||||
<ToggleGroupControlOption
|
||||
value="list"
|
||||
label={ __(
|
||||
'List',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
/>
|
||||
<ToggleGroupControlOption
|
||||
value="dropdown"
|
||||
label={ __(
|
||||
'Dropdown',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
/>
|
||||
</ToggleGroupControl>
|
||||
<ToggleControl
|
||||
label={ __(
|
||||
"Show 'Apply filters' button",
|
||||
|
|
|
@ -8,15 +8,18 @@ export const previewOptions = [
|
|||
value: 'preview-1',
|
||||
name: 'In Stock',
|
||||
label: <Label name="In Stock" count={ 3 } />,
|
||||
textLabel: 'In Stock (3)',
|
||||
},
|
||||
{
|
||||
value: 'preview-2',
|
||||
name: 'Out of sotck',
|
||||
name: 'Out of stock',
|
||||
label: <Label name="Out of stock" count={ 3 } />,
|
||||
textLabel: 'Out of stock (3)',
|
||||
},
|
||||
{
|
||||
value: 'preview-3',
|
||||
name: 'On backorder',
|
||||
label: <Label name="On backorder" count={ 2 } />,
|
||||
textLabel: 'On backorder (2)',
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
@import "../shared/styles/style";
|
||||
|
||||
.wp-block-woocommerce-stock-filter {
|
||||
h1,
|
||||
h2,
|
||||
|
@ -33,6 +35,116 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.style-dropdown {
|
||||
@include includeFormTokenFieldFix();
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: $gap;
|
||||
align-items: flex-start;
|
||||
|
||||
.wc-block-components-filter-submit-button {
|
||||
height: 36px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
> svg {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.wc-blocks-components-form-token-field-wrapper {
|
||||
flex-grow: 1;
|
||||
max-width: unset;
|
||||
width: 0;
|
||||
height: max-content;
|
||||
|
||||
&:not(.is-loading) {
|
||||
border: 1px solid $gray-700 !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&.is-loading {
|
||||
border-radius: em(4px);
|
||||
}
|
||||
|
||||
.components-form-token-field {
|
||||
border-radius: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.wc-blocks-components-form-token-field-wrapper .components-form-token-field__input-container {
|
||||
@include reset-typography();
|
||||
border: 0;
|
||||
padding: $gap-smaller;
|
||||
border-radius: inherit;
|
||||
|
||||
.components-form-token-field__input {
|
||||
@include font-size(small);
|
||||
|
||||
&::placeholder {
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
|
||||
.components-form-token-field__suggestions-list {
|
||||
border: 1px solid $gray-700;
|
||||
border-radius: 4px;
|
||||
margin-top: $gap-smaller;
|
||||
max-height: 21em;
|
||||
|
||||
.components-form-token-field__suggestion {
|
||||
color: $black;
|
||||
border: 1px solid $gray-400;
|
||||
border-radius: 4px;
|
||||
margin: $gap-small;
|
||||
padding: $gap-small;
|
||||
}
|
||||
}
|
||||
|
||||
.components-form-token-field__token,
|
||||
.components-form-token-field__suggestion {
|
||||
@include font-size(small);
|
||||
}
|
||||
}
|
||||
|
||||
.wc-block-components-product-rating {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.wc-blocks-components-form-token-field-wrapper:not(.single-selection) .components-form-token-field__input-container {
|
||||
padding: $gap-smallest 30px $gap-smallest $gap-smaller;
|
||||
|
||||
.components-form-token-field__token-text {
|
||||
background-color: $white;
|
||||
border: 1px solid;
|
||||
border-right: 0;
|
||||
border-radius: 25px 0 0 25px;
|
||||
padding: em($gap-smallest) em($gap-smaller) em($gap-smallest) em($gap-small);
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
> .components-form-token-field__input {
|
||||
margin: em($gap-smallest) 0;
|
||||
}
|
||||
|
||||
.components-button.components-form-token-field__remove-token {
|
||||
background-color: $white;
|
||||
border: 1px solid;
|
||||
border-left: 0;
|
||||
border-radius: 0 25px 25px 0;
|
||||
padding: 1px em($gap-smallest) 0 0;
|
||||
|
||||
&.has-icon svg {
|
||||
background-color: $gray-200;
|
||||
border-radius: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wc-block-stock-filter__actions {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Testing stock filter renders the stock filter block 1`] = `
|
||||
exports[`Filter by Stock block renders the stock filter block 1`] = `
|
||||
<div>
|
||||
|
||||
<div
|
||||
class="wc-block-stock-filter"
|
||||
class="wc-block-stock-filter style-list"
|
||||
>
|
||||
<ul
|
||||
class="wc-block-checkbox-list wc-block-components-checkbox-list wc-block-stock-filter-list"
|
||||
|
@ -109,10 +110,11 @@ exports[`Testing stock filter renders the stock filter block 1`] = `
|
|||
</div>
|
||||
`;
|
||||
|
||||
exports[`Testing stock filter renders the stock filter block with the filter button 1`] = `
|
||||
exports[`Filter by Stock block renders the stock filter block with the filter button 1`] = `
|
||||
<div>
|
||||
|
||||
<div
|
||||
class="wc-block-stock-filter"
|
||||
class="wc-block-stock-filter style-list"
|
||||
>
|
||||
<ul
|
||||
class="wc-block-checkbox-list wc-block-components-checkbox-list wc-block-stock-filter-list"
|
||||
|
@ -234,10 +236,11 @@ exports[`Testing stock filter renders the stock filter block with the filter but
|
|||
</div>
|
||||
`;
|
||||
|
||||
exports[`Testing stock filter renders the stock filter block with the product counts 1`] = `
|
||||
exports[`Filter by Stock block renders the stock filter block with the product counts 1`] = `
|
||||
<div>
|
||||
|
||||
<div
|
||||
class="wc-block-stock-filter"
|
||||
class="wc-block-stock-filter style-list"
|
||||
>
|
||||
<ul
|
||||
class="wc-block-checkbox-list wc-block-components-checkbox-list wc-block-stock-filter-list"
|
|
@ -1,87 +0,0 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { render } from '@testing-library/react';
|
||||
import { default as fetchMock } from 'jest-fetch-mock';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from '../block';
|
||||
import { allSettings } from '../../../settings/shared/settings-init';
|
||||
|
||||
const mockResults = {
|
||||
stock_status_counts: [
|
||||
{ status: 'instock', count: '18' },
|
||||
{ status: 'outofstock', count: '1' },
|
||||
{ status: 'onbackorder', count: '5' },
|
||||
],
|
||||
};
|
||||
|
||||
jest.mock( '@woocommerce/base-context/hooks', () => {
|
||||
return {
|
||||
...jest.requireActual( '@woocommerce/base-context/hooks' ),
|
||||
useCollectionData: () => ( { isLoading: false, results: mockResults } ),
|
||||
};
|
||||
} );
|
||||
|
||||
jest.mock( '@woocommerce/settings', () => {
|
||||
return {
|
||||
...jest.requireActual( '@woocommerce/settings' ),
|
||||
getSettingWithCoercion: jest
|
||||
.fn()
|
||||
.mockImplementation( ( key, defaultValue ) => {
|
||||
if ( key === 'has_filterable_products' ) {
|
||||
return true;
|
||||
}
|
||||
return defaultValue;
|
||||
} ),
|
||||
};
|
||||
} );
|
||||
|
||||
const StockFilterBlock = ( props ) => <Block { ...props } />;
|
||||
describe( 'Testing stock filter', () => {
|
||||
beforeEach( () => {
|
||||
allSettings.stockStatusOptions = {
|
||||
instock: 'In stock',
|
||||
outofstock: 'Out of stock',
|
||||
onbackorder: 'On backorder',
|
||||
};
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
fetchMock.resetMocks();
|
||||
} );
|
||||
|
||||
it( 'renders the stock filter block', async () => {
|
||||
const { container } = render(
|
||||
<StockFilterBlock attributes={ { isPreview: false } } />
|
||||
);
|
||||
expect( container ).toMatchSnapshot();
|
||||
} );
|
||||
|
||||
it( 'renders the stock filter block with the filter button', async () => {
|
||||
const { container } = render(
|
||||
<StockFilterBlock
|
||||
attributes={ { isPreview: false, showFilterButton: true } }
|
||||
/>
|
||||
);
|
||||
expect( container ).toMatchSnapshot();
|
||||
} );
|
||||
|
||||
it( 'renders the stock filter block with the product counts', async () => {
|
||||
const { container } = render(
|
||||
<StockFilterBlock
|
||||
attributes={ {
|
||||
isPreview: false,
|
||||
showCounts: true,
|
||||
} }
|
||||
/>
|
||||
);
|
||||
expect( container ).toMatchSnapshot();
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,552 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render, screen, within, waitFor } from '@testing-library/react';
|
||||
import { default as fetchMock } from 'jest-fetch-mock';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from '../block';
|
||||
import { allSettings } from '../../../settings/shared/settings-init';
|
||||
import { Attributes } from '../types';
|
||||
|
||||
const setWindowUrl = ( { url }: { url: string } ) => {
|
||||
global.window = Object.create( window );
|
||||
Object.defineProperty( window, 'location', {
|
||||
value: {
|
||||
href: url,
|
||||
},
|
||||
writable: true,
|
||||
} );
|
||||
};
|
||||
|
||||
const mockResults = {
|
||||
stock_status_counts: [
|
||||
{ status: 'instock', count: '18' },
|
||||
{ status: 'outofstock', count: '1' },
|
||||
{ status: 'onbackorder', count: '5' },
|
||||
],
|
||||
};
|
||||
|
||||
jest.mock( '@woocommerce/base-context/hooks', () => {
|
||||
return {
|
||||
...jest.requireActual( '@woocommerce/base-context/hooks' ),
|
||||
useCollectionData: () => ( { isLoading: false, results: mockResults } ),
|
||||
};
|
||||
} );
|
||||
|
||||
jest.mock( '@woocommerce/settings', () => {
|
||||
return {
|
||||
...jest.requireActual( '@woocommerce/settings' ),
|
||||
getSettingWithCoercion: jest
|
||||
.fn()
|
||||
.mockImplementation( ( key, defaultValue ) => {
|
||||
if ( key === 'has_filterable_products' ) {
|
||||
return true;
|
||||
}
|
||||
return defaultValue;
|
||||
} ),
|
||||
};
|
||||
} );
|
||||
|
||||
type DisplayStyle = 'list' | 'dropdown';
|
||||
type SelectType = 'single' | 'multiple';
|
||||
interface SetupParams {
|
||||
filterStock?: string;
|
||||
displayStyle?: DisplayStyle;
|
||||
selectType?: SelectType;
|
||||
showCounts?: boolean;
|
||||
showFilterButton?: boolean;
|
||||
}
|
||||
|
||||
const selectors = {
|
||||
list: '.wc-block-stock-filter.style-list',
|
||||
suggestionsContainer: '.components-form-token-field__suggestions-list',
|
||||
chipsContainer: '.components-form-token-field__token',
|
||||
};
|
||||
|
||||
const setup = ( params: SetupParams = {} ) => {
|
||||
const url = `http://woo.local/${
|
||||
params.filterStock ? '?filter_stock_status=' + params.filterStock : ''
|
||||
}`;
|
||||
setWindowUrl( { url } );
|
||||
|
||||
const attributes: Attributes = {
|
||||
displayStyle: params.displayStyle || 'list',
|
||||
selectType: params.selectType || 'single',
|
||||
showCounts: params.showCounts !== undefined ? params.showCounts : true,
|
||||
showFilterButton:
|
||||
params.showFilterButton !== undefined
|
||||
? params.showFilterButton
|
||||
: true,
|
||||
isPreview: false,
|
||||
heading: '',
|
||||
headingLevel: 3,
|
||||
};
|
||||
|
||||
const { container, ...utils } = render(
|
||||
<Block attributes={ attributes } />
|
||||
);
|
||||
|
||||
const getList = () => container.querySelector( selectors.list );
|
||||
const getDropdown = () => screen.queryByRole( 'combobox' );
|
||||
|
||||
const getChipsContainers = () =>
|
||||
container.querySelectorAll( selectors.chipsContainer );
|
||||
const getSuggestionsContainer = () =>
|
||||
container.querySelector( selectors.suggestionsContainer );
|
||||
|
||||
const getChips = ( value: string ) => {
|
||||
const chipsContainers = getChipsContainers();
|
||||
const chips = Array.from( chipsContainers ).find( ( chipsContainer ) =>
|
||||
chipsContainer
|
||||
? within( chipsContainer ).queryByText( value, {
|
||||
exact: false,
|
||||
ignore: '.components-visually-hidden',
|
||||
} )
|
||||
: false
|
||||
);
|
||||
|
||||
return chips || null;
|
||||
};
|
||||
const getSuggestion = ( value: string ) => {
|
||||
const suggestionsContainer = getSuggestionsContainer();
|
||||
if ( suggestionsContainer ) {
|
||||
return within( suggestionsContainer ).queryByText( value, {
|
||||
exact: false,
|
||||
} );
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const getCheckbox = ( value: string ) => {
|
||||
const checkboxesContainer = getList();
|
||||
const checkboxes = checkboxesContainer
|
||||
? checkboxesContainer.querySelectorAll( 'input' )
|
||||
: [];
|
||||
|
||||
const checkbox = Array.from( checkboxes ).find(
|
||||
( input ) => input.id === value
|
||||
);
|
||||
|
||||
return checkbox;
|
||||
};
|
||||
|
||||
const getRemoveButtonFromChips = ( chips: HTMLElement | null ) =>
|
||||
chips ? within( chips ).getByLabelText( 'Remove stock filter.' ) : null;
|
||||
|
||||
const inStockLabel = 'In stock';
|
||||
const outOfStockLabel = 'Out of stock';
|
||||
const onBackstockLabel = 'On backorder';
|
||||
const inStockId = 'instock';
|
||||
const outOfStockId = 'outofstock';
|
||||
const onBackstoreId = 'onbackorder';
|
||||
|
||||
const getInStockChips = () => getChips( inStockLabel );
|
||||
const getOutOfStockChips = () => getChips( outOfStockLabel );
|
||||
const getOnBackorderChips = () => getChips( onBackstockLabel );
|
||||
|
||||
const getInStockSuggestion = () => getSuggestion( inStockLabel );
|
||||
const getOutOfStockSuggestion = () => getSuggestion( outOfStockLabel );
|
||||
const getOnBackorderSuggestion = () => getSuggestion( onBackstockLabel );
|
||||
|
||||
const getInStockCheckbox = () => getCheckbox( inStockId );
|
||||
const getOutOfStockCheckbox = () => getCheckbox( outOfStockId );
|
||||
const getOnBackorderCheckbox = () => getCheckbox( onBackstoreId );
|
||||
|
||||
return {
|
||||
...utils,
|
||||
container,
|
||||
getDropdown,
|
||||
getList,
|
||||
getInStockChips,
|
||||
getOutOfStockChips,
|
||||
getOnBackorderChips,
|
||||
getInStockSuggestion,
|
||||
getOutOfStockSuggestion,
|
||||
getOnBackorderSuggestion,
|
||||
getInStockCheckbox,
|
||||
getOutOfStockCheckbox,
|
||||
getOnBackorderCheckbox,
|
||||
getRemoveButtonFromChips,
|
||||
};
|
||||
};
|
||||
|
||||
interface SetupParams {
|
||||
filterStock?: string;
|
||||
displayStyle?: DisplayStyle;
|
||||
selectType?: SelectType;
|
||||
}
|
||||
|
||||
const setupSingleChoiceList = ( filterStock = 'instock' ) =>
|
||||
setup( {
|
||||
filterStock,
|
||||
displayStyle: 'list',
|
||||
selectType: 'single',
|
||||
} );
|
||||
|
||||
const setupMultipleChoiceList = ( filterStock = 'instock' ) =>
|
||||
setup( {
|
||||
filterStock,
|
||||
displayStyle: 'list',
|
||||
selectType: 'multiple',
|
||||
} );
|
||||
|
||||
const setupSingleChoiceDropdown = ( filterStock = 'instock' ) =>
|
||||
setup( {
|
||||
filterStock,
|
||||
displayStyle: 'dropdown',
|
||||
selectType: 'single',
|
||||
} );
|
||||
|
||||
const setupMultipleChoiceDropdown = ( filterStock = 'instock' ) =>
|
||||
setup( {
|
||||
filterStock,
|
||||
displayStyle: 'dropdown',
|
||||
selectType: 'multiple',
|
||||
} );
|
||||
|
||||
describe( 'Filter by Stock block', () => {
|
||||
beforeEach( () => {
|
||||
allSettings.stockStatusOptions = {
|
||||
instock: 'In stock',
|
||||
outofstock: 'Out of stock',
|
||||
onbackorder: 'On backorder',
|
||||
};
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
fetchMock.resetMocks();
|
||||
} );
|
||||
|
||||
it( 'renders the stock filter block', async () => {
|
||||
const { container } = setup( {
|
||||
showFilterButton: false,
|
||||
showCounts: false,
|
||||
} );
|
||||
expect( container ).toMatchSnapshot();
|
||||
} );
|
||||
|
||||
it( 'renders the stock filter block with the filter button', async () => {
|
||||
const { container } = setup( {
|
||||
showFilterButton: true,
|
||||
showCounts: false,
|
||||
} );
|
||||
expect( container ).toMatchSnapshot();
|
||||
} );
|
||||
|
||||
it( 'renders the stock filter block with the product counts', async () => {
|
||||
const { container } = setup( {
|
||||
showFilterButton: false,
|
||||
showCounts: true,
|
||||
} );
|
||||
expect( container ).toMatchSnapshot();
|
||||
} );
|
||||
|
||||
describe( 'Single choice Dropdown', () => {
|
||||
test( 'renders dropdown', () => {
|
||||
const { getDropdown, getList } = setupSingleChoiceDropdown();
|
||||
expect( getDropdown() ).toBeInTheDocument();
|
||||
expect( getList() ).toBeNull();
|
||||
} );
|
||||
|
||||
test( 'renders chips based on URL params', () => {
|
||||
const ratingParam = 'instock';
|
||||
const { getInStockChips, getOutOfStockChips, getOnBackorderChips } =
|
||||
setupSingleChoiceDropdown( ratingParam );
|
||||
|
||||
expect( getInStockChips() ).toBeInTheDocument();
|
||||
expect( getOutOfStockChips() ).toBeNull();
|
||||
expect( getOnBackorderChips() ).toBeNull();
|
||||
} );
|
||||
|
||||
test( 'replaces chosen option when another one is clicked', () => {
|
||||
const ratingParam = 'instock';
|
||||
const {
|
||||
getDropdown,
|
||||
getInStockChips,
|
||||
getOutOfStockChips,
|
||||
getOutOfStockSuggestion,
|
||||
} = setupSingleChoiceDropdown( ratingParam );
|
||||
|
||||
expect( getInStockChips() ).toBeInTheDocument();
|
||||
expect( getOutOfStockChips() ).toBeNull();
|
||||
|
||||
const dropdown = getDropdown();
|
||||
|
||||
if ( dropdown ) {
|
||||
userEvent.click( dropdown );
|
||||
}
|
||||
|
||||
const outOfStockSuggestion = getOutOfStockSuggestion();
|
||||
|
||||
if ( outOfStockSuggestion ) {
|
||||
userEvent.click( outOfStockSuggestion );
|
||||
}
|
||||
|
||||
expect( getInStockChips() ).toBeNull();
|
||||
expect( getOutOfStockChips() ).toBeInTheDocument();
|
||||
} );
|
||||
|
||||
test( 'removes the option when the X button is clicked', () => {
|
||||
const ratingParam = 'outofstock';
|
||||
const {
|
||||
getInStockChips,
|
||||
getOutOfStockChips,
|
||||
getOnBackorderChips,
|
||||
getRemoveButtonFromChips,
|
||||
} = setupMultipleChoiceDropdown( ratingParam );
|
||||
|
||||
expect( getInStockChips() ).toBeNull();
|
||||
expect( getOutOfStockChips() ).toBeInTheDocument();
|
||||
expect( getOnBackorderChips() ).toBeNull();
|
||||
|
||||
const removeOutOfStockButton = getRemoveButtonFromChips(
|
||||
getOutOfStockChips()
|
||||
);
|
||||
|
||||
if ( removeOutOfStockButton ) {
|
||||
userEvent.click( removeOutOfStockButton );
|
||||
}
|
||||
|
||||
expect( getInStockChips() ).toBeNull();
|
||||
expect( getOutOfStockChips() ).toBeNull();
|
||||
expect( getOnBackorderChips() ).toBeNull();
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'Multiple choice Dropdown', () => {
|
||||
test( 'renders dropdown', () => {
|
||||
const { getDropdown, getList } = setupMultipleChoiceDropdown();
|
||||
expect( getDropdown() ).toBeDefined();
|
||||
expect( getList() ).toBeNull();
|
||||
} );
|
||||
|
||||
test( 'renders chips based on URL params', () => {
|
||||
const ratingParam = 'instock,onbackorder';
|
||||
const { getInStockChips, getOutOfStockChips, getOnBackorderChips } =
|
||||
setupMultipleChoiceDropdown( ratingParam );
|
||||
|
||||
expect( getInStockChips() ).toBeInTheDocument();
|
||||
expect( getOutOfStockChips() ).toBeNull();
|
||||
expect( getOnBackorderChips() ).toBeInTheDocument();
|
||||
} );
|
||||
|
||||
test( 'adds chosen option to another one that is clicked', async () => {
|
||||
const ratingParam = 'onbackorder';
|
||||
const {
|
||||
getDropdown,
|
||||
getInStockChips,
|
||||
getOutOfStockChips,
|
||||
getOnBackorderChips,
|
||||
getInStockSuggestion,
|
||||
getOutOfStockSuggestion,
|
||||
} = setupMultipleChoiceDropdown( ratingParam );
|
||||
|
||||
expect( getInStockChips() ).toBeNull();
|
||||
expect( getOutOfStockChips() ).toBeNull();
|
||||
expect( getOnBackorderChips() ).toBeInTheDocument();
|
||||
|
||||
const dropdown = getDropdown();
|
||||
|
||||
if ( dropdown ) {
|
||||
userEvent.click( dropdown );
|
||||
}
|
||||
|
||||
const inStockSuggestion = getInStockSuggestion();
|
||||
|
||||
if ( inStockSuggestion ) {
|
||||
userEvent.click( inStockSuggestion );
|
||||
}
|
||||
|
||||
expect( getInStockChips() ).toBeInTheDocument();
|
||||
expect( getOutOfStockChips() ).toBeNull();
|
||||
expect( getOnBackorderChips() ).toBeInTheDocument();
|
||||
|
||||
const freshDropdown = getDropdown();
|
||||
if ( freshDropdown ) {
|
||||
userEvent.click( freshDropdown );
|
||||
}
|
||||
|
||||
const outOfStockSuggestion = getOutOfStockSuggestion();
|
||||
|
||||
if ( outOfStockSuggestion ) {
|
||||
userEvent.click( outOfStockSuggestion );
|
||||
}
|
||||
|
||||
await waitFor( () => {
|
||||
expect( getInStockChips() ).toBeInTheDocument();
|
||||
expect( getOutOfStockChips() ).toBeInTheDocument();
|
||||
expect( getOnBackorderChips() ).toBeInTheDocument();
|
||||
} );
|
||||
} );
|
||||
|
||||
test( 'removes the option when the X button is clicked', () => {
|
||||
const ratingParam = 'instock,outofstock,onbackorder';
|
||||
const {
|
||||
getInStockChips,
|
||||
getOutOfStockChips,
|
||||
getOnBackorderChips,
|
||||
getRemoveButtonFromChips,
|
||||
} = setupMultipleChoiceDropdown( ratingParam );
|
||||
|
||||
expect( getInStockChips() ).toBeInTheDocument();
|
||||
expect( getOutOfStockChips() ).toBeInTheDocument();
|
||||
expect( getOnBackorderChips() ).toBeInTheDocument();
|
||||
|
||||
const removeOutOfStockButton = getRemoveButtonFromChips(
|
||||
getOutOfStockChips()
|
||||
);
|
||||
|
||||
if ( removeOutOfStockButton ) {
|
||||
userEvent.click( removeOutOfStockButton );
|
||||
}
|
||||
|
||||
expect( getInStockChips() ).toBeInTheDocument();
|
||||
expect( getOutOfStockChips() ).toBeNull();
|
||||
expect( getOnBackorderChips() ).toBeInTheDocument();
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'Single choice List', () => {
|
||||
test( 'renders list', () => {
|
||||
const { getDropdown, getList } = setupSingleChoiceList();
|
||||
expect( getDropdown() ).toBeNull();
|
||||
expect( getList() ).toBeInTheDocument();
|
||||
} );
|
||||
|
||||
test( 'renders checked options based on URL params', () => {
|
||||
const ratingParam = 'instock';
|
||||
const {
|
||||
getInStockCheckbox,
|
||||
getOutOfStockCheckbox,
|
||||
getOnBackorderCheckbox,
|
||||
} = setupSingleChoiceList( ratingParam );
|
||||
|
||||
expect( getInStockCheckbox()?.checked ).toBeTruthy();
|
||||
expect( getOutOfStockCheckbox()?.checked ).toBeFalsy();
|
||||
expect( getOnBackorderCheckbox()?.checked ).toBeFalsy();
|
||||
} );
|
||||
|
||||
test( 'replaces chosen option when another one is clicked', async () => {
|
||||
const ratingParam = 'outofstock';
|
||||
const {
|
||||
getInStockCheckbox,
|
||||
getOutOfStockCheckbox,
|
||||
getOnBackorderCheckbox,
|
||||
} = setupSingleChoiceList( ratingParam );
|
||||
|
||||
expect( getInStockCheckbox()?.checked ).toBeFalsy();
|
||||
expect( getOutOfStockCheckbox()?.checked ).toBeTruthy();
|
||||
expect( getOnBackorderCheckbox()?.checked ).toBeFalsy();
|
||||
|
||||
const onBackorderCheckbox = getOnBackorderCheckbox();
|
||||
|
||||
if ( onBackorderCheckbox ) {
|
||||
userEvent.click( onBackorderCheckbox );
|
||||
}
|
||||
|
||||
expect( getInStockCheckbox()?.checked ).toBeFalsy();
|
||||
expect( getOutOfStockCheckbox()?.checked ).toBeFalsy();
|
||||
expect( getOnBackorderCheckbox()?.checked ).toBeTruthy();
|
||||
} );
|
||||
|
||||
test( 'removes the option when it is clicked again', async () => {
|
||||
const ratingParam = 'onbackorder';
|
||||
const {
|
||||
getInStockCheckbox,
|
||||
getOutOfStockCheckbox,
|
||||
getOnBackorderCheckbox,
|
||||
} = setupMultipleChoiceList( ratingParam );
|
||||
|
||||
expect( getInStockCheckbox()?.checked ).toBeFalsy();
|
||||
expect( getOutOfStockCheckbox()?.checked ).toBeFalsy();
|
||||
expect( getOnBackorderCheckbox()?.checked ).toBeTruthy();
|
||||
|
||||
const onBackorderCheckbox = getOnBackorderCheckbox();
|
||||
|
||||
if ( onBackorderCheckbox ) {
|
||||
userEvent.click( onBackorderCheckbox );
|
||||
}
|
||||
|
||||
await waitFor( () => {
|
||||
expect( getInStockCheckbox()?.checked ).toBeFalsy();
|
||||
expect( getOutOfStockCheckbox()?.checked ).toBeFalsy();
|
||||
expect( getOnBackorderCheckbox()?.checked ).toBeFalsy();
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'Multiple choice List', () => {
|
||||
test( 'renders list', () => {
|
||||
const { getDropdown, getList } = setupMultipleChoiceList();
|
||||
expect( getDropdown() ).toBeNull();
|
||||
expect( getList() ).toBeInTheDocument();
|
||||
} );
|
||||
|
||||
test( 'renders chips based on URL params', () => {
|
||||
const ratingParam = 'instock,onbackorder';
|
||||
const {
|
||||
getInStockCheckbox,
|
||||
getOutOfStockCheckbox,
|
||||
getOnBackorderCheckbox,
|
||||
} = setupMultipleChoiceList( ratingParam );
|
||||
|
||||
expect( getInStockCheckbox()?.checked ).toBeTruthy();
|
||||
expect( getOutOfStockCheckbox()?.checked ).toBeFalsy();
|
||||
expect( getOnBackorderCheckbox()?.checked ).toBeTruthy();
|
||||
} );
|
||||
|
||||
test( 'adds chosen option to another one that is clicked', async () => {
|
||||
const ratingParam = 'outofstock,onbackorder';
|
||||
const {
|
||||
getInStockCheckbox,
|
||||
getOutOfStockCheckbox,
|
||||
getOnBackorderCheckbox,
|
||||
} = setupMultipleChoiceList( ratingParam );
|
||||
|
||||
expect( getInStockCheckbox()?.checked ).toBeFalsy();
|
||||
expect( getOutOfStockCheckbox()?.checked ).toBeTruthy();
|
||||
expect( getOnBackorderCheckbox()?.checked ).toBeTruthy();
|
||||
|
||||
const inStockCheckbox = getInStockCheckbox();
|
||||
|
||||
if ( inStockCheckbox ) {
|
||||
userEvent.click( inStockCheckbox );
|
||||
}
|
||||
|
||||
await waitFor( () => {
|
||||
expect( getInStockCheckbox()?.checked ).toBeTruthy();
|
||||
expect( getOutOfStockCheckbox()?.checked ).toBeTruthy();
|
||||
expect( getOnBackorderCheckbox()?.checked ).toBeTruthy();
|
||||
} );
|
||||
} );
|
||||
|
||||
test( 'removes the option when it is clicked again', async () => {
|
||||
const ratingParam = 'instock,outofstock';
|
||||
const {
|
||||
getInStockCheckbox,
|
||||
getOutOfStockCheckbox,
|
||||
getOnBackorderCheckbox,
|
||||
} = setupMultipleChoiceList( ratingParam );
|
||||
|
||||
expect( getInStockCheckbox()?.checked ).toBeTruthy();
|
||||
expect( getOutOfStockCheckbox()?.checked ).toBeTruthy();
|
||||
expect( getOnBackorderCheckbox()?.checked ).toBeFalsy();
|
||||
|
||||
const inStockCheckbox = getInStockCheckbox();
|
||||
|
||||
if ( inStockCheckbox ) {
|
||||
userEvent.click( inStockCheckbox );
|
||||
}
|
||||
|
||||
await waitFor( () => {
|
||||
expect( getInStockCheckbox()?.checked ).toBeFalsy();
|
||||
expect( getOutOfStockCheckbox()?.checked ).toBeTruthy();
|
||||
expect( getOnBackorderCheckbox()?.checked ).toBeFalsy();
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
|
@ -5,10 +5,18 @@ export interface Attributes {
|
|||
showCounts: boolean;
|
||||
showFilterButton: boolean;
|
||||
isPreview?: boolean;
|
||||
displayStyle: string;
|
||||
selectType: string;
|
||||
}
|
||||
|
||||
export interface DisplayOption {
|
||||
value: string;
|
||||
name: string;
|
||||
label: JSX.Element;
|
||||
textLabel: string;
|
||||
}
|
||||
|
||||
export type Current = {
|
||||
slug: string;
|
||||
name: string;
|
||||
};
|
||||
|
|
|
@ -28,6 +28,18 @@ export const getActiveFilters = (
|
|||
);
|
||||
};
|
||||
|
||||
export function generateUniqueId() {
|
||||
return Math.floor( Math.random() * Date.now() );
|
||||
}
|
||||
|
||||
export const formatSlug = ( slug: string ) =>
|
||||
slug
|
||||
.trim()
|
||||
.replace( /\s/g, '' )
|
||||
.replace( /_/g, '-' )
|
||||
.replace( /-+/g, '-' )
|
||||
.replace( /[^a-zA-Z0-9-]/g, '' );
|
||||
|
||||
export const parseAttributes = ( data: Record< string, unknown > ) => {
|
||||
return {
|
||||
heading: isString( data?.heading ) ? data.heading : '',
|
||||
|
@ -38,5 +50,11 @@ export const parseAttributes = ( data: Record< string, unknown > ) => {
|
|||
showFilterButton: data?.showFilterButton === 'true',
|
||||
showCounts: data?.showCounts !== 'false',
|
||||
isPreview: false,
|
||||
displayStyle:
|
||||
( isString( data?.displayStyle ) && data.displayStyle ) ||
|
||||
metadata.attributes.displayStyle.default,
|
||||
selectType:
|
||||
( isString( data?.selectType ) && data.selectType ) ||
|
||||
metadata.attributes.selectType.default,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -49,5 +49,22 @@ describe( `${ block.name } Block`, () => {
|
|||
`${ block.class } .wc-block-filter-submit-button`
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'allows changing the Display Style', async () => {
|
||||
// Turn the display style to Dropdown
|
||||
await expect( page ).toClick( 'button', { text: 'Dropdown' } );
|
||||
|
||||
await expect( page ).toMatchElement(
|
||||
'.wc-block-stock-filter.style-dropdown'
|
||||
);
|
||||
// Turn the display style to List
|
||||
await expect( page ).toClick( 'button', {
|
||||
text: 'List',
|
||||
} );
|
||||
|
||||
await expect( page ).toMatchElement(
|
||||
'.wc-block-stock-filter.style-list'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
|
Loading…
Reference in New Issue