diff --git a/source/components/conversation/FormDecorator.js b/source/components/conversation/FormDecorator.js index 5c8ff0ad0..53b166757 100644 --- a/source/components/conversation/FormDecorator.js +++ b/source/components/conversation/FormDecorator.js @@ -5,6 +5,7 @@ import { getFormatersFromUnit } from 'Engine/format' import React from 'react' import { useDispatch, useSelector } from 'react-redux' import { situationSelector } from 'Selectors/analyseSelectors' +import { useTranslation } from 'react-i18next' /* This higher order component wraps "Form" components (e.g. Question.js), that represent user inputs, @@ -15,9 +16,10 @@ to understand those precious higher order components. */ export const FormDecorator = formType => RenderField => - function({ fieldName, question, inversion, unit, ...otherProps }) { + function FormStep({ fieldName, question, inversion, unit, ...otherProps }) { const dispatch = useDispatch() const situation = useSelector(situationSelector) + const { language } = useTranslation().i18n const submit = source => dispatch({ @@ -30,7 +32,7 @@ export const FormDecorator = formType => RenderField => dispatch(updateSituation(fieldName, normalize(value))) } - const { format, normalize } = getFormatersFromUnit(unit) + const { format, normalize } = getFormatersFromUnit(unit, language) const value = format(situation[fieldName]) return ( @@ -48,6 +50,7 @@ export const FormDecorator = formType => RenderField => setFormValue={setFormValue} submit={submit} format={format} + normalize={normalize} unit={unit} {...otherProps} /> diff --git a/source/components/conversation/Input.js b/source/components/conversation/Input.js index 8e32f1cd8..9f62406fc 100644 --- a/source/components/conversation/Input.js +++ b/source/components/conversation/Input.js @@ -2,7 +2,7 @@ import classnames from 'classnames' import { T } from 'Components' import withColours from 'Components/utils/withColours' import { compose } from 'ramda' -import React, { useCallback, useState } from 'react' +import React, { useCallback } from 'react' import { usePeriod } from 'Selectors/analyseSelectors' import { debounce } from '../../utils' import { FormDecorator } from './FormDecorator' @@ -33,7 +33,7 @@ export default compose( { - setFormValue(format(value)) + setFormValue(value) }} onSecondClick={() => submit('suggestion')} rulePeriod={rulePeriod} @@ -45,7 +45,7 @@ export default compose( type="text" key={value} autoFocus - defaultValue={value} + value={format(value)} onChange={evt => { debouncedSetFormValue(evt.target.value) }} diff --git a/source/engine/format.js b/source/engine/format.js new file mode 100644 index 000000000..87216f700 --- /dev/null +++ b/source/engine/format.js @@ -0,0 +1,96 @@ +import { serialiseUnit } from 'Engine/units' +import { memoizeWith } from 'ramda' + +export const formatCurrency = (value, language) => { + return value == null + ? '' + : Intl.NumberFormat(language, { + style: 'currency', + currency: 'EUR', + maximumFractionDigits: 0, + minimumFractionDigits: 0 + }) + .format(value) + .replace(/^(-)?€/, '$1€\u00A0') +} + +const sanitizeValue = language => value => + language === 'fr' ? String(value).replace(',', '.') : value + +export const formatPercentage = value => +(value * 100).toFixed(2) +export const normalizePercentage = value => value / 100 + +export const getFormatersFromUnit = (unit, language = 'en') => { + const serializedUnit = typeof unit == 'object' ? serialiseUnit(unit) : unit + const sanitize = sanitizeValue(language) + switch (serializedUnit) { + case '%': + return { + format: numberFormatter({ style: 'percent', language }).replace( + ' %', + '' + ), + normalize: v => normalizePercentage(language)(sanitize(v)) + } + default: + return { + format: x => + Number(x) + ? numberFormatter({ style: 'decimal', language })(Number(x)) + : x, + normalize: x => sanitize(x) + } + } +} + +const NumberFormat = memoizeWith( + (...args) => JSON.stringify(args), + Intl.NumberFormat +) + +export let numberFormatter = ({ + style, + maximumFractionDigits = 2, + minimumFractionDigits = 0, + language +}) => value => + NumberFormat(language, { + style, + currency: 'EUR', + maximumFractionDigits, + minimumFractionDigits + }).format(value) + +export function formatValue({ + maximumFractionDigits, + minimumFractionDigits, + language, + unit, + value +}) { + const serializedUnit = typeof unit == 'object' ? serialiseUnit(unit) : unit + + switch (serializedUnit) { + case '€': + return numberFormatter({ + style: 'currency', + maximumFractionDigits, + minimumFractionDigits, + language + })(value) + case '%': + return numberFormatter({ style: 'percent', maximumFractionDigits })(value) + default: + if (typeof value !== 'number') { + return value + } + return ( + numberFormatter({ + style: 'decimal', + minimumFractionDigits, + maximumFractionDigits + })(value) + + (typeof serializedUnit === 'string' ? `\u00A0${serializedUnit}` : '') + ) + } +}