From 96be97988b84808fb0489b5f9e756d3b7fcd6103 Mon Sep 17 00:00:00 2001 From: Mateusz Ziarko Date: Wed, 24 Feb 2021 23:48:12 +0100 Subject: [PATCH] fix: use uid instead of collection name for linking --- __mocks__/helpers/blog-post.settings.json | 2 +- ...page.settings.json => pages.settings.json} | 4 +- __mocks__/helpers/strapi.js | 4 +- admin/src/components/Item/index.js | 6 ++- admin/src/components/List/index.js | 5 ++- .../components/NavigationItemForm/index.js | 38 +++++++++-------- .../components/NavigationItemPopup/index.js | 5 ++- admin/src/containers/View/index.js | 15 +++++-- admin/src/containers/View/utils/parsers.js | 42 +++++++++---------- package.json | 2 +- services/__tests__/navigation.test.js | 4 +- services/navigation.js | 8 ++-- services/utils/functions.js | 4 ++ 13 files changed, 79 insertions(+), 60 deletions(-) rename __mocks__/helpers/{page.settings.json => pages.settings.json} (88%) diff --git a/__mocks__/helpers/blog-post.settings.json b/__mocks__/helpers/blog-post.settings.json index 1c285517..c9fa71b4 100644 --- a/__mocks__/helpers/blog-post.settings.json +++ b/__mocks__/helpers/blog-post.settings.json @@ -3,7 +3,7 @@ "kind": "collectionType", "collectionName": "blog_posts", "info": { - "name": "Blog posts" + "name": "Blog post" }, "options": { "increments": true, diff --git a/__mocks__/helpers/page.settings.json b/__mocks__/helpers/pages.settings.json similarity index 88% rename from __mocks__/helpers/page.settings.json rename to __mocks__/helpers/pages.settings.json index 1ea5a9d0..c7457af8 100644 --- a/__mocks__/helpers/page.settings.json +++ b/__mocks__/helpers/pages.settings.json @@ -1,9 +1,9 @@ { - "uid": "application::page.page", + "uid": "application::pages.pages", "kind": "collectionType", "collectionName": "pages", "info": { - "name": "page" + "name": "pages" }, "options": { "increments": true, diff --git a/__mocks__/helpers/strapi.js b/__mocks__/helpers/strapi.js index 9d723fab..3540fa1d 100644 --- a/__mocks__/helpers/strapi.js +++ b/__mocks__/helpers/strapi.js @@ -14,7 +14,7 @@ function setupStrapi() { }, contentTypes: { 'page': { - ...require('./page.settings.json'), + ...require('./pages.settings.json'), apiName: 'pages', associations: [{ model: 'navigationitem' }], }, @@ -30,7 +30,7 @@ function setupStrapi() { navigation: jest.fn().mockImplementation(), }, models: { - 'page': require('./page.settings.json'), + 'pages': require('./pages.settings.json'), 'blog-post': require('./blog-post.settings.json'), } } diff --git a/admin/src/components/Item/index.js b/admin/src/components/Item/index.js index bf971e60..2edbc0c0 100644 --- a/admin/src/components/Item/index.js +++ b/admin/src/components/Item/index.js @@ -28,6 +28,7 @@ const Item = (props) => { relatedRef, isFirst = false, isLast = false, + isParentAttachedToMenu, onItemClick, onItemReOrder, onItemRestoreClick, @@ -79,6 +80,7 @@ const Item = (props) => { removed ? null : onItemClick(e, { ...item, isMenuAllowedLevel, + isParentAttachedToMenu, }, levelPath) } > @@ -106,7 +108,7 @@ const Item = (props) => { } - onClick={(e) => onItemLevelAddClick(e, viewId, isNextMenuAllowedLevel, levelPath)} + onClick={(e) => onItemLevelAddClick(e, viewId, isNextMenuAllowedLevel, levelPath, menuAttached)} menuLevel={isNextMenuAllowedLevel} /> )} @@ -121,6 +123,7 @@ const Item = (props) => { level={level + 1} levelPath={absolutePath} allowedLevels={allowedLevels} + isParentAttachedToMenu={menuAttached} contentTypesNameFields={contentTypesNameFields} error={error} /> @@ -146,6 +149,7 @@ Item.propTypes = { levelPath: PropTypes.string, isFirst: PropTypes.bool, isLast: PropTypes.bool, + isParentAttachedToMenu: PropTypes.bool, onItemClick: PropTypes.func.isRequired, onItemRestoreClick: PropTypes.func.isRequired, onItemLevelAddClick: PropTypes.func.isRequired, diff --git a/admin/src/components/List/index.js b/admin/src/components/List/index.js index 3e2aa884..1e309af8 100644 --- a/admin/src/components/List/index.js +++ b/admin/src/components/List/index.js @@ -18,6 +18,7 @@ const List = ({ level = 0, levelPath = '', allowedLevels, + isParentAttachedToMenu = false, contentTypesNameFields, error, }) => { @@ -35,6 +36,7 @@ const List = ({ levelPath={levelPath} isFirst={n === 0} isLast={n === items.length - 1} + isParentAttachedToMenu={isParentAttachedToMenu} allowedLevels={allowedLevels} contentTypesNameFields={contentTypesNameFields} onItemClick={onItemClick} @@ -52,7 +54,7 @@ const List = ({ menuLevel color="primary" icon={} - onClick={e => onItemLevelAddClick(e, null, true, levelPath)} + onClick={e => onItemLevelAddClick(e, null, true, levelPath, true)} /> )} @@ -65,6 +67,7 @@ List.propTypes = { items: PropTypes.array, level: PropTypes.number, allowedLevels: PropTypes.number, + isParentAttachedToMenu: PropTypes.bool, contentTypesNameFields: PropTypes.object.isRequired, onItemClick: PropTypes.func.isRequired, onItemReOrder: PropTypes.func.isRequired, diff --git a/admin/src/containers/View/components/NavigationItemForm/index.js b/admin/src/containers/View/components/NavigationItemForm/index.js index 75e96483..5c867084 100644 --- a/admin/src/containers/View/components/NavigationItemForm/index.js +++ b/admin/src/containers/View/components/NavigationItemForm/index.js @@ -42,8 +42,6 @@ const NavigationItemForm = ({ if (!hasBeenInitialized && !isEmpty(data)) { setInitializedState(true); setFormState({ ...data }); - } else if (hasBeenInitialized && !isEmpty(data) && data.related && data.related.label && !(form.related || {}).label) { - setFormState({ ...data }); } const sanitizePayload = (payload = {}) => { @@ -98,10 +96,13 @@ const NavigationItemForm = ({ }; const onChangeRelatedType = ({ target: { name, value } }) => { + const relatedTypeBeingReverted = data.relatedType && (data.relatedType.value === get(value, 'value', value)); setFormState(prevState => ({ ...prevState, updated: true, - related: undefined, + related: relatedTypeBeingReverted ? { + ...data.related + } : undefined, [name]: value, })); if (!hasChanged) { @@ -126,36 +127,37 @@ const NavigationItemForm = ({ ); const isSingleSelected = useMemo( - () => relatedTypeSelectValue ? contentTypes.find(_ => _.collectionName === relatedType.value)?.isSingle : false, + () => relatedTypeSelectValue ? contentTypes.find(_ => _.uid === relatedType.value)?.isSingle : false, [relatedTypeSelectValue, contentTypes], ); const relatedTypeSelectOptions = useMemo( () => contentTypes - .filter((contentType) => { - if (contentType.isSingle) { - return !usedContentTypesData.some((_) => _.__collectionName === contentType.collectionName); - } - return true; - }) + .filter((contentType) => { + if (contentType.isSingle) { + return !usedContentTypesData.some((_) => _.__collectionName === contentType.uid); + } + return true; + }) .map((item) => ({ - value: get(item, 'collectionName'), + value: get(item, 'uid'), label: get(item, 'label', get(item, 'name')), })), [contentTypes, usedContentTypesData], ); const relatedSelectOptions = contentTypeEntities - .filter((item) => !find(usedContentTypeEntities.filter(uctItem => uctItem.id !== get(relatedSelectValue, 'value')), - uctItem => - (get(relatedTypeSelectValue, 'value') === uctItem.__collectionName) && (item.id === uctItem.id), - )) + .filter((item) => { + const usedContentTypeEntitiesOfSameType = usedContentTypeEntities + .filter(uctItem => (get(relatedTypeSelectValue, 'value') === uctItem.__collectionName) && (uctItem.id !== get(relatedSelectValue, 'value'))); + return !find(usedContentTypeEntitiesOfSameType, uctItem => item.id === uctItem.id); + }) .map((item) => ({ value: item.id, label: extractRelatedItemLabel({ ...item, __collectionName: get(relatedTypeSelectValue, 'value', relatedTypeSelectValue), - }, contentTypesNameFields), + }, contentTypesNameFields, { contentTypes }), })); const isExternal = form.type === navigationItemType.EXTERNAL; @@ -200,7 +202,7 @@ const NavigationItemForm = ({ if (value) { const item = find( contentTypes, - (_) => _.collectionName === value, + (_) => _.uid === value, ); if (item) { await getContentTypeEntities(item.endpoint || item.collectionName, item.plugin); @@ -241,7 +243,7 @@ const NavigationItemForm = ({ diff --git a/admin/src/containers/View/components/NavigationItemPopup/index.js b/admin/src/containers/View/components/NavigationItemPopup/index.js index 63554a6e..75a19fbb 100644 --- a/admin/src/containers/View/components/NavigationItemPopup/index.js +++ b/admin/src/containers/View/components/NavigationItemPopup/index.js @@ -38,6 +38,7 @@ const NavigationItemPopUp = ({ contentTypesNameFields = {}, } = config; + const relatedTypeItem = find(contentTypes, item => item.uid === relatedType, {}); const prepareFormData = data => ({ ...data, related: related ? { @@ -45,11 +46,11 @@ const NavigationItemPopUp = ({ label: extractRelatedItemLabel({ ...find(contentTypeItems, item => item.id === related, {}), __collectionName: relatedType, - }, contentTypesNameFields), + }, contentTypesNameFields, config), } : undefined, relatedType: relatedType ? { value: relatedType, - label: find(contentTypes, item => item.collectionName === relatedType, {}).label, + label: relatedTypeItem.label || relatedTypeItem.name, } : undefined, }); diff --git a/admin/src/containers/View/index.js b/admin/src/containers/View/index.js index 2b032d2d..126b3d6e 100644 --- a/admin/src/containers/View/index.js +++ b/admin/src/containers/View/index.js @@ -32,12 +32,10 @@ const View = () => { activeItem: activeNavigation, changedActiveItem: changedActiveNavigation, config, - navigationPopupOpened, navigationItemPopupOpened, isLoading, isLoadingForAdditionalDataToBeSet, isLoadingForSubmit, - handleChangeNavigationPopupVisibility, handleChangeNavigationItemPopupVisibility, handleChangeSelection, handleChangeNavigationData, @@ -78,7 +76,7 @@ const View = () => { __collectionName: curr.relatedRef.__collectionName, id: curr.relatedRef.id } : undefined, ...pullUsedContentTypeItem(curr.items)].filter(item => item) - , []) + , []); const usedContentTypeItems = pullUsedContentTypeItem((changedActiveNavigation || {}).items); const changeNavigationItemPopupState = (visible, editedItem = {}) => { @@ -91,6 +89,7 @@ const View = () => { viewId = null, isMenuAllowedLevel = true, levelPath = '', + parentAttachedToMenu = true, ) => { e.preventDefault(); e.stopPropagation(); @@ -98,15 +97,22 @@ const View = () => { viewParentId: viewId, isMenuAllowedLevel, levelPath, + parentAttachedToMenu, }); }; - const editNavigationItem = (e, item, levelPath = '') => { + const editNavigationItem = ( + e, + item, + levelPath = '', + parentAttachedToMenu = true, + ) => { e.preventDefault(); e.stopPropagation(); changeNavigationItemPopupState(true, { ...item, levelPath, + parentAttachedToMenu, }); }; @@ -204,6 +210,7 @@ const View = () => { root error={error} allowedLevels={config.allowedLevels} + isParentAttachedToMenu={true} contentTypesNameFields={config.contentTypesNameFields} /> )} diff --git a/admin/src/containers/View/utils/parsers.js b/admin/src/containers/View/utils/parsers.js index 89945824..121a8f99 100644 --- a/admin/src/containers/View/utils/parsers.js +++ b/admin/src/containers/View/utils/parsers.js @@ -23,18 +23,16 @@ export const transformItemToRESTPayload = ( order, audience = [], items = [], - isSingle, } = item; const isExternal = type === navigationItemType.EXTERNAL; - const { contentTypeItems = [], contentTypes = [] } = config; + const { contentTypes = [] } = config; const parsedRelated = Number(related); const relatedId = isExternal || isNaN(parsedRelated) ? related : parsedRelated; - const relatedContentTypeItem = isExternal ? undefined : find(contentTypeItems, cti => cti.id === relatedId); - const relatedContentType = relatedContentTypeItem || relatedType ? + const relatedContentType = relatedType ? find(contentTypes, - ct => ct.collectionName === (relatedContentTypeItem ? relatedContentTypeItem.__collectionName : relatedType)) : + ct => ct.uid === relatedType) : undefined; return { @@ -58,7 +56,7 @@ export const transformItemToRESTPayload = ( : [ { refId: relatedId, - ref: relatedContentType ? relatedContentType.name : relatedType, + ref: relatedContentType ? relatedContentType.uid : relatedType, field: relatedContentType && relatedContentType.relatedField ? relatedContentType.relatedField : 'navigation', }, ], @@ -79,7 +77,6 @@ export const transformToRESTPayload = (payload, config = {}) => { const linkRelations = (item, config) => { const { contentTypeItems = [], contentTypes = [] } = config; const { type, related, relatedType, relatedRef, isSingle } = item; - // console.log('linkRelations:item:', item); let relation = { related: undefined, relatedRef: undefined, @@ -87,14 +84,14 @@ const linkRelations = (item, config) => { }; if (isSingle && relatedType) { - const relatedContentType = contentTypes.find(_ => relatedType === _.collectionName) || {}; + const relatedContentType = contentTypes.find(_ => relatedType === _.uid) || {}; return { ...item, relatedType, relatedRef: { - isSingle, - __collectionName: relatedContentType.collectionName, ...omit(relatedContentType, 'collectionName'), + isSingle, + __collectionName: relatedContentType.uid, }, }; } @@ -122,31 +119,30 @@ const linkRelations = (item, config) => { const shouldFindRelated = (isNumber(related) || isUuid(related) || isString(related)) && !relatedRef; const shouldBuildRelated = !relatedRef || (relatedRef && (relatedRef.id !== relatedId)); if (shouldBuildRelated && !shouldFindRelated) { - const { __contentType } = relatedItem; const relatedContentType = find(contentTypes, - ct => ct.contentTypeName.toLowerCase() === __contentType.toLowerCase(), {}); - const { collectionName, labelSingular, isSingle } = relatedContentType; + ct => ct.contentTypeName.toLowerCase() === relatedItem.__contentType.toLowerCase(), {}); + const { uid, labelSingular, isSingle } = relatedContentType; relation = { related: relatedItem.id, relatedRef: { - __collectionName: collectionName, + ...relatedItem, + __collectionName: uid, isSingle, labelSingular, - ...relatedItem, }, - relatedType: collectionName, + relatedType: uid, }; } else if (shouldFindRelated) { const relatedRef = find(contentTypeItems, cti => cti.id === relatedId); - const relatedContentType = find(contentTypes, ct => ct.collectionName.toLowerCase() === relatedType.toLowerCase()); - const { contentTypeName, labelSingular, isSingle } = relatedContentType; + const relatedContentType = find(contentTypes, ct => ct.uid === relatedType); + const { uid, contentTypeName, labelSingular, isSingle } = relatedContentType; relation = { relatedRef: { - __collectionName: relatedType, + ...relatedRef, + __collectionName: uid, __contentType: contentTypeName, isSingle, labelSingular, - ...relatedRef, }, }; } else { @@ -252,10 +248,12 @@ export const prepareItemToViewPayload = (items = [], viewParentId = null, config }; }); -export const extractRelatedItemLabel = (item = {}, fields = {}) => { +export const extractRelatedItemLabel = (item = {}, fields = {}, config = {}) => { + const { contentTypes = [] } = config; const { __collectionName } = item; + const contentType = contentTypes.find(_ => _.uid === __collectionName) const { default: defaultFields = [] } = fields; - return get(fields, `${__collectionName}`, defaultFields).map((_) => item[_]).filter((_) => _)[0] || ''; + return get(fields, `${contentType ? contentType.collectionName : ''}`, defaultFields).map((_) => item[_]).filter((_) => _)[0] || ''; }; export const usedContentTypes = (items) => items.flatMap( diff --git a/package.json b/package.json index aa3adfbf..21cbfbdd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "strapi-plugin-navigation", - "version": "1.0.0-beta.15", + "version": "1.0.0-beta.16", "description": "Strapi - Navigation plugin", "strapi": { "name": "Navigation", diff --git a/services/__tests__/navigation.test.js b/services/__tests__/navigation.test.js index 60261bcf..557f4b7d 100644 --- a/services/__tests__/navigation.test.js +++ b/services/__tests__/navigation.test.js @@ -12,10 +12,10 @@ describe('Navigation service', () => { it('Config Content Types', () => { const { configContentTypes } = require("../navigation"); const result = [{ - uid: "application::page.page", + uid: "application::pages.pages", collectionName: "pages", isSingle: false, - contentTypeName: "Page", + contentTypeName: "Pages", endpoint: "pages", label: "Pages", labelSingular: "Page", diff --git a/services/navigation.js b/services/navigation.js index ca6703a5..429c732a 100644 --- a/services/navigation.js +++ b/services/navigation.js @@ -89,9 +89,9 @@ module.exports = { const { isManaged, hidden } = options; const isSingle = kind === KIND_TYPES.SINGLE; const endpoint = isSingle ? apiName : pluralize(apiName); - const relationName = last(apiName) === 's' ? apiName.substr(0, apiName.length - 1) : apiName; - const relationNameParts = relationName.split('-'); - const contentTypeName = relationNameParts.length > 1 ? relationNameParts.reduce((prev, curr) => `${prev}${upperFirst(curr)}`, '') : upperFirst(relationName); + const relationName = utilsFunctions.singularize(apiName); + const relationNameParts = last(uid.split('.')).split('-'); + const contentTypeName = relationNameParts.length > 1 ? relationNameParts.reduce((prev, curr) => `${prev}${upperFirst(curr)}`, '') : upperFirst(apiName); const labelSingular = upperFirst(relationNameParts.length > 1 ? relationNameParts.join(' ') : relationName); return { uid, @@ -102,7 +102,7 @@ module.exports = { contentTypeName, label: isSingle ? labelSingular : pluralize(labelSingular || name), relatedField: relatedField ? relatedField.alias : undefined, - labelSingular, + labelSingular: utilsFunctions.singularize(labelSingular), endpoint, plugin, visible: (isManaged || isNil(isManaged)) && !hidden, diff --git a/services/utils/functions.js b/services/utils/functions.js index 8190f4b1..c685acba 100644 --- a/services/utils/functions.js +++ b/services/utils/functions.js @@ -31,6 +31,10 @@ module.exports = { }; }, + singularize(value = '') { + return last(value) === 's' ? value.substr(0, value.length - 1) : value; + }, + checkDuplicatePath(parentItem, checkData) { return new Promise((resolve, reject) => { if (parentItem && parentItem.items) {