From 349529e2fcd464ccfe1f050f18ad1e9aebed571a Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Thu, 26 May 2022 10:32:56 -1000 Subject: [PATCH] fix: throw when ``'s `field` or `document` prop value is missing required properties (#153) * fix: throw when ``'s `field` or `document` prop value is missing required properties * docs: update `missing-link-properties` message [skip ci] * docs: add `target` property recommendation [skip ci] * docs: reformat `missing-link-properties` [skip ci] --- messages/missing-link-properties.md | 65 +++++++++++++++++++++++++++++ src/PrismicLink.tsx | 32 ++++++++++++++ test/PrismicLink.test.tsx | 49 ++++++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 messages/missing-link-properties.md diff --git a/messages/missing-link-properties.md b/messages/missing-link-properties.md new file mode 100644 index 0000000..b28df67 --- /dev/null +++ b/messages/missing-link-properties.md @@ -0,0 +1,65 @@ +# Missing Link properties + +`` requires specific properties in the provided field or document to render properly. This requirement extends to [Link][link-fields], [Link to Media][link-fields], and [Content Relationship][link-fields] fields. + +If the required properties are missing, `` will not render the link. + +**Note**: When using Prismic's [Rest API][rest-api] (the default when using `@prismicio/client`), the required fields are automatically included. When using Prismic's [GraphQL API][graphql-api], you must include these fields in your query. + +## Required Properties + +### With the `field` prop + +The following properties are requried when using the `field` prop with a Link, Link to Media, or Content Relationship field: + +- `link_type` (or `_linkType` when using Prismic's [GraphQL API][graphql-api]) +- `id` +- `url` +- `uid` (only if your website uses a [Link Resolver][link-resolver] that uses a document's UID field) + +The following properties are not required, but are recommended: + +- `target` + +**Example**: + +```tsx +Click me +``` + +### With the `document` prop + +The following properties are requried when using the `document` prop with a Prismic document: + +- `id` +- `url` +- `uid` (only if your website uses a [Link Resolver][link-resolver] that uses a document's UID field) + +**Example**: + +```tsx +Click me +``` + +## GraphQL Example + +When using Prismic's [GraphQL API][graphql-api], Link fields must be queried with at least the following properties: + +```diff + { + page(uid: "home", lang: "en-us") { + linkField { ++ _linkType ++ id ++ url ++ uid # only required if your website uses a Link Resolver that uses a document's UID field. ++ target # not required, but recommended for automatic `target` handling + } + } + } +``` + +[link-fields]: https://prismic.io/docs/core-concepts/link-content-relationship +[link-resolver]: https://prismic.io/docs/core-concepts/link-resolver-route-resolver +[rest-api]: https://prismic.io/docs/technologies/rest-api-technical-reference +[graphql-api]: https://prismic.io/docs/technologies/graphql diff --git a/src/PrismicLink.tsx b/src/PrismicLink.tsx index 9750305..9bd7a50 100644 --- a/src/PrismicLink.tsx +++ b/src/PrismicLink.tsx @@ -3,6 +3,7 @@ import * as prismicH from "@prismicio/helpers"; import * as prismicT from "@prismicio/types"; import { __PRODUCTION__ } from "./lib/__PRODUCTION__"; +import { devMsg } from "./lib/devMsg"; import { isInternalURL } from "./lib/isInternalURL"; import { usePrismicContext } from "./usePrismicContext"; @@ -135,6 +136,37 @@ const _PrismicLink = < ): JSX.Element | null => { const context = usePrismicContext(); + if (!__PRODUCTION__) { + if ("field" in props && props.field) { + if ( + !("link_type" in props.field) || + !("url" in props.field || "id" in props.field) + ) { + console.error( + `[PrismicLink] This "field" prop value caused an error to be thrown.\n`, + props.field, + ); + throw new Error( + `[PrismicLink] The provided field is missing required properties to properly render a link. The link may not render. For more details, see ${devMsg( + "missing-link-properties", + )}`, + ); + } + } else if ("document" in props && props.document) { + if (!("url" in props.document || "id" in props.document)) { + console.error( + `[PrismicLink] This "document" prop value caused an error to be thrown.\n`, + props.document, + ); + throw new Error( + `[PrismicLink] The provided document is missing required properties to properly render a link. The link may not render. For more details, see ${devMsg( + "missing-link-properties", + )}`, + ); + } + } + } + const linkResolver = props.linkResolver || context.linkResolver; let href: string | null | undefined; diff --git a/test/PrismicLink.test.tsx b/test/PrismicLink.test.tsx index 74856cb..ff8e3f6 100644 --- a/test/PrismicLink.test.tsx +++ b/test/PrismicLink.test.tsx @@ -3,6 +3,7 @@ import * as prismicT from "@prismicio/types"; import * as prismicH from "@prismicio/helpers"; import * as prismicM from "@prismicio/mock"; import * as React from "react"; +import * as sinon from "sinon"; import { renderJSON } from "./__testutils__/renderJSON"; @@ -430,3 +431,51 @@ test("forwards ref to external component", (t) => { t.is(spanRef?.tagName, "span"); t.is(customComponentRef?.tagName, "input"); }); + +test.serial( + "throws error if properties are missing from a given field", + (t) => { + const field = {} as prismicT.FilledLinkToDocumentField; + + const consoleErrorStub = sinon.stub(console, "error"); + + t.throws( + () => { + renderJSON(); + }, + { message: /missing-link-properties/ }, + ); + + consoleErrorStub.restore(); + + t.true( + consoleErrorStub.calledWithMatch( + /this "field" prop value caused an error to be thrown./i, + ), + ); + }, +); + +test.serial( + "throws error if properties are missing from a given document", + (t) => { + const document = {} as prismicT.PrismicDocument; + + const consoleErrorStub = sinon.stub(console, "error"); + + t.throws( + () => { + renderJSON(); + }, + { message: /missing-link-properties/ }, + ); + + consoleErrorStub.restore(); + + t.true( + consoleErrorStub.calledWithMatch( + /this "document" prop value caused an error to be thrown./i, + ), + ); + }, +);