[Experimental] Product Filters: add layout and block spacing support (#48429)

* add: layout and block spacing support

* chore: changelog

* add: vertical aligment controls

* fix: update test to check for inspector styles controls

* test: ensure the block spacing setting is visible

* test: ensure the layout settings is rendered

* test: default layout settings

* test: justification setting

* test: orientation

* test: block spacing

* test: remove unnecessary util

* test: store selector in blockData

* test: use layout matching
This commit is contained in:
Tung Du 2024-06-21 11:37:46 +07:00 committed by GitHub
parent ee2a545dce
commit 921e86e6ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 241 additions and 76 deletions

View File

@ -24,6 +24,19 @@
"typography": { "typography": {
"fontSize": true, "fontSize": true,
"textAlign": true "textAlign": true
},
"layout": {
"default": {
"type": "flex",
"orientation": "vertical",
"justifyContent": "stretch",
"verticalAlignment": "top"
},
"allowVerticalAlignment": true,
"allowInheriting": false
},
"spacing": {
"blockGap": true
} }
}, },
"textdomain": "woocommerce", "textdomain": "woocommerce",

View File

@ -14,10 +14,8 @@ pnpm --filter='@woocommerce/plugin-woocommerce' watch:build
Next, run the following command from the [`woocommerce-blocks` plugin folder](../../../woocommerce-blocks/) to start a `wp-env` instance and install all the testing products, languages, etc.: Next, run the following command from the [`woocommerce-blocks` plugin folder](../../../woocommerce-blocks/) to start a `wp-env` instance and install all the testing products, languages, etc.:
```shell ````shell
cd plugins/woocommerce-blocks/ cd plugins/woocommerce-blocks/
pnpm env:start
```
> [!TIP] > [!TIP]
> If you want to start/stop the environment without running the whole setup, use the native `wp-env` commands directly, e.g. `npx wp-env start` and `npx wp-env stop`. > If you want to start/stop the environment without running the whole setup, use the native `wp-env` commands directly, e.g. `npx wp-env start` and `npx wp-env stop`.
@ -30,14 +28,14 @@ Occasionally, you'll need to reset the environment, e.g., when testing products
```shell ```shell
pnpm env:restart pnpm env:restart
``` ````
## Running and debugging tests ## Running and debugging tests
> [!NOTE] > [!NOTE]
> If you're using VSCode, we recommend using the [Playwright Test](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright) extension to run and debug the tests. > If you're using VSCode, we recommend using the [Playwright Test](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright) extension to run and debug the tests.
Here is a basic set of commands to quickly start running and debugging the tests. For full documentation, see the official Playwright guide on [Running and Debugging Tests](https://playwright.dev/docs/running-tests). Here is a basic set of commands to quickly start running and debugging the tests. For full documentation, see the official Playwright guide on [Running and Debugging Tests](https://playwright.dev/docs/running-tests).
```shell ```shell
# Run all available tests. # Run all available tests.
@ -120,7 +118,6 @@ function my_fancy_plugin() {
add_action('wp_footer', 'my_fancy_plugin'); add_action('wp_footer', 'my_fancy_plugin');
``` ```
Once the plugin is saved, it will be automatically picked up by `wp-env` - no need to restart the environment. To activate your plugin, use the `RequestUtils.activatePlugin()` API, for example: Once the plugin is saved, it will be automatically picked up by `wp-env` - no need to restart the environment. To activate your plugin, use the `RequestUtils.activatePlugin()` API, for example:
```ts ```ts
@ -129,17 +126,17 @@ Once the plugin is saved, it will be automatically picked up by `wp-env` - no ne
import { test, expect } from '@woocommerce/e2e-utils'; import { test, expect } from '@woocommerce/e2e-utils';
test( 'My fancy plugin', async ( { page, requestUtils } ) => { test( 'My fancy plugin', async ( { page, requestUtils } ) => {
await requestUtils.activatePlugin( await requestUtils.activatePlugin(
'woocommerce-blocks-test-my-fancy-plugin' 'woocommerce-blocks-test-my-fancy-plugin'
); );
await page.goto( '/shop' ); await page.goto( '/shop' );
await expect( page.getByText( 'Howdy!' ) ).toBeVisible(); await expect( page.getByText( 'Howdy!' ) ).toBeVisible();
} ); } );
``` ```
> [!IMPORTANT] > [!IMPORTANT]
> A plugin's slug is created automatically **from the plugin's name**, not from the `@package` statement as you might think. So, if your plugin is named `WooCommerce Blocks Test Bazzinga`, you'll need to activate it by `woocommerce-blocks-test-bazzinga`. > A plugin's slug is created automatically **from the plugin's name**, not from the `@package` statement as you might think. So, if your plugin is named `WooCommerce Blocks Test Bazzinga`, you'll need to activate it by `woocommerce-blocks-test-bazzinga`.
### Themes ### Themes
@ -148,11 +145,11 @@ Currently, the default theme is Twenty Twenty Four. Activating other themes is d
```ts ```ts
test.beforeEach( async ( { page, requestUtils } ) => { test.beforeEach( async ( { page, requestUtils } ) => {
await requestUtils.activateTheme( 'storefront' ); await requestUtils.activateTheme( 'storefront' );
} ); } );
``` ```
> [!NOTE] > [!NOTE]
> Unless it's a one-off thing, remember to use the `beforeEach` hook to activate your theme. Each test starts with a clean database, which means the theme will be reset to the default one as well. > Unless it's a one-off thing, remember to use the `beforeEach` hook to activate your theme. Each test starts with a clean database, which means the theme will be reset to the default one as well.
#### Adding a new theme #### Adding a new theme
@ -171,36 +168,36 @@ Most of the time, it's better to do a little repetition instead of creating a ut
import { test as base, expect, Editor } from '@woocommerce/e2e-utils'; import { test as base, expect, Editor } from '@woocommerce/e2e-utils';
class CartUtils { class CartUtils {
editor: Editor editor: Editor;
constructor( { editor }: { editor: Editor } ) { constructor( { editor }: { editor: Editor } ) {
this.editor = editor; this.editor = editor;
} }
async addClothes( list ) { async addClothes( list ) {
// Add clothes from the list. // Add clothes from the list.
} }
async addBooks( list ) { async addBooks( list ) {
// Add books from the list. // Add books from the list.
} }
} }
const test = base.extend< { cartUtils: CartUtils } >( { const test = base.extend< { cartUtils: CartUtils } >( {
cartUtils: async ( { editor }, use ) => { cartUtils: async ( { editor }, use ) => {
await use( new CartUtils( { editor } ) ); await use( new CartUtils( { editor } ) );
}, },
} ); } );
test( 'Add products', async( { admin, cartUtils } ) => { test( 'Add products', async ( { admin, cartUtils } ) => {
await admin.createNewPost(); await admin.createNewPost();
await cartUtils.addClotes( [ 'Shirt', 'Cap', 'Pants' ] ); await cartUtils.addClotes( [ 'Shirt', 'Cap', 'Pants' ] );
await cartUtils.addBooks( [ 'Cooking with Woo' ] ) await cartUtils.addBooks( [ 'Cooking with Woo' ] );
await page.goto( '/cart' ); await page.goto( '/cart' );
await expect( this.page.getByLabel( 'Shirt' ) ).toBeVisible(); await expect( this.page.getByLabel( 'Shirt' ) ).toBeVisible();
// etc. // etc.
} ); } );
``` ```
@ -214,11 +211,11 @@ If you've come up with a utility that you think should be a part of the Core uti
import { Editor as CoreEditor } from '@wordpress/e2e-test-utils-playwright'; import { Editor as CoreEditor } from '@wordpress/e2e-test-utils-playwright';
export class Editor extends CoreEditor { export class Editor extends CoreEditor {
async insertAllWooBlocks() { async insertAllWooBlocks() {
for ( const wooBlock of [ 'all', 'woo', 'blocks' ] ) { for ( const wooBlock of [ 'all', 'woo', 'blocks' ] ) {
await this.insertBlock( wooBlock ); await this.insertBlock( wooBlock );
} }
} }
} }
``` ```
@ -237,51 +234,51 @@ When you have the template ready, we recommend creating a [test fixture](https:/
import { test as base, expect, TemplateCompiler } from '@woocommerce/e2e-utils'; import { test as base, expect, TemplateCompiler } from '@woocommerce/e2e-utils';
const test = base.extend< { const test = base.extend< {
filteredProductsTemplate: TemplateCompiler filteredProductsTemplate: TemplateCompiler;
} >( { } >( {
filteredProductsTemplate: async ( { requestUtils }, use ) => { filteredProductsTemplate: async ( { requestUtils }, use ) => {
const compiler = await requestUtils.createTemplateFromFile( const compiler = await requestUtils.createTemplateFromFile(
'archive-product_with-filters' 'archive-product_with-filters'
); );
await use( compiler ); await use( compiler );
}, },
} ); } );
test( 'Renders correct products for $10-$99 price range', async ( { test( 'Renders correct products for $10-$99 price range', async ( {
page, page,
filteredProductsTemplate, filteredProductsTemplate,
} ) => { } ) => {
await filteredProductsTemplate.compile( { await filteredProductsTemplate.compile( {
price: { price: {
from: '$10', from: '$10',
to: '$99', to: '$99',
}, },
} ); } );
await page.goto( '/shop' ); await page.goto( '/shop' );
await expect( page.getByLabel( 'Products' ) ).toHaveText( [ await expect( page.getByLabel( 'Products' ) ).toHaveText( [
'Socks', 'Socks',
'T-Shirt', 'T-Shirt',
] ); ] );
} ); } );
test( 'Renders correct products for $100-$999 price range', async ( { test( 'Renders correct products for $100-$999 price range', async ( {
page, page,
filteredProductsTemplate, filteredProductsTemplate,
} ) => { } ) => {
await filteredProductsTemplate.compile( { await filteredProductsTemplate.compile( {
price: { price: {
from: '$100', from: '$100',
to: '$990', to: '$990',
}, },
} ); } );
await page.goto( '/shop' ); await page.goto( '/shop' );
await expect( page.getByLabel( 'Products' ) ).toHaveText( [ await expect( page.getByLabel( 'Products' ) ).toHaveText( [
'Rolex', 'Rolex',
'Lambo', 'Lambo',
] ); ] );
} ); } );
``` ```

View File

@ -15,6 +15,8 @@ const blockData = {
frontend: {}, frontend: {},
editor: { editor: {
settings: {}, settings: {},
layoutWrapper:
'.wp-block-woocommerce-product-filters-is-layout-flex',
}, },
}, },
slug: 'archive-product', slug: 'archive-product',
@ -119,7 +121,7 @@ test.describe( `${ blockData.name }`, () => {
await expect( ratingFilterBlock ).toBeVisible(); await expect( ratingFilterBlock ).toBeVisible();
} ); } );
test( 'should display the correct customization settings', async ( { test( 'should display the correct inspector style controls', async ( {
editor, editor,
pageObject, pageObject,
} ) => { } ) => {
@ -132,6 +134,8 @@ test.describe( `${ blockData.name }`, () => {
await editor.openDocumentSettingsSidebar(); await editor.openDocumentSettingsSidebar();
await editor.page.getByRole( 'tab', { name: 'Styles' } ).click();
// Color settings // Color settings
const colorSettings = editor.page.getByText( 'ColorTextBackground' ); const colorSettings = editor.page.getByText( 'ColorTextBackground' );
const colorTextStylesSetting = const colorTextStylesSetting =
@ -158,5 +162,153 @@ test.describe( `${ blockData.name }`, () => {
name: 'Border', name: 'Border',
} ); } );
await expect( borderSettings ).toBeVisible(); await expect( borderSettings ).toBeVisible();
// Block spacing settings
await expect(
editor.page.getByText( 'DimensionsBlock spacing' )
).toBeVisible();
} );
test( 'should display the correct inspector setting controls', async ( {
editor,
pageObject,
} ) => {
await pageObject.addProductFiltersBlock( { cleanContent: true } );
const block = editor.canvas.getByLabel(
'Block: Product Filters (Experimental)'
);
await expect( block ).toBeVisible();
await editor.openDocumentSettingsSidebar();
// Layout settings
await expect(
editor.page.getByText( 'LayoutJustificationOrientation' )
).toBeVisible();
} );
test( 'Layout > default to vertical stretch', async ( {
editor,
pageObject,
} ) => {
await pageObject.addProductFiltersBlock( { cleanContent: true } );
const block = editor.canvas.getByLabel(
'Block: Product Filters (Experimental)'
);
await expect( block ).toBeVisible();
await editor.openDocumentSettingsSidebar();
const layoutSettings = editor.page.getByText(
'LayoutJustificationOrientation'
);
await expect(
layoutSettings.getByLabel( 'Justify items left' )
).not.toHaveAttribute( 'data-active-item' );
await expect(
layoutSettings.getByLabel( 'Stretch items' )
).toHaveAttribute( 'data-active-item' );
await expect(
layoutSettings.getByLabel( 'Horizontal' )
).not.toHaveAttribute( 'data-active-item' );
await expect( layoutSettings.getByLabel( 'Vertical' ) ).toHaveAttribute(
'data-active-item'
);
} );
test( 'Layout > Justification: changing option should update the preview', async ( {
editor,
pageObject,
} ) => {
await pageObject.addProductFiltersBlock( { cleanContent: true } );
const block = editor.canvas.getByLabel(
'Block: Product Filters (Experimental)'
);
await expect( block ).toBeVisible();
await editor.openDocumentSettingsSidebar();
const layoutSettings = editor.page.getByText(
'LayoutJustificationOrientation'
);
await layoutSettings.getByLabel( 'Justify items left' ).click();
await expect(
layoutSettings.getByLabel( 'Justify items left' )
).toHaveAttribute( 'data-active-item' );
await expect(
block.locator( blockData.selectors.editor.layoutWrapper )
).toHaveCSS( 'align-items', 'flex-start' );
await layoutSettings.getByLabel( 'Justify items center' ).click();
await expect(
layoutSettings.getByLabel( 'Justify items center' )
).toHaveAttribute( 'data-active-item' );
await expect(
block.locator( blockData.selectors.editor.layoutWrapper )
).toHaveCSS( 'align-items', 'center' );
} );
test( 'Layout > Orientation: changing option should update the preview', async ( {
editor,
pageObject,
} ) => {
await pageObject.addProductFiltersBlock( { cleanContent: true } );
const block = editor.canvas.getByLabel(
'Block: Product Filters (Experimental)'
);
await expect( block ).toBeVisible();
await editor.openDocumentSettingsSidebar();
const layoutSettings = editor.page.getByText(
'LayoutJustificationOrientation'
);
await layoutSettings.getByLabel( 'Horizontal' ).click();
await expect(
layoutSettings.getByLabel( 'Stretch items' )
).toBeHidden();
await expect(
layoutSettings.getByLabel( 'Space between items' )
).toBeVisible();
await expect(
block.locator( ':text("Status"):right-of(:text("Price"))' )
).toBeVisible();
await layoutSettings.getByLabel( 'Vertical' ).click();
await expect(
block.locator( ':text("Status"):below(:text("Price"))' )
).toBeVisible();
} );
test( 'Dimentions > Block spacing: changing option should update the preview', async ( {
editor,
pageObject,
} ) => {
await pageObject.addProductFiltersBlock( { cleanContent: true } );
const block = editor.canvas.getByLabel(
'Block: Product Filters (Experimental)'
);
await expect( block ).toBeVisible();
await editor.openDocumentSettingsSidebar();
await editor.page.getByRole( 'tab', { name: 'Styles' } ).click();
const blockSpacingSettings = editor.page.getByLabel( 'Block spacing' );
await blockSpacingSettings.fill( '4' );
await expect(
block.locator( blockData.selectors.editor.layoutWrapper )
).not.toHaveCSS( 'gap', '0px' );
await blockSpacingSettings.fill( '0' );
await expect(
block.locator( blockData.selectors.editor.layoutWrapper )
).toHaveCSS( 'gap', '0px' );
} ); } );
} ); } );

View File

@ -0,0 +1,5 @@
Significance: patch
Type: add
Comment: Add layout and blockGap supports to the Product Filters block.

View File

@ -39,8 +39,6 @@ class ProductFilters extends AbstractBlock {
* @return string Rendered block type output. * @return string Rendered block type output.
*/ */
protected function render( $attributes, $content, $block ) { protected function render( $attributes, $content, $block ) {
return <<<HTML return $content;
<div>Product Filters</div>
HTML;
} }
} }