mirror of
https://github.com/strapi/strapi.git
synced 2025-08-07 08:16:35 +00:00
Merge branch 'master' into colors-fix
This commit is contained in:
commit
026078566a
@ -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');
|
||||
|
@ -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;
|
@ -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.",
|
||||
|
@ -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 = {
|
||||
|
@ -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(),
|
||||
};
|
||||
|
@ -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 });
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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]));
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user