Skip to content

Commit

Permalink
feat: add *BySomeTags and *ByEveryTag hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
angeloashmore committed Nov 1, 2021
1 parent c02a557 commit 7faa6b3
Show file tree
Hide file tree
Showing 6 changed files with 360 additions and 24 deletions.
66 changes: 57 additions & 9 deletions src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ export const useAllPrismicDocumentsByTag = <

/**
* A hook that queries documents from the Prismic repository with specific tags.
* A document must be tagged with at least one of the queried tags to be included.
*
* @remarks
* An additional `@prismicio/client` instance can be provided at `params.client`.
Expand All @@ -329,18 +330,65 @@ export const useAllPrismicDocumentsByTag = <
* @returns The composable payload {@link ClientHookReturnType}
* @see Underlying `@prismicio/client` method {@link proto.getByTags}
*/
export const usePrismicDocumentsByTags = <
export const usePrismicDocumentsBySomeTags = <
TDocument extends prismicT.PrismicDocument,
>(
...args: [
tag: ClientMethodParameters<"getByTags">[0],
params?: ClientMethodParameters<"getByTags">[1] & HookOnlyParameters,
tag: ClientMethodParameters<"getBySomeTags">[0],
params?: ClientMethodParameters<"getBySomeTags">[1] & HookOnlyParameters,
]
): ClientHookReturnType<prismicT.Query<TDocument>> =>
useStatefulPrismicClientMethod(proto.getByTags, args);
useStatefulPrismicClientMethod(proto.getBySomeTags, args);

/**
* A hook that queries all documents from the Prismic repository with specific tags.
* A hook that queries all documents from the Prismic repository with specific
* tags. A document must be tagged with at least one of the queried tags to be included.
*
* @remarks
* An additional `@prismicio/client` instance can be provided at `params.client`.
* @typeParam TDocument - Type of Prismic documents returned
* @param tags - A list of tags that must be included on a document
* @param params - Parameters to filter and sort results
*
* @returns The composable payload {@link ClientHookReturnType}
* @see Underlying `@prismicio/client` method {@link proto.getAllByTags}
*/
export const useAllPrismicDocumentsBySomeTags = <
TDocument extends prismicT.PrismicDocument,
>(
...args: [
tag: ClientMethodParameters<"getAllBySomeTags">[0],
params?: ClientMethodParameters<"getAllBySomeTags">[1] & HookOnlyParameters,
]
): ClientHookReturnType<TDocument[]> =>
useStatefulPrismicClientMethod(proto.getAllBySomeTags, args);

/**
* A hook that queries documents from the Prismic repository with specific tags.
* A document must be tagged with all of the queried tags to be included.
*
* @remarks
* An additional `@prismicio/client` instance can be provided at `params.client`.
* @typeParam TDocument - Type of Prismic documents returned
* @param tags - A list of tags that must be included on a document
* @param params - Parameters to filter, sort, and paginate results
*
* @returns The composable payload {@link ClientHookReturnType}
* @see Underlying `@prismicio/client` method {@link proto.getByTags}
*/
export const usePrismicDocumentsByEveryTag = <
TDocument extends prismicT.PrismicDocument,
>(
...args: [
tag: ClientMethodParameters<"getByEveryTag">[0],
params?: ClientMethodParameters<"getByEveryTag">[1] & HookOnlyParameters,
]
): ClientHookReturnType<prismicT.Query<TDocument>> =>
useStatefulPrismicClientMethod(proto.getByEveryTag, args);

/**
* A hook that queries all documents from the Prismic repository with specific
* tags. A document must be tagged with all of the queried tags to be included.
*
* @remarks
* An additional `@prismicio/client` instance can be provided at `params.client`.
Expand All @@ -351,12 +399,12 @@ export const usePrismicDocumentsByTags = <
* @returns The composable payload {@link ClientHookReturnType}
* @see Underlying `@prismicio/client` method {@link proto.getAllByTags}
*/
export const useAllPrismicDocumentsByTags = <
export const useAllPrismicDocumentsByEveryTag = <
TDocument extends prismicT.PrismicDocument,
>(
...args: [
tag: ClientMethodParameters<"getAllByTags">[0],
params?: ClientMethodParameters<"getAllByTags">[1] & HookOnlyParameters,
tag: ClientMethodParameters<"getAllByEveryTag">[0],
params?: ClientMethodParameters<"getAllByEveryTag">[1] & HookOnlyParameters,
]
): ClientHookReturnType<TDocument[]> =>
useStatefulPrismicClientMethod(proto.getAllByTags, args);
useStatefulPrismicClientMethod(proto.getAllByEveryTag, args);
6 changes: 4 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,20 @@ export type { PrismicToolbarProps } from "./PrismicToolbar";

