enables conditional field logic

This commit is contained in:
James
2020-04-02 15:59:27 -04:00
parent 0429fb939e
commit 87b5952e6a
9 changed files with 108 additions and 15 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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':

View File

@@ -61,7 +61,7 @@ const Number = (props) => {
/>
<input
value={value || ''}
onChange={onFieldChange}
onChange={e => onFieldChange(parseInt(e.target.value, 10))}
disabled={formProcessing ? 'disabled' : undefined}
placeholder={placeholder}
type="number"

View File

@@ -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);

View File

@@ -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]);

View File

@@ -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 <Field {...props} />;
}
return null;
}
return <Field {...props} />;
};
WithCondition.defaultProps = {
condition: null,
};
WithCondition.propTypes = {
condition: PropTypes.func,
name: PropTypes.string.isRequired,
};
return WithCondition;
};
export default withCondition;

View File

@@ -50,12 +50,12 @@ const EditView = (props) => {
<Form
className={`${baseClass}__form`}
method={data ? 'put' : 'post'}
action={`${serverURL}/globals/${global.slug}`}
action={`${serverURL}${api}/globals/${global.slug}`}
>
<StickyHeader
showStatus
content={
<APIURL url={`${serverURL}/globals/${global.slug}`} />
<APIURL url={`${serverURL}${api}/globals/${global.slug}`} />
}
action={(
<>
@@ -77,6 +77,7 @@ EditView.propTypes = {
global: PropTypes.shape({
label: PropTypes.string,
slug: PropTypes.string,
fields: PropTypes.arrayOf(PropTypes.shape({})),
}).isRequired,
};

View File

@@ -1,12 +1,39 @@
const sanitizeConfig = require('../utilities/sanitizeConfig');
const secureConfig = require('../utilities/secureConfig');
function convertToText(obj) {
const string = [];
if (obj === undefined || obj === null) {
return String(obj);
} if (typeof (obj) === 'object' && (obj.join === undefined || obj.join === null)) {
Object.keys(obj).forEach((prop) => {
if (Object.prototype.hasOwnProperty.call(obj, prop)) string.push(`${prop}: ${convertToText(obj[prop])}`);
});
return `{${string.join(',')}}`;
} if (typeof (obj) === 'object' && !(obj.join === undefined || obj.join === null)) {
Object.keys(obj).forEach((prop) => {
string.push(convertToText(obj[prop]));
});
return `[${string.join(',')}]`;
} if (typeof (obj) === 'function') {
string.push(obj.toString());
} else {
string.push(JSON.stringify(obj));
}
return string.join(',');
}
module.exports = (config) => {
const sanitizedConfig = sanitizeConfig(config);
const securedConfig = secureConfig(sanitizedConfig);
const stringifiedConfig = convertToText(securedConfig);
return {
code: `
module.exports = ${JSON.stringify(securedConfig)}`,
module.exports = ${stringifiedConfig}`,
};
};