From e089b74fbb24e866c42b081146d29dcdcd069d2f Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 29 May 2020 16:43:26 +0100 Subject: [PATCH 01/38] Register Atomic Blocks and save some block content --- assets/js/atomic/blocks/product/save.js | 12 +++++ .../js/atomic/blocks/product/shared-config.js | 12 ++++- src/BlockTypes/AtomicBlock.php | 50 +++++++++++++++++++ src/Library.php | 20 ++++++++ 4 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 assets/js/atomic/blocks/product/save.js create mode 100644 src/BlockTypes/AtomicBlock.php diff --git a/assets/js/atomic/blocks/product/save.js b/assets/js/atomic/blocks/product/save.js new file mode 100644 index 00000000000..d99801c45d9 --- /dev/null +++ b/assets/js/atomic/blocks/product/save.js @@ -0,0 +1,12 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +const save = ( { attributes } ) => { + return ( +
+ ); +}; + +export default save; diff --git a/assets/js/atomic/blocks/product/shared-config.js b/assets/js/atomic/blocks/product/shared-config.js index bbab0257a43..f9f0065ad94 100644 --- a/assets/js/atomic/blocks/product/shared-config.js +++ b/assets/js/atomic/blocks/product/shared-config.js @@ -4,6 +4,11 @@ import { __ } from '@wordpress/i18n'; import { Icon, grid } from '@woocommerce/icons'; +/** + * Internal dependencies + */ +import save from './save'; + /** * Holds default config for this collection of blocks. */ @@ -18,5 +23,10 @@ export default { html: false, }, parent: [ 'woocommerce/all-products', 'woocommerce/single-product' ], - save() {}, + save, + deprecated: [ + { + save() {}, + }, + ], }; diff --git a/src/BlockTypes/AtomicBlock.php b/src/BlockTypes/AtomicBlock.php new file mode 100644 index 00000000000..f01672f7381 --- /dev/null +++ b/src/BlockTypes/AtomicBlock.php @@ -0,0 +1,50 @@ +attributes : $attributes; + return $this->inject_html_data_attributes( $content, $block_attributes ); + } + + /** + * Registers the block type with WordPress. + */ + public function register_block_type() { + register_block_type( + $this->namespace . '/' . $this->block_name, + array( + 'render_callback' => array( $this, 'render' ), + ) + ); + } + + /** + * Converts block attributes to HTML data attributes. + * + * @param array $attributes Key value pairs of attributes. + * @return string Rendered HTML attributes. + */ + protected function get_html_data_attributes( array $attributes ) { + $data = parent::get_html_data_attributes( $attributes ); + return trim( $data . ' data-atomic-block="' . esc_attr( $this->namespace . '/' . $this->block_name ) . '"' ); + } +} diff --git a/src/Library.php b/src/Library.php index 587e214aee4..e6b7a335d4b 100644 --- a/src/Library.php +++ b/src/Library.php @@ -77,6 +77,7 @@ public static function register_blocks() { } if ( 'experimental' === WOOCOMMERCE_BLOCKS_PHASE ) { $blocks[] = 'SingleProduct'; + self::register_atomic_blocks(); } foreach ( $blocks as $class ) { $class = __NAMESPACE__ . '\\BlockTypes\\' . $class; @@ -85,6 +86,25 @@ public static function register_blocks() { } } + /** + * Register atomic blocks on the PHP side. + */ + protected static function register_atomic_blocks() { + $atomic_blocks = [ + 'product-title', + 'product-button', + 'product-image', + 'product-price', + 'product-rating', + 'product-sale-badge', + 'product-summary', + ]; + foreach ( $atomic_blocks as $atomic_block ) { + $instance = new \Automattic\WooCommerce\Blocks\BlockTypes\AtomicBlock( $atomic_block ); + $instance->register_block_type(); + } + } + /** * Register custom order status for orders created via the API during checkout. * From 0b6839a35c048b705ed8eb80e99282877604dd41 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 29 May 2020 17:18:07 +0100 Subject: [PATCH 02/38] renderInnerBlocks utility --- assets/js/atomic/utils/index.js | 1 + assets/js/atomic/utils/render-inner-blocks.js | 64 +++++++++++++++++++ assets/js/base/utils/render-frontend.js | 20 +++++- assets/js/blocks/single-product/frontend.js | 4 +- package-lock.json | 64 ++++++++++++------- package.json | 1 + src/BlockTypes/AtomicBlock.php | 2 +- 7 files changed, 127 insertions(+), 29 deletions(-) create mode 100644 assets/js/atomic/utils/render-inner-blocks.js diff --git a/assets/js/atomic/utils/index.js b/assets/js/atomic/utils/index.js index 2ebc4357778..1276d0e5db5 100644 --- a/assets/js/atomic/utils/index.js +++ b/assets/js/atomic/utils/index.js @@ -1,2 +1,3 @@ export * from './get-block-map.js'; export * from './create-blocks-from-template.js'; +export * from './render-inner-blocks.js'; diff --git a/assets/js/atomic/utils/render-inner-blocks.js b/assets/js/atomic/utils/render-inner-blocks.js new file mode 100644 index 00000000000..0b7630523a3 --- /dev/null +++ b/assets/js/atomic/utils/render-inner-blocks.js @@ -0,0 +1,64 @@ +/** + * External dependencies + */ +import { cloneElement, isValidElement } from '@wordpress/element'; +import parse from 'html-react-parser'; + +/** + * Internal dependencies + */ +import { getBlockMap } from './get-block-map'; + +/** + * Replaces saved block HTML markup with Inner Block Components. + * + * @param {Object} props Render props. + * @param {Array} props.children Children/inner blocks to render. + * @param {string} props.blockName Parent Block Name used to get the block map and for keys. + * @param {number} props.depth Depth of inner blocks being rendered. + */ +export const renderInnerBlocks = ( { children, blockName, depth = 1 } ) => { + const blockMap = getBlockMap( blockName ); + + return Array.from( children ).map( ( el, index ) => { + const componentProps = { + ...el.dataset, + key: `${ blockName }_${ depth }_${ index }`, + }; + + const componentChildren = + el.children && el.children.length + ? renderInnerBlocks( { + children: el.children, + blockName, + depth: depth + 1, + } ) + : null; + + const LayoutComponent = + componentProps.blockName && blockMap[ componentProps.blockName ] + ? blockMap[ componentProps.blockName ] + : null; + + if ( ! LayoutComponent ) { + const element = parse( el.outerHTML ); + + if ( isValidElement( element ) ) { + return componentChildren + ? cloneElement( element, componentProps, componentChildren ) + : cloneElement( element, componentProps ); + } + return null; + } + + return ( + // eslint-disable-next-line react/jsx-key + + { componentChildren } + + ); + } ); +}; diff --git a/assets/js/base/utils/render-frontend.js b/assets/js/base/utils/render-frontend.js index b335a2267df..acbbde1c534 100644 --- a/assets/js/base/utils/render-frontend.js +++ b/assets/js/base/utils/render-frontend.js @@ -2,6 +2,7 @@ * External dependencies */ import { render } from 'react-dom'; +import { renderInnerBlocks } from '@woocommerce/atomic-utils'; import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundary'; /** @@ -39,14 +40,16 @@ export const getAttributesFromDataset = ( blockAttributes, dataset ) => { * Renders a block component in the place of a specified set of selectors. * * @param {Object} props Render props. - * @param {string} props.selector CSS selector to match the elements to replace. + * @param {string} [props.blockName] Optional Block Name. Used for inner block component mapping. * @param {Function} props.Block React component to use as a replacement. + * @param {string} props.selector CSS selector to match the elements to replace. * @param {Function} [props.getProps ] Function to generate the props object for the block. * @param {Function} [props.getErrorBoundaryProps] Function to generate the props object for the error boundary. */ export const renderFrontend = ( { - selector, + blockName = '', Block, + selector, getProps = () => {}, getErrorBoundaryProps = () => {}, } ) => { @@ -61,12 +64,23 @@ export const renderFrontend = ( { ...el.dataset, ...props.attributes, }; + const children = + el.children && el.children.length + ? renderInnerBlocks( { + blockName, + children: el.children, + } ) + : null; el.classList.remove( 'is-loading' ); render( - + , el ); diff --git a/assets/js/blocks/single-product/frontend.js b/assets/js/blocks/single-product/frontend.js index 834e0d3f58d..6d63afbf155 100644 --- a/assets/js/blocks/single-product/frontend.js +++ b/assets/js/blocks/single-product/frontend.js @@ -12,6 +12,7 @@ import { */ import Block from './block'; import blockAttributes from './attributes'; +import { BLOCK_NAME } from './constants'; /** * Wrapper component to supply the notice provider. @@ -33,7 +34,8 @@ const getProps = ( el ) => { }; renderFrontend( { - selector: '.wp-block-woocommerce-single-product', Block: FrontendBlock, + blockName: BLOCK_NAME, + selector: '.wp-block-woocommerce-single-product', getProps, } ); diff --git a/package-lock.json b/package-lock.json index e89478ebf54..a5d73dce881 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7581,6 +7581,11 @@ "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" }, + "@types/domhandler": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/domhandler/-/domhandler-2.4.1.tgz", + "integrity": "sha512-cfBw6q6tT5sa1gSPFSRKzF/xxYrrmeiut7E0TxNBObiLSBTuFEHibcfEe3waQPEDbqBsq+ql/TOniw65EyDFMA==" + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -15823,7 +15828,6 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "dev": true, "requires": { "domelementtype": "^2.0.1", "entities": "^2.0.0" @@ -15832,14 +15836,12 @@ "domelementtype": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", - "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", - "dev": true + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" }, "entities": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.2.tgz", - "integrity": "sha512-dmD3AvJQBUjKpcNkoqr+x+IF0SdRtPz9Vk0uTy4yWqga9ibB6s4v++QFWNohjiUGoMlF552ZvNyXDxz5iW0qmw==", - "dev": true + "integrity": "sha512-dmD3AvJQBUjKpcNkoqr+x+IF0SdRtPz9Vk0uTy4yWqga9ibB6s4v++QFWNohjiUGoMlF552ZvNyXDxz5iW0qmw==" } } }, @@ -15858,8 +15860,7 @@ "domelementtype": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" }, "domexception": { "version": "2.0.1", @@ -15880,7 +15881,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "dev": true, "requires": { "domelementtype": "1" } @@ -15889,7 +15889,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, "requires": { "dom-serializer": "0", "domelementtype": "1" @@ -16119,8 +16118,7 @@ "entities": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" }, "enzyme": { "version": "3.11.0", @@ -19127,6 +19125,16 @@ "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", "dev": true }, + "html-dom-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-0.2.3.tgz", + "integrity": "sha512-GdzE63/U0IQEvcpAz0cUdYx2zQx0Ai+HWvE9TXEgwP27+SymUzKa7iB4DhjYpf2IdNLfTTOBuMS5nxeWOosSMQ==", + "requires": { + "@types/domhandler": "2.4.1", + "domhandler": "2.4.2", + "htmlparser2": "3.10.1" + } + }, "html-element-map": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/html-element-map/-/html-element-map-1.2.0.tgz", @@ -19171,6 +19179,17 @@ "terser": "^4.6.3" } }, + "html-react-parser": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-0.10.5.tgz", + "integrity": "sha512-rtMWZ7KZjd9sO8jyIX7am0vGCIL45tCmctTnassT/BrTGeTaAZ4nQyqoGcx2v+vB8CAY+Q5PZiiV6eOiowq8dQ==", + "requires": { + "@types/domhandler": "2.4.1", + "html-dom-parser": "0.2.3", + "react-property": "1.0.1", + "style-to-object": "0.3.0" + } + }, "html-tags": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", @@ -19236,7 +19255,6 @@ "version": "3.10.1", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "dev": true, "requires": { "domelementtype": "^1.3.1", "domhandler": "^2.3.0", @@ -19250,7 +19268,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -19660,8 +19677,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { "version": "1.3.5", @@ -19672,8 +19688,7 @@ "inline-style-parser": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", - "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==", - "dev": true + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" }, "inquirer": { "version": "7.1.0", @@ -31356,6 +31371,11 @@ "prop-types": "^15.5.8" } }, + "react-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/react-property/-/react-property-1.0.1.tgz", + "integrity": "sha512-1tKOwxFn3dXVomH6pM9IkLkq2Y8oh+fh/lYW3MJ/B03URswUTqttgckOlbxY2XHF3vPG6uanSc4dVsLW/wk3wQ==" + }, "react-redux": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.0.tgz", @@ -34555,7 +34575,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" }, @@ -34563,8 +34582,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, @@ -34672,7 +34690,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz", "integrity": "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==", - "dev": true, "requires": { "inline-style-parser": "0.1.1" } @@ -36559,8 +36576,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "util.promisify": { "version": "1.0.1", @@ -37877,7 +37893,7 @@ } }, "woocommerce": { - "version": "git+https://github.com/woocommerce/woocommerce.git#5213b05d57b0e0e703eaed3519694fa98a51516f", + "version": "git+https://github.com/woocommerce/woocommerce.git#5a746a0775d8407127e314ffde73d5ebb15284bf", "from": "git+https://github.com/woocommerce/woocommerce.git#release/4.1", "dev": true }, diff --git a/package.json b/package.json index c53eddb63bd..fe2bf33f2cf 100644 --- a/package.json +++ b/package.json @@ -165,6 +165,7 @@ "config": "3.3.1", "dinero.js": "1.8.1", "downshift": "4.1.0", + "html-react-parser": "^0.10.5", "jest-environment-jsdom-sixteen": "1.0.3", "react-number-format": "4.4.1", "reakit": "1.0.2", diff --git a/src/BlockTypes/AtomicBlock.php b/src/BlockTypes/AtomicBlock.php index f01672f7381..b67495712e5 100644 --- a/src/BlockTypes/AtomicBlock.php +++ b/src/BlockTypes/AtomicBlock.php @@ -45,6 +45,6 @@ public function register_block_type() { */ protected function get_html_data_attributes( array $attributes ) { $data = parent::get_html_data_attributes( $attributes ); - return trim( $data . ' data-atomic-block="' . esc_attr( $this->namespace . '/' . $this->block_name ) . '"' ); + return trim( $data . ' data-block-name="' . esc_attr( $this->namespace . '/' . $this->block_name ) . '"' ); } } From aef45cd7e732a87f57e607289232638924557e07 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 1 Jun 2020 12:26:18 +0100 Subject: [PATCH 03/38] Frontend Rendering --- assets/js/atomic/utils/render-inner-blocks.js | 16 ++++++---- assets/js/blocks/single-product/block.js | 31 +++++++++++++++++-- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/assets/js/atomic/utils/render-inner-blocks.js b/assets/js/atomic/utils/render-inner-blocks.js index 0b7630523a3..2e071c5f1a2 100644 --- a/assets/js/atomic/utils/render-inner-blocks.js +++ b/assets/js/atomic/utils/render-inner-blocks.js @@ -15,22 +15,26 @@ import { getBlockMap } from './get-block-map'; * @param {Object} props Render props. * @param {Array} props.children Children/inner blocks to render. * @param {string} props.blockName Parent Block Name used to get the block map and for keys. - * @param {number} props.depth Depth of inner blocks being rendered. + * @param {number} [props.depth] Depth of inner blocks being rendered. */ -export const renderInnerBlocks = ( { children, blockName, depth = 1 } ) => { - const blockMap = getBlockMap( blockName ); +export const renderInnerBlocks = ( { + children, + blockName: parentBlockName, + depth = 1, +} ) => { + const blockMap = getBlockMap( parentBlockName ); return Array.from( children ).map( ( el, index ) => { const componentProps = { ...el.dataset, - key: `${ blockName }_${ depth }_${ index }`, + key: `${ parentBlockName }_${ depth }_${ index }`, }; const componentChildren = el.children && el.children.length ? renderInnerBlocks( { children: el.children, - blockName, + blockName: parentBlockName, depth: depth + 1, } ) : null; @@ -54,7 +58,7 @@ export const renderInnerBlocks = ( { children, blockName, depth = 1 } ) => { return ( // eslint-disable-next-line react/jsx-key { componentChildren } diff --git a/assets/js/blocks/single-product/block.js b/assets/js/blocks/single-product/block.js index 5977c270b7a..cbe26134308 100644 --- a/assets/js/blocks/single-product/block.js +++ b/assets/js/blocks/single-product/block.js @@ -2,12 +2,39 @@ * External dependencies */ import { withProduct } from '@woocommerce/block-hocs'; +import { + InnerBlockLayoutContextProvider, + ProductDataContextProvider, +} from '@woocommerce/shared-context'; +import classnames from 'classnames'; + +/** + * Internal dependencies + */ +import { BLOCK_NAME } from './constants'; /** * The Single Product Block. */ -const Block = () => { - return null; +const Block = ( { isLoading, product, children } ) => { + const baseClassName = 'wc-block-single-product'; + + return ( + + +
+ { children } +
+
+
+ ); }; export default withProduct( Block ); From 05504c194dd0b554aa376f680542c52091507dd1 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 1 Jun 2020 14:22:03 +0100 Subject: [PATCH 04/38] Clean up atomic block classnames --- .../js/atomic/blocks/product/button/block.js | 9 +---- .../js/atomic/blocks/product/image/block.js | 40 +++++++------------ .../js/atomic/blocks/product/price/block.js | 33 ++++++++------- .../js/atomic/blocks/product/rating/block.js | 13 +++--- .../atomic/blocks/product/sale-badge/block.js | 10 ++--- .../js/atomic/blocks/product/summary/block.js | 12 +++--- .../js/atomic/blocks/product/title/block.js | 15 +++---- .../components/product-list-item/index.js | 4 +- .../js/base/components/product-list/index.js | 6 +-- .../product-list/no-matching-products.js | 14 +++---- .../components/product-list/no-products.js | 14 +++---- .../base/components/product-list/style.scss | 6 +-- .../js/blocks/products/all-products/block.js | 2 +- .../js/blocks/products/all-products/edit.js | 2 +- assets/js/blocks/single-product/block.js | 6 +-- .../single-product/edit/layout-editor.js | 2 +- .../context/inner-block-layout-context.js | 8 ++-- 17 files changed, 89 insertions(+), 107 deletions(-) diff --git a/assets/js/atomic/blocks/product/button/block.js b/assets/js/atomic/blocks/product/button/block.js index 8ad550122da..109197e9569 100644 --- a/assets/js/atomic/blocks/product/button/block.js +++ b/assets/js/atomic/blocks/product/button/block.js @@ -25,19 +25,14 @@ import { const ProductButton = ( { className, ...props } ) => { const productDataContext = useProductDataContext(); const product = props.product || productDataContext.product; - - const { layoutStyleClassPrefix } = useInnerBlockLayoutContext(); - const componentClass = `${ layoutStyleClassPrefix }__product-add-to-cart`; + const { parentClassName } = useInnerBlockLayoutContext(); return (
{ product ? ( diff --git a/assets/js/atomic/blocks/product/image/block.js b/assets/js/atomic/blocks/product/image/block.js index fa9620d3c0e..ae3b69caf7a 100644 --- a/assets/js/atomic/blocks/product/image/block.js +++ b/assets/js/atomic/blocks/product/image/block.js @@ -36,10 +36,7 @@ const ProductImage = ( { } ) => { const productDataContext = useProductDataContext(); const product = props.product || productDataContext.product; - - const { layoutStyleClassPrefix } = useInnerBlockLayoutContext(); - const componentClass = `${ layoutStyleClassPrefix }__product-image`; - + const { parentClassName } = useInnerBlockLayoutContext(); const [ imageLoaded, setImageLoaded ] = useState( false ); if ( ! product ) { @@ -47,11 +44,11 @@ const ProductImage = ( {
- +
); } @@ -60,14 +57,18 @@ const ProductImage = ( { product?.images && product.images.length ? product.images[ 0 ] : null; return ( -
+
{ productLink ? ( { showSaleBadge && ( ) } setImageLoaded( true ) } loaded={ imageLoaded } @@ -79,7 +80,6 @@ const ProductImage = ( { ) } setImageLoaded( true ) } loaded={ imageLoaded } @@ -90,26 +90,16 @@ const ProductImage = ( { ); }; -const ImagePlaceholder = ( { componentClass } ) => { - return ( - - ); +const ImagePlaceholder = () => { + return ; }; -const Image = ( { componentClass, image, onLoad, loaded } ) => { +const Image = ( { image, onLoad, loaded } ) => { const { thumbnail, srcset, sizes, alt } = image || {}; return ( <> { onLoad={ onLoad } hidden={ ! loaded } /> - { ! loaded && ( - - ) } + { ! loaded && } ); }; diff --git a/assets/js/atomic/blocks/product/price/block.js b/assets/js/atomic/blocks/product/price/block.js index f401a804820..c17f8305c9a 100644 --- a/assets/js/atomic/blocks/product/price/block.js +++ b/assets/js/atomic/blocks/product/price/block.js @@ -22,18 +22,15 @@ import { const ProductPrice = ( { className, ...props } ) => { const productDataContext = useProductDataContext(); const product = props.product || productDataContext.product; - - const { layoutStyleClassPrefix } = useInnerBlockLayoutContext(); - const componentClass = `${ layoutStyleClassPrefix }__product-price`; + const { parentClassName } = useInnerBlockLayoutContext(); if ( ! product ) { return (
); @@ -43,17 +40,23 @@ const ProductPrice = ( { className, ...props } ) => { const currency = getCurrencyFromPriceResponse( prices ); return ( -
+
{ hasPriceRange( prices ) ? ( ) : ( { ); }; -const PriceRange = ( { componentClass, currency, minAmount, maxAmount } ) => { +const PriceRange = ( { parentClassName, currency, minAmount, maxAmount } ) => { return ( - + { ); }; -const Price = ( { componentClass, currency, price, regularPrice } ) => { +const Price = ( { parentClassName, currency, price, regularPrice } ) => { return ( <> { regularPrice !== price && ( - + ) } - + { const productDataContext = useProductDataContext(); const product = props.product || productDataContext.product; - - const { layoutStyleClassPrefix } = useInnerBlockLayoutContext(); - const componentClass = `${ layoutStyleClassPrefix }__product-rating`; - + const { parentClassName } = useInnerBlockLayoutContext(); const rating = getAverageRating( product ); if ( ! rating ) { @@ -42,10 +39,14 @@ const ProductRating = ( { className, ...props } ) => { return (
diff --git a/assets/js/atomic/blocks/product/sale-badge/block.js b/assets/js/atomic/blocks/product/sale-badge/block.js index c259cf44a38..e7620b77cae 100644 --- a/assets/js/atomic/blocks/product/sale-badge/block.js +++ b/assets/js/atomic/blocks/product/sale-badge/block.js @@ -23,16 +23,16 @@ import { const ProductSaleBadge = ( { className, align, ...props } ) => { const productDataContext = useProductDataContext(); const product = props.product || productDataContext.product; - - const { layoutStyleClassPrefix } = useInnerBlockLayoutContext(); - const componentClass = `${ layoutStyleClassPrefix }__product-onsale`; + const { parentClassName } = useInnerBlockLayoutContext(); if ( ! product || ! product.on_sale ) { return null; } const alignClass = - typeof align === 'string' ? `${ componentClass }--align${ align }` : ''; + typeof align === 'string' + ? `${ parentClassName }__product-onsale--align${ align }` + : ''; return (
{ 'wc-block-component__sale-badge', className, alignClass, - componentClass + `${ parentClassName }__product-onsale` ) } >
); @@ -50,7 +47,10 @@ const ProductSummary = ( { className, ...props } ) => { return ( { const productDataContext = useProductDataContext(); const product = props.product || productDataContext.product; - - const { layoutStyleClassPrefix } = useInnerBlockLayoutContext(); - const componentClass = `${ layoutStyleClassPrefix }__product-title`; - + const { parentClassName } = useInnerBlockLayoutContext(); const TagName = `h${ headingLevel }`; if ( ! product ) { @@ -40,8 +37,7 @@ const ProductTitle = ( { // @ts-ignore className={ classnames( className, - componentClass, - 'is-loading' + `${ parentClassName }__product-title` ) } /> ); @@ -51,7 +47,12 @@ const ProductTitle = ( { return ( // @ts-ignore - + { productLink ? ( { productName } diff --git a/assets/js/base/components/product-list-item/index.js b/assets/js/base/components/product-list-item/index.js index 0be7b199387..2dc4d377270 100644 --- a/assets/js/base/components/product-list-item/index.js +++ b/assets/js/base/components/product-list-item/index.js @@ -13,9 +13,9 @@ import { renderProductLayout } from './utils'; const ProductListItem = ( { product, attributes, instanceId } ) => { const { layoutConfig } = attributes; - const { layoutStyleClassPrefix, parentName } = useInnerBlockLayoutContext(); + const { parentClassName, parentName } = useInnerBlockLayoutContext(); const isLoading = Object.keys( product ).length === 0; - const classes = classnames( `${ layoutStyleClassPrefix }__product`, { + const classes = classnames( `${ parentClassName }__product`, { 'is-loading': isLoading, } ); diff --git a/assets/js/base/components/product-list/index.js b/assets/js/base/components/product-list/index.js index ecaffd94448..ae249e857e3 100644 --- a/assets/js/base/components/product-list/index.js +++ b/assets/js/base/components/product-list/index.js @@ -114,7 +114,7 @@ const ProductList = ( { const { products, totalProducts, productsLoading } = useStoreProducts( queryState ); - const { layoutStyleClassPrefix } = useInnerBlockLayoutContext(); + const { parentClassName } = useInnerBlockLayoutContext(); const totalQuery = extractPaginationAndSortAttributes( queryState ); // These are possible filters. @@ -162,7 +162,7 @@ const ProductList = ( { const alignClass = typeof align !== 'undefined' ? 'align' + align : ''; return classnames( - layoutStyleClassPrefix, + parentClassName, alignClass, 'has-' + columns + '-columns', { @@ -206,7 +206,7 @@ const ProductList = ( { ) } { ! hasProducts && ! hasFilters && } { hasProducts && ( -