@pareto-engineering/design-system 4.9.3 → 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.
@@ -124,6 +124,7 @@ $default-animation-time: .31s;
124
124
  }
125
125
 
126
126
  &.#{bem.$modifier-simple} {
127
+ --stroke-color: var(--x, var(--#{$default-color}));
127
128
  background: transparent;
128
129
  border: 1px solid transparent;
129
130
  color: var(--x, var(--#{$default-color}));
@@ -34,8 +34,8 @@ const FormBuilder = _ref => {
34
34
  formBuilderId,
35
35
  onBuilderFormSave,
36
36
  onBuilderError,
37
- onRendererError,
38
37
  onRendererFormSave,
38
+ onFileUpload,
39
39
  onBuilderValidate,
40
40
  initialBuilderValues,
41
41
  fileUploadStatus,
@@ -62,9 +62,9 @@ const FormBuilder = _ref => {
62
62
  onSave: onRendererFormSave,
63
63
  readOnly: readOnly,
64
64
  shouldSubmit: shouldSubmit,
65
- onError: onRendererError,
66
65
  fileUploadStatus: fileUploadStatus,
67
- handleFileDelete: handleFileDelete
66
+ handleFileDelete: handleFileDelete,
67
+ onFileUpload: onFileUpload
68
68
  }));
69
69
  };
70
70
  FormBuilder.propTypes = {
@@ -18,28 +18,6 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
18
18
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } /* @pareto-engineering/generator-front 1.1.1-alpha.2 */ // Local Definitions
19
19
  const baseClassName = _exports.default.base;
20
20
  const componentClassName = 'renderer';
21
- const reconstructFormDataWithValues = (formData, values) => {
22
- const valuesMap = {};
23
- Object.keys(values).forEach(async key => {
24
- if (key.includes('files')) {
25
- const files = values[key].map(file => file instanceof File ? URL.createObjectURL(file) : file);
26
- valuesMap[key] = files;
27
- } else {
28
- valuesMap[key] = values[key];
29
- }
30
- });
31
- const newData = {
32
- ...formData,
33
- sections: formData.sections.map(section => ({
34
- ...section,
35
- inputs: section.inputs.map(input => ({
36
- ...input,
37
- value: valuesMap[input.name] !== undefined ? valuesMap[input.name] : input.value
38
- }))
39
- }))
40
- };
41
- return newData;
42
- };
43
21
  const validate = (currentSectionIndex, formData, values) => {
44
22
  const errors = {};
45
23
  const hasAtLeastOneValue = Object.keys(values).some(valueKey => {
@@ -58,6 +36,25 @@ const validate = (currentSectionIndex, formData, values) => {
58
36
  });
59
37
  return errors;
60
38
  };
39
+ const reconstructFormDataWithValues = (formData, values) => {
40
+ const valuesMap = {};
41
+ Object.keys(values).forEach(async key => {
42
+ if (!key.includes('files')) {
43
+ valuesMap[key] = values[key];
44
+ }
45
+ });
46
+ const newData = {
47
+ ...formData,
48
+ sections: formData.sections.map(section => ({
49
+ ...section,
50
+ inputs: section.inputs.map(input => ({
51
+ ...input,
52
+ value: valuesMap[input.name] !== undefined ? valuesMap[input.name] : input.value
53
+ }))
54
+ }))
55
+ };
56
+ return newData;
57
+ };
61
58
 
62
59
  /**
63
60
  * This is the component description.
@@ -70,15 +67,18 @@ const Renderer = _ref => {
70
67
  formData,
71
68
  readOnly,
72
69
  onSave,
73
- onError,
74
70
  shouldSubmit,
75
71
  fileUploadStatus,
76
- handleFileDelete
72
+ handleFileDelete,
73
+ onFileUpload,
74
+ shouldUpdateInputStateInRealTime = true
77
75
  // ...otherProps
78
76
  } = _ref;
79
77
  const [currentSectionIndex, setCurrentSectionIndex] = (0, _react.useState)(0);
80
78
  const [sectionHistory, setSectionHistory] = (0, _react.useState)([]);
81
- const [updatedFormData, setUpdatedFormData] = (0, _react.useState)(formData);
79
+ const [updatedFormData, setUpdatedFormData] = (0, _react.useState)({
80
+ ...formData
81
+ });
82
82
  (0, _react.useEffect)(() => {
83
83
  setUpdatedFormData(formData);
84
84
  }, [formData]);
@@ -118,6 +118,9 @@ const Renderer = _ref => {
118
118
  }
119
119
  };
120
120
  const isSubmit = currentSectionIndex === updatedFormData.sections.length - 1 || updatedFormData.sections[currentSectionIndex].navigation.nextSection === 'submit';
121
+ const ref = (0, _react.useRef)(null);
122
+ const currentSectionInputs = updatedFormData.sections[currentSectionIndex].inputs;
123
+ const hasErrorsOnInitialRender = currentSectionInputs.some(input => input.required && !ref.current?.values[input.name] || ref.current?.errors[input.name]);
121
124
  return /*#__PURE__*/React.createElement("div", {
122
125
  id: id,
123
126
  className: [baseClassName, componentClassName, userClassName].filter(e => e).join(' '),
@@ -125,31 +128,28 @@ const Renderer = _ref => {
125
128
  }, /*#__PURE__*/React.createElement(_formik.Formik, {
126
129
  initialValues: initialValues,
127
130
  onSubmit: handleSubmit,
128
- validate: values => validate(currentSectionIndex, formData, values)
131
+ validate: values => validate(currentSectionIndex, updatedFormData, values),
132
+ innerRef: ref
129
133
  }, _ref2 => {
130
134
  let {
131
135
  values,
132
136
  errors
133
137
  } = _ref2;
134
138
  (0, _react.useEffect)(() => {
135
- const formDataWithValues = reconstructFormDataWithValues(updatedFormData, values);
136
- setUpdatedFormData(formDataWithValues);
137
- onSave?.(formDataWithValues);
139
+ if (shouldUpdateInputStateInRealTime) {
140
+ const formDataWithValues = reconstructFormDataWithValues(updatedFormData, values);
141
+ onSave?.(formDataWithValues);
142
+ }
138
143
  }, [values]);
139
- (0, _react.useEffect)(() => {
140
- onError?.({
141
- errors,
142
- values
143
- });
144
- }, [errors, values]);
145
144
  const hasErrors = Object.keys(errors).length > 0;
146
145
  return /*#__PURE__*/React.createElement(_formik.Form, null, updatedFormData.sections.map((section, sectionIndex) => sectionIndex === currentSectionIndex && /*#__PURE__*/React.createElement(_common.Section, _extends({
147
146
  key: `${section.title}`
148
147
  }, section, {
149
148
  readOnly: readOnly,
150
- setUpdatedFormData: setUpdatedFormData,
151
149
  fileUploadStatus: fileUploadStatus,
152
- handleFileDelete: handleFileDelete
150
+ handleFileDelete: handleFileDelete,
151
+ onFileUpload: onFileUpload,
152
+ sectionIndex: sectionIndex
153
153
  }))), /*#__PURE__*/React.createElement("div", {
154
154
  className: "navigator-container"
155
155
  }, updatedFormData.sections.length > 1 && /*#__PURE__*/React.createElement(_b.Button, {
@@ -164,7 +164,7 @@ const Renderer = _ref => {
164
164
  color: "interactive",
165
165
  isGradient: true,
166
166
  type: "submit",
167
- disabled: hasErrors
167
+ disabled: hasErrors || hasErrorsOnInitialRender
168
168
  }, isSubmit ? 'Submit' : 'Next')));
169
169
  }));
170
170
  };
@@ -15,6 +15,14 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
15
15
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } /* eslint-disable no-alert */ /* @pareto-engineering/generator-front 1.1.1-alpha.2 */ // Local Definitions
16
16
  const baseClassName = _exports.default.base;
17
17
  const componentClassName = 'section';
18
+ const fileTypeMapper = {
19
+ VID: 'Video',
20
+ TXT: 'Text',
21
+ IMG: 'Image',
22
+ PDF: 'PDF',
23
+ AUD: 'Audio',
24
+ FILE: 'Generic'
25
+ };
18
26
 
19
27
  /**
20
28
  * This is the component description.
@@ -29,7 +37,9 @@ const Section = _ref => {
29
37
  inputs,
30
38
  readOnly,
31
39
  fileUploadStatus,
32
- handleFileDelete
40
+ handleFileDelete,
41
+ onFileUpload,
42
+ sectionIndex
33
43
  // ...otherProps
34
44
  } = _ref;
35
45
  return /*#__PURE__*/React.createElement("div", {
@@ -41,13 +51,29 @@ const Section = _ref => {
41
51
  }, title), /*#__PURE__*/React.createElement(_.ExpandableLexicalPreview, {
42
52
  nodes: description,
43
53
  name: "instructions"
44
- }), inputs?.map(input => /*#__PURE__*/React.createElement(_f.FormInput, _extends({
45
- key: input.name
46
- }, input, {
47
- disabled: readOnly,
48
- uploadStatus: fileUploadStatus,
49
- handleFileDelete: handleFileDelete
50
- }))));
54
+ }), inputs?.map((input, inputIndex) => {
55
+ const isFileInput = input.type === 'file';
56
+ return /*#__PURE__*/React.createElement(_f.FormInput, _extends({
57
+ key: input.name
58
+ }, input, {
59
+ disabled: readOnly
60
+ }, isFileInput && {
61
+ uploadStatus: fileUploadStatus,
62
+ handleFileDelete,
63
+ onChange: files => {
64
+ const filesToUpload = files?.filter(file => file instanceof File).map(file => ({
65
+ file,
66
+ name: file.name,
67
+ mimeType: file.type,
68
+ type: fileTypeMapper[(0, _f.getFileType)(file)] || 'Generic',
69
+ title: file.name,
70
+ sectionIndex,
71
+ inputIndex
72
+ }));
73
+ onFileUpload(filesToUpload);
74
+ }
75
+ }));
76
+ }));
51
77
  };
