diff --git a/src/components/CreditCardInput.js b/src/components/CreditCardInput.js
index d676ac1b1..aabe0d3c0 100644
--- a/src/components/CreditCardInput.js
+++ b/src/components/CreditCardInput.js
@@ -13,8 +13,8 @@ export default class CreditCardInput extends Component {
const expirationIsValid = new Date(year, month) >= TODAY;
this.props.onChange({ expirationMonth: month, expirationYear: year, expirationIsValid });
}
- handleCardNumberChange = (cardNumber, cardNumberIsValid) => {
- this.props.onChange({ cardNumber, cardNumberIsValid });
+ handleCardNumberChange = ({ cardNumber, cardType }, cardNumberIsValid) => {
+ this.props.onChange({ cardNumber, cardType, cardNumberIsValid });
}
render() {
diff --git a/src/components/CreditCardNumber.js b/src/components/CreditCardNumber.js
index 1494f32f8..837d7f960 100644
--- a/src/components/CreditCardNumber.js
+++ b/src/components/CreditCardNumber.js
@@ -43,16 +43,19 @@ export default class CreditCardNumber extends Component {
setValue = (proposedValue) => {
let value = proposedValue.replace(/[^0-9]/g, '');
if (proposedValue === '') {
- this.props.onChange(value, false);
+ this.props.onChange(value, false, undefined);
this.setState({ value, cardType: undefined });
return;
}
const { card, isValid, isPotentiallyValid } = number(value);
- let cardType = undefined;
- if (card && card.type && includes(this.props.allowedBrands, card.type)) {
- cardType = typeToIconName(card.type);
+ let cardTypeIconName = undefined;
+ let cardTypeIsAllowed = false;
+
+ if (card && card.type) {
+ cardTypeIconName = typeToIconName(card.type);
+ cardTypeIsAllowed = includes(this.props.allowedBrands, card.type);
}
const typeInfo = cardTypeInfo(value);
@@ -70,15 +73,15 @@ export default class CreditCardNumber extends Component {
}
// Only accept the change if we recognize the card type, and it is/may be valid
- if (!this.props.restrictInput || cardType && (isValid || isPotentiallyValid)) {
- this.props.onChange(value, isValid);
- this.setState({ value, cardType, isValid });
+ if (!this.props.restrictInput || cardTypeIsAllowed && (isValid || isPotentiallyValid)) {
+ this.props.onChange({ cardNumber: value, cardType: card.type }, isValid);
+ this.setState({ value, cardTypeIconName, isValid });
}
}
render() {
const { placeholder } = this.props;
- const { cardType, value } = this.state;
+ const { cardTypeIconName, value } = this.state;
return (
@@ -87,9 +90,9 @@ export default class CreditCardNumber extends Component {
placeholder={placeholder} value={value}
onChange={this.onInputChange}
/>
- {cardType &&
+ {cardTypeIconName &&
-
+
}
@@ -103,7 +106,7 @@ CreditCardNumber.defaultProps = {
restrictInput: false,
value: '',
- onChange: (cardNumber, isValid) => true, // eslint-disable-line no-unused-vars
+ onChange: (cardNumber, isValid, cardType) => true, // eslint-disable-line no-unused-vars
};
CreditCardNumber.propTypes = {
allowedBrands: PropTypes.arrayOf(PropTypes.string),
diff --git a/test/components/CreditCardNumber.spec.js b/test/components/CreditCardNumber.spec.js
index d236ea2c0..f087c46a6 100644
--- a/test/components/CreditCardNumber.spec.js
+++ b/test/components/CreditCardNumber.spec.js
@@ -2,18 +2,23 @@
import React from 'react';
import assert from 'assert';
import { mount, shallow } from 'enzyme';
+import sinon from 'sinon';
import CreditCardNumber from '../../src/components/CreditCardNumber';
import Icon from '../../src/components/Icon';
const EXAMPLES = {
+ 'american-express': '378282246310005',
'diners-club': '30569309025904',
- amex: '378282246310005',
+ 'master-card': '5555555555554444',
discover: '6011111111111117',
jcb: '3530111333300000',
- mastercard: '5555555555554444',
visa: '4111111111111111',
};
+const ICON_MAP = {
+ 'american-express': 'amex',
+ 'master-card': 'mastercard',
+};
describe('', () => {
it('should render no icon, by default', () => {
@@ -30,16 +35,22 @@ describe('', () => {
assert.equal(component.find(Icon).prop('name'), 'cc-visa');
});
- it('should render correct icons for valid card numbers', () => {
+ it('should report/render icon for correct cardType for valid numbers', () => {
Object.keys(EXAMPLES).forEach(key => {
- const expectedIcon = `cc-${key}`;
const cardNumber = EXAMPLES[key];
+ const onChange = sinon.spy();
- const component = mount();
+ const component = mount();
const input = component.find('input');
input.simulate('change', { target: { value: cardNumber } });
- assert.equal(component.find(Icon).prop('name'), expectedIcon);
+ assert(onChange.called);
+ const [values, returnedIsValid] = [...onChange.lastCall.args];
+ assert.equal(values.cardNumber.replace(/ /g, ''), cardNumber);
+ assert.equal(values.cardType, key);
+ assert.equal(returnedIsValid, true);
+
+ assert.equal(component.find(Icon).prop('name'), `cc-${ICON_MAP[key] || key}`);
});
});
@@ -65,7 +76,7 @@ describe('', () => {
it('restrictInput prop should reject numbers for invalid card types', () => {
const component = mount();
const input = component.find('input');
- input.simulate('change', { target: { value: EXAMPLES.mastercard } });
+ input.simulate('change', { target: { value: EXAMPLES['master-card'] } });
assert.equal(input.get(0).value, '');
assert.equal(component.find(Icon).length, 0);