From 1a36a740130d52e70ac500570621cd0e0a66de0d Mon Sep 17 00:00:00 2001 From: Per-Kristian Nordnes Date: Wed, 21 Feb 2024 11:10:01 +0100 Subject: [PATCH] feat(core/inputs): support custom editor change callback (#5803) * feat(form/inputs): add onEditorChange prop to PT-input This will allow consumers to hook into editor changes by supplying their own handler to the PortableTextInput props * test(playwright-ct): add test for PortableTextInput onEditorChange prop --- .../formBuilder/inputs/PortableText/Input.spec.tsx | 13 ++++++++++++- .../formBuilder/inputs/PortableText/InputStory.tsx | 6 ++++-- .../form/inputs/PortableText/PortableTextInput.tsx | 6 +++++- packages/sanity/src/core/form/types/inputProps.ts | 5 +++++ 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/Input.spec.tsx b/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/Input.spec.tsx index f727cdc7e9f..118995d6002 100644 --- a/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/Input.spec.tsx +++ b/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/Input.spec.tsx @@ -1,5 +1,5 @@ import {expect, test} from '@playwright/experimental-ct-react' -import {type PortableTextEditor} from '@sanity/portable-text-editor' +import {type EditorChange, type PortableTextEditor} from '@sanity/portable-text-editor' import {type RefObject} from 'react' import {testHelpers} from '../../../utils/testHelpers' @@ -60,4 +60,15 @@ test.describe('Portable Text Input', () => { expect(ref?.current?.schemaTypes.block).toBeDefined() }) }) + + test.describe('onEditorChange', () => { + test(`Supports own handler of editor changes through props`, async ({mount, page}) => { + const changes: EditorChange[] = [] + const pushChange = (change: EditorChange) => changes.push(change) + await mount() + const $editor = page.getByTestId('pt-input-with-editor-ref') + await expect($editor).toBeVisible() + expect(changes.slice(-1)[0].type).toEqual('ready') + }) + }) }) diff --git a/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/InputStory.tsx b/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/InputStory.tsx index 7a6d5d0dd05..d45fefbf182 100644 --- a/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/InputStory.tsx +++ b/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/InputStory.tsx @@ -1,4 +1,4 @@ -import {type PortableTextEditor} from '@sanity/portable-text-editor' +import {type EditorChange, type PortableTextEditor} from '@sanity/portable-text-editor' import {defineArrayMember, defineField, defineType} from '@sanity/types' import {createRef, type RefObject, useMemo, useState} from 'react' import {type InputProps, type PortableTextInputProps} from 'sanity' @@ -8,6 +8,7 @@ import {TestWrapper} from '../../utils/TestWrapper' export function InputStory(props: { getRef?: (editorRef: RefObject) => void + onEditorChange?: (change: EditorChange) => void }) { // Use a state as ref here to be make sure we are able to call the ref callback when // the ref is ready @@ -35,6 +36,7 @@ export function InputStory(props: { input: (inputProps: InputProps) => { const editorProps = { ...inputProps, + onEditorChange: props.onEditorChange, editorRef: createRef(), } as PortableTextInputProps if (editorProps.editorRef) { @@ -51,7 +53,7 @@ export function InputStory(props: { ], }), ], - [], + [props.onEditorChange], ) return ( diff --git a/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx b/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx index 84573aefee0..ceaa7af068b 100644 --- a/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx +++ b/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx @@ -65,6 +65,7 @@ export function PortableTextInput(props: PortableTextInputProps) { hotkeys, markers = EMPTY_ARRAY, onChange, + onEditorChange, onCopy, onInsert, onItemRemove, @@ -220,8 +221,11 @@ export function PortableTextInput(props: PortableTextInputProps) { break default: } + if (editorRef.current && onEditorChange) { + onEditorChange(change, editorRef.current) + } }, - [onBlur, onChange, setFocusPathFromEditorSelection, toast], + [editorRef, onBlur, onChange, onEditorChange, setFocusPathFromEditorSelection, toast], ) useEffect(() => { diff --git a/packages/sanity/src/core/form/types/inputProps.ts b/packages/sanity/src/core/form/types/inputProps.ts index a312f49971f..d6e17c1a42e 100644 --- a/packages/sanity/src/core/form/types/inputProps.ts +++ b/packages/sanity/src/core/form/types/inputProps.ts @@ -1,4 +1,5 @@ import { + type EditorChange, type HotkeyOptions, type OnCopyFn, type OnPasteFn, @@ -507,6 +508,10 @@ export interface PortableTextInputProps * Use the `renderBlock` interface instead. */ markers?: PortableTextMarker[] + /** + * Returns changes from the underlying editor + */ + onEditorChange?: (change: EditorChange, editor: PortableTextEditor) => void /** * Custom copy function */