Allow component creation

This commit is contained in:
soupette 2019-11-25 14:49:50 +01:00
parent 5d0384f045
commit bc28d3daf1
7 changed files with 141 additions and 48 deletions

View File

@ -1,26 +1,32 @@
import React from 'react';
import Creatable from 'react-select/creatable';
import PropTypes from 'prop-types';
import {
SelectWrapper,
SelectNav,
useGlobalContext,
} from 'strapi-helper-plugin';
import { SelectWrapper, SelectNav } from 'strapi-helper-plugin';
import { ErrorMessage } from '@buffetjs/styles';
import useDataManager from '../../hooks/useDataManager';
const CreatableSelect = ({ label, name }) => {
const CreatableSelect = ({ error, label, onChange, name }) => {
const { allComponentsCategories } = useDataManager();
console.log({ label });
const { formatMessage } = useGlobalContext();
const handleInputChange = (newValue, actionMeta) => {
console.log({ actionMeta, newValue });
console.log(actionMeta);
const handleChange = (inputValue, actionMeta) => {
const { action } = actionMeta;
if (action === 'clear') {
onChange({ target: { name, value: '' } });
}
if (action === 'create-option' || action === 'select-option') {
onChange({ target: { name, value: inputValue.value } });
}
};
const styles = {
control: (base, state) => ({
...base,
border: state.isFocused
? '1px solid #78caff !important'
: error
? '1px solid red !important'
: '1px solid #E3E9F3 !important',
}),
menu: base => {
@ -36,7 +42,7 @@ const CreatableSelect = ({ label, name }) => {
};
return (
<SelectWrapper className="form-group">
<SelectWrapper className="form-group" style={{ marginBottom: 0 }}>
<SelectNav>
<div>
<label htmlFor={name}>{label}</label>
@ -44,18 +50,24 @@ const CreatableSelect = ({ label, name }) => {
</SelectNav>
<Creatable
isClearable
onInputChange={handleInputChange}
onChange={handleChange}
styles={styles}
options={formatOptions()}
// menuIsOpen
/>
{error && <ErrorMessage style={{ paddingTop: 10 }}>{error}</ErrorMessage>}
</SelectWrapper>
);
};
CreatableSelect.defaultProps = {
error: null,
};
CreatableSelect.propTypes = {
error: PropTypes.string,
label: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
};
export default CreatableSelect;

View File

@ -93,10 +93,16 @@ const DataManagerProvider = ({ children }) => {
});
};
const createSchema = (data, schemaType, uid) => {
const createSchema = (data, schemaType, uid, componentCategory) => {
const type =
schemaType === 'contentType'
? 'CREATE_SCHEMA'
: 'CREATE_COMPONENT_SCHEMA';
dispatch({
type: 'CREATE_SCHEMA',
type,
data,
componentCategory,
schemaType,
uid,
});
@ -169,8 +175,6 @@ const DataManagerProvider = ({ children }) => {
return <Redirect to={`/plugins/${pluginId}/content-types/${firstCTUid}`} />;
}
console.log({ modifiedData });
return (
<DataManagerContext.Provider
value={{

View File

@ -88,10 +88,27 @@ const reducer = (state, action) => {
attributes: {},
},
};
const key =
action.schemaType === 'contentType' ? 'contentTypes' : 'components';
return state.updateIn([key, action.uid], () => fromJS(newSchema));
// const key =
// action.schemaType === 'contentType' ? 'contentTypes' : 'components';
return state.updateIn(['contentTypes', action.uid], () =>
fromJS(newSchema)
);
}
case 'CREATE_COMPONENT_SCHEMA': {
const newSchema = {
uid: action.uid,
isTemporary: true,
category: action.componentCategory,
schema: {
...action.data,
attributes: {},
},
};
return state.updateIn(['components', action.uid], () =>
fromJS(newSchema)
);
}
case 'EDIT_ATTRIBUTE': {
const {

View File

@ -28,7 +28,7 @@ import HeaderNavLink from '../../components/HeaderNavLink';
import getTrad from '../../utils/getTrad';
import getAttributes from './utils/attributes';
import forms from './utils/forms';
import { createUid } from './utils/createUid';
import { createComponentUid, createUid } from './utils/createUid';
import init from './init';
import reducer, { initialState } from './reducer';
import RelationForm from '../../components/RelationForm';
@ -57,6 +57,7 @@ const FormModal = () => {
const {
addAttribute,
contentTypes,
components,
createSchema,
modifiedData: allDataSchema,
sortedContentTypesList,
@ -155,13 +156,14 @@ const FormModal = () => {
const checkFormValidity = async () => {
let schema;
if (state.modalType === 'contentType' || state.modalType === 'component') {
schema = forms[state.modalType].schema(Object.keys(contentTypes));
} else if (
state.modalType === 'attribute'
// && state.forTarget !== 'components' &&
// state.forTarget !== 'component'
) {
if (state.modalType === 'contentType') {
schema = forms.contentType.schema(Object.keys(contentTypes));
} else if (state.modalType === 'component') {
schema = forms.component.schema(
Object.keys(components),
modifiedData.category || ''
);
} else if (state.modalType === 'attribute') {
const type =
state.attributeType === 'relation' ? 'relation' : modifiedData.type;
@ -275,11 +277,12 @@ const FormModal = () => {
await checkFormValidity();
const targetUid =
state.forTarget === 'components' ? state.targetUid : uid;
const nextSearch = `modalType=chooseAttribute&forTarget=${
state.forTarget
}&targetUid=${targetUid}&headerDisplayName=${state.headerDisplayName ||
modifiedData.name}`;
const createNextSearch = searchUid => {
return `modalType=chooseAttribute&forTarget=${
state.forTarget
}&targetUid=${searchUid}&headerDisplayName=${state.headerDisplayName ||
modifiedData.name}`;
};
if (state.modalType === 'contentType') {
// Create the content type schema
@ -289,7 +292,7 @@ const FormModal = () => {
: 'component-categories';
push({
pathname: `/plugins/${pluginId}/${nextSlug}/${uid}`,
search: nextSearch,
search: createNextSearch(targetUid),
});
} else if (state.modalType === 'attribute') {
addAttribute(
@ -299,9 +302,22 @@ const FormModal = () => {
state.actionType === 'edit',
initialData
);
push({ search: nextSearch });
push({ search: createNextSearch(targetUid) });
} else if (state.modalType === 'component') {
// Create the component schema
const componentUid = createComponentUid(
modifiedData.name,
modifiedData.category
);
const { category, ...rest } = modifiedData;
createSchema(rest, 'component', componentUid, category);
push({
search: createNextSearch(componentUid),
pathname: `/plugins/${pluginId}/component-categories/${category}/${componentUid}`,
});
} else {
console.log('Do something with component later');
console.log('Do somethign later');
}
dispatch({
type: 'RESET_PROPS',

View File

@ -9,4 +9,9 @@ const createUid = name => {
return uid;
};
export { createUid, nameToSlug };
// From `content-type-builder/services/Components/createComponentUid`
const createComponentUid = (name, category) => {
return `${nameToSlug(category)}.${nameToSlug(name)}`;
};
export { createComponentUid, createUid, nameToSlug };

View File

@ -5,7 +5,7 @@ import { translatedErrors as errorsTrads } from 'strapi-helper-plugin';
import { FormattedMessage } from 'react-intl';
import pluginId from '../../../pluginId';
import getTrad from '../../../utils/getTrad';
import { createUid, nameToSlug } from './createUid';
import { createComponentUid, createUid, nameToSlug } from './createUid';
yup.addMethod(yup.mixed, 'defined', function() {
return this.test(
@ -18,7 +18,8 @@ yup.addMethod(yup.mixed, 'defined', function() {
yup.addMethod(yup.string, 'unique', function(
message,
alreadyTakenAttributes,
validator
validator,
category = ''
) {
return this.test('unique', message, function(string) {
if (!string) {
@ -26,7 +27,7 @@ yup.addMethod(yup.string, 'unique', function(
}
return !alreadyTakenAttributes.includes(
typeof validator === 'function' ? validator(string) : string
typeof validator === 'function' ? validator(string, category) : string
);
});
});
@ -604,8 +605,6 @@ const forms = {
]);
}
console.log({ items });
return {
items,
};
@ -676,14 +675,44 @@ const forms = {
},
},
component: {
schema(alreadyTakenAttributes) {
schema(alreadyTakenAttributes, componentCategory) {
return yup.object().shape({
name: yup.string().required(),
name: yup
.string()
.unique(
errorsTrads.unique,
alreadyTakenAttributes,
createComponentUid,
componentCategory
)
.required(),
category: yup.string().required(),
icon: yup.string().required(),
collectionName: yup.string(),
});
},
form: {
base(data) {
advanced() {
return {
items: [
[
{
autoFocus: true,
label: {
id: `${pluginId}.contentType.collectionName.label`,
},
description: {
id: `${pluginId}.contentType.collectionName.description`,
},
name: 'collectionName',
type: 'text',
validations: {},
},
],
],
};
},
base() {
const defaultItems = [
[
{
@ -706,7 +735,7 @@ const forms = {
type: 'creatableSelect',
label: {
id: getTrad(
'modalForm.components.create-component.categoryLabel'
'modalForm.components.create-component.category.label'
),
},
validations: {
@ -714,6 +743,15 @@ const forms = {
},
},
],
[
{
name: 'icon',
type: 'componentIconPicker',
label: {
id: getTrad('modalForm.components.icon.label'),
},
},
],
];
return {

View File

@ -246,5 +246,6 @@
"modalForm.sub-header.chooseAttribute.contentType": "Select a field for your content type",
"modalForm.sub-header.attribute.create": "Add new {type} field",
"modalForm.sub-header.attribute.edit": "Edit {name}",
"modalForm.components.create-component.categoryLabel": "Select a category or enter a name to create a new one"
"modalForm.components.create-component.category.label": "Select a category or enter a name to create a new one",
"modalForm.components.icon.label": "Icon"
}