@jasperoosthoek/react-toolbox 0.8.0 → 0.9.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.
Files changed (63) hide show
  1. package/change-log.md +330 -309
  2. package/dist/components/buttons/ConfirmButton.d.ts +2 -2
  3. package/dist/components/buttons/DeleteConfirmButton.d.ts +2 -2
  4. package/dist/components/buttons/IconButtons.d.ts +40 -41
  5. package/dist/components/errors/Errors.d.ts +1 -2
  6. package/dist/components/forms/FormField.d.ts +22 -0
  7. package/dist/components/forms/FormFields.d.ts +1 -56
  8. package/dist/components/forms/FormModal.d.ts +7 -34
  9. package/dist/components/forms/FormModalProvider.d.ts +19 -26
  10. package/dist/components/forms/FormProvider.d.ts +66 -0
  11. package/dist/components/forms/fields/FormBadgesSelection.d.ts +26 -0
  12. package/dist/components/forms/fields/FormCheckbox.d.ts +7 -0
  13. package/dist/components/forms/fields/FormDropdown.d.ts +19 -0
  14. package/dist/components/forms/fields/FormInput.d.ts +17 -0
  15. package/dist/components/forms/fields/FormSelect.d.ts +12 -0
  16. package/dist/components/forms/fields/index.d.ts +5 -0
  17. package/dist/components/indicators/CheckIndicator.d.ts +1 -2
  18. package/dist/components/indicators/LoadingIndicator.d.ts +4 -4
  19. package/dist/components/login/LoginPage.d.ts +1 -1
  20. package/dist/components/tables/DataTable.d.ts +2 -2
  21. package/dist/components/tables/DragAndDropList.d.ts +2 -2
  22. package/dist/components/tables/SearchBox.d.ts +2 -2
  23. package/dist/index.d.ts +4 -1
  24. package/dist/index.js +2 -2
  25. package/dist/index.js.LICENSE.txt +0 -4
  26. package/dist/localization/LocalizationContext.d.ts +1 -1
  27. package/dist/utils/hooks.d.ts +1 -1
  28. package/dist/utils/timeAndDate.d.ts +5 -2
  29. package/dist/utils/utils.d.ts +3 -3
  30. package/package.json +10 -11
  31. package/src/__tests__/buttons.test.tsx +545 -0
  32. package/src/__tests__/errors.test.tsx +339 -0
  33. package/src/__tests__/forms.test.tsx +3021 -0
  34. package/src/__tests__/hooks.test.tsx +413 -0
  35. package/src/__tests__/indicators.test.tsx +284 -0
  36. package/src/__tests__/localization.test.tsx +462 -0
  37. package/src/__tests__/login.test.tsx +417 -0
  38. package/src/__tests__/setupTests.ts +328 -0
  39. package/src/__tests__/tables.test.tsx +609 -0
  40. package/src/__tests__/timeAndDate.test.tsx +308 -0
  41. package/src/__tests__/utils.test.tsx +422 -0
  42. package/src/components/forms/FormField.tsx +92 -0
  43. package/src/components/forms/FormFields.tsx +3 -423
  44. package/src/components/forms/FormModal.tsx +168 -243
  45. package/src/components/forms/FormModalProvider.tsx +141 -95
  46. package/src/components/forms/FormProvider.tsx +218 -0
  47. package/src/components/forms/fields/FormBadgesSelection.tsx +108 -0
  48. package/src/components/forms/fields/FormCheckbox.tsx +76 -0
  49. package/src/components/forms/fields/FormDropdown.tsx +123 -0
  50. package/src/components/forms/fields/FormInput.tsx +114 -0
  51. package/src/components/forms/fields/FormSelect.tsx +47 -0
  52. package/src/components/forms/fields/index.ts +6 -0
  53. package/src/index.ts +32 -28
  54. package/src/localization/LocalizationContext.tsx +156 -131
  55. package/src/localization/localization.ts +131 -131
  56. package/src/utils/hooks.ts +108 -94
  57. package/src/utils/timeAndDate.ts +33 -4
  58. package/src/utils/utils.ts +74 -66
  59. package/dist/components/forms/CreateEditModal.d.ts +0 -41
  60. package/dist/components/forms/CreateEditModalProvider.d.ts +0 -41
  61. package/dist/components/forms/FormFields.test.d.ts +0 -4
  62. package/dist/login/Login.d.ts +0 -70
  63. package/src/components/forms/FormFields.test.tsx +0 -107
@@ -1,8 +1,5 @@
1
- import React, { ReactElement, ChangeEvent, KeyboardEvent, useMemo } from 'react';
2
- import { Form, FormControl, Badge, Dropdown, BadgeProps, FormControlProps, FormCheckProps } from 'react-bootstrap';
3
- import { Variant} from 'react-bootstrap/types';
4
- import { useLocalization } from '../../localization/LocalizationContext';
5
- import moment, { Moment } from 'moment';
1
+ import React, { ReactElement } from 'react';
2
+ import { FormControlProps, FormCheckProps } from 'react-bootstrap';
6
3
 
