Payload is the open-source, fullstack Next.js framework, giving you instant backend superpowers. Get a full TypeScript backend and admin panel instantly. Use Payload as a headless CMS or for building powerful applications.
When running payload version 2.30.1 or above and using the rich text slate editor plugin, the "Add Link" button to add link to text does not work and produces the following error:
Uncaught (in promise) TypeError: openModal is not a function
at index.tsx:106:13
at step (index.tsx:1:1)
at Object.next (index.tsx:1:1)
at asyncGeneratorStep (index.tsx:1:1)
at _next (index.tsx:1:1)
at index.tsx:1:1
at new Promise (<anonymous>)
at index.tsx:1:1
at HTMLUnknownElement.callCallback (react-dom.development.js:4164:14)
at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:16)
When looking into the code in the specific file it looks like this:
'use client'
import type { Fields } from 'payload/types'
import { useModal } from '@faceless-ui/modal'
import { useDrawerSlug } from 'payload/components/elements'
import { reduceFieldsToValues } from 'payload/components/forms'
import {
buildStateFromSchema,
useAuth,
useConfig,
useDocumentInfo,
useLocale,
} from 'payload/components/utilities'
import { sanitizeFields } from 'payload/config'
import React, { Fragment, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Editor, Range, Transforms } from 'slate'
import { ReactEditor, useSlate } from 'slate-react'
import type { FieldProps } from '../../../../types'
import LinkIcon from '../../../icons/Link'
import ElementButton from '../../Button'
import isElementActive from '../../isActive'
import { LinkDrawer } from '../LinkDrawer'
import { transformExtraFields, unwrapLink } from '../utilities'
/**
* This function is called when an new link is created - not when an existing link is edited.
*/
const insertLink = (editor, fields) => {
const isCollapsed = editor.selection && Range.isCollapsed(editor.selection)
const data = reduceFieldsToValues(fields, true)
const newLink = {
children: [],
doc: data.doc,
fields: data.fields, // Any custom user-added fields are part of data.fields
linkType: data.linkType,
newTab: data.newTab,
type: 'link',
url: data.url,
}
if (isCollapsed || !editor.selection) {
// If selection anchor and focus are the same,
// Just inject a new node with children already set
Transforms.insertNodes(editor, {
...newLink,
children: [{ text: String(data.text) }],
})
} else if (editor.selection) {
// Otherwise we need to wrap the selected node in a link,
// Delete its old text,
// Move the selection one position forward into the link,
// And insert the text back into the new link
Transforms.wrapNodes(editor, newLink, { split: true })
Transforms.delete(editor, { at: editor.selection.focus.path, unit: 'word' })
Transforms.move(editor, { distance: 1, unit: 'offset' })
Transforms.insertText(editor, String(data.text), { at: editor.selection.focus.path })
}
ReactEditor.focus(editor)
}
export const LinkButton: React.FC<{
fieldProps: FieldProps
path: string
}> = ({ fieldProps }) => {
const customFieldSchema = fieldProps?.admin?.link?.fields
const { user } = useAuth()
const { code: locale } = useLocale()
const [initialState, setInitialState] = useState<Fields>({})
const { i18n, t } = useTranslation(['upload', 'general'])
const editor = useSlate()
const config = useConfig()
const [fieldSchema] = useState(() => {
const fieldsUnsanitized = transformExtraFields(customFieldSchema, config, i18n)
// Sanitize custom fields here
const validRelationships = config.collections.map((c) => c.slug) || []
const fields = sanitizeFields({
config: config,
fields: fieldsUnsanitized,
validRelationships,
})
return fields
})
const { closeModal, openModal } = useModal()
const drawerSlug = useDrawerSlug('rich-text-link')
const { getDocPreferences } = useDocumentInfo()
return (
<Fragment>
<ElementButton
className="link"
format="link"
onClick={async () => {
if (isElementActive(editor, 'link')) {
unwrapLink(editor)
} else {
openModal(drawerSlug) // This is the row that throws the error: Uncaught (in promise) TypeError: openModal is not a function
const isCollapsed = editor.selection && Range.isCollapsed(editor.selection)
if (!isCollapsed) {
const data = {
text: editor.selection ? Editor.string(editor, editor.selection) : '',
}
const preferences = await getDocPreferences()
const state = await buildStateFromSchema({
config,
data,
fieldSchema,
locale,
operation: 'create',
preferences,
t,
user,
})
setInitialState(state)
}
}
}}
tooltip={t('fields:addLink')}
>
<LinkIcon />
</ElementButton>
<LinkDrawer
drawerSlug={drawerSlug}
fieldSchema={fieldSchema}
handleClose={() => {
closeModal(drawerSlug)
}}
handleModalSubmit={(fields) => {
insertLink(editor, fields)
closeModal(drawerSlug)
}}
initialState={initialState}
/>
</Fragment>
)
}
To Reproduce
Run payload version 2.30.1
Having version 1.5.2 of the payloadcms/richtext-slate
Have a rich text field using the slate editor.
Run payload in dev
Go into the browser and try set a link on text using the "Add Link" button in the slate editor of your field.
Should return the error "Uncaught (in promise) TypeError: openModal is not a function"
Link to reproduction
No response
Describe the Bug
When running payload version 2.30.1 or above and using the rich text slate editor plugin, the "Add Link" button to add link to text does not work and produces the following error:
When looking into the code in the specific file it looks like this:
To Reproduce
Should return the error "Uncaught (in promise) TypeError: openModal is not a function"
Payload Version
^2.30.1
Adapters and Plugins
richtext-slate@1.5.2