@pareto-engineering/design-system 4.2.0 → 4.2.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/f/fields/EditorInput/EditorInput.js +4 -6
- package/dist/cjs/g/FormBuilder/FormBuilder.js +38 -5
- package/dist/cjs/g/FormBuilder/common/Builder/Builder.js +62 -25
- package/dist/cjs/g/FormBuilder/common/Builder/common/ActionsContainer/ActionsContainer.js +80 -0
- package/dist/cjs/g/FormBuilder/common/Builder/common/ActionsContainer/index.js +13 -0
- package/dist/cjs/g/FormBuilder/common/Builder/common/ActionsContainer/styles.scss +59 -0
- package/dist/cjs/g/FormBuilder/common/Builder/common/InputBuilder/InputBuilder.js +19 -45
- package/dist/cjs/g/FormBuilder/common/Builder/common/InputBuilder/styles.scss +0 -49
- package/dist/cjs/g/FormBuilder/common/Builder/common/Section/Section.js +14 -3
- package/dist/cjs/g/FormBuilder/common/Builder/common/Section/styles.scss +61 -0
- package/dist/cjs/g/FormBuilder/common/Builder/common/index.js +8 -1
- package/dist/cjs/g/FormBuilder/common/Builder/styles.scss +5 -48
- package/dist/cjs/g/FormBuilder/common/Renderer/Renderer.js +76 -33
- package/dist/cjs/g/FormBuilder/common/Renderer/common/Section/Section.js +8 -3
- package/dist/cjs/g/FormBuilder/common/Renderer/styles.scss +12 -1
- package/dist/es/f/fields/EditorInput/EditorInput.js +5 -7
- package/dist/es/g/FormBuilder/FormBuilder.js +38 -5
- package/dist/es/g/FormBuilder/common/Builder/Builder.js +56 -20
- package/dist/es/g/FormBuilder/common/Builder/common/ActionsContainer/ActionsContainer.js +70 -0
- package/dist/es/g/FormBuilder/common/Builder/common/ActionsContainer/index.js +2 -0
- package/dist/es/g/FormBuilder/common/Builder/common/ActionsContainer/styles.scss +59 -0
- package/dist/es/g/FormBuilder/common/Builder/common/InputBuilder/InputBuilder.js +21 -47
- package/dist/es/g/FormBuilder/common/Builder/common/InputBuilder/styles.scss +0 -49
- package/dist/es/g/FormBuilder/common/Builder/common/Section/Section.js +14 -3
- package/dist/es/g/FormBuilder/common/Builder/common/Section/styles.scss +61 -0
- package/dist/es/g/FormBuilder/common/Builder/common/index.js +2 -1
- package/dist/es/g/FormBuilder/common/Builder/styles.scss +5 -48
- package/dist/es/g/FormBuilder/common/Renderer/Renderer.js +76 -34
- package/dist/es/g/FormBuilder/common/Renderer/common/Section/Section.js +8 -3
- package/dist/es/g/FormBuilder/common/Renderer/styles.scss +12 -1
- package/package.json +3 -3
- package/src/stories/g/FormBuilder.stories.jsx +116 -21
- package/src/ui/f/fields/EditorInput/EditorInput.jsx +6 -8
- package/src/ui/g/FormBuilder/FormBuilder.jsx +50 -3
- package/src/ui/g/FormBuilder/common/Builder/Builder.jsx +56 -18
- package/src/ui/g/FormBuilder/common/Builder/common/ActionsContainer/ActionsContainer.jsx +97 -0
- package/src/ui/g/FormBuilder/common/Builder/common/ActionsContainer/index.js +2 -0
- package/src/ui/g/FormBuilder/common/Builder/common/ActionsContainer/styles.scss +59 -0
- package/src/ui/g/FormBuilder/common/Builder/common/InputBuilder/InputBuilder.jsx +40 -60
- package/src/ui/g/FormBuilder/common/Builder/common/InputBuilder/styles.scss +0 -49
- package/src/ui/g/FormBuilder/common/Builder/common/Section/Section.jsx +26 -4
- package/src/ui/g/FormBuilder/common/Builder/common/Section/styles.scss +61 -0
- package/src/ui/g/FormBuilder/common/Builder/common/index.js +1 -0
- package/src/ui/g/FormBuilder/common/Builder/styles.scss +5 -48
- package/src/ui/g/FormBuilder/common/Renderer/Renderer.jsx +84 -33
- package/src/ui/g/FormBuilder/common/Renderer/common/Section/Section.jsx +3 -2
- package/src/ui/g/FormBuilder/common/Renderer/styles.scss +12 -1
- package/tests/__snapshots__/Storyshots.test.js.snap +395 -133
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* @pareto-engineering/generator-front 1.1.1-alpha.2 */
|
|
2
2
|
import * as React from 'react'
|
|
3
3
|
|
|
4
|
-
import { useState
|
|
4
|
+
import { useState } from 'react'
|
|
5
5
|
|
|
6
6
|
import PropTypes from 'prop-types'
|
|
7
7
|
|
|
@@ -11,14 +11,14 @@ import styleNames from '@pareto-engineering/bem/exports'
|
|
|
11
11
|
|
|
12
12
|
import { SelectInput, TextInput } from 'ui/f'
|
|
13
13
|
|
|
14
|
-
import { ToggleSwitch
|
|
15
|
-
|
|
16
|
-
import { useOutsideClick } from 'ui/utils'
|
|
14
|
+
import { ToggleSwitch } from 'ui/a'
|
|
17
15
|
|
|
18
16
|
import './styles.scss'
|
|
19
17
|
|
|
20
18
|
// Local Definitions
|
|
21
19
|
|
|
20
|
+
import { ActionsContainer } from '../ActionsContainer'
|
|
21
|
+
|
|
22
22
|
const baseClassName = styleNames.base
|
|
23
23
|
|
|
24
24
|
const componentClassName = 'input-builder'
|
|
@@ -34,14 +34,13 @@ const InputBuilder = ({
|
|
|
34
34
|
inputIndex,
|
|
35
35
|
onDelete,
|
|
36
36
|
// onCopy,
|
|
37
|
-
preferredPopoverOrder,
|
|
38
37
|
// ...otherProps
|
|
39
38
|
}) => {
|
|
40
39
|
const { values, setFieldValue } = useFormikContext()
|
|
41
|
-
const input = values.sections[sectionIndex]
|
|
40
|
+
const input = values.sections[sectionIndex]?.inputs[inputIndex]
|
|
42
41
|
|
|
43
42
|
const handleToggleRequired = () => {
|
|
44
|
-
setFieldValue(`sections.${sectionIndex}.inputs.${inputIndex}.required`, !input
|
|
43
|
+
setFieldValue(`sections.${sectionIndex}.inputs.${inputIndex}.required`, !input?.required)
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
const toggleSwitchStyles = {
|
|
@@ -53,17 +52,15 @@ const InputBuilder = ({
|
|
|
53
52
|
),
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
const parentRef = useRef(null)
|
|
57
|
-
const [isOpen, setIsOpen] = useState(false)
|
|
58
|
-
useOutsideClick(parentRef, () => setIsOpen(false))
|
|
59
|
-
|
|
60
55
|
const [hasDescription, setHasDescription] = useState(false)
|
|
61
56
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
57
|
+
// TODO: handle nav logic
|
|
58
|
+
// const handleToggleConditionalNavigation = () => {
|
|
59
|
+
// setFieldValue(`sections.${sectionIndex}.inputs.${inputIndex}.conditionalNavigation`,
|
|
60
|
+
// !input?.conditionalNavigation)
|
|
61
|
+
// }
|
|
65
62
|
|
|
66
|
-
const availableSections = values
|
|
63
|
+
const availableSections = values?.sections?.map((section, idx) => ({
|
|
67
64
|
value:idx,
|
|
68
65
|
label:`Go to ${section.title || `Untitled Section ${idx + 1}`}`,
|
|
69
66
|
}))
|
|
@@ -116,10 +113,6 @@ const InputBuilder = ({
|
|
|
116
113
|
value:'select',
|
|
117
114
|
label:'Dropdown select',
|
|
118
115
|
},
|
|
119
|
-
{
|
|
120
|
-
value:'editor',
|
|
121
|
-
label:'Editor input',
|
|
122
|
-
},
|
|
123
116
|
{
|
|
124
117
|
value:'multiple',
|
|
125
118
|
label:'Checkboxes',
|
|
@@ -149,39 +142,32 @@ const InputBuilder = ({
|
|
|
149
142
|
inputId={`sections_${sectionIndex}_inputs.${inputIndex}_toggle`}
|
|
150
143
|
/>
|
|
151
144
|
</div>
|
|
152
|
-
<
|
|
153
|
-
<
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
isOpen={isOpen}
|
|
158
|
-
parentRef={parentRef}
|
|
159
|
-
preferredPrimaryOrder={preferredPopoverOrder}
|
|
160
|
-
>
|
|
161
|
-
<p>Show</p>
|
|
162
|
-
<ul>
|
|
163
|
-
<li className={`option ${hasDescription ? 'with-check-mark' : ''}`}>
|
|
164
|
-
{hasDescription
|
|
145
|
+
<ActionsContainer>
|
|
146
|
+
<p>Show</p>
|
|
147
|
+
<ul>
|
|
148
|
+
<li className={`option ${hasDescription ? 'with-check-mark' : ''}`}>
|
|
149
|
+
{hasDescription
|
|
165
150
|
&& <span className="x-ui-icons c-x icon">I</span>}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
151
|
+
<button
|
|
152
|
+
onClick={() => setHasDescription(!hasDescription)}
|
|
153
|
+
type="button"
|
|
154
|
+
>
|
|
155
|
+
Description
|
|
156
|
+
</button>
|
|
157
|
+
</li>
|
|
158
|
+
{/* TODO: implement logic */}
|
|
159
|
+
{/*
|
|
160
|
+
<li className={`option ${input?.conditionalNavigation ? 'with-check-mark' : ''}`}>
|
|
161
|
+
{input?.conditionalNavigation && <span className="x-ui-icons c-x icon">I</span>}
|
|
162
|
+
<button
|
|
163
|
+
onClick={handleToggleConditionalNavigation}
|
|
164
|
+
type="button"
|
|
165
|
+
>
|
|
166
|
+
Go to section based on answer
|
|
167
|
+
</button>
|
|
168
|
+
</li> */}
|
|
169
|
+
</ul>
|
|
170
|
+
</ActionsContainer>
|
|
185
171
|
</div>
|
|
186
172
|
</div>
|
|
187
173
|
<div className="input-name-description">
|
|
@@ -205,7 +191,7 @@ const InputBuilder = ({
|
|
|
205
191
|
<p>Options</p>
|
|
206
192
|
{input?.options.map((option, optionIndex) => (
|
|
207
193
|
<div key={option.key} className="option-container">
|
|
208
|
-
{input
|
|
194
|
+
{input?.type !== 'select' && <div className={`input-icon-sample ${input?.type}`} />}
|
|
209
195
|
|
|
210
196
|
<TextInput
|
|
211
197
|
name={`sections.${sectionIndex}.inputs.${inputIndex}.options.${optionIndex}.value`}
|
|
@@ -228,7 +214,7 @@ const InputBuilder = ({
|
|
|
228
214
|
className="add-option-cta"
|
|
229
215
|
type="button"
|
|
230
216
|
onClick={() => push({
|
|
231
|
-
key :`sections-${sectionIndex}-inputs-${inputIndex}-options-${input
|
|
217
|
+
key :`sections-${sectionIndex}-inputs-${inputIndex}-options-${input?.options.length - 1}`,
|
|
232
218
|
value :'',
|
|
233
219
|
label :'',
|
|
234
220
|
nextSection:'',
|
|
@@ -264,16 +250,10 @@ InputBuilder.propTypes = {
|
|
|
264
250
|
* The React-written, css properties for this element.
|
|
265
251
|
*/
|
|
266
252
|
style:PropTypes.objectOf(PropTypes.string),
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* The preferred primary order of the popover
|
|
270
|
-
*/
|
|
271
|
-
preferredPopoverOrder:PropTypes.arrayOf(PropTypes.string),
|
|
272
|
-
|
|
273
253
|
}
|
|
274
254
|
|
|
275
255
|
InputBuilder.defaultProps = {
|
|
276
|
-
|
|
256
|
+
// some properties
|
|
277
257
|
}
|
|
278
258
|
|
|
279
259
|
export default InputBuilder
|
|
@@ -54,55 +54,6 @@ $default-list-width: var(--action-button-width, 18rem);
|
|
|
54
54
|
font-size: calc(var(--s1) * 1rem);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
-
|
|
58
|
-
> .actions {
|
|
59
|
-
position: relative;
|
|
60
|
-
|
|
61
|
-
> button {
|
|
62
|
-
background-color: transparent;
|
|
63
|
-
border: 0;
|
|
64
|
-
cursor: pointer;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
> .#{bem.$base}.popover {
|
|
68
|
-
background-color: var(--background-cards);
|
|
69
|
-
border: var(--theme-default-border-style) var(--main2);
|
|
70
|
-
border-radius: calc(var(--theme-default-border-radius) / 2);
|
|
71
|
-
cursor: default;
|
|
72
|
-
padding: $default-padding;
|
|
73
|
-
width: $default-list-width;
|
|
74
|
-
|
|
75
|
-
> p {
|
|
76
|
-
margin: 0;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
> ul {
|
|
80
|
-
list-style: none;
|
|
81
|
-
margin: 0;
|
|
82
|
-
padding: 0;
|
|
83
|
-
white-space: nowrap;
|
|
84
|
-
|
|
85
|
-
> .option {
|
|
86
|
-
cursor: pointer;
|
|
87
|
-
padding-block: calc($default-padding / 4);
|
|
88
|
-
|
|
89
|
-
&:not(.with-check-mark) {
|
|
90
|
-
padding-inline: $default-padding;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
> button {
|
|
94
|
-
background: transparent;
|
|
95
|
-
border: 0;
|
|
96
|
-
color: var(--paragraph);
|
|
97
|
-
cursor: pointer;
|
|
98
|
-
font-size: calc(var(--s0) * .875rem);
|
|
99
|
-
text-align: left;
|
|
100
|
-
width: 100%;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
57
|
}
|
|
107
58
|
}
|
|
108
59
|
|
|
@@ -19,9 +19,13 @@ import { DragAndDrop } from 'ui/g'
|
|
|
19
19
|
|
|
20
20
|
import { InputBuilder } from '../InputBuilder'
|
|
21
21
|
|
|
22
|
+
import { ActionsContainer } from '../ActionsContainer'
|
|
23
|
+
|
|
24
|
+
import './styles.scss'
|
|
25
|
+
|
|
22
26
|
const baseClassName = styleNames.base
|
|
23
27
|
|
|
24
|
-
const componentClassName = 'section'
|
|
28
|
+
const componentClassName = 'builder-section'
|
|
25
29
|
|
|
26
30
|
/**
|
|
27
31
|
* This is the component description.
|
|
@@ -31,6 +35,7 @@ const Section = ({
|
|
|
31
35
|
className:userClassName,
|
|
32
36
|
style,
|
|
33
37
|
index,
|
|
38
|
+
handleDeleteSection,
|
|
34
39
|
// ...otherProps
|
|
35
40
|
}) => {
|
|
36
41
|
const { values, setFieldValue } = useFormikContext()
|
|
@@ -84,9 +89,26 @@ const Section = ({
|
|
|
84
89
|
.join(' ')}
|
|
85
90
|
style={style}
|
|
86
91
|
>
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
<div className="section-header">
|
|
93
|
+
<p className="h4">
|
|
94
|
+
{index === 0 ? 'Form title' : `Section ${index + 1}`}
|
|
95
|
+
</p>
|
|
96
|
+
{index !== 0 && (
|
|
97
|
+
<ActionsContainer>
|
|
98
|
+
<ul>
|
|
99
|
+
<li className="option">
|
|
100
|
+
<button
|
|
101
|
+
type="button"
|
|
102
|
+
className="remove-section-cta"
|
|
103
|
+
onClick={handleDeleteSection}
|
|
104
|
+
>
|
|
105
|
+
Delete Section
|
|
106
|
+
</button>
|
|
107
|
+
</li>
|
|
108
|
+
</ul>
|
|
109
|
+
</ActionsContainer>
|
|
110
|
+
)}
|
|
111
|
+
</div>
|
|
90
112
|
<TextInput
|
|
91
113
|
name={`sections.${index}.title`}
|
|
92
114
|
placeholder={`Title of the ${index === 0 ? 'form' : 'section'}`}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/* @pareto-engineering/generator-front 1.1.1-alpha.2 */
|
|
2
|
+
|
|
3
|
+
@use "@pareto-engineering/bem";
|
|
4
|
+
|
|
5
|
+
$default-margin: 1rem;
|
|
6
|
+
|
|
7
|
+
.#{bem.$base}.builder-section {
|
|
8
|
+
border-bottom: var(--theme-default-border-style) var(--on-background-inputs);
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
> .section-header {
|
|
12
|
+
align-items: center;
|
|
13
|
+
display: flex;
|
|
14
|
+
justify-content: space-between;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
> .#{bem.$base}.editor-input {
|
|
18
|
+
margin-top: $default-margin;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
> form {
|
|
22
|
+
/* stylelint-disable selector-max-compound-selectors -- required */
|
|
23
|
+
> .add-question-cta {
|
|
24
|
+
align-items: center;
|
|
25
|
+
background-color: transparent;
|
|
26
|
+
border: 0;
|
|
27
|
+
color: var(--hard-interactive);
|
|
28
|
+
cursor: pointer;
|
|
29
|
+
display: flex;
|
|
30
|
+
gap: var(--gap);
|
|
31
|
+
margin-block: calc($default-margin * 1.5);
|
|
32
|
+
margin-left: auto;
|
|
33
|
+
padding: 0;
|
|
34
|
+
transition: all .2s;
|
|
35
|
+
|
|
36
|
+
&:hover {
|
|
37
|
+
color: var(--interactive);
|
|
38
|
+
|
|
39
|
+
> .icon-container {
|
|
40
|
+
background-color: var(--interactive);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
> .icon-container {
|
|
45
|
+
align-items: center;
|
|
46
|
+
background-color: var(--hard-interactive);
|
|
47
|
+
border-radius: 50%;
|
|
48
|
+
display: flex;
|
|
49
|
+
height: 1.6rem;
|
|
50
|
+
justify-content: center;
|
|
51
|
+
width: 1.6rem;
|
|
52
|
+
|
|
53
|
+
> .icon {
|
|
54
|
+
color: var(--on-interactive);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
@@ -49,56 +49,13 @@ $default-margin: 1rem;
|
|
|
49
49
|
flex: .4;
|
|
50
50
|
gap: var(--gap);
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
/* stylelint-disable-next-line selector-max-compound-selectors -- required */
|
|
53
|
+
> span {
|
|
54
|
+
white-space: nowrap;
|
|
54
55
|
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
> .#{bem.$base}.section {
|
|
58
|
-
border-bottom: var(--theme-default-border-style) var(--on-background-inputs);
|
|
59
|
-
|
|
60
|
-
> .#{bem.$base}.editor-input {
|
|
61
|
-
margin-top: $default-margin;
|
|
62
|
-
}
|
|
63
56
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
> .add-question-cta {
|
|
67
|
-
align-items: center;
|
|
68
|
-
background-color: transparent;
|
|
69
|
-
border: 0;
|
|
70
|
-
color: var(--hard-interactive);
|
|
71
|
-
cursor: pointer;
|
|
72
|
-
display: flex;
|
|
73
|
-
gap: var(--gap);
|
|
74
|
-
margin-block: calc($default-margin * 1.5);
|
|
75
|
-
margin-left: auto;
|
|
76
|
-
padding: 0;
|
|
77
|
-
transition: all .2s;
|
|
78
|
-
|
|
79
|
-
/* stylelint-disable-next-line max-nesting-depth -- required */
|
|
80
|
-
&:hover {
|
|
81
|
-
color: var(--interactive);
|
|
82
|
-
|
|
83
|
-
/* stylelint-disable max-nesting-depth -- required */
|
|
84
|
-
> .icon-container {
|
|
85
|
-
background-color: var(--interactive);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
> .icon-container {
|
|
90
|
-
align-items: center;
|
|
91
|
-
background-color: var(--hard-interactive);
|
|
92
|
-
border-radius: 50%;
|
|
93
|
-
display: flex;
|
|
94
|
-
height: 1.6rem;
|
|
95
|
-
justify-content: center;
|
|
96
|
-
width: 1.6rem;
|
|
97
|
-
|
|
98
|
-
> .icon {
|
|
99
|
-
color: var(--on-interactive);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
57
|
+
> .#{bem.$base}.select-input {
|
|
58
|
+
flex: auto;
|
|
102
59
|
}
|
|
103
60
|
}
|
|
104
61
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* @pareto-engineering/generator-front 1.1.1-alpha.2 */
|
|
2
2
|
import * as React from 'react'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { useState, useEffect } from 'react'
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { Formik, Form } from 'formik'
|
|
7
7
|
|
|
8
8
|
import PropTypes from 'prop-types'
|
|
9
9
|
|
|
@@ -21,6 +21,30 @@ const baseClassName = styleNames.base
|
|
|
21
21
|
|
|
22
22
|
const componentClassName = 'renderer'
|
|
23
23
|
|
|
24
|
+
const reconstructFormDataWithValues = (formData, values) => {
|
|
25
|
+
const newData = { ...formData }
|
|
26
|
+
Object.keys(values).forEach((key) => {
|
|
27
|
+
const [sectionIdx, inputIdx] = key.match(/\d+/g).map(Number)
|
|
28
|
+
newData.sections[sectionIdx].inputs[inputIdx].value = values[key]
|
|
29
|
+
})
|
|
30
|
+
return newData
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const validate = (values) => {
|
|
34
|
+
const errors = {}
|
|
35
|
+
|
|
36
|
+
const hasAtLeastOneValue = Object.keys(values).some((valueKey) => {
|
|
37
|
+
const inputValue = values[valueKey]
|
|
38
|
+
return inputValue && inputValue !== ''
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
if (!hasAtLeastOneValue) {
|
|
42
|
+
errors.message = 'At least one input must have a value'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return errors
|
|
46
|
+
}
|
|
47
|
+
|
|
24
48
|
/**
|
|
25
49
|
* This is the component description.
|
|
26
50
|
*/
|
|
@@ -29,32 +53,41 @@ const Renderer = ({
|
|
|
29
53
|
className:userClassName,
|
|
30
54
|
style,
|
|
31
55
|
formData,
|
|
56
|
+
readOnly,
|
|
32
57
|
onSave,
|
|
58
|
+
onError,
|
|
59
|
+
shouldSubmit,
|
|
33
60
|
// ...otherProps
|
|
34
61
|
}) => {
|
|
35
62
|
const [currentSectionIndex, setCurrentSectionIndex] = useState(0)
|
|
36
63
|
const [sectionHistory, setSectionHistory] = useState([])
|
|
64
|
+
const [updatedFormData, setUpdatedFormData] = useState(formData)
|
|
37
65
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
setUpdatedFormData(formData)
|
|
68
|
+
}, [formData])
|
|
69
|
+
|
|
70
|
+
const initialValues = formData.sections.reduce((acc, section, sectionIndex) => {
|
|
71
|
+
section.inputs.forEach((input, inputIndex) => {
|
|
72
|
+
const inputName = `section_${sectionIndex}_input_${inputIndex}`
|
|
73
|
+
acc[inputName] = input.value || ''
|
|
41
74
|
})
|
|
42
75
|
return acc
|
|
43
76
|
}, {})
|
|
44
77
|
|
|
45
78
|
const handleSubmit = (values) => {
|
|
46
|
-
const currentSection =
|
|
79
|
+
const currentSection = updatedFormData.sections[currentSectionIndex]
|
|
47
80
|
// by default, 'next' section is the immediate section after the current one
|
|
48
81
|
let nextSectionIndex = currentSectionIndex + 1
|
|
49
82
|
|
|
50
83
|
// Any other value is either submit or an index to a section
|
|
51
84
|
if (currentSection.navigation.nextSection !== 'next') {
|
|
52
85
|
nextSectionIndex = currentSection.navigation.nextSection === 'submit'
|
|
53
|
-
?
|
|
86
|
+
? updatedFormData.sections.length // submit
|
|
54
87
|
: parseInt(currentSection.navigation.nextSection, 10) // go to specific section
|
|
55
88
|
}
|
|
56
89
|
|
|
57
|
-
if (nextSectionIndex >=
|
|
90
|
+
if (nextSectionIndex >= updatedFormData.sections.length) {
|
|
58
91
|
// submit
|
|
59
92
|
onSave(values)
|
|
60
93
|
} else {
|
|
@@ -75,6 +108,9 @@ const Renderer = ({
|
|
|
75
108
|
}
|
|
76
109
|
}
|
|
77
110
|
|
|
111
|
+
const isSubmit = currentSectionIndex === updatedFormData.sections.length - 1
|
|
112
|
+
|| updatedFormData.sections[currentSectionIndex].navigation.nextSection === 'submit'
|
|
113
|
+
|
|
78
114
|
return (
|
|
79
115
|
<div
|
|
80
116
|
id={id}
|
|
@@ -91,22 +127,46 @@ const Renderer = ({
|
|
|
91
127
|
<Formik
|
|
92
128
|
initialValues={initialValues}
|
|
93
129
|
onSubmit={handleSubmit}
|
|
130
|
+
validate={validate}
|
|
94
131
|
>
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
)
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
132
|
+
{({ values, errors }) => {
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
const formDataWithValues = reconstructFormDataWithValues(updatedFormData, values)
|
|
135
|
+
setUpdatedFormData(formDataWithValues)
|
|
136
|
+
onSave?.(formDataWithValues)
|
|
137
|
+
}, [values])
|
|
138
|
+
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
onError?.({ errors, values })
|
|
141
|
+
}, [errors, values])
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<Form>
|
|
145
|
+
{updatedFormData.sections.map((section, sectionIndex) => (
|
|
146
|
+
sectionIndex === currentSectionIndex && (
|
|
147
|
+
<Section key={`${section.title}`} {...section} readOnly={readOnly} />
|
|
148
|
+
)
|
|
149
|
+
))}
|
|
150
|
+
<div className="navigator-container">
|
|
151
|
+
<Button
|
|
152
|
+
color="interactive"
|
|
153
|
+
isGradient
|
|
154
|
+
isCompact
|
|
155
|
+
isGhost
|
|
156
|
+
onClick={handlePrevious}
|
|
157
|
+
disabled={sectionHistory.length === 0}
|
|
158
|
+
>
|
|
159
|
+
Previous
|
|
160
|
+
</Button>
|
|
161
|
+
{(!isSubmit || shouldSubmit) && (
|
|
162
|
+
<Button color="interactive" isGradient type="submit">
|
|
163
|
+
{isSubmit ? 'Submit' : 'Next'}
|
|
164
|
+
</Button>
|
|
165
|
+
)}
|
|
166
|
+
</div>
|
|
167
|
+
</Form>
|
|
168
|
+
)
|
|
169
|
+
}}
|
|
110
170
|
</Formik>
|
|
111
171
|
</div>
|
|
112
172
|
)
|
|
@@ -127,19 +187,10 @@ Renderer.propTypes = {
|
|
|
127
187
|
* The React-written, css properties for this element.
|
|
128
188
|
*/
|
|
129
189
|
style:PropTypes.objectOf(PropTypes.string),
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* The children JSX
|
|
133
|
-
*/
|
|
134
|
-
formData:PropTypes.shape({
|
|
135
|
-
formTitle :PropTypes.string.isRequired,
|
|
136
|
-
formDescription:PropTypes.string.isRequired,
|
|
137
|
-
sections :PropTypes.arrayOf(PropTypes.objectOf(PropTypes.any)),
|
|
138
|
-
}),
|
|
139
190
|
}
|
|
140
191
|
|
|
141
192
|
Renderer.defaultProps = {
|
|
142
|
-
// someProp:false
|
|
193
|
+
// someProp: false,
|
|
143
194
|
}
|
|
144
195
|
|
|
145
196
|
export default Renderer
|
|
@@ -25,6 +25,7 @@ const Section = ({
|
|
|
25
25
|
title,
|
|
26
26
|
description,
|
|
27
27
|
inputs,
|
|
28
|
+
readOnly,
|
|
28
29
|
// ...otherProps
|
|
29
30
|
}) => (
|
|
30
31
|
<div
|
|
@@ -40,13 +41,13 @@ const Section = ({
|
|
|
40
41
|
.join(' ')}
|
|
41
42
|
style={style}
|
|
42
43
|
>
|
|
43
|
-
<h3>{title}</
|
|
44
|
+
<p className="h3">{title}</p>
|
|
44
45
|
<ExpandableLexicalPreview
|
|
45
46
|
nodes={description}
|
|
46
47
|
name="instructions"
|
|
47
48
|
/>
|
|
48
49
|
{inputs.map((input) => (
|
|
49
|
-
<FormInput key={input.name} {...input} />
|
|
50
|
+
<FormInput key={input.name} {...input} disabled={readOnly} />
|
|
50
51
|
))}
|
|
51
52
|
</div>
|
|
52
53
|
)
|
|
@@ -6,10 +6,21 @@ $default-margin: 1rem;
|
|
|
6
6
|
|
|
7
7
|
.#{bem.$base}.renderer {
|
|
8
8
|
> form {
|
|
9
|
+
> .#{bem.$base}.section {
|
|
10
|
+
margin-bottom: calc($default-margin * 2);
|
|
11
|
+
|
|
12
|
+
> p {
|
|
13
|
+
margin: 0;
|
|
14
|
+
}
|
|
15
|
+
> .#{bem.$base}.expandable-lexical-preview {
|
|
16
|
+
margin-bottom: $default-margin;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
9
20
|
> .navigator-container {
|
|
10
21
|
display: flex;
|
|
11
22
|
gap: var(--gap);
|
|
12
|
-
|
|
23
|
+
justify-content: space-between;
|
|
13
24
|
}
|
|
14
25
|
}
|
|
15
26
|
}
|