Lexical editor forcibly in readonly mode
Describe the Bug
Lexical editor is in readonly mode, the toolbar at the top doesn't show, and it is not sparking the joy I had assumed it would spark. I don't have any type errors anywhere, no other errors. Types build correctly, import map works, etc.
Link to the code that reproduces this issue
NA
Reproduction Steps
- Made a blank template, using v127
- Put lexical inside of tabs
- Updated to 3.0.1
Which area(s) are affected? (Select all that apply)
area: core
Environment Info
NA
Please add a reproduction in order for us to be able to investigate.
Depending on the quality of reproduction steps, this issue may be closed if no reproduction is provided.
Why was this issue marked with the invalid-reproduction label?
To be able to investigate, we need access to a reproduction to identify what triggered the issue. We prefer a link to a public GitHub repository created with create-payload-app@beta -t blank or a forked/branched version of this repository with tests added (more info in the reproduction-guide).
To make sure the issue is resolved as quickly as possible, please make sure that the reproduction is as minimal as possible. This means that you should remove unnecessary code, files, and dependencies that do not contribute to the issue. Ensure your reproduction does not depend on secrets, 3rd party registries, private dependencies, or any other data that cannot be made public. Avoid a reproduction including a whole monorepo (unless relevant to the issue). The easier it is to reproduce the issue, the quicker we can help.
Please test your reproduction against the latest version of Payload to make sure your issue has not already been fixed.
I added a link, why was it still marked?
Ensure the link is pointing to a codebase that is accessible (e.g. not a private repository). "example.com", "n/a", "will add later", etc. are not acceptable links -- we need to see a public codebase. See the above section for accepted links.
Useful Resources
I have the same exact issue 3.0.1 - I followed all the upgrades from each beta and never had this problem until the stable release.
I'm having the exact same issue. Some instances of the lexical editor, with the exact same config yet in different collections are greyed out / read-only state.
The affected instances is occurring within tabs. I'm importing a hero (which is a group) and that is used within the tabs.
Nightmare as it now in prod and the editors are stuck.
Another issue, also within tabs is relationship selects are randomly blank.
You can see reusableContent is working in some places, and not others.
to provide some more context here, I did some testing and it appears as though beta.132 broke this. I downgrade to beta.131 and the lexical editors are no longer read only mode. Also a ton of other bugs seemed to be resolved
I have done the same as @bowendevteam. ~Downgrading to 131 has solved the lexical editor readonly issue.~
Nope... the issue still persists in 131. I have just found another area where it's affected.
The relationship issue also still persists, so must have been introduced in the migration script...?
in >= 131 there are layout blocks that are now blank / unpopulated since the migration... again within tabs
Hey all, we are actively looking into this. @jakehopking — are your tabs named, or unnamed? Can you provide us with an exact structure of fields where your rich text editor is either blank or rendering as read-only?
We tried reproducing with this:
- Made a blank template, using v127
- Put lexical inside of tabs
- Updated to 3.0.1
But 3.0.1 works as expected. @stuckinsnow is it possible that you might have older versions of packages in your node_modules still? Or, maybe Next.js has cached older versions?
Try and delete your .next folder and restart the server. It's possible that you might have a cached build of Payload in your .next folder.
Note that this was indeed a bug up to beta 132: https://github.com/payloadcms/payload/pull/9237
We could reproduce it and it was resolved, but if there are more remaining cases to catch, we will do so immediately.
Keep us updated!
Here's an example of one collection where the lexical editors are broken. Worth noting that in some other collections, VERY similar patterns don't produce a broken
heroArticle.ts
import type { Field } from 'payload'
import {
FixedToolbarFeature,
HeadingFeature,
InlineToolbarFeature,
lexicalEditor,
} from '@payloadcms/richtext-lexical'
export const heroArticle: Field = {
name: 'hero',
type: 'group',
fields: [
{
name: 'type',
type: 'select',
defaultValue: 'articleHorizontal',
label: 'Type',
options: [
{
label: 'Article Vertical',
value: 'articleVertical',
},
{
label: 'Article Horizontal',
value: 'articleHorizontal',
},
],
admin: {
isClearable: false,
},
required: true,
},
{
name: 'richTextHeading',
type: 'richText',
label: 'Heading',
required: true,
admin: {
description: 'The heading that will be displayed in the hero section.',
},
editor: lexicalEditor({
features: ({ rootFeatures }) => {
return [
...rootFeatures,
HeadingFeature({ enabledHeadingSizes: ['h1'] }),
FixedToolbarFeature(),
InlineToolbarFeature(),
]
},
}),
},
{
name: 'richTextIntro',
type: 'richText',
label: 'Intro',
required: true,
editor: lexicalEditor({
features: ({ rootFeatures }) => {
return [
...rootFeatures,
// HeadingFeature({ enabledHeadingSizes: [] }),
FixedToolbarFeature(),
InlineToolbarFeature(),
]
},
}),
},
{
name: 'media',
type: 'upload',
admin: {
description:
'The image that will be displayed in the hero section. Note: To specify an images to use within carousels, cards, and SEO, see the `Carousel`, `Card`, and `SEO` tabs respectively.',
},
relationTo: 'media',
required: true,
},
{
name: 'showCaption',
type: 'checkbox',
defaultValue: false,
label: 'Show Media Caption',
admin: {
description: 'Show the caption for the media. Note: Caption is set in the Media Library.',
},
},
],
label: false,
}
is imported into article index.ts
import type { CollectionConfig } from 'payload'
import { authenticated } from '@payload/access/authenticated'
import { authenticatedOrPublished } from '@payload/access/authenticatedOrPublished'
import { slugField } from '@payload/fields/slug'
import { heroArticle } from '@payload/fields/heroArticle'
import { publishedAt } from '@payload/fields/publishedAt'
import { collectionNameField } from '@payload/fields/collectionName'
import { populatePublishedAt } from '@payload/hooks/populatePublishedAt'
import { generatePreviewPath } from '@payload/utilities/generatePreviewPath'
import { CollectionSlugs } from '@tokens/slugs'
import { revalidateArticle } from './hooks/revalidateArticle'
import { themeField } from '@payload/fields/theme'
import { categoriesField } from '@payload/fields/categories'
import { tagsField } from '@payload/fields/tags'
import { authorsField } from '@payload/fields/authors'
import { populateAuthors } from '@payload/hooks/populateAuthors'
import { ArticleMediaBlock } from '@payload/blocks/ArticleMediaBlock'
import { ArticleQuoteBlock } from '@payload/blocks/ArticleQuoteBlock'
import { ArticleButtonBlock } from '@payload/blocks/ArticleButtonBlock'
import { FormBlock } from '@payload/blocks/Form'
import { ReusableContent } from '@payload/blocks/ReusableContent'
import { LinksGridBlock } from '@payload/blocks/LinksGrid'
import { commonTabsCardFields, commonTabsCarouselFields } from '@payload/fields/commonTabs'
import {
BlocksFeature,
FixedToolbarFeature,
HeadingFeature,
HorizontalRuleFeature,
InlineToolbarFeature,
lexicalEditor,
} from '@payloadcms/richtext-lexical'
import {
MetaDescriptionField,
MetaImageField,
MetaTitleField,
OverviewField,
PreviewField,
} from '@payloadcms/plugin-seo/fields'
import { revalidateCollectionAndSlug } from '@payload/hooks/revalidateCollectionAndSlug'
import { ArticleVideoBlock } from '@payload/blocks/ArticleVideoBlock'
import { deleteCollectionAndSlug } from '@payload/hooks/deleteCollectionAndSlug'
import { deleteArticle } from './hooks/deleteArticle'
export const Articles: CollectionConfig = {
slug: CollectionSlugs.articles,
access: {
create: authenticated,
delete: authenticated,
read: authenticatedOrPublished,
update: authenticated,
},
admin: {
useAsTitle: 'title',
defaultColumns: ['title', 'slug', 'publishedAt', 'updatedAt'],
livePreview: {
url: ({ data }) => {
const path = generatePreviewPath({
// path: `${RouterPaths.articles}${typeof data?.slug === 'string' ? data.slug : ''}`,
slug: `${typeof data?.slug === 'string' ? data.slug : ''}`,
collection: 'articles',
})
return `${process.env.NEXT_PUBLIC_SERVER_URL}${path}`
},
},
preview: (data) =>
generatePreviewPath({
slug: `${typeof data?.slug === 'string' ? data.slug : ''}`,
collection: 'articles',
}),
},
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'description',
type: 'textarea',
required: true,
admin: {
description:
'This description, or abstract, is used (unless overridden) for carousel descriptions, card descriptions, and SEO descriptions. To override the aforementioned descriptions, see the `Carousel`, `Card`, and `SEO` tabs respectively.',
},
},
{
type: 'tabs',
tabs: [
{
label: 'Hero',
fields: [heroArticle],
},
{
label: 'Content',
fields: [
{
name: 'content',
type: 'richText',
editor: lexicalEditor({
features: ({ rootFeatures }) => {
return [
...rootFeatures,
HeadingFeature({ enabledHeadingSizes: ['h2', 'h3', 'h4', 'h5'] }),
BlocksFeature({
blocks: [
ArticleMediaBlock,
ArticleVideoBlock,
ArticleQuoteBlock,
ArticleButtonBlock,
FormBlock,
],
}),
FixedToolbarFeature(),
InlineToolbarFeature(),
HorizontalRuleFeature(),
]
},
}),
label: false,
required: true,
},
],
},
{
label: 'Blocks',
fields: [
{
name: 'layout',
type: 'blocks',
blocks: [ReusableContent, LinksGridBlock],
admin: {
description:
'Additional blocks can be added here. These are placed at the bottom of the article.',
},
},
],
},
{
label: 'Carousel',
description: 'This tab is used to set how this page appears when used within carousels.',
fields: [...commonTabsCarouselFields],
},
{
label: 'Card',
description: 'This tab is used to set how this page appears when used within cards.',
fields: [...commonTabsCardFields],
},
{
label: 'Meta',
description: "This tab is used to set the article's meta settings.",
fields: [
{
name: 'relatedArticles',
type: 'relationship',
admin: {
position: 'sidebar',
},
filterOptions: ({ id }) => {
return {
id: {
not_in: [id],
},
}
},
hasMany: true,
relationTo: CollectionSlugs.articles,
},
categoriesField,
tagsField,
themeField({}),
],
},
{
label: 'SEO',
name: 'meta',
fields: [
OverviewField({
titlePath: 'meta.title',
descriptionPath: 'meta.description',
imagePath: 'meta.image',
}),
MetaTitleField({
hasGenerateFn: true,
}),
MetaImageField({
relationTo: 'media',
}),
MetaDescriptionField({}),
PreviewField({
// if the `generateUrl` function is configured
hasGenerateFn: true,
// field paths to match the target field for data
titlePath: 'meta.title',
descriptionPath: 'meta.description',
}),
],
},
],
},
publishedAt,
slugField(),
authorsField,
// This field is only used to populate the user data via the `populateAuthors` hook
// This is because the `user` collection has access control locked to protect user privacy
// GraphQL will also not return mutated user data that differs from the underlying schema
{
name: 'populatedAuthors',
type: 'array',
access: {
update: () => false,
},
admin: {
disabled: true,
readOnly: true,
},
fields: [
{
name: 'id',
type: 'text',
},
{
name: 'name',
type: 'text',
},
],
},
collectionNameField(CollectionSlugs.articles),
],
hooks: {
afterChange: [revalidateCollectionAndSlug, revalidateArticle],
afterDelete: [deleteCollectionAndSlug, deleteArticle],
afterRead: [populateAuthors],
beforeChange: [populatePublishedAt],
},
versions: {
drafts: {
autosave: {
interval: 100, // We set this interval for optimal live preview
},
},
maxPerDoc: 50,
},
}
Produces this:
Issue fixed thanks to Philip!
name: 'populatedAuthors',
type: 'array',
access: {
update: () => false, // <--- change this to true until a fix is released
},
https://discord.com/channels/967097582721572934/1308565576104546458/1308570040748081262
Fix incoming first thing tomorrow - thank you all for helping reproduce!
🚀 This is included in version v3.0.2
This issue has been automatically locked. Please open a new issue if this issue persists with any additional detail.