Skip to content

Commit

Permalink
fix(checkout): CHECKOUT-5006 Changes for formatting date as per store…
Browse files Browse the repository at this point in the history
… config
  • Loading branch information
ankurbigcomm committed Jul 20, 2020
1 parent ebecc0b commit a81656d
Show file tree
Hide file tree
Showing 17 changed files with 160 additions and 44 deletions.
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
},
"homepage": "https://github.com/bigcommerce/checkout-js#readme",
"dependencies": {
"@bigcommerce/checkout-sdk": "^1.82.3",
"@bigcommerce/checkout-sdk": "^1.82.4",
"@bigcommerce/citadel": "^2.15.1",
"@bigcommerce/form-poster": "^1.2.2",
"@bigcommerce/memoize": "^1.0.0",
Expand Down
65 changes: 45 additions & 20 deletions src/app/address/DynamicInput.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,39 @@
import { shallow } from 'enzyme';
import React from 'react';
import { mount } from 'enzyme';
import React, { FunctionComponent } from 'react';
import ReactDatePicker from 'react-datepicker';

import { getStoreConfig } from '../config/config.mock';
import { createLocaleContext, LocaleContext, LocaleContextType, WithDateProps } from '../locale';
import { CheckboxInput, RadioInput, TextArea, TextInput } from '../ui/form';

import DynamicFormFieldType from './DynamicFormFieldType';
import DynamicInput from './DynamicInput';
import DynamicInput, { DynamicInputProps } from './DynamicInput';

describe('DynamicInput', () => {
let localeContext: Required<LocaleContextType>;
let date: Required<LocaleContextType>['date'];
let DynamicInputTest: FunctionComponent<DynamicInputProps & WithDateProps>;

beforeEach(() => {
localeContext = createLocaleContext(getStoreConfig());
date = localeContext.date;

DynamicInputTest = props => (
<LocaleContext.Provider value={ localeContext }>
<DynamicInput { ...props } />
</LocaleContext.Provider>
);
});

it('renders text input for default input with passed props', () => {
expect(shallow(<DynamicInput id="field_33" name="x" />).html())
expect(mount(<DynamicInputTest date={ date } id="field_33" name="x" />).html())
.toMatchSnapshot();
});

it('renders textarea for multiline type', () => {
expect(shallow(
<DynamicInput
expect(mount(
<DynamicInputTest
date={ date }
fieldType={ DynamicFormFieldType.multiline }
id="field_33"
rows={ 4 }
Expand All @@ -26,8 +44,9 @@ describe('DynamicInput', () => {
});

it('renders date picker for date type', () => {
const datePicker = shallow(
<DynamicInput
const datePicker = mount(
<DynamicInputTest
date={ date }
fieldType={ DynamicFormFieldType.date }
id="field_33"
max="2019-2-1"
Expand All @@ -43,8 +62,9 @@ describe('DynamicInput', () => {
});

it('renders checkbox input for checkbox type', () => {
const component = shallow(
<DynamicInput
const component = mount(
<DynamicInputTest
date={ date }
fieldType={ DynamicFormFieldType.checkbox }
id="id"
options={ [
Expand All @@ -68,8 +88,9 @@ describe('DynamicInput', () => {
});

it('renders radio type input for radio type', () => {
const component = shallow(
<DynamicInput
const component = mount(
<DynamicInputTest
date={ date }
fieldType={ DynamicFormFieldType.radio }
id="id"
onChange={ jest.fn() }
Expand All @@ -94,8 +115,9 @@ describe('DynamicInput', () => {
});

it('renders number type input for number type', () => {
expect(shallow(
<DynamicInput
expect(mount(
<DynamicInputTest
date={ date }
fieldType={ DynamicFormFieldType.number }
id="field_33"
/>)
Expand All @@ -105,8 +127,9 @@ describe('DynamicInput', () => {
});

it('renders password type input for password type', () => {
expect(shallow(
<DynamicInput
expect(mount(
<DynamicInputTest
date={ date }
fieldType={ DynamicFormFieldType.password }
id="field_33"
/>)
Expand All @@ -116,8 +139,9 @@ describe('DynamicInput', () => {
});

it('renders tel type input for phone type', () => {
expect(shallow(
<DynamicInput
expect(mount(
<DynamicInputTest
date={ date }
fieldType={ DynamicFormFieldType.telephone }
id="field_33"
/>)
Expand All @@ -127,8 +151,9 @@ describe('DynamicInput', () => {
});

it('renders select input with passed props', () => {
expect(shallow(
<DynamicInput
expect(mount(
<DynamicInputTest
date={ date }
defaultValue="foo"
fieldType={ DynamicFormFieldType.dropdown }
id="field_33"
Expand Down
14 changes: 9 additions & 5 deletions src/app/address/DynamicInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { isDate, noop } from 'lodash';
import React, { memo, useCallback, FunctionComponent } from 'react';
import ReactDatePicker from 'react-datepicker';

import { withDate, WithDateProps } from '../locale';
import { CheckboxInput, InputProps, RadioInput, TextArea, TextInput } from '../ui/form';

import DynamicFormFieldType from './DynamicFormFieldType';
Expand All @@ -16,8 +17,9 @@ export interface DynamicInputProps extends InputProps {
options?: FormFieldItem[];
}

const DynamicInput: FunctionComponent<DynamicInputProps> = ({
const DynamicInput: FunctionComponent<DynamicInputProps & WithDateProps> = ({
additionalClassName,
date,
fieldType,
id,
name,
Expand All @@ -27,11 +29,12 @@ const DynamicInput: FunctionComponent<DynamicInputProps> = ({
value,
...rest
}) => {
const handleDateChange = useCallback((date, event) => onChange({
const { inputFormat } = date;
const handleDateChange = useCallback((dateValue, event) => onChange({
...event,
target: {
name,
value: date,
value: dateValue,
},
}), [
onChange,
Expand Down Expand Up @@ -115,11 +118,12 @@ const DynamicInput: FunctionComponent<DynamicInputProps> = ({
// onChangeRaw={ rest.onChange }
calendarClassName="optimizedCheckout-contentPrimary"
className="form-input optimizedCheckout-form-input"
dateFormat={ inputFormat }
maxDate={ rest.max ? new Date(`${rest.max} 00:00:00`) : undefined }
minDate={ rest.min ? new Date(`${rest.min} 00:00:00`) : undefined }
name={ name }
onChange={ handleDateChange }
placeholderText="MM/DD/YYYY"
placeholderText={ inputFormat.toUpperCase() }
popperClassName="optimizedCheckout-contentPrimary"
selected={ isDate(value) ? value : undefined }
/>
Expand Down Expand Up @@ -155,4 +159,4 @@ const DynamicInput: FunctionComponent<DynamicInputProps> = ({
}
};

export default memo(DynamicInput);
export default memo(withDate(DynamicInput));
4 changes: 2 additions & 2 deletions src/app/address/__snapshots__/DynamicInput.spec.tsx.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`DynamicInput renders select input with passed props 1`] = `"<select class=\\"form-select optimizedCheckout-form-select\\" data-test=\\"field_33-select\\" id=\\"field_33\\" name=\\"select\\"><option value=\\"\\">Select an option</option><option selected=\\"\\" value=\\"foo\\">Foo</option><option value=\\"foo1\\">Foo1</option></select>"`;
exports[`DynamicInput renders select input with passed props 1`] = `"<select class=\\"form-select optimizedCheckout-form-select\\" data-test=\\"field_33-select\\" id=\\"field_33\\" name=\\"select\\"><option value=\\"\\">Select an option</option><option value=\\"foo\\" selected=\\"\\">Foo</option><option value=\\"foo1\\">Foo1</option></select>"`;
exports[`DynamicInput renders text input for default input with passed props 1`] = `"<input type=\\"text\\" id=\\"field_33\\" name=\\"x\\" class=\\"form-input optimizedCheckout-form-input\\" data-test=\\"field_33-text\\"/>"`;
exports[`DynamicInput renders text input for default input with passed props 1`] = `"<input id=\\"field_33\\" name=\\"x\\" class=\\"form-input optimizedCheckout-form-input\\" type=\\"text\\" data-test=\\"field_33-text\\" value=\\"\\">"`;
2 changes: 2 additions & 0 deletions src/app/config/config.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { StoreConfig } from '@bigcommerce/checkout-sdk';
export function getStoreConfig(): StoreConfig {
return {
cdnPath: 'https://cdn.bcapp.dev/rHEAD',
inputDateFormat: 'dd/MM/yyyy',
displayDateFormat: 'dd/MM/yyyy',
formFields: {
shippingAddressFields: [],
billingAddressFields: [],
Expand Down
3 changes: 3 additions & 0 deletions src/app/locale/LocaleContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { createContext } from 'react';

export interface LocaleContextType {
language: LanguageService;
date?: {
inputFormat: string;
};
currency?: CurrencyService;
}

Expand Down
8 changes: 7 additions & 1 deletion src/app/locale/LocaleProvider.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ describe('LocaleProvider', () => {

expect(component.find(Child).prop('language'))
.toBeDefined();

expect(component.find(Child).prop('date'))
.toBeDefined();
});

it('provides locale context without currency service to child components when config is not available yet', () => {
it('provides locale context without currency service and date to child components when config is not available yet', () => {
jest.spyOn(checkoutService.getState().data, 'getConfig')
.mockReturnValue(undefined);

Expand All @@ -52,5 +55,8 @@ describe('LocaleProvider', () => {

expect(component.find(Child).prop('language'))
.toBeDefined();

expect(component.find(Child).prop('date'))
.not.toBeDefined();
});
});
4 changes: 4 additions & 0 deletions src/app/locale/LocaleProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ class LocaleProvider extends Component<LocaleProviderProps> {
private unsubscribe?: () => void;

private getContextValue = memoizeOne((config?: StoreConfig) => {

return {
currency: config ? createCurrencyService(config) : undefined,
date: config ? {
inputFormat: config.inputDateFormat,
} : undefined,
language: this.languageService,
};
});
Expand Down
6 changes: 6 additions & 0 deletions src/app/locale/createLocaleContext.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,10 @@ describe('createLocaleContext', () => {
expect(localeContext).toHaveProperty('language');
expect(localeContext.language.translate).toBeDefined();
});

it('returns an object with date', () => {
expect(localeContext).toHaveProperty('date');
// tslint:disable-next-line:no-non-null-assertion
expect(localeContext.date!.inputFormat).toBeDefined();
});
});
5 changes: 5 additions & 0 deletions src/app/locale/createLocaleContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ export default function createLocaleContext(config: StoreConfig): Required<Local
throw new Error('Missing configuration data');
}

const { inputDateFormat } = config;

return {
currency: createCurrencyService(config),
date: {
inputFormat: inputDateFormat,
},
language: getLanguageService(),
};
}
3 changes: 3 additions & 0 deletions src/app/locale/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { WithCurrencyProps } from './withCurrency';
import { WithDateProps } from './withDate';
import { WithLanguageProps } from './withLanguage';
import { LocaleContextType } from './LocaleContext';
import { TranslatedHtmlProps } from './TranslatedHtml';
Expand All @@ -7,6 +8,7 @@ import { TranslatedStringProps } from './TranslatedString';

export type LocaleContextType = LocaleContextType;
export type WithCurrencyProps = WithCurrencyProps;
export type WithDateProps = WithDateProps;
export type WithLanguageProps = WithLanguageProps;
export type TranslatedHtmlProps = TranslatedHtmlProps;
export type TranslatedStringProps = TranslatedStringProps;
Expand All @@ -17,6 +19,7 @@ export { default as createLocaleContext } from './createLocaleContext';
export { default as getLanguageService } from './getLanguageService';
export { default as withCurrency } from './withCurrency';
export { default as withLanguage } from './withLanguage';
export { default as withDate } from './withDate';
export { default as LocaleProvider } from './LocaleProvider';
export { default as TranslatedHtml } from './TranslatedHtml';
export { default as TranslatedLink } from './TranslatedLink';
Expand Down
3 changes: 3 additions & 0 deletions src/app/locale/localeContext.mock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import { LocaleContextType } from './LocaleContext';
export function getLocaleContext(): Required<LocaleContextType> {
return {
currency: createCurrencyService(getStoreConfig()),
date: {
inputFormat: 'dd/mm/yyyy',
},
language: createLanguageService({
...(window as any).language,
defaultTranslations: DEFAULT_TRANSLATIONS,
Expand Down
29 changes: 29 additions & 0 deletions src/app/locale/withDate.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { mount } from 'enzyme';
import React from 'react';

import { getStoreConfig } from '../config/config.mock';

import createLocaleContext from './createLocaleContext';
import withDate from './withDate';
import LocaleContext, { LocaleContextType } from './LocaleContext';

describe('withDate()', () => {
let contextValue: Required<LocaleContextType>;

beforeEach(() => {
contextValue = createLocaleContext(getStoreConfig());
});

it('injects date prop to inner component', () => {
const Inner = () => <div />;
const Outer = withDate(Inner);
const container = mount(
<LocaleContext.Provider value={ contextValue }>
<Outer />
</LocaleContext.Provider>
);

expect(container.find(Inner).prop('date'))
.toEqual(contextValue.date);
});
});
16 changes: 16 additions & 0 deletions src/app/locale/withDate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createInjectHoc, InjectHoc } from '../common/hoc';

import LocaleContext from './LocaleContext';

export interface WithDateProps {
date: {
inputFormat: string;
};
}

const withDate: InjectHoc<WithDateProps> = createInjectHoc(LocaleContext, {
displayNamePrefix: 'withDate',
pickProps: (value, key) => key === 'date' && !!value,
});

export default withDate;
Loading

0 comments on commit a81656d

Please sign in to comment.