revises the way that recursive backend validation works
This commit is contained in:
@@ -22,18 +22,16 @@
|
||||
svg {
|
||||
opacity: .2;
|
||||
}
|
||||
|
||||
.checkbox__input {
|
||||
background: darken($color-light-gray, 2%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__input {
|
||||
@include formInput;
|
||||
padding: 0;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
width: $baseline;
|
||||
height: $baseline;
|
||||
background: $color-light-gray;
|
||||
margin-right: base(.5);
|
||||
|
||||
svg {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@import '../../../../../scss/styles';
|
||||
@import '../../shared.scss';
|
||||
|
||||
.radio-input {
|
||||
display: flex;
|
||||
@@ -11,14 +12,24 @@
|
||||
}
|
||||
|
||||
&__styled-radio {
|
||||
@include formInput;
|
||||
width: $baseline;
|
||||
height: $baseline;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
background-color: white;
|
||||
box-shadow:
|
||||
inset 0px 0px 0px 1px $color-dark-gray,
|
||||
inset 0px 0px 0px 4px white;
|
||||
|
||||
&:before {
|
||||
content: ' ';
|
||||
display: block;
|
||||
border-radius: 100%;
|
||||
background-color: $color-dark-gray;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-shadow: inset 0 0 0 base(.1875) white;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__label {
|
||||
@@ -28,7 +39,9 @@
|
||||
&--is-selected {
|
||||
.radio-input {
|
||||
&__styled-radio {
|
||||
background-color: $color-dark-gray;
|
||||
&:before {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,7 +50,10 @@
|
||||
&:hover {
|
||||
.radio-input {
|
||||
&__styled-radio {
|
||||
background-color: rgba($color-dark-gray, .4);
|
||||
|
||||
&:before {
|
||||
opacity: .2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
const { ValidationError } = require('../errors');
|
||||
|
||||
const createValidationPromise = async (data, field) => {
|
||||
const result = await field.validate(data, field);
|
||||
return { result, field };
|
||||
};
|
||||
|
||||
exports.iterateFields = async (data, fields, path = '') => {
|
||||
const validationPromises = [];
|
||||
const validatedFields = [];
|
||||
|
||||
fields.forEach((field) => {
|
||||
const dataToValidate = data || {};
|
||||
@@ -13,34 +17,47 @@ exports.iterateFields = async (data, fields, path = '') => {
|
||||
// validated against directly
|
||||
if (field.name === undefined && field.fields) {
|
||||
field.fields.forEach((subField) => {
|
||||
validationPromises.push(subField.validate(dataToValidate[subField.name], {
|
||||
...subField,
|
||||
path: `${path}${subField.name}`,
|
||||
}));
|
||||
validatedFields.push(subField);
|
||||
validationPromises.push(createValidationPromise(dataToValidate[subField.name], subField));
|
||||
});
|
||||
} else if (field.fields) {
|
||||
if (field.type === 'repeater' || field.type === 'flexible') {
|
||||
const rowCount = Array.isArray(dataToValidate[field.name]) ? dataToValidate[field.name].length : 0;
|
||||
validationPromises.push(createValidationPromise(rowCount, field));
|
||||
|
||||
if (Array.isArray(dataToValidate[field.name])) {
|
||||
dataToValidate[field.name].forEach((rowData, i) => {
|
||||
validationPromises.push(exports.iterateFields(rowData, field.fields, `${path}${field.name}.${i}.`));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
validationPromises.push(exports.iterateFields(dataToValidate[field.name], field.fields, `${path}${field.name}`));
|
||||
}
|
||||
} else {
|
||||
validationPromises.push(field.validate(dataToValidate[field.name], field));
|
||||
validatedFields.push(field);
|
||||
validationPromises.push(createValidationPromise(dataToValidate[field.name], field));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const validationResults = await Promise.all(validationPromises);
|
||||
|
||||
const errors = validationResults.reduce((results, result, i) => {
|
||||
const field = validatedFields[i];
|
||||
const errors = validationResults.reduce((results, result) => {
|
||||
const { field, result: validationResult } = result;
|
||||
|
||||
if (Array.isArray(result)) {
|
||||
return [
|
||||
...results,
|
||||
...result,
|
||||
];
|
||||
} if (result !== true) {
|
||||
}
|
||||
|
||||
if (validationResult === false || typeof validationResult === 'string') {
|
||||
const fieldPath = `${path}${field.name}`;
|
||||
|
||||
return [
|
||||
...results,
|
||||
{
|
||||
field: `${path}${field.name}`,
|
||||
message: result,
|
||||
field: fieldPath,
|
||||
message: validationResult,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
const { iterateFields } = require('./validateCreate');
|
||||
|
||||
const optionsToValidatorMap = {
|
||||
number: (value, options = {}) => {
|
||||
const parsedValue = parseInt(value, 10);
|
||||
@@ -93,41 +91,17 @@ const optionsToValidatorMap = {
|
||||
if (value) return true;
|
||||
return 'This field is required.';
|
||||
},
|
||||
group: async (value, options) => {
|
||||
return iterateFields(value, options.fields, `${options.name}.`);
|
||||
},
|
||||
repeater: async (value, options = {}) => {
|
||||
const path = options.path || options.name;
|
||||
|
||||
if (!value || value.length === 0) {
|
||||
return 'This field requires at least one row.';
|
||||
}
|
||||
|
||||
if (options.minRows && value.length < options.minRows) {
|
||||
repeater: (value, options = {}) => {
|
||||
if (options.minRows && value < options.minRows) {
|
||||
return `This field requires at least ${options.minRows} row(s).`;
|
||||
}
|
||||
|
||||
if (options.maxRows && value.length > options.maxRows) {
|
||||
if (options.maxRows && value > options.maxRows) {
|
||||
return `This field requires no more than ${options.maxRows} row(s).`;
|
||||
}
|
||||
|
||||
const errors = [];
|
||||
const rowPromises = [];
|
||||
|
||||
value.forEach((row, i) => {
|
||||
rowPromises.push(iterateFields(row, options.fields, `${path}.${i}.`));
|
||||
});
|
||||
|
||||
const rowResults = await Promise.all(rowPromises);
|
||||
|
||||
rowResults.forEach((row) => {
|
||||
row.forEach((fieldError) => {
|
||||
errors.push(fieldError);
|
||||
});
|
||||
});
|
||||
|
||||
if (errors.length > 0) {
|
||||
return errors;
|
||||
if (!value) {
|
||||
return 'This field requires at least one row.';
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -140,38 +114,17 @@ const optionsToValidatorMap = {
|
||||
if (value) return true;
|
||||
return 'This field is required.';
|
||||
},
|
||||
flexible: async (value, options) => {
|
||||
if (value.length === 0) {
|
||||
return `This field requires at least one ${options.singularLabel}.`;
|
||||
flexible: (value, options) => {
|
||||
if (options.minRows && value < options.minRows) {
|
||||
return `This field requires at least ${options.minRows} row(s).`;
|
||||
}
|
||||
|
||||
const errors = [];
|
||||
const rowPromises = [];
|
||||
if (options.maxRows && value > options.maxRows) {
|
||||
return `This field requires no more than ${options.maxRows} row(s).`;
|
||||
}
|
||||
|
||||
value.forEach((row, i) => {
|
||||
const { blockType } = row;
|
||||
const block = options.blocks.find(availableBlock => availableBlock.slug === blockType);
|
||||
|
||||
if (!block) {
|
||||
return errors.push({
|
||||
message: `Block ${i + 1} is missing a block type.`,
|
||||
field: options.name,
|
||||
});
|
||||
}
|
||||
|
||||
return rowPromises.push(iterateFields(row, block.fields, `${options.name}.${i}.`));
|
||||
});
|
||||
|
||||
const rowResults = await Promise.all(rowPromises);
|
||||
|
||||
rowResults.forEach((row) => {
|
||||
row.forEach((fieldError) => {
|
||||
errors.push(fieldError);
|
||||
});
|
||||
});
|
||||
|
||||
if (errors.length > 0) {
|
||||
return errors;
|
||||
if (!value) {
|
||||
return 'This field requires at least one row.';
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user