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 = {}; injectedComponents = {};
internals = {}; apis = {};
constructor(pluginConf) { constructor(pluginConf) {
this.pluginId = pluginConf.id; this.pluginId = pluginConf.id;
this.decorators = pluginConf.decorators || {}; this.decorators = pluginConf.decorators || {};
this.injectedComponents = pluginConf.injectedComponents || {}; this.injectedComponents = pluginConf.injectedComponents || {};
this.internals = pluginConf.internals || {}; this.apis = pluginConf.apis || {};
} }
decorate(compoName, compo) { decorate(compoName, compo) {

View File

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

View File

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

View File

@ -16,6 +16,7 @@ import trads from './translations';
import pluginPermissions from './permissions'; import pluginPermissions from './permissions';
import pluginId from './pluginId'; import pluginId from './pluginId';
import reducers from './reducers'; import reducers from './reducers';
import formsAPI from './utils/formAPI';
export default strapi => { export default strapi => {
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description; 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 // Internal APIs exposed by the CTB for the other plugins to use
internals: { apis: {
forms: { forms: formsAPI,
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];
}
});
},
},
}, },
}; };

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, trads,
boot(app) { boot(app) {
const ctbPlugin = app.getPlugin('content-type-builder'); const ctbPlugin = app.getPlugin('content-type-builder');
const ctbForms = ctbPlugin.internals.forms;
if (ctbPlugin) { 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({ ctbFormsAPI.extendContentType({
validator: { validator: () => ({
i18n: yup.bool().required(), i18n: yup.bool(),
}, }),
form: { form: {
advanced() { advanced() {
return [ return [
@ -70,10 +70,10 @@ export default strapi => {
}, },
}); });
ctbForms.extendFields(['text', 'string'], { ctbFormsAPI.extendFields(['text', 'string'], {
validator: { validator: () => ({
localize: yup.bool(), localize: yup.bool(),
}, }),
form: { form: {
advanced(args) { advanced(args) {
console.log('advanced', args); console.log('advanced', args);