envoc-form 5.0.2 → 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,32 +1,32 @@
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
-
|
3
|
-
exports[`NumberInputGroup has matching snapshot 1`] = `
|
4
|
-
<DocumentFragment>
|
5
|
-
<form
|
6
|
-
action="#"
|
7
|
-
class="envoc-form-form"
|
8
|
-
>
|
9
|
-
<div
|
10
|
-
class="envoc-form-number-group envoc-form-input-group envoc-form-group"
|
11
|
-
>
|
12
|
-
<div
|
13
|
-
id="numberofarms-error-scroll-target"
|
14
|
-
style="display: none;"
|
15
|
-
/>
|
16
|
-
<label
|
17
|
-
for="numberOfArms"
|
18
|
-
>
|
19
|
-
Arm Count
|
20
|
-
</label>
|
21
|
-
<input
|
22
|
-
aria-invalid="false"
|
23
|
-
class="envoc-form-number-group envoc-form-input-group"
|
24
|
-
id="numberOfArms"
|
25
|
-
name="numberOfArms"
|
26
|
-
type="number"
|
27
|
-
value=""
|
28
|
-
/>
|
29
|
-
</div>
|
30
|
-
</form>
|
31
|
-
</DocumentFragment>
|
32
|
-
`;
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
+
|
3
|
+
exports[`NumberInputGroup has matching snapshot 1`] = `
|
4
|
+
<DocumentFragment>
|
5
|
+
<form
|
6
|
+
action="#"
|
7
|
+
class="envoc-form-form"
|
8
|
+
>
|
9
|
+
<div
|
10
|
+
class="envoc-form-number-group envoc-form-input-group envoc-form-group"
|
11
|
+
>
|
12
|
+
<div
|
13
|
+
id="numberofarms-error-scroll-target"
|
14
|
+
style="display: none;"
|
15
|
+
/>
|
16
|
+
<label
|
17
|
+
for="numberOfArms"
|
18
|
+
>
|
19
|
+
Arm Count
|
20
|
+
</label>
|
21
|
+
<input
|
22
|
+
aria-invalid="false"
|
23
|
+
class="envoc-form-number-group envoc-form-input-group"
|
24
|
+
id="numberOfArms"
|
25
|
+
name="numberOfArms"
|
26
|
+
type="number"
|
27
|
+
value=""
|
28
|
+
/>
|
29
|
+
</div>
|
30
|
+
</form>
|
31
|
+
</DocumentFragment>
|
32
|
+
`;
|
@@ -1,33 +1,33 @@
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
-
|
3
|
-
exports[`PhoneNumberInputGroup has matching snapshot 1`] = `
|
4
|
-
<DocumentFragment>
|
5
|
-
<form
|
6
|
-
action="#"
|
7
|
-
class="envoc-form-form"
|
8
|
-
>
|
9
|
-
<div
|
10
|
-
class="something-here envoc-form-phone-number-group envoc-form-input-group envoc-form-group"
|
11
|
-
>
|
12
|
-
<div
|
13
|
-
id="phonenumber-error-scroll-target"
|
14
|
-
style="display: none;"
|
15
|
-
/>
|
16
|
-
<label
|
17
|
-
for="phoneNumber"
|
18
|
-
>
|
19
|
-
Phone Number
|
20
|
-
</label>
|
21
|
-
<input
|
22
|
-
aria-invalid="false"
|
23
|
-
autocomplete="tel-national"
|
24
|
-
class="something-here envoc-form-phone-number-group envoc-form-input-group"
|
25
|
-
id="phoneNumber"
|
26
|
-
name="phoneNumber"
|
27
|
-
type="text"
|
28
|
-
value=""
|
29
|
-
/>
|
30
|
-
</div>
|
31
|
-
</form>
|
32
|
-
</DocumentFragment>
|
33
|
-
`;
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
+
|
3
|
+
exports[`PhoneNumberInputGroup has matching snapshot 1`] = `
|
4
|
+
<DocumentFragment>
|
5
|
+
<form
|
6
|
+
action="#"
|
7
|
+
class="envoc-form-form"
|
8
|
+
>
|
9
|
+
<div
|
10
|
+
class="something-here envoc-form-phone-number-group envoc-form-input-group envoc-form-group"
|
11
|
+
>
|
12
|
+
<div
|
13
|
+
id="phonenumber-error-scroll-target"
|
14
|
+
style="display: none;"
|
15
|
+
/>
|
16
|
+
<label
|
17
|
+
for="phoneNumber"
|
18
|
+
>
|
19
|
+
Phone Number
|
20
|
+
</label>
|
21
|
+
<input
|
22
|
+
aria-invalid="false"
|
23
|
+
autocomplete="tel-national"
|
24
|
+
class="something-here envoc-form-phone-number-group envoc-form-input-group"
|
25
|
+
id="phoneNumber"
|
26
|
+
name="phoneNumber"
|
27
|
+
type="text"
|
28
|
+
value=""
|
29
|
+
/>
|
30
|
+
</div>
|
31
|
+
</form>
|
32
|
+
</DocumentFragment>
|
33
|
+
`;
|
@@ -1,31 +1,31 @@
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
-
|
3
|
-
exports[`StringInputGroup has matching snapshot 1`] = `
|
4
|
-
<DocumentFragment>
|
5
|
-
<form
|
6
|
-
action="#"
|
7
|
-
class="envoc-form-form"
|
8
|
-
>
|
9
|
-
<div
|
10
|
-
class="envoc-form-string-group envoc-form-input-group envoc-form-group"
|
11
|
-
>
|
12
|
-
<div
|
13
|
-
id="name-error-scroll-target"
|
14
|
-
style="display: none;"
|
15
|
-
/>
|
16
|
-
<label
|
17
|
-
for="name"
|
18
|
-
>
|
19
|
-
Name
|
20
|
-
</label>
|
21
|
-
<input
|
22
|
-
aria-invalid="false"
|
23
|
-
class="envoc-form-string-group envoc-form-input-group"
|
24
|
-
id="name"
|
25
|
-
name="name"
|
26
|
-
value=""
|
27
|
-
/>
|
28
|
-
</div>
|
29
|
-
</form>
|
30
|
-
</DocumentFragment>
|
31
|
-
`;
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
+
|
3
|
+
exports[`StringInputGroup has matching snapshot 1`] = `
|
4
|
+
<DocumentFragment>
|
5
|
+
<form
|
6
|
+
action="#"
|
7
|
+
class="envoc-form-form"
|
8
|
+
>
|
9
|
+
<div
|
10
|
+
class="envoc-form-string-group envoc-form-input-group envoc-form-group"
|
11
|
+
>
|
12
|
+
<div
|
13
|
+
id="name-error-scroll-target"
|
14
|
+
style="display: none;"
|
15
|
+
/>
|
16
|
+
<label
|
17
|
+
for="name"
|
18
|
+
>
|
19
|
+
Name
|
20
|
+
</label>
|
21
|
+
<input
|
22
|
+
aria-invalid="false"
|
23
|
+
class="envoc-form-string-group envoc-form-input-group"
|
24
|
+
id="name"
|
25
|
+
name="name"
|
26
|
+
value=""
|
27
|
+
/>
|
28
|
+
</div>
|
29
|
+
</form>
|
30
|
+
</DocumentFragment>
|
31
|
+
`;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
/** Normalization of values */
|
2
|
-
export interface NormalizationFunction<TValue> {
|
3
|
-
(value: TValue): TValue;
|
4
|
-
}
|
1
|
+
/** Normalization of values */
|
2
|
+
export interface NormalizationFunction<TValue> {
|
3
|
+
(value: TValue): TValue;
|
4
|
+
}
|
@@ -1,44 +1,44 @@
|
|
1
|
-
/** Normalizer for converting a string into a valid phone number. */
|
2
|
-
export const phoneNumber = (value: string | undefined | null) => {
|
3
|
-
if (!value) {
|
4
|
-
return undefined;
|
5
|
-
}
|
6
|
-
|
7
|
-
const onlyNums = value.replace(/[^\d]/g, '');
|
8
|
-
if (onlyNums.length <= 3) {
|
9
|
-
return onlyNums;
|
10
|
-
}
|
11
|
-
if (onlyNums.length <= 7) {
|
12
|
-
return `${onlyNums.slice(0, 3)}-${onlyNums.slice(3)}`;
|
13
|
-
}
|
14
|
-
if (onlyNums.length <= 10) {
|
15
|
-
return `${onlyNums.slice(0, 3)}-${onlyNums.slice(3, 6)}-${onlyNums.slice(
|
16
|
-
6,
|
17
|
-
10
|
18
|
-
)}`;
|
19
|
-
}
|
20
|
-
if (onlyNums.length <= 13) {
|
21
|
-
const countryCodeLength = onlyNums.length - 10;
|
22
|
-
return `+${onlyNums.slice(0, countryCodeLength)} ${onlyNums.slice(
|
23
|
-
countryCodeLength,
|
24
|
-
3 + countryCodeLength
|
25
|
-
)}-${onlyNums.slice(
|
26
|
-
3 + countryCodeLength,
|
27
|
-
6 + countryCodeLength
|
28
|
-
)}-${onlyNums.slice(6 + countryCodeLength, onlyNums.length)}`;
|
29
|
-
}
|
30
|
-
|
31
|
-
return onlyNums;
|
32
|
-
};
|
33
|
-
|
34
|
-
/** Normalizer for converting a string into a valid zip code. Allows for XXXXX and XXXXX-XXXX format. */
|
35
|
-
export const zipCode = (value: string | undefined | null) => {
|
36
|
-
if (!value) {
|
37
|
-
return undefined;
|
38
|
-
}
|
39
|
-
const onlyNums = value.replace(/[^\d]/g, '');
|
40
|
-
if (onlyNums.length > 5) {
|
41
|
-
return onlyNums.substring(0, 5) + '-' + onlyNums.substring(5, 9);
|
42
|
-
}
|
43
|
-
return onlyNums;
|
44
|
-
};
|
1
|
+
/** Normalizer for converting a string into a valid phone number. */
|
2
|
+
export const phoneNumber = (value: string | undefined | null) => {
|
3
|
+
if (!value) {
|
4
|
+
return undefined;
|
5
|
+
}
|
6
|
+
|
7
|
+
const onlyNums = value.replace(/[^\d]/g, '');
|
8
|
+
if (onlyNums.length <= 3) {
|
9
|
+
return onlyNums;
|
10
|
+
}
|
11
|
+
if (onlyNums.length <= 7) {
|
12
|
+
return `${onlyNums.slice(0, 3)}-${onlyNums.slice(3)}`;
|
13
|
+
}
|
14
|
+
if (onlyNums.length <= 10) {
|
15
|
+
return `${onlyNums.slice(0, 3)}-${onlyNums.slice(3, 6)}-${onlyNums.slice(
|
16
|
+
6,
|
17
|
+
10
|
18
|
+
)}`;
|
19
|
+
}
|
20
|
+
if (onlyNums.length <= 13) {
|
21
|
+
const countryCodeLength = onlyNums.length - 10;
|
22
|
+
return `+${onlyNums.slice(0, countryCodeLength)} ${onlyNums.slice(
|
23
|
+
countryCodeLength,
|
24
|
+
3 + countryCodeLength
|
25
|
+
)}-${onlyNums.slice(
|
26
|
+
3 + countryCodeLength,
|
27
|
+
6 + countryCodeLength
|
28
|
+
)}-${onlyNums.slice(6 + countryCodeLength, onlyNums.length)}`;
|
29
|
+
}
|
30
|
+
|
31
|
+
return onlyNums;
|
32
|
+
};
|
33
|
+
|
34
|
+
/** Normalizer for converting a string into a valid zip code. Allows for XXXXX and XXXXX-XXXX format. */
|
35
|
+
export const zipCode = (value: string | undefined | null) => {
|
36
|
+
if (!value) {
|
37
|
+
return undefined;
|
38
|
+
}
|
39
|
+
const onlyNums = value.replace(/[^\d]/g, '');
|
40
|
+
if (onlyNums.length > 5) {
|
41
|
+
return onlyNums.substring(0, 5) + '-' + onlyNums.substring(5, 9);
|
42
|
+
}
|
43
|
+
return onlyNums;
|
44
|
+
};
|
@@ -1,28 +1,28 @@
|
|
1
|
-
import SelectGroup, { SelectOption } from './SelectGroup';
|
2
|
-
import { SelectGroupPropsHelper } from './SelectGroupPropsHelper';
|
3
|
-
|
4
|
-
const booleanOptions: SelectOption<boolean>[] = [
|
5
|
-
{
|
6
|
-
label: 'Yes',
|
7
|
-
value: true,
|
8
|
-
},
|
9
|
-
{
|
10
|
-
label: 'No',
|
11
|
-
value: false,
|
12
|
-
},
|
13
|
-
];
|
14
|
-
|
15
|
-
export interface BooleanSelectGroupProps
|
16
|
-
extends Omit<SelectGroupPropsHelper<boolean | undefined | null>, 'options'> {
|
17
|
-
options?: SelectOption<boolean>[];
|
18
|
-
}
|
19
|
-
/** A `<SelectGroup/>` dropdown with two options. Default options are 'Yes' and 'No' with values of `true` and `false`. */
|
20
|
-
export function BooleanSelectGroup(props: BooleanSelectGroupProps) {
|
21
|
-
return (
|
22
|
-
<SelectGroup
|
23
|
-
multiple={false}
|
24
|
-
{...props}
|
25
|
-
options={props.options ?? booleanOptions}
|
26
|
-
/>
|
27
|
-
);
|
28
|
-
}
|
1
|
+
import SelectGroup, { SelectOption } from './SelectGroup';
|
2
|
+
import { SelectGroupPropsHelper } from './SelectGroupPropsHelper';
|
3
|
+
|
4
|
+
const booleanOptions: SelectOption<boolean>[] = [
|
5
|
+
{
|
6
|
+
label: 'Yes',
|
7
|
+
value: true,
|
8
|
+
},
|
9
|
+
{
|
10
|
+
label: 'No',
|
11
|
+
value: false,
|
12
|
+
},
|
13
|
+
];
|
14
|
+
|
15
|
+
export interface BooleanSelectGroupProps
|
16
|
+
extends Omit<SelectGroupPropsHelper<boolean | undefined | null>, 'options'> {
|
17
|
+
options?: SelectOption<boolean>[];
|
18
|
+
}
|
19
|
+
/** A `<SelectGroup/>` dropdown with two options. Default options are 'Yes' and 'No' with values of `true` and `false`. */
|
20
|
+
export function BooleanSelectGroup(props: BooleanSelectGroupProps) {
|
21
|
+
return (
|
22
|
+
<SelectGroup
|
23
|
+
multiple={false}
|
24
|
+
{...props}
|
25
|
+
options={props.options ?? booleanOptions}
|
26
|
+
/>
|
27
|
+
);
|
28
|
+
}
|
@@ -1,16 +1,16 @@
|
|
1
|
-
import SelectGroup from './SelectGroup';
|
2
|
-
import { SelectGroupPropsHelper } from './SelectGroupPropsHelper';
|
3
|
-
|
4
|
-
export interface MultiNumberSelectGroupProps
|
5
|
-
extends SelectGroupPropsHelper<number[] | undefined | null> {}
|
6
|
-
/** A `<SelectGroup/>` dropdown with values as numbers. Many options can be selected at a time. */
|
7
|
-
export function MultiNumberSelectGroup(props: MultiNumberSelectGroupProps) {
|
8
|
-
return <SelectGroup multiple={true} {...props} />;
|
9
|
-
}
|
10
|
-
|
11
|
-
export interface SingleNumberSelectGroupProps
|
12
|
-
extends SelectGroupPropsHelper<number | undefined | null> {}
|
13
|
-
/** A `<SelectGroup/>` dropdown with values as numbers. Only one option can be selected at a time. */
|
14
|
-
export function SingleNumberSelectGroup(props: SingleNumberSelectGroupProps) {
|
15
|
-
return <SelectGroup multiple={false} {...props} />;
|
16
|
-
}
|
1
|
+
import SelectGroup from './SelectGroup';
|
2
|
+
import { SelectGroupPropsHelper } from './SelectGroupPropsHelper';
|
3
|
+
|
4
|
+
export interface MultiNumberSelectGroupProps
|
5
|
+
extends SelectGroupPropsHelper<number[] | undefined | null> {}
|
6
|
+
/** A `<SelectGroup/>` dropdown with values as numbers. Many options can be selected at a time. */
|
7
|
+
export function MultiNumberSelectGroup(props: MultiNumberSelectGroupProps) {
|
8
|
+
return <SelectGroup multiple={true} {...props} />;
|
9
|
+
}
|
10
|
+
|
11
|
+
export interface SingleNumberSelectGroupProps
|
12
|
+
extends SelectGroupPropsHelper<number | undefined | null> {}
|
13
|
+
/** A `<SelectGroup/>` dropdown with values as numbers. Only one option can be selected at a time. */
|
14
|
+
export function SingleNumberSelectGroup(props: SingleNumberSelectGroupProps) {
|
15
|
+
return <SelectGroup multiple={false} {...props} />;
|
16
|
+
}
|
@@ -1,124 +1,124 @@
|
|
1
|
-
import { default as ReactSelect } from 'react-select';
|
2
|
-
import classNames from 'classnames';
|
3
|
-
import { InjectedFieldProps } from '../Field/InjectedFieldProps';
|
4
|
-
import { FormDefaults } from '../FormDefaults';
|
5
|
-
import Group, { GroupProps } from '../Group';
|
6
|
-
|
7
|
-
export interface SelectOption<TValue> {
|
8
|
-
/** Value for the select. This will be sent to the API. */
|
9
|
-
value: (TValue extends Array<infer P> ? P : TValue) | undefined;
|
10
|
-
/** Label for the select. This is displayed to the user. */
|
11
|
-
label: string;
|
12
|
-
}
|
13
|
-
|
14
|
-
// types roughly like the useService (envoc-core) result
|
15
|
-
interface OptionsApiResult<TValue> {
|
16
|
-
// we must apply Partial here because all results from template code gen are optional
|
17
|
-
result?: Partial<SelectOption<TValue>>[];
|
18
|
-
}
|
19
|
-
|
20
|
-
interface OptionsUseServiceResult<TValue> {
|
21
|
-
loading?: boolean;
|
22
|
-
// we must apply Partial here because all results from template code gen are optional
|
23
|
-
resp?: Partial<SelectOption<TValue>>[] | OptionsApiResult<TValue> | null;
|
24
|
-
error?: any;
|
25
|
-
}
|
26
|
-
|
27
|
-
export interface SelectGroupProps<TValue>
|
28
|
-
extends InjectedFieldProps<TValue | undefined | null>,
|
29
|
-
Omit<GroupProps, 'input' | 'meta' | 'children'> {
|
30
|
-
// allows for "useService" or other handles to control the data - including cache
|
31
|
-
// TODO: do we still want a version of select that has a "url" or maybe a promise func or something?
|
32
|
-
// eventually we can just add the shape of, say, useQuery (TanStack) to the union type
|
33
|
-
/** Options for the dropdown. Includes the label and value. */
|
34
|
-
options: SelectOption<TValue>[] | OptionsUseServiceResult<TValue>;
|
35
|
-
/** Whether the user should be able to have multiple values selected. */
|
36
|
-
multiple: TValue extends Array<any> ? true : false;
|
37
|
-
/** Text diplayed when no value is selected. */
|
38
|
-
placeholder?: string;
|
39
|
-
}
|
40
|
-
|
41
|
-
// TODO: we could also name this "ReactSelectGroup" or similar but the types are strongly defined now so kept the names consistent
|
42
|
-
/** Generic select dropdown. Uses [react-select](https://react-select.com/home). */
|
43
|
-
export default function SelectGroup<TValue>({
|
44
|
-
input,
|
45
|
-
meta,
|
46
|
-
className,
|
47
|
-
required,
|
48
|
-
disabled,
|
49
|
-
options,
|
50
|
-
multiple,
|
51
|
-
placeholder,
|
52
|
-
...rest
|
53
|
-
}: SelectGroupProps<TValue>) {
|
54
|
-
const effectiveOptions: Partial<SelectOption<TValue>>[] = !options
|
55
|
-
? []
|
56
|
-
: Array.isArray(options)
|
57
|
-
? options
|
58
|
-
: 'resp' in options &&
|
59
|
-
!!options.resp &&
|
60
|
-
'result' in options.resp &&
|
61
|
-
!!options.resp.result
|
62
|
-
? options.resp.result
|
63
|
-
: 'resp' in options && !!options.resp && Array.isArray(options.resp)
|
64
|
-
? options.resp
|
65
|
-
: [];
|
66
|
-
|
67
|
-
const isLoading =
|
68
|
-
(options && 'loading' in options && options.loading) || false;
|
69
|
-
|
70
|
-
return (
|
71
|
-
<Group
|
72
|
-
{...rest}
|
73
|
-
input={input}
|
74
|
-
meta={meta}
|
75
|
-
required={required}
|
76
|
-
disabled={disabled}
|
77
|
-
className={classNames(
|
78
|
-
className,
|
79
|
-
{
|
80
|
-
[FormDefaults.cssClassPrefix + 'multiple']: multiple,
|
81
|
-
[FormDefaults.cssClassPrefix + 'loading']: isLoading,
|
82
|
-
},
|
83
|
-
FormDefaults.cssClassPrefix + 'select-group'
|
84
|
-
)}>
|
85
|
-
<ReactSelect<
|
86
|
-
Partial<SelectOption<TValue>>,
|
87
|
-
TValue extends Array<any> ? true : false
|
88
|
-
>
|
89
|
-
inputId={input.id}
|
90
|
-
isMulti={multiple}
|
91
|
-
isDisabled={disabled}
|
92
|
-
options={effectiveOptions}
|
93
|
-
onBlur={input.onBlur}
|
94
|
-
value={getValue()}
|
95
|
-
onChange={(e: any) => {
|
96
|
-
if (multiple === true) {
|
97
|
-
input.onChange(e?.map((x: any) => x.value));
|
98
|
-
} else {
|
99
|
-
input.onChange(e?.value as any);
|
100
|
-
}
|
101
|
-
}}
|
102
|
-
getOptionLabel={(option) => option?.label ?? ''}
|
103
|
-
className={classNames(
|
104
|
-
className,
|
105
|
-
FormDefaults.cssClassPrefix + 'select-group'
|
106
|
-
)}
|
107
|
-
classNamePrefix="react-select"
|
108
|
-
menuPortalTarget={document.body}
|
109
|
-
menuPlacement="auto"
|
110
|
-
placeholder={placeholder}
|
111
|
-
/>
|
112
|
-
</Group>
|
113
|
-
);
|
114
|
-
|
115
|
-
function getValue() {
|
116
|
-
if (multiple) {
|
117
|
-
return effectiveOptions.filter(
|
118
|
-
(o) =>
|
119
|
-
Array.isArray(input.value) && !!input.value.find((x) => o.value === x)
|
120
|
-
);
|
121
|
-
}
|
122
|
-
return effectiveOptions.find((o) => o.value === input.value);
|
123
|
-
}
|
124
|
-
}
|
1
|
+
import { default as ReactSelect } from 'react-select';
|
2
|
+
import classNames from 'classnames';
|
3
|
+
import { InjectedFieldProps } from '../Field/InjectedFieldProps';
|
4
|
+
import { FormDefaults } from '../FormDefaults';
|
5
|
+
import Group, { GroupProps } from '../Group';
|
6
|
+
|
7
|
+
export interface SelectOption<TValue> {
|
8
|
+
/** Value for the select. This will be sent to the API. */
|
9
|
+
value: (TValue extends Array<infer P> ? P : TValue) | undefined;
|
10
|
+
/** Label for the select. This is displayed to the user. */
|
11
|
+
label: string;
|
12
|
+
}
|
13
|
+
|
14
|
+
// types roughly like the useService (envoc-core) result
|
15
|
+
interface OptionsApiResult<TValue> {
|
16
|
+
// we must apply Partial here because all results from template code gen are optional
|
17
|
+
result?: Partial<SelectOption<TValue>>[];
|
18
|
+
}
|
19
|
+
|
20
|
+
interface OptionsUseServiceResult<TValue> {
|
21
|
+
loading?: boolean;
|
22
|
+
// we must apply Partial here because all results from template code gen are optional
|
23
|
+
resp?: Partial<SelectOption<TValue>>[] | OptionsApiResult<TValue> | null;
|
24
|
+
error?: any;
|
25
|
+
}
|
26
|
+
|
27
|
+
export interface SelectGroupProps<TValue>
|
28
|
+
extends InjectedFieldProps<TValue | undefined | null>,
|
29
|
+
Omit<GroupProps, 'input' | 'meta' | 'children'> {
|
30
|
+
// allows for "useService" or other handles to control the data - including cache
|
31
|
+
// TODO: do we still want a version of select that has a "url" or maybe a promise func or something?
|
32
|
+
// eventually we can just add the shape of, say, useQuery (TanStack) to the union type
|
33
|
+
/** Options for the dropdown. Includes the label and value. */
|
34
|
+
options: SelectOption<TValue>[] | OptionsUseServiceResult<TValue>;
|
35
|
+
/** Whether the user should be able to have multiple values selected. */
|
36
|
+
multiple: TValue extends Array<any> ? true : false;
|
37
|
+
/** Text diplayed when no value is selected. */
|
38
|
+
placeholder?: string;
|
39
|
+
}
|
40
|
+
|
41
|
+
// TODO: we could also name this "ReactSelectGroup" or similar but the types are strongly defined now so kept the names consistent
|
42
|
+
/** Generic select dropdown. Uses [react-select](https://react-select.com/home). */
|
43
|
+
export default function SelectGroup<TValue>({
|
44
|
+
input,
|
45
|
+
meta,
|
46
|
+
className,
|
47
|
+
required,
|
48
|
+
disabled,
|
49
|
+
options,
|
50
|
+
multiple,
|
51
|
+
placeholder,
|
52
|
+
...rest
|
53
|
+
}: SelectGroupProps<TValue>) {
|
54
|
+
const effectiveOptions: Partial<SelectOption<TValue>>[] = !options
|
55
|
+
? []
|
56
|
+
: Array.isArray(options)
|
57
|
+
? options
|
58
|
+
: 'resp' in options &&
|
59
|
+
!!options.resp &&
|
60
|
+
'result' in options.resp &&
|
61
|
+
!!options.resp.result
|
62
|
+
? options.resp.result
|
63
|
+
: 'resp' in options && !!options.resp && Array.isArray(options.resp)
|
64
|
+
? options.resp
|
65
|
+
: [];
|
66
|
+
|
67
|
+
const isLoading =
|
68
|
+
(options && 'loading' in options && options.loading) || false;
|
69
|
+
|
70
|
+
return (
|
71
|
+
<Group
|
72
|
+
{...rest}
|
73
|
+
input={input}
|
74
|
+
meta={meta}
|
75
|
+
required={required}
|
76
|
+
disabled={disabled}
|
77
|
+
className={classNames(
|
78
|
+
className,
|
79
|
+
{
|
80
|
+
[FormDefaults.cssClassPrefix + 'multiple']: multiple,
|
81
|
+
[FormDefaults.cssClassPrefix + 'loading']: isLoading,
|
82
|
+
},
|
83
|
+
FormDefaults.cssClassPrefix + 'select-group'
|
84
|
+
)}>
|
85
|
+
<ReactSelect<
|
86
|
+
Partial<SelectOption<TValue>>,
|
87
|
+
TValue extends Array<any> ? true : false
|
88
|
+
>
|
89
|
+
inputId={input.id}
|
90
|
+
isMulti={multiple}
|
91
|
+
isDisabled={disabled}
|
92
|
+
options={effectiveOptions}
|
93
|
+
onBlur={input.onBlur}
|
94
|
+
value={getValue()}
|
95
|
+
onChange={(e: any) => {
|
96
|
+
if (multiple === true) {
|
97
|
+
input.onChange(e?.map((x: any) => x.value));
|
98
|
+
} else {
|
99
|
+
input.onChange(e?.value as any);
|
100
|
+
}
|
101
|
+
}}
|
102
|
+
getOptionLabel={(option) => option?.label ?? ''}
|
103
|
+
className={classNames(
|
104
|
+
className,
|
105
|
+
FormDefaults.cssClassPrefix + 'select-group'
|
106
|
+
)}
|
107
|
+
classNamePrefix="react-select"
|
108
|
+
menuPortalTarget={document.body}
|
109
|
+
menuPlacement="auto"
|
110
|
+
placeholder={placeholder}
|
111
|
+
/>
|
112
|
+
</Group>
|
113
|
+
);
|
114
|
+
|
115
|
+
function getValue() {
|
116
|
+
if (multiple) {
|
117
|
+
return effectiveOptions.filter(
|
118
|
+
(o) =>
|
119
|
+
Array.isArray(input.value) && !!input.value.find((x) => o.value === x)
|
120
|
+
);
|
121
|
+
}
|
122
|
+
return effectiveOptions.find((o) => o.value === input.value);
|
123
|
+
}
|
124
|
+
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { SelectGroupProps } from './SelectGroup';
|
2
|
-
|
3
|
-
export interface SelectGroupPropsHelper<T>
|
4
|
-
extends Omit<SelectGroupProps<T>, 'multiple'> {}
|
1
|
+
import { SelectGroupProps } from './SelectGroup';
|
2
|
+
|
3
|
+
export interface SelectGroupPropsHelper<T>
|
4
|
+
extends Omit<SelectGroupProps<T>, 'multiple'> {}
|