[Experimental] Product filters/remove template parts (#52041)

* Remove product filters template part

* Remove product filters overlay template part

* Update e2e tests

* Add changefile(s) from automation for the following project(s): woocommerce-blocks, woocommerce

* Fix linting

* Remove unused template part class and remove overlay navigation

---------

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Roy Ho 2024-10-16 14:02:25 -07:00 committed by GitHub
parent 92c8f4168d
commit bd838d824a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 16 additions and 1079 deletions

View File

@ -21,8 +21,8 @@
"background": true, "background": true,
"text": true "text": true
}, },
"multiple": false, "multiple": true,
"inserter": false, "inserter": true,
"interactivity": true, "interactivity": true,
"typography": { "typography": {
"fontSize": true, "fontSize": true,
@ -46,15 +46,6 @@
}, },
"textdomain": "woocommerce", "textdomain": "woocommerce",
"usesContext": [ "postId" ], "usesContext": [ "postId" ],
"providesContext": {
"woocommerce/product-filters/overlay": "overlay"
},
"attributes": {
"overlay": {
"type": "string",
"default": "never"
}
},
"viewScript": "wc-product-filters-frontend", "viewScript": "wc-product-filters-frontend",
"example": {} "example": {}
} }

View File

@ -1,9 +1,3 @@
export const BlockOverlayAttribute = {
NEVER: 'never',
MOBILE: 'mobile',
ALWAYS: 'always',
} as const;
export const EXCLUDED_BLOCKS = [ export const EXCLUDED_BLOCKS = [
'woocommerce/product-filters', 'woocommerce/product-filters',
'woocommerce/product-filter-attribute', 'woocommerce/product-filter-attribute',

View File

@ -1,40 +1,15 @@
/** /**
* External dependencies * External dependencies
*/ */
import { getSetting } from '@woocommerce/settings'; import { InnerBlocks, useBlockProps } from '@wordpress/block-editor';
import { import { BlockEditProps, InnerBlockTemplate } from '@wordpress/blocks';
InnerBlocks,
InspectorControls,
useBlockProps,
useInnerBlocksProps,
} from '@wordpress/block-editor';
import {
BlockEditProps,
BlockInstance,
InnerBlockTemplate,
createBlock,
} from '@wordpress/blocks';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { useEffect } from '@wordpress/element';
import { select, dispatch } from '@wordpress/data';
import { useLocalStorageState } from '@woocommerce/base-hooks';
import {
ExternalLink,
PanelBody,
// @ts-expect-error - no types.
// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
__experimentalToggleGroupControl as ToggleGroupControl,
// @ts-expect-error - no types.
// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
__experimentalToggleGroupControlOption as ToggleGroupControlOption,
} from '@wordpress/components';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import './editor.scss'; import './editor.scss';
import { type BlockAttributes } from './types'; import { type BlockAttributes } from './types';
import { BlockOverlayAttribute } from './constants';
const TEMPLATE: InnerBlockTemplate[] = [ const TEMPLATE: InnerBlockTemplate[] = [
[ [
@ -73,138 +48,12 @@ const TEMPLATE: InnerBlockTemplate[] = [
], ],
]; ];
export const Edit = ( { export const Edit = ( {}: BlockEditProps< BlockAttributes > ) => {
setAttributes,
attributes,
clientId,
}: BlockEditProps< BlockAttributes > ) => {
const blockProps = useBlockProps(); const blockProps = useBlockProps();
const templatePartEditUri = getSetting< string >(
'templatePartProductFiltersOverlayEditUri',
''
);
const [
productFiltersOverlayNavigationAttributes,
setProductFiltersOverlayNavigationAttributes,
] = useLocalStorageState< Record< string, unknown > >(
'product-filters-overlay-navigation-attributes',
{}
);
useEffect( () => {
const filtersClientIds = select( 'core/block-editor' ).getBlocksByName(
'woocommerce/product-filters'
);
let overlayBlock:
| BlockInstance< { [ k: string ]: unknown } >
| undefined;
for ( const filterClientId of filtersClientIds ) {
const filterBlock =
select( 'core/block-editor' ).getBlock( filterClientId );
if ( filterBlock ) {
for ( const innerBlock of filterBlock.innerBlocks ) {
if (
innerBlock.name ===
'woocommerce/product-filters-overlay-navigation' &&
innerBlock.attributes.triggerType === 'open-overlay'
) {
overlayBlock = innerBlock;
}
}
}
}
if ( attributes.overlay === 'never' && overlayBlock ) {
setProductFiltersOverlayNavigationAttributes(
overlayBlock.attributes
);
dispatch( 'core/block-editor' ).updateBlockAttributes(
overlayBlock.clientId,
{
lock: {},
}
);
dispatch( 'core/block-editor' ).removeBlock(
overlayBlock.clientId
);
} else if ( attributes.overlay !== 'never' && ! overlayBlock ) {
if ( productFiltersOverlayNavigationAttributes ) {
productFiltersOverlayNavigationAttributes.triggerType =
'open-overlay';
}
dispatch( 'core/block-editor' ).insertBlock(
createBlock(
'woocommerce/product-filters-overlay-navigation',
productFiltersOverlayNavigationAttributes
? productFiltersOverlayNavigationAttributes
: {
align: 'left',
triggerType: 'open-overlay',
lock: { move: true, remove: true },
}
),
0,
clientId,
false
);
}
}, [
attributes.overlay,
clientId,
productFiltersOverlayNavigationAttributes,
setProductFiltersOverlayNavigationAttributes,
] );
return ( return (
<div { ...blockProps }> <div { ...blockProps }>
<InspectorControls>
<PanelBody title={ __( 'Overlay', 'woocommerce' ) }>
<ToggleGroupControl
className="wc-block-editor-product-filters__overlay-toggle"
isBlock={ true }
value={ attributes.overlay }
onChange={ ( value: BlockAttributes[ 'overlay' ] ) => {
setAttributes( { overlay: value } );
} }
>
<ToggleGroupControlOption
value={ BlockOverlayAttribute.NEVER }
label={ __( 'Never', 'woocommerce' ) }
/>
<ToggleGroupControlOption
value={ BlockOverlayAttribute.MOBILE }
label={ __( 'Mobile', 'woocommerce' ) }
/>
<ToggleGroupControlOption
value={ BlockOverlayAttribute.ALWAYS }
label={ __( 'Always', 'woocommerce' ) }
/>
</ToggleGroupControl>
{ attributes.overlay !== 'never' && (
<ExternalLink
href={ templatePartEditUri }
className="wc-block-editor-product-filters__overlay-link"
>
{ __( 'Edit overlay', 'woocommerce' ) }
</ExternalLink>
) }
</PanelBody>
</InspectorControls>
<InnerBlocks templateLock={ false } template={ TEMPLATE } /> <InnerBlocks templateLock={ false } template={ TEMPLATE } />
</div> </div>
); );
}; };
export const Save = () => {
const blockProps = useBlockProps.save();
const innerBlocksProps = useInnerBlocksProps.save( blockProps );
return <div { ...innerBlocksProps } />;
};

View File

@ -46,7 +46,7 @@
"supports": { "supports": {
"interactivity": true, "interactivity": true,
"align": [ "left", "right", "center"], "align": [ "left", "right", "center"],
"inserter": false, "inserter": true,
"color": { "color": {
"__experimentalDefaultControls": { "__experimentalDefaultControls": {
"text": false, "text": false,

View File

@ -12,12 +12,7 @@ import { filter, filterThreeLines } from '@woocommerce/icons';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import type { import type { BlockAttributes, BlockVariationTriggerType } from './types';
BlockAttributes,
BlockContext,
BlockVariationTriggerType,
} from './types';
import { BlockOverlayAttribute as ProductFiltersBlockOverlayAttribute } from '../../constants';
import './editor.scss'; import './editor.scss';
import { Inspector } from './inspector-controls'; import { Inspector } from './inspector-controls';
@ -128,9 +123,9 @@ const OverlayNavigationContent = ( {
return null; return null;
}; };
type BlockProps = BlockEditProps< BlockAttributes > & { context: BlockContext }; type BlockProps = BlockEditProps< BlockAttributes >;
export const Edit = ( { attributes, setAttributes, context }: BlockProps ) => { export const Edit = ( { attributes, setAttributes }: BlockProps ) => {
const { const {
navigationStyle, navigationStyle,
buttonStyle, buttonStyle,
@ -139,57 +134,19 @@ export const Edit = ( { attributes, setAttributes, context }: BlockProps ) => {
style, style,
triggerType, triggerType,
} = attributes; } = attributes;
const { 'woocommerce/product-filters/overlay': productFiltersOverlayMode } =
context;
const blockProps = useBlockProps( { const blockProps = useBlockProps( {
className: clsx( 'wc-block-product-filters-overlay-navigation', { className: clsx( 'wc-block-product-filters-overlay-navigation', {
'wp-block-button__link wp-element-button': buttonStyle !== 'link', 'wp-block-button__link wp-element-button': buttonStyle !== 'link',
} ), } ),
} ); } );
const {
isWithinProductFiltersOverlayTemplatePart,
}: {
isWithinProductFiltersOverlayTemplatePart: boolean;
} = useSelect( ( select ) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const { getCurrentPostId, getCurrentPostType } =
select( 'core/editor' );
const currentPostId = getCurrentPostId< string >();
const currentPostIdParts = currentPostId?.split( '//' );
const currentPostType = getCurrentPostType< string >();
let isProductFiltersOverlayTemplatePart = false;
if (
currentPostType === 'wp_template_part' &&
currentPostIdParts?.length > 1
) {
const [ , postId ] = currentPostIdParts;
isProductFiltersOverlayTemplatePart =
postId === 'product-filters-overlay';
}
return {
isWithinProductFiltersOverlayTemplatePart:
isProductFiltersOverlayTemplatePart,
};
} );
const shouldHideBlock = () => { const shouldHideBlock = () => {
if ( triggerType === 'open-overlay' ) { if ( triggerType === 'open-overlay' ) {
if (
productFiltersOverlayMode ===
ProductFiltersBlockOverlayAttribute.NEVER
) {
return true;
}
if ( isWithinProductFiltersOverlayTemplatePart ) {
return true;
}
}
return false; return false;
}
return true;
}; };
// We need useInnerBlocksProps because Gutenberg only applies layout classes // We need useInnerBlocksProps because Gutenberg only applies layout classes
// to parent block. We don't have any inner blocks but we want to use the // to parent block. We don't have any inner blocks but we want to use the

View File

@ -1,8 +1,3 @@
/**
* Internal dependencies
*/
import { BlockOverlayAttributeOptions as ProductFiltersBlockOverlayAttributeOptions } from '../../types';
type BorderRadius = { type BorderRadius = {
bottomLeft: string; bottomLeft: string;
bottomRight: string; bottomRight: string;
@ -14,18 +9,12 @@ type BorderSide = {
width: string; width: string;
}; };
export interface BlockContext {
// eslint-disable-next-line @typescript-eslint/naming-convention
'woocommerce/product-filters/overlay': ProductFiltersBlockOverlayAttributeOptions;
}
export type BlockVariationTriggerType = 'open-overlay' | 'close-overlay'; export type BlockVariationTriggerType = 'open-overlay' | 'close-overlay';
export type BlockAttributes = { export type BlockAttributes = {
navigationStyle: 'label-and-icon' | 'label-only' | 'icon-only'; navigationStyle: 'label-and-icon' | 'label-only' | 'icon-only';
buttonStyle: string; buttonStyle: string;
iconSize?: number; iconSize?: number;
overlayMode: ProductFiltersBlockOverlayAttributeOptions;
triggerType: BlockVariationTriggerType; triggerType: BlockVariationTriggerType;
overlayIcon: string; overlayIcon: string;
style: { style: {

View File

@ -1,15 +1,6 @@
/**
* Internal dependencies
*/
import { BlockOverlayAttribute } from './constants';
export type BlockOverlayAttributeOptions =
( typeof BlockOverlayAttribute )[ keyof typeof BlockOverlayAttribute ];
export interface BlockAttributes { export interface BlockAttributes {
setAttributes: ( attributes: ProductFiltersBlockAttributes ) => void; setAttributes: ( attributes: ProductFiltersBlockAttributes ) => void;
productId?: string; productId?: string;
overlay: BlockOverlayAttributeOptions;
overlayIcon: overlayIcon:
| 'filter-icon-1' | 'filter-icon-1'
| 'filter-icon-2' | 'filter-icon-2'

View File

@ -1,305 +0,0 @@
/**
* External dependencies
*/
import { test, expect } from '@woocommerce/e2e-utils';
const templatePartData = {
selectors: {
frontend: {},
editor: {
blocks: {
activeFilters: {
title: 'Active (Experimental)',
blockLabel: 'Block: Active (Experimental)',
},
productFilters: {
title: 'Product Filters (Experimental)',
blockLabel: 'Block: Product Filters (Experimental)',
},
filterOptions: {
title: 'Filter Options',
blockLabel: 'Block: Filter Options',
},
productFiltersOverlayNavigation: {
title: 'Overlay Navigation (Experimental)',
name: 'woocommerce/product-filters-overlay-navigation',
blockLabel: 'Block: Overlay Navigation (Experimental)',
},
},
},
},
slug: 'product-filters',
productPage: '/product/hoodie/',
};
test.describe( 'Filters Overlay Template Part', () => {
test.beforeEach( async ( { admin, requestUtils } ) => {
await requestUtils.activatePlugin(
'woocommerce-blocks-test-enable-experimental-features'
);
await admin.visitSiteEditor( {
postType: 'wp_template_part',
postId: 'woocommerce/woocommerce//product-filters-overlay',
canvas: 'edit',
} );
} );
test( 'should be visible in the template parts list', async ( {
page,
admin,
} ) => {
await admin.visitSiteEditor( {
postType: 'wp_template_part',
} );
const block = page
.getByLabel( 'Patterns content' )
.getByText( 'Filters Overlay' )
.and( page.getByRole( 'button' ) );
await expect( block ).toBeVisible();
} );
test( 'should render the correct inner blocks', async ( { editor } ) => {
const productFiltersTemplatePart = editor.canvas
.locator( '[data-type="core/template-part"]' )
.filter( {
has: editor.canvas.getByLabel(
templatePartData.selectors.editor.blocks.productFilters
.blockLabel
),
} );
await expect( productFiltersTemplatePart ).toBeVisible();
} );
test.describe( 'frontend', () => {
test.beforeEach( async ( { admin } ) => {
await admin.visitSiteEditor( {
postId: `woocommerce/woocommerce//archive-product`,
postType: 'wp_template',
canvas: 'edit',
} );
} );
// Since we need to overhaul the overlay area, we can skip this test for now.
// eslint-disable-next-line playwright/no-skipped-test
test.skip( 'should open and close the dialog when clicking on the Product Filters Overlay Navigation block', async ( {
editor,
page,
frontendUtils,
} ) => {
await editor.setContent( '' );
await editor.openGlobalBlockInserter();
await page
.getByText(
templatePartData.selectors.editor.blocks.productFilters
.title
)
.click();
const block = editor.canvas.getByLabel(
templatePartData.selectors.editor.blocks.productFilters
.blockLabel
);
await expect( block ).toBeVisible();
// This forces the list view to show the inner blocks of the Product Filters template part.
await editor.canvas
.getByLabel(
templatePartData.selectors.editor.blocks.activeFilters
.blockLabel
)
.click();
await editor.openDocumentSettingsSidebar();
await page.getByLabel( 'Document Overview' ).click();
await page
.getByRole( 'link', {
name: templatePartData.selectors.editor.blocks
.productFilters.title,
} )
.nth( 1 )
.click();
const layoutSettings = editor.page.getByText(
'OverlayNeverMobileAlways'
);
await layoutSettings.getByLabel( 'Always' ).click();
await editor.page
.getByRole( 'link', {
name: templatePartData.selectors.editor.blocks
.productFiltersOverlayNavigation.title,
} )
.click();
await editor.saveSiteEditorEntities( {
isOnlyCurrentEntityDirty: false,
} );
await page.goto( '/shop/' );
const productFiltersOverlayNavigation = (
await frontendUtils.getBlockByName(
templatePartData.selectors.editor.blocks
.productFiltersOverlayNavigation.name
)
).filter( {
has: page.locator( ':visible' ),
} );
await expect( productFiltersOverlayNavigation ).toBeVisible();
await page
.locator( '.wc-block-product-filters-overlay-navigation' )
.first()
.click();
const productFiltersDialog = page.locator(
'.wc-block-product-filters--dialog-open'
);
await expect( productFiltersDialog ).toBeVisible();
const productFiltersDialogCloseButton = (
await frontendUtils.getBlockByName(
templatePartData.selectors.editor.blocks
.productFiltersOverlayNavigation.name
)
).filter( { hasText: 'Close' } );
await expect( productFiltersDialogCloseButton ).toBeVisible();
await productFiltersDialogCloseButton.click();
await expect( productFiltersDialog ).toBeHidden();
} );
// Since we need to overhaul the overlay area, we can skip this test for now.
// eslint-disable-next-line playwright/no-skipped-test
test.skip( 'should hide Product Filters Overlay Navigation block when the Overlay mode is set to `Never`', async ( {
editor,
page,
frontendUtils,
} ) => {
await editor.setContent( '' );
await editor.openGlobalBlockInserter();
await page
.getByText(
templatePartData.selectors.editor.blocks.productFilters
.title
)
.click();
const block = editor.canvas.getByLabel(
templatePartData.selectors.editor.blocks.productFilters
.blockLabel
);
await expect( block ).toBeVisible();
// This forces the list view to show the inner blocks of the Product Filters template part.
await editor.canvas
.getByLabel(
templatePartData.selectors.editor.blocks.activeFilters
.blockLabel
)
.click();
await editor.openDocumentSettingsSidebar();
await page.getByLabel( 'Document Overview' ).click();
await page
.getByRole( 'link', {
name: templatePartData.selectors.editor.blocks
.productFilters.title,
} )
.nth( 1 )
.click();
const layoutSettings = editor.page.getByText(
'OverlayNeverMobileAlways'
);
await layoutSettings.getByLabel( 'Never' ).click();
await editor.saveSiteEditorEntities( {
isOnlyCurrentEntityDirty: true,
} );
await page.goto( '/shop/' );
const productFiltersOverlayNavigation = (
await frontendUtils.getBlockByName(
templatePartData.selectors.editor.blocks
.productFiltersOverlayNavigation.name
)
).filter( {
has: page.locator( ':visible' ),
} );
await expect( productFiltersOverlayNavigation ).toBeHidden();
} );
// Since we need to overhaul the overlay area, we can skip this test for now.
// eslint-disable-next-line playwright/no-skipped-test
test.skip( 'should hide Product Filters Overlay Navigation block when the Overlay mode is set to `Mobile` and user is on desktop', async ( {
editor,
page,
frontendUtils,
} ) => {
await editor.setContent( '' );
await editor.openGlobalBlockInserter();
await page
.getByText(
templatePartData.selectors.editor.blocks.productFilters
.title
)
.click();
const block = editor.canvas.getByLabel(
templatePartData.selectors.editor.blocks.productFilters
.blockLabel
);
await expect( block ).toBeVisible();
// This forces the list view to show the inner blocks of the Product Filters template part.
await editor.canvas
.getByLabel(
templatePartData.selectors.editor.blocks.activeFilters
.blockLabel
)
.click();
await editor.openDocumentSettingsSidebar();
await page.getByLabel( 'Document Overview' ).click();
await page
.getByRole( 'link', {
name: templatePartData.selectors.editor.blocks
.productFilters.title,
} )
.nth( 1 )
.click();
const layoutSettings = editor.page.getByText(
'OverlayNeverMobileAlways'
);
await layoutSettings.getByLabel( 'Mobile' ).click();
await editor.page
.getByRole( 'link', {
name: templatePartData.selectors.editor.blocks
.productFiltersOverlayNavigation.title,
} )
.click();
await editor.saveSiteEditorEntities( {
isOnlyCurrentEntityDirty: false,
} );
await page.goto( '/shop/' );
const productFiltersOverlayNavigation = (
await frontendUtils.getBlockByName(
templatePartData.selectors.editor.blocks
.productFiltersOverlayNavigation.name
)
).filter( {
has: page.locator( ':visible' ),
} );
await expect( productFiltersOverlayNavigation ).toBeHidden();
} );
} );
} );

View File

@ -1,80 +0,0 @@
/**
* External dependencies
*/
import { test, expect, BlockData } from '@woocommerce/e2e-utils';
const blockData: BlockData = {
name: 'Product Filters (Experimental)',
slug: 'woocommerce/product-filters',
mainClass: '.wp-block-woocommerce-product-filters',
selectors: {
editor: {
block: '.wp-block-woocommerce-product-filters',
},
frontend: {},
},
};
test.describe( 'Product Filters Template Part', () => {
test.beforeEach( async ( { admin, requestUtils } ) => {
await requestUtils.activatePlugin(
'woocommerce-blocks-test-enable-experimental-features'
);
await admin.visitSiteEditor( {
postType: 'wp_template_part',
postId: 'woocommerce/woocommerce//product-filters',
canvas: 'edit',
} );
} );
test( 'should be visible in the templates part list', async ( {
page,
admin,
} ) => {
await admin.visitSiteEditor( {
postType: 'wp_template_part',
} );
const templatePart = page
.getByLabel( 'Patterns content' )
.getByText( 'Product Filters (Experimental)', { exact: true } )
.and( page.getByRole( 'button' ) );
await expect( templatePart ).toBeVisible();
} );
test( 'should render the Product Filters block', async ( { editor } ) => {
const productFiltersBlock = editor.canvas.getByLabel(
`Block: ${ blockData.name }`
);
await expect( productFiltersBlock ).toBeVisible();
} );
test( 'Filters > can be added multiple times', async ( { editor } ) => {
const block = editor.canvas.getByLabel( `Block: ${ blockData.name }` );
await expect( block ).toBeVisible();
const searchTerms = [ 'Color (Experimental)', 'Active (Experimental)' ];
for ( const filter of searchTerms ) {
await editor.selectBlocks( blockData.selectors.editor.block );
const addBlock = block.getByRole( 'button', {
name: 'Add block',
} );
await addBlock.click();
await editor.page.getByPlaceholder( 'Search' ).fill( filter );
const searchResult = editor.page.getByRole( 'option', {
name: filter,
} );
await expect( searchResult ).toBeVisible();
await searchResult.click();
const _locator = `[aria-label="Block: ${ filter }"]`;
await expect( editor.canvas.locator( _locator ) ).toHaveCount( 2 );
}
} );
} );

View File

@ -183,63 +183,6 @@ test.describe( `${ blockData.name }`, () => {
).toBeVisible(); ).toBeVisible();
} ); } );
test( 'should display the correct inspector setting controls', async ( {
editor,
pageObject,
} ) => {
await pageObject.addProductFiltersBlock( { cleanContent: true } );
const filtersBlock = editor.canvas.getByLabel(
blockData.selectors.editor.blocks.filters.label
);
await expect( filtersBlock ).toBeVisible();
const overlayBlock = editor.canvas.getByLabel(
blockData.selectors.editor.blocks.overlay.label
);
// Overlay mode is set to 'Never' by default so the block should be hidden
await expect( overlayBlock ).toBeHidden();
await editor.openDocumentSettingsSidebar();
// Layout settings
await expect(
editor.page.getByText( 'LayoutJustificationOrientation' )
).toBeVisible();
// Overlay settings
const overlayModeSettings = [ 'Never', 'Mobile', 'Always' ];
await expect( editor.page.getByText( 'Overlay' ) ).toBeVisible();
for ( const mode of overlayModeSettings ) {
await expect( editor.page.getByText( mode ) ).toBeVisible();
}
await editor.page.getByLabel( 'Never' ).click();
await expect( editor.page.getByText( 'Edit overlay' ) ).toBeHidden();
await expect( overlayBlock ).toBeHidden();
await editor.page.getByLabel( 'Mobile' ).click();
await expect( editor.page.getByText( 'Edit overlay' ) ).toBeVisible();
await expect( overlayBlock ).toBeVisible();
await editor.page.getByLabel( 'Always' ).click();
await expect( editor.page.getByText( 'Edit overlay' ) ).toBeVisible();
await expect( overlayBlock ).toBeVisible();
await editor.page.getByLabel( 'Never' ).click();
await expect( overlayBlock ).toBeHidden();
} );
test( 'Layout > default to vertical stretch', async ( { test( 'Layout > default to vertical stretch', async ( {
editor, editor,
pageObject, pageObject,

View File

@ -1,32 +0,0 @@
<svg width="248" height="140" viewBox="0 0 248 140" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_dd_1397_9617)">
<rect width="248" height="140" rx="2" fill="white" shape-rendering="crispEdges"/>
<rect x="84" y="15" width="80" height="8" rx="1" fill="#E0E0E0"/>
<rect x="84" y="31" width="56" height="6" rx="1" fill="#E0E0E0"/>
<rect x="84" y="41" width="56" height="16" rx="1" fill="#E0E0E0"/>
<rect x="84" y="65" width="80" height="6" rx="1" fill="#E0E0E0"/>
<rect x="84" y="75" width="31" height="16" rx="1" fill="#E0E0E0"/>
<rect x="84" y="99" width="70" height="6" rx="1" fill="#E0E0E0"/>
<rect x="84" y="109" width="56" height="16" rx="1" fill="#E0E0E0"/>
<path d="M230.773 17.8336L234.485 21.5459L235.546 20.4853L231.834 16.773L235.546 13.0607L234.485 12L230.773 15.7123L227.061 12L226 13.0607L229.712 16.773L226 20.4853L227.061 21.5459L230.773 17.8336Z" fill="#BBBBBB"/>
</g>
<rect x="0.5" y="0.5" width="247" height="139" rx="1.5" stroke="#DDDDDD"/>
<defs>
<filter id="filter0_dd_1397_9617" x="-9" y="-6" width="266" height="158" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="3"/>
<feGaussianBlur stdDeviation="4.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1397_9617"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_1397_9617" result="effect2_dropShadow_1397_9617"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_1397_9617" result="shape"/>
</filter>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,38 +0,0 @@
<svg width="248" height="140" viewBox="0 0 248 140" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_481_510179)">
<rect width="248" height="140" rx="2" fill="black" fill-opacity="0.05"/>
<g filter="url(#filter0_dd_481_510179)">
<path d="M0 0H146C147.105 0 148 0.895431 148 2V138C148 139.105 147.105 140 146 140H0V0Z" fill="white"/>
<rect x="24" y="15" width="80" height="8" rx="1" fill="#E0E0E0"/>
<rect x="24" y="31" width="56" height="6" rx="1" fill="#E0E0E0"/>
<rect x="24" y="41" width="56" height="16" rx="1" fill="#E0E0E0"/>
<rect x="24" y="65" width="80" height="6" rx="1" fill="#E0E0E0"/>
<rect x="24" y="75" width="31" height="16" rx="1" fill="#E0E0E0"/>
<rect x="24" y="99" width="70" height="6" rx="1" fill="#E0E0E0"/>
<rect x="24" y="109" width="56" height="16" rx="1" fill="#E0E0E0"/>
<path d="M130.773 17.8336L134.485 21.5459L135.546 20.4853L131.834 16.773L135.546 13.0607L134.485 12L130.773 15.7123L127.061 12L126 13.0607L129.712 16.773L126 20.4853L127.061 21.5459L130.773 17.8336Z" fill="#BBBBBB"/>
</g>
</g>
<rect x="0.5" y="0.5" width="247" height="139" rx="1.5" stroke="#DDDDDD"/>
<defs>
<filter id="filter0_dd_481_510179" x="-9" y="-6" width="166" height="158" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="3"/>
<feGaussianBlur stdDeviation="4.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_481_510179"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_481_510179" result="effect2_dropShadow_481_510179"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_481_510179" result="shape"/>
</filter>
<clipPath id="clip0_481_510179">
<rect width="248" height="140" rx="2" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1,38 +0,0 @@
<svg width="248" height="140" viewBox="0 0 248 140" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1429_82805)">
<rect width="248" height="140" rx="2" fill="black" fill-opacity="0.05"/>
<g filter="url(#filter0_dd_1429_82805)">
<path d="M100 0H246C247.105 0 248 0.895431 248 2V138C248 139.105 247.105 140 246 140H100V0Z" fill="white"/>
<rect x="143.996" y="15" width="80" height="8" rx="1" fill="#E0E0E0"/>
<rect x="143.996" y="31" width="56" height="6" rx="1" fill="#E0E0E0"/>
<rect x="143.996" y="41" width="56" height="16" rx="1" fill="#E0E0E0"/>
<rect x="143.996" y="65" width="80" height="6" rx="1" fill="#E0E0E0"/>
<rect x="143.996" y="75" width="31" height="16" rx="1" fill="#E0E0E0"/>
<rect x="143.996" y="99" width="70" height="6" rx="1" fill="#E0E0E0"/>
<rect x="143.996" y="109" width="56" height="16" rx="1" fill="#E0E0E0"/>
<path d="M117.223 17.8336L120.935 21.5459L121.996 20.4853L118.284 16.773L121.996 13.0607L120.935 12L117.223 15.7123L113.511 12L112.45 13.0607L116.163 16.773L112.45 20.4853L113.511 21.5459L117.223 17.8336Z" fill="#BBBBBB"/>
</g>
</g>
<rect x="0.5" y="0.5" width="247" height="139" rx="1.5" stroke="#DDDDDD"/>
<defs>
<filter id="filter0_dd_1429_82805" x="91" y="-6" width="166" height="158" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="3"/>
<feGaussianBlur stdDeviation="4.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1429_82805"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_1429_82805" result="effect2_dropShadow_1429_82805"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_1429_82805" result="shape"/>
</filter>
<clipPath id="clip0_1429_82805">
<rect width="248" height="140" rx="2" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,4 @@
Significance: patch
Type: tweak
Comment: Experimental block - remove template parts

View File

@ -17,8 +17,6 @@ use Automattic\WooCommerce\Blocks\Templates\ProductCategoryTemplate;
use Automattic\WooCommerce\Blocks\Templates\ProductTagTemplate; use Automattic\WooCommerce\Blocks\Templates\ProductTagTemplate;
use Automattic\WooCommerce\Blocks\Templates\ProductSearchResultsTemplate; use Automattic\WooCommerce\Blocks\Templates\ProductSearchResultsTemplate;
use Automattic\WooCommerce\Blocks\Templates\SingleProductTemplate; use Automattic\WooCommerce\Blocks\Templates\SingleProductTemplate;
use Automattic\WooCommerce\Blocks\Templates\ProductFiltersTemplate;
use Automattic\WooCommerce\Blocks\Templates\ProductFiltersOverlayTemplate;
/** /**
* BlockTemplatesRegistry class. * BlockTemplatesRegistry class.
@ -61,11 +59,6 @@ class BlockTemplatesRegistry {
MiniCartTemplate::SLUG => new MiniCartTemplate(), MiniCartTemplate::SLUG => new MiniCartTemplate(),
CheckoutHeaderTemplate::SLUG => new CheckoutHeaderTemplate(), CheckoutHeaderTemplate::SLUG => new CheckoutHeaderTemplate(),
); );
if ( Features::is_enabled( 'experimental-blocks' ) ) {
$template_parts[ ProductFiltersTemplate::SLUG ] = new ProductFiltersTemplate();
$template_parts[ ProductFiltersOverlayTemplate::SLUG ] = new ProductFiltersOverlayTemplate();
}
} else { } else {
$template_parts = array(); $template_parts = array();
} }

View File

@ -1,8 +1,6 @@
<?php <?php
namespace Automattic\WooCommerce\Blocks\BlockTypes; namespace Automattic\WooCommerce\Blocks\BlockTypes;
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
/** /**
* ProductFilters class. * ProductFilters class.
*/ */
@ -40,90 +38,6 @@ class ProductFilters extends AbstractBlock {
$this->asset_data_registry->add( 'isWidgetEditor', 'widgets.php' === $pagenow || 'customize.php' === $pagenow ); $this->asset_data_registry->add( 'isWidgetEditor', 'widgets.php' === $pagenow || 'customize.php' === $pagenow );
} }
/**
* Return the dialog content.
*
* @return string
*/
protected function render_dialog() {
$template_part = BlockTemplateUtils::get_template_part( 'product-filters-overlay' );
$html = $this->render_template_part( $template_part );
$html = strtr(
'<dialog hidden role="dialog" aria-modal="true">
{{html}}
</dialog>',
array(
'{{html}}' => $html,
)
);
$p = new \WP_HTML_Tag_Processor( $html );
if ( $p->next_tag() ) {
$p->set_attribute( 'data-wc-interactive', wp_json_encode( array( 'namespace' => 'woocommerce/product-filters' ), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ) );
$p->set_attribute( 'data-wc-bind--hidden', '!state.isDialogOpen' );
$p->set_attribute( 'data-wc-class--wc-block-product-filters--dialog-open', 'state.isDialogOpen' );
$p->set_attribute( 'data-wc-class--wc-block-product-filters--with-admin-bar', 'context.hasPageWithWordPressAdminBar' );
$html = $p->get_updated_html();
}
return $html;
}
/**
* This method is used to render the template part. For each template part, we parse the blocks and render them.
*
* @param string $template_part The template part to render.
* @return string The rendered template part.
*/
protected function render_template_part( $template_part ) {
$parsed_blocks = parse_blocks( $template_part );
$wrapper_template_part_block = $parsed_blocks[0];
$html = $wrapper_template_part_block['innerHTML'];
$target_div = '</div>';
$template_part_content_html = array_reduce(
$wrapper_template_part_block['innerBlocks'],
function ( $carry, $item ) {
if ( 'core/template-part' === $item['blockName'] ) {
$inner_template_part = BlockTemplateUtils::get_template_part( $item['attrs']['slug'] );
$inner_template_part_content_html = $this->render_template_part( $inner_template_part );
return $carry . $inner_template_part_content_html;
}
return $carry . render_block( $item );
},
''
);
$html = str_replace( $target_div, $template_part_content_html . $target_div, $html );
return $html;
}
/**
* Inject dialog into the product filters HTML.
*
* @param string $product_filters_html The Product Filters HTML.
* @param string $dialog_html The dialog HTML.
*
* @return string
*/
protected function inject_dialog( $product_filters_html, $dialog_html ) {
// Find the position of the last </div>.
$pos = strrpos( $product_filters_html, '</div>' );
if ( $pos ) {
// Inject the dialog_html at the correct position.
$html = substr_replace( $product_filters_html, $dialog_html, $pos, 0 );
return $html;
}
return $product_filters_html;
}
/** /**
* Include and render the block. * Include and render the block.
* *
@ -151,13 +65,6 @@ class ProductFilters extends AbstractBlock {
$tags->set_attribute( 'data-wc-navigation-id', $this->generate_navigation_id( $block ) ); $tags->set_attribute( 'data-wc-navigation-id', $this->generate_navigation_id( $block ) );
$tags->set_attribute( 'data-wc-watch', 'callbacks.maybeNavigate' ); $tags->set_attribute( 'data-wc-watch', 'callbacks.maybeNavigate' );
if (
'always' === $attributes['overlay'] ||
( 'mobile' === $attributes['overlay'] && wp_is_mobile() )
) {
return $this->inject_dialog( $tags->get_updated_html(), $this->render_dialog() );
}
return $tags->get_updated_html(); return $tags->get_updated_html();
} }
} }

View File

@ -35,11 +35,6 @@ class ProductFiltersOverlayNavigation extends AbstractBlock {
'class' => 'wc-block-product-filters-overlay-navigation', 'class' => 'wc-block-product-filters-overlay-navigation',
) )
); );
$overlay_mode = isset( $block->context['woocommerce/product-filters/overlay'] ) ? $block->context['woocommerce/product-filters/overlay'] : 'never';
if ( 'open-overlay' === $attributes['triggerType'] && ( 'never' === $overlay_mode || ( ! wp_is_mobile() && 'mobile' === $overlay_mode ) ) ) {
return null;
}
$html = strtr( $html = strtr(
'<div {{wrapper_attributes}}> '<div {{wrapper_attributes}}>

View File

@ -1,47 +0,0 @@
<?php
namespace Automattic\WooCommerce\Blocks\Templates;
/**
* ProductFiltersOverlayTemplate class.
*
* @internal
*/
class ProductFiltersOverlayTemplate extends AbstractTemplatePart {
/**
* The slug of the template.
*
* @var string
*/
const SLUG = 'product-filters-overlay';
/**
* The template part area where the template part belongs.
*
* @var string
*/
public $template_area = 'uncategorized';
/**
* Initialization method.
*/
public function init() {}
/**
* Returns the title of the template.
*
* @return string
*/
public function get_template_title() {
return _x( 'Filters Overlay', 'Template name', 'woocommerce' );
}
/**
* Returns the description of the template.
*
* @return string
*/
public function get_template_description() {
return __( 'Template used to display the Product Filters Overlay.', 'woocommerce' );
}
}

View File

@ -1,95 +0,0 @@
<?php
declare(strict_types=1);
namespace Automattic\WooCommerce\Blocks\Templates;
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
/**
* ProductFiltersTemplate class.
*
* @internal
*/
class ProductFiltersTemplate extends AbstractTemplatePart {
/**
* The slug of the template.
*
* @var string
*/
const SLUG = 'product-filters';
/**
* The template part area where the template part belongs.
*
* @var string
*/
public $template_area = 'uncategorized';
/**
* Initialization method.
*/
public function init() {
add_filter( 'get_block_type_variations', array( $this, 'register_block_type_variation' ), 10, 2 );
}
/**
* Returns the title of the template.
*
* @return string
*/
public function get_template_title() {
return _x( 'Product Filters (Experimental)', 'Template name', 'woocommerce' );
}
/**
* Returns the description of the template.
*
* @return string
*/
public function get_template_description() {
return __(
'This is the template part for the product filters displayed on different pages across your store.',
'woocommerce'
);
}
/**
* Add variation for this template part to make it available in the block inserter.
*
* @param array $variations Array of registered variations for a block type.
* @param WP_Block_Type $block_type The full block type object.
*/
public function register_block_type_variation( $variations, $block_type ) {
if ( 'core/template-part' !== $block_type->name ) {
return $variations;
}
// If template part is modified, Core will pick it up and register a variation
// for it. Check if the variation already exists before adding it.
foreach ( $variations as $variation ) {
if ( ! empty( $variation['attributes']['slug'] ) && 'product-filters' === $variation['attributes']['slug'] ) {
return $variations;
}
}
$theme = 'woocommerce/woocommerce';
// Check if current theme overrides this template part.
if ( BlockTemplateUtils::theme_has_template_part( 'product-filters' ) ) {
$theme = wp_get_theme()->get( 'TextDomain' );
}
$variations[] = array(
'name' => 'file_' . self::SLUG,
'title' => $this->get_template_title(),
'description' => true,
'attributes' => array(
'slug' => self::SLUG,
'theme' => $theme,
'area' => $this->template_area,
),
'scope' => array( 'inserter' ),
'icon' => 'layout',
);
return $variations;
}
}

View File

@ -319,11 +319,6 @@ class BlockTemplateUtils {
'mini-cart.html', 'mini-cart.html',
); );
if ( Features::is_enabled( 'experimental-blocks' ) ) {
$wp_template_part_filenames[] = 'product-filters.html';
$wp_template_part_filenames[] = 'product-filters-overlay.html';
}
/* /*
* This may return the blockified directory for wp_templates. * This may return the blockified directory for wp_templates.
* At the moment every template file has a corresponding blockified file. * At the moment every template file has a corresponding blockified file.

View File

@ -1,8 +0,0 @@
<!-- wp:woocommerce/product-filters-overlay {"lock":{"move":true,"remove":true}} -->
<div class="wp-block-woocommerce-product-filters-overlay wc-block-product-filters-overlay" style="padding-top:1rem;padding-right:1rem;padding-bottom:1rem;padding-left:1rem">
<!-- wp:woocommerce/product-filters-overlay-navigation {"triggerType":"close-overlay","lock":{"move":true,"remove":true}} -->
<div class="wp-block-woocommerce-product-filters-overlay-navigation alignright wc-block-product-filters-overlay-navigation"></div>
<!-- /wp:woocommerce/product-filters-overlay-navigation -->
<!-- wp:template-part {"slug":"product-filters","theme":"woocommerce/woocommerce","area":"uncategorized","lock":{"move":true,"remove":true}} /--></div>
<!-- /wp:woocommerce/product-filters-overlay -->

View File

@ -1,28 +0,0 @@
<!-- wp:woocommerce/product-filters -->
<div class="wp-block-woocommerce-product-filters wc-block-product-filters">
<!-- wp:heading {"level":2} -->
<h2 class="wp-block-heading">Filters</h2>
<!-- /wp:heading -->
<!-- wp:woocommerce/product-filter-active {"displayStyle":"chips"} /-->
<!-- wp:pattern {"slug":"woocommerce/default-attribute-filter"} /-->
<!-- wp:buttons {"layout":{"type":"flex"}} -->
<div class="wp-block-buttons">
<!-- wp:button {"className":"wc-block-product-filters__apply-button","style":{"border":{"width":"0px","style":"none"},"typography":{"textDecoration":"none"},"outline":"none","fontSize":"medium"}} -->
<div
class="wp-block-button wc-block-product-filters__apply-button"
style="text-decoration: none"
>
<a
class="wp-block-button__link wp-element-button"
style="border-style: none; border-width: 0px"
>Apply</a
>
</div>
<!-- /wp:button -->
</div>
<!-- /wp:buttons -->
</div>
<!-- /wp:woocommerce/product-filters -->