diff --git a/src/client/components/forms/Form/flattenFilters.js b/src/client/components/forms/Form/flattenFilters.js
new file mode 100644
index 0000000000..bfd3972c5b
--- /dev/null
+++ b/src/client/components/forms/Form/flattenFilters.js
@@ -0,0 +1,10 @@
+const flattenFilters = [{
+ test: (_, value) => {
+ const hasValidProperty = Object.prototype.hasOwnProperty.call(value, 'valid');
+ const hasValueProperty = Object.prototype.hasOwnProperty.call(value, 'value');
+
+ return (hasValidProperty && hasValueProperty);
+ },
+}];
+
+export default flattenFilters;
diff --git a/src/client/components/forms/Form/index.js b/src/client/components/forms/Form/index.js
index fb76f3fc7f..d33da3ae61 100644
--- a/src/client/components/forms/Form/index.js
+++ b/src/client/components/forms/Form/index.js
@@ -1,4 +1,4 @@
-import React, { useState, useReducer } from 'react';
+import React, { useContext, useState, useReducer } from 'react';
import { useHistory } from 'react-router-dom';
import PropTypes from 'prop-types';
import { unflatten } from 'flatley';
@@ -15,6 +15,8 @@ import './index.scss';
const baseClass = 'form';
+export const useForm = () => useContext(FormContext);
+
const Form = (props) => {
const [fields, dispatchFields] = useReducer(fieldReducer, {});
const [submitted, setSubmitted] = useState(false);
diff --git a/src/client/components/forms/Form/reducer.js b/src/client/components/forms/Form/reducer.js
index 91c0cce7e6..2b0629a617 100644
--- a/src/client/components/forms/Form/reducer.js
+++ b/src/client/components/forms/Form/reducer.js
@@ -1,4 +1,5 @@
import { unflatten, flatten } from 'flatley';
+import flattenFilters from './flattenFilters';
const splitRowsFromState = (state, name) => {
// Take a copy of state
@@ -26,15 +27,6 @@ const splitRowsFromState = (state, name) => {
};
};
-const flattenFilters = [{
- test: (_, value) => {
- const hasValidProperty = Object.prototype.hasOwnProperty.call(value, 'valid');
- const hasValueProperty = Object.prototype.hasOwnProperty.call(value, 'value');
-
- return (hasValidProperty && hasValueProperty);
- },
-}];
-
function fieldReducer(state, action) {
switch (action.type) {
case 'REPLACE_ALL':
diff --git a/src/client/components/forms/field-types/Number/index.js b/src/client/components/forms/field-types/Number/index.js
index 37db01e8f2..cc36341d74 100644
--- a/src/client/components/forms/field-types/Number/index.js
+++ b/src/client/components/forms/field-types/Number/index.js
@@ -61,7 +61,7 @@ const Number = (props) => {
/>
onFieldChange(parseInt(e.target.value, 10))}
disabled={formProcessing ? 'disabled' : undefined}
placeholder={placeholder}
type="number"
diff --git a/src/client/components/forms/field-types/Textarea/index.js b/src/client/components/forms/field-types/Textarea/index.js
index 715668308b..9d17a868a7 100644
--- a/src/client/components/forms/field-types/Textarea/index.js
+++ b/src/client/components/forms/field-types/Textarea/index.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import useFieldType from '../../useFieldType';
+import withCondition from '../../withCondition';
import Label from '../../Label';
import Error from '../../Error';
@@ -79,6 +80,7 @@ Textarea.defaultProps = {
errorMessage: defaultError,
width: 100,
style: {},
+ placeholder: null,
};
Textarea.propTypes = {
@@ -90,6 +92,7 @@ Textarea.propTypes = {
width: PropTypes.number,
style: PropTypes.shape({}),
label: PropTypes.string,
+ placeholder: PropTypes.string,
};
-export default Textarea;
+export default withCondition(Textarea);
diff --git a/src/client/components/forms/useFieldType/index.js b/src/client/components/forms/useFieldType/index.js
index c74c3fcc49..6e967ce877 100644
--- a/src/client/components/forms/useFieldType/index.js
+++ b/src/client/components/forms/useFieldType/index.js
@@ -26,14 +26,18 @@ const useFieldType = (options) => {
});
}, [name, required, dispatchFields, validate]);
+ // Send value up to form on mount and when value changes
useEffect(() => {
sendField(mountValue);
}, [sendField, mountValue]);
+ // Remove field from state on "unmount"
useEffect(() => {
return () => dispatchFields({ name, type: 'REMOVE' });
}, [dispatchFields, name]);
+ // Send up new value when default is loaded
+ // only if it's not null
useEffect(() => {
if (defaultValue != null) sendField(defaultValue);
}, [defaultValue, sendField]);
diff --git a/src/client/components/forms/withCondition/index.js b/src/client/components/forms/withCondition/index.js
new file mode 100644
index 0000000000..dcb2f6f32e
--- /dev/null
+++ b/src/client/components/forms/withCondition/index.js
@@ -0,0 +1,54 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { useForm } from '../Form';
+
+const withCondition = (Field) => {
+ const WithCondition = (props) => {
+ const { condition, name } = props;
+ const { fields } = useForm();
+
+ if (condition) {
+ let siblingFields = {};
+
+ // If this field is nested
+ // We can provide a list of sibling fields
+ if (name.indexOf('.') > 0) {
+ const parentFieldPath = name.substring(0, name.lastIndexOf('.') + 1);
+ siblingFields = Object.keys(fields).reduce((siblings, fieldKey) => {
+ if (fieldKey.indexOf(parentFieldPath) === 0) {
+ return {
+ ...siblings,
+ [fieldKey.replace(parentFieldPath, '')]: fields[fieldKey],
+ };
+ }
+
+ return siblings;
+ }, {});
+ }
+
+ const passesCondition = condition ? condition(fields, siblingFields) : true;
+
+ if (passesCondition) {
+ return