52
78
  Section.propTypes = {
53
79
  /**
@@ -124,6 +124,7 @@ $default-animation-time: .31s;
124
124
  }
125
125
 
126
126
  &.#{bem.$modifier-simple} {
127
+ --stroke-color: var(--x, var(--#{$default-color}));
127
128
  background: transparent;
128
129
  border: 1px solid transparent;
129
130
  color: var(--x, var(--#{$default-color}));
@@ -24,8 +24,8 @@ const FormBuilder = ({
24
24
  formBuilderId,
25
25
  onBuilderFormSave,
26
26
  onBuilderError,
27
- onRendererError,
28
27
  onRendererFormSave,
28
+ onFileUpload,
29
29
  onBuilderValidate,
30
30
  initialBuilderValues,
31
31
  fileUploadStatus,
@@ -52,9 +52,9 @@ const FormBuilder = ({
52
52
  onSave: onRendererFormSave,
53
53
  readOnly: readOnly,
54
54
  shouldSubmit: shouldSubmit,
55
- onError: onRendererError,
56
55
  fileUploadStatus: fileUploadStatus,
57
- handleFileDelete: handleFileDelete
56
+ handleFileDelete: handleFileDelete,
57
+ onFileUpload: onFileUpload
58
58
  }));
59
59
  };
60
60
  FormBuilder.propTypes = {
@@ -1,7 +1,7 @@
1
1
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2
2
  /* @pareto-engineering/generator-front 1.1.1-alpha.2 */
3
3
  import * as React from 'react';
4
- import { useState, useEffect } from 'react';
4
+ import { useState, useEffect, useRef } from 'react';
5
5
  import { Formik, Form } from 'formik';
6
6
  import PropTypes from 'prop-types';
7
7
  import styleNames from '@pareto-engineering/bem/exports';
@@ -13,28 +13,6 @@ import "./styles.scss";
13
13
  import { Section } from "./common";
14
14
  const baseClassName = styleNames.base;
15
15
  const componentClassName = 'renderer';
16
- const reconstructFormDataWithValues = (formData, values) => {
17
- const valuesMap = {};
18
- Object.keys(values).forEach(async key => {
19
- if (key.includes('files')) {
20
- const files = values[key].map(file => file instanceof File ? URL.createObjectURL(file) : file);
21
- valuesMap[key] = files;
22
- } else {
23
- valuesMap[key] = values[key];
24
- }
25
- });
26
- const newData = {
27
- ...formData,
28
- sections: formData.sections.map(section => ({
29
- ...section,
30
- inputs: section.inputs.map(input => ({
31
- ...input,
32
- value: valuesMap[input.name] !== undefined ? valuesMap[input.name] : input.value
33
- }))
34
- }))
35
- };
36
- return newData;
37
- };
38
16
  const validate = (currentSectionIndex, formData, values) => {
39
17
  const errors = {};
40
18
  const hasAtLeastOneValue = Object.keys(values).some(valueKey => {
@@ -53,6 +31,25 @@ const validate = (currentSectionIndex, formData, values) => {
53
31
  });
54
32
  return errors;
55
33
  };
34
+ const reconstructFormDataWithValues = (formData, values) => {
35
+ const valuesMap = {};
36
+ Object.keys(values).forEach(async key => {
37
+ if (!key.includes('files')) {
38
+ valuesMap[key] = values[key];
39
+ }
40
+ });
41
+ const newData = {
42
+ ...formData,
43
+ sections: formData.sections.map(section => ({
44
+ ...section,
45
+ inputs: section.inputs.map(input => ({
46
+ ...input,
47
+ value: valuesMap[input.name] !== undefined ? valuesMap[input.name] : input.value
48
+ }))
49
+ }))
50
+ };
51
+ return newData;
52
+ };
56
53
 
57
54
  /**
58
55
  * This is the component description.
@@ -64,15 +61,18 @@ const Renderer = ({
64
61
  formData,
65
62
  readOnly,
66
63
  onSave,
67
- onError,
68
64
  shouldSubmit,
69
65
  fileUploadStatus,
70
- handleFileDelete
66
+ handleFileDelete,
67
+ onFileUpload,
68
+ shouldUpdateInputStateInRealTime = true
71
69
  // ...otherProps
72
70
  }) => {
73
71
  const [currentSectionIndex, setCurrentSectionIndex] = useState(0);
74
72
  const [sectionHistory, setSectionHistory] = useState([]);
75
- const [updatedFormData, setUpdatedFormData] = useState(formData);
73
+ const [updatedFormData, setUpdatedFormData] = useState({
74
+ ...formData
75
+ });
76
76
  useEffect(() => {
77
77
  setUpdatedFormData(formData);
78
78
  }, [formData]);
@@ -112,6 +112,9 @@ const Renderer = ({
112
112
  }
113
113
  };
114
114
  const isSubmit = currentSectionIndex === updatedFormData.sections.length - 1 || updatedFormData.sections[currentSectionIndex].navigation.nextSection === 'submit';
115
+ const ref = useRef(null);
116
+ const currentSectionInputs = updatedFormData.sections[currentSectionIndex].inputs;
117
+ const hasErrorsOnInitialRender = currentSectionInputs.some(input => input.required && !ref.current?.values[input.name] || ref.current?.errors[input.name]);
115
118
  return /*#__PURE__*/React.createElement("div", {
116
119
  id: id,
117
120
  className: [baseClassName, componentClassName, userClassName].filter(e => e).join(' '),
@@ -119,30 +122,27 @@ const Renderer = ({
119
122
  }, /*#__PURE__*/React.createElement(Formik, {
120
123
  initialValues: initialValues,
121
124
  onSubmit: handleSubmit,
122
- validate: values => validate(currentSectionIndex, formData, values)
125
+ validate: values => validate(currentSectionIndex, updatedFormData, values),
126
+ innerRef: ref
123
127
  }, ({
124
128
  values,
125
129
  errors
126
130
  }) => {
127
131
  useEffect(() => {
128
- const formDataWithValues = reconstructFormDataWithValues(updatedFormData, values);
129
- setUpdatedFormData(formDataWithValues);
130
- onSave?.(formDataWithValues);
132
+ if (shouldUpdateInputStateInRealTime) {
133
+ const formDataWithValues = reconstructFormDataWithValues(updatedFormData, values);
134
+ onSave?.(formDataWithValues);
135
+ }
131
136
  }, [values]);
132
- useEffect(() => {
133
- onError?.({
134
- errors,
135
- values
136
- });
137
- }, [errors, values]);
138
137
  const hasErrors = Object.keys(errors).length > 0;
139
138
  return /*#__PURE__*/React.createElement(Form, null, updatedFormData.sections.map((section, sectionIndex) => sectionIndex === currentSectionIndex && /*#__PURE__*/React.createElement(Section, _extends({
140
139
  key: `${section.title}`
141
140
  }, section, {
142
141
  readOnly: readOnly,
143
- setUpdatedFormData: setUpdatedFormData,
144
142
  fileUploadStatus: fileUploadStatus,
145
- handleFileDelete: handleFileDelete
143
+ handleFileDelete: handleFileDelete,
144
+ onFileUpload: onFileUpload,
145
+ sectionIndex: sectionIndex
146
146
  }))), /*#__PURE__*/React.createElement("div", {
147
147
  className: "navigator-container"
148
148
  }, updatedFormData.sections.length > 1 && /*#__PURE__*/React.createElement(Button, {
@@ -157,7 +157,7 @@ const Renderer = ({
157
157
  color: "interactive",
158
158
  isGradient: true,
159
159
  type: "submit",
160
- disabled: hasErrors
160
+ disabled: hasErrors || hasErrorsOnInitialRender
161
161
  }, isSubmit ? 'Submit' : 'Next')));
162
162
  }));
163
163
  };
@@ -6,11 +6,19 @@ import PropTypes from 'prop-types';
6
6
 
7
7
  // Local Definitions
8
8
 
9
- import { FormInput } from "../../../../../../f";
9
+ import { FormInput, getFileType } from "../../../../../../f";
10
10
  import { ExpandableLexicalPreview } from "../../../../..";
11
11
  import styleNames from '@pareto-engineering/bem/exports';
12
12
  const baseClassName = styleNames.base;
13
13
  const componentClassName = 'section';
14
+ const fileTypeMapper = {
15
+ VID: 'Video',
16
+ TXT: 'Text',
17
+ IMG: 'Image',
18
+ PDF: 'PDF',
19
+ AUD: 'Audio',
20
+ FILE: 'Generic'
21
+ };
14
22
 
15
23
  /**
16
24
  * This is the component description.
@@ -24,7 +32,9 @@ const Section = ({
24
32
  inputs,
25
33
  readOnly,
26
34
  fileUploadStatus,
27
- handleFileDelete
35
+ handleFileDelete,
36
+ onFileUpload,
37
+ sectionIndex
28
38
  // ...otherProps
29
39
  }) => /*#__PURE__*/React.createElement("div", {
30
40
  id: id,
@@ -35,13 +45,29 @@ const Section = ({
35
45
  }, title), /*#__PURE__*/React.createElement(ExpandableLexicalPreview, {
36
46
  nodes: description,
37
47
  name: "instructions"
38
- }), inputs?.map(input => /*#__PURE__*/React.createElement(FormInput, _extends({
39
- key: input.name
40
- }, input, {
41
- disabled: readOnly,
42
- uploadStatus: fileUploadStatus,
43
- handleFileDelete: handleFileDelete
44
- }))));
48
+ }), inputs?.map((input, inputIndex) => {
49
+ const isFileInput = input.type === 'file';
50
+ return /*#__PURE__*/React.createElement(FormInput, _extends({
51
+ key: input.name
52
+ }, input, {
53
+ disabled: readOnly
54
+ }, isFileInput && {
55
+ uploadStatus: fileUploadStatus,
56
+ handleFileDelete,
57
+ onChange: files => {
58
+ const filesToUpload = files?.filter(file => file instanceof File).map(file => ({
59
+ file,
60
+ name: file.name,
61
+ mimeType: file.type,
62
+ type: fileTypeMapper[getFileType(file)] || 'Generic',
63
+ title: file.name,
64
+ sectionIndex,
65
+ inputIndex
66
+ }));
67
+ onFileUpload(filesToUpload);
68
+ }
69
+ }));
70
+ }));
45
71
  Section.propTypes = {
46
72
  /**
47
73
  * The HTML id for this element
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pareto-engineering/design-system",
3
- "version": "4.9.3",
3
+ "version": "4.10.0",
4
4
  "description": "",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/es/index.js",
@@ -83,5 +83,5 @@
83
83
  "relay-test-utils": "^15.0.0"
84
84
  },
85
85
  "browserslist": "> 2%",
86
- "gitHead": "d69af6b2eea2c7f0897d665c3d13e3dda118e293"
86
+ "gitHead": "a7230fcb13b180d33c831ed413f8fc039e95fc89"
87
87
  }
@@ -124,6 +124,7 @@ $default-animation-time: .31s;
124
124
  }
125
125
 
126
126
  &.#{bem.$modifier-simple} {
127
+ --stroke-color: var(--x, var(--#{$default-color}));
127
128
  background: transparent;
128
129
  border: 1px solid transparent;
129
130
  color: var(--x, var(--#{$default-color}));
@@ -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, formData, values)}
157
+ validate={(values) => validate(currentSectionIndex, updatedFormData, values)}
158
+ innerRef={ref}
155
159
  >
156
160
  {({ values, errors }) => {
157
161
  useEffect(() => {
158
- const formDataWithValues = reconstructFormDataWithValues(updatedFormData, values)
159
- setUpdatedFormData(formDataWithValues)
160
- onSave?.(formDataWithValues)
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
- <FormInput
54
- key={input.name}
55
- {...input}
56
- disabled={readOnly}
57
- uploadStatus={fileUploadStatus}
58
- handleFileDelete={handleFileDelete}
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
 
@@ -12416,7 +12416,7 @@ exports[`Storyshots a/Timestamp Distance Format 1`] = `
12416
12416
  className="base timestamp"
12417
12417
  onClick={[Function]}
12418
12418
  >
12419
- about 2 years ago
12419
+ over 2 years ago
12420
12420
  </p>
12421
12421
  `;
12422
12422