diff --git a/assets/js/atomic/components/product/button/index.js b/assets/js/atomic/components/product/button/index.js index 01c458db991..2ab3d668264 100644 --- a/assets/js/atomic/components/product/button/index.js +++ b/assets/js/atomic/components/product/button/index.js @@ -6,7 +6,7 @@ import classnames from 'classnames'; import { _n, sprintf } from '@wordpress/i18n'; import { useEffect, useRef } from '@wordpress/element'; import { useStoreAddToCart } from '@woocommerce/base-hooks'; -import { useProductLayoutContext } from '@woocommerce/base-context'; +import { useInnerBlockConfigurationContext } from '@woocommerce/shared-context'; import { decodeEntities } from '@wordpress/html-entities'; import { triggerFragmentRefresh } from '@woocommerce/base-utils'; @@ -25,7 +25,7 @@ const ProductButton = ( { product, className } ) => { cartIsLoading, addToCart, } = useStoreAddToCart( id ); - const { layoutStyleClassPrefix } = useProductLayoutContext(); + const { layoutStyleClassPrefix } = useInnerBlockConfigurationContext(); const addedToCart = Number.isFinite( cartQuantity ) && cartQuantity > 0; const getButtonText = () => { if ( addedToCart ) { diff --git a/assets/js/atomic/components/product/image/index.js b/assets/js/atomic/components/product/image/index.js index 81e11effdcf..68dfa08e17b 100644 --- a/assets/js/atomic/components/product/image/index.js +++ b/assets/js/atomic/components/product/image/index.js @@ -5,7 +5,7 @@ import PropTypes from 'prop-types'; import { Fragment, useState } from '@wordpress/element'; import classnames from 'classnames'; import { PLACEHOLDER_IMG_SRC } from '@woocommerce/block-settings'; -import { useProductLayoutContext } from '@woocommerce/base-context'; +import { useInnerBlockConfigurationContext } from '@woocommerce/shared-context'; /** * Internal dependencies @@ -56,7 +56,7 @@ const ProductImage = ( { saleBadgeAlign = 'right', } ) => { const [ imageLoaded, setImageLoaded ] = useState( false ); - const { layoutStyleClassPrefix } = useProductLayoutContext(); + const { layoutStyleClassPrefix } = useInnerBlockConfigurationContext(); const image = product.images && product.images.length ? product.images[ 0 ] : null; diff --git a/assets/js/atomic/components/product/price/index.js b/assets/js/atomic/components/product/price/index.js index f406b4d841b..be5c7b0f099 100644 --- a/assets/js/atomic/components/product/price/index.js +++ b/assets/js/atomic/components/product/price/index.js @@ -2,12 +2,12 @@ * External dependencies */ import classnames from 'classnames'; -import { useProductLayoutContext } from '@woocommerce/base-context'; +import { useInnerBlockConfigurationContext } from '@woocommerce/shared-context'; import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount'; import { getCurrencyFromPriceResponse } from '@woocommerce/base-utils'; const ProductPrice = ( { className, product } ) => { - const { layoutStyleClassPrefix } = useProductLayoutContext(); + const { layoutStyleClassPrefix } = useInnerBlockConfigurationContext(); const prices = product.prices || {}; const currency = getCurrencyFromPriceResponse( prices ); diff --git a/assets/js/atomic/components/product/rating/index.js b/assets/js/atomic/components/product/rating/index.js index b87c3c20e15..449a3f10c13 100644 --- a/assets/js/atomic/components/product/rating/index.js +++ b/assets/js/atomic/components/product/rating/index.js @@ -4,11 +4,11 @@ import PropTypes from 'prop-types'; import { __, sprintf } from '@wordpress/i18n'; import classnames from 'classnames'; -import { useProductLayoutContext } from '@woocommerce/base-context'; +import { useInnerBlockConfigurationContext } from '@woocommerce/shared-context'; const ProductRating = ( { className, product } ) => { const rating = parseFloat( product.average_rating ); - const { layoutStyleClassPrefix } = useProductLayoutContext(); + const { layoutStyleClassPrefix } = useInnerBlockConfigurationContext(); if ( ! Number.isFinite( rating ) || rating === 0 ) { return null; diff --git a/assets/js/atomic/components/product/sale-badge/index.js b/assets/js/atomic/components/product/sale-badge/index.js index 2a35c2a5c44..f5d5b8a7dca 100644 --- a/assets/js/atomic/components/product/sale-badge/index.js +++ b/assets/js/atomic/components/product/sale-badge/index.js @@ -3,11 +3,11 @@ */ import { __ } from '@wordpress/i18n'; import classnames from 'classnames'; -import { useProductLayoutContext } from '@woocommerce/base-context'; +import { useInnerBlockConfigurationContext } from '@woocommerce/shared-context'; import Label from '@woocommerce/base-components/label'; const ProductSaleBadge = ( { className, product, align } ) => { - const { layoutStyleClassPrefix } = useProductLayoutContext(); + const { layoutStyleClassPrefix } = useInnerBlockConfigurationContext(); const alignClass = typeof align === 'string' ? `${ layoutStyleClassPrefix }__product-onsale--align${ align }` diff --git a/assets/js/atomic/components/product/summary/index.js b/assets/js/atomic/components/product/summary/index.js index b883c7ef069..4b62a2a7901 100644 --- a/assets/js/atomic/components/product/summary/index.js +++ b/assets/js/atomic/components/product/summary/index.js @@ -3,12 +3,12 @@ */ import PropTypes from 'prop-types'; import classnames from 'classnames'; -import { useProductLayoutContext } from '@woocommerce/base-context'; +import { useInnerBlockConfigurationContext } from '@woocommerce/shared-context'; import Summary from '@woocommerce/base-components/summary'; import { getSetting } from '@woocommerce/settings'; const ProductSummary = ( { className, product } ) => { - const { layoutStyleClassPrefix } = useProductLayoutContext(); + const { layoutStyleClassPrefix } = useInnerBlockConfigurationContext(); const source = product.short_description ? product.short_description : product.description; diff --git a/assets/js/atomic/components/product/title/index.js b/assets/js/atomic/components/product/title/index.js index 2e5b27795a9..26ed9f10aac 100644 --- a/assets/js/atomic/components/product/title/index.js +++ b/assets/js/atomic/components/product/title/index.js @@ -3,7 +3,7 @@ */ import PropTypes from 'prop-types'; import classnames from 'classnames'; -import { useProductLayoutContext } from '@woocommerce/base-context'; +import { useInnerBlockConfigurationContext } from '@woocommerce/shared-context'; import { decodeEntities } from '@wordpress/html-entities'; const ProductTitle = ( { @@ -12,7 +12,7 @@ const ProductTitle = ( { headingLevel = 2, productLink = true, } ) => { - const { layoutStyleClassPrefix } = useProductLayoutContext(); + const { layoutStyleClassPrefix } = useInnerBlockConfigurationContext(); if ( ! product.name ) { return null; } diff --git a/assets/js/base/components/product-list-item/index.js b/assets/js/base/components/product-list-item/index.js index fc91d3a77bc..88e40d23c2c 100644 --- a/assets/js/base/components/product-list-item/index.js +++ b/assets/js/base/components/product-list-item/index.js @@ -3,10 +3,7 @@ */ import PropTypes from 'prop-types'; import classnames from 'classnames'; -import { - useInnerBlockConfigurationContext, - useProductLayoutContext, -} from '@woocommerce/base-context'; +import { useInnerBlockConfigurationContext } from '@woocommerce/shared-context'; import { withInstanceId } from '@woocommerce/base-hocs/with-instance-id'; /** @@ -16,8 +13,10 @@ import { renderProductLayout } from './utils'; const ProductListItem = ( { product, attributes, instanceId } ) => { const { layoutConfig } = attributes; - const { parentName } = useInnerBlockConfigurationContext(); - const { layoutStyleClassPrefix } = useProductLayoutContext(); + const { + layoutStyleClassPrefix, + parentName, + } = useInnerBlockConfigurationContext(); const isLoading = Object.keys( product ).length === 0; const classes = classnames( `${ layoutStyleClassPrefix }__product`, { 'is-loading': isLoading, diff --git a/assets/js/base/components/product-list/index.js b/assets/js/base/components/product-list/index.js index d866bd05a7b..e9444e835d7 100644 --- a/assets/js/base/components/product-list/index.js +++ b/assets/js/base/components/product-list/index.js @@ -16,7 +16,7 @@ import { useQueryStateByKey, } from '@woocommerce/base-hooks'; import withScrollToTop from '@woocommerce/base-hocs/with-scroll-to-top'; -import { useProductLayoutContext } from '@woocommerce/base-context'; +import { useInnerBlockConfigurationContext } from '@woocommerce/shared-context'; import { speak } from '@wordpress/a11y'; /** @@ -114,7 +114,7 @@ const ProductList = ( { const { products, totalProducts, productsLoading } = useStoreProducts( queryState ); - const { layoutStyleClassPrefix } = useProductLayoutContext(); + const { layoutStyleClassPrefix } = useInnerBlockConfigurationContext(); const totalQuery = extractPaginationAndSortAttributes( queryState ); // These are possible filters. diff --git a/assets/js/base/components/product-list/no-matching-products.js b/assets/js/base/components/product-list/no-matching-products.js index 9170ec4d5e2..982e93a12a2 100644 --- a/assets/js/base/components/product-list/no-matching-products.js +++ b/assets/js/base/components/product-list/no-matching-products.js @@ -2,11 +2,11 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import { useProductLayoutContext } from '@woocommerce/base-context'; +import { useInnerBlockConfigurationContext } from '@woocommerce/shared-context'; import { Icon, search } from '@woocommerce/icons'; const NoMatchingProducts = ( { resetCallback = () => {} } ) => { - const { layoutStyleClassPrefix } = useProductLayoutContext(); + const { layoutStyleClassPrefix } = useInnerBlockConfigurationContext(); return (
{ - const { layoutStyleClassPrefix } = useProductLayoutContext(); + const { layoutStyleClassPrefix } = useInnerBlockConfigurationContext(); return (
- useContext( InnerBlockConfigurationContext ); -export const InnerBlockConfigurationProvider = ( { value, children } ) => { - useEffect( () => { - assertValidContextValue( - 'InnerBlockConfigurationProvider', - validationMap, - value - ); - }, [ value ] ); - return ( - - { children } - - ); -}; diff --git a/assets/js/base/context/product-layout-context.js b/assets/js/base/context/product-layout-context.js deleted file mode 100644 index 6716f6f01ea..00000000000 --- a/assets/js/base/context/product-layout-context.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * External dependencies - */ -import { createContext, useContext, useEffect } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import { assertValidContextValue } from './utils'; - -const validationMap = { - layoutStyleClassPrefix: { - required: true, - type: 'string', - }, -}; - -/** - * ProductLayoutContext is a configuration object for layout options shared - * among all components in a tree. - * - * @member {Object} ProductLayoutContext A react context object - */ -const ProductLayoutContext = createContext( { - layoutStyleClassPrefix: '', -} ); - -export const useProductLayoutContext = () => useContext( ProductLayoutContext ); -export const ProductLayoutContextProvider = ( { value, children } ) => { - useEffect( () => { - assertValidContextValue( - 'ProductLayoutContextProvider', - validationMap, - value - ); - }, [ value ] ); - return ( - - { children } - - ); -}; diff --git a/assets/js/blocks/products/all-products/block.js b/assets/js/blocks/products/all-products/block.js index 55383d581d3..7949f9d8995 100644 --- a/assets/js/blocks/products/all-products/block.js +++ b/assets/js/blocks/products/all-products/block.js @@ -4,18 +4,9 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; import ProductListContainer from '@woocommerce/base-components/product-list/container'; -import { - InnerBlockConfigurationProvider, - ProductLayoutContextProvider, -} from '@woocommerce/base-context'; +import { InnerBlockConfigurationProvider } from '@woocommerce/shared-context'; import { gridBlockPreview } from '@woocommerce/resource-previews'; -const layoutContextConfig = { - layoutStyleClassPrefix: 'wc-block-grid', -}; - -const parentBlockConfig = { parentName: 'woocommerce/all-products' }; - /** * The All Products Block. */ @@ -41,13 +32,14 @@ class Block extends Component { * wc-block-{$this->block_name}, */ return ( - - - - + + ); } diff --git a/assets/js/blocks/products/all-products/edit.js b/assets/js/blocks/products/all-products/edit.js index 1656fdbdbf6..d6f76943527 100644 --- a/assets/js/blocks/products/all-products/edit.js +++ b/assets/js/blocks/products/all-products/edit.js @@ -25,10 +25,7 @@ import PropTypes from 'prop-types'; import { Icon, grid } from '@woocommerce/icons'; import GridLayoutControl from '@woocommerce/block-components/grid-layout-control'; import { HAS_PRODUCTS } from '@woocommerce/block-settings'; -import { - InnerBlockConfigurationProvider, - ProductLayoutContextProvider, -} from '@woocommerce/base-context'; +import { InnerBlockConfigurationProvider } from '@woocommerce/shared-context'; /** * Internal dependencies @@ -46,12 +43,6 @@ import { import { getSharedContentControls, getSharedListControls } from '../edit'; import Block from './block'; -const layoutContextConfig = { - layoutStyleClassPrefix: 'wc-block-grid', -}; - -const parentBlockConfig = { parentName: 'woocommerce/all-products' }; - /** * Component to handle edit mode of "All Products". */ @@ -281,21 +272,22 @@ class Editor extends Component { } return ( - - -
- { this.getBlockControls() } - { this.getInspectorControls() } - { isEditing - ? this.renderEditMode() - : this.renderViewMode() } -
-
+ +
+ { this.getBlockControls() } + { this.getInspectorControls() } + { isEditing + ? this.renderEditMode() + : this.renderViewMode() } +
); }; diff --git a/assets/js/shared/context/index.js b/assets/js/shared/context/index.js new file mode 100644 index 00000000000..817d357bb3d --- /dev/null +++ b/assets/js/shared/context/index.js @@ -0,0 +1,2 @@ +export * from './inner-block-configuration-context'; +export * from './product-data-context'; diff --git a/assets/js/shared/context/inner-block-configuration-context.js b/assets/js/shared/context/inner-block-configuration-context.js new file mode 100644 index 00000000000..38282f834c9 --- /dev/null +++ b/assets/js/shared/context/inner-block-configuration-context.js @@ -0,0 +1,43 @@ +/** + * External dependencies + */ +import PropTypes from 'prop-types'; +import { createContext, useContext } from '@wordpress/element'; + +/** + * This context is a configuration object used for connecting + * all children blocks in a given tree contained in the context with information + * about the parent block. Typically this is used for extensibility features. + * + * @member {Object} InnerBlockConfigurationContext A react context object + */ +const InnerBlockConfigurationContext = createContext( { + parentName: '', + layoutStyleClassPrefix: '', +} ); + +export const useInnerBlockConfigurationContext = () => + useContext( InnerBlockConfigurationContext ); + +export const InnerBlockConfigurationProvider = ( { + parentName = '', + layoutStyleClassPrefix = '', + children, +} ) => { + const contextValue = { + parentName, + layoutStyleClassPrefix, + }; + + return ( + + { children } + + ); +}; + +InnerBlockConfigurationProvider.propTypes = { + children: PropTypes.node, + parentName: PropTypes.string, + layoutStyleClassPrefix: PropTypes.string, +}; diff --git a/assets/js/shared/context/product-data-context.js b/assets/js/shared/context/product-data-context.js new file mode 100644 index 00000000000..d5c051f7193 --- /dev/null +++ b/assets/js/shared/context/product-data-context.js @@ -0,0 +1,34 @@ +/** + * External dependencies + */ +import PropTypes from 'prop-types'; +import { createContext, useContext } from '@wordpress/element'; + +/** + * This context is used to pass product data down to all children blocks in a given tree. + * + * @member {Object} ProductDataContext A react context object + */ +const ProductDataContext = createContext( { + product: null, +} ); + +export const useProductDataContextContext = () => + useContext( ProductDataContext ); + +export const ProductDataContextProvider = ( { product = null, children } ) => { + const contextValue = { + product, + }; + + return ( + + { children } + + ); +}; + +ProductDataContextProvider.propTypes = { + children: PropTypes.node, + product: PropTypes.object, +}; diff --git a/bin/webpack-helpers.js b/bin/webpack-helpers.js index 117ce56f11a..cc2562ab226 100644 --- a/bin/webpack-helpers.js +++ b/bin/webpack-helpers.js @@ -26,6 +26,7 @@ const requestToExternal = ( request ) => { '@woocommerce/blocks-registry': [ 'wc', 'wcBlocksRegistry' ], '@woocommerce/settings': [ 'wc', 'wcSettings' ], '@woocommerce/block-data': [ 'wc', 'wcBlocksData' ], + '@woocommerce/shared-context': [ 'wc', 'wcSharedContext' ], }; if ( wcDepMap[ request ] ) { return wcDepMap[ request ]; @@ -38,6 +39,7 @@ const requestToHandle = ( request ) => { '@woocommerce/settings': 'wc-settings', '@woocommerce/block-settings': 'wc-settings', '@woocommerce/block-data': 'wc-blocks-data-store', + '@woocommerce/shared-context': 'wc-shared-context', }; if ( wcHandleMap[ request ] ) { return wcHandleMap[ request ]; diff --git a/src/Assets.php b/src/Assets.php index f36b30900c3..1c58400e122 100644 --- a/src/Assets.php +++ b/src/Assets.php @@ -50,6 +50,7 @@ public static function register_assets() { self::register_script( 'wc-blocks', plugins_url( self::get_block_asset_build_path( 'blocks' ), __DIR__ ), [], false ); self::register_script( 'wc-vendors', plugins_url( self::get_block_asset_build_path( 'vendors' ), __DIR__ ), [], false ); self::register_script( 'wc-blocks-registry', plugins_url( 'build/wc-blocks-registry.js', __DIR__ ), [], false ); + self::register_script( 'wc-shared-context', plugins_url( 'build/wc-shared-context.js', __DIR__ ), [], false ); // Inline data. wp_add_inline_script( diff --git a/tests/js/jest.config.json b/tests/js/jest.config.json index f185459d1d4..398fa67c6c7 100644 --- a/tests/js/jest.config.json +++ b/tests/js/jest.config.json @@ -20,7 +20,8 @@ "@woocommerce/base-hooks(.*)$": "assets/js/base/hooks/$1", "@woocommerce/base-utils(.*)$": "assets/js/base/utils", "@woocommerce/block-data": "assets/js/data", - "@woocommerce/resource-previews": "assets/js/previews" + "@woocommerce/resource-previews": "assets/js/previews", + "@woocommerce/shared-context": "assets/js/shared/context" }, "setupFiles": [ "/node_modules/@wordpress/jest-preset-default/scripts/setup-globals.js", diff --git a/webpack.config.js b/webpack.config.js index 396f4acc2d2..caf41b286fa 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -46,6 +46,7 @@ const CoreConfig = { wcSettings: './assets/js/settings/shared/index.js', wcBlocksData: './assets/js/data/index.js', wcBlocksMiddleware: './assets/js/middleware/index.js', + wcSharedContext: './assets/js/shared/context/index.js', }, output: { filename: ( chunkData ) => {