envoc-form 5.0.3 → 5.0.5
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/README.md +158 -15
- package/es/Input/CheckboxGroup.d.ts +6 -0
- package/es/Input/CheckboxGroup.js +14 -0
- package/es/Input/CheckboxInputGroup.d.ts +13 -0
- package/es/Input/CheckboxInputGroup.js +41 -0
- package/es/index.d.ts +2 -0
- package/es/index.js +1 -0
- package/lib/Input/CheckboxGroup.d.ts +6 -0
- package/lib/Input/CheckboxGroup.js +20 -0
- package/lib/Input/CheckboxInputGroup.d.ts +13 -0
- package/lib/Input/CheckboxInputGroup.js +46 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +3 -1
- package/package.json +111 -111
- package/src/AddressInput/AddressInput.test.tsx +27 -27
- package/src/AddressInput/AddressInput.tsx +82 -82
- package/src/AddressInput/UsStates.ts +55 -55
- package/src/AddressInput/__snapshots__/AddressInput.test.tsx.snap +182 -182
- package/src/ConfirmBaseForm/ConfirmBaseForm.test.tsx +24 -24
- package/src/ConfirmBaseForm/ConfirmBaseForm.tsx +74 -74
- package/src/ConfirmBaseForm/__snapshots__/ConfirmBaseForm.test.tsx.snap +23 -23
- package/src/ConfirmDeleteForm/ConfirmDeleteForm.test.tsx +24 -24
- package/src/ConfirmDeleteForm/ConfirmDeleteForm.tsx +87 -87
- package/src/ConfirmDeleteForm/__snapshots__/ConfirmDeleteForm.test.tsx.snap +25 -25
- package/src/DatePicker/DatePicker.test.tsx +48 -48
- package/src/DatePicker/DatePickerGroup.tsx +115 -115
- package/src/DatePicker/DatePickerHelper.ts +4 -4
- package/src/DatePicker/StringDateOnlyPickerGroup.tsx +28 -28
- package/src/DatePicker/StringDatePickerGroup.tsx +20 -20
- package/src/DatePicker/__snapshots__/DatePicker.test.tsx.snap +152 -152
- package/src/Field/CustomFieldInputProps.ts +10 -10
- package/src/Field/CustomFieldMetaProps.ts +5 -5
- package/src/Field/Field.tsx +113 -113
- package/src/Field/FieldErrorScrollTarget.tsx +12 -12
- package/src/Field/FieldNameContext.ts +6 -6
- package/src/Field/FieldSection.tsx +18 -18
- package/src/Field/InjectedFieldProps.ts +8 -8
- package/src/Field/StandAloneInput.tsx +55 -55
- package/src/Field/useStandardField.ts +125 -125
- package/src/FieldArray/FieldArray.tsx +154 -154
- package/src/File/FileGroup.test.tsx +35 -35
- package/src/File/FileGroup.tsx +85 -85
- package/src/File/FileList.tsx +21 -21
- package/src/File/__snapshots__/FileGroup.test.tsx.snap +34 -34
- package/src/File/humanFileSize.ts +8 -8
- package/src/Form/FocusError.tsx +55 -55
- package/src/Form/Form.test.tsx +14 -14
- package/src/Form/Form.tsx +237 -237
- package/src/Form/FormBasedPreventNavigation.tsx +56 -56
- package/src/Form/LegacyFormBasedPreventNavigation.tsx +77 -77
- package/src/Form/NewFormBasedPreventNavigation.tsx +59 -59
- package/src/Form/ServerErrorContext.ts +18 -18
- package/src/Form/__snapshots__/Form.test.tsx.snap +10 -10
- package/src/FormActions.tsx +47 -47
- package/src/FormDefaults.ts +2 -2
- package/src/Group.tsx +62 -62
- package/src/Input/CheckboxGroup.tsx +60 -0
- package/src/Input/CheckboxInputGroup.tsx +78 -0
- package/src/Input/IconInputGroup.tsx +54 -54
- package/src/Input/InputGroup.tsx +72 -72
- package/src/Input/MoneyInputGroup.tsx +50 -50
- package/src/Input/NumberInputGroup.tsx +48 -48
- package/src/Input/PhoneNumberInputGroup.tsx +45 -45
- package/src/Input/StringInputGroup.tsx +53 -53
- package/src/Input/__Tests__/CheckboxInputGroup.test.tsx +26 -0
- package/src/Input/__Tests__/IconInputGroup.test.tsx +35 -35
- package/src/Input/__Tests__/MoneyInputGroup.test.tsx +37 -37
- package/src/Input/__Tests__/NumberInputGroup.test.tsx +35 -35
- package/src/Input/__Tests__/PhoneNumberInputGroup.test.tsx +36 -36
- package/src/Input/__Tests__/StringInputGroup.test.tsx +27 -27
- package/src/Input/__Tests__/__snapshots__/CheckboxInputGroup.test.tsx.snap +33 -0
- package/src/Input/__Tests__/__snapshots__/IconInputGroup.test.tsx.snap +32 -32
- package/src/Input/__Tests__/__snapshots__/MoneyInputGroup.test.tsx.snap +34 -34
- package/src/Input/__Tests__/__snapshots__/NumberInputGroup.test.tsx.snap +32 -32
- package/src/Input/__Tests__/__snapshots__/PhoneNumberInputGroup.test.tsx.snap +33 -33
- package/src/Input/__Tests__/__snapshots__/StringInputGroup.test.tsx.snap +31 -31
- package/src/Normalization/NormalizationFunction.ts +4 -4
- package/src/Normalization/normalizers.ts +44 -44
- package/src/Select/BooleanSelectGroup.tsx +28 -28
- package/src/Select/NumberSelectGroup.tsx +16 -16
- package/src/Select/SelectGroup.tsx +124 -124
- package/src/Select/SelectGroupPropsHelper.ts +4 -4
- package/src/Select/StringSelectGroup.tsx +16 -16
- package/src/Select/__tests__/BooleanSelectGroup.test.tsx +35 -35
- package/src/Select/__tests__/NumberSelectGroup.test.tsx +87 -87
- package/src/Select/__tests__/StringSelectGroup.test.tsx +89 -89
- package/src/Select/__tests__/__snapshots__/BooleanSelectGroup.test.tsx.snap +98 -98
- package/src/Select/__tests__/__snapshots__/NumberSelectGroup.test.tsx.snap +195 -195
- package/src/Select/__tests__/__snapshots__/StringSelectGroup.test.tsx.snap +195 -195
- package/src/StandardFormActions.tsx +41 -41
- package/src/SubmitFormButton.tsx +54 -54
- package/src/TextArea/TextAreaGroup.tsx +64 -64
- package/src/Validation/ValidatedApiResult.ts +8 -8
- package/src/Validation/ValidationError.ts +6 -6
- package/src/Validation/ValidationFunction.ts +4 -4
- package/src/Validation/validators.test.tsx +81 -81
- package/src/Validation/validators.ts +97 -97
- package/src/__Tests__/FormTestBase.tsx +65 -64
- package/src/__Tests__/RealisticForm.test.tsx +82 -82
- package/src/__Tests__/StandardFormActions.test.tsx +17 -17
- package/src/__Tests__/SubmitFormButton.test.tsx +17 -17
- package/src/__Tests__/__snapshots__/StandardFormActions.test.tsx.snap +27 -27
- package/src/__Tests__/__snapshots__/SubmitFormButton.test.tsx.snap +20 -20
- package/src/__Tests__/index.ts +3 -3
- package/src/_variables.scss +11 -11
- package/src/index.ts +156 -153
- package/src/react-app-env.d.ts +1 -1
- package/src/setupTests.ts +1 -1
- package/src/utils/objectContainsNonSerializableProperty.test.tsx +49 -49
- package/src/utils/objectContainsNonSerializableProperty.ts +17 -17
- package/src/utils/objectToFormData.test.tsx +76 -76
- package/src/utils/objectToFormData.ts +105 -105
- package/src/utils/typeChecks.ts +18 -18
@@ -1,87 +1,87 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import { useNavigate, useParams } from 'react-router-dom';
|
3
|
-
import { toast } from 'react-toastify';
|
4
|
-
import { useAxiosRequestProps } from 'envoc-request';
|
5
|
-
import ConfirmBaseForm, {
|
6
|
-
ConfirmBaseFormProps,
|
7
|
-
} from '../ConfirmBaseForm/ConfirmBaseForm';
|
8
|
-
import { FormDefaults } from '../FormDefaults';
|
9
|
-
|
10
|
-
export interface ConfirmDeleteFormProps
|
11
|
-
extends Pick<ConfirmBaseFormProps, 'style'> {
|
12
|
-
/** Url to navigate to on success. */
|
13
|
-
successUrl?: string;
|
14
|
-
/** Form name (key) to apply the confirmation on. */
|
15
|
-
form: string;
|
16
|
-
/** Custom message to display.
|
17
|
-
* @defaultValue `Are you sure you want to delete this?`
|
18
|
-
*/
|
19
|
-
title?: string;
|
20
|
-
/** Custom function when the axios request succeeds. */
|
21
|
-
handleComplete?: Function;
|
22
|
-
/** Custom function when the axios request fails. */
|
23
|
-
handleError?: Function;
|
24
|
-
/** Any components to be rendered in between the title and the buttons. */
|
25
|
-
children?: React.ReactNode;
|
26
|
-
}
|
27
|
-
|
28
|
-
/**
|
29
|
-
* Deletion confirmation. Navigates to a different route to allow the user to either confirm or cancel an action.
|
30
|
-
*
|
31
|
-
* Wraps `<ConfirmBaseForm/>`.
|
32
|
-
*/
|
33
|
-
export default function ConfirmDeleteForm({
|
34
|
-
successUrl,
|
35
|
-
form,
|
36
|
-
title,
|
37
|
-
handleComplete,
|
38
|
-
handleError,
|
39
|
-
children,
|
40
|
-
...props
|
41
|
-
}: ConfirmDeleteFormProps) {
|
42
|
-
const navigate = useNavigate();
|
43
|
-
const { entityId } = useParams();
|
44
|
-
const onComplete =
|
45
|
-
handleComplete ??
|
46
|
-
function () {
|
47
|
-
toast.success('Deleted!');
|
48
|
-
goBack();
|
49
|
-
};
|
50
|
-
const onError =
|
51
|
-
handleError ??
|
52
|
-
function (error: any) {
|
53
|
-
toast.error(
|
54
|
-
error.response?.data?.validationFailures[0]?.errorMessage ??
|
55
|
-
'Something went wrong talking to the portal. Contact support if this continues.'
|
56
|
-
);
|
57
|
-
};
|
58
|
-
|
59
|
-
const request = {
|
60
|
-
method: 'delete',
|
61
|
-
url: `/api/${form}/${entityId}`,
|
62
|
-
onComplete: onComplete,
|
63
|
-
onError: onError,
|
64
|
-
} as useAxiosRequestProps;
|
65
|
-
|
66
|
-
return (
|
67
|
-
<ConfirmBaseForm
|
68
|
-
request={request}
|
69
|
-
handleCancel={goBack}
|
70
|
-
title={title ?? 'Are you sure you want to delete this?'}
|
71
|
-
confirmButtonText="Yes"
|
72
|
-
confirmButtonClass={
|
73
|
-
FormDefaults.cssClassPrefix + 'confirm-delete-form-yes-button'
|
74
|
-
}
|
75
|
-
{...props}>
|
76
|
-
{children}
|
77
|
-
</ConfirmBaseForm>
|
78
|
-
);
|
79
|
-
|
80
|
-
function goBack() {
|
81
|
-
if (successUrl) {
|
82
|
-
navigate(successUrl);
|
83
|
-
} else {
|
84
|
-
navigate(-1);
|
85
|
-
}
|
86
|
-
}
|
87
|
-
}
|
1
|
+
import React from 'react';
|
2
|
+
import { useNavigate, useParams } from 'react-router-dom';
|
3
|
+
import { toast } from 'react-toastify';
|
4
|
+
import { useAxiosRequestProps } from 'envoc-request';
|
5
|
+
import ConfirmBaseForm, {
|
6
|
+
ConfirmBaseFormProps,
|
7
|
+
} from '../ConfirmBaseForm/ConfirmBaseForm';
|
8
|
+
import { FormDefaults } from '../FormDefaults';
|
9
|
+
|
10
|
+
export interface ConfirmDeleteFormProps
|
11
|
+
extends Pick<ConfirmBaseFormProps, 'style'> {
|
12
|
+
/** Url to navigate to on success. */
|
13
|
+
successUrl?: string;
|
14
|
+
/** Form name (key) to apply the confirmation on. */
|
15
|
+
form: string;
|
16
|
+
/** Custom message to display.
|
17
|
+
* @defaultValue `Are you sure you want to delete this?`
|
18
|
+
*/
|
19
|
+
title?: string;
|
20
|
+
/** Custom function when the axios request succeeds. */
|
21
|
+
handleComplete?: Function;
|
22
|
+
/** Custom function when the axios request fails. */
|
23
|
+
handleError?: Function;
|
24
|
+
/** Any components to be rendered in between the title and the buttons. */
|
25
|
+
children?: React.ReactNode;
|
26
|
+
}
|
27
|
+
|
28
|
+
/**
|
29
|
+
* Deletion confirmation. Navigates to a different route to allow the user to either confirm or cancel an action.
|
30
|
+
*
|
31
|
+
* Wraps `<ConfirmBaseForm/>`.
|
32
|
+
*/
|
33
|
+
export default function ConfirmDeleteForm({
|
34
|
+
successUrl,
|
35
|
+
form,
|
36
|
+
title,
|
37
|
+
handleComplete,
|
38
|
+
handleError,
|
39
|
+
children,
|
40
|
+
...props
|
41
|
+
}: ConfirmDeleteFormProps) {
|
42
|
+
const navigate = useNavigate();
|
43
|
+
const { entityId } = useParams();
|
44
|
+
const onComplete =
|
45
|
+
handleComplete ??
|
46
|
+
function () {
|
47
|
+
toast.success('Deleted!');
|
48
|
+
goBack();
|
49
|
+
};
|
50
|
+
const onError =
|
51
|
+
handleError ??
|
52
|
+
function (error: any) {
|
53
|
+
toast.error(
|
54
|
+
error.response?.data?.validationFailures[0]?.errorMessage ??
|
55
|
+
'Something went wrong talking to the portal. Contact support if this continues.'
|
56
|
+
);
|
57
|
+
};
|
58
|
+
|
59
|
+
const request = {
|
60
|
+
method: 'delete',
|
61
|
+
url: `/api/${form}/${entityId}`,
|
62
|
+
onComplete: onComplete,
|
63
|
+
onError: onError,
|
64
|
+
} as useAxiosRequestProps;
|
65
|
+
|
66
|
+
return (
|
67
|
+
<ConfirmBaseForm
|
68
|
+
request={request}
|
69
|
+
handleCancel={goBack}
|
70
|
+
title={title ?? 'Are you sure you want to delete this?'}
|
71
|
+
confirmButtonText="Yes"
|
72
|
+
confirmButtonClass={
|
73
|
+
FormDefaults.cssClassPrefix + 'confirm-delete-form-yes-button'
|
74
|
+
}
|
75
|
+
{...props}>
|
76
|
+
{children}
|
77
|
+
</ConfirmBaseForm>
|
78
|
+
);
|
79
|
+
|
80
|
+
function goBack() {
|
81
|
+
if (successUrl) {
|
82
|
+
navigate(successUrl);
|
83
|
+
} else {
|
84
|
+
navigate(-1);
|
85
|
+
}
|
86
|
+
}
|
87
|
+
}
|
@@ -1,25 +1,25 @@
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
-
|
3
|
-
exports[`ConfirmDeleteForm has matching snapshot 1`] = `
|
4
|
-
<DocumentFragment>
|
5
|
-
<div
|
6
|
-
style="text-align: center;"
|
7
|
-
>
|
8
|
-
<h3>
|
9
|
-
Are you sure you want to delete this?
|
10
|
-
</h3>
|
11
|
-
<button
|
12
|
-
class="envoc-form-confirm-delete-form-yes-button"
|
13
|
-
type="button"
|
14
|
-
>
|
15
|
-
Yes
|
16
|
-
</button>
|
17
|
-
<button
|
18
|
-
class="envoc-form-confirm-base-form-cancel-button"
|
19
|
-
type="button"
|
20
|
-
>
|
21
|
-
Cancel
|
22
|
-
</button>
|
23
|
-
</div>
|
24
|
-
</DocumentFragment>
|
25
|
-
`;
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
+
|
3
|
+
exports[`ConfirmDeleteForm has matching snapshot 1`] = `
|
4
|
+
<DocumentFragment>
|
5
|
+
<div
|
6
|
+
style="text-align: center;"
|
7
|
+
>
|
8
|
+
<h3>
|
9
|
+
Are you sure you want to delete this?
|
10
|
+
</h3>
|
11
|
+
<button
|
12
|
+
class="envoc-form-confirm-delete-form-yes-button"
|
13
|
+
type="button"
|
14
|
+
>
|
15
|
+
Yes
|
16
|
+
</button>
|
17
|
+
<button
|
18
|
+
class="envoc-form-confirm-base-form-cancel-button"
|
19
|
+
type="button"
|
20
|
+
>
|
21
|
+
Cancel
|
22
|
+
</button>
|
23
|
+
</div>
|
24
|
+
</DocumentFragment>
|
25
|
+
`;
|
@@ -1,48 +1,48 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import { render } from '@testing-library/react';
|
3
|
-
import StringDatePickerGroup from './StringDatePickerGroup';
|
4
|
-
import FormTestBase from '../__Tests__/FormTestBase';
|
5
|
-
|
6
|
-
//hack so the datepicker internals don't complain about this not existing in the context of jest
|
7
|
-
HTMLCanvasElement.prototype.getContext = () => null;
|
8
|
-
|
9
|
-
describe('IconInputGroup', () => {
|
10
|
-
it('renders without crashing', () => {
|
11
|
-
render(
|
12
|
-
<FormTestBase>
|
13
|
-
{({ Field }) => (
|
14
|
-
<Field
|
15
|
-
name="favoriteDate"
|
16
|
-
Component={StringDatePickerGroup}
|
17
|
-
label="Favorite Date"
|
18
|
-
monthPlaceholder="mm"
|
19
|
-
dayPlaceholder="dd"
|
20
|
-
yearPlaceholder="yyyy"
|
21
|
-
maxDate={new Date('9/23/2023')}
|
22
|
-
minDate={new Date('6/22/2022')}
|
23
|
-
/>
|
24
|
-
)}
|
25
|
-
</FormTestBase>
|
26
|
-
);
|
27
|
-
});
|
28
|
-
|
29
|
-
it('has matching snapshot', () => {
|
30
|
-
const renderResult = render(
|
31
|
-
<FormTestBase>
|
32
|
-
{({ Field }) => (
|
33
|
-
<Field
|
34
|
-
name="favoriteDate"
|
35
|
-
Component={StringDatePickerGroup}
|
36
|
-
label="Favorite Date"
|
37
|
-
monthPlaceholder="mm"
|
38
|
-
dayPlaceholder="dd"
|
39
|
-
yearPlaceholder="yyyy"
|
40
|
-
maxDate={new Date('9/23/2023')}
|
41
|
-
minDate={new Date('6/22/2022')}
|
42
|
-
/>
|
43
|
-
)}
|
44
|
-
</FormTestBase>
|
45
|
-
);
|
46
|
-
expect(renderResult.asFragment()).toMatchSnapshot();
|
47
|
-
});
|
48
|
-
});
|
1
|
+
import React from 'react';
|
2
|
+
import { render } from '@testing-library/react';
|
3
|
+
import StringDatePickerGroup from './StringDatePickerGroup';
|
4
|
+
import FormTestBase from '../__Tests__/FormTestBase';
|
5
|
+
|
6
|
+
//hack so the datepicker internals don't complain about this not existing in the context of jest
|
7
|
+
HTMLCanvasElement.prototype.getContext = () => null;
|
8
|
+
|
9
|
+
describe('IconInputGroup', () => {
|
10
|
+
it('renders without crashing', () => {
|
11
|
+
render(
|
12
|
+
<FormTestBase>
|
13
|
+
{({ Field }) => (
|
14
|
+
<Field
|
15
|
+
name="favoriteDate"
|
16
|
+
Component={StringDatePickerGroup}
|
17
|
+
label="Favorite Date"
|
18
|
+
monthPlaceholder="mm"
|
19
|
+
dayPlaceholder="dd"
|
20
|
+
yearPlaceholder="yyyy"
|
21
|
+
maxDate={new Date('9/23/2023')}
|
22
|
+
minDate={new Date('6/22/2022')}
|
23
|
+
/>
|
24
|
+
)}
|
25
|
+
</FormTestBase>
|
26
|
+
);
|
27
|
+
});
|
28
|
+
|
29
|
+
it('has matching snapshot', () => {
|
30
|
+
const renderResult = render(
|
31
|
+
<FormTestBase>
|
32
|
+
{({ Field }) => (
|
33
|
+
<Field
|
34
|
+
name="favoriteDate"
|
35
|
+
Component={StringDatePickerGroup}
|
36
|
+
label="Favorite Date"
|
37
|
+
monthPlaceholder="mm"
|
38
|
+
dayPlaceholder="dd"
|
39
|
+
yearPlaceholder="yyyy"
|
40
|
+
maxDate={new Date('9/23/2023')}
|
41
|
+
minDate={new Date('6/22/2022')}
|
42
|
+
/>
|
43
|
+
)}
|
44
|
+
</FormTestBase>
|
45
|
+
);
|
46
|
+
expect(renderResult.asFragment()).toMatchSnapshot();
|
47
|
+
});
|
48
|
+
});
|
@@ -1,115 +1,115 @@
|
|
1
|
-
import { useEffect, useState } from 'react';
|
2
|
-
import DatePicker, {
|
3
|
-
DatePickerProps,
|
4
|
-
} from 'react-date-picker/dist/entry.nostyle';
|
5
|
-
import classnames from 'classnames';
|
6
|
-
import parseISO from 'date-fns/parseISO';
|
7
|
-
import { InjectedFieldProps } from '../Field/InjectedFieldProps';
|
8
|
-
import { FormDefaults } from '../FormDefaults';
|
9
|
-
import Group, { GroupProps } from '../Group';
|
10
|
-
|
11
|
-
export interface DatePickerGroupProps<T>
|
12
|
-
extends InjectedFieldProps<T | undefined | null>,
|
13
|
-
Omit<
|
14
|
-
DatePickerProps,
|
15
|
-
keyof InjectedFieldProps<T> | 'name' | 'value' | 'className'
|
16
|
-
>,
|
17
|
-
Omit<GroupProps, keyof InjectedFieldProps<T> | 'children'> {
|
18
|
-
convert: (arg: Date) => T;
|
19
|
-
}
|
20
|
-
|
21
|
-
/**
|
22
|
-
* Field for inputting dates. Uses `<Group/>` and `<DatePicker/>`.
|
23
|
-
*
|
24
|
-
* Uses [react-date-picker](https://www.npmjs.com/package/react-date-picker)
|
25
|
-
*/
|
26
|
-
export default function DatePickerGroup<T>({
|
27
|
-
input,
|
28
|
-
meta,
|
29
|
-
label,
|
30
|
-
helpText,
|
31
|
-
className,
|
32
|
-
required,
|
33
|
-
disabled,
|
34
|
-
convert,
|
35
|
-
...rest
|
36
|
-
}: DatePickerGroupProps<T>) {
|
37
|
-
const [displayDate, setDisplayDate] = useState<Date | null>(null);
|
38
|
-
|
39
|
-
useEffect(() => {
|
40
|
-
if (!input.value) {
|
41
|
-
setDisplayDate(null);
|
42
|
-
} else if (typeof input.value === 'string') {
|
43
|
-
if (input.value.indexOf('T') === -1) {
|
44
|
-
setDisplayDate(new Date(`${input.value}T00:00:00.000`));
|
45
|
-
} else {
|
46
|
-
setDisplayDate(new Date(input.value));
|
47
|
-
}
|
48
|
-
}
|
49
|
-
}, [setDisplayDate, input.value]);
|
50
|
-
|
51
|
-
return (
|
52
|
-
<Group
|
53
|
-
input={input}
|
54
|
-
meta={meta}
|
55
|
-
label={label}
|
56
|
-
helpText={helpText}
|
57
|
-
className={classnames(
|
58
|
-
className,
|
59
|
-
FormDefaults.cssClassPrefix + 'date-picker'
|
60
|
-
)}
|
61
|
-
required={required}
|
62
|
-
disabled={disabled}>
|
63
|
-
<DatePicker
|
64
|
-
{...rest}
|
65
|
-
className={classnames(
|
66
|
-
FormDefaults.cssClassPrefix + 'date-picker',
|
67
|
-
className
|
68
|
-
)}
|
69
|
-
value={displayDate}
|
70
|
-
onChange={handleChange}
|
71
|
-
/>
|
72
|
-
</Group>
|
73
|
-
);
|
74
|
-
|
75
|
-
function handleChange(e: Date | Date[] | string | undefined) {
|
76
|
-
const { onChange } = input;
|
77
|
-
if (onChange === null) {
|
78
|
-
return;
|
79
|
-
}
|
80
|
-
|
81
|
-
if (!e) {
|
82
|
-
onChange(undefined);
|
83
|
-
setDisplayDate(null);
|
84
|
-
return;
|
85
|
-
} else if (typeof e === 'string') {
|
86
|
-
const parsedValue = parseISO(e.toString().split('T')[0]);
|
87
|
-
setDisplayDate(parsedValue);
|
88
|
-
onChange(convert(parsedValue));
|
89
|
-
} else if (e instanceof Date) {
|
90
|
-
const parsedValue = parseISO(
|
91
|
-
convertToTimeZoneInsensitiveISOString(e).split('T')[0]
|
92
|
-
);
|
93
|
-
setDisplayDate(parsedValue);
|
94
|
-
onChange(convert(parsedValue));
|
95
|
-
} else {
|
96
|
-
// e is instanceof Date[]
|
97
|
-
const parsedValue = parseISO(
|
98
|
-
convertToTimeZoneInsensitiveISOString(e[0]).split('T')[0]
|
99
|
-
);
|
100
|
-
setDisplayDate(parsedValue);
|
101
|
-
onChange(convert(parsedValue));
|
102
|
-
}
|
103
|
-
}
|
104
|
-
}
|
105
|
-
|
106
|
-
export function convertToTimeZoneInsensitiveISOString(date: Date): string {
|
107
|
-
const year = new Intl.DateTimeFormat('en', { year: 'numeric' })
|
108
|
-
.format(date)
|
109
|
-
.padStart(4, '0');
|
110
|
-
const month = new Intl.DateTimeFormat('en', { month: '2-digit' }).format(
|
111
|
-
date
|
112
|
-
);
|
113
|
-
const day = new Intl.DateTimeFormat('en', { day: '2-digit' }).format(date);
|
114
|
-
return `${year}-${month}-${day}T00:00:00.000Z`;
|
115
|
-
}
|
1
|
+
import { useEffect, useState } from 'react';
|
2
|
+
import DatePicker, {
|
3
|
+
DatePickerProps,
|
4
|
+
} from 'react-date-picker/dist/entry.nostyle';
|
5
|
+
import classnames from 'classnames';
|
6
|
+
import parseISO from 'date-fns/parseISO';
|
7
|
+
import { InjectedFieldProps } from '../Field/InjectedFieldProps';
|
8
|
+
import { FormDefaults } from '../FormDefaults';
|
9
|
+
import Group, { GroupProps } from '../Group';
|
10
|
+
|
11
|
+
export interface DatePickerGroupProps<T>
|
12
|
+
extends InjectedFieldProps<T | undefined | null>,
|
13
|
+
Omit<
|
14
|
+
DatePickerProps,
|
15
|
+
keyof InjectedFieldProps<T> | 'name' | 'value' | 'className'
|
16
|
+
>,
|
17
|
+
Omit<GroupProps, keyof InjectedFieldProps<T> | 'children'> {
|
18
|
+
convert: (arg: Date) => T;
|
19
|
+
}
|
20
|
+
|
21
|
+
/**
|
22
|
+
* Field for inputting dates. Uses `<Group/>` and `<DatePicker/>`.
|
23
|
+
*
|
24
|
+
* Uses [react-date-picker](https://www.npmjs.com/package/react-date-picker)
|
25
|
+
*/
|
26
|
+
export default function DatePickerGroup<T>({
|
27
|
+
input,
|
28
|
+
meta,
|
29
|
+
label,
|
30
|
+
helpText,
|
31
|
+
className,
|
32
|
+
required,
|
33
|
+
disabled,
|
34
|
+
convert,
|
35
|
+
...rest
|
36
|
+
}: DatePickerGroupProps<T>) {
|
37
|
+
const [displayDate, setDisplayDate] = useState<Date | null>(null);
|
38
|
+
|
39
|
+
useEffect(() => {
|
40
|
+
if (!input.value) {
|
41
|
+
setDisplayDate(null);
|
42
|
+
} else if (typeof input.value === 'string') {
|
43
|
+
if (input.value.indexOf('T') === -1) {
|
44
|
+
setDisplayDate(new Date(`${input.value}T00:00:00.000`));
|
45
|
+
} else {
|
46
|
+
setDisplayDate(new Date(input.value));
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}, [setDisplayDate, input.value]);
|
50
|
+
|
51
|
+
return (
|
52
|
+
<Group
|
53
|
+
input={input}
|
54
|
+
meta={meta}
|
55
|
+
label={label}
|
56
|
+
helpText={helpText}
|
57
|
+
className={classnames(
|
58
|
+
className,
|
59
|
+
FormDefaults.cssClassPrefix + 'date-picker'
|
60
|
+
)}
|
61
|
+
required={required}
|
62
|
+
disabled={disabled}>
|
63
|
+
<DatePicker
|
64
|
+
{...rest}
|
65
|
+
className={classnames(
|
66
|
+
FormDefaults.cssClassPrefix + 'date-picker',
|
67
|
+
className
|
68
|
+
)}
|
69
|
+
value={displayDate}
|
70
|
+
onChange={handleChange}
|
71
|
+
/>
|
72
|
+
</Group>
|
73
|
+
);
|
74
|
+
|
75
|
+
function handleChange(e: Date | Date[] | string | undefined) {
|
76
|
+
const { onChange } = input;
|
77
|
+
if (onChange === null) {
|
78
|
+
return;
|
79
|
+
}
|
80
|
+
|
81
|
+
if (!e) {
|
82
|
+
onChange(undefined);
|
83
|
+
setDisplayDate(null);
|
84
|
+
return;
|
85
|
+
} else if (typeof e === 'string') {
|
86
|
+
const parsedValue = parseISO(e.toString().split('T')[0]);
|
87
|
+
setDisplayDate(parsedValue);
|
88
|
+
onChange(convert(parsedValue));
|
89
|
+
} else if (e instanceof Date) {
|
90
|
+
const parsedValue = parseISO(
|
91
|
+
convertToTimeZoneInsensitiveISOString(e).split('T')[0]
|
92
|
+
);
|
93
|
+
setDisplayDate(parsedValue);
|
94
|
+
onChange(convert(parsedValue));
|
95
|
+
} else {
|
96
|
+
// e is instanceof Date[]
|
97
|
+
const parsedValue = parseISO(
|
98
|
+
convertToTimeZoneInsensitiveISOString(e[0]).split('T')[0]
|
99
|
+
);
|
100
|
+
setDisplayDate(parsedValue);
|
101
|
+
onChange(convert(parsedValue));
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
export function convertToTimeZoneInsensitiveISOString(date: Date): string {
|
107
|
+
const year = new Intl.DateTimeFormat('en', { year: 'numeric' })
|
108
|
+
.format(date)
|
109
|
+
.padStart(4, '0');
|
110
|
+
const month = new Intl.DateTimeFormat('en', { month: '2-digit' }).format(
|
111
|
+
date
|
112
|
+
);
|
113
|
+
const day = new Intl.DateTimeFormat('en', { day: '2-digit' }).format(date);
|
114
|
+
return `${year}-${month}-${day}T00:00:00.000Z`;
|
115
|
+
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { DatePickerGroupProps } from './DatePickerGroup';
|
2
|
-
|
3
|
-
export interface DatePickerHelper<T>
|
4
|
-
extends Omit<DatePickerGroupProps<T>, 'convert'> {}
|
1
|
+
import { DatePickerGroupProps } from './DatePickerGroup';
|
2
|
+
|
3
|
+
export interface DatePickerHelper<T>
|
4
|
+
extends Omit<DatePickerGroupProps<T>, 'convert'> {}
|
@@ -1,28 +1,28 @@
|
|
1
|
-
import DatePickerGroup from './DatePickerGroup';
|
2
|
-
import { DatePickerHelper } from './DatePickerHelper';
|
3
|
-
|
4
|
-
export interface StringDateOnlyPickerGroupProps
|
5
|
-
extends DatePickerHelper<string | undefined | null> {}
|
6
|
-
|
7
|
-
/**
|
8
|
-
* Date picker input that consumes and outputs as a date only string in ISO format `YYYY-MM-DD`.
|
9
|
-
*
|
10
|
-
* Default display to the user is in `MM/DD/YYYY` format.
|
11
|
-
*/
|
12
|
-
export default function StringDatePickerGroup(
|
13
|
-
props: StringDateOnlyPickerGroupProps
|
14
|
-
) {
|
15
|
-
return <DatePickerGroup {...props} convert={convertToDateOnly} />;
|
16
|
-
}
|
17
|
-
|
18
|
-
function convertToDateOnly(arg: Date) {
|
19
|
-
const year = new Intl.DateTimeFormat('en', { year: 'numeric' })
|
20
|
-
.format(arg)
|
21
|
-
.padStart(4, '0');
|
22
|
-
|
23
|
-
const month = new Intl.DateTimeFormat('en', { month: '2-digit' }).format(arg);
|
24
|
-
|
25
|
-
const day = new Intl.DateTimeFormat('en', { day: '2-digit' }).format(arg);
|
26
|
-
|
27
|
-
return `${year}-${month}-${day}`;
|
28
|
-
}
|
1
|
+
import DatePickerGroup from './DatePickerGroup';
|
2
|
+
import { DatePickerHelper } from './DatePickerHelper';
|
3
|
+
|
4
|
+
export interface StringDateOnlyPickerGroupProps
|
5
|
+
extends DatePickerHelper<string | undefined | null> {}
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Date picker input that consumes and outputs as a date only string in ISO format `YYYY-MM-DD`.
|
9
|
+
*
|
10
|
+
* Default display to the user is in `MM/DD/YYYY` format.
|
11
|
+
*/
|
12
|
+
export default function StringDatePickerGroup(
|
13
|
+
props: StringDateOnlyPickerGroupProps
|
14
|
+
) {
|
15
|
+
return <DatePickerGroup {...props} convert={convertToDateOnly} />;
|
16
|
+
}
|
17
|
+
|
18
|
+
function convertToDateOnly(arg: Date) {
|
19
|
+
const year = new Intl.DateTimeFormat('en', { year: 'numeric' })
|
20
|
+
.format(arg)
|
21
|
+
.padStart(4, '0');
|
22
|
+
|
23
|
+
const month = new Intl.DateTimeFormat('en', { month: '2-digit' }).format(arg);
|
24
|
+
|
25
|
+
const day = new Intl.DateTimeFormat('en', { day: '2-digit' }).format(arg);
|
26
|
+
|
27
|
+
return `${year}-${month}-${day}`;
|
28
|
+
}
|
@@ -1,20 +1,20 @@
|
|
1
|
-
import DatePickerGroup from './DatePickerGroup';
|
2
|
-
import { DatePickerHelper } from './DatePickerHelper';
|
3
|
-
|
4
|
-
export interface StringDatePickerGroupProps
|
5
|
-
extends DatePickerHelper<string | undefined | null> {}
|
6
|
-
|
7
|
-
/**
|
8
|
-
* Date picker input that consumes and outputs as a date only string in ISO format `YYYY-MM-DDTHH:mm:ss.sssZ` or `±YYYYYY-MM-DDTHH:mm:ss.sssZ`
|
9
|
-
*
|
10
|
-
* If you need `YYYY-MM-DD` format use `<StringDateOnlyPickerGroup/>`.
|
11
|
-
*/
|
12
|
-
export default function StringDatePickerGroup(
|
13
|
-
props: StringDatePickerGroupProps
|
14
|
-
) {
|
15
|
-
return <DatePickerGroup {...props} convert={convertToDateOnly} />;
|
16
|
-
}
|
17
|
-
|
18
|
-
function convertToDateOnly(arg: Date) {
|
19
|
-
return arg.toISOString();
|
20
|
-
}
|
1
|
+
import DatePickerGroup from './DatePickerGroup';
|
2
|
+
import { DatePickerHelper } from './DatePickerHelper';
|
3
|
+
|
4
|
+
export interface StringDatePickerGroupProps
|
5
|
+
extends DatePickerHelper<string | undefined | null> {}
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Date picker input that consumes and outputs as a date only string in ISO format `YYYY-MM-DDTHH:mm:ss.sssZ` or `±YYYYYY-MM-DDTHH:mm:ss.sssZ`
|
9
|
+
*
|
10
|
+
* If you need `YYYY-MM-DD` format use `<StringDateOnlyPickerGroup/>`.
|
11
|
+
*/
|
12
|
+
export default function StringDatePickerGroup(
|
13
|
+
props: StringDatePickerGroupProps
|
14
|
+
) {
|
15
|
+
return <DatePickerGroup {...props} convert={convertToDateOnly} />;
|
16
|
+
}
|
17
|
+
|
18
|
+
function convertToDateOnly(arg: Date) {
|
19
|
+
return arg.toISOString();
|
20
|
+
}
|