@pareto-engineering/design-system 4.9.2 → 4.10.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/a/AreaChart/AreaChart.js +176 -0
- package/dist/cjs/a/AreaChart/index.js +13 -0
- package/dist/cjs/a/AreaChart/styles.scss +89 -0
- package/dist/cjs/a/XMLEditor/XMLEditor.js +1 -1
- package/dist/cjs/a/index.js +8 -1
- package/dist/cjs/b/Button/styles.scss +1 -0
- package/dist/cjs/g/FormBuilder/FormBuilder.js +3 -3
- package/dist/cjs/g/FormBuilder/common/Renderer/Renderer.js +38 -38
- package/dist/cjs/g/FormBuilder/common/Renderer/common/Section/Section.js +34 -8
- package/dist/es/a/AreaChart/AreaChart.js +163 -0
- package/dist/es/a/AreaChart/index.js +1 -0
- package/dist/es/a/AreaChart/styles.scss +89 -0
- package/dist/es/a/XMLEditor/XMLEditor.js +2 -2
- package/dist/es/a/index.js +2 -1
- package/dist/es/b/Button/styles.scss +1 -0
- package/dist/es/g/FormBuilder/FormBuilder.js +3 -3
- package/dist/es/g/FormBuilder/common/Renderer/Renderer.js +39 -39
- package/dist/es/g/FormBuilder/common/Renderer/common/Section/Section.js +35 -9
- package/package.json +4 -3
- package/src/stories/a/AreaChart.stories.jsx +118 -0
- package/src/ui/a/AreaChart/AreaChart.jsx +185 -0
- package/src/ui/a/AreaChart/index.js +1 -0
- package/src/ui/a/AreaChart/styles.scss +89 -0
- package/src/ui/a/XMLEditor/XMLEditor.jsx +4 -1
- package/src/ui/a/index.js +1 -0
- package/src/ui/b/Button/styles.scss +1 -0
- package/src/ui/g/FormBuilder/FormBuilder.jsx +2 -2
- package/src/ui/g/FormBuilder/common/Renderer/Renderer.jsx +40 -39
- package/src/ui/g/FormBuilder/common/Renderer/common/Section/Section.jsx +41 -10
- package/tests/__snapshots__/Storyshots.test.js.snap +501 -1
- package/tests/test-setup.js +11 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// front/packages/design-system/src/ui/a/AreaChart/AreaChart.jsx
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
|
|
5
|
+
import PropTypes from 'prop-types'
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
AreaChart as RechartsAreaChart,
|
|
9
|
+
Area,
|
|
10
|
+
XAxis,
|
|
11
|
+
YAxis,
|
|
12
|
+
CartesianGrid,
|
|
13
|
+
Tooltip,
|
|
14
|
+
ResponsiveContainer,
|
|
15
|
+
} from 'recharts'
|
|
16
|
+
|
|
17
|
+
import styleNames from '@pareto-engineering/bem/exports'
|
|
18
|
+
|
|
19
|
+
import './styles.scss'
|
|
20
|
+
|
|
21
|
+
// Local Definitions
|
|
22
|
+
|
|
23
|
+
const baseClassName = styleNames.base
|
|
24
|
+
|
|
25
|
+
const componentClassName = 'area-chart'
|
|
26
|
+
|
|
27
|
+
const AreaChart = ({
|
|
28
|
+
id,
|
|
29
|
+
className: userClassName,
|
|
30
|
+
data,
|
|
31
|
+
title,
|
|
32
|
+
xKey,
|
|
33
|
+
yKeys,
|
|
34
|
+
xLabel,
|
|
35
|
+
yLabel,
|
|
36
|
+
colors,
|
|
37
|
+
filled,
|
|
38
|
+
height,
|
|
39
|
+
width,
|
|
40
|
+
// ...otherProps
|
|
41
|
+
}) => {
|
|
42
|
+
const processedData = data.map((item) => {
|
|
43
|
+
const yValues = yKeys.map((key) => item[key])
|
|
44
|
+
const lowerBound = Math.min(...yValues)
|
|
45
|
+
const upperBound = Math.max(...yValues)
|
|
46
|
+
const margin = (upperBound - lowerBound) * 0.1
|
|
47
|
+
return {
|
|
48
|
+
...item,
|
|
49
|
+
bounds:[lowerBound - margin, upperBound + margin],
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const yAxisBounds = () => {
|
|
54
|
+
const yValues = data.map((item) => yKeys.map((key) => item[key]))
|
|
55
|
+
const min = Math.min(...yValues.flat())
|
|
56
|
+
const max = Math.max(...yValues.flat())
|
|
57
|
+
const margin = (max - min) * 0.1
|
|
58
|
+
return [min - margin, max + margin]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const CustomTooltipContent = ({ active, payload, label }) => {
|
|
62
|
+
if (active && payload && payload.length) {
|
|
63
|
+
const newPayload = payload.filter((item) => item.name !== 'bounds')
|
|
64
|
+
return (
|
|
65
|
+
<div className="custom-tooltip">
|
|
66
|
+
<p className="label">{`${xLabel}: ${label}`}</p>
|
|
67
|
+
{newPayload.map((entry) => (
|
|
68
|
+
<p className="label" key={`${entry.name}`} style={{ color: entry.color }}>
|
|
69
|
+
{`${entry.name}: ${entry.value}`}
|
|
70
|
+
</p>
|
|
71
|
+
))}
|
|
72
|
+
</div>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const CustomLegend = ({ colorsArray, yKeysArray }) => (
|
|
79
|
+
<div className="custom-legend">
|
|
80
|
+
{yKeysArray.map((key, index) => (
|
|
81
|
+
<div key={key} className="item">
|
|
82
|
+
<span
|
|
83
|
+
className="line"
|
|
84
|
+
style={{ backgroundColor: colorsArray[index] }}
|
|
85
|
+
/>
|
|
86
|
+
<span className="text">{key}</span>
|
|
87
|
+
</div>
|
|
88
|
+
))}
|
|
89
|
+
</div>
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<div
|
|
94
|
+
id={id}
|
|
95
|
+
className={[
|
|
96
|
+
baseClassName,
|
|
97
|
+
componentClassName,
|
|
98
|
+
userClassName,
|
|
99
|
+
]
|
|
100
|
+
.filter((e) => e)
|
|
101
|
+
.join(' ')}
|
|
102
|
+
>
|
|
103
|
+
<h3>{title}</h3>
|
|
104
|
+
<CustomLegend colorsArray={colors} yKeysArray={yKeys} />
|
|
105
|
+
<ResponsiveContainer width={width} height={height}>
|
|
106
|
+
<RechartsAreaChart data={processedData}>
|
|
107
|
+
<CartesianGrid strokeDasharray="3 3" />
|
|
108
|
+
<XAxis
|
|
109
|
+
dataKey={xKey}
|
|
110
|
+
label={{ value: xLabel, position: 'insideBottom', offset: -5 }} // Adjusted offset for padding
|
|
111
|
+
axisLine={false}
|
|
112
|
+
tickLine={false}
|
|
113
|
+
tickCount={3}
|
|
114
|
+
/>
|
|
115
|
+
<YAxis
|
|
116
|
+
domain={yAxisBounds}
|
|
117
|
+
label={{
|
|
118
|
+
value:yLabel, angle:-90, position:'insideLeft', offset:15,
|
|
119
|
+
}}
|
|
120
|
+
axisLine={false}
|
|
121
|
+
tickLine={false}
|
|
122
|
+
tickFormatter={(value) => value.toFixed(2)}
|
|
123
|
+
/>
|
|
124
|
+
<Tooltip content={<CustomTooltipContent />} />
|
|
125
|
+
{filled && (
|
|
126
|
+
<Area
|
|
127
|
+
id="bounds"
|
|
128
|
+
type="linear"
|
|
129
|
+
dataKey="bounds"
|
|
130
|
+
stroke="none"
|
|
131
|
+
fill="var(--hard-ui-main-2)"
|
|
132
|
+
fillOpacity={0.4}
|
|
133
|
+
activeDot={false}
|
|
134
|
+
dot={false}
|
|
135
|
+
label={false}
|
|
136
|
+
isAnimationActive={false}
|
|
137
|
+
/>
|
|
138
|
+
)}
|
|
139
|
+
{yKeys.map((key, index) => (
|
|
140
|
+
<Area
|
|
141
|
+
id={key}
|
|
142
|
+
key={key}
|
|
143
|
+
type="linear"
|
|
144
|
+
dataKey={key}
|
|
145
|
+
stroke={colors[index]}
|
|
146
|
+
fill="none"
|
|
147
|
+
connectNulls
|
|
148
|
+
dot={false}
|
|
149
|
+
activeDot={{ r: 4 }}
|
|
150
|
+
isAnimationActive={false}
|
|
151
|
+
/>
|
|
152
|
+
))}
|
|
153
|
+
</RechartsAreaChart>
|
|
154
|
+
</ResponsiveContainer>
|
|
155
|
+
</div>
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
AreaChart.propTypes = {
|
|
160
|
+
// eslint-disable-next-line react/forbid-prop-types
|
|
161
|
+
data :PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
162
|
+
title :PropTypes.string.isRequired,
|
|
163
|
+
xKey :PropTypes.string.isRequired,
|
|
164
|
+
yKeys :PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
165
|
+
xLabel:PropTypes.string,
|
|
166
|
+
yLabel:PropTypes.string,
|
|
167
|
+
colors:PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
168
|
+
filled:PropTypes.bool,
|
|
169
|
+
height:PropTypes.oneOfType([
|
|
170
|
+
PropTypes.string,
|
|
171
|
+
PropTypes.number,
|
|
172
|
+
]),
|
|
173
|
+
width:PropTypes.oneOfType([
|
|
174
|
+
PropTypes.string,
|
|
175
|
+
PropTypes.number,
|
|
176
|
+
]),
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
AreaChart.defaultProps = {
|
|
180
|
+
filled:false,
|
|
181
|
+
width :'100%',
|
|
182
|
+
height:300,
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export default AreaChart
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as AreaChart } from './AreaChart'
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
@use "@pareto-engineering/bem";
|
|
2
|
+
|
|
3
|
+
$default-margin: 1rem;
|
|
4
|
+
$default-padding: 1rem;
|
|
5
|
+
$default-box-shadow: 0 .25rem .75rem var(--ui-lines);
|
|
6
|
+
$default-text-font-size: calc(var(--s-1) * 1rem);
|
|
7
|
+
$default-border-radius: .25rem;
|
|
8
|
+
$default-legend-gap: .625rem;
|
|
9
|
+
$default-legend-padding: calc($default-padding * .125) calc($default-padding * .625);
|
|
10
|
+
$default-legend-line-width: 1.25rem;
|
|
11
|
+
$default-legend-line-height: .125rem;
|
|
12
|
+
$default-legend-line-margin-right: .3125rem;
|
|
13
|
+
$default-border-line-width: .0625rem;
|
|
14
|
+
|
|
15
|
+
.#{bem.$base} {
|
|
16
|
+
&.area-chart {
|
|
17
|
+
background-color: var(--background-far);
|
|
18
|
+
border-radius: var(--theme-default-border-radius);
|
|
19
|
+
box-shadow: $default-box-shadow;
|
|
20
|
+
margin: $default-margin 0;
|
|
21
|
+
padding: $default-padding;
|
|
22
|
+
|
|
23
|
+
h3 {
|
|
24
|
+
color: var(--subtitle);
|
|
25
|
+
margin: calc($default-margin / 5);
|
|
26
|
+
text-align: left;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.custom-legend {
|
|
30
|
+
display: flex;
|
|
31
|
+
gap: $default-legend-gap;
|
|
32
|
+
justify-content: flex-end;
|
|
33
|
+
padding-bottom: $default-padding;
|
|
34
|
+
padding-right: calc($default-padding * .25);
|
|
35
|
+
|
|
36
|
+
.item {
|
|
37
|
+
align-items: center;
|
|
38
|
+
border: $default-border-line-width solid var(--ui-lines);
|
|
39
|
+
border-radius: $default-border-radius;
|
|
40
|
+
display: flex;
|
|
41
|
+
padding: $default-legend-padding;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.line {
|
|
45
|
+
display: inline-block;
|
|
46
|
+
height: $default-legend-line-height;
|
|
47
|
+
margin-right: $default-legend-line-margin-right;
|
|
48
|
+
width: $default-legend-line-width;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.text {
|
|
52
|
+
color: var(--paragraph);
|
|
53
|
+
font-size: calc($default-text-font-size * .75);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.custom-tooltip {
|
|
58
|
+
background-color: var(--background-far);
|
|
59
|
+
border: $default-border-line-width solid var(--ui-lines);
|
|
60
|
+
border-radius: $default-border-radius;
|
|
61
|
+
padding: calc($default-padding * .25);
|
|
62
|
+
|
|
63
|
+
.label {
|
|
64
|
+
color: var(--hard-paragraph);
|
|
65
|
+
font-size: $default-text-font-size;
|
|
66
|
+
margin: calc($default-margin * .25);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* stylelint-disable selector-max-compound-selectors -- nested elements */
|
|
71
|
+
.recharts-wrapper {
|
|
72
|
+
.recharts-surface {
|
|
73
|
+
.recharts-cartesian-grid line {
|
|
74
|
+
stroke: var(--ui-lines);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.recharts-text {
|
|
78
|
+
fill: var(--soft-paragraph);
|
|
79
|
+
font-size: calc($default-text-font-size * .75);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.recharts-text.recharts-label {
|
|
83
|
+
fill: var(--paragraph);
|
|
84
|
+
font-size: $default-text-font-size;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -23,6 +23,8 @@ import {
|
|
|
23
23
|
import {
|
|
24
24
|
defaultKeymap,
|
|
25
25
|
indentWithTab,
|
|
26
|
+
history,
|
|
27
|
+
historyKeymap,
|
|
26
28
|
} from '@codemirror/commands'
|
|
27
29
|
|
|
28
30
|
import {
|
|
@@ -65,7 +67,7 @@ const XMLEditor = ({
|
|
|
65
67
|
const startState = EditorState.create({
|
|
66
68
|
doc :config,
|
|
67
69
|
extensions:[
|
|
68
|
-
keymap.of([defaultKeymap, indentWithTab]),
|
|
70
|
+
keymap.of([defaultKeymap, indentWithTab, ...historyKeymap]),
|
|
69
71
|
indentOnInput(),
|
|
70
72
|
lineNumbers(),
|
|
71
73
|
bracketMatching(),
|
|
@@ -78,6 +80,7 @@ const XMLEditor = ({
|
|
|
78
80
|
rectangularSelection(),
|
|
79
81
|
crosshairCursor(),
|
|
80
82
|
xml(),
|
|
83
|
+
history(),
|
|
81
84
|
theme,
|
|
82
85
|
EditorState.readOnly.of(readOnly),
|
|
83
86
|
EditorView.updateListener.of((view) => {
|
package/src/ui/a/index.js
CHANGED
|
@@ -29,8 +29,8 @@ const FormBuilder = ({
|
|
|
29
29
|
formBuilderId,
|
|
30
30
|
onBuilderFormSave,
|
|
31
31
|
onBuilderError,
|
|
32
|
-
onRendererError,
|
|
33
32
|
onRendererFormSave,
|
|
33
|
+
onFileUpload,
|
|
34
34
|
onBuilderValidate,
|
|
35
35
|
initialBuilderValues,
|
|
36
36
|
fileUploadStatus,
|
|
@@ -75,9 +75,9 @@ const FormBuilder = ({
|
|
|
75
75
|
onSave={onRendererFormSave}
|
|
76
76
|
readOnly={readOnly}
|
|
77
77
|
shouldSubmit={shouldSubmit}
|
|
78
|
-
onError={onRendererError}
|
|
79
78
|
fileUploadStatus={fileUploadStatus}
|
|
80
79
|
handleFileDelete={handleFileDelete}
|
|
80
|
+
onFileUpload={onFileUpload}
|
|
81
81
|
/>
|
|
82
82
|
)}
|
|
83
83
|
</div>
|
|
@@ -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, useEffect } from 'react'
|
|
4
|
+
import { useState, useEffect, useRef } from 'react'
|
|
5
5
|
|
|
6
6
|
import { Formik, Form } from 'formik'
|
|
7
7
|
|
|
@@ -21,31 +21,6 @@ const baseClassName = styleNames.base
|
|
|
21
21
|
|
|
22
22
|
const componentClassName = 'renderer'
|
|
23
23
|
|
|
24
|
-
const reconstructFormDataWithValues = (formData, values) => {
|
|
25
|
-
const valuesMap = {}
|
|
26
|
-
Object.keys(values).forEach(async (key) => {
|
|
27
|
-
if (key.includes('files')) {
|
|
28
|
-
const files = values[key].map((file) => (
|
|
29
|
-
file instanceof File ? URL.createObjectURL(file) : file
|
|
30
|
-
))
|
|
31
|
-
valuesMap[key] = files
|
|
32
|
-
} else {
|
|
33
|
-
valuesMap[key] = values[key]
|
|
34
|
-
}
|
|
35
|
-
})
|
|
36
|
-
const newData = {
|
|
37
|
-
...formData,
|
|
38
|
-
sections:formData.sections.map((section) => ({
|
|
39
|
-
...section,
|
|
40
|
-
inputs:section.inputs.map((input) => ({
|
|
41
|
-
...input,
|
|
42
|
-
value:valuesMap[input.name] !== undefined ? valuesMap[input.name] : input.value,
|
|
43
|
-
})),
|
|
44
|
-
})),
|
|
45
|
-
}
|
|
46
|
-
return newData
|
|
47
|
-
}
|
|
48
|
-
|
|
49
24
|
const validate = (currentSectionIndex, formData, values) => {
|
|
50
25
|
const errors = {}
|
|
51
26
|
|
|
@@ -68,6 +43,26 @@ const validate = (currentSectionIndex, formData, values) => {
|
|
|
68
43
|
return errors
|
|
69
44
|
}
|
|
70
45
|
|
|
46
|
+
const reconstructFormDataWithValues = (formData, values) => {
|
|
47
|
+
const valuesMap = {}
|
|
48
|
+
Object.keys(values).forEach(async (key) => {
|
|
49
|
+
if (!key.includes('files')) {
|
|
50
|
+
valuesMap[key] = values[key]
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
const newData = {
|
|
54
|
+
...formData,
|
|
55
|
+
sections:formData.sections.map((section) => ({
|
|
56
|
+
...section,
|
|
57
|
+
inputs:section.inputs.map((input) => ({
|
|
58
|
+
...input,
|
|
59
|
+
value:valuesMap[input.name] !== undefined ? valuesMap[input.name] : input.value,
|
|
60
|
+
})),
|
|
61
|
+
})),
|
|
62
|
+
}
|
|
63
|
+
return newData
|
|
64
|
+
}
|
|
65
|
+
|
|
71
66
|
/**
|
|
72
67
|
* This is the component description.
|
|
73
68
|
*/
|
|
@@ -78,15 +73,16 @@ const Renderer = ({
|
|
|
78
73
|
formData,
|
|
79
74
|
readOnly,
|
|
80
75
|
onSave,
|
|
81
|
-
onError,
|
|
82
76
|
shouldSubmit,
|
|
83
77
|
fileUploadStatus,
|
|
84
78
|
handleFileDelete,
|
|
79
|
+
onFileUpload,
|
|
80
|
+
shouldUpdateInputStateInRealTime = true,
|
|
85
81
|
// ...otherProps
|
|
86
82
|
}) => {
|
|
87
83
|
const [currentSectionIndex, setCurrentSectionIndex] = useState(0)
|
|
88
84
|
const [sectionHistory, setSectionHistory] = useState([])
|
|
89
|
-
const [updatedFormData, setUpdatedFormData] = useState(formData)
|
|
85
|
+
const [updatedFormData, setUpdatedFormData] = useState({ ...formData })
|
|
90
86
|
|
|
91
87
|
useEffect(() => {
|
|
92
88
|
setUpdatedFormData(formData)
|
|
@@ -135,6 +131,13 @@ const Renderer = ({
|
|
|
135
131
|
const isSubmit = currentSectionIndex === updatedFormData.sections.length - 1
|
|
136
132
|
|| updatedFormData.sections[currentSectionIndex].navigation.nextSection === 'submit'
|
|
137
133
|
|
|
134
|
+
const ref = useRef(null)
|
|
135
|
+
|
|
136
|
+
const currentSectionInputs = updatedFormData.sections[currentSectionIndex].inputs
|
|
137
|
+
const hasErrorsOnInitialRender = currentSectionInputs
|
|
138
|
+
.some((input) => (input.required && !ref.current?.values[input.name])
|
|
139
|
+
|| ref.current?.errors[input.name])
|
|
140
|
+
|
|
138
141
|
return (
|
|
139
142
|
<div
|
|
140
143
|
id={id}
|
|
@@ -151,21 +154,18 @@ const Renderer = ({
|
|
|
151
154
|
<Formik
|
|
152
155
|
initialValues={initialValues}
|
|
153
156
|
onSubmit={handleSubmit}
|
|
154
|
-
validate={(values) => validate(currentSectionIndex,
|
|
157
|
+
validate={(values) => validate(currentSectionIndex, updatedFormData, values)}
|
|
158
|
+
innerRef={ref}
|
|
155
159
|
>
|
|
156
160
|
{({ values, errors }) => {
|
|
157
161
|
useEffect(() => {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
162
|
+
if (shouldUpdateInputStateInRealTime) {
|
|
163
|
+
const formDataWithValues = reconstructFormDataWithValues(updatedFormData, values)
|
|
164
|
+
onSave?.(formDataWithValues)
|
|
165
|
+
}
|
|
161
166
|
}, [values])
|
|
162
167
|
|
|
163
|
-
useEffect(() => {
|
|
164
|
-
onError?.({ errors, values })
|
|
165
|
-
}, [errors, values])
|
|
166
|
-
|
|
167
168
|
const hasErrors = Object.keys(errors).length > 0
|
|
168
|
-
|
|
169
169
|
return (
|
|
170
170
|
<Form>
|
|
171
171
|
{updatedFormData.sections.map((section, sectionIndex) => (
|
|
@@ -174,9 +174,10 @@ const Renderer = ({
|
|
|
174
174
|
key={`${section.title}`}
|
|
175
175
|
{...section}
|
|
176
176
|
readOnly={readOnly}
|
|
177
|
-
setUpdatedFormData={setUpdatedFormData}
|
|
178
177
|
fileUploadStatus={fileUploadStatus}
|
|
179
178
|
handleFileDelete={handleFileDelete}
|
|
179
|
+
onFileUpload={onFileUpload}
|
|
180
|
+
sectionIndex={sectionIndex}
|
|
180
181
|
/>
|
|
181
182
|
)
|
|
182
183
|
))}
|
|
@@ -197,7 +198,7 @@ const Renderer = ({
|
|
|
197
198
|
)
|
|
198
199
|
}
|
|
199
200
|
{(!isSubmit || shouldSubmit) && (
|
|
200
|
-
<Button color="interactive" isGradient type="submit" disabled={hasErrors}>
|
|
201
|
+
<Button color="interactive" isGradient type="submit" disabled={hasErrors || hasErrorsOnInitialRender}>
|
|
201
202
|
{isSubmit ? 'Submit' : 'Next'}
|
|
202
203
|
</Button>
|
|
203
204
|
)}
|
|
@@ -6,7 +6,7 @@ import PropTypes from 'prop-types'
|
|
|
6
6
|
|
|
7
7
|
// Local Definitions
|
|
8
8
|
|
|
9
|
-
import { FormInput } from 'ui/f'
|
|
9
|
+
import { FormInput, getFileType } from 'ui/f'
|
|
10
10
|
|
|
11
11
|
import { ExpandableLexicalPreview } from 'ui/g'
|
|
12
12
|
|
|
@@ -16,6 +16,15 @@ const baseClassName = styleNames.base
|
|
|
16
16
|
|
|
17
17
|
const componentClassName = 'section'
|
|
18
18
|
|
|
19
|
+
const fileTypeMapper = {
|
|
20
|
+
VID :'Video',
|
|
21
|
+
TXT :'Text',
|
|
22
|
+
IMG :'Image',
|
|
23
|
+
PDF :'PDF',
|
|
24
|
+
AUD :'Audio',
|
|
25
|
+
FILE:'Generic',
|
|
26
|
+
}
|
|
27
|
+
|
|
19
28
|
/**
|
|
20
29
|
* This is the component description.
|
|
21
30
|
*/
|
|
@@ -29,6 +38,8 @@ const Section = ({
|
|
|
29
38
|
readOnly,
|
|
30
39
|
fileUploadStatus,
|
|
31
40
|
handleFileDelete,
|
|
41
|
+
onFileUpload,
|
|
42
|
+
sectionIndex,
|
|
32
43
|
// ...otherProps
|
|
33
44
|
}) => (
|
|
34
45
|
<div
|
|
@@ -49,15 +60,35 @@ const Section = ({
|
|
|
49
60
|
nodes={description}
|
|
50
61
|
name="instructions"
|
|
51
62
|
/>
|
|
52
|
-
{inputs?.map((input) =>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
{inputs?.map((input, inputIndex) => {
|
|
64
|
+
const isFileInput = input.type === 'file'
|
|
65
|
+
return (
|
|
66
|
+
<FormInput
|
|
67
|
+
key={input.name}
|
|
68
|
+
{...input}
|
|
69
|
+
disabled={readOnly}
|
|
70
|
+
{...(isFileInput && {
|
|
71
|
+
uploadStatus:fileUploadStatus,
|
|
72
|
+
handleFileDelete,
|
|
73
|
+
onChange :(files) => {
|
|
74
|
+
const filesToUpload = files
|
|
75
|
+
?.filter((file) => file instanceof File)
|
|
76
|
+
.map((file) => ({
|
|
77
|
+
file,
|
|
78
|
+
name :file.name,
|
|
79
|
+
mimeType:file.type,
|
|
80
|
+
type :fileTypeMapper[getFileType(file)] || 'Generic',
|
|
81
|
+
title :file.name,
|
|
82
|
+
sectionIndex,
|
|
83
|
+
inputIndex,
|
|
84
|
+
}))
|
|
85
|
+
|
|
86
|
+
onFileUpload(filesToUpload)
|
|
87
|
+
},
|
|
88
|
+
})}
|
|
89
|
+
/>
|
|
90
|
+
)
|
|
91
|
+
})}
|
|
61
92
|
</div>
|
|
62
93
|
)
|
|
63
94
|
|