Merge branch 'master' into colors-fix

This commit is contained in:
cyril lopez 2022-01-10 14:45:51 +01:00 committed by GitHub
commit 026078566a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 76 additions and 17 deletions

View File

@ -28,6 +28,7 @@ import retrieveComponentsFromSchema from './utils/retrieveComponentsFromSchema';
import retrieveNestedComponents from './utils/retrieveNestedComponents';
import { retrieveComponentsThatHaveComponents } from './utils/retrieveComponentsThatHaveComponents';
import { getComponentsToPost, formatMainDataType, sortContentType } from './utils/cleanData';
import validateSchema from './utils/validateSchema';
import {
ADD_ATTRIBUTE,
@ -440,6 +441,21 @@ const DataManagerProvider = ({
initialData.contentType
);
const isValidSchema = validateSchema(contentType);
if (!isValidSchema) {
toggleNotification({
type: 'warning',
message: {
id: getTrad('notification.error.dynamiczone-min.validation'),
defaultMessage:
'At least one component is required in a dynamic zone to be able to save a content type',
},
});
return;
}
body.contentType = contentType;
trackUsage('willSaveContentType');

View File

@ -0,0 +1,11 @@
const validateSchema = schema => {
const dynamicZoneAttributes = Object.values(schema.attributes).filter(
({ type }) => type === 'dynamiczone'
);
return dynamicZoneAttributes.every(
({ components }) => Array.isArray(components) && components.length > 0
);
};
export default validateSchema;

View File

@ -166,6 +166,7 @@
"modelPage.attribute.relation-polymorphic": "Relation (polymorphic)",
"modelPage.attribute.relationWith": "Relation with",
"none": "None",
"notification.error.dynamiczone-min.validation": "At least one component is required in a dynamic zone to be able to save a content type",
"notification.info.autoreaload-disable": "The autoReload feature is required to use this plugin. Start your server with `strapi develop`",
"notification.info.creating.notSaved": "Please save your work before creating a new collection type or component",
"plugin.description.long": "Modelize the data structure of your API. Create new fields and relations in just a minute. The files are automatically created and updated in your project.",

View File

@ -43,6 +43,42 @@ describe('Type validators', () => {
});
});
describe('Dynamiczone type validator', () => {
test('Components cannot be empty', () => {
const attributes = {
dz: {
type: 'dynamiczone',
components: [],
},
};
const validator = getTypeValidator(attributes.dz, {
types: ['dynamiczone'],
modelType: 'collectionType',
attributes,
});
expect(validator.isValidSync(attributes.dz)).toBeFalsy();
});
test('Components must have at least one item', () => {
const attributes = {
dz: {
type: 'dynamiczone',
components: ['compoA', 'compoB'],
},
};
const validator = getTypeValidator(attributes.dz, {
types: ['dynamiczone'],
modelType: 'collectionType',
attributes,
});
expect(validator.isValidSync(attributes.dz)).toBeTruthy();
});
});
describe('UID type validator', () => {
test('Target field can be null', () => {
const attributes = {

View File

@ -258,7 +258,8 @@ const getTypeShape = (attribute, { modelType, attributes } = {}) => {
components: yup
.array()
.of(yup.string().required())
.test('isArray', '${path} must be an array', value => Array.isArray(value)),
.test('isArray', '${path} must be an array', value => Array.isArray(value))
.min(1),
min: yup.number(),
max: yup.number(),
};

View File

@ -45,7 +45,7 @@ const Content = ({ appLocales, currentLocale, localizations, readPermissions })
const toggleNotification = useNotification();
const { formatMessage } = useIntl();
const dispatch = useDispatch();
const { allLayoutData, slug } = useCMEditViewDataManager();
const { allLayoutData, initialData, slug } = useCMEditViewDataManager();
const [isLoading, setIsLoading] = useState(false);
const [isOpen, setIsOpen] = useState(false);
const [value, setValue] = useState(options[0]?.value || '');
@ -59,12 +59,15 @@ const Content = ({ appLocales, currentLocale, localizations, readPermissions })
const requestURL = `/content-manager/collection-types/${slug}/${value}`;
setIsLoading(true);
try {
setIsLoading(true);
const { data: response } = await axiosInstance.get(requestURL);
const cleanedData = cleanData(response, allLayoutData, localizations);
['createdBy', 'updatedBy', 'publishedAt', 'id', 'createdAt'].forEach(key => {
if (!initialData[key]) return;
cleanedData[key] = initialData[key];
});
dispatch({ type: 'ContentManager/CrudReducer/GET_DATA_SUCCEEDED', data: cleanedData });

View File

@ -13,15 +13,7 @@ const cleanData = (data, { contentType, components }, initialLocalizations) => {
dataWithoutPasswordsAndRelations.localizations = initialLocalizations;
const fieldsToRemove = [
'createdBy',
'updatedBy',
'publishedAt',
'id',
'_id',
'updatedAt',
'createdAt',
];
const fieldsToRemove = ['createdBy', 'updatedBy', 'publishedAt', 'id', 'updatedAt', 'createdAt'];
const cleanedClonedData = contentManagementUtilRemoveFieldsFromData(
dataWithoutPasswordsAndRelations,

View File

@ -113,9 +113,8 @@ const getNonLocalizedAttributes = model => {
};
const removeId = value => {
if (typeof value === 'object' && (has('id', value) || has('_id', value))) {
if (typeof value === 'object' && has('id', value)) {
delete value.id;
delete value._id;
}
};

View File

@ -114,7 +114,7 @@ describe('i18n - Relation-list route', () => {
});
expect(res.body).toHaveLength(1);
expect(res.body[0]).toStrictEqual(pick(['_id', 'id', 'name'], data.products[1]));
expect(res.body[0]).toStrictEqual(pick(['id', 'name'], data.products[1]));
});
test('Can filter on any locale', async () => {
@ -125,6 +125,6 @@ describe('i18n - Relation-list route', () => {
});
expect(res.body).toHaveLength(1);
expect(res.body[0]).toStrictEqual(pick(['_id', 'id', 'name'], data.products[0]));
expect(res.body[0]).toStrictEqual(pick(['id', 'name'], data.products[0]));
});
});