* Stock Indicator Block

* Add to block map

* Put behind flag

* Move EditProductLink context

* stock text props
This commit is contained in:
Mike Jolley 2020-06-11 10:02:00 +01:00 committed by GitHub
parent 41ca430ffe
commit 52496a3506
16 changed files with 198 additions and 21 deletions

View File

@ -11,3 +11,4 @@ import './product/sale-badge';
import './product/sku';
import './product/category-list';
import './product/tag-list';
import './product/stock-indicator';

View File

@ -3,7 +3,6 @@
*/
import { Disabled } from '@wordpress/components';
import EditProductLink from '@woocommerce/block-components/edit-product-link';
import { useProductDataContext } from '@woocommerce/shared-context';
/**
* Internal dependencies
@ -11,12 +10,9 @@ import { useProductDataContext } from '@woocommerce/shared-context';
import Block from './block';
export default ( { attributes } ) => {
const productDataContext = useProductDataContext();
const product = productDataContext.product || {};
return (
<>
<EditProductLink productId={ product.id } />
<EditProductLink />
<Disabled>
<Block { ...attributes } />
</Disabled>

View File

@ -2,7 +2,6 @@
* External dependencies
*/
import EditProductLink from '@woocommerce/block-components/edit-product-link';
import { useProductDataContext } from '@woocommerce/shared-context';
/**
* Internal dependencies
@ -10,12 +9,9 @@ import { useProductDataContext } from '@woocommerce/shared-context';
import Block from './block';
export default ( { attributes } ) => {
const productDataContext = useProductDataContext();
const product = productDataContext.product || {};
return (
<>
<EditProductLink productId={ product.id } />
<EditProductLink />
<Block { ...attributes } />
</>
);

View File

@ -0,0 +1,83 @@
/**
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import {
useInnerBlockLayoutContext,
useProductDataContext,
} from '@woocommerce/shared-context';
/**
* Internal dependencies
*/
import './style.scss';
/**
* Product Stock Indicator Block Component.
*
* @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 { parentClassName } = useInnerBlockLayoutContext();
const productDataContext = useProductDataContext();
const product = props.product || productDataContext.product || null;
if ( ! product ) {
return null;
}
const inStock = !! product.is_in_stock;
const lowStock = product.low_stock_remaining;
const isBackordered = product.is_on_backorder;
return (
<div
className={ classnames(
className,
'wc-block-components-product-stock-indicator',
`${ parentClassName }__stock-indicator`,
{
'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,
'wc-block-components-product-stock-indicator--available-on-backorder': !! isBackordered,
}
) }
>
{ lowStock
? lowStockText( lowStock )
: stockText( inStock, isBackordered ) }
</div>
);
};
const lowStockText = ( lowStock ) => {
return sprintf(
/* translators: %d stock amount (number of items in stock for product) */
__( '%d left in stock', 'woo-gutenberg-products-block' ),
lowStock
);
};
const stockText = ( inStock, isBackordered ) => {
if ( isBackordered ) {
return __( 'Available on backorder', 'woo-gutenberg-products-block' );
}
return inStock
? __( 'In Stock', 'woo-gutenberg-products-block' )
: __( 'Out of Stock', 'woo-gutenberg-products-block' );
};
Block.propTypes = {
className: PropTypes.string,
product: PropTypes.object,
};
export default Block;

View File

@ -0,0 +1,18 @@
/**
* External dependencies
*/
import EditProductLink from '@woocommerce/block-components/edit-product-link';
/**
* Internal dependencies
*/
import Block from './block';
export default ( { attributes } ) => {
return (
<>
<EditProductLink />
<Block { ...attributes } />
</>
);
};

View File

@ -0,0 +1,30 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { registerExperimentalBlockType } from '@woocommerce/block-settings';
import { Icon, box } from '@woocommerce/icons';
/**
* Internal dependencies
*/
import sharedConfig from '../shared-config';
import edit from './edit';
const blockConfig = {
title: __( 'Product Stock Indicator', 'woo-gutenberg-products-block' ),
description: __(
'Display product stock status.',
'woo-gutenberg-products-block'
),
icon: {
src: <Icon srcElement={ box } />,
foreground: '#96588a',
},
edit,
};
registerExperimentalBlockType( 'woocommerce/product-stock-indicator', {
...sharedConfig,
...blockConfig,
} );

View File

@ -0,0 +1,19 @@
.wc-block-layout {
.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;
}
}
}

View File

@ -3,7 +3,6 @@
*/
import { Disabled } from '@wordpress/components';
import EditProductLink from '@woocommerce/block-components/edit-product-link';
import { useProductDataContext } from '@woocommerce/shared-context';
/**
* Internal dependencies
@ -11,12 +10,9 @@ import { useProductDataContext } from '@woocommerce/shared-context';
import Block from './block';
export default ( { attributes } ) => {
const productDataContext = useProductDataContext();
const product = productDataContext.product || {};
return (
<>
<EditProductLink productId={ product.id } />
<EditProductLink />
<Disabled>
<Block { ...attributes } />
</Disabled>

View File

@ -16,6 +16,7 @@ import ProductTitle from '../blocks/product/title/frontend';
import ProductSku from '../blocks/product/sku/block';
import ProductCategoryList from '../blocks/product/category-list/block';
import ProductTagList from '../blocks/product/tag-list/block';
import ProductStockIndicator from '../blocks/product/stock-indicator/block';
/**
* Map blocks to components suitable for use on the frontend.
@ -33,5 +34,6 @@ export const getBlockMap = ( blockName ) => ( {
'woocommerce/product-sku': ProductSku,
'woocommerce/product-category-list': ProductCategoryList,
'woocommerce/product-tag-list': ProductTagList,
'woocommerce/product-stock-indicator': ProductStockIndicator,
...getRegisteredInnerBlocks( blockName ),
} );

View File

@ -35,6 +35,7 @@ export const DEFAULT_INNER_BLOCKS = [
[ 'woocommerce/product-rating' ],
[ 'woocommerce/product-price' ],
[ 'woocommerce/product-summary' ],
[ 'woocommerce/product-stock-indicator' ],
[ 'woocommerce/product-button' ],
[ 'woocommerce/product-sku' ],
[ 'woocommerce/product-category-list' ],

View File

@ -5,11 +5,22 @@ import { __ } from '@wordpress/i18n';
import { Icon, external } from '@woocommerce/icons';
import { ADMIN_URL } from '@woocommerce/settings';
import { InspectorControls } from '@wordpress/block-editor';
import { useProductDataContext } from '@woocommerce/shared-context';
/**
* Component to render an edit product link in the sidebar.
*
* @param {Object} props Component props.
*/
const EditProductLink = ( { productId } ) => {
const EditProductLink = ( props ) => {
const productDataContext = useProductDataContext();
const product = productDataContext.product || {};
const productId = product.id || props.productId || 0;
if ( ! productId ) {
return null;
}
return (
<InspectorControls>
<div className="wc-block-single-product__edit-card">

View File

@ -5,6 +5,7 @@ export { default as arrowDownAlt2 } from './library/arrow-down-alt2';
export { default as bank } from './library/bank';
export { default as barcode } from './library/barcode';
export { default as bill } from './library/bill';
export { default as box } from './library/box';
export { default as card } from './library/card';
export { default as cart } from './library/cart';
export { default as checkPayment } from './library/check-payment';

View File

@ -5,10 +5,7 @@ import { SVG } from 'wordpress-components';
const barcode = (
<SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
fill="#626262"
d="M2 6h2v12H2V6m3 0h1v12H5V6m2 0h3v12H7V6m4 0h1v12h-1V6m3 0h2v12h-2V6m3 0h3v12h-3V6m4 0h1v12h-1V6z"
/>
<path d="M2 6h2v12H2V6m3 0h1v12H5V6m2 0h3v12H7V6m4 0h1v12h-1V6m3 0h2v12h-2V6m3 0h3v12h-3V6m4 0h1v12h-1V6z" />
</SVG>
);

View File

@ -0,0 +1,18 @@
/**
* External dependencies
*/
import { SVG } from 'wordpress-components';
const box = (
<SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g fillRule="evenodd">
<path d="M0 0h24v24H0z" fill="none" />
<path
fillRule="nonzero"
d="M20.5 5.2l-1.4-1.7C19 3.3 18.5 3 18 3H6c-.5 0-.9.2-1.2.5L3.5 5.3A2 2 0 003 6.5V19c0 1.1.9 2 2 2h14a2 2 0 002-2V6.5c0-.5-.2-1-.5-1.3zM6.2 5h11.6l.8 1H5.4l.8-1zM5 19V8h14v11H5z"
/>
</g>
</SVG>
);
export default box;

View File

@ -106,6 +106,7 @@ class Library {
'product-sku',
'product-category-list',
'product-tag-list',
'product-stock-indicator',
];
foreach ( $atomic_blocks as $atomic_block ) {
$instance = new \Automattic\WooCommerce\Blocks\BlockTypes\AtomicBlock( $atomic_block );

View File

@ -263,6 +263,12 @@ class ProductSchema extends AbstractSchema {
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'is_on_backorder' => [
'description' => __( 'Is the product stock backordered? This will also return false if backorder notifications are turned off.', 'woo-gutenberg-products-block' ),
'type' => 'boolean',
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'low_stock_remaining' => [
'description' => __( 'Quantity left in stock if stock is low, or null if not applicable.', 'woo-gutenberg-products-block' ),
'type' => [ 'integer', 'null' ],
@ -320,6 +326,7 @@ class ProductSchema extends AbstractSchema {
'has_options' => $product->has_options(),
'is_purchasable' => $product->is_purchasable(),
'is_in_stock' => $product->is_in_stock(),
'is_on_backorder' => 'onbackorder' === $product->get_stock_status(),
'low_stock_remaining' => $this->get_low_stock_remaining( $product ),
'add_to_cart' => (object) $this->prepare_html_response(
[
@ -365,7 +372,7 @@ class ProductSchema extends AbstractSchema {
$remaining_stock = $this->get_remaining_stock( $product );
if ( ! is_null( $remaining_stock ) && $remaining_stock <= wc_get_low_stock_amount( $product ) ) {
return $remaining_stock;
return max( $remaining_stock, 0 );
}
return null;