⚛️ Introduce Product Element Blocks (https://github.com/woocommerce/woocommerce-blocks/pull/2871)
* Product selection when out of context for price and title blocks * Move product element name/description/icons to constant files * Add attributes and hocs to all elements * Standalone block rendering * Add a placeholder if title has no content * Revert "Add a placeholder if title has no content" This reverts commit 29115154b33eedc661ccd3cc758acdbc5041ffbc. * parentClassName is not always present * Loading state * Wrap description in P * Fixed loading styles when nested * Maintain product shape in useProductData * feature gate elements from showing in inserter * fix feature flag * include price PR * edit withProductSelector to be a hoc * fix lint issue Co-authored-by: Seghir Nadir <nadir.seghir@gmail.com>
This commit is contained in:
parent
ff2f135e7e
commit
cd9f7e0ccb
|
@ -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;
|
||||
|
|
|
@ -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'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -3,6 +3,10 @@ export const blockAttributes = {
|
|||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
productId: {
|
||||
type: 'number',
|
||||
default: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export default blockAttributes;
|
|
@ -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 } ) => {
|
|||
<div className={ componentClass }>
|
||||
<>
|
||||
{ showFormElements ? (
|
||||
<AddToCartForm
|
||||
productType={ product.type || 'simple' }
|
||||
/>
|
||||
<AddToCartForm productType={ product.type } />
|
||||
) : (
|
||||
<AddToCartButton />
|
||||
) }
|
||||
|
@ -80,7 +76,6 @@ const AddToCartForm = ( { productType } ) => {
|
|||
|
||||
Block.propTypes = {
|
||||
className: PropTypes.string,
|
||||
product: PropTypes.object,
|
||||
};
|
||||
|
||||
export default Block;
|
||||
export default withProductDataContext( Block );
|
|
@ -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 = <Icon srcElement={ cart } />;
|
||||
export const BLOCK_DESCRIPTION = __(
|
||||
'Displays an add to cart button. Optionally displays other add to cart form elements.',
|
||||
'woo-gutenberg-products-block'
|
||||
);
|
|
@ -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'
|
||||
) }
|
||||
>
|
||||
<EditProductLink productId={ product.id || 0 } />
|
||||
<EditProductLink productId={ product.id } />
|
||||
{ product.type !== 'external' && (
|
||||
<InspectorControls>
|
||||
<PanelBody
|
||||
|
@ -57,3 +58,12 @@ export default ( { attributes, setAttributes } ) => {
|
|||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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 );
|
|
@ -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,
|
||||
} );
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
export const blockAttributes = {
|
||||
productId: {
|
||||
type: 'number',
|
||||
default: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export default blockAttributes;
|
|
@ -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 (
|
||||
<div
|
||||
|
@ -38,10 +36,12 @@ const Block = ( { className, ...props } ) => {
|
|||
className,
|
||||
'wp-block-button',
|
||||
'wc-block-components-product-button',
|
||||
`${ parentClassName }__product-add-to-cart`
|
||||
{
|
||||
[ `${ parentClassName }__product-add-to-cart` ]: parentClassName,
|
||||
}
|
||||
) }
|
||||
>
|
||||
{ product ? (
|
||||
{ product.id ? (
|
||||
<AddToCartButton product={ product } />
|
||||
) : (
|
||||
<AddToCartButtonPlaceholder />
|
||||
|
@ -142,7 +142,6 @@ const AddToCartButtonPlaceholder = () => {
|
|||
|
||||
Block.propTypes = {
|
||||
className: PropTypes.string,
|
||||
product: PropTypes.object,
|
||||
};
|
||||
|
||||
export default Block;
|
||||
export default withProductDataContext( Block );
|
|
@ -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 = <Icon srcElement={ cart } />;
|
||||
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'
|
||||
);
|
|
@ -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 (
|
||||
<Disabled>
|
||||
<Block { ...attributes } />
|
||||
</Disabled>
|
||||
);
|
||||
};
|
||||
|
||||
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 );
|
|
@ -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,
|
||||
} );
|
|
@ -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;
|
|
@ -0,0 +1,8 @@
|
|||
export const blockAttributes = {
|
||||
productId: {
|
||||
type: 'number',
|
||||
default: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export default blockAttributes;
|
|
@ -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 );
|
|
@ -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 = <Icon srcElement={ folder } />;
|
||||
export const BLOCK_DESCRIPTION = __(
|
||||
'Display a list of categories belonging to a product.',
|
||||
'woo-gutenberg-products-block'
|
||||
);
|
|
@ -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 (
|
||||
<>
|
||||
<EditProductLink />
|
||||
<Disabled>
|
||||
<Block { ...attributes } />
|
||||
</Disabled>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default withProductSelector( {
|
||||
icon: BLOCK_ICON,
|
||||
label: BLOCK_TITLE,
|
||||
description: __(
|
||||
"Choose a product to display it's categories.",
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
} )( Edit );
|
|
@ -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,
|
||||
} );
|
|
@ -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);
|
||||
|
|
@ -15,6 +15,10 @@ export const blockAttributes = {
|
|||
type: 'string',
|
||||
default: 'full-size',
|
||||
},
|
||||
productId: {
|
||||
type: 'number',
|
||||
default: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export default blockAttributes;
|
|
@ -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 (
|
||||
<div
|
||||
className={ classnames(
|
||||
className,
|
||||
'wc-block-components-product-image',
|
||||
'wc-block-components-product-image--placeholder',
|
||||
`${ parentClassName }__product-image`
|
||||
{
|
||||
[ `${ parentClassName }__product-image` ]: parentClassName,
|
||||
}
|
||||
) }
|
||||
>
|
||||
<ImagePlaceholder />
|
||||
|
@ -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 (
|
||||
<div
|
||||
className={ classnames(
|
||||
className,
|
||||
'wc-block-components-product-image',
|
||||
`${ parentClassName }__product-image`
|
||||
{
|
||||
[ `${ parentClassName }__product-image` ]: parentClassName,
|
||||
}
|
||||
) }
|
||||
>
|
||||
{ productLink ? (
|
||||
|
@ -103,7 +105,9 @@ const Block = ( {
|
|||
};
|
||||
|
||||
const ImagePlaceholder = () => {
|
||||
return <img src={ PLACEHOLDER_IMG_SRC } alt="" />;
|
||||
return (
|
||||
<img src={ PLACEHOLDER_IMG_SRC } alt="" width={ 500 } height={ 500 } />
|
||||
);
|
||||
};
|
||||
|
||||
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 );
|
|
@ -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 = <Icon srcElement={ image } />;
|
||||
export const BLOCK_DESCRIPTION = __(
|
||||
'Display the main product image',
|
||||
'woo-gutenberg-products-block'
|
||||
);
|
|
@ -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 );
|
|
@ -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,
|
||||
} );
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
|
@ -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 (
|
||||
<div
|
||||
className={ classnames(
|
||||
className,
|
||||
'price',
|
||||
'wc-block-components-product-price',
|
||||
`${ parentClassName }__product-price`
|
||||
{
|
||||
[ `${ parentClassName }__product-price` ]: parentClassName,
|
||||
}
|
||||
) }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
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 } ) => {
|
|||
<span
|
||||
className={ classnames(
|
||||
'wc-block-components-product-price__value',
|
||||
`${ parentClassName }__product-price__value`,
|
||||
{ [ classes ]: isFeaturePluginBuild() }
|
||||
{
|
||||
[ `${ parentClassName }__product-price__value` ]: parentClassName,
|
||||
[ classes ]: isFeaturePluginBuild()
|
||||
}
|
||||
) }
|
||||
style={ isFeaturePluginBuild() ? style : {} }
|
||||
>
|
||||
|
@ -188,8 +192,9 @@ const SalePrice = ( {
|
|||
<del
|
||||
className={ classnames(
|
||||
'wc-block-components-product-price__regular',
|
||||
`${ parentClassName }__product-price__regular`,
|
||||
{ [ classes ]: isFeaturePluginBuild() }
|
||||
{
|
||||
[ `${ parentClassName }__product-price__regular` ]: parentClassName,
|
||||
[ classes ]: isFeaturePluginBuild() }
|
||||
) }
|
||||
style={ isFeaturePluginBuild() ? style : {} }
|
||||
>
|
||||
|
@ -201,8 +206,9 @@ const SalePrice = ( {
|
|||
<span
|
||||
className={ classnames(
|
||||
'wc-block-components-product-price__value',
|
||||
`${ parentClassName }__product-price__value`,
|
||||
{ [ saleClasses ]: isFeaturePluginBuild() }
|
||||
{
|
||||
[ `${ parentClassName }__product-price__value` ]: parentClassName,
|
||||
[ saleClasses ]: isFeaturePluginBuild() }
|
||||
) }
|
||||
style={ isFeaturePluginBuild() ? saleStyle : {} }
|
||||
>
|
||||
|
@ -245,4 +251,4 @@ Block.propTypes = {
|
|||
customSaleColor: PropTypes.string,
|
||||
};
|
||||
|
||||
export default Block;
|
||||
export default withProductDataContext( Block );
|
|
@ -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 = <Icon srcElement={ bill } />;
|
||||
export const BLOCK_DESCRIPTION = __(
|
||||
'Display the price of a product.',
|
||||
'woo-gutenberg-products-block'
|
||||
);
|
|
@ -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;
|
||||
|
|
@ -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,
|
||||
} );
|
|
@ -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*/
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
export const blockAttributes = {
|
||||
productId: {
|
||||
type: 'number',
|
||||
default: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export default blockAttributes;
|
|
@ -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,
|
||||
}
|
||||
) }
|
||||
>
|
||||
<div
|
||||
|
@ -67,14 +67,13 @@ const Block = ( { className, ...props } ) => {
|
|||
|
||||
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 );
|
|
@ -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 = <Icon srcElement={ star } />;
|
||||
export const BLOCK_DESCRIPTION = __(
|
||||
'Display the average rating of a product.',
|
||||
'woo-gutenberg-products-block'
|
||||
);
|
|
@ -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 <Block { ...attributes } />;
|
||||
};
|
||||
export default withProductSelector( {
|
||||
icon: BLOCK_ICON,
|
||||
label: BLOCK_TITLE,
|
||||
description: __(
|
||||
"Choose a product to display it's rating.",
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
} )( Edit );
|
|
@ -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,
|
||||
} );
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
export const blockAttributes = {
|
||||
productId: {
|
||||
type: 'number',
|
||||
default: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export default blockAttributes;
|
|
@ -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,
|
||||
}
|
||||
) }
|
||||
>
|
||||
<Label
|
||||
|
@ -62,7 +62,6 @@ const Block = ( { className, align, ...props } ) => {
|
|||
Block.propTypes = {
|
||||
className: PropTypes.string,
|
||||
align: PropTypes.string,
|
||||
product: PropTypes.object,
|
||||
};
|
||||
|
||||
export default Block;
|
||||
export default withProductDataContext( Block );
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { tag, Icon } from '@woocommerce/icons';
|
||||
|
||||
export const BLOCK_TITLE = __(
|
||||
'On-Sale Badge',
|
||||
'woo-gutenberg-products-block'
|
||||
);
|
||||
export const BLOCK_ICON = <Icon srcElement={ tag } />;
|
||||
export const BLOCK_DESCRIPTION = __(
|
||||
'Displays an on-sale badge if the product is on-sale.',
|
||||
'woo-gutenberg-products-block'
|
||||
);
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* 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 <Block { ...attributes } />;
|
||||
};
|
||||
|
||||
export default withProductSelector( {
|
||||
icon: BLOCK_ICON,
|
||||
label: BLOCK_TITLE,
|
||||
description: __(
|
||||
"Choose a product to display it's sale-badge.",
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
} )( Edit );
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* 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',
|
||||
},
|
||||
supports: {
|
||||
html: false,
|
||||
},
|
||||
attributes,
|
||||
edit,
|
||||
};
|
||||
|
||||
registerBlockType( 'woocommerce/product-sale-badge', {
|
||||
...sharedConfig,
|
||||
...blockConfig,
|
||||
} );
|
|
@ -3,17 +3,17 @@
|
|||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Icon, grid } from '@woocommerce/icons';
|
||||
|
||||
import { isExperimentalBuild } from '@woocommerce/block-settings';
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import save from './save';
|
||||
import save from '../save';
|
||||
|
||||
/**
|
||||
* Holds default config for this collection of blocks.
|
||||
*/
|
||||
export default {
|
||||
category: 'woocommerce',
|
||||
category: 'woocommerce-product-elements',
|
||||
keywords: [ __( 'WooCommerce', 'woo-gutenberg-products-block' ) ],
|
||||
icon: {
|
||||
src: <Icon srcElement={ grid } />,
|
||||
|
@ -22,7 +22,9 @@ export default {
|
|||
supports: {
|
||||
html: false,
|
||||
},
|
||||
parent: [ 'woocommerce/all-products', 'woocommerce/single-product' ],
|
||||
parent: isExperimentalBuild()
|
||||
? null
|
||||
: [ '@woocommerce/all-products', '@woocommerce/single-product' ],
|
||||
save,
|
||||
deprecated: [
|
||||
{
|
|
@ -0,0 +1,11 @@
|
|||
.wc-atomic-blocks-product__selection {
|
||||
width: 100%;
|
||||
}
|
||||
.wc-atomic-blocks-product__edit-card {
|
||||
padding: 16px;
|
||||
border-top: 1px solid #e2e4e7;
|
||||
|
||||
.wc-atomic-blocks-product__edit-card-title {
|
||||
margin: 0 0 $gap;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useState } from '@wordpress/element';
|
||||
import ProductControl from '@woocommerce/block-components/product-control';
|
||||
import { Placeholder, Button, Toolbar } from '@wordpress/components';
|
||||
import { BlockControls } from '@wordpress/block-editor';
|
||||
import TextToolbarButton from '@woocommerce/block-components/text-toolbar-button';
|
||||
import { useProductDataContext } from '@woocommerce/shared-context';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './editor.scss';
|
||||
|
||||
/**
|
||||
* This HOC shows a product selection interface if context is not present in the editor.
|
||||
*
|
||||
* @param {Object} selectorArgs Options for the selector.
|
||||
*
|
||||
*/
|
||||
const withProductSelector = ( selectorArgs ) => ( OriginalComponent ) => {
|
||||
return ( props ) => {
|
||||
const productDataContext = useProductDataContext();
|
||||
const { attributes, setAttributes } = props;
|
||||
const { productId } = attributes;
|
||||
const [ isEditing, setIsEditing ] = useState( ! productId );
|
||||
|
||||
if ( productDataContext.hasContext ) {
|
||||
return <OriginalComponent { ...props } />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{ isEditing ? (
|
||||
<Placeholder
|
||||
icon={ selectorArgs.icon || '' }
|
||||
label={ selectorArgs.label || '' }
|
||||
className="wc-atomic-blocks-product"
|
||||
>
|
||||
{ !! selectorArgs.description && (
|
||||
<div>{ selectorArgs.description }</div>
|
||||
) }
|
||||
<div className="wc-atomic-blocks-product__selection">
|
||||
<ProductControl
|
||||
selected={ productId || 0 }
|
||||
showVariations
|
||||
onChange={ ( value = [] ) => {
|
||||
setAttributes( {
|
||||
productId: value[ 0 ]
|
||||
? value[ 0 ].id
|
||||
: 0,
|
||||
} );
|
||||
} }
|
||||
/>
|
||||
<Button
|
||||
isDefault
|
||||
disabled={ ! productId }
|
||||
onClick={ () => {
|
||||
setIsEditing( false );
|
||||
} }
|
||||
>
|
||||
{ __( 'Done', 'woo-gutenberg-products-block' ) }
|
||||
</Button>
|
||||
</div>
|
||||
</Placeholder>
|
||||
) : (
|
||||
<>
|
||||
<BlockControls>
|
||||
<Toolbar>
|
||||
<TextToolbarButton
|
||||
onClick={ () => setIsEditing( true ) }
|
||||
>
|
||||
{ __(
|
||||
'Switch product…',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</TextToolbarButton>
|
||||
</Toolbar>
|
||||
</BlockControls>
|
||||
<OriginalComponent { ...props } />
|
||||
</>
|
||||
) }
|
||||
</>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export default withProductSelector;
|
|
@ -0,0 +1,8 @@
|
|||
export const blockAttributes = {
|
||||
productId: {
|
||||
type: 'number',
|
||||
default: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export default blockAttributes;
|
|
@ -8,6 +8,7 @@ import {
|
|||
useInnerBlockLayoutContext,
|
||||
useProductDataContext,
|
||||
} from '@woocommerce/shared-context';
|
||||
import { withProductDataContext } from '@woocommerce/shared-hocs';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -19,15 +20,12 @@ 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 sku = product.sku || '';
|
||||
const { product } = useProductDataContext();
|
||||
const sku = product.sku;
|
||||
|
||||
if ( ! sku ) {
|
||||
return null;
|
||||
|
@ -38,7 +36,9 @@ const Block = ( { className, ...props } ) => {
|
|||
className={ classnames(
|
||||
className,
|
||||
'wc-block-components-product-sku',
|
||||
`${ parentClassName }__product-sku`
|
||||
{
|
||||
[ `${ parentClassName }__product-sku` ]: parentClassName,
|
||||
}
|
||||
) }
|
||||
>
|
||||
{ __( 'SKU:', 'woo-gutenberg-products-block' ) }{ ' ' }
|
||||
|
@ -49,7 +49,6 @@ const Block = ( { className, ...props } ) => {
|
|||
|
||||
Block.propTypes = {
|
||||
className: PropTypes.string,
|
||||
product: PropTypes.object,
|
||||
};
|
||||
|
||||
export default Block;
|
||||
export default withProductDataContext( Block );
|
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { barcode, Icon } from '@woocommerce/icons';
|
||||
|
||||
export const BLOCK_TITLE = __( 'Product SKU', 'woo-gutenberg-products-block' );
|
||||
export const BLOCK_ICON = <Icon srcElement={ barcode } />;
|
||||
export const BLOCK_DESCRIPTION = __(
|
||||
'Display the SKU of a product.',
|
||||
'woo-gutenberg-products-block'
|
||||
);
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
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 (
|
||||
<>
|
||||
<EditProductLink />
|
||||
<Block { ...attributes } />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default withProductSelector( {
|
||||
icon: BLOCK_ICON,
|
||||
label: BLOCK_TITLE,
|
||||
description: __(
|
||||
"Choose a product to display it's SKU.",
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
} )( Edit );
|
|
@ -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-sku', {
|
||||
...sharedConfig,
|
||||
...blockConfig,
|
||||
} );
|
|
@ -0,0 +1,7 @@
|
|||
.wc-block-components-product-sku {
|
||||
margin-top: 0;
|
||||
margin-bottom: $gap-small;
|
||||
display: block;
|
||||
text-transform: uppercase;
|
||||
@include font-size(small);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
export const blockAttributes = {
|
||||
productId: {
|
||||
type: 'number',
|
||||
default: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export default blockAttributes;
|
|
@ -8,7 +8,7 @@ import {
|
|||
useInnerBlockLayoutContext,
|
||||
useProductDataContext,
|
||||
} from '@woocommerce/shared-context';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { withProductDataContext } from '@woocommerce/shared-hocs';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -20,16 +20,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 = props.product || productDataContext.product || {};
|
||||
const { product } = useProductDataContext();
|
||||
|
||||
if ( isEmpty( product ) || ! product.is_purchasable ) {
|
||||
if ( ! product.id || ! product.is_purchasable ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -42,8 +39,8 @@ const Block = ( { className, ...props } ) => {
|
|||
className={ classnames(
|
||||
className,
|
||||
'wc-block-components-product-stock-indicator',
|
||||
`${ parentClassName }__stock-indicator`,
|
||||
{
|
||||
[ `${ parentClassName }__stock-indicator` ]: parentClassName,
|
||||
'wc-block-components-product-stock-indicator--in-stock': inStock,
|
||||
'wc-block-components-product-stock-indicator--out-of-stock': ! inStock,
|
||||
'wc-block-components-product-stock-indicator--low-stock': !! lowStock,
|
||||
|
@ -78,7 +75,6 @@ const stockText = ( inStock, isBackordered ) => {
|
|||
|
||||
Block.propTypes = {
|
||||
className: PropTypes.string,
|
||||
product: PropTypes.object,
|
||||
};
|
||||
|
||||
export default Block;
|
||||
export default withProductDataContext( Block );
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { box, Icon } from '@woocommerce/icons';
|
||||
|
||||
export const BLOCK_TITLE = __(
|
||||
'Product Stock Indicator',
|
||||
'woo-gutenberg-products-block'
|
||||
);
|
||||
export const BLOCK_ICON = <Icon srcElement={ box } />;
|
||||
export const BLOCK_DESCRIPTION = __(
|
||||
'Display product stock status.',
|
||||
'woo-gutenberg-products-block'
|
||||
);
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
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 (
|
||||
<>
|
||||
<EditProductLink />
|
||||
<Block { ...attributes } />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default withProductSelector( {
|
||||
icon: BLOCK_ICON,
|
||||
label: BLOCK_TITLE,
|
||||
description: __(
|
||||
"Choose a product to display it's stock.",
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
} )( Edit );
|
|
@ -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-stock-indicator', {
|
||||
...sharedConfig,
|
||||
...blockConfig,
|
||||
} );
|
|
@ -0,0 +1,17 @@
|
|||
.wc-block-components-product-stock-indicator {
|
||||
margin-top: 0;
|
||||
margin-bottom: em($gap-small);
|
||||
display: block;
|
||||
@include font-size(small);
|
||||
|
||||
&--in-stock {
|
||||
color: $valid-green;
|
||||
}
|
||||
&--out-of-stock {
|
||||
color: $error-red;
|
||||
}
|
||||
&--low-stock,
|
||||
&--available-on-backorder {
|
||||
color: $notice-yellow;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
export const blockAttributes = {
|
||||
productId: {
|
||||
type: 'number',
|
||||
default: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export default blockAttributes;
|
|
@ -9,6 +9,7 @@ import {
|
|||
useInnerBlockLayoutContext,
|
||||
useProductDataContext,
|
||||
} from '@woocommerce/shared-context';
|
||||
import { withProductDataContext } from '@woocommerce/shared-hocs';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -20,21 +21,21 @@ 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 ( ! product ) {
|
||||
return (
|
||||
<div
|
||||
className={ classnames(
|
||||
className,
|
||||
`wc-block-components-product-summary`
|
||||
`wc-block-components-product-summary`,
|
||||
{
|
||||
[ `${ parentClassName }__product-summary` ]: parentClassName,
|
||||
}
|
||||
) }
|
||||
/>
|
||||
);
|
||||
|
@ -55,7 +56,9 @@ const Block = ( { className, ...props } ) => {
|
|||
className={ classnames(
|
||||
className,
|
||||
`wc-block-components-product-summary`,
|
||||
`${ parentClassName }__product-summary`
|
||||
{
|
||||
[ `${ parentClassName }__product-summary` ]: parentClassName,
|
||||
}
|
||||
) }
|
||||
source={ source }
|
||||
maxLength={ 150 }
|
||||
|
@ -66,7 +69,6 @@ const Block = ( { className, ...props } ) => {
|
|||
|
||||
Block.propTypes = {
|
||||
className: PropTypes.string,
|
||||
product: PropTypes.object,
|
||||
};
|
||||
|
||||
export default Block;
|
||||
export default withProductDataContext( Block );
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { notes, Icon } from '@woocommerce/icons';
|
||||
|
||||
export const BLOCK_TITLE = __(
|
||||
'Product Summary',
|
||||
'woo-gutenberg-products-block'
|
||||
);
|
||||
export const BLOCK_ICON = <Icon srcElement={ notes } />;
|
||||
export const BLOCK_DESCRIPTION = __(
|
||||
'Display a short description about a product.',
|
||||
'woo-gutenberg-products-block'
|
||||
);
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* 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 <Block { ...attributes } />;
|
||||
};
|
||||
|
||||
export default withProductSelector( {
|
||||
icon: BLOCK_ICON,
|
||||
label: BLOCK_TITLE,
|
||||
description: __(
|
||||
"Choose a product to display it's short description.",
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
} )( Edit );
|
|
@ -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-summary', {
|
||||
...sharedConfig,
|
||||
...blockConfig,
|
||||
} );
|
|
@ -1,9 +1,8 @@
|
|||
.wc-block-layout .wc-block-components-product-summary {
|
||||
.wc-block-components-product-summary {
|
||||
margin-top: 0;
|
||||
margin-bottom: $gap-small;
|
||||
}
|
||||
|
||||
.wc-block-layout--is-loading .wc-block-components-product-summary::before {
|
||||
.is-loading .wc-block-components-product-summary::before {
|
||||
@include placeholder();
|
||||
content: ".";
|
||||
display: block;
|
|
@ -0,0 +1,8 @@
|
|||
export const blockAttributes = {
|
||||
productId: {
|
||||
type: 'number',
|
||||
default: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export default blockAttributes;
|
|
@ -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.tags ) ) {
|
||||
if ( isEmpty( product.tags ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -38,7 +36,9 @@ const Block = ( { className, ...props } ) => {
|
|||
className={ classnames(
|
||||
className,
|
||||
'wc-block-components-product-tag-list',
|
||||
`${ parentClassName }__product-tag-list`
|
||||
{
|
||||
[ `${ parentClassName }__product-tag-list` ]: parentClassName,
|
||||
}
|
||||
) }
|
||||
>
|
||||
{ __( 'Tags:', '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 );
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { tag, Icon } from '@woocommerce/icons';
|
||||
|
||||
export const BLOCK_TITLE = __(
|
||||
'Product Tag List',
|
||||
'woo-gutenberg-products-block'
|
||||
);
|
||||
export const BLOCK_ICON = <Icon srcElement={ tag } />;
|
||||
export const BLOCK_DESCRIPTION = __(
|
||||
'Display a list of tags belonging to a product.',
|
||||
'woo-gutenberg-products-block'
|
||||
);
|
|
@ -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 (
|
||||
<>
|
||||
<EditProductLink />
|
||||
<Disabled>
|
||||
<Block { ...attributes } />
|
||||
</Disabled>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default withProductSelector( {
|
||||
icon: BLOCK_ICON,
|
||||
label: BLOCK_TITLE,
|
||||
description: __(
|
||||
"Choose a product to display it's tags.",
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
} )( Edit );
|
|
@ -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-tag-list', {
|
||||
...sharedConfig,
|
||||
...blockConfig,
|
||||
} );
|
|
@ -1,4 +1,4 @@
|
|||
.wc-block-layout .wc-block-components-product-category-list {
|
||||
.wc-block-components-product-tag-list {
|
||||
margin-top: 0;
|
||||
margin-bottom: em($gap-small);
|
||||
|
|
@ -12,6 +12,10 @@ let blockAttributes = {
|
|||
type: 'boolean',
|
||||
default: true,
|
||||
},
|
||||
productId: {
|
||||
type: 'number',
|
||||
default: 0,
|
||||
},
|
||||
};
|
||||
|
||||
if ( isFeaturePluginBuild() ) {
|
|
@ -11,6 +11,8 @@ import {
|
|||
import { getColorClassName, getFontSizeClass } from '@wordpress/block-editor';
|
||||
import { isFeaturePluginBuild } from '@woocommerce/block-settings';
|
||||
import { gatedStyledText } from '@woocommerce/atomic-utils';
|
||||
import { withProductDataContext } from '@woocommerce/shared-hocs';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
|
@ -41,11 +43,9 @@ export const Block = ( {
|
|||
customColor,
|
||||
fontSize,
|
||||
customFontSize,
|
||||
...props
|
||||
} ) => {
|
||||
const { parentClassName } = useInnerBlockLayoutContext();
|
||||
const productDataContext = useProductDataContext();
|
||||
const product = props.product || productDataContext.product;
|
||||
const { product } = useProductDataContext();
|
||||
const TagName = `h${ headingLevel }`;
|
||||
|
||||
const colorClass = getColorClassName( 'color', color );
|
||||
|
@ -58,19 +58,20 @@ export const Block = ( {
|
|||
[ fontSizeClass ]: fontSizeClass,
|
||||
} );
|
||||
|
||||
if ( ! product ) {
|
||||
if ( ! product.id ) {
|
||||
return (
|
||||
<TagName
|
||||
// @ts-ignore
|
||||
className={ classnames(
|
||||
className,
|
||||
'wc-block-components-product-title',
|
||||
`${ parentClassName }__product-title`,
|
||||
{
|
||||
[ `${ parentClassName }__product-title` ]: parentClassName,
|
||||
[ `wc-block-components-product-title--align-${ align }` ]:
|
||||
align && isFeaturePluginBuild(),
|
||||
[ titleClasses ]: isFeaturePluginBuild()
|
||||
},
|
||||
{ [ titleClasses ]: isFeaturePluginBuild() }
|
||||
|
||||
) }
|
||||
style={ gatedStyledText( {
|
||||
color: customColor,
|
||||
|
@ -88,11 +89,11 @@ export const Block = ( {
|
|||
className={ classnames(
|
||||
className,
|
||||
'wc-block-components-product-title',
|
||||
`${ parentClassName }__product-title`,
|
||||
{
|
||||
[ `${ parentClassName }__product-title` ]: parentClassName,
|
||||
[ `wc-block-components-product-title__align-${ align }` ]:
|
||||
align && isFeaturePluginBuild(),
|
||||
}
|
||||
},
|
||||
) }
|
||||
>
|
||||
{ productLink ? (
|
||||
|
@ -118,7 +119,6 @@ export const Block = ( {
|
|||
|
||||
Block.propTypes = {
|
||||
className: PropTypes.string,
|
||||
product: PropTypes.object,
|
||||
headingLevel: PropTypes.number,
|
||||
productLink: PropTypes.bool,
|
||||
align: PropTypes.string,
|
||||
|
@ -128,4 +128,4 @@ Block.propTypes = {
|
|||
customFontSize: PropTypes.number,
|
||||
};
|
||||
|
||||
export default Block;
|
||||
export default withProductDataContext( Block );
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { bookmark, Icon } from '@woocommerce/icons';
|
||||
|
||||
export const BLOCK_TITLE = __(
|
||||
'Product Title',
|
||||
'woo-gutenberg-products-block'
|
||||
);
|
||||
export const BLOCK_ICON = <Icon srcElement={ bookmark } />;
|
||||
export const BLOCK_DESCRIPTION = __(
|
||||
'Display the title of a product.',
|
||||
'woo-gutenberg-products-block'
|
||||
);
|
|
@ -20,6 +20,8 @@ import HeadingToolbar from '@woocommerce/block-components/heading-toolbar';
|
|||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
import withProductSelector from '../shared/with-product-selector';
|
||||
import { BLOCK_TITLE, BLOCK_ICON } from './constants';
|
||||
|
||||
const TitleEdit = ( {
|
||||
color,
|
||||
|
@ -115,6 +117,14 @@ const Title = isFeaturePluginBuild()
|
|||
? compose( [
|
||||
withFontSizes( 'fontSize' ),
|
||||
withColors( 'color', { textColor: 'color' } ),
|
||||
withProductSelector( {
|
||||
icon: BLOCK_ICON,
|
||||
label: BLOCK_TITLE,
|
||||
description: __(
|
||||
"Choose a product to display it's title.",
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
} ),
|
||||
] )( TitleEdit )
|
||||
: TitleEdit;
|
||||
|
|
@ -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-title', {
|
||||
...sharedConfig,
|
||||
...blockConfig,
|
||||
} );
|
|
@ -0,0 +1,35 @@
|
|||
.wc-block-components-product-title {
|
||||
margin-top: 0;
|
||||
margin-bottom: $gap-small;
|
||||
}
|
||||
.wc-block-grid .wc-block-components-product-title {
|
||||
line-height: 1.5;
|
||||
font-weight: 700;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
display: block;
|
||||
}
|
||||
.is-loading {
|
||||
.wc-block-components-product-title::before {
|
||||
@include placeholder();
|
||||
content: ".";
|
||||
display: inline-block;
|
||||
width: 7em;
|
||||
}
|
||||
.wc-block-grid .wc-block-components-product-title::before {
|
||||
width: 10em;
|
||||
}
|
||||
}
|
||||
|
||||
/*rtl:begin:ignore*/
|
||||
.wc-block-components-product-title--align-left {
|
||||
text-align: left;
|
||||
}
|
||||
.wc-block-components-product-title--align-center {
|
||||
text-align: center;
|
||||
}
|
||||
.wc-block-components-product-title--align-right {
|
||||
text-align: right;
|
||||
}
|
||||
/*rtl:end:ignore*/
|
|
@ -1,32 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { registerExperimentalBlockType } from '@woocommerce/block-settings';
|
||||
import { Icon, cart } from '@woocommerce/icons';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import sharedConfig from '../shared-config';
|
||||
import edit from './edit';
|
||||
import attributes from './attributes';
|
||||
|
||||
const blockConfig = {
|
||||
title: __( 'Add to Cart', 'woo-gutenberg-products-block' ),
|
||||
description: __(
|
||||
'Displays an add to cart button. Optionally displays other add to cart form elements.',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
icon: {
|
||||
src: <Icon srcElement={ cart } />,
|
||||
foreground: '#96588a',
|
||||
},
|
||||
edit,
|
||||
attributes,
|
||||
};
|
||||
|
||||
registerExperimentalBlockType( 'woocommerce/product-add-to-cart', {
|
||||
...sharedConfig,
|
||||
...blockConfig,
|
||||
} );
|
|
@ -1,45 +0,0 @@
|
|||
.wc-block-layout {
|
||||
.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;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Disabled } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
|
||||
export default ( { attributes } ) => {
|
||||
return (
|
||||
<Disabled>
|
||||
<Block { ...attributes } />
|
||||
</Disabled>
|
||||
);
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue