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: support untyped or partially typed Slices in <SliceZone> #154

Merged
merged 2 commits into from
Jun 8, 2022
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 36 additions & 22 deletions src/SliceZone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ export type SliceLike<SliceType extends string = string> =
*
* @typeParam TSlice - The type(s) of a Slice in the Slice Zone.
*/
export type SliceZoneLike<TSlice extends SliceLike> = readonly TSlice[];
export type SliceZoneLike<TSlice extends SliceLike = SliceLike> =
readonly TSlice[];

/**
* React props for a component rendering content from a Prismic Slice using the
Expand All @@ -70,7 +71,8 @@ export type SliceZoneLike<TSlice extends SliceLike> = readonly TSlice[];
* available to all Slice components.
*/
export type SliceComponentProps<
TSlice extends SliceLike = SliceLike,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
TSlice extends SliceLike = any,
TContext = unknown,
> = {
/**
Expand Down Expand Up @@ -105,14 +107,17 @@ export type SliceComponentProps<
* @typeParam TContext - Arbitrary data made available to all Slice components.
*/
export type SliceComponentType<
TSlice extends SliceLike = SliceLike,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
TSlice extends SliceLike = any,
TContext = unknown,
> = React.ComponentType<SliceComponentProps<TSlice, TContext>>;

/**
* A record of Slice types mapped to a React component. The component will be
* rendered for each instance of its Slice.
*
* @deprecated This type is no longer used by `@prismicio/react`. Prefer using
* `Record<string, SliceComponentType<any>>` instead.
* @typeParam TSlice - The type(s) of a Slice in the Slice Zone.
* @typeParam TContext - Arbitrary data made available to all Slice components.
*/
Expand Down Expand Up @@ -169,7 +174,10 @@ export const TODOSliceComponent = __PRODUCTION__
/**
* Arguments for a `<SliceZone>` `resolver` function.
*/
type SliceZoneResolverArgs<TSlice extends SliceLike = SliceLike> = {
type SliceZoneResolverArgs<
// eslint-disable-next-line @typescript-eslint/no-explicit-any
TSlice extends SliceLike = any,
> = {
/**
* The Slice to resolve to a React component.
*/
Expand Down Expand Up @@ -198,31 +206,35 @@ type SliceZoneResolverArgs<TSlice extends SliceLike = SliceLike> = {
* @returns The React component to render for a Slice.
*/
export type SliceZoneResolver<
TSlice extends SliceLike = SliceLike,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
TSlice extends SliceLike = any,
TContext = unknown,
> = (
args: SliceZoneResolverArgs<TSlice>,
) => SliceComponentType<TSlice, TContext> | undefined | null;
> = (args: SliceZoneResolverArgs<TSlice>) =>
| SliceComponentType<
// eslint-disable-next-line @typescript-eslint/no-explicit-any
any,
TContext
>
| undefined
| null;

/**
* React props for the `<SliceZone>` component.
*
* @typeParam TSlice - The type(s) of a Slice in the Slice Zone.
* @typeParam TContext - Arbitrary data made available to all Slice components.
*/
export type SliceZoneProps<
TSlice extends SliceLike = SliceLike,
TContext = unknown,
> = {
export type SliceZoneProps<TContext = unknown> = {
/**
* List of Slice data from the Slice Zone.
*/
slices?: SliceZoneLike<TSlice>;
slices?: SliceZoneLike;

/**
* A record mapping Slice types to React components.
*/
components?: SliceZoneComponents<TSlice, TContext>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
components?: Record<string, SliceComponentType<any>>;

/**
* A function that determines the rendered React component for each Slice in
Expand All @@ -234,13 +246,15 @@ export type SliceZoneProps<
*
* @returns The React component to render for a Slice.
*/
resolver?: SliceZoneResolver<TSlice, TContext>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
resolver?: SliceZoneResolver<any, TContext>;

/**
* The React component rendered if a component mapping from the `components`
* prop cannot be found.
*/
defaultComponent?: SliceComponentType<TSlice, TContext>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
defaultComponent?: SliceComponentType<any, TContext>;

/**
* Arbitrary data made available to all Slice components.
Expand All @@ -263,19 +277,19 @@ export type SliceZoneProps<
*
* @see Learn about Prismic Slices and Slice Zones {@link https://prismic.io/docs/core-concepts/slices}
*/
export const SliceZone = <TSlice extends SliceLike, TContext>({
export const SliceZone = <TContext,>({
angeloashmore marked this conversation as resolved.
Show resolved Hide resolved
slices = [],
components = {} as SliceZoneComponents<TSlice, TContext>,
components = {},
resolver,
defaultComponent = TODOSliceComponent,
context = {} as TContext,
}: SliceZoneProps<TSlice, TContext>): JSX.Element => {
}: SliceZoneProps<TContext>): JSX.Element => {
const renderedSlices = React.useMemo(() => {
return slices.map((slice, index) => {
const type = "slice_type" in slice ? slice.slice_type : slice.type;

let Comp = (components[type as keyof typeof components] ||
defaultComponent) as SliceComponentType<TSlice, TContext>;
let Comp =
components[type as keyof typeof components] || defaultComponent;

// TODO: Remove `resolver` in v3 in favor of `components`.
if (resolver) {
Expand All @@ -286,7 +300,7 @@ export const SliceZone = <TSlice extends SliceLike, TContext>({
});

if (resolvedComp) {
Comp = resolvedComp;
Comp = resolvedComp as typeof Comp;
}
}

Expand Down
2 changes: 1 addition & 1 deletion test/SliceZone.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ test("renders TODO component if component mapping is missing", (t) => {
const actual = renderJSON(
<SliceZone
slices={slices}
// @ts-expect-error - We are leaving `bar` out of the test on purpose.
components={{
foo: (props) => <StringifySliceComponent id="foo" {...props} />,
// NOTE: The `bar` component is purposely left out of this test.
// bar: (props) => <StringifySliceComponent id="bar" {...props} />,
}}
/>,
Expand Down