Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

H-4069, H-4081, H-4110: Data type conversions in data type editor; entity editor fixes #6598

Merged
merged 8 commits into from
Mar 4, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,12 @@ const migrate: MigrationFunction = async ({
"A measure of the length of time in the International System of Units (SI), defined as exactly 1/1000 of a second.",
type: "number",
},
conversions: {},
conversions: {
[secondDataType.metadata.recordId.baseUrl]: {
from: { expression: ["/", "self", { const: 1000, type: "number" }] },
to: { expression: ["*", "self", { const: 1000, type: "number" }] },
},
},
webShortname: "h",
migrationState,
});
Expand All @@ -78,7 +83,12 @@ const migrate: MigrationFunction = async ({
"A measure of the length of time in the International System of Units (SI), defined as exactly 1/1000000 (1 millionth) of a second.",
type: "number",
},
conversions: {},
conversions: {
[secondDataType.metadata.recordId.baseUrl]: {
from: { expression: ["/", "self", { const: 1000000, type: "number" }] },
to: { expression: ["*", "self", { const: 1000000, type: "number" }] },
},
},
webShortname: "h",
migrationState,
});
Expand Down
7 changes: 4 additions & 3 deletions apps/hash-api/src/graph/ontology/primitive/data-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ import type {
} from "@local/hash-graph-client";
import type {
ConstructDataTypeParams,
DataTypeConversionTargets,
DataTypeDirectConversionsMap,
DataTypeMetadata,
DataTypeWithMetadata,
OntologyTypeRecordId,
} from "@local/hash-graph-types/ontology";
import type { OwnedById } from "@local/hash-graph-types/web";
import type { DataTypeConversionTargets } from "@local/hash-isomorphic-utils/data-types";
import { currentTimeInstantTemporalAxes } from "@local/hash-isomorphic-utils/graph-queries";
import { generateTypeId } from "@local/hash-isomorphic-utils/ontology-types";
import {
Expand Down Expand Up @@ -65,7 +66,7 @@ export const createDataType: ImpureGraphFunction<
webShortname?: string;
relationships: DataTypeRelationAndSubject[];
provenance?: ProvidedOntologyEditionProvenance;
conversions: Record<BaseUrl, Conversions>;
conversions?: DataTypeDirectConversionsMap | null;
},
Promise<DataTypeWithMetadata>
> = async (ctx, authentication, params) => {
Expand Down Expand Up @@ -102,7 +103,7 @@ export const createDataType: ImpureGraphFunction<
...ctx.provenance,
...params.provenance,
},
conversions,
conversions: conversions ?? {},
},
);

Expand Down
10 changes: 5 additions & 5 deletions apps/hash-api/src/graphql/resolvers/ontology/data-type.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { DataTypeWithMetadata } from "@blockprotocol/graph";
import type { OntologyTemporalMetadata } from "@local/hash-graph-client/dist/api.d";
import type { DataTypeConversionsMap } from "@local/hash-isomorphic-utils/data-types";
import type { OntologyTemporalMetadata } from "@local/hash-graph-client";
import type { DataTypeFullConversionTargetsMap } from "@local/hash-graph-types/ontology";
import {
currentTimeInstantTemporalAxes,
defaultDataTypeAuthorizationRelationships,
Expand Down Expand Up @@ -115,7 +115,7 @@ export const getDataType: ResolverFn<
);

