mirror of
https://github.com/strapi/strapi.git
synced 2025-08-06 07:50:02 +00:00
Merge branch 'custom-fields/add-custom-field-attribute' of github.com:strapi/strapi into add-custom-field-attribute/error-handling
This commit is contained in:
commit
b3384fb6e8
@ -49,10 +49,21 @@ export default {
|
|||||||
id: 'color-picker.color.format.label',
|
id: 'color-picker.color.format.label',
|
||||||
defaultMessage: 'Color format',
|
defaultMessage: 'Color format',
|
||||||
},
|
},
|
||||||
name: 'options.color-picker.format',
|
name: 'options.format',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
value: 'hex',
|
value: 'hex',
|
||||||
options: [
|
options: [
|
||||||
|
{
|
||||||
|
key: '__null_reset_value__',
|
||||||
|
value: '',
|
||||||
|
metadatas: {
|
||||||
|
intlLabel: {
|
||||||
|
id: 'color-picker.color.format.placeholder',
|
||||||
|
defaultMessage: 'Select a format',
|
||||||
|
},
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'hex',
|
key: 'hex',
|
||||||
value: 'hex',
|
value: 'hex',
|
||||||
@ -111,13 +122,11 @@ export default {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
validator: args => ({
|
validator: args => ({
|
||||||
'color-picker': yup.object().shape({
|
|
||||||
format: yup.string().required({
|
format: yup.string().required({
|
||||||
id: 'options.color-picker.format.error',
|
id: 'options.color-picker.format.error',
|
||||||
defaultMessage: 'The color format is required',
|
defaultMessage: 'The color format is required',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -173,17 +173,19 @@ const DataManagerProvider = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const addCustomFieldAttribute = (
|
const addCustomFieldAttribute = ({ attributeToSet, forTarget, targetUid, initialAttribute }) => {
|
||||||
|
dispatch({
|
||||||
|
type: ADD_CUSTOM_FIELD_ATTRIBUTE,
|
||||||
attributeToSet,
|
attributeToSet,
|
||||||
forTarget,
|
forTarget,
|
||||||
targetUid,
|
targetUid,
|
||||||
isEditing = false,
|
initialAttribute,
|
||||||
initialAttribute
|
});
|
||||||
) => {
|
};
|
||||||
const actionType = isEditing ? EDIT_CUSTOM_FIELD_ATTRIBUTE : ADD_CUSTOM_FIELD_ATTRIBUTE;
|
|
||||||
|
|
||||||
|
const editCustomFieldAttribute = ({ attributeToSet, forTarget, targetUid, initialAttribute }) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: actionType,
|
type: EDIT_CUSTOM_FIELD_ATTRIBUTE,
|
||||||
attributeToSet,
|
attributeToSet,
|
||||||
forTarget,
|
forTarget,
|
||||||
targetUid,
|
targetUid,
|
||||||
@ -573,6 +575,7 @@ const DataManagerProvider = ({
|
|||||||
deleteCategory,
|
deleteCategory,
|
||||||
deleteData,
|
deleteData,
|
||||||
editCategory,
|
editCategory,
|
||||||
|
editCustomFieldAttribute,
|
||||||
isInDevelopmentMode,
|
isInDevelopmentMode,
|
||||||
initialData,
|
initialData,
|
||||||
isInContentTypeView,
|
isInContentTypeView,
|
||||||
|
@ -36,7 +36,39 @@ const findAttributeIndex = (schema, attributeToFind) => {
|
|||||||
return schema.schema.attributes.findIndex(({ name }) => name === attributeToFind);
|
return schema.schema.attributes.findIndex(({ name }) => name === attributeToFind);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAddAttributeUpdate = (action, state) => {
|
const reducer = (state = initialState, action) =>
|
||||||
|
// eslint-disable-next-line consistent-return
|
||||||
|
produce(state, draftState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case actions.ADD_CUSTOM_FIELD_ATTRIBUTE: {
|
||||||
|
const {
|
||||||
|
attributeToSet: { name, ...rest },
|
||||||
|
forTarget,
|
||||||
|
targetUid,
|
||||||
|
} = action;
|
||||||
|
|
||||||
|
const pathToDataToEdit = ['component', 'contentType'].includes(forTarget)
|
||||||
|
? [forTarget]
|
||||||
|
: [forTarget, targetUid];
|
||||||
|
|
||||||
|
const currentAttributes = get(
|
||||||
|
state,
|
||||||
|
['modifiedData', ...pathToDataToEdit, 'schema', 'attributes'],
|
||||||
|
[]
|
||||||
|
).slice();
|
||||||
|
|
||||||
|
// Add the createdAttribute
|
||||||
|
const updatedAttributes = [...currentAttributes, { ...rest, name }];
|
||||||
|
|
||||||
|
set(
|
||||||
|
draftState,
|
||||||
|
['modifiedData', ...pathToDataToEdit, 'schema', 'attributes'],
|
||||||
|
updatedAttributes
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case actions.ADD_ATTRIBUTE: {
|
||||||
const {
|
const {
|
||||||
attributeToSet: { name, ...rest },
|
attributeToSet: { name, ...rest },
|
||||||
forTarget,
|
forTarget,
|
||||||
@ -57,45 +89,6 @@ const getAddAttributeUpdate = (action, state) => {
|
|||||||
// Add the createdAttribute
|
// Add the createdAttribute
|
||||||
const updatedAttributes = [...currentAttributes, { ...rest, name }];
|
const updatedAttributes = [...currentAttributes, { ...rest, name }];
|
||||||
|
|
||||||
return { pathToDataToEdit, updatedAttributes, attributeToSet: { ...rest, name } };
|
|
||||||
};
|
|
||||||
|
|
||||||
const getEditAttributeUpdate = (action, state) => {
|
|
||||||
const { forTarget, targetUid, initialAttribute } = action;
|
|
||||||
|
|
||||||
const initialAttributeName = initialAttribute.name;
|
|
||||||
const pathToDataToEdit = ['component', 'contentType'].includes(forTarget)
|
|
||||||
? [forTarget]
|
|
||||||
: [forTarget, targetUid];
|
|
||||||
|
|
||||||
const initialAttributeIndex = findAttributeIndex(
|
|
||||||
get(state, ['modifiedData', ...pathToDataToEdit]),
|
|
||||||
initialAttributeName
|
|
||||||
);
|
|
||||||
|
|
||||||
return { pathToDataToEdit, initialAttributeIndex };
|
|
||||||
};
|
|
||||||
|
|
||||||
const reducer = (state = initialState, action) =>
|
|
||||||
// eslint-disable-next-line consistent-return
|
|
||||||
produce(state, draftState => {
|
|
||||||
switch (action.type) {
|
|
||||||
case actions.ADD_CUSTOM_FIELD_ATTRIBUTE: {
|
|
||||||
const { pathToDataToEdit, updatedAttributes } = getAddAttributeUpdate(action, state);
|
|
||||||
set(
|
|
||||||
draftState,
|
|
||||||
['modifiedData', ...pathToDataToEdit, 'schema', 'attributes'],
|
|
||||||
updatedAttributes
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case actions.ADD_ATTRIBUTE: {
|
|
||||||
const {
|
|
||||||
pathToDataToEdit,
|
|
||||||
updatedAttributes,
|
|
||||||
attributeToSet: { name, ...rest },
|
|
||||||
} = getAddAttributeUpdate(action, state);
|
|
||||||
|
|
||||||
set(
|
set(
|
||||||
draftState,
|
draftState,
|
||||||
['modifiedData', ...pathToDataToEdit, 'schema', 'attributes'],
|
['modifiedData', ...pathToDataToEdit, 'schema', 'attributes'],
|
||||||
@ -270,9 +263,17 @@ const reducer = (state = initialState, action) =>
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case actions.EDIT_CUSTOM_FIELD_ATTRIBUTE: {
|
case actions.EDIT_CUSTOM_FIELD_ATTRIBUTE: {
|
||||||
const { attributeToSet } = action;
|
const { forTarget, targetUid, initialAttribute, attributeToSet } = action;
|
||||||
|
|
||||||
const { pathToDataToEdit, initialAttributeIndex } = getEditAttributeUpdate(action, state);
|
const initialAttributeName = initialAttribute.name;
|
||||||
|
const pathToDataToEdit = ['component', 'contentType'].includes(forTarget)
|
||||||
|
? [forTarget]
|
||||||
|
: [forTarget, targetUid];
|
||||||
|
|
||||||
|
const initialAttributeIndex = findAttributeIndex(
|
||||||
|
get(state, ['modifiedData', ...pathToDataToEdit]),
|
||||||
|
initialAttributeName
|
||||||
|
);
|
||||||
|
|
||||||
set(
|
set(
|
||||||
draftState,
|
draftState,
|
||||||
@ -285,10 +286,20 @@ const reducer = (state = initialState, action) =>
|
|||||||
case actions.EDIT_ATTRIBUTE: {
|
case actions.EDIT_ATTRIBUTE: {
|
||||||
const {
|
const {
|
||||||
attributeToSet: { name, ...rest },
|
attributeToSet: { name, ...rest },
|
||||||
|
forTarget,
|
||||||
|
targetUid,
|
||||||
initialAttribute,
|
initialAttribute,
|
||||||
} = action;
|
} = action;
|
||||||
|
|
||||||
const { pathToDataToEdit, initialAttributeIndex } = getEditAttributeUpdate(action, state);
|
const initialAttributeName = initialAttribute.name;
|
||||||
|
const pathToDataToEdit = ['component', 'contentType'].includes(forTarget)
|
||||||
|
? [forTarget]
|
||||||
|
: [forTarget, targetUid];
|
||||||
|
|
||||||
|
const initialAttributeIndex = findAttributeIndex(
|
||||||
|
get(state, ['modifiedData', ...pathToDataToEdit]),
|
||||||
|
initialAttributeName
|
||||||
|
);
|
||||||
|
|
||||||
const isEditingRelation = rest.type === 'relation';
|
const isEditingRelation = rest.type === 'relation';
|
||||||
|
|
||||||
|
@ -7,16 +7,9 @@ import { createComponentSchema, componentForm } from '../component';
|
|||||||
import { dynamiczoneForm } from '../dynamicZone';
|
import { dynamiczoneForm } from '../dynamicZone';
|
||||||
import { nameField } from '../attributes/nameField';
|
import { nameField } from '../attributes/nameField';
|
||||||
import addItemsToFormSection from './utils/addItemsToFormSection';
|
import addItemsToFormSection from './utils/addItemsToFormSection';
|
||||||
|
import getUsedAttributeNames from './utils/getUsedAttributeNames';
|
||||||
import getTrad from '../../../utils/getTrad';
|
import getTrad from '../../../utils/getTrad';
|
||||||
|
|
||||||
const getUsedAttributeNames = (attributes, schemaData) => {
|
|
||||||
return attributes
|
|
||||||
.filter(({ name }) => {
|
|
||||||
return name !== schemaData.initialData.name;
|
|
||||||
})
|
|
||||||
.map(({ name }) => name);
|
|
||||||
};
|
|
||||||
|
|
||||||
const forms = {
|
const forms = {
|
||||||
customField: {
|
customField: {
|
||||||
schema({
|
schema({
|
||||||
@ -68,7 +61,6 @@ const forms = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (injectedInputs) {
|
if (injectedInputs) {
|
||||||
// TODO: Discuss how to handle settings from other plugins
|
|
||||||
const extendedSettings = {
|
const extendedSettings = {
|
||||||
sectionTitle: {
|
sectionTitle: {
|
||||||
id: getTrad('modalForm.custom-fields.advanced.settings.extended'),
|
id: getTrad('modalForm.custom-fields.advanced.settings.extended'),
|
||||||
|
@ -15,7 +15,7 @@ const addItemsToFormSection = (formTypeOptions, sections) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, when no sectionTitle is present or sectionTitle has a value (including null),
|
// Otherwise, when sectionTitle has a value (including null),
|
||||||
// add the item as a new section
|
// add the item as a new section
|
||||||
sections.push(item);
|
sections.push(item);
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {array} attributes The attributes found on the dataManager's modifiedData object
|
||||||
|
* @param {object} schemaData The modifiedData and SchemaData objects from the reducer state
|
||||||
|
* @returns A list of names already being used
|
||||||
|
*/
|
||||||
|
const getUsedAttributeNames = (attributes, schemaData) => {
|
||||||
|
return attributes
|
||||||
|
.filter(({ name }) => {
|
||||||
|
return name !== schemaData.initialData.name;
|
||||||
|
})
|
||||||
|
.map(({ name }) => name);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getUsedAttributeNames;
|
@ -116,6 +116,7 @@ const FormModal = () => {
|
|||||||
deleteCategory,
|
deleteCategory,
|
||||||
deleteData,
|
deleteData,
|
||||||
editCategory,
|
editCategory,
|
||||||
|
editCustomFieldAttribute,
|
||||||
submitData,
|
submitData,
|
||||||
modifiedData: allDataSchema,
|
modifiedData: allDataSchema,
|
||||||
nestedComponents,
|
nestedComponents,
|
||||||
@ -347,7 +348,6 @@ const FormModal = () => {
|
|||||||
ctbFormsAPI,
|
ctbFormsAPI,
|
||||||
customFieldValidator: customField.options.validator,
|
customFieldValidator: customField.options.validator,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check for validity for creating a component
|
// Check for validity for creating a component
|
||||||
// This is happening when the user creates a component "on the fly"
|
// This is happening when the user creates a component "on the fly"
|
||||||
// Since we temporarily store the component info in another object
|
// Since we temporarily store the component info in another object
|
||||||
@ -550,13 +550,18 @@ const FormModal = () => {
|
|||||||
// Add/edit a field to a content type
|
// Add/edit a field to a content type
|
||||||
// Add/edit a field to a created component (the end modal is not step 2)
|
// Add/edit a field to a created component (the end modal is not step 2)
|
||||||
} else if (isCreatingCustomFieldAttribute) {
|
} else if (isCreatingCustomFieldAttribute) {
|
||||||
addCustomFieldAttribute(
|
const customFieldAttributeUpdate = {
|
||||||
{ ...modifiedData, customField: customFieldUid },
|
attributeToSet: { ...modifiedData, customField: customFieldUid },
|
||||||
forTarget,
|
forTarget,
|
||||||
targetUid,
|
targetUid,
|
||||||
actionType === 'edit',
|
initialAttribute: initialData,
|
||||||
initialData
|
};
|
||||||
);
|
|
||||||
|
if (actionType === 'edit') {
|
||||||
|
editCustomFieldAttribute(customFieldAttributeUpdate);
|
||||||
|
} else {
|
||||||
|
addCustomFieldAttribute(customFieldAttributeUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
if (shouldContinue) {
|
if (shouldContinue) {
|
||||||
onNavigateToChooseAttributeModal({
|
onNavigateToChooseAttributeModal({
|
||||||
@ -1050,10 +1055,10 @@ const FormModal = () => {
|
|||||||
onSubmitCreateContentType={handleSubmit}
|
onSubmitCreateContentType={handleSubmit}
|
||||||
onSubmitCreateDz={handleSubmit}
|
onSubmitCreateDz={handleSubmit}
|
||||||
onSubmitEditAttribute={handleSubmit}
|
onSubmitEditAttribute={handleSubmit}
|
||||||
onSubmitEditCusomFieldAttribute={handleSubmit}
|
|
||||||
onSubmitEditCategory={handleSubmit}
|
onSubmitEditCategory={handleSubmit}
|
||||||
onSubmitEditComponent={handleSubmit}
|
onSubmitEditComponent={handleSubmit}
|
||||||
onSubmitEditContentType={handleSubmit}
|
onSubmitEditContentType={handleSubmit}
|
||||||
|
onSubmitEditCustomFieldAttribute={handleSubmit}
|
||||||
onSubmitEditDz={handleSubmit}
|
onSubmitEditDz={handleSubmit}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
import addItemsToFormSections from '../forms/utils/addItemsToFormSection';
|
||||||
|
|
||||||
|
describe('addItemsToFormSection', () => {
|
||||||
|
it('adds items to the default section', () => {
|
||||||
|
const sections = [{ sectionTitle: null, items: [] }];
|
||||||
|
const itemsToAdd = [
|
||||||
|
{
|
||||||
|
intlLabel: {
|
||||||
|
id: 'color-picker.color.format.label',
|
||||||
|
defaultMessage: 'Color format',
|
||||||
|
},
|
||||||
|
name: 'options.color-picker.format',
|
||||||
|
type: 'select',
|
||||||
|
value: 'hex',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
key: 'hex',
|
||||||
|
value: 'hex',
|
||||||
|
metadatas: {
|
||||||
|
intlLabel: {
|
||||||
|
id: 'color-picker.color.format.hex',
|
||||||
|
defaultMessage: 'Hexadecimal',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'rgba',
|
||||||
|
value: 'rgba',
|
||||||
|
metadatas: {
|
||||||
|
intlLabel: {
|
||||||
|
id: 'color-picker.color.format.rgba',
|
||||||
|
defaultMessage: 'RGBA',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
addItemsToFormSections(itemsToAdd, sections);
|
||||||
|
|
||||||
|
expect(sections.length).toBe(1);
|
||||||
|
expect(sections[0].items.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds the item as a new section', () => {
|
||||||
|
const sections = [{ sectionTitle: null, items: [] }];
|
||||||
|
const itemsToAdd = [
|
||||||
|
{
|
||||||
|
sectionTitle: null,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
intlLabel: {
|
||||||
|
id: 'color-picker.color.format.label',
|
||||||
|
defaultMessage: 'Color format',
|
||||||
|
},
|
||||||
|
name: 'options.color-picker.format',
|
||||||
|
type: 'select',
|
||||||
|
value: 'hex',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
key: 'hex',
|
||||||
|
value: 'hex',
|
||||||
|
metadatas: {
|
||||||
|
intlLabel: {
|
||||||
|
id: 'color-picker.color.format.hex',
|
||||||
|
defaultMessage: 'Hexadecimal',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'rgba',
|
||||||
|
value: 'rgba',
|
||||||
|
metadatas: {
|
||||||
|
intlLabel: {
|
||||||
|
id: 'color-picker.color.format.rgba',
|
||||||
|
defaultMessage: 'RGBA',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
addItemsToFormSections(itemsToAdd, sections);
|
||||||
|
|
||||||
|
expect(sections.length).toBe(2);
|
||||||
|
});
|
||||||
|
});
|
@ -38,10 +38,10 @@ const FormModalEndActions = ({
|
|||||||
onSubmitCreateComponent,
|
onSubmitCreateComponent,
|
||||||
onSubmitCreateDz,
|
onSubmitCreateDz,
|
||||||
onSubmitEditAttribute,
|
onSubmitEditAttribute,
|
||||||
onSubmitEditCusomFieldAttribute,
|
|
||||||
onSubmitEditCategory,
|
onSubmitEditCategory,
|
||||||
onSubmitEditComponent,
|
onSubmitEditComponent,
|
||||||
onSubmitEditContentType,
|
onSubmitEditContentType,
|
||||||
|
onSubmitEditCustomFieldAttribute,
|
||||||
onSubmitEditDz,
|
onSubmitEditDz,
|
||||||
}) => {
|
}) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
@ -394,7 +394,7 @@ const FormModalEndActions = ({
|
|||||||
onClick={e => {
|
onClick={e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
onSubmitEditCusomFieldAttribute(e, true);
|
onSubmitEditCustomFieldAttribute(e, true);
|
||||||
}}
|
}}
|
||||||
startIcon={<Plus />}
|
startIcon={<Plus />}
|
||||||
>
|
>
|
||||||
@ -409,7 +409,7 @@ const FormModalEndActions = ({
|
|||||||
onClick={e => {
|
onClick={e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
onSubmitEditCusomFieldAttribute(e, false);
|
onSubmitEditCustomFieldAttribute(e, false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{formatMessage({
|
{formatMessage({
|
||||||
@ -455,10 +455,10 @@ FormModalEndActions.propTypes = {
|
|||||||
onSubmitCreateComponent: PropTypes.func.isRequired,
|
onSubmitCreateComponent: PropTypes.func.isRequired,
|
||||||
onSubmitCreateDz: PropTypes.func.isRequired,
|
onSubmitCreateDz: PropTypes.func.isRequired,
|
||||||
onSubmitEditAttribute: PropTypes.func.isRequired,
|
onSubmitEditAttribute: PropTypes.func.isRequired,
|
||||||
onSubmitEditCusomFieldAttribute: PropTypes.func.isRequired,
|
|
||||||
onSubmitEditCategory: PropTypes.func.isRequired,
|
onSubmitEditCategory: PropTypes.func.isRequired,
|
||||||
onSubmitEditComponent: PropTypes.func.isRequired,
|
onSubmitEditComponent: PropTypes.func.isRequired,
|
||||||
onSubmitEditContentType: PropTypes.func.isRequired,
|
onSubmitEditContentType: PropTypes.func.isRequired,
|
||||||
|
onSubmitEditCustomFieldAttribute: PropTypes.func.isRequired,
|
||||||
onSubmitEditDz: PropTypes.func.isRequired,
|
onSubmitEditDz: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ const FormModalNavigationProvider = ({ children }) => {
|
|||||||
return {
|
return {
|
||||||
...prevState,
|
...prevState,
|
||||||
actionType: 'create',
|
actionType: 'create',
|
||||||
// TODO: Create a new modalType on EXPANSION-245
|
|
||||||
modalType: 'customField',
|
modalType: 'customField',
|
||||||
attributeType,
|
attributeType,
|
||||||
customFieldUid,
|
customFieldUid,
|
||||||
|
@ -17,14 +17,6 @@ const FormModalSubHeader = ({
|
|||||||
customField,
|
customField,
|
||||||
}) => {
|
}) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const type =
|
|
||||||
modalType === 'customField'
|
|
||||||
? upperFirst(formatMessage(customField.intlLabel))
|
|
||||||
: upperFirst(
|
|
||||||
formatMessage({
|
|
||||||
id: getTrad(`attribute.${attributeType}`),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Typography as="h2" variant="beta">
|
<Typography as="h2" variant="beta">
|
||||||
@ -40,7 +32,9 @@ const FormModalSubHeader = ({
|
|||||||
defaultMessage: 'Add new field',
|
defaultMessage: 'Add new field',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type,
|
type: upperFirst(
|
||||||
|
formatMessage(customField?.intlLabel ?? { id: getTrad(`attribute.${attributeType}`) })
|
||||||
|
),
|
||||||
name: upperFirst(attributeName),
|
name: upperFirst(attributeName),
|
||||||
step,
|
step,
|
||||||
}
|
}
|
||||||
|
@ -88,8 +88,12 @@ const formsAPI = {
|
|||||||
return sectionsToAdd;
|
return sectionsToAdd;
|
||||||
},
|
},
|
||||||
|
|
||||||
makeCustomFieldValidator(initShape, validator, ...validatorArgs) {
|
makeCustomFieldValidator(attributeShape, validator, ...validatorArgs) {
|
||||||
return initShape.shape({ options: yup.object().shape(validator(validatorArgs)) });
|
// When no validator, return the attribute shape
|
||||||
|
if (!validator) return attributeShape;
|
||||||
|
|
||||||
|
// Otherwise extend the shape with the provided validator
|
||||||
|
return attributeShape.shape({ options: yup.object().shape(validator(validatorArgs)) });
|
||||||
},
|
},
|
||||||
|
|
||||||
makeValidator(target, initShape, ...args) {
|
makeValidator(target, initShape, ...args) {
|
||||||
|
@ -4,7 +4,6 @@ const _ = require('lodash');
|
|||||||
|
|
||||||
const { formatAttributes, replaceTemporaryUIDs } = require('../utils/attributes');
|
const { formatAttributes, replaceTemporaryUIDs } = require('../utils/attributes');
|
||||||
const createBuilder = require('./schema-builder');
|
const createBuilder = require('./schema-builder');
|
||||||
const convertCustomFieldType = require('./utils/convert-custom-field-type');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a component attributes
|
* Formats a component attributes
|
||||||
@ -42,11 +41,9 @@ const createComponent = async ({ component, components = [] }) => {
|
|||||||
const uidMap = builder.createNewComponentUIDMap(components);
|
const uidMap = builder.createNewComponentUIDMap(components);
|
||||||
const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
|
const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
|
||||||
|
|
||||||
convertCustomFieldType(component.attributes);
|
|
||||||
const newComponent = builder.createComponent(replaceTmpUIDs(component));
|
const newComponent = builder.createComponent(replaceTmpUIDs(component));
|
||||||
|
|
||||||
components.forEach(component => {
|
components.forEach(component => {
|
||||||
convertCustomFieldType(component.attributes);
|
|
||||||
if (!_.has(component, 'uid')) {
|
if (!_.has(component, 'uid')) {
|
||||||
return builder.createComponent(replaceTmpUIDs(component));
|
return builder.createComponent(replaceTmpUIDs(component));
|
||||||
}
|
}
|
||||||
@ -70,14 +67,12 @@ const editComponent = async (uid, { component, components = [] }) => {
|
|||||||
const uidMap = builder.createNewComponentUIDMap(components);
|
const uidMap = builder.createNewComponentUIDMap(components);
|
||||||
const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
|
const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
|
||||||
|
|
||||||
convertCustomFieldType(component.attributes);
|
|
||||||
const updatedComponent = builder.editComponent({
|
const updatedComponent = builder.editComponent({
|
||||||
uid,
|
uid,
|
||||||
...replaceTmpUIDs(component),
|
...replaceTmpUIDs(component),
|
||||||
});
|
});
|
||||||
|
|
||||||
components.forEach(component => {
|
components.forEach(component => {
|
||||||
convertCustomFieldType(component.attributes);
|
|
||||||
if (!_.has(component, 'uid')) {
|
if (!_.has(component, 'uid')) {
|
||||||
return builder.createComponent(replaceTmpUIDs(component));
|
return builder.createComponent(replaceTmpUIDs(component));
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ const { ApplicationError } = require('@strapi/utils').errors;
|
|||||||
const { formatAttributes, replaceTemporaryUIDs } = require('../utils/attributes');
|
const { formatAttributes, replaceTemporaryUIDs } = require('../utils/attributes');
|
||||||
const createBuilder = require('./schema-builder');
|
const createBuilder = require('./schema-builder');
|
||||||
const { coreUids, pluginsUids } = require('./constants');
|
const { coreUids, pluginsUids } = require('./constants');
|
||||||
const convertCustomFieldType = require('./utils/convert-custom-field-type');
|
|
||||||
|
|
||||||
const isContentTypeVisible = model =>
|
const isContentTypeVisible = model =>
|
||||||
getOr(true, 'pluginOptions.content-type-builder.visible', model) === true;
|
getOr(true, 'pluginOptions.content-type-builder.visible', model) === true;
|
||||||
@ -85,7 +84,6 @@ const createContentType = async ({ contentType, components = [] }, options = {})
|
|||||||
|
|
||||||
const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
|
const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
|
||||||
|
|
||||||
convertCustomFieldType(contentType.attributes);
|
|
||||||
const newContentType = builder.createContentType(replaceTmpUIDs(contentType));
|
const newContentType = builder.createContentType(replaceTmpUIDs(contentType));
|
||||||
|
|
||||||
// allow components to target the new contentType
|
// allow components to target the new contentType
|
||||||
@ -101,7 +99,6 @@ const createContentType = async ({ contentType, components = [] }, options = {})
|
|||||||
};
|
};
|
||||||
|
|
||||||
components.forEach(component => {
|
components.forEach(component => {
|
||||||
convertCustomFieldType(component.attributes);
|
|
||||||
const options = replaceTmpUIDs(targetContentType(component));
|
const options = replaceTmpUIDs(targetContentType(component));
|
||||||
|
|
||||||
if (!_.has(component, 'uid')) {
|
if (!_.has(component, 'uid')) {
|
||||||
@ -173,15 +170,12 @@ const editContentType = async (uid, { contentType, components = [] }) => {
|
|||||||
const uidMap = builder.createNewComponentUIDMap(components);
|
const uidMap = builder.createNewComponentUIDMap(components);
|
||||||
const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
|
const replaceTmpUIDs = replaceTemporaryUIDs(uidMap);
|
||||||
|
|
||||||
convertCustomFieldType(contentType.attributes);
|
|
||||||
const updatedContentType = builder.editContentType({
|
const updatedContentType = builder.editContentType({
|
||||||
uid,
|
uid,
|
||||||
...replaceTmpUIDs(contentType),
|
...replaceTmpUIDs(contentType),
|
||||||
});
|
});
|
||||||
|
|
||||||
components.forEach(component => {
|
components.forEach(component => {
|
||||||
convertCustomFieldType(component.attributes);
|
|
||||||
|
|
||||||
if (!_.has(component, 'uid')) {
|
if (!_.has(component, 'uid')) {
|
||||||
return builder.createComponent(replaceTmpUIDs(component));
|
return builder.createComponent(replaceTmpUIDs(component));
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ const { nameToSlug, nameToCollectionName } = require('@strapi/utils');
|
|||||||
const { ApplicationError } = require('@strapi/utils').errors;
|
const { ApplicationError } = require('@strapi/utils').errors;
|
||||||
const { isConfigurable } = require('../../utils/attributes');
|
const { isConfigurable } = require('../../utils/attributes');
|
||||||
const createSchemaHandler = require('./schema-handler');
|
const createSchemaHandler = require('./schema-handler');
|
||||||
|
const convertCustomFieldType = require('./utils/convert-custom-field-type');
|
||||||
|
|
||||||
module.exports = function createComponentBuilder() {
|
module.exports = function createComponentBuilder() {
|
||||||
return {
|
return {
|
||||||
@ -32,12 +33,15 @@ module.exports = function createComponentBuilder() {
|
|||||||
* create a component in the tmpComponent map
|
* create a component in the tmpComponent map
|
||||||
*/
|
*/
|
||||||
createComponent(infos) {
|
createComponent(infos) {
|
||||||
|
const { attributes } = infos;
|
||||||
const uid = this.createComponentUID(infos);
|
const uid = this.createComponentUID(infos);
|
||||||
|
|
||||||
if (this.components.has(uid)) {
|
if (this.components.has(uid)) {
|
||||||
throw new ApplicationError('component.alreadyExists');
|
throw new ApplicationError('component.alreadyExists');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
convertCustomFieldType(attributes);
|
||||||
|
|
||||||
const handler = createSchemaHandler({
|
const handler = createSchemaHandler({
|
||||||
dir: path.join(strapi.dirs.components, nameToSlug(infos.category)),
|
dir: path.join(strapi.dirs.components, nameToSlug(infos.category)),
|
||||||
filename: `${nameToSlug(infos.displayName)}.json`,
|
filename: `${nameToSlug(infos.displayName)}.json`,
|
||||||
@ -72,12 +76,13 @@ module.exports = function createComponentBuilder() {
|
|||||||
* create a component in the tmpComponent map
|
* create a component in the tmpComponent map
|
||||||
*/
|
*/
|
||||||
editComponent(infos) {
|
editComponent(infos) {
|
||||||
const { uid } = infos;
|
const { uid, attributes } = infos;
|
||||||
|
|
||||||
if (!this.components.has(uid)) {
|
if (!this.components.has(uid)) {
|
||||||
throw new ApplicationError('component.notFound');
|
throw new ApplicationError('component.notFound');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
convertCustomFieldType(attributes);
|
||||||
const component = this.components.get(uid);
|
const component = this.components.get(uid);
|
||||||
|
|
||||||
const [, nameUID] = uid.split('.');
|
const [, nameUID] = uid.split('.');
|
||||||
|
@ -8,6 +8,7 @@ const { ApplicationError } = require('@strapi/utils').errors;
|
|||||||
const { isRelation, isConfigurable } = require('../../utils/attributes');
|
const { isRelation, isConfigurable } = require('../../utils/attributes');
|
||||||
const { typeKinds } = require('../constants');
|
const { typeKinds } = require('../constants');
|
||||||
const createSchemaHandler = require('./schema-handler');
|
const createSchemaHandler = require('./schema-handler');
|
||||||
|
const convertCustomFieldType = require('./utils/convert-custom-field-type');
|
||||||
|
|
||||||
const reuseUnsetPreviousProperties = (newAttribute, oldAttribute) => {
|
const reuseUnsetPreviousProperties = (newAttribute, oldAttribute) => {
|
||||||
_.defaults(
|
_.defaults(
|
||||||
@ -71,12 +72,15 @@ module.exports = function createComponentBuilder() {
|
|||||||
* @returns {object} new content type
|
* @returns {object} new content type
|
||||||
*/
|
*/
|
||||||
createContentType(infos) {
|
createContentType(infos) {
|
||||||
|
const { attributes } = infos;
|
||||||
const uid = createContentTypeUID(infos);
|
const uid = createContentTypeUID(infos);
|
||||||
|
|
||||||
if (this.contentTypes.has(uid)) {
|
if (this.contentTypes.has(uid)) {
|
||||||
throw new ApplicationError('contentType.alreadyExists');
|
throw new ApplicationError('contentType.alreadyExists');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
convertCustomFieldType(attributes);
|
||||||
|
|
||||||
const contentType = createSchemaHandler({
|
const contentType = createSchemaHandler({
|
||||||
modelName: infos.singularName,
|
modelName: infos.singularName,
|
||||||
dir: path.join(strapi.dirs.api, infos.singularName, 'content-types', infos.singularName),
|
dir: path.join(strapi.dirs.api, infos.singularName, 'content-types', infos.singularName),
|
||||||
@ -124,12 +128,14 @@ module.exports = function createComponentBuilder() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
editContentType(infos) {
|
editContentType(infos) {
|
||||||
const { uid } = infos;
|
const { uid, attributes } = infos;
|
||||||
|
|
||||||
if (!this.contentTypes.has(uid)) {
|
if (!this.contentTypes.has(uid)) {
|
||||||
throw new ApplicationError('contentType.notFound');
|
throw new ApplicationError('contentType.notFound');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
convertCustomFieldType(attributes);
|
||||||
|
|
||||||
const contentType = this.contentTypes.get(uid);
|
const contentType = this.contentTypes.get(uid);
|
||||||
|
|
||||||
const oldAttributes = contentType.schema.attributes;
|
const oldAttributes = contentType.schema.attributes;
|
||||||
|
@ -26,6 +26,16 @@ describe('format attributes', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
components: {
|
||||||
|
'default.test': {
|
||||||
|
attributes: {
|
||||||
|
color: {
|
||||||
|
type: 'customField',
|
||||||
|
customField: 'plugin::mycustomfields.color',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
convertCustomFieldType(global.strapi);
|
convertCustomFieldType(global.strapi);
|
||||||
@ -42,6 +52,16 @@ describe('format attributes', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
components: {
|
||||||
|
'default.test': {
|
||||||
|
attributes: {
|
||||||
|
color: {
|
||||||
|
type: 'text',
|
||||||
|
customField: 'plugin::mycustomfields.color',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(global.strapi).toEqual(expected);
|
expect(global.strapi).toEqual(expected);
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const convertCustomFieldType = strapi => {
|
const convertCustomFieldType = strapi => {
|
||||||
const allSchemasAttributes = Object.values(strapi.contentTypes).map(schema => schema.attributes);
|
const allContentTypeSchemaAttributes = Object.values(strapi.contentTypes).map(
|
||||||
|
schema => schema.attributes
|
||||||
|
);
|
||||||
|
const allComponentSchemaAttributes = Object.values(strapi.components).map(
|
||||||
|
schema => schema.attributes
|
||||||
|
);
|
||||||
|
const allSchemasAttributes = [...allContentTypeSchemaAttributes, ...allComponentSchemaAttributes];
|
||||||
|
|
||||||
for (const schemaAttrbutes of allSchemasAttributes) {
|
for (const schemaAttrbutes of allSchemasAttributes) {
|
||||||
for (const attribute of Object.values(schemaAttrbutes)) {
|
for (const attribute of Object.values(schemaAttrbutes)) {
|
||||||
if (attribute.type === 'customField') {
|
if (attribute.type === 'customField') {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user