fix: more advanced conditional logic edge cases

This commit is contained in:
James
2021-07-29 11:17:49 -04:00
committed by Elliot DeNolf
parent 12cba62930
commit 33983deb37
19 changed files with 69 additions and 18 deletions

View File

@@ -23,7 +23,7 @@ const buildStateFromSchema = async (fieldSchema: FieldSchema[], fullData: Data =
if (fieldSchema) {
const validationPromises = [];
const structureFieldState = (field, data = {}) => {
const structureFieldState = (field, passesCondition, data = {}) => {
const value = typeof data?.[field.name] !== 'undefined' ? data[field.name] : field.defaultValue;
const fieldState = {
@@ -31,6 +31,8 @@ const buildStateFromSchema = async (fieldSchema: FieldSchema[], fullData: Data =
initialValue: value,
valid: true,
validate: field.validate,
condition: field.admin?.condition,
passesCondition,
};
validationPromises.push(buildValidationPromise(fieldState, field));
@@ -38,7 +40,7 @@ const buildStateFromSchema = async (fieldSchema: FieldSchema[], fullData: Data =
return fieldState;
};
const iterateFields = (fields: FieldSchema[], data: Data, path = '') => fields.reduce((state, field) => {
const iterateFields = (fields: FieldSchema[], data: Data, parentPassesCondition: boolean, path = '') => fields.reduce((state, field) => {
let initialData = data;
if (!field?.admin?.disabled) {
@@ -46,6 +48,8 @@ const buildStateFromSchema = async (fieldSchema: FieldSchema[], fullData: Data =
initialData = { [field.name]: field.defaultValue };
}
const passesCondition = (field?.admin?.condition ? field.admin.condition(fullData || {}, initialData || {}) : true) && parentPassesCondition;
if (field.name) {
if (field.type === 'relationship' && initialData?.[field.name] === null) {
initialData[field.name] = 'null';
@@ -68,7 +72,7 @@ const buildStateFromSchema = async (fieldSchema: FieldSchema[], fullData: Data =
initialValue: row.id || new ObjectID().toHexString(),
valid: true,
},
...iterateFields(field.fields, row, rowPath),
...iterateFields(field.fields, row, passesCondition, rowPath),
};
}, {}),
};
@@ -97,7 +101,7 @@ const buildStateFromSchema = async (fieldSchema: FieldSchema[], fullData: Data =
initialValue: row.id || new ObjectID().toHexString(),
valid: true,
},
...(block?.fields ? iterateFields(block.fields, row, rowPath) : {}),
...(block?.fields ? iterateFields(block.fields, row, passesCondition, rowPath) : {}),
};
}, {}),
};
@@ -113,13 +117,13 @@ const buildStateFromSchema = async (fieldSchema: FieldSchema[], fullData: Data =
return {
...state,
...iterateFields(field.fields, subFieldData, `${path}${field.name}.`),
...iterateFields(field.fields, subFieldData, passesCondition, `${path}${field.name}.`),
};
}
return {
...state,
[`${path}${field.name}`]: structureFieldState(field, data),
[`${path}${field.name}`]: structureFieldState(field, passesCondition, data),
};
}
@@ -127,21 +131,21 @@ const buildStateFromSchema = async (fieldSchema: FieldSchema[], fullData: Data =
if (field.type === 'row') {
return {
...state,
...iterateFields(field.fields, data, path),
...iterateFields(field.fields, data, passesCondition, path),
};
}
// Handle normal fields
return {
...state,
[`${path}${field.name}`]: structureFieldState(field, data),
[`${path}${field.name}`]: structureFieldState(field, passesCondition, data),
};
}
return state;
}, {});
const resultingState = iterateFields(fieldSchema, fullData);
const resultingState = iterateFields(fieldSchema, fullData, true);
await Promise.all(validationPromises);
return resultingState;
}

View File

