* Merge ProductPrice atomic block and component

* Update assets/js/atomic/blocks/product-elements/price/block.js

Co-authored-by: Darren Ethier <darren@roughsmootheng.in>

* Update assets/js/atomic/blocks/product-elements/price/block.js

Co-authored-by: Darren Ethier <darren@roughsmootheng.in>

* If product price component has alignment, make it a block

* Make ProductPrice propTypes more specific

* Add align prop to loading product price

* Add stories to ProductPrice component

Co-authored-by: Darren Ethier <darren@roughsmootheng.in>
This commit is contained in:
Albert Juhé Lluveras 2020-09-14 12:56:10 +02:00 committed by GitHub
parent 727935f04c
commit 76ebf9c860
10 changed files with 333 additions and 253 deletions

View File

@ -3,7 +3,7 @@
*/
import PropTypes from 'prop-types';
import classnames from 'classnames';
import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount';
import ProductPrice from '@woocommerce/base-components/product-price';
import { getCurrencyFromPriceResponse } from '@woocommerce/base-utils';
import {
useInnerBlockLayoutContext,
@ -13,11 +13,6 @@ import { getColorClassName, getFontSizeClass } from '@wordpress/block-editor';
import { isFeaturePluginBuild } from '@woocommerce/block-settings';
import { withProductDataContext } from '@woocommerce/shared-hocs';
/**
* Internal dependencies
*/
import './style.scss';
/**
* Product Price Block Component.
*
@ -51,6 +46,14 @@ const Block = ( {
const { parentClassName } = useInnerBlockLayoutContext();
const { product } = useProductDataContext();
const wrapperClassName = classnames( className, {
[ `${ parentClassName }__product-price` ]: parentClassName,
} );
if ( ! product.id ) {
return <ProductPrice align={ align } className={ wrapperClassName } />;
}
const colorClass = getColorClassName( 'color', color );
const fontSizeClass = getFontSizeClass( fontSize );
const saleColorClass = getColorClassName( 'color', saleColor );
@ -80,163 +83,39 @@ const Block = ( {
fontSize: customSaleFontSize,
};
if ( ! product.id ) {
return (
<div
className={ classnames(
className,
'price',
'wc-block-components-product-price',
{
[ `${ parentClassName }__product-price` ]: parentClassName,
}
) }
/>
);
}
const prices = product.prices;
const currency = getCurrencyFromPriceResponse( prices );
const isOnSale = prices.price !== prices.regular_price;
const priceClassName = isOnSale
? classnames( {
[ `${ parentClassName }__product-price__value` ]: parentClassName,
[ saleClasses ]: isFeaturePluginBuild(),
} )
: classnames( {
[ `${ parentClassName }__product-price__value` ]: parentClassName,
[ classes ]: isFeaturePluginBuild(),
} );
const priceStyle = isOnSale ? saleStyle : style;
return (
<div
className={ classnames(
className,
'price',
'wc-block-components-product-price',
{
[ `${ parentClassName }__product-price` ]: parentClassName,
[ `wc-block-components-product-price--align-${ align }` ]:
align && isFeaturePluginBuild(),
}
) }
>
{ /* eslint-disable-next-line no-nested-ternary */ }
{ hasPriceRange( prices ) ? (
<PriceRange
currency={ currency }
minAmount={ prices.price_range.min_amount }
maxAmount={ prices.price_range.max_amount }
classes={ classes }
style={ style }
/>
) : prices.price !== prices.regular_price ? (
<SalePrice
currency={ currency }
price={ prices.price }
regularPrice={ prices.regular_price }
saleClasses={ saleClasses }
saleStyle={ saleStyle }
classes={ classes }
style={ style }
/>
) : (
<Price
currency={ currency }
price={ prices.price }
classes={ classes }
style={ style }
/>
) }
</div>
);
};
const hasPriceRange = ( prices ) => {
return (
prices.price_range &&
prices.price_range.min_amount &&
prices.price_range.max_amount
);
};
const PriceRange = ( { currency, minAmount, maxAmount, classes, style } ) => {
const { parentClassName } = useInnerBlockLayoutContext();
return (
<span
className={ classnames(
'wc-block-components-product-price__value',
{
[ `${ parentClassName }__product-price__value` ]: parentClassName,
[ classes ]: isFeaturePluginBuild(),
}
) }
style={ isFeaturePluginBuild() ? style : {} }
>
<FormattedMonetaryAmount
currency={ currency }
value={ minAmount }
/>
&nbsp;&mdash;&nbsp;
<FormattedMonetaryAmount
currency={ currency }
value={ maxAmount }
/>
</span>
);
};
const SalePrice = ( {
currency,
price,
regularPrice,
saleClasses = '',
saleStyle = {},
classes = '',
style = {},
} ) => {
const { parentClassName } = useInnerBlockLayoutContext();
return (
<>
<del
className={ classnames(
'wc-block-components-product-price__regular',
{
[ `${ parentClassName }__product-price__regular` ]: parentClassName,
[ classes ]: isFeaturePluginBuild(),
}
) }
style={ isFeaturePluginBuild() ? style : {} }
>
<FormattedMonetaryAmount
currency={ currency }
value={ regularPrice }
/>
</del>
<span
className={ classnames(
'wc-block-components-product-price__value',
'is-discounted',
{
[ `${ parentClassName }__product-price__value` ]: parentClassName,
[ saleClasses ]: isFeaturePluginBuild(),
}
) }
style={ isFeaturePluginBuild() ? saleStyle : {} }
>
<FormattedMonetaryAmount
currency={ currency }
value={ price }
/>
</span>
</>
);
};
const Price = ( { currency, price, classes = '', style = {} } ) => {
const { parentClassName } = useInnerBlockLayoutContext();
return (
<span
className={ classnames(
'wc-block-components-product-price__value',
`${ parentClassName }__product-price__value`,
{ [ classes ]: isFeaturePluginBuild() }
) }
style={ isFeaturePluginBuild() ? style : {} }
>
<FormattedMonetaryAmount currency={ currency } value={ price } />
</span>
<ProductPrice
align={ align }
className={ wrapperClassName }
currency={ currency }
price={ prices.price }
priceClassName={ priceClassName }
priceStyle={ isFeaturePluginBuild() ? priceStyle : {} }
// Range price props
minPrice={ prices?.price_range?.min_amount }
maxPrice={ prices?.price_range?.max_amount }
// This is the regular or original price when the `price` value is a sale price.
regularPrice={ prices.regular_price }
regularPriceClassName={ classnames( {
[ `${ parentClassName }__product-price__regular` ]: parentClassName,
[ classes ]: isFeaturePluginBuild(),
} ) }
regularPriceStyle={ isFeaturePluginBuild() ? style : {} }
/>
);
};

