Add code explanations and refacto drastically the code so it is more understandable

This commit is contained in:
soupette 2019-11-27 17:29:41 +01:00
parent 08e8b59786
commit 6b49fa778f

View File

@ -139,11 +139,17 @@ const FormModal = () => {
const iconType = ['components', 'contentType'].includes(state.modalType)
? state.modalType
: state.forTarget;
const isCreatingCT = state.modalType === 'contentType';
const isCreatingContentType = state.modalType === 'contentType';
const isCreatingComponent = state.modalType === 'component';
const isCreatingAttribute = state.modalType === 'attribute';
const isComponentAttribute =
state.attributeType === 'component' && isCreatingAttribute;
const isCreating = state.actionType === 'create';
const isCreatingComponentFromAView =
get(modifiedData, 'createComponent', false) ||
isCreatingComponentWhileAddingAField;
const isInFirstComponentStep = state.step === '1';
const isOpen = !isEmpty(search);
const isPickingAttribute = state.modalType === 'chooseAttribute';
@ -164,26 +170,35 @@ const FormModal = () => {
? get(modifiedData, 'componentToCreate', {})
: modifiedData;
if (state.modalType === 'contentType') {
// Check form validity for content type
if (isCreatingContentType) {
schema = forms.contentType.schema(Object.keys(contentTypes));
} else if (state.modalType === 'component') {
// Check form validity for component
// This is happening when the user click on the link from the left menu
} else if (isCreatingComponent) {
schema = forms.component.schema(
Object.keys(components),
modifiedData.category || ''
);
// Check for validity for creating a component
// This is happening when the user creates a component "on the fly"
// Since we temporarily store the component info in another object
// The data is set in the componentToCreate key
} else if (
// We could definitely simplify this condition
// TODO
state.modalType === 'attribute' &&
state.attributeType === 'component' &&
isComponentAttribute &&
isCreatingComponentFromAView &&
state.step === '1'
isInFirstComponentStep
) {
schema = forms.component.schema(
Object.keys(components),
get(modifiedData, 'componentToCreate.category', '')
);
} else if (state.modalType === 'attribute' && state.step !== '1') {
// Check form validity for creating a 'common attribute'
// We need to make sure that it is independent from the step
} else if (isCreatingAttribute && !isInFirstComponentStep) {
const type =
state.attributeType === 'relation' ? 'relation' : modifiedData.type;
@ -195,6 +210,8 @@ const FormModal = () => {
state.attributeName,
initialData
);
// This might be used for dynamic zones if not remove this part
} else {
// TODO validate component schema
console.log('Will do something');
@ -228,6 +245,9 @@ const FormModal = () => {
name === 'repeatable')
) {
val = value === 'false' ? false : true;
// The boolean default accepts 3 different values
// This check has been added to allow a reset to null for the bool
} else if (type === 'radio' && name === 'default') {
if (value === 'false') {
val = false;
@ -236,7 +256,8 @@ const FormModal = () => {
} else {
val = null;
}
// val = value === 'false' ? false : true;
// We store an array for the enum
} else if (name === 'enum') {
val = value.split('\n');
} else {
@ -245,14 +266,17 @@ const FormModal = () => {
const clonedErrors = Object.assign({}, formErrors);
// Reset min error when modifying the max
if (name === 'max') {
delete clonedErrors.min;
}
// Same here
if (name === 'maxLength') {
delete clonedErrors.minLength;
}
// Since the onBlur is deactivated we remove the errors directly when changing an input
delete clonedErrors[name];
dispatch({
@ -274,6 +298,7 @@ const FormModal = () => {
await checkFormValidity();
const targetUid =
state.forTarget === 'components' ? state.targetUid : uid;
// This should be improved
const createNextSearch = searchUid => {
return `modalType=chooseAttribute&forTarget=${
state.forTarget
@ -281,106 +306,15 @@ const FormModal = () => {
modifiedData.name}`;
};
if (state.modalType === 'contentType') {
if (isCreatingContentType) {
// Create the content type schema
createSchema(modifiedData, state.modalType, uid);
const nextSlug = isCreatingCT
? 'content-types'
: 'component-categories';
push({
pathname: `/plugins/${pluginId}/${nextSlug}/${uid}`,
pathname: `/plugins/${pluginId}/content-types/${uid}`,
search: createNextSearch(targetUid),
});
} else if (
state.modalType === 'attribute' &&
!isCreatingComponentFromAView &&
state.attributeType !== 'component'
) {
addAttribute(
modifiedData,
state.forTarget,
state.targetUid,
state.actionType === 'edit',
initialData
);
// Adding a component to a dynamiczone is not the same logic as creating a simple field
// so the search is different
const nextSearch =
state.attributeType === 'dynamiczone'
? ''
: createNextSearch(targetUid);
push({ search: nextSearch });
} else if (
state.modalType === 'attribute' &&
!isCreatingComponentFromAView &&
state.attributeType === 'component'
) {
if (state.step === '1') {
push({
search: `modalType=attribute&actionType=${state.actionType}&settingType=base&forTarget=${state.forTarget}&targetUid=${state.targetUid}&attributeType=component&headerDisplayName=${state.headerDisplayName}&step=2`,
});
dispatch({
type: 'RESET_PROPS_AND_SET_FORM_FOR_ADDING_AN_EXISTING_COMPO',
});
return;
} else {
addAttribute(
modifiedData,
state.forTarget,
state.targetUid,
state.actionType === 'edit',
initialData,
true
);
// Adding a component to a dynamiczone is not the same logic as creating a simple field
// so the search is different
const nextSearch =
state.attributeType === 'dynamiczone'
? ''
: createNextSearch(targetUid);
push({ search: nextSearch });
}
} else if (
state.modalType === 'attribute' &&
isCreatingComponentFromAView
) {
if (state.step === '1') {
push({
search: `modalType=attribute&actionType=${state.actionType}&settingType=base&forTarget=${state.forTarget}&targetUid=${state.targetUid}&attributeType=component&headerDisplayName=${state.headerDisplayName}&step=2`,
});
dispatch({
type: 'RESET_PROPS_AND_SAVE_CURRENT_DATA',
});
return;
} else {
const { category, type, ...rest } = componentToCreate;
const componentUid = createComponentUid(
componentToCreate.name,
category
);
// Create the component first and add it to the components data
createSchema(
rest,
type,
componentUid,
category,
isCreatingComponentFromAView
);
// Add the field to the schema
addAttribute(modifiedData, state.forTarget, state.targetUid, false);
// add the component to main modified data component object
dispatch({ type: 'RESET_PROPS' });
push({ search: '' });
return;
}
} else if (state.modalType === 'component') {
} else if (isCreatingComponent) {
// Create the component schema
const componentUid = createComponentUid(
modifiedData.name,
@ -393,9 +327,125 @@ const FormModal = () => {
search: createNextSearch(componentUid),
pathname: `/plugins/${pluginId}/component-categories/${category}/${componentUid}`,
});
// Add/edit a field
} else if (isCreatingAttribute && !isCreatingComponentFromAView) {
// Normal fields like boolean relations
if (!isComponentAttribute) {
addAttribute(
modifiedData,
state.forTarget,
state.targetUid,
state.actionType === 'edit',
initialData
);
// Adding a component to a dynamiczone is not the same logic as creating a simple field
// so the search is different
const nextSearch =
state.attributeType === 'dynamiczone'
? ''
: createNextSearch(targetUid);
push({ search: nextSearch });
// Adding an existing component
} else {
if (isInFirstComponentStep) {
// Navigate the user to step 2
push({
search: `modalType=attribute&actionType=${state.actionType}&settingType=base&forTarget=${state.forTarget}&targetUid=${state.targetUid}&attributeType=component&headerDisplayName=${state.headerDisplayName}&step=2`,
});
// Clear the reducer and prepare the modified data
// This way we don't have to add some logic to re-run the useEffect
// The first step is either needed to create a component or just to navigate
// To the modal for adding a "common field"
dispatch({
type: 'RESET_PROPS_AND_SET_FORM_FOR_ADDING_AN_EXISTING_COMPO',
});
// We don't want all the props to be reset
return;
// Here we are in step 2
} else {
addAttribute(
modifiedData,
state.forTarget,
state.targetUid,
// This change the dispatched type
// either 'EDIT_ATTRIBUTE' or 'ADD_ATTRIBUTE' in the DataManagerProvider
state.actionType === 'edit',
// This is for the edit part
initialData,
// Passing true will add the component to the components object
// This way we can add fields to the added component (if it wasn't there already)
true
);
push({ search: createNextSearch(targetUid) });
// We don't need to end the loop here we want the reducer to be reinitialised
}
}
// Logic for creating a component without clicking on the link in
// the left menu
// We need to separate the logic otherwise the component would be created
// even though the user didn't set any field
// The use case is happening when closing the modal at step 2 without any submission
} else if (isCreatingAttribute && isCreatingComponentFromAView) {
if (isInFirstComponentStep) {
// Here the search could be refactored since it is the same as the case from above
push({
search: `modalType=attribute&actionType=${state.actionType}&settingType=base&forTarget=${state.forTarget}&targetUid=${state.targetUid}&attributeType=component&headerDisplayName=${state.headerDisplayName}&step=2`,
});
// Here we clear the reducer state but we also keep the created component
// If we were to create the component before
dispatch({
type: 'RESET_PROPS_AND_SAVE_CURRENT_DATA',
});
// Terminate because we don't want the reducer to be entirely reset
return;
// Step 2
} else {
// We are destructuring because the modifiedData object doesn't have the appropriate format to create a field
const { category, type, ...rest } = componentToCreate;
// Create a the component temp UID
// This could be refactored but I think it's more understandable to separate the logic
const componentUid = createComponentUid(
componentToCreate.name,
category
);
// Create the component first and add it to the components data
createSchema(
// Component data
rest,
// Type will always be component
// It will dispatch the CREATE_COMPONENT_SCHEMA action
// So the component will be added in the main components object
// This might not be needed if we don't allow navigation between entries while editing
type,
componentUid,
category,
// This will add the created component in the datamanager modifiedData components key
// Like explained above we will be able to modify the created component structure
isCreatingComponentFromAView
);
// Add the field to the schema
addAttribute(modifiedData, state.forTarget, state.targetUid, false);
// TODO temporary
dispatch({ type: 'RESET_PROPS' });
push({ search: '' });
return;
}
} else {
console.log('Do somethign later');
console.log('handle submit unknown case');
}
dispatch({
type: 'RESET_PROPS',
});
@ -425,13 +475,13 @@ const FormModal = () => {
};
const shouldDisableAdvancedTab = () => {
return (
state.modalType === 'attribute' &&
isCreatingAttribute &&
state.attributeType === 'component' &&
get(modifiedData, ['createComponent'], null) === false
);
};
// Display data
// Display data for the attributes picker modal
const displayedAttributes = getAttributes(state.forTarget);
// Styles
@ -448,6 +498,7 @@ const FormModal = () => {
>
<HeaderModal>
<ModalHeader
// We need to add the category here
name={state.headerDisplayName}
headerId={headerId}
iconType={iconType || 'contentType'}
@ -476,6 +527,8 @@ const FormModal = () => {
{NAVLINKS.map((link, index) => {
return (
<HeaderNavLink
// The advanced tab is disabled when adding an existing component
// step 1
isDisabled={index === 1 && shouldDisableAdvancedTab()}
isActive={state.settingType === link.id}
key={link.id}
@ -546,6 +599,8 @@ const FormModal = () => {
return (
<div className="row" key={index}>
{row.map(input => {
// The divider type is used mainly the advanced tab
// It is the one responsible for displaying the settings label
if (input.type === 'divider') {
return (
<div
@ -566,6 +621,7 @@ const FormModal = () => {
);
}
// The spacer type is used mainly to aligne the icon picker
if (input.type === 'spacer') {
return (
<div key="spacer" style={{ height: 20 }}></div>
@ -585,11 +641,17 @@ const FormModal = () => {
);
}
// Retrieve the error for a specific input
const errorId = get(
formErrors,
[
...input.name
.split('.')
// The filter here is used when creating a component
// in the component step 1 modal
// Since the component info is stored in the
// componentToCreate object we can access the error
// By removing the key
.filter(key => key !== 'componentToCreate'),
'id',
],
@ -604,11 +666,17 @@ const FormModal = () => {
let value;
// Condition for the boolean default value
// The radio input doesn't accept false, true or null as value
// So we pass them as string
// This way the data stays accurate and we don't have to operate
// any data mutation
if (
input.name === 'default' &&
state.attributeType === 'boolean'
) {
value = toString(retrievedValue);
// Same here for the enum
} else if (
input.name === 'enum' &&
Array.isArray(retrievedValue)
@ -618,6 +686,9 @@ const FormModal = () => {
value = retrievedValue;
}
// The addon input is not present in buffet so we are used the all lib
// for the moment that's why we don't want them be passed to buffet
// like the other created inputs
if (input.type === 'addon') {
return (
<InputsIndex
@ -639,20 +710,24 @@ const FormModal = () => {
customInputs={{
componentIconPicker: ComponentIconPicker,
componentSelect: WrapperSelect,
// creatableSelect: CreatableSelect,
creatableSelect: WrapperSelect,
customCheckboxWithChildren: CustomCheckbox,
booleanBox: BooleanBox,
}}
isCreating={isCreating}
// Props for the componentSelect
isCreatingComponentWhileAddingAField={
isCreatingComponentWhileAddingAField
}
// Props for the componentSelect
// Since the component is created after adding it to a type
// its name and category can't be retrieved from the data manager
componentCategoryNeededForAddingAfieldWhileCreatingAComponent={get(
componentToCreate,
'category',
null
)}
// Props for the componentSelect same explanation
componentNameNeededForAddingAfieldWhileCreatingAComponent={get(
componentToCreate,
'name',