export const getDataTypeConversionTargetsResolver: ResolverFn<
Promise<DataTypeConversionsMap>,
Promise<DataTypeFullConversionTargetsMap>,
Record<string, never>,
GraphQLContext,
QueryGetDataTypeConversionTargetsArgs
Expand All @@ -133,7 +133,7 @@ export const createDataTypeResolver: ResolverFn<
LoggedInGraphQLContext,
MutationCreateDataTypeArgs
> = async (_, params, { dataSources, authentication, provenance }) => {
const { ownedById, dataType } = params;
const { ownedById, conversions, dataType } = params;

const createdDataType = await createDataType(
{
Expand All @@ -145,7 +145,7 @@ export const createDataTypeResolver: ResolverFn<
ownedById,
schema: dataType,
relationships: defaultDataTypeAuthorizationRelationships,
conversions: {},
conversions: conversions ?? {},
},
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,21 @@ export const checkUserPermissionsOnDataTypeQuery = gql`
`;

export const createDataTypeMutation = gql`
mutation createDataType($ownedById: OwnedById!, $dataType: ConstructDataTypeParams!) {
createDataType(ownedById: $ownedById, dataType: $dataType)
mutation createDataType(
$ownedById: OwnedById!
$dataType: ConstructDataTypeParams!
$conversions: DataTypeDirectConversionsMap
) {
createDataType(ownedById: $ownedById, dataType: $dataType, conversions: $conversions)
}
`;

export const updateDataTypeMutation = gql`
mutation updateDataType($dataTypeId: VersionedUrl!, $dataType: ConstructDataTypeParams!) {
updateDataType(dataTypeId: $dataTypeId, dataType: $dataType)
mutation updateDataType(
$dataTypeId: VersionedUrl!
$dataType: ConstructDataTypeParams!
$conversions: DataTypeDirectConversionsMap
) {
updateDataType(dataTypeId: $dataTypeId, dataType: $dataType, conversions: $conversions)
}
`;
4 changes: 4 additions & 0 deletions apps/hash-frontend/src/pages/shared/create-data-type-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { getDataTypeQuery } from "../../graphql/queries/ontology/data-type.queri
import { Button } from "../../shared/ui/button";
import { useAuthenticatedUser } from "./auth-info-context";
import { useDataTypesContext } from "./data-types-context";
import { useSlideStack } from "./slide-stack";
import { useGenerateTypeUrlsForUser } from "./use-generate-type-urls-for-user";
import { WorkspaceContext } from "./workspace-context";

Expand Down Expand Up @@ -76,6 +77,8 @@ export const CreateDataTypeForm = ({

const { dataTypes } = useDataTypesContext();

const { closeSlideStack } = useSlideStack();

const parentType = extendsDataTypeId ? dataTypes?.[extendsDataTypeId] : null;

if (!activeWorkspace) {
Expand Down Expand Up @@ -126,6 +129,7 @@ export const CreateDataTypeForm = ({
afterSubmit?.();

await router.push(nextUrl);
closeSlideStack();
});

const formItemWidth = `min(calc(100% - ${HELPER_TEXT_WIDTH + 52}px), 600px)`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
import { useEntityTypesOptional } from "../../shared/entity-types-context/hooks";
import { Button } from "../../shared/ui/button";
import { useAuthenticatedUser } from "./auth-info-context";
import { useSlideStack } from "./slide-stack";
import { useGenerateTypeUrlsForUser } from "./use-generate-type-urls-for-user";
import { WorkspaceContext } from "./workspace-context";

Expand Down Expand Up @@ -76,6 +77,8 @@ export const CreateEntityTypeForm = ({
defaultValues: initialData,
});

const { closeSlideStack } = useSlideStack();

const title = watch("title");
const titlePlural = watch("titlePlural");
const inverseTitle = watch("inverseTitle");
Expand Down Expand Up @@ -217,6 +220,7 @@ export const CreateEntityTypeForm = ({
afterSubmit?.();

await router.push(nextUrl);
closeSlideStack();
});

const formItemWidth = `min(calc(100% - ${HELPER_TEXT_WIDTH + 52}px), 600px)`;
Expand Down
39 changes: 31 additions & 8 deletions apps/hash-frontend/src/pages/shared/data-type.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { generateLinkParameters } from "../../shared/generate-link-parameters";
import { Link } from "../../shared/ui/link";
import { useUserPermissionsOnDataType } from "../../shared/use-user-permissions-on-data-type";
import { DataTypeConstraints } from "./data-type/data-type-constraints";
import { DataTypeConversions } from "./data-type/data-type-conversions";
import {
type DataTypeFormData,
getDataTypeFromFormData,
Expand All @@ -43,6 +44,7 @@ import {
import { DataTypeHeader } from "./data-type/data-type-header";
import { DataTypeLabels } from "./data-type/data-type-labels";
import { DataTypesParents } from "./data-type/data-type-parents";
import { InheritedConstraintsProvider } from "./data-type/shared/use-inherited-constraints";
import { useDataTypesContext } from "./data-types-context";
import { EditBarTypeEditor } from "./entity-type-page/edit-bar-type-editor";
import { NotFound } from "./not-found";
Expand Down Expand Up @@ -118,9 +120,19 @@ export const DataType = ({
defaultValue: [],
});

const abstract = useWatch({
control,
name: "abstract",
});

useEffect(() => {
if (draftNewDataType) {
reset(getFormDataFromDataType(draftNewDataType.schema));
reset(
getFormDataFromDataType({
schema: draftNewDataType.schema,
metadata: {},
}),
);
}
}, [draftNewDataType, reset]);

Expand Down Expand Up @@ -194,7 +206,7 @@ export const DataType = ({

useEffect(() => {
if (remoteDataType) {
formMethods.reset(getFormDataFromDataType(remoteDataType.schema));
formMethods.reset(getFormDataFromDataType(remoteDataType));
}
}, [remoteDataType, formMethods]);

Expand All @@ -219,7 +231,8 @@ export const DataType = ({
return;
}

const inputData = getDataTypeFromFormData(data);
const { dataType: inputDataType, conversions } =
getDataTypeFromFormData(data);

if (isDraft) {
if (!ownedById) {
Expand All @@ -228,7 +241,8 @@ export const DataType = ({

const response = await createDataType({
variables: {
dataType: inputData,
dataType: inputDataType,
conversions,
ownedById,
},
});
Expand All @@ -248,7 +262,8 @@ export const DataType = ({
const response = await updateDataType({
variables: {
dataTypeId: remoteDataType.schema.$id,
dataType: inputData,
dataType: inputDataType,
conversions,
},
});

Expand Down Expand Up @@ -310,7 +325,7 @@ export const DataType = ({

return (
<>
<NextSeo title={`${dataType.schema.title} | Data Type`} />
{!inSlide && <NextSeo title={`${dataType.schema.title} | Data Type`} />}
<FormProvider {...formMethods}>
<Box display="contents" component="form" onSubmit={handleSubmit}>
<TopContextBar
Expand Down Expand Up @@ -393,8 +408,16 @@ export const DataType = ({
}}
/>

<DataTypeConstraints isReadOnly={isReadOnly} />
<DataTypeLabels isReadOnly={isReadOnly} />
<InheritedConstraintsProvider>
<DataTypeConstraints isReadOnly={isReadOnly} />
<DataTypeLabels isReadOnly={isReadOnly} />
{!abstract && (
<DataTypeConversions
dataType={dataType.schema}
isReadOnly={isReadOnly}
/>
)}
</InheritedConstraintsProvider>
</Stack>
</TypeDefinitionContainer>
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const AbstractConstraint = ({
}

return (
<Stack mt={3} gap={1}>
<Stack mt={2} gap={1}>
<ItemLabel tooltip="You can disable assigning a data type directly to a property. This is useful if you want to create a parent type which isn't suitable for direct use (e.g. 'Length' can't be used directly, because 'Length: 4' doesn't make sense).">
{abstract ? "Not assignable" : "Assignable"}
</ItemLabel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ export const NumberConstraintEditor = ({
inheritedConstraints: InheritedConstraints;
}) => {
return (
<Stack gap={4} mt={2}>
<Stack gap={3} mt={2}>
{!inheritedConstraints.enum && (
<NumberRangeEditor
hasEnum={"enum" in inheritedConstraints || !!ownEnum}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,22 @@ import {
CloseIcon,
IconButton,
} from "@hashintel/design-system";
import type { MergedValueSchema } from "@local/hash-isomorphic-utils/data-types";
import {
createFormattedValueParts,
type MergedValueSchema,
} from "@local/hash-isomorphic-utils/data-types";
import DragIndicatorIcon from "@mui/icons-material/DragIndicator";
import { Tooltip, Typography } from "@mui/material";
import { Box, Stack, type SxProps, type Theme } from "@mui/system";
import { type ReactElement, useEffect, useMemo, useRef, useState } from "react";
import { useFormContext } from "react-hook-form";
import { useFormContext, useWatch } from "react-hook-form";

import { TriangleExclamationRegularIcon } from "../../../../../shared/icons/triangle-exclamation-regular-icon";
import { Button } from "../../../../../shared/ui";
import { NumberOrTextInput } from "../../../number-or-text-input";
import type { DataTypeFormData } from "../../data-type-form";
import { ItemLabel } from "../../shared/item-label";
import { useInheritedConstraints } from "../../shared/use-inherited-constraints";
import type { InheritedConstraints } from "../types";

const SaveButton = ({ onClick }: { onClick: () => void }) => (
Expand Down Expand Up @@ -128,6 +132,17 @@ const EnumItem = ({
id: item,
});

const { control } = useFormContext<DataTypeFormData>();

const ownLabel = useWatch({ control, name: "label" });

const inheritedLabel = useInheritedConstraints().label;

const label = {
left: ownLabel?.left ?? inheritedLabel?.left?.value,
right: ownLabel?.right ?? inheritedLabel?.right?.value,
};

const isLastUnremovableItem = isOnlyItem && inheritedFromTitle;

return (
Expand Down Expand Up @@ -170,7 +185,14 @@ const EnumItem = ({
ml: inheritedFromTitle ? 0.5 : 0,
}}
>
{item}
{createFormattedValueParts({
inner: item.toString(),
schema: { label },
}).map(({ color, text }) => (
<span key={text} style={{ color }}>
{text}
</span>
))}
</Typography>
</Stack>
<Stack direction="row" alignItems="center" gap={0.2}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box } from "@mui/material";
import { Box, type SxProps, type Theme } from "@mui/material";

import { inputStyles } from "../../shared/input-styles";

Expand All @@ -8,6 +8,7 @@ export const NumberInput = ({
min,
max,
multipleOf,
sx,
value,
onChange,
width = 120,
Expand All @@ -18,6 +19,7 @@ export const NumberInput = ({
max?: number;
multipleOf?: number;
onChange: (value: number | null) => void;
sx?: SxProps<Theme>;
value: number | null;
width?: number;
}) => {
Expand All @@ -40,7 +42,7 @@ export const NumberInput = ({
onChange(parsedValue);
}
}}
sx={[inputStyles, { width }]}
sx={[inputStyles, { width }, ...(Array.isArray(sx) ? sx : [sx])]}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export const StringConstraintEditor = ({
const hasEnum = "enum" in inheritedConstraints || !!ownEnum;

return (
<Stack gap={4} mt={2}>
<Stack gap={3} mt={2}>
{!inheritedConstraints.enum && !isStringLengthIrrelevant(format) && (
<StringLengthEditor
hasEnum={hasEnum}
Expand Down
Loading
Loading