Closed danrivett closed 1 year ago
The PR (#646) is built on top of the main
branch, but in the meantime I've also built patch files on top of the latest released version as of this comment (2.0.0-alpha.8
) in order for us to proceed with dependent stories. I've attached them below in case they are useful for anyone else.
They can be applied by:
patches
sub-directory.txt
sufficesAs they contain no runtime changes the patch is safe to consume by intermediate libraries in order to provide additional formatting options. We've done just that in our intermediate Money
library.
Here's a simplified example of what this PR/patches enable:
// Internal Shared Formatter
const sharedCreateFormatter = <T>(
locale: Intl.Locale,
numberFormatter: NumberFormatter<T>,
formattingOptionsFn?: FormattingOptionsProvider
): MoneyFormatter<T> => {
// This Transformer function returns a generic return type which requires the following PR to be merged into dinerojs:
// https://github.com/dinerojs/dinero.js/pull/646
// And in the meantime is being patched via patch-package
const transformer = (transformerOptions: TransformerOptions): T => {
const options = formattingOptionsFn ? formattingOptionsFn(transformerOptions) : undefined;
const format = new Intl.NumberFormat(locale.baseName, options);
return numberFormatter(transformerOptions, format);
};
return (money: Money) => toFormat(money, transformer);
};
// --------------------------
// External String Formatter
// --------------------------
const stringFormatter: NumberFormatter<string> = ({ amount }: TransformerOptions, numberFormat: Intl.NumberFormat) => numberFormat.format(amount);
export const createFormatter = (locale: Intl.Locale, style: FormatStyle | keyof typeof FormatStyle = FormatStyle.STANDARD): MoneyFormatter<string> =>
sharedCreateFormatter(locale, stringFormatter, createFormattingOptions(style));
// Locale is passed as an Intl.Locale Object for additional typesafety to avoid style parameter accidentally being set as locale if missed
export const formatMoney = (money: Money, locale: Intl.Locale, style?: FormatStyle | keyof typeof FormatStyle): string =>
createFormatter(locale, style)(money);
// -------------------------
// External Parts Formatter
// -------------------------
const partsFormatter: NumberFormatter<FormattedMoneyParts> = ({ amount, currency }: TransformerOptions, numberFormat: Intl.NumberFormat) => ({
amount,
currency: currency.code,
formattedParts: numberFormat.formatToParts(amount),
});
export const createPartsFormatter = (
locale: Intl.Locale,
style: FormatStyle | keyof typeof FormatStyle = FormatStyle.STANDARD
): MoneyFormatter<FormattedMoneyParts> => sharedCreateFormatter(locale, partsFormatter, createFormattingOptions(style));
// Locale is passed as an Intl.Locale Object for additional typesafety to avoid style parameter accidentally being set as locale if missed
export const formatMoneyIntoParts = (money: Money, locale: Intl.Locale, style?: FormatStyle | keyof typeof FormatStyle): FormattedMoneyParts =>
createPartsFormatter(locale, style)(money);
toFormat
was removed in v2.0.0-alpha.11
in favor of toDecimal
which supports a generic output.
Use Case
We ran into a scenario where we want to format a
Dinero
object in a user-specified locale but not to a singlestring
, but to its component parts - e.g. localized currency symbol, localized numeric amount in the localized order (e.g. currency symbol may prefix or suffix the numeric amount).The reason for doing that is so we can use a library such as react-native-currency-input where we can specify separate
prefix
andsuffix
components to render the localized currency symbol separate from the monetary numeric value.Currently the toFormat() function only supports a
Transformer
function which returns astring
, theTransformer
can't return a generic return type, even though there does not appear to be any reason not to.Existed Related Support
A common way of localizing monetary values is to use Intl.NumberFormat#format which returns a single
string
and so can already be used withtoFormat
:However
Intl.NumberFormat
also provides a formatToParts() function which localizes and formats a monetary value and returns an array of component parts that can be rendered individually as desired.Desired Solution
What is desired is for
toFormat()
to support a generic return type so that the following is also supported:For backwards compatibility that generic return type can default to a string, but the passed in formatter could return a different result object type if desired, like the above does.
Contribution
The changes required to support this use case are minimal and are fully backwards compatible (as far as I can tell) and so I've implemented it myself in #646 and submitted for review.
It's worth pointing out that this PR purely contribues type changes, and doesn't introduce any runtime changes.
I would love to see this merged in as this was motivated by a near-term upcoming user story to implement a localized
TextInput
field where the user just edits the monetary amount and the currency symbol is localized appropriately and rendered separately. There are also later stories that we believe will also make use of this.