Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(money-input): add formatting workaround #102

Merged
merged 1 commit into from
Sep 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions src/components/inputs/money-input/money-input.form.story.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,15 @@ storiesOf('Examples|Forms/Inputs', module)
<Section>
<IntlProvider locale="en">
<Formik
initialValues={{
price: MoneyInput.parseMoneyValue({
currencyCode: 'EUR',
centAmount: 1200,
}),
}}
initialValues={{ price: { currencyCode: '', amount: '' } }}
validate={validate}
onSubmit={(values, formik, ...rest) => {
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 => (
Expand All @@ -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 &&
Expand Down
12 changes: 11 additions & 1 deletion src/components/inputs/money-input/money-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
120 changes: 66 additions & 54 deletions src/components/inputs/money-input/money-input.story.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 <Value />
// 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 (
<React.Fragment>
<Section>
<Value
key={`${defaultCurrencyCode}-${defaultAmount}`}
defaultValue={{
amount: defaultAmount,
currencyCode: defaultCurrencyCode,
}}
render={(value, onChange) => (
<MoneyInput
id={text('id', '')}
name={name}
value={value}
currencies={boolean('dropdown', true) ? currencies : undefined}
placeholder={text('placeholder', 'Placeholder')}
onBlur={action('onBlur')}
isDisabled={boolean('isDisabled', false)}
onChange={event => {
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 });
}
};
<MoneyInput
id={text('id', '')}
name={name}
value={value}
currencies={boolean('dropdown', true) ? currencies : undefined}
placeholder={text('placeholder', 'Placeholder')}
onBlur={action('onBlur')}
isDisabled={boolean('isDisabled', false)}
onChange={event => {
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'
)}
/>
</Section>
Expand All @@ -80,4 +86,10 @@ storiesOf('Inputs', module)
</Section>
</React.Fragment>
);
});
}
}

storiesOf('Inputs', module)
.addDecorator(withKnobs)
.addDecorator(withReadme(MoneyInputReadme))
.add('MoneyInput', () => <MoneyInputStory />);