diff --git a/assets/css/style.scss b/assets/css/style.scss
index ab4af14111a..787fcf3f1e2 100644
--- a/assets/css/style.scss
+++ b/assets/css/style.scss
@@ -236,7 +236,7 @@
}
.wc-block-grid__products .wc-block-grid__product-onsale,
- .wc-block-layout .wc-block-components-product-sale-badge {
+ .wc-block-components-product-sale-badge {
background: $twentytwenty-highlights-color;
color: #fff;
font-family: $twentytwenty-headings;
diff --git a/assets/js/atomic/blocks/component-init.js b/assets/js/atomic/blocks/component-init.js
index c3a721df8a1..7cb50775594 100644
--- a/assets/js/atomic/blocks/component-init.js
+++ b/assets/js/atomic/blocks/component-init.js
@@ -13,7 +13,7 @@ registerBlockComponent( {
blockName: 'woocommerce/product-price',
component: lazy( () =>
import(
- /* webpackChunkName: "atomic-block-components/price" */ './product/price/block'
+ /* webpackChunkName: "atomic-block-components/price" */ './product-elements/price/block'
)
),
} );
@@ -22,7 +22,7 @@ registerBlockComponent( {
blockName: 'woocommerce/product-image',
component: lazy( () =>
import(
- /* webpackChunkName: "atomic-block-components/image" */ './product/image/frontend'
+ /* webpackChunkName: "atomic-block-components/image" */ './product-elements/image/frontend'
)
),
} );
@@ -31,7 +31,7 @@ registerBlockComponent( {
blockName: 'woocommerce/product-title',
component: lazy( () =>
import(
- /* webpackChunkName: "atomic-block-components/title" */ './product/title/frontend'
+ /* webpackChunkName: "atomic-block-components/title" */ './product-elements/title/frontend'
)
),
} );
@@ -40,7 +40,7 @@ registerBlockComponent( {
blockName: 'woocommerce/product-rating',
component: lazy( () =>
import(
- /* webpackChunkName: "atomic-block-components/rating" */ './product/rating/block'
+ /* webpackChunkName: "atomic-block-components/rating" */ './product-elements/rating/block'
)
),
} );
@@ -49,7 +49,7 @@ registerBlockComponent( {
blockName: 'woocommerce/product-button',
component: lazy( () =>
import(
- /* webpackChunkName: "atomic-block-components/button" */ './product/button/block'
+ /* webpackChunkName: "atomic-block-components/button" */ './product-elements/button/block'
)
),
} );
@@ -58,7 +58,7 @@ registerBlockComponent( {
blockName: 'woocommerce/product-summary',
component: lazy( () =>
import(
- /* webpackChunkName: "atomic-block-components/summary" */ './product/summary/block'
+ /* webpackChunkName: "atomic-block-components/summary" */ './product-elements/summary/block'
)
),
} );
@@ -67,7 +67,7 @@ registerBlockComponent( {
blockName: 'woocommerce/product-sale-badge',
component: lazy( () =>
import(
- /* webpackChunkName: "atomic-block-components/sale-badge" */ './product/sale-badge/block'
+ /* webpackChunkName: "atomic-block-components/sale-badge" */ './product-elements/sale-badge/block'
)
),
} );
@@ -76,7 +76,7 @@ registerBlockComponent( {
blockName: 'woocommerce/product-sku',
component: lazy( () =>
import(
- /* webpackChunkName: "atomic-block-components/sku" */ './product/sku/block'
+ /* webpackChunkName: "atomic-block-components/sku" */ './product-elements/sku/block'
)
),
} );
@@ -85,7 +85,7 @@ registerBlockComponent( {
blockName: 'woocommerce/product-category-list',
component: lazy( () =>
import(
- /* webpackChunkName: "atomic-block-components/category-list" */ './product/category-list/block'
+ /* webpackChunkName: "atomic-block-components/category-list" */ './product-elements/category-list/block'
)
),
} );
@@ -94,7 +94,7 @@ registerBlockComponent( {
blockName: 'woocommerce/product-tag-list',
component: lazy( () =>
import(
- /* webpackChunkName: "atomic-block-components/tag-list" */ './product/tag-list/block'
+ /* webpackChunkName: "atomic-block-components/tag-list" */ './product-elements/tag-list/block'
)
),
} );
@@ -103,7 +103,7 @@ registerBlockComponent( {
blockName: 'woocommerce/product-stock-indicator',
component: lazy( () =>
import(
- /* webpackChunkName: "atomic-block-components/stock-indicator" */ './product/stock-indicator/block'
+ /* webpackChunkName: "atomic-block-components/stock-indicator" */ './product-elements/stock-indicator/block'
)
),
} );
@@ -112,7 +112,7 @@ registerBlockComponent( {
blockName: 'woocommerce/product-add-to-cart',
component: lazy( () =>
import(
- /* webpackChunkName: "atomic-block-components/add-to-cart" */ './product/add-to-cart/frontend'
+ /* webpackChunkName: "atomic-block-components/add-to-cart" */ './product-elements/add-to-cart/frontend'
)
),
} );
diff --git a/assets/js/atomic/blocks/index.js b/assets/js/atomic/blocks/index.js
index 1c55feb04e6..5a1ff791cbd 100644
--- a/assets/js/atomic/blocks/index.js
+++ b/assets/js/atomic/blocks/index.js
@@ -1,15 +1,15 @@
/**
* Internal dependencies
*/
-import './product/title';
-import './product/price';
-import './product/image';
-import './product/rating';
-import './product/button';
-import './product/summary';
-import './product/sale-badge';
-import './product/sku';
-import './product/category-list';
-import './product/tag-list';
-import './product/stock-indicator';
-import './product/add-to-cart';
+import './product-elements/title';
+import './product-elements/price';
+import './product-elements/image';
+import './product-elements/rating';
+import './product-elements/button';
+import './product-elements/summary';
+import './product-elements/sale-badge';
+import './product-elements/sku';
+import './product-elements/category-list';
+import './product-elements/tag-list';
+import './product-elements/stock-indicator';
+import './product-elements/add-to-cart';
diff --git a/assets/js/atomic/blocks/product/add-to-cart/attributes.js b/assets/js/atomic/blocks/product-elements/add-to-cart/attributes.js
similarity index 72%
rename from assets/js/atomic/blocks/product/add-to-cart/attributes.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/attributes.js
index dd6bdd2c27f..993ebcc8ec6 100644
--- a/assets/js/atomic/blocks/product/add-to-cart/attributes.js
+++ b/assets/js/atomic/blocks/product-elements/add-to-cart/attributes.js
@@ -3,6 +3,10 @@ export const blockAttributes = {
type: 'boolean',
default: false,
},
+ productId: {
+ type: 'number',
+ default: 0,
+ },
};
export default blockAttributes;
diff --git a/assets/js/atomic/blocks/product/add-to-cart/block.js b/assets/js/atomic/blocks/product-elements/add-to-cart/block.js
similarity index 77%
rename from assets/js/atomic/blocks/product/add-to-cart/block.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/block.js
index 59cde68d877..be555fb5029 100644
--- a/assets/js/atomic/blocks/product/add-to-cart/block.js
+++ b/assets/js/atomic/blocks/product-elements/add-to-cart/block.js
@@ -6,6 +6,7 @@ import classnames from 'classnames';
import { AddToCartFormContextProvider } from '@woocommerce/base-context';
import { useProductDataContext } from '@woocommerce/shared-context';
import { isEmpty } from 'lodash';
+import { withProductDataContext } from '@woocommerce/shared-hocs';
/**
* Internal dependencies
@@ -25,13 +26,10 @@ import {
* @param {Object} props Incoming props.
* @param {string} [props.className] CSS Class name for the component.
* @param {boolean} [props.showFormElements] Should form elements be shown?
- * @param {Object} [props.product] Optional product object. Product from context will be
- * used if this is not provided.
* @return {*} The component.
*/
-const Block = ( { className, showFormElements, ...props } ) => {
- const productDataContext = useProductDataContext();
- const product = props.product || productDataContext.product || {};
+const Block = ( { className, showFormElements } ) => {
+ const { product } = useProductDataContext();
const componentClass = classnames(
className,
'wc-block-components-product-add-to-cart',
@@ -50,9 +48,7 @@ const Block = ( { className, showFormElements, ...props } ) => {
<>
{ showFormElements ? (
-
+
) : (
) }
@@ -80,7 +76,6 @@ const AddToCartForm = ( { productType } ) => {
Block.propTypes = {
className: PropTypes.string,
- product: PropTypes.object,
};
-export default Block;
+export default withProductDataContext( Block );
diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/constants.js b/assets/js/atomic/blocks/product-elements/add-to-cart/constants.js
new file mode 100644
index 00000000000..b01bd0f7f3b
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/add-to-cart/constants.js
@@ -0,0 +1,12 @@
+/**
+ * External dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { cart, Icon } from '@woocommerce/icons';
+
+export const BLOCK_TITLE = __( 'Add to Cart', 'woo-gutenberg-products-block' );
+export const BLOCK_ICON =
;
+export const BLOCK_DESCRIPTION = __(
+ 'Displays an add to cart button. Optionally displays other add to cart form elements.',
+ 'woo-gutenberg-products-block'
+);
diff --git a/assets/js/atomic/blocks/product/add-to-cart/edit.js b/assets/js/atomic/blocks/product-elements/add-to-cart/edit.js
similarity index 74%
rename from assets/js/atomic/blocks/product/add-to-cart/edit.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/edit.js
index 35ad598ba62..091e68ea5d3 100644
--- a/assets/js/atomic/blocks/product/add-to-cart/edit.js
+++ b/assets/js/atomic/blocks/product-elements/add-to-cart/edit.js
@@ -13,10 +13,11 @@ import { InspectorControls } from '@wordpress/block-editor';
*/
import './style.scss';
import Block from './block';
+import withProductSelector from '../shared/with-product-selector';
+import { BLOCK_TITLE, BLOCK_ICON } from './constants';
-export default ( { attributes, setAttributes } ) => {
- const productDataContext = useProductDataContext();
- const product = productDataContext.product || {};
+const Edit = ( { attributes, setAttributes } ) => {
+ const { product } = useProductDataContext();
const { className, showFormElements } = attributes;
return (
@@ -26,7 +27,7 @@ export default ( { attributes, setAttributes } ) => {
'wc-block-components-product-add-to-cart'
) }
>
-
+
{ product.type !== 'external' && (
{
);
};
+
+export default withProductSelector( {
+ icon: BLOCK_ICON,
+ label: BLOCK_TITLE,
+ description: __(
+ "Choose a product to display it's add to cart form.",
+ 'woo-gutenberg-products-block'
+ ),
+} )( Edit );
diff --git a/assets/js/atomic/blocks/product/add-to-cart/frontend.js b/assets/js/atomic/blocks/product-elements/add-to-cart/frontend.js
similarity index 100%
rename from assets/js/atomic/blocks/product/add-to-cart/frontend.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/frontend.js
diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/index.js b/assets/js/atomic/blocks/product-elements/add-to-cart/index.js
new file mode 100644
index 00000000000..d807f143175
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/add-to-cart/index.js
@@ -0,0 +1,32 @@
+/**
+ * External dependencies
+ */
+import { registerExperimentalBlockType } from '@woocommerce/block-settings';
+
+/**
+ * Internal dependencies
+ */
+import sharedConfig from '../shared/config';
+import edit from './edit';
+import attributes from './attributes';
+import {
+ BLOCK_TITLE as title,
+ BLOCK_ICON as icon,
+ BLOCK_DESCRIPTION as description,
+} from './constants';
+
+const blockConfig = {
+ title,
+ description,
+ icon: {
+ src: icon,
+ foreground: '#874FB9',
+ },
+ edit,
+ attributes,
+};
+
+registerExperimentalBlockType( 'woocommerce/product-add-to-cart', {
+ ...sharedConfig,
+ ...blockConfig,
+} );
diff --git a/assets/js/atomic/blocks/product/add-to-cart/product-types/external.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/external.js
similarity index 100%
rename from assets/js/atomic/blocks/product/add-to-cart/product-types/external.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/external.js
diff --git a/assets/js/atomic/blocks/product/add-to-cart/product-types/grouped/group-list/index.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/grouped/group-list/index.js
similarity index 100%
rename from assets/js/atomic/blocks/product/add-to-cart/product-types/grouped/group-list/index.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/grouped/group-list/index.js
diff --git a/assets/js/atomic/blocks/product/add-to-cart/product-types/grouped/index.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/grouped/index.js
similarity index 100%
rename from assets/js/atomic/blocks/product/add-to-cart/product-types/grouped/index.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/grouped/index.js
diff --git a/assets/js/atomic/blocks/product/add-to-cart/product-types/index.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/index.js
similarity index 100%
rename from assets/js/atomic/blocks/product/add-to-cart/product-types/index.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/index.js
diff --git a/assets/js/atomic/blocks/product/add-to-cart/product-types/simple.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/simple.js
similarity index 100%
rename from assets/js/atomic/blocks/product/add-to-cart/product-types/simple.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/simple.js
diff --git a/assets/js/atomic/blocks/product/add-to-cart/product-types/variable/index.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/index.js
similarity index 100%
rename from assets/js/atomic/blocks/product/add-to-cart/product-types/variable/index.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/index.js
diff --git a/assets/js/atomic/blocks/product/add-to-cart/product-types/variable/variation-attributes/attribute-picker.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/attribute-picker.js
similarity index 100%
rename from assets/js/atomic/blocks/product/add-to-cart/product-types/variable/variation-attributes/attribute-picker.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/attribute-picker.js
diff --git a/assets/js/atomic/blocks/product/add-to-cart/product-types/variable/variation-attributes/attribute-select-control.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/attribute-select-control.js
similarity index 100%
rename from assets/js/atomic/blocks/product/add-to-cart/product-types/variable/variation-attributes/attribute-select-control.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/attribute-select-control.js
diff --git a/assets/js/atomic/blocks/product/add-to-cart/product-types/variable/variation-attributes/index.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/index.js
similarity index 100%
rename from assets/js/atomic/blocks/product/add-to-cart/product-types/variable/variation-attributes/index.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/index.js
diff --git a/assets/js/atomic/blocks/product/add-to-cart/product-types/variable/variation-attributes/style.scss b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/style.scss
similarity index 100%
rename from assets/js/atomic/blocks/product/add-to-cart/product-types/variable/variation-attributes/style.scss
rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/style.scss
diff --git a/assets/js/atomic/blocks/product/add-to-cart/product-types/variable/variation-attributes/utils.js b/assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/utils.js
similarity index 100%
rename from assets/js/atomic/blocks/product/add-to-cart/product-types/variable/variation-attributes/utils.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/product-types/variable/variation-attributes/utils.js
diff --git a/assets/js/atomic/blocks/product/add-to-cart/shared/add-to-cart-button.js b/assets/js/atomic/blocks/product-elements/add-to-cart/shared/add-to-cart-button.js
similarity index 100%
rename from assets/js/atomic/blocks/product/add-to-cart/shared/add-to-cart-button.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/shared/add-to-cart-button.js
diff --git a/assets/js/atomic/blocks/product/add-to-cart/shared/index.js b/assets/js/atomic/blocks/product-elements/add-to-cart/shared/index.js
similarity index 100%
rename from assets/js/atomic/blocks/product/add-to-cart/shared/index.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/shared/index.js
diff --git a/assets/js/atomic/blocks/product/add-to-cart/shared/product-unavailable.js b/assets/js/atomic/blocks/product-elements/add-to-cart/shared/product-unavailable.js
similarity index 100%
rename from assets/js/atomic/blocks/product/add-to-cart/shared/product-unavailable.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/shared/product-unavailable.js
diff --git a/assets/js/atomic/blocks/product/add-to-cart/shared/quantity-input.js b/assets/js/atomic/blocks/product-elements/add-to-cart/shared/quantity-input.js
similarity index 100%
rename from assets/js/atomic/blocks/product/add-to-cart/shared/quantity-input.js
rename to assets/js/atomic/blocks/product-elements/add-to-cart/shared/quantity-input.js
diff --git a/assets/js/atomic/blocks/product-elements/add-to-cart/style.scss b/assets/js/atomic/blocks/product-elements/add-to-cart/style.scss
new file mode 100644
index 00000000000..e3eb377ace2
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/add-to-cart/style.scss
@@ -0,0 +1,43 @@
+.wc-block-components-product-add-to-cart {
+ margin: 0;
+ display: flex;
+ flex-wrap: wrap;
+
+ .wc-block-components-product-add-to-cart-button {
+ margin: 0 0 em($gap-small) 0;
+
+ .wc-block-components-button__text {
+ display: block;
+
+ > svg {
+ fill: currentColor;
+ vertical-align: top;
+ width: 1.5em;
+ height: 1.5em;
+ margin: -0.25em 0 -0.25em 0.5em;
+ }
+ }
+ }
+
+ .wc-block-components-product-add-to-cart-quantity {
+ margin: 0 1em em($gap-small) 0;
+ width: 5em;
+ padding: 0.618em;
+ background: $white;
+ border: 1px solid #ccc;
+ border-radius: 2px;
+ color: #43454b;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.125);
+ text-align: center;
+ }
+}
+.wc-block-components-product-add-to-cart--placeholder {
+ .wc-block-components-product-add-to-cart-quantity,
+ .wc-block-components-product-add-to-cart-button {
+ @include placeholder();
+ }
+}
+
+.wc-block-grid .wc-block-components-product-add-to-cart {
+ justify-content: center;
+}
diff --git a/assets/js/atomic/blocks/product-elements/button/attributes.js b/assets/js/atomic/blocks/product-elements/button/attributes.js
new file mode 100644
index 00000000000..5bac8820c40
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/button/attributes.js
@@ -0,0 +1,8 @@
+export const blockAttributes = {
+ productId: {
+ type: 'number',
+ default: 0,
+ },
+};
+
+export default blockAttributes;
diff --git a/assets/js/atomic/blocks/product/button/block.js b/assets/js/atomic/blocks/product-elements/button/block.js
similarity index 87%
rename from assets/js/atomic/blocks/product/button/block.js
rename to assets/js/atomic/blocks/product-elements/button/block.js
index 9a8c7089d22..15e1ed4483d 100644
--- a/assets/js/atomic/blocks/product/button/block.js
+++ b/assets/js/atomic/blocks/product-elements/button/block.js
@@ -12,6 +12,7 @@ import {
useInnerBlockLayoutContext,
useProductDataContext,
} from '@woocommerce/shared-context';
+import { withProductDataContext } from '@woocommerce/shared-hocs';
/**
* Internal dependencies
@@ -23,14 +24,11 @@ import './style.scss';
*
* @param {Object} props Incoming props.
* @param {string} [props.className] CSS Class name for the component.
- * @param {Object} [props.product] Optional product object. Product from context will be used if
- * this is not provided.
* @return {*} The component.
*/
-const Block = ( { className, ...props } ) => {
+const Block = ( { className } ) => {
const { parentClassName } = useInnerBlockLayoutContext();
- const productDataContext = useProductDataContext();
- const product = props.product || productDataContext.product;
+ const { product } = useProductDataContext();
return (
{
className,
'wp-block-button',
'wc-block-components-product-button',
- `${ parentClassName }__product-add-to-cart`
+ {
+ [ `${ parentClassName }__product-add-to-cart` ]: parentClassName,
+ }
) }
>
- { product ? (
+ { product.id ? (
) : (
@@ -142,7 +142,6 @@ const AddToCartButtonPlaceholder = () => {
Block.propTypes = {
className: PropTypes.string,
- product: PropTypes.object,
};
-export default Block;
+export default withProductDataContext( Block );
diff --git a/assets/js/atomic/blocks/product-elements/button/constants.js b/assets/js/atomic/blocks/product-elements/button/constants.js
new file mode 100644
index 00000000000..75a90cb397f
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/button/constants.js
@@ -0,0 +1,15 @@
+/**
+ * External dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { cart, Icon } from '@woocommerce/icons';
+
+export const BLOCK_TITLE = __(
+ 'Add to Cart Button',
+ 'woo-gutenberg-products-block'
+);
+export const BLOCK_ICON =
;
+export const BLOCK_DESCRIPTION = __(
+ 'Display a call to action button which either adds the product to the cart, or links to the product page.',
+ 'woo-gutenberg-products-block'
+);
diff --git a/assets/js/atomic/blocks/product-elements/button/edit.js b/assets/js/atomic/blocks/product-elements/button/edit.js
new file mode 100644
index 00000000000..7259d387c8f
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/button/edit.js
@@ -0,0 +1,29 @@
+/**
+ * External dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { Disabled } from '@wordpress/components';
+
+/**
+ * Internal dependencies
+ */
+import Block from './block';
+import withProductSelector from '../shared/with-product-selector';
+import { BLOCK_TITLE, BLOCK_ICON } from './constants';
+
+const Edit = ( { attributes } ) => {
+ return (
+
+
+
+ );
+};
+
+export default withProductSelector( {
+ icon: BLOCK_ICON,
+ label: BLOCK_TITLE,
+ description: __(
+ "Choose a product to display it's add to cart button.",
+ 'woo-gutenberg-products-block'
+ ),
+} )( Edit );
diff --git a/assets/js/atomic/blocks/product-elements/button/index.js b/assets/js/atomic/blocks/product-elements/button/index.js
new file mode 100644
index 00000000000..305b9c520d7
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/button/index.js
@@ -0,0 +1,32 @@
+/**
+ * External dependencies
+ */
+import { registerBlockType } from '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+import sharedConfig from '../shared/config';
+import attributes from './attributes';
+import edit from './edit';
+import {
+ BLOCK_TITLE as title,
+ BLOCK_ICON as icon,
+ BLOCK_DESCRIPTION as description,
+} from './constants';
+
+const blockConfig = {
+ title,
+ description,
+ icon: {
+ src: icon,
+ foreground: '#874FB9',
+ },
+ attributes,
+ edit,
+};
+
+registerBlockType( 'woocommerce/product-button', {
+ ...sharedConfig,
+ ...blockConfig,
+} );
diff --git a/assets/js/atomic/blocks/product/button/style.scss b/assets/js/atomic/blocks/product-elements/button/style.scss
similarity index 72%
rename from assets/js/atomic/blocks/product/button/style.scss
rename to assets/js/atomic/blocks/product-elements/button/style.scss
index 197205468db..aead6e03a94 100644
--- a/assets/js/atomic/blocks/product/button/style.scss
+++ b/assets/js/atomic/blocks/product-elements/button/style.scss
@@ -1,4 +1,4 @@
-.wc-block-layout .wc-block-components-product-button {
+.wp-block-button.wc-block-components-product-button {
word-break: break-word;
white-space: normal;
margin-top: 0;
@@ -19,7 +19,7 @@
}
}
-.wc-block-layout--is-loading .wc-block-components-product-button > .wc-block-components-product-button__button {
+.is-loading .wc-block-components-product-button > .wc-block-components-product-button__button {
@include placeholder();
min-width: 8em;
min-height: 3em;
diff --git a/assets/js/atomic/blocks/product-elements/category-list/attributes.js b/assets/js/atomic/blocks/product-elements/category-list/attributes.js
new file mode 100644
index 00000000000..5bac8820c40
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/category-list/attributes.js
@@ -0,0 +1,8 @@
+export const blockAttributes = {
+ productId: {
+ type: 'number',
+ default: 0,
+ },
+};
+
+export default blockAttributes;
diff --git a/assets/js/atomic/blocks/product/category-list/block.js b/assets/js/atomic/blocks/product-elements/category-list/block.js
similarity index 70%
rename from assets/js/atomic/blocks/product/category-list/block.js
rename to assets/js/atomic/blocks/product-elements/category-list/block.js
index 7a4ce4c4a7e..0f8ef195d36 100644
--- a/assets/js/atomic/blocks/product/category-list/block.js
+++ b/assets/js/atomic/blocks/product-elements/category-list/block.js
@@ -9,6 +9,7 @@ import {
useProductDataContext,
} from '@woocommerce/shared-context';
import { isEmpty } from 'lodash';
+import { withProductDataContext } from '@woocommerce/shared-hocs';
/**
* Internal dependencies
@@ -20,16 +21,13 @@ import './style.scss';
*
* @param {Object} props Incoming props.
* @param {string} [props.className] CSS Class name for the component.
- * @param {Object} [props.product] Optional product object. Product from context will be used if
- * this is not provided.
* @return {*} The component.
*/
-const Block = ( { className, ...props } ) => {
+const Block = ( { className } ) => {
const { parentClassName } = useInnerBlockLayoutContext();
- const productDataContext = useProductDataContext();
- const { product } = productDataContext || props || {};
+ const { product } = useProductDataContext();
- if ( isEmpty( product ) || isEmpty( product.categories ) ) {
+ if ( isEmpty( product.categories ) ) {
return null;
}
@@ -38,7 +36,9 @@ const Block = ( { className, ...props } ) => {
className={ classnames(
className,
'wc-block-components-product-category-list',
- `${ parentClassName }__product-category-list`
+ {
+ [ `${ parentClassName }__product-category-list` ]: parentClassName,
+ }
) }
>
{ __( 'Categories:', 'woo-gutenberg-products-block' ) }{ ' ' }
@@ -59,7 +59,6 @@ const Block = ( { className, ...props } ) => {
Block.propTypes = {
className: PropTypes.string,
- product: PropTypes.object,
};
-export default Block;
+export default withProductDataContext( Block );
diff --git a/assets/js/atomic/blocks/product-elements/category-list/constants.js b/assets/js/atomic/blocks/product-elements/category-list/constants.js
new file mode 100644
index 00000000000..18b53f405a2
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/category-list/constants.js
@@ -0,0 +1,15 @@
+/**
+ * External dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { folder, Icon } from '@woocommerce/icons';
+
+export const BLOCK_TITLE = __(
+ 'Product Category List',
+ 'woo-gutenberg-products-block'
+);
+export const BLOCK_ICON =
;
+export const BLOCK_DESCRIPTION = __(
+ 'Display a list of categories belonging to a product.',
+ 'woo-gutenberg-products-block'
+);
diff --git a/assets/js/atomic/blocks/product-elements/category-list/edit.js b/assets/js/atomic/blocks/product-elements/category-list/edit.js
new file mode 100644
index 00000000000..01fb2104a70
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/category-list/edit.js
@@ -0,0 +1,33 @@
+/**
+ * External dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { Disabled } from '@wordpress/components';
+import EditProductLink from '@woocommerce/block-components/edit-product-link';
+
+/**
+ * Internal dependencies
+ */
+import Block from './block';
+import withProductSelector from '../shared/with-product-selector';
+import { BLOCK_TITLE, BLOCK_ICON } from './constants';
+
+const Edit = ( { attributes } ) => {
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
+
+export default withProductSelector( {
+ icon: BLOCK_ICON,
+ label: BLOCK_TITLE,
+ description: __(
+ "Choose a product to display it's categories.",
+ 'woo-gutenberg-products-block'
+ ),
+} )( Edit );
diff --git a/assets/js/atomic/blocks/product-elements/category-list/index.js b/assets/js/atomic/blocks/product-elements/category-list/index.js
new file mode 100644
index 00000000000..34c818ff92d
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/category-list/index.js
@@ -0,0 +1,32 @@
+/**
+ * External dependencies
+ */
+import { registerExperimentalBlockType } from '@woocommerce/block-settings';
+
+/**
+ * Internal dependencies
+ */
+import sharedConfig from '../shared/config';
+import attributes from './attributes';
+import edit from './edit';
+import {
+ BLOCK_TITLE as title,
+ BLOCK_ICON as icon,
+ BLOCK_DESCRIPTION as description,
+} from './constants';
+
+const blockConfig = {
+ title,
+ description,
+ icon: {
+ src: icon,
+ foreground: '#874FB9',
+ },
+ attributes,
+ edit,
+};
+
+registerExperimentalBlockType( 'woocommerce/product-category-list', {
+ ...sharedConfig,
+ ...blockConfig,
+} );
diff --git a/assets/js/atomic/blocks/product/tag-list/style.scss b/assets/js/atomic/blocks/product-elements/category-list/style.scss
similarity index 80%
rename from assets/js/atomic/blocks/product/tag-list/style.scss
rename to assets/js/atomic/blocks/product-elements/category-list/style.scss
index df535224e68..b3fa4fd4c10 100644
--- a/assets/js/atomic/blocks/product/tag-list/style.scss
+++ b/assets/js/atomic/blocks/product-elements/category-list/style.scss
@@ -1,4 +1,4 @@
-.wc-block-layout .wc-block-components-product-tag-list {
+.wc-block-components-product-category-list {
margin-top: 0;
margin-bottom: em($gap-small);
diff --git a/assets/js/atomic/blocks/product/image/attributes.js b/assets/js/atomic/blocks/product-elements/image/attributes.js
similarity index 85%
rename from assets/js/atomic/blocks/product/image/attributes.js
rename to assets/js/atomic/blocks/product-elements/image/attributes.js
index c1190c99448..bff0b5f4a79 100644
--- a/assets/js/atomic/blocks/product/image/attributes.js
+++ b/assets/js/atomic/blocks/product-elements/image/attributes.js
@@ -15,6 +15,10 @@ export const blockAttributes = {
type: 'string',
default: 'full-size',
},
+ productId: {
+ type: 'number',
+ default: 0,
+ },
};
export default blockAttributes;
diff --git a/assets/js/atomic/blocks/product/image/block.js b/assets/js/atomic/blocks/product-elements/image/block.js
similarity index 82%
rename from assets/js/atomic/blocks/product/image/block.js
rename to assets/js/atomic/blocks/product-elements/image/block.js
index 46fb1631e8a..c9a59e8bf7e 100644
--- a/assets/js/atomic/blocks/product/image/block.js
+++ b/assets/js/atomic/blocks/product-elements/image/block.js
@@ -9,6 +9,8 @@ import {
useInnerBlockLayoutContext,
useProductDataContext,
} from '@woocommerce/shared-context';
+import { withProductDataContext } from '@woocommerce/shared-hocs';
+import { isEmpty } from 'lodash';
/**
* Internal dependencies
@@ -21,11 +23,10 @@ import './style.scss';
*
* @param {Object} props Incoming props.
* @param {string} [props.className] CSS Class name for the component.
+ * @param {string} [props.imageSizing] Size of image to use.
* @param {boolean} [props.productLink] Whether or not to display a link to the product page.
* @param {boolean} [props.showSaleBadge] Whether or not to display the on sale badge.
* @param {string} [props.saleBadgeAlign] How should the sale badge be aligned if displayed.
- * @param {Object} [props.product] Optional product object. Product from context will be used if
- * this is not provided.
* @return {*} The component.
*/
const Block = ( {
@@ -34,21 +35,21 @@ const Block = ( {
productLink = true,
showSaleBadge,
saleBadgeAlign = 'right',
- ...props
} ) => {
const { parentClassName } = useInnerBlockLayoutContext();
- const productDataContext = useProductDataContext();
- const product = props.product || productDataContext.product;
+ const { product } = useProductDataContext();
const [ imageLoaded, setImageLoaded ] = useState( false );
- if ( ! product ) {
+ if ( ! product.id ) {
return (
@@ -56,15 +57,16 @@ const Block = ( {
);
}
- const image =
- product?.images && product.images.length ? product.images[ 0 ] : null;
+ const image = ! isEmpty( product.images ) ? product.images[ 0 ] : null;
return (
{ productLink ? (
@@ -103,7 +105,9 @@ const Block = ( {
};
const ImagePlaceholder = () => {
- return
;
+ return (
+
+ );
};
const Image = ( { image, onLoad, loaded, showFullSize } ) => {
@@ -135,10 +139,9 @@ const Image = ( { image, onLoad, loaded, showFullSize } ) => {
Block.propTypes = {
className: PropTypes.string,
- product: PropTypes.object,
productLink: PropTypes.bool,
showSaleBadge: PropTypes.bool,
saleBadgeAlign: PropTypes.string,
};
-export default Block;
+export default withProductDataContext( Block );
diff --git a/assets/js/atomic/blocks/product-elements/image/constants.js b/assets/js/atomic/blocks/product-elements/image/constants.js
new file mode 100644
index 00000000000..eeedac58c8a
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/image/constants.js
@@ -0,0 +1,15 @@
+/**
+ * External dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { image, Icon } from '@woocommerce/icons';
+
+export const BLOCK_TITLE = __(
+ 'Product Image',
+ 'woo-gutenberg-products-block'
+);
+export const BLOCK_ICON =
;
+export const BLOCK_DESCRIPTION = __(
+ 'Display the main product image',
+ 'woo-gutenberg-products-block'
+);
diff --git a/assets/js/atomic/blocks/product/image/edit.js b/assets/js/atomic/blocks/product-elements/image/edit.js
similarity index 90%
rename from assets/js/atomic/blocks/product/image/edit.js
rename to assets/js/atomic/blocks/product-elements/image/edit.js
index 228aec31776..a0f62ec9165 100644
--- a/assets/js/atomic/blocks/product/image/edit.js
+++ b/assets/js/atomic/blocks/product-elements/image/edit.js
@@ -12,8 +12,10 @@ import { getAdminLink } from '@woocommerce/settings';
* Internal dependencies
*/
import Block from './block';
+import withProductSelector from '../shared/with-product-selector';
+import { BLOCK_TITLE, BLOCK_ICON } from './constants';
-export default ( { attributes, setAttributes } ) => {
+const Edit = ( { attributes, setAttributes } ) => {
const {
productLink,
imageSizing,
@@ -146,3 +148,12 @@ export default ( { attributes, setAttributes } ) => {
>
);
};
+
+export default withProductSelector( {
+ icon: BLOCK_ICON,
+ label: BLOCK_TITLE,
+ description: __(
+ "Choose a product to display it's image.",
+ 'woo-gutenberg-products-block'
+ ),
+} )( Edit );
diff --git a/assets/js/atomic/blocks/product/image/frontend.js b/assets/js/atomic/blocks/product-elements/image/frontend.js
similarity index 100%
rename from assets/js/atomic/blocks/product/image/frontend.js
rename to assets/js/atomic/blocks/product-elements/image/frontend.js
diff --git a/assets/js/atomic/blocks/product-elements/image/index.js b/assets/js/atomic/blocks/product-elements/image/index.js
new file mode 100644
index 00000000000..07dd162514d
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/image/index.js
@@ -0,0 +1,32 @@
+/**
+ * External dependencies
+ */
+import { registerBlockType } from '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+import sharedConfig from '../shared/config';
+import attributes from './attributes';
+import edit from './edit';
+import {
+ BLOCK_TITLE as title,
+ BLOCK_ICON as icon,
+ BLOCK_DESCRIPTION as description,
+} from './constants';
+
+const blockConfig = {
+ title,
+ description,
+ icon: {
+ src: icon,
+ foreground: '#874FB9',
+ },
+ attributes,
+ edit,
+};
+
+registerBlockType( 'woocommerce/product-image', {
+ ...sharedConfig,
+ ...blockConfig,
+} );
diff --git a/assets/js/atomic/blocks/product/image/style.scss b/assets/js/atomic/blocks/product-elements/image/style.scss
similarity index 75%
rename from assets/js/atomic/blocks/product/image/style.scss
rename to assets/js/atomic/blocks/product-elements/image/style.scss
index f7401e8e6d8..bc85701726f 100644
--- a/assets/js/atomic/blocks/product/image/style.scss
+++ b/assets/js/atomic/blocks/product-elements/image/style.scss
@@ -1,5 +1,5 @@
-.editor-styles-wrapper .wc-block-layout .wc-block-grid__products .wc-block-grid__product .wc-block-components-product-image,
-.wc-block-layout .wc-block-components-product-image {
+.editor-styles-wrapper .wc-block-grid__products .wc-block-grid__product .wc-block-components-product-image,
+.wc-block-components-product-image {
margin-top: 0;
margin-bottom: $gap-small;
text-decoration: none;
@@ -48,6 +48,6 @@
}
}
-.wc-block-layout--is-loading .wc-block-components-product-image {
+.is-loading .wc-block-components-product-image {
@include placeholder();
}
diff --git a/assets/js/atomic/blocks/product-elements/price/attributes.js b/assets/js/atomic/blocks/product-elements/price/attributes.js
new file mode 100644
index 00000000000..7ade4214318
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/price/attributes.js
@@ -0,0 +1,45 @@
+/**
+ * External dependencies
+ */
+import { isFeaturePluginBuild } from '@woocommerce/block-settings';
+
+let blockAttributes = {
+ productId: {
+ type: 'number',
+ default: 0,
+ },
+};
+
+if ( isFeaturePluginBuild() ) {
+ blockAttributes = {
+ ...blockAttributes,
+ align: {
+ type: 'string',
+ },
+ fontSize: {
+ type: 'string',
+ },
+ customFontSize: {
+ type: 'number',
+ },
+ saleFontSize: {
+ type: 'string',
+ },
+ customSaleFontSize: {
+ type: 'number',
+ },
+ color: {
+ type: 'string',
+ },
+ saleColor: {
+ type: 'string',
+ },
+ customColor: {
+ type: 'string',
+ },
+ customSaleColor: {
+ type: 'string',
+ },
+ };
+}
+export default blockAttributes;
diff --git a/assets/js/atomic/blocks/product/price/block.js b/assets/js/atomic/blocks/product-elements/price/block.js
similarity index 89%
rename from assets/js/atomic/blocks/product/price/block.js
rename to assets/js/atomic/blocks/product-elements/price/block.js
index 1c9b05fc579..dcb261e6e66 100644
--- a/assets/js/atomic/blocks/product/price/block.js
+++ b/assets/js/atomic/blocks/product-elements/price/block.js
@@ -11,6 +11,8 @@ import {
} from '@woocommerce/shared-context';
import { getColorClassName, getFontSizeClass } from '@wordpress/block-editor';
import { isFeaturePluginBuild } from '@woocommerce/block-settings';
+import { withProductDataContext } from '@woocommerce/shared-hocs';
+
/**
* Internal dependencies
*/
@@ -45,11 +47,9 @@ const Block = ( {
customColor,
saleColor,
customSaleColor,
- ...props
} ) => {
const { parentClassName } = useInnerBlockLayoutContext();
- const productDataContext = useProductDataContext();
- const product = props.product || productDataContext.product;
+ const { product } = useProductDataContext();
const colorClass = getColorClassName( 'color', color );
const fontSizeClass = getFontSizeClass( fontSize );
@@ -80,20 +80,22 @@ const Block = ( {
fontSize: customSaleFontSize,
};
- if ( ! product ) {
+ if ( ! product.id ) {
return (
);
}
- const prices = product.prices || {};
+ const prices = product.prices;
const currency = getCurrencyFromPriceResponse( prices );
return (
@@ -102,8 +104,8 @@ const Block = ( {
className,
'price',
'wc-block-components-product-price',
- `${ parentClassName }__product-price`,
{
+ [ `${ parentClassName }__product-price` ]: parentClassName,
[ `wc-block-components-product-price__align-${ align }` ]:
align && isFeaturePluginBuild(),
}
@@ -155,8 +157,10 @@ const PriceRange = ( { currency, minAmount, maxAmount, classes, style } ) => {
@@ -188,8 +192,9 @@ const SalePrice = ( {
@@ -201,8 +206,9 @@ const SalePrice = ( {
@@ -245,4 +251,4 @@ Block.propTypes = {
customSaleColor: PropTypes.string,
};
-export default Block;
+export default withProductDataContext( Block );
diff --git a/assets/js/atomic/blocks/product-elements/price/constants.js b/assets/js/atomic/blocks/product-elements/price/constants.js
new file mode 100644
index 00000000000..bee87dc6221
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/price/constants.js
@@ -0,0 +1,15 @@
+/**
+ * External dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { bill, Icon } from '@woocommerce/icons';
+
+export const BLOCK_TITLE = __(
+ 'Product Price',
+ 'woo-gutenberg-products-block'
+);
+export const BLOCK_ICON = ;
+export const BLOCK_DESCRIPTION = __(
+ 'Display the price of a product.',
+ 'woo-gutenberg-products-block'
+);
diff --git a/assets/js/atomic/blocks/product/price/edit.js b/assets/js/atomic/blocks/product-elements/price/edit.js
similarity index 89%
rename from assets/js/atomic/blocks/product/price/edit.js
rename to assets/js/atomic/blocks/product-elements/price/edit.js
index fba9b11b446..58054ee4847 100644
--- a/assets/js/atomic/blocks/product/price/edit.js
+++ b/assets/js/atomic/blocks/product-elements/price/edit.js
@@ -18,6 +18,8 @@ import { isFeaturePluginBuild } from '@woocommerce/block-settings';
* Internal dependencies
*/
import Block from './block';
+import withProductSelector from '../shared/with-product-selector';
+import { BLOCK_TITLE, BLOCK_ICON } from './constants';
const TextControl = ( {
fontSize,
@@ -117,6 +119,14 @@ const Price = isFeaturePluginBuild()
withColors( 'color', { textColor: 'color' } ),
withColors( 'saleColor', { textColor: 'saleColor' } ),
withColors( 'originalColor', { textColor: 'originalColor' } ),
+ withProductSelector( {
+ icon: BLOCK_ICON,
+ label: BLOCK_TITLE,
+ description: __(
+ "Choose a product to display it's price.",
+ 'woo-gutenberg-products-block'
+ ),
+ } ),
] )( PriceEdit )
: PriceEdit;
diff --git a/assets/js/atomic/blocks/product-elements/price/index.js b/assets/js/atomic/blocks/product-elements/price/index.js
new file mode 100644
index 00000000000..15115a01fb5
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/price/index.js
@@ -0,0 +1,32 @@
+/**
+ * External dependencies
+ */
+import { registerBlockType } from '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+import sharedConfig from '../shared/config';
+import edit from './edit';
+import attributes from './attributes';
+import {
+ BLOCK_TITLE as title,
+ BLOCK_ICON as icon,
+ BLOCK_DESCRIPTION as description,
+} from './constants';
+
+const blockConfig = {
+ title,
+ description,
+ icon: {
+ src: icon,
+ foreground: '#874FB9',
+ },
+ attributes,
+ edit,
+};
+
+registerBlockType( 'woocommerce/product-price', {
+ ...sharedConfig,
+ ...blockConfig,
+} );
diff --git a/assets/js/atomic/blocks/product-elements/price/style.scss b/assets/js/atomic/blocks/product-elements/price/style.scss
new file mode 100644
index 00000000000..c03543c9de8
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/price/style.scss
@@ -0,0 +1,28 @@
+.wc-block-components-product-price {
+ margin-top: 0;
+ margin-bottom: $gap-small;
+ display: block;
+
+ &__regular {
+ margin-right: 0.5em;
+ }
+}
+.is-loading {
+ .wc-block-components-product-price::before {
+ @include placeholder();
+ content: ".";
+ display: inline-block;
+ width: 5em;
+ }
+ /*rtl:begin:ignore*/
+ .wc-block-components-product-price__align-left {
+ text-align: left;
+ }
+ .wc-block-components-product-price__align-center {
+ text-align: center;
+ }
+ .wc-block-components-product-price__align-right {
+ text-align: right;
+ }
+ /*rtl:end:ignore*/
+}
diff --git a/assets/js/atomic/blocks/product-elements/rating/attributes.js b/assets/js/atomic/blocks/product-elements/rating/attributes.js
new file mode 100644
index 00000000000..5bac8820c40
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/rating/attributes.js
@@ -0,0 +1,8 @@
+export const blockAttributes = {
+ productId: {
+ type: 'number',
+ default: 0,
+ },
+};
+
+export default blockAttributes;
diff --git a/assets/js/atomic/blocks/product/rating/block.js b/assets/js/atomic/blocks/product-elements/rating/block.js
similarity index 75%
rename from assets/js/atomic/blocks/product/rating/block.js
rename to assets/js/atomic/blocks/product-elements/rating/block.js
index 56db48fc58f..e0e19433793 100644
--- a/assets/js/atomic/blocks/product/rating/block.js
+++ b/assets/js/atomic/blocks/product-elements/rating/block.js
@@ -8,6 +8,7 @@ import {
useInnerBlockLayoutContext,
useProductDataContext,
} from '@woocommerce/shared-context';
+import { withProductDataContext } from '@woocommerce/shared-hocs';
/**
* Internal dependencies
@@ -19,14 +20,11 @@ import './style.scss';
*
* @param {Object} props Incoming props.
* @param {string} [props.className] CSS Class name for the component.
- * @param {Object} [props.product] Optional product object. Product from context will be used if
- * this is not provided.
* @return {*} The component.
*/
-const Block = ( { className, ...props } ) => {
+const Block = ( { className } ) => {
const { parentClassName } = useInnerBlockLayoutContext();
- const productDataContext = useProductDataContext();
- const product = props.product || productDataContext.product;
+ const { product } = useProductDataContext();
const rating = getAverageRating( product );
if ( ! rating ) {
@@ -48,7 +46,9 @@ const Block = ( { className, ...props } ) => {
className,
'star-rating',
'wc-block-components-product-rating',
- `${ parentClassName }__product-rating`
+ {
+ [ `${ parentClassName }__product-rating` ]: parentClassName,
+ }
) }
>
{
const getAverageRating = ( product ) => {
// eslint-disable-next-line camelcase
- const rating = parseFloat( product?.average_rating || 0 );
+ const rating = parseFloat( product.average_rating );
return Number.isFinite( rating ) && rating > 0 ? rating : 0;
};
Block.propTypes = {
className: PropTypes.string,
- product: PropTypes.object,
};
-export default Block;
+export default withProductDataContext( Block );
diff --git a/assets/js/atomic/blocks/product-elements/rating/constants.js b/assets/js/atomic/blocks/product-elements/rating/constants.js
new file mode 100644
index 00000000000..4e20ec561af
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/rating/constants.js
@@ -0,0 +1,15 @@
+/**
+ * External dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { star, Icon } from '@woocommerce/icons';
+
+export const BLOCK_TITLE = __(
+ 'Product Rating',
+ 'woo-gutenberg-products-block'
+);
+export const BLOCK_ICON =
;
+export const BLOCK_DESCRIPTION = __(
+ 'Display the average rating of a product.',
+ 'woo-gutenberg-products-block'
+);
diff --git a/assets/js/atomic/blocks/product-elements/rating/edit.js b/assets/js/atomic/blocks/product-elements/rating/edit.js
new file mode 100644
index 00000000000..c37381a3186
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/rating/edit.js
@@ -0,0 +1,23 @@
+/**
+ * External dependencies
+ */
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import Block from './block';
+import withProductSelector from '../shared/with-product-selector';
+import { BLOCK_TITLE, BLOCK_ICON } from './constants';
+
+const Edit = ( { attributes } ) => {
+ return
;
+};
+export default withProductSelector( {
+ icon: BLOCK_ICON,
+ label: BLOCK_TITLE,
+ description: __(
+ "Choose a product to display it's rating.",
+ 'woo-gutenberg-products-block'
+ ),
+} )( Edit );
diff --git a/assets/js/atomic/blocks/product-elements/rating/index.js b/assets/js/atomic/blocks/product-elements/rating/index.js
new file mode 100644
index 00000000000..ce926cb1421
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/rating/index.js
@@ -0,0 +1,32 @@
+/**
+ * External dependencies
+ */
+import { registerBlockType } from '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+import sharedConfig from '../shared/config';
+import attributes from './attributes';
+import edit from './edit';
+import {
+ BLOCK_TITLE as title,
+ BLOCK_ICON as icon,
+ BLOCK_DESCRIPTION as description,
+} from './constants';
+
+const blockConfig = {
+ title,
+ description,
+ icon: {
+ src: icon,
+ foreground: '#874FB9',
+ },
+ attributes,
+ edit,
+};
+
+registerBlockType( 'woocommerce/product-rating', {
+ ...sharedConfig,
+ ...blockConfig,
+} );
diff --git a/assets/js/atomic/blocks/product-elements/rating/style.scss b/assets/js/atomic/blocks/product-elements/rating/style.scss
new file mode 100644
index 00000000000..816673c2716
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/rating/style.scss
@@ -0,0 +1,52 @@
+.wc-block-components-product-rating {
+ display: block;
+ margin-top: 0;
+ margin-bottom: $gap-small;
+
+ &__stars {
+ overflow: hidden;
+ position: relative;
+ width: 5.3em;
+ height: 1.618em;
+ line-height: 1.618;
+ font-size: 1em;
+ /* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
+ font-family: star;
+ font-weight: 400;
+ margin: 0 auto;
+ text-align: left;
+
+ &::before {
+ content: "\53\53\53\53\53";
+ top: 0;
+ left: 0;
+ right: 0;
+ position: absolute;
+ opacity: 0.5;
+ color: #aaa;
+ white-space: nowrap;
+ }
+ span {
+ overflow: hidden;
+ top: 0;
+ left: 0;
+ right: 0;
+ position: absolute;
+ padding-top: 1.5em;
+ }
+ span::before {
+ content: "\53\53\53\53\53";
+ top: 0;
+ left: 0;
+ right: 0;
+ position: absolute;
+ color: #000;
+ white-space: nowrap;
+ }
+ }
+}
+.wc-block-single-product {
+ .wc-block-components-product-rating__stars {
+ margin: 0;
+ }
+}
diff --git a/assets/js/atomic/blocks/product-elements/sale-badge/attributes.js b/assets/js/atomic/blocks/product-elements/sale-badge/attributes.js
new file mode 100644
index 00000000000..5bac8820c40
--- /dev/null
+++ b/assets/js/atomic/blocks/product-elements/sale-badge/attributes.js
@@ -0,0 +1,8 @@
+export const blockAttributes = {
+ productId: {
+ type: 'number',
+ default: 0,
+ },
+};
+
+export default blockAttributes;
diff --git a/assets/js/atomic/blocks/product/sale-badge/block.js b/assets/js/atomic/blocks/product-elements/sale-badge/block.js
similarity index 72%
rename from assets/js/atomic/blocks/product/sale-badge/block.js
rename to assets/js/atomic/blocks/product-elements/sale-badge/block.js
index b586bdff490..b12056d56d9 100644
--- a/assets/js/atomic/blocks/product/sale-badge/block.js
+++ b/assets/js/atomic/blocks/product-elements/sale-badge/block.js
@@ -9,6 +9,7 @@ import {
useInnerBlockLayoutContext,
useProductDataContext,
} from '@woocommerce/shared-context';
+import { withProductDataContext } from '@woocommerce/shared-hocs';
/**
* Internal dependencies
@@ -21,16 +22,13 @@ import './style.scss';
* @param {Object} props Incoming props.
* @param {string} [props.className] CSS Class name for the component.
* @param {string} [props.align] Alignment of the badge.
- * @param {Object} [props.product] Optional product object. Product from context will be used if
- * this is not provided.
* @return {*} The component.
*/
-const Block = ( { className, align, ...props } ) => {
+const Block = ( { className, align } ) => {
const { parentClassName } = useInnerBlockLayoutContext();
- const productDataContext = useProductDataContext();
- const product = props.product || productDataContext.product;
+ const { product } = useProductDataContext();
- if ( ! product || ! product.on_sale ) {
+ if ( ! product.id || ! product.on_sale ) {
return null;
}
@@ -45,7 +43,9 @@ const Block = ( { className, align, ...props } ) => {
'wc-block-components-product-sale-badge',
className,
alignClass,
- `${ parentClassName }__product-onsale`
+ {
+ [ `${ parentClassName }__product-onsale` ]: parentClassName,
+ }
) }
>