View File

@ -9,7 +9,6 @@ export { default as ProductLowStockBadge } from './product-low-stock-badge';
export { default as ProductSummary } from './product-summary';
export { default as ProductMetadata } from './product-metadata';
export { default as ProductName } from './product-name';
export { default as ProductPrice } from './product-price';
export { default as ProductSaleBadge } from './product-sale-badge';
export { default as ProductVariationData } from './product-variation-data';
export { default as ReturnToCartButton } from './return-to-cart-button';

View File

@ -4,13 +4,13 @@
import { __, sprintf } from '@wordpress/i18n';
import { getCurrency } from '@woocommerce/base-utils';
import Label from '@woocommerce/base-components/label';
import ProductPrice from '@woocommerce/base-components/product-price';
import {
ProductBackorderBadge,
ProductImage,
ProductLowStockBadge,
ProductMetadata,
ProductName,
ProductPrice,
} from '@woocommerce/base-components/cart-checkout';
import PropTypes from 'prop-types';
import Dinero from 'dinero.js';
@ -57,9 +57,9 @@ const OrderSummaryItem = ( { cartItem } ) => {
<div className="wc-block-components-order-summary-item__header">
<ProductName permalink={ permalink } name={ name } />
<ProductPrice
className="wc-block-components-order-summary-item__total-price"
currency={ currency }
value={ linePrice }
price={ linePrice }
priceClassName="wc-block-components-order-summary-item__total-price"
/>
</div>
{ showBackorderBadge ? (

View File

@ -9,7 +9,7 @@ import { decodeEntities } from '@wordpress/html-entities';
*/
import './style.scss';
const ProductName = ( { name, permalink, disabled } ) => {
const ProductName = ( { name, permalink, disabled = false } ) => {
return (
// we use tabIndex -1 to prevent the link from being focused, pointer-events
// disabled click events, so we get an almost disabled link.
@ -24,6 +24,7 @@ const ProductName = ( { name, permalink, disabled } ) => {
};
ProductName.propTypes = {
disabled: PropTypes.bool,
name: PropTypes.string,
permalink: PropTypes.string,
};

View File

@ -1,81 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount';
import classNames from 'classnames';
import PropTypes from 'prop-types';
/**
* Internal dependencies
*/
import './style.scss';
const ProductPrice = ( { className, currency, regularValue, value } ) => {
const isDiscounted =
Number.isFinite( regularValue ) && regularValue > value;
if ( isDiscounted ) {
return (
<span className="price wc-block-components-product-price">
<span className="screen-reader-text">
{ __( 'Previous price:', 'woo-gutenberg-products-block' ) }
</span>
<del>
<FormattedMonetaryAmount
className={ classNames(
'wc-block-components-product-price__regular',
className
) }
currency={ currency }
value={ regularValue }
/>
</del>
<span className="screen-reader-text">
{ __(
'Discounted price:',
'woo-gutenberg-products-block'
) }
</span>
<ins>
<FormattedMonetaryAmount
className={ classNames(
'wc-block-components-product-price__value',
className,
{
'is-discounted': isDiscounted,
}
) }
currency={ currency }
value={ value }
/>
</ins>
</span>
);
}
return (
<span className="price wc-block-components-product-price">
<FormattedMonetaryAmount
className={ classNames(
'wc-block-components-product-price__value',
className,
{
'is-discounted': isDiscounted,
}
) }
currency={ currency }
value={ value }
/>
</span>
);
};
ProductPrice.propTypes = {
currency: PropTypes.object.isRequired,
value: PropTypes.number.isRequired,
className: PropTypes.string,
regularValue: PropTypes.number,
};
export default ProductPrice;

View File

@ -1,5 +0,0 @@
.wc-block-components-product-price__value {
&.is-discounted {
margin-left: 0.5em;
}
}

View File

@ -0,0 +1,196 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount';
import classNames from 'classnames';
import PropTypes from 'prop-types';
/**
* Internal dependencies
*/
import './style.scss';
const PriceRange = ( {
className,
currency,
maxPrice,
minPrice,
priceClassName,
priceStyle,
} ) => {
return (
<span className={ className }>
<FormattedMonetaryAmount
className={ classNames(
'wc-block-components-product-price__value',
priceClassName
) }
currency={ currency }
value={ minPrice }
style={ priceStyle }
/>
&nbsp;&mdash;&nbsp;
<FormattedMonetaryAmount
className={ classNames(
'wc-block-components-product-price__value',
priceClassName
) }
currency={ currency }
value={ maxPrice }
style={ priceStyle }
/>
</span>
);
};
const SalePrice = ( {
className,
currency,
regularPriceClassName,
regularPriceStyle,
regularPrice,
priceClassName,
priceStyle,
price,
} ) => {
return (
<span className={ className }>
<span className="screen-reader-text">
{ __( 'Previous price:', 'woo-gutenberg-products-block' ) }
</span>
<FormattedMonetaryAmount
currency={ currency }
renderText={ ( value ) => (
<del
className={ classNames(
'wc-block-components-product-price__regular',
regularPriceClassName
) }
style={ regularPriceStyle }
>
{ value }
</del>
) }
value={ regularPrice }
/>
<span className="screen-reader-text">
{ __( 'Discounted price:', 'woo-gutenberg-products-block' ) }
</span>
<FormattedMonetaryAmount
currency={ currency }
renderText={ ( value ) => (
<ins
className={ classNames(
'wc-block-components-product-price__value',
'is-discounted',
priceClassName
) }
style={ priceStyle }
>
{ value }
</ins>
) }
value={ price }
/>
</span>
);
};
const ProductPrice = ( {
align,
className,
currency,
maxPrice = null,
minPrice = null,
price = null,
priceClassName,
priceStyle,
regularPrice,
regularPriceClassName,
regularPriceStyle,
} ) => {
const wrapperClassName = classNames(
className,
'price',
'wc-block-components-product-price',
{
[ `wc-block-components-product-price--align-${ align }` ]: align,
}
);
const isDiscounted = regularPrice && price !== regularPrice;
if ( isDiscounted ) {
return (
<SalePrice
className={ wrapperClassName }
currency={ currency }
price={ price }
priceClassName={ priceClassName }
priceStyle={ priceStyle }
regularPrice={ regularPrice }
regularPriceClassName={ regularPriceClassName }
regularPriceStyle={ regularPriceStyle }
/>
);
}
if ( minPrice !== null && maxPrice !== null ) {
return (
<PriceRange
className={ wrapperClassName }
currency={ currency }
maxPrice={ maxPrice }
minPrice={ minPrice }
priceClassName={ priceClassName }
priceStyle={ priceStyle }
/>
);
}
if ( price !== null ) {
return (
<span className={ wrapperClassName }>
<FormattedMonetaryAmount
className={ classNames(
'wc-block-components-product-price__value',
priceClassName
) }
currency={ currency }
value={ price }
style={ priceStyle }
/>
</span>
);
}
return (
<span className={ wrapperClassName }>
<span
className={ classNames(
'wc-block-components-product-price__value',
priceClassName
) }
/>
</span>
);
};
ProductPrice.propTypes = {
align: PropTypes.oneOf( [ 'left', 'center', 'right' ] ),
className: PropTypes.string,
currency: PropTypes.object,
price: PropTypes.oneOfType( [ PropTypes.number, PropTypes.string ] ),
priceClassName: PropTypes.string,
priceStyle: PropTypes.object,
// Range price props
maxPrice: PropTypes.oneOfType( [ PropTypes.number, PropTypes.string ] ),
minPrice: PropTypes.oneOfType( [ PropTypes.number, PropTypes.string ] ),
// On sale price props
regularPrice: PropTypes.oneOfType( [ PropTypes.number, PropTypes.string ] ),
regularPriceClassName: PropTypes.string,
regularPriceStyle: PropTypes.object,
};
export default ProductPrice;

View File

@ -0,0 +1,82 @@
/**
* External dependencies
*/
import { number, select } from '@storybook/addon-knobs';
/**
* Internal dependencies
*/
import ProductPrice from '../';
export default {
title: 'WooCommerce Blocks/@base-components/ProductPrice',
component: ProductPrice,
};
const getKnobs = () => {
const align = select( 'Align', [ 'left', 'center', 'right' ], 'left' );
const currencies = [
{
label: 'USD',
code: 'USD',
symbol: '$',
thousandSeparator: ',',
decimalSeparator: '.',
minorUnit: 2,
prefix: '$',
suffix: '',
},
{
label: 'EUR',
code: 'EUR',
symbol: '€',
thousandSeparator: '.',
decimalSeparator: ',',
minorUnit: 2,
prefix: '',
suffix: '€',
},
];
const currency = select( 'Currency', currencies, currencies[ 0 ] );
return { align, currency };
};
export const standard = () => {
const { align, currency } = getKnobs();
const price = number( 'Price', 4000 );
return (
<ProductPrice align={ align } currency={ currency } price={ price } />
);
};
export const sale = () => {
const { align, currency } = getKnobs();
const price = number( 'Price', 3000 );
const regularPrice = number( 'Regular price', 4000 );
return (
<ProductPrice
align={ align }
currency={ currency }
price={ price }
regularPrice={ regularPrice }
/>
);
};
export const range = () => {
const { align, currency } = getKnobs();
const minPrice = number( 'Min price', 3000 );
const maxPrice = number( 'Max price', 5000 );
return (
<ProductPrice
align={ align }
currency={ currency }
minPrice={ minPrice }
maxPrice={ maxPrice }
/>
);
};

View File

@ -1,15 +1,24 @@
/*rtl:begin:ignore*/
.wc-block-components-product-price--align-left {
display: block;
text-align: left;
}
.wc-block-components-product-price--align-center {
display: block;
text-align: center;
}
.wc-block-components-product-price--align-right {
display: block;
text-align: right;
}
/*rtl:end:ignore*/
.wc-block-components-product-price__value {
&.is-discounted {
margin-left: 0.5em;
}
}
.is-loading {
.wc-block-components-product-price::before {
@include placeholder();

View File

@ -5,6 +5,7 @@ import classnames from 'classnames';
import { __ } from '@wordpress/i18n';
import PropTypes from 'prop-types';
import QuantitySelector from '@woocommerce/base-components/quantity-selector';
import ProductPrice from '@woocommerce/base-components/product-price';
import { getCurrency } from '@woocommerce/base-utils';
import { useStoreCartItemQuantity } from '@woocommerce/base-hooks';
import { Icon, trash } from '@woocommerce/icons';
@ -14,7 +15,6 @@ import {
ProductLowStockBadge,
ProductMetadata,
ProductName,
ProductPrice,
ProductSaleBadge,
} from '@woocommerce/base-components/cart-checkout';
import Dinero from 'dinero.js';
@ -156,11 +156,11 @@ const CartLineItemRow = ( { lineItem = {} } ) => {
<td className="wc-block-cart-item__total">
<ProductPrice
currency={ currency }
regularValue={ getAmountFromRawPrice(
regularPrice={ getAmountFromRawPrice(
regularAmount,
currency
) }
value={ getAmountFromRawPrice( purchaseAmount, currency ) }
price={ getAmountFromRawPrice( purchaseAmount, currency ) }
/>
<ProductSaleBadge
currency={ currency }