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

Align useMutation with Relay #117

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Use useMutation_deprecated in FeedbackComponent",
"packageName": "@nova/examples",
"email": "132382266+DarynaAkhmedova@users.noreply.github.com",
"dependentChangeType": "patch"
}
7 changes: 7 additions & 0 deletions change/@nova-react-c9299b3a-611a-4555-ad5a-a1c2cf647f6f.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "major",
"comment": "Align useMutation with Relay",
"packageName": "@nova/react",
"email": "132382266+DarynaAkhmedova@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Update @graphitation/apollo-react-relay-duct-tape",
"packageName": "@nova/react-test-utils",
"email": "132382266+DarynaAkhmedova@users.noreply.github.com",
"dependentChangeType": "patch"
}
7 changes: 7 additions & 0 deletions change/@nova-types-f14269f8-868b-49cf-9c08-777b5e415c9c.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "major",
"comment": "Align useMutation with Relay",
"packageName": "@nova/types",
"email": "132382266+DarynaAkhmedova@users.noreply.github.com",
"dependentChangeType": "patch"
}
4 changes: 2 additions & 2 deletions packages/examples/src/Feedback/Feedback.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { graphql, useFragment, useMutation } from "@nova/react";
import { graphql, useFragment, useMutation_deprecated } from "@nova/react";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not use deprecated functions in examples - they should be as up to date as possible

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would that mean we need to update graphitation first? If so I would be in favor of that as otherwise nova bump in apollo based hosts will require search and replace and TBH I am not even sure how would we handle that in people app

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that would mean that we need to update graphitation first. We discussed it with Mark, and the initial plan was to start with this change. But we can revisit the initial decision

import * as React from "react";
import type { FeedbackComponent_LikeMutation } from "./__generated__/FeedbackComponent_LikeMutation.graphql";
import type { Feedback_feedbackFragment$key } from "./__generated__/Feedback_feedbackFragment.graphql";
Expand All @@ -20,7 +20,7 @@ export const Feedback_feedbackFragment = graphql`
export const FeedbackComponent = (props: Props) => {
const [errorMessage, setErrorMessage] = React.useState<string | null>(null);
const feedback = useFragment(Feedback_feedbackFragment, props.feedback);
const [like, isPending] = useMutation<FeedbackComponent_LikeMutation>(
const [like, isPending] = useMutation_deprecated<FeedbackComponent_LikeMutation>(
graphql`
mutation FeedbackComponent_LikeMutation($input: FeedbackLikeInput!) {
feedbackLike(input: $input) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,11 @@ function createNovaEnvironment(
const client = createMockClient(schema, options);
const env: NovaMockEnvironment<"storybook"> = {
graphql: {
...(GraphQLHooks as NovaGraphQL),
...(GraphQLHooks as unknown as NovaGraphQL),
useMutation_deprecated: GraphQLHooks.useMutation,
useMutation() {
throw new Error("Not supported yet in Apollo");
},
mock: client.mock as MockFunctions<any, any>,
},
providerWrapper: ({ children }) => (
Expand Down
6 changes: 5 additions & 1 deletion packages/nova-react-test-utils/src/test-utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ export function createMockEnvironment(
bubble: jest.fn(),
},
graphql: {
...(GraphQLHooks as NovaGraphQL),
...(GraphQLHooks as unknown as NovaGraphQL),
useMutation_deprecated: GraphQLHooks.useMutation,
useMutation() {
throw new Error("Not supported yet in Apollo");
},
mock: client.mock as MockFunctions<any, any>,
},
providerWrapper: ({ children }) => (
Expand Down
1 change: 1 addition & 0 deletions packages/nova-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@types/jest": "^29.2.0",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
"@types/react-relay": "^16.0.6",
"monorepo-scripts": "*",
"react": "^18.3.1",
"react-dom": "^18.3.1"
Expand Down
6 changes: 6 additions & 0 deletions packages/nova-react/src/graphql/hooks.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,19 @@ import {
usePaginationFragment,
useRefetchableFragment,
useSubscription,
useMutation,
} from "./hooks";
import type { GraphQLTaggedNode } from "./taggedNode";
import type { FragmentRefs } from "./types";
import type { useMutation as relayUseMutation, GraphQLTaggedNode as RelayGraphQLTaggedNode } from "react-relay";

type IsNotNull<T> = null extends T ? false : true;
type IsNotUndefined<T> = undefined extends T ? false : true;

// This is used to verify type compatibility with Relay useMutation hook
declare function useMutationRelay(mutation: RelayGraphQLTaggedNode): ReturnType<typeof useMutation>;
export const typeVerification: typeof relayUseMutation = useMutationRelay;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is nice :D


describe(useLazyLoadQuery, () => {
it("ensures an implementation is supplied", () => {
const graphql: NovaGraphQL = {};
Expand Down
34 changes: 30 additions & 4 deletions packages/nova-react/src/graphql/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import type {
KeyTypeData,
OperationType,
PaginationFn,
PayloadError,
RefetchFn,
FetchPolicy
} from "./types";
import type { Disposable } from "@nova/types";

/**
* Executes a GraphQL query.
Expand Down Expand Up @@ -331,15 +333,24 @@ interface MutationCommitterOptions<TMutationPayload extends OperationType> {
*/
context?: TMutationPayload["context"];
optimisticResponse?: Partial<TMutationPayload["response"]> | null;
onError?: (error: Error) => void;
onCompleted?: ((response: TMutationPayload["response"], errors: PayloadError[] | null) => void | null) | undefined;
}

interface MutationCommitterOptions_deprecated<TMutationPayload extends OperationType> {
variables: TMutationPayload["variables"];
/**
* Should be avoided when possible as it will not be compatible with Relay APIs.
*/
context?: TMutationPayload["context"];
optimisticResponse?: Partial<TMutationPayload["response"]> | null;
onError?: (error: Error) => void;
onCompleted?: (response: TMutationPayload["response"]) => void;
}

type MutationCommitter<TMutationPayload extends OperationType> = (
options: MutationCommitterOptions<TMutationPayload>,
) => Promise<{
errors?: readonly Error[];
data?: TMutationPayload["response"];
}>;
) => Disposable;

export function useMutation<TMutationPayload extends OperationType>(
mutation: GraphQLTaggedNode,
Expand All @@ -348,3 +359,18 @@ export function useMutation<TMutationPayload extends OperationType>(
invariant(graphql.useMutation, "Expected host to provide a useMutation hook");
return graphql.useMutation(mutation);
}

type MutationCommitter_deprecated<TMutationPayload extends OperationType> = (
options: MutationCommitterOptions_deprecated<TMutationPayload>,
) => Promise<{
errors?: readonly Error[];
data?: TMutationPayload["response"];
}>;

export function useMutation_deprecated<TMutationPayload extends OperationType>(
mutation: GraphQLTaggedNode,
): [MutationCommitter_deprecated<TMutationPayload>, boolean] {
const graphql = useNovaGraphQL();
invariant(graphql.useMutation_deprecated, "Expected host to provide a useMutation_deprecated hook");
return graphql.useMutation_deprecated(mutation);
}
21 changes: 4 additions & 17 deletions packages/nova-react/src/graphql/types.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,8 @@
export interface Variables {
[name: string]: any;
}

export interface Context {
[name: string]: any;
}

export interface OperationType {
readonly variables: Variables;
readonly context?: Context;
readonly response: unknown;
readonly rawResponse?: unknown;
}

/**
* relay-compiler-language-typescript support for fragment references
*/
*/

export type { OperationType, PayloadError, Disposable } from "@nova/types";

export interface _RefType<Ref extends string> {
" $refType": Ref;
Expand Down Expand Up @@ -95,4 +82,4 @@ type RefetchOptions = {

type Disposable = {
dispose(): void;
};
};
39 changes: 37 additions & 2 deletions packages/nova-types/src/nova-graphql.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
* In case the host application uses Apollo Client, these hooks can be provided by using the
* `@graphitation/apollo-react-relay-duct-tape` package. See https://github.com/microsoft/graphitation for details.
*/

export interface NovaGraphQL<GraphQLDocument = any> {
useFragment?: (
fragmentInput: GraphQLDocument,
Expand Down Expand Up @@ -64,15 +63,51 @@ export interface NovaGraphQL<GraphQLDocument = any> {
onError?: (error: Error) => void;
}) => void;

useMutation?: (
useMutation?: <TMutationPayload extends OperationType>(
mutation: GraphQLDocument,
) => [
(options: {
variables: { [name: string]: unknown };
context?: { [name: string]: unknown };
optimisticResponse?: unknown | null;
onCompleted?: ((response: TMutationPayload["response"], errors: PayloadError[] | null) => void | null) | undefined;
onError?: (error: Error) => void;
}) => Disposable,
boolean,
];

useMutation_deprecated?: (
mutation: GraphQLDocument,
) => [
(options: {
variables: { [name: string]: unknown };
context?: { [name: string]: unknown };
optimisticResponse?: Partial<any["response"]> | null;
onCompleted?: (response: unknown) => void;
}) => Promise<{ errors?: readonly Error[]; data?: unknown }>,
boolean,
];
}

export type Disposable = {
dispose(): void;
};

export interface OperationType {
readonly variables: { [name: string]: unknown };
readonly context?: { [name: string]: unknown };
readonly response: unknown;
readonly rawResponse?: unknown | undefined;
}

export interface PayloadError {
message: string;
locations?:
| Array<{
line: number;
column: number;
}>
| undefined;
path?: Array<string | number>;
severity?: "CRITICAL" | "ERROR" | "WARNING" | undefined;
}
44 changes: 41 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3841,6 +3841,14 @@
dependencies:
"@types/react" "*"

"@types/react-relay@^16.0.6":
version "16.0.6"
resolved "https://registry.yarnpkg.com/@types/react-relay/-/react-relay-16.0.6.tgz#afc467fab89dc4c96fb1424f84b869750f5c42f2"
integrity sha512-VTntVQJhlwQYNUlbNgGf8RYy7EtQPRZqsD/w2Si0ygZspJXuNlVdRkklWMFN99EMRhHDpqlNHD8i3wIs7QRz9g==
dependencies:
"@types/react" "*"
"@types/relay-runtime" "*"

"@types/react-test-renderer@^18.3.0":
version "18.3.0"
resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.3.0.tgz#839502eae70058a4ae161f63385a8e7929cef4c0"
Expand All @@ -3856,6 +3864,11 @@
"@types/prop-types" "*"
csstype "^3.0.2"

"@types/relay-runtime@*":
version "17.0.4"
resolved "https://registry.yarnpkg.com/@types/relay-runtime/-/relay-runtime-17.0.4.tgz#428526fc3e6dfb6e0a5730c38ad521cb1eea189b"
integrity sha512-fB77br4lXlBYM/HpI6VI6KCrj5pw0LiAnkZOkffjirNYso+dzXGWkeIm0G0MGszD8WY1et+r1Uj2TA6rscBXNQ==

"@types/resolve@^1.20.2":
version "1.20.6"
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.6.tgz#e6e60dad29c2c8c206c026e6dd8d6d1bdda850b8"
Expand Down Expand Up @@ -10446,7 +10459,16 @@ string-length@^4.0.1:
char-regex "^1.0.2"
strip-ansi "^6.0.0"

"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand Down Expand Up @@ -10478,7 +10500,14 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"

"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"

strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
Expand Down Expand Up @@ -11440,7 +11469,7 @@ workspace-tools@^0.30.0:
js-yaml "^4.1.0"
micromatch "^4.0.0"

"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
Expand All @@ -11458,6 +11487,15 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
Expand Down