Skip to content

Commit 3e7db30

Browse files
authored
fix(richtext-lexical): newTab not being able to be checked to true by default (#12389)
Previously the value of new tab checkbox in the link feature was not able to be set to true by default because we were passing `false` as a default value. This fixes that and adds test coverage for customising that link drawer.
1 parent 7498d09 commit 3e7db30

File tree

7 files changed

+215
-1
lines changed

7 files changed

+215
-1
lines changed

packages/richtext-lexical/src/features/link/client/index.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ const toolbarGroups: ToolbarGroup[] = [
6464

6565
const linkFields: Partial<LinkFields> = {
6666
doc: null,
67-
newTab: false,
6867
}
6968

7069
editor.dispatchCommand(TOGGLE_LINK_WITH_MODAL_COMMAND, {

test/lexical/baseConfig.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
} from './collections/Lexical/index.js'
1212
import { LexicalAccessControl } from './collections/LexicalAccessControl/index.js'
1313
import { LexicalInBlock } from './collections/LexicalInBlock/index.js'
14+
import { LexicalLinkFeature } from './collections/LexicalLinkFeature/index.js'
1415
import { LexicalLocalizedFields } from './collections/LexicalLocalized/index.js'
1516
import { LexicalMigrateFields } from './collections/LexicalMigrate/index.js'
1617
import { LexicalObjectReferenceBugCollection } from './collections/LexicalObjectReferenceBug/index.js'
@@ -28,6 +29,7 @@ export const baseConfig: Partial<Config> = {
2829
// ...extend config here
2930
collections: [
3031
LexicalFullyFeatured,
32+
LexicalLinkFeature,
3133
getLexicalFieldsCollection({
3234
blocks: lexicalBlocks,
3335
inlineBlocks: lexicalInlineBlocks,
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { expect, test } from '@playwright/test'
2+
import { AdminUrlUtil } from 'helpers/adminUrlUtil.js'
3+
import { reInitializeDB } from 'helpers/reInitializeDB.js'
4+
import { lexicalLinkFeatureSlug } from 'lexical/slugs.js'
5+
import path from 'path'
6+
import { fileURLToPath } from 'url'
7+
8+
import { ensureCompilationIsDone } from '../../../helpers.js'
9+
import { initPayloadE2ENoConfig } from '../../../helpers/initPayloadE2ENoConfig.js'
10+
import { TEST_TIMEOUT_LONG } from '../../../playwright.config.js'
11+
import { LexicalHelpers } from './utils.js'
12+
const filename = fileURLToPath(import.meta.url)
13+
const currentFolder = path.dirname(filename)
14+
const dirname = path.resolve(currentFolder, '../../')
15+
16+
const { beforeAll, beforeEach, describe } = test
17+
18+
// Unlike the other suites, this one runs in parallel, as they run on the `lexical-fully-featured/create` URL and are "pure" tests
19+
test.describe.configure({ mode: 'parallel' })
20+
21+
const { serverURL } = await initPayloadE2ENoConfig({
22+
dirname,
23+
})
24+
25+
describe('Lexical Link Feature', () => {
26+
beforeAll(async ({ browser }, testInfo) => {
27+
testInfo.setTimeout(TEST_TIMEOUT_LONG)
28+
process.env.SEED_IN_CONFIG_ONINIT = 'false' // Makes it so the payload config onInit seed is not run. Otherwise, the seed would be run unnecessarily twice for the initial test run - once for beforeEach and once for onInit
29+
const page = await browser.newPage()
30+
await ensureCompilationIsDone({ page, serverURL })
31+
await page.close()
32+
})
33+
beforeEach(async ({ page }) => {
34+
await reInitializeDB({
35+
serverURL,
36+
snapshotKey: 'fieldsTest',
37+
uploadsDir: [
38+
path.resolve(dirname, './collections/Upload/uploads'),
39+
path.resolve(dirname, './collections/Upload2/uploads2'),
40+
],
41+
})
42+
const url = new AdminUrlUtil(serverURL, lexicalLinkFeatureSlug)
43+
const lexical = new LexicalHelpers(page)
44+
await page.goto(url.create)
45+
await lexical.editor.first().focus()
46+
})
47+
48+
test('can add new custom fields in link feature modal', async ({ page }) => {
49+
const lexical = new LexicalHelpers(page)
50+
51+
await lexical.editor.fill('link')
52+
await lexical.editor.selectText()
53+
54+
const linkButtonClass = `.rich-text-lexical__wrap .fixed-toolbar .toolbar-popup__button-link`
55+
const linkButton = page.locator(linkButtonClass).first()
56+
57+
await linkButton.click()
58+
59+
const customField = lexical.drawer.locator('#field-someText')
60+
61+
await expect(customField).toBeVisible()
62+
})
63+
64+
test('can set default value of newTab checkbox to checked', async ({ page }) => {
65+
const lexical = new LexicalHelpers(page)
66+
67+
await lexical.editor.fill('link')
68+
await lexical.editor.selectText()
69+
70+
const linkButtonClass = `.rich-text-lexical__wrap .fixed-toolbar .toolbar-popup__button-link`
71+
const linkButton = page.locator(linkButtonClass).first()
72+
73+
await linkButton.click()
74+
75+
const checkboxField = lexical.drawer.locator(`[id^="field-newTab"]`)
76+
77+
await expect(checkboxField).toBeChecked()
78+
})
79+
})
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import type { CheckboxField, CollectionConfig } from 'payload'
2+
3+
import {
4+
FixedToolbarFeature,
5+
lexicalEditor,
6+
LinkFeature,
7+
TreeViewFeature,
8+
} from '@payloadcms/richtext-lexical'
9+
10+
import { lexicalLinkFeatureSlug } from '../../slugs.js'
11+
12+
export const LexicalLinkFeature: CollectionConfig = {
13+
slug: lexicalLinkFeatureSlug,
14+
labels: {
15+
singular: 'Lexical Link Feature',
16+
plural: 'Lexical Link Feature',
17+
},
18+
fields: [
19+
{
20+
name: 'richText',
21+
type: 'richText',
22+
editor: lexicalEditor({
23+
features: ({ defaultFeatures }) => [
24+
...defaultFeatures,
25+
TreeViewFeature(),
26+
LinkFeature({
27+
fields: ({ defaultFields }) => {
28+
const modifiedFields = defaultFields.map((field) => {
29+
if (field.name === 'newTab') {
30+
return { ...field, defaultValue: true } as CheckboxField
31+
}
32+
33+
return field
34+
})
35+
36+
return [...modifiedFields, { type: 'text', name: 'someText' }]
37+
},
38+
}),
39+
FixedToolbarFeature(),
40+
],
41+
}),
42+
},
43+
],
44+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import type { Page } from 'playwright'
2+
3+
import { expect } from '@playwright/test'
4+
5+
export class LexicalHelpers {
6+
page: Page
7+
constructor(page: Page) {
8+
this.page = page
9+
}
10+
11+
async save(container: 'document' | 'drawer') {
12+
if (container === 'drawer') {
13+
await this.drawer.getByText('Save').click()
14+
} else {
15+
throw new Error('Not implemented')
16+
}
17+
await this.page.waitForTimeout(1000)
18+
}
19+
20+
async slashCommand(
21+
// prettier-ignore
22+
command: 'block' | 'check' | 'code' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' |'h6' | 'inline'
23+
| 'link' | 'ordered' | 'paragraph' | 'quote' | 'relationship' | 'unordered' | 'upload',
24+
) {
25+
await this.page.keyboard.press(`/`)
26+
27+
const slashMenuPopover = this.page.locator('#slash-menu .slash-menu-popup')
28+
await expect(slashMenuPopover).toBeVisible()
29+
await this.page.keyboard.type(command)
30+
await this.page.keyboard.press(`Enter`)
31+
await expect(slashMenuPopover).toBeHidden()
32+
}
33+
34+
get decorator() {
35+
return this.editor.locator('[data-lexical-decorator="true"]')
36+
}
37+
38+
get drawer() {
39+
return this.page.locator('.drawer__content')
40+
}
41+
42+
get editor() {
43+
return this.page.locator('[data-lexical-editor="true"]')
44+
}
45+
46+
get paragraph() {
47+
return this.editor.locator('p')
48+
}
49+
}

test/lexical/payload-types.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export interface Config {
8484
blocks: {};
8585
collections: {
8686
'lexical-fully-featured': LexicalFullyFeatured;
87+
'lexical-link-feature': LexicalLinkFeature;
8788
'lexical-fields': LexicalField;
8889
'lexical-migrate-fields': LexicalMigrateField;
8990
'lexical-localized-fields': LexicalLocalizedField;
@@ -103,6 +104,7 @@ export interface Config {
103104
collectionsJoins: {};
104105
collectionsSelect: {
105106
'lexical-fully-featured': LexicalFullyFeaturedSelect<false> | LexicalFullyFeaturedSelect<true>;
107+
'lexical-link-feature': LexicalLinkFeatureSelect<false> | LexicalLinkFeatureSelect<true>;
106108
'lexical-fields': LexicalFieldsSelect<false> | LexicalFieldsSelect<true>;
107109
'lexical-migrate-fields': LexicalMigrateFieldsSelect<false> | LexicalMigrateFieldsSelect<true>;
108110
'lexical-localized-fields': LexicalLocalizedFieldsSelect<false> | LexicalLocalizedFieldsSelect<true>;
@@ -179,6 +181,30 @@ export interface LexicalFullyFeatured {
179181
updatedAt: string;
180182
createdAt: string;
181183
}
184+
/**
185+
* This interface was referenced by `Config`'s JSON-Schema
186+
* via the `definition` "lexical-link-feature".
187+
*/
188+
export interface LexicalLinkFeature {
189+
id: string;
190+
richText?: {
191+
root: {
192+
type: string;
193+
children: {
194+
type: string;
195+
version: number;
196+
[k: string]: unknown;
197+
}[];
198+
direction: ('ltr' | 'rtl') | null;
199+
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
200+
indent: number;
201+
version: number;
202+
};
203+
[k: string]: unknown;
204+
} | null;
205+
updatedAt: string;
206+
createdAt: string;
207+
}
182208
/**
183209
* This interface was referenced by `Config`'s JSON-Schema
184210
* via the `definition` "lexical-fields".
@@ -804,6 +830,10 @@ export interface PayloadLockedDocument {
804830
relationTo: 'lexical-fully-featured';
805831
value: string | LexicalFullyFeatured;
806832
} | null)
833+
| ({
834+
relationTo: 'lexical-link-feature';
835+
value: string | LexicalLinkFeature;
836+
} | null)
807837
| ({
808838
relationTo: 'lexical-fields';
809839
value: string | LexicalField;
@@ -903,6 +933,15 @@ export interface LexicalFullyFeaturedSelect<T extends boolean = true> {
903933
updatedAt?: T;
904934
createdAt?: T;
905935
}
936+
/**
937+
* This interface was referenced by `Config`'s JSON-Schema
938+
* via the `definition` "lexical-link-feature_select".
939+
*/
940+
export interface LexicalLinkFeatureSelect<T extends boolean = true> {
941+
richText?: T;
942+
updatedAt?: T;
943+
createdAt?: T;
944+
}
906945
/**
907946
* This interface was referenced by `Config`'s JSON-Schema
908947
* via the `definition` "lexical-fields_select".

test/lexical/slugs.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ export const usersSlug = 'users'
22

33
export const lexicalFullyFeaturedSlug = 'lexical-fully-featured'
44
export const lexicalFieldsSlug = 'lexical-fields'
5+
6+
export const lexicalLinkFeatureSlug = 'lexical-link-feature'
57
export const lexicalLocalizedFieldsSlug = 'lexical-localized-fields'
68
export const lexicalMigrateFieldsSlug = 'lexical-migrate-fields'
79
export const lexicalRelationshipFieldsSlug = 'lexical-relationship-fields'

0 commit comments

Comments
 (0)