Add dropdown display style to Attribute Filter block (https://github.com/woocommerce/woocommerce-blocks/pull/1255)
* Add dropdown display style to Attribute Filter block * Unify filter blocks margin * Show attribute label inside dropdown input * Minor CSS reorganization * Refactor code to smaller files * Preserve input values on blur * Only save data-display-style if it's different than 'list' * Remove inputRef prop in DropdownSelectorInputWrapper * Accessibility: fix missing label * Prevent input field being unselected when removing an item with the backspace * Remove isLoading styles and don't set isDisabled when it's not actually disabled * Accessibility: increase color contrast * Add package-lock.json * Prevent input field being unfocused when removing an item with its chip card * Don't show menu when input is unfocused
This commit is contained in:
parent
1e92555c8c
commit
f7c807a0c6
|
@ -97,7 +97,7 @@ const CheckboxList = ( {
|
|||
return (
|
||||
<Fragment>
|
||||
{ options.map( ( option, index ) => (
|
||||
<Fragment key={ option.key }>
|
||||
<Fragment key={ option.value }>
|
||||
<li
|
||||
{ ...shouldTruncateOptions &&
|
||||
! showExpanded &&
|
||||
|
@ -105,13 +105,15 @@ const CheckboxList = ( {
|
|||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
id={ option.key }
|
||||
value={ option.key }
|
||||
onChange={ onChange }
|
||||
checked={ checked.includes( option.key ) }
|
||||
id={ option.value }
|
||||
value={ option.value }
|
||||
onChange={ ( event ) => {
|
||||
onChange( event.target.value );
|
||||
} }
|
||||
checked={ checked.includes( option.value ) }
|
||||
disabled={ isDisabled }
|
||||
/>
|
||||
<label htmlFor={ option.key }>
|
||||
<label htmlFor={ option.value }>
|
||||
{ option.label }
|
||||
</label>
|
||||
</li>
|
||||
|
@ -152,8 +154,8 @@ CheckboxList.propTypes = {
|
|||
onChange: PropTypes.func,
|
||||
options: PropTypes.arrayOf(
|
||||
PropTypes.shape( {
|
||||
key: PropTypes.string.isRequired,
|
||||
label: PropTypes.node.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
} )
|
||||
),
|
||||
checked: PropTypes.array,
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import PropTypes from 'prop-types';
|
||||
import { useRef } from '@wordpress/element';
|
||||
import classNames from 'classnames';
|
||||
import Downshift from 'downshift';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import DropdownSelectorInput from './input';
|
||||
import DropdownSelectorInputWrapper from './input-wrapper';
|
||||
import DropdownSelectorMenu from './menu';
|
||||
import DropdownSelectorSelectedChip from './selected-chip';
|
||||
import './style.scss';
|
||||
|
||||
/**
|
||||
* State reducer for the downshift component.
|
||||
* See: https://github.com/downshift-js/downshift#statereducer
|
||||
*/
|
||||
const stateReducer = ( state, changes ) => {
|
||||
switch ( changes.type ) {
|
||||
case Downshift.stateChangeTypes.keyDownEnter:
|
||||
case Downshift.stateChangeTypes.clickItem:
|
||||
return {
|
||||
...changes,
|
||||
highlightedIndex: state.highlightedIndex,
|
||||
isOpen: true,
|
||||
inputValue: '',
|
||||
};
|
||||
case Downshift.stateChangeTypes.blurInput:
|
||||
case Downshift.stateChangeTypes.mouseUp:
|
||||
return {
|
||||
...changes,
|
||||
inputValue: state.inputValue,
|
||||
};
|
||||
default:
|
||||
return changes;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Component used to show an input box with a dropdown with suggestions.
|
||||
*/
|
||||
const DropdownSelector = ( {
|
||||
attributeLabel = '',
|
||||
className,
|
||||
checked = [],
|
||||
inputLabel = '',
|
||||
isDisabled = false,
|
||||
isLoading = false,
|
||||
onChange = () => {},
|
||||
options = [],
|
||||
} ) => {
|
||||
const inputRef = useRef( null );
|
||||
|
||||
const classes = classNames( className, 'wc-block-dropdown-selector', {
|
||||
'is-disabled': isDisabled,
|
||||
'is-loading': isLoading,
|
||||
} );
|
||||
|
||||
const focusInput = ( isOpen ) => {
|
||||
if ( ! isOpen ) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Downshift
|
||||
onChange={ onChange }
|
||||
selectedItem={ null }
|
||||
stateReducer={ stateReducer }
|
||||
>
|
||||
{ ( {
|
||||
getInputProps,
|
||||
getItemProps,
|
||||
getLabelProps,
|
||||
getMenuProps,
|
||||
highlightedIndex,
|
||||
inputValue,
|
||||
isOpen,
|
||||
openMenu,
|
||||
} ) => (
|
||||
<div className={ classes }>
|
||||
{ /* eslint-disable-next-line jsx-a11y/label-has-for */ }
|
||||
<label
|
||||
{ ...getLabelProps( {
|
||||
className: 'screen-reader-text',
|
||||
} ) }
|
||||
>
|
||||
{ inputLabel }
|
||||
</label>
|
||||
<DropdownSelectorInputWrapper
|
||||
isOpen={ isOpen }
|
||||
onClick={ () => focusInput( isOpen ) }
|
||||
>
|
||||
{ checked.map( ( value ) => {
|
||||
const option = options.find(
|
||||
( o ) => o.value === value
|
||||
);
|
||||
return (
|
||||
<DropdownSelectorSelectedChip
|
||||
key={ value }
|
||||
onRemoveItem={ ( val ) => {
|
||||
onChange( val );
|
||||
focusInput( isOpen );
|
||||
} }
|
||||
option={ option }
|
||||
/>
|
||||
);
|
||||
} ) }
|
||||
<DropdownSelectorInput
|
||||
attributeLabel={ attributeLabel }
|
||||
checked={ checked }
|
||||
getInputProps={ getInputProps }
|
||||
inputRef={ inputRef }
|
||||
isDisabled={ isDisabled }
|
||||
onFocus={ openMenu }
|
||||
onRemoveItem={ ( val ) => {
|
||||
onChange( val );
|
||||
focusInput( isOpen );
|
||||
} }
|
||||
value={ inputValue }
|
||||
/>
|
||||
</DropdownSelectorInputWrapper>
|
||||
{ isOpen && ! isDisabled && (
|
||||
<DropdownSelectorMenu
|
||||
checked={ checked }
|
||||
getItemProps={ getItemProps }
|
||||
getMenuProps={ getMenuProps }
|
||||
highlightedIndex={ highlightedIndex }
|
||||
options={ options.filter(
|
||||
( option ) =>
|
||||
! inputValue ||
|
||||
option.value.startsWith( inputValue )
|
||||
) }
|
||||
/>
|
||||
) }
|
||||
</div>
|
||||
) }
|
||||
</Downshift>
|
||||
);
|
||||
};
|
||||
|
||||
DropdownSelector.propTypes = {
|
||||
attributeLabel: PropTypes.string,
|
||||
checked: PropTypes.array,
|
||||
className: PropTypes.string,
|
||||
inputLabel: PropTypes.string,
|
||||
isDisabled: PropTypes.bool,
|
||||
isLoading: PropTypes.bool,
|
||||
limit: PropTypes.number,
|
||||
onChange: PropTypes.func,
|
||||
options: PropTypes.arrayOf(
|
||||
PropTypes.shape( {
|
||||
label: PropTypes.node.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
} )
|
||||
),
|
||||
};
|
||||
|
||||
export default DropdownSelector;
|
|
@ -0,0 +1,13 @@
|
|||
const DropdownSelectorInputWrapper = ( { children, onClick } ) => {
|
||||
return (
|
||||
/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */
|
||||
<div
|
||||
className="wc-block-dropdown-selector__input-wrapper"
|
||||
onClick={ onClick }
|
||||
>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DropdownSelectorInputWrapper;
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
|
||||
const DropdownSelectorInput = ( {
|
||||
attributeLabel,
|
||||
checked,
|
||||
getInputProps,
|
||||
inputRef,
|
||||
isDisabled,
|
||||
onFocus,
|
||||
onRemoveItem,
|
||||
value,
|
||||
} ) => {
|
||||
return (
|
||||
<input
|
||||
{ ...getInputProps( {
|
||||
ref: inputRef,
|
||||
className: 'wc-block-dropdown-selector__input',
|
||||
disabled: isDisabled,
|
||||
onFocus,
|
||||
onKeyDown( e ) {
|
||||
if (
|
||||
e.key === 'Backspace' &&
|
||||
! value &&
|
||||
checked.length > 0
|
||||
) {
|
||||
onRemoveItem( checked[ checked.length - 1 ] );
|
||||
}
|
||||
},
|
||||
placeholder:
|
||||
checked.length === 0
|
||||
? sprintf(
|
||||
// Translators: %s attribute name.
|
||||
__( 'Any %s', 'woo-gutenberg-products-block' ),
|
||||
attributeLabel
|
||||
)
|
||||
: null,
|
||||
} ) }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DropdownSelectorInput;
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const DropdownSelectorMenu = ( {
|
||||
checked,
|
||||
getItemProps,
|
||||
getMenuProps,
|
||||
highlightedIndex,
|
||||
options,
|
||||
} ) => {
|
||||
return (
|
||||
<ul
|
||||
{ ...getMenuProps( {
|
||||
className: 'wc-block-dropdown-selector__list',
|
||||
} ) }
|
||||
>
|
||||
{ options.map( ( option, index ) => {
|
||||
const selected = checked.includes( option.value );
|
||||
return (
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
<li
|
||||
{ ...getItemProps( {
|
||||
key: option.value,
|
||||
className: classNames(
|
||||
'wc-block-dropdown-selector__list-item',
|
||||
{
|
||||
'is-selected': selected,
|
||||
'is-highlighted':
|
||||
highlightedIndex === index,
|
||||
}
|
||||
),
|
||||
index,
|
||||
item: option.value,
|
||||
'aria-label': selected
|
||||
? sprintf(
|
||||
__(
|
||||
'Remove %s filter',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
option.name
|
||||
)
|
||||
: null,
|
||||
} ) }
|
||||
>
|
||||
{ option.label }
|
||||
</li>
|
||||
);
|
||||
} ) }
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
export default DropdownSelectorMenu;
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
|
||||
const DropdownSelectorSelectedChip = ( { onRemoveItem, option } ) => {
|
||||
return (
|
||||
<button
|
||||
className="wc-block-dropdown-selector__selected-chip"
|
||||
onClick={ ( e ) => {
|
||||
e.stopPropagation();
|
||||
onRemoveItem( option.value );
|
||||
} }
|
||||
onKeyDown={ ( e ) => {
|
||||
if ( e.key === 'Backspace' || e.key === 'Delete' ) {
|
||||
onRemoveItem( option.value );
|
||||
}
|
||||
} }
|
||||
aria-label={ sprintf(
|
||||
__( 'Remove %s filter', 'woo-gutenberg-products-block' ),
|
||||
option.name
|
||||
) }
|
||||
>
|
||||
{ option.label }
|
||||
<span className="wc-block-dropdown-selector__selected-chip__remove">
|
||||
𝘅
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default DropdownSelectorSelectedChip;
|
|
@ -0,0 +1,105 @@
|
|||
.wc-block-dropdown-selector {
|
||||
max-width: 300px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.wc-block-dropdown-selector__input-wrapper {
|
||||
align-items: baseline;
|
||||
border: 1px solid #9f9f9f;
|
||||
border-radius: 4px;
|
||||
cursor: text;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 2px;
|
||||
|
||||
.is-disabled & {
|
||||
background-color: $core-grey-light-500;
|
||||
}
|
||||
}
|
||||
|
||||
.wc-block-dropdown-selector__placeholder {
|
||||
font-size: 0.8em;
|
||||
height: 1.8em;
|
||||
margin: 0 $gap-smallest;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.wc-block-dropdown-selector__input {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
flex: 1;
|
||||
font-size: 0.8em;
|
||||
height: 1.8em;
|
||||
min-width: 0;
|
||||
margin: 1.5px;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.wc-block-dropdown-selector__selected-chip {
|
||||
background-color: $core-grey-light-600;
|
||||
border: 1px solid #9f9f9f;
|
||||
border-radius: 4px;
|
||||
color: $core-grey-dark-600;
|
||||
display: inline-block;
|
||||
font-size: 0.8em;
|
||||
font-weight: inherit;
|
||||
height: 1.8em;
|
||||
margin: 1.5px;
|
||||
padding: 0 0 0 0.3em;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
background-color: $core-grey-light-600;
|
||||
border: 1px solid #9f9f9f;
|
||||
color: $core-grey-dark-600;
|
||||
}
|
||||
}
|
||||
|
||||
.wc-block-dropdown-selector__selected-chip__remove {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
display: inline-block;
|
||||
padding: 0 0.3em;
|
||||
}
|
||||
|
||||
.wc-block-dropdown-selector__list {
|
||||
list-style: none;
|
||||
margin: -1px 0 0;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 100%;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
z-index: 1;
|
||||
|
||||
&:not(:empty) {
|
||||
border: 1px solid #9f9f9f;
|
||||
}
|
||||
}
|
||||
|
||||
.wc-block-dropdown-selector__list-item {
|
||||
background-color: #fff;
|
||||
color: $core-grey-dark-600;
|
||||
padding: 0 $gap-smallest;
|
||||
|
||||
&.is-selected {
|
||||
background-color: $core-grey-light-600;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&.is-highlighted,
|
||||
&:active {
|
||||
background-color: #00669e;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
|
@ -48,8 +48,8 @@ const Label = ( {
|
|||
};
|
||||
|
||||
Label.propTypes = {
|
||||
label: PropTypes.string,
|
||||
screenReaderLabel: PropTypes.string,
|
||||
label: PropTypes.node,
|
||||
screenReaderLabel: PropTypes.node,
|
||||
wrapperElement: PropTypes.elementType,
|
||||
wrapperProps: PropTypes.object,
|
||||
};
|
||||
|
|
|
@ -47,6 +47,8 @@
|
|||
}
|
||||
|
||||
.wc-block-price-filter {
|
||||
margin-bottom: $gap-large;
|
||||
|
||||
.wc-block-price-filter__range-input-wrapper {
|
||||
@include reset;
|
||||
height: 9px;
|
||||
|
@ -73,7 +75,6 @@
|
|||
flex-flow: row nowrap;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin: 0 0 20px;
|
||||
|
||||
.wc-block-price-filter__amount {
|
||||
margin: 0;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.wc-block-active-filters {
|
||||
margin: 0 0 $gap;
|
||||
margin-bottom: $gap-large;
|
||||
overflow: hidden;
|
||||
|
||||
.wc-block-active-filters__clear-all {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __, _n, sprintf } from '@wordpress/i18n';
|
||||
import { speak } from '@wordpress/a11y';
|
||||
import {
|
||||
useCollection,
|
||||
useQueryStateByKey,
|
||||
|
@ -15,6 +17,8 @@ import {
|
|||
useMemo,
|
||||
} from '@wordpress/element';
|
||||
import CheckboxList from '@woocommerce/base-components/checkbox-list';
|
||||
import DropdownSelector from '@woocommerce/base-components/dropdown-selector';
|
||||
import Label from '@woocommerce/base-components/label';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -39,9 +43,24 @@ const AttributeFilterBlock = ( {
|
|||
<Fragment>
|
||||
{ name }
|
||||
{ blockAttributes.showCounts && count !== null && (
|
||||
<span className="wc-block-attribute-filter-list-count">
|
||||
{ count }
|
||||
</span>
|
||||
<Label
|
||||
label={ count }
|
||||
screenReaderLabel={ sprintf(
|
||||
// translators: %s number of products.
|
||||
_n(
|
||||
'%s product',
|
||||
'%s products',
|
||||
count,
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
count
|
||||
) }
|
||||
wrapperElement="span"
|
||||
wrapperProps={ {
|
||||
className:
|
||||
'wc-block-attribute-filter-list-count',
|
||||
} }
|
||||
/>
|
||||
) }
|
||||
</Fragment>
|
||||
);
|
||||
|
@ -62,15 +81,18 @@ const AttributeFilterBlock = ( {
|
|||
blockAttributes.isPreview && ! blockAttributes.attributeId
|
||||
? [
|
||||
{
|
||||
key: 'preview-1',
|
||||
value: 'preview-1',
|
||||
name: 'Blue',
|
||||
label: getLabel( 'Blue', 3 ),
|
||||
},
|
||||
{
|
||||
key: 'preview-2',
|
||||
value: 'preview-2',
|
||||
name: 'Green',
|
||||
label: getLabel( 'Green', 3 ),
|
||||
},
|
||||
{
|
||||
key: 'preview-3',
|
||||
value: 'preview-3',
|
||||
name: 'Red',
|
||||
label: getLabel( 'Red', 2 ),
|
||||
},
|
||||
]
|
||||
|
@ -149,7 +171,8 @@ const AttributeFilterBlock = ( {
|
|||
}
|
||||
|
||||
newOptions.push( {
|
||||
key: term.slug,
|
||||
value: term.slug,
|
||||
name: term.name,
|
||||
label: getLabel( term.name, count ),
|
||||
} );
|
||||
} );
|
||||
|
@ -183,16 +206,37 @@ const AttributeFilterBlock = ( {
|
|||
* When a checkbox in the list changes, update state.
|
||||
*/
|
||||
const onChange = useCallback(
|
||||
( event ) => {
|
||||
const isChecked = event.target.checked;
|
||||
const checkedValue = event.target.value;
|
||||
( checkedValue ) => {
|
||||
const isChecked = ! checked.includes( checkedValue );
|
||||
const newChecked = checked.filter(
|
||||
( value ) => value !== checkedValue
|
||||
);
|
||||
const checkedOption = displayedOptions.find(
|
||||
( option ) => option.value === checkedValue
|
||||
);
|
||||
|
||||
if ( isChecked ) {
|
||||
newChecked.push( checkedValue );
|
||||
newChecked.sort();
|
||||
speak(
|
||||
sprintf(
|
||||
__(
|
||||
'%s filter added.',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
checkedOption.name
|
||||
)
|
||||
);
|
||||
} else {
|
||||
speak(
|
||||
sprintf(
|
||||
__(
|
||||
'%s filter removed.',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
checkedOption.name
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const newSelectedTerms = getSelectedTerms( newChecked );
|
||||
|
@ -212,6 +256,7 @@ const AttributeFilterBlock = ( {
|
|||
setProductAttributesQuery,
|
||||
attributeObject,
|
||||
blockAttributes,
|
||||
displayedOptions,
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -220,6 +265,8 @@ const AttributeFilterBlock = ( {
|
|||
}
|
||||
|
||||
const TagName = `h${ blockAttributes.headingLevel }`;
|
||||
const isLoading = ! blockAttributes.isPreview && attributeTermsLoading;
|
||||
const isDisabled = ! blockAttributes.isPreview && filteredCountsLoading;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
|
@ -227,18 +274,26 @@ const AttributeFilterBlock = ( {
|
|||
<TagName>{ blockAttributes.heading }</TagName>
|
||||
) }
|
||||
<div className="wc-block-attribute-filter">
|
||||
{ blockAttributes.displayStyle === 'dropdown' ? (
|
||||
<DropdownSelector
|
||||
attributeLabel={ attributeObject.label }
|
||||
checked={ checked }
|
||||
className={ 'wc-block-attribute-filter-dropdown' }
|
||||
inputLabel={ blockAttributes.heading }
|
||||
isLoading={ isLoading }
|
||||
onChange={ onChange }
|
||||
options={ displayedOptions }
|
||||
/>
|
||||
) : (
|
||||
<CheckboxList
|
||||
className={ 'wc-block-attribute-filter-list' }
|
||||
options={ displayedOptions }
|
||||
checked={ checked }
|
||||
onChange={ onChange }
|
||||
isLoading={
|
||||
! blockAttributes.isPreview && attributeTermsLoading
|
||||
}
|
||||
isDisabled={
|
||||
! blockAttributes.isPreview && filteredCountsLoading
|
||||
}
|
||||
isLoading={ isLoading }
|
||||
isDisabled={ isDisabled }
|
||||
/>
|
||||
) }
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
|
|
|
@ -33,6 +33,7 @@ const Edit = ( { attributes, setAttributes, debouncedSpeak } ) => {
|
|||
const {
|
||||
attributeId,
|
||||
className,
|
||||
displayStyle,
|
||||
heading,
|
||||
headingLevel,
|
||||
isPreview,
|
||||
|
@ -151,6 +152,34 @@ const Edit = ( { attributes, setAttributes, debouncedSpeak } ) => {
|
|||
} )
|
||||
}
|
||||
/>
|
||||
<ToggleButtonControl
|
||||
label={ __(
|
||||
'Display Style',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
value={ displayStyle }
|
||||
options={ [
|
||||
{
|
||||
label: __(
|
||||
'List',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
value: 'list',
|
||||
},
|
||||
{
|
||||
label: __(
|
||||
'Dropdown',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
value: 'dropdown',
|
||||
},
|
||||
] }
|
||||
onChange={ ( value ) =>
|
||||
setAttributes( {
|
||||
displayStyle: value,
|
||||
} )
|
||||
}
|
||||
/>
|
||||
</PanelBody>
|
||||
<PanelBody
|
||||
title={ __(
|
||||
|
|
|
@ -17,6 +17,7 @@ const getProps = ( el ) => {
|
|||
queryType: el.dataset.queryType,
|
||||
heading: el.dataset.heading,
|
||||
headingLevel: el.dataset.headingLevel || 3,
|
||||
displayStyle: el.dataset.displayStyle,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -53,6 +53,10 @@ registerBlockType( 'woocommerce/attribute-filter', {
|
|||
type: 'number',
|
||||
default: 3,
|
||||
},
|
||||
displayStyle: {
|
||||
type: 'string',
|
||||
default: 'list',
|
||||
},
|
||||
/**
|
||||
* Are we previewing?
|
||||
*/
|
||||
|
@ -73,6 +77,7 @@ registerBlockType( 'woocommerce/attribute-filter', {
|
|||
attributeId,
|
||||
heading,
|
||||
headingLevel,
|
||||
displayStyle,
|
||||
} = attributes;
|
||||
const data = {
|
||||
'data-attribute-id': attributeId,
|
||||
|
@ -81,6 +86,9 @@ registerBlockType( 'woocommerce/attribute-filter', {
|
|||
'data-heading': heading,
|
||||
'data-heading-level': headingLevel,
|
||||
};
|
||||
if ( displayStyle !== 'list' ) {
|
||||
data[ 'data-display-style' ] = displayStyle;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={ classNames( 'is-loading', className ) }
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
.wc-block-attribute-filter {
|
||||
margin-bottom: $gap-large;
|
||||
|
||||
.wc-block-attribute-filter-list-count::before {
|
||||
content: " (";
|
||||
}
|
||||
.wc-block-attribute-filter-list-count::after {
|
||||
content: ")";
|
||||
}
|
||||
|
||||
.wc-block-attribute-filter-list {
|
||||
margin: 0 0 $gap;
|
||||
margin: 0;
|
||||
|
||||
li {
|
||||
text-decoration: underline;
|
||||
|
@ -14,11 +23,11 @@
|
|||
.wc-block-attribute-filter-list-count {
|
||||
float: right;
|
||||
}
|
||||
.wc-block-attribute-filter-list-count::before {
|
||||
content: " (";
|
||||
}
|
||||
.wc-block-attribute-filter-list-count::after {
|
||||
content: ")";
|
||||
|
||||
.wc-block-dropdown-selector {
|
||||
.wc-block-dropdown-selector__list .wc-block-attribute-filter-list-count {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7106,6 +7106,11 @@
|
|||
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
|
||||
"dev": true
|
||||
},
|
||||
"compute-scroll-into-view": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.11.tgz",
|
||||
"integrity": "sha512-uUnglJowSe0IPmWOdDtrlHXof5CTIJitfJEyITHBW6zDVOGu9Pjk5puaLM73SLcwak0L4hEjO7Td88/a6P5i7A=="
|
||||
},
|
||||
"computed-style": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/computed-style/-/computed-style-0.1.4.tgz",
|
||||
|
@ -8176,6 +8181,17 @@
|
|||
"is-obj": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"downshift": {
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/downshift/-/downshift-3.4.3.tgz",
|
||||
"integrity": "sha512-lk0Q1VF4eTDe4EMzYtdVCPdu58ZRFyK3wxEAGUeKqPRDoHDgoS9/TaxW2w+hEbeh9yBMU2IKX8lQkNn6YTfZ4w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.5",
|
||||
"compute-scroll-into-view": "^1.0.9",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-is": "^16.9.0"
|
||||
}
|
||||
},
|
||||
"duplexer": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
|
||||
|
|
|
@ -106,6 +106,7 @@
|
|||
"dependencies": {
|
||||
"@woocommerce/components": "4.0.0",
|
||||
"compare-versions": "3.5.1",
|
||||
"downshift": "^3.4.2",
|
||||
"eslint-plugin-woocommerce": "file:bin/eslint-plugin-woocommerce",
|
||||
"gridicons": "3.3.1",
|
||||
"react-number-format": "4.3.1",
|
||||
|
|
Loading…
Reference in New Issue