initial commit

This commit is contained in:
2021-12-10 12:03:04 +00:00
commit c46c7ddbf0
3643 changed files with 582794 additions and 0 deletions

View File

@ -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 }
/>
&nbsp;&mdash;&nbsp;
<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;

View File

@ -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 }
/>
);
};

View File

@ -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;
}
}

View File

@ -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>
`;

View File

@ -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();
} );
} );