@digigov/form 2.0.0-254f7786 → 2.0.0-2a26e869
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/Field/ErrorGroup/index.js +48 -0
- package/Field/ErrorGroup/package.json +6 -0
- package/Field/ErrorGroup.d.ts +15 -0
- package/Field/ErrorGroup.js.map +7 -0
- package/Field/FieldBase/index.js +30 -22
- package/Field/FieldBase.js.map +2 -2
- package/Field/FieldBaseContainer/index.js +3 -2
- package/Field/FieldBaseContainer.js.map +2 -2
- package/Field/types.d.ts +2 -0
- package/FieldArray/index.js +1 -0
- package/FieldArray/index.js.map +2 -2
- package/FormBuilder/FormBuilder.stories.d.ts +2 -0
- package/FormBuilder/__stories__/AutoErrorGrouping.d.ts +3 -0
- package/FormBuilder/__stories__/ErrorGrouping.d.ts +3 -0
- package/FormBuilder/index.js +83 -4
- package/FormBuilder/index.js.map +2 -2
- package/FormBuilder/interaction.test.d.ts +1 -0
- package/MultiplicityField/index.js +1 -0
- package/MultiplicityField/index.js.map +2 -2
- package/cjs/Field/ErrorGroup/index.js +82 -0
- package/cjs/Field/ErrorGroup.js.map +7 -0
- package/cjs/Field/FieldBase/index.js +29 -21
- package/cjs/Field/FieldBase.js.map +2 -2
- package/cjs/Field/FieldBaseContainer/index.js +3 -2
- package/cjs/Field/FieldBaseContainer.js.map +2 -2
- package/cjs/Field/types.js.map +1 -1
- package/cjs/FieldArray/index.js +1 -0
- package/cjs/FieldArray/index.js.map +2 -2
- package/cjs/FormBuilder/index.js +83 -4
- package/cjs/FormBuilder/index.js.map +2 -2
- package/cjs/MultiplicityField/index.js +1 -0
- package/cjs/MultiplicityField/index.js.map +2 -2
- package/cjs/inputs/DateInput/index.js +3 -3
- package/cjs/inputs/DateInput/index.js.map +2 -2
- package/cjs/inputs/Input/index.js +1 -0
- package/cjs/inputs/Input/index.js.map +2 -2
- package/cjs/lazy/index.js +4 -2
- package/cjs/lazy.js.map +2 -2
- package/cjs/registry/index.js +8 -6
- package/cjs/registry.js.map +2 -2
- package/cjs/validators/index.js +19 -13
- package/cjs/validators/index.js.map +2 -2
- package/cjs/validators/utils/int/index.js +1 -1
- package/cjs/validators/utils/int.js.map +2 -2
- package/cjs/validators/utils/number/index.js +1 -1
- package/cjs/validators/utils/number.js.map +2 -2
- package/index.js +1 -1
- package/inputs/DateInput/index.js +3 -3
- package/inputs/DateInput/index.js.map +2 -2
- package/inputs/Input/Input.stories.d.ts +1 -0
- package/inputs/Input/__stories__/StringWithTrimValidation.d.ts +3 -0
- package/inputs/Input/index.js +1 -0
- package/inputs/Input/index.js.map +2 -2
- package/lazy/index.js +4 -2
- package/package.json +4 -4
- package/registry/index.js +8 -6
- package/src/Field/ErrorGroup.tsx +84 -0
- package/src/Field/FieldBase.tsx +33 -22
- package/src/Field/FieldBaseContainer.tsx +3 -2
- package/src/Field/types.tsx +2 -0
- package/src/FieldArray/index.test.tsx +13 -14
- package/src/FieldArray/index.tsx +1 -0
- package/src/FormBuilder/FormBuilder.stories.js +2 -0
- package/src/FormBuilder/__stories__/AutoErrorGrouping.tsx +63 -0
- package/src/FormBuilder/__stories__/ErrorGrouping.tsx +43 -0
- package/src/FormBuilder/index.test.tsx +9 -1
- package/src/FormBuilder/index.tsx +92 -6
- package/src/FormBuilder/interaction.test.tsx +32 -0
- package/src/FormBuilder/scenarios.test.tsx +111 -4
- package/src/MultiplicityField/index.test.tsx +25 -26
- package/src/MultiplicityField/index.tsx +1 -0
- package/src/Questions/__snapshots__/index.spec.tsx.snap +2 -1
- package/src/Questions/index.spec.tsx +8 -0
- package/src/Questions/index.test.tsx +10 -11
- package/src/inputs/AutoCompleteInput/index.test.tsx +13 -14
- package/src/inputs/Checkboxes/index.test.tsx +16 -17
- package/src/inputs/DateInput/index.test.tsx +10 -11
- package/src/inputs/DateInput/index.tsx +3 -3
- package/src/inputs/FileInput/index.test.tsx +10 -11
- package/src/inputs/ImageInput/index.test.tsx +19 -20
- package/src/inputs/Input/Input.stories.js +1 -0
- package/src/inputs/Input/__stories__/StringWithTrimValidation.tsx +26 -0
- package/src/inputs/Input/__stories__/TextWithLimit.tsx +1 -0
- package/src/inputs/Input/index.test.tsx +41 -38
- package/src/inputs/Input/index.tsx +1 -0
- package/src/inputs/Label/index.test.tsx +10 -11
- package/src/inputs/OtpInput/index.test.tsx +10 -11
- package/src/inputs/Radio/index.test.tsx +16 -17
- package/src/inputs/Select/index.test.tsx +10 -11
- package/src/lazy.js +69 -0
- package/src/registry.js +159 -0
- package/src/validators/index.ts +41 -14
- package/src/validators/utils/int.ts +1 -1
- package/src/validators/utils/number.ts +1 -1
- package/validators/index.js +19 -13
- package/validators/index.js.map +2 -2
- package/validators/utils/int/index.js +1 -1
- package/validators/utils/int.js.map +2 -2
- package/validators/utils/number/index.js +1 -1
- package/validators/utils/number.js.map +2 -2
package/registry/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import * as _digigov_form from "@digigov/form";
|
|
|
4
4
|
import * as _digigov_form_internal from "@digigov/form/internal";
|
|
5
5
|
import * as _digigov_form_types from "@digigov/form/types";
|
|
6
6
|
import * as _digigov_form_utils from "@digigov/form/utils";
|
|
7
|
+
import * as _digigov_form_Field_ErrorGroup from "@digigov/form/Field/ErrorGroup";
|
|
7
8
|
import * as _digigov_form_Field_FieldBase from "@digigov/form/Field/FieldBase";
|
|
8
9
|
import * as _digigov_form_Field_FieldBaseContainer from "@digigov/form/Field/FieldBaseContainer";
|
|
9
10
|
import * as _digigov_form_Field_FieldConditional from "@digigov/form/Field/FieldConditional";
|
|
@@ -44,15 +45,15 @@ import * as _digigov_form_Questions_Step_getAddMoreFields from "@digigov/form/Qu
|
|
|
44
45
|
import * as _digigov_form_Questions_Step from "@digigov/form/Questions/Step";
|
|
45
46
|
import * as _digigov_form_Questions_Step_types from "@digigov/form/Questions/Step/types";
|
|
46
47
|
import * as _digigov_form_inputs_AutoCompleteInput from "@digigov/form/inputs/AutoCompleteInput";
|
|
47
|
-
import * as _digigov_form_inputs_DateInput from "@digigov/form/inputs/DateInput";
|
|
48
48
|
import * as _digigov_form_inputs_Checkboxes from "@digigov/form/inputs/Checkboxes";
|
|
49
|
-
import * as _digigov_form_inputs_FileInput from "@digigov/form/inputs/FileInput";
|
|
50
49
|
import * as _digigov_form_inputs_ImageInput from "@digigov/form/inputs/ImageInput";
|
|
50
|
+
import * as _digigov_form_inputs_DateInput from "@digigov/form/inputs/DateInput";
|
|
51
|
+
import * as _digigov_form_inputs_FileInput from "@digigov/form/inputs/FileInput";
|
|
51
52
|
import * as _digigov_form_inputs_Input from "@digigov/form/inputs/Input";
|
|
52
53
|
import * as _digigov_form_inputs_Input_inputsInputScenarios from "@digigov/form/inputs/Input/inputsInputScenarios";
|
|
53
54
|
import * as _digigov_form_inputs_Label from "@digigov/form/inputs/Label";
|
|
54
|
-
import * as _digigov_form_inputs_Radio from "@digigov/form/inputs/Radio";
|
|
55
55
|
import * as _digigov_form_inputs_OtpInput from "@digigov/form/inputs/OtpInput";
|
|
56
|
+
import * as _digigov_form_inputs_Radio from "@digigov/form/inputs/Radio";
|
|
56
57
|
import * as _digigov_form_inputs_Select from "@digigov/form/inputs/Select";
|
|
57
58
|
import * as _digigov_form_validators_utils_afm from "@digigov/form/validators/utils/afm";
|
|
58
59
|
import * as _digigov_form_validators_utils_date from "@digigov/form/validators/utils/date";
|
|
@@ -91,6 +92,7 @@ export default {
|
|
|
91
92
|
'@digigov/form/internal': lazyImport(_digigov_form_internal),
|
|
92
93
|
'@digigov/form/types': lazyImport(_digigov_form_types),
|
|
93
94
|
'@digigov/form/utils': lazyImport(_digigov_form_utils),
|
|
95
|
+
'@digigov/form/Field/ErrorGroup': lazyImport(_digigov_form_Field_ErrorGroup),
|
|
94
96
|
'@digigov/form/Field/FieldBase': lazyImport(_digigov_form_Field_FieldBase),
|
|
95
97
|
'@digigov/form/Field/FieldBaseContainer': lazyImport(_digigov_form_Field_FieldBaseContainer),
|
|
96
98
|
'@digigov/form/Field/FieldConditional': lazyImport(_digigov_form_Field_FieldConditional),
|
|
@@ -131,15 +133,15 @@ export default {
|
|
|
131
133
|
'@digigov/form/Questions/Step': lazyImport(_digigov_form_Questions_Step),
|
|
132
134
|
'@digigov/form/Questions/Step/types': lazyImport(_digigov_form_Questions_Step_types),
|
|
133
135
|
'@digigov/form/inputs/AutoCompleteInput': lazyImport(_digigov_form_inputs_AutoCompleteInput),
|
|
134
|
-
'@digigov/form/inputs/DateInput': lazyImport(_digigov_form_inputs_DateInput),
|
|
135
136
|
'@digigov/form/inputs/Checkboxes': lazyImport(_digigov_form_inputs_Checkboxes),
|
|
136
|
-
'@digigov/form/inputs/FileInput': lazyImport(_digigov_form_inputs_FileInput),
|
|
137
137
|
'@digigov/form/inputs/ImageInput': lazyImport(_digigov_form_inputs_ImageInput),
|
|
138
|
+
'@digigov/form/inputs/DateInput': lazyImport(_digigov_form_inputs_DateInput),
|
|
139
|
+
'@digigov/form/inputs/FileInput': lazyImport(_digigov_form_inputs_FileInput),
|
|
138
140
|
'@digigov/form/inputs/Input': lazyImport(_digigov_form_inputs_Input),
|
|
139
141
|
'@digigov/form/inputs/Input/inputsInputScenarios': lazyImport(_digigov_form_inputs_Input_inputsInputScenarios),
|
|
140
142
|
'@digigov/form/inputs/Label': lazyImport(_digigov_form_inputs_Label),
|
|
141
|
-
'@digigov/form/inputs/Radio': lazyImport(_digigov_form_inputs_Radio),
|
|
142
143
|
'@digigov/form/inputs/OtpInput': lazyImport(_digigov_form_inputs_OtpInput),
|
|
144
|
+
'@digigov/form/inputs/Radio': lazyImport(_digigov_form_inputs_Radio),
|
|
143
145
|
'@digigov/form/inputs/Select': lazyImport(_digigov_form_inputs_Select),
|
|
144
146
|
'@digigov/form/validators/utils/afm': lazyImport(_digigov_form_validators_utils_afm),
|
|
145
147
|
'@digigov/form/validators/utils/date': lazyImport(_digigov_form_validators_utils_date),
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React, { createContext, useEffect, useState } from 'react';
|
|
2
|
+
import { ErrorOption } from 'react-hook-form';
|
|
3
|
+
import { FieldLabelProps } from '@digigov/form/Field/types';
|
|
4
|
+
import { List, ListItem } from '@digigov/ui/content/List';
|
|
5
|
+
import { ErrorSummary } from '@digigov/ui/feedback/ErrorSummary';
|
|
6
|
+
import { useTranslation } from '@digigov/ui/i18n';
|
|
7
|
+
import { Link } from '@digigov/ui/navigation/Link';
|
|
8
|
+
import { Heading } from '@digigov/ui/typography/Heading';
|
|
9
|
+
import { Base } from '@digigov/ui/utils/Base';
|
|
10
|
+
import { Breakpoints } from '@digigov/ui/utils/hooks/useScreen';
|
|
11
|
+
interface ErrorGroupContextType {
|
|
12
|
+
setError: (
|
|
13
|
+
name?: string,
|
|
14
|
+
label?: FieldLabelProps,
|
|
15
|
+
error?: ErrorOption
|
|
16
|
+
) => void;
|
|
17
|
+
}
|
|
18
|
+
export const ErrorGroupContext = createContext<ErrorGroupContextType | null>(
|
|
19
|
+
null
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export interface ErrorGroupProps {
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
screenSize?: Breakpoints | undefined;
|
|
25
|
+
fieldOrder?: any[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const ErrorGroup: React.FC<ErrorGroupProps> = ({
|
|
29
|
+
children,
|
|
30
|
+
screenSize,
|
|
31
|
+
fieldOrder,
|
|
32
|
+
}) => {
|
|
33
|
+
const [errors, setErrors] = useState<ErrorOption>({});
|
|
34
|
+
const { t } = useTranslation();
|
|
35
|
+
const setError = (name, label, error) => {
|
|
36
|
+
if (errors[name] !== error && errors[name]?.message !== error?.message) {
|
|
37
|
+
if (!error?.message) {
|
|
38
|
+
const updatedErrors = { ...errors };
|
|
39
|
+
delete updatedErrors[name];
|
|
40
|
+
setErrors(updatedErrors);
|
|
41
|
+
} else {
|
|
42
|
+
setErrors({
|
|
43
|
+
...errors,
|
|
44
|
+
[name]: {
|
|
45
|
+
...error,
|
|
46
|
+
label: label.primary,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
setErrors({});
|
|
55
|
+
}, [screenSize]);
|
|
56
|
+
return (
|
|
57
|
+
<ErrorGroupContext.Provider value={{ setError }}>
|
|
58
|
+
{Object.keys(errors).length !== 0 && (
|
|
59
|
+
<ErrorSummary variant="error-group">
|
|
60
|
+
<Heading size="sm">
|
|
61
|
+
Τα παρακάτω πεδία της φόρμας ειναι λάθος.{' '}
|
|
62
|
+
</Heading>
|
|
63
|
+
<List listStyle="bullet">
|
|
64
|
+
{(fieldOrder || Object.keys(errors).reverse())
|
|
65
|
+
.filter((fieldName) => errors[fieldName])
|
|
66
|
+
.map((fieldName, index) => {
|
|
67
|
+
return (
|
|
68
|
+
<ListItem key={index} id={`${fieldName}-error`}>
|
|
69
|
+
<Base as="b">{t(errors[fieldName]?.label)}:</Base>{' '}
|
|
70
|
+
<Link href={`#${fieldName}`}>
|
|
71
|
+
{t(errors[fieldName]?.message)}
|
|
72
|
+
</Link>
|
|
73
|
+
</ListItem>
|
|
74
|
+
);
|
|
75
|
+
})}
|
|
76
|
+
</List>
|
|
77
|
+
</ErrorSummary>
|
|
78
|
+
)}
|
|
79
|
+
{children}
|
|
80
|
+
</ErrorGroupContext.Provider>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default ErrorGroup;
|
package/src/Field/FieldBase.tsx
CHANGED
|
@@ -1,36 +1,46 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useContext, useEffect } from 'react';
|
|
2
2
|
import { Controller } from 'react-hook-form';
|
|
3
|
+
import { ErrorGroupContext } from '@digigov/form/Field/ErrorGroup';
|
|
3
4
|
import FieldBaseContainer from '@digigov/form/Field/FieldBaseContainer';
|
|
4
5
|
import { FieldBaseProps } from '@digigov/form/Field/types';
|
|
5
6
|
|
|
6
|
-
export const FieldBase: React.FC<FieldBaseProps> = (
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
7
|
+
export const FieldBase: React.FC<FieldBaseProps> = ({
|
|
8
|
+
required,
|
|
9
|
+
name,
|
|
10
|
+
component: Component,
|
|
11
|
+
wrapper,
|
|
12
|
+
control,
|
|
13
|
+
type,
|
|
14
|
+
controlled = false,
|
|
15
|
+
enabled = true,
|
|
16
|
+
editable,
|
|
17
|
+
defaultValue,
|
|
18
|
+
label,
|
|
19
|
+
extra = {},
|
|
20
|
+
layout,
|
|
21
|
+
error,
|
|
22
|
+
register,
|
|
23
|
+
...componentProps
|
|
24
|
+
}) => {
|
|
25
|
+
const errorGroupContext = useContext(ErrorGroupContext);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (errorGroupContext) {
|
|
29
|
+
errorGroupContext.setError(name, label, error);
|
|
30
|
+
}
|
|
31
|
+
}, [error, name, errorGroupContext?.setError]);
|
|
32
|
+
|
|
25
33
|
if (!enabled) {
|
|
26
34
|
return null;
|
|
27
35
|
}
|
|
36
|
+
|
|
28
37
|
if (controlled) {
|
|
29
38
|
return (
|
|
30
39
|
<FieldBaseContainer
|
|
31
40
|
label={label}
|
|
32
41
|
layout={layout}
|
|
33
|
-
error={error}
|
|
42
|
+
error={errorGroupContext ? undefined : error}
|
|
43
|
+
hasError={!!error}
|
|
34
44
|
wrapper={wrapper}
|
|
35
45
|
name={name}
|
|
36
46
|
>
|
|
@@ -64,7 +74,8 @@ export const FieldBase: React.FC<FieldBaseProps> = (props) => {
|
|
|
64
74
|
<FieldBaseContainer
|
|
65
75
|
label={label}
|
|
66
76
|
layout={layout}
|
|
67
|
-
error={error}
|
|
77
|
+
error={errorGroupContext ? undefined : error}
|
|
78
|
+
hasError={!!error}
|
|
68
79
|
wrapper={wrapper}
|
|
69
80
|
name={name}
|
|
70
81
|
>
|
|
@@ -15,6 +15,7 @@ export const FieldBaseContainer: React.FC<FieldContainerProps> = ({
|
|
|
15
15
|
label,
|
|
16
16
|
children,
|
|
17
17
|
error,
|
|
18
|
+
hasError,
|
|
18
19
|
layout,
|
|
19
20
|
}) => {
|
|
20
21
|
const { t } = useTranslation();
|
|
@@ -24,7 +25,7 @@ export const FieldBaseContainer: React.FC<FieldContainerProps> = ({
|
|
|
24
25
|
const errorContext = error?.message.context || {};
|
|
25
26
|
if (wrapper === 'fieldset') {
|
|
26
27
|
return (
|
|
27
|
-
<FieldContainer error={
|
|
28
|
+
<FieldContainer error={hasError} {...layout}>
|
|
28
29
|
<CoreFieldset>
|
|
29
30
|
<FieldsetLegend size="sm">{label && label.primary}</FieldsetLegend>
|
|
30
31
|
{label && label.secondary && <Hint>{t(label.secondary)}</Hint>}
|
|
@@ -39,7 +40,7 @@ export const FieldBaseContainer: React.FC<FieldContainerProps> = ({
|
|
|
39
40
|
);
|
|
40
41
|
} else {
|
|
41
42
|
return (
|
|
42
|
-
<FieldContainer error={
|
|
43
|
+
<FieldContainer error={hasError} {...layout}>
|
|
43
44
|
<LabelContainer>
|
|
44
45
|
{label && <Label label={label} />}
|
|
45
46
|
{error && (
|
package/src/Field/types.tsx
CHANGED
|
@@ -30,6 +30,7 @@ export interface FieldSpec {
|
|
|
30
30
|
| 'phone_number'
|
|
31
31
|
| 'array'
|
|
32
32
|
| 'object';
|
|
33
|
+
trim?: boolean;
|
|
33
34
|
component?: any;
|
|
34
35
|
autoComplete?: string;
|
|
35
36
|
maxLength?: number;
|
|
@@ -91,6 +92,7 @@ export interface FieldContainerProps {
|
|
|
91
92
|
layout?: FieldSpec['layout'];
|
|
92
93
|
label?: FieldSpec['label'];
|
|
93
94
|
error?: ErrorOption | Record<string, any>;
|
|
95
|
+
hasError?: boolean;
|
|
94
96
|
children?: React.ReactNode;
|
|
95
97
|
wrapper?: FieldSpec['wrapper'];
|
|
96
98
|
}
|
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { test, expect } from '@playwright/experimental-ct-react';
|
|
3
|
+
import TestVariant from '@digigov/ui/utils/TestVariant'
|
|
3
4
|
import { Default } from '@digigov/form/FieldArray/__stories__/Default';
|
|
4
5
|
import { WithExactLength } from '@digigov/form/FieldArray/__stories__/WithExactLength';
|
|
5
|
-
import TestVariant from '@digigov/ui/utils/TestVariant';
|
|
6
6
|
|
|
7
7
|
test('renders the All FieldArray variants', async ({ mount, page }) => {
|
|
8
8
|
await mount(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
</
|
|
17
|
-
|
|
9
|
+
|
|
10
|
+
<div>
|
|
11
|
+
<TestVariant title="Default">
|
|
12
|
+
<Default />
|
|
13
|
+
</TestVariant>
|
|
14
|
+
<TestVariant title="WithExactLength">
|
|
15
|
+
<WithExactLength />
|
|
16
|
+
</TestVariant>
|
|
17
|
+
</div>
|
|
18
|
+
)
|
|
18
19
|
await page.evaluate(() => document.fonts.ready);
|
|
19
20
|
|
|
20
|
-
const screenshot = await page.screenshot({
|
|
21
|
-
fullPage: true,
|
|
22
|
-
animations: 'disabled',
|
|
23
|
-
});
|
|
21
|
+
const screenshot = await page.screenshot({ fullPage: true, animations: 'disabled' });
|
|
24
22
|
expect(screenshot).toMatchSnapshot();
|
|
25
23
|
});
|
|
24
|
+
|
package/src/FieldArray/index.tsx
CHANGED
|
@@ -5,3 +5,5 @@ export default {
|
|
|
5
5
|
displayName: 'FormBuilder',
|
|
6
6
|
};
|
|
7
7
|
export { Default } from '@digigov/form/FormBuilder/__stories__/Default';
|
|
8
|
+
export { ErrorGrouping } from '@digigov/form/FormBuilder/__stories__/ErrorGrouping';
|
|
9
|
+
export { AutoErrorGrouping } from '@digigov/form/FormBuilder/__stories__/AutoErrorGrouping';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import FormBuilder, { FieldSpec } from '@digigov/form';
|
|
3
|
+
|
|
4
|
+
const FIELDS: FieldSpec[] = [
|
|
5
|
+
{
|
|
6
|
+
key: 'name',
|
|
7
|
+
type: 'string',
|
|
8
|
+
required: true,
|
|
9
|
+
layout: { xl: 4, lg: 4, md: 6, xs: 6 },
|
|
10
|
+
label: {
|
|
11
|
+
primary: 'Όνομα',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
key: 'surname',
|
|
16
|
+
type: 'string',
|
|
17
|
+
required: true,
|
|
18
|
+
layout: { xl: 4, lg: 4, md: 6, xs: 6 },
|
|
19
|
+
label: {
|
|
20
|
+
primary: 'Επίθετο',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
key: 'address',
|
|
25
|
+
type: 'string',
|
|
26
|
+
required: true,
|
|
27
|
+
layout: { xl: 4, lg: 2, md: 6, xs: 6 },
|
|
28
|
+
label: {
|
|
29
|
+
primary: 'Διεύθυνση',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
key: 'town',
|
|
34
|
+
type: 'string',
|
|
35
|
+
required: true,
|
|
36
|
+
layout: { xl: 4, lg: 4, md: 6, xs: 6 },
|
|
37
|
+
label: {
|
|
38
|
+
primary: 'Πόλη',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
key: 'country',
|
|
43
|
+
type: 'string',
|
|
44
|
+
required: true,
|
|
45
|
+
layout: { xs: 12 },
|
|
46
|
+
label: {
|
|
47
|
+
primary: 'Χώρα',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
export const AutoErrorGrouping = () => (
|
|
53
|
+
<FormBuilder
|
|
54
|
+
grid={true}
|
|
55
|
+
auto={true}
|
|
56
|
+
onSubmit={(data) => {
|
|
57
|
+
console.log(data);
|
|
58
|
+
}}
|
|
59
|
+
fields={FIELDS}
|
|
60
|
+
></FormBuilder>
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
export default AutoErrorGrouping;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import FormBuilder, { Field } from '@digigov/form';
|
|
3
|
+
import { ErrorGroup } from '@digigov/form/Field/ErrorGroup';
|
|
4
|
+
import { Button } from '@digigov/ui/form/Button';
|
|
5
|
+
|
|
6
|
+
export const ErrorGrouping = () => (
|
|
7
|
+
<FormBuilder
|
|
8
|
+
grid={true}
|
|
9
|
+
onSubmit={(data) => {
|
|
10
|
+
console.log(data);
|
|
11
|
+
}}
|
|
12
|
+
>
|
|
13
|
+
<ErrorGroup>
|
|
14
|
+
<Field
|
|
15
|
+
key="name"
|
|
16
|
+
name="name"
|
|
17
|
+
type="string"
|
|
18
|
+
label={{ primary: 'Όνομα' }}
|
|
19
|
+
layout={{ xs: 4 }}
|
|
20
|
+
required
|
|
21
|
+
/>
|
|
22
|
+
<Field
|
|
23
|
+
key="surname"
|
|
24
|
+
name="surname"
|
|
25
|
+
type="string"
|
|
26
|
+
label={{ primary: 'Επίθετο' }}
|
|
27
|
+
layout={{ xs: 4 }}
|
|
28
|
+
required
|
|
29
|
+
/>
|
|
30
|
+
<Field
|
|
31
|
+
key="address"
|
|
32
|
+
name="address"
|
|
33
|
+
type="string"
|
|
34
|
+
label={{ primary: 'Διεύθυνση' }}
|
|
35
|
+
layout={{ xs: 4 }}
|
|
36
|
+
required
|
|
37
|
+
/>
|
|
38
|
+
</ErrorGroup>
|
|
39
|
+
<Button type="submit">Συνέχεια</Button>
|
|
40
|
+
</FormBuilder>
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
export default ErrorGrouping;
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { test, expect } from '@playwright/experimental-ct-react';
|
|
3
|
-
import { Default } from '@digigov/form/FormBuilder/__stories__/Default';
|
|
4
3
|
import TestVariant from '@digigov/ui/utils/TestVariant';
|
|
4
|
+
import { AutoErrorGrouping } from '@digigov/form/FormBuilder/__stories__/AutoErrorGrouping';
|
|
5
|
+
import { Default } from '@digigov/form/FormBuilder/__stories__/Default';
|
|
6
|
+
import { ErrorGrouping } from '@digigov/form/FormBuilder/__stories__/ErrorGrouping';
|
|
5
7
|
|
|
6
8
|
test('renders the All FormBuilder variants', async ({ mount, page }) => {
|
|
7
9
|
await mount(
|
|
8
10
|
<div>
|
|
11
|
+
<TestVariant title="AutoErrorGrouping">
|
|
12
|
+
<AutoErrorGrouping />
|
|
13
|
+
</TestVariant>
|
|
9
14
|
<TestVariant title="Default">
|
|
10
15
|
<Default />
|
|
11
16
|
</TestVariant>
|
|
17
|
+
<TestVariant title="ErrorGrouping">
|
|
18
|
+
<ErrorGrouping />
|
|
19
|
+
</TestVariant>
|
|
12
20
|
</div>
|
|
13
21
|
);
|
|
14
22
|
await page.evaluate(() => document.fonts.ready);
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import React, { useCallback, useContext, useRef } from 'react';
|
|
2
2
|
import { useForm } from 'react-hook-form';
|
|
3
3
|
import { Field } from '@digigov/form/Field';
|
|
4
|
+
import { ErrorGroup } from '@digigov/form/Field/ErrorGroup';
|
|
4
5
|
import { CONTROLLED_FIELD_COMPONENTS } from '@digigov/form/Field/utils';
|
|
5
|
-
import {
|
|
6
|
+
import { FieldsetWithContext } from '@digigov/form/Fieldset/FieldsetWithContext';
|
|
6
7
|
import { FormContext } from '@digigov/form/FormContext';
|
|
7
8
|
import { FormBaseProps, FormData, FormBuilderProps } from '@digigov/form/types';
|
|
8
9
|
import { yupResolver } from '@digigov/form/utils';
|
|
9
10
|
import { useValidationSchema } from '@digigov/form/validators';
|
|
10
11
|
import { Button } from '@digigov/ui/form/Button';
|
|
11
12
|
import { Form } from '@digigov/ui/form/Form';
|
|
13
|
+
import { useScreenSize } from '@digigov/ui/utils/hooks/useScreen';
|
|
12
14
|
|
|
13
15
|
const FormBase = React.forwardRef(function FormBase(
|
|
14
16
|
{
|
|
@@ -40,7 +42,10 @@ const FormBase = React.forwardRef(function FormBase(
|
|
|
40
42
|
|
|
41
43
|
const handleSubmit = useCallback(
|
|
42
44
|
(data: FormData): void => {
|
|
43
|
-
if (onSubmit)
|
|
45
|
+
if (onSubmit) {
|
|
46
|
+
form.reset(data);
|
|
47
|
+
onSubmit(data);
|
|
48
|
+
}
|
|
44
49
|
},
|
|
45
50
|
[onSubmit]
|
|
46
51
|
);
|
|
@@ -76,6 +81,43 @@ const FormBase = React.forwardRef(function FormBase(
|
|
|
76
81
|
);
|
|
77
82
|
});
|
|
78
83
|
|
|
84
|
+
const DEFAULT_LAYOUT = {
|
|
85
|
+
xs: 12,
|
|
86
|
+
sm: 12,
|
|
87
|
+
md: 12,
|
|
88
|
+
lg: 12,
|
|
89
|
+
xl: 12,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const BREAKPOINTS = ['xs', 'sm', 'md', 'lg', 'xl'];
|
|
93
|
+
|
|
94
|
+
const groupFieldsByLayout = (fields) => {
|
|
95
|
+
return BREAKPOINTS.reduce((sizes, size) => {
|
|
96
|
+
sizes[size] = fields
|
|
97
|
+
.reduce((acc, item) => {
|
|
98
|
+
const layoutValue = item.layout?.[size] || 12;
|
|
99
|
+
if (layoutValue === 0) {
|
|
100
|
+
return [...acc, item];
|
|
101
|
+
}
|
|
102
|
+
const lastElement = acc[acc.length - 1];
|
|
103
|
+
const currentSum = Array.isArray(lastElement)
|
|
104
|
+
? lastElement.reduce((sum, i) => sum + (i.layout?.[size] || 0), 0)
|
|
105
|
+
: 0;
|
|
106
|
+
if (!Array.isArray(lastElement) || currentSum + layoutValue > 12) {
|
|
107
|
+
return layoutValue === 12 ? [...acc, item] : [...acc, [item]];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return [...acc.slice(0, -1), [...lastElement, item]];
|
|
111
|
+
}, [])
|
|
112
|
+
.map((item) => {
|
|
113
|
+
if (Array.isArray(item) && item.length === 1) {
|
|
114
|
+
return item[0];
|
|
115
|
+
}
|
|
116
|
+
return item;
|
|
117
|
+
});
|
|
118
|
+
return sizes;
|
|
119
|
+
}, {});
|
|
120
|
+
};
|
|
79
121
|
const useFormContext = () => {
|
|
80
122
|
return useContext(FormContext);
|
|
81
123
|
};
|
|
@@ -102,6 +144,34 @@ const FormBuilder = React.forwardRef(function FormBuilder(
|
|
|
102
144
|
}: FormBuilderProps,
|
|
103
145
|
ref: React.Ref<HTMLFormElement>
|
|
104
146
|
): React.ReactElement {
|
|
147
|
+
const { screenSize } = useScreenSize();
|
|
148
|
+
fields = fields.map((field) => {
|
|
149
|
+
const layout = field.layout;
|
|
150
|
+
if (!layout) {
|
|
151
|
+
return {
|
|
152
|
+
...field,
|
|
153
|
+
layout: DEFAULT_LAYOUT,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
...field,
|
|
158
|
+
layout: BREAKPOINTS.reduce(
|
|
159
|
+
(expandedLayout, breakpoint, breakpointIndex) => {
|
|
160
|
+
if (field?.layout?.[breakpoint]) {
|
|
161
|
+
expandedLayout[breakpoint] = field?.layout[breakpoint];
|
|
162
|
+
} else if (expandedLayout[BREAKPOINTS[breakpointIndex - 1]]) {
|
|
163
|
+
expandedLayout[breakpoint] =
|
|
164
|
+
expandedLayout[BREAKPOINTS[breakpointIndex - 1]];
|
|
165
|
+
} else {
|
|
166
|
+
expandedLayout[breakpoint] = 12;
|
|
167
|
+
}
|
|
168
|
+
return expandedLayout;
|
|
169
|
+
},
|
|
170
|
+
{}
|
|
171
|
+
),
|
|
172
|
+
};
|
|
173
|
+
});
|
|
174
|
+
|
|
105
175
|
const fieldsState = useRef(fields);
|
|
106
176
|
const setFieldsState = useCallback((newFields) => {
|
|
107
177
|
fieldsState.current = newFields;
|
|
@@ -137,12 +207,28 @@ const FormBuilder = React.forwardRef(function FormBuilder(
|
|
|
137
207
|
if (auto) {
|
|
138
208
|
if (fieldsets) {
|
|
139
209
|
fieldChildren = fieldsets.map((fieldset) => (
|
|
140
|
-
<
|
|
210
|
+
<FieldsetWithContext key={fieldset.key} name={fieldset.key} />
|
|
141
211
|
));
|
|
142
212
|
} else if (fields) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
)
|
|
213
|
+
const fieldsByLayouts = groupFieldsByLayout(fields);
|
|
214
|
+
const fieldsByLayoutSize = fieldsByLayouts[screenSize];
|
|
215
|
+
fieldChildren = fieldsByLayoutSize.map((item, index) => {
|
|
216
|
+
if (Array.isArray(item)) {
|
|
217
|
+
return (
|
|
218
|
+
<ErrorGroup
|
|
219
|
+
key={index}
|
|
220
|
+
screenSize={screenSize}
|
|
221
|
+
fieldOrder={item.map((field) => field.key)}
|
|
222
|
+
>
|
|
223
|
+
{item.map((f) => {
|
|
224
|
+
return <Field key={f.key} name={f.key} />;
|
|
225
|
+
})}
|
|
226
|
+
</ErrorGroup>
|
|
227
|
+
);
|
|
228
|
+
} else {
|
|
229
|
+
return <Field key={item.key} name={item.key} />;
|
|
230
|
+
}
|
|
231
|
+
});
|
|
146
232
|
}
|
|
147
233
|
}
|
|
148
234
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { test, expect } from '@playwright/experimental-ct-react';
|
|
3
|
+
|
|
4
|
+
import { AutoErrorGrouping } from '@digigov/form/FormBuilder/__stories__/AutoErrorGrouping';
|
|
5
|
+
import { ErrorGrouping } from '@digigov/form/FormBuilder/__stories__/ErrorGrouping';
|
|
6
|
+
|
|
7
|
+
test('renders the ErrorGrouping and click submit', async ({ mount, page }) => {
|
|
8
|
+
await mount(<ErrorGrouping />);
|
|
9
|
+
await page.evaluate(() => document.fonts.ready);
|
|
10
|
+
await page.click('text="Συνέχεια"');
|
|
11
|
+
|
|
12
|
+
const screenshot = await page.screenshot({
|
|
13
|
+
fullPage: true,
|
|
14
|
+
animations: 'disabled',
|
|
15
|
+
});
|
|
16
|
+
expect(screenshot).toMatchSnapshot();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('renders the AutoErrorGrouping and click submit', async ({
|
|
20
|
+
mount,
|
|
21
|
+
page,
|
|
22
|
+
}) => {
|
|
23
|
+
await mount(<AutoErrorGrouping />);
|
|
24
|
+
await page.evaluate(() => document.fonts.ready);
|
|
25
|
+
await page.click('text="Συνέχεια"');
|
|
26
|
+
|
|
27
|
+
const screenshot = await page.screenshot({
|
|
28
|
+
fullPage: true,
|
|
29
|
+
animations: 'disabled',
|
|
30
|
+
});
|
|
31
|
+
expect(screenshot).toMatchSnapshot();
|
|
32
|
+
});
|