Add editable filter widget headings (https://github.com/woocommerce/woocommerce-blocks/pull/1169)
* Move heading toolbar component so it can be resused * Add heading to attribute block * Add heading to price filter block * Fix missing loading state
This commit is contained in:
parent
9c9b248c6d
commit
a84990d10f
|
@ -8,12 +8,12 @@ import { Disabled, PanelBody, ToggleControl } from '@wordpress/components';
|
|||
import { InspectorControls } from '@wordpress/editor';
|
||||
import { ProductTitle } from '@woocommerce/atomic-components/product';
|
||||
import { previewProducts } from '@woocommerce/resource-previews';
|
||||
import HeadingToolbar from '@woocommerce/block-components/heading-toolbar';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import sharedConfig from '../shared-config';
|
||||
import HeadingToolbar from './heading-toolbar';
|
||||
|
||||
const blockConfig = {
|
||||
title: __( 'Product Title', 'woo-gutenberg-products-block' ),
|
||||
|
|
|
@ -25,7 +25,7 @@ import { getTaxonomyFromAttributeId } from '../../utils/attributes';
|
|||
/**
|
||||
* Component displaying an attribute filter.
|
||||
*/
|
||||
const AttributeFilterBlock = ( { attributes } ) => {
|
||||
const AttributeFilterBlock = ( { attributes, isPreview = false } ) => {
|
||||
const [ options, setOptions ] = useState( [] );
|
||||
const [ checkedOptions, setCheckedOptions ] = useState( [] );
|
||||
const { showCounts, attributeId, queryType } = attributes;
|
||||
|
@ -168,19 +168,26 @@ const AttributeFilterBlock = ( { attributes } ) => {
|
|||
setCheckedOptions( checked );
|
||||
}, [] );
|
||||
|
||||
if ( ! taxonomy ) {
|
||||
if ( ! taxonomy || ( options.length === 0 && ! attributeTermsLoading ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const TagName = `h${ attributes.headingLevel }`;
|
||||
|
||||
return (
|
||||
<div className="wc-block-attribute-filter">
|
||||
<CheckboxList
|
||||
className={ 'wc-block-attribute-filter-list' }
|
||||
options={ options }
|
||||
onChange={ onChange }
|
||||
isLoading={ attributeTermsLoading }
|
||||
/>
|
||||
</div>
|
||||
<Fragment>
|
||||
{ ! isPreview && attributes.heading && (
|
||||
<TagName>{ attributes.heading }</TagName>
|
||||
) }
|
||||
<div className="wc-block-attribute-filter">
|
||||
<CheckboxList
|
||||
className={ 'wc-block-attribute-filter-list' }
|
||||
options={ options }
|
||||
onChange={ onChange }
|
||||
isLoading={ attributeTermsLoading }
|
||||
/>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
import { __, sprintf, _n } from '@wordpress/i18n';
|
||||
import { Fragment, useState, useCallback } from '@wordpress/element';
|
||||
import { InspectorControls, BlockControls } from '@wordpress/editor';
|
||||
import { InspectorControls, BlockControls, PlainText } from '@wordpress/editor';
|
||||
import {
|
||||
Placeholder,
|
||||
Disabled,
|
||||
|
@ -15,9 +15,10 @@ import {
|
|||
} from '@wordpress/components';
|
||||
import Gridicon from 'gridicons';
|
||||
import { SearchListControl } from '@woocommerce/components';
|
||||
import { mapValues, toArray, sortBy } from 'lodash';
|
||||
import { mapValues, toArray, sortBy, find } from 'lodash';
|
||||
import { ATTRIBUTES } from '@woocommerce/block-settings';
|
||||
import { getAdminLink } from '@woocommerce/navigation';
|
||||
import HeadingToolbar from '@woocommerce/block-components/heading-toolbar';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -78,6 +79,21 @@ const Edit = ( { attributes, setAttributes, debouncedSpeak } ) => {
|
|||
} )
|
||||
}
|
||||
/>
|
||||
<p>
|
||||
{ __(
|
||||
'Heading Level',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</p>
|
||||
<HeadingToolbar
|
||||
isCollapsed={ false }
|
||||
minLevel={ 2 }
|
||||
maxLevel={ 7 }
|
||||
selectedLevel={ attributes.headingLevel }
|
||||
onChange={ ( newLevel ) =>
|
||||
setAttributes( { headingLevel: newLevel } )
|
||||
}
|
||||
/>
|
||||
</PanelBody>
|
||||
<PanelBody
|
||||
title={ __(
|
||||
|
@ -190,8 +206,25 @@ const Edit = ( { attributes, setAttributes, debouncedSpeak } ) => {
|
|||
}, [] );
|
||||
|
||||
const onChange = useCallback( ( selected ) => {
|
||||
const selectedId = selected[ 0 ].id;
|
||||
const productAttribute = find( ATTRIBUTES, [
|
||||
'attribute_id',
|
||||
selectedId.toString(),
|
||||
] );
|
||||
|
||||
if ( ! productAttribute || attributes.attributeId === selectedId ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const attributeName = productAttribute.attribute_name;
|
||||
|
||||
setAttributes( {
|
||||
attributeId: selected[ 0 ].id,
|
||||
attributeId: selectedId,
|
||||
heading: sprintf(
|
||||
// Translators: %s attribute name.
|
||||
__( 'Filter by %s', 'woo-gutenberg-products-block' ),
|
||||
attributeName
|
||||
),
|
||||
} );
|
||||
}, [] );
|
||||
|
||||
|
@ -276,6 +309,8 @@ const Edit = ( { attributes, setAttributes, debouncedSpeak } ) => {
|
|||
);
|
||||
};
|
||||
|
||||
const TagName = `h${ attributes.headingLevel }`;
|
||||
|
||||
return Object.keys( ATTRIBUTES ).length === 0 ? (
|
||||
noAttributesPlaceholder()
|
||||
) : (
|
||||
|
@ -285,9 +320,20 @@ const Edit = ( { attributes, setAttributes, debouncedSpeak } ) => {
|
|||
{ isEditing ? (
|
||||
renderEditMode()
|
||||
) : (
|
||||
<Disabled>
|
||||
<Block attributes={ attributes } isPreview />
|
||||
</Disabled>
|
||||
<Fragment>
|
||||
<TagName>
|
||||
<PlainText
|
||||
className="wc-block-attribute-filter-heading"
|
||||
value={ attributes.heading }
|
||||
onChange={ ( value ) =>
|
||||
setAttributes( { heading: value } )
|
||||
}
|
||||
/>
|
||||
</TagName>
|
||||
<Disabled>
|
||||
<Block attributes={ attributes } isPreview />
|
||||
</Disabled>
|
||||
</Fragment>
|
||||
) }
|
||||
</Fragment>
|
||||
);
|
||||
|
|
|
@ -10,6 +10,8 @@ const getProps = ( el ) => {
|
|||
attributeId: parseInt( el.dataset.attributeId || 0, 10 ),
|
||||
showCounts: el.dataset.showCounts === 'true',
|
||||
queryType: el.dataset.queryType,
|
||||
heading: el.dataset.heading,
|
||||
headingLevel: el.dataset.headingLevel || 3,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -22,9 +22,7 @@ registerBlockType( 'woocommerce/attribute-filter', {
|
|||
'Display a list of filters based on a chosen product attribute.',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
supports: {
|
||||
align: [ 'wide', 'full' ],
|
||||
},
|
||||
supports: {},
|
||||
attributes: {
|
||||
attributeId: {
|
||||
type: 'number',
|
||||
|
@ -38,18 +36,38 @@ registerBlockType( 'woocommerce/attribute-filter', {
|
|||
type: 'string',
|
||||
default: 'or',
|
||||
},
|
||||
heading: {
|
||||
type: 'string',
|
||||
default: __(
|
||||
'Filter by attribute',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
},
|
||||
headingLevel: {
|
||||
type: 'number',
|
||||
default: 3,
|
||||
},
|
||||
},
|
||||
edit,
|
||||
/**
|
||||
* Save the props to post content.
|
||||
*/
|
||||
save( { attributes } ) {
|
||||
const { showCounts, displayStyle, queryType, attributeId } = attributes;
|
||||
const {
|
||||
showCounts,
|
||||
displayStyle,
|
||||
queryType,
|
||||
attributeId,
|
||||
heading,
|
||||
headingLevel,
|
||||
} = attributes;
|
||||
const data = {
|
||||
'data-attribute-id': attributeId,
|
||||
'data-show-counts': showCounts,
|
||||
'data-display-style': displayStyle,
|
||||
'data-query-type': queryType,
|
||||
'data-heading': heading,
|
||||
'data-heading-level': headingLevel,
|
||||
};
|
||||
return (
|
||||
<div className="is-loading" { ...data }>
|
||||
|
|
|
@ -6,14 +6,14 @@ import {
|
|||
useQueryStateByKey,
|
||||
useQueryStateByContext,
|
||||
} from '@woocommerce/base-hooks';
|
||||
import { useCallback } from '@wordpress/element';
|
||||
import { useCallback, Fragment } from '@wordpress/element';
|
||||
import PriceSlider from '@woocommerce/base-components/price-slider';
|
||||
import { CURRENCY } from '@woocommerce/settings';
|
||||
|
||||
/**
|
||||
* Component displaying a price filter.
|
||||
*/
|
||||
const PriceFilterBlock = ( { attributes } ) => {
|
||||
const PriceFilterBlock = ( { attributes, isPreview = false } ) => {
|
||||
const [ minPrice, setMinPrice ] = useQueryStateByKey(
|
||||
'product-grid',
|
||||
'min_price'
|
||||
|
@ -65,22 +65,29 @@ const PriceFilterBlock = ( { attributes } ) => {
|
|||
[ minConstraint, maxConstraint, minPrice, maxPrice ]
|
||||
);
|
||||
|
||||
const TagName = `h${ attributes.headingLevel }`;
|
||||
|
||||
return (
|
||||
<div className="wc-block-price-slider">
|
||||
<PriceSlider
|
||||
minConstraint={ minConstraint }
|
||||
maxConstraint={ maxConstraint }
|
||||
initialMin={ undefined }
|
||||
initialMax={ undefined }
|
||||
step={ 10 }
|
||||
currencySymbol={ CURRENCY.symbol }
|
||||
priceFormat={ CURRENCY.price_format }
|
||||
showInputFields={ showInputFields }
|
||||
showFilterButton={ showFilterButton }
|
||||
onChange={ onChange }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
</div>
|
||||
<Fragment>
|
||||
{ ! isPreview && attributes.heading && (
|
||||
<TagName>{ attributes.heading }</TagName>
|
||||
) }
|
||||
<div className="wc-block-price-slider">
|
||||
<PriceSlider
|
||||
minConstraint={ minConstraint }
|
||||
maxConstraint={ maxConstraint }
|
||||
initialMin={ undefined }
|
||||
initialMax={ undefined }
|
||||
step={ 10 }
|
||||
currencySymbol={ CURRENCY.symbol }
|
||||
priceFormat={ CURRENCY.price_format }
|
||||
showInputFields={ showInputFields }
|
||||
showFilterButton={ showFilterButton }
|
||||
onChange={ onChange }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Fragment } from '@wordpress/element';
|
||||
import { InspectorControls } from '@wordpress/editor';
|
||||
import { InspectorControls, PlainText } from '@wordpress/editor';
|
||||
import {
|
||||
Placeholder,
|
||||
Disabled,
|
||||
|
@ -13,6 +13,7 @@ import {
|
|||
} from '@wordpress/components';
|
||||
import { PRODUCT_COUNT } from '@woocommerce/block-settings';
|
||||
import { getAdminLink } from '@woocommerce/navigation';
|
||||
import HeadingToolbar from '@woocommerce/block-components/heading-toolbar';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -85,6 +86,21 @@ export default function( { attributes, setAttributes } ) {
|
|||
} )
|
||||
}
|
||||
/>
|
||||
<p>
|
||||
{ __(
|
||||
'Heading Level',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</p>
|
||||
<HeadingToolbar
|
||||
isCollapsed={ false }
|
||||
minLevel={ 2 }
|
||||
maxLevel={ 7 }
|
||||
selectedLevel={ attributes.headingLevel }
|
||||
onChange={ ( newLevel ) =>
|
||||
setAttributes( { headingLevel: newLevel } )
|
||||
}
|
||||
/>
|
||||
</PanelBody>
|
||||
</InspectorControls>
|
||||
);
|
||||
|
@ -129,6 +145,8 @@ export default function( { attributes, setAttributes } ) {
|
|||
</Placeholder>
|
||||
);
|
||||
|
||||
const TagName = `h${ attributes.headingLevel }`;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{ PRODUCT_COUNT === 0 ? (
|
||||
|
@ -136,6 +154,15 @@ export default function( { attributes, setAttributes } ) {
|
|||
) : (
|
||||
<Fragment>
|
||||
{ getInspectorControls() }
|
||||
<TagName>
|
||||
<PlainText
|
||||
className="wc-block-attribute-filter-heading"
|
||||
value={ attributes.heading }
|
||||
onChange={ ( value ) =>
|
||||
setAttributes( { heading: value } )
|
||||
}
|
||||
/>
|
||||
</TagName>
|
||||
<Disabled>
|
||||
<Block attributes={ attributes } isPreview />
|
||||
</Disabled>
|
||||
|
|
|
@ -22,10 +22,7 @@ registerBlockType( 'woocommerce/price-filter', {
|
|||
'Display a slider to filter products in your store by price.',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
supports: {
|
||||
align: [ 'wide', 'full' ],
|
||||
},
|
||||
|
||||
supports: {},
|
||||
attributes: {
|
||||
showInputFields: {
|
||||
type: 'boolean',
|
||||
|
@ -35,6 +32,14 @@ registerBlockType( 'woocommerce/price-filter', {
|
|||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
heading: {
|
||||
type: 'string',
|
||||
default: __( 'Filter by price', 'woo-gutenberg-products-block' ),
|
||||
},
|
||||
headingLevel: {
|
||||
type: 'number',
|
||||
default: 3,
|
||||
},
|
||||
},
|
||||
|
||||
edit,
|
||||
|
@ -43,14 +48,24 @@ registerBlockType( 'woocommerce/price-filter', {
|
|||
* Save the props to post content.
|
||||
*/
|
||||
save( { attributes } ) {
|
||||
const { showInputFields, showFilterButton } = attributes;
|
||||
const {
|
||||
showInputFields,
|
||||
showFilterButton,
|
||||
heading,
|
||||
headingLevel,
|
||||
} = attributes;
|
||||
const data = {
|
||||
'data-showinputfields': showInputFields,
|
||||
'data-showfilterbutton': showFilterButton,
|
||||
'data-heading': heading,
|
||||
'data-heading-level': headingLevel,
|
||||
};
|
||||
return (
|
||||
<div className="is-loading" { ...data }>
|
||||
<span aria-hidden className="wc-block-product-categories__placeholder" />
|
||||
<span
|
||||
aria-hidden
|
||||
className="wc-block-product-categories__placeholder"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue