Skip to content

Type the widget registry per key, like UtilityTypeMap #264

Description

@sneridagh

Follow-up of #262 and #263. A debate first, before anyone implements it.

Today

WidgetPropsMap in @plone/types is augmented per app, like UtilityTypeMap. It holds one props type for every widget slot. @plone/cmsui declares props: FormWidgetProps<any>, so every registered widget is checked against the widget contract, whatever value it reads and writes.

That catches widgets that don't follow the contract (#262). It can't catch a widget registered for the wrong kind of field. For example, NumberWidget (FormWidgetProps<number | null>) registered for type: 'boolean' type-checks.

Idea

Type each slot by its key, the way UtilityTypeMap types each utility by its type. Keys the map doesn't list fall back to the app-wide props, the way UtilityMethodFor falls back.

declare module '@plone/types' {
  interface WidgetKeyPropsMap {
    type: {
      boolean: FormWidgetProps<boolean>;
      number: FormWidgetProps<number | null>;
      integer: FormWidgetProps<number | null>;
      array: FormWidgetProps<TermValue[] | null>;
    };
    widget: {
      date: FormWidgetProps<string | null>;
      datetime: FormWidgetProps<string | null>;
    };
    factory: {
      File: FormWidgetProps<FileValue | null>;
      Image: FormWidgetProps<FileValue | null>;
    };
  }
}

registerWidget({ key: 'type', definition: { boolean: NumberWidget } }) would then fail the type check. getWidget('boolean', 'type') could return a typed component.

Open questions

  • What does it buy? It only checks registration. The form picks the widget from the schema at runtime, so SchemaField still only sees FormWidgetProps<unknown>. Is checking registrations worth a map to maintain?
  • Where does the map live? In @plone/cmsui, next to the widgets, augmenting @plone/types like WidgetPropsMap does? Volto must stay unaffected, with no augmentation and any props.
  • Variance. onChange: (value: T) => void makes FormWidgetProps<T> invariant in T, so an add-on that overrides a slot must match the declared value type exactly. Is that what we want, or should a slot declare the value it reads and accept wider widgets?
  • Field values from the API. Some values come as terms ({ token, title }) or as another type than their token (allow_discussion). The declared value type has to say what the widget reads, not only what it writes.

Related: #243, #244, #262, #263.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions