Skip to content

Commit 39a99ac

Browse files
committed
feat(carbon): add string variant of timepicker
1 parent a5ab931 commit 39a99ac

File tree

17 files changed

+639
-128
lines changed

17 files changed

+639
-128
lines changed
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
import React from 'react';
2+
import { mount } from 'enzyme';
3+
4+
import { FormRenderer, componentTypes } from '@data-driven-forms/react-form-renderer';
5+
6+
import FormTemplate from '../form-template';
7+
import { act } from 'react-dom/test-utils';
8+
import TimePicker from '../time-picker';
9+
10+
describe('TimePicker<String>', () => {
11+
let initialProps;
12+
let onSubmit;
13+
let wrapper;
14+
let schema;
15+
16+
beforeEach(() => {
17+
onSubmit = jest.fn();
18+
initialProps = {
19+
onSubmit: (values) => onSubmit(values),
20+
componentMapper: {
21+
[componentTypes.TIME_PICKER]: {
22+
component: TimePicker,
23+
useStringFormat: true,
24+
},
25+
},
26+
FormTemplate,
27+
};
28+
});
29+
30+
it('change AM/PM', async () => {
31+
schema = {
32+
fields: [
33+
{
34+
component: componentTypes.TIME_PICKER,
35+
name: 'time-picker',
36+
twelveHoursFormat: true,
37+
},
38+
],
39+
};
40+
41+
wrapper = mount(<FormRenderer schema={schema} {...initialProps} />);
42+
43+
await act(async () => {
44+
wrapper.find('input').simulate('change', { target: { value: '00:35' } });
45+
});
46+
wrapper.update();
47+
48+
await act(async () => {
49+
wrapper.find('select#time-picker-12h').simulate('change', { target: { value: 'PM' } });
50+
});
51+
wrapper.update();
52+
53+
await act(async () => {
54+
wrapper.find('form').simulate('submit');
55+
});
56+
wrapper.update();
57+
58+
expect(wrapper.find('input').props().value).toEqual('00:35');
59+
expect(onSubmit).toHaveBeenLastCalledWith({ 'time-picker': '00:35 PM' });
60+
61+
onSubmit.mockReset();
62+
63+
await act(async () => {
64+
wrapper.find('select#time-picker-12h').simulate('change', { target: { value: 'AM' } });
65+
});
66+
wrapper.update();
67+
68+
await act(async () => {
69+
wrapper.find('form').simulate('submit');
70+
});
71+
wrapper.update();
72+
73+
expect(wrapper.find('input').props().value).toEqual('00:35');
74+
expect(onSubmit).toHaveBeenLastCalledWith({ 'time-picker': '00:35 AM' });
75+
});
76+
77+
it('does not handle invalid date', async () => {
78+
schema = {
79+
fields: [
80+
{
81+
component: componentTypes.TIME_PICKER,
82+
name: 'time-picker',
83+
},
84+
],
85+
};
86+
87+
wrapper = mount(<FormRenderer schema={schema} {...initialProps} />);
88+
89+
await act(async () => {
90+
wrapper.find('input').simulate('change', { target: { value: 'aa:BB' } });
91+
});
92+
wrapper.update();
93+
94+
await act(async () => {
95+
wrapper.find('input').simulate('blur');
96+
});
97+
wrapper.update();
98+
99+
await act(async () => {
100+
wrapper.find('form').simulate('submit');
101+
});
102+
wrapper.update();
103+
104+
expect(wrapper.find('input').props().value).toEqual('aa:BB');
105+
expect(onSubmit).toHaveBeenLastCalledWith({ 'time-picker': 'aa:BB' });
106+
});
107+
108+
it('handle change', async () => {
109+
schema = {
110+
fields: [
111+
{
112+
component: componentTypes.TIME_PICKER,
113+
name: 'time-picker',
114+
},
115+
],
116+
};
117+
118+
wrapper = mount(<FormRenderer schema={schema} {...initialProps} />);
119+
120+
await act(async () => {
121+
wrapper.find('input').simulate('change', { target: { value: '13:87' } });
122+
});
123+
wrapper.update();
124+
125+
await act(async () => {
126+
wrapper.find('input').simulate('blur');
127+
});
128+
wrapper.update();
129+
130+
await act(async () => {
131+
wrapper.find('form').simulate('submit');
132+
});
133+
wrapper.update();
134+
135+
expect(wrapper.find('input').props().value).toEqual('13:87');
136+
expect(onSubmit).toHaveBeenLastCalledWith({ 'time-picker': '13:87' });
137+
onSubmit.mockReset();
138+
139+
await act(async () => {
140+
wrapper.find('input').simulate('change', { target: { value: '25:16' } });
141+
});
142+
wrapper.update();
143+
144+
await act(async () => {
145+
wrapper.find('input').simulate('blur');
146+
});
147+
wrapper.update();
148+
149+
await act(async () => {
150+
wrapper.find('form').simulate('submit');
151+
});
152+
wrapper.update();
153+
154+
expect(wrapper.find('input').props().value).toEqual('25:16');
155+
expect(onSubmit).toHaveBeenLastCalledWith({ 'time-picker': '25:16' });
156+
});
157+
158+
it('change timezone', async () => {
159+
schema = {
160+
fields: [
161+
{
162+
component: componentTypes.TIME_PICKER,
163+
name: 'time-picker',
164+
twelveHoursFormat: true,
165+
timezones: [
166+
{ label: 'UTC', value: 'UTC' },
167+
{ label: 'EST', value: 'EAST' },
168+
],
169+
},
170+
],
171+
};
172+
173+
wrapper = mount(<FormRenderer schema={schema} {...initialProps} />);
174+
175+
await act(async () => {
176+
wrapper.find('input').simulate('change', { target: { value: '00:35' } });
177+
});
178+
wrapper.update();
179+
180+
await act(async () => {
181+
wrapper.find('select#time-picker-timezones').simulate('change', { target: { value: 'EST' } });
182+
});
183+
wrapper.update();
184+
185+
await act(async () => {
186+
wrapper.find('form').simulate('submit');
187+
});
188+
wrapper.update();
189+
190+
expect(wrapper.find('input').props().value).toEqual('00:35');
191+
expect(onSubmit).toHaveBeenLastCalledWith({ 'time-picker': '00:35 AM EST' });
192+
193+
onSubmit.mockReset();
194+
195+
await act(async () => {
196+
wrapper.find('select#time-picker-12h').simulate('change', { target: { value: 'UTC' } });
197+
});
198+
wrapper.update();
199+
200+
await act(async () => {
201+
wrapper.find('form').simulate('submit');
202+
});
203+
wrapper.update();
204+
205+
expect(wrapper.find('input').props().value).toEqual('00:35');
206+
expect(onSubmit).toHaveBeenLastCalledWith({ 'time-picker': '00:35 UTC EST' });
207+
});
208+
209+
it('handles initial value', async () => {
210+
schema = {
211+
fields: [
212+
{
213+
component: componentTypes.TIME_PICKER,
214+
name: 'time-picker',
215+
initialValue: '12:57 PM EAST',
216+
twelveHoursFormat: true,
217+
timezones: [
218+
{ label: 'UTC', value: 'UTC' },
219+
{ label: 'EST', value: 'EAST' },
220+
],
221+
},
222+
],
223+
};
224+
225+
wrapper = mount(<FormRenderer schema={schema} {...initialProps} />);
226+
227+
expect(wrapper.find('input').props().value).toEqual('12:57');
228+
229+
await act(async () => {
230+
wrapper.find('input').simulate('change', { target: { value: '00:35' } });
231+
});
232+
wrapper.update();
233+
234+
await act(async () => {
235+
wrapper.find('form').simulate('submit');
236+
});
237+
wrapper.update();
238+
239+
expect(onSubmit).toHaveBeenLastCalledWith({ 'time-picker': '00:35 PM EAST' });
240+
});
241+
});
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { default } from './time-picker-base';
2+
export * from './time-picker-base';
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { default } from './time-picker-base';
2+
export * from './time-picker-base';
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { ReactNode } from "react";
2+
import { AnyObject, Input } from "@data-driven-forms/react-form-renderer";
3+
4+
import { FormGroupProps } from "../form-group";
5+
6+
import { TimePickerProps as CarbonTimePickerProps, SelectItemProps } from 'carbon-components-react';
7+
8+
export interface Timezone extends SelectItemProps {
9+
value: string;
10+
label?: string;
11+
}
12+
13+
interface InternalTimePickerBaseProps extends CarbonTimePickerProps, AnyObject {
14+
twelveHoursFormat?: boolean;
15+
timezones?: Timezone[];
16+
input: Input<any>;
17+
enhnancedOnBlur?: () => void;
18+
enhancedOnChange?: (value: string) => void;
19+
finalValue: any;
20+
warnText?: ReactNode;
21+
selectFormat: (value: 'AM' | 'PM') => void;
22+
selectTimezone: (value: string) => void;
23+
}
24+
25+
export type TimePickerBaseProps = InternalTimePickerBaseProps & FormGroupProps;
26+
27+
declare const TimePickerBase: React.ComponentType<TimePickerBaseProps>;
28+
29+
export default TimePickerBase;
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
import { TimePicker as CarbonTimePicker, TimePickerSelect, SelectItem } from 'carbon-components-react';
5+
6+
import HelperTextBlock from '../helper-text-block/helper-text-block';
7+
8+
const TimePickerBase = ({
9+
WrapperProps,
10+
input,
11+
enhnancedOnBlur,
12+
enhancedOnChange,
13+
finalValue,
14+
invalid,
15+
twelveHoursFormat,
16+
timezones,
17+
helperText,
18+
warnText,
19+
selectFormat,
20+
selectTimezone,
21+
...rest
22+
}) => (
23+
<div {...WrapperProps}>
24+
<CarbonTimePicker
25+
{...input}
26+
{...(enhnancedOnBlur && { onBlur: enhnancedOnBlur })}
27+
{...(enhancedOnChange && { onChange: (e) => enhancedOnChange(e.target.value) })}
28+
onBlur={enhnancedOnBlur}
29+
value={finalValue}
30+
key={input.name}
31+
id={input.name}
32+
invalid={Boolean(invalid)}
33+
invalidText={invalid || ''}
34+
{...rest}
35+
>
36+
{twelveHoursFormat && (
37+
<TimePickerSelect labelText="Period" id={`${rest.id || input.name}-12h`} onChange={({ target: { value } }) => selectFormat(value)}>
38+
<SelectItem value="AM" text="AM" />
39+
<SelectItem value="PM" text="PM" />
40+
</TimePickerSelect>
41+
)}
42+
{timezones && (
43+
<TimePickerSelect labelText="Timezone" id={`${rest.id || input.name}-timezones`} onChange={({ target: { value } }) => selectTimezone(value)}>
44+
{timezones.map(({ showAs, ...tz }) => (
45+
<SelectItem key={tz.value} text={tz.label} {...tz} />
46+
))}
47+
</TimePickerSelect>
48+
)}
49+
</CarbonTimePicker>
50+
<HelperTextBlock helperText={!invalid && helperText} warnText={warnText} />
51+
</div>
52+
);
53+
54+
TimePickerBase.propTypes = {
55+
isDisabled: PropTypes.bool,
56+
isReadOnly: PropTypes.bool,
57+
isRequired: PropTypes.bool,
58+
label: PropTypes.node,
59+
labelText: PropTypes.node,
60+
description: PropTypes.node,
61+
twelveHoursFormat: PropTypes.bool,
62+
timezones: PropTypes.arrayOf(
63+
PropTypes.shape({
64+
value: PropTypes.string.isRequired,
65+
label: PropTypes.node.isRequired,
66+
showAs: PropTypes.string,
67+
})
68+
),
69+
WrapperProps: PropTypes.object,
70+
input: PropTypes.shape({
71+
name: PropTypes.string,
72+
}).isRequired,
73+
enhnancedOnBlur: PropTypes.func,
74+
enhancedOnChange: PropTypes.func,
75+
finalValue: PropTypes.any,
76+
invalid: PropTypes.node,
77+
helperText: PropTypes.node,
78+
warnText: PropTypes.node,
79+
selectFormat: PropTypes.func.isRequired,
80+
selectTimezone: PropTypes.func.isRequired,
81+
};
82+
83+
export default TimePickerBase;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { default } from './time-picker-date';
2+
export * from './time-picker-date';
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { default } from './time-picker-date';
2+
export * from './time-picker-date';
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { UseFieldApiComponentConfig } from "@data-driven-forms/react-form-renderer";
2+
3+
import { FormGroupProps } from "../form-group";
4+
5+
import { TimePickerProps as CarbonTimePickerProps, SelectItemProps } from 'carbon-components-react';
6+
7+
export interface Timezone extends SelectItemProps {
8+
value: string;
9+
label?: string;
10+
}
11+
12+
interface InternalTimePickerProps extends CarbonTimePickerProps {
13+
twelveHoursFormat?: boolean;
14+
timezones?: Timezone[];
15+
}
16+
17+
export type TimePickerDateProps = InternalTimePickerProps & FormGroupProps & UseFieldApiComponentConfig;
18+
19+
declare const TimePickerDate: React.ComponentType<TimePickerDateProps>;
20+
21+
export default TimePickerDate;

0 commit comments

Comments
 (0)