Improve form api

Signed-off-by: soupette <cyril.lpz@gmail.com>
This commit is contained in:
soupette 2021-01-27 09:32:54 +01:00
parent 3395ab5bca
commit 2d6d30643e
6 changed files with 142 additions and 105 deletions

View File

@ -5,13 +5,13 @@ class Plugin {
injectedComponents = {};
internals = {};
apis = {};
constructor(pluginConf) {
this.pluginId = pluginConf.id;
this.decorators = pluginConf.decorators || {};
this.injectedComponents = pluginConf.injectedComponents || {};
this.internals = pluginConf.internals || {};
this.apis = pluginConf.apis || {};
}
decorate(compoName, compo) {

View File

@ -22,59 +22,34 @@ const forms = {
return attr !== options.initialData.name;
});
const validators = [];
const attributeFormExtensions = extensions.attribute[attributeType];
if (attributeFormExtensions) {
attributeFormExtensions.forEach(({ validator }) => {
if (validator) {
validators.push(validator);
}
});
}
try {
let shape = attributeTypes[attributeType](
let attributeShape = attributeTypes[attributeType](
usedAttributeNames,
reservedNames.attributes,
alreadyTakenTargetContentTypeAttributes,
options
);
validators.forEach(validator => {
console.log({ validator });
shape = shape.shape(validator);
});
return shape;
return extensions.makeValidator(
['attribute', attributeType],
attributeShape,
usedAttributeNames,
reservedNames.attributes,
alreadyTakenTargetContentTypeAttributes,
options
);
} catch (err) {
console.error('form', err);
console.error('Error yup build schema', err);
return attributeTypes.default(usedAttributeNames, reservedNames.attributes);
}
},
form: {
advanced(data, type, step, actionType, attributes, extensions) {
const attributeFormExtensions = extensions.attribute[type];
let customForms = [];
if (attributeFormExtensions) {
attributeFormExtensions.forEach(({ form }) => {
if (form.advanced) {
const blocksToAdd = form.advanced(data, type, step, actionType, attributes);
blocksToAdd.forEach(block => {
customForms.push(block);
});
}
});
}
try {
const baseForm = attributesForm.advanced[type](data, step).items;
return { items: [...baseForm, ...customForms] };
return extensions.makeAdvancedForm(['attribute', type], baseForm, data, step);
} catch (err) {
console.error(err);
@ -91,12 +66,19 @@ const forms = {
},
},
contentType: {
schema(alreadyTakenNames, isEditing, ctUid, reservedNames) {
schema(alreadyTakenNames, isEditing, ctUid, reservedNames, extensions) {
const takenNames = isEditing
? alreadyTakenNames.filter(uid => uid !== ctUid)
: alreadyTakenNames;
return createContentTypeSchema(takenNames, reservedNames.models);
const contentTypeShape = createContentTypeSchema(takenNames, reservedNames.models);
return extensions.makeValidator(
['contentType'],
contentTypeShape,
takenNames,
reservedNames.models
);
},
form: {
base(data = {}, type, step, actionType) {
@ -110,19 +92,8 @@ const forms = {
},
advanced(data, type, step, actionType, attributes, extensions) {
const baseForm = contentTypeForm.advanced.default().items;
const customForms = [];
extensions.contentType.forEach(({ form }) => {
if (form.advanced) {
const blocksToAdd = form.advanced(data, type, step, actionType, attributes);
blocksToAdd.forEach(block => {
customForms.push(block);
});
}
});
return { items: [...baseForm, ...customForms] };
return extensions.makeAdvancedForm(['contentType'], baseForm);
},
},
},

View File

@ -75,8 +75,8 @@ const FormModal = () => {
strapi: { getPlugin },
} = useStrapi();
const ctbPlugin = getPlugin(pluginId);
const types = ctbPlugin.internals.forms.types;
const inputsFromPlugins = ctbPlugin.internals.forms.components.inputs;
const ctbFormsAPI = ctbPlugin.apis.forms;
const inputsFromPlugins = ctbFormsAPI.components.inputs;
const query = useQuery();
const attributeOptionRef = useRef();
@ -404,7 +404,8 @@ const FormModal = () => {
state.actionType === 'edit',
// currentUID
get(allDataSchema, [...state.pathToSchema, 'uid'], null),
reservedNames
reservedNames,
ctbFormsAPI
);
// Check form validity for component
@ -415,7 +416,8 @@ const FormModal = () => {
modifiedData.category || '',
reservedNames,
state.actionType === 'edit',
get(allDataSchema, [...state.pathToSchema, 'uid'], null)
get(allDataSchema, [...state.pathToSchema, 'uid'], null),
ctbFormsAPI
);
// Check for validity for creating a component
@ -426,7 +428,8 @@ const FormModal = () => {
schema = forms.component.schema(
Object.keys(components),
get(modifiedData, 'componentToCreate.category', ''),
reservedNames
reservedNames,
ctbFormsAPI
);
// Check form validity for creating a 'common attribute'
@ -465,10 +468,10 @@ const FormModal = () => {
reservedNames,
alreadyTakenTargetContentTypeAttributes,
{ modifiedData, initialData },
types
ctbFormsAPI
);
} else if (isEditingCategory) {
schema = forms.editCategory.schema(allComponentsCategories, initialData);
schema = forms.editCategory.schema(allComponentsCategories, initialData, ctbFormsAPI);
} else {
// The user is either in the addComponentToDynamicZone modal or
// in step 1 of the add component (modalType=attribute&attributeType=component) but not creating a component
@ -478,7 +481,8 @@ const FormModal = () => {
schema = forms.component.schema(
Object.keys(components),
get(modifiedData, 'componentToCreate.category', ''),
reservedNames
reservedNames,
ctbFormsAPI
);
} else {
// The form is valid
@ -1208,7 +1212,7 @@ const FormModal = () => {
state.step,
state.actionType,
attributes,
types
ctbFormsAPI
).items.map((row, index) => {
return (
<div className="row" key={index}>

View File

@ -16,6 +16,7 @@ import trads from './translations';
import pluginPermissions from './permissions';
import pluginId from './pluginId';
import reducers from './reducers';
import formsAPI from './utils/formAPI';
export default strapi => {
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
@ -73,41 +74,8 @@ export default strapi => {
],
},
// Internal APIs exposed by the CTB for the other plugins to use
internals: {
forms: {
components: {
inputs: {},
add({ id, component }) {
if (!this.inputs[id]) {
this.inputs[id] = component;
}
},
},
types: {
attribute: {
test: [],
},
contentType: [],
component: [],
},
extendContentType(extension) {
this.types.contentType.push(extension);
},
extendFields(fields, extension) {
const formType = this.types.attribute;
fields.forEach(field => {
let currentField = formType[field];
if (currentField) {
currentField.push(extension);
} else {
formType[field] = [extension];
}
});
},
},
apis: {
forms: formsAPI,
},
};

View File

@ -0,0 +1,94 @@
import { get } from 'lodash';
const formsAPI = {
components: {
inputs: {},
add({ id, component }) {
if (!this.inputs[id]) {
this.inputs[id] = component;
}
},
},
types: {
attribute: {
// test: {
// validators: [],
// form: {
// advanced: [
// /* cb */
// ],
// base: [
// /* cb */
// ],
// },
// },
},
contentType: {
validators: [],
form: {
advanced: [],
base: [],
},
},
component: {
validators: [],
form: {
advanced: [],
base: [],
},
},
},
extendContentType({ validator, form: { advanced, base } }) {
const { contentType } = this.types;
contentType.validators.push(validator);
contentType.form.advanced.push(advanced);
contentType.form.base.push(base);
},
extendFields(fields, { validator, form: { advanced, base } }) {
const formType = this.types.attribute;
fields.forEach(field => {
if (!formType[field]) {
formType[field] = {
validators: [],
form: {
advanced: [
/* cb */
],
base: [
/* cb */
],
},
};
formType[field].validators.push(validator);
formType[field].form.advanced.push(advanced);
formType[field].form.base.push(base);
}
});
},
makeAdvancedForm(target, initSections, ...args) {
const sectionsToAdd = get(this.types, [...target, 'form', 'advanced'], []).reduce(
(acc, current) => {
const sections = current(args);
return [...acc, ...sections];
},
[]
);
return { items: [...initSections, ...sectionsToAdd] };
},
makeValidator(target, initShape, ...args) {
const validators = get(this.types, [...target, 'validators'], []).reduce((acc, current) => {
const validator = current(args);
return { ...acc, ...validator };
}, {});
return initShape.shape(validators);
},
};
export default formsAPI;

View File

@ -45,15 +45,15 @@ export default strapi => {
trads,
boot(app) {
const ctbPlugin = app.getPlugin('content-type-builder');
const ctbForms = ctbPlugin.internals.forms;
if (ctbPlugin) {
ctbForms.components.add({ id: 'localesPicker', component: () => 'locale picker' });
const ctbFormsAPI = ctbPlugin.apis.forms;
ctbFormsAPI.components.add({ id: 'localesPicker', component: () => 'locale picker' });
ctbForms.extendContentType({
validator: {
i18n: yup.bool().required(),
},
ctbFormsAPI.extendContentType({
validator: () => ({
i18n: yup.bool(),
}),
form: {
advanced() {
return [
@ -70,10 +70,10 @@ export default strapi => {
},
});
ctbForms.extendFields(['text', 'string'], {
validator: {
ctbFormsAPI.extendFields(['text', 'string'], {
validator: () => ({
localize: yup.bool(),
},
}),
form: {
advanced(args) {
console.log('advanced', args);