Merge pull request #5897 from strapi/fix/single-types-issues

Fix single types navigation
This commit is contained in:
cyril lopez 2020-04-24 09:44:53 +02:00 committed by GitHub
commit 81c94c9ea0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 48 additions and 47 deletions

View File

@ -14,7 +14,8 @@
}, },
"slug": { "slug": {
"type": "uid", "type": "uid",
"targetField": "title" "targetField": "title",
"required": true
}, },
"single": { "single": {
"model": "file", "model": "file",

View File

@ -32,13 +32,8 @@ const FieldComponent = ({
const componentValue = get(modifiedData, name, null); const componentValue = get(modifiedData, name, null);
const componentValueLength = size(componentValue); const componentValueLength = size(componentValue);
const isInitialized = componentValue !== null || isFromDynamicZone; const isInitialized = componentValue !== null || isFromDynamicZone;
const showResetComponent = const showResetComponent = !isRepeatable && isInitialized && !isFromDynamicZone;
!isRepeatable && isInitialized && !isFromDynamicZone; const currentComponentSchema = get(allLayoutData, ['components', componentUid], {});
const currentComponentSchema = get(
allLayoutData,
['components', componentUid],
{}
);
const displayedFields = get(currentComponentSchema, ['layouts', 'edit'], []); const displayedFields = get(currentComponentSchema, ['layouts', 'edit'], []);

View File

@ -7,7 +7,7 @@ import { useDebounce, useClickAwayListener } from '@buffetjs/hooks';
import styled from 'styled-components'; import styled from 'styled-components';
import { request, LoadingIndicator } from 'strapi-helper-plugin'; import { request, LoadingIndicator } from 'strapi-helper-plugin';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { isEmpty } from 'lodash'; import { get } from 'lodash';
import pluginId from '../../pluginId'; import pluginId from '../../pluginId';
import getRequestUrl from '../../utils/getRequestUrl'; import getRequestUrl from '../../utils/getRequestUrl';
@ -42,13 +42,12 @@ const InputUID = ({
error: inputError, error: inputError,
name, name,
onChange, onChange,
required,
validations, validations,
value, value,
editable, editable,
...inputProps ...inputProps
}) => { }) => {
const { modifiedData, initialData } = useDataManager(); const { modifiedData, initialData, layout } = useDataManager();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [availability, setAvailability] = useState(null); const [availability, setAvailability] = useState(null);
const [isSuggestionOpen, setIsSuggestionOpen] = useState(true); const [isSuggestionOpen, setIsSuggestionOpen] = useState(true);
@ -59,9 +58,10 @@ const InputUID = ({
const wrapperRef = useRef(null); const wrapperRef = useRef(null);
const generateUid = useRef(); const generateUid = useRef();
const initialValue = initialData[name]; const initialValue = initialData[name];
const isCreation = isEmpty(initialData); const createdAtName = get(layout, ['schema', 'options', 'timestamps', 0]);
const isCreation = !initialData[createdAtName];
generateUid.current = async () => { generateUid.current = async (shouldSetInitialValue = false) => {
setIsLoading(true); setIsLoading(true);
const requestURL = getRequestUrl('explorer/uid/generate'); const requestURL = getRequestUrl('explorer/uid/generate');
try { try {
@ -73,7 +73,8 @@ const InputUID = ({
data: modifiedData, data: modifiedData,
}, },
}); });
onChange({ target: { name, value: data, type: 'text' } });
onChange({ target: { name, value: data, type: 'text' } }, shouldSetInitialValue);
setIsLoading(false); setIsLoading(false);
} catch (err) { } catch (err) {
console.error({ err }); console.error({ err });
@ -106,8 +107,8 @@ const InputUID = ({
}; };
useEffect(() => { useEffect(() => {
if (!value && required) { if (!value && validations.required) {
generateUid.current(); generateUid.current(true);
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
@ -143,9 +144,15 @@ const InputUID = ({
}, [availability]); }, [availability]);
useEffect(() => { useEffect(() => {
if (!isCustomized && isCreation && debouncedTargetFieldValue !== null) { if (
generateUid.current(); !isCustomized &&
isCreation &&
debouncedTargetFieldValue &&
modifiedData[attribute.targetField]
) {
generateUid.current(true);
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [debouncedTargetFieldValue, isCustomized, isCreation]); }, [debouncedTargetFieldValue, isCustomized, isCreation]);
useClickAwayListener(wrapperRef, () => setIsSuggestionOpen(false)); useClickAwayListener(wrapperRef, () => setIsSuggestionOpen(false));
@ -220,7 +227,7 @@ const InputUID = ({
<RegenerateButton <RegenerateButton
onMouseEnter={handleGenerateMouseEnter} onMouseEnter={handleGenerateMouseEnter}
onMouseLeave={handleGenerateMouseLeave} onMouseLeave={handleGenerateMouseLeave}
onClick={generateUid.current} onClick={() => generateUid.current()}
> >
{isLoading ? ( {isLoading ? (
<LoadingIndicator small /> <LoadingIndicator small />
@ -264,7 +271,6 @@ InputUID.propTypes = {
error: PropTypes.string, error: PropTypes.string,
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
required: PropTypes.bool,
validations: PropTypes.object, validations: PropTypes.object,
value: PropTypes.string, value: PropTypes.string,
}; };
@ -273,7 +279,6 @@ InputUID.defaultProps = {
description: '', description: '',
editable: false, editable: false,
error: null, error: null,
required: false,
validations: {}, validations: {},
value: '', value: '',
}; };

View File

@ -53,13 +53,10 @@ const RepeatableComponent = ({
}); });
}; };
const missingComponentsValue = min - componentValueLength; const missingComponentsValue = min - componentValueLength;
const errorsArray = componentErrorKeys.map(key => const errorsArray = componentErrorKeys.map(key => get(formErrors, [key, 'id'], ''));
get(formErrors, [key, 'id'], '')
);
const hasMinError = const hasMinError =
get(errorsArray, [0], '').includes('min') && get(errorsArray, [0], '').includes('min') && !collapses.some(obj => obj.isOpen === true);
!collapses.some(obj => obj.isOpen === true);
return ( return (
<div> <div>
@ -84,9 +81,7 @@ const RepeatableComponent = ({
<DraggedItem <DraggedItem
fields={fields} fields={fields}
componentFieldName={componentFieldName} componentFieldName={componentFieldName}
doesPreviousFieldContainErrorsAndIsOpen={ doesPreviousFieldContainErrorsAndIsOpen={doesPreviousFieldContainErrorsAndIsOpen}
doesPreviousFieldContainErrorsAndIsOpen
}
hasErrors={hasErrors} hasErrors={hasErrors}
hasMinError={hasMinError} hasMinError={hasMinError}
isFirst={index === 0} isFirst={index === 0}
@ -129,11 +124,7 @@ const RepeatableComponent = ({
if (componentValueLength < max) { if (componentValueLength < max) {
const shouldCheckErrors = hasMinError; const shouldCheckErrors = hasMinError;
addRepeatableComponentToField( addRepeatableComponentToField(name, componentUid, shouldCheckErrors);
name,
componentUid,
shouldCheckErrors
);
dispatch({ dispatch({
type: 'ADD_NEW_FIELD', type: 'ADD_NEW_FIELD',
}); });

View File

@ -21,7 +21,7 @@ import reducer, { initialState } from './reducer';
/* eslint-disable react/no-array-index-key */ /* eslint-disable react/no-array-index-key */
const EditView = ({ components, currentEnvironment, layouts, plugins, slug }) => { const EditView = ({ components, currentEnvironment, deleteLayout, layouts, plugins, slug }) => {
const formatLayoutRef = useRef(); const formatLayoutRef = useRef();
formatLayoutRef.current = createAttributesLayout; formatLayoutRef.current = createAttributesLayout;
// Retrieve push to programmatically navigate between views // Retrieve push to programmatically navigate between views
@ -95,6 +95,9 @@ const EditView = ({ components, currentEnvironment, layouts, plugins, slug }) =>
currentContentTypeSchema.attributes currentContentTypeSchema.attributes
), ),
}); });
return () => deleteLayout(slug);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentContentTypeLayout, currentContentTypeSchema.attributes]); }, [currentContentTypeLayout, currentContentTypeSchema.attributes]);
const { formattedContentTypeLayout, isDraggingComponent } = reducerState.toJS(); const { formattedContentTypeLayout, isDraggingComponent } = reducerState.toJS();
@ -265,12 +268,13 @@ EditView.defaultProps = {
}; };
EditView.propTypes = { EditView.propTypes = {
currentEnvironment: PropTypes.string,
components: PropTypes.array.isRequired, components: PropTypes.array.isRequired,
currentEnvironment: PropTypes.string,
deleteLayout: PropTypes.func.isRequired,
emitEvent: PropTypes.func, emitEvent: PropTypes.func,
layouts: PropTypes.object.isRequired, layouts: PropTypes.object.isRequired,
slug: PropTypes.string.isRequired,
plugins: PropTypes.object, plugins: PropTypes.object,
slug: PropTypes.string.isRequired,
}; };
export { EditView }; export { EditView };

View File

@ -77,6 +77,7 @@ const EditViewDataManagerProvider = ({ allLayoutData, children, redirectToPrevio
return acc; return acc;
}, {}); }, {});
const contentTypeDataStructure = createDefaultForm( const contentTypeDataStructure = createDefaultForm(
currentContentTypeLayout.schema.attributes, currentContentTypeLayout.schema.attributes,
allLayoutData.components allLayoutData.components
@ -181,7 +182,7 @@ const EditViewDataManagerProvider = ({ allLayoutData, children, redirectToPrevio
}); });
}; };
const handleChange = ({ target: { name, value, type } }) => { const handleChange = ({ target: { name, value, type } }, shouldSetInitialValue = false) => {
let inputValue = value; let inputValue = value;
// Empty string is not a valid date, // Empty string is not a valid date,
@ -213,6 +214,7 @@ const EditViewDataManagerProvider = ({ allLayoutData, children, redirectToPrevio
type: 'ON_CHANGE', type: 'ON_CHANGE',
keys: name.split('.'), keys: name.split('.'),
value: inputValue, value: inputValue,
shouldSetInitialValue,
}); });
}; };

View File

@ -135,11 +135,21 @@ const reducer = (state, action) => {
let newState = state; let newState = state;
const [nonRepeatableComponentKey] = action.keys; const [nonRepeatableComponentKey] = action.keys;
// This is used to set the initialData for inputs
// that needs an asynchronous initial value like the UID field
// This is just a temporary patch.
// TODO : Refactor the default form creation (workflow) to accept async default values.
if (action.shouldSetInitialValue) {
newState = state.updateIn(['initialData', ...action.keys], () => {
return action.value;
});
}
if ( if (
action.keys.length === 2 && action.keys.length === 2 &&
state.getIn(['modifiedData', nonRepeatableComponentKey]) === null state.getIn(['modifiedData', nonRepeatableComponentKey]) === null
) { ) {
newState = state.updateIn(['modifiedData', nonRepeatableComponentKey], () => fromJS({})); newState = newState.updateIn(['modifiedData', nonRepeatableComponentKey], () => fromJS({}));
} }
return newState.updateIn(['modifiedData', ...action.keys], () => { return newState.updateIn(['modifiedData', ...action.keys], () => {

View File

@ -3,14 +3,7 @@ import { get } from 'lodash';
const createDefaultForm = (attributes, allComponentsSchema) => { const createDefaultForm = (attributes, allComponentsSchema) => {
return Object.keys(attributes).reduce((acc, current) => { return Object.keys(attributes).reduce((acc, current) => {
const attribute = get(attributes, [current], {}); const attribute = get(attributes, [current], {});
const { const { default: defaultValue, component, type, required, min, repeatable } = attribute;
default: defaultValue,
component,
type,
required,
min,
repeatable,
} = attribute;
if (type === 'json') { if (type === 'json') {
acc[current] = null; acc[current] = null;