Skip to content
This repository was archived by the owner on Feb 23, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
47a36b2
Block setup
mikejolley Nov 4, 2019
dd1fe39
Working filtering and intersection of arrays
mikejolley Nov 4, 2019
7f36fdf
Implement block settings and no attribute placeholder
mikejolley Nov 4, 2019
63c3489
Correctly toggle counts
mikejolley Nov 4, 2019
2a4bf48
Implement filtering
mikejolley Nov 4, 2019
3f2e1df
Fix price slider constraints
mikejolley Nov 4, 2019
378d861
Fix price slider constraints
mikejolley Nov 4, 2019
8941166
Edit mode
mikejolley Nov 5, 2019
d1695c5
Rename ProductAttributeControl to ProductAttributeTermControl
mikejolley Nov 5, 2019
b92b99d
Attribute ID saving
mikejolley Nov 5, 2019
7d90236
fix incorrect test fixtures
nerrad Nov 4, 2019
98fae73
fix incorrect regex for parsing model (or resource names) from the ro…
nerrad Nov 4, 2019
4aeaed1
Fix query classes for some endpoints
mikejolley Nov 5, 2019
dcb704c
Style improvements
mikejolley Nov 5, 2019
7c26a00
Update inline comments
mikejolley Nov 6, 2019
2792bac
use previous tests
mikejolley Nov 6, 2019
9e3ef5d
Show attribute control in sidebar
mikejolley Nov 6, 2019
56f8d95
Remove displayStyle option
mikejolley Nov 6, 2019
62711cf
Sort attributes by name
mikejolley Nov 6, 2019
505ce96
Merge branch 'master' into update/price-slider-constraints
mikejolley Nov 6, 2019
b02150c
Merge branch 'master' into experiment/filter-by-attribute
mikejolley Nov 6, 2019
b8a7780
Merge branch 'update/price-slider-constraints' into experiment/filter…
mikejolley Nov 6, 2019
e95e19c
Show more/less toggle
mikejolley Nov 6, 2019
d11aa29
Merge branch 'master' into experiment/filter-by-attribute
mikejolley Nov 7, 2019
18562a0
Use renderFrontend
mikejolley Nov 7, 2019
c5dfef4
Only sort when adding values
mikejolley Nov 7, 2019
9b955e8
Rename memo placeholder
mikejolley Nov 7, 2019
21c244d
More specific CSS for pointer
mikejolley Nov 7, 2019
df96edb
Update assets/js/base/hooks/use-query-state.js
mikejolley Nov 7, 2019
8aa6811
Remove always true taxonomy check
mikejolley Nov 7, 2019
0109336
Update assets/js/blocks/attribute-filter/block.js
mikejolley Nov 7, 2019
24bba5c
Merge branch 'experiment/filter-by-attribute' of https://github.com/w…
mikejolley Nov 7, 2019
0af54f9
Remove lodash join
mikejolley Nov 7, 2019
e830631
native js for string casting
mikejolley Nov 7, 2019
a0dc217
Move internal deps
mikejolley Nov 7, 2019
97561a6
hyphenate attributes
mikejolley Nov 7, 2019
171fb41
Correct data set names
mikejolley Nov 7, 2019
18cb5d8
Remove unwanted dependency
mikejolley Nov 7, 2019
81b8abb
Moving imports
mikejolley Nov 7, 2019
0ba0591
Missing deps
mikejolley Nov 7, 2019
ae21f20
replace yoda conditonal
mikejolley Nov 7, 2019
d9a691c
Missing deps
mikejolley Nov 7, 2019
1a8e4af
Missing deps
mikejolley Nov 7, 2019
b1a24d6
Check value exists
mikejolley Nov 7, 2019
b4bcd39
Remove undefined filter
mikejolley Nov 7, 2019
9c14ae7
renderedOptions usememo
mikejolley Nov 7, 2019
2270310
Set defaults in checkbox list
mikejolley Nov 7, 2019
bc8dbfd
Show more/less refactor
mikejolley Nov 7, 2019
ca426f4
Use getAdminLink
mikejolley Nov 7, 2019
4a02e64
Fix object length check
mikejolley Nov 7, 2019
cdb3f09
Correct AND query handling for counts
mikejolley Nov 7, 2019
0867c94
Merge branch 'master' into experiment/filter-by-attribute
mikejolley Nov 7, 2019
1d53d56
useQueryStateByContext
mikejolley Nov 7, 2019
e250eba
Add store rest endpoints
mikejolley Nov 7, 2019
fba12a2
Update assets/js/base/components/checkbox-list/index.js
mikejolley Nov 7, 2019
7631fb8
Update assets/js/base/components/checkbox-list/index.js
mikejolley Nov 7, 2019
4e1f250
Update assets/js/base/components/checkbox-list/index.js
mikejolley Nov 7, 2019
dc492a2
Update assets/js/blocks/attribute-filter/block.js
mikejolley Nov 7, 2019
7785207
Feedback
mikejolley Nov 7, 2019
53ba361
Merge branch 'master' into experiment/filter-by-attribute
mikejolley Nov 7, 2019
8c22ba0
Merge conflict
mikejolley Nov 7, 2019
129aebe
feedback
mikejolley Nov 8, 2019
4a77711
API readme
mikejolley Nov 8, 2019
0432ef7
Fix API relation queries for multiple attributes
mikejolley Nov 8, 2019
607aeee
Prevent all options flashing visible during loads
mikejolley Nov 8, 2019
ab2351f
null check
mikejolley Nov 8, 2019
ded36b6
Improve loading state
mikejolley Nov 8, 2019
e87da8c
Remove null options change - it's no longer needed
mikejolley Nov 8, 2019
bbcebcb
update from master
mikejolley Nov 11, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Attribute ID saving
  • Loading branch information
