decaporg / decaporg/decap-cms

Custom Widget type not assignable to CmsFieldBase - Typescript

Open
#6,527 0 comments 0 reactions 0 assignees View on GitHub
type: bug
Dominant language
JavaScript
Stars
19.4k
Forks
3.1k
Avg merge
1d 14h
Merged PRs (30d)
9

Description

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](https://user-images.githubusercontent.com/1437143/182423276-9976abff-6303-41bf-ac2b-e8754c791edd.png)

## 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:
```typescript
{
label: 'Video',
name: 'videoEmbed',
widget: 'videoEmbed',
} as unknown as CmsField,
```
This "works" as you can see in the screenshot:
![image](https://user-images.githubusercontent.com/1437143/182423534-8d84ce2f-609e-47de-9e7a-5664ef41e18e.png)

### 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:
```typescript
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 = Omit & R`).
![image](https://user-images.githubusercontent.com/1437143/182432837-715a774f-7fb9-4141-8026-7f2ccd797729.png)

This all works and it _**feels**_ like the right approach. Is it?
![image](https://user-images.githubusercontent.com/1437143/182432979-8a3cd200-6a20-4e7d-a5e2-8bbe2db253f7.png)

## Workaround 3
Modify `type CmsField` in `netlify-cms-core/index.d.ts` to include the new widget type:
```typescript index.d.ts
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
```typescript
{
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' }],
},
],
},
],
},
],
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.