mirror of
https://github.com/strapi/strapi.git
synced 2025-09-02 13:23:12 +00:00
Add code explanations and refacto drastically the code so it is more understandable
This commit is contained in:
parent
08e8b59786
commit
6b49fa778f
@ -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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user