[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:
parent
ee2a545dce
commit
921e86e6ab
|
@ -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",
|
||||||
|
|
|
@ -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',
|
||||||
] );
|
] );
|
||||||
} );
|
} );
|
||||||
```
|
```
|
||||||
|
|
|
@ -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' );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
Significance: patch
|
||||||
|
Type: add
|
||||||
|
Comment: Add layout and blockGap supports to the Product Filters block.
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue