diff --git a/packages/core/content-type-builder/admin/src/components/FormModal/constants.js b/packages/core/content-type-builder/admin/src/components/FormModal/constants.js
index 47b0eda748..0636faf222 100644
--- a/packages/core/content-type-builder/admin/src/components/FormModal/constants.js
+++ b/packages/core/content-type-builder/admin/src/components/FormModal/constants.js
@@ -1,5 +1,3 @@
-export const ADD_COMPONENTS_TO_DYNAMIC_ZONE =
- 'ContentTypeBuilder/FormModal/ADD_COMPONENTS_TO_DYNAMIC_ZONE';
export const ON_CHANGE = 'ContentTypeBuilder/FormModal/ON_CHANGE';
export const ON_CHANGE_RELATION_TARGET = 'ContentTypeBuilder/FormModal/ON_CHANGE_RELATION_TARGET';
export const ON_CHANGE_RELATION_TYPE = 'ContentTypeBuilder/FormModal/ON_CHANGE_RELATION_TYPE';
diff --git a/packages/core/content-type-builder/admin/src/components/FormModal/dynamicZone/form.js b/packages/core/content-type-builder/admin/src/components/FormModal/dynamicZone/form.js
index b2b1ab3921..5ba1850cc2 100644
--- a/packages/core/content-type-builder/admin/src/components/FormModal/dynamicZone/form.js
+++ b/packages/core/content-type-builder/admin/src/components/FormModal/dynamicZone/form.js
@@ -35,7 +35,7 @@ const form = {
},
{
name: 'components',
- type: 'componentSelect',
+ type: 'select-components',
intlLabel: {
id: getTrad('modalForm.attributes.select-components'),
defaultMessage: 'Select the components',
diff --git a/packages/core/content-type-builder/admin/src/components/FormModal/index.js b/packages/core/content-type-builder/admin/src/components/FormModal/index.js
index 2587c718cc..cd2a703a86 100644
--- a/packages/core/content-type-builder/admin/src/components/FormModal/index.js
+++ b/packages/core/content-type-builder/admin/src/components/FormModal/index.js
@@ -37,6 +37,7 @@ import ComponentIconPicker from '../ComponentIconPicker';
import Relation from '../Relation';
import SelectCategory from '../SelectCategory';
import SelectComponent from '../SelectComponent';
+import SelectComponents from '../SelectComponents';
// import SelectCategory from '../WrapperSelect';
// import WrapperSelect from '../WrapperSelect';
import findAttribute from '../../utils/findAttribute';
@@ -59,7 +60,6 @@ import {
SET_DATA_TO_EDIT,
SET_DYNAMIC_ZONE_DATA_SCHEMA,
SET_ATTRIBUTE_DATA_SCHEMA,
- // ADD_COMPONENTS_TO_DYNAMIC_ZONE,
SET_ERRORS,
ON_CHANGE,
RESET_PROPS_AND_SET_THE_FORM_FOR_ADDING_A_COMPO_TO_A_DZ,
@@ -554,17 +554,6 @@ const FormModal = () => {
}, '');
};
- // const handleClickAddComponentsToDynamicZone = ({
- // target: { name, components, shouldAddComponents },
- // }) => {
- // dispatch({
- // type: ADD_COMPONENTS_TO_DYNAMIC_ZONE,
- // name,
- // components,
- // shouldAddComponents,
- // });
- // };
-
const handleChange = useCallback(
({ target: { name, value, type, ...rest } }) => {
const namesThatCanResetToNullValue = [
@@ -1099,11 +1088,13 @@ const FormModal = () => {
relation: Relation,
'select-category': SelectCategory,
'select-component': SelectComponent,
+ 'select-components': SelectComponents,
'select-default-boolean': BooleanDefaultValueSelect,
'toggle-draft-publish': DraftAndPublishToggle,
...inputsFromPlugins,
},
componentToCreate,
+ dynamicZoneTarget: state.dynamicZoneTarget,
formErrors,
isAddingAComponentToAnotherComponent,
isCreatingComponentWhileAddingAField,
diff --git a/packages/core/content-type-builder/admin/src/components/FormModal/reducer.js b/packages/core/content-type-builder/admin/src/components/FormModal/reducer.js
index 69945dbad8..fbb83ab491 100644
--- a/packages/core/content-type-builder/admin/src/components/FormModal/reducer.js
+++ b/packages/core/content-type-builder/admin/src/components/FormModal/reducer.js
@@ -3,7 +3,6 @@ import pluralize from 'pluralize';
import set from 'lodash/set';
import snakeCase from 'lodash/snakeCase';
import getRelationType from '../../utils/getRelationType';
-import makeUnique from '../../utils/makeUnique';
import { createComponentUid } from './utils/createUid';
import { shouldPluralizeName, shouldPluralizeTargetAttribute } from './utils/relations';
import * as actions from './constants';
@@ -20,22 +19,6 @@ const reducer = (state = initialState, action) =>
// eslint-disable-next-line consistent-return
produce(state, draftState => {
switch (action.type) {
- case actions.ADD_COMPONENTS_TO_DYNAMIC_ZONE: {
- const { components, shouldAddComponents } = action;
- let currentList = [];
-
- if (shouldAddComponents) {
- currentList = [...state.modifiedData.components, ...components];
- } else {
- currentList = state.modifiedData.components.filter(comp => {
- return components.indexOf(comp) === -1;
- });
- }
-
- draftState.modifiedData.components = makeUnique(currentList);
-
- break;
- }
case actions.ON_CHANGE: {
const { keys, value } = action;
const obj = state.modifiedData;
diff --git a/packages/core/content-type-builder/admin/src/components/FormModal/tests/reducer.test.js b/packages/core/content-type-builder/admin/src/components/FormModal/tests/reducer.test.js
index e34b3fd117..28439f4dbd 100644
--- a/packages/core/content-type-builder/admin/src/components/FormModal/tests/reducer.test.js
+++ b/packages/core/content-type-builder/admin/src/components/FormModal/tests/reducer.test.js
@@ -2,66 +2,6 @@ import reducer, { initialState } from '../reducer';
import * as actions from '../constants';
describe('CTB | components | FormModal | reducer | actions', () => {
- describe('ADD_COMPONENTS_TO_DYNAMIC_ZONE', () => {
- it('Should add the components correctly', () => {
- const action = {
- type: actions.ADD_COMPONENTS_TO_DYNAMIC_ZONE,
- components: ['default.test', 'default.test2', 'default.test3'],
- shouldAddComponents: true,
- name: 'components',
- };
-
- const state = {
- initialData: {},
- modifiedData: {
- type: 'dynamiczone',
- name: 'dz',
- components: ['default.test'],
- },
- };
-
- const expected = {
- initialData: {},
- modifiedData: {
- type: 'dynamiczone',
- name: 'dz',
- components: ['default.test', 'default.test2', 'default.test3'],
- },
- };
-
- expect(reducer(state, action)).toEqual(expected);
- });
-
- it('Should remove the components correctly', () => {
- const action = {
- type: actions.ADD_COMPONENTS_TO_DYNAMIC_ZONE,
- components: ['default.test2', 'default.test3'],
- shouldAddComponents: false,
- name: 'components',
- };
-
- const state = {
- initialData: {},
- modifiedData: {
- type: 'dynamiczone',
- name: 'dz',
- components: ['default.test', 'default.test2', 'default.test3'],
- },
- };
-
- const expected = {
- initialData: {},
- modifiedData: {
- type: 'dynamiczone',
- name: 'dz',
- components: ['default.test'],
- },
- };
-
- expect(reducer(state, action)).toEqual(expected);
- });
- });
-
describe(actions.ON_CHANGE, () => {
it('Should update the modifiedData object correctly', () => {
const action = {
diff --git a/packages/core/content-type-builder/admin/src/components/SelectComponent/tests/index.test.js b/packages/core/content-type-builder/admin/src/components/SelectComponent/tests/index.test.js
deleted file mode 100644
index 17004df28a..0000000000
--- a/packages/core/content-type-builder/admin/src/components/SelectComponent/tests/index.test.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- *
- * Tests for SelectComponent
- *
- */
-
-import React from 'react';
-import { render } from '@testing-library/react';
-import { ThemeProvider, lightTheme } from '@strapi/parts';
-import { IntlProvider } from 'react-intl';
-import SelectComponent from '../index';
-
-const messages = {
- 'content-type-builder.component.name': 'Select Component',
-};
-
-describe('', () => {
- it('renders and matches the snapshot', () => {
- const {
- container: { firstChild },
- } = render(
-
-
-
-
-
- );
-
- expect(firstChild).toMatchInlineSnapshot();
- });
-});
diff --git a/packages/core/content-type-builder/admin/src/components/SelectComponents/index.js b/packages/core/content-type-builder/admin/src/components/SelectComponents/index.js
new file mode 100644
index 0000000000..e0b30532f6
--- /dev/null
+++ b/packages/core/content-type-builder/admin/src/components/SelectComponents/index.js
@@ -0,0 +1,85 @@
+/**
+ *
+ * SelectComponents
+ *
+ */
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import { MultiSelectNested } from '@strapi/parts/Select';
+import { useIntl } from 'react-intl';
+import useDataManager from '../../hooks/useDataManager';
+import findAttribute from '../../utils/findAttribute';
+import { getTrad } from '../../utils';
+
+const SelectComponents = ({ dynamicZoneTarget, intlLabel, name, onChange, value }) => {
+ const { formatMessage } = useIntl();
+ const { componentsGroupedByCategory, modifiedData } = useDataManager();
+ const dzSchema =
+ findAttribute(modifiedData.contentType.schema.attributes, dynamicZoneTarget) || {};
+ const alreadyUsedComponents = dzSchema.components || [];
+ const filteredComponentsGroupedByCategory = Object.keys(componentsGroupedByCategory).reduce(
+ (acc, current) => {
+ const filteredComponents = componentsGroupedByCategory[current].filter(({ uid }) => {
+ return !alreadyUsedComponents.includes(uid);
+ });
+
+ if (filteredComponents.length > 0) {
+ acc[current] = filteredComponents;
+ }
+
+ return acc;
+ },
+ {}
+ );
+ const options = Object.entries(filteredComponentsGroupedByCategory).reduce((acc, current) => {
+ const [categoryName, components] = current;
+ const section = {
+ label: categoryName,
+ children: components.map(({ uid, schema: { name } }) => {
+ return { label: name, value: uid };
+ }),
+ };
+
+ acc.push(section);
+
+ return acc;
+ }, []);
+
+ const displayedValue = formatMessage(
+ {
+ id: getTrad('components.SelectComponents.displayed-value'),
+ defaultMessage:
+ '{number, plural, =0 {# components} one {# component} other {# components}} selected',
+ },
+ { number: value.length }
+ );
+
+ return (
+ displayedValue}
+ name={name}
+ onChange={values => {
+ onChange({ target: { name, value: values, type: 'select-components' } });
+ }}
+ options={options}
+ value={value || []}
+ />
+ );
+};
+
+SelectComponents.propTypes = {
+ intlLabel: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ defaultMessage: PropTypes.string.isRequired,
+ values: PropTypes.object,
+ }).isRequired,
+ dynamicZoneTarget: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ onChange: PropTypes.func.isRequired,
+ value: PropTypes.array.isRequired,
+};
+
+export default SelectComponents;
diff --git a/packages/core/content-type-builder/admin/src/translations/en.json b/packages/core/content-type-builder/admin/src/translations/en.json
index 8301377be7..25bda0890b 100644
--- a/packages/core/content-type-builder/admin/src/translations/en.json
+++ b/packages/core/content-type-builder/admin/src/translations/en.json
@@ -190,5 +190,6 @@
"table.button.no-fields": "Add new field",
"table.headers.name": "Name",
"table.headers.type": "Type",
- "ComponentIconPicker.search.placeholder": "Search for an icon"
+ "ComponentIconPicker.search.placeholder": "Search for an icon",
+ "components.SelectComponents.displayed-value": "{number, plural, =0 {# components} one {# component} other {# components}} selected"
}