[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 <github-actions@github.com>
This commit is contained in:
parent
d43b3af43f
commit
dd38a6ed2d
|
@ -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"
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,8 +69,40 @@ const Edit = ( props: EditProps ): JSX.Element => {
|
|||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div { ...blockProps }>
|
||||
<div { ...innerBlocksProps }>
|
||||
<BlockControls>
|
||||
<ToolbarGroup>
|
||||
<ToolbarButton
|
||||
icon={ arrowRight }
|
||||
label={ __( 'Horizontal', 'woocommerce' ) }
|
||||
onClick={ () =>
|
||||
setAttributes( {
|
||||
layout: {
|
||||
...usedLayout,
|
||||
orientation: 'horizontal',
|
||||
},
|
||||
} )
|
||||
}
|
||||
isPressed={
|
||||
usedLayout.orientation === 'horizontal' ||
|
||||
! usedLayout.orientation
|
||||
}
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={ arrowDown }
|
||||
label={ __( 'Vertical', 'woocommerce' ) }
|
||||
onClick={ () =>
|
||||
setAttributes( {
|
||||
layout: {
|
||||
...usedLayout,
|
||||
orientation: 'vertical',
|
||||
},
|
||||
} )
|
||||
}
|
||||
isPressed={ usedLayout.orientation === 'vertical' }
|
||||
/>
|
||||
</ToolbarGroup>
|
||||
</BlockControls>
|
||||
<ul className="wc-block-product-filter-removable-chips__items">
|
||||
{ items?.map( ( item, index ) => (
|
||||
<li
|
||||
|
@ -84,7 +127,6 @@ const Edit = ( props: EditProps ): JSX.Element => {
|
|||
</li>
|
||||
) ) }
|
||||
</ul>
|
||||
</div>
|
||||
<InspectorControls group="color">
|
||||
{ colorGradientSettings.hasColorsOrGradients && (
|
||||
<ColorGradientSettingsDropdown
|
||||
|
@ -147,7 +189,7 @@ const Edit = ( props: EditProps ): JSX.Element => {
|
|||
/>
|
||||
) }
|
||||
</InspectorControls>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,29 @@
|
|||
.is-vertical {
|
||||
.wc-block-product-filter-removable-chips__items {
|
||||
display: flex;
|
||||
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: 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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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/ );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: tweak
|
||||
Comment: Adds additional styling settings to active filter block
|
||||
|
|
@ -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();
|
||||
|
||||
$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(
|
||||
$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();
|
||||
?>
|
||||
|
||||
<div <?php echo $wrapper_attributes; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
|
||||
<div <?php echo get_block_wrapper_attributes( $wrapper_attributes ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
|
||||
<?php if ( ! empty( $filters ) ) : ?>
|
||||
<ul class="wc-block-product-filter-removable-chips__items">
|
||||
<?php foreach ( $filters as $filter ) : ?>
|
||||
|
|
Loading…
Reference in New Issue