fix(cm): nested components never have permission (#20291)

* fix(cm): nested components never have permission

* chore: memoize form inputs for complex forms

* fix(cm): performance regression where selectFromResult was running too often due to arguments
This commit is contained in:
Josh 2024-05-09 15:41:07 +01:00 committed by GitHub
parent 8592bde151
commit 9381bbbca6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 111 additions and 80 deletions

View File

@ -1,4 +1,4 @@
import { forwardRef } from 'react';
import { forwardRef, memo } from 'react';
import { Toggle, useComposedRefs, Field } from '@strapi/design-system';
import { useIntl } from 'react-intl';
@ -40,4 +40,6 @@ const BooleanInput = forwardRef<HTMLInputElement, InputProps>(
}
);
export { BooleanInput };
const MemoizedBooleanInput = memo(BooleanInput);
export { MemoizedBooleanInput as BooleanInput };

View File

@ -1,4 +1,4 @@
import { forwardRef } from 'react';
import { forwardRef, memo } from 'react';
import { Checkbox, useComposedRefs, Field } from '@strapi/design-system';
@ -31,4 +31,6 @@ const CheckboxInput = forwardRef<HTMLInputElement, InputProps>(
}
);
export { CheckboxInput };
const MemoizedCheckboxInput = memo(CheckboxInput);
export { MemoizedCheckboxInput as CheckboxInput };

View File

@ -1,4 +1,4 @@
import { forwardRef } from 'react';
import { forwardRef, memo } from 'react';
import { DatePicker, useComposedRefs, Field } from '@strapi/design-system';
import { useIntl } from 'react-intl';
@ -37,4 +37,6 @@ const DateInput = forwardRef<HTMLInputElement, InputProps>(
}
);
export { DateInput };
const MemoizedDateInput = memo(DateInput);
export { MemoizedDateInput as DateInput };

View File

@ -1,4 +1,4 @@
import { forwardRef } from 'react';
import { forwardRef, memo } from 'react';
import { DateTimePicker, useComposedRefs, Field } from '@strapi/design-system';
import { useIntl } from 'react-intl';
@ -37,4 +37,6 @@ const DateTimeInput = forwardRef<HTMLInputElement, InputProps>(
}
);
export { DateTimeInput };
const MemoizedDateTimeInput = memo(DateTimeInput);
export { MemoizedDateTimeInput as DateTimeInput };

View File

@ -1,4 +1,4 @@
import { forwardRef } from 'react';
import { forwardRef, memo } from 'react';
import { TextInput, useComposedRefs, Field } from '@strapi/design-system';
@ -7,7 +7,7 @@ import { useField } from '../Form';
import type { StringProps } from './types';
export const EmailInput = forwardRef<HTMLInputElement, StringProps>(
const EmailInput = forwardRef<HTMLInputElement, StringProps>(
({ name, required, label, hint, labelAction, ...props }, ref) => {
const field = useField(name);
const fieldRef = useFocusInputField<HTMLInputElement>(name);
@ -32,3 +32,7 @@ export const EmailInput = forwardRef<HTMLInputElement, StringProps>(
);
}
);
const MemoizedEmailInput = memo(EmailInput);
export { MemoizedEmailInput as EmailInput };

View File

@ -1,4 +1,4 @@
import { forwardRef } from 'react';
import { forwardRef, memo } from 'react';
import { SingleSelect, SingleSelectOption, useComposedRefs, Field } from '@strapi/design-system';
@ -7,7 +7,7 @@ import { useField } from '../Form';
import { EnumerationProps } from './types';
export const EnumerationInput = forwardRef<HTMLDivElement, EnumerationProps>(
const EnumerationInput = forwardRef<HTMLDivElement, EnumerationProps>(
({ name, required, label, hint, labelAction, options = [], ...props }, ref) => {
const field = useField(name);
const fieldRef = useFocusInputField<HTMLDivElement>(name);
@ -39,3 +39,7 @@ export const EnumerationInput = forwardRef<HTMLDivElement, EnumerationProps>(
);
}
);
const MemoizedEnumerationInput = memo(EnumerationInput);
export { MemoizedEnumerationInput as EnumerationInput };

View File

@ -1,4 +1,4 @@
import { forwardRef } from 'react';
import { forwardRef, memo } from 'react';
import {
JSONInput as JSONInputImpl,
@ -12,7 +12,7 @@ import { useField } from '../Form';
import { InputProps } from './types';
export const JsonInput = forwardRef<JSONInputRef, InputProps>(
const JsonInput = forwardRef<JSONInputRef, InputProps>(
({ name, required, label, hint, labelAction, ...props }, ref) => {
const field = useField(name);
const fieldRef = useFocusInputField(name);
@ -40,3 +40,7 @@ export const JsonInput = forwardRef<JSONInputRef, InputProps>(
);
}
);
const MemoizedJsonInput = memo(JsonInput);
export { MemoizedJsonInput as JsonInput };

View File

@ -1,4 +1,4 @@
import { forwardRef } from 'react';
import { forwardRef, memo } from 'react';
import { NumberInput, useComposedRefs, Field } from '@strapi/design-system';
@ -34,4 +34,6 @@ const NumberInputImpl = forwardRef<HTMLInputElement, InputProps>(
}
);
export { NumberInputImpl as NumberInput };
const MemoizedNumberInput = memo(NumberInputImpl);
export { MemoizedNumberInput as NumberInput };

View File

@ -1,4 +1,4 @@
import { forwardRef, useState } from 'react';
import { forwardRef, memo, useState } from 'react';
import { TextInput, useComposedRefs, Field } from '@strapi/design-system';
import { Eye, EyeStriked } from '@strapi/icons';
@ -9,7 +9,7 @@ import { useField } from '../Form';
import type { StringProps } from './types';
export const PasswordInput = forwardRef<HTMLInputElement, StringProps>(
const PasswordInput = forwardRef<HTMLInputElement, StringProps>(
({ name, required, label, hint, labelAction, ...props }, ref) => {
const [showPassword, setShowPassword] = useState(false);
const { formatMessage } = useIntl();
@ -55,3 +55,7 @@ export const PasswordInput = forwardRef<HTMLInputElement, StringProps>(
);
}
);
const MemoizedPasswordInput = memo(PasswordInput);
export { MemoizedPasswordInput as PasswordInput };

View File

@ -93,4 +93,6 @@ const NotSupportedField = forwardRef<any, InputProps>(
}
);
export { InputRenderer };
const MemoizedInputRenderer = memo(InputRenderer);
export { MemoizedInputRenderer as InputRenderer };

View File

@ -1,11 +1,11 @@
import { forwardRef } from 'react';
import { forwardRef, memo } from 'react';
import { TextInput, useComposedRefs, Field } from '@strapi/design-system';
import { useFocusInputField } from '../../hooks/useFocusInputField';
import { type InputProps, useField } from '../Form';
export const StringInput = forwardRef<HTMLInputElement, InputProps>(
const StringInput = forwardRef<HTMLInputElement, InputProps>(
({ name, required, label, hint, labelAction, ...props }, ref) => {
const field = useField(name);
const fieldRef = useFocusInputField<HTMLInputElement>(name);
@ -28,3 +28,7 @@ export const StringInput = forwardRef<HTMLInputElement, InputProps>(
);
}
);
const MemoizedStringInput = memo(StringInput);
export { MemoizedStringInput as StringInput };

View File

@ -1,4 +1,4 @@
import { forwardRef } from 'react';
import { forwardRef, memo } from 'react';
import { Textarea, useComposedRefs, Field } from '@strapi/design-system';
@ -7,7 +7,7 @@ import { useField } from '../Form';
import type { StringProps } from './types';
export const TextareaInput = forwardRef<HTMLTextAreaElement, StringProps>(
const TextareaInput = forwardRef<HTMLTextAreaElement, StringProps>(
({ name, required, label, hint, labelAction, ...props }, ref) => {
const field = useField(name);
const fieldRef = useFocusInputField<HTMLTextAreaElement>(name);
@ -30,3 +30,7 @@ export const TextareaInput = forwardRef<HTMLTextAreaElement, StringProps>(
);
}
);
const MemoizedTextareaInput = memo(TextareaInput);
export { MemoizedTextareaInput as TextareaInput };

View File

@ -1,4 +1,4 @@
import { forwardRef } from 'react';
import { forwardRef, memo } from 'react';
import { TimePicker, useComposedRefs, Field } from '@strapi/design-system';
import { useIntl } from 'react-intl';
@ -36,4 +36,6 @@ const TimeInput = forwardRef<HTMLInputElement, InputProps>(
}
);
export { TimeInput };
const MemoizedTimeInput = memo(TimeInput);
export { MemoizedTimeInput as TimeInput };

View File

@ -104,12 +104,9 @@ const DocumentRBAC = ({ children, permissions }: DocumentRBACProps) => {
.filter((field) => field.split('.').length > 1);
if (fieldType === 'component') {
const componentOrDynamicZoneFields = componentFieldNames
// then map to give us the dot separate path as an array
.map((field) => field.split('.'));
// check if the field name is within any of those arrays
return componentOrDynamicZoneFields.some((field) => {
return field.includes(fieldName);
return componentFieldNames.some((field) => {
return field.includes(name.join('.'));
});
}

View File

@ -26,32 +26,25 @@ const useContentTypeSchema = (model?: string) => {
const { toggleNotification } = useNotification();
const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
const { components, contentType, contentTypes, error, isLoading, isFetching } =
useGetInitialDataQuery(undefined, {
selectFromResult: (res) => {
const contentType = res.data?.contentTypes.find((ct) => ct.uid === model);
const { data, error, isLoading, isFetching } = useGetInitialDataQuery(undefined);
const componentsByKey = res.data?.components.reduce<ComponentsDictionary>(
(acc, component) => {
acc[component.uid] = component;
const { components, contentType, contentTypes } = React.useMemo(() => {
const contentType = data?.contentTypes.find((ct) => ct.uid === model);
return acc;
},
{}
);
const componentsByKey = data?.components.reduce<ComponentsDictionary>((acc, component) => {
acc[component.uid] = component;
const components = extractContentTypeComponents(contentType?.attributes, componentsByKey);
return acc;
}, {});
return {
isLoading: res.isLoading,
isFetching: res.isFetching,
error: res.error,
components: Object.keys(components).length === 0 ? undefined : components,
contentType,
contentTypes: res.data?.contentTypes ?? [],
};
},
});
const components = extractContentTypeComponents(contentType?.attributes, componentsByKey);
return {
components: Object.keys(components).length === 0 ? undefined : components,
contentType,
contentTypes: data?.contentTypes ?? [],
};
}, [model, data]);
React.useEffect(() => {
if (error) {

View File

@ -38,4 +38,6 @@ const BlocksInput = React.forwardRef<{ focus: () => void }, BlocksInputProps>(
}
);
export { BlocksInput };
const MemoizedBlocksInput = React.memo(BlocksInput);
export { MemoizedBlocksInput as BlocksInput };

View File

@ -109,5 +109,7 @@ const ComponentInput = ({
);
};
export { ComponentInput };
const MemoizedComponentInput = React.memo(ComponentInput);
export { MemoizedComponentInput as ComponentInput };
export type { ComponentInputProps };

View File

@ -1,7 +1,6 @@
import { TextInput } from '@strapi/design-system';
import { Field, TextInput } from '@strapi/design-system';
import { EyeStriked } from '@strapi/icons';
import { useIntl } from 'react-intl';
import { styled } from 'styled-components';
import type { InputProps } from '@strapi/admin/strapi-admin';
import type { Schema } from '@strapi/types';
@ -19,26 +18,18 @@ const NotAllowedInput = ({ hint, label, required, name }: NotAllowedInputProps)
});
return (
<TextInput
disabled
// @ts-expect-error label _could_ be a ReactNode since it's a child, this should be fixed in the DS.
label={label}
id={name}
hint={hint}
name={name}
placeholder={placeholder}
required={required}
startAction={<StyledIcon />}
type="text"
value=""
/>
<Field.Root id={name} hint={hint} name={name} required={required}>
<Field.Label>{label}</Field.Label>
<TextInput
disabled
placeholder={placeholder}
startAction={<EyeStriked fill="neutral600" />}
type="text"
value=""
/>
<Field.Hint />
</Field.Root>
);
};
const StyledIcon = styled(EyeStriked)`
& > path {
fill: ${({ theme }) => theme.colors.neutral600};
}
`;
export { NotAllowedInput };

View File

@ -1032,5 +1032,7 @@ const RelationItemPlaceholder = () => (
/>
);
export { RelationsField as RelationsInput, FlexWrapper, DisconnectButton, LinkEllipsis };
const MemoizedRelationsField = React.memo(RelationsField);
export { MemoizedRelationsField as RelationsInput, FlexWrapper, DisconnectButton, LinkEllipsis };
export type { RelationsFieldProps };

View File

@ -322,5 +322,7 @@ const LoadingWrapper = styled<FlexComponent>(Flex)`
animation: ${rotation} 2s infinite linear;
`;
export { UIDInput };
const MemoizedUIDInput = React.memo(UIDInput);
export { MemoizedUIDInput as UIDInput };
export type { UIDInputProps };

View File

@ -150,5 +150,7 @@ const Wysiwyg = React.forwardRef<EditorApi, WysiwygProps>(
}
);
export { Wysiwyg };
const MemoizedWysiwyg = React.memo(Wysiwyg);
export { MemoizedWysiwyg as Wysiwyg };
export type { WysiwygProps };

View File

@ -1,4 +1,4 @@
import { ReactNode } from 'react';
import { ReactNode, memo } from 'react';
import {
useStrapiApp,
@ -228,5 +228,7 @@ const getMinMax = (attribute: Schema.Attribute.AnyAttribute) => {
}
};
const MemoizedInputRenderer = memo(InputRenderer);
export type { InputRendererProps };
export { InputRenderer, useFieldHint };
export { MemoizedInputRenderer as InputRenderer, useFieldHint };