diff --git a/packages/strapi-plugin-content-type-builder/admin/src/InjectedComponents/ContentManager/EditSettingViewButton.js b/packages/strapi-plugin-content-type-builder/admin/src/InjectedComponents/ContentManager/EditSettingViewButton.js index 52f1c5e588..95a7155838 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/InjectedComponents/ContentManager/EditSettingViewButton.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/InjectedComponents/ContentManager/EditSettingViewButton.js @@ -36,7 +36,6 @@ function EditViewButton(props) { : `${category}/${componentSlug}`; const handleClick = () => { - // TODO emitEvent('willEditEditLayout'); props.push(`${baseUrl}/${suffixUrl}`); }; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/components/AttributeOption/index.js b/packages/strapi-plugin-content-type-builder/admin/src/components/AttributeOption/index.js index 6819788e1a..9efae86ff7 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/components/AttributeOption/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/components/AttributeOption/index.js @@ -44,7 +44,24 @@ const AttributeOption = forwardRef(({ tabIndex, type }, ref) => { const handleClick = () => { const forTarget = query.get('forTarget'); const targetUid = query.get('targetUid'); - const headerDisplayName = query.get('headerDisplayName'); + const header_label_1 = query.get('header_label_1'); + const header_info_category_1 = query.get('header_info_category_1'); + const header_info_name_1 = query.get('header_info_name_1'); + const header_label_2 = query.get('header_label_2'); + const header_icon_name_2 = query.get('header_icon_name_2'); + const header_icon_isCustom_2 = query.get('header_icon_isCustom_2'); + const header_info_category_2 = query.get('header_info_category_2'); + const header_info_name_2 = query.get('header_info_name_2'); + const header_label_3 = query.get('header_label_3'); + const header_icon_name_3 = query.get('header_icon_name_3'); + const header_icon_isCustom_3 = query.get('header_icon_isCustom_3'); + const header_info_category_3 = query.get('header_info_category_3'); + const header_info_name_3 = query.get('header_info_name_3'); + const header_label_4 = query.get('header_label_4'); + const header_icon_name_4 = query.get('header_icon_name_4'); + const header_icon_isCustom_4 = query.get('header_icon_isCustom_4'); + const header_info_category_4 = query.get('header_info_category_4'); + const header_info_name_4 = query.get('header_info_name_4'); const search = makeSearch({ modalType: 'attribute', @@ -53,11 +70,28 @@ const AttributeOption = forwardRef(({ tabIndex, type }, ref) => { forTarget, targetUid, attributeType: type, - headerDisplayName, step: type === 'component' ? '1' : null, - headerDisplayCategory: query.get('headerDisplayCategory'), - headerDisplaySubCategory: query.get('headerDisplaySubCategory'), - subTargetUid: query.get('subTargetUid'), + + header_label_1, + header_info_name_1, + header_info_category_1, + header_label_2, + header_icon_name_2, + header_icon_isCustom_2, + header_info_name_2, + header_info_category_2, + header_label_3, + header_icon_name_3, + header_icon_isCustom_3, + header_info_name_3, + header_info_category_3, + header_label_4, + header_icon_name_4, + header_icon_isCustom_4, + header_info_name_4, + header_info_category_4, + header_icon_isCustom_1: false, + header_icon_name_1: type, }); if (forTarget === 'contentType') { diff --git a/packages/strapi-plugin-content-type-builder/admin/src/components/ComponentList/index.js b/packages/strapi-plugin-content-type-builder/admin/src/components/ComponentList/index.js index 9c9380412e..9ffdedae3c 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/components/ComponentList/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/components/ComponentList/index.js @@ -15,8 +15,10 @@ import Td from '../Td'; function ComponentList({ customRowComponent, component, + dzName, mainTypeName, isFromDynamicZone, + isNestedInDZComponent, firstLoopComponentName, firstLoopComponentUid, }) { @@ -32,6 +34,7 @@ function ComponentList({ { - let headerDisplayName = mainTypeName; + const firstComponentIcon = get( + modifiedData, + ['components', firstLoopComponentUid, 'schema', 'icon'], + '' + ); + const firstComponentCategory = get( + modifiedData, + ['components', firstLoopComponentUid, 'category'], + null + ); + const firstComponentFriendlyName = get( + modifiedData, + ['components', firstLoopComponentUid, 'schema', 'name'], + null + ); + const secondComponentCategory = get( + modifiedData, + ['components', secondLoopComponentUid, 'category'], + null + ); + const secondComponentFriendlyName = get( + modifiedData, + ['components', secondLoopComponentUid, 'schema', 'name'], + null + ); + const secondComponentIcon = get( + modifiedData, + ['components', secondLoopComponentUid, 'schema', 'icon'], + '' + ); + let firstHeaderObject = { + header_label_1: mainTypeName, + header_icon_name_1: editTarget, + header_icon_isCustom_1: false, + header_info_category_1: null, + header_info_name_1: null, + }; + let secondHeaderObject = { + header_label_2: firstLoopComponentName, + header_icon_name_2: 'component', + header_icon_isCustom_2: false, + header_info_category_2: firstComponentCategory, + header_info_name_2: firstComponentFriendlyName, + }; + let thirdHeaderObject = { + header_icon_name_3: 'component', + header_icon_isCustom_3: false, + header_info_category_3: secondComponentCategory, + header_info_name_3: secondComponentFriendlyName, + }; + let fourthHeaderObject = { + header_icon_name_4: null, + header_icon_isCustom_4: false, + header_info_category_4: secondComponentCategory, + header_info_name_4: secondComponentFriendlyName, + }; if (firstLoopComponentName) { - headerDisplayName = firstLoopComponentName; + firstHeaderObject = { + ...firstHeaderObject, + header_icon_name_1: firstComponentIcon, + header_icon_isCustom_1: true, + }; } if (secondLoopComponentUid) { - headerDisplayName = secondLoopComponentName; + firstHeaderObject = { + ...firstHeaderObject, + header_icon_name_1: secondComponentIcon, + header_icon_isCustom_1: true, + }; + thirdHeaderObject = { + ...thirdHeaderObject, + header_label_3: secondLoopComponentName, + }; + } + + if (isFromDynamicZone || isNestedInDZComponent) { + secondHeaderObject = { + ...secondHeaderObject, + header_label_2: dzName, + header_icon_name_2: 'dynamiczone', + header_icon_isCustom_2: false, + header_info_category_2: null, + header_info_name_2: null, + }; + thirdHeaderObject = { + ...thirdHeaderObject, + header_icon_name_3: isNestedInDZComponent ? 'component' : null, + header_label_3: firstLoopComponentName, + header_info_category_3: firstComponentCategory, + header_info_name_3: firstComponentFriendlyName, + }; + fourthHeaderObject = { + ...fourthHeaderObject, + header_label_4: secondLoopComponentName, + }; } openModalAddField( editTarget, targetUid, - headerDisplayName, - firstLoopComponentUid ? mainTypeName : null, - secondLoopComponentName ? firstLoopComponentName : null, - secondLoopComponentUid ? firstLoopComponentUid : null + firstHeaderObject, + secondHeaderObject, + thirdHeaderObject, + fourthHeaderObject ); }; @@ -87,8 +179,9 @@ function List({ {}, className: null, customRowComponent: null, + dzName: null, firstLoopComponentName: null, firstLoopComponentUid: null, isFromDynamicZone: false, + isNestedInDZComponent: false, isMain: false, isSub: false, items: [], @@ -162,10 +257,12 @@ List.propTypes = { addComponentToDZ: PropTypes.func, className: PropTypes.string, customRowComponent: PropTypes.func, + dzName: PropTypes.string, editTarget: PropTypes.string.isRequired, firstLoopComponentName: PropTypes.string, firstLoopComponentUid: PropTypes.string, isFromDynamicZone: PropTypes.bool, + isNestedInDZComponent: PropTypes.bool, isMain: PropTypes.bool, items: PropTypes.instanceOf(Array), mainTypeName: PropTypes.string.isRequired, diff --git a/packages/strapi-plugin-content-type-builder/admin/src/components/ListRow/index.js b/packages/strapi-plugin-content-type-builder/admin/src/components/ListRow/index.js index 1bdc055780..c0057d0fb3 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/components/ListRow/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/components/ListRow/index.js @@ -6,6 +6,7 @@ import { AttributeIcon } from '@buffetjs/core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import pluginId from '../../pluginId'; import useDataManager from '../../hooks/useDataManager'; +import getAttributeDisplayedType from '../../utils/getAttributeDisplayedType'; import getTrad from '../../utils/getTrad'; import Curve from '../../icons/Curve'; import UpperFist from '../UpperFirst'; @@ -14,6 +15,7 @@ import Wrapper from './Wrapper'; function ListRow({ configurable, name, + dzName, nature, onClick, plugin, @@ -28,12 +30,15 @@ function ListRow({ repeatable, secondLoopComponentName, secondLoopComponentUid, + isNestedInDZComponent, }) { const { contentTypes, isInDevelopmentMode, + modifiedData, removeAttribute, } = useDataManager(); + const ico = ['integer', 'biginteger', 'float', 'decimal'].includes(type) ? 'number' : type; @@ -54,15 +59,120 @@ function ListRow({ const handleClick = () => { if (configurable !== false) { + const firstComponentCategory = get( + modifiedData, + ['components', firstLoopComponentUid, 'category'], + null + ); + const secondComponentCategory = get( + modifiedData, + ['components', secondLoopComponentUid, 'category'], + null + ); + const attrType = nature ? 'relation' : type; - let headerDisplayName = mainTypeName; + const icoType = getAttributeDisplayedType(attrType); + + let firstHeaderObject = { + header_label_1: mainTypeName, + header_icon_name_1: icoType, + header_icon_isCustom_1: false, + header_info_category_1: null, + header_info_name_1: null, + }; + let secondHeaderObject = { + header_label_2: name, + header_icon_name_2: null, + header_icon_isCustom_2: false, + header_info_category_2: null, + header_info_name_2: null, + }; + let thirdHeaderObject = { + header_icon_name_3: 'component', + header_icon_isCustom_3: false, + header_info_category_3: null, + header_info_name_3: null, + }; + let fourthHeaderObject = { + header_icon_name_4: null, + header_icon_isCustom_4: false, + header_info_category_4: null, + header_info_name_4: null, + }; + let fifthHeaderObject = { + header_icon_name_5: null, + header_icon_isCustom_5: false, + header_info_category_5: null, + header_info_name_5: null, + }; if (firstLoopComponentName) { - headerDisplayName = firstLoopComponentName; + secondHeaderObject = { + header_label_2: firstLoopComponentName, + header_icon_name_2: 'component', + header_icon_isCustom_2: false, + header_info_category_2: firstComponentCategory, + header_info_name_2: firstLoopComponentName, + }; + + thirdHeaderObject = { + ...thirdHeaderObject, + header_label_3: name, + header_icon_name_3: null, + }; } if (secondLoopComponentUid) { - headerDisplayName = secondLoopComponentName; + thirdHeaderObject = { + ...thirdHeaderObject, + header_label_3: secondLoopComponentName, + header_icon_name_3: 'component', + header_info_category_3: secondComponentCategory, + header_info_name_3: secondLoopComponentName, + }; + fourthHeaderObject = { + ...fourthHeaderObject, + header_label_4: name, + header_icon_name_4: null, + }; + } + + if (isFromDynamicZone || isNestedInDZComponent) { + secondHeaderObject = { + header_label_2: dzName, + header_icon_name_2: 'dynamiczone', + header_icon_isCustom_2: false, + header_info_name_2: null, + header_info_category_2: null, + }; + thirdHeaderObject = { + header_icon_name_3: 'component', + header_label_3: firstLoopComponentName, + header_info_name_3: firstComponentCategory, + header_info_category_3: firstComponentCategory, + }; + + if (!isNestedInDZComponent) { + fourthHeaderObject = { + header_icon_name_4: null, + header_icon_isCustom_4: false, + header_info_category_4: null, + header_label_4: name, + }; + } else { + fourthHeaderObject = { + header_icon_name_4: 'components', + header_icon_isCustom_4: false, + header_info_category_4: secondComponentCategory, + header_info_name_4: secondLoopComponentName, + header_label_4: secondLoopComponentName, + }; + + fifthHeaderObject = { + ...fifthHeaderObject, + header_label_5: name, + }; + } } onClick( @@ -74,10 +184,11 @@ function ListRow({ name, // Type of the attribute attrType, - headerDisplayName, - firstLoopComponentUid ? mainTypeName : null, - secondLoopComponentName ? firstLoopComponentName : null, - secondLoopComponentUid ? firstLoopComponentUid : null + firstHeaderObject, + secondHeaderObject, + thirdHeaderObject, + fourthHeaderObject, + fifthHeaderObject ); } }; @@ -171,9 +282,11 @@ function ListRow({ ListRow.defaultProps = { configurable: true, + dzName: null, firstLoopComponentName: null, firstLoopComponentUid: null, isFromDynamicZone: false, + isNestedInDZComponent: false, nature: null, onClick: () => {}, onClickDelete: () => {}, @@ -188,10 +301,12 @@ ListRow.defaultProps = { ListRow.propTypes = { configurable: PropTypes.bool, + dzName: PropTypes.string, editTarget: PropTypes.string.isRequired, firstLoopComponentName: PropTypes.string, firstLoopComponentUid: PropTypes.string, isFromDynamicZone: PropTypes.bool, + isNestedInDZComponent: PropTypes.bool, mainTypeName: PropTypes.string.isRequired, name: PropTypes.string.isRequired, nature: PropTypes.string, diff --git a/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/ComponentInfos.js b/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/ComponentInfos.js index c48c48fe24..6f6cf23877 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/ComponentInfos.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/ComponentInfos.js @@ -1,33 +1,20 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { get, upperFirst } from 'lodash'; -import ComponentInfosWrapper from './ComponentInfosWrapper'; -import useDataManager from '../../hooks/useDataManager'; import UpperFirst from '../UpperFirst'; +import ComponentInfosWrapper from './ComponentInfosWrapper'; -const ComponentInfos = ({ uid }) => { - // We might want to change to initialData... - // @Aurelsicoko - const { modifiedData } = useDataManager(); - const currentComponent = get(modifiedData, ['components', uid], {}); - const currentComponentCategory = get(currentComponent, 'category', ''); - const currentComponentFriendlyName = get( - currentComponent, - ['schema', 'name'], - '' - ); - +const ComponentInfos = ({ category, name }) => { return ( -   ({upperFirst(currentComponentCategory)} -  —  - ) +   (  —  + ) ); }; ComponentInfos.propTypes = { - uid: PropTypes.string.isRequired, + category: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, }; export default ComponentInfos; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/DropdownInfos.js b/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/DropdownInfos.js new file mode 100644 index 0000000000..e6af105937 --- /dev/null +++ b/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/DropdownInfos.js @@ -0,0 +1,47 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import UpperFirst from '../UpperFirst'; +import Icon from './Icon'; +import Item from './Item'; +import Menu from './Menu'; +import Toggle from './Toggle'; +import Wrapper from './Wrapper'; + +const DropdownInfos = ({ headers, shouldDisplaySecondHeader }) => { + const [dropdownOpen, setDropdownOpen] = useState(false); + + const toggle = () => setDropdownOpen(prevState => !prevState); + return ( + + ... + + {headers.map((header, index) => { + if (!shouldDisplaySecondHeader && index === 1) { + return null; + } + + return ( + + + + + + + ); + })} + + + ); +}; + +DropdownInfos.defaultProps = { + headers: [], + shouldDisplaySecondHeader: false, +}; + +DropdownInfos.propTypes = { + headers: PropTypes.array, + shouldDisplaySecondHeader: PropTypes.bool, +}; + +export default DropdownInfos; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Header.js b/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Header.js deleted file mode 100644 index 0603b5393e..0000000000 --- a/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Header.js +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import UpperFirst from '../UpperFirst'; -import ComponentInfos from './ComponentInfos'; -import IconWrapper from './IconWrapper'; - -const Header = ({ - category, - name, - subCategory, - target, - targetUid, - subTargetUid, -}) => { - const shouldDisplayComponentCatInfos = target === 'components'; - - const content = ( - <> - - - - - - - {subCategory && ( - <> - - - - - - - - - )} - - ); - - return ( - <> - {category && content} - - - - {shouldDisplayComponentCatInfos && } - - ); -}; - -Header.defaultProps = { - category: null, - name: null, - subCategory: null, - subTargetUid: null, - target: null, - targetUid: null, -}; - -Header.propTypes = { - category: PropTypes.string, - name: PropTypes.string, - subCategory: PropTypes.string, - subTargetUid: PropTypes.string, - target: PropTypes.string, - targetUid: PropTypes.string, -}; - -export default Header; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Icon.js b/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Icon.js new file mode 100644 index 0000000000..e3b43e8c3a --- /dev/null +++ b/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Icon.js @@ -0,0 +1,13 @@ +import React from 'react'; +import { AttributeIcon } from '@buffetjs/core'; +import PropTypes from 'prop-types'; + +const Icon = ({ type }) => ( + +); + +Icon.propTypes = { + type: PropTypes.string.isRequired, +}; + +export default Icon; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Item.js b/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Item.js new file mode 100644 index 0000000000..cc74645fa9 --- /dev/null +++ b/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Item.js @@ -0,0 +1,19 @@ +import styled from 'styled-components'; +import { DropdownItem } from 'reactstrap'; + +const Item = styled(DropdownItem)` + display: flex; + padding-left: 10px; + padding-right: 10px; + color: #3b3b3b; + font-weight: 600; + font-size: 14px; + &:active, + &:focus, + &:hover { + background-color: transparent; + outline: 0; + } +`; + +export default Item; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Menu.js b/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Menu.js new file mode 100644 index 0000000000..798a681a39 --- /dev/null +++ b/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Menu.js @@ -0,0 +1,8 @@ +import styled from 'styled-components'; +import { DropdownMenu } from 'reactstrap'; + +const Menu = styled(DropdownMenu)` + top: 8px; +`; + +export default Menu; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Toggle.js b/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Toggle.js new file mode 100644 index 0000000000..a032de0504 --- /dev/null +++ b/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Toggle.js @@ -0,0 +1,14 @@ +import styled from 'styled-components'; +import { DropdownToggle } from 'reactstrap'; + +const Toggle = styled(DropdownToggle)` + height: 12px; + background: transparent; + border: 0; + margin-top: -14px; + color: #3b3b3b; + font-weight: 600; + font-size: 14px; +`; + +export default Toggle; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Wrapper.js b/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Wrapper.js new file mode 100644 index 0000000000..26d85e9cef --- /dev/null +++ b/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/Wrapper.js @@ -0,0 +1,12 @@ +import styled from 'styled-components'; +import { Dropdown } from 'reactstrap'; + +const Wrapper = styled(Dropdown)` + .dropdown-menu { + top: 8px !important; + box-shadow: 0 2px 4px #e3e9f3; + border: 0; + } +`; + +export default Wrapper; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/index.js b/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/index.js index 5014d5b29a..8646d1ea9b 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/components/ModalHeader/index.js @@ -1,89 +1,103 @@ -import React from 'react'; +import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import { HeaderModalTitle } from 'strapi-helper-plugin'; import { get } from 'lodash'; -import { AttributeIcon } from '@buffetjs/core'; import { FormattedMessage } from 'react-intl'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import useDataManager from '../../hooks/useDataManager'; -import pluginId from '../../pluginId'; import ComponentIcon from './ComponentIcon'; -import Header from './Header'; +import ComponentInfos from './ComponentInfos'; +import Icon from './Icon'; +import IconWrapper from './IconWrapper'; +import UpperFirst from '../UpperFirst'; +import DropdownInfos from './DropdownInfos'; -const ModalHeader = ({ - category, - headerId, - iconType, - name, - target, - targetUid, - subCategory, - subTargetUid, -}) => { - const { modifiedData } = useDataManager(); - const currentComponent = get(modifiedData, ['components', targetUid], {}); - const shouldDisplayComponentCatInfos = target === 'components'; - const currentComponentIcon = get(currentComponent, ['schema', 'icon'], ''); - - let iconName; - - if (iconType === 'components') { - iconName = 'component'; - } else { - iconName = iconType; - } +const ModalHeader = ({ headerId, headers }) => { + const shouldDisplayDropDown = headers.length > 3; return (
- {shouldDisplayComponentCatInfos ? ( - - - - ) : ( - - )} {headerId && ( - - )} - {!headerId && ( -
+ <> + + + )} + {!headerId && + headers.map((header, index) => { + const iconName = get(header, ['icon', 'name'], ''); + const iconType = iconName === null ? '' : iconName; + const icon = get(header, ['icon', 'isCustom'], false) ? ( + + + + ) : ( + + ); + + if (shouldDisplayDropDown && index === 1) { + return ( + + + + + 4} + /> + + ); + } + + if (shouldDisplayDropDown && index === 2 && headers.length > 4) { + return null; + } + + if (index === 0) { + return ( + + {icon} + + + + + ); + } + + return ( + + + + + + + + + {header.info.category && ( + + )} + + ); + })}
); }; ModalHeader.defaultProps = { - category: null, headerId: '', - iconType: 'contentType', - name: '', - target: null, - targetUid: null, - subCategory: null, - subTargetUid: null, + headers: [], }; ModalHeader.propTypes = { - category: PropTypes.string, headerId: PropTypes.string, - iconType: PropTypes.string, - name: PropTypes.string, - target: PropTypes.string, - targetUid: PropTypes.string, - subCategory: PropTypes.string, - subTargetUid: PropTypes.string, + headers: PropTypes.array, }; export default ModalHeader; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/reducer.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/reducer.js index 634056bc7e..d1d8ef40ac 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/reducer.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/reducer.js @@ -1,5 +1,6 @@ import { fromJS, OrderedMap } from 'immutable'; import { get, has } from 'lodash'; +import retrieveComponentsFromSchema from './utils/retrieveComponentsFromSchema'; import makeUnique from '../../utils/makeUnique'; const initialState = fromJS({ @@ -24,6 +25,47 @@ const getOppositeNature = originalNature => { } }; +const addComponentsToState = (state, componentToAddUid, objToUpdate) => { + let newObj = objToUpdate; + const componentToAdd = state.getIn(['components', componentToAddUid]); + const isTemporaryComponent = componentToAdd.get('isTemporary'); + const componentToAddSchema = componentToAdd.getIn(['schema', 'attributes']); + const hasComponentAlreadyBeenAdded = + state.getIn(['modifiedData', 'components', componentToAddUid]) !== + undefined; + + // created components are already in the modifiedData.components + // We don't add them because all modifications will be lost + if (isTemporaryComponent || hasComponentAlreadyBeenAdded) { + return newObj; + } + + // Add the added components to the modifiedData.compontnes + newObj = newObj.set(componentToAddUid, componentToAdd); + const nestedComponents = retrieveComponentsFromSchema( + componentToAddSchema.toJS(), + state.get('components').toJS() + ); + + // We need to add the nested components to the modifiedData.components as well + nestedComponents.forEach(componentUid => { + const isTemporary = + state.getIn(['components', componentUid, 'isTemporary']) || false; + const hasNestedComponentAlreadyBeenAdded = + state.getIn(['modifiedData', 'components', componentUid]) !== undefined; + + // Same logic here otherwise we will lose the modifications added to the components + if (!isTemporary && !hasNestedComponentAlreadyBeenAdded) { + newObj = newObj.set( + componentUid, + state.getIn(['components', componentUid]) + ); + } + }); + + return newObj; +}; + const reducer = (state, action) => { switch (action.type) { case 'ADD_ATTRIBUTE': { @@ -86,12 +128,7 @@ const reducer = (state, action) => { ) .updateIn(['modifiedData', 'components'], existingCompos => { if (action.shouldAddComponentToData) { - const componentToAdd = state.getIn(['components', rest.component]); - - return existingCompos.update( - componentToAdd.get('uid'), - () => componentToAdd - ); + return addComponentsToState(state, rest.component, existingCompos); } return existingCompos; @@ -138,15 +175,7 @@ const reducer = (state, action) => { ) .updateIn(['modifiedData', 'components'], old => { const componentsSchema = newComponents.reduce((acc, current) => { - const addedCompoSchema = state.getIn(['components', current]); - const isTemporaryComponent = addedCompoSchema.get('isTemporary'); - - // created components are already in the modifiedData.components - if (isTemporaryComponent) { - return acc; - } - - return acc.set(current, addedCompoSchema); + return addComponentsToState(state, current, acc); }, old); return componentsSchema; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/index.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/index.js index f369cb1fd0..3daf815e4d 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/index.js @@ -31,6 +31,8 @@ import getTrad from '../../utils/getTrad'; import makeSearch from '../../utils/makeSearch'; import getAttributes from './utils/attributes'; import forms from './utils/forms'; +import createHeadersArray from './utils/createHeadersArray'; +import createHeadersObjectFromArray from './utils/createHeadersObjectFromArray'; import { createComponentUid, createUid } from './utils/createUid'; import getModalTitleSubHeader from './utils/getModalTitleSubHeader'; import getNextSearch from './utils/getNextSearch'; @@ -75,18 +77,40 @@ const FormModal = () => { useEffect(() => { if (!isEmpty(search)) { const actionType = query.get('actionType'); - // Return 'null' if there isn't any attributeType search params + // Returns 'null' if there isn't any attributeType search params const attributeName = query.get('attributeName'); const attributeType = query.get('attributeType'); const dynamicZoneTarget = query.get('dynamicZoneTarget'); const forTarget = query.get('forTarget'); - const headerDisplayCategory = query.get('headerDisplayCategory'); - const headerDisplayName = query.get('headerDisplayName'); - const headerDisplaySubCategory = query.get('headerDisplaySubCategory'); const modalType = query.get('modalType'); const targetUid = query.get('targetUid'); const settingType = query.get('settingType'); - const subTargetUid = query.get('subTargetUid'); + const headerId = query.get('headerId'); + const header_label_1 = query.get('header_label_1'); + const header_icon_name_1 = query.get('header_icon_name_1'); + const header_icon_isCustom_1 = query.get('header_icon_isCustom_1'); + const header_info_category_1 = query.get('header_info_category_1'); + const header_info_name_1 = query.get('header_info_name_1'); + const header_label_2 = query.get('header_label_2'); + const header_icon_name_2 = query.get('header_icon_name_2'); + const header_icon_isCustom_2 = query.get('header_icon_isCustom_2'); + const header_info_category_2 = query.get('header_info_category_2'); + const header_info_name_2 = query.get('header_info_name_2'); + const header_label_3 = query.get('header_label_3'); + const header_icon_name_3 = query.get('header_icon_name_3'); + const header_icon_isCustom_3 = query.get('header_icon_isCustom_3'); + const header_info_category_3 = query.get('header_info_category_3'); + const header_info_name_3 = query.get('header_info_name_3'); + const header_label_4 = query.get('header_label_4'); + const header_icon_name_4 = query.get('header_icon_name_4'); + const header_icon_isCustom_4 = query.get('header_icon_isCustom_4'); + const header_info_category_4 = query.get('header_info_category_4'); + const header_info_name_4 = query.get('header_info_name_4'); + const header_label_5 = query.get('header_label_5'); + const header_icon_name_5 = query.get('header_icon_name_5'); + const header_icon_isCustom_5 = query.get('header_icon_isCustom_5'); + const header_info_category_5 = query.get('header_info_category_5'); + const header_info_name_5 = query.get('header_info_name_5'); const step = query.get('step'); const pathToSchema = forTarget === 'contentType' || forTarget === 'component' @@ -98,16 +122,38 @@ const FormModal = () => { attributeName, attributeType, dynamicZoneTarget, - headerDisplayName, - headerDisplayCategory, - headerDisplaySubCategory, forTarget, modalType, pathToSchema, settingType, - subTargetUid, step, targetUid, + header_label_1, + header_icon_name_1, + header_icon_isCustom_1, + header_info_name_1, + header_info_category_1, + header_label_2, + header_icon_name_2, + header_icon_isCustom_2, + header_info_name_2, + header_info_category_2, + header_label_3, + header_icon_name_3, + header_icon_isCustom_3, + header_info_name_3, + header_info_category_3, + header_label_4, + header_icon_name_4, + header_icon_isCustom_4, + header_info_name_4, + header_info_category_4, + header_label_5, + header_icon_name_5, + header_icon_isCustom_5, + header_info_name_5, + header_info_category_5, + headerId, }); // Reset all the modification when opening the edit category modal @@ -280,19 +326,7 @@ const FormModal = () => { const form = get(forms, [state.modalType, 'form', state.settingType], () => ({ items: [], })); - - // TODO improve icon logic - let iconType = ['component', 'contentType'].includes(state.modalType) - ? state.modalType - : state.forTarget; - - if (state.modalType === 'addComponentToDynamicZone') { - iconType = 'dynamiczone'; - } - - if (state.modalType === 'editCategory') { - iconType = 'component'; - } + const headers = createHeadersArray(state); const isCreatingContentType = state.modalType === 'contentType'; const isCreatingComponent = state.modalType === 'component'; @@ -310,14 +344,6 @@ const FormModal = () => { const isPickingAttribute = state.modalType === 'chooseAttribute'; const uid = createUid(modifiedData.name || ''); - let headerId = isCreating - ? `modalForm.${state.modalType}.header-create` - : 'modalForm.header-edit'; - - if (!['contentType', 'component'].includes(state.modalType)) { - headerId = null; - } - const checkFormValidity = async () => { let schema; const dataToValidate = @@ -532,6 +558,7 @@ const FormModal = () => { ...rest, }); }; + const handleSubmit = async (e, shouldContinue = isCreating) => { e.preventDefault(); @@ -541,17 +568,21 @@ const FormModal = () => { const targetUid = state.forTarget === 'components' ? state.targetUid : uid; - // TODO REMOVE and use makeSearch - const createNextSearch = searchUid => { - if (!shouldContinue) { - return ''; - } + const headerIcon = ['contentType', 'component'].includes(state.forTarget) + ? state.forTarget + : get( + allDataSchema, + ['components', state.targetUid, 'schema', 'icon'], + '' + ); - return `modalType=chooseAttribute&forTarget=${ - state.forTarget - }&targetUid=${searchUid}&headerDisplayName=${state.headerDisplayName || - modifiedData.name}`; - }; + // Remove the last header when editing + if (state.actionType === 'edit') { + headers.pop(); + } + + const headersObject = createHeadersObjectFromArray(headers); + const nextHeaderIndex = headers.length + 1; if (isCreatingContentType) { // Create the content type schema @@ -571,7 +602,9 @@ const FormModal = () => { modalType: 'chooseAttribute', forTarget: state.forTarget, targetUid, - headerDisplayName: modifiedData.name, + header_label_1: modifiedData.name, + header_icon_name_1: 'contentType', + header_icon_isCustom_1: null, }), }); } else if (isCreatingComponent) { @@ -590,7 +623,9 @@ const FormModal = () => { modalType: 'chooseAttribute', forTarget: state.forTarget, targetUid: componentUid, - headerDisplayName: modifiedData.name, + header_label_1: modifiedData.name, + header_icon_name_1: 'contentType', + header_icon_isCustom_1: null, }), pathname: `/plugins/${pluginId}/component-categories/${category}/${componentUid}`, }); @@ -604,6 +639,7 @@ const FormModal = () => { } } else if (isEditingCategory) { if (toLower(initialData.name) === toLower(modifiedData.name)) { + // Close the modal push({ search: '' }); return; @@ -629,20 +665,19 @@ const FormModal = () => { // Adding a component to a dynamiczone is not the same logic as creating a simple field // so the search is different - // For the modal header - const displayCategory = state.headerDisplayName; - const displayName = modifiedData.name; - const dzSearch = makeNextSearch({ modalType: 'addComponentToDynamicZone', forTarget: 'contentType', targetUid: state.targetUid, - headerDisplayName: displayName, - headerDisplayCategory: displayCategory, + dynamicZoneTarget: modifiedData.name, settingType: 'base', step: '1', actionType: 'create', + ...headersObject, + header_label_2: modifiedData.name, + header_icon_name_2: null, + header_icon_isCustom_2: false, }); const nextSearch = isDynamicZoneAttribute ? dzSearch @@ -651,11 +686,12 @@ const FormModal = () => { modalType: 'chooseAttribute', forTarget: state.forTarget, targetUid, - headerDisplayName: state.headerDisplayName, - headerDisplayCategory: state.headerDisplayCategory, - // keep the old state - headerDisplaySubCategory: state.headerDisplaySubCategory, - subTargetUid: state.subTargetUid, + ...headersObject, + header_icon_isCustom_1: ![ + 'contentType', + 'component', + ].includes(state.forTarget), + header_icon_name_1: headerIcon, }, shouldContinue ); @@ -680,7 +716,6 @@ const FormModal = () => { } else { if (isInFirstComponentStep) { // Navigate the user to step 2 - // TODO refacto const nextSearchObj = { modalType: 'attribute', actionType: state.actionType, @@ -688,8 +723,12 @@ const FormModal = () => { forTarget: state.forTarget, targetUid: state.targetUid, attributeType: 'component', - headerDisplayName: state.headerDisplayName, step: '2', + ...headersObject, + header_icon_isCustom_1: !['contentType', 'component'].includes( + state.forTarget + ), + header_icon_name_1: headerIcon, }; push({ @@ -723,9 +762,18 @@ const FormModal = () => { // This way we can add fields to the added component (if it wasn't there already) true ); + const nextSearch = { + modalType: 'chooseAttribute', + forTarget: state.forTarget, + targetUid: state.targetUid, + ...headersObject, + header_icon_isCustom_1: !['contentType', 'component'].includes( + state.forTarget + ), + header_icon_name_1: headerIcon, + }; - // TODO change the search so the modal header is kept - push({ search: shouldContinue ? createNextSearch(targetUid) : '' }); + push({ search: makeSearch(nextSearch, shouldContinue) }); // We don't need to end the loop here we want the reducer to be reinitialised } @@ -736,7 +784,6 @@ const FormModal = () => { // even though the user didn't set any field // We need to prevent the component from being created if the user closes the modal at step 2 without any submission } else if (isCreatingAttribute && isCreatingComponentFromAView) { - const { headerDisplayCategory } = state; // Step 1 if (isInFirstComponentStep) { // Here the search could be refactored since it is the same as the case from above @@ -749,18 +796,12 @@ const FormModal = () => { forTarget: state.forTarget, targetUid: state.targetUid, attributeType: 'component', - headerDisplayName: state.headerDisplayName, step: '2', + ...headersObject, + header_icon_isCustom_1: false, + header_icon_name_1: 'component', }; - // Modify the searchObj for the modal header - // This case is happening when creating a nestedComponent after creating a component - if (headerDisplayCategory) { - searchObj.headerDisplayCategory = state.headerDisplayCategory; - searchObj.headerDisplayName = state.headerDisplayName; - searchObj.targetUid = state.targetUid; - } - emitEvent('willCreateComponentFromAttributesModal'); push({ @@ -812,21 +853,16 @@ const FormModal = () => { modalType: 'chooseAttribute', forTarget: 'components', targetUid: componentUid, - headerDisplayName: modifiedData.name, - headerDisplayCategory: - state.headerDisplayCategory || state.headerDisplayName, + ...headersObject, + header_icon_isCustom_1: true, + header_icon_name_1: componentToCreate.icon, + [`header_label_${nextHeaderIndex}`]: modifiedData.name, + [`header_icon_name_${nextHeaderIndex}`]: 'component', + [`header_icon_isCustom_${nextHeaderIndex}`]: false, + [`header_info_category_${nextHeaderIndex}`]: category, + [`header_info_name_${nextHeaderIndex}`]: componentToCreate.name, }; - // Then we inverse the headerDisplayName because it becomes the last one displayed - // The case is created a component then created a nested one - if (headerDisplayCategory) { - // This is allows to modify the modal header - searchToOpenModalAttributeToAddAttributesToAComponent.headerDisplaySubCategory = - state.headerDisplayName; - searchToOpenModalAttributeToAddAttributesToAComponent.subTargetUid = - state.targetUid; - } - push({ search: makeNextSearch( searchToOpenModalAttributeToAddAttributesToAComponent, @@ -869,13 +905,19 @@ const FormModal = () => { // The Dynamic Zone and the component is created created // Open the modal to add fields to the created component - // TODO search for modal header const searchToOpenAddField = { modalType: 'chooseAttribute', forTarget: 'components', targetUid: componentUid, - headerDisplayName: modifiedData.componentToCreate.name, - headerDisplayCategory: state.headerDisplayCategory, + ...headersObject, + header_icon_isCustom_1: true, + header_icon_name_1: modifiedData.componentToCreate.icon, + [`header_label_${nextHeaderIndex}`]: modifiedData.name, + [`header_icon_name_${nextHeaderIndex}`]: 'component', + [`header_icon_isCustom_${nextHeaderIndex}`]: false, + [`header_info_category_${nextHeaderIndex}`]: category, + [`header_info_name_${nextHeaderIndex}`]: modifiedData + .componentToCreate.name, }; push({ search: makeSearch(searchToOpenAddField, true) }); @@ -901,7 +943,6 @@ const FormModal = () => { type: 'RESET_PROPS', }); } catch (err) { - console.log({ err }); const errors = getYupInnerErrors(err); dispatch({ @@ -986,16 +1027,7 @@ const FormModal = () => { withoverflow={toString(state.modalType === 'addComponentToDynamicZone')} > - +
{ return ( { + const array = Object.keys(obj).reduce((acc, current) => { + const splitted = current.split('_'); + const currentKeys = splitted.filter((_, i) => i !== splitted.length - 1); + + if (ALLOWED_KEYS.includes(currentKeys.join('_'))) { + const currentKeysIndex = parseInt(splitted[splitted.length - 1] - 1, 10); + const path = [ + currentKeysIndex, + ...currentKeys.filter(key => key !== 'header'), + ]; + + let value = obj[current]; + + if (current.includes('isCustom')) { + value = obj[current] === 'true'; + } + + set(acc, path, value); + } + + return acc; + }, []); + + return array.filter(obj => obj.label !== null); +}; + +export default createHeadersArray; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/utils/createHeadersObjectFromArray.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/utils/createHeadersObjectFromArray.js new file mode 100644 index 0000000000..9712d9e0c8 --- /dev/null +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/utils/createHeadersObjectFromArray.js @@ -0,0 +1,27 @@ +import { isObject } from 'lodash'; + +const createHeadersObjectFromArray = array => { + return array.reduce((acc, current, index) => { + const createHeaderObject = (obj, i, middle = '') => + Object.keys(obj).reduce((acc1, current1) => { + if (isObject(obj[current1])) { + return { + ...acc1, + ...createHeaderObject(obj[current1], i, `_${current1}`), + }; + } + + const name = `header${middle}_${current1}_${i}`; + + acc1[name] = obj[current1]; + + return acc1; + }, {}); + + const headerObject = createHeaderObject(current, index + 1); + + return { ...acc, ...headerObject }; + }, {}); +}; + +export default createHeadersObjectFromArray; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/utils/staticData.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/utils/staticData.js index e168a52a9c..a782f34dfd 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/utils/staticData.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/utils/staticData.js @@ -6,15 +6,37 @@ const INITIAL_STATE_DATA = { attributeType: null, dynamicZoneTarget: null, forTarget: null, - headerDisplayCategory: null, - headerDisplayName: null, - headerDisplaySubCategory: null, modalType: null, pathToSchema: [], settingType: null, step: null, - subTargetUid: null, targetUid: null, + headerId: null, + header_label_1: null, + header_icon_name_1: null, + header_icon_isCustom_1: null, + header_info_category_1: null, + header_info_name_1: null, + header_label_2: null, + header_icon_name_2: null, + header_icon_isCustom_2: null, + header_info_category_2: null, + header_info_name_2: null, + header_label_3: null, + header_icon_name_3: null, + header_icon_isCustom_3: null, + header_info_category_3: null, + header_info_name_3: null, + header_label_4: null, + header_icon_name_4: null, + header_icon_isCustom_4: null, + header_info_category_4: null, + header_info_name_4: null, + header_label_5: null, + header_icon_name_5: null, + header_icon_isCustom_5: null, + header_info_category_5: null, + header_info_name_5: null, }; export { NAVLINKS, INITIAL_STATE_DATA }; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/utils/tests/createHeadersArray.test.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/utils/tests/createHeadersArray.test.js new file mode 100644 index 0000000000..1cbe4a9638 --- /dev/null +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/utils/tests/createHeadersArray.test.js @@ -0,0 +1,143 @@ +import createHeadersArray from '../createHeadersArray'; + +describe('FormModal | utils | createHeadersArray', () => { + it('should return an empty array if no header key is set', () => { + const data = { + actionType: null, + attributeName: null, + attributeType: null, + dynamicZoneTarget: null, + forTarget: null, + modalType: null, + pathToSchema: [], + settingType: null, + step: null, + targetUid: null, + headerId: null, + header_label_1: null, + header_icon_name_1: null, + header_icon_isCustom_1: null, + header_info_category_1: null, + header_info_name_1: null, + header_label_2: null, + header_icon_name_2: null, + header_icon_isCustom_2: null, + header_info_category_2: null, + header_info_name_2: null, + header_label_3: null, + header_icon_name_3: null, + header_icon_isCustom_3: null, + header_info_category_3: null, + header_info_name_3: null, + }; + + expect(createHeadersArray(data)).toEqual([]); + }); + + it('should return an array containing a header object', () => { + const data = { + actionType: 'something', + attributeName: null, + attributeType: null, + dynamicZoneTarget: null, + forTarget: null, + + modalType: null, + pathToSchema: [], + settingType: null, + step: null, + targetUid: null, + headerId: null, + header_label_1: 'restaurant', + header_icon_name_1: 'contentType', + header_icon_isCustom_1: 'false', + header_info_category_1: null, + header_info_name_1: null, + header_label_2: null, + header_icon_name_2: null, + header_icon_isCustom_2: null, + header_info_category_2: null, + header_info_name_2: null, + header_label_3: null, + header_icon_name_3: null, + header_icon_isCustom_3: null, + header_info_category_3: null, + header_info_name_3: null, + }; + const expected = [ + { + label: 'restaurant', + icon: { + name: 'contentType', + isCustom: false, + }, + info: { + name: null, + category: null, + }, + }, + ]; + + expect(createHeadersArray(data)).toEqual(expected); + }); + + it('should handle multiple headers correctly', () => { + const data = { + actionType: 'something', + attributeName: null, + attributeType: null, + dynamicZoneTarget: null, + forTarget: null, + + modalType: null, + pathToSchema: [], + settingType: null, + step: null, + + targetUid: null, + headerId: null, + header_label_1: 'restaurant', + header_icon_name_1: 'bool', + header_icon_isCustom_1: 'true', + header_info_category_1: null, + header_info_name_1: null, + header_label_2: 'closing period', + header_icon_name_2: null, + header_icon_isCustom_2: null, + header_info_category_2: 'default', + header_info_name_2: 'closingperiod', + header_label_3: null, + header_icon_name_3: null, + header_icon_isCustom_3: null, + header_info_category_3: null, + header_info_name_3: null, + }; + + const expected = [ + { + label: 'restaurant', + icon: { + name: 'bool', + isCustom: true, + }, + info: { + name: null, + category: null, + }, + }, + { + label: 'closing period', + icon: { + name: null, + isCustom: false, + }, + info: { + name: 'closingperiod', + category: 'default', + }, + }, + ]; + + expect(createHeadersArray(data)).toEqual(expected); + }); +}); diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/utils/tests/createHeadersObjectFromArray.test.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/utils/tests/createHeadersObjectFromArray.test.js new file mode 100644 index 0000000000..8acf3a881c --- /dev/null +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/utils/tests/createHeadersObjectFromArray.test.js @@ -0,0 +1,45 @@ +import createHeadersObjectFromArray from '../createHeadersObjectFromArray'; + +describe('FormModal | utils | createHeadersArray', () => { + it('should return a header object', () => { + const data = [ + { + label: 'test', + icon: { + name: 'contentType', + isCustom: false, + }, + info: { + name: null, + category: null, + }, + }, + { + label: 'test2', + icon: { + name: 'book', + isCustom: true, + }, + info: { + name: 'something', + category: 'default', + }, + }, + ]; + + const expected = { + header_label_1: 'test', + header_icon_name_1: 'contentType', + header_icon_isCustom_1: false, + header_info_name_1: null, + header_info_category_1: null, + header_label_2: 'test2', + header_icon_name_2: 'book', + header_icon_isCustom_2: true, + header_info_name_2: 'something', + header_info_category_2: 'default', + }; + + expect(createHeadersObjectFromArray(data)).toEqual(expected); + }); +}); diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/LeftMenu/index.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/LeftMenu/index.js index eb0fd9c3d5..9a8bcc4016 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/LeftMenu/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/LeftMenu/index.js @@ -43,10 +43,19 @@ function LeftMenu({ wait }) { actionType: 'edit', modalType: 'editCategory', categoryName: data.name, - headerDisplayName: data.name, - headerDisplayCategory: formatMessage({ + header_label_1: formatMessage({ id: getTrad('modalForm.header.categories'), }), + header_icon_name_1: 'component', + header_icon_isCustom_1: false, + header_info_category_1: null, + header_info_name_1: null, + header_label_2: data.name, + header_icon_name_2: null, + header_icon_isCustom_2: false, + header_info_category_2: null, + header_info_name_2: null, + settingType: 'base', }); @@ -86,7 +95,9 @@ function LeftMenu({ wait }) { await wait(); push({ - search: `modalType=${type}&actionType=create&settingType=base&forTarget=${type}`, + search: `modalType=${type}&actionType=create&settingType=base&forTarget=${type}&headerId=${getTrad( + `modalForm.${type}.header-create` + )}&header_icon_name_1=${type}&header_icon_isCustom_1=false&header_label_1=null`, }); } else { displayNotificationCTNotSaved(); diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/ListView/index.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/ListView/index.js index 5c89907d61..1c6072d54d 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/ListView/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/ListView/index.js @@ -11,6 +11,7 @@ import { import { Header } from '@buffetjs/custom'; import ListViewContext from '../../contexts/ListViewContext'; import convertAttrObjToArray from '../../utils/convertAttrObjToArray'; +import getAttributeDisplayedType from '../../utils/getAttributeDisplayedType'; import getTrad from '../../utils/getTrad'; import makeSearch from '../../utils/makeSearch'; import ListRow from '../../components/ListRow'; @@ -71,41 +72,49 @@ const ListView = () => { const hasModelBeenModified = !isEqual(modifiedData, initialData); const forTarget = isInContentTypeView ? 'contentType' : 'component'; - const handleClickOpenModalAddField = async ( + const handleClickAddField = async ( forTarget, targetUid, - headerDisplayName, - headerDisplayCategory = null, - headerDisplaySubCategory = null, - subTargetUid = null + firstHeaderObj, + secondHeaderObj, + thirdHeaderObj, + fourthHeaderObj ) => { + // disable the prompt + await wait(); + const searchObj = { modalType: 'chooseAttribute', forTarget, targetUid, - headerDisplayName, - headerDisplayCategory, - headerDisplaySubCategory, - subTargetUid, + ...firstHeaderObj, + ...secondHeaderObj, + ...thirdHeaderObj, + ...fourthHeaderObj, }; - // Disable the prompt - await wait(); - - push({ search: makeSearch(searchObj, true) }); + push({ search: makeSearch(searchObj) }); }; const handleClickAddComponentToDZ = async dzName => { + const firstHeaderObject = { + header_label_1: currentDataName, + header_icon_name_1: 'dynamiczone', + header_icon_isCustom_1: false, + }; + const secondHeaderObj = { + header_label_2: dzName, + }; const search = { modalType: 'addComponentToDynamicZone', forTarget: 'contentType', targetUid, - headerDisplayCategory: currentDataName, dynamicZoneTarget: dzName, settingType: 'base', step: '1', actionType: 'edit', - headerDisplayName: dzName, + ...firstHeaderObject, + ...secondHeaderObj, }; await wait(); @@ -118,30 +127,13 @@ const ListView = () => { targetUid, attributeName, type, - headerDisplayName, - headerDisplayCategory = null, - headerDisplaySubCategory = null, - subTargetUid = null + firstHeaderObj, + secondHeaderObj, + thirdHeaderObj, + fourthHeaderObj, + fifthHeaderObj ) => { - let attributeType; - - switch (type) { - case 'integer': - case 'biginteger': - case 'decimal': - case 'float': - attributeType = 'number'; - break; - case 'string': - case 'text': - attributeType = 'text'; - break; - case '': - attributeType = 'relation'; - break; - default: - attributeType = type; - } + const attributeType = getAttributeDisplayedType(type); await wait(); @@ -153,11 +145,12 @@ const ListView = () => { targetUid, attributeName, attributeType, - headerDisplayName, - headerDisplayCategory, - headerDisplaySubCategory, step: type === 'component' ? '2' : null, - subTargetUid, + ...firstHeaderObj, + ...secondHeaderObj, + ...thirdHeaderObj, + ...fourthHeaderObj, + ...fifthHeaderObj, }; await wait(); @@ -181,6 +174,7 @@ const ListView = () => { const wait = async () => { togglePrompt(false); + return new Promise(resolve => setTimeout(resolve, 100)); }; const label = get(modifiedData, [firstMainDataPath, 'schema', 'name'], ''); @@ -231,7 +225,10 @@ const ListView = () => { settingType: 'base', forTarget: firstMainDataPath, targetUid, - headerDisplayName: label, + header_label_1: label, + header_icon_isCustom_1: false, + header_icon_name_1: firstMainDataPath, + headerId: getTrad('modalForm.header-edit'), }), }); }, @@ -258,7 +255,12 @@ const ListView = () => { color: 'primary', label: formatMessage({ id: `${pluginId}.button.attributes.add.another` }), onClick: () => { - handleClickOpenModalAddField(forTarget, targetUid, currentDataName); + const headerDisplayObject = { + header_label_1: currentDataName, + header_icon_name_1: forTarget, + header_icon_isCustom_1: false, + }; + handleClickAddField(forTarget, targetUid, headerDisplayObject); }, }; const goToCMSettingsPage = () => { @@ -281,8 +283,6 @@ const ListView = () => { ? [{ ...configureButtonProps }, { ...addButtonProps }] : [configureButtonProps]; - const handleClickOnTrashIcon = () => {}; - const CustomRow = props => { const { name } = props; @@ -292,7 +292,6 @@ const ListView = () => { attributeName={name} name={name} onClick={handleClickEditField} - onClickDelete={handleClickOnTrashIcon} /> ); }; @@ -307,7 +306,7 @@ const ListView = () => { return ( diff --git a/packages/strapi-plugin-content-type-builder/admin/src/utils/getAttributeDisplayedType.js b/packages/strapi-plugin-content-type-builder/admin/src/utils/getAttributeDisplayedType.js new file mode 100644 index 0000000000..cb088b225b --- /dev/null +++ b/packages/strapi-plugin-content-type-builder/admin/src/utils/getAttributeDisplayedType.js @@ -0,0 +1,25 @@ +const getAttributeDisplayedType = type => { + let displayedType; + + switch (type) { + case 'integer': + case 'biginteger': + case 'decimal': + case 'float': + displayedType = 'number'; + break; + case 'string': + case 'text': + displayedType = 'text'; + break; + case '': + displayedType = 'relation'; + break; + default: + displayedType = type; + } + + return displayedType; +}; + +export default getAttributeDisplayedType; diff --git a/packages/strapi-plugin-content-type-builder/controllers/validation/relations.js b/packages/strapi-plugin-content-type-builder/controllers/validation/relations.js index e9375af1ed..e4330459dd 100644 --- a/packages/strapi-plugin-content-type-builder/controllers/validation/relations.js +++ b/packages/strapi-plugin-content-type-builder/controllers/validation/relations.js @@ -22,6 +22,7 @@ module.exports = (obj, validNatures) => { .required(), unique: validators.unique.nullable(), configurable: yup.boolean().nullable(), + autoPopulate: yup.boolean().nullable(), dominant: yup.boolean().nullable(), columnName: yup.string().nullable(), targetAttribute: REVERSE_RELATIONS.includes(obj.nature) diff --git a/packages/strapi-plugin-content-type-builder/services/schema-builder/content-type-builder.js b/packages/strapi-plugin-content-type-builder/services/schema-builder/content-type-builder.js index e80e1864a8..692975529d 100644 --- a/packages/strapi-plugin-content-type-builder/services/schema-builder/content-type-builder.js +++ b/packages/strapi-plugin-content-type-builder/services/schema-builder/content-type-builder.js @@ -172,6 +172,10 @@ module.exports = function createComponentBuilder() { this.unsetRelation(oldAttribute); } + if (Object.keys(oldAttribute).includes('autoPopulate')) { + newAttribute.autoPopulate = oldAttribute.autoPopulate; + } + return this.setRelation({ key, modelName: contentType.modelName, diff --git a/packages/strapi-plugin-content-type-builder/services/schema-builder/index.js b/packages/strapi-plugin-content-type-builder/services/schema-builder/index.js index 12a343e3e8..871c51cea8 100644 --- a/packages/strapi-plugin-content-type-builder/services/schema-builder/index.js +++ b/packages/strapi-plugin-content-type-builder/services/schema-builder/index.js @@ -119,12 +119,14 @@ function createSchemaBuilder({ components, contentTypes }) { targetAttribute, columnName, dominant, + autoPopulate, } = attribute; const attr = { unique: unique === true ? true : undefined, columnName: columnName || undefined, configurable: configurable === false ? false : undefined, + autoPopulate: autoPopulate === false ? false : undefined, }; if (!this.contentTypes.has(target)) { diff --git a/packages/strapi-plugin-content-type-builder/utils/attributes.js b/packages/strapi-plugin-content-type-builder/utils/attributes.js index 08459acbab..1a093ef79b 100644 --- a/packages/strapi-plugin-content-type-builder/utils/attributes.js +++ b/packages/strapi-plugin-content-type-builder/utils/attributes.js @@ -85,6 +85,7 @@ const formatAttribute = (key, attribute, { model }) => { undefined ), unique: attribute.unique ? true : false, + autoPopulate: attribute.autoPopulate === false ? false : undefined, }; } };