diff --git a/assets/js/blocks/products/all-products/index.js b/assets/js/blocks/products/all-products/index.js
index ac52b3d50ee..62c3859bffe 100644
--- a/assets/js/blocks/products/all-products/index.js
+++ b/assets/js/blocks/products/all-products/index.js
@@ -10,14 +10,11 @@ import Gridicon from 'gridicons';
* Internal dependencies
*/
import Editor from './edit';
-import sharedAttributes from '../attributes';
+import { attributes as sharedAttributes, defaults } from '../attributes';
import { getBlockClassName } from '../utils.js';
import '../../../atomic/blocks/product';
-/**
- * Register and run the "All Products" block.
- */
-registerBlockType( 'woocommerce/all-products', {
+const blockSettings = {
title: __( 'All Products', 'woo-gutenberg-products-block' ),
icon: {
src: ,
@@ -39,10 +36,8 @@ registerBlockType( 'woocommerce/all-products', {
isPreview: true,
},
},
- attributes: {
- ...sharedAttributes,
- },
-
+ attributes: sharedAttributes,
+ defaults,
/**
* Renders and manages the block.
*
@@ -51,7 +46,6 @@ registerBlockType( 'woocommerce/all-products', {
edit( props ) {
return ;
},
-
/**
* Save the props to post content.
*
@@ -59,9 +53,11 @@ registerBlockType( 'woocommerce/all-products', {
*/
save( { attributes } ) {
const data = {
- 'data-attributes': JSON.stringify( attributes ),
+ 'data-attributes': JSON.stringify(
+ attributes,
+ Object.keys( attributes ).sort()
+ ),
};
-
return (
);
},
+};
+
+/**
+ * Register and run the "All Products" block.
+ */
+registerBlockType( 'woocommerce/all-products', {
+ ...blockSettings,
+ /**
+ * Deprecation rule to handle the previous default rows which was 1 instead of 3.
+ */
+ deprecated: [
+ {
+ attributes: Object.assign( {}, blockSettings.attributes, {
+ rows: { type: 'number', default: 1 },
+ } ),
+ save( { attributes } ) {
+ const data = {
+ 'data-attributes': JSON.stringify( attributes ),
+ };
+ return (
+
+
+
+ );
+ },
+ },
+ ],
} );
diff --git a/assets/js/blocks/products/attributes.js b/assets/js/blocks/products/attributes.js
index 2d143fd20c7..f8af9c3675f 100644
--- a/assets/js/blocks/products/attributes.js
+++ b/assets/js/blocks/products/attributes.js
@@ -8,57 +8,55 @@ import { DEFAULT_COLUMNS, DEFAULT_ROWS } from '@woocommerce/block-settings';
*/
import { DEFAULT_PRODUCT_LIST_LAYOUT } from './base-utils';
-export default {
+export const defaults = {
+ columns: DEFAULT_COLUMNS,
+ rows: DEFAULT_ROWS,
+ alignButtons: false,
+ contentVisibility: {
+ orderBy: true,
+ },
+ orderby: 'date',
+ layoutConfig: DEFAULT_PRODUCT_LIST_LAYOUT,
+ isPreview: false,
+};
+
+export const attributes = {
/**
* Number of columns.
*/
columns: {
type: 'number',
- default: DEFAULT_COLUMNS,
},
-
/**
* Number of rows.
*/
rows: {
type: 'number',
- default: DEFAULT_ROWS,
},
-
/**
* How to align cart buttons.
*/
alignButtons: {
type: 'boolean',
- default: false,
},
-
/**
* Content visibility setting
*/
contentVisibility: {
type: 'object',
- default: {
- orderBy: true,
- },
},
-
/**
* Order to use for the products listing.
*/
orderby: {
type: 'string',
- default: 'date',
},
-
/**
* Layout config.
*/
layoutConfig: {
type: 'array',
- default: DEFAULT_PRODUCT_LIST_LAYOUT,
},
-
/**
* Are we previewing?
*/
diff --git a/assets/js/blocks/products/utils.js b/assets/js/blocks/products/utils.js
index 43f019a4003..3f2ea04f406 100644
--- a/assets/js/blocks/products/utils.js
+++ b/assets/js/blocks/products/utils.js
@@ -11,11 +11,11 @@ export const getBlockClassName = ( blockClassName, attributes ) => {
const { className, contentVisibility } = attributes;
return classNames( blockClassName, className, {
- 'has-image': contentVisibility.image,
- 'has-title': contentVisibility.title,
- 'has-rating': contentVisibility.rating,
- 'has-price': contentVisibility.price,
- 'has-button': contentVisibility.button,
+ 'has-image': contentVisibility && contentVisibility.image,
+ 'has-title': contentVisibility && contentVisibility.title,
+ 'has-rating': contentVisibility && contentVisibility.rating,
+ 'has-price': contentVisibility && contentVisibility.price,
+ 'has-button': contentVisibility && contentVisibility.button,
} );
};
diff --git a/assets/js/filters/block-list-block.js b/assets/js/filters/block-list-block.js
new file mode 100644
index 00000000000..d98089262d1
--- /dev/null
+++ b/assets/js/filters/block-list-block.js
@@ -0,0 +1,84 @@
+/**
+ * External dependencies
+ */
+import { Component } from '@wordpress/element';
+import { createHigherOrderComponent } from '@wordpress/compose';
+const { getBlockType } = wp.blocks;
+const { addFilter } = wp.hooks;
+
+/**
+ * withDefaultAttributes HOC for editor.BlockListBlock.
+ *
+ * @param object BlockListBlock The BlockListBlock element.
+ */
+const withDefaultAttributes = createHigherOrderComponent(
+ ( BlockListBlock ) => {
+ class WrappedComponent extends Component {
+ constructor() {
+ super( ...arguments );
+
+ const blockType = getBlockType( this.props.block.name );
+ const attributes = Object.assign(
+ {},
+ this.props.attributes || {}
+ );
+
+ if (
+ this.props.block.name.startsWith( 'woocommerce/' ) &&
+ typeof blockType.attributes !== 'undefined' &&
+ typeof blockType.defaults !== 'undefined'
+ ) {
+ Object.keys( blockType.attributes ).map( ( key ) => {
+ if (
+ typeof attributes[ key ] === 'undefined' &&
+ typeof blockType.defaults[ key ] !== 'undefined'
+ ) {
+ attributes[ key ] = blockType.defaults[ key ];
+ }
+ return key;
+ } );
+ }
+
+ this.attributesWithDefaults = attributes;
+ }
+
+ componentDidMount() {
+ const { block, setAttributes } = this.props;
+
+ if ( block.name.startsWith( 'woocommerce/' ) ) {
+ setAttributes( this.attributesWithDefaults );
+ }
+ }
+
+ render() {
+ return (
+
+ );
+ }
+ }
+ return WrappedComponent;
+ },
+ 'withDefaultAttributes'
+);
+
+/**
+ * Hook into `editor.BlockListBlock` to set default attributes (if blocks
+ * define them separately) when a block is inserted.
+ *
+ * This is a workaround for Gutenberg which does not save "default" attributes
+ * to the post, which means if defaults change, all existing blocks change too.
+ *
+ * See https://github.com/WordPress/gutenberg/issues/7342
+ *
+ * To use this, the block name needs a `woocommerce/` prefix, and as well
+ * as defining `attributes` during block registration, you must also declare an
+ * array called `defaults`. Defaults should be omitted from `attributes`.
+ */
+addFilter(
+ 'editor.BlockListBlock',
+ 'woocommerce-blocks/block-list-block',
+ withDefaultAttributes
+);
diff --git a/assets/js/filters/get-block-attributes.js b/assets/js/filters/get-block-attributes.js
new file mode 100644
index 00000000000..3b02d3db013
--- /dev/null
+++ b/assets/js/filters/get-block-attributes.js
@@ -0,0 +1,44 @@
+const { addFilter } = wp.hooks;
+
+/**
+ * Adjust attributes on load to set defaults so default attributes get saved.
+ *
+ * @param {Object} blockAttributes Original block attributes.
+ * @param {Object} blockType Block type settings.
+ *
+ * @return {Object} Filtered block attributes.
+ */
+const setBlockAttributeDefaults = ( blockAttributes, blockType ) => {
+ if ( blockType.name.startsWith( 'woocommerce/' ) ) {
+ Object.keys( blockType.attributes ).map( ( key ) => {
+ if (
+ typeof blockAttributes[ key ] === 'undefined' &&
+ typeof blockType.defaults !== 'undefined' &&
+ typeof blockType.defaults[ key ] !== 'undefined'
+ ) {
+ blockAttributes[ key ] = blockType.defaults[ key ];
+ }
+ return key;
+ } );
+ }
+ return blockAttributes;
+};
+
+/**
+ * Hook into `blocks.getBlockAttributes` to set default attributes (if blocks
+ * define them separately) when a block is loaded.
+ *
+ * This is a workaround for Gutenberg which does not save "default" attributes
+ * to the post, which means if defaults change, all existing blocks change too.
+ *
+ * See https://github.com/WordPress/gutenberg/issues/7342
+ *
+ * To use this, the block name needs a `woocommerce/` prefix, and as well
+ * as defining `attributes` during block registration, you must also declare an
+ * array called `defaults`. Defaults should be omitted from `attributes`.
+ */
+addFilter(
+ 'blocks.getBlockAttributes',
+ 'woocommerce-blocks/get-block-attributes',
+ setBlockAttributeDefaults
+);
diff --git a/assets/js/hocs/index.js b/assets/js/hocs/index.js
index 0c08301c170..5c14b873b5b 100644
--- a/assets/js/hocs/index.js
+++ b/assets/js/hocs/index.js
@@ -5,7 +5,5 @@ export { default as withFeedbackPrompt } from './with-feedback-prompt';
export { default as withProduct } from './with-product';
export { default as withProductVariations } from './with-product-variations';
export { default as withSearchedProducts } from './with-searched-products';
-export {
- default as withTransformSingleSelectToMultipleSelect,
-} from './with-transform-single-select-to-multiple-select';
+export { default as withTransformSingleSelectToMultipleSelect } from './with-transform-single-select-to-multiple-select';
export { default as withRestApiHydration } from './with-rest-api-hydration';
diff --git a/assets/js/index.js b/assets/js/index.js
index 7bbad17cf70..d8bad76be01 100644
--- a/assets/js/index.js
+++ b/assets/js/index.js
@@ -9,6 +9,8 @@ import { __ } from '@wordpress/i18n';
*/
import '../css/editor.scss';
import '../css/style.scss';
+import './filters/block-list-block';
+import './filters/get-block-attributes';
import { IconWoo } from './components/icons';
setCategories( [
diff --git a/assets/js/settings/blocks/constants.js b/assets/js/settings/blocks/constants.js
index 7be3699f87d..a83160ddbd3 100644
--- a/assets/js/settings/blocks/constants.js
+++ b/assets/js/settings/blocks/constants.js
@@ -13,7 +13,7 @@ export const MIN_COLUMNS = getSetting( 'min_columns', 1 );
export const DEFAULT_COLUMNS = getSetting( 'default_columns', 3 );
export const MAX_ROWS = getSetting( 'max_rows', 6 );
export const MIN_ROWS = getSetting( 'min_rows', 1 );
-export const DEFAULT_ROWS = getSetting( 'default_rows', 2 );
+export const DEFAULT_ROWS = getSetting( 'default_rows', 3 );
export const MIN_HEIGHT = getSetting( 'min_height', 500 );
export const DEFAULT_HEIGHT = getSetting( 'default_height', 500 );
export const PLACEHOLDER_IMG_SRC = getSetting( 'placeholderImgSrc', '' );
diff --git a/package.json b/package.json
index c46e915424b..5f6354b89be 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,8 @@
"sideEffects": [
"*.css",
"*.scss",
- "./assets/js/atomic/blocks/product/**"
+ "./assets/js/atomic/blocks/product/**",
+ "./assets/js/filters/**"
],
"repository": {
"type": "git",
diff --git a/src/Assets.php b/src/Assets.php
index 87a444cac7b..af22d47b23d 100644
--- a/src/Assets.php
+++ b/src/Assets.php
@@ -108,7 +108,7 @@ public static function get_wc_block_data( $settings ) {
'default_columns' => wc_get_theme_support( 'product_blocks::default_columns', 3 ),
'min_rows' => wc_get_theme_support( 'product_blocks::min_rows', 1 ),
'max_rows' => wc_get_theme_support( 'product_blocks::max_rows', 6 ),
- 'default_rows' => wc_get_theme_support( 'product_blocks::default_rows', 1 ),
+ 'default_rows' => wc_get_theme_support( 'product_blocks::default_rows', 3 ),
'thumbnail_size' => wc_get_theme_support( 'thumbnail_image_width', 300 ),
'placeholderImgSrc' => wc_placeholder_img_src(),
'min_height' => wc_get_theme_support( 'featured_block::min_height', 500 ),
diff --git a/src/BlockTypes/AbstractProductGrid.php b/src/BlockTypes/AbstractProductGrid.php
index 1f4fa81e3e9..df5bf802c05 100644
--- a/src/BlockTypes/AbstractProductGrid.php
+++ b/src/BlockTypes/AbstractProductGrid.php
@@ -46,7 +46,7 @@ protected function get_attributes() {
return array(
'className' => $this->get_schema_string(),
'columns' => $this->get_schema_number( wc_get_theme_support( 'product_blocks::default_columns', 3 ) ),
- 'rows' => $this->get_schema_number( wc_get_theme_support( 'product_blocks::default_rows', 1 ) ),
+ 'rows' => $this->get_schema_number( wc_get_theme_support( 'product_blocks::default_rows', 3 ) ),
'categories' => $this->get_schema_list_ids(),
'catOperator' => array(
'type' => 'string',
@@ -122,7 +122,7 @@ protected function parse_attributes( $attributes ) {
// These should match what's set in JS `registerBlockType`.
$defaults = array(
'columns' => wc_get_theme_support( 'product_blocks::default_columns', 3 ),
- 'rows' => wc_get_theme_support( 'product_blocks::default_rows', 1 ),
+ 'rows' => wc_get_theme_support( 'product_blocks::default_rows', 3 ),
'alignButtons' => false,
'categories' => array(),
'catOperator' => 'any',
diff --git a/src/BlockTypes/ProductTag.php b/src/BlockTypes/ProductTag.php
index 4cd882521e9..6f361afad69 100644
--- a/src/BlockTypes/ProductTag.php
+++ b/src/BlockTypes/ProductTag.php
@@ -45,7 +45,7 @@ protected function get_attributes() {
return array(
'className' => $this->get_schema_string(),
'columns' => $this->get_schema_number( wc_get_theme_support( 'product_blocks::default_columns', 3 ) ),
- 'rows' => $this->get_schema_number( wc_get_theme_support( 'product_blocks::default_rows', 1 ) ),
+ 'rows' => $this->get_schema_number( wc_get_theme_support( 'product_blocks::default_rows', 3 ) ),
'contentVisibility' => $this->get_schema_content_visibility(),
'align' => $this->get_schema_align(),
'alignButtons' => $this->get_schema_boolean( false ),
diff --git a/src/BlockTypes/ProductsByAttribute.php b/src/BlockTypes/ProductsByAttribute.php
index 2ca58458842..310f90dd6fd 100644
--- a/src/BlockTypes/ProductsByAttribute.php
+++ b/src/BlockTypes/ProductsByAttribute.php
@@ -73,7 +73,7 @@ protected function get_attributes() {
'contentVisibility' => $this->get_schema_content_visibility(),
'editMode' => $this->get_schema_boolean( true ),
'orderby' => $this->get_schema_orderby(),
- 'rows' => $this->get_schema_number( wc_get_theme_support( 'product_blocks::default_rows', 1 ) ),
+ 'rows' => $this->get_schema_number( wc_get_theme_support( 'product_blocks::default_rows', 3 ) ),
'isPreview' => $this->get_schema_boolean( false ),
);
}