Revert "Add "Filter Products by Stock" block (https://github.com/woocommerce/woocommerce-blocks/pull/4145)"
This reverts commit 44db8317a7
.
This commit is contained in:
parent
44db8317a7
commit
8f2bc114a4
|
@ -1,9 +0,0 @@
|
||||||
.wc-filter-element-label-list-count {
|
|
||||||
&::before {
|
|
||||||
content: " (";
|
|
||||||
}
|
|
||||||
&::after {
|
|
||||||
content: ")";
|
|
||||||
}
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
|
@ -9,7 +9,10 @@ import PropTypes from 'prop-types';
|
||||||
*/
|
*/
|
||||||
import ProductList from './product-list';
|
import ProductList from './product-list';
|
||||||
|
|
||||||
const ProductListContainer = ( { attributes } ) => {
|
const ProductListContainer = ( {
|
||||||
|
attributes,
|
||||||
|
hideOutOfStockItems = false,
|
||||||
|
} ) => {
|
||||||
const [ currentPage, setPage ] = useState( 1 );
|
const [ currentPage, setPage ] = useState( 1 );
|
||||||
const [ currentSort, setSort ] = useState( attributes.orderby );
|
const [ currentSort, setSort ] = useState( attributes.orderby );
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
|
@ -28,6 +31,7 @@ const ProductListContainer = ( { attributes } ) => {
|
||||||
return (
|
return (
|
||||||
<ProductList
|
<ProductList
|
||||||
attributes={ attributes }
|
attributes={ attributes }
|
||||||
|
hideOutOfStockItems={ hideOutOfStockItems }
|
||||||
currentPage={ currentPage }
|
currentPage={ currentPage }
|
||||||
onPageChange={ onPageChange }
|
onPageChange={ onPageChange }
|
||||||
onSortChange={ onSortChange }
|
onSortChange={ onSortChange }
|
||||||
|
|
|
@ -27,7 +27,12 @@ import ProductSortSelect from './product-sort-select';
|
||||||
import ProductListItem from './product-list-item';
|
import ProductListItem from './product-list-item';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
const generateQuery = ( { sortValue, currentPage, attributes } ) => {
|
const generateQuery = ( {
|
||||||
|
sortValue,
|
||||||
|
currentPage,
|
||||||
|
attributes,
|
||||||
|
hideOutOfStockItems,
|
||||||
|
} ) => {
|
||||||
const { columns, rows } = attributes;
|
const { columns, rows } = attributes;
|
||||||
const getSortArgs = ( orderName ) => {
|
const getSortArgs = ( orderName ) => {
|
||||||
switch ( orderName ) {
|
switch ( orderName ) {
|
||||||
|
@ -57,6 +62,9 @@ const generateQuery = ( { sortValue, currentPage, attributes } ) => {
|
||||||
catalog_visibility: 'catalog',
|
catalog_visibility: 'catalog',
|
||||||
per_page: columns * rows,
|
per_page: columns * rows,
|
||||||
page: currentPage,
|
page: currentPage,
|
||||||
|
...( hideOutOfStockItems && {
|
||||||
|
stock_status: [ 'instock', 'onbackorder' ],
|
||||||
|
} ),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -110,24 +118,14 @@ const ProductList = ( {
|
||||||
onSortChange,
|
onSortChange,
|
||||||
sortValue,
|
sortValue,
|
||||||
scrollToTop,
|
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(
|
const [ queryState ] = useSynchronizedQueryState(
|
||||||
generateQuery( {
|
generateQuery( {
|
||||||
attributes,
|
attributes,
|
||||||
sortValue,
|
sortValue,
|
||||||
currentPage,
|
currentPage,
|
||||||
|
hideOutOfStockItems,
|
||||||
} )
|
} )
|
||||||
);
|
);
|
||||||
const { products, totalProducts, productsLoading } = useStoreProducts(
|
const { products, totalProducts, productsLoading } = useStoreProducts(
|
||||||
|
@ -137,6 +135,14 @@ const ProductList = ( {
|
||||||
const totalQuery = extractPaginationAndSortAttributes( queryState );
|
const totalQuery = extractPaginationAndSortAttributes( queryState );
|
||||||
const { dispatchStoreEvent } = useStoreEvents();
|
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.
|
// Only update previous query totals if the query is different and the total number of products is a finite number.
|
||||||
const previousQueryTotals = usePrevious(
|
const previousQueryTotals = usePrevious(
|
||||||
{ totalQuery, totalProducts },
|
{ totalQuery, totalProducts },
|
||||||
|
@ -203,7 +209,6 @@ const ProductList = ( {
|
||||||
const hasProducts = products.length !== 0 || productsLoading;
|
const hasProducts = products.length !== 0 || productsLoading;
|
||||||
const hasFilters =
|
const hasFilters =
|
||||||
productAttributes.length > 0 ||
|
productAttributes.length > 0 ||
|
||||||
productStockStatus.length > 0 ||
|
|
||||||
Number.isFinite( minPrice ) ||
|
Number.isFinite( minPrice ) ||
|
||||||
Number.isFinite( maxPrice );
|
Number.isFinite( maxPrice );
|
||||||
|
|
||||||
|
@ -219,7 +224,6 @@ const ProductList = ( {
|
||||||
<NoMatchingProducts
|
<NoMatchingProducts
|
||||||
resetCallback={ () => {
|
resetCallback={ () => {
|
||||||
setProductAttributes( [] );
|
setProductAttributes( [] );
|
||||||
setProductStockStatus( [] );
|
|
||||||
setMinPrice( null );
|
setMinPrice( null );
|
||||||
setMaxPrice( null );
|
setMaxPrice( null );
|
||||||
} }
|
} }
|
||||||
|
@ -250,6 +254,7 @@ const ProductList = ( {
|
||||||
|
|
||||||
ProductList.propTypes = {
|
ProductList.propTypes = {
|
||||||
attributes: PropTypes.object.isRequired,
|
attributes: PropTypes.object.isRequired,
|
||||||
|
hideOutOfStockItems: PropTypes.bool,
|
||||||
// From withScrollToTop.
|
// From withScrollToTop.
|
||||||
scrollToTop: PropTypes.func,
|
scrollToTop: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,7 +36,6 @@ const buildCollectionDataQuery = ( collectionDataQueryState ) => {
|
||||||
export const useCollectionData = ( {
|
export const useCollectionData = ( {
|
||||||
queryAttribute,
|
queryAttribute,
|
||||||
queryPrices,
|
queryPrices,
|
||||||
queryStock,
|
|
||||||
queryState,
|
queryState,
|
||||||
} ) => {
|
} ) => {
|
||||||
let context = useQueryStateContext();
|
let context = useQueryStateContext();
|
||||||
|
@ -51,14 +50,9 @@ export const useCollectionData = ( {
|
||||||
calculatePriceRangeQueryState,
|
calculatePriceRangeQueryState,
|
||||||
setCalculatePriceRangeQueryState,
|
setCalculatePriceRangeQueryState,
|
||||||
] = useQueryStateByKey( 'calculate_price_range', null, context );
|
] = useQueryStateByKey( 'calculate_price_range', null, context );
|
||||||
const [
|
|
||||||
calculateStockStatusQueryState,
|
|
||||||
setCalculateStockStatusQueryState,
|
|
||||||
] = useQueryStateByKey( 'calculate_stock_status_counts', null, context );
|
|
||||||
|
|
||||||
const currentQueryAttribute = useShallowEqual( queryAttribute || {} );
|
const currentQueryAttribute = useShallowEqual( queryAttribute || {} );
|
||||||
const currentQueryPrices = useShallowEqual( queryPrices );
|
const currentQueryPrices = useShallowEqual( queryPrices );
|
||||||
const currentQueryStock = useShallowEqual( queryStock );
|
|
||||||
|
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
if (
|
if (
|
||||||
|
@ -99,19 +93,6 @@ export const useCollectionData = ( {
|
||||||
calculatePriceRangeQueryState,
|
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.
|
// Defer the select query so all collection-data query vars can be gathered.
|
||||||
const [ shouldSelect, setShouldSelect ] = useState( false );
|
const [ shouldSelect, setShouldSelect ] = useState( false );
|
||||||
const [ debouncedShouldSelect ] = useDebounce( shouldSelect, 200 );
|
const [ debouncedShouldSelect ] = useDebounce( shouldSelect, 200 );
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
*/
|
*/
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { useQueryStateByKey } from '@woocommerce/base-context/hooks';
|
import { useQueryStateByKey } from '@woocommerce/base-context/hooks';
|
||||||
import { getSetting } from '@woocommerce/settings';
|
|
||||||
import { useMemo } from '@wordpress/element';
|
import { useMemo } from '@wordpress/element';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
@ -32,39 +31,9 @@ const ActiveFiltersBlock = ( {
|
||||||
'attributes',
|
'attributes',
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
const [ productStockStatus, setProductStockStatus ] = useQueryStateByKey(
|
|
||||||
'stock_status',
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
const [ minPrice, setMinPrice ] = useQueryStateByKey( 'min_price' );
|
const [ minPrice, setMinPrice ] = useQueryStateByKey( 'min_price' );
|
||||||
const [ maxPrice, setMaxPrice ] = useQueryStateByKey( 'max_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( () => {
|
const activePriceFilters = useMemo( () => {
|
||||||
if ( ! Number.isFinite( minPrice ) && ! Number.isFinite( maxPrice ) ) {
|
if ( ! Number.isFinite( minPrice ) && ! Number.isFinite( maxPrice ) ) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -106,7 +75,6 @@ const ActiveFiltersBlock = ( {
|
||||||
const hasFilters = () => {
|
const hasFilters = () => {
|
||||||
return (
|
return (
|
||||||
productAttributes.length > 0 ||
|
productAttributes.length > 0 ||
|
||||||
productStockStatus.length > 0 ||
|
|
||||||
Number.isFinite( minPrice ) ||
|
Number.isFinite( minPrice ) ||
|
||||||
Number.isFinite( maxPrice )
|
Number.isFinite( maxPrice )
|
||||||
);
|
);
|
||||||
|
@ -157,7 +125,6 @@ const ActiveFiltersBlock = ( {
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{ activePriceFilters }
|
{ activePriceFilters }
|
||||||
{ activeStockStatusFilters }
|
|
||||||
{ activeAttributeFilters }
|
{ activeAttributeFilters }
|
||||||
</>
|
</>
|
||||||
) }
|
) }
|
||||||
|
@ -168,7 +135,6 @@ const ActiveFiltersBlock = ( {
|
||||||
setMinPrice( undefined );
|
setMinPrice( undefined );
|
||||||
setMaxPrice( undefined );
|
setMaxPrice( undefined );
|
||||||
setProductAttributes( [] );
|
setProductAttributes( [] );
|
||||||
setProductStockStatus( [] );
|
|
||||||
} }
|
} }
|
||||||
>
|
>
|
||||||
<Label
|
<Label
|
||||||
|
|
|
@ -13,7 +13,6 @@ import {
|
||||||
import { useCallback, useEffect, useState, useMemo } from '@wordpress/element';
|
import { useCallback, useEffect, useState, useMemo } from '@wordpress/element';
|
||||||
import CheckboxList from '@woocommerce/base-components/checkbox-list';
|
import CheckboxList from '@woocommerce/base-components/checkbox-list';
|
||||||
import DropdownSelector from '@woocommerce/base-components/dropdown-selector';
|
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 FilterSubmitButton from '@woocommerce/base-components/filter-submit-button';
|
||||||
import isShallowEqual from '@wordpress/is-shallow-equal';
|
import isShallowEqual from '@wordpress/is-shallow-equal';
|
||||||
import { decodeEntities } from '@wordpress/html-entities';
|
import { decodeEntities } from '@wordpress/html-entities';
|
||||||
|
@ -23,6 +22,7 @@ import { decodeEntities } from '@wordpress/html-entities';
|
||||||
*/
|
*/
|
||||||
import { getAttributeFromID } from '../../utils/attributes';
|
import { getAttributeFromID } from '../../utils/attributes';
|
||||||
import { updateAttributeFilter } from '../../utils/attributes-query';
|
import { updateAttributeFilter } from '../../utils/attributes-query';
|
||||||
|
import Label from './label';
|
||||||
import { previewAttributeObject, previewOptions } from './preview';
|
import { previewAttributeObject, previewOptions } from './preview';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
|
|
|
@ -5,18 +5,13 @@ import { _n, sprintf } from '@wordpress/i18n';
|
||||||
import Label from '@woocommerce/base-components/label';
|
import Label from '@woocommerce/base-components/label';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* The label for an attribute term filter.
|
||||||
*/
|
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The label for an filter elements.
|
|
||||||
*
|
*
|
||||||
* @param {Object} props Incoming props for the component.
|
* @param {Object} props Incoming props for the component.
|
||||||
* @param {string} props.name The name for the label.
|
* @param {string} props.name The name for the label.
|
||||||
* @param {number} props.count The count of products this status is attached to.
|
* @param {number} props.count The count of products this attribute is attached to.
|
||||||
*/
|
*/
|
||||||
const FilterElementLabel = ( { name, count } ) => {
|
const AttributeFilterLabel = ( { name, count } ) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{ name }
|
{ name }
|
||||||
|
@ -35,7 +30,7 @@ const FilterElementLabel = ( { name, count } ) => {
|
||||||
) }
|
) }
|
||||||
wrapperElement="span"
|
wrapperElement="span"
|
||||||
wrapperProps={ {
|
wrapperProps={ {
|
||||||
className: 'wc-filter-element-label-list-count',
|
className: 'wc-block-attribute-filter-list-count',
|
||||||
} }
|
} }
|
||||||
/>
|
/>
|
||||||
) }
|
) }
|
||||||
|
@ -43,4 +38,4 @@ const FilterElementLabel = ( { name, count } ) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FilterElementLabel;
|
export default AttributeFilterLabel;
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import Label from '@woocommerce/base-components/filter-element-label';
|
import Label from './label';
|
||||||
|
|
||||||
export const previewOptions = [
|
export const previewOptions = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,15 @@
|
||||||
.wc-block-attribute-filter {
|
.wc-block-attribute-filter {
|
||||||
margin-bottom: $gap-large;
|
margin-bottom: $gap-large;
|
||||||
|
|
||||||
|
.wc-block-attribute-filter-list-count {
|
||||||
|
&::before {
|
||||||
|
content: " (";
|
||||||
|
}
|
||||||
|
&::after {
|
||||||
|
content: ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.wc-block-attribute-filter-list {
|
.wc-block-attribute-filter-list {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
|
@ -16,6 +25,10 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wc-block-attribute-filter-list-count {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-single .wc-block-attribute-filter-list-count,
|
.is-single .wc-block-attribute-filter-list-count,
|
||||||
|
|
|
@ -6,6 +6,7 @@ import PropTypes from 'prop-types';
|
||||||
import { ProductListContainer } from '@woocommerce/base-components/product-list';
|
import { ProductListContainer } from '@woocommerce/base-components/product-list';
|
||||||
import { InnerBlockLayoutContextProvider } from '@woocommerce/shared-context';
|
import { InnerBlockLayoutContextProvider } from '@woocommerce/shared-context';
|
||||||
import { gridBlockPreview } from '@woocommerce/resource-previews';
|
import { gridBlockPreview } from '@woocommerce/resource-previews';
|
||||||
|
import { getSetting } from '@woocommerce/settings';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The All Products Block.
|
* The All Products Block.
|
||||||
|
@ -25,6 +26,8 @@ class Block extends Component {
|
||||||
return gridBlockPreview;
|
return gridBlockPreview;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hideOutOfStockItems = getSetting( 'hideOutOfStockItems', false );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Todo classes
|
* Todo classes
|
||||||
*
|
*
|
||||||
|
@ -39,6 +42,7 @@ class Block extends Component {
|
||||||
<ProductListContainer
|
<ProductListContainer
|
||||||
attributes={ attributes }
|
attributes={ attributes }
|
||||||
urlParameterSuffix={ urlParameterSuffix }
|
urlParameterSuffix={ urlParameterSuffix }
|
||||||
|
hideOutOfStockItems={ hideOutOfStockItems }
|
||||||
/>
|
/>
|
||||||
</InnerBlockLayoutContextProvider>
|
</InnerBlockLayoutContextProvider>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,278 +0,0 @@
|
||||||
/**
|
|
||||||
* External dependencies
|
|
||||||
*/
|
|
||||||
import { __, sprintf } from '@wordpress/i18n';
|
|
||||||
import { speak } from '@wordpress/a11y';
|
|
||||||
import { usePrevious, useShallowEqual } from '@woocommerce/base-hooks';
|
|
||||||
import {
|
|
||||||
useQueryStateByKey,
|
|
||||||
useQueryStateByContext,
|
|
||||||
useCollectionData,
|
|
||||||
} from '@woocommerce/base-context/hooks';
|
|
||||||
import { getSetting } from '@woocommerce/settings';
|
|
||||||
import { useCallback, useEffect, useState, useMemo } from '@wordpress/element';
|
|
||||||
import CheckboxList from '@woocommerce/base-components/checkbox-list';
|
|
||||||
import FilterSubmitButton from '@woocommerce/base-components/filter-submit-button';
|
|
||||||
import Label from '@woocommerce/base-components/filter-element-label';
|
|
||||||
import isShallowEqual from '@wordpress/is-shallow-equal';
|
|
||||||
import { decodeEntities } from '@wordpress/html-entities';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal dependencies
|
|
||||||
*/
|
|
||||||
import { previewOptions } from './preview';
|
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
const hideOutOfStockItems = getSetting( 'hideOutOfStockItems', false );
|
|
||||||
const { outofstock, ...otherStockStatusOptions } = getSetting(
|
|
||||||
'stockStatusOptions',
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
const STOCK_STATUS_OPTIONS = hideOutOfStockItems
|
|
||||||
? otherStockStatusOptions
|
|
||||||
: { outofstock, ...otherStockStatusOptions };
|
|
||||||
// Filter added to handle if there are slugs without a corresponding name defined.
|
|
||||||
const initialOptions = Object.entries( STOCK_STATUS_OPTIONS )
|
|
||||||
.map( ( [ slug, name ] ) => ( { slug, name } ) )
|
|
||||||
.filter( ( status ) => !! status.name )
|
|
||||||
.sort( ( a, b ) => a.slug.localeCompare( b.slug ) );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component displaying an stock status filter.
|
|
||||||
*
|
|
||||||
* @param {Object} props Incoming props for the component.
|
|
||||||
* @param {Object} props.attributes Incoming block attributes.
|
|
||||||
* @param {boolean} props.isEditor
|
|
||||||
*/
|
|
||||||
const StockStatusFilterBlock = ( {
|
|
||||||
attributes: blockAttributes,
|
|
||||||
isEditor = false,
|
|
||||||
} ) => {
|
|
||||||
const [ checked, setChecked ] = useState( [] );
|
|
||||||
const [ displayedOptions, setDisplayedOptions ] = useState(
|
|
||||||
blockAttributes.isPreview ? previewOptions : []
|
|
||||||
);
|
|
||||||
|
|
||||||
const [ queryState ] = useQueryStateByContext();
|
|
||||||
const [
|
|
||||||
productStockStatusQuery,
|
|
||||||
setProductStockStatusQuery,
|
|
||||||
] = useQueryStateByKey( 'stock_status', [] );
|
|
||||||
|
|
||||||
const {
|
|
||||||
results: filteredCounts,
|
|
||||||
isLoading: filteredCountsLoading,
|
|
||||||
} = useCollectionData( {
|
|
||||||
queryStock: true,
|
|
||||||
queryState,
|
|
||||||
} );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get count data about a given status by slug.
|
|
||||||
*/
|
|
||||||
const getFilteredStock = useCallback(
|
|
||||||
( slug ) => {
|
|
||||||
if ( ! filteredCounts.stock_status_counts ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return filteredCounts.stock_status_counts.find(
|
|
||||||
( { status, count } ) =>
|
|
||||||
status === slug && Number( count ) !== 0
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[ filteredCounts ]
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare intersection of all stock statuses and filtered counts to get a list of options to display.
|
|
||||||
*/
|
|
||||||
useEffect( () => {
|
|
||||||
/**
|
|
||||||
* Checks if a status slug is in the query state.
|
|
||||||
*
|
|
||||||
* @param {string} queryStatus The status slug to check.
|
|
||||||
*/
|
|
||||||
const isStockStatusInQueryState = ( queryStatus ) => {
|
|
||||||
if ( ! queryState?.stock_status ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return queryState.stock_status.some( ( { status = [] } ) =>
|
|
||||||
status.includes( queryStatus )
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
if ( filteredCountsLoading || blockAttributes.isPreview ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newOptions = initialOptions
|
|
||||||
.map( ( status ) => {
|
|
||||||
const filteredStock = getFilteredStock( status.slug );
|
|
||||||
|
|
||||||
if (
|
|
||||||
! filteredStock &&
|
|
||||||
! checked.includes( status.slug ) &&
|
|
||||||
! isStockStatusInQueryState( status.slug )
|
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const count = filteredStock ? Number( filteredStock.count ) : 0;
|
|
||||||
|
|
||||||
return {
|
|
||||||
value: status.slug,
|
|
||||||
name: decodeEntities( status.name ),
|
|
||||||
label: (
|
|
||||||
<Label
|
|
||||||
name={ decodeEntities( status.name ) }
|
|
||||||
count={ blockAttributes.showCounts ? count : null }
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
};
|
|
||||||
} )
|
|
||||||
.filter( Boolean );
|
|
||||||
|
|
||||||
setDisplayedOptions( newOptions );
|
|
||||||
}, [
|
|
||||||
blockAttributes.showCounts,
|
|
||||||
blockAttributes.isPreview,
|
|
||||||
filteredCountsLoading,
|
|
||||||
getFilteredStock,
|
|
||||||
checked,
|
|
||||||
queryState.stock_status,
|
|
||||||
] );
|
|
||||||
|
|
||||||
const onSubmit = useCallback(
|
|
||||||
( isChecked ) => {
|
|
||||||
if ( isEditor ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( isChecked ) {
|
|
||||||
setProductStockStatusQuery( checked );
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[ isEditor, setProductStockStatusQuery, checked ]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Track checked STATE changes - if state changes, update the query.
|
|
||||||
useEffect( () => {
|
|
||||||
if ( ! blockAttributes.showFilterButton ) {
|
|
||||||
onSubmit( checked );
|
|
||||||
}
|
|
||||||
}, [ blockAttributes.showFilterButton, checked, onSubmit ] );
|
|
||||||
|
|
||||||
const checkedQuery = useMemo( () => {
|
|
||||||
return productStockStatusQuery;
|
|
||||||
}, [ productStockStatusQuery ] );
|
|
||||||
|
|
||||||
const currentCheckedQuery = useShallowEqual( checkedQuery );
|
|
||||||
const previousCheckedQuery = usePrevious( currentCheckedQuery );
|
|
||||||
// Track Stock query changes so the block reflects current filters.
|
|
||||||
useEffect( () => {
|
|
||||||
if (
|
|
||||||
! isShallowEqual( previousCheckedQuery, currentCheckedQuery ) && // Checked query changed.
|
|
||||||
! isShallowEqual( checked, currentCheckedQuery ) // Checked query doesn't match the UI.
|
|
||||||
) {
|
|
||||||
setChecked( currentCheckedQuery );
|
|
||||||
}
|
|
||||||
}, [ checked, currentCheckedQuery, previousCheckedQuery ] );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When a checkbox in the list changes, update state.
|
|
||||||
*/
|
|
||||||
const onChange = useCallback(
|
|
||||||
( checkedValue ) => {
|
|
||||||
const getFilterNameFromValue = ( filterValue ) => {
|
|
||||||
const { name } = displayedOptions.find(
|
|
||||||
( option ) => option.value === filterValue
|
|
||||||
);
|
|
||||||
|
|
||||||
return name;
|
|
||||||
};
|
|
||||||
|
|
||||||
const announceFilterChange = ( { filterAdded, filterRemoved } ) => {
|
|
||||||
const filterAddedName = filterAdded
|
|
||||||
? getFilterNameFromValue( filterAdded )
|
|
||||||
: null;
|
|
||||||
const filterRemovedName = filterRemoved
|
|
||||||
? getFilterNameFromValue( filterRemoved )
|
|
||||||
: null;
|
|
||||||
if ( filterAddedName ) {
|
|
||||||
speak(
|
|
||||||
sprintf(
|
|
||||||
/* translators: %s stock statuses (for example: 'instock'...) */
|
|
||||||
__(
|
|
||||||
'%s filter added.',
|
|
||||||
'woo-gutenberg-products-block'
|
|
||||||
),
|
|
||||||
filterAddedName
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else if ( filterRemovedName ) {
|
|
||||||
speak(
|
|
||||||
sprintf(
|
|
||||||
/* translators: %s stock statuses (for example:'instock'...) */
|
|
||||||
__(
|
|
||||||
'%s filter removed.',
|
|
||||||
'woo-gutenberg-products-block'
|
|
||||||
),
|
|
||||||
filterRemovedName
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const previouslyChecked = checked.includes( checkedValue );
|
|
||||||
|
|
||||||
const newChecked = checked.filter(
|
|
||||||
( value ) => value !== checkedValue
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( ! previouslyChecked ) {
|
|
||||||
newChecked.push( checkedValue );
|
|
||||||
newChecked.sort();
|
|
||||||
announceFilterChange( { filterAdded: checkedValue } );
|
|
||||||
} else {
|
|
||||||
announceFilterChange( { filterRemoved: checkedValue } );
|
|
||||||
}
|
|
||||||
|
|
||||||
setChecked( newChecked );
|
|
||||||
},
|
|
||||||
[ checked, displayedOptions ]
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( displayedOptions.length === 0 ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TagName = `h${ blockAttributes.headingLevel }`;
|
|
||||||
const isLoading = ! blockAttributes.isPreview && ! STOCK_STATUS_OPTIONS;
|
|
||||||
const isDisabled = ! blockAttributes.isPreview && filteredCountsLoading;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{ ! isEditor && blockAttributes.heading && (
|
|
||||||
<TagName>{ blockAttributes.heading }</TagName>
|
|
||||||
) }
|
|
||||||
<div className="wc-block-stock-filter">
|
|
||||||
<CheckboxList
|
|
||||||
className={ 'wc-block-stock-filter-list' }
|
|
||||||
options={ displayedOptions }
|
|
||||||
checked={ checked }
|
|
||||||
onChange={ onChange }
|
|
||||||
isLoading={ isLoading }
|
|
||||||
isDisabled={ isDisabled }
|
|
||||||
/>
|
|
||||||
{ blockAttributes.showFilterButton && (
|
|
||||||
<FilterSubmitButton
|
|
||||||
className="wc-block-stock-filter__button"
|
|
||||||
disabled={ isLoading || isDisabled }
|
|
||||||
onClick={ () => onSubmit( checked ) }
|
|
||||||
/>
|
|
||||||
) }
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default StockStatusFilterBlock;
|
|
|
@ -1,130 +0,0 @@
|
||||||
/**
|
|
||||||
* External dependencies
|
|
||||||
*/
|
|
||||||
import { __ } from '@wordpress/i18n';
|
|
||||||
import { InspectorControls } from '@wordpress/block-editor';
|
|
||||||
import {
|
|
||||||
Disabled,
|
|
||||||
PanelBody,
|
|
||||||
ToggleControl,
|
|
||||||
withSpokenMessages,
|
|
||||||
} from '@wordpress/components';
|
|
||||||
import HeadingToolbar from '@woocommerce/editor-components/heading-toolbar';
|
|
||||||
import BlockTitle from '@woocommerce/editor-components/block-title';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal dependencies
|
|
||||||
*/
|
|
||||||
import Block from './block.js';
|
|
||||||
import './editor.scss';
|
|
||||||
|
|
||||||
const Edit = ( { attributes, setAttributes } ) => {
|
|
||||||
const {
|
|
||||||
className,
|
|
||||||
heading,
|
|
||||||
headingLevel,
|
|
||||||
showCounts,
|
|
||||||
showFilterButton,
|
|
||||||
} = attributes;
|
|
||||||
|
|
||||||
const getInspectorControls = () => {
|
|
||||||
return (
|
|
||||||
<InspectorControls key="inspector">
|
|
||||||
<PanelBody
|
|
||||||
title={ __( 'Content', 'woo-gutenberg-products-block' ) }
|
|
||||||
>
|
|
||||||
<ToggleControl
|
|
||||||
label={ __(
|
|
||||||
'Product count',
|
|
||||||
'woo-gutenberg-products-block'
|
|
||||||
) }
|
|
||||||
help={
|
|
||||||
showCounts
|
|
||||||
? __(
|
|
||||||
'Product count is visible.',
|
|
||||||
'woo-gutenberg-products-block'
|
|
||||||
)
|
|
||||||
: __(
|
|
||||||
'Product count is hidden.',
|
|
||||||
'woo-gutenberg-products-block'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
checked={ showCounts }
|
|
||||||
onChange={ () =>
|
|
||||||
setAttributes( {
|
|
||||||
showCounts: ! showCounts,
|
|
||||||
} )
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<p>
|
|
||||||
{ __(
|
|
||||||
'Heading Level',
|
|
||||||
'woo-gutenberg-products-block'
|
|
||||||
) }
|
|
||||||
</p>
|
|
||||||
<HeadingToolbar
|
|
||||||
isCollapsed={ false }
|
|
||||||
minLevel={ 2 }
|
|
||||||
maxLevel={ 7 }
|
|
||||||
selectedLevel={ headingLevel }
|
|
||||||
onChange={ ( newLevel ) =>
|
|
||||||
setAttributes( { headingLevel: newLevel } )
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</PanelBody>
|
|
||||||
<PanelBody
|
|
||||||
title={ __(
|
|
||||||
'Block Settings',
|
|
||||||
'woo-gutenberg-products-block'
|
|
||||||
) }
|
|
||||||
>
|
|
||||||
<ToggleControl
|
|
||||||
label={ __(
|
|
||||||
'Filter button',
|
|
||||||
'woo-gutenberg-products-block'
|
|
||||||
) }
|
|
||||||
help={
|
|
||||||
showFilterButton
|
|
||||||
? __(
|
|
||||||
'Products will only update when the button is pressed.',
|
|
||||||
'woo-gutenberg-products-block'
|
|
||||||
)
|
|
||||||
: __(
|
|
||||||
'Products will update as options are selected.',
|
|
||||||
'woo-gutenberg-products-block'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
checked={ showFilterButton }
|
|
||||||
onChange={ ( value ) =>
|
|
||||||
setAttributes( {
|
|
||||||
showFilterButton: value,
|
|
||||||
} )
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</PanelBody>
|
|
||||||
</InspectorControls>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{ getInspectorControls() }
|
|
||||||
{
|
|
||||||
<div className={ className }>
|
|
||||||
<BlockTitle
|
|
||||||
headingLevel={ headingLevel }
|
|
||||||
heading={ heading }
|
|
||||||
onChange={ ( value ) =>
|
|
||||||
setAttributes( { heading: value } )
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Disabled>
|
|
||||||
<Block attributes={ attributes } isEditor={ true } />
|
|
||||||
</Disabled>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withSpokenMessages( Edit );
|
|
|
@ -1,36 +0,0 @@
|
||||||
.wc-block-stock-filter {
|
|
||||||
.components-placeholder__instructions {
|
|
||||||
border-bottom: 1px solid #e0e2e6;
|
|
||||||
width: 100%;
|
|
||||||
padding-bottom: 1em;
|
|
||||||
margin-bottom: 2em;
|
|
||||||
}
|
|
||||||
.components-placeholder__label svg {
|
|
||||||
fill: currentColor;
|
|
||||||
margin-right: 1ch;
|
|
||||||
}
|
|
||||||
.components-placeholder__fieldset {
|
|
||||||
display: block; /* Disable flex box */
|
|
||||||
}
|
|
||||||
.woocommerce-search-list__search {
|
|
||||||
border-top: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
.wc-block-stock-filter__add-stock-button {
|
|
||||||
margin: 0 0 1em;
|
|
||||||
vertical-align: middle;
|
|
||||||
height: auto;
|
|
||||||
padding: 0.5em 1em;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
fill: currentColor;
|
|
||||||
margin-left: 0.5ch;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.wc-block-stock-filter__read_more_button {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
/**
|
|
||||||
* External dependencies
|
|
||||||
*/
|
|
||||||
import { withRestApiHydration } from '@woocommerce/block-hocs';
|
|
||||||
import { renderFrontend } from '@woocommerce/base-utils';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal dependencies
|
|
||||||
*/
|
|
||||||
import Block from './block.js';
|
|
||||||
|
|
||||||
const getProps = ( el ) => {
|
|
||||||
return {
|
|
||||||
attributes: {
|
|
||||||
showCounts: el.dataset.showCounts === 'true',
|
|
||||||
heading: el.dataset.heading,
|
|
||||||
headingLevel: el.dataset.headingLevel || 3,
|
|
||||||
showFilterButton: el.dataset.showFilterButton === 'true',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
renderFrontend( {
|
|
||||||
selector: '.wp-block-woocommerce-stock-filter',
|
|
||||||
Block: withRestApiHydration( Block ),
|
|
||||||
getProps,
|
|
||||||
} );
|
|
|
@ -1,93 +0,0 @@
|
||||||
/**
|
|
||||||
* External dependencies
|
|
||||||
*/
|
|
||||||
import { __ } from '@wordpress/i18n';
|
|
||||||
import { registerBlockType } from '@wordpress/blocks';
|
|
||||||
import { Icon, server } from '@woocommerce/icons';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal dependencies
|
|
||||||
*/
|
|
||||||
import edit from './edit.js';
|
|
||||||
|
|
||||||
registerBlockType( 'woocommerce/stock-filter', {
|
|
||||||
title: __( 'Filter Products by Stock', 'woo-gutenberg-products-block' ),
|
|
||||||
icon: {
|
|
||||||
src: <Icon srcElement={ server } />,
|
|
||||||
foreground: '#96588a',
|
|
||||||
},
|
|
||||||
category: 'woocommerce',
|
|
||||||
keywords: [ __( 'WooCommerce', 'woo-gutenberg-products-block' ) ],
|
|
||||||
description: __(
|
|
||||||
'Allow customers to filter the grid by products stock status. Works in combination with the All Products block.',
|
|
||||||
'woo-gutenberg-products-block'
|
|
||||||
),
|
|
||||||
supports: {
|
|
||||||
html: false,
|
|
||||||
multiple: false,
|
|
||||||
},
|
|
||||||
example: {
|
|
||||||
attributes: {
|
|
||||||
isPreview: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
attributes: {
|
|
||||||
heading: {
|
|
||||||
type: 'string',
|
|
||||||
default: __(
|
|
||||||
'Filter by stock status',
|
|
||||||
'woo-gutenberg-products-block'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
headingLevel: {
|
|
||||||
type: 'number',
|
|
||||||
default: 3,
|
|
||||||
},
|
|
||||||
showCounts: {
|
|
||||||
type: 'boolean',
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
showFilterButton: {
|
|
||||||
type: 'boolean',
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Are we previewing?
|
|
||||||
*/
|
|
||||||
isPreview: {
|
|
||||||
type: 'boolean',
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
edit,
|
|
||||||
// Save the props to post content.
|
|
||||||
save( { attributes } ) {
|
|
||||||
const {
|
|
||||||
className,
|
|
||||||
showCounts,
|
|
||||||
heading,
|
|
||||||
headingLevel,
|
|
||||||
showFilterButton,
|
|
||||||
} = attributes;
|
|
||||||
const data = {
|
|
||||||
'data-show-counts': showCounts,
|
|
||||||
'data-heading': heading,
|
|
||||||
'data-heading-level': headingLevel,
|
|
||||||
};
|
|
||||||
if ( showFilterButton ) {
|
|
||||||
data[ 'data-show-filter-button' ] = showFilterButton;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={ classNames( 'is-loading', className ) }
|
|
||||||
{ ...data }
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
aria-hidden
|
|
||||||
className="wc-block-product-stock-filter__placeholder"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
} );
|
|
|
@ -1,22 +0,0 @@
|
||||||
/**
|
|
||||||
* External dependencies
|
|
||||||
*/
|
|
||||||
import Label from '@woocommerce/base-components/filter-element-label';
|
|
||||||
|
|
||||||
export const previewOptions = [
|
|
||||||
{
|
|
||||||
value: 'preview-1',
|
|
||||||
name: 'In Stock',
|
|
||||||
label: <Label name="In Stock" count={ 3 } />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'preview-2',
|
|
||||||
name: 'Out of sotck',
|
|
||||||
label: <Label name="Out of stock" count={ 3 } />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'preview-3',
|
|
||||||
name: 'On backorder',
|
|
||||||
label: <Label name="On backorder" count={ 2 } />,
|
|
||||||
},
|
|
||||||
];
|
|
|
@ -1,29 +0,0 @@
|
||||||
.wc-block-stock-filter {
|
|
||||||
margin-bottom: $gap-large;
|
|
||||||
|
|
||||||
.wc-block-stock-filter-list {
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
li {
|
|
||||||
text-decoration: underline;
|
|
||||||
|
|
||||||
label {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-single,
|
|
||||||
.wc-block-dropdown-selector .wc-block-dropdown-selector__list {
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wc-block-stock-filter__button {
|
|
||||||
margin-top: $gap-smaller;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -38,7 +38,6 @@ const blocks = {
|
||||||
},
|
},
|
||||||
'price-filter': {},
|
'price-filter': {},
|
||||||
'attribute-filter': {},
|
'attribute-filter': {},
|
||||||
'stock-filter': {},
|
|
||||||
'active-filters': {},
|
'active-filters': {},
|
||||||
cart: {
|
cart: {
|
||||||
customDir: 'cart-checkout/cart',
|
customDir: 'cart-checkout/cart',
|
||||||
|
|
|
@ -27,5 +27,6 @@ class AllProducts extends AbstractBlock {
|
||||||
$this->asset_data_registry->add( 'min_rows', wc_get_theme_support( 'product_blocks::min_rows', 1 ), true );
|
$this->asset_data_registry->add( 'min_rows', wc_get_theme_support( 'product_blocks::min_rows', 1 ), true );
|
||||||
$this->asset_data_registry->add( 'max_rows', wc_get_theme_support( 'product_blocks::max_rows', 6 ), true );
|
$this->asset_data_registry->add( 'max_rows', wc_get_theme_support( 'product_blocks::max_rows', 6 ), true );
|
||||||
$this->asset_data_registry->add( 'default_rows', wc_get_theme_support( 'product_blocks::default_rows', 3 ), true );
|
$this->asset_data_registry->add( 'default_rows', wc_get_theme_support( 'product_blocks::default_rows', 3 ), true );
|
||||||
|
$this->asset_data_registry->add( 'hideOutOfStockItems', 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ), true );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AttributeFilter class.
|
|
||||||
*/
|
|
||||||
class StockFilter extends AbstractBlock {
|
|
||||||
/**
|
|
||||||
* Block name.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $block_name = 'stock-filter';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extra data passed through from server to client for block.
|
|
||||||
*
|
|
||||||
* @param array $stock_statuses Any stock statuses that currently are available from the block.
|
|
||||||
* Note, this will be empty in the editor context when the block is
|
|
||||||
* not in the post content on editor load.
|
|
||||||
*/
|
|
||||||
protected function enqueue_data( array $stock_statuses = [] ) {
|
|
||||||
parent::enqueue_data( $stock_statuses );
|
|
||||||
$this->asset_data_registry->add( 'stockStatusOptions', wc_get_product_stock_status_options(), true );
|
|
||||||
$this->asset_data_registry->add( 'hideOutOfStockItems', 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ), true );
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -116,7 +116,6 @@ final class BlockTypesController {
|
||||||
'AllProducts',
|
'AllProducts',
|
||||||
'PriceFilter',
|
'PriceFilter',
|
||||||
'AttributeFilter',
|
'AttributeFilter',
|
||||||
'StockFilter',
|
|
||||||
'ActiveFilters',
|
'ActiveFilters',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -140,7 +139,6 @@ final class BlockTypesController {
|
||||||
'AllProducts',
|
'AllProducts',
|
||||||
'PriceFilter',
|
'PriceFilter',
|
||||||
'AttributeFilter',
|
'AttributeFilter',
|
||||||
'StockFilter',
|
|
||||||
'ActiveFilters',
|
'ActiveFilters',
|
||||||
'Cart',
|
'Cart',
|
||||||
'Checkout',
|
'Checkout',
|
||||||
|
|
|
@ -47,11 +47,10 @@ class ProductCollectionData extends AbstractRoute {
|
||||||
*/
|
*/
|
||||||
protected function get_route_response( \WP_REST_Request $request ) {
|
protected function get_route_response( \WP_REST_Request $request ) {
|
||||||
$data = [
|
$data = [
|
||||||
'min_price' => null,
|
'min_price' => null,
|
||||||
'max_price' => null,
|
'max_price' => null,
|
||||||
'attribute_counts' => null,
|
'attribute_counts' => null,
|
||||||
'stock_status_counts' => null,
|
'rating_counts' => null,
|
||||||
'rating_counts' => null,
|
|
||||||
];
|
];
|
||||||
$filters = new ProductQueryFilters();
|
$filters = new ProductQueryFilters();
|
||||||
|
|
||||||
|
@ -65,20 +64,6 @@ class ProductCollectionData extends AbstractRoute {
|
||||||
$data['max_price'] = $price_results->max_price;
|
$data['max_price'] = $price_results->max_price;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! empty( $request['calculate_stock_status_counts'] ) ) {
|
|
||||||
$filter_request = clone $request;
|
|
||||||
$counts = $filters->get_stock_status_counts( $filter_request );
|
|
||||||
|
|
||||||
$data['stock_status_counts'] = [];
|
|
||||||
|
|
||||||
foreach ( $counts as $key => $value ) {
|
|
||||||
$data['stock_status_counts'][] = (object) [
|
|
||||||
'status' => $key,
|
|
||||||
'count' => $value,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! empty( $request['calculate_attribute_counts'] ) ) {
|
if ( ! empty( $request['calculate_attribute_counts'] ) ) {
|
||||||
$taxonomy__or_queries = [];
|
$taxonomy__or_queries = [];
|
||||||
$taxonomy__and_queries = [];
|
$taxonomy__and_queries = [];
|
||||||
|
@ -163,12 +148,6 @@ class ProductCollectionData extends AbstractRoute {
|
||||||
'default' => false,
|
'default' => false,
|
||||||
];
|
];
|
||||||
|
|
||||||
$params['calculate_stock_status_counts'] = [
|
|
||||||
'description' => __( 'If true, calculates stock counts for products in the collection.', 'woo-gutenberg-products-block' ),
|
|
||||||
'type' => 'boolean',
|
|
||||||
'default' => false,
|
|
||||||
];
|
|
||||||
|
|
||||||
$params['calculate_attribute_counts'] = [
|
$params['calculate_attribute_counts'] = [
|
||||||
'description' => __( 'If requested, calculates attribute term counts for products in the collection.', 'woo-gutenberg-products-block' ),
|
'description' => __( 'If requested, calculates attribute term counts for products in the collection.', 'woo-gutenberg-products-block' ),
|
||||||
'type' => 'array',
|
'type' => 'array',
|
||||||
|
|
|
@ -29,7 +29,7 @@ class ProductCollectionDataSchema extends AbstractSchema {
|
||||||
*/
|
*/
|
||||||
public function get_properties() {
|
public function get_properties() {
|
||||||
return [
|
return [
|
||||||
'price_range' => [
|
'price_range' => [
|
||||||
'description' => __( 'Min and max prices found in collection of products, provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
|
'description' => __( 'Min and max prices found in collection of products, provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
|
||||||
'type' => [ 'object', 'null' ],
|
'type' => [ 'object', 'null' ],
|
||||||
'context' => [ 'view', 'edit' ],
|
'context' => [ 'view', 'edit' ],
|
||||||
|
@ -52,7 +52,7 @@ class ProductCollectionDataSchema extends AbstractSchema {
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
'attribute_counts' => [
|
'attribute_counts' => [
|
||||||
'description' => __( 'Returns number of products within attribute terms.', 'woo-gutenberg-products-block' ),
|
'description' => __( 'Returns number of products within attribute terms.', 'woo-gutenberg-products-block' ),
|
||||||
'type' => [ 'array', 'null' ],
|
'type' => [ 'array', 'null' ],
|
||||||
'context' => [ 'view', 'edit' ],
|
'context' => [ 'view', 'edit' ],
|
||||||
|
@ -75,7 +75,7 @@ class ProductCollectionDataSchema extends AbstractSchema {
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'rating_counts' => [
|
'rating_counts' => [
|
||||||
'description' => __( 'Returns number of products with each average rating.', 'woo-gutenberg-products-block' ),
|
'description' => __( 'Returns number of products with each average rating.', 'woo-gutenberg-products-block' ),
|
||||||
'type' => [ 'array', 'null' ],
|
'type' => [ 'array', 'null' ],
|
||||||
'context' => [ 'view', 'edit' ],
|
'context' => [ 'view', 'edit' ],
|
||||||
|
@ -98,29 +98,6 @@ class ProductCollectionDataSchema extends AbstractSchema {
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'stock_status_counts' => [
|
|
||||||
'description' => __( 'Returns number of products with each stock status.', 'woo-gutenberg-products-block' ),
|
|
||||||
'type' => [ 'array', 'null' ],
|
|
||||||
'context' => [ 'view', 'edit' ],
|
|
||||||
'readonly' => true,
|
|
||||||
'items' => [
|
|
||||||
'type' => 'object',
|
|
||||||
'properties' => [
|
|
||||||
'status' => [
|
|
||||||
'description' => __( 'Status', 'woo-gutenberg-products-block' ),
|
|
||||||
'type' => 'string',
|
|
||||||
'context' => [ 'view', 'edit' ],
|
|
||||||
'readonly' => true,
|
|
||||||
],
|
|
||||||
'count' => [
|
|
||||||
'description' => __( 'Number of products.', 'woo-gutenberg-products-block' ),
|
|
||||||
'type' => 'integer',
|
|
||||||
'context' => [ 'view', 'edit' ],
|
|
||||||
'readonly' => true,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,15 +109,14 @@ class ProductCollectionDataSchema extends AbstractSchema {
|
||||||
*/
|
*/
|
||||||
public function get_item_response( $data ) {
|
public function get_item_response( $data ) {
|
||||||
return [
|
return [
|
||||||
'price_range' => ! is_null( $data['min_price'] ) && ! is_null( $data['max_price'] ) ? (object) $this->prepare_currency_response(
|
'price_range' => ! is_null( $data['min_price'] ) && ! is_null( $data['max_price'] ) ? (object) $this->prepare_currency_response(
|
||||||
[
|
[
|
||||||
'min_price' => $this->prepare_money_response( $data['min_price'], wc_get_price_decimals() ),
|
'min_price' => $this->prepare_money_response( $data['min_price'], wc_get_price_decimals() ),
|
||||||
'max_price' => $this->prepare_money_response( $data['max_price'], wc_get_price_decimals() ),
|
'max_price' => $this->prepare_money_response( $data['max_price'], wc_get_price_decimals() ),
|
||||||
]
|
]
|
||||||
) : null,
|
) : null,
|
||||||
'attribute_counts' => $data['attribute_counts'],
|
'attribute_counts' => $data['attribute_counts'],
|
||||||
'rating_counts' => $data['rating_counts'],
|
'rating_counts' => $data['rating_counts'],
|
||||||
'stock_status_counts' => $data['stock_status_counts'],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -319,9 +319,6 @@ class ProductQuery {
|
||||||
if ( $wp_query->get( 'stock_status' ) ) {
|
if ( $wp_query->get( 'stock_status' ) ) {
|
||||||
$args['join'] = $this->append_product_sorting_table_join( $args['join'] );
|
$args['join'] = $this->append_product_sorting_table_join( $args['join'] );
|
||||||
$args['where'] .= ' AND wc_product_meta_lookup.stock_status IN ("' . implode( '","', array_map( 'esc_sql', $wp_query->get( 'stock_status' ) ) ) . '")';
|
$args['where'] .= ' AND wc_product_meta_lookup.stock_status IN ("' . implode( '","', array_map( 'esc_sql', $wp_query->get( 'stock_status' ) ) ) . '")';
|
||||||
} elseif ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) {
|
|
||||||
$args['join'] = $this->append_product_sorting_table_join( $args['join'] );
|
|
||||||
$args['where'] .= ' AND wc_product_meta_lookup.stock_status NOT IN ("outofstock")';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $wp_query->get( 'min_price' ) || $wp_query->get( 'max_price' ) ) {
|
if ( $wp_query->get( 'min_price' ) || $wp_query->get( 'max_price' ) ) {
|
||||||
|
|
|
@ -47,72 +47,6 @@ class ProductQueryFilters {
|
||||||
return $wpdb->get_row( $price_filter_sql ); // phpcs:ignore
|
return $wpdb->get_row( $price_filter_sql ); // phpcs:ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get stock status counts for the current products.
|
|
||||||
*
|
|
||||||
* @param \WP_REST_Request $request The request object.
|
|
||||||
* @return array status=>count pairs.
|
|
||||||
*/
|
|
||||||
public function get_stock_status_counts( $request ) {
|
|
||||||
global $wpdb;
|
|
||||||
$product_query = new ProductQuery();
|
|
||||||
$stock_status_options = array_map( 'esc_sql', array_keys( wc_get_product_stock_status_options() ) );
|
|
||||||
$hide_outofstock_items = get_option( 'woocommerce_hide_out_of_stock_items' );
|
|
||||||
if ( 'yes' === $hide_outofstock_items ) {
|
|
||||||
unset( $stock_status_options['outofstock'] );
|
|
||||||
}
|
|
||||||
|
|
||||||
add_filter( 'posts_clauses', array( $product_query, 'add_query_clauses' ), 10, 2 );
|
|
||||||
add_filter( 'posts_pre_query', '__return_empty_array' );
|
|
||||||
|
|
||||||
$query_args = $product_query->prepare_objects_query( $request );
|
|
||||||
unset( $query_args['stock_status'] );
|
|
||||||
$query_args['no_found_rows'] = true;
|
|
||||||
$query_args['posts_per_page'] = -1;
|
|
||||||
$query = new \WP_Query();
|
|
||||||
$result = $query->query( $query_args );
|
|
||||||
$product_query_sql = $query->request;
|
|
||||||
|
|
||||||
remove_filter( 'posts_clauses', array( $product_query, 'add_query_clauses' ), 10 );
|
|
||||||
remove_filter( 'posts_pre_query', '__return_empty_array' );
|
|
||||||
|
|
||||||
$stock_status_counts = array();
|
|
||||||
|
|
||||||
foreach ( $stock_status_options as $status ) {
|
|
||||||
$stock_status_count_sql = $this->generate_stock_status_count_query( $status, $product_query_sql, $stock_status_options );
|
|
||||||
|
|
||||||
$result = $wpdb->get_row( $stock_status_count_sql ); // phpcs:ignore
|
|
||||||
$stock_status_counts[ $status ] = $result->status_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $stock_status_counts;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate calculate query by stock status.
|
|
||||||
*
|
|
||||||
* @param string $status status to calculate.
|
|
||||||
* @param string $product_query_sql product query for current filter state.
|
|
||||||
* @param array $stock_status_options available stock status options.
|
|
||||||
*
|
|
||||||
* @return false|string
|
|
||||||
*/
|
|
||||||
private function generate_stock_status_count_query( $status, $product_query_sql, $stock_status_options ) {
|
|
||||||
if ( ! in_array( $status, $stock_status_options, true ) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
global $wpdb;
|
|
||||||
$status = esc_sql( $status );
|
|
||||||
return "
|
|
||||||
SELECT COUNT( DISTINCT posts.ID ) as status_count
|
|
||||||
FROM {$wpdb->posts} as posts
|
|
||||||
INNER JOIN {$wpdb->postmeta} as postmeta ON posts.ID = postmeta.post_id
|
|
||||||
AND postmeta.meta_key = '_stock_status'
|
|
||||||
AND postmeta.meta_value = '{$status}'
|
|
||||||
WHERE posts.ID IN ( {$product_query_sql} )
|
|
||||||
";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get attribute counts for the current products.
|
* Get attribute counts for the current products.
|
||||||
*
|
*
|
||||||
|
|
|
@ -100,7 +100,6 @@ const LegacyMainConfig = {
|
||||||
'all-products',
|
'all-products',
|
||||||
'price-filter',
|
'price-filter',
|
||||||
'attribute-filter',
|
'attribute-filter',
|
||||||
'stock-filter',
|
|
||||||
'active-filters',
|
'active-filters',
|
||||||
'checkout',
|
'checkout',
|
||||||
'cart',
|
'cart',
|
||||||
|
@ -124,7 +123,6 @@ const LegacyFrontendConfig = {
|
||||||
'all-products',
|
'all-products',
|
||||||
'price-filter',
|
'price-filter',
|
||||||
'attribute-filter',
|
'attribute-filter',
|
||||||
'stock-filter',
|
|
||||||
'active-filters',
|
'active-filters',
|
||||||
'checkout',
|
'checkout',
|
||||||
'cart',
|
'cart',
|
||||||
|
@ -141,7 +139,6 @@ const LegacyStylingConfig = {
|
||||||
'all-products',
|
'all-products',
|
||||||
'price-filter',
|
'price-filter',
|
||||||
'attribute-filter',
|
'attribute-filter',
|
||||||
'stock-filter',
|
|
||||||
'active-filters',
|
'active-filters',
|
||||||
'checkout',
|
'checkout',
|
||||||
'cart',
|
'cart',
|
||||||
|
|
Loading…
Reference in New Issue