1 | import React, { PureComponent, Fragment } from 'react';
|
2 | import { number, string, shape } from 'prop-types';
|
3 | import patches from '../util/intlPatches';
|
4 |
|
5 | /**
|
6 | * The **Price** component is used anywhere a price needs to be displayed.
|
7 | *
|
8 | * Formatting of prices and currency symbol selection is handled entirely by the ECMAScript Internationalization API available in modern browsers.
|
9 | *
|
10 | * A [polyfill][] is required for any JavaScript runtime that does not have [Intl.NumberFormat.prototype.formatToParts][].
|
11 | *
|
12 | * [polyfill]: https://www.npmjs.com/package/intl
|
13 | * [Intl.NumberFormat.prototype.formatToParts]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat/formatToParts
|
14 | */
|
15 | export default class Price extends PureComponent {
|
16 | static propTypes = {
|
17 | /**
|
18 | * The numeric price
|
19 | */
|
20 | value: number.isRequired,
|
21 | /**
|
22 | * A string with any of the currency code supported by Intl.NumberFormat
|
23 | */
|
24 | currencyCode: string.isRequired,
|
25 | /**
|
26 | * Class names to use when styling this component
|
27 | */
|
28 | classes: shape({
|
29 | currency: string,
|
30 | integer: string,
|
31 | decimal: string,
|
32 | fraction: string
|
33 | })
|
34 | };
|
35 |
|
36 | static defaultProps = {
|
37 | classes: {}
|
38 | };
|
39 |
|
40 | render() {
|
41 | const { value, currencyCode, classes } = this.props;
|
42 |
|
43 | const parts = patches.toParts.call(
|
44 | Intl.NumberFormat(undefined, {
|
45 | style: 'currency',
|
46 | currency: currencyCode
|
47 | }),
|
48 | value
|
49 | );
|
50 |
|
51 | const children = parts.map((part, i) => {
|
52 | const partClass = classes[part.type];
|
53 | const key = `${i}-${part.value}`;
|
54 |
|
55 | return (
|
56 | <span key={key} className={partClass}>
|
57 | {part.value}
|
58 | </span>
|
59 | );
|
60 | });
|
61 |
|
62 | return <Fragment>{children}</Fragment>;
|
63 | }
|
64 | }
|