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

Add "Filter Products by Stock" block #4145

Merged
merged 19 commits into from
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ import { _n, sprintf } from '@wordpress/i18n';
import Label from '@woocommerce/base-components/label';

/**
* The label for an attribute term filter.
* Internal dependencies
*/
import './style.scss';

/**
* The label for an filter elements.
*
* @param {Object} props Incoming props for the component.
* @param {string} props.name The name for the label.
* @param {number} props.count The count of products this attribute is attached to.
* @param {number} props.count The count of products this status is attached to.
*/
const AttributeFilterLabel = ( { name, count } ) => {
const FilterElementLabel = ( { name, count } ) => {
return (
<>
{ name }
Expand All @@ -30,12 +35,12 @@ const AttributeFilterLabel = ( { name, count } ) => {
) }
wrapperElement="span"
wrapperProps={ {
className: 'wc-block-attribute-filter-list-count',
className: 'wc-filter-element-label-list-count',
} }
/>
) }
</>
);
};

export default AttributeFilterLabel;
export default FilterElementLabel;
9 changes: 9 additions & 0 deletions assets/js/base/components/filter-element-label/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.wc-filter-element-label-list-count {
&::before {
content: " (";
}
&::after {
content: ")";
}
opacity: 0.6;
}
6 changes: 1 addition & 5 deletions assets/js/base/components/product-list/container.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ import PropTypes from 'prop-types';
*/
import ProductList from './product-list';

