@hyphen/hyphen-components 7.10.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/css/fonts.css +1 -1
- package/dist/css/index.css +292 -150
- package/dist/css/reset.css +1 -1
- package/dist/css/utilities.css +480 -38
- package/dist/css/variables.css +170 -25
- package/dist/hyphen-components.cjs.development.js +244 -311
- package/dist/hyphen-components.cjs.development.js.map +1 -1
- package/dist/hyphen-components.cjs.production.min.js +2 -2
- package/dist/hyphen-components.cjs.production.min.js.map +1 -1
- package/dist/hyphen-components.esm.js +198 -265
- package/dist/hyphen-components.esm.js.map +1 -1
- package/dist/index.d.ts +25 -60
- package/package.json +3 -3
- package/src/components/Alert/Alert.mdx +1 -1
- package/src/components/Alert/Alert.module.scss +29 -34
- package/src/components/Alert/Alert.stories.tsx +11 -3
- package/src/components/Alert/Alert.test.tsx +17 -11
- package/src/components/Alert/Alert.tsx +9 -16
- package/src/components/Badge/Badge.mdx +46 -4
- package/src/components/Badge/Badge.module.scss +235 -185
- package/src/components/Badge/Badge.stories.tsx +126 -36
- package/src/components/Badge/Badge.test.tsx +180 -21
- package/src/components/Badge/Badge.tsx +56 -21
- package/src/components/Drawer/Drawer.mdx +14 -16
- package/src/components/Formik/Formik.stories.tsx +3 -5
- package/src/components/Formik/FormikTimePicker/FormikTimePicker.test.tsx +34 -67
- package/src/components/Formik/FormikTimePicker/FormikTimePicker.tsx +0 -2
- package/src/components/Table/Table.stories.tsx +4 -4
- package/src/components/TimePicker/TimePicker.mdx +3 -11
- package/src/components/TimePicker/TimePicker.stories.tsx +61 -60
- package/src/components/TimePicker/TimePicker.test.tsx +76 -7
- package/src/components/TimePicker/TimePicker.tsx +37 -7
- package/src/components/Tooltip/Tooltip.stories.tsx +1 -1
- package/src/docs/Colors.mdx +13 -0
- package/src/index.ts +0 -2
- package/src/components/Formik/FormikTimePickerNative/FormikTimePickerNative.test.tsx +0 -175
- package/src/components/Formik/FormikTimePickerNative/FormikTimePickerNative.tsx +0 -38
- package/src/components/TimePickerNative/TimePickerNative.mdx +0 -67
- package/src/components/TimePickerNative/TimePickerNative.stories.tsx +0 -150
- package/src/components/TimePickerNative/TimePickerNative.test.tsx +0 -117
- package/src/components/TimePickerNative/TimePickerNative.tsx +0 -93
|
@@ -6,13 +6,12 @@ import * as Stories from './TimePicker.stories';
|
|
|
6
6
|
|
|
7
7
|
# TimePicker
|
|
8
8
|
|
|
9
|
-
Use a TimePicker when you want users to select from a list of
|
|
9
|
+
Use a TimePicker when you want users to select from a list of available times, agnostic of date.
|
|
10
10
|
|
|
11
|
-
_NOTE:_ This component is abstracted from the [
|
|
11
|
+
_NOTE:_ This component is abstracted from the [SelectInputNative](?path=/docs/components-selectinputnative-overview--default-story)
|
|
12
12
|
and as such includes the same underlying props interface with an added method for generating the time options automatically.
|
|
13
13
|
|
|
14
|
-
_ABOUT TIME FORMAT:_
|
|
15
|
-
be a JS ISO date string, like this: `2016-07-13T18:46:01.933Z`.
|
|
14
|
+
_ABOUT TIME FORMAT:_ The value returned will always be a JS ISO date string, like this: `2016-07-13T18:46:01.933Z`.
|
|
16
15
|
|
|
17
16
|
## Props
|
|
18
17
|
|
|
@@ -53,13 +52,6 @@ The example below is shown in military (24-hour) time.
|
|
|
53
52
|
|
|
54
53
|
<Canvas of={Stories.WithCustomDateDisplay} />
|
|
55
54
|
|
|
56
|
-
## With Open Menu
|
|
57
|
-
|
|
58
|
-
The menu can be rendered open with the `menuIsOpen` prop. We do this here to confirm option generation
|
|
59
|
-
via our UI visual snapshot testing (Chromatic).
|
|
60
|
-
|
|
61
|
-
<Canvas of={Stories.WithOpenMenu} />
|
|
62
|
-
|
|
63
55
|
## Help Text
|
|
64
56
|
|
|
65
57
|
<Canvas of={Stories.HelpText} />
|
|
@@ -16,11 +16,11 @@ export default meta;
|
|
|
16
16
|
export const Default = () => {
|
|
17
17
|
const [value, setValue] = useState<string | null>(null);
|
|
18
18
|
return (
|
|
19
|
-
<Box
|
|
19
|
+
<Box>
|
|
20
20
|
<TimePicker
|
|
21
21
|
id="defaultTimePicker"
|
|
22
22
|
name="defaultTimePicker"
|
|
23
|
-
onChange={(event: ChangeEvent<
|
|
23
|
+
onChange={(event: ChangeEvent<HTMLSelectElement>) => {
|
|
24
24
|
setValue(event.target.value);
|
|
25
25
|
}}
|
|
26
26
|
label="Pick a Time"
|
|
@@ -30,74 +30,75 @@ export const Default = () => {
|
|
|
30
30
|
);
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
export const WithASpecificInterval = () =>
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
onChange={() => {}}
|
|
51
|
-
label="Pick a Time"
|
|
52
|
-
startTime={{ hour: 9, minute: 0 }}
|
|
53
|
-
endTime={{ hour: 15, minute: 31 }}
|
|
54
|
-
/>
|
|
55
|
-
</Box>
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
export const WithCustomDateDisplay = () => (
|
|
59
|
-
<Box height="360px">
|
|
60
|
-
<TimePicker
|
|
61
|
-
id="customDate"
|
|
62
|
-
name="customDate"
|
|
63
|
-
onChange={() => {}}
|
|
64
|
-
label="Pick a Time"
|
|
65
|
-
startTime={{ hour: 9, minute: 0 }}
|
|
66
|
-
endTime={{ hour: 15, minute: 31 }}
|
|
67
|
-
dateDisplayOptions={{ hour12: false, hour: '2-digit', minute: '2-digit' }}
|
|
68
|
-
/>
|
|
69
|
-
</Box>
|
|
70
|
-
);
|
|
33
|
+
export const WithASpecificInterval = () => {
|
|
34
|
+
const [value, setValue] = useState<string | null>(null);
|
|
35
|
+
return (
|
|
36
|
+
<Box>
|
|
37
|
+
<TimePicker
|
|
38
|
+
id="intervalTimePicker"
|
|
39
|
+
name="intervalTimePicker"
|
|
40
|
+
onChange={(event: ChangeEvent<HTMLSelectElement>) =>
|
|
41
|
+
setValue(event.target.value)
|
|
42
|
+
}
|
|
43
|
+
value={value}
|
|
44
|
+
label="Pick a Time"
|
|
45
|
+
interval={3600}
|
|
46
|
+
/>
|
|
47
|
+
</Box>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
71
50
|
|
|
72
|
-
export const
|
|
51
|
+
export const WithMinAndMaxTimes = () => {
|
|
73
52
|
const [value, setValue] = useState<string | null>(null);
|
|
74
53
|
return (
|
|
75
|
-
<Box
|
|
54
|
+
<Box>
|
|
76
55
|
<TimePicker
|
|
77
|
-
id="
|
|
78
|
-
name="
|
|
79
|
-
onChange={(event: ChangeEvent<
|
|
80
|
-
setValue(event.target.value)
|
|
81
|
-
}
|
|
56
|
+
id="startEnd"
|
|
57
|
+
name="startEnd"
|
|
58
|
+
onChange={(event: ChangeEvent<HTMLSelectElement>) =>
|
|
59
|
+
setValue(event.target.value)
|
|
60
|
+
}
|
|
61
|
+
value={value}
|
|
82
62
|
label="Pick a Time"
|
|
63
|
+
startTime={{ hour: 9, minute: 0 }}
|
|
64
|
+
endTime={{ hour: 15, minute: 31 }}
|
|
65
|
+
/>
|
|
66
|
+
</Box>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const WithCustomDateDisplay = () => {
|
|
71
|
+
const [value, setValue] = useState<string | null>(null);
|
|
72
|
+
return (
|
|
73
|
+
<Box>
|
|
74
|
+
<TimePicker
|
|
75
|
+
id="customDate"
|
|
76
|
+
name="customDate"
|
|
77
|
+
onChange={(event: ChangeEvent<HTMLSelectElement>) =>
|
|
78
|
+
setValue(event.target.value)
|
|
79
|
+
}
|
|
83
80
|
value={value}
|
|
84
|
-
|
|
85
|
-
interval={3600}
|
|
81
|
+
label="Pick a Time"
|
|
86
82
|
startTime={{ hour: 9, minute: 0 }}
|
|
87
|
-
endTime={{ hour:
|
|
83
|
+
endTime={{ hour: 15, minute: 31 }}
|
|
84
|
+
dateDisplayOptions={{
|
|
85
|
+
hour12: false,
|
|
86
|
+
hour: '2-digit',
|
|
87
|
+
minute: '2-digit',
|
|
88
|
+
}}
|
|
88
89
|
/>
|
|
89
90
|
</Box>
|
|
90
91
|
);
|
|
91
92
|
};
|
|
92
93
|
|
|
93
94
|
export const HelpText = () => {
|
|
94
|
-
const [value, setValue] = useState<string>(
|
|
95
|
+
const [value, setValue] = useState<string | null>(null);
|
|
95
96
|
return (
|
|
96
|
-
<Box
|
|
97
|
+
<Box>
|
|
97
98
|
<TimePicker
|
|
98
99
|
id="helpText"
|
|
99
100
|
name="helpText"
|
|
100
|
-
onChange={(event: ChangeEvent<
|
|
101
|
+
onChange={(event: ChangeEvent<HTMLSelectElement>) => {
|
|
101
102
|
setValue(event.target.value);
|
|
102
103
|
}}
|
|
103
104
|
label="Pick a Time"
|
|
@@ -109,15 +110,15 @@ export const HelpText = () => {
|
|
|
109
110
|
};
|
|
110
111
|
|
|
111
112
|
export const Sizes = () => {
|
|
112
|
-
const [value, setValue] = useState<string>();
|
|
113
|
-
const [value1, setValue1] = useState<string>();
|
|
114
|
-
const [value2, setValue2] = useState<string>();
|
|
113
|
+
const [value, setValue] = useState<string | null>(null);
|
|
114
|
+
const [value1, setValue1] = useState<string | null>(null);
|
|
115
|
+
const [value2, setValue2] = useState<string | null>(null);
|
|
115
116
|
return (
|
|
116
|
-
<Box gap="md"
|
|
117
|
+
<Box gap="md">
|
|
117
118
|
<TimePicker
|
|
118
119
|
id="smTimePicker"
|
|
119
120
|
name="smTimePicker"
|
|
120
|
-
onChange={(event: ChangeEvent<
|
|
121
|
+
onChange={(event: ChangeEvent<HTMLSelectElement>) => {
|
|
121
122
|
setValue(event.target.value);
|
|
122
123
|
}}
|
|
123
124
|
label="Small"
|
|
@@ -127,7 +128,7 @@ export const Sizes = () => {
|
|
|
127
128
|
<TimePicker
|
|
128
129
|
id="mdTimePicker"
|
|
129
130
|
name="mdTimePicker"
|
|
130
|
-
onChange={(event: ChangeEvent<
|
|
131
|
+
onChange={(event: ChangeEvent<HTMLSelectElement>) => {
|
|
131
132
|
setValue1(event.target.value);
|
|
132
133
|
}}
|
|
133
134
|
label="Medium"
|
|
@@ -137,7 +138,7 @@ export const Sizes = () => {
|
|
|
137
138
|
<TimePicker
|
|
138
139
|
id="lgTimePicker"
|
|
139
140
|
name="lgTimePicker"
|
|
140
|
-
onChange={(event: ChangeEvent<
|
|
141
|
+
onChange={(event: ChangeEvent<HTMLSelectElement>) => {
|
|
141
142
|
setValue2(event.target.value);
|
|
142
143
|
}}
|
|
143
144
|
label="Large"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { screen, render } from '@testing-library/react';
|
|
2
|
+
import { screen, render, fireEvent } from '@testing-library/react';
|
|
3
3
|
import { TimePicker } from './TimePicker';
|
|
4
4
|
|
|
5
5
|
describe('TimePicker', () => {
|
|
@@ -12,8 +12,6 @@ describe('TimePicker', () => {
|
|
|
12
12
|
onChange={() => null}
|
|
13
13
|
value={null}
|
|
14
14
|
label="Select Time"
|
|
15
|
-
menuIsOpen
|
|
16
|
-
maxMenuHeight={2000}
|
|
17
15
|
/>
|
|
18
16
|
);
|
|
19
17
|
|
|
@@ -43,8 +41,6 @@ describe('TimePicker', () => {
|
|
|
43
41
|
onChange={() => null}
|
|
44
42
|
value={null}
|
|
45
43
|
label="Select Time"
|
|
46
|
-
menuIsOpen
|
|
47
|
-
maxMenuHeight={2000}
|
|
48
44
|
interval={3600}
|
|
49
45
|
startTime={{ hour: 9, minute: 0 }}
|
|
50
46
|
endTime={{ hour: 12, minute: 0 }}
|
|
@@ -65,6 +61,55 @@ describe('TimePicker', () => {
|
|
|
65
61
|
expect(screen.queryByText(time)).toBe(null);
|
|
66
62
|
});
|
|
67
63
|
});
|
|
64
|
+
|
|
65
|
+
it('supports intervals shorter than one minute', () => {
|
|
66
|
+
render(
|
|
67
|
+
<TimePicker
|
|
68
|
+
name="timePicker"
|
|
69
|
+
id="timePicker"
|
|
70
|
+
onChange={() => null}
|
|
71
|
+
value={null}
|
|
72
|
+
label="Select Time"
|
|
73
|
+
interval={45}
|
|
74
|
+
startTime={{ hour: 1, minute: 0 }}
|
|
75
|
+
endTime={{ hour: 1, minute: 2 }}
|
|
76
|
+
dateDisplayOptions={{
|
|
77
|
+
hour: '2-digit',
|
|
78
|
+
minute: '2-digit',
|
|
79
|
+
second: '2-digit',
|
|
80
|
+
hour12: false,
|
|
81
|
+
}}
|
|
82
|
+
/>
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
expect(screen.getByText('01:00:00')).toBeInTheDocument();
|
|
86
|
+
expect(screen.getByText('01:00:45')).toBeInTheDocument();
|
|
87
|
+
expect(screen.getByText('01:01:30')).toBeInTheDocument();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('Selected value', () => {
|
|
92
|
+
it('selects a matching time from a previous date', () => {
|
|
93
|
+
const savedValue = new Date(2020, 0, 1, 13, 0, 0, 123).toISOString();
|
|
94
|
+
const expectedValue = new Date();
|
|
95
|
+
expectedValue.setHours(13, 0, 0, 0);
|
|
96
|
+
|
|
97
|
+
render(
|
|
98
|
+
<TimePicker
|
|
99
|
+
name="timePicker"
|
|
100
|
+
id="timePicker"
|
|
101
|
+
onChange={() => null}
|
|
102
|
+
value={savedValue}
|
|
103
|
+
label="Select Time"
|
|
104
|
+
startTime={{ hour: 13, minute: 0 }}
|
|
105
|
+
endTime={{ hour: 14, minute: 0 }}
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
expect(screen.getByLabelText('Select Time')).toHaveValue(
|
|
110
|
+
expectedValue.toISOString()
|
|
111
|
+
);
|
|
112
|
+
});
|
|
68
113
|
});
|
|
69
114
|
|
|
70
115
|
describe('Custom Date Display', () => {
|
|
@@ -76,8 +121,6 @@ describe('TimePicker', () => {
|
|
|
76
121
|
onChange={() => null}
|
|
77
122
|
value={null}
|
|
78
123
|
label="Select Time"
|
|
79
|
-
menuIsOpen
|
|
80
|
-
maxMenuHeight={2000}
|
|
81
124
|
dateDisplayOptions={{ hour12: false }}
|
|
82
125
|
startTime={{ hour: 13, minute: 0 }}
|
|
83
126
|
endTime={{ hour: 15, minute: 1 }}
|
|
@@ -94,4 +137,30 @@ describe('TimePicker', () => {
|
|
|
94
137
|
});
|
|
95
138
|
});
|
|
96
139
|
});
|
|
140
|
+
|
|
141
|
+
describe('Callback Handling', () => {
|
|
142
|
+
it('it fires an onchange callback with the correct value', async () => {
|
|
143
|
+
const mockedHandleChange = jest.fn(() => {}); // eslint-disable-line
|
|
144
|
+
|
|
145
|
+
render(
|
|
146
|
+
<TimePicker
|
|
147
|
+
name="timePicker"
|
|
148
|
+
id="timePicker"
|
|
149
|
+
onChange={mockedHandleChange}
|
|
150
|
+
value={null}
|
|
151
|
+
label="Select Time"
|
|
152
|
+
dateDisplayOptions={{ hour12: false }}
|
|
153
|
+
startTime={{ hour: 13, minute: 0 }}
|
|
154
|
+
endTime={{ hour: 15, minute: 1 }}
|
|
155
|
+
interval={3600}
|
|
156
|
+
/>
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const timePicker = screen.getByLabelText('Select Time');
|
|
160
|
+
|
|
161
|
+
fireEvent.change(timePicker, { target: { value: 'hello' } });
|
|
162
|
+
|
|
163
|
+
expect(mockedHandleChange).toBeCalledTimes(1);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
97
166
|
});
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import React, { FC } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
SelectInputNative,
|
|
4
|
+
SelectInputNativeProps,
|
|
5
|
+
} from '../SelectInputNative/SelectInputNative';
|
|
3
6
|
|
|
4
|
-
export
|
|
7
|
+
export interface TimePickerProps
|
|
8
|
+
extends Omit<SelectInputNativeProps, 'options'> {
|
|
5
9
|
/**
|
|
6
10
|
* Options to govern the display of the option labels in the select.
|
|
7
11
|
* This is a direct passthrough to the second argument of JS `toLocaleTimeString`.
|
|
@@ -26,7 +30,11 @@ export type TimePickerProps = Omit<SelectInputProps, 'options'> & {
|
|
|
26
30
|
* Start hour and minute
|
|
27
31
|
*/
|
|
28
32
|
startTime?: { hour: number; minute: number };
|
|
29
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Should be ISO timestamp as returned by `onChange`, and matching value of option object.
|
|
35
|
+
*/
|
|
36
|
+
value: SelectInputNativeProps['value'];
|
|
37
|
+
}
|
|
30
38
|
|
|
31
39
|
export const TimePicker: FC<TimePickerProps> = ({
|
|
32
40
|
id,
|
|
@@ -62,22 +70,44 @@ export const TimePicker: FC<TimePickerProps> = ({
|
|
|
62
70
|
value: currentTime.toISOString(),
|
|
63
71
|
label: currentTime.toLocaleTimeString(locales, dateDisplayOptions),
|
|
64
72
|
});
|
|
65
|
-
currentTime.setSeconds(
|
|
73
|
+
currentTime.setSeconds(currentTime.getSeconds() + interval);
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
return timeOptions;
|
|
69
77
|
};
|
|
70
78
|
|
|
79
|
+
const options = generateTimes();
|
|
80
|
+
let valueDate: Date | undefined;
|
|
81
|
+
|
|
82
|
+
if (typeof value === 'string') {
|
|
83
|
+
valueDate = new Date(value);
|
|
84
|
+
} else if (typeof value === 'number') {
|
|
85
|
+
valueDate = new Date(value);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const matchingOption =
|
|
89
|
+
valueDate && !Number.isNaN(valueDate.getTime())
|
|
90
|
+
? options.find(({ value: optionValue }) => {
|
|
91
|
+
const optionDate = new Date(optionValue);
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
optionDate.getHours() === valueDate.getHours() &&
|
|
95
|
+
optionDate.getMinutes() === valueDate.getMinutes() &&
|
|
96
|
+
optionDate.getSeconds() === valueDate.getSeconds()
|
|
97
|
+
);
|
|
98
|
+
})
|
|
99
|
+
: undefined;
|
|
100
|
+
|
|
71
101
|
return (
|
|
72
|
-
<
|
|
102
|
+
<SelectInputNative
|
|
73
103
|
{...restProps}
|
|
74
104
|
id={id}
|
|
75
105
|
name={name}
|
|
76
106
|
label={label}
|
|
77
107
|
onChange={onChange}
|
|
78
|
-
options={
|
|
108
|
+
options={options}
|
|
79
109
|
placeholder={placeholder}
|
|
80
|
-
value={value}
|
|
110
|
+
value={matchingOption?.value ?? value}
|
|
81
111
|
/>
|
|
82
112
|
);
|
|
83
113
|
};
|
package/src/docs/Colors.mdx
CHANGED
|
@@ -54,6 +54,19 @@ import designTokens from '@hyphen/hyphen-design-tokens/build/json/variables.json
|
|
|
54
54
|
{}
|
|
55
55
|
)}
|
|
56
56
|
/>
|
|
57
|
+
<ColorItem
|
|
58
|
+
title="color-base-orange"
|
|
59
|
+
subtitle="Orange"
|
|
60
|
+
colors={Object.keys(designTokens.color.base.orange).reduce(
|
|
61
|
+
(acc, item, i) => {
|
|
62
|
+
acc[item] = Object.keys(designTokens.color.base.orange).map(
|
|
63
|
+
(g) => designTokens.color.base.orange[g].value
|
|
64
|
+
)[i];
|
|
65
|
+
return acc;
|
|
66
|
+
},
|
|
67
|
+
{}
|
|
68
|
+
)}
|
|
69
|
+
/>
|
|
57
70
|
<ColorItem
|
|
58
71
|
title="color-base-green"
|
|
59
72
|
subtitle="Green"
|
package/src/index.ts
CHANGED
|
@@ -23,7 +23,6 @@ export * from './components/Formik/FormikSelectInputNative/FormikSelectInputNati
|
|
|
23
23
|
export * from './components/Formik/FormikTextInput/FormikTextInput';
|
|
24
24
|
export * from './components/Formik/FormikTextareaInput/FormikTextareaInput';
|
|
25
25
|
export * from './components/Formik/FormikTimePicker/FormikTimePicker';
|
|
26
|
-
export * from './components/Formik/FormikTimePickerNative/FormikTimePickerNative';
|
|
27
26
|
export * from './components/Formik/FormikSwitch/FormikSwitch';
|
|
28
27
|
export * from './components/Formik/FormikToggleGroup/FormikToggleGroup';
|
|
29
28
|
export * from './components/Formik/FormikToggleGroupMulti/FormikToggleGroupMulti';
|
|
@@ -49,7 +48,6 @@ export * from './components/TextInput/TextInput';
|
|
|
49
48
|
export * from './components/TextInputInset/TextInputInset';
|
|
50
49
|
export * from './components/ThemeProvider/ThemeProvider';
|
|
51
50
|
export * from './components/TimePicker/TimePicker';
|
|
52
|
-
export * from './components/TimePickerNative/TimePickerNative';
|
|
53
51
|
export * from './components/Toast';
|
|
54
52
|
export * from './components/Toggle/Toggle';
|
|
55
53
|
export * from './components/ToggleGroup/ToggleGroup';
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render, fireEvent, screen, waitFor } from '@testing-library/react';
|
|
3
|
-
import { Formik, Form, Field, FormikValues, getIn, setIn } from 'formik';
|
|
4
|
-
import { FormikTimePickerNative } from './FormikTimePickerNative';
|
|
5
|
-
|
|
6
|
-
const testLabelName = 'test select';
|
|
7
|
-
|
|
8
|
-
const handleValidation = (testValueKey: string) => (values: FormikValues) =>
|
|
9
|
-
getIn(values, testValueKey)?.length > 1
|
|
10
|
-
? {}
|
|
11
|
-
: setIn({}, testValueKey, 'input is required');
|
|
12
|
-
|
|
13
|
-
const renderForm = (
|
|
14
|
-
initialValue: any, // eslint-disable-line
|
|
15
|
-
props: {
|
|
16
|
-
placeholder?: string;
|
|
17
|
-
hideLabel?: boolean;
|
|
18
|
-
isRequired?: unknown;
|
|
19
|
-
isDisabled?: boolean;
|
|
20
|
-
onChange?: jest.Mock<void, [React.ChangeEvent<HTMLSelectElement>]>; // eslint-disable-line
|
|
21
|
-
interval?: number;
|
|
22
|
-
},
|
|
23
|
-
testValueKey = testLabelName
|
|
24
|
-
) => (
|
|
25
|
-
<Formik
|
|
26
|
-
initialValues={{
|
|
27
|
-
[testLabelName]: initialValue,
|
|
28
|
-
}}
|
|
29
|
-
validate={props.isRequired ? handleValidation(testValueKey) : undefined} // eslint-disable-line
|
|
30
|
-
onSubmit={() => {}} // eslint-disable-line
|
|
31
|
-
>
|
|
32
|
-
{() => (
|
|
33
|
-
<Form noValidate>
|
|
34
|
-
<Field
|
|
35
|
-
label={testValueKey}
|
|
36
|
-
name={testValueKey}
|
|
37
|
-
id={testValueKey}
|
|
38
|
-
component={FormikTimePickerNative}
|
|
39
|
-
{...props}
|
|
40
|
-
/>
|
|
41
|
-
<button type="submit">submit</button>
|
|
42
|
-
</Form>
|
|
43
|
-
)}
|
|
44
|
-
</Formik>
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
describe('FormikTimePickerNative', () => {
|
|
48
|
-
describe('States', () => {
|
|
49
|
-
describe('Hidden label, with a placeholder', () => {
|
|
50
|
-
test('it renders input without a visual label, and with a placeholder', () => {
|
|
51
|
-
render(
|
|
52
|
-
renderForm(undefined, {
|
|
53
|
-
placeholder: 'Test Placeholder',
|
|
54
|
-
hideLabel: true,
|
|
55
|
-
})
|
|
56
|
-
);
|
|
57
|
-
expect(screen.queryByText(testLabelName)).toBeNull();
|
|
58
|
-
expect(screen.getByText('Test Placeholder')).toBeInTheDocument();
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
describe('No Aria-labelledby', () => {
|
|
63
|
-
test('does not assign "aria-labelledby" attribute when a label is hidden', () => {
|
|
64
|
-
render(renderForm(undefined, { hideLabel: true }));
|
|
65
|
-
const inputElement = screen.getByLabelText(testLabelName);
|
|
66
|
-
expect(inputElement).not.toHaveAttribute('aria-labelledby');
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe('With a label, and no custom placeholder', () => {
|
|
71
|
-
test('it renders input with a label, and with a default placeholder', () => {
|
|
72
|
-
render(renderForm(undefined, {}));
|
|
73
|
-
|
|
74
|
-
expect(screen.getByLabelText(testLabelName)).toBeInTheDocument();
|
|
75
|
-
expect(screen.getByText('HH:MM')).toBeInTheDocument();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test('assigns the "aria-labelledby" attribute and renders label with correct id, when label is provided', () => {
|
|
79
|
-
render(renderForm(undefined, {}));
|
|
80
|
-
const inputElement = screen.getByLabelText(testLabelName);
|
|
81
|
-
expect(inputElement).toHaveAttribute(
|
|
82
|
-
'aria-labelledby',
|
|
83
|
-
`${testLabelName}Label`
|
|
84
|
-
);
|
|
85
|
-
expect(
|
|
86
|
-
document.getElementById(`${testLabelName}Label`)
|
|
87
|
-
).toBeInTheDocument();
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
describe('Single select, pre-selected', () => {
|
|
92
|
-
test('it renders with value pre-selected', () => {
|
|
93
|
-
render(renderForm('2020-10-23T04:30:00.120Z', {}));
|
|
94
|
-
|
|
95
|
-
expect(screen.getByText('12:00 AM')).toBeInTheDocument();
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
describe('Is Required', () => {
|
|
100
|
-
test('it sets aria-required on the input', () => {
|
|
101
|
-
render(renderForm(undefined, { isRequired: true }));
|
|
102
|
-
const inputElement = screen.getByLabelText(testLabelName);
|
|
103
|
-
expect(inputElement).toHaveAttribute('aria-required', 'true');
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
describe('Is Disabled', () => {
|
|
108
|
-
test('it disables the input', () => {
|
|
109
|
-
render(renderForm(undefined, { isDisabled: true }));
|
|
110
|
-
|
|
111
|
-
expect(screen.getByLabelText(testLabelName)).toBeDisabled();
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
describe('Is Invalid, with a helpful message', () => {
|
|
116
|
-
test('it renders the helpful message', async () => {
|
|
117
|
-
const { getByText } = render(
|
|
118
|
-
renderForm(undefined, { isRequired: true })
|
|
119
|
-
);
|
|
120
|
-
const submitButton = getByText('submit');
|
|
121
|
-
|
|
122
|
-
fireEvent.click(submitButton);
|
|
123
|
-
await waitFor(() =>
|
|
124
|
-
expect(screen.getByText('input is required')).toBeInTheDocument()
|
|
125
|
-
);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test('it renders the error message from nested object', async () => {
|
|
129
|
-
const { getByText } = render(
|
|
130
|
-
renderForm(
|
|
131
|
-
{ outer: { nested: [] } },
|
|
132
|
-
{ isRequired: true },
|
|
133
|
-
`${testLabelName}.outer.nested`
|
|
134
|
-
)
|
|
135
|
-
);
|
|
136
|
-
const submitButton = getByText('submit');
|
|
137
|
-
|
|
138
|
-
fireEvent.click(submitButton);
|
|
139
|
-
await waitFor(() =>
|
|
140
|
-
expect(screen.getByText('input is required')).toBeInTheDocument()
|
|
141
|
-
);
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
describe('Callback Handling', () => {
|
|
147
|
-
describe('onChange', () => {
|
|
148
|
-
test("Custom onChange event fires callback function, overwriting Formik's onChange", async () => {
|
|
149
|
-
let value: string | undefined;
|
|
150
|
-
const mockedHandleChange = jest.fn((event) => {
|
|
151
|
-
event.persist();
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
const { getByLabelText } = render(
|
|
155
|
-
renderForm(value, { onChange: mockedHandleChange })
|
|
156
|
-
);
|
|
157
|
-
const selectInput = getByLabelText(testLabelName);
|
|
158
|
-
fireEvent.change(selectInput, { target: { value: 'hello' } });
|
|
159
|
-
expect(mockedHandleChange).toHaveBeenCalledTimes(1);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
test('it fires onChange callback on change', async () => {
|
|
163
|
-
const mockedHandleChange = jest.fn();
|
|
164
|
-
|
|
165
|
-
const { getByLabelText } = render(
|
|
166
|
-
renderForm(undefined, { onChange: mockedHandleChange })
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
await fireEvent.change(getByLabelText(testLabelName));
|
|
170
|
-
|
|
171
|
-
expect(mockedHandleChange).toBeCalledTimes(1);
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
});
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import React, { FC } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
FormikTouched,
|
|
4
|
-
FormikErrors,
|
|
5
|
-
FormikValues,
|
|
6
|
-
FieldAttributes,
|
|
7
|
-
getIn,
|
|
8
|
-
} from 'formik';
|
|
9
|
-
import {
|
|
10
|
-
TimePickerNative,
|
|
11
|
-
TimePickerNativeProps,
|
|
12
|
-
} from '../../TimePickerNative/TimePickerNative';
|
|
13
|
-
|
|
14
|
-
export interface FormikTimePickerNativeProps
|
|
15
|
-
extends Omit<TimePickerNativeProps, 'onChange'> {
|
|
16
|
-
field: FieldAttributes<HTMLTextAreaElement>;
|
|
17
|
-
form: {
|
|
18
|
-
touched: FormikTouched<FormikValues>;
|
|
19
|
-
errors: FormikErrors<FormikValues>;
|
|
20
|
-
};
|
|
21
|
-
onChange: TimePickerNativeProps['onChange'];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export const FormikTimePickerNative: FC<FormikTimePickerNativeProps> = ({
|
|
25
|
-
field: { name, onBlur, onChange: formikOnChange, value },
|
|
26
|
-
form: { touched, errors },
|
|
27
|
-
onChange,
|
|
28
|
-
...props
|
|
29
|
-
}) => (
|
|
30
|
-
<TimePickerNative
|
|
31
|
-
{...props}
|
|
32
|
-
name={name}
|
|
33
|
-
onBlur={onBlur}
|
|
34
|
-
onChange={onChange ?? formikOnChange}
|
|
35
|
-
value={value}
|
|
36
|
-
error={getIn(touched, name) && getIn(errors, name)}
|
|
37
|
-
/>
|
|
38
|
-
);
|