mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-24 19:33:02 +03:00
✨ Updated price helper to output well formatted prices
refs https://github.com/TryGhost/Team/issues/472 The current `{{price}}` helper only works with `amount` to convert it into right value but doesn't allow any formatting with currency etc, leaving most of the work to theme. We want to be able to output well formatted prices. E.g. the API returns 5000 for 5 EUR but we want to output €5. The updated {{price}} helper can take a plan object or plan amount currency and use them to output a well formatted price. It works with JS's built in Intl.NumberFormat behaviour to return output in different formats, also allowing theme devs to override formatting with options. Examples: With Plan object => `{{price plan}} → "€5"` With Plan object and custom number format => `{{price plan numberFormat="long"}} → "€5.00"` Output only currency symbol => `{{price currency='EUR'}} → "€"`
This commit is contained in:
parent
30371b0986
commit
67bf3a77c1
@ -1,13 +1,79 @@
|
||||
// # {{price}} helper
|
||||
//
|
||||
// Usage: `{{price 2100}}`
|
||||
// Usage: `{{price plan}}`
|
||||
// Usage: `{{price plan numberFormat="long"}}`
|
||||
// Usage: `{{price plan currencyFormat="code"}}`
|
||||
// Usage: `{{price plan currencyFormat="name"}}`
|
||||
// Usage: `{{price 500 currency="USD"}}`
|
||||
// Usage: `{{price currency="USD"}}`
|
||||
//
|
||||
// Returns amount equal to the dominant denomintation of the currency.
|
||||
// For example, if 2100 is passed, it will return 21.
|
||||
const isNumber = require('lodash/isNumber');
|
||||
const {errors, i18n} = require('../services/proxy');
|
||||
const _ = require('lodash');
|
||||
|
||||
function formatter({amount, currency, numberFormat = 'short', currencyFormat = 'symbol', locale}) {
|
||||
const formatterOptions = {
|
||||
style: 'currency',
|
||||
currency: currency,
|
||||
currencyDisplay: currencyFormat
|
||||
};
|
||||
if (numberFormat === 'short') {
|
||||
formatterOptions.minimumFractionDigits = 0;
|
||||
}
|
||||
if (amount) {
|
||||
return new Intl.NumberFormat(locale, formatterOptions).format(amount);
|
||||
} else {
|
||||
const val = new Intl.NumberFormat('en', {
|
||||
style: 'currency',
|
||||
currency,
|
||||
currencyDisplay: 'symbol',
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0
|
||||
}).format(0);
|
||||
return val.replace(/[\d\s.,]/g, '');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function price(planOrAmount, options) {
|
||||
let plan;
|
||||
let amount;
|
||||
if (arguments.length === 1) {
|
||||
options = planOrAmount;
|
||||
}
|
||||
|
||||
if (arguments.length === 2) {
|
||||
if (_.isNumber(planOrAmount)) {
|
||||
amount = planOrAmount;
|
||||
} else if (_.isObject(planOrAmount)) {
|
||||
plan = planOrAmount;
|
||||
}
|
||||
}
|
||||
options = options || {};
|
||||
options.hash = options.hash || {};
|
||||
const {currency, numberFormat = 'short', currencyFormat = 'symbol', locale = _.get(options, 'data.site.lang', 'en')} = options.hash;
|
||||
if (plan) {
|
||||
return formatter({
|
||||
amount: plan.amount && (plan.amount / 100),
|
||||
currency: currency || plan.currency,
|
||||
currencyFormat,
|
||||
numberFormat,
|
||||
locale
|
||||
});
|
||||
}
|
||||
|
||||
if (currency) {
|
||||
return formatter({
|
||||
amount: amount && (amount / 100),
|
||||
currency: currency,
|
||||
currencyFormat,
|
||||
numberFormat,
|
||||
locale
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = function price(amount) {
|
||||
// CASE: if no amount is passed, e.g. `{{price}}` we throw an error
|
||||
if (arguments.length < 2) {
|
||||
throw new errors.IncorrectUsageError({
|
||||
|
@ -42,4 +42,63 @@ describe('{{price}} helper', function () {
|
||||
.with({})
|
||||
.should.equal('20');
|
||||
});
|
||||
|
||||
it('will format with plan object', function () {
|
||||
const plan = {
|
||||
nickname: 'Monthly',
|
||||
amount: 500,
|
||||
interval: 'month',
|
||||
currency: 'USD',
|
||||
currency_symbol: '$'
|
||||
};
|
||||
const rendered = helpers.price.call({}, plan, {});
|
||||
rendered.should.be.equal('$5');
|
||||
});
|
||||
|
||||
it('will format with plan object with number format', function () {
|
||||
const plan = {
|
||||
nickname: 'Monthly',
|
||||
amount: 500,
|
||||
interval: 'month',
|
||||
currency: 'USD',
|
||||
currency_symbol: '$'
|
||||
};
|
||||
const rendered = helpers.price.call({}, plan, {hash: {numberFormat: 'long'}});
|
||||
rendered.should.be.equal('$5.00');
|
||||
});
|
||||
|
||||
it('will format symbol if only currency - USD', function () {
|
||||
const rendered = helpers.price.call({}, {hash: {currency: 'USD'}});
|
||||
rendered.should.be.equal('$');
|
||||
});
|
||||
|
||||
it('will format symbol if only currency - EUR', function () {
|
||||
const rendered = helpers.price.call({}, {hash: {currency: 'EUR'}});
|
||||
rendered.should.be.equal('€');
|
||||
});
|
||||
|
||||
it('will format with amount and currency', function () {
|
||||
const rendered = helpers.price.call({}, 500, {hash: {currency: 'USD'}});
|
||||
rendered.should.be.equal('$5');
|
||||
});
|
||||
|
||||
it('will format with long number format', function () {
|
||||
const rendered = helpers.price.call({}, 500, {hash: {currency: 'USD', numberFormat: 'long'}});
|
||||
rendered.should.be.equal('$5.00');
|
||||
});
|
||||
|
||||
it('will format with short number format with decimal value', function () {
|
||||
const rendered = helpers.price.call({}, 505, {hash: {currency: 'EUR', numberFormat: 'short'}});
|
||||
rendered.should.be.equal('€5.05');
|
||||
});
|
||||
|
||||
it('will format with short number format without decimal value', function () {
|
||||
const rendered = helpers.price.call({}, 500, {hash: {currency: 'EUR', numberFormat: 'short'}});
|
||||
rendered.should.be.equal('€5');
|
||||
});
|
||||
|
||||
it('will format with name currency format', function () {
|
||||
const rendered = helpers.price.call({}, 500, {hash: {currency: 'USD', currencyFormat: 'name'}});
|
||||
rendered.should.be.equal('5 US dollars');
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user