diff --git a/src/components/inputs/money-input/money-input.form.story.js b/src/components/inputs/money-input/money-input.form.story.js
index ed0c015d49..98574afb40 100644
--- a/src/components/inputs/money-input/money-input.form.story.js
+++ b/src/components/inputs/money-input/money-input.form.story.js
@@ -33,20 +33,15 @@ storiesOf('Examples|Forms/Inputs', module)
{
+ onSubmit={(values, formik) => {
// eslint-disable-next-line no-console
console.log(
'money value',
MoneyInput.convertToMoneyValue(values.price)
);
- action('onSubmit')(values, formik, ...rest);
+ action('onSubmit')(values, formik);
formik.resetForm(values);
}}
render={formik => (
@@ -58,6 +53,11 @@ storiesOf('Examples|Forms/Inputs', module)
value={formik.values.price}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
+ hasCurrencyError={Boolean(
+ formik.touched.price &&
+ formik.touched.price.currencyCode &&
+ formik.errors.price
+ )}
hasAmountError={Boolean(
formik.touched.price &&
formik.touched.price.amount &&
diff --git a/src/components/inputs/money-input/money-input.js b/src/components/inputs/money-input/money-input.js
index 49103e5038..c02c3a0cde 100644
--- a/src/components/inputs/money-input/money-input.js
+++ b/src/components/inputs/money-input/money-input.js
@@ -279,7 +279,17 @@ export default class MoneyInput extends React.Component {
: formattedAmount,
},
};
- this.props.onChange(fakeEvent);
+ // There is a bug in Formik at the moment where the validation will run
+ // with wrong values when "handleChange" is called consecutively before
+ // Formik gets a chance to rerun.
+ // PR with fix is open: https://github.com/jaredpalmer/formik/pull/939
+ // Once merged, we can remove the setTimeout call and call
+ // onChange directly.
+ // While the setTimeout workaround is in place, the error messages
+ // will flicker (appear and disappear) for a split-second.
+ setTimeout(() => {
+ this.props.onChange(fakeEvent);
+ }, 0);
}
}
toggleMenu();
diff --git a/src/components/inputs/money-input/money-input.story.js b/src/components/inputs/money-input/money-input.story.js
index aa0e50aa59..044cc2daef 100644
--- a/src/components/inputs/money-input/money-input.story.js
+++ b/src/components/inputs/money-input/money-input.story.js
@@ -3,70 +3,76 @@ import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { withKnobs, boolean, text, select } from '@storybook/addon-knobs';
import withReadme from 'storybook-readme/with-readme';
-import { Value } from 'react-value';
import Section from '../../../../.storybook/decorators/section';
import MoneyInputReadme from './README.md';
import MoneyInput from './money-input';
-storiesOf('Inputs', module)
- .addDecorator(withKnobs)
- .addDecorator(withReadme(MoneyInputReadme))
- .add('MoneyInput', () => {
+// This uses a dedicated story component to keep track of state instead of
+// react-value. The reason is that MoneyInput can call twice onChange before
+// the component rerenders, so we'd need to use two separate
+// components to not lose data. So we use a dedicated component instead.
+// That makes it easier to log the parsed value as well.
+class MoneyInputStory extends React.Component {
+ static displayName = 'MoneyInputStory';
+
+ state = {
+ amount: '',
+ currencyCode: '',
+ };
+
+ componentDidUpdate(prevState) {
+ if (
+ prevState.amount !== this.state.amount ||
+ prevState.currencyCode !== this.state.currencyCode
+ ) {
+ // eslint-disable-next-line no-console
+ console.log(
+ 'parsed',
+ MoneyInput.convertToMoneyValue({
+ amount: this.state.amount,
+ currencyCode: this.state.currencyCode,
+ })
+ );
+ }
+ }
+
+ render() {
const currencies = ['EUR', 'USD', 'AED', 'KWD'];
- const defaultCurrencyCode = select(
- 'default value currencyCode',
- ['', ...currencies],
- ''
- );
- const defaultAmount = text('default value amount', '');
const name = text('name', '') || 'default-name';
+ const value = {
+ amount: this.state.amount,
+ currencyCode: this.state.currencyCode,
+ };
return (
- (
- {
- action('onChange')(event);
-
- const nextMoney = do {
- if (event.target.name.endsWith('.amount')) {
- ({ ...value, amount: event.target.value });
- } else if (event.target.name.endsWith('.currencyCode')) {
- ({ ...value, currencyCode: event.target.value });
- }
- };
+ {
+ action('onChange')(event);
- onChange(nextMoney);
+ if (event.target.name.endsWith('.amount')) {
+ this.setState({ amount: event.target.value });
+ }
- // eslint-disable-next-line no-console
- console.log(
- 'parsed',
- MoneyInput.convertToMoneyValue(nextMoney)
- );
- }}
- hasCurrencyError={boolean('hasCurrencyError', false)}
- hasCurrencyWarning={boolean('hasCurrencyWarning', false)}
- hasAmountError={boolean('hasAmountError', false)}
- hasAmountWarning={boolean('hasAmountWarning', false)}
- horizontalConstraint={select(
- 'horizontalConstraint',
- ['s', 'm', 'l', 'xl', 'scale'],
- 'm'
- )}
- />
+ if (event.target.name.endsWith('.currencyCode')) {
+ this.setState({ currencyCode: event.target.value });
+ }
+ }}
+ hasCurrencyError={boolean('hasCurrencyError', false)}
+ hasCurrencyWarning={boolean('hasCurrencyWarning', false)}
+ hasAmountError={boolean('hasAmountError', false)}
+ hasAmountWarning={boolean('hasAmountWarning', false)}
+ horizontalConstraint={select(
+ 'horizontalConstraint',
+ ['s', 'm', 'l', 'xl', 'scale'],
+ 'm'
)}
/>
@@ -80,4 +86,10 @@ storiesOf('Inputs', module)
);
- });
+ }
+}
+
+storiesOf('Inputs', module)
+ .addDecorator(withKnobs)
+ .addDecorator(withReadme(MoneyInputReadme))
+ .add('MoneyInput', () => );