diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/best-sellers.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/best-sellers.tsx
index 08398bd4f2e..1cadf8977cd 100644
--- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/best-sellers.tsx
+++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/best-sellers.tsx
@@ -32,7 +32,7 @@ const attributes = {
perPage: 5,
pages: 1,
},
- hideControls: [ CoreFilterNames.ORDER ],
+ hideControls: [ CoreFilterNames.ORDER, CoreFilterNames.FILTERABLE ],
};
const heading: InnerBlockTemplate = [
diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/featured.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/featured.tsx
index 7c5176108d3..bc4f73d9dd0 100644
--- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/featured.tsx
+++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/featured.tsx
@@ -31,7 +31,7 @@ const attributes = {
perPage: 5,
pages: 1,
},
- hideControls: [ CoreFilterNames.FEATURED ],
+ hideControls: [ CoreFilterNames.FEATURED, CoreFilterNames.FILTERABLE ],
};
const heading: InnerBlockTemplate = [
diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/new-arrivals.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/new-arrivals.tsx
index 9f3a3310a30..6171e87fb94 100644
--- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/new-arrivals.tsx
+++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/new-arrivals.tsx
@@ -40,7 +40,7 @@ const attributes = {
value: '-7 days',
},
},
- hideControls: [ CoreFilterNames.ORDER ],
+ hideControls: [ CoreFilterNames.ORDER, CoreFilterNames.FILTERABLE ],
};
const heading: InnerBlockTemplate = [
diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/on-sale.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/on-sale.tsx
index 9f8c0b502a6..214ec729fb5 100644
--- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/on-sale.tsx
+++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/on-sale.tsx
@@ -34,7 +34,7 @@ const attributes = {
perPage: 5,
pages: 1,
},
- hideControls: [ CoreFilterNames.ON_SALE ],
+ hideControls: [ CoreFilterNames.ON_SALE, CoreFilterNames.FILTERABLE ],
};
const heading: InnerBlockTemplate = [
diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/top-rated.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/top-rated.tsx
index 79b1e21c11d..4bbeecca87a 100644
--- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/top-rated.tsx
+++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/collections/top-rated.tsx
@@ -35,7 +35,7 @@ const attributes = {
perPage: 5,
pages: 1,
},
- hideControls: [ CoreFilterNames.ORDER ],
+ hideControls: [ CoreFilterNames.ORDER, CoreFilterNames.FILTERABLE ],
};
const heading: InnerBlockTemplate = [
diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/constants.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/constants.ts
index 2dcc28b0b5d..98a1d9be2f6 100644
--- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/constants.ts
+++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/constants.ts
@@ -62,6 +62,7 @@ export const DEFAULT_QUERY: ProductCollectionQuery = {
woocommerceHandPickedProducts: [],
timeFrame: undefined,
priceRange: undefined,
+ filterable: false,
};
export const DEFAULT_ATTRIBUTES: Pick<
diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/inspector-controls/index.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/inspector-controls/index.tsx
index 8df7f261fab..aea5e9447e1 100644
--- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/inspector-controls/index.tsx
+++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/inspector-controls/index.tsx
@@ -36,7 +36,10 @@ import {
import { setQueryAttribute, getDefaultSettings } from '../../utils';
import UpgradeNotice from './upgrade-notice';
import ColumnsControl from './columns-control';
-import InheritQueryControl from './inherit-query-control';
+import {
+ InheritQueryControl,
+ FilterableControl,
+} from './use-page-context-control';
import OrderByControl from './order-by-control';
import OnSaleControl from './on-sale-control';
import StockStatusControl from './stock-status-control';
@@ -79,6 +82,8 @@ const ProductCollectionInspectorControls = (
const showQueryControls = inherit === false;
const showInheritQueryControl =
isArchiveTemplate && shouldShowFilter( CoreFilterNames.INHERIT );
+ const showFilterableControl =
+ ! isArchiveTemplate && shouldShowFilter( CoreFilterNames.FILTERABLE );
const showOrderControl =
showQueryControls && shouldShowFilter( CoreFilterNames.ORDER );
const showOnSaleControl = shouldShowFilter( CoreFilterNames.ON_SALE );
@@ -129,6 +134,9 @@ const ProductCollectionInspectorControls = (
{ showInheritQueryControl && (
) }
+ { showFilterableControl && (
+
+ ) }
{ showOrderControl && (
diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/inspector-controls/inherit-query-control.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/inspector-controls/use-page-context-control.tsx
similarity index 51%
rename from plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/inspector-controls/inherit-query-control.tsx
rename to plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/inspector-controls/use-page-context-control.tsx
index 3fbf3cc24a1..63813b92e62 100644
--- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/inspector-controls/inherit-query-control.tsx
+++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/inspector-controls/use-page-context-control.tsx
@@ -3,7 +3,6 @@
*/
import { __ } from '@wordpress/i18n';
import { usePrevious } from '@woocommerce/base-hooks';
-import { select } from '@wordpress/data';
import { useMemo } from '@wordpress/element';
import {
ToggleControl,
@@ -17,62 +16,28 @@ import {
*/
import {
CoreFilterNames,
- ProductCollectionQuery,
- QueryControlProps,
+ type ProductCollectionQuery,
+ type QueryControlProps,
} from '../../types';
import { DEFAULT_QUERY } from '../../constants';
-import { getDefaultValueOfInheritQueryFromTemplate } from '../../utils';
+import {
+ getDefaultValueOfInherit,
+ getDefaultValueOfFilterable,
+} from '../../utils';
-const label = __( 'Sync with current query', 'woocommerce' );
+const label = __( 'Use page context', 'woocommerce' );
-const productArchiveHelpText = __(
- 'Enable to adjust the displayed products based on the current template and any applied filters.',
+const helpText = __(
+ 'Adjust the displayed products depending on the current template and any applied query filters.',
'woocommerce'
);
-const productsByCategoryHelpText = __(
- 'Enable to adjust the displayed products based on the current category and any applied filters.',
- 'woocommerce'
-);
-
-const productsByTagHelpText = __(
- 'Enable to adjust the displayed products based on the current tag and any applied filters.',
- 'woocommerce'
-);
-
-const productsByAttributeHelpText = __(
- 'Enable to adjust the displayed products based on the current attribute and any applied filters.',
- 'woocommerce'
-);
-
-const searchResultsHelpText = __(
- 'Enable to adjust the displayed products based on the current search and any applied filters.',
- 'woocommerce'
-);
-
-const getHelpTextForTemplate = ( templateId: string ): string => {
- if ( templateId.includes( '//taxonomy-product_cat' ) ) {
- return productsByCategoryHelpText;
- }
- if ( templateId.includes( '//taxonomy-product_tag' ) ) {
- return productsByTagHelpText;
- }
- if ( templateId.includes( '//taxonomy-product_attribute' ) ) {
- return productsByAttributeHelpText;
- }
- if ( templateId.includes( '//product-search-results' ) ) {
- return searchResultsHelpText;
- }
- return productArchiveHelpText;
-};
-
const InheritQueryControl = ( {
setQueryAttribute,
trackInteraction,
query,
}: QueryControlProps ) => {
const inherit = query?.inherit;
- const editSiteStore = select( 'core/edit-site' );
const queryObjectBeforeInheritEnabled = usePrevious(
query,
@@ -81,13 +46,7 @@ const InheritQueryControl = ( {
}
);
- const defaultValue = useMemo(
- () => getDefaultValueOfInheritQueryFromTemplate(),
- []
- );
-
- const currentTemplateId = editSiteStore.getEditedPostId() as string;
- const helpText = getHelpTextForTemplate( currentTemplateId );
+ const defaultValue = useMemo( () => getDefaultValueOfInherit(), [] );
return (
{
+ const filterable = query?.filterable;
+
+ const defaultValue = useMemo( () => getDefaultValueOfFilterable(), [] );
+
+ return (
+ filterable !== defaultValue }
+ isShownByDefault
+ onDeselect={ () => {
+ setQueryAttribute( {
+ filterable: defaultValue,
+ } );
+ trackInteraction( CoreFilterNames.FILTERABLE );
+ } }
+ >
+ {
+ setQueryAttribute( {
+ filterable: value,
+ } );
+ trackInteraction( CoreFilterNames.FILTERABLE );
+ } }
+ />
+
+ );
+};
+
+export { FilterableControl, InheritQueryControl };
diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/product-collection-content.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/product-collection-content.tsx
index b7a87bcb3e8..a973de10df1 100644
--- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/product-collection-content.tsx
+++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/product-collection-content.tsx
@@ -23,7 +23,8 @@ import type {
} from '../types';
import { DEFAULT_ATTRIBUTES, INNER_BLOCKS_TEMPLATE } from '../constants';
import {
- getDefaultValueOfInheritQueryFromTemplate,
+ getDefaultValueOfInherit,
+ getDefaultValueOfFilterable,
useSetPreviewState,
} from '../utils';
import InspectorControls from './inspector-controls';
@@ -95,7 +96,8 @@ const ProductCollectionContent = ( {
...DEFAULT_ATTRIBUTES,
query: {
...( DEFAULT_ATTRIBUTES.query as ProductCollectionQuery ),
- inherit: getDefaultValueOfInheritQueryFromTemplate(),
+ inherit: getDefaultValueOfInherit(),
+ filterable: getDefaultValueOfFilterable(),
},
...( attributes as Partial< ProductCollectionAttributes > ),
queryId,
diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts
index 1c884c513d1..c772de36dac 100644
--- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts
+++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts
@@ -28,6 +28,7 @@ export interface ProductCollectionAttributes {
*/
queryContextIncludes: string[];
forcePageReload: boolean;
+ filterable: boolean;
// eslint-disable-next-line @typescript-eslint/naming-convention
__privatePreviewState?: PreviewState;
}
@@ -93,6 +94,7 @@ export interface ProductCollectionQuery {
isProductCollectionBlock: boolean;
woocommerceHandPickedProducts: string[];
priceRange: undefined | PriceRange;
+ filterable: boolean;
}
export type ProductCollectionEditComponentProps =
@@ -118,13 +120,15 @@ export type ProductCollectionSetAttributes = (
attrs: Partial< ProductCollectionAttributes >
) => void;
+export type TrackInteraction = ( filter: CoreFilterNames | string ) => void;
+
export type DisplayLayoutControlProps = {
displayLayout: ProductCollectionDisplayLayout;
setAttributes: ProductCollectionSetAttributes;
};
export type QueryControlProps = {
query: ProductCollectionQuery;
- trackInteraction: ( filter: CoreFilterNames | string ) => void;
+ trackInteraction: TrackInteraction;
setQueryAttribute: ( attrs: Partial< ProductCollectionQuery > ) => void;
};
@@ -150,6 +154,7 @@ export enum CoreFilterNames {
STOCK_STATUS = 'stock-status',
TAXONOMY = 'taxonomy',
PRICE_RANGE = 'price-range',
+ FILTERABLE = 'filterable',
}
export type CollectionName = CoreCollectionNames | string;
diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/utils.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/utils.tsx
index 7e0a795d454..08205b3cb60 100644
--- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/utils.tsx
+++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/utils.tsx
@@ -80,7 +80,9 @@ const isInProductArchive = () => {
: false;
};
-const isFirstBlockThatSyncsWithQuery = () => {
+const isFirstBlockThatUsesPageContext = (
+ property: 'inherit' | 'filterable'
+) => {
// We use experimental selector because it's been graduated as stable (`getBlocksByName`)
// in Gutenberg 17.6 (https://github.com/WordPress/gutenberg/pull/58156) and will be
// available in WordPress 6.5.
@@ -97,15 +99,23 @@ const isFirstBlockThatSyncsWithQuery = () => {
( clientId ) => {
const block = getBlock( clientId );
- return block.attributes?.query?.inherit;
+ return block.attributes?.query?.[ property ];
}
);
return ! blockAlreadySyncedWithQuery;
};
-export function getDefaultValueOfInheritQueryFromTemplate() {
- return isInProductArchive() ? isFirstBlockThatSyncsWithQuery() : false;
+export function getDefaultValueOfInherit() {
+ return isInProductArchive()
+ ? isFirstBlockThatUsesPageContext( 'inherit' )
+ : false;
+}
+
+export function getDefaultValueOfFilterable() {
+ return ! isInProductArchive()
+ ? isFirstBlockThatUsesPageContext( 'filterable' )
+ : false;
}
/**
@@ -226,7 +236,8 @@ export const getDefaultQuery = (
...currentQuery,
orderBy: DEFAULT_QUERY.orderBy as TProductCollectionOrderBy,
order: DEFAULT_QUERY.order as TProductCollectionOrder,
- inherit: getDefaultValueOfInheritQueryFromTemplate(),
+ inherit: getDefaultValueOfInherit(),
+ filterable: getDefaultValueOfFilterable(),
} );
export const getDefaultDisplayLayout = () =>
@@ -246,7 +257,8 @@ export const getDefaultProductCollection = () =>
...DEFAULT_ATTRIBUTES,
query: {
...DEFAULT_ATTRIBUTES.query,
- inherit: getDefaultValueOfInheritQueryFromTemplate(),
+ inherit: getDefaultValueOfInherit(),
+ filterable: getDefaultValueOfFilterable(),
},
},
createBlocksFromInnerBlocksTemplate( INNER_BLOCKS_TEMPLATE )
diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.block_theme.spec.ts
index ebf89ca2b86..b6c6a6d5a6c 100644
--- a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.block_theme.spec.ts
+++ b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.block_theme.spec.ts
@@ -126,14 +126,12 @@ test.describe( 'Product Collection', () => {
} );
} );
- test.describe( 'Product Collection Sidebar Settings', () => {
- test.beforeEach( async ( { pageObject } ) => {
- await pageObject.createNewPostAndInsertBlock();
- } );
-
+ test.describe( 'Inspector Controls', () => {
test( 'Reflects the correct number of columns according to sidebar settings', async ( {
pageObject,
} ) => {
+ await pageObject.createNewPostAndInsertBlock();
+
await pageObject.setNumberOfColumns( 2 );
await expect( pageObject.productTemplate ).toHaveClass(
/columns-2/
@@ -154,6 +152,8 @@ test.describe( 'Product Collection', () => {
test( 'Order By - sort products by title in descending order correctly', async ( {
pageObject,
} ) => {
+ await pageObject.createNewPostAndInsertBlock();
+
const sortedTitles = [
'WordPress Pennant',
'V-Neck T-Shirt',
@@ -177,6 +177,8 @@ test.describe( 'Product Collection', () => {
test( 'Products can be filtered based on "on sale" status', async ( {
pageObject,
} ) => {
+ await pageObject.createNewPostAndInsertBlock();
+
const allProducts = pageObject.products;
const salePoducts = pageObject.products.filter( {
hasText: 'Product on sale',
@@ -201,6 +203,8 @@ test.describe( 'Product Collection', () => {
test( 'Products can be filtered based on selection in handpicked products option', async ( {
pageObject,
} ) => {
+ await pageObject.createNewPostAndInsertBlock();
+
await pageObject.addFilter( 'Show Hand-picked Products' );
const filterName = 'Hand-picked Products';
@@ -221,6 +225,7 @@ test.describe( 'Product Collection', () => {
pageObject,
} ) => {
await pageObject.createNewPostAndInsertBlock();
+
await pageObject.addFilter( 'Keyword' );
await pageObject.setKeyword( 'Album' );
@@ -236,6 +241,8 @@ test.describe( 'Product Collection', () => {
test( 'Products can be filtered based on category.', async ( {
pageObject,
} ) => {
+ await pageObject.createNewPostAndInsertBlock();
+
const filterName = 'Product categories';
await pageObject.addFilter( 'Show product categories' );
await pageObject.setFilterComboboxValue( filterName, [
@@ -276,6 +283,8 @@ test.describe( 'Product Collection', () => {
test( 'Products can be filtered based on tags.', async ( {
pageObject,
} ) => {
+ await pageObject.createNewPostAndInsertBlock();
+
const filterName = 'Product tags';
await pageObject.addFilter( 'Show product tags' );
await pageObject.setFilterComboboxValue( filterName, [
@@ -296,6 +305,8 @@ test.describe( 'Product Collection', () => {
test( 'Products can be filtered based on product attributes like color, size etc.', async ( {
pageObject,
} ) => {
+ await pageObject.createNewPostAndInsertBlock();
+
await pageObject.addFilter( 'Show Product Attributes' );
await pageObject.setProductAttribute( 'Color', 'Green' );
@@ -313,6 +324,8 @@ test.describe( 'Product Collection', () => {
test( 'Products can be filtered based on stock status (in stock, out of stock, or backorder).', async ( {
pageObject,
} ) => {
+ await pageObject.createNewPostAndInsertBlock();
+
await pageObject.setFilterComboboxValue( 'Stock status', [
'Out of stock',
] );
@@ -331,6 +344,8 @@ test.describe( 'Product Collection', () => {
test( 'Products can be filtered based on featured status.', async ( {
pageObject,
} ) => {
+ await pageObject.createNewPostAndInsertBlock();
+
await expect( pageObject.products ).toHaveCount( 9 );
await pageObject.addFilter( 'Featured' );
@@ -349,6 +364,8 @@ test.describe( 'Product Collection', () => {
test( 'Products can be filtered based on created date.', async ( {
pageObject,
} ) => {
+ await pageObject.createNewPostAndInsertBlock();
+
await expect( pageObject.products ).toHaveCount( 9 );
await pageObject.addFilter( 'Created' );
@@ -376,6 +393,8 @@ test.describe( 'Product Collection', () => {
test( 'Products can be filtered based on price range.', async ( {
pageObject,
} ) => {
+ await pageObject.createNewPostAndInsertBlock();
+
await expect( pageObject.products ).toHaveCount( 9 );
await pageObject.addFilter( 'Price Range' );
@@ -403,74 +422,62 @@ test.describe( 'Product Collection', () => {
await expect( pageObject.products ).toHaveCount( 4 );
} );
- test.describe( 'Sync with current template', () => {
- test( 'should not be visible on posts', async ( {
- pageObject,
- } ) => {
+ test.describe( '"Use page context" control', () => {
+ test( 'should be visible on posts', async ( { pageObject } ) => {
await pageObject.createNewPostAndInsertBlock();
- const sidebarSettings =
- await pageObject.locateSidebarSettings();
await expect(
- sidebarSettings.locator(
- SELECTORS.inheritQueryFromTemplateControl
- )
- ).toBeHidden();
+ pageObject
+ .locateSidebarSettings()
+ .locator( SELECTORS.usePageContextControl )
+ ).toBeVisible();
} );
- const archiveTemplates = [
+ [
'woocommerce/woocommerce//archive-product',
'woocommerce/woocommerce//taxonomy-product_cat',
'woocommerce/woocommerce//taxonomy-product_tag',
'woocommerce/woocommerce//taxonomy-product_attribute',
'woocommerce/woocommerce//product-search-results',
- ];
-
- const nonArchiveTemplates = [
- 'woocommerce/woocommerce//single-product',
- 'twentytwentyfour//home',
- 'twentytwentyfour//index',
- ];
-
- archiveTemplates.map( async ( template ) => {
- test( `should be visible in archive template: ${ template }`, async ( {
+ ].forEach( ( slug ) => {
+ test( `should be visible in archive template: ${ slug }`, async ( {
pageObject,
editor,
} ) => {
- await pageObject.goToEditorTemplate( template );
+ await pageObject.goToEditorTemplate( slug );
await pageObject.insertProductCollection();
await pageObject.chooseCollectionInTemplate();
await pageObject.focusProductCollection();
await editor.openDocumentSettingsSidebar();
- const sidebarSettings =
- await pageObject.locateSidebarSettings();
await expect(
- sidebarSettings.locator(
- SELECTORS.inheritQueryFromTemplateControl
- )
+ pageObject
+ .locateSidebarSettings()
+ .locator( SELECTORS.usePageContextControl )
).toBeVisible();
} );
} );
- nonArchiveTemplates.map( async ( template ) => {
- test( `should not be visible in non-archive template: ${ template }`, async ( {
+ [
+ 'woocommerce/woocommerce//single-product',
+ 'twentytwentyfour//home',
+ 'twentytwentyfour//index',
+ ].forEach( ( slug ) => {
+ test( `should be visible in non-archive template: ${ slug }`, async ( {
pageObject,
editor,
} ) => {
- await pageObject.goToEditorTemplate( template );
+ await pageObject.goToEditorTemplate( slug );
await pageObject.insertProductCollection();
await pageObject.chooseCollectionInTemplate();
await pageObject.focusProductCollection();
await editor.openDocumentSettingsSidebar();
- const sidebarSettings =
- await pageObject.locateSidebarSettings();
await expect(
- sidebarSettings.locator(
- SELECTORS.inheritQueryFromTemplateControl
- )
- ).toBeHidden();
+ pageObject
+ .locateSidebarSettings()
+ .locator( SELECTORS.usePageContextControl )
+ ).toBeVisible();
} );
} );
@@ -482,18 +489,15 @@ test.describe( 'Product Collection', () => {
await pageObject.focusProductCollection();
await editor.openDocumentSettingsSidebar();
- const sidebarSettings =
- await pageObject.locateSidebarSettings();
+ const sidebarSettings = pageObject.locateSidebarSettings();
// Inherit query from template should be visible & enabled by default
await expect(
- sidebarSettings.locator(
- SELECTORS.inheritQueryFromTemplateControl
- )
+ sidebarSettings.locator( SELECTORS.usePageContextControl )
).toBeVisible();
await expect(
sidebarSettings.locator(
- `${ SELECTORS.inheritQueryFromTemplateControl } input`
+ `${ SELECTORS.usePageContextControl } input`
)
).toBeChecked();
@@ -529,45 +533,136 @@ test.describe( 'Product Collection', () => {
).toBeChecked();
} );
- test( 'is enabled by default in 1st Product Collection and disabled in 2nd+', async ( {
+ test( 'is enabled by default unless already enabled elsewhere', async ( {
pageObject,
editor,
} ) => {
+ const productCollection = editor.canvas.getByLabel(
+ 'Block: Product Collection',
+ { exact: true }
+ );
+ const usePageContextToggle = pageObject
+ .locateSidebarSettings()
+ .locator( SELECTORS.usePageContextControl )
+ .locator( 'input' );
+
// First Product Catalog
// Option should be visible & ENABLED by default
await pageObject.goToEditorTemplate();
- await pageObject.focusProductCollection();
+ await editor.selectBlocks( productCollection.first() );
await editor.openDocumentSettingsSidebar();
- const sidebarSettings =
- await pageObject.locateSidebarSettings();
-
- await expect(
- sidebarSettings.locator(
- SELECTORS.inheritQueryFromTemplateControl
- )
- ).toBeVisible();
- await expect(
- sidebarSettings.locator(
- `${ SELECTORS.inheritQueryFromTemplateControl } input`
- )
- ).toBeChecked();
+ await expect( usePageContextToggle ).toBeChecked();
// Second Product Catalog
// Option should be visible & DISABLED by default
await pageObject.insertProductCollection();
await pageObject.chooseCollectionInTemplate( 'productCatalog' );
+ await editor.selectBlocks( productCollection.last() );
+
+ await expect( usePageContextToggle ).not.toBeChecked();
+
+ // Disable the option in the first Product Catalog
+ await editor.selectBlocks( productCollection.first() );
+ await usePageContextToggle.click();
+
+ // Third Product Catalog
+ // Option should be visible & ENABLED by default
+ await pageObject.insertProductCollection();
+ await pageObject.chooseCollectionInTemplate( 'productCatalog' );
+ await editor.selectBlocks( productCollection.last() );
+
+ await expect( usePageContextToggle ).toBeChecked();
+ } );
+
+ test( 'allows filtering in non-archive context', async ( {
+ pageObject,
+ editor,
+ page,
+ } ) => {
+ await pageObject.createNewPostAndInsertBlock();
+
+ await expect( pageObject.products ).toHaveCount( 9 );
+
+ await pageObject.insertProductCollection();
+ await pageObject.chooseCollectionInPost( 'productCatalog' );
+
+ await expect( pageObject.products ).toHaveCount( 18 );
+
+ await page.getByLabel( 'Toggle block inserter' ).click();
+ await page.getByRole( 'tab', { name: 'Patterns' } ).click();
+ await page
+ .getByPlaceholder( 'Search' )
+ .fill( 'product filters' );
+ await page.getByLabel( 'Product Filters' ).click();
+
+ const postId = await editor.publishPost();
+ await page.goto( `/?p=${ postId }` );
+
+ const productCollection = page.locator(
+ '.wp-block-woocommerce-product-collection'
+ );
await expect(
- sidebarSettings.locator(
- SELECTORS.inheritQueryFromTemplateControl
- )
- ).toBeVisible();
+ productCollection.first().locator( SELECTORS.product )
+ ).toHaveCount( 9 );
await expect(
- sidebarSettings.locator(
- `${ SELECTORS.inheritQueryFromTemplateControl } input`
- )
- ).not.toBeChecked();
+ productCollection.last().locator( SELECTORS.product )
+ ).toHaveCount( 9 );
+
+ await page
+ .getByRole( 'textbox', {
+ name: 'Filter products by maximum',
+ } )
+ .dblclick();
+ await page.keyboard.type( '10' );
+ await page.keyboard.press( 'Tab' );
+
+ await expect(
+ productCollection.first().locator( SELECTORS.product )
+ ).toHaveCount( 1 );
+ await expect(
+ productCollection.last().locator( SELECTORS.product )
+ ).toHaveCount( 9 );
+ } );
+
+ test( 'correctly combines editor and front-end filters', async ( {
+ pageObject,
+ editor,
+ page,
+ } ) => {
+ await pageObject.createNewPostAndInsertBlock();
+
+ await expect( pageObject.products ).toHaveCount( 9 );
+
+ await pageObject.addFilter( 'Show product categories' );
+ await pageObject.setFilterComboboxValue( 'Product categories', [
+ 'Music',
+ ] );
+
+ await page.getByLabel( 'Toggle block inserter' ).click();
+ await page.getByRole( 'tab', { name: 'Patterns' } ).click();
+ await page
+ .getByPlaceholder( 'Search' )
+ .fill( 'product filters' );
+ await page.getByLabel( 'Product Filters' ).click();
+
+ await expect( pageObject.products ).toHaveCount( 2 );
+
+ const postId = await editor.publishPost();
+ await page.goto( `/?p=${ postId }` );
+
+ await expect( pageObject.products ).toHaveCount( 2 );
+
+ await page
+ .getByRole( 'textbox', {
+ name: 'Filter products by maximum',
+ } )
+ .dblclick();
+ await page.keyboard.type( '5' );
+ await page.keyboard.press( 'Tab' );
+
+ await expect( pageObject.products ).toHaveCount( 1 );
} );
} );
} );
@@ -787,17 +882,16 @@ test.describe( 'Product Collection', () => {
await expect( pageObject.products ).toHaveCount( 4 );
} );
- test( "Product Catalog Collection can be added in post and doesn't sync query with template", async ( {
+ test( 'Product Catalog Collection can be added in post and syncs query with template', async ( {
pageObject,
} ) => {
await pageObject.createNewPostAndInsertBlock( 'productCatalog' );
- const sidebarSettings = await pageObject.locateSidebarSettings();
- const input = sidebarSettings.locator(
- `${ SELECTORS.inheritQueryFromTemplateControl } input`
- );
+ const usePageContextToggle = pageObject
+ .locateSidebarSettings()
+ .locator( `${ SELECTORS.usePageContextControl } input` );
- await expect( input ).toBeHidden();
+ await expect( usePageContextToggle ).toBeVisible();
await expect( pageObject.products ).toHaveCount( 9 );
await pageObject.publishAndGoToFrontend();
@@ -822,9 +916,9 @@ test.describe( 'Product Collection', () => {
await pageObject.chooseCollectionInTemplate();
await editor.openDocumentSettingsSidebar();
- const sidebarSettings = await pageObject.locateSidebarSettings();
+ const sidebarSettings = pageObject.locateSidebarSettings();
const input = sidebarSettings.locator(
- `${ SELECTORS.inheritQueryFromTemplateControl } input`
+ `${ SELECTORS.usePageContextControl } input`
);
await expect( input ).toBeChecked();
@@ -854,8 +948,7 @@ test.describe( 'Product Collection', () => {
test( 'On Sale', async ( { pageObject } ) => {
await pageObject.createNewPostAndInsertBlock( 'onSale' );
- const sidebarSettings =
- await pageObject.locateSidebarSettings();
+ const sidebarSettings = pageObject.locateSidebarSettings();
const input = sidebarSettings.getByLabel(
SELECTORS.onSaleControlLabel
);
@@ -865,8 +958,7 @@ test.describe( 'Product Collection', () => {
test( 'Featured', async ( { pageObject } ) => {
await pageObject.createNewPostAndInsertBlock( 'featured' );
- const sidebarSettings =
- await pageObject.locateSidebarSettings();
+ const sidebarSettings = pageObject.locateSidebarSettings();
const input = sidebarSettings.getByLabel(
SELECTORS.featuredControlLabel
);
@@ -1477,7 +1569,7 @@ test.describe( 'Testing registerProductCollection', () => {
'myCustomCollection'
);
- const sidebarSettings = await pageObject.locateSidebarSettings();
+ const sidebarSettings = pageObject.locateSidebarSettings();
const onsaleControl = sidebarSettings.getByLabel(
SELECTORS.onSaleControlLabel
);
diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.page.ts b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.page.ts
index db0737ceb5d..b154c1eb4c0 100644
--- a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.page.ts
+++ b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.page.ts
@@ -38,7 +38,7 @@ export const SELECTORS = {
},
onSaleControlLabel: 'Show only products on sale',
featuredControlLabel: 'Show only featured products',
- inheritQueryFromTemplateControl:
+ usePageContextControl:
'.wc-block-product-collection__inherit-query-control',
shrinkColumnsToFit: 'Responsive',
productSearchLabel: 'Search',
@@ -374,7 +374,7 @@ class ProductCollectionPage {
}
async setNumberOfColumns( numberOfColumns: number ) {
- const sidebarSettings = await this.locateSidebarSettings();
+ const sidebarSettings = this.locateSidebarSettings();
const inputField = sidebarSettings.getByRole( 'spinbutton', {
name: 'Columns',
} );
@@ -390,7 +390,7 @@ class ProductCollectionPage {
| 'popularity/desc'
| 'rating/desc'
) {
- const sidebarSettings = await this.locateSidebarSettings();
+ const sidebarSettings = this.locateSidebarSettings();
const orderByComboBox = sidebarSettings.getByRole( 'combobox', {
name: 'Order by',
} );
@@ -400,7 +400,7 @@ class ProductCollectionPage {
}
async getOrderByElement() {
- const sidebarSettings = await this.locateSidebarSettings();
+ const sidebarSettings = this.locateSidebarSettings();
return sidebarSettings.getByRole( 'combobox', {
name: 'Order by',
} );
@@ -423,7 +423,7 @@ class ProductCollectionPage {
onSale: true,
}
) {
- const sidebarSettings = await this.locateSidebarSettings();
+ const sidebarSettings = this.locateSidebarSettings();
const input = sidebarSettings.getByLabel(
SELECTORS.onSaleControlLabel
);
@@ -448,7 +448,7 @@ class ProductCollectionPage {
isLocatorsRefreshNeeded: true,
}
) {
- const sidebarSettings = await this.locateSidebarSettings();
+ const sidebarSettings = this.locateSidebarSettings();
const input = sidebarSettings.getByLabel(
SELECTORS.featuredControlLabel
);
@@ -475,7 +475,7 @@ class ProductCollectionPage {
const operatorSelector = SELECTORS.createdFilter.operator[ operator ];
const rangeSelector = SELECTORS.createdFilter.range[ range ];
- const sidebarSettings = await this.locateSidebarSettings();
+ const sidebarSettings = this.locateSidebarSettings();
const operatorButton = sidebarSettings.getByLabel( operatorSelector );
const rangeButton = sidebarSettings.getByLabel( rangeSelector );
@@ -487,7 +487,7 @@ class ProductCollectionPage {
const minInputSelector = SELECTORS.priceRangeFilter.min;
const maxInputSelector = SELECTORS.priceRangeFilter.max;
- const sidebarSettings = await this.locateSidebarSettings();
+ const sidebarSettings = this.locateSidebarSettings();
const minInput = sidebarSettings.getByLabel( minInputSelector );
const maxInput = sidebarSettings.getByLabel( maxInputSelector );
@@ -498,7 +498,7 @@ class ProductCollectionPage {
}
async setFilterComboboxValue( filterName: string, filterValue: string[] ) {
- const sidebarSettings = await this.locateSidebarSettings();
+ const sidebarSettings = this.locateSidebarSettings();
const input = sidebarSettings.getByLabel( filterName );
await input.click();
@@ -527,7 +527,7 @@ class ProductCollectionPage {
}
async setKeyword( keyword: string ) {
- const sidebarSettings = await this.locateSidebarSettings();
+ const sidebarSettings = this.locateSidebarSettings();
const input = sidebarSettings.getByLabel( 'Keyword' );
await input.clear();
await input.fill( keyword );
@@ -590,7 +590,7 @@ class ProductCollectionPage {
}
async setShrinkColumnsToFit( value = true ) {
- const sidebarSettings = await this.locateSidebarSettings();
+ const sidebarSettings = this.locateSidebarSettings();
const input = sidebarSettings.getByLabel(
SELECTORS.shrinkColumnsToFit
);
@@ -602,7 +602,7 @@ class ProductCollectionPage {
}
async setProductAttribute( attribute: 'Color' | 'Size', value: string ) {
- const sidebarSettings = await this.locateSidebarSettings();
+ const sidebarSettings = this.locateSidebarSettings();
const productAttributesContainer = sidebarSettings.locator(
'.woocommerce-product-attributes'
@@ -631,9 +631,9 @@ class ProductCollectionPage {
}
async setInheritQueryFromTemplate( inheritQueryFromTemplate: boolean ) {
- const sidebarSettings = await this.locateSidebarSettings();
+ const sidebarSettings = this.locateSidebarSettings();
const input = sidebarSettings.locator(
- `${ SELECTORS.inheritQueryFromTemplateControl } input`
+ `${ SELECTORS.usePageContextControl } input`
);
if ( inheritQueryFromTemplate ) {
await input.check();
@@ -687,7 +687,7 @@ class ProductCollectionPage {
/**
* Locators
*/
- async locateSidebarSettings() {
+ locateSidebarSettings() {
return this.page.getByRole( 'region', {
name: 'Editor settings',
} );
diff --git a/plugins/woocommerce/changelog/48749-product-collection-add-control-to-toggle-whether-block-is-filterable-in-non-archive-context b/plugins/woocommerce/changelog/48749-product-collection-add-control-to-toggle-whether-block-is-filterable-in-non-archive-context
new file mode 100644
index 00000000000..4b113e9a86a
--- /dev/null
+++ b/plugins/woocommerce/changelog/48749-product-collection-add-control-to-toggle-whether-block-is-filterable-in-non-archive-context
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Product Collection: Rename "Sync with current query" option to "Use page context" and make it working in non-archive context as well
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection.php
index 9d828541d02..e11038ba6ff 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection.php
@@ -524,7 +524,10 @@ class ProductCollection extends AbstractBlock {
// phpcs:ignore WordPress.DB.SlowDBQuery
$block_context_query['tax_query'] = ! empty( $query['tax_query'] ) ? $query['tax_query'] : array();
- $is_exclude_applied_filters = ! ( $block->context['query']['inherit'] ?? false );
+ $inherit = $block->context['query']['inherit'] ?? false;
+ $filterable = $block->context['query']['filterable'] ?? false;
+
+ $is_exclude_applied_filters = ! ( $inherit || $filterable );
return $this->get_final_frontend_query( $block_context_query, $page, $is_exclude_applied_filters );
}