mirror of
https://github.com/strapi/strapi.git
synced 2025-11-11 07:39:16 +00:00
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:
parent
8592bde151
commit
9381bbbca6
@ -1,4 +1,4 @@
|
|||||||
import { forwardRef } from 'react';
|
import { forwardRef, memo } from 'react';
|
||||||
|
|
||||||
import { Toggle, useComposedRefs, Field } from '@strapi/design-system';
|
import { Toggle, useComposedRefs, Field } from '@strapi/design-system';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
@ -40,4 +40,6 @@ const BooleanInput = forwardRef<HTMLInputElement, InputProps>(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export { BooleanInput };
|
const MemoizedBooleanInput = memo(BooleanInput);
|
||||||
|
|
||||||
|
export { MemoizedBooleanInput as BooleanInput };
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { forwardRef } from 'react';
|
import { forwardRef, memo } from 'react';
|
||||||
|
|
||||||
import { Checkbox, useComposedRefs, Field } from '@strapi/design-system';
|
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 };
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { forwardRef } from 'react';
|
import { forwardRef, memo } from 'react';
|
||||||
|
|
||||||
import { DatePicker, useComposedRefs, Field } from '@strapi/design-system';
|
import { DatePicker, useComposedRefs, Field } from '@strapi/design-system';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
@ -37,4 +37,6 @@ const DateInput = forwardRef<HTMLInputElement, InputProps>(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export { DateInput };
|
const MemoizedDateInput = memo(DateInput);
|
||||||
|
|
||||||
|
export { MemoizedDateInput as DateInput };
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { forwardRef } from 'react';
|
import { forwardRef, memo } from 'react';
|
||||||
|
|
||||||
import { DateTimePicker, useComposedRefs, Field } from '@strapi/design-system';
|
import { DateTimePicker, useComposedRefs, Field } from '@strapi/design-system';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
@ -37,4 +37,6 @@ const DateTimeInput = forwardRef<HTMLInputElement, InputProps>(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export { DateTimeInput };
|
const MemoizedDateTimeInput = memo(DateTimeInput);
|
||||||
|
|
||||||
|
export { MemoizedDateTimeInput as DateTimeInput };
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { forwardRef } from 'react';
|
import { forwardRef, memo } from 'react';
|
||||||
|
|
||||||
import { TextInput, useComposedRefs, Field } from '@strapi/design-system';
|
import { TextInput, useComposedRefs, Field } from '@strapi/design-system';
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ import { useField } from '../Form';
|
|||||||
|
|
||||||
import type { StringProps } from './types';
|
import type { StringProps } from './types';
|
||||||
|
|
||||||
export const EmailInput = forwardRef<HTMLInputElement, StringProps>(
|
const EmailInput = forwardRef<HTMLInputElement, StringProps>(
|
||||||
({ name, required, label, hint, labelAction, ...props }, ref) => {
|
({ name, required, label, hint, labelAction, ...props }, ref) => {
|
||||||
const field = useField(name);
|
const field = useField(name);
|
||||||
const fieldRef = useFocusInputField<HTMLInputElement>(name);
|
const fieldRef = useFocusInputField<HTMLInputElement>(name);
|
||||||
@ -32,3 +32,7 @@ export const EmailInput = forwardRef<HTMLInputElement, StringProps>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const MemoizedEmailInput = memo(EmailInput);
|
||||||
|
|
||||||
|
export { MemoizedEmailInput as EmailInput };
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { forwardRef } from 'react';
|
import { forwardRef, memo } from 'react';
|
||||||
|
|
||||||
import { SingleSelect, SingleSelectOption, useComposedRefs, Field } from '@strapi/design-system';
|
import { SingleSelect, SingleSelectOption, useComposedRefs, Field } from '@strapi/design-system';
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ import { useField } from '../Form';
|
|||||||
|
|
||||||
import { EnumerationProps } from './types';
|
import { EnumerationProps } from './types';
|
||||||
|
|
||||||
export const EnumerationInput = forwardRef<HTMLDivElement, EnumerationProps>(
|
const EnumerationInput = forwardRef<HTMLDivElement, EnumerationProps>(
|
||||||
({ name, required, label, hint, labelAction, options = [], ...props }, ref) => {
|
({ name, required, label, hint, labelAction, options = [], ...props }, ref) => {
|
||||||
const field = useField(name);
|
const field = useField(name);
|
||||||
const fieldRef = useFocusInputField<HTMLDivElement>(name);
|
const fieldRef = useFocusInputField<HTMLDivElement>(name);
|
||||||
@ -39,3 +39,7 @@ export const EnumerationInput = forwardRef<HTMLDivElement, EnumerationProps>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const MemoizedEnumerationInput = memo(EnumerationInput);
|
||||||
|
|
||||||
|
export { MemoizedEnumerationInput as EnumerationInput };
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { forwardRef } from 'react';
|
import { forwardRef, memo } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
JSONInput as JSONInputImpl,
|
JSONInput as JSONInputImpl,
|
||||||
@ -12,7 +12,7 @@ import { useField } from '../Form';
|
|||||||
|
|
||||||
import { InputProps } from './types';
|
import { InputProps } from './types';
|
||||||
|
|
||||||
export const JsonInput = forwardRef<JSONInputRef, InputProps>(
|
const JsonInput = forwardRef<JSONInputRef, InputProps>(
|
||||||
({ name, required, label, hint, labelAction, ...props }, ref) => {
|
({ name, required, label, hint, labelAction, ...props }, ref) => {
|
||||||
const field = useField(name);
|
const field = useField(name);
|
||||||
const fieldRef = useFocusInputField(name);
|
const fieldRef = useFocusInputField(name);
|
||||||
@ -40,3 +40,7 @@ export const JsonInput = forwardRef<JSONInputRef, InputProps>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const MemoizedJsonInput = memo(JsonInput);
|
||||||
|
|
||||||
|
export { MemoizedJsonInput as JsonInput };
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { forwardRef } from 'react';
|
import { forwardRef, memo } from 'react';
|
||||||
|
|
||||||
import { NumberInput, useComposedRefs, Field } from '@strapi/design-system';
|
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 };
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { forwardRef, useState } from 'react';
|
import { forwardRef, memo, useState } from 'react';
|
||||||
|
|
||||||
import { TextInput, useComposedRefs, Field } from '@strapi/design-system';
|
import { TextInput, useComposedRefs, Field } from '@strapi/design-system';
|
||||||
import { Eye, EyeStriked } from '@strapi/icons';
|
import { Eye, EyeStriked } from '@strapi/icons';
|
||||||
@ -9,7 +9,7 @@ import { useField } from '../Form';
|
|||||||
|
|
||||||
import type { StringProps } from './types';
|
import type { StringProps } from './types';
|
||||||
|
|
||||||
export const PasswordInput = forwardRef<HTMLInputElement, StringProps>(
|
const PasswordInput = forwardRef<HTMLInputElement, StringProps>(
|
||||||
({ name, required, label, hint, labelAction, ...props }, ref) => {
|
({ name, required, label, hint, labelAction, ...props }, ref) => {
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
@ -55,3 +55,7 @@ export const PasswordInput = forwardRef<HTMLInputElement, StringProps>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const MemoizedPasswordInput = memo(PasswordInput);
|
||||||
|
|
||||||
|
export { MemoizedPasswordInput as PasswordInput };
|
||||||
|
|||||||
@ -93,4 +93,6 @@ const NotSupportedField = forwardRef<any, InputProps>(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export { InputRenderer };
|
const MemoizedInputRenderer = memo(InputRenderer);
|
||||||
|
|
||||||
|
export { MemoizedInputRenderer as InputRenderer };
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { forwardRef } from 'react';
|
import { forwardRef, memo } from 'react';
|
||||||
|
|
||||||
import { TextInput, useComposedRefs, Field } from '@strapi/design-system';
|
import { TextInput, useComposedRefs, Field } from '@strapi/design-system';
|
||||||
|
|
||||||
import { useFocusInputField } from '../../hooks/useFocusInputField';
|
import { useFocusInputField } from '../../hooks/useFocusInputField';
|
||||||
import { type InputProps, useField } from '../Form';
|
import { type InputProps, useField } from '../Form';
|
||||||
|
|
||||||
export const StringInput = forwardRef<HTMLInputElement, InputProps>(
|
const StringInput = forwardRef<HTMLInputElement, InputProps>(
|
||||||
({ name, required, label, hint, labelAction, ...props }, ref) => {
|
({ name, required, label, hint, labelAction, ...props }, ref) => {
|
||||||
const field = useField(name);
|
const field = useField(name);
|
||||||
const fieldRef = useFocusInputField<HTMLInputElement>(name);
|
const fieldRef = useFocusInputField<HTMLInputElement>(name);
|
||||||
@ -28,3 +28,7 @@ export const StringInput = forwardRef<HTMLInputElement, InputProps>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const MemoizedStringInput = memo(StringInput);
|
||||||
|
|
||||||
|
export { MemoizedStringInput as StringInput };
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { forwardRef } from 'react';
|
import { forwardRef, memo } from 'react';
|
||||||
|
|
||||||
import { Textarea, useComposedRefs, Field } from '@strapi/design-system';
|
import { Textarea, useComposedRefs, Field } from '@strapi/design-system';
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ import { useField } from '../Form';
|
|||||||
|
|
||||||
import type { StringProps } from './types';
|
import type { StringProps } from './types';
|
||||||
|
|
||||||
export const TextareaInput = forwardRef<HTMLTextAreaElement, StringProps>(
|
const TextareaInput = forwardRef<HTMLTextAreaElement, StringProps>(
|
||||||
({ name, required, label, hint, labelAction, ...props }, ref) => {
|
({ name, required, label, hint, labelAction, ...props }, ref) => {
|
||||||
const field = useField(name);
|
const field = useField(name);
|
||||||
const fieldRef = useFocusInputField<HTMLTextAreaElement>(name);
|
const fieldRef = useFocusInputField<HTMLTextAreaElement>(name);
|
||||||
@ -30,3 +30,7 @@ export const TextareaInput = forwardRef<HTMLTextAreaElement, StringProps>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const MemoizedTextareaInput = memo(TextareaInput);
|
||||||
|
|
||||||
|
export { MemoizedTextareaInput as TextareaInput };
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { forwardRef } from 'react';
|
import { forwardRef, memo } from 'react';
|
||||||
|
|
||||||
import { TimePicker, useComposedRefs, Field } from '@strapi/design-system';
|
import { TimePicker, useComposedRefs, Field } from '@strapi/design-system';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
@ -36,4 +36,6 @@ const TimeInput = forwardRef<HTMLInputElement, InputProps>(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export { TimeInput };
|
const MemoizedTimeInput = memo(TimeInput);
|
||||||
|
|
||||||
|
export { MemoizedTimeInput as TimeInput };
|
||||||
|
|||||||
@ -104,12 +104,9 @@ const DocumentRBAC = ({ children, permissions }: DocumentRBACProps) => {
|
|||||||
.filter((field) => field.split('.').length > 1);
|
.filter((field) => field.split('.').length > 1);
|
||||||
|
|
||||||
if (fieldType === 'component') {
|
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
|
// check if the field name is within any of those arrays
|
||||||
return componentOrDynamicZoneFields.some((field) => {
|
return componentFieldNames.some((field) => {
|
||||||
return field.includes(fieldName);
|
return field.includes(name.join('.'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,32 +26,25 @@ const useContentTypeSchema = (model?: string) => {
|
|||||||
const { toggleNotification } = useNotification();
|
const { toggleNotification } = useNotification();
|
||||||
const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
|
const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
|
||||||
|
|
||||||
const { components, contentType, contentTypes, error, isLoading, isFetching } =
|
const { data, error, isLoading, isFetching } = useGetInitialDataQuery(undefined);
|
||||||
useGetInitialDataQuery(undefined, {
|
|
||||||
selectFromResult: (res) => {
|
|
||||||
const contentType = res.data?.contentTypes.find((ct) => ct.uid === model);
|
|
||||||
|
|
||||||
const componentsByKey = res.data?.components.reduce<ComponentsDictionary>(
|
const { components, contentType, contentTypes } = React.useMemo(() => {
|
||||||
(acc, component) => {
|
const contentType = data?.contentTypes.find((ct) => ct.uid === model);
|
||||||
|
|
||||||
|
const componentsByKey = data?.components.reduce<ComponentsDictionary>((acc, component) => {
|
||||||
acc[component.uid] = component;
|
acc[component.uid] = component;
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
},
|
}, {});
|
||||||
{}
|
|
||||||
);
|
|
||||||
|
|
||||||
const components = extractContentTypeComponents(contentType?.attributes, componentsByKey);
|
const components = extractContentTypeComponents(contentType?.attributes, componentsByKey);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isLoading: res.isLoading,
|
|
||||||
isFetching: res.isFetching,
|
|
||||||
error: res.error,
|
|
||||||
components: Object.keys(components).length === 0 ? undefined : components,
|
components: Object.keys(components).length === 0 ? undefined : components,
|
||||||
contentType,
|
contentType,
|
||||||
contentTypes: res.data?.contentTypes ?? [],
|
contentTypes: data?.contentTypes ?? [],
|
||||||
};
|
};
|
||||||
},
|
}, [model, data]);
|
||||||
});
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|||||||
@ -38,4 +38,6 @@ const BlocksInput = React.forwardRef<{ focus: () => void }, BlocksInputProps>(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export { BlocksInput };
|
const MemoizedBlocksInput = React.memo(BlocksInput);
|
||||||
|
|
||||||
|
export { MemoizedBlocksInput as BlocksInput };
|
||||||
|
|||||||
@ -109,5 +109,7 @@ const ComponentInput = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export { ComponentInput };
|
const MemoizedComponentInput = React.memo(ComponentInput);
|
||||||
|
|
||||||
|
export { MemoizedComponentInput as ComponentInput };
|
||||||
export type { ComponentInputProps };
|
export type { ComponentInputProps };
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { TextInput } from '@strapi/design-system';
|
import { Field, TextInput } from '@strapi/design-system';
|
||||||
import { EyeStriked } from '@strapi/icons';
|
import { EyeStriked } from '@strapi/icons';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { styled } from 'styled-components';
|
|
||||||
|
|
||||||
import type { InputProps } from '@strapi/admin/strapi-admin';
|
import type { InputProps } from '@strapi/admin/strapi-admin';
|
||||||
import type { Schema } from '@strapi/types';
|
import type { Schema } from '@strapi/types';
|
||||||
@ -19,26 +18,18 @@ const NotAllowedInput = ({ hint, label, required, name }: NotAllowedInputProps)
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Field.Root id={name} hint={hint} name={name} required={required}>
|
||||||
|
<Field.Label>{label}</Field.Label>
|
||||||
<TextInput
|
<TextInput
|
||||||
disabled
|
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}
|
placeholder={placeholder}
|
||||||
required={required}
|
startAction={<EyeStriked fill="neutral600" />}
|
||||||
startAction={<StyledIcon />}
|
|
||||||
type="text"
|
type="text"
|
||||||
value=""
|
value=""
|
||||||
/>
|
/>
|
||||||
|
<Field.Hint />
|
||||||
|
</Field.Root>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledIcon = styled(EyeStriked)`
|
|
||||||
& > path {
|
|
||||||
fill: ${({ theme }) => theme.colors.neutral600};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export { NotAllowedInput };
|
export { NotAllowedInput };
|
||||||
|
|||||||
@ -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 };
|
export type { RelationsFieldProps };
|
||||||
|
|||||||
@ -322,5 +322,7 @@ const LoadingWrapper = styled<FlexComponent>(Flex)`
|
|||||||
animation: ${rotation} 2s infinite linear;
|
animation: ${rotation} 2s infinite linear;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export { UIDInput };
|
const MemoizedUIDInput = React.memo(UIDInput);
|
||||||
|
|
||||||
|
export { MemoizedUIDInput as UIDInput };
|
||||||
export type { UIDInputProps };
|
export type { UIDInputProps };
|
||||||
|
|||||||
@ -150,5 +150,7 @@ const Wysiwyg = React.forwardRef<EditorApi, WysiwygProps>(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export { Wysiwyg };
|
const MemoizedWysiwyg = React.memo(Wysiwyg);
|
||||||
|
|
||||||
|
export { MemoizedWysiwyg as Wysiwyg };
|
||||||
export type { WysiwygProps };
|
export type { WysiwygProps };
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode, memo } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useStrapiApp,
|
useStrapiApp,
|
||||||
@ -228,5 +228,7 @@ const getMinMax = (attribute: Schema.Attribute.AnyAttribute) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MemoizedInputRenderer = memo(InputRenderer);
|
||||||
|
|
||||||
export type { InputRendererProps };
|
export type { InputRendererProps };
|
||||||
export { InputRenderer, useFieldHint };
|
export { MemoizedInputRenderer as InputRenderer, useFieldHint };
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user