Skip to content

Custom Widget type not assignable to CmsFieldBase - Typescript #6527

@chrisleyva

Description

@chrisleyva

Hello, team. Thank you for your hard work on this package.

Following the docs, I created a custom widget: videoEmbed.
But when I try to add it as a field in one of my collections of files, of course it's not an acceptable widget type according to netlify-cms-core's shipped typings.

To Reproduce

  1. Create custom widget & register with the CMS
  2. Create a config.ts file which specifies the CmsConfig object which is passed into CMS.init(config)
  3. In the CmsConfig object, add a File Collection with a single file with a single file/page.
  4. In that file's fields , add the custom widget (in my case videoEmbed)
  5. Notice the Typescript error (screenshots)

image

TLDR: Expected Behavior

I would expect a typescript-safe way to specify custom widgets in a typescript config file. Maybe there is?
Is there a "nice" and sanctioned way to add type support for custom widgets in cmsConfig.ts?

Workaround 1

Add as unknown as CmsField after each custom widget field like so:

{
  label: 'Video',
  name: 'videoEmbed',
  widget: 'videoEmbed',
} as unknown as CmsField,

This "works" as you can see in the screenshot:
image

Downside

The more I use custom widgets on more and more pages, the more I have to repeat the use of as unknown as CmsField. This doesn't feel right.

Of course, I could just create a single const and use that instead but I still don't think this is the right way to go:

const videoEmbedCmsField = {
  label: 'Video',
  name: 'videoEmbed',
  widget: 'videoEmbed',
} as unknown as CmsField;

// ...
{
  label: 'Intro',
  name: 'intro',
  widget: 'markdown',
},
videoEmbedCmsField,
{
  label: 'Show Gallery?',
  name: 'showGallery',
  widget: 'boolean',
},
// ...

Workaround 2

The other approach is to modify the CmsConfig type aaaaaaall the way until I reach the field: CmsField type so that I can add { widget: 'videoEmbed' } as an acceptable widget type.

This feels like the right way to do it could it have some unintended side-effects since I need to adjust the init function to make it happy CMS.init({ config: cmsConfig as CmsConfig });?

This screenshot shows how I got it to work (with a helper type Modify<T, R> = Omit<T, keyof R> & R).
image

This all works and it feels like the right approach. Is it?
image

Workaround 3

Modify type CmsField in netlify-cms-core/index.d.ts to include the new widget type:

  export interface CmsFieldVideoEmbed {
    widget: 'videoEmbed';
    default?: string;
  }
  
  export type CmsField = CmsFieldBase &
    (
      | CmsFieldBoolean
      | CmsFieldCode
      | CmsFieldColor
      | CmsFieldDateTime
      | CmsFieldFileOrImage
      | CmsFieldList
      | CmsFieldMap
      | CmsFieldMarkdown
      | CmsFieldNumber
      | CmsFieldObject
      | CmsFieldRelation
      | CmsFieldSelect
      | CmsFieldHidden
      | CmsFieldStringOrText
      | CmsFieldMeta
      | CmsFieldVideoEmbed // <-- my custom widget type
    );

Obviously, this is not the right way to do it but it allows my project to run locally. 🎉

Conclusion

Is there a proper way to do this? Thank you for your time.

Applicable Versions

  • netlify-cms-app 2.15.72
  • netlify-cms-core 2.55.2
  • Git provider: GitHub, BitBucket
  • OS: macOS Monterey 12.5
  • Browser version Chrome 103
  • Node.JS version: v16.10.0

CMS configuration

{
  backend: {
    name: 'git-gateway',
    branch: 'main',
    commit_messages: {
      create: 'docs: create {{collection}} “{{slug}}”',
      update: 'docs: update {{collection}} “{{slug}}”',
      delete: 'docs: delete {{collection}} “{{slug}}”',
      uploadMedia: 'docs: upload “{{path}}”',
      deleteMedia: 'docs: delete “{{path}}”',
      openAuthoring: 'docs: {{message}}',
    },
  },
  local_backend: true,
  media_folder: '/public/uploads',
  public_folder: '/uploads',
  collections: [
    {
      label: 'Pages',
      name: 'pages',
      files: [
        {
          label: 'Home',
          name: 'home',
          file: 'content/home.md',
          fields: [
            {
              label: 'Title',
              name: 'title',
              widget: 'string',
            },
            {
              label: 'Intro',
              name: 'intro',
              widget: 'markdown',
            },
            {
              label: 'Show Gallery?',
              name: 'showGallery',
              widget: 'boolean',
            },
            {
              label: 'Video',
              name: 'videoEmbed',
              widget: 'videoEmbed', // <-- This is the issue here
            },
            {
              label: 'Features',
              name: 'features',
              widget: 'list',
              fields: [
                { label: 'Title', name: 'title', widget: 'string' },
                {
                  label: 'Content',
                  name: 'content',
                  widget: 'markdown',
                  modes: ['rich_text', 'raw'],
                },
              ],
            },
            {
              label: 'Gallery Images',
              name: 'gallery',
              widget: 'list',
              label_singular: 'Image',
              add_to_top: true,
              fields: [{ label: 'Image', name: 'image', widget: 'image' }],
            },
          ],
        },
      ],
    },
  ],
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    type: bugcode to address defects in shipped code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions