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" }