Skip to content

Commit

Permalink
H-3553, H-3695, H-3805: Entities table and slideover fixes (#5899)
Browse files Browse the repository at this point in the history
  • Loading branch information
CiaranMn authored Dec 14, 2024
1 parent 02d9890 commit c19ea46
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 101 deletions.
38 changes: 0 additions & 38 deletions apps/hash-api/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,6 @@ export const exactlyOne = (...items: unknown[]): boolean =>
.map((val) => val !== null && val !== undefined)
.reduce((acc, val) => (val ? 1 : 0) + acc, 0) === 1;

export const isRecord = (thing: unknown): thing is Record<string, unknown> => {
if (typeof thing !== "object") {
return false;
}
if (thing == null) {
return false;
}
if (thing instanceof Array) {
return false;
}
return true;
};

/** Returns the set intersection of `left` and `right`. */
export const intersection = <T>(left: Set<T>, right: Set<T>): Set<T> => {
const result = new Set<T>();
Expand All @@ -34,31 +21,6 @@ export const intersection = <T>(left: Set<T>, right: Set<T>): Set<T> => {
return result;
};

/**
* @todo this assumption of the slug might be brittle,
*/
export const capitalizeComponentName = (cId: string) => {
let componentId = cId;

// If there's a trailing slash, remove it
const indexLastSlash = componentId.lastIndexOf("/");
if (indexLastSlash === componentId.length - 1) {
componentId = componentId.slice(0, -1);
}

// *
// "https://example.org/value"
const indexAfterLastSlash = componentId.lastIndexOf("/") + 1;
return (
// * and uppercase it
// "https://example.org/value"
componentId.charAt(indexAfterLastSlash).toUpperCase() +
// ****
// "https://example.org/value"
componentId.substring(indexAfterLastSlash + 1)
);
};

/**
* Given a tree structure that has links, flatten into an array with indices pointing to parent.
* Note, this _will_ behave badly with circular structures!
Expand Down
2 changes: 1 addition & 1 deletion apps/hash-frontend/src/components/grid/grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ export const Grid = <T extends GridRow>({
if (onSelectedRowsChange && sortedAndFilteredRows) {
newSelection.rows.toArray();
const updatedSelectedRows = sortedAndFilteredRows.filter(
(_, rowIndex) => selection.rows.hasIndex(rowIndex),
(_, rowIndex) => newSelection.rows.hasIndex(rowIndex),
);

onSelectedRowsChange(updatedSelectedRows);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Skeleton,
} from "@hashintel/design-system";
import {
type Entity,
getClosedMultiEntityTypeFromMap,
getDisplayFieldsForClosedEntityType,
isClosedMultiEntityTypeForEntityTypeIds,
Expand Down Expand Up @@ -46,6 +47,9 @@ import {
} from "../../../../graphql/queries/knowledge/entity.queries";
import { Button, Link } from "../../../../shared/ui";
import { SlideBackForwardCloseBar } from "../../../shared/shared/slide-back-forward-close-bar";
import { ArchivedItemBanner } from "../../../shared/top-context-bar/archived-item-banner";
import type { MinimalEntityValidationReport } from "../../../shared/use-validate-entity";
import { useValidateEntity } from "../../../shared/use-validate-entity";
import type { EntityEditorProps } from "./entity-editor";
import { EntityEditor } from "./entity-editor";
import { createDraftEntitySubgraph } from "./shared/create-draft-entity-subgraph";
Expand Down Expand Up @@ -261,7 +265,7 @@ const EditEntitySlideOver = memo(
* we need to fetch it and set it in the local state (from where it will be updated if the user uses the editor
* form).
*/
const { data: fetchedEntityData } = useQuery<
const { data: fetchedEntityData, refetch } = useQuery<
GetEntitySubgraphQuery,
GetEntitySubgraphQueryVariables
>(getEntitySubgraphQuery, {
Expand Down Expand Up @@ -467,8 +471,26 @@ const EditEntitySlideOver = memo(
updateEntity,
]);

const [validationReport, setValidationReport] =
useState<MinimalEntityValidationReport | null>(null);

const { validateEntity: validateFn } = useValidateEntity();

const validateEntity = useCallback(
async (entityToValidate: Entity) => {
const report = await validateFn({
properties: entityToValidate.propertiesWithMetadata,
entityTypeIds: entityToValidate.metadata.entityTypeIds,
});

setValidationReport(report);
},
[validateFn],
);

const submitDisabled =
!isDirty && !draftLinksToCreate.length && !draftLinksToArchive.length;
!!validationReport ||
(!isDirty && !draftLinksToCreate.length && !draftLinksToArchive.length);

const onEntityClick = useCallback(
(entityId: EntityId) =>
Expand Down Expand Up @@ -525,25 +547,32 @@ const EditEntitySlideOver = memo(
onForward={onForward}
onClose={onClose}
/>
{entity.metadata.archived && (
<Box mb={1}>
<ArchivedItemBanner item={entity} onUnarchived={refetch} />
</Box>
)}
<Stack gap={5} px={6} pb={5} pt={1}>
<Stack alignItems="center" direction="row">
<Box sx={{ minWidth: 40 }}>
<EntityOrTypeIcon
entity={entity}
icon={icon}
isLink={isLink}
fill={({ palette }) => palette.gray[50]}
fontSize={40}
/>
</Box>
<Typography
variant="h2"
color="gray.90"
fontWeight="bold"
ml={2}
>
{entityLabel}
</Typography>
<Stack alignItems="flex-start" direction="row">
<Stack alignItems="center" direction="row">
<Box sx={{ minWidth: 40 }}>
<EntityOrTypeIcon
entity={entity}
icon={icon}
isLink={isLink}
fill={({ palette }) => palette.gray[50]}
fontSize={40}
/>
</Box>
<Typography
variant="h2"
color="gray.90"
fontWeight="bold"
ml={2}
>
{entityLabel}
</Typography>
</Stack>
{entityOwningShortname && !hideOpenInNew && (
<Link
href={generateEntityPath({
Expand Down Expand Up @@ -579,7 +608,7 @@ const EditEntitySlideOver = memo(
entityLabel={entityLabel}
entitySubgraph={localEntitySubgraph}
handleTypesChange={async (change) => {
await handleTypeChanges(change);
const newEntity = await handleTypeChanges(change);

const originalEntity = originalEntitySubgraph
? getRoots(originalEntitySubgraph)[0]
Expand All @@ -591,8 +620,10 @@ const EditEntitySlideOver = memo(
originalEntity?.metadata.entityTypeIds.toSorted(),
),
);

await validateEntity(newEntity);
}}
setEntity={(changedEntity) => {
setEntity={async (changedEntity) => {
setLocalEntitySubgraph((prev) =>
createDraftEntitySubgraph({
entity: changedEntity,
Expand All @@ -601,6 +632,9 @@ const EditEntitySlideOver = memo(
omitProperties: [],
}),
);

await validateEntity(changedEntity);

setIsDirty(true);
}}
isDirty={isDirty}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,13 @@ export const EntityPageHeader = ({
isLink={!!entity?.linkData}
fontSize={40}
/>
<Typography variant="h1" fontWeight="bold" sx={{ lineHeight: 1 }}>
<Typography
variant="h1"
fontWeight="bold"
sx={{
lineHeight: 1.2,
}}
>
{entityLabel}
</Typography>
</Stack>
Expand Down
35 changes: 20 additions & 15 deletions apps/hash-frontend/src/pages/shared/entities-visualizer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export const EntitiesVisualizer: FunctionComponent<{
hadCachedContent,
loading,
propertyTypes,
refetch: refetchWithoutLinks,
subgraph: subgraphPossiblyWithoutLinks,
} = useEntityTypeEntitiesContext();

Expand Down Expand Up @@ -166,20 +167,21 @@ export const EntitiesVisualizer: FunctionComponent<{
}
}, [defaultView, isDisplayingFilesOnly]);

const { subgraph: subgraphWithLinkedEntities } = useEntityTypeEntities({
entityTypeBaseUrl,
entityTypeId,
graphResolveDepths: {
constrainsLinksOn: { outgoing: 255 },
constrainsLinkDestinationsOn: { outgoing: 255 },
constrainsPropertiesOn: { outgoing: 255 },
constrainsValuesOn: { outgoing: 255 },
inheritsFrom: { outgoing: 255 },
isOfType: { outgoing: 1 },
hasLeftEntity: { outgoing: 1, incoming: 1 },
hasRightEntity: { outgoing: 1, incoming: 1 },
},
});
const { subgraph: subgraphWithLinkedEntities, refetch: refetchWithLinks } =
useEntityTypeEntities({
entityTypeBaseUrl,
entityTypeId,
graphResolveDepths: {
constrainsLinksOn: { outgoing: 255 },
constrainsLinkDestinationsOn: { outgoing: 255 },
constrainsPropertiesOn: { outgoing: 255 },
constrainsValuesOn: { outgoing: 255 },
inheritsFrom: { outgoing: 255 },
isOfType: { outgoing: 1 },
hasLeftEntity: { outgoing: 1, incoming: 1 },
hasRightEntity: { outgoing: 1, incoming: 1 },
},
});

/**
The subgraphWithLinkedEntities can take a long time to load with many entities.
Expand Down Expand Up @@ -395,7 +397,10 @@ export const EntitiesVisualizer: FunctionComponent<{
toggleSearch={
view === "Table" ? () => setShowTableSearch(true) : undefined
}
onBulkActionCompleted={() => null}
onBulkActionCompleted={() => {
void refetchWithoutLinks();
void refetchWithLinks();
}}
/>
{!subgraph ? (
<Stack
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Backdrop } from "@mui/material";
import type { FunctionComponent, RefObject } from "react";
import { useCallback, useState } from "react";

import { useScrollLock } from "../../../shared/use-scroll-lock";
import { TypeSlideOverSlide } from "./type-slide-over-stack/type-slide-over-slide";

export const TypeSlideOverStack: FunctionComponent<{
Expand All @@ -17,6 +18,8 @@ export const TypeSlideOverStack: FunctionComponent<{
const [items, setItems] = useState<VersionedUrl[]>([rootTypeId]);
const [currentIndex, setCurrentIndex] = useState<number>(0);

useScrollLock(true);

if (rootTypeId !== items[0]) {
setCurrentIndex(0);
setItems([rootTypeId]);
Expand Down
7 changes: 7 additions & 0 deletions apps/hash-frontend/src/shared/is-of-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,12 @@ export const isTypePropertyType = (
| DataTypeWithMetadata,
) => type.schema.kind === "propertyType";

export const isTypeDataType = (
type:
| EntityTypeWithMetadata
| PropertyTypeWithMetadata
| DataTypeWithMetadata,
) => type.schema.kind === "dataType";

export const isEntityPageEntity = (item: Entity) =>
includesPageEntityTypeId(item.metadata.entityTypeIds);
Loading

0 comments on commit c19ea46

Please sign in to comment.