initial commit
This commit is contained in:
@ -0,0 +1,214 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount';
|
||||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import { formatPrice } from '@woocommerce/price-format';
|
||||
import { createInterpolateElement } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './style.scss';
|
||||
|
||||
const PriceRange = ( {
|
||||
currency,
|
||||
maxPrice,
|
||||
minPrice,
|
||||
priceClassName,
|
||||
priceStyle,
|
||||
} ) => {
|
||||
return (
|
||||
<>
|
||||
<span className="screen-reader-text">
|
||||
{ sprintf(
|
||||
/* translators: %1$s min price, %2$s max price */
|
||||
__(
|
||||
'Price between %1$s and %2$s',
|
||||
'woocommerce'
|
||||
),
|
||||
formatPrice( minPrice ),
|
||||
formatPrice( maxPrice )
|
||||
) }
|
||||
</span>
|
||||
<span aria-hidden={ true }>
|
||||
<FormattedMonetaryAmount
|
||||
className={ classNames(
|
||||
'wc-block-components-product-price__value',
|
||||
priceClassName
|
||||
) }
|
||||
currency={ currency }
|
||||
value={ minPrice }
|
||||
style={ priceStyle }
|
||||
/>
|
||||
—
|
||||
<FormattedMonetaryAmount
|
||||
className={ classNames(
|
||||
'wc-block-components-product-price__value',
|
||||
priceClassName
|
||||
) }
|
||||
currency={ currency }
|
||||
value={ maxPrice }
|
||||
style={ priceStyle }
|
||||
/>
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const SalePrice = ( {
|
||||
currency,
|
||||
regularPriceClassName,
|
||||
regularPriceStyle,
|
||||
regularPrice,
|
||||
priceClassName,
|
||||
priceStyle,
|
||||
price,
|
||||
} ) => {
|
||||
return (
|
||||
<>
|
||||
<span className="screen-reader-text">
|
||||
{ __( 'Previous price:', 'woocommerce' ) }
|
||||
</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:', 'woocommerce' ) }
|
||||
</span>
|
||||
<FormattedMonetaryAmount
|
||||
currency={ currency }
|
||||
renderText={ ( value ) => (
|
||||
<ins
|
||||
className={ classNames(
|
||||
'wc-block-components-product-price__value',
|
||||
'is-discounted',
|
||||
priceClassName
|
||||
) }
|
||||
style={ priceStyle }
|
||||
>
|
||||
{ value }
|
||||
</ins>
|
||||
) }
|
||||
value={ price }
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const ProductPrice = ( {
|
||||
align,
|
||||
className,
|
||||
currency,
|
||||
format = '<price/>',
|
||||
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,
|
||||
}
|
||||
);
|
||||
|
||||
if ( ! format.includes( '<price/>' ) ) {
|
||||
format = '<price/>';
|
||||
// eslint-disable-next-line no-console
|
||||
console.error( 'Price formats need to include the `<price/>` tag.' );
|
||||
}
|
||||
|
||||
const isDiscounted = regularPrice && price !== regularPrice;
|
||||
let priceComponent = (
|
||||
<span
|
||||
className={ classNames(
|
||||
'wc-block-components-product-price__value',
|
||||
priceClassName
|
||||
) }
|
||||
/>
|
||||
);
|
||||
|
||||
if ( isDiscounted ) {
|
||||
priceComponent = (
|
||||
<SalePrice
|
||||
currency={ currency }
|
||||
price={ price }
|
||||
priceClassName={ priceClassName }
|
||||
priceStyle={ priceStyle }
|
||||
regularPrice={ regularPrice }
|
||||
regularPriceClassName={ regularPriceClassName }
|
||||
regularPriceStyle={ regularPriceStyle }
|
||||
/>
|
||||
);
|
||||
} else if ( minPrice !== null && maxPrice !== null ) {
|
||||
priceComponent = (
|
||||
<PriceRange
|
||||
currency={ currency }
|
||||
maxPrice={ maxPrice }
|
||||
minPrice={ minPrice }
|
||||
priceClassName={ priceClassName }
|
||||
priceStyle={ priceStyle }
|
||||
/>
|
||||
);
|
||||
} else if ( price !== null ) {
|
||||
priceComponent = (
|
||||
<FormattedMonetaryAmount
|
||||
className={ classNames(
|
||||
'wc-block-components-product-price__value',
|
||||
priceClassName
|
||||
) }
|
||||
currency={ currency }
|
||||
value={ price }
|
||||
style={ priceStyle }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<span className={ wrapperClassName }>
|
||||
{ createInterpolateElement( format, {
|
||||
price: priceComponent,
|
||||
} ) }
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
ProductPrice.propTypes = {
|
||||
align: PropTypes.oneOf( [ 'left', 'center', 'right' ] ),
|
||||
className: PropTypes.string,
|
||||
currency: PropTypes.object,
|
||||
format: PropTypes.string,
|
||||
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;
|
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { number, select } from '@storybook/addon-knobs';
|
||||
import { currencyKnob } from '@woocommerce/knobs';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import ProductPrice from '../';
|
||||
|
||||
export default {
|
||||
title: 'WooCommerce Blocks/@base-components/ProductPrice',
|
||||
component: ProductPrice,
|
||||
};
|
||||
|
||||
export const standard = () => {
|
||||
const align = select( 'Align', [ 'left', 'center', 'right' ], 'left' );
|
||||
const currency = currencyKnob();
|
||||
const price = number( 'Price', 4000 );
|
||||
|
||||
return (
|
||||
<ProductPrice align={ align } currency={ currency } price={ price } />
|
||||
);
|
||||
};
|
||||
|
||||
export const sale = () => {
|
||||
const align = select( 'Align', [ 'left', 'center', 'right' ], 'left' );
|
||||
const currency = currencyKnob();
|
||||
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 = select( 'Align', [ 'left', 'center', 'right' ], 'left' );
|
||||
const currency = currencyKnob();
|
||||
const minPrice = number( 'Min price', 3000 );
|
||||
const maxPrice = number( 'Max price', 5000 );
|
||||
|
||||
return (
|
||||
<ProductPrice
|
||||
align={ align }
|
||||
currency={ currency }
|
||||
minPrice={ minPrice }
|
||||
maxPrice={ maxPrice }
|
||||
/>
|
||||
);
|
||||
};
|
@ -0,0 +1,29 @@
|
||||
/*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();
|
||||
content: ".";
|
||||
display: inline-block;
|
||||
width: 5em;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ProductPrice should apply the format if one is provided 1`] = `
|
||||
<span
|
||||
className="price wc-block-components-product-price"
|
||||
>
|
||||
pre price
|
||||
<span
|
||||
className="screen-reader-text"
|
||||
>
|
||||
Previous price:
|
||||
</span>
|
||||
<del
|
||||
className="wc-block-components-product-price__regular"
|
||||
>
|
||||
£1.00
|
||||
</del>
|
||||
<span
|
||||
className="screen-reader-text"
|
||||
>
|
||||
Discounted price:
|
||||
</span>
|
||||
<ins
|
||||
className="wc-block-components-product-price__value is-discounted"
|
||||
>
|
||||
£0.50
|
||||
</ins>
|
||||
Test format
|
||||
</span>
|
||||
`;
|
||||
|
||||
exports[`ProductPrice should use default price if no format is provided 1`] = `
|
||||
<span
|
||||
className="price wc-block-components-product-price"
|
||||
>
|
||||
<span
|
||||
className="screen-reader-text"
|
||||
>
|
||||
Previous price:
|
||||
</span>
|
||||
<del
|
||||
className="wc-block-components-product-price__regular"
|
||||
>
|
||||
£1.00
|
||||
</del>
|
||||
<span
|
||||
className="screen-reader-text"
|
||||
>
|
||||
Discounted price:
|
||||
</span>
|
||||
<ins
|
||||
className="wc-block-components-product-price__value is-discounted"
|
||||
>
|
||||
£0.50
|
||||
</ins>
|
||||
</span>
|
||||
`;
|
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import TestRenderer from 'react-test-renderer';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import ProductPrice from '../index';
|
||||
|
||||
describe( 'ProductPrice', () => {
|
||||
const currency = {
|
||||
code: 'GBP',
|
||||
currency_code: 'GBP',
|
||||
currency_decimal_separator: '.',
|
||||
currency_minor_unit: 2,
|
||||
currency_prefix: '£',
|
||||
currency_suffix: '',
|
||||
currency_symbol: '£',
|
||||
currency_thousand_separator: ',',
|
||||
decimalSeparator: '.',
|
||||
minorUnit: 2,
|
||||
prefix: '£',
|
||||
price: '61400',
|
||||
price_range: null,
|
||||
raw_prices: {
|
||||
precision: 6,
|
||||
price: '614000000',
|
||||
regular_price: '614000000',
|
||||
sale_price: '614000000',
|
||||
},
|
||||
regular_price: '61400',
|
||||
sale_price: '61400',
|
||||
suffix: '',
|
||||
symbol: '£',
|
||||
thousandSeparator: ',',
|
||||
};
|
||||
|
||||
test( 'should use default price if no format is provided', () => {
|
||||
const component = TestRenderer.create(
|
||||
<ProductPrice
|
||||
price={ 50 }
|
||||
regularPrice={ 100 }
|
||||
currency={ currency }
|
||||
/>
|
||||
);
|
||||
|
||||
expect( component.toJSON() ).toMatchSnapshot();
|
||||
} );
|
||||
|
||||
test( 'should apply the format if one is provided', () => {
|
||||
const component = TestRenderer.create(
|
||||
<ProductPrice
|
||||
price={ 50 }
|
||||
regularPrice={ 100 }
|
||||
currency={ currency }
|
||||
format="pre price <price/> Test format"
|
||||
/>
|
||||
);
|
||||
|
||||
expect( component.toJSON() ).toMatchSnapshot();
|
||||
} );
|
||||
} );
|
Reference in New Issue
Block a user