diff --git a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/stock-indicator/attributes.ts b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/stock-indicator/attributes.ts index b2cae177672..a92f80b861d 100644 --- a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/stock-indicator/attributes.ts +++ b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/stock-indicator/attributes.ts @@ -1,15 +1,23 @@ -interface BlockAttributes { - productId: { - type: string; - default: number; - }; -} +/** + * Internal dependencies + */ +import { BlockAttributes } from './types'; -export const blockAttributes: BlockAttributes = { +export const blockAttributes: Record< + keyof BlockAttributes, + { + type: string; + default: unknown; + } +> = { productId: { type: 'number', default: 0, }, + isDescendentOfQueryLoop: { + type: 'boolean', + default: false, + }, }; export default blockAttributes; diff --git a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/stock-indicator/edit.tsx b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/stock-indicator/edit.tsx index a27e2d88b2e..22f2ca99582 100644 --- a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/stock-indicator/edit.tsx +++ b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/stock-indicator/edit.tsx @@ -3,6 +3,9 @@ */ import EditProductLink from '@woocommerce/editor-components/edit-product-link'; import { useBlockProps } from '@wordpress/block-editor'; +import type { BlockEditProps } from '@wordpress/blocks'; +import { ProductQueryContext as Context } from '@woocommerce/blocks/product-query/types'; +import { useEffect } from 'react'; /** * Internal dependencies @@ -16,16 +19,28 @@ import { } from './constants'; import type { BlockAttributes } from './types'; -interface Props { - attributes: BlockAttributes; -} - -const Edit = ( { attributes }: Props ): JSX.Element => { +const Edit = ( { + attributes, + setAttributes, + context, +}: BlockEditProps< BlockAttributes > & { context: Context } ): JSX.Element => { const blockProps = useBlockProps(); + + const blockAttrs = { + ...attributes, + ...context, + }; + const isDescendentOfQueryLoop = Number.isFinite( context.queryId ); + + useEffect( + () => setAttributes( { isDescendentOfQueryLoop } ), + [ setAttributes, isDescendentOfQueryLoop ] + ); + return (
- +
); }; diff --git a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/stock-indicator/index.ts b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/stock-indicator/index.ts index 213ea732545..8acb8ebba6e 100644 --- a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/stock-indicator/index.ts +++ b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/stock-indicator/index.ts @@ -10,7 +10,6 @@ import type { BlockConfiguration } from '@wordpress/blocks'; import sharedConfig from '../shared/config'; import attributes from './attributes'; import edit from './edit'; -import { Save } from './save'; import { supports } from './supports'; import { @@ -28,7 +27,12 @@ const blockConfig: BlockConfiguration = { attributes, supports, edit, - save: Save, + usesContext: [ 'query', 'queryId', 'postId' ], + ancestor: [ + '@woocommerce/all-products', + '@woocommerce/single-product', + 'core/post-template', + ], }; registerExperimentalBlockType( 'woocommerce/product-stock-indicator', { diff --git a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/stock-indicator/types.ts b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/stock-indicator/types.ts index e64df6f3d4f..b695a3b5f5d 100644 --- a/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/stock-indicator/types.ts +++ b/plugins/woocommerce-blocks/assets/js/atomic/blocks/product-elements/stock-indicator/types.ts @@ -1,3 +1,4 @@ export interface BlockAttributes { productId: number; + isDescendentOfQueryLoop: boolean; } diff --git a/plugins/woocommerce-blocks/src/BlockTypes/ProductStockIndicator.php b/plugins/woocommerce-blocks/src/BlockTypes/ProductStockIndicator.php index 33a894f6865..51ae70dc3fd 100644 --- a/plugins/woocommerce-blocks/src/BlockTypes/ProductStockIndicator.php +++ b/plugins/woocommerce-blocks/src/BlockTypes/ProductStockIndicator.php @@ -1,6 +1,8 @@ register_chunk_translations( [ $this->block_name ] ); + return null; + } + + /** + * Register the context. + */ + protected function get_block_type_uses_context() { + return [ 'query', 'queryId', 'postId' ]; + } + + /** + * Get stock text based on stock. For example: + * - In stock + * - Out of stock + * - Available on backorder + * - 2 left in stock + * + * @param [bool] $is_in_stock Whether the product is in stock. + * @param [bool] $is_low_stock Whether the product is low in stock. + * @param [int|null] $low_stock_amount The amount of stock that is considered low. + * @param [bool] $is_on_backorder Whether the product is on backorder. + * @return string Stock text. + */ + protected static function getTextBasedOnStock( $is_in_stock, $is_low_stock, $low_stock_amount, $is_on_backorder ) { + if ( $is_low_stock ) { + return sprintf( + /* translators: %d is number of items in stock for product */ + __( '%d left in stock', 'woo-gutenberg-products-block' ), + $low_stock_amount + ); + } elseif ( $is_on_backorder ) { + return __( 'Available on backorder', 'woo-gutenberg-products-block' ); + } elseif ( $is_in_stock ) { + return __( 'In stock', 'woo-gutenberg-products-block' ); + } else { + return __( 'Out of stock', 'woo-gutenberg-products-block' ); + } + } + + + + /** + * Include and render the block. + * + * @param array $attributes Block attributes. Default empty array. + * @param string $content Block content. Default empty string. + * @param WP_Block $block Block instance. + * @return string Rendered block type output. + */ + protected function render( $attributes, $content, $block ) { + if ( ! empty( $content ) ) { + parent::register_block_type_assets(); + $this->register_chunk_translations( [ $this->block_name ] ); + return $content; + } + + $post_id = $block->context['postId']; + $product = wc_get_product( $post_id ); + $is_in_stock = $product->is_in_stock(); + $is_on_backorder = $product->is_on_backorder(); + + $low_stock_amount = $product->get_low_stock_amount(); + $total_stock = $product->get_stock_quantity(); + $is_low_stock = $low_stock_amount && $total_stock <= $low_stock_amount; + + $classes_and_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes ); + + $classnames = isset( $classes_and_styles['classes'] ) ? ' ' . $classes_and_styles['classes'] . ' ' : ''; + $classnames .= isset( $attributes['className'] ) ? ' ' . $attributes['className'] . ' ' : ''; + $classnames .= ! $is_in_stock ? ' wc-block-components-product-stock-indicator--out-of-stock ' : ''; + $classnames .= $is_in_stock ? ' wc-block-components-product-stock-indicator--in-stock ' : ''; + $classnames .= $is_low_stock ? ' wc-block-components-product-stock-indicator--low-stock ' : ''; + $classnames .= $is_on_backorder ? ' wc-block-components-product-stock-indicator--available-on-backorder ' : ''; + + $output = ''; + $output .= '