mikejolley committed Nov 5, 2019
commit b92b99d39b3e85606c1b6f0195d2ac2db35c14d1
46 changes: 41 additions & 5 deletions assets/js/base/components/checkbox-list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
* External dependencies
*/
import PropTypes from 'prop-types';
import { Fragment, useCallback, useMemo } from '@wordpress/element';
import {
Fragment,
useCallback,
useMemo,
useState,
useEffect,
} from '@wordpress/element';
import classNames from 'classnames';

/**
Expand All @@ -14,6 +20,13 @@ import './style.scss';
* Component used to show a list of checkboxes in a group.
*/
const CheckboxList = ( { className, onChange, options, isLoading } ) => {
// Holds all checked options.
const [ checked, setChecked ] = useState( [] );

useEffect( () => {
onChange( checked );
}, [ checked ] );

const renderPlaceholder = useMemo( () => {
return (
<Fragment>
Expand All @@ -30,6 +43,25 @@ const CheckboxList = ( { className, onChange, options, isLoading } ) => {
);
}, [] );

const onCheckboxChange = useCallback(
( event ) => {
const isChecked = event.target.checked;
const checkedValue = event.target.value;
const newChecked = checked;

if ( ! isChecked ) {
newChecked.filter( ( value ) => value !== checkedValue );
} else {
newChecked.push( checkedValue );
}

newChecked.sort();

setChecked( newChecked );
},
[ options, checked ]
);

const renderOptions = useCallback( () => {
return (
<Fragment>
Expand All @@ -38,16 +70,20 @@ const CheckboxList = ( { className, onChange, options, isLoading } ) => {
<input
type="checkbox"
id={ option.key }
name={ option.key }
value="1"
onChange={ onChange }
value={ option.key }
onChange={ onCheckboxChange }
checked={
checked.includes( option.key )
? 'checked'
: false
}
/>
<label htmlFor={ option.key }>{ option.label }</label>
</li>
) ) }
</Fragment>
);
}, [ options ] );
}, [ options, checked ] );

const listClass = classNames(
'wc-block-checkbox-list',
Expand Down
64 changes: 35 additions & 29 deletions assets/js/blocks/attribute-filter/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
useQueryStateContext,
} from '@woocommerce/base-hooks';
import { useCallback, Fragment, useEffect, useState } from '@wordpress/element';
import { keyBy, invert, map, trim, split, join } from 'lodash';
import { find, join, findIndex } from 'lodash';
import { ATTRIBUTES } from '@woocommerce/block-settings';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think ATTRIBUTES is something we'll want to bring in via api for client powered blocks. Eventually we could completely drop this from being provided by the server (I know we can't do that yet because of legacy blocks). Create a followup issue?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Attributes is something I consider a setting that doesn't change often, and I think getting this from the server will be more performant that more API requests. It's related to the users woo environment... like post counts or other settings we pull here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What potential is there for the size of the attributes data package? Is it typical for it to be a smaller number of bytes or larger? Reason I ask is because I wonder if it could impact time to first paint of a site loading by virtue of increasing the html package for large data sets. That's where lazy loading of attributes might have more of an impact. Granted, if the typical number of attributes is fairly small, this would definitely be an over-optimization.

