fix(richtext-lexical): enable select inputs with ctrl+a or cmd+a (#12453)

Fixes #6871

To review this PR, use `pnpm dev lexical` and the auto-created document
in the `lexical fields` collection. Select any input within the blocks
and press `cmd+a`. The selection should contain the entire input.

I made sure that `cmd+a` still works fine inside the editor but outside
of inputs.
This commit is contained in:
Germán Jabloñski
2025-05-30 18:28:51 -03:00
committed by GitHub
parent 836fd86090
commit 89ced5ec6b
6 changed files with 56 additions and 4 deletions

View File

@@ -151,7 +151,7 @@ export default buildConfig({
prefillOnly: true,
}
: false,
}
},
// highlight-end
})

View File

@@ -19,6 +19,7 @@ import { DraggableBlockPlugin } from './plugins/handles/DraggableBlockPlugin/ind
import { InsertParagraphAtEndPlugin } from './plugins/InsertParagraphAtEnd/index.js'
import { MarkdownShortcutPlugin } from './plugins/MarkdownShortcut/index.js'
import { NormalizeSelectionPlugin } from './plugins/NormalizeSelection/index.js'
import { SelectAllPlugin } from './plugins/SelectAllPlugin/index.js'
import { SlashMenuPlugin } from './plugins/SlashMenu/index.js'
import { TextPlugin } from './plugins/TextPlugin/index.js'
import { LexicalContentEditable } from './ui/ContentEditable.js'
@@ -111,6 +112,7 @@ export const LexicalEditor: React.FC<
<InsertParagraphAtEndPlugin />
<DecoratorPlugin />
<TextPlugin features={editorConfig.features} />
<SelectAllPlugin />
<OnChangePlugin
// Selection changes can be ignored here, reducing the
// frequency that the FieldComponent and Payload receive updates.

View File

@@ -0,0 +1,32 @@
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import { $getSelection, COMMAND_PRIORITY_LOW, SELECT_ALL_COMMAND } from 'lexical'
import { useEffect } from 'react'
/**
* Allows to select inputs with `ctrl+a` or `cmd+a`.
* Required because Lexical preventDefault the event.
* see: https://github.com/payloadcms/payload/issues/6871
*/
export function SelectAllPlugin() {
const [editor] = useLexicalComposerContext()
useEffect(() => {
return editor.registerCommand(
SELECT_ALL_COMMAND,
() => {
const selection = $getSelection()
if (selection) {
return false
}
const activeElement = document.activeElement
if (activeElement instanceof HTMLInputElement) {
activeElement.select()
}
return true
},
COMMAND_PRIORITY_LOW,
)
}, [editor])
return null
}

View File

@@ -1,4 +1,4 @@
import type { MigrateDownArgs, MigrateUpArgs} from '@payloadcms/db-postgres';
import type { MigrateDownArgs, MigrateUpArgs } from '@payloadcms/db-postgres'
import { sql } from '@payloadcms/db-postgres'

View File

@@ -62,6 +62,24 @@ describe('Lexical Fully Featured', () => {
const paragraph = lexical.editor.locator('> p')
await expect(paragraph).toHaveText('')
})
test('ControlOrMeta+A inside input should select all the text inside the input', async ({
page,
}) => {
const lexical = new LexicalHelpers(page)
await lexical.editor.first().focus()
await page.keyboard.type('Hello')
await page.keyboard.press('Enter')
await lexical.slashCommand('block')
await page.locator('#field-someText').first().focus()
await page.keyboard.type('World')
await page.keyboard.press('ControlOrMeta+A')
await page.keyboard.press('Backspace')
const paragraph = lexical.editor.locator('> p').first()
await expect(paragraph).toHaveText('Hello')
await expect(page.getByText('World')).toHaveCount(0)
})
test('text state feature', async ({ page }) => {
await page.keyboard.type('Hello')
await page.keyboard.press('ControlOrMeta+A')

View File

@@ -31,7 +31,7 @@
}
],
"paths": {
"@payload-config": ["./test/query-presets/config.ts"],
"@payload-config": ["./test/_community/config.ts"],
"@payloadcms/admin-bar": ["./packages/admin-bar/src"],
"@payloadcms/live-preview": ["./packages/live-preview/src"],
"@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"],