Skip to content

Commit

Permalink
feat: add resolver prop to SliceZone for backwards compatibility wi…
Browse files Browse the repository at this point in the history
…th `next-slicezone` (#103)
  • Loading branch information
angeloashmore committed Nov 22, 2021
1 parent 27a306f commit cfdcfb1
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 3 deletions.
69 changes: 68 additions & 1 deletion src/SliceZone.tsx
Expand Up @@ -136,6 +136,44 @@ export const TODOSliceComponent = __PRODUCTION__
);
};

/**
* Arguments for a `<SliceZone>` `resolver` function.
*/
type SliceZoneResolverArgs<TSlice extends SliceLike = SliceLike> = {
/**
* The Slice to resolve to a React component..
*/
slice: TSlice;

/**
* The name of the Slice.
*/
sliceName: TSlice["slice_type"];

/**
* The index of the Slice in the Slice Zone.
*/
i: number;
};

/**
* A function that determines the rendered React component for each Slice in the
* Slice Zone. If a nullish value is returned, the component will fallback to
* the `components` or `defaultComponent` props to determine the rendered component.
*
* @deprecated Use the `components` prop instead.
*
* @param args - Arguments for the resolver function.
*
* @returns The React component to render for a Slice.
*/
export type SliceZoneResolver<
TSlice extends SliceLike = SliceLike,
TContext = unknown,
> = (
args: SliceZoneResolverArgs<TSlice>,
) => SliceComponentType<TSlice, TContext> | undefined | null;

/**
* React props for the `<SliceZone>` component.
*
Expand All @@ -156,6 +194,18 @@ export type SliceZoneProps<
*/
components?: SliceZoneComponents<TSlice, TContext>;

/**
* A function that determines the rendered React component for each Slice in
* the Slice Zone.
*
* @deprecated Use the `components` prop instead.
*
* @param args - Arguments for the resolver function.
*
* @returns The React component to render for a Slice.
*/
resolver?: SliceZoneResolver<TSlice, TContext>;

/**
* The React component rendered if a component mapping from the `components`
* prop cannot be found.
Expand All @@ -178,19 +228,36 @@ export type SliceZoneProps<
*
* @typeParam TSlice - The type(s) of a Slice in the Slice Zone.
* @typeParam TContext - Arbitrary data made available to all Slice components.
*
* @returns The Slice Zone's content as React components.
*
* @see Learn about Prismic Slices and Slice Zones {@link https://prismic.io/docs/core-concepts/slices}
*/
export const SliceZone = <TSlice extends SliceLike, TContext>({
slices = [],
components = {} as SliceZoneComponents<TSlice, TContext>,
resolver,
defaultComponent = TODOSliceComponent,
context = {} as TContext,
}: SliceZoneProps<TSlice, TContext>): JSX.Element => {
const renderedSlices = React.useMemo(() => {
return slices.map((slice, index) => {
const Comp = (components[slice.slice_type as keyof typeof components] ||
let Comp = (components[slice.slice_type as keyof typeof components] ||
defaultComponent) as SliceComponentType<TSlice, TContext>;

// TODO: Remove `resolver` in v3 in favor of `components`.
if (resolver) {
const resolvedComp = resolver({
slice,
sliceName: slice.slice_type,
i: index,
});

if (resolvedComp) {
Comp = resolvedComp;
}
}

const key = JSON.stringify(slice);

return (
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Expand Up @@ -25,6 +25,7 @@ export type {
SliceZoneComponents,
SliceZoneLike,
SliceZoneProps,
SliceZoneResolver,
} from "./SliceZone";

export { PrismicToolbar } from "./PrismicToolbar";
Expand Down
52 changes: 50 additions & 2 deletions test/SliceZone.test.tsx
Expand Up @@ -6,10 +6,18 @@ import * as sinon from "sinon";

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

import { SliceZone, TODOSliceComponent, SliceComponentProps } from "../src";
import {
SliceZone,
TODOSliceComponent,
SliceComponentProps,
SliceZoneResolver,
} from "../src";

type StringifySliceComponentProps = {
/** A unique identifier for the component to differentiate this component from other instances. */
/**
* A unique identifier for the component to differentiate this component from
* other instances.
*/
id: string;
} & SliceComponentProps;

Expand Down Expand Up @@ -162,3 +170,43 @@ test.skip("TODO component does not warn in production", () => {
// We cannot override the `process.env.NODE_ENV` inline replacement.
// As a result, we cannot test for production currently.
});

test("renders components from a resolver function for backwards compatibility with next-slicezone", async (t) => {
const slices = [{ slice_type: "foo" }, { slice_type: "bar" }] as const;

const resolver: SliceZoneResolver<typeof slices[number]> = ({
sliceName,
}) => {
switch (sliceName) {
case "foo": {
return (props) => <StringifySliceComponent id="foo" {...props} />;
}

case "bar": {
return (props) => <StringifySliceComponent id="bar" {...props} />;
}
}
};

const actual = renderJSON(<SliceZone slices={slices} resolver={resolver} />);
const expected = renderJSON(
<>
<StringifySliceComponent
id="foo"
slice={slices[0]}
index={0}
slices={slices}
context={{}}
/>
<StringifySliceComponent
id="bar"
slice={slices[1]}
index={1}
slices={slices}
context={{}}
/>
</>,
);

t.deepEqual(actual, expected);
});

0 comments on commit cfdcfb1

Please sign in to comment.