@digigov/form 0.10.8 → 0.10.9

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 (138) hide show
  1. package/CHANGELOG.md +8 -1
  2. package/Form.stories.playwright.json +0 -72
  3. package/FormBuilder/FormBuilder.mdx +1 -1
  4. package/MultiplicityField/__stories__/PreviewDisplay.js +6 -6
  5. package/Questions/__stories__/Default.js +2 -2
  6. package/Questions/index.mdx +1 -1
  7. package/es/Form.stories.playwright.json +0 -72
  8. package/es/FormBuilder/FormBuilder.mdx +1 -1
  9. package/es/MultiplicityField/__stories__/PreviewDisplay.js +6 -6
  10. package/es/Questions/__stories__/Default.js +1 -1
  11. package/es/Questions/index.mdx +1 -1
  12. package/esm/Form.stories.playwright.json +0 -72
  13. package/esm/FormBuilder/FormBuilder.mdx +1 -1
  14. package/esm/MultiplicityField/__stories__/PreviewDisplay.js +6 -6
  15. package/esm/Questions/__stories__/Default.js +1 -1
  16. package/esm/Questions/index.mdx +1 -1
  17. package/esm/index.js +1 -1
  18. package/package.json +4 -4
  19. package/src/Field/FieldBase.tsx +99 -0
  20. package/src/Field/FieldBaseContainer.tsx +57 -0
  21. package/src/Field/FieldConditional.tsx +75 -0
  22. package/src/Field/index.mdx +6 -0
  23. package/src/Field/index.tsx +92 -0
  24. package/src/Field/types.tsx +102 -0
  25. package/src/Field/utils.ts +164 -0
  26. package/src/FieldArray/FieldArray.stories.js +8 -0
  27. package/src/FieldArray/FieldArray.stories.playwright.json +353 -0
  28. package/src/FieldArray/__stories__/Default.tsx +95 -0
  29. package/src/FieldArray/__stories__/WithExactLength.tsx +95 -0
  30. package/src/FieldArray/index.tsx +83 -0
  31. package/src/FieldObject/index.tsx +92 -0
  32. package/src/Fieldset/FieldsetWithContext.tsx +41 -0
  33. package/src/Fieldset/index.tsx +40 -0
  34. package/src/Fieldset/types.tsx +6 -0
  35. package/src/Form.stories.js +5 -0
  36. package/src/Form.stories.playwright.json +71 -0
  37. package/src/FormBuilder/FormBuilder.mdx +271 -0
  38. package/src/FormBuilder/FormBuilder.stories.js +7 -0
  39. package/src/FormBuilder/FormBuilder.stories.playwright.json +52 -0
  40. package/src/FormBuilder/FormBuilder.tsx +165 -0
  41. package/src/FormBuilder/__stories__/Default.tsx +23 -0
  42. package/src/FormBuilder/index.tsx +3 -0
  43. package/src/FormContext.tsx +8 -0
  44. package/src/MultiplicityField/MultiplicityField.mdx +580 -0
  45. package/src/MultiplicityField/MultiplicityField.stories.js +12 -0
  46. package/src/MultiplicityField/MultiplicityField.stories.playwright.json +1370 -0
  47. package/src/MultiplicityField/__stories__/Default.tsx +100 -0
  48. package/src/MultiplicityField/__stories__/PreviewDisplay.tsx +160 -0
  49. package/src/MultiplicityField/__stories__/WithExactLength.tsx +99 -0
  50. package/src/MultiplicityField/__stories__/WithMaxLength.tsx +102 -0
  51. package/src/MultiplicityField/__stories__/WithMinAndMaxLength.tsx +103 -0
  52. package/src/MultiplicityField/__stories__/WithMinLength.tsx +102 -0
  53. package/src/MultiplicityField/add-objects.tsx +186 -0
  54. package/src/MultiplicityField/index.tsx +166 -0
  55. package/src/Questions/Questions.stories.js +7 -0
  56. package/src/Questions/Questions.tsx +74 -0
  57. package/src/Questions/QuestionsContext.tsx +9 -0
  58. package/src/Questions/Step/ReviewStep.tsx +63 -0
  59. package/src/Questions/Step/Step.tsx +67 -0
  60. package/src/Questions/Step/StepArrayReview.tsx +68 -0
  61. package/src/Questions/Step/StepContext.tsx +21 -0
  62. package/src/Questions/Step/StepDescription.tsx +33 -0
  63. package/src/Questions/Step/StepForm.tsx +60 -0
  64. package/src/Questions/Step/StepQuote.tsx +8 -0
  65. package/src/Questions/Step/StepTitle.tsx +60 -0
  66. package/src/Questions/Step/getAddMoreFields.tsx +28 -0
  67. package/src/Questions/Step/index.ts +13 -0
  68. package/src/Questions/Step/types.tsx +32 -0
  69. package/src/Questions/__snapshots__/index.spec.tsx.snap +887 -0
  70. package/src/Questions/__stories__/Default.tsx +130 -0
  71. package/src/Questions/getNextStep.tsx +24 -0
  72. package/src/Questions/index.mdx +418 -0
  73. package/src/Questions/index.spec.tsx +72 -0
  74. package/src/Questions/index.tsx +5 -0
  75. package/src/Questions/types.tsx +25 -0
  76. package/src/__stories__/AutoCompleteField.tsx +45 -0
  77. package/src/__stories__/IntField.tsx +38 -0
  78. package/src/create-simple-form.mdx +518 -0
  79. package/src/index.mdx +44 -0
  80. package/src/index.ts +3 -0
  81. package/src/inputs/AutoComplete/index.tsx +85 -0
  82. package/src/inputs/Checkboxes/Checkboxes.stories.js +7 -0
  83. package/src/inputs/Checkboxes/Checkboxes.stories.playwright.json +69 -0
  84. package/src/inputs/Checkboxes/__stories__/Default.tsx +39 -0
  85. package/src/inputs/Checkboxes/index.mdx +0 -0
  86. package/src/inputs/Checkboxes/index.tsx +77 -0
  87. package/src/inputs/DateInput/DateInput.stories.js +7 -0
  88. package/src/inputs/DateInput/DateInput.stories.playwright.json +72 -0
  89. package/src/inputs/DateInput/__stories__/Default.tsx +41 -0
  90. package/src/inputs/DateInput/index.tsx +130 -0
  91. package/src/inputs/FileInput/FileInput.stories.js +7 -0
  92. package/src/inputs/FileInput/FileInput.stories.playwright.json +75 -0
  93. package/src/inputs/FileInput/__stories__/Default.tsx +23 -0
  94. package/src/inputs/FileInput/index.tsx +76 -0
  95. package/src/inputs/Input/Input.stories.js +14 -0
  96. package/src/inputs/Input/Input.stories.playwright.json +376 -0
  97. package/src/inputs/Input/__stories__/AFM.tsx +24 -0
  98. package/src/inputs/Input/__stories__/Boolean.tsx +26 -0
  99. package/src/inputs/Input/__stories__/Default.tsx +25 -0
  100. package/src/inputs/Input/__stories__/IBAN.tsx +26 -0
  101. package/src/inputs/Input/__stories__/Integer.tsx +25 -0
  102. package/src/inputs/Input/__stories__/MobilePhone.tsx +24 -0
  103. package/src/inputs/Input/__stories__/PhoneNumber.tsx +24 -0
  104. package/src/inputs/Input/__stories__/PostalCode.tsx +25 -0
  105. package/src/inputs/Input/index.mdx +8 -0
  106. package/src/inputs/Input/index.tsx +62 -0
  107. package/src/inputs/Label/Label.stories.js +7 -0
  108. package/src/inputs/Label/Label.stories.playwright.json +40 -0
  109. package/src/inputs/Label/__stories__/Default.tsx +28 -0
  110. package/src/inputs/Label/index.mdx +0 -0
  111. package/src/inputs/Label/index.tsx +44 -0
  112. package/src/inputs/Radio/Radio.stories.js +7 -0
  113. package/src/inputs/Radio/Radio.stories.playwright.json +57 -0
  114. package/src/inputs/Radio/__stories__/Default.tsx +42 -0
  115. package/src/inputs/Radio/index.mdx +0 -0
  116. package/src/inputs/Radio/index.tsx +57 -0
  117. package/src/inputs/Select/Select.stories.js +7 -0
  118. package/src/inputs/Select/Select.stories.playwright.json +22 -0
  119. package/src/inputs/Select/__stories__/Default.tsx +47 -0
  120. package/src/inputs/Select/index.tsx +37 -0
  121. package/src/inputs/index.ts +7 -0
  122. package/src/installation.mdx +72 -0
  123. package/src/internal.ts +34 -0
  124. package/src/registry.js +134 -0
  125. package/src/types.tsx +110 -0
  126. package/src/utils.ts +78 -0
  127. package/src/validators/index.ts +203 -0
  128. package/src/validators/types.ts +2 -0
  129. package/src/validators/utils/afm.ts +37 -0
  130. package/src/validators/utils/file.ts +25 -0
  131. package/src/validators/utils/iban.ts +119 -0
  132. package/src/validators/utils/index.ts +94 -0
  133. package/src/validators/utils/int.ts +22 -0
  134. package/src/validators/utils/number.ts +18 -0
  135. package/src/validators/utils/phone.ts +120 -0
  136. package/src/validators/utils/postal_code.ts +33 -0
  137. package/src/validators/utils/uuid4.ts +21 -0
  138. package/src/validators/validators.spec.ts +122 -0