I'm fine with not addressing this right now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nerrad Attributes (these are taxonomies) can vary in size and is unknown, however, woo core already caches these in a transient and they are loaded on each page anyway (to register them as taxonomies), so there is no negative to using these as block settings as it's essentially 'free' vs an API request.


/**
* Internal dependencies
Expand All @@ -22,6 +23,8 @@ const AttributeFilterBlock = ( { attributes } ) => {
const { showCounts, attributeId, queryType } = attributes;

const [ options, setOptions ] = useState( [] );
const [ checkedOptions, setCheckedOptions ] = useState( [] );
const [ currentAttribute, setCurrentAttribute ] = useState( [] );

const [ productAttributes, setProductAttributes ] = useQueryStateByKey(
'product-grid',
Expand Down Expand Up @@ -116,40 +119,43 @@ const AttributeFilterBlock = ( { attributes } ) => {
allTermsIsLoading,
] );

const onChange = useCallback( ( event ) => {
const checked = event.target.checked;
const slug = event.target.name;
const keyedAttributes = keyBy( productAttributes, 'attribute' );

// Get current terms as array.
const terms =
keyedAttributes.pa_color && keyedAttributes.pa_color.slug
? invert(
map( split( keyedAttributes.pa_color.slug, ',' ), trim )
)
: [];

if ( checked ) {
terms[ slug ] = 1;
} else {
delete terms[ slug ];
}

keyedAttributes.pa_color = {};
keyedAttributes.pa_color.attribute = 'pa_color';
keyedAttributes.pa_color.slug = join(
Object.values( invert( terms ) ).filter( Boolean ),
','
useEffect( () => {
setCurrentAttribute(
find( ATTRIBUTES, [ 'attribute_id', attributeId + '' ] )
);
keyedAttributes.pa_color.operator = 'or' === queryType ? 'in' : 'and';
}, [ attributeId ] );

if ( ! keyedAttributes.pa_color.slug ) {
delete keyedAttributes.pa_color;
useEffect( () => {
const taxonomy = currentAttribute.attribute_name;
const newProductAttributes = productAttributes;
const currentQueryIndex = findIndex( newProductAttributes, [
'attribute',
taxonomy,
] );

const updatedQuery = {
attribute: taxonomy,
operator: 'or' === queryType ? 'in' : 'and',
slug: join( checkedOptions, ',' ),
};

if ( ! updatedQuery.slug ) {
delete newProductAttributes[ currentQueryIndex ];
} else {
newProductAttributes[ currentQueryIndex ] = updatedQuery;
}

setProductAttributes( Object.values( keyedAttributes ) );
setProductAttributes( newProductAttributes );
}, [ checkedOptions, currentAttribute, productAttributes ] );

const onChange = useCallback( ( checked ) => {
setCheckedOptions( checked );
}, [] );

if ( ! currentAttribute ) {
return null;
}

return (
<div className="wc-block-attribute-filter">
<CheckboxList
Expand Down
85 changes: 71 additions & 14 deletions assets/js/blocks/attribute-filter/edit.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Fragment, useState } from '@wordpress/element';
import { __, sprintf, _n } from '@wordpress/i18n';
import { Fragment, useState, useCallback } from '@wordpress/element';
import { InspectorControls, BlockControls } from '@wordpress/editor';
import {
Placeholder,
Expand All @@ -15,6 +15,8 @@ import {
} from '@wordpress/components';
import Gridicon from 'gridicons';
import { ATTRIBUTES } from '@woocommerce/block-settings';
import { SearchListControl } from '@woocommerce/components';
import { mapValues, toArray } from 'lodash';

/**
* Internal dependencies
Expand All @@ -24,7 +26,6 @@ import './editor.scss';
import { IconExternal } from '../../components/icons';
import { ADMIN_URL } from '@woocommerce/settings';
import ToggleButtonControl from '../../components/toggle-button-control';
import ProductAttributeControl from '@woocommerce/block-components/product-attribute-control';

const Edit = ( { attributes, setAttributes, debouncedSpeak } ) => {
const [ isEditing, setIsEditing ] = useState( ! attributes.attributeId );
Expand Down Expand Up @@ -154,7 +155,7 @@ const Edit = ( { attributes, setAttributes, debouncedSpeak } ) => {
'woo-gutenberg-products-block'
) }
instructions={ __(
'Display a list of filters based on a chosen product attribute.',
'Display a list of filters based on a chosen attribute.',
'woo-gutenberg-products-block'
) }
>
Expand Down Expand Up @@ -187,17 +188,64 @@ const Edit = ( { attributes, setAttributes, debouncedSpeak } ) => {
</Placeholder>
);

const onDone = useCallback( () => {
setIsEditing( false );
debouncedSpeak(
__(
'Showing attribute filter block preview.',
'woo-gutenberg-products-block'
)
);
}, [] );

const onChange = useCallback( ( selected ) => {
setAttributes( {
attributeId: selected[ 0 ].id,
} );
}, [] );

const renderEditMode = () => {
const onDone = () => {
setIsEditing( false );
debouncedSpeak(
__(
'Showing attribute filter block preview.',
'woo-gutenberg-products-block'
)
);
const { attributeId } = attributes;

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 product attribute',
'woo-gutenberg-products-block'
),
selected: ( n ) =>
sprintf(
_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 = toArray(
mapValues( ATTRIBUTES, ( item ) => {
return {
id: parseInt( item.attribute_id, 10 ),
name: item.attribute_label,
};
} )
);

return (
<Placeholder
className="wc-block-attribute-filter"
Expand All @@ -207,12 +255,21 @@ const Edit = ( { attributes, setAttributes, debouncedSpeak } ) => {
'woo-gutenberg-products-block'
) }
instructions={ __(
'Display a list of filters based on a chosen product attribute.',
'Display a list of filters based on a chosen attribute.',
'woo-gutenberg-products-block'
) }
>
<div className="wc-block-attribute-filter__selection">
<ProductAttributeControl onChange={ () => {} } />
<SearchListControl
className="woocommerce-product-attributes"
list={ list }
selected={ list.filter(
( { id } ) => id === attributeId
) }
onChange={ onChange }
messages={ messages }
isSingle
/>
<Button isDefault onClick={ onDone }>
{ __( 'Done', 'woo-gutenberg-products-block' ) }
</Button>
Expand Down
5 changes: 5 additions & 0 deletions assets/js/blocks/attribute-filter/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
font-size: 14px;
}
}
.woocommerce-search-list__search {
border-top: 0;
margin-top: 0;
padding-top: 0;
}
.wc-block-attribute-filter__add_attribute_button {
margin: 0 0 1em;
line-height: 24px;
Expand Down
6 changes: 5 additions & 1 deletion assets/js/blocks/attribute-filter/frontend.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ const containers = document.querySelectorAll(
if ( containers.length ) {
Array.prototype.forEach.call( containers, ( el ) => {
const attributes = {
attributeId: el.dataset.attributeid || 1, // @todo
attributeId: parseInt( el.dataset.attributeid || 0, 10 ),
showCounts: el.dataset.showcounts === 'true',
displayStyle: el.dataset.displaystyle,
queryType: el.dataset.querytype,
};
el.classList.remove( 'is-loading' );

if ( ! attributes.attributeId ) {
return null;
}

render( <Block attributes={ attributes } />, el );
} );
}
3 changes: 2 additions & 1 deletion assets/js/blocks/attribute-filter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ registerBlockType( 'woocommerce/attribute-filter', {
* Save the props to post content.
*/
save( { attributes } ) {
const { showCounts, displayStyle, queryType } = attributes;
const { showCounts, displayStyle, queryType, attributeId } = attributes;
const data = {
'data-attributeid': attributeId,
'data-showcounts': showCounts,
'data-displaystyle': displayStyle,
'data-querytype': queryType,
Expand Down