7
4
  export type FormValue = boolean | string | string[] | number | number[];
8
5
 
@@ -33,427 +30,10 @@ export type FormType = {
33
30
  pristine: boolean;
34
31
  }
35
32
 
36
- export interface FormInputProps extends Omit<FormControlProps, 'onChange'>, FormType {
37
- onChange: (value: FormValue) => void;
38
- controlId?: string;
39
- label?: ReactElement;
40
- onEnter?: () => void;
41
- rows?: number;
42
- }
43
-
44
- export const FormInput = ({
45
- label,
46
- value,
47
- onEnter,
48
- placeholder,
49
- onChange,
50
- controlId,
51
- state,
52
- setState,
53
- initialState,
54
- initialValue,
55
- keyName,
56
- pristine,
57
- id,
58
- ...formProps
59
- }: FormInputProps) => (
60
- <Form.Group controlId={controlId}>
61
- {label && <Form.Label htmlFor={id || keyName}>{label}</Form.Label>}
62
- <Form.Control
63
- autoComplete="off"
64
- id={id || (label && keyName)}
65
- {...formProps}
66
- value={value || ''}
67
- onChange={(e: ChangeEvent<HTMLInputElement>) => onChange(e.target.value)}
68
- placeholder={placeholder}
69
- onKeyDown={(e: KeyboardEvent<HTMLInputElement>) => {
70
- if (e.key === 'Enter' && typeof onEnter === 'function') {
71
- e.preventDefault();
72
- onEnter();
73
- }
74
- }}
75
- />
76
- </Form.Group>
77
- );
78
-
79
- export const FormTextArea = ({ as = 'textarea', rows = 3, ...restProps }: FormInputProps) => (
80
- <FormInput
81
- as={as}
82
- rows={rows}
83
- {...restProps}
84
- />
85
- );
86
-
87
- export type FormTextAreaProps = FormInputProps;
88
-
89
- export const FormDate = (props: FormInputProps) => (
90
- <FormInput
91
- {...props}
92
- type='date'
93
- />
94
- )
95
-
96
- export interface FormDateTimeProps extends Omit<FormInputProps, 'onChange' | 'value'> {
97
- value: string | Moment;
98
- onChange: (value: string) => void;
99
- }
100
-
101
- export const FormDateTime = ({ value, onChange, ...restProps }: FormDateTimeProps) => (
102
- <FormInput
103
- {...restProps}
104
- value={
105
- typeof value === 'string'
106
- ? value
107
- : moment(value).format('YYYY-MM-DDTHH:mm')
108
- }
109
- onChange={newValue => onChange(moment(newValue as string).utc().format())}
110
- type='datetime-local'
111
- />
112
- );
113
-
114
-
115
- export interface FormCheckboxProps extends Omit<FormCheckProps, 'onChange'>, FormType {
116
- onChange: (value: boolean) => void;
117
- controlId?: string;
118
- label?: ReactElement;
119
- onEnter?: () => void;
120
- rows?: number;
121
- }
122
-
123
- export const FormCheckbox = ({
124
- value,
125
- onChange,
126
- state,
127
- label,
128
- keyName,
129
- controlId,
130
- initialState,
131
- initialValue,
132
- setState,
133
- pristine,
134
- id,
135
- ...restProps
136
- }: FormCheckboxProps) => (
137
- <Form.Group controlId={controlId}>
138
- {label && (
139
- <Form.Label
140
- onClick={() => onChange(!value)}
141
- htmlFor={id || keyName}
142
- >
143
- {label}
144
- </Form.Label>
145
- )}
146
- <Form.Check
147
- type="checkbox"
148
- checked={!!value}
149
- onClick={e => e.stopPropagation()}
150
- onChange={e => onChange(e.target.checked)}
151
- id={id || (label && keyName)}
152
- {...restProps}
153
- />
154
- </Form.Group>
155
- );
156
-
157
- export const FormSwitch = ({ className = '', ...restProps }: FormCheckboxProps) => (
158
- <FormCheckbox
159
- type="switch"
160
- {...restProps}
161
- />
162
- );
163
-
164
33
  export type DisabledProps = {
165
34
  list: any[];
166
35
  value: string | number;
167
36
  state: any;
168
37
  initialState: any;
169
38
  initialValue: any;
170
- }
171
-
172
- export interface FormSelectProps extends Omit<FormInputProps, 'disabled' | 'onChange' | 'list'> {
173
- onChange: (value: number | string | number[] | string[] | null) => void;
174
- list: any[];
175
- multiple?: boolean;
176
- integer?: boolean;
177
- formatTitle?: (item: any) => ReactElement;
178
- idKey?: string;
179
- disabled?: boolean | ((props: DisabledProps) => boolean);
180
- }
181
-
182
- export const FormSelect = ({
183
- list,
184
- idKey = 'id',
185
- integer = false,
186
- value,
187
- defaultValue,
188
- onChange,
189
- label,
190
- controlId,
191
- htmlSize = 5,
192
- state,
193
- formatTitle,
194
- multiple,
195
- disabled,
196
- isInvalid,
197
- initialState,
198
- initialValue,
199
- keyName,
200
- pristine,
201
- }: FormSelectProps) => {
202
- multiple = multiple || multiple === false ? multiple : value instanceof Array;
203
- const parseInteger = (value: string | number): string | number => integer ? parseInt(`${value}`) : `${value}`;
204
-
205
- return <>
206
- <Form.Group controlId={controlId}>
207
- {label && <Form.Label>{label}</Form.Label>}
208
- <FormControl as="select"
209
- htmlSize={htmlSize}
210
- multiple={multiple}
211
- value={Array.isArray(value) ? value.map((v: number | string) => v.toString()) : value ? value.toString() : ''}
212
- // Dummy onChange is used to suppress warning.
213
- onChange={() => {}}
214
- disabled={typeof disabled === 'boolean' ? disabled : false}
215
- isInvalid={isInvalid}
216
- >
217
- <option value='' disabled hidden />
218
- {Object.values(list).map((item, index) => {
219
- const selected = Array.isArray(value) ? value.find(id => parseInteger(id) === parseInteger(item[idKey])) : value === item[idKey];
220
- return (
221
- <option
222
- key={index}
223
- value={`${item[idKey]}`}
224
- disabled={
225
- typeof disabled === 'function'
226
- ? disabled({
227
- list,
228
- value: parseInteger(item[idKey]),
229
- state,
230
- initialState,
231
- initialValue,
232
- })
233
- : disabled
234
- }
235
- onClick={() => {
236
- // Use onClick here instead of onChange in Form.Select to be able to unset all the options which isn't possible otherwise
237
- if (onChange === null) {
238
- return;
239
- }
240
- if (Array.isArray(value)) {
241
- if(selected) {
242
- onChange(value.filter(id => parseInteger(id) !== parseInteger(item[idKey])));
243
- } else {
244
- // onChange([...value, parseInteger(item[idKey])]);
245
- onChange(
246
- integer
247
- ? [...value.map((v: string | number ) => parseInt(v as string)), parseInt(item[idKey])]
248
- : [...value.map((v: string | number ) => `${v}`), `${item[idKey]}`]
249
- );
250
- }
251
- } else {
252
- // onChange(selected ? null : parseInteger(item[idKey]))
253
- onChange(selected ? null : parseInteger(item[idKey]))
254
- }
255
- }}
256
- defaultValue={defaultValue}
257
- >
258
- {formatTitle ? formatTitle(item) : item.children }
259
- </option>
260
- )}
261
- )}
262
- </FormControl>
263
- </Form.Group>
264
- </>;
265
- }
266
-
267
- export interface BadgeSelectionProps extends BadgeProps {
268
- selected: boolean;
269
- cursor: string;
270
- disabled?: boolean;
271
- }
272
-
273
- export const BadgeSelection = ({ selected = true, disabled, cursor, onClick, style, bg, ...restProps }: BadgeSelectionProps) => (
274
- <Badge
275
- bg={bg || (selected ? 'primary' : 'secondary')}
276
- style={{
277
- userSelect: 'none',
278
- ...cursor ? { cursor } : {},
279
- ...style || {},
280
- }}
281
- {...disabled ? {} : { onClick }}
282
- {...restProps}
283
- />
284
- )
285
-
286
- export type FormBadgesSelectionProps = FormSelectProps;
287
-
288
- export const FormBadgesSelection = ({
289
- list,
290
- idKey = 'id',
291
- value,
292
- onChange,
293
- multiple,
294
- label,
295
- integer,
296
- controlId,
297
- isInvalid,
298
- state,
299
- setState,
300
- disabled,
301
- initialState,
302
- initialValue,
303
- keyName,
304
- pristine,
305
- ...restProps
306
- }: FormBadgesSelectionProps) => {
307
- multiple = multiple || multiple === false ? multiple : value instanceof Array;
308
- const parseInteger = (value: string | number): string | number => integer ? parseInt(`${value}`) : `${value}`;
309
-
310
- return (
311
- <Form.Group controlId={controlId}>
312
- {label && <Form.Label>{label}</Form.Label>}
313
- <div className={`form-control ${isInvalid ? 'is-invalid' : ''}`}>
314
- {list.map((item, key) => {
315
- const selected = multiple
316
- ? !!value && Array.isArray(value) && value.includes(item[idKey])
317
- : value === item[idKey];
318
- return (
319
- <BadgeSelection
320
- key={key}
321
- disabled={
322
- typeof disabled === 'function'
323
- ? disabled({ initialValue, list, value: item[idKey], state, initialState })
324
- : disabled
325
- }
326
- selected={selected}
327
- cursor='pointer'
328
- onClick={() => {
329
- if (Array.isArray(value)) {
330
- // if (selected) {
331
- // onChange(value.filter((i: number | string) => i !== item[id]));
332
- // } else {
333
- // onChange([...value || [], item[id]]);
334
- // }
335
- if(selected) {
336
- onChange(value.filter(id => parseInteger(id) !== parseInteger(item[idKey])));
337
- } else {
338
- // onChange([...value, parseInteger(item[idKey])]);
339
- onChange(
340
- integer
341
- ? [...value.map((v: string | number ) => parseInt(v as string)), parseInt(item[idKey])]
342
- : [...value.map((v: string | number ) => `${v}`), `${item[idKey]}`]
343
- );
344
- }
345
- } else {
346
- onChange(item[idKey])
347
- }
348
- }}
349
- {...restProps}
350
- >
351
- {item.name}
352
- </BadgeSelection>
353
- );
354
- })}
355
- </div>
356
- </Form.Group>
357
- );
358
- }
359
-
360
- export interface FormDropdownProps<T> extends Omit<FormInputProps, 'disabled' | 'onChange' | 'list'> {
361
- onChange: (value: number | string | number[] | string[] | null) => void;
362
- list: T[];
363
- idKey?: keyof T;
364
- nameKey?: keyof T;
365
- disabled?: boolean | ((props: DisabledProps) => boolean);
366
- variant?: Variant;
367
- }
368
-
369
- export const FormDropdown = <T,>({
370
- list: listBase,
371
- idKey = 'id' as keyof T,
372
- nameKey = 'name' as keyof T,
373
- value,
374
- onChange,
375
- label,
376
- controlId,
377
- isInvalid,
378
- state,
379
- setState,
380
- disabled,
381
- initialState,
382
- initialValue,
383
- variant = 'light',
384
- pristine,
385
- keyName,
386
- onEnter,
387
- ...restProps
388
- }: FormDropdownProps<T>) => {
389
- const { strings } = useLocalization();
390
-
391
- const [list, mismatch] = useMemo(() => {
392
- if (!listBase || !Array.isArray(listBase)) {
393
- return [null, null] as [T[] | null, any];
394
- }
395
- const mismatch = listBase?.find((item: any) => (
396
- !['number', 'string'].includes(typeof item[idKey])
397
- || !['number', 'string'].includes(typeof item[nameKey])
398
- ))
399
- if (mismatch) return [null, mismatch];
400
- return [listBase, null];
401
- }, [listBase])
402
-
403
- if (!list) {
404
- console.error(
405
- `FormDropdown Error:
406
- - Each item in 'list' must include a valid 'idKey' (${String(idKey)}) and 'nameKey' (${String(nameKey)}).
407
- - Both keys must exist on every item and be of type 'string' or 'number'.
408
- - One or more items failed this check.
409
-
410
- Received list:`,
411
- listBase,
412
- ...mismatch ? ['First mismatch:', mismatch] : []
413
- );
414
- return null;
415
- }
416
-
417
- const selectedItem = list.find(item => item[idKey] === value);
418
- return (
419
- <Form.Group controlId={controlId}>
420
- {label && <Form.Label>{label}</Form.Label>}
421
-
422
- <div className={`form-control ${isInvalid ? 'is-invalid' : ''}`}>
423
- <Dropdown
424
- onKeyDown={(e: KeyboardEvent<HTMLInputElement>) => {
425
- if (e.key === 'Enter' && typeof onEnter === 'function') {
426
- e.preventDefault();
427
- onEnter();
428
- }
429
- }}
430
- >
431
- <Dropdown.Toggle variant={variant}>
432
- {selectedItem
433
- ? selectedItem[nameKey] as string
434
- : strings.getString('choose_one')
435
- }
436
- </Dropdown.Toggle>
437
- <Dropdown.Menu>
438
- {list.map((item, key) =>
439
- <Dropdown.Item
440
- key={key}
441
- disabled={
442
- typeof disabled === 'function'
443
- ? disabled({ initialValue, list, value: item[idKey] as number | string, state, initialState })
444
- : disabled
445
- }
446
- selected={value === item[idKey]}
447
- cursor='pointer'
448
- onClick={() => onChange(item[idKey] as number | string)}
449
- {...restProps}
450
- >
451
- {item[nameKey] as string}
452
- </Dropdown.Item>
453
- )}
454
- </Dropdown.Menu>
455
- </Dropdown>
456
- </div>
457
- </Form.Group>
458
- );
459
- };
39
+ }