[Experimental] Refactor the interactivity dropdown to remove dependence on FormTokenField (#43183)
* Also fix https://github.com/woocommerce/woocommerce/issues/43154
This commit is contained in:
parent
e43eb1dcab
commit
5de0f4a274
|
@ -1,36 +1,28 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import styled from '@emotion/styled';
|
||||
import FormTokenField from '@woocommerce/base-components/form-token-field';
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { Icon, chevronDown } from '@wordpress/icons';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { PreviewDropdown } from '../../components/preview-dropdown';
|
||||
|
||||
type Props = {
|
||||
label: string;
|
||||
textColor: string;
|
||||
};
|
||||
|
||||
export const AttributeDropdown = ( { label, textColor }: Props ) => {
|
||||
const StyledFormTokenField = textColor
|
||||
? styled( FormTokenField )`
|
||||
.components-form-token-field__input::placeholder {
|
||||
color: ${ textColor } !important;
|
||||
}
|
||||
`
|
||||
: FormTokenField;
|
||||
|
||||
export const AttributeDropdown = ( { label }: Props ) => {
|
||||
return (
|
||||
<div className="wc-block-attribute-filter style-dropdown">
|
||||
<StyledFormTokenField
|
||||
suggestions={ [] }
|
||||
<PreviewDropdown
|
||||
placeholder={ sprintf(
|
||||
/* translators: %s attribute name. */
|
||||
__( 'Select %s', 'woocommerce' ),
|
||||
label
|
||||
) }
|
||||
onChange={ () => null }
|
||||
value={ [] }
|
||||
/>
|
||||
<Icon icon={ chevronDown } size={ 30 } />
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* Preview dropdown component for collection filter editor blocks that mimics the markup of interactivity dropdown.
|
||||
*/
|
||||
export const PreviewDropdown = ( { placeholder }: { placeholder: string } ) => {
|
||||
return (
|
||||
<div className="wc-interactivity-dropdown">
|
||||
<div className="wc-interactivity-dropdown__dropdown">
|
||||
<div
|
||||
className="wc-interactivity-dropdown__dropdown-selection"
|
||||
tabIndex={ 0 }
|
||||
>
|
||||
<span className="wc-interactivity-dropdown__placeholder">
|
||||
{ placeholder }
|
||||
</span>
|
||||
|
||||
<span className="wc-interactivity-dropdown__svg-container">
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="30"
|
||||
height="30"
|
||||
>
|
||||
<path d="M17.5 11.6L12 16l-5.5-4.4.9-1.2L12 14l4.5-3.6 1 1.2z"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -2,13 +2,10 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Icon, chevronDown } from '@wordpress/icons';
|
||||
import classnames from 'classnames';
|
||||
import { InnerBlocks, useBlockProps } from '@wordpress/block-editor';
|
||||
import type { BlockEditProps, Template } from '@wordpress/blocks';
|
||||
import Rating, {
|
||||
RatingValues,
|
||||
} from '@woocommerce/base-components/product-rating';
|
||||
import Rating from '@woocommerce/base-components/product-rating';
|
||||
import {
|
||||
useQueryStateByKey,
|
||||
useQueryStateByContext,
|
||||
|
@ -18,10 +15,7 @@ import { getSettingWithCoercion } from '@woocommerce/settings';
|
|||
import { isBoolean, isObject, objectHasProp } from '@woocommerce/types';
|
||||
import { useState, useMemo, useEffect } from '@wordpress/element';
|
||||
import { CheckboxList } from '@woocommerce/blocks-components';
|
||||
import FormTokenField from '@woocommerce/base-components/form-token-field';
|
||||
import { Disabled, Notice, withSpokenMessages } from '@wordpress/components';
|
||||
import { useStyleProps } from '@woocommerce/base-hooks';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -29,11 +23,11 @@ import styled from '@emotion/styled';
|
|||
import { previewOptions } from './preview';
|
||||
import './style.scss';
|
||||
import { Attributes } from './types';
|
||||
import { formatSlug, getActiveFilters, generateUniqueId } from './utils';
|
||||
import { getActiveFilters } from './utils';
|
||||
import { useSetWraperVisibility } from '../../../filter-wrapper/context';
|
||||
import './editor.scss';
|
||||
import { Inspector } from '../attribute-filter/components/inspector-controls';
|
||||
import { extractBuiltInColor } from '../../utils';
|
||||
import { PreviewDropdown } from '../components/preview-dropdown';
|
||||
|
||||
const NoRatings = () => (
|
||||
<Notice status="warning" isDismissible={ false }>
|
||||
|
@ -50,21 +44,6 @@ const Edit = ( props: BlockEditProps< Attributes > ) => {
|
|||
const { className } = props.attributes;
|
||||
const blockAttributes = props.attributes;
|
||||
|
||||
const { className: styleClass, style } = useStyleProps( props.attributes );
|
||||
const builtInColor = extractBuiltInColor( styleClass );
|
||||
|
||||
const textColor = builtInColor
|
||||
? `var(--wp--preset--color--${ builtInColor })`
|
||||
: style.color;
|
||||
|
||||
const StyledFormTokenField = textColor
|
||||
? styled( FormTokenField )`
|
||||
.components-form-token-field__input::placeholder {
|
||||
color: ${ textColor } !important;
|
||||
}
|
||||
`
|
||||
: FormTokenField;
|
||||
|
||||
const blockProps = useBlockProps( {
|
||||
className: classnames( 'wc-block-rating-filter', className ),
|
||||
} );
|
||||
|
@ -111,21 +90,9 @@ const Edit = ( props: BlockEditProps< Attributes > ) => {
|
|||
initialFilters
|
||||
);
|
||||
|
||||
/*
|
||||
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 [ displayNoProductRatingsNotice, setDisplayNoProductRatingsNotice ] =
|
||||
useState( false );
|
||||
|
||||
const multiple = blockAttributes.selectType !== 'single';
|
||||
|
||||
const showChevron = multiple
|
||||
? ! isLoading && checked.length < displayedOptions.length
|
||||
: ! isLoading && checked.length === 0;
|
||||
|
||||
/**
|
||||
* Compare intersection of all ratings and filtered counts to get a list of options to display.
|
||||
*/
|
||||
|
@ -173,7 +140,6 @@ const Edit = ( props: BlockEditProps< Attributes > ) => {
|
|||
} );
|
||||
|
||||
setDisplayedOptions( newOptions );
|
||||
setRemountKey( generateUniqueId() );
|
||||
}, [
|
||||
blockAttributes.showCounts,
|
||||
blockAttributes.isPreview,
|
||||
|
@ -221,107 +187,19 @@ const Edit = ( props: BlockEditProps< Attributes > ) => {
|
|||
>
|
||||
{ blockAttributes.displayStyle === 'dropdown' ? (
|
||||
<>
|
||||
<StyledFormTokenField
|
||||
key={ remountKey }
|
||||
className={ classnames( {
|
||||
'single-selection': ! multiple,
|
||||
'is-loading': isLoading,
|
||||
} ) }
|
||||
style={ {
|
||||
borderStyle: 'none',
|
||||
} }
|
||||
suggestions={ displayedOptions
|
||||
.filter(
|
||||
( option ) =>
|
||||
! checked.includes(
|
||||
option.value
|
||||
)
|
||||
)
|
||||
.map( ( option ) => option.value ) }
|
||||
disabled={ isLoading }
|
||||
placeholder={ __(
|
||||
'Select Rating',
|
||||
'woocommerce'
|
||||
) }
|
||||
onChange={ () => {
|
||||
// noop
|
||||
} }
|
||||
value={ checked }
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore - FormTokenField doesn't accept custom components, forcing it here to display component
|
||||
displayTransform={ ( value ) => {
|
||||
const resultWithZeroCount = {
|
||||
value,
|
||||
label: (
|
||||
<Rating
|
||||
key={
|
||||
Number(
|
||||
value
|
||||
) as RatingValues
|
||||
}
|
||||
rating={
|
||||
Number(
|
||||
value
|
||||
) as RatingValues
|
||||
}
|
||||
ratedProductsCount={ 0 }
|
||||
/>
|
||||
),
|
||||
};
|
||||
const resultWithNonZeroCount =
|
||||
displayedOptions.find(
|
||||
( option ) =>
|
||||
option.value === value
|
||||
);
|
||||
|
||||
const displayedResult =
|
||||
resultWithNonZeroCount ||
|
||||
resultWithZeroCount;
|
||||
|
||||
const { label, value: rawValue } =
|
||||
displayedResult;
|
||||
|
||||
// A label - JSX component - is extended with faked string methods to allow using JSX element as an option in FormTokenField
|
||||
const extendedLabel = Object.assign(
|
||||
{},
|
||||
label,
|
||||
{
|
||||
toLocaleLowerCase: () =>
|
||||
rawValue,
|
||||
substring: (
|
||||
start: number,
|
||||
end: number
|
||||
) =>
|
||||
start === 0 && end === 1
|
||||
? label
|
||||
: '',
|
||||
}
|
||||
);
|
||||
return extendedLabel;
|
||||
} }
|
||||
saveTransform={ formatSlug }
|
||||
messages={ {
|
||||
added: __(
|
||||
'Rating filter added.',
|
||||
'woocommerce'
|
||||
),
|
||||
removed: __(
|
||||
'Rating filter removed.',
|
||||
'woocommerce'
|
||||
),
|
||||
remove: __(
|
||||
'Remove rating filter.',
|
||||
'woocommerce'
|
||||
),
|
||||
__experimentalInvalid: __(
|
||||
'Invalid rating filter.',
|
||||
'woocommerce'
|
||||
),
|
||||
} }
|
||||
<PreviewDropdown
|
||||
placeholder={
|
||||
blockAttributes.selectType === 'single'
|
||||
? __(
|
||||
'Select a rating',
|
||||
'woocommerce'
|
||||
)
|
||||
: __(
|
||||
'Select ratings',
|
||||
'woocommerce'
|
||||
)
|
||||
}
|
||||
/>
|
||||
{ showChevron && (
|
||||
<Icon icon={ chevronDown } size={ 30 } />
|
||||
) }
|
||||
</>
|
||||
) : (
|
||||
<CheckboxList
|
||||
|
|
|
@ -37,7 +37,7 @@ export const Inspector = ( { attributes, setAttributes }: EditProps ) => {
|
|||
'Allow selecting multiple options?',
|
||||
'woocommerce'
|
||||
) }
|
||||
value={ selectType || 'multiple' }
|
||||
value={ selectType || 'single' }
|
||||
onChange={ ( value: string ) =>
|
||||
setAttributes( {
|
||||
selectType: value,
|
||||
|
|
|
@ -6,27 +6,20 @@ import classnames from 'classnames';
|
|||
import { InnerBlocks, useBlockProps } from '@wordpress/block-editor';
|
||||
import { Disabled } from '@wordpress/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Icon, chevronDown } from '@wordpress/icons';
|
||||
import { CheckboxList } from '@woocommerce/blocks-components';
|
||||
import Label from '@woocommerce/base-components/filter-element-label';
|
||||
import FormTokenField from '@woocommerce/base-components/form-token-field';
|
||||
import type { BlockEditProps, Template } from '@wordpress/blocks';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
import { useCollectionData } from '@woocommerce/base-context/hooks';
|
||||
import { useStyleProps } from '@woocommerce/base-hooks';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { BlockProps } from './types';
|
||||
import { Inspector } from './components/inspector';
|
||||
import { extractBuiltInColor } from '../../utils';
|
||||
import { PreviewDropdown } from '../components/preview-dropdown';
|
||||
|
||||
type CollectionData = {
|
||||
// attribute_counts: null | unknown;
|
||||
// price_range: null | unknown;
|
||||
// rating_counts: null | unknown;
|
||||
stock_status_counts: StockStatusCount[];
|
||||
};
|
||||
|
||||
|
@ -53,21 +46,6 @@ const Edit = ( props: BlockEditProps< BlockProps > ) => {
|
|||
],
|
||||
];
|
||||
|
||||
const { className, style } = useStyleProps( props.attributes );
|
||||
const builtInColor = extractBuiltInColor( className );
|
||||
|
||||
const textColor = builtInColor
|
||||
? `var(--wp--preset--color--${ builtInColor })`
|
||||
: style.color;
|
||||
|
||||
const StyledFormTokenField = textColor
|
||||
? styled( FormTokenField )`
|
||||
.components-form-token-field__input::placeholder {
|
||||
color: ${ textColor } !important;
|
||||
}
|
||||
`
|
||||
: FormTokenField;
|
||||
|
||||
const { showCounts, displayStyle } = props.attributes;
|
||||
const stockStatusOptions: Record< string, string > = getSetting(
|
||||
'stockStatusOptions',
|
||||
|
@ -121,27 +99,29 @@ const Edit = ( props: BlockEditProps< BlockProps > ) => {
|
|||
>
|
||||
{ displayStyle === 'dropdown' ? (
|
||||
<>
|
||||
<StyledFormTokenField
|
||||
className={ classnames( {
|
||||
'single-selection': true,
|
||||
'is-loading': false,
|
||||
} ) }
|
||||
suggestions={ [] }
|
||||
placeholder={ __(
|
||||
'Select stock status',
|
||||
'woocommerce'
|
||||
) }
|
||||
onChange={ () => null }
|
||||
value={ [] }
|
||||
<PreviewDropdown
|
||||
placeholder={
|
||||
props.attributes.selectType ===
|
||||
'single'
|
||||
? __(
|
||||
'Select stock status',
|
||||
'woocommerce'
|
||||
)
|
||||
: __(
|
||||
'Select stock statuses',
|
||||
'woocommerce'
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Icon icon={ chevronDown } size={ 30 } />
|
||||
</>
|
||||
) : (
|
||||
<CheckboxList
|
||||
className={ 'wc-block-stock-filter-list' }
|
||||
options={ listOptions }
|
||||
checked={ [] }
|
||||
onChange={ () => null }
|
||||
onChange={ () => {
|
||||
// noop
|
||||
} }
|
||||
isLoading={ false }
|
||||
isDisabled={ true }
|
||||
/>
|
||||
|
|
|
@ -26,7 +26,11 @@ store( 'woocommerce/collection-stock-filter', {
|
|||
'woocommerce/interactivity-dropdown'
|
||||
);
|
||||
|
||||
navigate( getUrl( context.selectedItem.value || '' ) );
|
||||
const selectedItems = context.selectedItems;
|
||||
const items = selectedItems || [];
|
||||
const filters = items.map( ( i ) => i.value );
|
||||
|
||||
navigate( getUrl( filters.join( ',' ) ) );
|
||||
},
|
||||
updateProducts: ( event: HTMLElementEvent< HTMLInputElement > ) => {
|
||||
// get the active filters from the url:
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { getContext, store } from '@woocommerce/interactivity';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -47,12 +48,12 @@ store< DropdownStore >( 'woocommerce/interactivity-dropdown', {
|
|||
if ( selectType === 'single' ) {
|
||||
return selectedItems?.length && selectedItems[ 0 ].label
|
||||
? selectedItems[ 0 ]?.label
|
||||
: 'Select an option';
|
||||
: __( 'Select an option', 'woocommerce' );
|
||||
} else if (
|
||||
selectType === 'multiple' &&
|
||||
selectedItems.length === 0
|
||||
) {
|
||||
return 'Select options';
|
||||
return __( 'Select options', 'woocommerce' );
|
||||
}
|
||||
|
||||
return '';
|
||||
|
@ -69,7 +70,6 @@ store< DropdownStore >( 'woocommerce/interactivity-dropdown', {
|
|||
actions: {
|
||||
toggleIsOpen: () => {
|
||||
const context = getContext< DropdownContext >();
|
||||
|
||||
context.isOpen = ! context.isOpen;
|
||||
},
|
||||
unselectDropdownItem: ( event: MouseEvent ) => {
|
||||
|
|
|
@ -1,145 +1,91 @@
|
|||
@import "../../../assets/js/blocks/shared/styles/style";
|
||||
|
||||
.wc-interactivity-dropdown {
|
||||
@include includeFormTokenFieldFix();
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: $gap;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
gap: 16px;
|
||||
|
||||
.wc-block-components-filter-submit-button {
|
||||
height: 36px;
|
||||
line-height: 1;
|
||||
}
|
||||
color: inherit;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
font-style: inherit;
|
||||
font-weight: inherit;
|
||||
letter-spacing: inherit;
|
||||
line-height: inherit;
|
||||
text-decoration: inherit;
|
||||
text-transform: inherit;
|
||||
|
||||
> svg {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.wc-interactivity-dropdown__selected-badge {
|
||||
@include font-size(smaller);
|
||||
padding: 0.25em 0.25em 0.25em 0.75em;
|
||||
margin: 2px 0;
|
||||
border-radius: em(20px);
|
||||
border: 1px solid $black;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: em(4px);
|
||||
|
||||
.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;
|
||||
.wc-interactivity-dropdown__badge-text {
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
|
||||
&.is-loading {
|
||||
.wc-interactivity-dropdown__badge-remove {
|
||||
background-color: $gray-200;
|
||||
cursor: pointer;
|
||||
border-radius: em(20px);
|
||||
}
|
||||
}
|
||||
|
||||
.wc-interactivity-dropdown__dropdown {
|
||||
.wc-interactivity-dropdown__dropdown-selection {
|
||||
display: flex;
|
||||
padding: 4px 30px 4px 8px;
|
||||
border: 1px solid $gray-700;
|
||||
border-radius: em(4px);
|
||||
}
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
|
||||
.components-form-token-field {
|
||||
border-radius: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.wc-blocks-components-form-token-field-wrapper .components-form-token-field__input-container {
|
||||
@include reset-color();
|
||||
@include reset-typography();
|
||||
border: 0;
|
||||
padding: $gap-smaller;
|
||||
border-radius: inherit;
|
||||
|
||||
.components-form-token-field__input {
|
||||
@include font-size(small);
|
||||
|
||||
&::placeholder {
|
||||
color: $black;
|
||||
.wc-interactivity-dropdown__placeholder {
|
||||
@include font-size(smaller);
|
||||
margin: 0.25em 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.components-form-token-field__suggestions-list {
|
||||
border: 1px solid $gray-700;
|
||||
border-radius: 4px;
|
||||
margin-top: $gap-smaller;
|
||||
max-height: 21em;
|
||||
.wc-interactivity-dropdown__svg-container {
|
||||
> svg {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.components-form-token-field__suggestion {
|
||||
color: $black;
|
||||
border: 1px solid $gray-400;
|
||||
border-radius: 4px;
|
||||
.wc-interactivity-dropdown__dropdown-list {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
background: $white;
|
||||
border: 1px solid $gray-700;
|
||||
border-radius: em(4px);
|
||||
z-index: 1000;
|
||||
margin-top: $gap-smaller;
|
||||
box-sizing: border-box;
|
||||
|
||||
.wc-interactivity-dropdown__dropdown-option {
|
||||
margin: $gap-small;
|
||||
padding: $gap-small;
|
||||
|
||||
&.is-selected,
|
||||
&:hover {
|
||||
background: $gray-100;
|
||||
color: $gray-800;
|
||||
border-radius: em(4px);
|
||||
border: 1px solid $gray-400;
|
||||
@include font-size(small);
|
||||
cursor: pointer;
|
||||
&:hover,
|
||||
&:focus,
|
||||
&.is-selected {
|
||||
background-color: $gray-100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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 {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: $gap;
|
||||
justify-content: flex-end;
|
||||
margin-top: $gap;
|
||||
|
||||
// The specificity here is needed to overwrite the margin-top that is inherited on WC block template pages such as Shop.
|
||||
button[type="submit"]:not(.wp-block-search__button).wc-block-components-filter-submit-button {
|
||||
margin-left: 0;
|
||||
margin-top: 0;
|
||||
@include font-size(small);
|
||||
}
|
||||
|
||||
.wc-block-stock-filter__button {
|
||||
margin-top: em($gap-smaller);
|
||||
padding: em($gap-smaller) em($gap);
|
||||
@include font-size(small);
|
||||
}
|
||||
}
|
||||
|
||||
.editor-styles-wrapper .wc-block-stock-filter .wc-block-stock-filter__button {
|
||||
margin-top: em($gap-smaller);
|
||||
padding: em($gap-smaller) em($gap);
|
||||
@include font-size(small);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: add
|
||||
|
||||
[Experimental] Refactor interactivity dropdown to remove FormTokenField. Also fix multi select for rating filter.
|
|
@ -67,8 +67,8 @@ final class AssetsController {
|
|||
// Register the interactivity components here for now.
|
||||
$this->api->register_script( 'wc-interactivity-dropdown', 'assets/client/blocks/wc-interactivity-dropdown.js', array() );
|
||||
$this->api->register_script( 'wc-interactivity-checkbox-list', 'assets/client/blocks/wc-interactivity-checkbox-list.js', array() );
|
||||
$this->register_style( 'wc-interactivity-checkbox-list', plugins_url( $this->api->get_block_asset_build_path( 'wc-interactivity-checkbox-list', 'css' ), __DIR__ ), array(), 'all', true );
|
||||
$this->register_style( 'wc-interactivity-dropdown', plugins_url( $this->api->get_block_asset_build_path( 'wc-interactivity-dropdown', 'css' ), __DIR__ ), array(), 'all', true );
|
||||
$this->register_style( 'wc-interactivity-checkbox-list', plugins_url( $this->api->get_block_asset_build_path( 'wc-interactivity-checkbox-list', 'css' ), dirname( __DIR__ ) ), array(), 'all', true );
|
||||
$this->register_style( 'wc-interactivity-dropdown', plugins_url( $this->api->get_block_asset_build_path( 'wc-interactivity-dropdown', 'css' ), dirname( __DIR__ ) ), array(), 'all', true );
|
||||
|
||||
wp_add_inline_script(
|
||||
'wc-blocks-middleware',
|
||||
|
|
|
@ -211,9 +211,6 @@ final class CollectionAttributeFilter extends AbstractBlock {
|
|||
return '';
|
||||
}
|
||||
|
||||
$text_color_class_and_style = StyleAttributesUtils::get_text_color_class_and_style( $attributes );
|
||||
$text_color = $text_color_class_and_style['value'] ?? '';
|
||||
|
||||
$list_items = array();
|
||||
$selected_item = array();
|
||||
|
||||
|
@ -232,10 +229,9 @@ final class CollectionAttributeFilter extends AbstractBlock {
|
|||
|
||||
return Dropdown::render(
|
||||
array(
|
||||
'items' => $list_items,
|
||||
'action' => 'woocommerce/collection-attribute-filter::actions.navigate',
|
||||
'selected_item' => $selected_item,
|
||||
'text_color' => $text_color,
|
||||
'items' => $list_items,
|
||||
'action' => 'woocommerce/collection-attribute-filter::actions.navigate',
|
||||
'selected_items' => array( $selected_item ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -111,9 +111,6 @@ final class CollectionRatingFilter extends AbstractBlock {
|
|||
return '';
|
||||
}
|
||||
|
||||
$text_color_class_and_style = StyleAttributesUtils::get_text_color_class_and_style( $attributes );
|
||||
$text_color = $text_color_class_and_style['value'] ?? '';
|
||||
|
||||
$rating_counts = $block->context['collectionData']['rating_counts'] ?? array();
|
||||
$display_style = $attributes['displayStyle'] ?? 'list';
|
||||
$show_counts = $attributes['showCounts'] ?? false;
|
||||
|
@ -141,7 +138,7 @@ final class CollectionRatingFilter extends AbstractBlock {
|
|||
'on_change' => 'woocommerce/collection-rating-filter::actions.onCheckboxChange',
|
||||
)
|
||||
) : Dropdown::render(
|
||||
$this->get_dropdown_props( $rating_counts, $selected_ratings_query_param, $show_counts, $attributes['selectType'], $text_color )
|
||||
$this->get_dropdown_props( $rating_counts, $selected_ratings_query_param, $show_counts, $attributes['selectType'] )
|
||||
);
|
||||
|
||||
return sprintf(
|
||||
|
@ -222,10 +219,9 @@ final class CollectionRatingFilter extends AbstractBlock {
|
|||
* @param mixed $selected_ratings_query The url query param for selected ratings.
|
||||
* @param bool $show_counts Whether to show the counts.
|
||||
* @param string $select_type The select type. (single|multiple).
|
||||
* @param string $text_color The text color.
|
||||
* @return array<array-key, array>
|
||||
*/
|
||||
private function get_dropdown_props( $rating_counts, $selected_ratings_query, $show_counts, $select_type, $text_color ) {
|
||||
private function get_dropdown_props( $rating_counts, $selected_ratings_query, $show_counts, $select_type ) {
|
||||
$ratings_array = explode( ',', $selected_ratings_query );
|
||||
|
||||
$selected_items = array_reduce(
|
||||
|
@ -263,7 +259,6 @@ final class CollectionRatingFilter extends AbstractBlock {
|
|||
'select_type' => $select_type,
|
||||
'selected_items' => $selected_items,
|
||||
'action' => 'woocommerce/collection-rating-filter::actions.onDropdownChange',
|
||||
'text_color' => $text_color,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,14 +148,13 @@ final class CollectionStockFilter extends AbstractBlock {
|
|||
private function get_stock_filter_html( $stock_counts, $attributes ) {
|
||||
$display_style = $attributes['displayStyle'] ?? 'list';
|
||||
$show_counts = $attributes['showCounts'] ?? false;
|
||||
$select_type = $attributes['selectType'] ?? 'single';
|
||||
$stock_statuses = wc_get_product_stock_status_options();
|
||||
|
||||
$text_color_class_and_style = StyleAttributesUtils::get_text_color_class_and_style( $attributes );
|
||||
$text_color = $text_color_class_and_style['value'] ?? '';
|
||||
|
||||
// check the url params to select initial item on page load.
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
|
||||
$selected_stock_status = isset( $_GET[ self::STOCK_STATUS_QUERY_VAR ] ) ? sanitize_text_field( wp_unslash( $_GET[ self::STOCK_STATUS_QUERY_VAR ] ) ) : '';
|
||||
$query = isset( $_GET[ self::STOCK_STATUS_QUERY_VAR ] ) ? sanitize_text_field( wp_unslash( $_GET[ self::STOCK_STATUS_QUERY_VAR ] ) ) : '';
|
||||
$selected_stock_statuses = explode( ',', $query );
|
||||
|
||||
$list_items = array_map(
|
||||
function( $item ) use ( $stock_statuses, $show_counts ) {
|
||||
|
@ -171,18 +170,12 @@ final class CollectionStockFilter extends AbstractBlock {
|
|||
$selected_items = array_values(
|
||||
array_filter(
|
||||
$list_items,
|
||||
function( $item ) use ( $selected_stock_status ) {
|
||||
return $item['value'] === $selected_stock_status;
|
||||
function( $item ) use ( $selected_stock_statuses ) {
|
||||
return in_array( $item['value'], $selected_stock_statuses, true );
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Just for the dropdown, we can only select 1 item.
|
||||
$selected_item = $selected_items[0] ?? array(
|
||||
'label' => null,
|
||||
'value' => null,
|
||||
);
|
||||
|
||||
$data_directive = wp_json_encode( array( 'namespace' => 'woocommerce/collection-stock-filter' ) );
|
||||
|
||||
ob_start();
|
||||
|
@ -203,7 +196,7 @@ final class CollectionStockFilter extends AbstractBlock {
|
|||
aria-invalid="false"
|
||||
data-wc-on--change="actions.updateProducts"
|
||||
value="<?php echo esc_attr( $stock_count['status'] ); ?>"
|
||||
<?php checked( strpos( $selected_stock_status, $stock_count['status'] ) !== false, 1 ); ?>
|
||||
<?php checked( strpos( $query, $stock_count['status'] ) !== false, 1 ); ?>
|
||||
>
|
||||
<svg class="wc-block-components-checkbox__mark" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 20">
|
||||
<path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"></path>
|
||||
|
@ -239,10 +232,10 @@ final class CollectionStockFilter extends AbstractBlock {
|
|||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Dropdown::render() escapes output.
|
||||
echo Dropdown::render(
|
||||
array(
|
||||
'items' => $list_items,
|
||||
'action' => 'woocommerce/collection-stock-filter::actions.navigate',
|
||||
'selected_item' => $selected_item,
|
||||
'text_color' => $text_color,
|
||||
'items' => $list_items,
|
||||
'action' => 'woocommerce/collection-stock-filter::actions.navigate',
|
||||
'selected_items' => $selected_items,
|
||||
'select_type' => $select_type,
|
||||
)
|
||||
);
|
||||
?>
|
||||
|
|
|
@ -18,10 +18,8 @@ class Dropdown {
|
|||
wp_enqueue_script( 'wc-interactivity-dropdown' );
|
||||
wp_enqueue_style( 'wc-interactivity-dropdown' );
|
||||
|
||||
$select_type = $props['select_type'] ?? 'single';
|
||||
$selected_items = $props['selected_items'] ?? array();
|
||||
$text_color = $props['text_color'] ?? 'inherit';
|
||||
$text_color_style = "color: {$text_color};";
|
||||
$select_type = $props['select_type'] ?? 'single';
|
||||
$selected_items = $props['selected_items'] ?? array();
|
||||
|
||||
// Items should be an array of objects with a label and value property.
|
||||
$items = $props['items'] ?? array();
|
||||
|
@ -32,117 +30,90 @@ class Dropdown {
|
|||
'selectType' => $select_type,
|
||||
);
|
||||
|
||||
$action = $props['action'] ?? '';
|
||||
$namespace = wp_json_encode( array( 'namespace' => 'woocommerce/interactivity-dropdown' ) );
|
||||
$wrapper_class = 'multiple' === $select_type ? '' : 'single-selection';
|
||||
$input_id = wp_unique_id( 'wc-interactivity-dropdown-input-' );
|
||||
|
||||
wp_add_inline_style(
|
||||
'wc-interactivity-dropdown',
|
||||
"#$input_id::placeholder {
|
||||
$text_color_style
|
||||
}"
|
||||
);
|
||||
|
||||
$wrapper_class = 'multiple' === $select_type ? '' : 'single-selection';
|
||||
$action = $props['action'] ?? '';
|
||||
$namespace = wp_json_encode( array( 'namespace' => 'woocommerce/interactivity-dropdown' ) );
|
||||
$default_placeholder = 'single' === $select_type ? __( 'Select an option', 'woocommerce' ) : __( 'Select options', 'woocommerce' );
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div data-wc-interactive='<?php echo esc_attr( $namespace ); ?>'>
|
||||
<div class="wc-interactivity-dropdown" data-wc-context='<?php echo esc_attr( wp_json_encode( $dropdown_context ) ); ?>' >
|
||||
<div class="wc-blocks-components-form-token-field-wrapper <?php echo esc_attr( $wrapper_class ); ?>" >
|
||||
<div class="components-form-token-field" tabindex="-1">
|
||||
<div class="components-form-token-field__input-container"
|
||||
data-wc-class--is-active="context.isOpen"
|
||||
tabindex="-1"
|
||||
data-wc-on--click="actions.toggleIsOpen"
|
||||
>
|
||||
<?php if ( 'multiple' === $select_type ) { ?>
|
||||
<div class="wc-interactivity-dropdown" data-wc-on--click="actions.toggleIsOpen" data-wc-context='<?php echo esc_attr( wp_json_encode( $dropdown_context ) ); ?>' >
|
||||
<div class="wc-interactivity-dropdown__dropdown" tabindex="-1" >
|
||||
<div class="wc-interactivity-dropdown__dropdown-selection" id="options-dropdown" tabindex="0" aria-haspopup="listbox">
|
||||
<span class="wc-interactivity-dropdown__placeholder" data-wc-text="state.placeholderText">
|
||||
<?php echo esc_html( $default_placeholder ); ?>
|
||||
</span>
|
||||
<?php if ( 'multiple' === $select_type ) { ?>
|
||||
<div class="selected-options">
|
||||
<template
|
||||
data-wc-each="context.selectedItems"
|
||||
data-wc-each-key="context.item.value"
|
||||
>
|
||||
<span class="components-form-token-field__token">
|
||||
<span
|
||||
class="components-form-token-field__token-text"
|
||||
data-wc-text="context.item.label"
|
||||
style="<?php echo esc_attr( $text_color_style ); ?>"
|
||||
></span>
|
||||
<button
|
||||
type="button"
|
||||
data-wc-on--click="actions.unselectDropdownItem"
|
||||
data-wc-on--click--parent-action="<?php echo esc_attr( $action ); ?>"
|
||||
class="components-button components-form-token-field__remove-token has-icon"
|
||||
>
|
||||
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
||||
data-wc-each="context.selectedItems"
|
||||
data-wc-each-key="context.item.value"
|
||||
>
|
||||
<div class="wc-interactivity-dropdown__selected-badge">
|
||||
<span class="wc-interactivity-dropdown__badge-text" data-wc-text="context.item.label"></span>
|
||||
<svg
|
||||
data-wc-on--click="actions.unselectDropdownItem"
|
||||
class="wc-interactivity-dropdown__badge-remove"
|
||||
width="24"
|
||||
height="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<?php foreach ( $selected_items as $selected ) { ?>
|
||||
<span
|
||||
class="components-form-token-field__token"
|
||||
<div
|
||||
class="wc-interactivity-dropdown__selected-badge"
|
||||
data-wc-key="<?php echo esc_attr( $selected['label'] ); ?>"
|
||||
data-wc-each-child
|
||||
>
|
||||
<span
|
||||
class="components-form-token-field__token-text"
|
||||
style="<?php echo esc_attr( $text_color_style ); ?>"
|
||||
>
|
||||
<?php echo esc_html( $selected['label'] ); ?>
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
class="components-button components-form-token-field__remove-token has-icon"
|
||||
>
|
||||
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
||||
<span class="wc-interactivity-dropdown__badge-text"><?php echo esc_html( $selected['label'] ); ?></span>
|
||||
<svg
|
||||
data-wc-on--click="actions.unselectDropdownItem"
|
||||
class="wc-interactivity-dropdown__badge-remove"
|
||||
width="24"
|
||||
height="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
<input
|
||||
id="<?php echo esc_attr( $input_id ); ?>"
|
||||
readonly
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
data-wc-bind--placeholder="state.placeholderText"
|
||||
class="components-form-token-field__input"
|
||||
role="combobox"
|
||||
aria-expanded="false"
|
||||
aria-autocomplete="list"
|
||||
value=""
|
||||
data-wc-key="input"
|
||||
</div>
|
||||
<?php } ?>
|
||||
<span class="wc-interactivity-dropdown__svg-container">
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="30" height="30" >
|
||||
<path d="M17.5 11.6L12 16l-5.5-4.4.9-1.2L12 14l4.5-3.6 1 1.2z" ></path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div data-wc-bind--hidden="!context.isOpen" class="wc-interactivity-dropdown__dropdown-list" aria-labelledby="options-dropdown" role="listbox">
|
||||
<?php
|
||||
foreach ( $items as $item ) :
|
||||
$context = array( 'item' => $item );
|
||||
?>
|
||||
<div
|
||||
class="wc-interactivity-dropdown__dropdown-option"
|
||||
role="option"
|
||||
tabindex="0"
|
||||
data-wc-on--click--select-item="actions.selectDropdownItem"
|
||||
data-wc-on--click--parent-action="<?php echo esc_attr( $action ); ?>"
|
||||
data-wc-class--is-selected="state.isSelected"
|
||||
class="components-form-token-field__suggestion"
|
||||
data-wc-bind--aria-selected="state.isSelected"
|
||||
data-wc-context='<?php echo wp_json_encode( $context ); ?>'
|
||||
>
|
||||
<ul hidden data-wc-bind--hidden="!context.isOpen" class="components-form-token-field__suggestions-list" id="components-form-token-suggestions-1" role="listbox" data-wc-key="ul">
|
||||
<?php
|
||||
foreach ( $items as $item ) :
|
||||
$context = array( 'item' => $item );
|
||||
?>
|
||||
<li
|
||||
role="option"
|
||||
data-wc-on--click--select-item="actions.selectDropdownItem"
|
||||
data-wc-on--click--parent-action="<?php echo esc_attr( $action ); ?>"
|
||||
data-wc-class--is-selected="state.isSelected"
|
||||
class="components-form-token-field__suggestion"
|
||||
data-wc-bind--aria-selected="state.isSelected"
|
||||
data-wc-context='<?php echo wp_json_encode( $context ); ?>'
|
||||
style="<?php echo esc_attr( $text_color_style ); ?>"
|
||||
>
|
||||
<?php // This attribute supports HTML so should be sanitized by caller. ?>
|
||||
<?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||
<?php echo $item['label']; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php echo $item['label']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="30" height="30" >
|
||||
<path d="M17.5 11.6L12 16l-5.5-4.4.9-1.2L12 14l4.5-3.6 1 1.2z" ></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
|
|
Loading…
Reference in New Issue