Skip to content

Commit c20d89e

Browse files
committed
feat(all): enable input type file
1 parent 38ec9cc commit c20d89e

File tree

5 files changed

+38
-101
lines changed

5 files changed

+38
-101
lines changed

packages/react-form-renderer/demo/index.js

Lines changed: 7 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,17 @@
11
/* eslint-disable camelcase */
22
import React from 'react';
33
import ReactDOM from 'react-dom';
4-
import FormRenderer, { validatorTypes } from '../src';
4+
import FormRenderer from '../src';
55
import componentMapper from './form-fields-mapper';
66
import FormTemplate from './form-template';
7-
// import sandboxSchema from './sandbox';
8-
import DefaultSchemaError from '../src/files/schema-errors';
97

10-
const intl = (name) => `translated ${name}`;
11-
12-
const actionMapper = {
13-
loadData: (data) => (...args) =>
14-
new Promise((resolve) => {
15-
setTimeout(() => resolve({ custom: 'ererewr', ...data }), 1700);
16-
}),
17-
loadLabel: intl
18-
};
19-
20-
const validatorMapper = {
21-
asyncValidator: (url, attributes) => (value, allValues) =>
22-
new Promise((resolve, reject) =>
23-
setTimeout(() => {
24-
if (value === 'error') {
25-
reject('Async validation failed');
26-
}
27-
28-
resolve('hola');
29-
}, 1700)
30-
)
31-
};
32-
33-
// eslint-disable-next-line no-unused-vars
34-
const asyncValidatorSchema = {
8+
const fileSchema = {
359
fields: [
36-
{
37-
component: 'composite-mapper-field',
38-
name: 'composite-field',
39-
label: 'Componsite field'
40-
},
4110
{
4211
component: 'text-field',
43-
name: 'async-validation-field',
44-
label: 'Async validation field',
45-
validate: [
46-
{ type: 'asyncValidator' },
47-
{ type: 'required' },
48-
{
49-
type: validatorTypes.PATTERN,
50-
pattern: '^Foo$',
51-
flags: 'i'
52-
}
53-
]
54-
}
55-
]
56-
};
57-
58-
const conditionLogicDemo = {
59-
title: 'Testing Conditions',
60-
description: 'Write X in both',
61-
fields: [
62-
{
63-
name: 'text_box_1',
64-
label: 'Text Box 1',
65-
title: 'Text Box',
66-
component: 'text-field',
67-
clearOnUnmount: true,
68-
condition: {
69-
sequence: [
70-
{ when: 'a', is: 'x', then: { visible: true, set: { password: 'defaultPassword' } }, else: { set: { password: 'no password' } } },
71-
{ when: 'b', is: 'x', then: { visible: true, set: { text_box_1: 'defaultText' } } }
72-
]
73-
}
74-
},
75-
{
76-
name: 'a',
77-
label: 'a',
78-
title: 'a',
79-
component: 'text-field'
80-
},
81-
{
82-
name: 'b',
83-
label: 'b',
84-
title: 'b',
85-
component: 'text-field'
86-
},
87-
{
88-
name: 'password',
89-
label: 'password',
90-
title: 'password',
91-
component: 'text-field'
12+
name: 'file-upload',
13+
type: 'file',
14+
label: 'file upload'
9215
}
9316
]
9417
};
@@ -98,23 +21,10 @@ const App = () => {
9821
return (
9922
<div style={{ padding: 20 }}>
10023
<FormRenderer
101-
validatorMapper={validatorMapper}
10224
componentMapper={componentMapper}
103-
onSubmit={(values) => console.log(values)}
104-
schema={conditionLogicDemo}
25+
onSubmit={(values, ...args) => console.log(values, args)}
10526
FormTemplate={FormTemplate}
106-
actionMapper={actionMapper}
107-
schemaValidatorMapper={{
108-
actions: {
109-
loadLabel: (action, fieldName) => {
110-
if (typeof action[1] !== 'string') {
111-
throw new DefaultSchemaError(
112-
`Second argument of loadLabel action has to be a string: ID of the text from the translated database. Error found in ${fieldName}`
113-
);
114-
}
115-
}
116-
}
117-
}}
27+
schema={fileSchema}
11828
/>
11929
</div>
12030
);

packages/react-form-renderer/src/files/form-renderer.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useState, useRef } from 'react';
22
import Form from './form';
33
import arrayMutators from 'final-form-arrays';
44
import PropTypes from 'prop-types';
@@ -25,6 +25,8 @@ const FormRenderer = ({
2525
schemaValidatorMapper,
2626
...props
2727
}) => {
28+
const [fileInputs, setFileInputs] = useState([]);
29+
const focusDecorator = useRef(createFocusDecorator());
2830
let schemaError;
2931

3032
const validatorMapperMerged = { ...defaultValidatorMapper, ...validatorMapper };
@@ -43,12 +45,16 @@ const FormRenderer = ({
4345
return <SchemaErrorComponent name={schemaError.name} message={schemaError.message} />;
4446
}
4547

48+
const registerInputFile = (name) => setFileInputs((prevFiles) => [...prevFiles, name]);
49+
50+
const unRegisterInputFile = (name) => setFileInputs((prevFiles) => [...prevFiles.splice(prevFiles.indexOf(name))]);
51+
4652
return (
4753
<Form
4854
{...props}
49-
onSubmit={onSubmit}
55+
onSubmit={(values, formApi, callback) => onSubmit(values, { ...formApi, fileInputs }, callback)}
5056
mutators={{ ...arrayMutators }}
51-
decorators={[createFocusDecorator()]}
57+
decorators={[focusDecorator.current]}
5258
subscription={{ pristine: true, submitting: true, valid: true, ...subscription }}
5359
render={({ handleSubmit, pristine, valid, form: { reset, mutators, getState, submit, ...form } }) => (
5460
<RendererContext.Provider
@@ -57,6 +63,8 @@ const FormRenderer = ({
5763
validatorMapper: validatorMapperMerged,
5864
actionMapper,
5965
formOptions: {
66+
registerInputFile,
67+
unRegisterInputFile,
6068
pristine,
6169
onSubmit,
6270
onCancel: onCancel ? (...args) => onCancel(getState().values, ...args) : undefined,
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { createContext } from 'react';
22

3-
const RendererContext = createContext({});
3+
const RendererContext = createContext({
4+
formOptions: {}
5+
});
46

57
export default RendererContext;

packages/react-form-renderer/src/files/use-field-api.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ const useFieldApi = ({ name, initializeOnMount, component, render, validate, ...
7676
};
7777

7878
const fieldProps = useField(name, enhancedProps);
79+
if (fieldProps.input.type === 'file' && typeof fieldProps.input.value === 'object') {
80+
fieldProps.input = { ...fieldProps.input, value: fieldProps.input.value.inputValue };
81+
}
7982

8083
/** Reinitilize type */
8184
useEffect(() => {
@@ -132,6 +135,9 @@ const useFieldApi = ({ name, initializeOnMount, component, render, validate, ...
132135
useEffect(
133136
() => {
134137
mounted.current = true;
138+
if (fieldProps.input.type === 'file') {
139+
formOptions.registerInputFile(fieldProps.input.name);
140+
}
135141

136142
return () => {
137143
mounted.current = false;
@@ -141,6 +147,10 @@ const useFieldApi = ({ name, initializeOnMount, component, render, validate, ...
141147
if ((formOptions.clearOnUnmount || props.clearOnUnmount) && props.clearOnUnmount !== false) {
142148
fieldProps.input.onChange(fieldClearedValue);
143149
}
150+
151+
if (fieldProps.input.type === 'file') {
152+
formOptions.unRegisterInputFile(fieldProps.input.name);
153+
}
144154
};
145155
},
146156
// eslint-disable-next-line react-hooks/exhaustive-deps

packages/react-form-renderer/src/form-renderer/enhanced-on-change.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ const sanitizeValue = (event) => {
1111
return event;
1212
}
1313

14+
if (event.target.type === 'file') {
15+
return {
16+
inputValue: event.target.value,
17+
inputFiles: event.target.files
18+
};
19+
}
20+
1421
return event.target.value;
1522
}
1623

0 commit comments

Comments
 (0)