export {
useAllPrismicDocuments,
useAllPrismicDocumentsByEveryTag,
useAllPrismicDocumentsByIDs,
useAllPrismicDocumentsBySomeTags,
useAllPrismicDocumentsByTag,
useAllPrismicDocumentsByTags,
useAllPrismicDocumentsByType,
useAllPrismicDocumentsByUIDs,
useFirstPrismicDocument,
usePrismicDocumentByID,
usePrismicDocumentByUID,
usePrismicDocuments,
usePrismicDocumentsByEveryTag,
usePrismicDocumentsByIDs,
usePrismicDocumentsBySomeTags,
usePrismicDocumentsByTag,
usePrismicDocumentsByTags,
usePrismicDocumentsByType,
usePrismicDocumentsByUIDs,
useSinglePrismicDocument,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { createRepositoryResponse } from "./__testutils__/createRepositoryRespon
import { getMasterRef } from "./__testutils__/getMasterRef";
import { md5 } from "./__testutils__/md5";

import { PrismicProvider, useAllPrismicDocumentsByTags } from "../src";
import { PrismicProvider, useAllPrismicDocumentsByEveryTag } from "../src";

const server = mswNode.setupServer();
test.before(() => server.listen({ onUnhandledRequest: "error" }));
Expand Down Expand Up @@ -51,7 +51,7 @@ test.serial("returns documents with matching IDs", async (t) => {
);

const { result, waitForValueToChange } = renderHook(
() => useAllPrismicDocumentsByTags(tags),
() => useAllPrismicDocumentsByEveryTag(tags),
{ wrapper },
);

Expand Down Expand Up @@ -83,7 +83,7 @@ test.serial("supports params", async (t) => {
);

const { result, waitForValueToChange } = renderHook(
() => useAllPrismicDocumentsByTags(tags, params),
() => useAllPrismicDocumentsByEveryTag(tags, params),
{ wrapper },
);

Expand All @@ -110,7 +110,7 @@ test.serial("supports explicit client", async (t) => {
);

const { result, waitForValueToChange } = renderHook(() =>
useAllPrismicDocumentsByTags(tags, { client }),
useAllPrismicDocumentsByEveryTag(tags, { client }),
);

await waitForValueToChange(() => result.current[1].state === "loaded");
Expand All @@ -134,7 +134,7 @@ test.serial("returns failed state on error", async (t) => {
);

const { result, waitForValueToChange } = renderHook(
() => useAllPrismicDocumentsByTags(["tag", "tag2"]),
() => useAllPrismicDocumentsByEveryTag(["tag", "tag2"]),
{ wrapper },
);

Expand Down
145 changes: 145 additions & 0 deletions test/useAllPrismicDocumentsBySomeTags.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/* 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 prismic from "@prismicio/client";
import { renderHook, cleanup } from "@testing-library/react-hooks";

import { createClient } from "./__testutils__/createClient";
import { createMockQueryHandler } from "./__testutils__/createMockQueryHandler";
import { createMockRepositoryHandler } from "./__testutils__/createMockRepositoryHandler";
import { createQueryResponsePages } from "./__testutils__/createQueryResponsePages";
import { createRepositoryResponse } from "./__testutils__/createRepositoryResponse";
import { getMasterRef } from "./__testutils__/getMasterRef";
import { md5 } from "./__testutils__/md5";

import { PrismicProvider, useAllPrismicDocumentsBySomeTags } 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 documents with matching IDs", async (t) => {
const client = createClient(t);
const wrapper = createWrapper(client);
const repositoryResponse = createRepositoryResponse();
const queryResponsePages = createQueryResponsePages();
const documents = queryResponsePages.flatMap((page) => page.results);
const tags = documents[0].tags;
const ref = getMasterRef(repositoryResponse);

server.use(
createMockRepositoryHandler(t, repositoryResponse),
createMockQueryHandler(t, queryResponsePages, {
ref,
q: `[${prismic.predicate.any("document.tags", tags)}]`,
pageSize: 100,
}),
);

const { result, waitForValueToChange } = renderHook(
() => useAllPrismicDocumentsBySomeTags(tags),
{ wrapper },
);

await waitForValueToChange(() => result.current[1].state === "loaded");

t.deepEqual(result.current[0], documents);
});

test.serial("supports params", async (t) => {
const client = createClient(t);
const wrapper = createWrapper(client);
const repositoryResponse = createRepositoryResponse();
const queryResponsePages = createQueryResponsePages();
const documents = queryResponsePages.flatMap((page) => page.results);
const tags = documents[0].tags;
const ref = getMasterRef(repositoryResponse);

const params = {
pageSize: 2,
};

server.use(
createMockRepositoryHandler(t, repositoryResponse),
createMockQueryHandler(t, queryResponsePages, {
ref,
q: `[${prismic.predicate.any("document.tags", tags)}]`,
pageSize: params.pageSize.toString(),
}),
);

const { result, waitForValueToChange } = renderHook(
() => useAllPrismicDocumentsBySomeTags(tags, params),
{ wrapper },
);

await waitForValueToChange(() => result.current[1].state === "loaded");

t.deepEqual(result.current[0], documents);
});

test.serial("supports explicit client", async (t) => {
const client = createClient(t);
const repositoryResponse = createRepositoryResponse();
const queryResponsePages = createQueryResponsePages();
const documents = queryResponsePages.flatMap((page) => page.results);
const tags = documents[0].tags;
const ref = getMasterRef(repositoryResponse);

server.use(
createMockRepositoryHandler(t, repositoryResponse),
createMockQueryHandler(t, queryResponsePages, {
ref,
q: `[${prismic.predicate.any("document.tags", tags)}]`,
pageSize: 100,
}),
);

const { result, waitForValueToChange } = renderHook(() =>
useAllPrismicDocumentsBySomeTags(tags, { client }),
);

await waitForValueToChange(() => result.current[1].state === "loaded");

t.deepEqual(result.current[0], documents);
});

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(
() => useAllPrismicDocumentsBySomeTags(["tag", "tag2"]),
{ wrapper },
);

await waitForValueToChange(() => result.current[1].state === "failed");

t.true(result.current[1].error instanceof prismic.ForbiddenError);
t.is(result.current[0], undefined);
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { createRepositoryResponse } from "./__testutils__/createRepositoryRespon
import { getMasterRef } from "./__testutils__/getMasterRef";
import { md5 } from "./__testutils__/md5";

import { PrismicProvider, usePrismicDocumentsByTags } from "../src";
import { PrismicProvider, usePrismicDocumentsByEveryTag } from "../src";

const server = mswNode.setupServer();
test.before(() => server.listen({ onUnhandledRequest: "error" }));
Expand Down Expand Up @@ -50,7 +50,7 @@ test.serial("returns documents with matching types", async (t) => {
);

const { result, waitForValueToChange } = renderHook(
() => usePrismicDocumentsByTags(tags),
() => usePrismicDocumentsByEveryTag(tags),
{ wrapper },
);

Expand Down Expand Up @@ -82,7 +82,7 @@ test.serial("supports params", async (t) => {
);

const { result, waitForValueToChange } = renderHook(
() => usePrismicDocumentsByTags(tags, params),
() => usePrismicDocumentsByEveryTag(tags, params),
{ wrapper },
);

Expand All @@ -108,7 +108,7 @@ test.serial("supports explicit client", async (t) => {
);

const { result, waitForValueToChange } = renderHook(() =>
usePrismicDocumentsByTags(tags, { client }),
usePrismicDocumentsByEveryTag(tags, { client }),
);

await waitForValueToChange(() => result.current[1].state === "loaded");
Expand All @@ -132,13 +132,11 @@ test.serial("returns failed state on error", async (t) => {
);

const { result, waitForValueToChange } = renderHook(
() => usePrismicDocumentsByTags(["tag", "tag2"]),
() => usePrismicDocumentsByEveryTag(["tag", "tag2"]),
{ wrapper },
);

await waitForValueToChange(
() => result.current[1].state === "failed",
);
await waitForValueToChange(() => result.current[1].state === "failed");

t.true(result.current[1].error instanceof prismic.ForbiddenError);
t.is(result.current[0], undefined);
Expand Down
Loading

0 comments on commit 7faa6b3

Please sign in to comment.