diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/index.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/index.js index fc4983d680..bd17cb0a9e 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/index.js @@ -1,6 +1,6 @@ import React, { memo, useEffect, useReducer, useState, useRef } from 'react'; import PropTypes from 'prop-types'; -import { camelCase, get, groupBy, set, size, sortBy } from 'lodash'; +import { get, groupBy, set, size } from 'lodash'; import { request, LoadingIndicatorPage, @@ -32,6 +32,7 @@ import { getComponentsToPost, formatMainDataType, getCreatedAndModifiedComponents, + sortContentType, } from './utils/cleanData'; const DataManagerProvider = ({ allIcons, children }) => { @@ -86,7 +87,6 @@ const DataManagerProvider = ({ allIcons, children }) => { }); }) ); - const components = createDataObject(componentsArray); const contentTypes = createDataObject(contentTypesArray); const orderedComponents = orderAllDataAttributesWithImmutable({ @@ -373,18 +373,6 @@ const DataManagerProvider = ({ allIcons, children }) => { }); }; - const sortedContentTypesList = sortBy( - Object.keys(contentTypes) - .map(uid => ({ - name: uid, - title: contentTypes[uid].schema.name, - uid, - to: `/plugins/${pluginId}/content-types/${uid}`, - })) - .filter(obj => obj !== null), - obj => camelCase(obj.title) - ); - const shouldRedirect = () => { const dataSet = isInContentTypeView ? contentTypes : components; @@ -520,7 +508,7 @@ const DataManagerProvider = ({ allIcons, children }) => { removeAttribute, removeComponentFromDynamicZone, setModifiedData, - sortedContentTypesList, + sortedContentTypesList: sortContentType(contentTypes), submitData, toggleModalCancel, updateSchema, diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/tests/reducer_basic_actions.test.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/tests/reducer_basic_actions.test.js index 3a5fb76c29..3a24b1710a 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/tests/reducer_basic_actions.test.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/tests/reducer_basic_actions.test.js @@ -385,6 +385,14 @@ describe('CTB | containers | DataManagerProvider | reducer | basics actions ', ( }, }, }; + const singleTypes = { + 'application::aboutpage.aboutpage': { + uid: 'application::aboutpage.aboutpage', + schema: { + attributes: {}, + }, + }, + }; const expected = initialState .set('components', fromJS(components)) .set('contentTypes', fromJS(contentTypes)) @@ -397,6 +405,7 @@ describe('CTB | containers | DataManagerProvider | reducer | basics actions ', ( type: 'GET_DATA_SUCCEEDED', components, contentTypes, + singleTypes, }) ).toEqual(expected); }); diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/utils/cleanData.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/utils/cleanData.js index 307411ebaa..f03fea7342 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/utils/cleanData.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/utils/cleanData.js @@ -1,4 +1,6 @@ -import { get, has, isEqual, omit } from 'lodash'; +import { get, has, isEqual, omit, sortBy, camelCase } from 'lodash'; + +import pluginId from '../../../pluginId'; import makeUnique from '../../../utils/makeUnique'; const getCreatedAndModifiedComponents = (allComponents, initialComponents) => { @@ -157,9 +159,24 @@ const getComponentsToPost = ( return formattedComponents; }; +const sortContentType = types => + sortBy( + Object.keys(types) + .map(uid => ({ + name: uid, + title: types[uid].schema.name, + uid, + to: `/plugins/${pluginId}/content-types/${uid}`, + kind: types[uid].schema.kind, + })) + .filter(obj => obj !== null), + obj => camelCase(obj.title) + ); + export { formatComponent, getComponentsToPost, getCreatedAndModifiedComponents, formatMainDataType, + sortContentType, }; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/utils/tests/cleanData.test.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/utils/tests/cleanData.test.js index f37b81b47d..bd3ec01268 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/utils/tests/cleanData.test.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/utils/tests/cleanData.test.js @@ -3,6 +3,7 @@ import { formatMainDataType, getComponentsToPost, getCreatedAndModifiedComponents, + sortContentType, } from '../cleanData'; import rawData from './rawData'; import expectedData from './expectedFormattedData'; @@ -207,4 +208,19 @@ describe('CleanData utils', () => { ).toEqual(componentsToFormat.sort()); }); }); + + describe('sortContentType', () => { + it('should return sorted collection types array', () => { + const { sortedContentTypes } = expectedData; + const { + rawData: { contentTypesToSort }, + } = rawData; + + const actual = sortContentType(contentTypesToSort); + expect(actual.sort()).toEqual(sortedContentTypes.sort()); + }); + it('should return an empty array if no content types', () => { + expect(sortContentType({})).toEqual([]); + }); + }); }); diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/utils/tests/expectedFormattedData.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/utils/tests/expectedFormattedData.js index 574ce6074b..bf9c1c382b 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/utils/tests/expectedFormattedData.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/utils/tests/expectedFormattedData.js @@ -96,6 +96,39 @@ const expectedData = { 'default.nested-compo', 'blog.quote', ], + sortedContentTypes: [ + { + uid: 'plugins::myplugins.atest', + name: 'plugins::myplugins.atest', + title: 'plugins::myplugins.atest', + to: + '/plugins/content-type-builder/content-types/plugins::myplugins.atest', + kind: 'collectionType', + }, + { + uid: 'plugins::myplugins.btest', + name: 'plugins::myplugins.btest', + title: 'plugins::myplugins.btest', + to: + '/plugins/content-type-builder/content-types/plugins::myplugins.btest', + kind: 'collectionType', + }, + { + uid: 'plugins::myplugins.ctest', + name: 'plugins::myplugins.ctest', + title: 'plugins::myplugins.ctest', + to: + '/plugins/content-type-builder/content-types/plugins::myplugins.ctest', + kind: 'collectionType', + }, + { + uid: 'plugins::myplugins.test', + name: 'plugins::myplugins.test', + title: 'plugins::myplugins.test', + to: '/plugins/content-type-builder/content-types/plugins::myplugins.test', + kind: 'singleType', + }, + ], components: [ { diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/utils/tests/rawData.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/utils/tests/rawData.js index 7b952bfe57..f10a907277 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/utils/tests/rawData.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/utils/tests/rawData.js @@ -147,6 +147,24 @@ const data = { }, }, }, + contentTypesToSort: { + 'plugins::myplugins.test': { + uid: 'plugins::myplugins.test', + schema: { name: 'plugins::myplugins.test', kind: 'singleType' }, + }, + 'plugins::myplugins.btest': { + uid: 'plugins::myplugins.btest', + schema: { name: 'plugins::myplugins.btest', kind: 'collectionType' }, + }, + 'plugins::myplugins.atest': { + uid: 'plugins::myplugins.atest', + schema: { name: 'plugins::myplugins.atest', kind: 'collectionType' }, + }, + 'plugins::myplugins.ctest': { + uid: 'plugins::myplugins.ctest', + schema: { name: 'plugins::myplugins.ctest', kind: 'collectionType' }, + }, + }, // TODO add test for component // componentToCreate: { diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/index.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/index.js index 094b4c98d0..776811e8d8 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/index.js @@ -86,6 +86,7 @@ const FormModal = () => { const dynamicZoneTarget = query.get('dynamicZoneTarget'); const forTarget = query.get('forTarget'); const modalType = query.get('modalType'); + const contentTypeKind = query.get('kind'); const targetUid = query.get('targetUid'); const settingType = query.get('settingType'); const headerId = query.get('headerId'); @@ -157,6 +158,7 @@ const FormModal = () => { header_info_name_5, header_info_category_5, headerId, + contentTypeKind, }); // Reset all the modification when opening the edit category modal diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/LeftMenu/index.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/LeftMenu/index.js index 3d236db5a4..f7225b1496 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/LeftMenu/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/LeftMenu/index.js @@ -6,7 +6,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { sortBy } from 'lodash'; +import { sortBy, camelCase, upperFirst } from 'lodash'; import { useHistory } from 'react-router-dom'; import { LeftMenuList, useGlobalContext } from 'strapi-helper-plugin'; import pluginId from '../../pluginId'; @@ -32,7 +32,6 @@ function LeftMenu({ wait }) { } = useDataManager(); const { emitEvent, formatMessage } = useGlobalContext(); const { push } = useHistory(); - const componentsData = sortBy( Object.keys(componentsGroupedByCategory).map(category => ({ name: category, @@ -86,18 +85,19 @@ function LeftMenu({ wait }) { ); }; - const handleClickOpenModal = async type => { + const handleClickOpenModal = async (type, kind = '') => { if (canOpenModalCreateCTorComponent()) { - const eventName = - type === 'contentType' - ? 'willCreateContentType' - : 'willCreateComponent'; - - emitEvent(eventName); + emitEvent( + `willCreate${upperFirst( + camelCase(kind === 'singleType' ? kind : type) + )}` + ); await wait(); push({ - search: `modalType=${type}&actionType=create&settingType=base&forTarget=${type}&headerId=${getTrad( + search: `modalType=${type}${ + kind ? `&kind=${kind}` : '' + }&actionType=create&settingType=base&forTarget=${type}&headerId=${getTrad( `modalForm.${type}.header-create` )}&header_icon_name_1=${type}&header_icon_isCustom_1=false&header_label_1=null`, }); @@ -105,7 +105,6 @@ function LeftMenu({ wait }) { displayNotificationCTNotSaved(); } }; - const data = [ { name: 'models', @@ -119,12 +118,35 @@ function LeftMenu({ wait }) { componentProps: { id: `${pluginId}.button.model.create`, onClick: () => { - handleClickOpenModal('contentType'); + handleClickOpenModal('contentType', 'collectionType'); }, }, } : null, - links: sortedContentTypesList, + links: sortedContentTypesList.filter( + contentType => contentType.kind === 'collectionType' + ), + }, + { + name: 'singleTypes', + title: { + id: `${pluginId}.menu.section.single-types.name.`, + }, + searchable: true, + customLink: isInDevelopmentMode + ? { + Component: CustomLink, + componentProps: { + id: `${pluginId}.button.single-types.create`, + onClick: () => { + handleClickOpenModal('contentType', 'singleType'); + }, + }, + } + : null, + links: sortedContentTypesList.filter( + singleType => singleType.kind === 'singleType' + ), }, { name: 'components', diff --git a/packages/strapi-plugin-content-type-builder/admin/src/translations/en.json b/packages/strapi-plugin-content-type-builder/admin/src/translations/en.json index 5637342712..e1d0c0fb5b 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/translations/en.json +++ b/packages/strapi-plugin-content-type-builder/admin/src/translations/en.json @@ -34,6 +34,7 @@ "button.component.add": "Add a component", "button.component.create": "Create new component", "button.model.create": "Create new collection-type", + "button.single-types.create": "Create new single-type", "components.componentSelect.no-component-available": "You have already added all your components", "components.componentSelect.no-component-available.with-search": "There is no component matching your search", "components.componentSelect.value-component": "{number} component selected (type to search for a component)", @@ -111,6 +112,8 @@ "menu.section.components.name.singular": "Component", "menu.section.models.name.plural": "Collection Types", "menu.section.models.name.singular": "Collection Type", + "menu.section.single-types.name.plural": "Single Types", + "menu.section.single-types.name.singular": "Single Type", "modalForm.attribute.form.base.name": "Name", "modalForm.attribute.form.base.name.description": "No space is allowed for the name of the attribute", "modalForm.attribute.text.type-selection": "Type", @@ -120,6 +123,7 @@ "modalForm.components.create-component.category.label": "Select a category or enter a name to create a new one", "modalForm.components.icon.label": "Icon", "modalForm.contentType.header-create": "Create a collection type", + "modalForm.singleType.header-create": "Create a single type", "modalForm.editCategory.base.name.description": "No space is allowed for the name of the category", "modalForm.header-edit": "Edit {name}", "modalForm.header.categories": "Categories", diff --git a/packages/strapi-plugin-content-type-builder/admin/src/translations/fr.json b/packages/strapi-plugin-content-type-builder/admin/src/translations/fr.json index be7edd6cf5..860d727413 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/translations/fr.json +++ b/packages/strapi-plugin-content-type-builder/admin/src/translations/fr.json @@ -10,8 +10,8 @@ "attribute.richtext": "Texte enrichi", "attribute.text": "Texte", "button.attributes.add.another": "Ajouter un autre champ", - "button.component.create": "Créer un Composant", - "button.model.create": "Créer un Type de Collection", + "button.component.create": "Créer un composant", + "button.model.create": "Créer un type de collection", "form.attribute.item.customColumnName": "Nom de colonne personalisée", "form.attribute.item.customColumnName.description": "Pratique pour renommer la colonne de la db dans un format plus comprehensible pour les responses de l'API", "form.attribute.item.defineRelation.fieldName": "Nom du Champ", @@ -44,6 +44,10 @@ "menu.section.components.name.singular": "Composant", "menu.section.models.name.plural": "Modèles", "menu.section.models.name.singular": "Modèle", + "menu.section.single-types.name.plural": "Single Types", + "menu.section.single-types.name.singular": "Single Type", + "modalForm.singleType.header-create": "Créer un single type", + "button.single-types.create": "Créer un single type", "modelPage.attribute.relationWith": "Relation avec", "modelPage.contentHeader.emptyDescription.description": "Il n'y a pas de description", "plugin.description.long": "Modélisez la structure de données de votre API. Créer des nouveaux champs et relations en un instant. Les fichiers se créent et se mettent à jour automatiquement.",