@prismicio/react 2.1.1 → 2.2.0

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.
@@ -10,7 +10,10 @@ import { JSXFunctionSerializer, JSXMapSerializer } from "./types";
10
10
  * React context value containing shared configuration for `@prismicio/react`
11
11
  * components and hooks.
12
12
  */
13
- export type PrismicContextValue = {
13
+ export type PrismicContextValue<
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ LinkResolverFunction extends prismicH.LinkResolverFunction<any> = prismicH.LinkResolverFunction,
16
+ > = {
14
17
  /**
15
18
  * A `@prismicio/client` instance used to fetch content from a Prismic
16
19
  * repository. This is used by `@prismicio/react` hooks, such as
@@ -26,7 +29,7 @@ export type PrismicContextValue = {
26
29
  * repository's content, a Link Resolver does not need to be provided.
27
30
  * @see Learn about Link Resolvers and Route Resolvers {@link https://prismic.io/docs/core-concepts/link-resolver-route-resolver}
28
31
  */
29
- linkResolver?: prismicH.LinkResolverFunction;
32
+ linkResolver?: LinkResolverFunction;
30
33
 
31
34
  /**
32
35
  * A map or function that maps a Rich Text block to a React component.
@@ -81,7 +84,9 @@ export const PrismicContext = React.createContext<PrismicContextValue>({});
81
84
  /**
82
85
  * Props for `<PrismicProvider>`.
83
86
  */
84
- export type PrismicProviderProps = PrismicContextValue;
87
+ export type PrismicProviderProps<
88
+ LinkResolverFunction extends prismicH.LinkResolverFunction,
89
+ > = PrismicContextValue<LinkResolverFunction>;
85
90
 
86
91
  /**
87
92
  * React context provider to share configuration for `@prismicio/react`
@@ -89,15 +94,18 @@ export type PrismicProviderProps = PrismicContextValue;
89
94
  *
90
95
  * @returns A React context provider with shared configuration.
91
96
  */
92
- export const PrismicProvider = ({
97
+ export const PrismicProvider = <
98
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
99
+ LinkResolverFunction extends prismicH.LinkResolverFunction<any>,
100
+ >({
93
101
  client,
94
102
  linkResolver,
95
103
  richTextComponents,
96
104
  internalLinkComponent,
97
105
  externalLinkComponent,
98
106
  children,
99
- }: PrismicProviderProps): JSX.Element => {
100
- const value = React.useMemo<PrismicContextValue>(
107
+ }: PrismicProviderProps<LinkResolverFunction>): JSX.Element => {
108
+ const value = React.useMemo<PrismicContextValue<LinkResolverFunction>>(
101
109
  () => ({
102
110
  client,
103
111
  linkResolver,
@@ -13,7 +13,10 @@ import { usePrismicContext } from "./usePrismicContext";
13
13
  /**
14
14
  * Props for `<PrismicRichText>`.
15
15
  */
16
- export type PrismicRichTextProps = {
16
+ export type PrismicRichTextProps<
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
+ LinkResolverFunction extends prismicH.LinkResolverFunction<any> = prismicH.LinkResolverFunction,
19
+ > = {
17
20
  /**
18
21
  * The Prismic Rich Text field to render.
19
22
  */
@@ -27,7 +30,7 @@ export type PrismicRichTextProps = {
27
30
  * repository's content, a Link Resolver does not need to be provided.
28
31
  * @see Learn about Link Resolvers and Route Resolvers {@link https://prismic.io/docs/core-concepts/link-resolver-route-resolver}
29
32
  */
30
- linkResolver?: PrismicLinkProps["linkResolver"];
33
+ linkResolver?: LinkResolverFunction;
31
34
 
32
35
  /**
33
36
  * A function that maps a Rich Text block to a React component.
@@ -58,7 +61,7 @@ export type PrismicRichTextProps = {
58
61
  * (type, node, content, children) => {
59
62
  * switch (type) {
60
63
  * case "heading1": {
61
- * return <Heading>{chidlren}</Heading>;
64
+ * return <Heading>{children}</Heading>;
62
65
  * }
63
66
  * }
64
67
  * };
@@ -79,10 +82,19 @@ export type PrismicRichTextProps = {
79
82
  * @defaultValue `<a>`
80
83
  */
81
84
  externalLinkComponent?: PrismicLinkProps["externalComponent"];
85
+
86
+ /**
87
+ * The value to be rendered when the field is empty. If a fallback is not
88
+ * given, `null` will be rendered.
89
+ */
90
+ fallback?: React.ReactNode;
82
91
  };
83
92
 
84
- type CreateDefaultSerializerArgs = {
85
- linkResolver: prismicH.LinkResolverFunction<string> | undefined;
93
+ type CreateDefaultSerializerArgs<
94
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
95
+ LinkResolverFunction extends prismicH.LinkResolverFunction<any> = prismicH.LinkResolverFunction,
96
+ > = {
97
+ linkResolver: LinkResolverFunction | undefined;
86
98
  internalLinkComponent: PrismicRichTextProps["internalLinkComponent"];
87
99
  externalLinkComponent: PrismicRichTextProps["externalLinkComponent"];
88
100
  };
@@ -214,8 +226,11 @@ const createDefaultSerializer = (
214
226
  * @see Learn about Rich Text fields {@link https://prismic.io/docs/core-concepts/rich-text-title}
215
227
  * @see Learn about Rich Text serializers {@link https://prismic.io/docs/core-concepts/html-serializer}
216
228
  */
217
- export const PrismicRichText = (
218
- props: PrismicRichTextProps,
229
+ export const PrismicRichText = <
230
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
231
+ LinkResolverFunction extends prismicH.LinkResolverFunction<any> = prismicH.LinkResolverFunction,
232
+ >(
233
+ props: PrismicRichTextProps<LinkResolverFunction>,
219
234
  ): JSX.Element | null => {
220
235
  const context = usePrismicContext();
221
236
 
@@ -255,7 +270,7 @@ export const PrismicRichText = (
255
270
 
256
271
  return <>{serialized}</>;
257
272
  } else {
258
- return null;
273
+ return props.fallback != null ? <>{props.fallback}</> : null;
259
274
  }
260
275
  }, [
261
276
  props.field,
@@ -263,6 +278,7 @@ export const PrismicRichText = (
263
278
  props.externalLinkComponent,
264
279
  props.components,
265
280
  props.linkResolver,
281
+ props.fallback,
266
282
  context.linkResolver,
267
283
  context.richTextComponents,
268
284
  ]);
package/src/SliceZone.tsx CHANGED
@@ -4,24 +4,57 @@ import * as prismicT from "@prismicio/types";
4
4
  import { __PRODUCTION__ } from "./lib/__PRODUCTION__";
5
5
  import { pascalCase, PascalCase } from "./lib/pascalCase";
6
6
 
7
+ /**
8
+ * Returns the type of a `SliceLike` type.
9
+ *
10
+ * @typeParam Slice - The Slice from which the type will be extracted.
11
+ */
12
+ type ExtractSliceType<Slice extends SliceLike> = Slice extends SliceLikeRestV2
13
+ ? Slice["slice_type"]
14
+ : Slice extends SliceLikeGraphQL
15
+ ? Slice["type"]
16
+ : never;
17
+
18
+ /**
19
+ * The minimum required properties to represent a Prismic Slice from the Prismic
20
+ * Rest API V2 for the `<SliceZone>` component.
21
+ *
22
+ * If using Prismic's Rest API V2, use the `Slice` export from
23
+ * `@prismicio/types` for a full interface.
24
+ *
25
+ * @typeParam SliceType - Type name of the Slice.
26
+ */
27
+ export type SliceLikeRestV2<SliceType extends string = string> = {
28
+ slice_type: prismicT.Slice<SliceType>["slice_type"];
29
+ };
30
+
31
+ /**
32
+ * The minimum required properties to represent a Prismic Slice from the Prismic
33
+ * GraphQL API for the `<SliceZone>` component.
34
+ *
35
+ * @typeParam SliceType - Type name of the Slice.
36
+ */
37
+ export type SliceLikeGraphQL<SliceType extends string = string> = {
38
+ type: prismicT.Slice<SliceType>["slice_type"];
39
+ };
40
+
7
41
  /**
8
42
  * The minimum required properties to represent a Prismic Slice for the
9
43
  * `<SliceZone>` component.
10
44
  *
11
- * If using Prismic's REST API, use the `Slice` export from `@prismicio/types`
12
- * for a full interface.
45
+ * If using Prismic's Rest API V2, use the `Slice` export from
46
+ * `@prismicio/types` for a full interface.
13
47
  *
14
48
  * @typeParam SliceType - Type name of the Slice.
15
49
  */
16
- export type SliceLike<SliceType extends string = string> = Pick<
17
- prismicT.Slice<SliceType>,
18
- "slice_type"
19
- >;
50
+ export type SliceLike<SliceType extends string = string> =
51
+ | SliceLikeRestV2<SliceType>
52
+ | SliceLikeGraphQL<SliceType>;
20
53
 
21
54
  /**
22
55
  * A looser version of the `SliceZone` type from `@prismicio/types` using `SliceLike`.
23
56
  *
24
- * If using Prismic's REST API, use the `SliceZone` export from
57
+ * If using Prismic's Rest API V2, use the `SliceZone` export from
25
58
  * `@prismicio/types` for the full type.
26
59
  *
27
60
  * @typeParam TSlice - The type(s) of a Slice in the Slice Zone.
@@ -97,10 +130,7 @@ export type SliceZoneComponents<
97
130
  // signals to future developers that it is a placeholder and should be
98
131
  // implemented.
99
132
  {
100
- [SliceType in keyof Record<
101
- TSlice["slice_type"],
102
- never
103
- >]: SliceComponentType<
133
+ [SliceType in ExtractSliceType<TSlice>]: SliceComponentType<
104
134
  Extract<TSlice, SliceLike<SliceType>> extends never
105
135
  ? SliceLike
106
136
  : Extract<TSlice, SliceLike<SliceType>>,
@@ -119,19 +149,18 @@ export const TODOSliceComponent = __PRODUCTION__
119
149
  : <TSlice extends SliceLike, TContext>({
120
150
  slice,
121
151
  }: SliceComponentProps<TSlice, TContext>): JSX.Element | null => {
152
+ const type = "slice_type" in slice ? slice.slice_type : slice.type;
153
+
122
154
  React.useEffect(() => {
123
155
  console.warn(
124
- `[SliceZone] Could not find a component for Slice type "${slice.slice_type}"`,
156
+ `[SliceZone] Could not find a component for Slice type "${type}"`,
125
157
  slice,
126
158
  );
127
- }, [slice]);
159
+ }, [slice, type]);
128
160
 
129
161
  return (
130
- <section
131
- data-slice-zone-todo-component=""
132
- data-slice-type={slice.slice_type}
133
- >
134
- Could not find a component for Slice type &ldquo;{slice.slice_type}
162
+ <section data-slice-zone-todo-component="" data-slice-type={type}>
163
+ Could not find a component for Slice type &ldquo;{type}
135
164
  &rdquo;
136
165
  </section>
137
166
  );
@@ -149,7 +178,7 @@ type SliceZoneResolverArgs<TSlice extends SliceLike = SliceLike> = {
149
178
  /**
150
179
  * The name of the Slice.
151
180
  */
152
- sliceName: PascalCase<TSlice["slice_type"]>;
181
+ sliceName: PascalCase<ExtractSliceType<TSlice>>;
153
182
 
154
183
  /**
155
184
  * The index of the Slice in the Slice Zone.
@@ -243,14 +272,16 @@ export const SliceZone = <TSlice extends SliceLike, TContext>({
243
272
  }: SliceZoneProps<TSlice, TContext>): JSX.Element => {
244
273
  const renderedSlices = React.useMemo(() => {
245
274
  return slices.map((slice, index) => {
246
- let Comp = (components[slice.slice_type as keyof typeof components] ||
275
+ const type = "slice_type" in slice ? slice.slice_type : slice.type;
276
+
277
+ let Comp = (components[type as keyof typeof components] ||
247
278
  defaultComponent) as SliceComponentType<TSlice, TContext>;
248
279
 
249
280
  // TODO: Remove `resolver` in v3 in favor of `components`.
250
281
  if (resolver) {
251
282
  const resolvedComp = resolver({
252
283
  slice,
253
- sliceName: pascalCase(slice.slice_type),
284
+ sliceName: pascalCase(type),
254
285
  i: index,
255
286
  });
256
287
 
@@ -34,7 +34,7 @@ type InnerCamelCaseStringArray<
34
34
 
35
35
  type CamelCaseStringArray<Parts extends readonly string[]> = Parts extends [
36
36
  `${infer FirstPart}`,
37
- ...infer RemainingParts
37
+ ...infer RemainingParts,
38
38
  ]
39
39
  ? Uncapitalize<`${FirstPart}${InnerCamelCaseStringArray<
40
40
  RemainingParts,