Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

MVP: Attribute filter block powered interactivity API #11749

Merged
merged 113 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from 109 commits
Commits
Show all changes
113 commits
Select commit Hold shift + click to select a range
1c582ed
Update Interactivity API JS files
DAreRodz Jun 19, 2023
cd0af51
Disable TS checks in the Interactivity API for now
DAreRodz Jun 19, 2023
5528a2f
Add new SSR files
DAreRodz Jun 19, 2023
e6d5a25
Replace wp_ prefixes with wc_ ones
DAreRodz Jun 19, 2023
68fb185
Replace wp- prefix with wc-
DAreRodz Jun 19, 2023
bf443b8
Replace guternberg_ prefix with woocommerce_
DAreRodz Jun 19, 2023
2e070ff
Remove file comments from Gutenberg
DAreRodz Jun 19, 2023
ada935d
Rename files with `wp` prefix
DAreRodz Jun 19, 2023
9185199
Fix code to load Interactivity API php files
DAreRodz Jun 19, 2023
fd08faa
Remove TODO comments
DAreRodz Jun 19, 2023
901151b
Replace @wordpress with @woocommerce
DAreRodz Jun 19, 2023
4f653f5
Update Webpack configuration
DAreRodz Jun 19, 2023
8920120
Fix directive prefix
DAreRodz Jun 19, 2023
9d14a7e
Remove interactivity folder from tsconfig exclude
DAreRodz Jun 19, 2023
872e827
Add client-side navigation meta tag code
DAreRodz Jun 19, 2023
ad7d14a
Remove unneeded blocks.php file
DAreRodz Jun 19, 2023
b7488bb
Fix store tag id
DAreRodz Jun 20, 2023
7ae8b81
Register Interactivity API runtime script
DAreRodz Jun 20, 2023
b27eedf
Fix Interactivity API runtime registering
DAreRodz Jun 20, 2023
98c6bf4
Add Simple Price Filter block
DAreRodz Jun 20, 2023
f08b670
Remove all files related to directive processing in PHP
DAreRodz Jun 21, 2023
f1b79aa
Merge branch 'update/interactivity-api' into try/simple-price-filter-…
DAreRodz Jun 21, 2023
5b1eea0
Use values directly for SimplePriceFilter SSR
DAreRodz Jun 21, 2023
0e468fe
Merge branch 'trunk' into try/simple-price-filter-block-demo
luisherranz Aug 15, 2023
70636c9
Reset pages to 0 when changing filter
luisherranz Aug 15, 2023
24f25e0
Merge branch 'trunk' into try/new-price-filter
dinhtungdu Aug 24, 2023
c0897c4
wip
dinhtungdu Aug 24, 2023
97cf8fc
phpcs
dinhtungdu Aug 25, 2023
75ade2a
register price filter as inner block
dinhtungdu Aug 25, 2023
7e8e622
try: render block using save
dinhtungdu Aug 25, 2023
1b10c0b
add types
dinhtungdu Sep 4, 2023
01b9727
use min range var instead of 0
dinhtungdu Sep 4, 2023
c5b40ef
inject dynamic data
dinhtungdu Sep 4, 2023
0b22bef
query price data in editor
dinhtungdu Sep 5, 2023
982512c
better injecting interactivity data
dinhtungdu Sep 5, 2023
d4a9a70
remove rounding
dinhtungdu Sep 5, 2023
910e8aa
Product Collection Data endpoint doesn't care about current query so …
dinhtungdu Sep 6, 2023
e7c0052
extract data injecting as a method, possbily a trait in the future
dinhtungdu Sep 6, 2023
f07d684
add sidebar setting
dinhtungdu Sep 6, 2023
a831b0f
duplicating the markup in php render callback for safety
dinhtungdu Sep 7, 2023
fbe0113
remove directive from edit component
dinhtungdu Sep 8, 2023
dfec31f
show prices without decimal
dinhtungdu Sep 13, 2023
71aaddd
Merge branch 'trunk' into try/new-price-filter
dinhtungdu Sep 15, 2023
dd3441e
use final class
dinhtungdu Sep 15, 2023
fb481f3
use sample collection data response
dinhtungdu Sep 15, 2023
ff1a6e1
prepare for multiple styles support
dinhtungdu Sep 19, 2023
875b0df
Merge branch 'trunk' into try/new-price-filter
dinhtungdu Sep 19, 2023
485fb37
use collection data from context
dinhtungdu Sep 19, 2023
90c9a7f
cleanup props and props passing
dinhtungdu Sep 19, 2023
a6e490a
pass only necessary states
dinhtungdu Sep 19, 2023
920643e
retire heredoc in favor of late escaping
dinhtungdu Sep 19, 2023
aba37a1
reorganize style
dinhtungdu Sep 20, 2023
de79a33
inherit style from current price filter react component, pre extract …
dinhtungdu Sep 21, 2023
985fad5
Merge branch 'trunk' into try/new-price-filter
dinhtungdu Oct 1, 2023
a0ea0f7
keep minPrice smaller than max
dinhtungdu Oct 3, 2023
54731f7
remove unnecessary active handler logic
dinhtungdu Oct 4, 2023
ee2dd11
update folder structure
dinhtungdu Oct 4, 2023
896fa5b
avoid whitespace change
dinhtungdu Oct 4, 2023
584d68d
clean up
dinhtungdu Oct 4, 2023
f18618a
title
dinhtungdu Oct 4, 2023
3559653
move inspector to component folder, ready to be extracted to inner block
dinhtungdu Oct 4, 2023
f88ab65
block icon
dinhtungdu Oct 4, 2023
6d7e27e
block name
dinhtungdu Oct 4, 2023
c130034
name
dinhtungdu Oct 4, 2023
1770f79
use inner block for view
dinhtungdu Oct 5, 2023
f73d701
inner block view switcher
dinhtungdu Oct 5, 2023
415da9a
Merge branch 'trunk' into try/filter-block-as-view-block
dinhtungdu Oct 10, 2023
5f4e6bc
try: process data in Collection Filtes block only
dinhtungdu Oct 10, 2023
bca2163
wip: query collection data from collection filters block only
dinhtungdu Oct 13, 2023
80df8c9
provide all context from collection filters block
dinhtungdu Oct 14, 2023
dc39507
Merge branch 'trunk' into try/filter-block-as-view-block
dinhtungdu Oct 14, 2023
b28eb65
simplify context passing
dinhtungdu Oct 14, 2023
913b1d1
feat: use default attribute to define filter type of view block
dinhtungdu Oct 14, 2023
6b3db54
rename
dinhtungdu Oct 14, 2023
de166fb
remove price block
dinhtungdu Oct 14, 2023
c82acd8
rename price slider to price, default price filter should be a slider
dinhtungdu Oct 15, 2023
d92ec5f
type cleanup
dinhtungdu Oct 15, 2023
fd1df2d
Merge branch 'trunk' into try/filter-block-as-view-block
dinhtungdu Oct 20, 2023
7e789fd
Merge branch 'trunk' into try/filter-block-as-view-block
dinhtungdu Oct 20, 2023
4c47d9e
fix ancestor block name
dinhtungdu Oct 23, 2023
555a0c6
only passing the collection data down
dinhtungdu Oct 26, 2023
19caf1a
wip
dinhtungdu Oct 26, 2023
09b93c9
recusive
dinhtungdu Oct 26, 2023
c57b03f
editor preview
dinhtungdu Oct 27, 2023
f56580b
refactor: data fetching, context passing, and code organization
dinhtungdu Oct 27, 2023
3db2817
initial attribute filter block
dinhtungdu Oct 25, 2023
c49b610
feat: attribute selector
dinhtungdu Oct 25, 2023
0ec9a12
inspector control
dinhtungdu Oct 27, 2023
b0c85a2
wip
dinhtungdu Oct 30, 2023
5cc7f3c
break the edit into smaller components
dinhtungdu Oct 30, 2023
d5d5ed7
wip: editor component
dinhtungdu Oct 31, 2023
3a84e93
extract checkbox list
dinhtungdu Nov 1, 2023
f7efd6a
wip editor preview
dinhtungdu Nov 1, 2023
40f2500
show count checkbox list
dinhtungdu Nov 7, 2023
7284e70
fix param aggregation for attribute filter blocks
dinhtungdu Nov 10, 2023
b845323
Merge branch 'trunk' into add/10812-new-attribute-filter-block-2
dinhtungdu Nov 10, 2023
ddd0bb3
post merge fix
dinhtungdu Nov 10, 2023
682baab
fix param aggregation for attribute filters
dinhtungdu Nov 13, 2023
f208fe9
fix: set correct selected attribute
dinhtungdu Nov 14, 2023
998de87
WIP checkbox list
dinhtungdu Nov 14, 2023
3106442
WIP checkbox list
dinhtungdu Nov 15, 2023
dcddb1b
Merge branch 'trunk' into add/10812-new-attribute-filter-block-2
dinhtungdu Nov 15, 2023
8699435
avoid action name conflicts
dinhtungdu Nov 15, 2023
2e561a2
Checkbox list front end
dinhtungdu Nov 15, 2023
3e0b039
phpcs
dinhtungdu Nov 16, 2023
493f778
update context on input, navigate on change
dinhtungdu Nov 16, 2023
1f33328
fix: attribute selection
dinhtungdu Nov 16, 2023
d6b1c45
dropdown
dinhtungdu Nov 16, 2023
a78e317
Merge branch 'trunk' into add/10812-new-attribute-filter-block-2
dinhtungdu Nov 16, 2023
041fce3
remove isDeepEqual
dinhtungdu Nov 20, 2023
66949ba
add: warning when attribute has no products or no attribute is selected
dinhtungdu Nov 20, 2023
95b56cc
update type
dinhtungdu Nov 21, 2023
51ee5d3
update type
dinhtungdu Nov 21, 2023
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
@@ -0,0 +1,52 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"name": "woocommerce/collection-attribute-filter",
"version": "1.0.0",
"title": "Collection Attribute Filter",
"description": "Enable customers to filter the product grid by selecting one or more attributes, such as color.",
"category": "woocommerce",
"keywords": [
"WooCommerce"
],
"textdomain": "woo-gutenberg-products-block",
"apiVersion": 2,
"ancestor": [
"woocommerce/collection-filters"
],
"supports": {
"interactivity": true
},
"usesContext": [
"collectionData"
],
"attributes": {
"queryParam": {
"type": "object",
"default": {}
},
"attributeId": {
"type": "number",
"default": 0
},
"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
}
}
}
@@ -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="attribute-checkbox-list"
onChange={ () => null }
options={ attributeTerms.map( ( term ) => ( {
label: (
<FilterElementLabel
name={ term.name }
count={ showCounts ? term.count : null }
/>
),
value: term.slug,
} ) ) }
/>
);
@@ -0,0 +1,26 @@
/**
* External dependencies
*/
import FormTokenField from '@woocommerce/base-components/form-token-field';
import { AttributeObject } from '@woocommerce/types';
import { __, sprintf } from '@wordpress/i18n';
import { Icon, chevronDown } from '@wordpress/icons';

type Props = {
attributeObject: Partial< AttributeObject > | undefined;
};
export const AttributeDropdown = ( { attributeObject }: Props ) => (
<div className="attribute-dropdown">
<FormTokenField
suggestions={ [] }
placeholder={ sprintf(
/* translators: %s attribute name. */
__( 'Select %s', 'woo-gutenberg-products-block' ),
attributeObject?.label
) }
onChange={ () => null }
value={ [] }
/>
<Icon icon={ chevronDown } size={ 30 } />
</div>
);
@@ -0,0 +1,89 @@
/**
* 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', 'woo-gutenberg-products-block' ),
list: __( 'Product Attributes', 'woo-gutenberg-products-block' ),
noItems: __(
"Your store doesn't have any product attributes.",
'woo-gutenberg-products-block'
),
search: __(
'Search for a product attribute:',
'woo-gutenberg-products-block'
),
selected: ( n: number ) =>
sprintf(
/* translators: %d is the number of attributes selected. */
_n(
'%d attribute selected',
'%d attributes selected',
n,
'woo-gutenberg-products-block'
),
n
),
updated: __(
'Product attribute search results updated.',
'woo-gutenberg-products-block'
),
};

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 }
/>
);
};
@@ -0,0 +1,151 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { InspectorControls } from '@wordpress/block-editor';
import {
PanelBody,
ToggleControl,
// 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';

/**
* Internal dependencies
*/
import { AttributeSelectControls } from './attribute-select-controls';
import { EditProps } from '../types';

export const Inspector = ( { attributes, setAttributes }: EditProps ) => {
const { attributeId, showCounts, queryType, displayStyle, selectType } =
attributes;
return (
<InspectorControls key="inspector">
<PanelBody
title={ __(
'Display Settings',
'woo-gutenberg-products-block'
) }
>
<ToggleControl
label={ __(
'Display product count',
'woo-gutenberg-products-block'
) }
checked={ showCounts }
onChange={ () =>
setAttributes( {
showCounts: ! showCounts,
} )
}
/>
<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>
{ selectType === 'multiple' && (
<ToggleGroupControl
label={ __(
'Filter Conditions',
'woo-gutenberg-products-block'
) }
help={
queryType === 'and'
? __(
'Choose to return filter results for all of the attributes selected.',
'woo-gutenberg-products-block'
)
: __(
'Choose to return filter results for any of the attributes selected.',
'woo-gutenberg-products-block'
)
}
value={ queryType }
onChange={ ( value: string ) =>
setAttributes( {
queryType: value,
} )
}
className="wc-block-attribute-filter__conditions-toggle"
>
<ToggleGroupControlOption
value="and"
label={ __(
'All',
'woo-gutenberg-products-block'
) }
/>
<ToggleGroupControlOption
value="or"
label={ __(
'Any',
'woo-gutenberg-products-block'
) }
/>
</ToggleGroupControl>
) }
<ToggleGroupControl
label={ __(
'Display Style',
'woo-gutenberg-products-block'
) }
value={ displayStyle }
onChange={ ( value: string ) =>
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>
</PanelBody>
<PanelBody
title={ __(
'Content Settings',
'woo-gutenberg-products-block'
) }
initialOpen={ false }
>
<AttributeSelectControls
isCompact={ true }
attributeId={ attributeId }
setAttributes={ setAttributes }
/>
</PanelBody>
</InspectorControls>
);
};