@punch-in/buffet-modern-core 3.3.11

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.
Files changed (131) hide show
  1. package/README.md +50 -0
  2. package/babel.config.js +18 -0
  3. package/build/bundle.development.js +850 -0
  4. package/build/bundle.production.js +1 -0
  5. package/build/esm/components/AttributeIcon/Div.js +51 -0
  6. package/build/esm/components/AttributeIcon/index.js +53 -0
  7. package/build/esm/components/Button/index.js +75 -0
  8. package/build/esm/components/Checkbox/index.js +78 -0
  9. package/build/esm/components/Count/Wrapper.js +32 -0
  10. package/build/esm/components/Count/index.js +27 -0
  11. package/build/esm/components/DatePicker/index.js +206 -0
  12. package/build/esm/components/DatePicker/reducer.js +42 -0
  13. package/build/esm/components/Enumeration/index.js +54 -0
  14. package/build/esm/components/Error/index.js +137 -0
  15. package/build/esm/components/Error/reducer.js +23 -0
  16. package/build/esm/components/Flex/index.js +29 -0
  17. package/build/esm/components/HeaderActions/index.js +41 -0
  18. package/build/esm/components/HeaderTitle/index.js +45 -0
  19. package/build/esm/components/Icon/index.js +26 -0
  20. package/build/esm/components/IconLinks/index.js +36 -0
  21. package/build/esm/components/InputNumber/index.js +70 -0
  22. package/build/esm/components/InputText/PrefixIcon.js +32 -0
  23. package/build/esm/components/InputText/index.js +89 -0
  24. package/build/esm/components/Label/index.js +40 -0
  25. package/build/esm/components/List/index.js +50 -0
  26. package/build/esm/components/ListHeader/BaselineAlignement.js +5 -0
  27. package/build/esm/components/ListHeader/index.js +52 -0
  28. package/build/esm/components/ListRow/index.js +30 -0
  29. package/build/esm/components/ListRow/tests/index.tests.js +21 -0
  30. package/build/esm/components/NavTabs/index.js +38 -0
  31. package/build/esm/components/Option/RemoveButton.js +5 -0
  32. package/build/esm/components/Option/index.js +32 -0
  33. package/build/esm/components/Padded/index.js +56 -0
  34. package/build/esm/components/Paging/index.js +57 -0
  35. package/build/esm/components/Picker/PickerButton.js +61 -0
  36. package/build/esm/components/Picker/PickerSection.js +48 -0
  37. package/build/esm/components/Picker/PickerWrapper.js +5 -0
  38. package/build/esm/components/Picker/index.js +50 -0
  39. package/build/esm/components/PrefixIcon/index.js +7 -0
  40. package/build/esm/components/Select/index.js +82 -0
  41. package/build/esm/components/Separator/index.js +44 -0
  42. package/build/esm/components/Table/ActionCollapse.js +40 -0
  43. package/build/esm/components/Table/index.js +140 -0
  44. package/build/esm/components/Table/tests/index.js +130 -0
  45. package/build/esm/components/TableHeader/index.js +88 -0
  46. package/build/esm/components/TableRow/index.js +93 -0
  47. package/build/esm/components/Text/index.js +67 -0
  48. package/build/esm/components/Textarea/index.js +16 -0
  49. package/build/esm/components/TimePicker/index.js +288 -0
  50. package/build/esm/components/Toggle/index.js +72 -0
  51. package/build/esm/components/UnknownInput/index.js +19 -0
  52. package/build/esm/index.js +33 -0
  53. package/build/esm/theme/colors.js +48 -0
  54. package/build/index.js +8 -0
  55. package/package.json +123 -0
  56. package/src/components/AttributeIcon/Div.js +63 -0
  57. package/src/components/AttributeIcon/index.js +72 -0
  58. package/src/components/Button/index.js +95 -0
  59. package/src/components/Checkbox/index.js +86 -0
  60. package/src/components/Checkbox/tests/Checkbox.test.js +49 -0
  61. package/src/components/Count/Wrapper.js +36 -0
  62. package/src/components/Count/index.js +30 -0
  63. package/src/components/DatePicker/index.js +213 -0
  64. package/src/components/DatePicker/reducer.js +27 -0
  65. package/src/components/DatePicker/tests/__snapshots__/index.test.js.snap +301 -0
  66. package/src/components/DatePicker/tests/index.test.js +111 -0
  67. package/src/components/Enumeration/index.js +71 -0
  68. package/src/components/Enumeration/tests/index.test.js +41 -0
  69. package/src/components/Error/index.js +118 -0
  70. package/src/components/Error/reducer.js +14 -0
  71. package/src/components/Flex/index.js +25 -0
  72. package/src/components/Flex/tests/__snapshots__/index.test.js.snap +28 -0
  73. package/src/components/Flex/tests/index.test.js +11 -0
  74. package/src/components/HeaderActions/index.js +52 -0
  75. package/src/components/HeaderActions/tests/index.test.js +15 -0
  76. package/src/components/HeaderTitle/index.js +59 -0
  77. package/src/components/HeaderTitle/tests/index.test.js +15 -0
  78. package/src/components/Icon/index.js +50 -0
  79. package/src/components/Icon/tests/Icon.test.js +33 -0
  80. package/src/components/IconLinks/index.js +39 -0
  81. package/src/components/IconLinks/tests/index.test.js +27 -0
  82. package/src/components/InputNumber/index.js +74 -0
  83. package/src/components/InputText/PrefixIcon.js +38 -0
  84. package/src/components/InputText/index.js +88 -0
  85. package/src/components/Label/index.js +53 -0
  86. package/src/components/Label/tests/Label.test.js +38 -0
  87. package/src/components/List/index.js +56 -0
  88. package/src/components/List/tests/index.test.js +19 -0
  89. package/src/components/ListHeader/BaselineAlignement.js +7 -0
  90. package/src/components/ListHeader/index.js +58 -0
  91. package/src/components/ListHeader/tests/index.test.js +11 -0
  92. package/src/components/ListRow/index.js +34 -0
  93. package/src/components/ListRow/tests/index.tests.js +32 -0
  94. package/src/components/NavTabs/index.js +51 -0
  95. package/src/components/Option/RemoveButton.js +18 -0
  96. package/src/components/Option/index.js +32 -0
  97. package/src/components/Padded/index.js +47 -0
  98. package/src/components/Padded/tests/__snapshots__/index.test.js.snap +8 -0
  99. package/src/components/Padded/tests/index.test.js +11 -0
  100. package/src/components/Paging/index.js +66 -0
  101. package/src/components/Picker/PickerButton.js +84 -0
  102. package/src/components/Picker/PickerSection.js +41 -0
  103. package/src/components/Picker/PickerWrapper.js +7 -0
  104. package/src/components/Picker/index.js +44 -0
  105. package/src/components/Picker/tests/__snapshots__/pickerButton.test.js.snap +54 -0
  106. package/src/components/Picker/tests/__snapshots__/pickerSection.test.js.snap +20 -0
  107. package/src/components/Picker/tests/pickerButton.test.js +11 -0
  108. package/src/components/Picker/tests/pickerSection.test.js +11 -0
  109. package/src/components/PrefixIcon/index.js +11 -0
  110. package/src/components/Select/index.js +110 -0
  111. package/src/components/Select/tests/index.test.js +85 -0
  112. package/src/components/Separator/index.js +49 -0
  113. package/src/components/Table/ActionCollapse.js +53 -0
  114. package/src/components/Table/index.js +172 -0
  115. package/src/components/Table/tests/index.js +146 -0
  116. package/src/components/TableHeader/index.js +103 -0
  117. package/src/components/TableHeader/tests/index.test.js +85 -0
  118. package/src/components/TableRow/index.js +116 -0
  119. package/src/components/TableRow/tests/index.test.js +89 -0
  120. package/src/components/Text/index.js +62 -0
  121. package/src/components/Text/tests/__snapshots__/index.test.js.snap +19 -0
  122. package/src/components/Text/tests/index.test.js +11 -0
  123. package/src/components/Textarea/index.js +19 -0
  124. package/src/components/Textarea/tests/index.test.js +23 -0
  125. package/src/components/TimePicker/index.js +328 -0
  126. package/src/components/TimePicker/tests/index.test.js +95 -0
  127. package/src/components/Toggle/index.js +83 -0
  128. package/src/components/Toggle/tests/index.test.js +40 -0
  129. package/src/components/UnknownInput/index.js +20 -0
  130. package/src/index.js +33 -0
  131. package/src/theme/colors.js +48 -0