@@ -1,5 +1,7 @@
import { unflatten, flatten } from 'flatley';
import flattenFilters from './flattenFilters';
import getSiblingData from './getSiblingData';
import reduceFieldsToValues from './reduceFieldsToValues';
import { Fields } from './types';
const unflattenRowsFromState = (state: Fields, path) => {
@@ -106,21 +108,31 @@ function fieldReducer(state: Fields, action): Fields {
case 'MODIFY_CONDITION': {
const { path, result } = action;
return Object.entries(state).reduce((newState, [key, val]) => {
if (key === path || key.indexOf(`${path}.`) === 0) {
return Object.entries(state).reduce((newState, [fieldPath, field]) => {
if (fieldPath === path || fieldPath.indexOf(`${path}.`) === 0) {
let passesCondition = result;
// If a condition is being set to true,
// Set all conditions to true
// Besides those who still fail their own conditions
if (passesCondition && field.condition) {
passesCondition = field.condition(reduceFieldsToValues(state), getSiblingData(state, path));
}
return {
...newState,
[key]: {
...val,
passesCondition: result,
[fieldPath]: {
...field,
passesCondition,
},
};
}
return {
...newState,
[key]: {
...val,
[fieldPath]: {
...field,
},
};
}, {});
@@ -136,6 +148,7 @@ function fieldReducer(state: Fields, action): Fields {
initialValue: action.initialValue,
stringify: action.stringify,
validate: action.validate,
condition: action.condition,
passesCondition: action.passesCondition,
};

View File

@@ -1,3 +1,5 @@
import { Condition } from '../../../../fields/config/types';
export type Field = {
value: unknown
initialValue: unknown
@@ -7,6 +9,7 @@ export type Field = {
disableFormData?: boolean
ignoreWhileFlattening?: boolean
stringify?: boolean
condition?: Condition
passesCondition?: boolean
}

View File

@@ -32,6 +32,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
admin: {
readOnly,
description,
condition,
},
} = props;
@@ -70,6 +71,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
validate: memoizedValidate,
disableFormData,
ignoreWhileFlattening: true,
condition,
});
const addRow = useCallback(async (rowIndex) => {

View File

@@ -46,6 +46,7 @@ const Blocks: React.FC<Props> = (props) => {
admin: {
readOnly,
description,
condition,
},
} = props;
@@ -80,6 +81,7 @@ const Blocks: React.FC<Props> = (props) => {
validate: memoizedValidate,
disableFormData,
ignoreWhileFlattening: true,
condition,
});
const addRow = useCallback(async (rowIndex, blockType) => {

View File

@@ -25,6 +25,7 @@ const Checkbox: React.FC<Props> = (props) => {
style,
width,
description,
condition,
} = {},
} = props;
@@ -44,6 +45,7 @@ const Checkbox: React.FC<Props> = (props) => {
path,
validate: memoizedValidate,
disableFormData,
condition,
});
return (

View File

@@ -25,6 +25,7 @@ const Code: React.FC<Props> = (props) => {
width,
language,
description,
condition,
} = {},
label,
minLength,
@@ -55,6 +56,7 @@ const Code: React.FC<Props> = (props) => {
path,
validate: memoizedValidate,
enableDebouncedValue: true,
condition,
});
const classes = [

View File

@@ -27,6 +27,7 @@ const DateTime: React.FC<Props> = (props) => {
width,
date,
description,
condition,
} = {},
} = props;
@@ -45,6 +46,7 @@ const DateTime: React.FC<Props> = (props) => {
} = useFieldType({
path,
validate: memoizedValidate,
condition,
});
const classes = [

View File

@@ -22,6 +22,7 @@ const Email: React.FC<Props> = (props) => {
placeholder,
autoComplete,
description,
condition,
} = {},
label,
} = props;
@@ -37,6 +38,7 @@ const Email: React.FC<Props> = (props) => {
path,
validate: memoizedValidate,
enableDebouncedValue: true,
condition,
});
const {

View File

@@ -25,6 +25,7 @@ const NumberField: React.FC<Props> = (props) => {
step,
placeholder,
description,
condition,
} = {},
} = props;
@@ -44,6 +45,7 @@ const NumberField: React.FC<Props> = (props) => {
path,
validate: memoizedValidate,
enableDebouncedValue: true,
condition,
});
const handleChange = useCallback((e) => {

View File

@@ -27,6 +27,7 @@ const RadioGroup: React.FC<Props> = (props) => {
style,
width,
description,
condition,
} = {},
options,
} = props;
@@ -46,6 +47,7 @@ const RadioGroup: React.FC<Props> = (props) => {
} = useFieldType({
path,
validate: memoizedValidate,
condition,
});
const classes = [

View File

@@ -37,6 +37,7 @@ const Relationship: React.FC<Props> = (props) => {
style,
width,
description,
condition,
} = {},
} = props;
@@ -73,6 +74,7 @@ const Relationship: React.FC<Props> = (props) => {
} = useFieldType({
path: path || name,
validate: memoizedValidate,
condition,
});
const addOptions = useCallback((data, relation) => {

View File

@@ -46,6 +46,7 @@ const RichText: React.FC<Props> = (props) => {
placeholder,
description,
hideGutter,
condition,
} = {},
} = props;
@@ -106,6 +107,7 @@ const RichText: React.FC<Props> = (props) => {
path,
validate: memoizedValidate,
stringify: true,
condition,
});
const {

View File

@@ -38,6 +38,7 @@ const Select: React.FC<Props> = (props) => {
style,
width,
description,
condition,
} = {},
} = props;
@@ -58,6 +59,7 @@ const Select: React.FC<Props> = (props) => {
} = useFieldType({
path,
validate: memoizedValidate,
condition,
});
const classes = [

View File

@@ -22,6 +22,7 @@ const Text: React.FC<Props> = (props) => {
style,
width,
description,
condition,
} = {},
} = props;
@@ -31,6 +32,7 @@ const Text: React.FC<Props> = (props) => {
path,
validate,
enableDebouncedValue: true,
condition,
});
const {

View File

@@ -22,6 +22,7 @@ const Textarea: React.FC<Props> = (props) => {
placeholder,
rows,
description,
condition,
} = {},
label,
minLength,
@@ -44,6 +45,7 @@ const Textarea: React.FC<Props> = (props) => {
path,
validate: memoizedValidate,
enableDebouncedValue: true,
condition,
});
const classes = [

View File

@@ -32,6 +32,7 @@ const Upload: React.FC<Props> = (props) => {
style,
width,
description,
condition,
} = {},
label,
validate = upload,
@@ -53,6 +54,7 @@ const Upload: React.FC<Props> = (props) => {
const fieldType = useFieldType({
path,
validate: memoizedValidate,
condition,
});
const {

View File

@@ -13,6 +13,7 @@ const useFieldType = <T extends unknown>(options: Options): FieldType<T> => {
disableFormData,
ignoreWhileFlattening,
stringify,
condition,
} = options;
const formContext = useForm();
@@ -48,6 +49,7 @@ const useFieldType = <T extends unknown>(options: Options): FieldType<T> => {
ignoreWhileFlattening,
initialValue,
validate,
condition,
value: valueToSend,
valid: false,
errorMessage: undefined,
@@ -63,7 +65,7 @@ const useFieldType = <T extends unknown>(options: Options): FieldType<T> => {
}
dispatchFields(fieldToDispatch);
}, [path, dispatchFields, validate, disableFormData, ignoreWhileFlattening, initialValue, stringify]);
}, [path, dispatchFields, validate, disableFormData, ignoreWhileFlattening, initialValue, stringify, condition]);
// Method to return from `useFieldType`, used to
// update internal field values from field component(s)

View File

@@ -1,4 +1,4 @@
import { Validate } from '../../../../fields/config/types';
import { Condition, Validate } from '../../../../fields/config/types';
export type Options = {
path: string
@@ -7,6 +7,7 @@ export type Options = {
disableFormData?: boolean
ignoreWhileFlattening?: boolean
stringify?: boolean
condition?: Condition
}
export type FieldType<T> = {