From a546a9072cc778b2a96962d43175d375d346b06f Mon Sep 17 00:00:00 2001 From: Bassel Kanso Date: Wed, 5 Jun 2024 12:39:12 +0300 Subject: [PATCH 1/9] feat: expand nested components to 6 levels of depth --- .../DataManagerProvider.tsx | 3 +- .../utils/retrieveNestedComponents.ts | 49 +++++++++--- .../tests/retrieveNestedComponents.test.ts | 78 ++++++++++++++++++- .../FormModal/utils/getAttributesToDisplay.ts | 54 +++++++++++-- .../admin/src/constants.ts | 2 + 5 files changed, 168 insertions(+), 18 deletions(-) diff --git a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/DataManagerProvider.tsx b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/DataManagerProvider.tsx index 59585a0196..6c3cd65f87 100644 --- a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/DataManagerProvider.tsx +++ b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/DataManagerProvider.tsx @@ -425,9 +425,8 @@ const DataManagerProvider = ({ children }: DataManagerProviderProps) => { const getAllNestedComponents = () => { const appNestedCompo = retrieveNestedComponents(components); - const editingDataNestedCompos = retrieveNestedComponents(modifiedData.components || {}); - return makeUnique([...editingDataNestedCompos, ...appNestedCompo]); + return appNestedCompo; }; const removeComponentFromDynamicZone = (dzName: string, componentToRemoveIndex: number) => { diff --git a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveNestedComponents.ts b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveNestedComponents.ts index d7b7c086f1..bc441a8124 100644 --- a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveNestedComponents.ts +++ b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveNestedComponents.ts @@ -1,24 +1,55 @@ -import { makeUnique } from '../../../utils/makeUnique'; +import type { Internal } from '@strapi/types'; -export const retrieveNestedComponents = (appComponents: any) => { - const nestedComponents = Object.keys(appComponents).reduce((acc: any, current) => { +export type NestedComponent = { + component: Internal.UID.Component; + parentCompoUid?: Internal.UID.Component[]; +}; + +export const retrieveNestedComponents = (appComponents: any): NestedComponent[] => { + const nestedComponents = Object.keys(appComponents).reduce((acc: any, current: any) => { const componentAttributes = appComponents?.[current]?.schema?.attributes ?? []; - const currentComponentNestedCompos = getComponentsFromComponent(componentAttributes); - + const currentComponentNestedCompos = getComponentsFromComponent(componentAttributes, current); return [...acc, ...currentComponentNestedCompos]; }, []); - return makeUnique(nestedComponents); + return mergeComponents(nestedComponents); }; -const getComponentsFromComponent = (componentAttributes: any) => { +const getComponentsFromComponent = ( + componentAttributes: any, + parentCompoUid: Internal.UID.Component +) => { return componentAttributes.reduce((acc: any, current: any) => { const { type, component } = current; - if (type === 'component') { - acc.push(component); + acc.push({ + component, + parentCompoUid, + }); } return acc; }, []); }; + +// merge components different parents if they exist +const mergeComponents = (originalComponents: NestedComponent[]): NestedComponent[] => { + const componentMap = new Map(); + // Populate the map with component and its parents + originalComponents.forEach(({ component, parentCompoUid }) => { + if (!componentMap.has(component)) { + componentMap.set(component, new Set()); + } + componentMap.get(component).add(parentCompoUid); + }); + + // Convert the map to the desired array format + const transformedComponents: NestedComponent[] = Array.from(componentMap.entries()).map( + ([component, parentCompoUidSet]) => ({ + component, + parentCompoUid: Array.from(parentCompoUidSet), + }) + ); + + return transformedComponents; +}; diff --git a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/tests/retrieveNestedComponents.test.ts b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/tests/retrieveNestedComponents.test.ts index 43c85a6698..5aba7dc1c4 100644 --- a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/tests/retrieveNestedComponents.test.ts +++ b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/tests/retrieveNestedComponents.test.ts @@ -49,7 +49,83 @@ describe('CONTENT TYPE BUILDER | COMPONENTS | DataManagerProvider | utils | retr }, }; - const expected = ['default.dish']; + const expected = [ + { + component: 'default.dish', + parentCompoUid: ['default.closingperiod'], + }, + ]; + + expect(retrieveNestedComponents(components)).toEqual(expected); + }); + + it('should return both parents', () => { + const components = { + 'default.closingperiod': { + uid: 'default.closingperiod', + category: 'default', + apiId: 'closingperiod', + schema: { + icon: 'angry', + name: 'closingperiod', + description: '', + collectionName: 'components_closingperiods', + attributes: [ + { type: 'string', name: 'label' }, + { type: 'date', required: true, name: 'start_date' }, + { type: 'date', required: true, name: 'end_date' }, + { type: 'media', multiple: false, required: false, name: 'media' }, + { component: 'default.dish', type: 'component', name: 'dish' }, + ], + }, + }, + 'default.dish': { + uid: 'default.dish', + category: 'default', + apiId: 'dish', + schema: { + icon: 'address-book', + name: 'dish', + description: '', + collectionName: 'components_dishes', + attributes: [ + { type: 'string', required: false, default: 'My super dish', name: 'name' }, + { type: 'text', name: 'description' }, + { type: 'float', name: 'price' }, + { type: 'media', multiple: false, required: false, name: 'picture' }, + { type: 'richtext', name: 'very_long_description' }, + { + type: 'relation', + relation: 'oneToOne', + target: 'api::category.category', + targetAttribute: null, + private: false, + name: 'categories', + }, + ], + }, + }, + + 'default.openingperiod': { + uid: 'default.openingperiod', + category: 'default', + apiId: 'openingperiod', + schema: { + icon: 'angry', + name: 'openingperiod', + description: '', + collectionName: 'components_openingperiods', + attributes: [{ component: 'default.dish', type: 'component', name: 'dish' }], + }, + }, + }; + + const expected = [ + { + component: 'default.dish', + parentCompoUid: ['default.closingperiod', 'default.openingperiod'], + }, + ]; expect(retrieveNestedComponents(components)).toEqual(expected); }); diff --git a/packages/core/content-type-builder/admin/src/components/FormModal/utils/getAttributesToDisplay.ts b/packages/core/content-type-builder/admin/src/components/FormModal/utils/getAttributesToDisplay.ts index 478a800b89..cae7449dc5 100644 --- a/packages/core/content-type-builder/admin/src/components/FormModal/utils/getAttributesToDisplay.ts +++ b/packages/core/content-type-builder/admin/src/components/FormModal/utils/getAttributesToDisplay.ts @@ -1,10 +1,13 @@ +import { MAX_COMPONENT_DEPTH } from '../../../constants'; + import type { IconByType } from '../../AttributeIcon'; +import type { NestedComponent } from '../../DataManagerProvider/utils/retrieveNestedComponents'; import type { Internal } from '@strapi/types'; export const getAttributesToDisplay = ( dataTarget = '', targetUid: Internal.UID.Schema, - nestedComponents: Array + nestedComponents: Array ): IconByType[][] => { const defaultAttributes: IconByType[] = [ 'text', @@ -22,9 +25,6 @@ export const getAttributesToDisplay = ( ]; const isPickingAttributeForAContentType = dataTarget === 'contentType'; - const isNestedInAnotherComponent = nestedComponents.includes(targetUid); - const canAddComponentInAnotherComponent = - !isPickingAttributeForAContentType && !isNestedInAnotherComponent; if (isPickingAttributeForAContentType) { return [ @@ -34,9 +34,51 @@ export const getAttributesToDisplay = ( ]; } - if (canAddComponentInAnotherComponent) { - return [defaultAttributes, ['component']]; + // this will only run when adding attributes to components + if (dataTarget) { + const componentDepth = getComponentMaxDepth(targetUid, nestedComponents); + const isNestedInAnotherComponent = componentDepth >= MAX_COMPONENT_DEPTH; + const canAddComponentInAnotherComponent = + !isPickingAttributeForAContentType && !isNestedInAnotherComponent; + if (canAddComponentInAnotherComponent) { + return [defaultAttributes, ['component']]; + } } return [defaultAttributes]; }; + +const findComponent = (component: Internal.UID.Schema, components: Array) => { + return components.find((c) => c.component === component); +}; + +const getComponentMaxDepth = ( + component: Internal.UID.Schema, + components: Array +) => { + const dfs = (currentComponent: NestedComponent, currentLevel: number): Array => { + const levels = []; + levels.push(currentLevel); + + if (!currentComponent.parentCompoUid) { + return levels; + } + + for (const parentUid of currentComponent.parentCompoUid) { + const parentComponent = findComponent(parentUid, components); + if (parentComponent) { + levels.push(...dfs(parentComponent, currentLevel + 1)); + } + } + + return levels; + }; + + const nestedCompo = findComponent(component, components); + // return depth 0 if component is not nested + if (!nestedCompo) { + return 0; + } + const compoDepth = Math.max(...dfs(nestedCompo, 1)); + return compoDepth; +}; diff --git a/packages/core/content-type-builder/admin/src/constants.ts b/packages/core/content-type-builder/admin/src/constants.ts index 85e1759a16..fa983278e7 100644 --- a/packages/core/content-type-builder/admin/src/constants.ts +++ b/packages/core/content-type-builder/admin/src/constants.ts @@ -5,3 +5,5 @@ export const PERMISSIONS = { // plugin directly in the browser main: [{ action: 'plugin::content-type-builder.read', subject: null }], }; + +export const MAX_COMPONENT_DEPTH = 6; From 394cd1906ccd6e81b4c18a0494849582712547b4 Mon Sep 17 00:00:00 2001 From: Bassel Kanso Date: Thu, 6 Jun 2024 15:31:31 +0300 Subject: [PATCH 2/9] feat: only allow nesting components that don't exceed the total limit of nesting --- .../retrieveComponentsThatHaveComponents.ts | 25 ++-- ...trieveComponentsThatHaveComponents.test.ts | 41 ++++++- .../FormModal/utils/getAttributesToDisplay.ts | 38 +----- .../admin/src/components/SelectComponent.tsx | 19 ++- .../admin/src/utils/getMaxDepth.ts | 79 ++++++++++++ .../admin/src/utils/tests/getMaxDepth.test.ts | 115 ++++++++++++++++++ 6 files changed, 258 insertions(+), 59 deletions(-) create mode 100644 packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts create mode 100644 packages/core/content-type-builder/admin/src/utils/tests/getMaxDepth.test.ts diff --git a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveComponentsThatHaveComponents.ts b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveComponentsThatHaveComponents.ts index 94be06afed..ce1337adc0 100644 --- a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveComponentsThatHaveComponents.ts +++ b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveComponentsThatHaveComponents.ts @@ -3,16 +3,15 @@ import get from 'lodash/get'; import { makeUnique } from '../../../utils/makeUnique'; import type { Component, AttributeType, Components } from '../../../types'; -import type { Internal } from '@strapi/types'; const retrieveComponentsThatHaveComponents = (allComponents: Components) => { const componentsThatHaveNestedComponents = Object.keys(allComponents).reduce( - (acc: Internal.UID.Component[], current) => { + (acc: any, current) => { const currentComponent = get(allComponents, [current]); - const uid = currentComponent.uid; - if (doesComponentHaveAComponentField(currentComponent)) { - acc.push(uid); + const compoWithChildren = getComponentWithChildComponents(currentComponent); + if (compoWithChildren.childComponents.length > 0) { + acc.push(compoWithChildren); } return acc; @@ -23,14 +22,16 @@ const retrieveComponentsThatHaveComponents = (allComponents: Components) => { return makeUnique(componentsThatHaveNestedComponents); }; -const doesComponentHaveAComponentField = (component: Component) => { +const getComponentWithChildComponents = (component: Component) => { const attributes = get(component, ['schema', 'attributes'], []) as AttributeType[]; + return { + component: component.uid, + childComponents: attributes.filter((attribute) => { + const { type } = attribute; - return attributes.some((attribute) => { - const { type } = attribute; - - return type === 'component'; - }); + return type === 'component'; + }), + }; }; -export { doesComponentHaveAComponentField, retrieveComponentsThatHaveComponents }; +export { getComponentWithChildComponents, retrieveComponentsThatHaveComponents }; diff --git a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/tests/retrieveComponentsThatHaveComponents.test.ts b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/tests/retrieveComponentsThatHaveComponents.test.ts index 1d1d5dbe8c..005be3bdee 100644 --- a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/tests/retrieveComponentsThatHaveComponents.test.ts +++ b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/tests/retrieveComponentsThatHaveComponents.test.ts @@ -1,5 +1,5 @@ import { - doesComponentHaveAComponentField, + getComponentWithChildComponents, retrieveComponentsThatHaveComponents, } from '../retrieveComponentsThatHaveComponents'; @@ -80,18 +80,47 @@ const data: any = { describe('retrieveComponentsThatHaveComponents', () => { describe('doesComponentHaveAComponentField', () => { - it('Should return true if one of its attributes is a component', () => { - expect(doesComponentHaveAComponentField(data['blog.slider'])).toBe(true); + it('Should return correct child component if component has a component', () => { + expect(getComponentWithChildComponents(data['blog.slider'])).toEqual({ + component: 'blog.slider', + childComponents: [ + { + name: 'slide', + component: 'default.slide', + type: 'component', + repeatable: true, + min: 1, + max: 5, + }, + ], + }); }); - it('Should return false if none of its attributes is a component', () => { - expect(doesComponentHaveAComponentField(data['default.dish'])).toBe(false); + it('Should return no child components if component has no child components', () => { + expect(getComponentWithChildComponents(data['default.dish'])).toEqual({ + component: 'default.dish', + childComponents: [], + }); }); }); describe('retrievComponentsThatHaveComponents', () => { it('should return an array with all the components that have nested components', () => { - expect(retrieveComponentsThatHaveComponents(data)).toEqual(['blog.slider']); + expect(retrieveComponentsThatHaveComponents(data)).toEqual([ + { + component: 'blog.slider', + childComponents: [ + { + name: 'slide', + component: 'default.slide', + type: 'component', + repeatable: true, + min: 1, + max: 5, + }, + ], + }, + ]); }); }); }); diff --git a/packages/core/content-type-builder/admin/src/components/FormModal/utils/getAttributesToDisplay.ts b/packages/core/content-type-builder/admin/src/components/FormModal/utils/getAttributesToDisplay.ts index cae7449dc5..61b89f4344 100644 --- a/packages/core/content-type-builder/admin/src/components/FormModal/utils/getAttributesToDisplay.ts +++ b/packages/core/content-type-builder/admin/src/components/FormModal/utils/getAttributesToDisplay.ts @@ -1,4 +1,5 @@ import { MAX_COMPONENT_DEPTH } from '../../../constants'; +import { getComponentDepth } from '../../../utils/getMaxDepth'; import type { IconByType } from '../../AttributeIcon'; import type { NestedComponent } from '../../DataManagerProvider/utils/retrieveNestedComponents'; @@ -36,7 +37,7 @@ export const getAttributesToDisplay = ( // this will only run when adding attributes to components if (dataTarget) { - const componentDepth = getComponentMaxDepth(targetUid, nestedComponents); + const componentDepth = getComponentDepth(targetUid, nestedComponents); const isNestedInAnotherComponent = componentDepth >= MAX_COMPONENT_DEPTH; const canAddComponentInAnotherComponent = !isPickingAttributeForAContentType && !isNestedInAnotherComponent; @@ -47,38 +48,3 @@ export const getAttributesToDisplay = ( return [defaultAttributes]; }; - -const findComponent = (component: Internal.UID.Schema, components: Array) => { - return components.find((c) => c.component === component); -}; - -const getComponentMaxDepth = ( - component: Internal.UID.Schema, - components: Array -) => { - const dfs = (currentComponent: NestedComponent, currentLevel: number): Array => { - const levels = []; - levels.push(currentLevel); - - if (!currentComponent.parentCompoUid) { - return levels; - } - - for (const parentUid of currentComponent.parentCompoUid) { - const parentComponent = findComponent(parentUid, components); - if (parentComponent) { - levels.push(...dfs(parentComponent, currentLevel + 1)); - } - } - - return levels; - }; - - const nestedCompo = findComponent(component, components); - // return depth 0 if component is not nested - if (!nestedCompo) { - return 0; - } - const compoDepth = Math.max(...dfs(nestedCompo, 1)); - return compoDepth; -}; diff --git a/packages/core/content-type-builder/admin/src/components/SelectComponent.tsx b/packages/core/content-type-builder/admin/src/components/SelectComponent.tsx index 1a63b7995b..2b9b9e8a68 100644 --- a/packages/core/content-type-builder/admin/src/components/SelectComponent.tsx +++ b/packages/core/content-type-builder/admin/src/components/SelectComponent.tsx @@ -1,8 +1,11 @@ import { SingleSelectOption, SingleSelect, Field } from '@strapi/design-system'; import { useIntl } from 'react-intl'; +import { MAX_COMPONENT_DEPTH } from '../constants'; import { useDataManager } from '../hooks/useDataManager'; +import { getChildrenMaxDepth, getComponentDepth } from '../utils/getMaxDepth'; +import type { Internal } from '@strapi/types'; interface Option { uid: string; label: string; @@ -22,7 +25,7 @@ interface SelectComponentProps { isCreatingComponentWhileAddingAField: boolean; name: string; onChange: (value: any) => void; - targetUid: string; + targetUid: Internal.UID.Schema; value: string; forTarget: string; } @@ -44,8 +47,11 @@ export const SelectComponent = ({ const errorMessage = error ? formatMessage({ id: error, defaultMessage: error }) : ''; const label = formatMessage(intlLabel); - const { componentsGroupedByCategory, componentsThatHaveOtherComponentInTheirAttributes } = - useDataManager(); + const { + componentsGroupedByCategory, + componentsThatHaveOtherComponentInTheirAttributes, + nestedComponents, + } = useDataManager(); const isTargetAComponent = ['component', 'components'].includes(forTarget); @@ -66,8 +72,11 @@ export const SelectComponent = ({ ); if (isAddingAComponentToAnotherComponent) { - options = options.filter((option) => { - return !componentsThatHaveOtherComponentInTheirAttributes.includes(option.uid); + options = options.filter(({ uid }: any) => { + const maxDepth = getChildrenMaxDepth(uid, componentsThatHaveOtherComponentInTheirAttributes); + const componentDepth = getComponentDepth(targetUid, nestedComponents); + const totalDepth = maxDepth + componentDepth; + return totalDepth <= MAX_COMPONENT_DEPTH; }); } diff --git a/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts b/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts new file mode 100644 index 0000000000..5f6449d64a --- /dev/null +++ b/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts @@ -0,0 +1,79 @@ +import type { NestedComponent } from '../components/DataManagerProvider/utils/retrieveNestedComponents'; +import type { Internal } from '@strapi/types'; + +type ChildComponent = { + repeatable: boolean; + component: Internal.UID.Component; + name?: string; + required?: boolean; + min?: number; +}; + +type Component = { + component: Internal.UID.Component; + childComponents: ChildComponent[]; +}; + +const findComponent = ( + componentName: Internal.UID.Schema, + components: Array +) => { + return components.find((c) => c.component === componentName); +}; + +/** + * gets the maximum depth child component + * for a specific component + */ +export const getChildrenMaxDepth = ( + componentUid: Internal.UID.Component, + components: Array, + currentDepth = 0 +) => { + const component = findComponent(componentUid, components); + if (!component || !component.childComponents || component.childComponents.length === 0) { + return currentDepth; + } + + let maxDepth = currentDepth; + component.childComponents.forEach((child) => { + const depth = getChildrenMaxDepth(child.component, components, currentDepth + 1); + if (depth > maxDepth) { + maxDepth = depth; + } + }); + + return maxDepth; +}; + +// get the current component depth +export const getComponentDepth = ( + component: Internal.UID.Schema, + components: Array +) => { + const getDepth = (currentComponent: NestedComponent, currentLevel: number): Array => { + const levels = []; + levels.push(currentLevel); + + if (!currentComponent.parentCompoUid) { + return levels; + } + + for (const parentUid of currentComponent.parentCompoUid) { + const parentComponent = findComponent(parentUid, components); + if (parentComponent) { + levels.push(...getDepth(parentComponent, currentLevel + 1)); + } + } + + return levels; + }; + + const nestedCompo = findComponent(component, components); + // return depth 0 if component is not nested + if (!nestedCompo) { + return 0; + } + const compoDepth = Math.max(...getDepth(nestedCompo, 1)); + return compoDepth; +}; diff --git a/packages/core/content-type-builder/admin/src/utils/tests/getMaxDepth.test.ts b/packages/core/content-type-builder/admin/src/utils/tests/getMaxDepth.test.ts new file mode 100644 index 0000000000..e1c63c6a79 --- /dev/null +++ b/packages/core/content-type-builder/admin/src/utils/tests/getMaxDepth.test.ts @@ -0,0 +1,115 @@ +import { getChildrenMaxDepth } from '../getMaxDepth'; + +const components = [ + { + component: 'basic.parent-compo', + childComponents: [ + { + repeatable: false, + component: 'basic.nested-compo1', + name: 'compofield', + }, + ], + }, + { + component: 'basic.nested-compo5', + childComponents: [ + { + repeatable: false, + component: 'basic.nested-compo6', + name: 'again', + }, + ], + }, + { + component: 'basic.nested-compo4', + childComponents: [ + { + repeatable: false, + component: 'basic.nested-compo5', + name: 'againsomecompo', + }, + ], + }, + { + component: 'basic.nested-compo3', + childComponents: [ + { + repeatable: false, + component: 'basic.nested-compo4', + name: 'somecompo', + }, + ], + }, + { + component: 'basic.nested-compo2', + childComponents: [ + { + repeatable: true, + component: 'basic.nested-compo3', + name: 'somecompo', + }, + ], + }, + { + component: 'basic.nested-compo1', + childComponents: [ + { + repeatable: false, + component: 'basic.nested-compo2', + name: 'compofield', + }, + ], + }, + { + component: 'basic.another-parent-compo', + childComponents: [ + { + repeatable: false, + component: 'basic.nested-compo6', + name: 'somecompo', + }, + ], + }, + { + component: 'default.openingtimes', + childComponents: [ + { + repeatable: true, + component: 'default.dish', + name: 'dishrep', + }, + { + repeatable: true, + component: 'basic.nested-compo3', + name: 'somecompo', + }, + ], + }, + { + component: 'default.closingperiod', + childComponents: [ + { + component: 'default.dish', + repeatable: true, + required: true, + min: 2, + name: 'dish', + }, + ], + }, +]; + +describe('getMaxDepth', () => { + test('A component with no child component should have 0 max depth', () => { + const componentsMaxDepth = getChildrenMaxDepth('basic.nested-compo6', components); + + expect(componentsMaxDepth).toEqual(0); + }); + + test('should accurately give the max depth of components children', () => { + const componentsMaxDepth = getChildrenMaxDepth('default.openingtimes', components); + + expect(componentsMaxDepth).toEqual(4); + }); +}); From 4fcf1cc8d1b474b751f1ff8966d2420a7676f621 Mon Sep 17 00:00:00 2001 From: Bassel Kanso Date: Thu, 6 Jun 2024 16:10:23 +0300 Subject: [PATCH 3/9] fix: ts issue --- .../core/content-type-builder/admin/src/utils/getMaxDepth.ts | 4 ++-- .../admin/src/utils/tests/getMaxDepth.test.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts b/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts index 5f6449d64a..f679e0cca2 100644 --- a/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts +++ b/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts @@ -9,7 +9,7 @@ type ChildComponent = { min?: number; }; -type Component = { +export type ComponentWithChildren = { component: Internal.UID.Component; childComponents: ChildComponent[]; }; @@ -27,7 +27,7 @@ const findComponent = ( */ export const getChildrenMaxDepth = ( componentUid: Internal.UID.Component, - components: Array, + components: Array, currentDepth = 0 ) => { const component = findComponent(componentUid, components); diff --git a/packages/core/content-type-builder/admin/src/utils/tests/getMaxDepth.test.ts b/packages/core/content-type-builder/admin/src/utils/tests/getMaxDepth.test.ts index e1c63c6a79..f7f475573c 100644 --- a/packages/core/content-type-builder/admin/src/utils/tests/getMaxDepth.test.ts +++ b/packages/core/content-type-builder/admin/src/utils/tests/getMaxDepth.test.ts @@ -1,6 +1,6 @@ -import { getChildrenMaxDepth } from '../getMaxDepth'; +import { getChildrenMaxDepth, type ComponentWithChildren } from '../getMaxDepth'; -const components = [ +const components: Array = [ { component: 'basic.parent-compo', childComponents: [ From e06b160f0857bbdb9f2d09c38f475da62d9ca86f Mon Sep 17 00:00:00 2001 From: Bassel Kanso Date: Mon, 10 Jun 2024 22:11:33 +0300 Subject: [PATCH 4/9] chore: adding some comments and types --- .../DataManagerProvider.tsx | 3 +- .../retrieveComponentsThatHaveComponents.ts | 32 +++++++++++----- .../utils/retrieveNestedComponents.ts | 21 ++++++---- ...trieveComponentsThatHaveComponents.test.ts | 10 ----- .../tests/retrieveNestedComponents.test.ts | 10 +++-- .../admin/src/utils/getMaxDepth.ts | 38 ++++++++++++++++--- 6 files changed, 76 insertions(+), 38 deletions(-) diff --git a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/DataManagerProvider.tsx b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/DataManagerProvider.tsx index 6c3cd65f87..9efaca1762 100644 --- a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/DataManagerProvider.tsx +++ b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/DataManagerProvider.tsx @@ -22,7 +22,6 @@ import { DataManagerContext } from '../../contexts/DataManagerContext'; import { useFormModalNavigation } from '../../hooks/useFormModalNavigation'; import { pluginId } from '../../pluginId'; import { getTrad } from '../../utils/getTrad'; -import { makeUnique } from '../../utils/makeUnique'; import { useAutoReloadOverlayBlocker } from '../AutoReloadOverlayBlocker'; import { FormModal } from '../FormModal/FormModal'; @@ -420,7 +419,7 @@ const DataManagerProvider = ({ children }: DataManagerProviderProps) => { const composWithCompos = retrieveComponentsThatHaveComponents(allCompos); - return makeUnique(composWithCompos); + return composWithCompos; }; const getAllNestedComponents = () => { diff --git a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveComponentsThatHaveComponents.ts b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveComponentsThatHaveComponents.ts index ce1337adc0..aa55f9af30 100644 --- a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveComponentsThatHaveComponents.ts +++ b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveComponentsThatHaveComponents.ts @@ -1,12 +1,20 @@ import get from 'lodash/get'; -import { makeUnique } from '../../../utils/makeUnique'; - import type { Component, AttributeType, Components } from '../../../types'; +import type { Internal } from '@strapi/types'; + +type ChildComponent = { + component: Internal.UID.Component; +}; + +export type ComponentWithChildren = { + component: Internal.UID.Component; + childComponents: ChildComponent[]; +}; const retrieveComponentsThatHaveComponents = (allComponents: Components) => { const componentsThatHaveNestedComponents = Object.keys(allComponents).reduce( - (acc: any, current) => { + (acc: ComponentWithChildren[], current) => { const currentComponent = get(allComponents, [current]); const compoWithChildren = getComponentWithChildComponents(currentComponent); @@ -19,18 +27,24 @@ const retrieveComponentsThatHaveComponents = (allComponents: Components) => { [] ); - return makeUnique(componentsThatHaveNestedComponents); + return componentsThatHaveNestedComponents; }; -const getComponentWithChildComponents = (component: Component) => { +const getComponentWithChildComponents = (component: Component): ComponentWithChildren => { const attributes = get(component, ['schema', 'attributes'], []) as AttributeType[]; return { component: component.uid, - childComponents: attributes.filter((attribute) => { - const { type } = attribute; + childComponents: attributes + .filter((attribute) => { + const { type } = attribute; - return type === 'component'; - }), + return type === 'component'; + }) + .map((attribute) => { + return { + component: attribute.component, + } as ChildComponent; + }), }; }; diff --git a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveNestedComponents.ts b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveNestedComponents.ts index bc441a8124..0a4ea85947 100644 --- a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveNestedComponents.ts +++ b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveNestedComponents.ts @@ -1,14 +1,19 @@ +import type { Components, AttributeType } from '../../../types'; import type { Internal } from '@strapi/types'; export type NestedComponent = { component: Internal.UID.Component; - parentCompoUid?: Internal.UID.Component[]; + uidsOfAllParents?: Internal.UID.Component[]; + parentCompoUid?: Internal.UID.Component; }; -export const retrieveNestedComponents = (appComponents: any): NestedComponent[] => { - const nestedComponents = Object.keys(appComponents).reduce((acc: any, current: any) => { +export const retrieveNestedComponents = (appComponents: Components): NestedComponent[] => { + const nestedComponents = Object.keys(appComponents).reduce((acc: NestedComponent[], current) => { const componentAttributes = appComponents?.[current]?.schema?.attributes ?? []; - const currentComponentNestedCompos = getComponentsFromComponent(componentAttributes, current); + const currentComponentNestedCompos = getComponentsFromComponent( + componentAttributes, + current as Internal.UID.Component + ); return [...acc, ...currentComponentNestedCompos]; }, []); @@ -16,10 +21,10 @@ export const retrieveNestedComponents = (appComponents: any): NestedComponent[] }; const getComponentsFromComponent = ( - componentAttributes: any, + componentAttributes: AttributeType[], parentCompoUid: Internal.UID.Component ) => { - return componentAttributes.reduce((acc: any, current: any) => { + return componentAttributes.reduce((acc: NestedComponent[], current) => { const { type, component } = current; if (type === 'component') { acc.push({ @@ -32,7 +37,7 @@ const getComponentsFromComponent = ( }, []); }; -// merge components different parents if they exist +// merge duplicate components const mergeComponents = (originalComponents: NestedComponent[]): NestedComponent[] => { const componentMap = new Map(); // Populate the map with component and its parents @@ -47,7 +52,7 @@ const mergeComponents = (originalComponents: NestedComponent[]): NestedComponent const transformedComponents: NestedComponent[] = Array.from(componentMap.entries()).map( ([component, parentCompoUidSet]) => ({ component, - parentCompoUid: Array.from(parentCompoUidSet), + uidsOfAllParents: Array.from(parentCompoUidSet), }) ); diff --git a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/tests/retrieveComponentsThatHaveComponents.test.ts b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/tests/retrieveComponentsThatHaveComponents.test.ts index 005be3bdee..6d979d23be 100644 --- a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/tests/retrieveComponentsThatHaveComponents.test.ts +++ b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/tests/retrieveComponentsThatHaveComponents.test.ts @@ -85,12 +85,7 @@ describe('retrieveComponentsThatHaveComponents', () => { component: 'blog.slider', childComponents: [ { - name: 'slide', component: 'default.slide', - type: 'component', - repeatable: true, - min: 1, - max: 5, }, ], }); @@ -111,12 +106,7 @@ describe('retrieveComponentsThatHaveComponents', () => { component: 'blog.slider', childComponents: [ { - name: 'slide', component: 'default.slide', - type: 'component', - repeatable: true, - min: 1, - max: 5, }, ], }, diff --git a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/tests/retrieveNestedComponents.test.ts b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/tests/retrieveNestedComponents.test.ts index 5aba7dc1c4..45a7d04df8 100644 --- a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/tests/retrieveNestedComponents.test.ts +++ b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/tests/retrieveNestedComponents.test.ts @@ -1,8 +1,10 @@ import { retrieveNestedComponents } from '../retrieveNestedComponents'; +import type { Components } from '../../../../types'; + describe('CONTENT TYPE BUILDER | COMPONENTS | DataManagerProvider | utils | retrieveNestedComponents', () => { it('should return an array of nested components', () => { - const components = { + const components: Components = { 'default.closingperiod': { uid: 'default.closingperiod', category: 'default', @@ -52,7 +54,7 @@ describe('CONTENT TYPE BUILDER | COMPONENTS | DataManagerProvider | utils | retr const expected = [ { component: 'default.dish', - parentCompoUid: ['default.closingperiod'], + uidsOfAllParents: ['default.closingperiod'], }, ]; @@ -60,7 +62,7 @@ describe('CONTENT TYPE BUILDER | COMPONENTS | DataManagerProvider | utils | retr }); it('should return both parents', () => { - const components = { + const components: Components = { 'default.closingperiod': { uid: 'default.closingperiod', category: 'default', @@ -123,7 +125,7 @@ describe('CONTENT TYPE BUILDER | COMPONENTS | DataManagerProvider | utils | retr const expected = [ { component: 'default.dish', - parentCompoUid: ['default.closingperiod', 'default.openingperiod'], + uidsOfAllParents: ['default.closingperiod', 'default.openingperiod'], }, ]; diff --git a/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts b/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts index f679e0cca2..72651735bb 100644 --- a/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts +++ b/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts @@ -22,8 +22,13 @@ const findComponent = ( }; /** - * gets the maximum depth child component - * for a specific component + * Recursively calculates the maximum depth of nested child components + * for a given component UID. + * + * @param componentUid - The UID of the component to start from. + * @param components - The array of all components with their child components. + * @param currentDepth - The current depth of the recursion. Defaults to 0. + * @returns The maximum depth of the nested child components. */ export const getChildrenMaxDepth = ( componentUid: Internal.UID.Component, @@ -31,13 +36,19 @@ export const getChildrenMaxDepth = ( currentDepth = 0 ) => { const component = findComponent(componentUid, components); + + // If the component doesn't exist or has no child components, return the current depth. if (!component || !component.childComponents || component.childComponents.length === 0) { return currentDepth; } let maxDepth = currentDepth; + + // Iterate through each child component to calculate their respective depths. component.childComponents.forEach((child) => { + // Recursively calculate the depth of the child component. const depth = getChildrenMaxDepth(child.component, components, currentDepth + 1); + // Update the maximum depth if the child's depth is greater. if (depth > maxDepth) { maxDepth = depth; } @@ -46,20 +57,37 @@ export const getChildrenMaxDepth = ( return maxDepth; }; -// get the current component depth +/** + * Calculates the depth of a component within a nested component tree. + * Depth is defined as the level at which the component is nested. + * For example, a component at Depth 3 is the third nested component. + * + * @param component - The UID of the component to find the depth for. + * @param components - The array of all nested components. + * @returns The depth level of the component within the nested tree. + */ export const getComponentDepth = ( component: Internal.UID.Schema, components: Array ) => { + /** + * Helper function to recursively calculate the depth of a component. + * + * @param currentComponent - The current component being inspected. + * @param currentLevel - The current level of depth in the tree. + * @returns An array of depth levels found for the component. + */ const getDepth = (currentComponent: NestedComponent, currentLevel: number): Array => { const levels = []; levels.push(currentLevel); - if (!currentComponent.parentCompoUid) { + // If the component has no parent UIDs, return the current levels + if (!currentComponent.uidsOfAllParents) { return levels; } - for (const parentUid of currentComponent.parentCompoUid) { + // Iterate over each parent UID to calculate their respective depths + for (const parentUid of currentComponent.uidsOfAllParents) { const parentComponent = findComponent(parentUid, components); if (parentComponent) { levels.push(...getDepth(parentComponent, currentLevel + 1)); From 0fc2163f33e0f0ecc5deb3b12681999f37e91795 Mon Sep 17 00:00:00 2001 From: Bassel Kanso Date: Tue, 11 Jun 2024 11:05:47 +0300 Subject: [PATCH 5/9] chore: fixing some types --- .../admin/src/utils/getMaxDepth.ts | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts b/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts index 72651735bb..5f8a1d7fcb 100644 --- a/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts +++ b/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts @@ -1,19 +1,7 @@ +import type { ComponentWithChildren } from '../components/DataManagerProvider/utils/retrieveComponentsThatHaveComponents'; import type { NestedComponent } from '../components/DataManagerProvider/utils/retrieveNestedComponents'; import type { Internal } from '@strapi/types'; -type ChildComponent = { - repeatable: boolean; - component: Internal.UID.Component; - name?: string; - required?: boolean; - min?: number; -}; - -export type ComponentWithChildren = { - component: Internal.UID.Component; - childComponents: ChildComponent[]; -}; - const findComponent = ( componentName: Internal.UID.Schema, components: Array From 4f56dbb82dac9d9f09d303e5a1db3f0b2ab56f96 Mon Sep 17 00:00:00 2001 From: Bassel Kanso Date: Tue, 11 Jun 2024 11:33:58 +0300 Subject: [PATCH 6/9] chore: fixing type import --- .../admin/src/utils/tests/getMaxDepth.test.ts | 26 +++---------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/packages/core/content-type-builder/admin/src/utils/tests/getMaxDepth.test.ts b/packages/core/content-type-builder/admin/src/utils/tests/getMaxDepth.test.ts index f7f475573c..eaa36a5895 100644 --- a/packages/core/content-type-builder/admin/src/utils/tests/getMaxDepth.test.ts +++ b/packages/core/content-type-builder/admin/src/utils/tests/getMaxDepth.test.ts @@ -1,13 +1,13 @@ -import { getChildrenMaxDepth, type ComponentWithChildren } from '../getMaxDepth'; +import { getChildrenMaxDepth } from '../getMaxDepth'; + +import type { ComponentWithChildren } from '../../components/DataManagerProvider/utils/retrieveComponentsThatHaveComponents'; const components: Array = [ { component: 'basic.parent-compo', childComponents: [ { - repeatable: false, component: 'basic.nested-compo1', - name: 'compofield', }, ], }, @@ -15,9 +15,7 @@ const components: Array = [ component: 'basic.nested-compo5', childComponents: [ { - repeatable: false, component: 'basic.nested-compo6', - name: 'again', }, ], }, @@ -25,9 +23,7 @@ const components: Array = [ component: 'basic.nested-compo4', childComponents: [ { - repeatable: false, component: 'basic.nested-compo5', - name: 'againsomecompo', }, ], }, @@ -35,9 +31,7 @@ const components: Array = [ component: 'basic.nested-compo3', childComponents: [ { - repeatable: false, component: 'basic.nested-compo4', - name: 'somecompo', }, ], }, @@ -45,9 +39,7 @@ const components: Array = [ component: 'basic.nested-compo2', childComponents: [ { - repeatable: true, component: 'basic.nested-compo3', - name: 'somecompo', }, ], }, @@ -55,9 +47,7 @@ const components: Array = [ component: 'basic.nested-compo1', childComponents: [ { - repeatable: false, component: 'basic.nested-compo2', - name: 'compofield', }, ], }, @@ -65,9 +55,7 @@ const components: Array = [ component: 'basic.another-parent-compo', childComponents: [ { - repeatable: false, component: 'basic.nested-compo6', - name: 'somecompo', }, ], }, @@ -75,14 +63,10 @@ const components: Array = [ component: 'default.openingtimes', childComponents: [ { - repeatable: true, component: 'default.dish', - name: 'dishrep', }, { - repeatable: true, component: 'basic.nested-compo3', - name: 'somecompo', }, ], }, @@ -91,10 +75,6 @@ const components: Array = [ childComponents: [ { component: 'default.dish', - repeatable: true, - required: true, - min: 2, - name: 'dish', }, ], }, From cc05b7b4cbd02cd910f69ea3c5d4038753b84a8d Mon Sep 17 00:00:00 2001 From: Bassel Kanso Date: Thu, 13 Jun 2024 16:42:38 +0300 Subject: [PATCH 7/9] chore: adding extra unit tests --- .../utils/retrieveNestedComponents.ts | 6 +- .../admin/src/utils/getMaxDepth.ts | 4 +- .../admin/src/utils/tests/getMaxDepth.test.ts | 68 ++++++++++++++++--- 3 files changed, 63 insertions(+), 15 deletions(-) diff --git a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveNestedComponents.ts b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveNestedComponents.ts index 0a4ea85947..0ca490c0d4 100644 --- a/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveNestedComponents.ts +++ b/packages/core/content-type-builder/admin/src/components/DataManagerProvider/utils/retrieveNestedComponents.ts @@ -10,7 +10,7 @@ export type NestedComponent = { export const retrieveNestedComponents = (appComponents: Components): NestedComponent[] => { const nestedComponents = Object.keys(appComponents).reduce((acc: NestedComponent[], current) => { const componentAttributes = appComponents?.[current]?.schema?.attributes ?? []; - const currentComponentNestedCompos = getComponentsFromComponent( + const currentComponentNestedCompos = getComponentsNestedWithinComponent( componentAttributes, current as Internal.UID.Component ); @@ -20,7 +20,7 @@ export const retrieveNestedComponents = (appComponents: Components): NestedCompo return mergeComponents(nestedComponents); }; -const getComponentsFromComponent = ( +const getComponentsNestedWithinComponent = ( componentAttributes: AttributeType[], parentCompoUid: Internal.UID.Component ) => { @@ -37,7 +37,7 @@ const getComponentsFromComponent = ( }, []); }; -// merge duplicate components +// Merge duplicate components const mergeComponents = (originalComponents: NestedComponent[]): NestedComponent[] => { const componentMap = new Map(); // Populate the map with component and its parents diff --git a/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts b/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts index 5f8a1d7fcb..dcda1d0945 100644 --- a/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts +++ b/packages/core/content-type-builder/admin/src/utils/getMaxDepth.ts @@ -3,10 +3,10 @@ import type { NestedComponent } from '../components/DataManagerProvider/utils/re import type { Internal } from '@strapi/types'; const findComponent = ( - componentName: Internal.UID.Schema, + componentUid: Internal.UID.Schema, components: Array ) => { - return components.find((c) => c.component === componentName); + return components.find((c) => c.component === componentUid); }; /** diff --git a/packages/core/content-type-builder/admin/src/utils/tests/getMaxDepth.test.ts b/packages/core/content-type-builder/admin/src/utils/tests/getMaxDepth.test.ts index eaa36a5895..0981bf8326 100644 --- a/packages/core/content-type-builder/admin/src/utils/tests/getMaxDepth.test.ts +++ b/packages/core/content-type-builder/admin/src/utils/tests/getMaxDepth.test.ts @@ -1,8 +1,9 @@ -import { getChildrenMaxDepth } from '../getMaxDepth'; +import { getChildrenMaxDepth, getComponentDepth } from '../getMaxDepth'; import type { ComponentWithChildren } from '../../components/DataManagerProvider/utils/retrieveComponentsThatHaveComponents'; +import type { NestedComponent } from '../../components/DataManagerProvider/utils/retrieveNestedComponents'; -const components: Array = [ +const componentsWithChildComponents: Array = [ { component: 'basic.parent-compo', childComponents: [ @@ -80,16 +81,63 @@ const components: Array = [ }, ]; -describe('getMaxDepth', () => { - test('A component with no child component should have 0 max depth', () => { - const componentsMaxDepth = getChildrenMaxDepth('basic.nested-compo6', components); +const nestedComponents: Array = [ + { + component: 'default.dish', + uidsOfAllParents: ['default.openingtimes', 'default.closingperiod'], + }, + { + component: 'basic.nested-compo1', + uidsOfAllParents: ['basic.parent-compo'], + }, + { + component: 'basic.nested-compo6', + uidsOfAllParents: ['basic.nested-compo5', 'basic.another-parent-compo'], + }, + { + component: 'basic.nested-compo5', + uidsOfAllParents: ['basic.nested-compo4'], + }, + { + component: 'basic.nested-compo4', + uidsOfAllParents: ['basic.nested-compo3'], + }, + { + component: 'basic.nested-compo3', + uidsOfAllParents: ['basic.nested-compo2'], + }, + { + component: 'basic.nested-compo2', + uidsOfAllParents: ['basic.nested-compo1'], + }, +]; - expect(componentsMaxDepth).toEqual(0); +describe('Component Depth Calculations', () => { + describe('getMaxDepth', () => { + it('A component with no child component should have 0 max depth', () => { + const componentsMaxDepth = getChildrenMaxDepth( + 'basic.nested-compo6', + componentsWithChildComponents + ); + + expect(componentsMaxDepth).toEqual(0); + }); + + it('should accurately give the max depth of components children', () => { + const componentsMaxDepth = getChildrenMaxDepth( + 'default.openingtimes', + componentsWithChildComponents + ); + + expect(componentsMaxDepth).toEqual(4); + }); }); - test('should accurately give the max depth of components children', () => { - const componentsMaxDepth = getChildrenMaxDepth('default.openingtimes', components); - - expect(componentsMaxDepth).toEqual(4); + describe('getComponentDepth', () => { + it('A component depth should reflect its position in the component tree', () => { + expect(getComponentDepth('basic.nested-compo1', nestedComponents)).toEqual(1); + expect(getComponentDepth('basic.nested-compo4', nestedComponents)).toEqual(4); + expect(getComponentDepth('basic.nested-compo6', nestedComponents)).toEqual(6); + }); }); }); From 0568e78862e54058620f0bc63000ad844207bd36 Mon Sep 17 00:00:00 2001 From: Bassel Kanso Date: Fri, 14 Jun 2024 15:40:17 +0300 Subject: [PATCH 8/9] fix: remove incorrect server validation --- .../src/controllers/validation/types.ts | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/packages/core/content-type-builder/server/src/controllers/validation/types.ts b/packages/core/content-type-builder/server/src/controllers/validation/types.ts index 7ebb5a1961..f341b651d6 100644 --- a/packages/core/content-type-builder/server/src/controllers/validation/types.ts +++ b/packages/core/content-type-builder/server/src/controllers/validation/types.ts @@ -219,24 +219,8 @@ const getTypeShape = ( return { required: validators.required, repeatable: yup.boolean(), - component: yup - .string() - .test({ - name: 'Check max component nesting is 1 lvl', - test(compoUID: unknown) { - const targetCompo = strapi.components[compoUID as UID.Component]; - if (!targetCompo) return true; // ignore this error as it will fail beforehand - - if (modelType === modelTypes.COMPONENT && hasComponent(targetCompo)) { - return this.createError({ - path: this.path, - message: `${targetCompo.modelName} already is a nested component. You cannot have more than one level of nesting inside your components.`, - }); - } - return true; - }, - }) - .required(), + // TODO: Add correct server validation for nested components + component: yup.string().required(), min: yup.number(), max: yup.number(), }; From 90aea0d45bc726f96bcc63e935276264a0ae7bc3 Mon Sep 17 00:00:00 2001 From: Bassel Kanso Date: Fri, 14 Jun 2024 15:59:29 +0300 Subject: [PATCH 9/9] chore: removed unused code --- .../server/src/controllers/validation/types.ts | 8 ++------ .../server/src/utils/attributes.ts | 10 +--------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/packages/core/content-type-builder/server/src/controllers/validation/types.ts b/packages/core/content-type-builder/server/src/controllers/validation/types.ts index f341b651d6..0b03fef29e 100644 --- a/packages/core/content-type-builder/server/src/controllers/validation/types.ts +++ b/packages/core/content-type-builder/server/src/controllers/validation/types.ts @@ -2,9 +2,8 @@ import _ from 'lodash'; import { yup } from '@strapi/utils'; import type { TestContext } from 'yup'; -import type { Schema, UID, Struct } from '@strapi/types'; +import type { Schema, Struct } from '@strapi/types'; -import { hasComponent } from '../../utils/attributes'; import { modelTypes, VALID_UID_TARGETS } from '../../services/constants'; import { validators, @@ -47,10 +46,7 @@ export const getTypeValidator = ( } as any); }; -const getTypeShape = ( - attribute: Schema.Attribute.AnyAttribute, - { modelType, attributes }: any = {} -) => { +const getTypeShape = (attribute: Schema.Attribute.AnyAttribute, { attributes }: any = {}) => { switch (attribute.type) { /** * complex types diff --git a/packages/core/content-type-builder/server/src/utils/attributes.ts b/packages/core/content-type-builder/server/src/utils/attributes.ts index 952a126820..e1113893ef 100644 --- a/packages/core/content-type-builder/server/src/utils/attributes.ts +++ b/packages/core/content-type-builder/server/src/utils/attributes.ts @@ -1,17 +1,9 @@ import _ from 'lodash'; import utils, { errors } from '@strapi/utils'; -import type { Schema, Struct } from '@strapi/types'; +import type { Schema } from '@strapi/types'; const { ApplicationError } = errors; -export const hasComponent = (model: Struct.Schema) => { - const compoKeys = Object.keys(model.attributes || {}).filter((key) => { - return model.attributes[key].type === 'component'; - }); - - return compoKeys.length > 0; -}; - export const isConfigurable = (attribute: Schema.Attribute.AnyAttribute) => _.get(attribute, 'configurable', true);