@@ -0,0 +1,62 @@
1
+ import styled from 'styled-components';
2
+ import PropTypes from 'prop-types';
3
+ import colors from '../../theme/colors';
4
+
5
+ const Text = styled.p`
6
+ margin: 0;
7
+ line-height: ${({ lineHeight }) => lineHeight};
8
+ color: ${({ theme, color }) => theme.main.colors[color] || color};
9
+ font-size: ${({ theme, fontSize }) => theme.main.sizes.fonts[fontSize]};
10
+ font-weight: ${({ theme, fontWeight }) => theme.main.fontWeights[fontWeight]};
11
+ text-transform: ${({ textTransform }) => textTransform};
12
+ ${({ ellipsis }) =>
13
+ ellipsis &&
14
+ `
15
+ white-space: nowrap;
16
+ overflow: hidden;
17
+ text-overflow: ellipsis;
18
+ `}
19
+ `;
20
+
21
+ Text.defaultProps = {
22
+ color: 'greyDark',
23
+ ellipsis: false,
24
+ fontSize: 'md',
25
+ fontWeight: 'regular',
26
+ lineHeight: 'normal',
27
+ textTransform: 'none',
28
+ // TODO : This is temporary
29
+ theme: {
30
+ main: {
31
+ colors,
32
+ sizes: {
33
+ fonts: {
34
+ xs: '11px',
35
+ sm: '12px',
36
+ md: '13px',
37
+ lg: '18px',
38
+ xl: '24px',
39
+ },
40
+ },
41
+ fontWeights: {
42
+ regular: 400,
43
+ semiBold: 500,
44
+ bold: 600,
45
+ black: 900,
46
+ },
47
+ },
48
+ },
49
+ };
50
+
51
+ Text.propTypes = {
52
+ color: PropTypes.string,
53
+ ellipsis: PropTypes.bool,
54
+ fontSize: PropTypes.string,
55
+ fontWeight: PropTypes.string,
56
+ lineHeight: PropTypes.string,
57
+ textTransform: PropTypes.string,
58
+ // eslint-disable-next-line react/forbid-prop-types
59
+ theme: PropTypes.object,
60
+ };
61
+
62
+ export default Text;
@@ -0,0 +1,19 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<Text /> should match snapshot 1`] = `
4
+ .c0 {
5
+ margin: 0;
6
+ line-height: normal;
7
+ color: #292b2c;
8
+ font-size: 13px;
9
+ font-weight: 400;
10
+ text-transform: none;
11
+ }
12
+
13
+ <p
14
+ className="c0"
15
+ color="greyDark"
16
+ fontSize="md"
17
+ fontWeight="regular"
18
+ />
19
+ `;
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import renderer from 'react-test-renderer';
3
+ import Text from '../index';
4
+
5
+ describe('<Text />', () => {
6
+ it('should match snapshot', () => {
7
+ const tree = renderer.create(<Text />).toJSON();
8
+
9
+ expect(tree).toMatchSnapshot();
10
+ });
11
+ });
@@ -0,0 +1,19 @@
1
+ /**
2
+ *
3
+ * Textarea
4
+ *
5
+ */
6
+
7
+ import React from 'react';
8
+ import PropTypes from 'prop-types';
9
+ import { Textarea as StyledTextArea } from '@punch-in/buffet-modern';
10
+
11
+ function Textarea(props) {
12
+ return <StyledTextArea {...props} />;
13
+ }
14
+
15
+ Textarea.propTypes = {
16
+ name: PropTypes.string.isRequired,
17
+ };
18
+
19
+ export default Textarea;
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import { mount } from 'enzyme';
3
+
4
+ import Textarea from '../index';
5
+
6
+ let renderedComponent;
7
+ const initProps = {
8
+ name: 'textarea',
9
+ onChange: jest.fn(),
10
+ value: '',
11
+ };
12
+ const renderComponent = (props = initProps) => mount(<Textarea {...props} />);
13
+
14
+ describe('<Textarea />', () => {
15
+ afterEach(() => {
16
+ renderedComponent.unmount();
17
+ });
18
+
19
+ it('should have a placeholder undefined if not specified', () => {
20
+ renderedComponent = renderComponent();
21
+ expect(renderedComponent.at(0).prop('placeholder')).toBe(undefined);
22
+ });
23
+ });
@@ -0,0 +1,328 @@
1
+ /**
2
+ *
3
+ * TimePicker
4
+ *
5
+ */
6
+
7
+ import React, {
8
+ useCallback,
9
+ useEffect,
10
+ useState,
11
+ useRef,
12
+ useMemo,
13
+ } from 'react';
14
+ import { isInteger, toNumber } from 'lodash';
15
+ import PropTypes from 'prop-types';
16
+
17
+ import {
18
+ IconWrapper,
19
+ TimePicker as StyledTimePicker,
20
+ TimePickerWrapper,
21
+ TimeList,
22
+ } from '@punch-in/buffet-modern';
23
+ import {
24
+ useEventListener,
25
+ useShortcutEffect,
26
+ } from '@punch-in/buffet-modern-hooks';
27
+ import Icon from '../Icon';
28
+
29
+ const MINUTES_IN_HOUR = 60;
30
+
31
+ // Returns string with two digits padded at start with 0
32
+ const pad = num => `0${num}`.substr(-2);
33
+
34
+ // Convert time array to formatted time string
35
+ export const timeFormatter = time => {
36
+ const newTime = Array(3)
37
+ .fill('00')
38
+ .concat(splitArray(time))
39
+ .reverse();
40
+ newTime.length = 3;
41
+
42
+ return format(newTime).join(':');
43
+ };
44
+
45
+ // Convert time string to time array
46
+ const splitArray = string => {
47
+ if (isInteger(toNumber(string)) && string) {
48
+ const stringFormat = string.length === 3 ? `0${string}` : string;
49
+
50
+ return stringFormat.match(/.{1,2}/g).reverse();
51
+ }
52
+
53
+ const lowercase = string ? string.toLowerCase() : '0';
54
+ const array = lowercase.includes('h')
55
+ ? lowercase.split('h')
56
+ : lowercase.split(':');
57
+
58
+ return array.reverse().filter(v => !!v);
59
+ };
60
+
61
+ // Ensure two-digit format for minutes and seconds
62
+ const format = array =>
63
+ array.map((string, i) => {
64
+ if (string.length < 2) {
65
+ return i === 0 ? `0${string}` : `${string}0`;
66
+ }
67
+
68
+ return string;
69
+ });
70
+
71
+ // Hide seconds if needed
72
+ const short = hour => {
73
+ const array = hour.split(':');
74
+ if (array.length > 2) {
75
+ return array.slice(0, -1).join(':');
76
+ }
77
+
78
+ return hour;
79
+ };
80
+
81
+ // return array of minutes in hours with current step
82
+ const getMinutesArr = step => {
83
+ const length = MINUTES_IN_HOUR / step;
84
+
85
+ return Array.from({ length }, (_v, i) => step * i);
86
+ };
87
+
88
+ // Generate options for TimeList display
89
+ const getOptions = step => {
90
+ const hours = Array.from({ length: 24 }, (_, i) => i);
91
+ const minutes = getMinutesArr(step);
92
+
93
+ const options = hours.reduce((acc, cur) => {
94
+ const hour = pad(cur);
95
+
96
+ const hourOptions = minutes.map(minute => {
97
+ const label = `${hour}:${pad(minute)}`;
98
+
99
+ return { value: `${label}:00`, label };
100
+ });
101
+
102
+ return acc.concat(hourOptions);
103
+ }, []);
104
+
105
+ return options;
106
+ };
107
+
108
+ // Find the nearest time option to select a TimeList value
109
+ const roundHour = (time, step) => {
110
+ const arr = splitArray(time);
111
+ const minutesArr = getMinutesArr(step);
112
+ const nearMin = nearest(
113
+ minutesArr.concat(MINUTES_IN_HOUR),
114
+ parseInt(arr[1], 10)
115
+ );
116
+
117
+ arr[1] = minutesArr.includes(arr[1]) ? '00' : pad(nearMin);
118
+ arr[2] = nearMin === 60 ? `${parseInt(arr[2], 10) + 1}` : arr[2];
119
+
120
+ return format(arr.reverse()).join(':');
121
+ };
122
+
123
+ // Set the nearest option to select a TimeList value
124
+ const nearest = (arr, val) =>
125
+ arr.reduce(
126
+ (p, n) => (Math.abs(p) > Math.abs(n - val) ? n - val : p),
127
+ Infinity
128
+ ) + val;
129
+
130
+ function TimePicker(props) {
131
+ const { name, onChange, seconds, tabIndex, value, step } = props;
132
+ const [inputVal, setInputVal] = useState('');
133
+ const [isOpen, setIsOpen] = useState(false);
134
+ const options = useMemo(() => getOptions(step), [step]);
135
+ const inputRef = useRef();
136
+ const wrapperRef = useRef();
137
+ const listRef = useRef();
138
+ const listRefs = options.reduce((acc, curr) => {
139
+ acc[curr.value] = useRef();
140
+
141
+ return acc;
142
+ }, {});
143
+
144
+ const currentTimeSelected = useMemo(
145
+ () => roundHour(timeFormatter(inputVal), step),
146
+ [inputVal, step]
147
+ );
148
+
149
+ // Effect to set the time
150
+ useEffect(() => {
151
+ if (!isOpen) {
152
+ const time = seconds ? value : short(value);
153
+
154
+ setInputVal(time);
155
+ }
156
+ }, [value, seconds, isOpen]);
157
+
158
+ // Effect to enable scrolling
159
+ useEffect(() => {
160
+ const currentRef = currentTimeSelected;
161
+
162
+ if (isOpen && listRefs[currentRef]) {
163
+ listRef.current.scrollTop = listRefs[currentRef].current.offsetTop;
164
+ }
165
+ }, [isOpen, currentTimeSelected, listRefs]);
166
+
167
+ // Custom hook to close the TimeList
168
+ useEventListener(
169
+ 'click',
170
+ event => {
171
+ if (!wrapperRef.current.contains(event.target)) {
172
+ setIsOpen(false);
173
+ }
174
+ },
175
+ isOpen
176
+ );
177
+
178
+ // Custom hook to select a time using the keyboard's up arrow
179
+ useShortcutEffect(
180
+ 'arrowUp',
181
+ () => {
182
+ if (isOpen) {
183
+ const currentIndex = options.findIndex(
184
+ o => o.value === currentTimeSelected
185
+ );
186
+ if (!currentIndex) return;
187
+ const nextIndex = currentIndex - 1;
188
+
189
+ const nextTime = options[nextIndex] || options[currentIndex];
190
+
191
+ updateTime(nextTime.value);
192
+ }
193
+ },
194
+ isOpen
195
+ );
196
+
197
+ // Custom hook to select a time using the keyboard's down arrow
198
+ useShortcutEffect(
199
+ 'arrowDown',
200
+ () => {
201
+ if (isOpen) {
202
+ const currentIndex = options.findIndex(
203
+ o => o.value === currentTimeSelected
204
+ );
205
+ const lastIndex = options.length - 1;
206
+ if (currentIndex >= lastIndex) return;
207
+ const nextIndex = currentIndex + 1;
208
+
209
+ const nextTime = options[nextIndex] || options[lastIndex];
210
+
211
+ updateTime(nextTime.value);
212
+ }
213
+ },
214
+ isOpen
215
+ );
216
+
217
+ // Custom hook to close the time list
218
+ useShortcutEffect(
219
+ 'enter',
220
+ () => {
221
+ if (isOpen) {
222
+ setIsOpen(false);
223
+ inputRef.current.blur();
224
+ }
225
+ },
226
+ isOpen
227
+ );
228
+
229
+ useShortcutEffect(
230
+ 'tab',
231
+ () => {
232
+ if (isOpen) {
233
+ setIsOpen(false);
234
+ inputRef.current.blur();
235
+ }
236
+ },
237
+ isOpen
238
+ );
239
+
240
+ const handleChange = ({ target }) => {
241
+ updateTime(target.value);
242
+ };
243
+
244
+ const handleChangeRadio = useCallback(() => {}, []);
245
+
246
+ const formatInputValue = time => {
247
+ if (!seconds) {
248
+ setInputVal(short(time));
249
+ } else {
250
+ setInputVal(time);
251
+ }
252
+ };
253
+
254
+ const handleClick = ({ target }) => {
255
+ updateTime(target.value);
256
+ setIsOpen(false);
257
+ };
258
+
259
+ const updateTime = time => {
260
+ formatInputValue(time);
261
+ onChange({
262
+ target: {
263
+ name,
264
+ type: 'time',
265
+ value: timeFormatter(time),
266
+ },
267
+ });
268
+ };
269
+
270
+ return (
271
+ <TimePickerWrapper ref={wrapperRef} className={props.className}>
272
+ <StyledTimePicker
273
+ {...props}
274
+ autoComplete="off"
275
+ onChange={handleChange}
276
+ onFocus={() => setIsOpen(true)}
277
+ ref={inputRef}
278
+ type="text"
279
+ value={inputVal}
280
+ tabIndex={tabIndex}
281
+ />
282
+ <IconWrapper>
283
+ <Icon icon="time" />
284
+ </IconWrapper>
285
+ <TimeList className={isOpen && 'displayed'} ref={listRef}>
286
+ {isOpen &&
287
+ options.map(option => (
288
+ <li key={option.value} ref={listRefs[option.value]}>
289
+ <input
290
+ type="radio"
291
+ onChange={handleChangeRadio}
292
+ onClick={handleClick}
293
+ value={option.value}
294
+ id={option.value}
295
+ name="time"
296
+ checked={option.value === currentTimeSelected}
297
+ tabIndex="0"
298
+ />
299
+ <label htmlFor={option.value}>{option.label}</label>
300
+ </li>
301
+ ))}
302
+ </TimeList>
303
+ </TimePickerWrapper>
304
+ );
305
+ }
306
+
307
+ TimePicker.defaultProps = {
308
+ className: null,
309
+ onChange: () => {},
310
+ tabIndex: '0',
311
+ seconds: false,
312
+ value: '',
313
+ step: 30,
314
+ };
315
+
316
+ TimePicker.propTypes = {
317
+ className: PropTypes.string,
318
+ name: PropTypes.string.isRequired,
319
+ onChange: PropTypes.func,
320
+ seconds: PropTypes.bool,
321
+ step: (props, propName) =>
322
+ MINUTES_IN_HOUR % props[propName] > 0 &&
323
+ new Error('step should be divisible by 60'),
324
+ tabIndex: PropTypes.string,
325
+ value: PropTypes.string,
326
+ };
327
+
328
+ export default TimePicker;
@@ -0,0 +1,95 @@
1
+ import React from 'react';
2
+ import { mount } from 'enzyme';
3
+
4
+ import { TimePicker as StyledTimePicker } from '@punch-in/buffet-modern';
5
+ import TimePicker, { timeFormatter } from '../index';
6
+
7
+ const defaultProps = { name: 'time' };
8
+ const renderComponent = (props = defaultProps) =>
9
+ mount(<TimePicker {...props} />);
10
+
11
+ describe('<TimePicker />', () => {
12
+ describe('Parser onBlur', () => {
13
+ it('Should format the value to 10:38:00 if 10:38 is given', () => {
14
+ expect(timeFormatter('10:38')).toEqual('10:38:00');
15
+ });
16
+
17
+ it('Should format the value to 10:38:00 if 10h38 is given', () => {
18
+ expect(timeFormatter('10h38')).toEqual('10:38:00');
19
+ });
20
+
21
+ it('Should format the value to 10:38:00 if 10H38 is given', () => {
22
+ expect(timeFormatter('10H38')).toEqual('10:38:00');
23
+ });
24
+
25
+ it('Should format the value to 10:00:00 if 10: is given', () => {
26
+ expect(timeFormatter('10:')).toEqual('10:00:00');
27
+ });
28
+
29
+ it('Should format the value to 10:00:00 if 10 is given', () => {
30
+ expect(timeFormatter('10')).toEqual('10:00:00');
31
+ });
32
+
33
+ it('Should format the value to 10:38:38 if 10:38:38 is given', () => {
34
+ expect(timeFormatter('10:38:38')).toEqual('10:38:38');
35
+ });
36
+
37
+ it('Should format the value to 01:11:00 if 1:11:00 is given', () => {
38
+ expect(timeFormatter('1:11:00')).toEqual('01:11:00');
39
+ });
40
+
41
+ it('Should format the value to 01:10:00 if 11:1:00 is given', () => {
42
+ expect(timeFormatter('11:1:00')).toEqual('11:10:00');
43
+ });
44
+
45
+ it('Should format the value to 11:10:2 if 11:10:20 is given', () => {
46
+ expect(timeFormatter('11:10:2')).toEqual('11:10:20');
47
+ });
48
+
49
+ it('Should format the value to 1200 if 12:00:00 is given', () => {
50
+ expect(timeFormatter('1200')).toEqual('12:00:00');
51
+ });
52
+
53
+ it('Should format the value to 111 if 01:11:00 is given', () => {
54
+ expect(timeFormatter('111')).toEqual('01:11:00');
55
+ });
56
+
57
+ it('Should format the value to 00:00:00 if null is given', () => {
58
+ expect(timeFormatter(null)).toEqual('00:00:00');
59
+ });
60
+
61
+ it('Should format the value to 00:00:00 if nothing is given', () => {
62
+ expect(timeFormatter('')).toEqual('00:00:00');
63
+ });
64
+ });
65
+
66
+ describe('<TimePicker /> behavior', () => {
67
+ // eslint-disable-next-line jest/expect-expect
68
+ it('should not crash', () => {
69
+ renderComponent();
70
+ });
71
+
72
+ it('should send a formatted string onChange', () => {
73
+ const onChange = jest.fn();
74
+ const value = '';
75
+ const renderedComponent = renderComponent({
76
+ ...defaultProps,
77
+ onChange,
78
+ value,
79
+ });
80
+
81
+ const element = renderedComponent.find(StyledTimePicker);
82
+ const mock = { target: { value: '10' } };
83
+ element.simulate('change', mock);
84
+
85
+ const expected = {
86
+ target: {
87
+ name: 'time',
88
+ type: 'time',
89
+ value: '10:00:00',
90
+ },
91
+ };
92
+ expect(onChange).toHaveBeenCalledWith(expected);
93
+ });
94
+ });
95
+ });
@@ -0,0 +1,83 @@
1
+ /**
2
+ *
3
+ * Toggle
4
+ *
5
+ */
6
+
7
+ import React, { useCallback } from 'react';
8
+ import PropTypes from 'prop-types';
9
+
10
+ import { Toggle as StyledToggle, ToggleWrapper } from '@punch-in/buffet-modern';
11
+ import Label from '../Label';
12
+
13
+ function Toggle({
14
+ disabled,
15
+ id,
16
+ className,
17
+ name,
18
+ onChange,
19
+ value,
20
+ leftLabel,
21
+ rightLabel,
22
+ }) {
23
+ const isIndeterminate = value === null;
24
+
25
+ const handleRef = useCallback(
26
+ element => {
27
+ if (element) {
28
+ element.indeterminate = isIndeterminate; // eslint-disable-line no-param-reassign
29
+ }
30
+ },
31
+ [isIndeterminate]
32
+ );
33
+
34
+ const handleChange = e => {
35
+ let targetValue = e.target.checked;
36
+ // Handle click when the state is inteterminate
37
+ if (isIndeterminate) {
38
+ // Select the right value depending on the mouse position
39
+ targetValue = e.nativeEvent.offsetX >= e.target.offsetWidth / 2;
40
+ }
41
+ onChange({ target: { name, value: targetValue } });
42
+ };
43
+
44
+ return (
45
+ <ToggleWrapper className={className}>
46
+ <Label htmlFor={id || name}>
47
+ <StyledToggle
48
+ disabled={disabled}
49
+ checked={value || false}
50
+ id={id || name}
51
+ name={id || name}
52
+ onChange={handleChange}
53
+ ref={handleRef}
54
+ />
55
+ <span>{leftLabel}</span>
56
+ <span>{rightLabel}</span>
57
+ </Label>
58
+ </ToggleWrapper>
59
+ );
60
+ }
61
+
62
+ Toggle.defaultProps = {
63
+ className: null,
64
+ disabled: false,
65
+ id: null,
66
+ leftLabel: 'OFF',
67
+ onChange: () => {},
68
+ rightLabel: 'ON',
69
+ value: false,
70
+ };
71
+
72
+ Toggle.propTypes = {
73
+ className: PropTypes.string,
74
+ disabled: PropTypes.bool,
75
+ id: PropTypes.string,
76
+ leftLabel: PropTypes.string,
77
+ name: PropTypes.string.isRequired,
78
+ onChange: PropTypes.func,
79
+ rightLabel: PropTypes.string,
80
+ value: PropTypes.bool,
81
+ };
82
+
83
+ export default Toggle;
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+ import { mount } from 'enzyme';
3
+
4
+ import Toggle from '../index';
5
+
6
+ const defaultProps = {
7
+ name: 'toggle',
8
+ type: 'checkbox',
9
+ };
10
+ const renderComponent = (props = defaultProps) => mount(<Toggle {...props} />);
11
+
12
+ describe('<Toggle />', () => {
13
+ // eslint-disable-next-line jest/expect-expect
14
+ it('should not crash', () => {
15
+ renderComponent();
16
+ });
17
+
18
+ it('should send a boolean', () => {
19
+ const onChange = jest.fn();
20
+ const value = false;
21
+ const renderedComponent = renderComponent({
22
+ ...defaultProps,
23
+ onChange,
24
+ value,
25
+ });
26
+ const element = renderedComponent.find('input');
27
+ element.simulate('change');
28
+
29
+ expect(onChange).toHaveBeenCalled();
30
+ });
31
+
32
+ it('should use the defaultProps', () => {
33
+ const {
34
+ defaultProps: { onChange },
35
+ } = Toggle;
36
+
37
+ expect(onChange).toBeDefined();
38
+ expect(onChange({ preventDefault: jest.fn() })).toBe(undefined);
39
+ });
40
+ });
@@ -0,0 +1,20 @@
1
+ /**
2
+ *
3
+ * UnknownInput
4
+ *
5
+ */
6
+
7
+ import React from 'react';
8
+ import PropTypes from 'prop-types';
9
+
10
+ const UnknownInput = ({ type }) => <div>This {type} is not available</div>;
11
+
12
+ UnknownInput.defaultProps = {
13
+ type: null,
14
+ };
15
+
16
+ UnknownInput.propTypes = {
17
+ type: PropTypes.string,
18
+ };
19
+
20
+ export default UnknownInput;