@@ -0,0 +1,186 @@
1
+ /* eslint-disable react/prop-types */
2
+ import React, { useEffect, useMemo } from 'react';
3
+ // import { FormBuilderProps } from '@digigov/form';
4
+ import Button from '@digigov/ui/core/Button';
5
+ import Fieldset from '@digigov/react-core/Fieldset';
6
+ import RadioConditional from '@digigov/react-core/RadioConditional';
7
+ import {
8
+ ErrorOption,
9
+ FieldArrayWithId,
10
+ UseFieldArrayReturn,
11
+ useWatch,
12
+ } from 'react-hook-form';
13
+ import { FieldBase } from '@digigov/form/Field/FieldBase';
14
+ import { CalculatedField } from '@digigov/form/Field/types';
15
+ import { calculateField, useField } from '@digigov/form/Field/utils';
16
+ import FieldObject from '@digigov/form/FieldObject';
17
+ import {
18
+ MultiplicityProps,
19
+ StashedObject,
20
+ } from '@digigov/form/MultiplicityField';
21
+ import { FieldSpec } from '@digigov/form/types';
22
+ export interface AddObjectsProps
23
+ extends Omit<MultiplicityProps, 'key' | 'component'> {
24
+ fields: FieldArrayWithId[];
25
+ error?: ErrorOption;
26
+ append: UseFieldArrayReturn['append'];
27
+ remove: UseFieldArrayReturn['remove'];
28
+ stashedObjects: StashedObject[];
29
+ onStash: (arg0: StashedObject) => void;
30
+ }
31
+
32
+ export const AddObjects: React.FC<AddObjectsProps> = ({
33
+ name,
34
+ fields,
35
+ formState,
36
+ extra,
37
+ append,
38
+ remove,
39
+ trigger,
40
+ clearErrors,
41
+ children,
42
+ setValue,
43
+ stashedObjects,
44
+ getValues,
45
+ onStash,
46
+ error,
47
+ }) => {
48
+ const currentIndex = fields.length > 0 ? fields.length - 1 : fields.length;
49
+ const currentName = `${name}.${currentIndex}`;
50
+ const radioField: FieldSpec = {
51
+ key: 'needs-more',
52
+ type: 'choice:single',
53
+ label: {
54
+ primary: extra?.label.question.title,
55
+ },
56
+ required: true,
57
+ validators: [
58
+ {
59
+ name: 'is-locked',
60
+ message: 'form.error.needs-more',
61
+ test: (data) => {
62
+ return data === 'no';
63
+ },
64
+ },
65
+ ],
66
+ extra: {
67
+ options: [
68
+ {
69
+ label: { primary: extra?.label.question.yes },
70
+ value: 'yes',
71
+ selected: function RadioSelected() {
72
+ return (
73
+ <RadioConditional
74
+ hidden={
75
+ needsMore !== 'yes' ||
76
+ (extra?.max && extra?.max - stashedObjects.length === 0)
77
+ }
78
+ >
79
+ {needsMore === 'yes' && fields.length > 0 && (
80
+ <>
81
+ <FieldObject
82
+ name={currentName}
83
+ error={error && error[currentIndex]}
84
+ register={register}
85
+ formState={formState}
86
+ control={control}
87
+ {...extra?.of}
88
+ label={
89
+ extra?.label.question.objectLabel || extra?.of.label
90
+ }
91
+ />
92
+ <Button
93
+ color="secondary"
94
+ onClick={async (e) => {
95
+ e.preventDefault();
96
+ const nestedFields = extra?.of.extra.fields.map(
97
+ (f) => `${currentName}.${f.key}`
98
+ );
99
+ const result = await trigger(nestedFields);
100
+ result && setValue('needs-more', '');
101
+ result && onStash(getValues(currentName));
102
+ }}
103
+ >
104
+ {extra?.label.object.add}
105
+ </Button>
106
+ </>
107
+ )}
108
+ </RadioConditional>
109
+ );
110
+ },
111
+ },
112
+ {
113
+ label: { primary: extra?.label.question.no },
114
+ value: 'no',
115
+ },
116
+ ],
117
+ },
118
+ };
119
+
120
+ const { field, control, register, reset, error: needsMoreError } = useField(
121
+ radioField.key,
122
+ radioField?.type ? radioField : null
123
+ );
124
+
125
+ const calculatedField: CalculatedField = useMemo(
126
+ () => calculateField(children, field),
127
+ [field]
128
+ );
129
+ const needsMore = useWatch({ name: field.key, control });
130
+
131
+ useEffect(() => {
132
+ if (needsMore === 'yes') {
133
+ append({ afm: '', firstName: '', lastName: '' });
134
+ }
135
+ if (needsMore === 'no' && stashedObjects.length < fields.length) {
136
+ remove(currentIndex);
137
+ }
138
+ if (needsMore === '') clearErrors('needs-more');
139
+ }, [needsMore]);
140
+
141
+ // TODO: Refactor types so that label is always required, the following
142
+ // assignment is a temp fix for typescript build to run
143
+ calculatedField.label = calculatedField.label || {};
144
+ if (extra?.length) {
145
+ calculatedField.label.secondary = `Έχετε προσθέσει ${
146
+ stashedObjects.length
147
+ } εγγραφές και σας απομένουν ακόμα ${
148
+ extra.length - stashedObjects.length
149
+ } εγγραφές.`;
150
+ } else if (extra?.min && extra?.max) {
151
+ calculatedField.label.secondary = `Το πεδίο πρέπει να έχει ακόμα ${
152
+ extra?.min - stashedObjects.length
153
+ } και το μέγιστο ${
154
+ extra?.max
155
+ } εγγραφές. Προσθέστε μια ακόμα επιλέγοντας την απάντηση «Ναι» στην επόμενη ερώτηση.`;
156
+ } else if (extra?.min) {
157
+ calculatedField.label.secondary = `Το πεδίο πρέπει να έχει ακόμα ${
158
+ extra?.min - stashedObjects.length
159
+ } εγγραφές. Προσθέστε μια ακόμα επιλέγοντας την απάντηση «Ναι» στην επόμενη ερώτηση.`;
160
+ } else if (extra?.max) {
161
+ calculatedField.label.secondary = ` Έχετε προσθέσει ${
162
+ stashedObjects.length
163
+ } εγγραφές και σας απομένουν ${
164
+ extra.max - stashedObjects.length
165
+ } επιπλέον εγγραφές.`;
166
+ }
167
+ return (
168
+ <Fieldset>
169
+ <FieldBase
170
+ {...calculatedField}
171
+ name={calculatedField.key}
172
+ control={control}
173
+ register={register}
174
+ reset={reset}
175
+ error={
176
+ extra?.max - stashedObjects.length === 0 ||
177
+ extra?.length - stashedObjects.length === 0
178
+ ? error
179
+ : needsMoreError
180
+ }
181
+ />
182
+ </Fieldset>
183
+ );
184
+ };
185
+
186
+ export default AddObjects;
@@ -0,0 +1,166 @@
1
+ import React, { useState } from 'react';
2
+ import { ControlledFieldProps } from '@digigov/form/Field/types';
3
+ import {
4
+ Card,
5
+ CardText,
6
+ CardAction,
7
+ Button,
8
+ Heading,
9
+ CardHeading,
10
+ } from '@digigov/react-core';
11
+ import { FieldSpec } from '@digigov/form';
12
+ import AddObjects from '@digigov/form/MultiplicityField/add-objects';
13
+ import { ErrorOption, useFieldArray, UseFormReturn } from 'react-hook-form';
14
+ import FieldObject from '@digigov/form/FieldObject';
15
+ import FieldContainer from '@digigov/form/Field/FieldBaseContainer';
16
+
17
+ export interface MultiplicityProps
18
+ extends Omit<ControlledFieldProps, 'value' | 'onChange' | 'error' | 'extra'> {
19
+ name: string;
20
+ control: UseFormReturn['control'];
21
+ register: UseFormReturn['register'];
22
+ trigger: UseFormReturn['trigger'];
23
+ clearErrors: UseFormReturn['clearErrors'];
24
+ formState: UseFormReturn['formState'];
25
+ setValue: UseFormReturn['setValue'];
26
+ getValues: UseFormReturn['getValues'];
27
+ unregister: UseFormReturn['unregister'];
28
+ error?: ErrorOption;
29
+ extra?: Record<string, any>;
30
+ label?: FieldSpec['label'];
31
+ layout?: FieldSpec['layout'];
32
+ }
33
+
34
+ export type StashedObject = Record<string, any>;
35
+
36
+ // epeidh einai forwardRef to ref mas to dinei xwrista apo ta props
37
+ export const Multiplicity: React.FC<MultiplicityProps> = ({
38
+ extra,
39
+ name,
40
+ control,
41
+ error,
42
+ register,
43
+ trigger,
44
+ clearErrors,
45
+ formState,
46
+ label,
47
+ layout,
48
+ setValue,
49
+ getValues,
50
+ unregister,
51
+ }) => {
52
+ const { fields, append, remove } = useFieldArray({
53
+ control,
54
+ name,
55
+ });
56
+ const [stashedObjects, setStashedObjects] = useState<StashedObject[]>([]);
57
+ const handleStash = (data) => {
58
+ setStashedObjects([...stashedObjects, data]);
59
+ };
60
+ const handleDelete = (index) => {
61
+ remove(index);
62
+ // creating a new array to maintain immutability
63
+ const newStashedObjects = [...stashedObjects];
64
+ newStashedObjects.splice(index, 1);
65
+ setStashedObjects(newStashedObjects);
66
+ };
67
+ return (
68
+ <FieldContainer
69
+ label={label}
70
+ layout={layout}
71
+ error={
72
+ formState.isSubmitted && !formState.isSubmitting && error?.message
73
+ ? error
74
+ : undefined
75
+ }
76
+ wrapper="fieldset"
77
+ name={name}
78
+ >
79
+ {stashedObjects.length > 0 ? (
80
+ <div>
81
+ <Heading size="m">{extra?.label.object.title_added}</Heading>
82
+ {stashedObjects.map((_Object, index) => {
83
+ return (
84
+ <ListObject
85
+ key={`${name}.${index}`}
86
+ index={index}
87
+ name={name}
88
+ extra={extra}
89
+ control={control}
90
+ register={register}
91
+ formState={formState}
92
+ onDelete={handleDelete}
93
+ />
94
+ );
95
+ })}
96
+ </div>
97
+ ) : (
98
+ ''
99
+ )}
100
+ <AddObjects
101
+ name={name}
102
+ register={register}
103
+ control={control}
104
+ fields={fields}
105
+ formState={formState}
106
+ error={error}
107
+ stashedObjects={stashedObjects}
108
+ extra={extra}
109
+ append={append}
110
+ remove={remove}
111
+ onStash={handleStash}
112
+ setValue={setValue}
113
+ getValues={getValues}
114
+ unregister={unregister}
115
+ trigger={trigger}
116
+ clearErrors={clearErrors}
117
+ />
118
+ </FieldContainer>
119
+ );
120
+ };
121
+
122
+ export default Multiplicity;
123
+
124
+ export interface ListObjectProps {
125
+ index: number;
126
+ name: string;
127
+ control: UseFormReturn['control'];
128
+ register: UseFormReturn['register'];
129
+ formState: UseFormReturn['formState'];
130
+ onDelete: (index: number) => void;
131
+ extra?: Record<string, any>;
132
+ }
133
+
134
+ export const ListObject: React.FC<ListObjectProps> = ({
135
+ index,
136
+ name,
137
+ extra,
138
+ control,
139
+ register,
140
+ onDelete,
141
+ formState,
142
+ // onEdit,
143
+ }) => {
144
+ const currentName = `${name}.${index}`;
145
+ return (
146
+ <Card variant={extra?.border && 'border'}>
147
+ <CardHeading>
148
+ {extra?.label.object.title} #{index + 1}
149
+ </CardHeading>
150
+ <CardText>
151
+ <FieldObject
152
+ name={currentName}
153
+ formState={formState}
154
+ register={register}
155
+ control={control}
156
+ {...extra?.of}
157
+ />
158
+ </CardText>
159
+ <CardAction>
160
+ <Button type="button" variant="link" onClick={() => onDelete(index)}>
161
+ {extra?.label.object.delete}
162
+ </Button>
163
+ </CardAction>
164
+ </Card>
165
+ );
166
+ };
@@ -0,0 +1,7 @@
1
+ import Questions from '@digigov/form/Questions';
2
+ export default {
3
+ title: 'Digigov Form/Questions',
4
+ component: Questions,
5
+ displayName: 'Questions'
6
+ };
7
+ export * from '@digigov/form/Questions/__stories__/Default';
@@ -0,0 +1,74 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { StepInterface } from '@digigov/form/Questions/Step/types';
3
+ import { QuestionsInterface } from '@digigov/form/Questions/types';
4
+ import { QuestionsContext } from '@digigov/form/Questions/QuestionsContext';
5
+ import { getNextStep } from '@digigov/form/Questions/getNextStep';
6
+
7
+ const isBrowser = typeof window !== 'undefined';
8
+
9
+ /**
10
+ * The Question component accepts question data as props
11
+ * uses composable components to provide a wholesome UX
12
+ *
13
+ */
14
+ export const Questions: React.FC<QuestionsInterface> = ({
15
+ name,
16
+ steps,
17
+ initialData = {},
18
+ onChange,
19
+ onSubmit,
20
+ onActiveStep,
21
+ forceStepName,
22
+ localDraft = false,
23
+ children,
24
+ }) => {
25
+ const [currentStep, setCurrentStep] = useState<StepInterface>({ name: '' });
26
+ useEffect(() => {
27
+ if (!forceStepName) {
28
+ setCurrentStep(steps[0]);
29
+ }
30
+ }, [forceStepName, steps]);
31
+ useEffect(() => {
32
+ if (forceStepName !== currentStep.name) {
33
+ const forceStep = steps.find(({ name }) => name === forceStepName);
34
+ forceStep && setCurrentStep(forceStep);
35
+ }
36
+ }, [forceStepName]);
37
+ const localData =
38
+ isBrowser && localDraft && window.localStorage.getItem(`questions-${name}`);
39
+ const [data, setData] = useState(
40
+ (localData && JSON.parse(localData)) || initialData
41
+ );
42
+ const submitStep = (stepName, stepData): void => {
43
+ data[stepName] = stepData;
44
+ if (localDraft) {
45
+ isBrowser &&
46
+ window.localStorage.setItem(`questions-${name}`, JSON.stringify(data));
47
+ }
48
+ if (onChange) {
49
+ onChange && onChange(data);
50
+ }
51
+ setData(data);
52
+ const nextStep = getNextStep(currentStep, steps, data);
53
+ if (nextStep) {
54
+ onActiveStep && onActiveStep(nextStep);
55
+ setCurrentStep(nextStep);
56
+ } else {
57
+ onSubmit(data);
58
+ }
59
+ };
60
+
61
+ return (
62
+ <QuestionsContext.Provider
63
+ value={{
64
+ steps,
65
+ currentStep,
66
+ submitStep,
67
+ data,
68
+ onActiveStep,
69
+ }}
70
+ >
71
+ {children}
72
+ </QuestionsContext.Provider>
73
+ );
74
+ };
@@ -0,0 +1,9 @@
1
+ import { createContext } from 'react';
2
+ import { QuestionsContextInterface } from '@digigov/form/Questions/types';
3
+
4
+ const defaultStep = { name: '' };
5
+ export const QuestionsContext = createContext<QuestionsContextInterface>({
6
+ currentStep: defaultStep,
7
+ steps: [],
8
+ submitStep: () => null,
9
+ });
@@ -0,0 +1,63 @@
1
+ import React, { useContext } from 'react';
2
+ import {
3
+ SummaryList,
4
+ SummaryListItem,
5
+ SummaryListItemKey,
6
+ SummaryListItemValue,
7
+ SummaryListItemAction,
8
+ } from '@digigov/ui/core/SummaryList';
9
+ import Title from '@digigov/ui/typography/Title';
10
+ import { Button } from '@digigov/ui/core';
11
+ import { QuestionsContext } from '@digigov/form/Questions/QuestionsContext';
12
+ import { StepTitleBase } from '@digigov/form/Questions/Step/StepTitle';
13
+ import { StepContext } from '@digigov/form/Questions/Step/StepContext';
14
+ import { useTranslation } from '@digigov/ui/app/i18n';
15
+
16
+ export const ReviewStep = (): React.ReactElement => {
17
+ const { t } = useTranslation();
18
+ const { submitStep, title } = useContext(StepContext);
19
+ const { data, steps, onActiveStep } = useContext(QuestionsContext);
20
+ const filledSteps = steps.filter((step) => !!data[step.name]);
21
+ return (
22
+ <>
23
+ <StepTitleBase title={title || 'Review answers'}></StepTitleBase>
24
+ {filledSteps.map((step) => {
25
+ return (
26
+ <div key={step.name}>
27
+ <Title size={'md'}>{step.title}</Title>
28
+ <SummaryList>
29
+ {Object.keys(data[step.name]).map((dataKey) => {
30
+ return (
31
+ <SummaryListItem key={dataKey}>
32
+ <SummaryListItemKey>
33
+ {
34
+ step.fields.find(({ key }) => key === dataKey).label
35
+ .primary
36
+ }
37
+ </SummaryListItemKey>
38
+ <SummaryListItemValue>
39
+ {data[step.name][dataKey]}
40
+ </SummaryListItemValue>
41
+ <SummaryListItemAction>
42
+ <a
43
+ style={{ cursor: 'pointer' }}
44
+ onClick={(): void => onActiveStep && onActiveStep(step)}
45
+ >
46
+ {t('button.edit')}
47
+ </a>
48
+ </SummaryListItemAction>
49
+ </SummaryListItem>
50
+ );
51
+ })}
52
+ </SummaryList>
53
+ </div>
54
+ );
55
+ })}
56
+ <Button onClick={(): void => submitStep && submitStep()}>
57
+ {t('button.submit')}
58
+ </Button>
59
+ </>
60
+ );
61
+ };
62
+
63
+ export default ReviewStep;
@@ -0,0 +1,67 @@
1
+ import React, { useContext, useState } from 'react';
2
+ import { QuestionsContext } from '@digigov/form/Questions/QuestionsContext';
3
+ import { StepContext } from '@digigov/form/Questions/Step/StepContext';
4
+ import { StepArrayReview } from '@digigov/form/Questions/Step/StepArrayReview';
5
+ import { StepInterface } from '@digigov/form/Questions/Step/types';
6
+
7
+ /**
8
+ * The Step component accepts Step data as props
9
+ * uses composable components to provide a wholesome UX
10
+ *
11
+ */
12
+ export const Step: React.FC<StepInterface> = (props) => {
13
+ // or return all Questions and currentStepName
14
+ // or return a specific Step object
15
+ const { currentStep, submitStep, data } = useContext(QuestionsContext);
16
+
17
+ const [stepData, setStepData] = useState<Record<string, any>[]>([]);
18
+ const [reviewActive, setReviewActive] = useState(false);
19
+
20
+ const handleArraySubmit = (_name: string, data: any): void => {
21
+ setStepData([...stepData, data]);
22
+ setReviewActive(true);
23
+ };
24
+
25
+ const handleArrayDeleteItem = (deleteIndex: number): void => {
26
+ const nextStepData = stepData.filter((_item, stepIndex) => {
27
+ return stepIndex !== deleteIndex;
28
+ });
29
+ setStepData(nextStepData);
30
+ if (nextStepData.length === 0) {
31
+ setReviewActive(false);
32
+ }
33
+ };
34
+
35
+ const handleArrayReviewStep = (data): void => {
36
+ if (data.addMore === 'yes') {
37
+ setReviewActive(false);
38
+ } else if (data.addMore === 'no') {
39
+ submitStep(currentStep.name, stepData);
40
+ }
41
+ };
42
+ if (!currentStep || props.name !== currentStep.name) return null;
43
+
44
+ // then provide the currentStep object
45
+ return (
46
+ <StepContext.Provider
47
+ value={{
48
+ ...currentStep,
49
+ submitStep:
50
+ currentStep.type === 'array' ? handleArraySubmit : submitStep,
51
+ initial: data[props.name],
52
+ }}
53
+ >
54
+ {reviewActive ? (
55
+ <StepArrayReview
56
+ array={stepData}
57
+ handleSubmit={handleArrayReviewStep}
58
+ handleDelete={handleArrayDeleteItem}
59
+ />
60
+ ) : (
61
+ props.children
62
+ )}
63
+ </StepContext.Provider>
64
+ );
65
+ };
66
+
67
+ export default Step;
@@ -0,0 +1,68 @@
1
+ import React, { useContext } from 'react';
2
+ import { QuestionsContext } from '@digigov/form/Questions/QuestionsContext';
3
+ import PageTitle, { PageTitleHeading } from '@digigov/ui/app/PageTitle';
4
+ import Button from '@digigov/ui/core/Button';
5
+ import FormBuilder from '@digigov/form/FormBuilder';
6
+ import { FieldSpec } from '@digigov/form/types';
7
+ import { Field } from '@digigov/form/Field';
8
+ import { Fieldset } from '@digigov/form/Fieldset';
9
+ import Label from '@digigov/form/inputs/Label';
10
+ import {
11
+ SummaryList,
12
+ SummaryListItem,
13
+ SummaryListItemKey,
14
+ SummaryListItemValue,
15
+ SummaryListItemAction,
16
+ } from '@digigov/ui/core/SummaryList';
17
+ import { useTranslation } from '@digigov/ui/app/i18n';
18
+ import { StepArrayReviewInterface } from '@digigov/form/Questions/Step/types';
19
+ import { getAddMoreFields } from '@digigov/form/Questions/Step/getAddMoreFields';
20
+
21
+ export const StepArrayReview: React.FC<StepArrayReviewInterface> = (props) => {
22
+ const { t } = useTranslation();
23
+ const { currentStep } = useContext(QuestionsContext);
24
+ const fields = getAddMoreFields(currentStep);
25
+ const primaryField =
26
+ currentStep.fields.find(
27
+ (field: { key: any }) => field.key === currentStep.review?.primaryFieldKey
28
+ ) || {};
29
+ return (
30
+ <>
31
+ <PageTitle>
32
+ <PageTitleHeading>
33
+ {currentStep.review?.title && t(currentStep.review?.title)}
34
+ </PageTitleHeading>
35
+ </PageTitle>
36
+ <SummaryList>
37
+ {props.array.map(
38
+ (item: { [x: string]: React.ReactNode }, idx: number) => (
39
+ <SummaryListItem key={idx}>
40
+ <SummaryListItemKey>
41
+ {t(primaryField.label.primary)}
42
+ </SummaryListItemKey>
43
+ <SummaryListItemValue>
44
+ {item[primaryField.key]}
45
+ </SummaryListItemValue>
46
+ <SummaryListItemAction
47
+ onClick={(): void => props.handleDelete(idx)}
48
+ >
49
+ {t('button.delete')}
50
+ </SummaryListItemAction>
51
+ </SummaryListItem>
52
+ )
53
+ )}
54
+ </SummaryList>
55
+ <FormBuilder key="addmore" fields={fields} onSubmit={props.handleSubmit}>
56
+ <Fieldset>
57
+ <Label label={currentStep.review?.addMore.title} />
58
+ {fields.map((field: FieldSpec) => (
59
+ <Field key={field.key} name={field.key} />
60
+ ))}
61
+ </Fieldset>
62
+ <Button type="submit">{t('button.submit')}</Button>
63
+ </FormBuilder>
64
+ </>
65
+ );
66
+ };
67
+
68
+ export default StepArrayReview;
@@ -0,0 +1,21 @@
1
+ import { createContext } from 'react';
2
+ import { StepInterface } from '@digigov/form/Questions/Step/types';
3
+
4
+ export const StepContext = createContext<StepInterface>({
5
+ name: '',
6
+ fields: [],
7
+ initial: [],
8
+ errorLabels: [],
9
+ submitStep: '',
10
+ review: {
11
+ title: '',
12
+ primaryFieldKey: '',
13
+ addMore: {
14
+ title: { primary: '' },
15
+ answers: { positive: { primary: 'Yes' }, negative: { primary: 'No' } },
16
+ },
17
+ },
18
+ type: 'object',
19
+ });
20
+
21
+ export default StepContext;