const ProductListContainer = ( {
attributes,
hideOutOfStockItems = false,
} ) => {
const ProductListContainer = ( { attributes } ) => {
const [ currentPage, setPage ] = useState( 1 );
const [ currentSort, setSort ] = useState( attributes.orderby );
useEffect( () => {
Expand All @@ -31,7 +28,6 @@ const ProductListContainer = ( {
return (
<ProductList
attributes={ attributes }
hideOutOfStockItems={ hideOutOfStockItems }
currentPage={ currentPage }
onPageChange={ onPageChange }
onSortChange={ onSortChange }
Expand Down
35 changes: 15 additions & 20 deletions assets/js/base/components/product-list/product-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,7 @@ import ProductSortSelect from './product-sort-select';
import ProductListItem from './product-list-item';
import './style.scss';

const generateQuery = ( {
sortValue,
currentPage,
attributes,
hideOutOfStockItems,
} ) => {
const generateQuery = ( { sortValue, currentPage, attributes } ) => {
const { columns, rows } = attributes;
const getSortArgs = ( orderName ) => {
switch ( orderName ) {
Expand Down Expand Up @@ -62,9 +57,6 @@ const generateQuery = ( {
catalog_visibility: 'catalog',
per_page: columns * rows,
page: currentPage,
...( hideOutOfStockItems && {
stock_status: [ 'instock', 'onbackorder' ],
} ),
};
};

Expand Down Expand Up @@ -118,14 +110,24 @@ const ProductList = ( {
onSortChange,
sortValue,
scrollToTop,
hideOutOfStockItems = false,
} ) => {
// These are possible filters.
const [ productAttributes, setProductAttributes ] = useQueryStateByKey(
'attributes',
[]
);
const [ productStockStatus, setProductStockStatus ] = useQueryStateByKey(
'stock_status',
[]
);
const [ minPrice, setMinPrice ] = useQueryStateByKey( 'min_price' );
const [ maxPrice, setMaxPrice ] = useQueryStateByKey( 'max_price' );

const [ queryState ] = useSynchronizedQueryState(
generateQuery( {
attributes,
sortValue,
currentPage,
hideOutOfStockItems,
} )
);
const { products, totalProducts, productsLoading } = useStoreProducts(
Expand All @@ -135,14 +137,6 @@ const ProductList = ( {
const totalQuery = extractPaginationAndSortAttributes( queryState );
const { dispatchStoreEvent } = useStoreEvents();

// These are possible filters.
const [ productAttributes, setProductAttributes ] = useQueryStateByKey(
'attributes',
[]
);
const [ minPrice, setMinPrice ] = useQueryStateByKey( 'min_price' );
const [ maxPrice, setMaxPrice ] = useQueryStateByKey( 'max_price' );

// Only update previous query totals if the query is different and the total number of products is a finite number.
const previousQueryTotals = usePrevious(
{ totalQuery, totalProducts },
Expand Down Expand Up @@ -209,6 +203,7 @@ const ProductList = ( {
const hasProducts = products.length !== 0 || productsLoading;
const hasFilters =
productAttributes.length > 0 ||
productStockStatus.length > 0 ||
Number.isFinite( minPrice ) ||
Number.isFinite( maxPrice );

Expand All @@ -224,6 +219,7 @@ const ProductList = ( {
<NoMatchingProducts
resetCallback={ () => {
setProductAttributes( [] );
setProductStockStatus( [] );
setMinPrice( null );
setMaxPrice( null );
} }
Expand Down Expand Up @@ -254,7 +250,6 @@ const ProductList = ( {

ProductList.propTypes = {
attributes: PropTypes.object.isRequired,
hideOutOfStockItems: PropTypes.bool,
// From withScrollToTop.
scrollToTop: PropTypes.func,
};
Expand Down
19 changes: 19 additions & 0 deletions assets/js/base/context/hooks/collections/use-collection-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const buildCollectionDataQuery = ( collectionDataQueryState ) => {
export const useCollectionData = ( {
queryAttribute,
queryPrices,
queryStock,
queryState,
} ) => {
let context = useQueryStateContext();
Expand All @@ -50,9 +51,14 @@ export const useCollectionData = ( {
calculatePriceRangeQueryState,
setCalculatePriceRangeQueryState,
] = useQueryStateByKey( 'calculate_price_range', null, context );
const [
calculateStockStatusQueryState,
setCalculateStockStatusQueryState,
] = useQueryStateByKey( 'calculate_stock_status_counts', null, context );

const currentQueryAttribute = useShallowEqual( queryAttribute || {} );
const currentQueryPrices = useShallowEqual( queryPrices );
const currentQueryStock = useShallowEqual( queryStock );

useEffect( () => {
if (
Expand Down Expand Up @@ -93,6 +99,19 @@ export const useCollectionData = ( {
calculatePriceRangeQueryState,
] );

useEffect( () => {
if (
calculateStockStatusQueryState !== currentQueryStock &&
currentQueryStock !== undefined
) {
setCalculateStockStatusQueryState( currentQueryStock );
}
}, [
currentQueryStock,
setCalculateStockStatusQueryState,
calculateStockStatusQueryState,
] );

// Defer the select query so all collection-data query vars can be gathered.
const [ shouldSelect, setShouldSelect ] = useState( false );
const [ debouncedShouldSelect ] = useDebounce( shouldSelect, 200 );
Expand Down
34 changes: 34 additions & 0 deletions assets/js/blocks/active-filters/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
import { __ } from '@wordpress/i18n';
import { useQueryStateByKey } from '@woocommerce/base-context/hooks';
import { getSetting } from '@woocommerce/settings';
import { useMemo } from '@wordpress/element';
import classnames from 'classnames';
import PropTypes from 'prop-types';
Expand Down Expand Up @@ -31,9 +32,39 @@ const ActiveFiltersBlock = ( {
'attributes',
[]
);
const [ productStockStatus, setProductStockStatus ] = useQueryStateByKey(
'stock_status',
[]
);
const [ minPrice, setMinPrice ] = useQueryStateByKey( 'min_price' );
const [ maxPrice, setMaxPrice ] = useQueryStateByKey( 'max_price' );

const STOCK_STATUS_OPTIONS = getSetting( 'stockStatusOptions', [] );
const activeStockStatusFilters = useMemo( () => {
if ( productStockStatus.length > 0 ) {
return productStockStatus.map( ( slug ) => {
return renderRemovableListItem( {
type: __( 'Stock Status', 'woo-gutenberg-products-block' ),
name: STOCK_STATUS_OPTIONS[ slug ],
removeCallback: () => {
const newStatuses = productStockStatus.filter(
( status ) => {
return status !== slug;
}
);
setProductStockStatus( newStatuses );
},
displayStyle: blockAttributes.displayStyle,
} );
} );
}
}, [
STOCK_STATUS_OPTIONS,
productStockStatus,
setProductStockStatus,
blockAttributes.displayStyle,
] );

const activePriceFilters = useMemo( () => {
if ( ! Number.isFinite( minPrice ) && ! Number.isFinite( maxPrice ) ) {
return null;
Expand Down Expand Up @@ -75,6 +106,7 @@ const ActiveFiltersBlock = ( {
const hasFilters = () => {
return (
productAttributes.length > 0 ||
productStockStatus.length > 0 ||
Number.isFinite( minPrice ) ||
Number.isFinite( maxPrice )
);
Expand Down Expand Up @@ -125,6 +157,7 @@ const ActiveFiltersBlock = ( {
) : (
<>
{ activePriceFilters }
{ activeStockStatusFilters }
{ activeAttributeFilters }
</>
) }
Expand All @@ -135,6 +168,7 @@ const ActiveFiltersBlock = ( {
setMinPrice( undefined );
setMaxPrice( undefined );
setProductAttributes( [] );
setProductStockStatus( [] );
} }
>
<Label
Expand Down
2 changes: 1 addition & 1 deletion assets/js/blocks/attribute-filter/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { useCallback, useEffect, useState, 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/filter-element-label';
import FilterSubmitButton from '@woocommerce/base-components/filter-submit-button';
import isShallowEqual from '@wordpress/is-shallow-equal';
import { decodeEntities } from '@wordpress/html-entities';
Expand All @@ -22,7 +23,6 @@ import { decodeEntities } from '@wordpress/html-entities';
*/
import { getAttributeFromID } from '../../utils/attributes';
import { updateAttributeFilter } from '../../utils/attributes-query';
import Label from './label';
import { previewAttributeObject, previewOptions } from './preview';
import './style.scss';

Expand Down
4 changes: 2 additions & 2 deletions assets/js/blocks/attribute-filter/preview.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Internal dependencies
* External dependencies
*/
import Label from './label';
import Label from '@woocommerce/base-components/filter-element-label';

export const previewOptions = [
{
Expand Down
13 changes: 0 additions & 13 deletions assets/js/blocks/attribute-filter/style.scss
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
.wc-block-attribute-filter {
margin-bottom: $gap-large;

.wc-block-attribute-filter-list-count {
&::before {
content: " (";
}
&::after {
content: ")";
}
}

.wc-block-attribute-filter-list {
margin: 0;

Expand All @@ -25,10 +16,6 @@
display: inline-block;
}
}

.wc-block-attribute-filter-list-count {
float: right;
}
}

.is-single .wc-block-attribute-filter-list-count,
Expand Down
4 changes: 0 additions & 4 deletions assets/js/blocks/products/all-products/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import PropTypes from 'prop-types';
import { ProductListContainer } from '@woocommerce/base-components/product-list';
import { InnerBlockLayoutContextProvider } from '@woocommerce/shared-context';
import { gridBlockPreview } from '@woocommerce/resource-previews';
import { getSetting } from '@woocommerce/settings';

/**
* The All Products Block.
Expand All @@ -26,8 +25,6 @@ class Block extends Component {
return gridBlockPreview;
}

const hideOutOfStockItems = getSetting( 'hideOutOfStockItems', false );

/**
* Todo classes
*
Expand All @@ -42,7 +39,6 @@ class Block extends Component {
<ProductListContainer
attributes={ attributes }
urlParameterSuffix={ urlParameterSuffix }
hideOutOfStockItems={ hideOutOfStockItems }
/>
</InnerBlockLayoutContextProvider>
);
Expand Down
Loading