@cedarjs/web 6.0.0-canary.2645 → 6.0.0-canary.2647
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apollo/fragmentRegistry.d.ts +8 -0
- package/dist/apollo/fragmentRegistry.d.ts.map +1 -1
- package/dist/apollo/fragmentRegistry.js +19 -1
- package/dist/apollo/index.d.ts.map +1 -1
- package/dist/apollo/index.js +4 -2
- package/dist/apollo/suspense.d.ts.map +1 -1
- package/dist/apollo/suspense.js +7 -4
- package/dist/cjs/apollo/fragmentRegistry.d.ts +8 -0
- package/dist/cjs/apollo/fragmentRegistry.d.ts.map +1 -1
- package/dist/cjs/apollo/fragmentRegistry.js +21 -2
- package/dist/cjs/apollo/index.d.ts.map +1 -1
- package/dist/cjs/apollo/index.js +2 -1
- package/dist/cjs/apollo/suspense.d.ts.map +1 -1
- package/dist/cjs/apollo/suspense.js +7 -4
- package/dist/cjs/components/GraphQLHooksProvider.d.ts +33 -1
- package/dist/cjs/components/GraphQLHooksProvider.d.ts.map +1 -1
- package/dist/cjs/components/GraphQLHooksProvider.js +14 -2
- package/dist/cjs/components/cell/cellTypes.d.ts +14 -1
- package/dist/cjs/components/cell/cellTypes.d.ts.map +1 -1
- package/dist/cjs/components/cell/createCell.d.ts +1 -7
- package/dist/cjs/components/cell/createCell.d.ts.map +1 -1
- package/dist/cjs/components/cell/createCell.js +21 -2
- package/dist/cjs/components/cell/createFragmentCell.d.ts +19 -0
- package/dist/cjs/components/cell/createFragmentCell.d.ts.map +1 -0
- package/dist/cjs/components/cell/createFragmentCell.js +108 -0
- package/dist/cjs/components/cell/createFragmentCell.test.js +235 -0
- package/dist/cjs/components/cell/createSuspendingCell.d.ts.map +1 -1
- package/dist/cjs/components/cell/createSuspendingCell.js +7 -1
- package/dist/cjs/index.d.ts +3 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +5 -0
- package/dist/components/GraphQLHooksProvider.d.ts +33 -1
- package/dist/components/GraphQLHooksProvider.d.ts.map +1 -1
- package/dist/components/GraphQLHooksProvider.js +13 -2
- package/dist/components/cell/cellTypes.d.ts +14 -1
- package/dist/components/cell/cellTypes.d.ts.map +1 -1
- package/dist/components/cell/createCell.d.ts +1 -7
- package/dist/components/cell/createCell.d.ts.map +1 -1
- package/dist/components/cell/createCell.js +21 -2
- package/dist/components/cell/createFragmentCell.d.ts +19 -0
- package/dist/components/cell/createFragmentCell.d.ts.map +1 -0
- package/dist/components/cell/createFragmentCell.js +74 -0
- package/dist/components/cell/createFragmentCell.test.js +212 -0
- package/dist/components/cell/createSuspendingCell.d.ts.map +1 -1
- package/dist/components/cell/createSuspendingCell.js +7 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/package.json +5 -5
|
@@ -3,9 +3,22 @@ import { fragmentRegistry } from "../../apollo/fragmentRegistry.js";
|
|
|
3
3
|
import { getOperationName } from "../../graphql.js";
|
|
4
4
|
import { useQuery } from "../GraphQLHooksProvider.js";
|
|
5
5
|
import { useCellCacheContext } from "./CellCacheContext.js";
|
|
6
|
+
import { createFragmentCell } from "./createFragmentCell.js";
|
|
6
7
|
import { createSuspendingCell } from "./createSuspendingCell.js";
|
|
7
8
|
import { isDataEmpty } from "./isCellEmpty.js";
|
|
8
|
-
|
|
9
|
+
function createCell(createCellProps) {
|
|
10
|
+
if (createCellProps.FRAGMENT) {
|
|
11
|
+
if (!createCellProps.QUERY) {
|
|
12
|
+
return createFragmentCell(createCellProps);
|
|
13
|
+
}
|
|
14
|
+
fragmentRegistry.register(createCellProps.FRAGMENT);
|
|
15
|
+
}
|
|
16
|
+
if (RWJS_ENV.RWJS_EXP_STREAMING_SSR) {
|
|
17
|
+
const suspendingCellProps = createCellProps;
|
|
18
|
+
return createSuspendingCell(suspendingCellProps);
|
|
19
|
+
}
|
|
20
|
+
return createNonSuspendingCell(createCellProps);
|
|
21
|
+
}
|
|
9
22
|
function createNonSuspendingCell({
|
|
10
23
|
QUERY,
|
|
11
24
|
beforeQuery = (props) => ({
|
|
@@ -28,10 +41,16 @@ function createNonSuspendingCell({
|
|
|
28
41
|
Success,
|
|
29
42
|
displayName = "Cell"
|
|
30
43
|
}) {
|
|
44
|
+
if (!QUERY) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`Can't create a Cell (${displayName}) without a QUERY or FRAGMENT export`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
const cellQuery = QUERY;
|
|
31
50
|
function NamedCell(props) {
|
|
32
51
|
const { children: _, ...variables } = props;
|
|
33
52
|
const options = beforeQuery(variables);
|
|
34
|
-
const query = typeof
|
|
53
|
+
const query = typeof cellQuery === "function" ? cellQuery(options) : cellQuery;
|
|
35
54
|
let {
|
|
36
55
|
// eslint-disable-next-line prefer-const
|
|
37
56
|
error,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { CreateCellProps } from './cellTypes.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a Cell out of a GraphQL fragment and components that render its
|
|
5
|
+
* data.
|
|
6
|
+
*
|
|
7
|
+
* Fragment Cells don't fire queries of their own. A parent Cell spreads the
|
|
8
|
+
* fragment in its QUERY and passes the matching slice of the query result
|
|
9
|
+
* down via a prop named after the fragment (`AuthorCell_author` -> `author`).
|
|
10
|
+
* `Success` receives the data as that same prop. The data is read
|
|
11
|
+
* synchronously, so fragment Cells never render `Loading`.
|
|
12
|
+
*
|
|
13
|
+
* When the GraphQL client supports it (Apollo does), the fragment data is
|
|
14
|
+
* read from the client's cache, so the Cell re-renders when other queries or
|
|
15
|
+
* mutations update the underlying entity. If the cache read is incomplete,
|
|
16
|
+
* the passed-in object itself is used as the data snapshot.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createFragmentCell<CellProps extends Record<string, unknown>, CellVariables extends Record<string, unknown>>({ FRAGMENT, afterQuery, isEmpty, Empty, Success, displayName, }: CreateCellProps<CellProps, CellVariables>): React.FC<CellProps>;
|
|
19
|
+
//# sourceMappingURL=createFragmentCell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createFragmentCell.d.ts","sourceRoot":"","sources":["../../../src/components/cell/createFragmentCell.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAQzB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAqDrD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACzC,aAAa,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7C,EACA,QAAQ,EACR,UAA2B,EAC3B,OAAqB,EACrB,KAAK,EACL,OAAO,EACP,WAAoB,GACrB,EAAE,eAAe,CAAC,SAAS,EAAE,aAAa,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,CAkEjE"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Kind } from "graphql";
|
|
3
|
+
import { fragmentRegistry } from "../../apollo/fragmentRegistry.js";
|
|
4
|
+
import { useFragment } from "../GraphQLHooksProvider.js";
|
|
5
|
+
import { isDataEmpty } from "./isCellEmpty.js";
|
|
6
|
+
function getFragmentDefinition(fragment, displayName) {
|
|
7
|
+
const fragmentDefinition = fragment.definitions.find(
|
|
8
|
+
(definition) => definition.kind === Kind.FRAGMENT_DEFINITION
|
|
9
|
+
);
|
|
10
|
+
if (!fragmentDefinition) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
`The FRAGMENT export in ${displayName} must contain a GraphQL fragment definition, like \`fragment AuthorCell_author on User { fullName }\``
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
return fragmentDefinition;
|
|
16
|
+
}
|
|
17
|
+
function getFragmentPropName(fragmentDefinition) {
|
|
18
|
+
const fragmentName = fragmentDefinition.name.value;
|
|
19
|
+
const underscoreIndex = fragmentName.lastIndexOf("_");
|
|
20
|
+
if (underscoreIndex > 0 && underscoreIndex < fragmentName.length - 1) {
|
|
21
|
+
return fragmentName.slice(underscoreIndex + 1);
|
|
22
|
+
}
|
|
23
|
+
const typename = fragmentDefinition.typeCondition.name.value;
|
|
24
|
+
return typename.charAt(0).toLowerCase() + typename.slice(1);
|
|
25
|
+
}
|
|
26
|
+
function isDataRef(value) {
|
|
27
|
+
return typeof value === "object" && value !== null;
|
|
28
|
+
}
|
|
29
|
+
function createFragmentCell({
|
|
30
|
+
FRAGMENT,
|
|
31
|
+
afterQuery = (data) => data,
|
|
32
|
+
isEmpty = isDataEmpty,
|
|
33
|
+
Empty,
|
|
34
|
+
Success,
|
|
35
|
+
displayName = "Cell"
|
|
36
|
+
}) {
|
|
37
|
+
if (!FRAGMENT) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`createFragmentCell() for ${displayName} requires a FRAGMENT`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
const fragment = FRAGMENT;
|
|
43
|
+
const fragmentDefinition = getFragmentDefinition(fragment, displayName);
|
|
44
|
+
const fragmentName = fragmentDefinition.name.value;
|
|
45
|
+
const propName = getFragmentPropName(fragmentDefinition);
|
|
46
|
+
fragmentRegistry.register(fragment);
|
|
47
|
+
function NamedCell(props) {
|
|
48
|
+
const { children: _, [propName]: rawSlice, ...rest } = props;
|
|
49
|
+
const dataRef = isDataRef(rawSlice) ? rawSlice : void 0;
|
|
50
|
+
const fragmentResult = useFragment({
|
|
51
|
+
fragment,
|
|
52
|
+
fragmentName,
|
|
53
|
+
from: dataRef ?? {}
|
|
54
|
+
});
|
|
55
|
+
if (!(propName in props)) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`${displayName} must be passed a \`${propName}\` prop. Render it from a parent Cell that spreads \`...${fragmentName}\` in its QUERY, and pass the matching data object: \`<${displayName} ${propName}={data.someField} />\``
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
const data = fragmentResult.complete ? fragmentResult.data : dataRef ?? null;
|
|
61
|
+
const afterQueryData = afterQuery({ [propName]: data });
|
|
62
|
+
if (isEmpty({ [propName]: data }, { isDataEmpty }) && Empty) {
|
|
63
|
+
return /* @__PURE__ */ React.createElement(Empty, { ...rest, ...afterQueryData });
|
|
64
|
+
}
|
|
65
|
+
return /* @__PURE__ */ React.createElement(Success, { ...rest, ...afterQueryData });
|
|
66
|
+
}
|
|
67
|
+
NamedCell.displayName = displayName;
|
|
68
|
+
return (props) => {
|
|
69
|
+
return /* @__PURE__ */ React.createElement(NamedCell, { ...props });
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export {
|
|
73
|
+
createFragmentCell
|
|
74
|
+
};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import { parse } from "graphql";
|
|
4
|
+
import { vi, describe, beforeAll, test, expect } from "vitest";
|
|
5
|
+
import { fragmentRegistry } from "../../apollo/fragmentRegistry.js";
|
|
6
|
+
import { GraphQLHooksProvider } from "../GraphQLHooksProvider.js";
|
|
7
|
+
import { createCell } from "./createCell.js";
|
|
8
|
+
const AUTHOR_FRAGMENT = parse(`
|
|
9
|
+
fragment AuthorCell_author on User {
|
|
10
|
+
email
|
|
11
|
+
fullName
|
|
12
|
+
}
|
|
13
|
+
`);
|
|
14
|
+
describe("createFragmentCell", () => {
|
|
15
|
+
beforeAll(() => {
|
|
16
|
+
globalThis.RWJS_ENV = {
|
|
17
|
+
RWJS_EXP_STREAMING_SSR: false
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
test("renders Success with data from its data prop", () => {
|
|
21
|
+
const TestCell = createCell({
|
|
22
|
+
FRAGMENT: AUTHOR_FRAGMENT,
|
|
23
|
+
Success: ({ author }) => /* @__PURE__ */ React.createElement(React.Fragment, null, "By ", author.fullName)
|
|
24
|
+
});
|
|
25
|
+
render(
|
|
26
|
+
// No useFragment hook registered, so the Cell falls back to reading
|
|
27
|
+
// data straight off the `author` prop
|
|
28
|
+
/* @__PURE__ */ React.createElement(GraphQLHooksProvider, { useQuery: null, useMutation: null }, /* @__PURE__ */ React.createElement(
|
|
29
|
+
TestCell,
|
|
30
|
+
{
|
|
31
|
+
author: { __typename: "User", id: 1, fullName: "Story Teller" }
|
|
32
|
+
}
|
|
33
|
+
))
|
|
34
|
+
);
|
|
35
|
+
screen.getByText(/^By Story Teller$/);
|
|
36
|
+
});
|
|
37
|
+
test("prefers complete data from the useFragment hook", () => {
|
|
38
|
+
const TestCell = createCell({
|
|
39
|
+
FRAGMENT: AUTHOR_FRAGMENT,
|
|
40
|
+
Success: ({ author }) => /* @__PURE__ */ React.createElement(React.Fragment, null, "By ", author.fullName)
|
|
41
|
+
});
|
|
42
|
+
const myUseFragmentHook = (options) => {
|
|
43
|
+
expect(options.fragmentName).toEqual("AuthorCell_author");
|
|
44
|
+
expect(options.from).toEqual({ __typename: "User", id: 1 });
|
|
45
|
+
return {
|
|
46
|
+
data: { fullName: "Cache Dweller" },
|
|
47
|
+
complete: true
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
render(
|
|
51
|
+
/* @__PURE__ */ React.createElement(
|
|
52
|
+
GraphQLHooksProvider,
|
|
53
|
+
{
|
|
54
|
+
useQuery: null,
|
|
55
|
+
useMutation: null,
|
|
56
|
+
useFragment: myUseFragmentHook
|
|
57
|
+
},
|
|
58
|
+
/* @__PURE__ */ React.createElement(TestCell, { author: { __typename: "User", id: 1 } })
|
|
59
|
+
)
|
|
60
|
+
);
|
|
61
|
+
screen.getByText(/^By Cache Dweller$/);
|
|
62
|
+
});
|
|
63
|
+
test("falls back to the data prop for incomplete useFragment reads", () => {
|
|
64
|
+
const TestCell = createCell({
|
|
65
|
+
FRAGMENT: AUTHOR_FRAGMENT,
|
|
66
|
+
Success: ({ author }) => /* @__PURE__ */ React.createElement(React.Fragment, null, "By ", author.fullName)
|
|
67
|
+
});
|
|
68
|
+
const myUseFragmentHook = () => {
|
|
69
|
+
return { data: void 0, complete: false };
|
|
70
|
+
};
|
|
71
|
+
render(
|
|
72
|
+
/* @__PURE__ */ React.createElement(
|
|
73
|
+
GraphQLHooksProvider,
|
|
74
|
+
{
|
|
75
|
+
useQuery: null,
|
|
76
|
+
useMutation: null,
|
|
77
|
+
useFragment: myUseFragmentHook
|
|
78
|
+
},
|
|
79
|
+
/* @__PURE__ */ React.createElement(
|
|
80
|
+
TestCell,
|
|
81
|
+
{
|
|
82
|
+
author: { __typename: "User", id: 1, fullName: "Ref Reader" }
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
);
|
|
87
|
+
screen.getByText(/^By Ref Reader$/);
|
|
88
|
+
});
|
|
89
|
+
test("derives the prop name from the fragment type when there is no underscore", () => {
|
|
90
|
+
const TestCell = createCell({
|
|
91
|
+
FRAGMENT: parse(`
|
|
92
|
+
fragment AuthorBio on User {
|
|
93
|
+
bio
|
|
94
|
+
}
|
|
95
|
+
`),
|
|
96
|
+
Success: ({ user }) => /* @__PURE__ */ React.createElement(React.Fragment, null, user.bio)
|
|
97
|
+
});
|
|
98
|
+
render(
|
|
99
|
+
/* @__PURE__ */ React.createElement(GraphQLHooksProvider, { useQuery: null, useMutation: null }, /* @__PURE__ */ React.createElement(TestCell, { user: { __typename: "User", id: 1, bio: "Writes things" } }))
|
|
100
|
+
);
|
|
101
|
+
screen.getByText(/^Writes things$/);
|
|
102
|
+
});
|
|
103
|
+
test("renders Empty when isEmpty says so", () => {
|
|
104
|
+
const TestCell = createCell({
|
|
105
|
+
FRAGMENT: AUTHOR_FRAGMENT,
|
|
106
|
+
isEmpty: () => true,
|
|
107
|
+
Empty: () => /* @__PURE__ */ React.createElement(React.Fragment, null, "Nothing to see"),
|
|
108
|
+
Success: ({ author }) => /* @__PURE__ */ React.createElement(React.Fragment, null, "By ", author.fullName)
|
|
109
|
+
});
|
|
110
|
+
render(
|
|
111
|
+
/* @__PURE__ */ React.createElement(GraphQLHooksProvider, { useQuery: null, useMutation: null }, /* @__PURE__ */ React.createElement(TestCell, { author: { __typename: "User", id: 1, fullName: "Nobody" } }))
|
|
112
|
+
);
|
|
113
|
+
screen.getByText(/^Nothing to see$/);
|
|
114
|
+
});
|
|
115
|
+
test("applies afterQuery to the fragment data", () => {
|
|
116
|
+
const TestCell = createCell({
|
|
117
|
+
FRAGMENT: AUTHOR_FRAGMENT,
|
|
118
|
+
afterQuery: (data) => ({
|
|
119
|
+
author: { ...data.author, fullName: "Changed Name" }
|
|
120
|
+
}),
|
|
121
|
+
Success: ({ author }) => /* @__PURE__ */ React.createElement(React.Fragment, null, "By ", author.fullName)
|
|
122
|
+
});
|
|
123
|
+
render(
|
|
124
|
+
/* @__PURE__ */ React.createElement(GraphQLHooksProvider, { useQuery: null, useMutation: null }, /* @__PURE__ */ React.createElement(
|
|
125
|
+
TestCell,
|
|
126
|
+
{
|
|
127
|
+
author: { __typename: "User", id: 1, fullName: "Original Name" }
|
|
128
|
+
}
|
|
129
|
+
))
|
|
130
|
+
);
|
|
131
|
+
screen.getByText(/^By Changed Name$/);
|
|
132
|
+
});
|
|
133
|
+
test("renders Empty when the data prop is null", () => {
|
|
134
|
+
const TestCell = createCell({
|
|
135
|
+
FRAGMENT: AUTHOR_FRAGMENT,
|
|
136
|
+
Empty: () => /* @__PURE__ */ React.createElement(React.Fragment, null, "No author"),
|
|
137
|
+
Success: ({ author }) => /* @__PURE__ */ React.createElement(React.Fragment, null, "By ", author.fullName)
|
|
138
|
+
});
|
|
139
|
+
render(
|
|
140
|
+
// A nullable field, or a partial error with `errorPolicy: 'all'`, can
|
|
141
|
+
// make the parent pass null here
|
|
142
|
+
/* @__PURE__ */ React.createElement(GraphQLHooksProvider, { useQuery: null, useMutation: null }, /* @__PURE__ */ React.createElement(TestCell, { author: null }))
|
|
143
|
+
);
|
|
144
|
+
screen.getByText(/^No author$/);
|
|
145
|
+
});
|
|
146
|
+
test("renders Success with null data when the data prop is null and there is no Empty", () => {
|
|
147
|
+
const TestCell = createCell({
|
|
148
|
+
FRAGMENT: AUTHOR_FRAGMENT,
|
|
149
|
+
Success: ({ author }) => /* @__PURE__ */ React.createElement(React.Fragment, null, "By ", author?.fullName ?? "unknown")
|
|
150
|
+
});
|
|
151
|
+
render(
|
|
152
|
+
/* @__PURE__ */ React.createElement(GraphQLHooksProvider, { useQuery: null, useMutation: null }, /* @__PURE__ */ React.createElement(TestCell, { author: null }))
|
|
153
|
+
);
|
|
154
|
+
screen.getByText(/^By unknown$/);
|
|
155
|
+
});
|
|
156
|
+
test("throws a helpful error naming the prop when it is not passed", () => {
|
|
157
|
+
const TestCell = createCell({
|
|
158
|
+
FRAGMENT: AUTHOR_FRAGMENT,
|
|
159
|
+
Success: ({ author }) => /* @__PURE__ */ React.createElement(React.Fragment, null, "By ", author.fullName),
|
|
160
|
+
displayName: "AuthorCell"
|
|
161
|
+
});
|
|
162
|
+
const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {
|
|
163
|
+
});
|
|
164
|
+
try {
|
|
165
|
+
expect(() => {
|
|
166
|
+
render(
|
|
167
|
+
/* @__PURE__ */ React.createElement(GraphQLHooksProvider, { useQuery: null, useMutation: null }, /* @__PURE__ */ React.createElement(TestCell, null))
|
|
168
|
+
);
|
|
169
|
+
}).toThrow(/`author` prop/);
|
|
170
|
+
} finally {
|
|
171
|
+
consoleErrorSpy.mockRestore();
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
test("stays a query Cell when both QUERY and FRAGMENT are exported", () => {
|
|
175
|
+
const TestCell = createCell({
|
|
176
|
+
// @ts-expect-error - Purposefully using a plain string here.
|
|
177
|
+
QUERY: "query AuthorQuery { author { fullName } }",
|
|
178
|
+
FRAGMENT: AUTHOR_FRAGMENT,
|
|
179
|
+
Success: ({ author }) => /* @__PURE__ */ React.createElement(React.Fragment, null, "By ", author.fullName)
|
|
180
|
+
});
|
|
181
|
+
const myUseQueryHook = () => {
|
|
182
|
+
return { data: { author: { fullName: "Query Result" } } };
|
|
183
|
+
};
|
|
184
|
+
render(
|
|
185
|
+
/* @__PURE__ */ React.createElement(GraphQLHooksProvider, { useQuery: myUseQueryHook, useMutation: null }, /* @__PURE__ */ React.createElement(TestCell, null))
|
|
186
|
+
);
|
|
187
|
+
screen.getByText(/^By Query Result$/);
|
|
188
|
+
});
|
|
189
|
+
test("registers the FRAGMENT even when the Cell stays a query Cell", () => {
|
|
190
|
+
createCell({
|
|
191
|
+
// @ts-expect-error - Purposefully using a plain string here.
|
|
192
|
+
QUERY: "query MixedExportQuery { author { fullName } }",
|
|
193
|
+
// Using a unique fragment name since the registry is shared between
|
|
194
|
+
// tests
|
|
195
|
+
FRAGMENT: parse(`
|
|
196
|
+
fragment MixedExportCell_author on User {
|
|
197
|
+
fullName
|
|
198
|
+
}
|
|
199
|
+
`),
|
|
200
|
+
Success: ({ author }) => /* @__PURE__ */ React.createElement(React.Fragment, null, "By ", author.fullName)
|
|
201
|
+
});
|
|
202
|
+
expect(fragmentRegistry.lookup("MixedExportCell_author")).toBeTruthy();
|
|
203
|
+
});
|
|
204
|
+
test("throws when the FRAGMENT export is not a fragment", () => {
|
|
205
|
+
expect(
|
|
206
|
+
() => createCell({
|
|
207
|
+
FRAGMENT: parse("query FindAuthor { author { id } }"),
|
|
208
|
+
Success: () => /* @__PURE__ */ React.createElement(React.Fragment, null, "hello")
|
|
209
|
+
})
|
|
210
|
+
).toThrow(/fragment definition/);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createSuspendingCell.d.ts","sourceRoot":"","sources":["../../../src/components/cell/createSuspendingCell.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAA;AAYvC,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,gBAAgB,CAAA;AAGvB,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AACrC;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,SAAS,MAAM,EACxB,aAAa,SAAS,MAAM,EAE5B,eAAe,EAAE,eAAe,CAAC,MAAM,EAAE,aAAa,CAAC,GACtD,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"createSuspendingCell.d.ts","sourceRoot":"","sources":["../../../src/components/cell/createSuspendingCell.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAA;AAYvC,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,gBAAgB,CAAA;AAGvB,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AACrC;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,SAAS,MAAM,EACxB,aAAa,SAAS,MAAM,EAE5B,eAAe,EAAE,eAAe,CAAC,MAAM,EAAE,aAAa,CAAC,GACtD,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,CAiJrB"}
|
|
@@ -26,6 +26,12 @@ function createSuspendingCell(createCellProps) {
|
|
|
26
26
|
Success,
|
|
27
27
|
displayName = "Cell"
|
|
28
28
|
} = createCellProps;
|
|
29
|
+
if (!QUERY) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Can't create a Cell (${displayName}) without a QUERY or FRAGMENT export`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
const cellQuery = QUERY;
|
|
29
35
|
function SuspendingSuccess(props) {
|
|
30
36
|
const { queryRef, suspenseQueryResult, userProps } = props;
|
|
31
37
|
const { data, networkStatus } = useReadQuery(queryRef);
|
|
@@ -57,7 +63,7 @@ function createSuspendingCell(createCellProps) {
|
|
|
57
63
|
return (props) => {
|
|
58
64
|
const { children: _, ...variables } = props;
|
|
59
65
|
const options = beforeQuery(variables);
|
|
60
|
-
const query = typeof
|
|
66
|
+
const query = typeof cellQuery === "function" ? cellQuery(options) : cellQuery;
|
|
61
67
|
const [queryRef, other] = useBackgroundQuery(query, options);
|
|
62
68
|
const client = useApolloClient();
|
|
63
69
|
const suspenseQueryResult = {
|
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,11 @@ import './global.web-auto-imports.js';
|
|
|
2
2
|
import './config.js';
|
|
3
3
|
export { FatalErrorBoundary } from './components/FatalErrorBoundary.js';
|
|
4
4
|
export { FetchConfigProvider, useFetchConfig, } from './components/FetchConfigProvider.js';
|
|
5
|
-
export { GraphQLHooksProvider, useQuery, useMutation, useSubscription, } from './components/GraphQLHooksProvider.js';
|
|
5
|
+
export { GraphQLHooksProvider, useQuery, useMutation, useSubscription, useFragment, } from './components/GraphQLHooksProvider.js';
|
|
6
|
+
export type { FragmentHookOptions, FragmentHookResult, } from './components/GraphQLHooksProvider.js';
|
|
6
7
|
export * from './components/cell/CellCacheContext.js';
|
|
7
8
|
export { createCell } from './components/cell/createCell.js';
|
|
9
|
+
export { createFragmentCell } from './components/cell/createFragmentCell.js';
|
|
8
10
|
export type { CellProps, CellFailureProps, CellLoadingProps, CellSuccessProps, CellSuccessData, } from './components/cell/cellTypes.js';
|
|
9
11
|
export * from './graphql.js';
|
|
10
12
|
export * from './components/RedwoodProvider.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,8BAA8B,CAAA;AACrC,OAAO,aAAa,CAAA;AAEpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAA;AAEvE,OAAO,EACL,mBAAmB,EACnB,cAAc,GACf,MAAM,qCAAqC,CAAA;AAC5C,OAAO,EACL,oBAAoB,EACpB,QAAQ,EACR,WAAW,EACX,eAAe,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,8BAA8B,CAAA;AACrC,OAAO,aAAa,CAAA;AAEpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAA;AAEvE,OAAO,EACL,mBAAmB,EACnB,cAAc,GACf,MAAM,qCAAqC,CAAA;AAC5C,OAAO,EACL,oBAAoB,EACpB,QAAQ,EACR,WAAW,EACX,eAAe,EACf,WAAW,GACZ,MAAM,sCAAsC,CAAA;AAC7C,YAAY,EACV,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,sCAAsC,CAAA;AAE7C,cAAc,uCAAuC,CAAA;AAErD,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AAE5E,YAAY,EACV,SAAS,EACT,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,GAChB,MAAM,gCAAgC,CAAA;AAEvC,cAAc,cAAc,CAAA;AAE5B,cAAc,iCAAiC,CAAA;AAE/C,cAAc,0BAA0B,CAAA;AACxC,cAAc,0BAA0B,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AACjD,OAAO,EAAE,MAAM,IAAI,IAAI,EAAE,MAAM,EAAE,CAAA;AAEjC,cAAc,0BAA0B,CAAA;AACxC,cAAc,uBAAuB,CAAA;AAErC,cAAc,8BAA8B,CAAA;AAE5C,YAAY,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -9,10 +9,12 @@ import {
|
|
|
9
9
|
GraphQLHooksProvider,
|
|
10
10
|
useQuery,
|
|
11
11
|
useMutation,
|
|
12
|
-
useSubscription
|
|
12
|
+
useSubscription,
|
|
13
|
+
useFragment
|
|
13
14
|
} from "./components/GraphQLHooksProvider.js";
|
|
14
15
|
export * from "./components/cell/CellCacheContext.js";
|
|
15
16
|
import { createCell } from "./components/cell/createCell.js";
|
|
17
|
+
import { createFragmentCell } from "./components/cell/createFragmentCell.js";
|
|
16
18
|
export * from "./graphql.js";
|
|
17
19
|
export * from "./components/RedwoodProvider.js";
|
|
18
20
|
export * from "./components/MetaTags.js";
|
|
@@ -28,7 +30,9 @@ export {
|
|
|
28
30
|
Helmet as Head,
|
|
29
31
|
Helmet,
|
|
30
32
|
createCell,
|
|
33
|
+
createFragmentCell,
|
|
31
34
|
useFetchConfig,
|
|
35
|
+
useFragment,
|
|
32
36
|
useMutation,
|
|
33
37
|
useQuery,
|
|
34
38
|
useSubscription
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cedarjs/web",
|
|
3
|
-
"version": "6.0.0-canary.
|
|
3
|
+
"version": "6.0.0-canary.2647",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/cedarjs/cedar.git",
|
|
@@ -140,7 +140,7 @@
|
|
|
140
140
|
},
|
|
141
141
|
"dependencies": {
|
|
142
142
|
"@apollo/client": "3.14.1",
|
|
143
|
-
"@cedarjs/server-store": "6.0.0-canary.
|
|
143
|
+
"@cedarjs/server-store": "6.0.0-canary.2647",
|
|
144
144
|
"@dr.pogodin/react-helmet": "2.0.4",
|
|
145
145
|
"@whatwg-node/fetch": "0.10.13",
|
|
146
146
|
"apollo-upload-client": "18.0.1",
|
|
@@ -159,9 +159,9 @@
|
|
|
159
159
|
"@babel/core": "^7.26.10",
|
|
160
160
|
"@babel/plugin-transform-runtime": "7.29.7",
|
|
161
161
|
"@babel/runtime": "7.29.7",
|
|
162
|
-
"@cedarjs/auth": "6.0.0-canary.
|
|
163
|
-
"@cedarjs/framework-tools": "6.0.0-canary.
|
|
164
|
-
"@cedarjs/internal": "6.0.0-canary.
|
|
162
|
+
"@cedarjs/auth": "6.0.0-canary.2647",
|
|
163
|
+
"@cedarjs/framework-tools": "6.0.0-canary.2647",
|
|
164
|
+
"@cedarjs/internal": "6.0.0-canary.2647",
|
|
165
165
|
"@rollup/plugin-babel": "6.1.0",
|
|
166
166
|
"@testing-library/dom": "10.4.1",
|
|
167
167
|
"@testing-library/jest-dom": "6.9.1",
|