Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d9cc445
commit 4e64de2
Showing
4 changed files
with
250 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import type * as prismic from "@prismicio/client"; | ||
|
||
import * as React from "react"; | ||
|
||
import { | ||
ClientHookReturnType, | ||
useStatefulPrismicClientMethod, | ||
} from "./useStatefulPrismicClientMethod"; | ||
|
||
export type UsePrismicPreviewResolverArgs = { | ||
/** | ||
* An optional `@prismicio/client` instance to override the Client provided to | ||
* `<PrismicProvider>` | ||
*/ | ||
client?: prismic.Client; | ||
|
||
/** | ||
* A function that maps a Prismic document to a URL within your app. | ||
*/ | ||
linkResolver?: Parameters< | ||
prismic.Client["resolvePreviewURL"] | ||
>[0]["linkResolver"]; | ||
|
||
/** | ||
* A fallback URL if the Link Resolver does not return a value. | ||
*/ | ||
defaultURL?: Parameters<prismic.Client["resolvePreviewURL"]>[0]["defaultURL"]; | ||
|
||
/** | ||
* The preview token (also known as a ref) that will be used to query preview | ||
* content from the Prismic repository. | ||
*/ | ||
previewToken?: Parameters< | ||
prismic.Client["resolvePreviewURL"] | ||
>[0]["previewToken"]; | ||
|
||
/** | ||
* The previewed document that will be used to determine the destination URL. | ||
*/ | ||
documentID?: Parameters<prismic.Client["resolvePreviewURL"]>[0]["documentID"]; | ||
|
||
/** | ||
* A function to automatically navigate to the resolved URL. If a function is | ||
* not provded, `usePreviewResolver` will not navigate to the URL. | ||
* | ||
* @param url - The resolved preview URL. | ||
*/ | ||
navigate?: (url: string) => unknown; | ||
}; | ||
|
||
/** | ||
* Resolve a preview session's URL. The resolved URL can be used to redirect to | ||
* the previewed document. | ||
* | ||
* If a `navigate` function is provided, the hook will automatically navigate to | ||
* the previewed document's URL. | ||
* | ||
* @param args - Arguments to configure how a URL is resolved. | ||
* | ||
* @returns A tuple containing the resolved URL and the hook's state. | ||
*/ | ||
export const usePrismicPreviewResolver = ( | ||
args: UsePrismicPreviewResolverArgs = {}, | ||
): ClientHookReturnType<string> => { | ||
const result = useStatefulPrismicClientMethod( | ||
"resolvePreviewURL", | ||
[ | ||
{ | ||
linkResolver: args.linkResolver, | ||
defaultURL: args.defaultURL || "/", | ||
previewToken: args.previewToken, | ||
documentID: args.documentID, | ||
}, | ||
], | ||
args.client, | ||
); | ||
|
||
React.useEffect(() => { | ||
if (result[0] && args.navigate) { | ||
args.navigate(result[0]); | ||
} | ||
}, [result[0], args.navigate]); | ||
|
||
return result; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
/* eslint-disable react/display-name */ | ||
/* eslint-disable react/prop-types */ | ||
|
||
import test from "ava"; | ||
import * as React from "react"; | ||
import * as msw from "msw"; | ||
import * as mswNode from "msw/node"; | ||
import * as sinon from "sinon"; | ||
import * as prismic from "@prismicio/client"; | ||
import { renderHook, cleanup } from "@testing-library/react-hooks"; | ||
|
||
import { createClient } from "./__testutils__/createClient"; | ||
import { createDocument } from "./__testutils__/createDocument"; | ||
import { createMockQueryHandler } from "./__testutils__/createMockQueryHandler"; | ||
import { createMockRepositoryHandler } from "./__testutils__/createMockRepositoryHandler"; | ||
import { createQueryResponse } from "./__testutils__/createQueryResponse"; | ||
import { createRef } from "./__testutils__/createRef"; | ||
import { createRepositoryResponse } from "./__testutils__/createRepositoryResponse"; | ||
import { md5 } from "./__testutils__/md5"; | ||
|
||
import { PrismicProvider, usePrismicPreviewResolver } from "../src"; | ||
|
||
const server = mswNode.setupServer(); | ||
test.before(() => server.listen({ onUnhandledRequest: "error" })); | ||
test.after(() => server.close()); | ||
|
||
// We must clean up after each test. We also must run each test serially to | ||
// ensure the clean up process only occurs between tests. | ||
test.afterEach(() => { | ||
cleanup(); | ||
}); | ||
|
||
const createWrapper = (client: prismic.Client): React.ComponentType => { | ||
return (props) => <PrismicProvider client={client} {...props} />; | ||
}; | ||
|
||
test.serial("returns a resolved preview URL", async (t) => { | ||
const client = createClient(t); | ||
const wrapper = createWrapper(client); | ||
|
||
const repositoryResponse = createRepositoryResponse(); | ||
const document = createDocument({ url: "preview-url" }); | ||
const queryResponsePages = [createQueryResponse([document])]; | ||
const previewToken = createRef(false).ref; | ||
|
||
server.use( | ||
createMockRepositoryHandler(t, repositoryResponse), | ||
createMockQueryHandler(t, queryResponsePages, { | ||
ref: previewToken, | ||
q: `[${prismic.predicate.at("document.id", document.id)}]`, | ||
}), | ||
); | ||
|
||
const { result, waitForValueToChange } = renderHook( | ||
() => | ||
usePrismicPreviewResolver({ | ||
documentID: document.id, | ||
previewToken, | ||
}), | ||
{ wrapper }, | ||
); | ||
|
||
await waitForValueToChange(() => result.current[1].state === "loaded"); | ||
|
||
t.deepEqual(result.current[0], document.url); | ||
}); | ||
|
||
test.serial("navigates if a navigate function is provided", async (t) => { | ||
const client = createClient(t); | ||
const wrapper = createWrapper(client); | ||
|
||
const repositoryResponse = createRepositoryResponse(); | ||
const document = createDocument({ url: "preview-url" }); | ||
const queryResponsePages = [createQueryResponse([document])]; | ||
const previewToken = createRef(false).ref; | ||
|
||
const navigate = sinon.stub(); | ||
|
||
server.use( | ||
createMockRepositoryHandler(t, repositoryResponse), | ||
createMockQueryHandler(t, queryResponsePages, { | ||
ref: previewToken, | ||
q: `[${prismic.predicate.at("document.id", document.id)}]`, | ||
}), | ||
); | ||
|
||
const { result, waitForValueToChange } = renderHook( | ||
() => | ||
usePrismicPreviewResolver({ | ||
documentID: document.id, | ||
previewToken, | ||
navigate, | ||
}), | ||
{ wrapper }, | ||
); | ||
|
||
await waitForValueToChange(() => result.current[1].state === "loaded"); | ||
|
||
t.true(navigate.calledWith(document.url)); | ||
}); | ||
|
||
test.serial("supports explicit client", async (t) => { | ||
const client = createClient(t); | ||
|
||
const repositoryResponse = createRepositoryResponse(); | ||
const document = createDocument({ url: "preview-url" }); | ||
const queryResponsePages = [createQueryResponse([document])]; | ||
const previewToken = createRef(false).ref; | ||
|
||
server.use( | ||
createMockRepositoryHandler(t, repositoryResponse), | ||
createMockQueryHandler(t, queryResponsePages, { | ||
ref: previewToken, | ||
q: `[${prismic.predicate.at("document.id", document.id)}]`, | ||
}), | ||
); | ||
|
||
const { result, waitForValueToChange } = renderHook(() => | ||
usePrismicPreviewResolver({ | ||
client, | ||
documentID: document.id, | ||
previewToken, | ||
}), | ||
); | ||
|
||
await waitForValueToChange(() => result.current[1].state === "loaded"); | ||
|
||
t.deepEqual(result.current[0], document.url); | ||
}); | ||
|
||
test.serial("returns failed state on error", async (t) => { | ||
const client = createClient(t); | ||
const wrapper = createWrapper(client); | ||
const repositoryResponse = { | ||
message: "invalid access token", | ||
oauth_initiate: "oauth_initiate", | ||
oauth_token: "oauth_token", | ||
}; | ||
|
||
server.use( | ||
msw.rest.get(prismic.getEndpoint(md5(t.title)), (_req, res, ctx) => { | ||
return res(ctx.status(403), ctx.json(repositoryResponse)); | ||
}), | ||
); | ||
|
||
const { result, waitForValueToChange } = renderHook( | ||
() => | ||
usePrismicPreviewResolver({ | ||
documentID: "documentID", | ||
previewToken: "previewToken", | ||
}), | ||
{ wrapper }, | ||
); | ||
|
||
await waitForValueToChange(() => result.current[1].state === "failed"); | ||
|
||
t.true(result.current[1].error instanceof prismic.ForbiddenError); | ||
t.is(result.current[0], undefined); | ||
}); |