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

fix: warn when passing className to <PrismicRichText> or <PrismicText> #179

Merged
merged 1 commit into from
May 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 30 additions & 0 deletions messages/classname-is-not-a-valid-prop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# `className` is not a valid prop

`<PrismicRichText>` and `<PrismicText>` do not accept a `className` prop. These components render an array of React components and do not have a wrapping element.

To add a `className` as a wrapper around the output of `<PrismicRichText>` or `<PrismicText>`, add a wrapper element with the `className`.

```tsx
// ✅ Correct
<div className="prose">
<PrismicRichText field={doc.data.richTextField} />
</div>
```

```tsx
// ❌ Incorrect
<PrismicRichText field={doc.data.richTextField} className="prose" />
```

To add a `className` to a specific block type when using `<PrismicRichText>`, provide a custom component.

```tsx
<PrismicRichText
field={doc.data.richTextField}
components={{
heading1: ({ children }) => (
<h1 className="font-bold text-3xl">{children}</h1>
),
}}
/>
```
12 changes: 10 additions & 2 deletions src/PrismicText.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from "react";
import * as prismic from "@prismicio/client";

import { __PRODUCTION__ } from "./lib/__PRODUCTION__";
import { devMsg } from "./lib/devMsg";

/**
Expand Down Expand Up @@ -45,7 +44,16 @@ export type PrismicTextProps = {
* @see Learn about Rich Text fields {@link https://prismic.io/docs/core-concepts/rich-text-title}
*/
export const PrismicText = (props: PrismicTextProps): JSX.Element | null => {
if (!__PRODUCTION__) {
if (process.env.NODE_ENV === "development") {
if ("className" in props) {
console.warn(
`[PrismicText] className cannot be passed to <PrismicText> since it renders plain text without a wrapping component. For more details, see ${devMsg(
"classname-is-not-a-valid-prop",
)}.`,
props.field,
);
}

if (typeof props.field === "string") {
throw new Error(
`[PrismicText] The "field" prop only accepts a Rich Text or Title field's value but was provided a different type of field instead (e.g. a Key Text or Select field). You can resolve this error by rendering the field value inline without <PrismicText>. For more details, see ${devMsg(
Expand Down
13 changes: 13 additions & 0 deletions src/react-server/PrismicRichText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import { JSXFunctionSerializer, JSXMapSerializer } from "../types";
import { LinkProps, PrismicLink } from "./PrismicLink";
import { devMsg } from "../lib/devMsg";

/**
* Props for `<PrismicRichText>`.
Expand Down Expand Up @@ -222,8 +223,20 @@
components,
externalLinkComponent,
internalLinkComponent,
...restProps
}: PrismicRichTextProps<LinkResolverFunction>): JSX.Element | null {
return React.useMemo(() => {
if (process.env.NODE_ENV === "development") {
if ("className" in restProps) {
console.warn(
`[PrismicRichText] className cannot be passed to <PrismicRichText> since it renders an array without a wrapping component. For more details, see ${devMsg(
"classname-is-not-a-valid-prop",
)}.`,
field,
);
}
}

if (prismic.isFilled.richText(field)) {
const serializer = prismicR.composeSerializers(
typeof components === "object"
Expand Down Expand Up @@ -256,7 +269,7 @@
} else {
return fallback != null ? <>{fallback}</> : null;
}
}, [

Check warning on line 272 in src/react-server/PrismicRichText.tsx

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 16)

React Hook React.useMemo has a missing dependency: 'restProps'. Either include it or remove the dependency array
field,
internalLinkComponent,
externalLinkComponent,
Expand Down
39 changes: 36 additions & 3 deletions test/PrismicText.test.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { it, expect, vi } from "vitest";
import * as prismicT from "@prismicio/types";
import * as prismic from "@prismicio/client";

import { PrismicText } from "../src";

import { renderJSON } from "./__testutils__/renderJSON";

it("returns string when passed RichTextField", () => {
const field: prismicT.RichTextField = [
const field: prismic.RichTextField = [
{
type: prismicT.RichTextNodeType.heading1,
type: prismic.RichTextNodeType.heading1,
text: "Heading 1",
spans: [],
},
Expand Down Expand Up @@ -54,6 +54,10 @@ it("returns fallback when passed empty field", () => {
});

it("throws error if passed a string-based field (e.g. Key Text or Select)", () => {
// The error is only thrown in "development".
const originalNodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = "development";

// Used to supress logging the error in this it.
const consoleErrorStub = vi
.spyOn(console, "error")
Expand All @@ -69,4 +73,33 @@ it("throws error if passed a string-based field (e.g. Key Text or Select)", () =
}).throws(/prismictext-works-only-with-rich-text-and-title-fields/);

consoleErrorStub.mockRestore();
process.env.NODE_ENV = originalNodeEnv;
});

it("warns if a className prop is provided", async () => {
const field: prismic.RichTextField = [];

// The warning only logs in "development".
const originalNodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = "development";

const consoleWarnSpy = vi
.spyOn(console, "warn")
.mockImplementation(() => void 0);

renderJSON(
<PrismicText
field={field}
// @ts-expect-error - We are purposely passing an invalid prop to trigger the console wraning.
className="foo"
/>,
);

expect(consoleWarnSpy).toHaveBeenCalledWith(
expect.stringMatching(/classname-is-not-a-valid-prop/),
field,
);

consoleWarnSpy.mockRestore();
process.env.NODE_ENV = originalNodeEnv;
});