Skip to content

Ability to make allow and disallow type safe #1099

Description

@fkhadra

Description

While using DropZone, it was possible to override the component interface to make both allow and disallow type safe. In practice, we created a thin wrapper around DropZone, this is how it looks like in our case:

import { DropZone as PuckDropZone } from '@measured/puck';
import { styled } from '@mui/material';

import type { WidgetKeyValue } from '../registry';

interface DropZoneProps extends React.ComponentPropsWithRef<typeof PuckDropZone> {
  /**
   * Only allows the specified widgets to be added.
   *
   * By default, any widgets are allowed.
   */
  allow?: WidgetKeyValue[];

  /**
   * Prevents the specified widgets to be added.
   *
   * By default, no widgets are disallowed
   */
  disallow?: WidgetKeyValue[];
}

// Wrapper to make `allow` and `disallow` type safe and add sxProps support as well
export const DropZone = styled(PuckDropZone)<DropZoneProps>();

It prevented engineers from providing wrong keys and saved time during development and debugging.

Ideally, we should be able to define the same behavior with the new slot API.

Considerations

A possible workaround alternative is to rely on a constant like Widget.KeyOfMyComponent but it doesn't guarantee type safety during development and can fails at runtime, which we want to avoid.

Proposals

Proposal

Add a new generic parameter is added to the PuckComponent interface. The second generic parameter would allow to specify the supported keys for both allow and disallow. It would also require to update the interface for SlotComponent and DropZoneProps in order to use the new generic parameter.

type DropZoneProps<T extends string = string> = {
    zone: string;
    allow?: T[];
    disallow?: T[];
    style?: CSSProperties;
    minEmptyHeight?: number;
    className?: string;
    collisionAxis?: DragAxis;
};

type SlotComponent<ComponentKeys = string> = (props?: Omit<DropZoneProps<ComponentKeys>, "zone">) => ReactNode;

type PuckComponent<Props, ComponentKeys = string> = (props: WithId<WithPuckProps<{
    [K in keyof Props]: WithDeepSlots<Props[K], SlotComponent<ComponentKeys> >;
}>>) => JSX.Element;


// usage

interface Props {
  Slot: Slot;
}

const MyComponent: PuckComponent<Props, "foo" | "bar"> = ({ Slot }) => {
  return <Slot 
               allow={["foo"]}  // OK + autocomplete
               disallow={["xxx"]} // won't compile
             />
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions