Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try: Taxonomy filter block #45386

Draft
wants to merge 3 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export const BLOCK_NAME_MAP = {
'stock-filter': 'woocommerce/product-filter-stock-status',
'rating-filter': 'woocommerce/product-filter-rating',
'attribute-filter': 'woocommerce/product-filter-attribute',
'taxonomy-filter': 'woocommerce/product-filter-taxonomy',
};
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,26 @@ if ( isExperimentalBuild() ) {
),
},
},
{
name: 'product-filter-taxonomy',
title: __( 'Product Filter: Taxonomy (Beta)', 'woocommerce' ),
description: __(
'Enable customers to filter the product collection by selecting one or more taxonomy, such as category.',
'woocommerce'
),
attributes: {
filterType: 'taxonomy-filter',
heading: __( 'Filter by Taxonomy', 'woocommerce' ),
},
icon: {
src: (
<Icon
icon={ category }
className="wc-block-editor-components-block-icon"
/>
),
},
},
],
transforms: {
from: [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"name": "woocommerce/product-filter-taxonomy",
"version": "1.0.0",
"title": "Product Filter: Taxonomy (Beta)",
"description": "Enable customers to filter the product grid by selecting one or more taxonomy, such as category.",
"category": "woocommerce",
"keywords": [
"WooCommerce"
],
"textdomain": "woocommerce",
"apiVersion": 2,
"ancestor": [
"woocommerce/product-filter"
],
"supports": {
"interactivity": true,
"inserter": false,
"color": {
"text": true,
"background": false
}
},
"usesContext": [
"query",
"queryId"
],
"attributes": {
"taxonomyName": {
"type": "string",
"default": ""
},
"showCounts": {
"type": "boolean",
"default": false
},
"queryType": {
"type": "string",
"default": "or"
},
"displayStyle": {
"type": "string",
"default": "list"
},
"selectType": {
"type": "string",
"default": "multiple"
},
"isPreview": {
"type": "boolean",
"default": false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* External dependencies
*/
import FilterElementLabel from '@woocommerce/base-components/filter-element-label';
import { CheckboxList } from '@woocommerce/blocks-components';
import { AttributeTerm } from '@woocommerce/types';

type Props = {
attributeTerms: AttributeTerm[];
showCounts?: boolean;
};
export const AttributeCheckboxList = ( {
attributeTerms,
showCounts,
}: Props ) => (
<CheckboxList
className="wc-block-attribute-filter style-list"
onChange={ () => null }
options={ attributeTerms.map( ( term ) => ( {
label: (
<FilterElementLabel
name={ term.name }
count={ showCounts ? term.count : null }
/>
),
value: term.slug,
} ) ) }
/>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { Icon, chevronDown } from '@wordpress/icons';

/**
* Internal dependencies
*/
import { PreviewDropdown } from '../../components/preview-dropdown';

type Props = {
label: string;
};

export const AttributeDropdown = ( { label }: Props ) => {
return (
<div className="wc-block-attribute-filter style-dropdown">
<PreviewDropdown
placeholder={ sprintf(
/* translators: %s attribute name. */
__( 'Select %s', 'woocommerce' ),
label
) }
/>
<Icon icon={ chevronDown } size={ 30 } />
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* External dependencies
*/
import { sort } from 'fast-sort';
import { __, sprintf, _n } from '@wordpress/i18n';
import { SearchListControl } from '@woocommerce/editor-components/search-list-control';
import { getSetting } from '@woocommerce/settings';
import { SearchListItem } from '@woocommerce/editor-components/search-list-control/types';
import { AttributeSetting } from '@woocommerce/types';

const ATTRIBUTES = getSetting< AttributeSetting[] >( 'attributes', [] );

type AttributeSelectControlsProps = {
isCompact: boolean;
setAttributeId: ( id: number ) => void;
attributeId: number;
};

export const AttributeSelectControls = ( {
isCompact,
setAttributeId,
attributeId,
}: AttributeSelectControlsProps ) => {
const messages = {
clear: __( 'Clear selected attribute', 'woocommerce' ),
list: __( 'Product Attributes', 'woocommerce' ),
noItems: __(
"Your store doesn't have any product attributes.",
'woocommerce'
),
search: __( 'Search for a product attribute:', 'woocommerce' ),
selected: ( n: number ) =>
sprintf(
/* translators: %d is the number of attributes selected. */
_n(
'%d attribute selected',
'%d attributes selected',
n,
'woocommerce'
),
n
),
updated: __(
'Product attribute search results updated.',
'woocommerce'
),
};

const list = sort(
ATTRIBUTES.map( ( item ) => {
return {
id: parseInt( item.attribute_id, 10 ),
name: item.attribute_label,
};
} )
).asc( 'name' );

const onChange = ( selected: SearchListItem[] ) => {
if ( ! selected || ! selected.length ) {
return;
}

const selectedId = selected[ 0 ].id;
const productAttribute = ATTRIBUTES.find(
( attribute ) => attribute.attribute_id === selectedId.toString()
);

if ( ! productAttribute || attributeId === selectedId ) {
return;
}

setAttributeId( selectedId as number );
};

return (
<SearchListControl
className="woocommerce-product-attributes"
list={ list }
selected={ list.filter( ( { id } ) => id === attributeId ) }
onChange={ onChange }
messages={ messages }
isSingle
isCompact={ isCompact }
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { useSelect } from '@wordpress/data';
import { InspectorControls } from '@wordpress/block-editor';
import {
PanelBody,
ToggleControl,
SelectControl,
// @ts-expect-error - no types.
// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
__experimentalToggleGroupControl as ToggleGroupControl,
// @ts-expect-error - no types.
// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
__experimentalToggleGroupControlOption as ToggleGroupControlOption,
} from '@wordpress/components';

/**
* Internal dependencies
*/
import { EditProps } from '../types';

export const Inspector = ( { attributes, setAttributes }: EditProps ) => {
const { taxonomyName, showCounts, queryType, displayStyle, selectType } =
attributes;

const taxonomies = useSelect( ( select ) => {
const { getTaxonomies } = select( 'core' );
return getTaxonomies( {
type: 'product',
per_page: -1,
} );
} );

console.log( taxonomies );

return (
<InspectorControls key="inspector">
<PanelBody title={ __( 'Filter Settings', 'woocommerce' ) }>
{ taxonomies && (
<SelectControl
label={ __( 'Select taxonomy', 'woocommerce' ) }
value={ taxonomyName }
options={ taxonomies.map( ( item ) => ( {
value: item.slug,
label: item.name,
} ) ) }
onChange={ ( newTaxonomy ) =>
setAttributes( { taxonomyName: newTaxonomy } )
}
/>
) }
<ToggleControl
label={ __( 'Display product count', 'woocommerce' ) }
checked={ showCounts }
onChange={ () =>
setAttributes( {
showCounts: ! showCounts,
} )
}
/>
<ToggleGroupControl
label={ __(
'Allow selecting multiple options?',
'woocommerce'
) }
value={ selectType || 'multiple' }
onChange={ ( value: string ) =>
setAttributes( {
selectType: value,
} )
}
className="wc-block-attribute-filter__multiple-toggle"
>
<ToggleGroupControlOption
value="multiple"
label={ __( 'Multiple', 'woocommerce' ) }
/>
<ToggleGroupControlOption
value="single"
label={ __( 'Single', 'woocommerce' ) }
/>
</ToggleGroupControl>
{ selectType === 'multiple' && (
<ToggleGroupControl
label={ __( 'Filter Conditions', 'woocommerce' ) }
help={
queryType === 'and'
? __(
'Choose to return filter results for all of the attributes selected.',
'woocommerce'
)
: __(
'Choose to return filter results for any of the attributes selected.',
'woocommerce'
)
}
value={ queryType }
onChange={ ( value: string ) =>
setAttributes( {
queryType: value,
} )
}
className="wc-block-attribute-filter__conditions-toggle"
>
<ToggleGroupControlOption
value="and"
label={ __( 'All', 'woocommerce' ) }
/>
<ToggleGroupControlOption
value="or"
label={ __( 'Any', 'woocommerce' ) }
/>
</ToggleGroupControl>
) }
<ToggleGroupControl
label={ __( 'Display Style', 'woocommerce' ) }
value={ displayStyle }
onChange={ ( value: string ) =>
setAttributes( {
displayStyle: value,
} )
}
className="wc-block-attribute-filter__display-toggle"
>
<ToggleGroupControlOption
value="list"
label={ __( 'List', 'woocommerce' ) }
/>
<ToggleGroupControlOption
value="dropdown"
label={ __( 'Dropdown', 'woocommerce' ) }
/>
</ToggleGroupControl>
</PanelBody>
</InspectorControls>
);
};