Relations creation TU

This commit is contained in:
Virginie Ky 2019-07-25 16:04:23 +02:00
parent fa094f1ede
commit 56af6dd558
11 changed files with 640 additions and 263 deletions

View File

@ -2,9 +2,23 @@ import styled from 'styled-components';
const RelationsWrapper = styled.div`
width: 100%;
display: flex;
padding: 2.7rem 1.5rem 3.3rem 1.5rem;
justify-content: space-between;
.relation-base {
display: flex;
padding: 2.7rem 1.5rem 3.3rem 1.5rem;
justify-content: space-between;
}
.relation-advanced {
.row {
margin: 0;
}
hr {
width: 100%;
margin: 1.3rem 1.5rem 2.1rem 1.5rem;
height: 1px;
background-color: rgba(14, 22, 34, 0.04);
border: 0;
}
}
`;
export default RelationsWrapper;

View File

@ -227,18 +227,17 @@ export function getDataSucceeded({ allModels, models }, connections, { data }) {
}, {});
const initialDataGroup = data.reduce((acc, current) => {
const {
schema: { attributes, name },
uid,
} = current;
const { schema, uid } = current;
const { attributes, name } = schema;
const group = {
...current,
...schema,
attributes: buildGroupAttributes(attributes),
uid,
name: name || uid,
isTemporary: false,
};
set(group, ['schema', 'attributes'], buildGroupAttributes(attributes));
acc[current.uid] = group;
acc[uid] = group;
return acc;
}, {});
@ -251,13 +250,13 @@ export function getDataSucceeded({ allModels, models }, connections, { data }) {
} = current;
acc.push({
uid,
description: description || '',
fields: Object.keys(attributes).length,
icon: 'fa-cube',
isTemporary: false,
name: name || uid,
source,
uid,
source: source || null,
});
return acc;
@ -292,12 +291,9 @@ export function onChangeExistingGroupMainInfos({ target }) {
? camelCase(target.value.trim()).toLowerCase()
: target.value;
const array = target.name.split('.');
array.splice(1, 0, 'schema');
return {
type: ON_CHANGE_EXISTING_GROUP_MAIN_INFOS,
keys: array,
keys: target.name.split('.'),
value,
};
}
@ -571,6 +567,7 @@ export function submitContentType(oldContentTypeName, data, context, source) {
export function submitGroup(oldGroupName, data, context, source) {
const attributes = formatGroupAttributes(data.attributes);
delete data['isTemporary'];
const body = Object.assign(cloneDeep(data), { attributes });
return {
@ -612,8 +609,7 @@ export function submitTempContentTypeSucceeded() {
}
export function submitTempGroup(data, context) {
const attributes = formatGroupAttributes(data.schema.attributes);
delete data['schema'];
const attributes = formatGroupAttributes(data.attributes);
const body = Object.assign(cloneDeep(data), { attributes });
return {
@ -700,9 +696,11 @@ export const buildGroupAttributes = attributes =>
export const formatGroupAttributes = attributes => {
const formattedAttributes = attributes.reduce((acc, current) => {
acc[current.name] = current;
delete current['name'];
const name = current['name'];
let newAttribute = { ...current };
delete newAttribute['name'];
acc[name] = newAttribute;
return acc;
}, {});

View File

@ -83,7 +83,6 @@ export class App extends React.Component {
/* istanbul ignore next */
componentDidUpdate(prevProps) {
if (prevProps.shouldRefetchData !== this.props.shouldRefetchData) {
console.log('UPDATE !!');
this.props.getData();
}
}
@ -134,22 +133,9 @@ export class App extends React.Component {
return newGroup;
}
return get(
modifiedDataGroup,
[this.getFeatureNameFromSearch(), 'schema'],
{}
);
return get(modifiedDataGroup, this.getFeatureNameFromSearch(), {}, {});
};
// getFeatureName = () => {
// const { modifiedDataGroup } = this.props;
// return get(
// modifiedDataGroup,
// [this.getFeatureNameFromSearch(), 'schema', 'name'],
// {}
// );
// };
getFeatureNameFromSearch = () =>
getQueryParameters(this.getSearch(), `${this.getFeatureType()}Name`);
@ -221,7 +207,6 @@ export class App extends React.Component {
} = this.props;
if (isLoading) {
console.log('loading');
return <Loader />;
}

View File

@ -123,22 +123,18 @@ export const initialState = fromJS({
initialDataGroup: {},
modifiedDataGroup: {},
newGroup: {
attributes: [],
collectionName: '',
connection: '',
description: '',
name: '',
schema: {
attributes: [],
description: '',
},
},
newGroupClone: {
attributes: [],
collectionName: '',
connection: '',
description: '',
name: '',
schema: {
attributes: [],
description: '',
},
},
});
@ -196,7 +192,7 @@ function appReducer(state = initialState, action) {
: ['modifiedDataGroup', groupName];
return state
.updateIn([...basePath, 'schema', 'attributes'], arr =>
.updateIn([...basePath, 'attributes'], arr =>
arr.push(state.get('temporaryAttributeRelationGroup'))
)
.update('temporaryAttributeRelationGroup', () =>
@ -246,9 +242,8 @@ function appReducer(state = initialState, action) {
.setIn(['type'], type);
return state
.updateIn(
['modifiedDataGroup', action.groupName, 'schema', 'attributes'],
arr => arr.push(newAttribute)
.updateIn(['modifiedDataGroup', action.groupName, 'attributes'], arr =>
arr.push(newAttribute)
)
.update('temporaryAttributeGroup', () => Map({}));
}
@ -294,9 +289,7 @@ function appReducer(state = initialState, action) {
.setIn(['type'], type);
return state
.updateIn(['newGroup', 'schema', 'attributes'], arr =>
arr.push(newAttribute)
)
.updateIn(['newGroup', 'attributes'], arr => arr.push(newAttribute))
.update('temporaryAttributeGroup', () => Map({}));
}
case CANCEL_NEW_CONTENT_TYPE:
@ -581,9 +574,7 @@ function appReducer(state = initialState, action) {
case RESET_EDIT_TEMP_CONTENT_TYPE:
return state.updateIn(['newContentType', 'attributes'], () => Map({}));
case RESET_EDIT_TEMP_GROUP:
return state.updateIn(['newGroup', 'schema', 'attributes'], () =>
List([])
);
return state.updateIn(['newGroup', 'attributes'], () => List([]));
case RESET_EXISTING_CONTENT_TYPE_MAIN_INFOS:
return state.updateIn(['modifiedData', action.contentTypeName], () => {
const initialContentType = state
@ -596,24 +587,16 @@ function appReducer(state = initialState, action) {
return initialContentType;
});
case RESET_EXISTING_GROUP_MAIN_INFOS: {
return state.updateIn(
['modifiedDataGroup', action.groupName, 'schema'],
() => {
const initialGroup = state
.getIn(['initialDataGroup', action.groupName, 'schema'])
.set(
'attributes',
state.getIn([
'modifiedDataGroup',
action.groupName,
'schema',
'attributes',
])
);
return state.updateIn(['modifiedDataGroup', action.groupName], () => {
const initialGroup = state
.getIn(['initialDataGroup', action.groupName])
.set(
'attributes',
state.getIn(['modifiedDataGroup', action.groupName, 'attributes'])
);
return initialGroup;
}
);
return initialGroup;
});
}
case RESET_NEW_CONTENT_TYPE_MAIN_INFOS:
return state.updateIn(['newContentType'], () => {
@ -660,8 +643,8 @@ function appReducer(state = initialState, action) {
case SAVE_EDITED_ATTRIBUTE_GROUP: {
const basePath = action.isGroupTemporary
? ['newGroup', 'schema']
: ['modifiedDataGroup', action.groupName, 'schema'];
? ['newGroup']
: ['modifiedDataGroup', action.groupName];
const temporaryAttribute = state.get('temporaryAttributeGroup');
state.update('temporaryAttributeGroup', () => {});
@ -791,8 +774,8 @@ function appReducer(state = initialState, action) {
case SET_TEMPORARY_ATTRIBUTE_GROUP:
return state.update('temporaryAttributeGroup', () => {
const basePath = action.isGroupTemporary
? ['newGroup', 'schema']
: ['modifiedDataGroup', action.groupName, 'schema'];
? ['newGroup']
: ['modifiedDataGroup', action.groupName];
const attribute = state
.getIn([...basePath, 'attributes', action.attributeIndex])
@ -844,22 +827,12 @@ function appReducer(state = initialState, action) {
return state
.update('temporaryAttributeRelationGroup', () =>
state
.getIn([
...basePath,
'schema',
'attributes',
action.attributeName,
])
.getIn([...basePath, 'attributes', action.attributeName])
.set('name', action.attributeName)
)
.update('initialTemporaryAttributeRelationGroup', () =>
state
.getIn([
...basePath,
'schema',
'attributes',
action.attributeName,
])
.getIn([...basePath, 'attributes', action.attributeName])
.set('name', action.attributeName)
);
}
@ -885,24 +858,6 @@ function appReducer(state = initialState, action) {
return state
.update('isLoading', () => true)
.update('shouldRefetchData', v => !v);
// {
// let modifiedGroup = state
// .get('modifiedDataGroup')
// .find(
// (group, key) => !group.equals(state.getIn(['initialDataGroup', key]))
// );
// const uid = modifiedGroup.get('uid');
// const groupToUpdate = state.get('groups').findIndex(group => {
// return group.get('uid') === uid;
// });
// return state
// .updateIn(['initialDataGroup', uid], () => modifiedGroup)
// .updateIn(['groups', groupToUpdate, 'name'], () =>
// modifiedGroup.getIn(['schema', 'name'])
// );
// }
case SUBMIT_TEMP_CONTENT_TYPE_SUCCEEDED:
return state
.update('isLoading', () => true)

View File

@ -501,9 +501,8 @@ describe('App actions', () => {
data: [
{
uid: 'ingredients',
name: 'ingredients',
source: null,
schema: {
name: 'ingredients',
connection: 'default',
collectionName: 'ingredients',
description: 'Little description',
@ -556,30 +555,27 @@ describe('App actions', () => {
isTemporary: false,
uid: 'ingredients',
name: 'ingredients',
source: null,
schema: {
connection: 'default',
collectionName: 'ingredients',
description: 'Little description',
attributes: [
{
name: 'name',
type: 'string',
required: true,
},
{
name: 'quantity',
type: 'float',
required: true,
},
{
name: 'picture',
model: 'file',
via: 'related',
plugin: 'upload',
},
],
},
connection: 'default',
collectionName: 'ingredients',
description: 'Little description',
attributes: [
{
name: 'name',
type: 'string',
required: true,
},
{
name: 'quantity',
type: 'float',
required: true,
},
{
name: 'picture',
model: 'file',
via: 'related',
plugin: 'upload',
},
],
},
};
const connections = ['default'];
@ -596,8 +592,8 @@ describe('App actions', () => {
icon: 'fa-cube',
isTemporary: false,
name: 'ingredients',
source: null,
uid: 'ingredients',
source: null,
},
],
};

View File

@ -147,25 +147,31 @@ describe('appReducer', () => {
target: '',
unique: false,
},
temporaryAttributeRelationGroup: {
name: '',
columnName: '',
dominant: false,
targetColumnName: '',
key: '-',
nature: 'oneWay',
plugin: '',
target: '',
unique: false,
},
shouldRefetchData: false,
newGroup: {
collectionName: '',
connection: '',
name: '',
schema: {
attributes: [],
description: '',
},
attributes: [],
description: '',
},
newGroupClone: {
collectionName: '',
connection: '',
name: '',
schema: {
attributes: [],
description: '',
},
attributes: [],
description: '',
},
});
});
@ -749,24 +755,21 @@ describe('appReducer', () => {
tests: {
uid: 'tests',
name: 'tests',
source: null,
schema: {
connection: 'default',
collectionName: 'tests',
description: '',
attributes: [
{
name: 'name',
type: 'string',
required: true,
},
{
name: 'quantity',
type: 'float',
required: true,
},
],
},
connection: 'default',
collectionName: 'tests',
description: '',
attributes: [
{
name: 'name',
type: 'string',
required: true,
},
{
name: 'quantity',
type: 'float',
required: true,
},
],
isTemporary: false,
},
};
@ -776,7 +779,6 @@ describe('appReducer', () => {
{
uid: 'tests',
name: 'tests',
source: null,
schema: {
connection: 'default',
collectionName: 'tests',
@ -1046,24 +1048,31 @@ describe('appReducer', () => {
target: '',
unique: false,
},
temporaryAttributeRelationGroup: {
name: '',
columnName: '',
dominant: false,
targetColumnName: '',
key: '-',
nature: 'oneWay',
plugin: '',
target: '',
unique: false,
},
shouldRefetchData: false,
newGroup: {
collectionName: '',
connection: '',
name: '',
schema: {
attributes: [],
description: '',
},
attributes: [],
description: '',
},
newGroupClone: {
collectionName: '',
connection: '',
name: '',
schema: {
attributes: [],
description: '',
},
attributes: [],
description: '',
},
initialDataGroup: {},
modifiedDataGroup: {},

View File

@ -104,9 +104,7 @@ export class GroupPage extends React.Component {
return get(modifiedDataGroup, this.getFeatureName(), {});
};
getFeatureSchema = () => get(this.getFeature(), 'schema', {});
getFeatureAttributes = () => get(this.getFeatureSchema(), 'attributes', []);
getFeatureAttributes = () => get(this.getFeature(), 'attributes', []);
getFeatureAttributesNames = () => {
return this.getFeatureAttributes().map(attribute => {
@ -139,25 +137,13 @@ export class GroupPage extends React.Component {
return displayName;
};
getFeatureHeaderTitle = () => {
const { modifiedDataGroup, newGroup } = this.props;
const name = this.getFeatureName();
/* istanbul ignore if */
const displayName = this.isUpdatingTempFeature()
? get(newGroup, ['schema', 'name'], null)
: get(modifiedDataGroup, [name, 'schema', 'name'], null);
return displayName;
};
getFeatureHeaderDescription = () => {
const { modifiedDataGroup, newGroup } = this.props;
const name = this.getFeatureName();
const description = this.isUpdatingTempFeature()
? get(newGroup, ['schema', 'description'], null)
: get(modifiedDataGroup, [name, 'schema', 'description'], null);
? get(newGroup, 'description', null)
: get(modifiedDataGroup, [name, 'description'], null);
/* istanbul ignore if */
/* eslint-disable indent */
@ -190,7 +176,7 @@ export class GroupPage extends React.Component {
? submitTempGroup(newGroup, this.context)
: submitGroup(
featureName,
get(modifiedDataGroup, [featureName, 'schema']),
get(modifiedDataGroup, featureName),
Object.assign(this.context, {
history: this.props.history,
}),
@ -287,11 +273,10 @@ export class GroupPage extends React.Component {
const { attrToDelete } = this.state;
const keys = this.isUpdatingTempFeature()
? ['newGroup', 'schema', 'attributes', attrToDelete]
? ['newGroup', 'attributes', attrToDelete]
: [
'modifiedDataGroup',
this.getFeatureName(),
'schema',
'attributes',
attrToDelete,
];
@ -444,7 +429,7 @@ export class GroupPage extends React.Component {
<ViewContainer
{...this.props}
featureType={this.featureType}
headerTitle={this.getFeatureHeaderTitle()}
headerTitle={this.getFeatureDisplayName()}
headerDescription={this.getFeatureHeaderDescription()}
pluginHeaderActions={this.getPluginHeaderActions()}
onClickIcon={this.openEditFeatureModal}

View File

@ -61,23 +61,21 @@ const props = {
uid: 'tests',
name: 'Tests',
source: null,
schema: {
connection: 'default',
collectionName: 'tests',
description: 'tests description',
attributes: [
{
name: 'name',
type: 'string',
required: true,
},
{
name: 'quantity',
type: 'float',
required: true,
},
],
},
connection: 'default',
collectionName: 'tests',
description: 'tests description',
attributes: [
{
name: 'name',
type: 'string',
required: true,
},
{
name: 'quantity',
type: 'float',
required: true,
},
],
},
},
location: {
@ -89,23 +87,21 @@ const props = {
uid: 'tests',
name: 'Tests',
source: null,
schema: {
connection: 'default',
collectionName: 'tests',
description: 'tests description',
attributes: [
{
name: 'name',
type: 'string',
required: true,
},
{
name: 'quantity',
type: 'float',
required: true,
},
],
},
connection: 'default',
collectionName: 'tests',
description: 'tests description',
attributes: [
{
name: 'name',
type: 'string',
required: true,
},
{
name: 'quantity',
type: 'float',
required: true,
},
],
},
},
match: {
@ -117,12 +113,13 @@ const props = {
collectionName: '',
connection: '',
name: '',
schema: {
attributes: [],
description: '',
},
attributes: [],
description: '',
},
onChangeAttributeGroup: jest.fn(),
onChangeRelationGroup: jest.fn(),
onChangeRelationNatureGroup: jest.fn(),
onChangeRelationTargetGroup: jest.fn(),
resetEditTempGroup: jest.fn(),
saveEditedAttributeGroup: jest.fn(),
setTemporaryAttributeGroup: jest.fn(),
@ -130,6 +127,17 @@ const props = {
submitTempGroup: jest.fn(),
submitGroup: jest.fn(),
temporaryAttributeGroup: {},
temporaryAttributeRelationGroup: {
name: '',
columnName: '',
dominant: false,
targetColumnName: '',
key: '-',
nature: 'oneWay',
plugin: '',
target: '',
unique: false,
},
};
describe('CTB <GroupPage />', () => {
@ -229,9 +237,9 @@ describe('CTB <GroupPage />', () => {
});
it('should call the openAttributesModal when clicking on the EmptyAttributesBlock', () => {
props.initialDataGroup.tests.schema.attributes = [];
props.modifiedDataGroup.tests.schema.attributes = [];
props.newGroup.schema.attributes = [];
props.initialDataGroup.tests.attributes = [];
props.modifiedDataGroup.tests.attributes = [];
props.newGroup.attributes = [];
const wrapper = shallow(<GroupPage {...props} />);
const spyOnClick = jest.spyOn(wrapper.instance(), 'openAttributesModal');
@ -456,7 +464,7 @@ describe('CTB <GroupPage />, lifecycle', () => {
props.groups.find(item => item.name == 'tests').isTemporary = true;
props.newGroup.name = 'tests';
props.newGroup.schema.attributes = [
props.newGroup.attributes = [
{
name: 'name',
type: 'string',
@ -479,7 +487,7 @@ describe('CTB <GroupPage />, lifecycle', () => {
it('should call submitGroup with modifiedDataGroup param when isTemporary is false', () => {
props.groups.find(item => item.name == 'tests').isTemporary = false;
props.initialDataGroup.tests.schema.attributes = [
props.initialDataGroup.tests.attributes = [
{
name: 'name',
type: 'string',
@ -491,7 +499,7 @@ describe('CTB <GroupPage />, lifecycle', () => {
required: true,
},
];
props.modifiedDataGroup.tests.schema.attributes = [
props.modifiedDataGroup.tests.attributes = [
{
name: 'firstname',
type: 'string',
@ -563,7 +571,7 @@ describe('CTB <GroupPage />, lifecycle', () => {
showWarning: true,
});
handleDeleteAttribute();
const keys = ['modifiedDataGroup', 'tests', 'schema', 'attributes', 0];
const keys = ['modifiedDataGroup', 'tests', 'attributes', 0];
expect(props.deleteGroupAttribute).toHaveBeenCalledWith(keys);
expect(context.emitEvent).toHaveBeenCalledWith('willDeleteFieldOfGroup');
});
@ -582,7 +590,7 @@ describe('CTB <GroupPage />, lifecycle', () => {
handleClickOnTrashIcon(0);
handleDeleteAttribute();
const keys = ['newGroup', 'schema', 'attributes', 0];
const keys = ['newGroup', 'attributes', 0];
expect(props.deleteGroupAttribute).toHaveBeenCalledWith(keys);
expect(context.emitEvent).toHaveBeenCalledWith('willDeleteFieldOfGroup');
});

View File

@ -0,0 +1,36 @@
[
{
"customBootstrapClass": "col-md-12",
"label": {
"id": "content-type-builder.form.attribute.item.uniqueField"
},
"name": "unique",
"type": "checkbox",
"value": false,
"validations": {},
"inputDescription": {
"id": "content-type-builder.form.attribute.item.uniqueField.description"
}
},
{
"addon": "name",
"label": {
"id": "content-type-builder.form.attribute.item.customColumnName"
},
"name": "columnName",
"type": "string",
"value": "",
"validations": {},
"inputDescription": {
"id": "content-type-builder.form.attribute.item.customColumnName.description"
}
},
{
"addon": "key",
"label": "",
"name": "targetColumnName",
"type": "string",
"value": "",
"validations": {}
}
]

View File

@ -9,6 +9,8 @@ import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { get, isEmpty } from 'lodash';
import { InputsIndex as Input } from 'strapi-helper-plugin';
import pluginId from '../../pluginId';
import BodyModal from '../../components/BodyModal';
@ -28,6 +30,8 @@ import WrapperModal from '../../components/WrapperModal';
import Icon from '../../assets/icons/icon_type_ct.png';
import IconGroup from '../../assets/icons/icon_type_groups.png';
import formAdvanced from './advanced.json';
const NAVLINKS = [{ id: 'base', custom: 'relation' }, { id: 'advanced' }];
class RelationFormGroup extends React.Component {
@ -110,6 +114,21 @@ class RelationFormGroup extends React.Component {
onChangeRelationTarget(group, featureToEditName, actionType === 'edit');
};
handleGoTo = to => {
const { emitEvent } = this.context;
const { actionType, attributeToEditName, push } = this.props;
const attributeName =
actionType === 'edit' ? `&attributeName=${attributeToEditName}` : '';
if (to === 'advanced') {
emitEvent('didSelectContentTypeFieldSettings');
}
push({
search: `modalType=attributeForm&attributeType=relation&settingType=${to}&actionType=${actionType}${attributeName}`,
});
};
handleOnClosed = () => {
const { onCancel } = this.props;
@ -146,21 +165,6 @@ class RelationFormGroup extends React.Component {
push({ search: '' });
};
handleGoTo = to => {
const { emitEvent } = this.context;
const { actionType, attributeToEditName, push } = this.props;
const attributeName =
actionType === 'edit' ? `&attributeName=${attributeToEditName}` : '';
if (to === 'advanced') {
emitEvent('didSelectContentTypeFieldSettings');
}
push({
search: `modalType=attributeForm&attributeType=relation&settingType=${to}&actionType=${actionType}${attributeName}`,
});
};
handleSubmit = e => {
e.preventDefault();
@ -191,13 +195,39 @@ class RelationFormGroup extends React.Component {
);
};
renderAdvancedSettings = () => {
const { didCheckErrors } = this.state;
const { modifiedData, onChange } = this.props;
return (
<div className="relation-advanced">
<div className="row">
{formAdvanced.map((input, i) => {
return (
<React.Fragment key={input.name}>
<Input
{...input}
addon={modifiedData[input.addon]}
didCheckErrors={didCheckErrors}
key={input.name}
onChange={onChange}
value={modifiedData[input.name]}
/>
{i === 0 && <hr />}
</React.Fragment>
);
})}
</div>
</div>
);
};
submit = (shouldContinue = false) => {
const { actionType, onSubmit, onSubmitEdit } = this.props;
if (actionType === 'edit') {
onSubmitEdit(shouldContinue);
} else {
console.log('SUUUBMIT');
onSubmit(shouldContinue);
}
};
@ -213,7 +243,7 @@ class RelationFormGroup extends React.Component {
} = this.props;
const { formErrors, didCheckErrors } = this.state;
return (
<RelationsWrapper>
<div className="relation-base">
<RelationBox
autoFocus
didCheckErrors={didCheckErrors}
@ -244,7 +274,7 @@ class RelationFormGroup extends React.Component {
source={source}
value={key}
/>
</RelationsWrapper>
</div>
);
};
@ -287,7 +317,9 @@ class RelationFormGroup extends React.Component {
</section>
</HeaderModal>
<form onSubmit={this.handleSubmitAndContinue}>
<BodyModal>{showForm && content}</BodyModal>
<BodyModal>
<RelationsWrapper>{showForm && content}</RelationsWrapper>
</BodyModal>
<FooterModal>
<section>
<ButtonModalPrimary
@ -321,30 +353,30 @@ RelationFormGroup.contextTypes = {
RelationFormGroup.defaultProps = {
actionType: 'create',
activeTab: 'base',
alreadyTakenAttributes: [],
featureType: 'model',
features: [],
featuereToEditName: '',
featureToEditName: '',
isOpen: false,
isUpdatingTemporary: false,
onChange: () => {},
onChangeRelationTarget: () => {},
onSubmit: () => {},
source: null,
};
RelationFormGroup.propTypes = {
actionType: PropTypes.string,
activeTab: PropTypes.string,
alreadyTakenAttributes: PropTypes.array,
features: PropTypes.array,
featureType: PropTypes.string,
featuereToEditName: PropTypes.string,
featureToEditName: PropTypes.string,
isOpen: PropTypes.bool,
isUpdatingTemporary: PropTypes.bool,
modifiedData: PropTypes.object.isRequired,
onChange: PropTypes.func,
onChangeRelationTarget: PropTypes.func,
onSubmit: PropTypes.func,
onChange: PropTypes.func.isRequired,
onChangeRelationTarget: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
push: PropTypes.func.isRequired,
setTempAttribute: PropTypes.func.isRequired,
source: PropTypes.string,
};

View File

@ -1,7 +1,366 @@
import React from 'react';
import mountWithIntl from 'testUtils/mountWithIntl';
import formatMessagesWithPluginId from 'testUtils/formatMessages';
import pluginId from '../../../pluginId';
import pluginTradsEn from '../../../translations/en.json';
import RelationFormGroup from '../index';
const messages = formatMessagesWithPluginId(pluginId, pluginTradsEn);
const renderComponent = (props = {}, context = {}) =>
mountWithIntl(<RelationFormGroup {...props} />, messages, context);
describe('<RelationFormGroup />', () => {
let props;
let wrapper;
beforeEach(() => {
props = {
activeTab: 'base',
alreadyTakenAttributes: [],
attributeToEditName: '',
setTempAttribute: jest.fn(),
isOpen: true,
features: [],
featureToEditName: '',
modifiedData: {
key: '',
name: '',
source: '',
},
onCancel: jest.fn(),
onChange: jest.fn(),
onChangeRelationNature: jest.fn(),
onChangeRelationTarget: jest.fn(),
onSubmit: jest.fn(),
onSubmitEdit: jest.fn(),
setTempAttribute: jest.fn(),
push: jest.fn(),
source: null,
};
});
afterEach(() => {
wrapper.unmount();
});
it('should not crash', () => {
expect(true).toBe(true);
wrapper = renderComponent(props);
});
it('should render the advanced tab if the active tab is advanced', () => {
props.activeTab = 'advanced';
wrapper = renderComponent(props);
const compo = wrapper.find(RelationFormGroup);
const spyOnRenderAdvancedSettings = jest.spyOn(
compo.instance(),
'renderAdvancedSettings'
);
compo.instance().forceUpdate();
expect(spyOnRenderAdvancedSettings).toHaveBeenCalled();
});
describe('GetFormErrors', () => {
it('should return an object with the errors if the form is empty', () => {
wrapper = renderComponent(props);
const { getFormErrors } = wrapper.find(RelationFormGroup).instance();
expect(getFormErrors()).toEqual({
name: [{ id: 'content-type-builder.error.validation.required' }],
key: [{ id: 'content-type-builder.error.validation.required' }],
});
});
it('should return an object if a name is already taken', () => {
props.alreadyTakenAttributes = ['test'];
props.modifiedData.name = 'test';
props.modifiedData.key = 'strapi';
wrapper = renderComponent(props);
const { getFormErrors } = wrapper.find(RelationFormGroup).instance();
expect(getFormErrors()).toEqual({
name: [{ id: 'content-type-builder.error.attribute.key.taken' }],
});
});
it('should return an error if the key is equal to the name', () => {
props.alreadyTakenAttributes = [];
props.modifiedData.name = 'test';
props.modifiedData.key = 'test';
wrapper = renderComponent(props);
const { getFormErrors } = wrapper.find(RelationFormGroup).instance();
expect(getFormErrors()).toEqual({
key: [{ id: 'content-type-builder.error.attribute.key.taken' }],
});
});
it('should not return an error when editing', () => {
props.alreadyTakenAttributes = ['test', 'strapi'];
props.modifiedData.name = 'test';
props.modifiedData.key = 'strapi';
props.actionType = 'edit';
props.attributeToEditName = 'test';
wrapper = renderComponent(props);
const { getFormErrors } = wrapper.find(RelationFormGroup).instance();
expect(getFormErrors()).toEqual({});
});
});
describe('<RelationFormGroup />, basic instances', () => {
describe('HandleChangeRelationTarget', () => {
it('should call the onChangeRelationTarget with the correct data (not editing)', () => {
props.featureToEditName = 'test';
wrapper = renderComponent(props);
const { handleChangeRelationTarget } = wrapper
.find(RelationFormGroup)
.instance();
handleChangeRelationTarget('strapi');
expect(props.onChangeRelationTarget).toHaveBeenLastCalledWith(
'strapi',
'test',
false
);
});
it('should call the onChangeRelationTarget with the correct data (editing)', () => {
props.featureToEditName = 'test';
props.actionType = 'edit';
wrapper = renderComponent(props);
const { handleChangeRelationTarget } = wrapper
.find(RelationFormGroup)
.instance();
handleChangeRelationTarget('strapi');
expect(props.onChangeRelationTarget).toHaveBeenLastCalledWith(
'strapi',
'test',
true
);
});
});
describe('HandleCancel', () => {
it('should clear the search', () => {
wrapper = renderComponent(props);
const { handleCancel } = wrapper.find(RelationFormGroup).instance();
handleCancel();
expect(props.push).toHaveBeenCalledWith({ search: '' });
});
});
describe('HandleGoTo', () => {
it('should emit the event didSelectContentTypeFieldSettings if the user clicks on the advanced tab', () => {
const context = { emitEvent: jest.fn() };
wrapper = renderComponent(props, context);
const { handleGoTo } = wrapper.find(RelationFormGroup).instance();
handleGoTo('advanced');
expect(props.push).toHaveBeenCalledWith({
search:
'modalType=attributeForm&attributeType=relation&settingType=advanced&actionType=create',
});
expect(context.emitEvent).toHaveBeenCalledWith(
'didSelectContentTypeFieldSettings'
);
});
it('should add the keep the attribute name if the action is edit', () => {
const context = { emitEvent: jest.fn() };
props.actionType = 'edit';
props.attributeToEditName = 'test';
wrapper = renderComponent(props, context);
const { handleGoTo } = wrapper.find(RelationFormGroup).instance();
handleGoTo('advanced');
expect(props.push).toHaveBeenCalledWith({
search:
'modalType=attributeForm&attributeType=relation&settingType=advanced&actionType=edit&attributeName=test',
});
expect(context.emitEvent).toHaveBeenCalledWith(
'didSelectContentTypeFieldSettings'
);
});
it('should not emit the event if the tab is base', () => {
const context = { emitEvent: jest.fn() };
wrapper = renderComponent(props, context);
const { handleGoTo } = wrapper.find(RelationFormGroup).instance();
handleGoTo('base');
expect(props.push).toHaveBeenCalledWith({
search:
'modalType=attributeForm&attributeType=relation&settingType=base&actionType=create',
});
expect(context.emitEvent).not.toHaveBeenCalled();
});
});
describe('HandleOnClosed', () => {
it('should update the state and call the onCancel prop', () => {
wrapper = renderComponent(props);
const compo = wrapper.find(RelationFormGroup);
compo.setState({ showForm: true, formErrors: { name: [] } });
expect(compo.state('showForm')).toBeTruthy();
const { handleOnClosed } = compo.instance();
handleOnClosed();
expect(compo.state('formErrors')).toEqual({});
expect(compo.state('showForm')).toBeFalsy();
expect(props.onCancel).toHaveBeenCalled();
});
});
describe('HandleOnOpened', () => {
it('should update the state and call the onCancel prop', () => {
props.features = [{ name: 'test', source: 'test' }];
wrapper = renderComponent(props);
const compo = wrapper.find(RelationFormGroup);
expect(compo.state('showForm')).toBeFalsy();
const { handleOnOpened } = compo.instance();
handleOnOpened();
expect(compo.state('showForm')).toBeTruthy();
expect(props.setTempAttribute).toHaveBeenCalledWith(
'test',
false,
'test',
'',
false
);
});
it('should update the state and call the onCancel prop', () => {
props.features = [{ name: 'test' }];
props.featureToEditName = 'strapi';
props.actionType = 'edit';
props.attributeToEditName = 'test';
wrapper = renderComponent(props);
const compo = wrapper.find(RelationFormGroup);
expect(compo.state('showForm')).toBeFalsy();
const { handleOnOpened } = compo.instance();
handleOnOpened();
expect(compo.state('showForm')).toBeTruthy();
expect(props.setTempAttribute).toHaveBeenCalledWith(
'strapi',
false,
undefined,
'test',
true
);
});
});
describe('HandleSubmit', () => {
it('should call the submit prop if there is no error', () => {
props.modifiedData = {
name: 'test',
nature: 'oneWay',
target: 'test',
key: '-',
};
wrapper = renderComponent(props);
const { handleSubmit } = wrapper.instance();
handleSubmit({ preventDefault: jest.fn() });
expect(props.onSubmit).toHaveBeenCalledWith(false);
});
it('should not call the submit if the form is empty', () => {
wrapper = renderComponent(props);
const { handleSubmit } = wrapper.instance();
handleSubmit({ preventDefault: jest.fn() });
expect(props.onSubmit).not.toHaveBeenCalled();
});
});
describe('HandleSubmitAndContinue', () => {
it('should call the submit prop if there is no error', () => {
props.modifiedData = {
name: 'test',
nature: 'oneWay',
target: 'test',
key: '-',
};
wrapper = renderComponent(props);
const { handleSubmitAndContinue } = wrapper.instance();
handleSubmitAndContinue({ preventDefault: jest.fn() });
expect(props.onSubmit).toHaveBeenCalledWith(true);
});
it('should not call the submit if the form is empty', () => {
wrapper = renderComponent(props);
const { handleSubmitAndContinue } = wrapper.instance();
handleSubmitAndContinue({ preventDefault: jest.fn() });
expect(props.onSubmit).not.toHaveBeenCalled();
});
});
describe('HandleToggle', () => {
it('should clear the search', () => {
wrapper = renderComponent(props);
const { handleToggle } = wrapper.instance();
handleToggle();
expect(props.push).toHaveBeenCalledWith({ search: '' });
});
});
describe('Submit', () => {
it('should call the onSubmitEditProp if the actionType is edit', () => {
props.actionType = 'edit';
wrapper = renderComponent(props);
const { submit } = wrapper.instance();
submit();
expect(props.onSubmitEdit).toHaveBeenCalledWith(false);
});
it('should call the onSubmitEdit if the actionType is create', () => {
wrapper = renderComponent(props);
const { submit } = wrapper.instance();
submit(true);
expect(props.onSubmit).toHaveBeenCalledWith(true);
});
});
});
});