From 43a5baf6a6b743d3177e95ddf473326f809cca47 Mon Sep 17 00:00:00 2001 From: Per-Kristian Nordnes Date: Mon, 19 Feb 2024 19:07:15 +0100 Subject: [PATCH 1/3] fix(core/inputs): report focusPath normally for everything else than spans We forgot to call this normally for anything else than spans. Also added some code comments. --- .../inputs/PortableText/PortableTextInput.tsx | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx b/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx index 856bb23db59..58dd62d882f 100644 --- a/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx +++ b/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx @@ -146,22 +146,27 @@ export function PortableTextInput(props: PortableTextInputProps) { } }, [hasFocusWithin]) + // Report focus on spans with `.text` appended to the reported focusPath. + // This is done to support the Presentation tool which uses this kind of paths to refer to texts. + // The PT-input already supports these paths the other way around. + // It's a bit ugly right here, but it's a rather simple way to support the Presentation tool without + // having to change the PTE's internals. const setFocusPathFromEditorSelection = useCallback( (focusPath: Path) => { - // Report focus on spans with `.text` appended to the reported focusPath. - // This is done to support the Presentation tool which uses this kind of paths to refer to texts. - // The PT-input already supports these paths the other way around. - // It's a bit ugly right here, but it's a rather simple way to support the Presentation tool without - // having to change the PTE's internals. - if ( - focusPath.length === 3 && - focusPath[1] === 'children' && - isKeySegment(focusPath[2]) && + // Test if the focusPath is pointing directly to a span + const isSpanPath = + focusPath.length === 3 && // A span path is always 3 segments long + focusPath[1] === 'children' && // Is a child of a block + isKeySegment(focusPath[2]) && // Contains the key of the child !portableTextMemberItems.some( (item) => isKeySegment(focusPath[2]) && item.key === focusPath[2]._key, - ) // Not an inline object - ) { + ) // Not an inline object (it would be a member in this list, where spans are not). By doing this check we avoid depending on the value. + if (isSpanPath) { + // Append `.text` to the focusPath onPathFocus(focusPath.concat('text')) + } else { + // Call normally + onPathFocus(focusPath) } }, [onPathFocus, portableTextMemberItems], From a9c8eb96dc433be8ddf3c6aea2b19e58a78a3bd0 Mon Sep 17 00:00:00 2001 From: Per-Kristian Nordnes Date: Mon, 19 Feb 2024 20:00:36 +0100 Subject: [PATCH 2/3] fix(form/inputs): fix key test, it was testing the wrong key --- .../src/core/form/inputs/PortableText/PortableTextInput.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx b/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx index 58dd62d882f..0ef1329c7ab 100644 --- a/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx +++ b/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx @@ -159,7 +159,7 @@ export function PortableTextInput(props: PortableTextInputProps) { focusPath[1] === 'children' && // Is a child of a block isKeySegment(focusPath[2]) && // Contains the key of the child !portableTextMemberItems.some( - (item) => isKeySegment(focusPath[2]) && item.key === focusPath[2]._key, + (item) => isKeySegment(focusPath[2]) && item.member.key === focusPath[2]._key, ) // Not an inline object (it would be a member in this list, where spans are not). By doing this check we avoid depending on the value. if (isSpanPath) { // Append `.text` to the focusPath From 291d6bb0b99044c781bd9ec3616761f5c47900d3 Mon Sep 17 00:00:00 2001 From: Per-Kristian Nordnes Date: Mon, 19 Feb 2024 20:01:02 +0100 Subject: [PATCH 3/3] test(playwright-ct): test focusPath reporting from PT-input Test that spans are reported with .text and everything else not. --- .../PortableText/FocusTracking.spec.tsx | 19 +++++++++++++++++++ .../PortableText/FocusTrackingStory.tsx | 4 +++- .../tests/formBuilder/utils/TestForm.tsx | 5 ++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/FocusTracking.spec.tsx b/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/FocusTracking.spec.tsx index 447ede9b85e..600c8313ee3 100644 --- a/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/FocusTracking.spec.tsx +++ b/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/FocusTracking.spec.tsx @@ -3,6 +3,7 @@ import {expect, test} from '@playwright/experimental-ct-react' import {type Page} from '@playwright/test' import {type Path, type SanityDocument} from '@sanity/types' +import {testHelpers} from '../../../utils/testHelpers' import FocusTrackingStory from './FocusTrackingStory' export type UpdateFn = () => {focusPath: Path; document: SanityDocument} @@ -136,6 +137,24 @@ test.describe('Portable Text Input', () => { await expect(blockObjectInput).not.toBeVisible() }) }) + test(`reports focus on spans with with .text prop, and everything else without`, async ({ + mount, + page, + }) => { + const paths: Path[] = [] + const pushPath = (path: Path) => paths.push(path) + await mount() + const {getFocusedPortableTextEditor} = testHelpers({page}) + const $pte = await getFocusedPortableTextEditor('field-body') + await expect($pte).toBeFocused() + expect(paths.slice(-1)[0]).toEqual(['body', {_key: 'a'}, 'children', {_key: 'b'}, 'text']) + const $inlineObject = page.getByTestId('inline-preview') + await $inlineObject.click() + expect(paths.slice(-1)[0]).toEqual(['body', {_key: 'g'}, 'children', {_key: 'i'}]) + const $blockObject = page.getByTestId('pte-block-object') + await $blockObject.click() + expect(paths.slice(-1)[0]).toEqual(['body', {_key: 'k'}]) + }) }) function waitForFocusedNodeText(page: Page, text: string) { diff --git a/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/FocusTrackingStory.tsx b/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/FocusTrackingStory.tsx index a5c2e2edd0d..0d9687c9729 100644 --- a/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/FocusTrackingStory.tsx +++ b/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/FocusTrackingStory.tsx @@ -57,14 +57,16 @@ const SCHEMA_TYPES = [ export function FocusTrackingStory({ focusPath, + onPathFocus, document, }: { focusPath?: Path + onPathFocus?: (path: Path) => void document?: SanityDocument }) { return ( - + ) } diff --git a/packages/sanity/playwright-ct/tests/formBuilder/utils/TestForm.tsx b/packages/sanity/playwright-ct/tests/formBuilder/utils/TestForm.tsx index f9dea6b8586..523065c5919 100644 --- a/packages/sanity/playwright-ct/tests/formBuilder/utils/TestForm.tsx +++ b/packages/sanity/playwright-ct/tests/formBuilder/utils/TestForm.tsx @@ -34,10 +34,12 @@ declare global { export function TestForm({ focusPath: focusPathFromProps, + onPathFocus: onPathFocusFromProps, document: documentFromProps, id: idFromProps = 'root', }: { focusPath?: Path + onPathFocus?: (path: Path) => void document?: SanityDocument id?: string }) { @@ -115,8 +117,9 @@ export function TestForm({ const handleFocus = useCallback( (nextFocusPath: Path) => { setFocusPath(nextFocusPath) + onPathFocusFromProps?.(nextFocusPath) }, - [setFocusPath], + [onPathFocusFromProps], ) const handleBlur = useCallback(() => {