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(() => {
diff --git a/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx b/packages/sanity/src/core/form/inputs/PortableText/PortableTextInput.tsx
index 856bb23db59..0ef1329c7ab 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
- ) {
+ (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
onPathFocus(focusPath.concat('text'))
+ } else {
+ // Call normally
+ onPathFocus(focusPath)
}
},
[onPathFocus, portableTextMemberItems],