@financial-times/n-conversion-forms
Version:
Containing jsx components and styles for forms included on Accounts and Acqusition apps (next-signup, next-profile, next-retention, etc).
139 lines (123 loc) • 3.99 kB
JavaScript
const { Period, Monthly } = require('@financial-times/n-pricing');
/**
* Utility for the `n-conversion-forms/partial/payment-term.html` partial
* @example
* const paymentTerm = new PaymentTerm(document);
*
* // Return the currently selected payment term
* paymentTerm.getSelected();
*
* // Update the payment term options displayed
* const options = [{
* name: 'Name of term',
* value: 'Value to send',
* description: 'Can contain <strong>HTML</strong>'
* }];
* paymentTerm.updateOptions(options);
*/
const ITEM_CLASS = '.ncf__payment-term__item';
const VALUE_CLASS = '.ncf__payment-term__item input';
const PRICE_CLASS = '.ncf__payment-term__price';
const TRIAL_PRICE_CLASS = '.ncf__payment-term__trial-price';
const MONTHLY_PRICE_CLASS = '.ncf__payment-term__monthly-price';
class PaymentTerm {
/**
* Initalise the PaymentTerm utility
* @param {Element} element Usually the window.document
* @throws If the document not passed
* @throws When the paymentTermField element not found
*/
constructor(element) {
if (!element) {
throw new Error('Please supply a DOM element');
}
this.element = element;
this.$paymentTerm = element.querySelector('.ncf #paymentTermField');
if (!this.$paymentTerm) {
throw new Error('Please include the payment term partial on the page');
}
}
/**
* Returns the value of the currently selected item
* @return {String}
* @throws If nothing has been selected
*/
getSelected() {
const checked = this.$paymentTerm.querySelector('input:checked');
if (!checked) {
throw new Error('No payment term has been selected');
}
return checked.getAttribute('value');
}
/**
* Returns the base amount for the selected payment term
* @return {Number}
* @throws If no payment term has been selected
*/
getBaseAmount() {
const checked = this.$paymentTerm.querySelector('input:checked');
if (!checked) {
throw new Error('No payment term has been selected');
}
return checked.dataset.baseAmount;
}
/**
* Register on change an event listener
* @param {Function} callback Called with event when changed
*/
onChange(callback = () => {}) {
return this.$paymentTerm.addEventListener('change', callback);
}
isValidPeriod(period) {
try {
// Period should throw an error if it is not properly provided
// in order to validate it, we just send in case type is string
new Period(typeof period === 'string' ? period : '');
return true;
} catch (e) {
return false;
}
}
getMontlyPriceFromPeriod(amount, currency, period) {
const periodObj = new Period(period);
const monthlyPrice = periodObj.calculatePrice('P1M', amount);
return new Monthly({ value: monthlyPrice, currency }).getAmount('monthly');
}
/**
* Update the payment term options
* @param {Array} options Array of objects contain terms information
*/
updateOptions(options) {
const terms = this.$paymentTerm.querySelectorAll(ITEM_CLASS);
for (let i = 0; i < terms.length; i++) {
const term = terms[i];
const value = term.querySelector(VALUE_CLASS).value;
const price = term.querySelector(PRICE_CLASS);
const trialPrice = term.querySelector(TRIAL_PRICE_CLASS);
const monthlyPrice = term.querySelector(MONTHLY_PRICE_CLASS);
const update = options.find((option) => option.value === value);
if (!update) {
throw new Error(`Payment term update not found for "${value}"`);
}
const baseAmount = update.isTrial ? update.trialAmount : update.amount;
term.setAttribute('data-base-amount', baseAmount);
// Update prices if they are found in the term
if (price) {
price.innerHTML = update.price;
}
if (trialPrice) {
trialPrice.innerHTML = update.trialPrice;
}
if (monthlyPrice) {
monthlyPrice.innerHTML = this.isValidPeriod(update.value)
? this.getMontlyPriceFromPeriod(
update.amount,
update.currency,
update.value
)
: update.monthlyPrice;
}
}
}
}
module.exports = PaymentTerm;