fix - keep autoPopulate:false options on edit

This commit is contained in:
Alexandre BODIN 2020-01-08 11:40:49 +01:00 committed by Adam Patai
commit c5cef7dcbb
29 changed files with 1016 additions and 339 deletions

View File

@ -36,7 +36,6 @@ function EditViewButton(props) {
: `${category}/${componentSlug}`; : `${category}/${componentSlug}`;
const handleClick = () => { const handleClick = () => {
// TODO
emitEvent('willEditEditLayout'); emitEvent('willEditEditLayout');
props.push(`${baseUrl}/${suffixUrl}`); props.push(`${baseUrl}/${suffixUrl}`);
}; };

View File

@ -44,7 +44,24 @@ const AttributeOption = forwardRef(({ tabIndex, type }, ref) => {
const handleClick = () => { const handleClick = () => {
const forTarget = query.get('forTarget'); const forTarget = query.get('forTarget');
const targetUid = query.get('targetUid'); 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({ const search = makeSearch({
modalType: 'attribute', modalType: 'attribute',
@ -53,11 +70,28 @@ const AttributeOption = forwardRef(({ tabIndex, type }, ref) => {
forTarget, forTarget,
targetUid, targetUid,
attributeType: type, attributeType: type,
headerDisplayName,
step: type === 'component' ? '1' : null, step: type === 'component' ? '1' : null,
headerDisplayCategory: query.get('headerDisplayCategory'),
headerDisplaySubCategory: query.get('headerDisplaySubCategory'), header_label_1,
subTargetUid: query.get('subTargetUid'), 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') { if (forTarget === 'contentType') {

View File

@ -15,8 +15,10 @@ import Td from '../Td';
function ComponentList({ function ComponentList({
customRowComponent, customRowComponent,
component, component,
dzName,
mainTypeName, mainTypeName,
isFromDynamicZone, isFromDynamicZone,
isNestedInDZComponent,
firstLoopComponentName, firstLoopComponentName,
firstLoopComponentUid, firstLoopComponentUid,
}) { }) {
@ -32,6 +34,7 @@ function ComponentList({
<Td colSpan={12} isChildOfDynamicZone={isFromDynamicZone}> <Td colSpan={12} isChildOfDynamicZone={isFromDynamicZone}>
<List <List
customRowComponent={customRowComponent} customRowComponent={customRowComponent}
dzName={dzName}
items={convertAttrObjToArray(attributes)} items={convertAttrObjToArray(attributes)}
targetUid={component} targetUid={component}
mainTypeName={mainTypeName} mainTypeName={mainTypeName}
@ -39,6 +42,7 @@ function ComponentList({
firstLoopComponentUid={firstLoopComponentUid || component} firstLoopComponentUid={firstLoopComponentUid || component}
editTarget="components" editTarget="components"
isFromDynamicZone={isFromDynamicZone} isFromDynamicZone={isFromDynamicZone}
isNestedInDZComponent={isNestedInDZComponent}
isSub isSub
secondLoopComponentName={ secondLoopComponentName={
firstLoopComponentName ? componentName : null firstLoopComponentName ? componentName : null
@ -53,15 +57,19 @@ function ComponentList({
ComponentList.defaultProps = { ComponentList.defaultProps = {
component: null, component: null,
customRowComponent: null, customRowComponent: null,
dzName: null,
isFromDynamicZone: false, isFromDynamicZone: false,
isNestedInDZComponent: false,
}; };
ComponentList.propTypes = { ComponentList.propTypes = {
component: PropTypes.string, component: PropTypes.string,
customRowComponent: PropTypes.func, customRowComponent: PropTypes.func,
dzName: PropTypes.string,
firstLoopComponentName: PropTypes.string, firstLoopComponentName: PropTypes.string,
firstLoopComponentUid: PropTypes.string, firstLoopComponentUid: PropTypes.string,
isFromDynamicZone: PropTypes.bool, isFromDynamicZone: PropTypes.bool,
isNestedInDZComponent: PropTypes.bool,
mainTypeName: PropTypes.string.isRequired, mainTypeName: PropTypes.string.isRequired,
targetUid: PropTypes.string.isRequired, targetUid: PropTypes.string.isRequired,
}; };

View File

@ -85,6 +85,7 @@ function DynamicZoneList({
<ComponentList <ComponentList
{...props} {...props}
isFromDynamicZone isFromDynamicZone
dzName={name}
mainTypeName={mainTypeName} mainTypeName={mainTypeName}
targetUid={targetUid} targetUid={targetUid}
key={component} key={component}

View File

@ -6,6 +6,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { get } from 'lodash';
import { useGlobalContext } from 'strapi-helper-plugin'; import { useGlobalContext } from 'strapi-helper-plugin';
import { Plus } from '@buffetjs/icons'; import { Plus } from '@buffetjs/icons';
@ -26,34 +27,125 @@ function List({
mainTypeName, mainTypeName,
editTarget, editTarget,
isFromDynamicZone, isFromDynamicZone,
isNestedInDZComponent,
isMain, isMain,
firstLoopComponentName, firstLoopComponentName,
firstLoopComponentUid, firstLoopComponentUid,
secondLoopComponentName, secondLoopComponentName,
secondLoopComponentUid, secondLoopComponentUid,
isSub, isSub,
dzName,
}) { }) {
const { formatMessage } = useGlobalContext(); const { formatMessage } = useGlobalContext();
const { isInDevelopmentMode } = useDataManager(); const { isInDevelopmentMode, modifiedData } = useDataManager();
const { openModalAddField } = useListView(); const { openModalAddField } = useListView();
const onClickAddField = () => { const onClickAddField = () => {
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) { if (firstLoopComponentName) {
headerDisplayName = firstLoopComponentName; firstHeaderObject = {
...firstHeaderObject,
header_icon_name_1: firstComponentIcon,
header_icon_isCustom_1: true,
};
} }
if (secondLoopComponentUid) { 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( openModalAddField(
editTarget, editTarget,
targetUid, targetUid,
headerDisplayName, firstHeaderObject,
firstLoopComponentUid ? mainTypeName : null, secondHeaderObject,
secondLoopComponentName ? firstLoopComponentName : null, thirdHeaderObject,
secondLoopComponentUid ? firstLoopComponentUid : null fourthHeaderObject
); );
}; };
@ -87,8 +179,9 @@ function List({
<React.Fragment key={item.name}> <React.Fragment key={item.name}>
<CustomRow <CustomRow
{...item} {...item}
dzName={dzName}
isNestedInDZComponent={isNestedInDZComponent}
targetUid={targetUid} targetUid={targetUid}
// NEW props
mainTypeName={mainTypeName} mainTypeName={mainTypeName}
editTarget={editTarget} editTarget={editTarget}
firstLoopComponentName={firstLoopComponentName} firstLoopComponentName={firstLoopComponentName}
@ -103,8 +196,8 @@ function List({
{...item} {...item}
customRowComponent={customRowComponent} customRowComponent={customRowComponent}
targetUid={targetUid} targetUid={targetUid}
// NEW PROPS dzName={dzName}
isNestedInDZComponent={isFromDynamicZone}
mainTypeName={mainTypeName} mainTypeName={mainTypeName}
editTarget={editTarget} editTarget={editTarget}
firstLoopComponentName={firstLoopComponentName} firstLoopComponentName={firstLoopComponentName}
@ -147,9 +240,11 @@ List.defaultProps = {
addComponentToDZ: () => {}, addComponentToDZ: () => {},
className: null, className: null,
customRowComponent: null, customRowComponent: null,
dzName: null,
firstLoopComponentName: null, firstLoopComponentName: null,
firstLoopComponentUid: null, firstLoopComponentUid: null,
isFromDynamicZone: false, isFromDynamicZone: false,
isNestedInDZComponent: false,
isMain: false, isMain: false,
isSub: false, isSub: false,
items: [], items: [],
@ -162,10 +257,12 @@ List.propTypes = {
addComponentToDZ: PropTypes.func, addComponentToDZ: PropTypes.func,
className: PropTypes.string, className: PropTypes.string,
customRowComponent: PropTypes.func, customRowComponent: PropTypes.func,
dzName: PropTypes.string,
editTarget: PropTypes.string.isRequired, editTarget: PropTypes.string.isRequired,
firstLoopComponentName: PropTypes.string, firstLoopComponentName: PropTypes.string,
firstLoopComponentUid: PropTypes.string, firstLoopComponentUid: PropTypes.string,
isFromDynamicZone: PropTypes.bool, isFromDynamicZone: PropTypes.bool,
isNestedInDZComponent: PropTypes.bool,
isMain: PropTypes.bool, isMain: PropTypes.bool,
items: PropTypes.instanceOf(Array), items: PropTypes.instanceOf(Array),
mainTypeName: PropTypes.string.isRequired, mainTypeName: PropTypes.string.isRequired,

View File

@ -6,6 +6,7 @@ import { AttributeIcon } from '@buffetjs/core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import pluginId from '../../pluginId'; import pluginId from '../../pluginId';
import useDataManager from '../../hooks/useDataManager'; import useDataManager from '../../hooks/useDataManager';
import getAttributeDisplayedType from '../../utils/getAttributeDisplayedType';
import getTrad from '../../utils/getTrad'; import getTrad from '../../utils/getTrad';
import Curve from '../../icons/Curve'; import Curve from '../../icons/Curve';
import UpperFist from '../UpperFirst'; import UpperFist from '../UpperFirst';
@ -14,6 +15,7 @@ import Wrapper from './Wrapper';
function ListRow({ function ListRow({
configurable, configurable,
name, name,
dzName,
nature, nature,
onClick, onClick,
plugin, plugin,
@ -28,12 +30,15 @@ function ListRow({
repeatable, repeatable,
secondLoopComponentName, secondLoopComponentName,
secondLoopComponentUid, secondLoopComponentUid,
isNestedInDZComponent,
}) { }) {
const { const {
contentTypes, contentTypes,
isInDevelopmentMode, isInDevelopmentMode,
modifiedData,
removeAttribute, removeAttribute,
} = useDataManager(); } = useDataManager();
const ico = ['integer', 'biginteger', 'float', 'decimal'].includes(type) const ico = ['integer', 'biginteger', 'float', 'decimal'].includes(type)
? 'number' ? 'number'
: type; : type;
@ -54,15 +59,120 @@ function ListRow({
const handleClick = () => { const handleClick = () => {
if (configurable !== false) { if (configurable !== false) {
const firstComponentCategory = get(
modifiedData,
['components', firstLoopComponentUid, 'category'],
null
);
const secondComponentCategory = get(
modifiedData,
['components', secondLoopComponentUid, 'category'],
null
);
const attrType = nature ? 'relation' : type; 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) { 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) { 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( onClick(
@ -74,10 +184,11 @@ function ListRow({
name, name,
// Type of the attribute // Type of the attribute
attrType, attrType,
headerDisplayName, firstHeaderObject,
firstLoopComponentUid ? mainTypeName : null, secondHeaderObject,
secondLoopComponentName ? firstLoopComponentName : null, thirdHeaderObject,
secondLoopComponentUid ? firstLoopComponentUid : null fourthHeaderObject,
fifthHeaderObject
); );
} }
}; };
@ -171,9 +282,11 @@ function ListRow({
ListRow.defaultProps = { ListRow.defaultProps = {
configurable: true, configurable: true,
dzName: null,
firstLoopComponentName: null, firstLoopComponentName: null,
firstLoopComponentUid: null, firstLoopComponentUid: null,
isFromDynamicZone: false, isFromDynamicZone: false,
isNestedInDZComponent: false,
nature: null, nature: null,
onClick: () => {}, onClick: () => {},
onClickDelete: () => {}, onClickDelete: () => {},
@ -188,10 +301,12 @@ ListRow.defaultProps = {
ListRow.propTypes = { ListRow.propTypes = {
configurable: PropTypes.bool, configurable: PropTypes.bool,
dzName: PropTypes.string,
editTarget: PropTypes.string.isRequired, editTarget: PropTypes.string.isRequired,
firstLoopComponentName: PropTypes.string, firstLoopComponentName: PropTypes.string,
firstLoopComponentUid: PropTypes.string, firstLoopComponentUid: PropTypes.string,
isFromDynamicZone: PropTypes.bool, isFromDynamicZone: PropTypes.bool,
isNestedInDZComponent: PropTypes.bool,
mainTypeName: PropTypes.string.isRequired, mainTypeName: PropTypes.string.isRequired,
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
nature: PropTypes.string, nature: PropTypes.string,

View File

@ -1,33 +1,20 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { get, upperFirst } from 'lodash';
import ComponentInfosWrapper from './ComponentInfosWrapper';
import useDataManager from '../../hooks/useDataManager';
import UpperFirst from '../UpperFirst'; import UpperFirst from '../UpperFirst';
import ComponentInfosWrapper from './ComponentInfosWrapper';
const ComponentInfos = ({ uid }) => { const ComponentInfos = ({ category, name }) => {
// 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'],
''
);
return ( return (
<ComponentInfosWrapper> <ComponentInfosWrapper>
&nbsp; ({upperFirst(currentComponentCategory)} &nbsp; (<UpperFirst content={category} /> &nbsp;&nbsp;
&nbsp;&nbsp; <UpperFirst content={name} />)
<UpperFirst content={currentComponentFriendlyName} />)
</ComponentInfosWrapper> </ComponentInfosWrapper>
); );
}; };
ComponentInfos.propTypes = { ComponentInfos.propTypes = {
uid: PropTypes.string.isRequired, category: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
}; };
export default ComponentInfos; export default ComponentInfos;

View File

@ -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 (
<Wrapper isOpen={dropdownOpen} toggle={toggle} style={{ margin: 'auto 0' }}>
<Toggle>...</Toggle>
<Menu style={{ top: '8px' }}>
{headers.map((header, index) => {
if (!shouldDisplaySecondHeader && index === 1) {
return null;
}
return (
<Item key={index}>
<Icon type={header.icon.name} />
<span>
<UpperFirst content={header.label} />
</span>
</Item>
);
})}
</Menu>
</Wrapper>
);
};
DropdownInfos.defaultProps = {
headers: [],
shouldDisplaySecondHeader: false,
};
DropdownInfos.propTypes = {
headers: PropTypes.array,
shouldDisplaySecondHeader: PropTypes.bool,
};
export default DropdownInfos;

View File

@ -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 = (
<>
<span>
<UpperFirst content={category} />
</span>
<IconWrapper>
<FontAwesomeIcon icon="chevron-right" />
</IconWrapper>
{subCategory && (
<>
<span>
<UpperFirst content={subCategory} />
</span>
<ComponentInfos uid={subTargetUid} />
<IconWrapper>
<FontAwesomeIcon icon="chevron-right" />
</IconWrapper>
</>
)}
</>
);
return (
<>
{category && content}
<span>
<UpperFirst content={name} />
</span>
{shouldDisplayComponentCatInfos && <ComponentInfos uid={targetUid} />}
</>
);
};
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;

View File

@ -0,0 +1,13 @@
import React from 'react';
import { AttributeIcon } from '@buffetjs/core';
import PropTypes from 'prop-types';
const Icon = ({ type }) => (
<AttributeIcon type={type} style={{ margin: 'auto 20px auto 0' }} />
);
Icon.propTypes = {
type: PropTypes.string.isRequired,
};
export default Icon;

View File

@ -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;

View File

@ -0,0 +1,8 @@
import styled from 'styled-components';
import { DropdownMenu } from 'reactstrap';
const Menu = styled(DropdownMenu)`
top: 8px;
`;
export default Menu;

View File

@ -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;

View File

@ -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;

View File

@ -1,89 +1,103 @@
import React from 'react'; import React, { Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { HeaderModalTitle } from 'strapi-helper-plugin'; import { HeaderModalTitle } from 'strapi-helper-plugin';
import { get } from 'lodash'; import { get } from 'lodash';
import { AttributeIcon } from '@buffetjs/core';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import useDataManager from '../../hooks/useDataManager';
import pluginId from '../../pluginId';
import ComponentIcon from './ComponentIcon'; 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 = ({ const ModalHeader = ({ headerId, headers }) => {
category, const shouldDisplayDropDown = headers.length > 3;
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;
}
return ( return (
<section> <section>
<HeaderModalTitle style={{ textTransform: 'none' }}> <HeaderModalTitle style={{ textTransform: 'none' }}>
{shouldDisplayComponentCatInfos ? (
<ComponentIcon isSelected>
<FontAwesomeIcon icon={currentComponentIcon} />
</ComponentIcon>
) : (
<AttributeIcon
type={iconName}
style={{ margin: 'auto 20px auto 0' }}
/>
)}
{headerId && ( {headerId && (
<FormattedMessage id={`${pluginId}.${headerId}`} values={{ name }} /> <>
)} <Icon type={get(headers, [0, 'icon', 'name'], '')} />
{!headerId && ( <FormattedMessage
<Header id={headerId}
category={category} values={{ name: get(headers, [0, 'label'], '') }}
name={name} />
target={target} </>
targetUid={targetUid}
subCategory={subCategory}
subTargetUid={subTargetUid}
/>
)} )}
{!headerId &&
headers.map((header, index) => {
const iconName = get(header, ['icon', 'name'], '');
const iconType = iconName === null ? '' : iconName;
const icon = get(header, ['icon', 'isCustom'], false) ? (
<ComponentIcon isSelected>
<FontAwesomeIcon icon={iconType} />
</ComponentIcon>
) : (
<Icon type={iconType} />
);
if (shouldDisplayDropDown && index === 1) {
return (
<Fragment key={index}>
<IconWrapper>
<FontAwesomeIcon icon="chevron-right" />
</IconWrapper>
<DropdownInfos
headers={[headers[1], headers[2]]}
shouldDisplaySecondHeader={headers.length > 4}
/>
</Fragment>
);
}
if (shouldDisplayDropDown && index === 2 && headers.length > 4) {
return null;
}
if (index === 0) {
return (
<Fragment key={index}>
{icon}
<span>
<UpperFirst content={get(header, ['label'], '')} />
</span>
</Fragment>
);
}
return (
<Fragment key={index}>
<IconWrapper>
<FontAwesomeIcon icon="chevron-right" />
</IconWrapper>
<span>
<UpperFirst content={get(header, ['label'], '')} />
</span>
{header.info.category && (
<ComponentInfos
category={header.info.category}
name={header.info.name}
/>
)}
</Fragment>
);
})}
</HeaderModalTitle> </HeaderModalTitle>
</section> </section>
); );
}; };
ModalHeader.defaultProps = { ModalHeader.defaultProps = {
category: null,
headerId: '', headerId: '',
iconType: 'contentType', headers: [],
name: '',
target: null,
targetUid: null,
subCategory: null,
subTargetUid: null,
}; };
ModalHeader.propTypes = { ModalHeader.propTypes = {
category: PropTypes.string,
headerId: PropTypes.string, headerId: PropTypes.string,
iconType: PropTypes.string, headers: PropTypes.array,
name: PropTypes.string,
target: PropTypes.string,
targetUid: PropTypes.string,
subCategory: PropTypes.string,
subTargetUid: PropTypes.string,
}; };
export default ModalHeader; export default ModalHeader;

View File

@ -1,5 +1,6 @@
import { fromJS, OrderedMap } from 'immutable'; import { fromJS, OrderedMap } from 'immutable';
import { get, has } from 'lodash'; import { get, has } from 'lodash';
import retrieveComponentsFromSchema from './utils/retrieveComponentsFromSchema';
import makeUnique from '../../utils/makeUnique'; import makeUnique from '../../utils/makeUnique';
const initialState = fromJS({ 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) => { const reducer = (state, action) => {
switch (action.type) { switch (action.type) {
case 'ADD_ATTRIBUTE': { case 'ADD_ATTRIBUTE': {
@ -86,12 +128,7 @@ const reducer = (state, action) => {
) )
.updateIn(['modifiedData', 'components'], existingCompos => { .updateIn(['modifiedData', 'components'], existingCompos => {
if (action.shouldAddComponentToData) { if (action.shouldAddComponentToData) {
const componentToAdd = state.getIn(['components', rest.component]); return addComponentsToState(state, rest.component, existingCompos);
return existingCompos.update(
componentToAdd.get('uid'),
() => componentToAdd
);
} }
return existingCompos; return existingCompos;
@ -138,15 +175,7 @@ const reducer = (state, action) => {
) )
.updateIn(['modifiedData', 'components'], old => { .updateIn(['modifiedData', 'components'], old => {
const componentsSchema = newComponents.reduce((acc, current) => { const componentsSchema = newComponents.reduce((acc, current) => {
const addedCompoSchema = state.getIn(['components', current]); return addComponentsToState(state, current, acc);
const isTemporaryComponent = addedCompoSchema.get('isTemporary');
// created components are already in the modifiedData.components
if (isTemporaryComponent) {
return acc;
}
return acc.set(current, addedCompoSchema);
}, old); }, old);
return componentsSchema; return componentsSchema;

View File

@ -31,6 +31,8 @@ import getTrad from '../../utils/getTrad';
import makeSearch from '../../utils/makeSearch'; import makeSearch from '../../utils/makeSearch';
import getAttributes from './utils/attributes'; import getAttributes from './utils/attributes';
import forms from './utils/forms'; import forms from './utils/forms';
import createHeadersArray from './utils/createHeadersArray';
import createHeadersObjectFromArray from './utils/createHeadersObjectFromArray';
import { createComponentUid, createUid } from './utils/createUid'; import { createComponentUid, createUid } from './utils/createUid';
import getModalTitleSubHeader from './utils/getModalTitleSubHeader'; import getModalTitleSubHeader from './utils/getModalTitleSubHeader';
import getNextSearch from './utils/getNextSearch'; import getNextSearch from './utils/getNextSearch';
@ -75,18 +77,40 @@ const FormModal = () => {
useEffect(() => { useEffect(() => {
if (!isEmpty(search)) { if (!isEmpty(search)) {
const actionType = query.get('actionType'); 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 attributeName = query.get('attributeName');
const attributeType = query.get('attributeType'); const attributeType = query.get('attributeType');
const dynamicZoneTarget = query.get('dynamicZoneTarget'); const dynamicZoneTarget = query.get('dynamicZoneTarget');
const forTarget = query.get('forTarget'); 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 modalType = query.get('modalType');
const targetUid = query.get('targetUid'); const targetUid = query.get('targetUid');
const settingType = query.get('settingType'); 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 step = query.get('step');
const pathToSchema = const pathToSchema =
forTarget === 'contentType' || forTarget === 'component' forTarget === 'contentType' || forTarget === 'component'
@ -98,16 +122,38 @@ const FormModal = () => {
attributeName, attributeName,
attributeType, attributeType,
dynamicZoneTarget, dynamicZoneTarget,
headerDisplayName,
headerDisplayCategory,
headerDisplaySubCategory,
forTarget, forTarget,
modalType, modalType,
pathToSchema, pathToSchema,
settingType, settingType,
subTargetUid,
step, step,
targetUid, 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 // 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], () => ({ const form = get(forms, [state.modalType, 'form', state.settingType], () => ({
items: [], items: [],
})); }));
const headers = createHeadersArray(state);
// 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 isCreatingContentType = state.modalType === 'contentType'; const isCreatingContentType = state.modalType === 'contentType';
const isCreatingComponent = state.modalType === 'component'; const isCreatingComponent = state.modalType === 'component';
@ -310,14 +344,6 @@ const FormModal = () => {
const isPickingAttribute = state.modalType === 'chooseAttribute'; const isPickingAttribute = state.modalType === 'chooseAttribute';
const uid = createUid(modifiedData.name || ''); 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 () => { const checkFormValidity = async () => {
let schema; let schema;
const dataToValidate = const dataToValidate =
@ -532,6 +558,7 @@ const FormModal = () => {
...rest, ...rest,
}); });
}; };
const handleSubmit = async (e, shouldContinue = isCreating) => { const handleSubmit = async (e, shouldContinue = isCreating) => {
e.preventDefault(); e.preventDefault();
@ -541,17 +568,21 @@ const FormModal = () => {
const targetUid = const targetUid =
state.forTarget === 'components' ? state.targetUid : uid; state.forTarget === 'components' ? state.targetUid : uid;
// TODO REMOVE and use makeSearch const headerIcon = ['contentType', 'component'].includes(state.forTarget)
const createNextSearch = searchUid => { ? state.forTarget
if (!shouldContinue) { : get(
return ''; allDataSchema,
} ['components', state.targetUid, 'schema', 'icon'],
''
);
return `modalType=chooseAttribute&forTarget=${ // Remove the last header when editing
state.forTarget if (state.actionType === 'edit') {
}&targetUid=${searchUid}&headerDisplayName=${state.headerDisplayName || headers.pop();
modifiedData.name}`; }
};
const headersObject = createHeadersObjectFromArray(headers);
const nextHeaderIndex = headers.length + 1;
if (isCreatingContentType) { if (isCreatingContentType) {
// Create the content type schema // Create the content type schema
@ -571,7 +602,9 @@ const FormModal = () => {
modalType: 'chooseAttribute', modalType: 'chooseAttribute',
forTarget: state.forTarget, forTarget: state.forTarget,
targetUid, targetUid,
headerDisplayName: modifiedData.name, header_label_1: modifiedData.name,
header_icon_name_1: 'contentType',
header_icon_isCustom_1: null,
}), }),
}); });
} else if (isCreatingComponent) { } else if (isCreatingComponent) {
@ -590,7 +623,9 @@ const FormModal = () => {
modalType: 'chooseAttribute', modalType: 'chooseAttribute',
forTarget: state.forTarget, forTarget: state.forTarget,
targetUid: componentUid, 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}`, pathname: `/plugins/${pluginId}/component-categories/${category}/${componentUid}`,
}); });
@ -604,6 +639,7 @@ const FormModal = () => {
} }
} else if (isEditingCategory) { } else if (isEditingCategory) {
if (toLower(initialData.name) === toLower(modifiedData.name)) { if (toLower(initialData.name) === toLower(modifiedData.name)) {
// Close the modal
push({ search: '' }); push({ search: '' });
return; return;
@ -629,20 +665,19 @@ const FormModal = () => {
// Adding a component to a dynamiczone is not the same logic as creating a simple field // Adding a component to a dynamiczone is not the same logic as creating a simple field
// so the search is different // so the search is different
// For the modal header
const displayCategory = state.headerDisplayName;
const displayName = modifiedData.name;
const dzSearch = makeNextSearch({ const dzSearch = makeNextSearch({
modalType: 'addComponentToDynamicZone', modalType: 'addComponentToDynamicZone',
forTarget: 'contentType', forTarget: 'contentType',
targetUid: state.targetUid, targetUid: state.targetUid,
headerDisplayName: displayName,
headerDisplayCategory: displayCategory,
dynamicZoneTarget: modifiedData.name, dynamicZoneTarget: modifiedData.name,
settingType: 'base', settingType: 'base',
step: '1', step: '1',
actionType: 'create', actionType: 'create',
...headersObject,
header_label_2: modifiedData.name,
header_icon_name_2: null,
header_icon_isCustom_2: false,
}); });
const nextSearch = isDynamicZoneAttribute const nextSearch = isDynamicZoneAttribute
? dzSearch ? dzSearch
@ -651,11 +686,12 @@ const FormModal = () => {
modalType: 'chooseAttribute', modalType: 'chooseAttribute',
forTarget: state.forTarget, forTarget: state.forTarget,
targetUid, targetUid,
headerDisplayName: state.headerDisplayName, ...headersObject,
headerDisplayCategory: state.headerDisplayCategory, header_icon_isCustom_1: ![
// keep the old state 'contentType',
headerDisplaySubCategory: state.headerDisplaySubCategory, 'component',
subTargetUid: state.subTargetUid, ].includes(state.forTarget),
header_icon_name_1: headerIcon,
}, },
shouldContinue shouldContinue
); );
@ -680,7 +716,6 @@ const FormModal = () => {
} else { } else {
if (isInFirstComponentStep) { if (isInFirstComponentStep) {
// Navigate the user to step 2 // Navigate the user to step 2
// TODO refacto
const nextSearchObj = { const nextSearchObj = {
modalType: 'attribute', modalType: 'attribute',
actionType: state.actionType, actionType: state.actionType,
@ -688,8 +723,12 @@ const FormModal = () => {
forTarget: state.forTarget, forTarget: state.forTarget,
targetUid: state.targetUid, targetUid: state.targetUid,
attributeType: 'component', attributeType: 'component',
headerDisplayName: state.headerDisplayName,
step: '2', step: '2',
...headersObject,
header_icon_isCustom_1: !['contentType', 'component'].includes(
state.forTarget
),
header_icon_name_1: headerIcon,
}; };
push({ push({
@ -723,9 +762,18 @@ const FormModal = () => {
// This way we can add fields to the added component (if it wasn't there already) // This way we can add fields to the added component (if it wasn't there already)
true 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: makeSearch(nextSearch, shouldContinue) });
push({ search: shouldContinue ? createNextSearch(targetUid) : '' });
// We don't need to end the loop here we want the reducer to be reinitialised // 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 // 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 // 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) { } else if (isCreatingAttribute && isCreatingComponentFromAView) {
const { headerDisplayCategory } = state;
// Step 1 // Step 1
if (isInFirstComponentStep) { if (isInFirstComponentStep) {
// Here the search could be refactored since it is the same as the case from above // 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, forTarget: state.forTarget,
targetUid: state.targetUid, targetUid: state.targetUid,
attributeType: 'component', attributeType: 'component',
headerDisplayName: state.headerDisplayName,
step: '2', 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'); emitEvent('willCreateComponentFromAttributesModal');
push({ push({
@ -812,21 +853,16 @@ const FormModal = () => {
modalType: 'chooseAttribute', modalType: 'chooseAttribute',
forTarget: 'components', forTarget: 'components',
targetUid: componentUid, targetUid: componentUid,
headerDisplayName: modifiedData.name, ...headersObject,
headerDisplayCategory: header_icon_isCustom_1: true,
state.headerDisplayCategory || state.headerDisplayName, 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({ push({
search: makeNextSearch( search: makeNextSearch(
searchToOpenModalAttributeToAddAttributesToAComponent, searchToOpenModalAttributeToAddAttributesToAComponent,
@ -869,13 +905,19 @@ const FormModal = () => {
// The Dynamic Zone and the component is created created // The Dynamic Zone and the component is created created
// Open the modal to add fields to the created component // Open the modal to add fields to the created component
// TODO search for modal header
const searchToOpenAddField = { const searchToOpenAddField = {
modalType: 'chooseAttribute', modalType: 'chooseAttribute',
forTarget: 'components', forTarget: 'components',
targetUid: componentUid, targetUid: componentUid,
headerDisplayName: modifiedData.componentToCreate.name, ...headersObject,
headerDisplayCategory: state.headerDisplayCategory, 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) }); push({ search: makeSearch(searchToOpenAddField, true) });
@ -901,7 +943,6 @@ const FormModal = () => {
type: 'RESET_PROPS', type: 'RESET_PROPS',
}); });
} catch (err) { } catch (err) {
console.log({ err });
const errors = getYupInnerErrors(err); const errors = getYupInnerErrors(err);
dispatch({ dispatch({
@ -986,16 +1027,7 @@ const FormModal = () => {
withoverflow={toString(state.modalType === 'addComponentToDynamicZone')} withoverflow={toString(state.modalType === 'addComponentToDynamicZone')}
> >
<HeaderModal> <HeaderModal>
<ModalHeader <ModalHeader headerId={state.headerId} headers={headers} />
name={state.headerDisplayName}
category={state.headerDisplayCategory}
headerId={headerId}
iconType={iconType || 'contentType'}
subCategory={state.headerDisplaySubCategory}
subTargetUid={state.subTargetUid}
target={state.forTarget}
targetUid={state.targetUid}
/>
<section> <section>
<HeaderModalTitle> <HeaderModalTitle>
<FormattedMessage <FormattedMessage
@ -1160,7 +1192,7 @@ const FormModal = () => {
return ( return (
<RelationForm <RelationForm
key="relation" key="relation"
mainBoxHeader={state.headerDisplayName} mainBoxHeader={get(headers, [0, 'label'], '')}
modifiedData={modifiedData} modifiedData={modifiedData}
naturePickerType={state.forTarget} naturePickerType={state.forTarget}
onChange={handleChange} onChange={handleChange}

View File

@ -0,0 +1,37 @@
import { set } from 'lodash';
const ALLOWED_KEYS = [
'header_label',
'header_icon_name',
'header_icon_isCustom',
'header_info_category',
'header_info_name',
];
const createHeadersArray = obj => {
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;

View File

@ -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;

View File

@ -6,15 +6,37 @@ const INITIAL_STATE_DATA = {
attributeType: null, attributeType: null,
dynamicZoneTarget: null, dynamicZoneTarget: null,
forTarget: null, forTarget: null,
headerDisplayCategory: null,
headerDisplayName: null,
headerDisplaySubCategory: null,
modalType: null, modalType: null,
pathToSchema: [], pathToSchema: [],
settingType: null, settingType: null,
step: null, step: null,
subTargetUid: null,
targetUid: 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 }; export { NAVLINKS, INITIAL_STATE_DATA };

View File

@ -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);
});
});

View File

@ -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);
});
});

View File

@ -43,10 +43,19 @@ function LeftMenu({ wait }) {
actionType: 'edit', actionType: 'edit',
modalType: 'editCategory', modalType: 'editCategory',
categoryName: data.name, categoryName: data.name,
headerDisplayName: data.name, header_label_1: formatMessage({
headerDisplayCategory: formatMessage({
id: getTrad('modalForm.header.categories'), 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', settingType: 'base',
}); });
@ -86,7 +95,9 @@ function LeftMenu({ wait }) {
await wait(); await wait();
push({ 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 { } else {
displayNotificationCTNotSaved(); displayNotificationCTNotSaved();

View File

@ -11,6 +11,7 @@ import {
import { Header } from '@buffetjs/custom'; import { Header } from '@buffetjs/custom';
import ListViewContext from '../../contexts/ListViewContext'; import ListViewContext from '../../contexts/ListViewContext';
import convertAttrObjToArray from '../../utils/convertAttrObjToArray'; import convertAttrObjToArray from '../../utils/convertAttrObjToArray';
import getAttributeDisplayedType from '../../utils/getAttributeDisplayedType';
import getTrad from '../../utils/getTrad'; import getTrad from '../../utils/getTrad';
import makeSearch from '../../utils/makeSearch'; import makeSearch from '../../utils/makeSearch';
import ListRow from '../../components/ListRow'; import ListRow from '../../components/ListRow';
@ -71,41 +72,49 @@ const ListView = () => {
const hasModelBeenModified = !isEqual(modifiedData, initialData); const hasModelBeenModified = !isEqual(modifiedData, initialData);
const forTarget = isInContentTypeView ? 'contentType' : 'component'; const forTarget = isInContentTypeView ? 'contentType' : 'component';
const handleClickOpenModalAddField = async ( const handleClickAddField = async (
forTarget, forTarget,
targetUid, targetUid,
headerDisplayName, firstHeaderObj,
headerDisplayCategory = null, secondHeaderObj,
headerDisplaySubCategory = null, thirdHeaderObj,
subTargetUid = null fourthHeaderObj
) => { ) => {
// disable the prompt
await wait();
const searchObj = { const searchObj = {
modalType: 'chooseAttribute', modalType: 'chooseAttribute',
forTarget, forTarget,
targetUid, targetUid,
headerDisplayName, ...firstHeaderObj,
headerDisplayCategory, ...secondHeaderObj,
headerDisplaySubCategory, ...thirdHeaderObj,
subTargetUid, ...fourthHeaderObj,
}; };
// Disable the prompt push({ search: makeSearch(searchObj) });
await wait();
push({ search: makeSearch(searchObj, true) });
}; };
const handleClickAddComponentToDZ = async dzName => { 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 = { const search = {
modalType: 'addComponentToDynamicZone', modalType: 'addComponentToDynamicZone',
forTarget: 'contentType', forTarget: 'contentType',
targetUid, targetUid,
headerDisplayCategory: currentDataName,
dynamicZoneTarget: dzName, dynamicZoneTarget: dzName,
settingType: 'base', settingType: 'base',
step: '1', step: '1',
actionType: 'edit', actionType: 'edit',
headerDisplayName: dzName, ...firstHeaderObject,
...secondHeaderObj,
}; };
await wait(); await wait();
@ -118,30 +127,13 @@ const ListView = () => {
targetUid, targetUid,
attributeName, attributeName,
type, type,
headerDisplayName, firstHeaderObj,
headerDisplayCategory = null, secondHeaderObj,
headerDisplaySubCategory = null, thirdHeaderObj,
subTargetUid = null fourthHeaderObj,
fifthHeaderObj
) => { ) => {
let attributeType; const attributeType = getAttributeDisplayedType(type);
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;
}
await wait(); await wait();
@ -153,11 +145,12 @@ const ListView = () => {
targetUid, targetUid,
attributeName, attributeName,
attributeType, attributeType,
headerDisplayName,
headerDisplayCategory,
headerDisplaySubCategory,
step: type === 'component' ? '2' : null, step: type === 'component' ? '2' : null,
subTargetUid, ...firstHeaderObj,
...secondHeaderObj,
...thirdHeaderObj,
...fourthHeaderObj,
...fifthHeaderObj,
}; };
await wait(); await wait();
@ -181,6 +174,7 @@ const ListView = () => {
const wait = async () => { const wait = async () => {
togglePrompt(false); togglePrompt(false);
return new Promise(resolve => setTimeout(resolve, 100)); return new Promise(resolve => setTimeout(resolve, 100));
}; };
const label = get(modifiedData, [firstMainDataPath, 'schema', 'name'], ''); const label = get(modifiedData, [firstMainDataPath, 'schema', 'name'], '');
@ -231,7 +225,10 @@ const ListView = () => {
settingType: 'base', settingType: 'base',
forTarget: firstMainDataPath, forTarget: firstMainDataPath,
targetUid, 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', color: 'primary',
label: formatMessage({ id: `${pluginId}.button.attributes.add.another` }), label: formatMessage({ id: `${pluginId}.button.attributes.add.another` }),
onClick: () => { 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 = () => { const goToCMSettingsPage = () => {
@ -281,8 +283,6 @@ const ListView = () => {
? [{ ...configureButtonProps }, { ...addButtonProps }] ? [{ ...configureButtonProps }, { ...addButtonProps }]
: [configureButtonProps]; : [configureButtonProps];
const handleClickOnTrashIcon = () => {};
const CustomRow = props => { const CustomRow = props => {
const { name } = props; const { name } = props;
@ -292,7 +292,6 @@ const ListView = () => {
attributeName={name} attributeName={name}
name={name} name={name}
onClick={handleClickEditField} onClick={handleClickEditField}
onClickDelete={handleClickOnTrashIcon}
/> />
); );
}; };
@ -307,7 +306,7 @@ const ListView = () => {
return ( return (
<ListViewContext.Provider <ListViewContext.Provider
value={{ openModalAddField: handleClickOpenModalAddField }} value={{ openModalAddField: handleClickAddField }}
> >
<Wrapper> <Wrapper>
<BackHeader onClick={goBack} /> <BackHeader onClick={goBack} />

View File

@ -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;

View File

@ -22,6 +22,7 @@ module.exports = (obj, validNatures) => {
.required(), .required(),
unique: validators.unique.nullable(), unique: validators.unique.nullable(),
configurable: yup.boolean().nullable(), configurable: yup.boolean().nullable(),
autoPopulate: yup.boolean().nullable(),
dominant: yup.boolean().nullable(), dominant: yup.boolean().nullable(),
columnName: yup.string().nullable(), columnName: yup.string().nullable(),
targetAttribute: REVERSE_RELATIONS.includes(obj.nature) targetAttribute: REVERSE_RELATIONS.includes(obj.nature)

View File

@ -172,6 +172,10 @@ module.exports = function createComponentBuilder() {
this.unsetRelation(oldAttribute); this.unsetRelation(oldAttribute);
} }
if (Object.keys(oldAttribute).includes('autoPopulate')) {
newAttribute.autoPopulate = oldAttribute.autoPopulate;
}
return this.setRelation({ return this.setRelation({
key, key,
modelName: contentType.modelName, modelName: contentType.modelName,

View File

@ -119,12 +119,14 @@ function createSchemaBuilder({ components, contentTypes }) {
targetAttribute, targetAttribute,
columnName, columnName,
dominant, dominant,
autoPopulate,
} = attribute; } = attribute;
const attr = { const attr = {
unique: unique === true ? true : undefined, unique: unique === true ? true : undefined,
columnName: columnName || undefined, columnName: columnName || undefined,
configurable: configurable === false ? false : undefined, configurable: configurable === false ? false : undefined,
autoPopulate: autoPopulate === false ? false : undefined,
}; };
if (!this.contentTypes.has(target)) { if (!this.contentTypes.has(target)) {

View File

@ -85,6 +85,7 @@ const formatAttribute = (key, attribute, { model }) => {
undefined undefined
), ),
unique: attribute.unique ? true : false, unique: attribute.unique ? true : false,
autoPopulate: attribute.autoPopulate === false ? false : undefined,
}; };
} }
}; };