From dd38a6ed2def4e5fc8a53190713616ffe2974d1d Mon Sep 17 00:00:00 2001 From: Roy Ho Date: Mon, 11 Nov 2024 13:44:03 -0800 Subject: [PATCH] [Experimental] Add styling controls (#52598) * Add alignment controls * Add changefile(s) from automation for the following project(s): woocommerce-blocks * Fix justication and orientation layouts * Move styling settings to inner blocks * Add e2e tests * Fix chips not displaying correctly on first load * Fix security error --------- Co-authored-by: github-actions --- .../inner-blocks/active-filters/block.json | 24 +++- .../inner-blocks/active-filters/edit.tsx | 3 +- .../inner-blocks/removable-chips/block.json | 12 +- .../inner-blocks/removable-chips/edit.tsx | 102 +++++++++----- .../inner-blocks/removable-chips/style.scss | 29 +++- .../inner-blocks/removable-chips/types.ts | 4 + ...removable-chips-editor.block_theme.spec.ts | 127 ++++++++++++++++++ ...598-product-filters-active-filters-styling | 4 + .../ProductFilterRemovableChips.php | 28 ++-- 9 files changed, 285 insertions(+), 48 deletions(-) create mode 100644 plugins/woocommerce-blocks/tests/e2e/tests/product-filters/removable-chips-editor.block_theme.spec.ts create mode 100644 plugins/woocommerce/changelog/52598-product-filters-active-filters-styling diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/block.json index 0dcbeab2893..10644b09757 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/block.json @@ -14,7 +14,29 @@ "woocommerce/product-filters" ], "supports": { - "interactivity": true + "interactivity": true, + "__experimentalBorder": { + "color": true, + "radius": true, + "style": true, + "width": true, + "__experimentalDefaultControls": { + "color": false, + "radius": false, + "style": false, + "width": false + } + }, + "spacing": { + "margin": true, + "padding": true, + "blockGap": false, + "__experimentalDefaultControls": { + "margin": false, + "padding": false, + "blockGap": false + } + } }, "usesContext": [ "filterParams" diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/edit.tsx index f0080e4ca57..20e8177949b 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/edit.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/edit.tsx @@ -18,7 +18,8 @@ import { EditProps } from './types'; import { filtersPreview } from './constants'; const Edit = ( props: EditProps ) => { - const { clearButton } = props.attributes; + const { attributes } = props; + const { clearButton } = attributes; const { children, ...innerBlocksProps } = useInnerBlocksProps( useBlockProps(), diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/removable-chips/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/removable-chips/block.json index c711b0f699f..055ff35c042 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/removable-chips/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/removable-chips/block.json @@ -13,7 +13,17 @@ "ancestor": [ "woocommerce/product-filter-active" ], - "supports": {}, + "supports": { + "layout": { + "allowSwitching": false, + "allowInheriting": false, + "allowJustification": false, + "allowVerticalAlignment": false, + "default": { + "type": "flex" + } + } + }, "usesContext": [ "queryId", "filterData" diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/removable-chips/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/removable-chips/edit.tsx index 85f72c4255d..641b373e51c 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/removable-chips/edit.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/removable-chips/edit.tsx @@ -3,11 +3,15 @@ */ import { __, sprintf } from '@wordpress/i18n'; import clsx from 'clsx'; -import { Icon, closeSmall } from '@wordpress/icons'; +import { Icon, closeSmall, arrowRight, arrowDown } from '@wordpress/icons'; import { Label } from '@woocommerce/blocks-components'; +import { ToolbarGroup, ToolbarButton } from '@wordpress/components'; +import { getBlockSupport } from '@wordpress/blocks'; import { InspectorControls, useBlockProps, + useInnerBlocksProps, + BlockControls, withColors, // @ts-expect-error - no types. // eslint-disable-next-line @wordpress/no-unsafe-wp-apis @@ -26,6 +30,7 @@ import { getColorClasses, getColorVars } from './utils'; const Edit = ( props: EditProps ): JSX.Element => { const colorGradientSettings = useMultipleOriginColorsAndGradients(); const { + name, context, clientId, attributes, @@ -37,11 +42,16 @@ const Edit = ( props: EditProps ): JSX.Element => { chipBorder, setChipBorder, } = props; - const { customChipText, customChipBackground, customChipBorder } = + const { customChipText, customChipBackground, customChipBorder, layout } = attributes; const { filterData } = context; const { items } = filterData; + // Extract attributes from block layout + const layoutBlockSupport = getBlockSupport( name, 'layout' ); + const defaultBlockLayout = layoutBlockSupport?.default; + const usedLayout = layout || defaultBlockLayout || {}; + const blockProps = useBlockProps( { className: clsx( 'wc-block-product-filter-removable-chips', { ...getColorClasses( attributes ), @@ -49,6 +59,7 @@ const Edit = ( props: EditProps ): JSX.Element => { style: getColorVars( attributes ), } ); + const innerBlocksProps = useInnerBlocksProps( blockProps, {} ); const removeText = ( label: string ): string => { return sprintf( /* translators: %s attribute value used in the filter. For example: yellow, green, small, large. */ @@ -58,33 +69,64 @@ const Edit = ( props: EditProps ): JSX.Element => { }; return ( - <> -
-
    - { items?.map( ( item, index ) => ( -
  • - - { item.type + ': ' + item.label } - - -
  • - ) ) } -
-
+
+ + + + setAttributes( { + layout: { + ...usedLayout, + orientation: 'horizontal', + }, + } ) + } + isPressed={ + usedLayout.orientation === 'horizontal' || + ! usedLayout.orientation + } + /> + + setAttributes( { + layout: { + ...usedLayout, + orientation: 'vertical', + }, + } ) + } + isPressed={ usedLayout.orientation === 'vertical' } + /> + + +
    + { items?.map( ( item, index ) => ( +
  • + + { item.type + ': ' + item.label } + + +
  • + ) ) } +
{ colorGradientSettings.hasColorsOrGradients && ( { /> ) } - +
); }; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/removable-chips/style.scss b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/removable-chips/style.scss index a497f63b4fb..57ddee51c15 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/removable-chips/style.scss +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/removable-chips/style.scss @@ -1,5 +1,29 @@ +.is-vertical { + .wc-block-product-filter-removable-chips__items { + flex-direction: column; + } +} + +.is-content-justification-center { + .wc-block-product-filter-removable-chips__items { + justify-content: center; + } +} + +.is-content-justification-right { + .wc-block-product-filter-removable-chips__items { + justify-content: flex-end; + } +} + +.is-content-justification-space-between { + .wc-block-product-filter-removable-chips__items { + justify-content: space-between; + } +} + .wc-block-product-filter-removable-chips__items { - display: flex; + display: inline-flex; gap: 4px; flex-wrap: wrap; list-style: none; @@ -16,13 +40,14 @@ font-size: 0.875em; display: flex; align-items: center; + justify-content: space-between; .wc-block-product-filter-removable-chips__remove { background: none; border: none; cursor: pointer; padding: 0; - margin: 0 0 0 5px; + margin: 0; display: flex; align-items: center; fill: var(--wc-product-filter-removable-chips-text, currentColor); diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/removable-chips/types.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/removable-chips/types.ts index d99206eec54..8b290ea6ff0 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/removable-chips/types.ts +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/removable-chips/types.ts @@ -23,6 +23,9 @@ export type BlockAttributes = { customChipBackground?: string; chipBorder?: string; customChipBorder?: string; + layout: { + orientation: string; + }; }; export type EditProps = BlockEditProps< BlockAttributes > & { @@ -34,4 +37,5 @@ export type EditProps = BlockEditProps< BlockAttributes > & { setChipBackground: ( value: string ) => void; chipBorder: Color; setChipBorder: ( value: string ) => void; + name: string; }; diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/product-filters/removable-chips-editor.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/product-filters/removable-chips-editor.block_theme.spec.ts new file mode 100644 index 00000000000..5839d70e7dc --- /dev/null +++ b/plugins/woocommerce-blocks/tests/e2e/tests/product-filters/removable-chips-editor.block_theme.spec.ts @@ -0,0 +1,127 @@ +/** + * External dependencies + */ +import { test as base, expect } from '@woocommerce/e2e-utils'; + +/** + * Internal dependencies + */ +import { ProductFiltersPage } from './product-filters.page'; + +const blockData = { + name: 'woocommerce/product-filter-active', + selectors: { + frontend: {}, + editor: { + settings: {}, + label: 'Block: Active (Experimental)', + innerBlocks: { + chips: { + label: 'Block: Chips', + }, + }, + }, + }, + slug: 'archive-product', +}; + +const test = base.extend< { pageObject: ProductFiltersPage } >( { + pageObject: async ( { page, editor, frontendUtils }, use ) => { + const pageObject = new ProductFiltersPage( { + page, + editor, + frontendUtils, + } ); + await use( pageObject ); + }, +} ); + +test.describe( `${ blockData.name }`, () => { + test.beforeEach( async ( { admin, requestUtils } ) => { + await requestUtils.setFeatureFlag( 'experimental-blocks', true ); + await admin.visitSiteEditor( { + postId: `woocommerce/woocommerce//${ blockData.slug }`, + postType: 'wp_template', + canvas: 'edit', + } ); + } ); + + test( 'should display the correct inspector layout controls', async ( { + editor, + pageObject, + } ) => { + await pageObject.addProductFiltersBlock( { cleanContent: true } ); + + const activeBlock = editor.canvas.getByLabel( + blockData.selectors.editor.label + ); + + await expect( activeBlock ).toBeVisible(); + + await activeBlock.click(); + + const chipsBlock = editor.canvas.getByLabel( + blockData.selectors.editor.innerBlocks.chips.label + ); + + await expect( chipsBlock ).toBeVisible(); + + await chipsBlock.click(); + + await editor.openDocumentSettingsSidebar(); + + await expect( editor.page.getByText( 'Justification' ) ).toBeVisible(); + await expect( editor.page.getByText( 'Orientation' ) ).toBeVisible(); + } ); + + test( 'should add correct layout CSS class when modifying layout settings', async ( { + editor, + pageObject, + } ) => { + await pageObject.addProductFiltersBlock( { cleanContent: true } ); + + const activeBlock = editor.canvas.getByLabel( + blockData.selectors.editor.label + ); + + await expect( activeBlock ).toBeVisible(); + + await activeBlock.click(); + + const chipsBlock = editor.canvas.getByLabel( + blockData.selectors.editor.innerBlocks.chips.label + ); + + await expect( chipsBlock ).toBeVisible(); + + await chipsBlock.click(); + + await editor.openDocumentSettingsSidebar(); + + await editor.page.getByLabel( 'Space between items' ).click(); + await expect( chipsBlock ).toHaveClass( + /is-content-justification-space-between/ + ); + + await editor.page.getByLabel( 'Justify items right' ).click(); + await expect( chipsBlock ).toHaveClass( + /is-content-justification-right/ + ); + + await editor.page.getByLabel( 'Justify items center' ).click(); + await expect( chipsBlock ).toHaveClass( + /is-content-justification-center/ + ); + + await editor.page.getByLabel( 'Justify items left' ).click(); + await expect( chipsBlock ).toHaveClass( + /is-content-justification-left/ + ); + + await editor.page.getByRole( 'button', { name: 'Horizontal' } ).click(); + await expect( chipsBlock ).toHaveClass( /is-horizontal/ ); + + await editor.page.getByRole( 'button', { name: 'Vertical' } ).click(); + await expect( chipsBlock ).toHaveClass( /is-vertical/ ); + } ); +} ); diff --git a/plugins/woocommerce/changelog/52598-product-filters-active-filters-styling b/plugins/woocommerce/changelog/52598-product-filters-active-filters-styling new file mode 100644 index 00000000000..d9227bcde7e --- /dev/null +++ b/plugins/woocommerce/changelog/52598-product-filters-active-filters-styling @@ -0,0 +1,4 @@ +Significance: patch +Type: tweak +Comment: Adds additional styling settings to active filter block + diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterRemovableChips.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterRemovableChips.php index 5b0b660fce7..707dfd2bb4e 100644 --- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterRemovableChips.php +++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterRemovableChips.php @@ -24,13 +24,13 @@ final class ProductFilterRemovableChips extends AbstractBlock { * @return string Rendered block type output. */ protected function render( $attributes, $content, $block ) { - if ( empty( $block->context['filterData'] ) || empty( $block->context['filterData']['items'] ) ) { - return ''; + $filters = array(); + + if ( ! empty( $block->context['filterData'] ) && ! empty( $block->context['filterData']['items'] ) ) { + $filters = $block->context['filterData']['items']; } - $style = ''; - $context = $block->context['filterData']; - $filters = $context['items'] ?? array(); + $style = ''; $tags = new \WP_HTML_Tag_Processor( $content ); if ( $tags->next_tag( array( 'class_name' => 'wc-block-product-filter-removable-chips' ) ) ) { @@ -38,19 +38,21 @@ final class ProductFilterRemovableChips extends AbstractBlock { $style = $tags->get_attribute( 'style' ); } - $wrapper_attributes = get_block_wrapper_attributes( - array( - 'data-wc-interactive' => wp_json_encode( array( 'namespace' => $this->get_full_block_name() ), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ), - 'data-wc-key' => wp_unique_prefixed_id( $this->get_full_block_name() ), - 'class' => esc_attr( $classes ), - 'style' => esc_attr( $style ), - ) + $wrapper_attributes = array( + 'data-wc-interactive' => wp_json_encode( array( 'namespace' => $this->get_full_block_name() ), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ), + 'data-wc-key' => wp_unique_prefixed_id( $this->get_full_block_name() ), + 'class' => esc_attr( $classes ), + 'style' => esc_attr( $style ), ); + if ( empty( $filters ) ) { + $wrapper_attributes['hidden'] = true; + } + ob_start(); ?> -
> +
>