mirror of
https://github.com/zhigang1992/firecms.git
synced 2026-06-16 10:33:46 +08:00
Performance updates
This commit is contained in:
@@ -82,6 +82,13 @@ export const testEntitySchema = buildSchema({
|
||||
properties: properties
|
||||
});
|
||||
},
|
||||
|
||||
product: {
|
||||
title: "Product",
|
||||
dataType: "reference",
|
||||
path: "products",
|
||||
previewProperties: ["name", "main_image"]
|
||||
},
|
||||
// gallery: {
|
||||
// title: 'Gallery',
|
||||
// dataType: 'array',
|
||||
@@ -223,12 +230,6 @@ export const testEntitySchema = buildSchema({
|
||||
// unique: true
|
||||
// }
|
||||
// },
|
||||
// product: {
|
||||
// title: "Product",
|
||||
// dataType: "reference",
|
||||
// path: "products",
|
||||
// previewProperties: ["name", "main_image"]
|
||||
// },
|
||||
// disabled_product: {
|
||||
// title: "Disabled product",
|
||||
// dataType: "reference",
|
||||
|
||||
@@ -68,7 +68,17 @@ export const useStyles = makeStyles<Theme>(theme => createStyles({
|
||||
* @see Table
|
||||
* @category Components
|
||||
*/
|
||||
export const CollectionTable = React.memo<CollectionTableProps<any, any>>(CollectionTableInternal) as React.FunctionComponent<CollectionTableProps<any, any>>;
|
||||
export const CollectionTable = React.memo<CollectionTableProps<any, any>>(CollectionTableInternal, areEqual) as React.FunctionComponent<CollectionTableProps<any, any>>;
|
||||
|
||||
function areEqual(prevProps: CollectionTableProps<any, any>, nextProps: CollectionTableProps<any, any>) {
|
||||
return prevProps.path === nextProps.path
|
||||
&& prevProps.collection === nextProps.collection
|
||||
&& prevProps.title === nextProps.title
|
||||
&& prevProps.toolbarActionsBuilder === nextProps.toolbarActionsBuilder
|
||||
&& prevProps.tableRowActionsBuilder === nextProps.tableRowActionsBuilder
|
||||
&& prevProps.inlineEditing === nextProps.inlineEditing
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
export function CollectionTableInternal<M extends { [Key: string]: any },
|
||||
@@ -89,7 +99,6 @@ export function CollectionTableInternal<M extends { [Key: string]: any },
|
||||
hoverRow = true
|
||||
}: CollectionTableProps<M, AdditionalKey>) {
|
||||
|
||||
|
||||
const context = useFireCMSContext();
|
||||
const dataSource = useDataSource();
|
||||
const sideEntityController = useSideEntityController();
|
||||
|
||||
@@ -56,7 +56,7 @@ export function useSelectionController<M = any>(): SelectionController {
|
||||
|
||||
const [selectedEntities, setSelectedEntities] = useState<Entity<M>[]>([]);
|
||||
|
||||
const toggleEntitySelection = (entity: Entity<M>) => {
|
||||
const toggleEntitySelection = useCallback((entity: Entity<M>) => {
|
||||
let newValue;
|
||||
if (selectedEntities.map(e => e.id).includes(entity.id)) {
|
||||
newValue = selectedEntities.filter((item: Entity<M>) => item.id !== entity.id);
|
||||
@@ -64,9 +64,9 @@ export function useSelectionController<M = any>(): SelectionController {
|
||||
newValue = [...selectedEntities, entity];
|
||||
}
|
||||
setSelectedEntities(newValue);
|
||||
};
|
||||
}, [selectedEntities]);
|
||||
|
||||
const isEntitySelected = (entity: Entity<M>) => selectedEntities.map(e => e.id).includes(entity.id);
|
||||
const isEntitySelected = useCallback((entity: Entity<M>) => selectedEntities.map(e => e.id).includes(entity.id), [selectedEntities]);
|
||||
|
||||
return {
|
||||
selectedEntities,
|
||||
@@ -269,10 +269,10 @@ export function EntityCollectionView<M extends { [Key: string]: any }>({
|
||||
</div>
|
||||
), [path, collection]);
|
||||
|
||||
const tableRowActionsBuilder = ({
|
||||
entity,
|
||||
size
|
||||
}: { entity: Entity<any>, size: CollectionSize }) => {
|
||||
const tableRowActionsBuilder = useCallback(({
|
||||
entity,
|
||||
size
|
||||
}: { entity: Entity<any>, size: CollectionSize }) => {
|
||||
|
||||
const isSelected = isEntitySelected(entity);
|
||||
|
||||
@@ -322,9 +322,9 @@ export function EntityCollectionView<M extends { [Key: string]: any }>({
|
||||
/>
|
||||
);
|
||||
|
||||
};
|
||||
}, [selectionController, sideEntityController, collection.permissions, authController, path,]);
|
||||
|
||||
const toolbarActionsBuilder = (_: { size: CollectionSize, data: Entity<any>[] }) => {
|
||||
const toolbarActionsBuilder = useCallback((_: { size: CollectionSize, data: Entity<any>[] }) => {
|
||||
|
||||
const addButton = canCreate(collection.permissions, authController, path, context) && onNewClick && (largeLayout ?
|
||||
<Button
|
||||
@@ -395,7 +395,7 @@ export function EntityCollectionView<M extends { [Key: string]: any }>({
|
||||
{addButton}
|
||||
</>
|
||||
);
|
||||
};
|
||||
}, [selectionController, path, collection, largeLayout]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useRef } from "react";
|
||||
import React, { useCallback, useEffect, useRef } from "react";
|
||||
import BaseTable, { Column, ColumnShape } from "react-base-table";
|
||||
import Measure, { ContentRect } from "react-measure";
|
||||
import { Box, Paper, Typography } from "@mui/material";
|
||||
@@ -61,7 +61,17 @@ export function Table<T>({
|
||||
|
||||
const tableRef = useRef<BaseTable>(null);
|
||||
|
||||
// these refs are a workaround to prevent the scroll jump caused by Firestore
|
||||
// firing listeners with incomplete data
|
||||
const scrollRef = useRef<number>(0);
|
||||
const endReachedTimestampRef = useRef<number>(0);
|
||||
|
||||
const classes = useTableStyles();
|
||||
useEffect(() => {
|
||||
if (tableRef.current && data) {
|
||||
tableRef.current.scrollToTop(scrollRef.current);
|
||||
}
|
||||
}, [data?.length]);
|
||||
|
||||
const onColumnSort = (key: string) => {
|
||||
|
||||
@@ -96,11 +106,31 @@ export function Table<T>({
|
||||
|
||||
const scrollToTop = () => {
|
||||
if (tableRef.current) {
|
||||
scrollRef.current = 0;
|
||||
tableRef.current.scrollToTop(0);
|
||||
}
|
||||
};
|
||||
|
||||
const clickRow = (props: { rowData: T; rowIndex: number; rowKey: string ; event: React.SyntheticEvent }) => {
|
||||
const onScroll = ({ scrollTop, scrollUpdateWasRequested }: {
|
||||
scrollLeft: number;
|
||||
scrollTop: number;
|
||||
horizontalScrollDirection: 'forward' | 'backward';
|
||||
verticalScrollDirection: 'forward' | 'backward';
|
||||
scrollUpdateWasRequested: boolean;
|
||||
}) => {
|
||||
const prudentTime = Date.now() - endReachedTimestampRef.current > 3000;
|
||||
if (!scrollUpdateWasRequested && prudentTime) {
|
||||
scrollRef.current = scrollTop;
|
||||
}
|
||||
};
|
||||
|
||||
const onEndReachedInternal = () => {
|
||||
endReachedTimestampRef.current = Date.now();
|
||||
if (onEndReached)
|
||||
onEndReached();
|
||||
};
|
||||
|
||||
const clickRow = (props: { rowData: T; rowIndex: number; rowKey: string; event: React.SyntheticEvent }) => {
|
||||
if (!onRowClick)
|
||||
return;
|
||||
onRowClick(props);
|
||||
@@ -211,10 +241,10 @@ export function Table<T>({
|
||||
);
|
||||
}
|
||||
|
||||
const onBaseTableColumnResize = ({
|
||||
column,
|
||||
width
|
||||
}: { column: ColumnShape; width: number }) => {
|
||||
const onBaseTableColumnResize = useCallback(({
|
||||
column,
|
||||
width
|
||||
}: { column: ColumnShape; width: number }) => {
|
||||
if (onColumnResize) {
|
||||
onColumnResize({
|
||||
width,
|
||||
@@ -222,7 +252,7 @@ export function Table<T>({
|
||||
column: column as TableColumn<any>
|
||||
});
|
||||
}
|
||||
};
|
||||
}, [onColumnResize]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -232,6 +262,7 @@ export function Table<T>({
|
||||
bounds
|
||||
onResize={setTableSize}>
|
||||
{({ measureRef }) => {
|
||||
|
||||
return (
|
||||
<div ref={measureRef}
|
||||
className={classes.tableContainer}
|
||||
@@ -249,9 +280,10 @@ export function Table<T>({
|
||||
ignoreFunctionInColumnCompare={false}
|
||||
rowHeight={getRowHeight(size)}
|
||||
ref={tableRef}
|
||||
onScroll={onScroll}
|
||||
overscanRowCount={2}
|
||||
onEndReachedThreshold={PIXEL_NEXT_PAGE_OFFSET}
|
||||
onEndReached={onEndReached}
|
||||
onEndReached={onEndReachedInternal}
|
||||
rowEventHandlers={
|
||||
{ onClick: clickRow as any }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
AuthController,
|
||||
ConfigurationPersistence,
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
User
|
||||
} from "../../models";
|
||||
import {
|
||||
getCollectionFromCollections,
|
||||
getCollectionByPath,
|
||||
removeInitialAndTrailingSlashes
|
||||
} from "../util/navigation_utils";
|
||||
import { getValueInPath, mergeDeep } from "../util/objects";
|
||||
@@ -77,19 +77,51 @@ export function useBuildNavigationContext<UserType>({
|
||||
locale,
|
||||
dataSource,
|
||||
storageSource
|
||||
})
|
||||
.then((result: Navigation) => {
|
||||
setNavigation(result);
|
||||
setNavigationLoading(false);
|
||||
}).catch(setNavigationLoadingError);
|
||||
}).then((result: Navigation) => {
|
||||
setNavigation(result);
|
||||
setNavigationLoading(false);
|
||||
}).catch(setNavigationLoadingError);
|
||||
}, [authController.user, authController.canAccessMainView, navigationOrBuilder]);
|
||||
|
||||
|
||||
const getCollectionResolver = <M extends { [Key: string]: any }>(path: string, entityId?: string, collection?:EntityCollection<M>): EntityCollectionResolver<M> => {
|
||||
const getSchemaOverride = useCallback(<M extends any>(path: string): PartialEntitySchema<M> | undefined => {
|
||||
if (!userConfigPersistence)
|
||||
return undefined
|
||||
const collectionOverride = userConfigPersistence.getCollectionConfig<M>(path);
|
||||
return collectionOverride?.schema;
|
||||
}, [userConfigPersistence]);
|
||||
|
||||
const buildSchemaResolver = useCallback(<M extends { [Key: string]: any } = any>({
|
||||
schema,
|
||||
path
|
||||
}: { schema: EntitySchema<M>, path: string }): EntitySchemaResolver<M> => ({
|
||||
entityId,
|
||||
values,
|
||||
}: EntitySchemaResolverProps<M>) => {
|
||||
|
||||
const schemaOverride = getSchemaOverride<M>(path);
|
||||
const storedProperties: PartialProperties<M> | undefined = getValueInPath(schemaOverride, "properties");
|
||||
|
||||
const properties = computeProperties({
|
||||
propertiesOrBuilder: schema.properties,
|
||||
path,
|
||||
entityId,
|
||||
values: values ?? schema.defaultValues
|
||||
});
|
||||
|
||||
return {
|
||||
...schema,
|
||||
properties: mergeDeep(properties, storedProperties),
|
||||
originalSchema: schema
|
||||
};
|
||||
}, [getSchemaOverride]);
|
||||
|
||||
|
||||
const getCollectionResolver = useCallback(<M extends { [Key: string]: any }>(path: string, entityId?: string, collection?: EntityCollection<M>): EntityCollectionResolver<M> => {
|
||||
|
||||
const collections = navigation?.collections;
|
||||
|
||||
const baseCollection = collection ?? (collections && getCollectionFromCollections<M>(removeInitialAndTrailingSlashes(path), collections));
|
||||
const baseCollection = collection ?? (collections && getCollectionByPath<M>(removeInitialAndTrailingSlashes(path), collections));
|
||||
|
||||
const collectionOverride = getCollectionOverride(path);
|
||||
|
||||
@@ -158,19 +190,26 @@ export function useBuildNavigationContext<UserType>({
|
||||
|
||||
return { ...resolvedCollection, ...(result as EntityCollectionResolver<M>) };
|
||||
|
||||
};
|
||||
}, [
|
||||
navigation,
|
||||
basePath,
|
||||
baseCollectionPath,
|
||||
schemaOverrideHandler,
|
||||
schemaConfigRecord.current,
|
||||
buildSchemaResolver
|
||||
]);
|
||||
|
||||
const setOverride = ({
|
||||
path,
|
||||
entityId,
|
||||
schemaConfig,
|
||||
overrideSchemaRegistry
|
||||
}: {
|
||||
path: string,
|
||||
entityId?: string,
|
||||
schemaConfig?: Partial<EntityCollectionResolver>
|
||||
overrideSchemaRegistry?: boolean
|
||||
}
|
||||
const setOverride = useCallback(({
|
||||
path,
|
||||
entityId,
|
||||
schemaConfig,
|
||||
overrideSchemaRegistry
|
||||
}: {
|
||||
path: string,
|
||||
entityId?: string,
|
||||
schemaConfig?: Partial<EntityCollectionResolver>
|
||||
overrideSchemaRegistry?: boolean
|
||||
}
|
||||
) => {
|
||||
|
||||
const key = getSidePanelKey(path, entityId);
|
||||
@@ -185,9 +224,9 @@ export function useBuildNavigationContext<UserType>({
|
||||
};
|
||||
return key;
|
||||
}
|
||||
};
|
||||
}, [schemaConfigRecord.current]);
|
||||
|
||||
const removeAllOverridesExcept = (entityRefs: {
|
||||
const removeAllOverridesExcept = useCallback((entityRefs: {
|
||||
path: string, entityId?: string
|
||||
}[]) => {
|
||||
const keys = entityRefs.map(({
|
||||
@@ -198,98 +237,38 @@ export function useBuildNavigationContext<UserType>({
|
||||
if (!keys.includes(currentKey))
|
||||
delete schemaConfigRecord.current[currentKey];
|
||||
});
|
||||
};
|
||||
}, [schemaConfigRecord.current]);
|
||||
|
||||
function buildSchemaResolver<M>({
|
||||
schema,
|
||||
path
|
||||
}: { schema: EntitySchema<M>, path: string }): EntitySchemaResolver<M> {
|
||||
const isUrlCollectionPath = useCallback(
|
||||
(path: string): boolean => removeInitialAndTrailingSlashes(path + "/").startsWith(removeInitialAndTrailingSlashes(fullCollectionPath) + "/"),
|
||||
[fullCollectionPath]);
|
||||
|
||||
return ({
|
||||
entityId,
|
||||
values,
|
||||
}: EntitySchemaResolverProps) => {
|
||||
|
||||
const schemaOverride = getSchemaOverride<M>(path);
|
||||
const storedProperties: PartialProperties<M> | undefined = getValueInPath(schemaOverride, "properties");
|
||||
|
||||
const properties = computeProperties({
|
||||
propertiesOrBuilder: schema.properties,
|
||||
path,
|
||||
entityId,
|
||||
values: values ?? schema.defaultValues
|
||||
});
|
||||
|
||||
return {
|
||||
...schema,
|
||||
properties: mergeDeep(properties, storedProperties),
|
||||
originalSchema: schema
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
async function getNavigation<UserType>({ navigationOrCollections, user, authController, dateTimeFormat, locale, dataSource, storageSource }:
|
||||
{
|
||||
navigationOrCollections: Navigation | NavigationBuilder<UserType> | EntityCollection[],
|
||||
user: User | null,
|
||||
authController: AuthController<UserType>,
|
||||
dateTimeFormat?: string,
|
||||
locale?: Locale,
|
||||
dataSource: DataSource,
|
||||
storageSource: StorageSource
|
||||
}
|
||||
): Promise<Navigation> {
|
||||
|
||||
if (Array.isArray(navigationOrCollections)) {
|
||||
return {
|
||||
collections: navigationOrCollections
|
||||
};
|
||||
} else if (typeof navigationOrCollections === "function") {
|
||||
return navigationOrCollections({ user, authController, dateTimeFormat,locale, dataSource, storageSource });
|
||||
} else {
|
||||
return navigationOrCollections;
|
||||
}
|
||||
}
|
||||
|
||||
function isUrlCollectionPath(path: string): boolean {
|
||||
return removeInitialAndTrailingSlashes(path + "/").startsWith(removeInitialAndTrailingSlashes(fullCollectionPath) + "/");
|
||||
}
|
||||
|
||||
function urlPathToDataPath(path: string): string {
|
||||
const urlPathToDataPath = useCallback((path: string): string => {
|
||||
if (path.startsWith(fullCollectionPath))
|
||||
return path.replace(fullCollectionPath, "");
|
||||
throw Error("Expected path starting with " + fullCollectionPath);
|
||||
}
|
||||
}, [fullCollectionPath]);
|
||||
|
||||
function buildUrlCollectionPath(path: string): string {
|
||||
return `${baseCollectionPath}/${removeInitialAndTrailingSlashes(path)}`;
|
||||
}
|
||||
const buildUrlCollectionPath = useCallback((path: string): string => `${baseCollectionPath}/${removeInitialAndTrailingSlashes(path)}`,
|
||||
[baseCollectionPath]);
|
||||
|
||||
function buildCMSUrlPath(path: string): string {
|
||||
return cleanBasePath ? `/${cleanBasePath}/${removeInitialAndTrailingSlashes(path)}` : `/${path}`;
|
||||
}
|
||||
const buildCMSUrlPath = useCallback((path: string): string => cleanBasePath ? `/${cleanBasePath}/${removeInitialAndTrailingSlashes(path)}` : `/${path}`,
|
||||
[cleanBasePath]);
|
||||
|
||||
const onCollectionModifiedForUser = <M extends any>(path: string, partialCollection: PartialEntityCollection<M>) => {
|
||||
const onCollectionModifiedForUser = useCallback(<M extends any>(path: string, partialCollection: PartialEntityCollection<M>) => {
|
||||
if (userConfigPersistence) {
|
||||
const currentStoredConfig = userConfigPersistence.getCollectionConfig(path);
|
||||
userConfigPersistence.onCollectionModified(path, mergeDeep(currentStoredConfig, partialCollection));
|
||||
}
|
||||
}
|
||||
}, [userConfigPersistence]);
|
||||
|
||||
const getCollectionOverride = <M extends any>(path: string): PartialEntityCollection<M> | undefined => {
|
||||
const getCollectionOverride = useCallback(<M extends any>(path: string): PartialEntityCollection<M> | undefined => {
|
||||
if (!userConfigPersistence)
|
||||
return undefined
|
||||
const dynamicCollectionConfig = { ...userConfigPersistence.getCollectionConfig<M>(path) };
|
||||
delete dynamicCollectionConfig["schema"];
|
||||
return dynamicCollectionConfig;
|
||||
}
|
||||
|
||||
const getSchemaOverride = <M extends any>(path: string): PartialEntitySchema<M> | undefined => {
|
||||
if (!userConfigPersistence)
|
||||
return undefined
|
||||
const collectionOverride = userConfigPersistence.getCollectionConfig<M>(path);
|
||||
return collectionOverride?.schema;
|
||||
}
|
||||
}, [userConfigPersistence]);
|
||||
|
||||
return {
|
||||
navigation,
|
||||
@@ -310,10 +289,48 @@ export function useBuildNavigationContext<UserType>({
|
||||
};
|
||||
}
|
||||
|
||||
const getNavigation = async <UserType extends any>({
|
||||
navigationOrCollections,
|
||||
user,
|
||||
authController,
|
||||
dateTimeFormat,
|
||||
locale,
|
||||
dataSource,
|
||||
storageSource
|
||||
}:
|
||||
{
|
||||
navigationOrCollections: Navigation | NavigationBuilder<UserType> | EntityCollection[],
|
||||
user: User | null,
|
||||
authController: AuthController<UserType>,
|
||||
dateTimeFormat?: string,
|
||||
locale?: Locale,
|
||||
dataSource: DataSource,
|
||||
storageSource: StorageSource
|
||||
}
|
||||
): Promise<Navigation> => {
|
||||
|
||||
if (Array.isArray(navigationOrCollections)) {
|
||||
return {
|
||||
collections: navigationOrCollections
|
||||
};
|
||||
} else if (typeof navigationOrCollections === "function") {
|
||||
return navigationOrCollections({
|
||||
user,
|
||||
authController,
|
||||
dateTimeFormat,
|
||||
locale,
|
||||
dataSource,
|
||||
storageSource
|
||||
});
|
||||
} else {
|
||||
return navigationOrCollections;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export function getSidePanelKey(path: string, entityId?: string) {
|
||||
if (entityId)
|
||||
return `${removeInitialAndTrailingSlashes(path)}/${removeInitialAndTrailingSlashes(entityId)}`;
|
||||
else
|
||||
return removeInitialAndTrailingSlashes(path);
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ export function getLastSegment(path: string) {
|
||||
* @param path
|
||||
* @param collections
|
||||
*/
|
||||
export function getCollectionFromCollections<M>(path: string, collections?: EntityCollection[]): EntityCollection<M> | undefined {
|
||||
export function getCollectionByPath<M>(path: string, collections?: EntityCollection[]): EntityCollection<M> | undefined {
|
||||
|
||||
if (!collections)
|
||||
return undefined;
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
ArrayOfReferencesPreview,
|
||||
ArrayOfStorageComponentsPreview,
|
||||
ArrayOfStringsPreview,
|
||||
ArrayOneOfPreview,
|
||||
ArrayPreview,
|
||||
ArrayPropertyEnumPreview,
|
||||
BooleanPreview,
|
||||
@@ -26,21 +27,14 @@ import {
|
||||
StorageThumbnail,
|
||||
StringPreview,
|
||||
TimestampPreview,
|
||||
UrlComponentPreview,
|
||||
ArrayOneOfPreview
|
||||
UrlComponentPreview
|
||||
} from "./internal";
|
||||
import { ErrorView } from "../core/components";
|
||||
|
||||
import { PreviewComponentProps } from "./PreviewComponentProps";
|
||||
|
||||
import { Markdown } from "./components/Markdown";
|
||||
|
||||
/**
|
||||
* @category Preview components
|
||||
*/
|
||||
export function PreviewComponent<T extends CMSType>(props: PreviewComponentProps<T>) {
|
||||
return <MemoPreviewComponent {...props} />;
|
||||
}
|
||||
import deepEqual from "deep-equal";
|
||||
|
||||
export function PreviewComponentInternal<T extends CMSType>(props: PreviewComponentProps<T>) {
|
||||
let content: JSX.Element | any;
|
||||
@@ -198,5 +192,17 @@ function buildWrongValueType(name: string | undefined, dataType: string, value:
|
||||
);
|
||||
}
|
||||
|
||||
const MemoPreviewComponent = React.memo<PreviewComponentProps<any>>(PreviewComponentInternal) as React.FunctionComponent<PreviewComponentProps<any>>;
|
||||
|
||||
/**
|
||||
* @category Preview components
|
||||
*/
|
||||
export const PreviewComponent = React.memo<PreviewComponentProps<any>>(PreviewComponentInternal, areEqual) as React.FunctionComponent<PreviewComponentProps<any>>;
|
||||
|
||||
function areEqual(prevProps: PreviewComponentProps<any>, nextProps: PreviewComponentProps<any>) {
|
||||
return prevProps.name === nextProps.name
|
||||
&& prevProps.size === nextProps.size
|
||||
&& prevProps.height === nextProps.height
|
||||
&& prevProps.width === nextProps.width
|
||||
&& deepEqual(prevProps.value, nextProps.value)
|
||||
;
|
||||
}
|
||||
|
||||
@@ -35,10 +35,6 @@ export type ReferencePreviewProps =
|
||||
PreviewComponentProps<EntityReference>
|
||||
& { onHover?: boolean };
|
||||
|
||||
/**
|
||||
* @category Preview components
|
||||
*/
|
||||
export const ReferencePreview = React.memo<ReferencePreviewProps>(ReferencePreviewComponent) as React.FunctionComponent<ReferencePreviewProps>;
|
||||
|
||||
const useReferenceStyles = makeStyles<Theme, { size: PreviewSize, onHover?: boolean }>((theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -90,6 +86,23 @@ const useReferenceStyles = makeStyles<Theme, { size: PreviewSize, onHover?: bool
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
/**
|
||||
* @category Preview components
|
||||
*/
|
||||
export const ReferencePreview = React.memo<ReferencePreviewProps>(ReferencePreviewComponent, areEqual) as React.FunctionComponent<ReferencePreviewProps>;
|
||||
|
||||
function areEqual(prevProps: ReferencePreviewProps, nextProps: ReferencePreviewProps) {
|
||||
return prevProps.name === nextProps.name
|
||||
&& prevProps.size === nextProps.size
|
||||
&& prevProps.height === nextProps.height
|
||||
&& prevProps.width === nextProps.width
|
||||
&& prevProps.onHover === nextProps.onHover
|
||||
&& prevProps.value?.id === nextProps.value?.id
|
||||
&& prevProps.value?.path === nextProps.value?.path
|
||||
;
|
||||
}
|
||||
|
||||
function ReferencePreviewComponent<M extends { [Key: string]: any }>(
|
||||
{
|
||||
value,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getCollectionFromCollections } from "../core/util/navigation_utils";
|
||||
import { getCollectionByPath } from "../core/util/navigation_utils";
|
||||
import { siteConfig } from "./test_site_config";
|
||||
import { EntityCollection } from "../models";
|
||||
import { getNavigationEntriesFromPathInternal } from "../core/util/navigation_from_path";
|
||||
@@ -6,37 +6,37 @@ import { getNavigationEntriesFromPathInternal } from "../core/util/navigation_fr
|
||||
const collectionViews = siteConfig.navigation as EntityCollection[];
|
||||
it("collection view matches ok", () => {
|
||||
|
||||
const collectionViewFromPath = getCollectionFromCollections("products", collectionViews);
|
||||
const collectionViewFromPath = getCollectionByPath("products", collectionViews);
|
||||
expect(
|
||||
collectionViewFromPath && collectionViewFromPath.path
|
||||
).toEqual("products");
|
||||
|
||||
const collectionViewFromPath1 = getCollectionFromCollections("products/pid/locales", collectionViews);
|
||||
const collectionViewFromPath1 = getCollectionByPath("products/pid/locales", collectionViews);
|
||||
expect(
|
||||
collectionViewFromPath1 && collectionViewFromPath1.path
|
||||
).toEqual("locales");
|
||||
|
||||
const collectionViewFromPath2 = getCollectionFromCollections("sites/es/products", collectionViews);
|
||||
const collectionViewFromPath2 = getCollectionByPath("sites/es/products", collectionViews);
|
||||
expect(
|
||||
collectionViewFromPath2 && collectionViewFromPath2.path
|
||||
).toEqual("sites/es/products");
|
||||
|
||||
const collectionViewFromPath3 = getCollectionFromCollections("sites/es/products/pid/locales", collectionViews);
|
||||
const collectionViewFromPath3 = getCollectionByPath("sites/es/products/pid/locales", collectionViews);
|
||||
expect(
|
||||
collectionViewFromPath3 && collectionViewFromPath3.path
|
||||
).toEqual("locales");
|
||||
|
||||
expect(
|
||||
() => getCollectionFromCollections("products/pid", collectionViews)
|
||||
() => getCollectionByPath("products/pid", collectionViews)
|
||||
).toThrow(
|
||||
"Collection paths must have an odd number of segments: products/pid"
|
||||
);
|
||||
|
||||
expect(
|
||||
getCollectionFromCollections("products", [])
|
||||
getCollectionByPath("products", [])
|
||||
).toEqual(undefined);
|
||||
|
||||
const collectionViewFromPath10 = getCollectionFromCollections("products/id/subcollection_inline", collectionViews);
|
||||
const collectionViewFromPath10 = getCollectionByPath("products/id/subcollection_inline", collectionViews);
|
||||
expect(
|
||||
collectionViewFromPath10 && collectionViewFromPath10.path
|
||||
).toEqual("products/id/subcollection_inline");
|
||||
|
||||
Reference in New Issue
Block a user