diff --git a/dev/test-studio/preview/SimpleBlockPortableText.tsx b/dev/test-studio/preview/SimpleBlockPortableText.tsx index 4cd27549f65..2bb43aea409 100644 --- a/dev/test-studio/preview/SimpleBlockPortableText.tsx +++ b/dev/test-studio/preview/SimpleBlockPortableText.tsx @@ -24,10 +24,11 @@ export function SimpleBlockPortableText(): React.JSX.Element { { _id: string title: string | null + bodyString: string body: PortableTextBlock[] notes: {_key: string; title?: string; minutes?: number; notes?: PortableTextBlock[]}[] }[] - >(/* groq */ `*[_type == "simpleBlock"]{_id,title,body,notes}`) + >(/* groq */ `*[_type == "simpleBlock"]{_id,title,"bodyString":pt::text(body),body,notes}`) if (error) { throw error @@ -52,6 +53,7 @@ export function SimpleBlockPortableText(): React.JSX.Element { }} >

{item.title || 'Untitled'}

+

{item.bodyString}

{item.notes?.map((note) => (
diff --git a/packages/sanity/package.json b/packages/sanity/package.json index 7fccc22dc30..eb5663be129 100644 --- a/packages/sanity/package.json +++ b/packages/sanity/package.json @@ -156,6 +156,7 @@ "@portabletext/block-tools": "^1.1.2", "@portabletext/editor": "^1.25.0", "@portabletext/react": "^3.0.0", + "@portabletext/toolkit": "^2.0.16", "@rexxars/react-json-inspector": "^9.0.1", "@sanity/asset-utils": "^2.0.6", "@sanity/bifur-client": "^0.4.1", diff --git a/packages/sanity/src/presentation/loader/LiveQueries.tsx b/packages/sanity/src/presentation/loader/LiveQueries.tsx index e94e872a0ae..60e1025442c 100644 --- a/packages/sanity/src/presentation/loader/LiveQueries.tsx +++ b/packages/sanity/src/presentation/loader/LiveQueries.tsx @@ -41,7 +41,7 @@ import { type PresentationPerspective, } from '../types' import {type DocumentOnPage} from '../useDocumentsOnPage' -import {useQueryParams, useRevalidate} from './utils' +import {mapChangedValue, useQueryParams, useRevalidate} from './utils' export interface LoaderQueriesProps { liveDocument: Partial | null | undefined @@ -197,8 +197,6 @@ export default function LoaderQueries(props: LoaderQueriesProps): React.JSX.Elem if (comlink) { // eslint-disable-next-line @typescript-eslint/no-shadow const {projectId, dataset} = clientConfig - // @todo - Can this be migrated/deprecated in favour of emitting - // `presentation/perspective` at a higher level? comlink.post('loader/perspective', { projectId: projectId!, dataset: dataset!, @@ -482,14 +480,7 @@ export function turboChargeResultIfSourceMap( } return null }, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (changedValue: any, {previousValue}) => { - if (typeof changedValue === 'number' && typeof previousValue === 'string') { - // If the string() function was used in the query, we need to convert the source value to a string as well - return `${changedValue}` - } - return changedValue - }, + mapChangedValue, perspective, ) } diff --git a/packages/sanity/src/presentation/loader/LoaderQueries.tsx b/packages/sanity/src/presentation/loader/LoaderQueries.tsx index 54a641f2859..b02de31950e 100644 --- a/packages/sanity/src/presentation/loader/LoaderQueries.tsx +++ b/packages/sanity/src/presentation/loader/LoaderQueries.tsx @@ -41,7 +41,7 @@ import { type PresentationPerspective, } from '../types' import {type DocumentOnPage} from '../useDocumentsOnPage' -import {useQueryParams, useRevalidate} from './utils' +import {mapChangedValue, useQueryParams, useRevalidate} from './utils' export interface LoaderQueriesProps { liveDocument: Partial | null | undefined @@ -174,8 +174,6 @@ export default function LoaderQueries(props: LoaderQueriesProps): React.JSX.Elem if (comlink) { // eslint-disable-next-line @typescript-eslint/no-shadow const {projectId, dataset} = clientConfig - // @todo - Can this be migrated/deprecated in favour of emitting - // `presentation/perspective` at a higher level? comlink.post('loader/perspective', { projectId: projectId!, dataset: dataset!, @@ -556,14 +554,7 @@ export function turboChargeResultIfSourceMap( // Fallback to general documents cache return cache.get(sourceDocument._id) }, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (changedValue: any, {previousValue}) => { - if (typeof changedValue === 'number' && typeof previousValue === 'string') { - // If the string() function was used in the query, we need to convert the source value to a string as well - return `${changedValue}` - } - return changedValue - }, + mapChangedValue, perspective, ) } diff --git a/packages/sanity/src/presentation/loader/utils.ts b/packages/sanity/src/presentation/loader/utils.ts index 1092203b48d..6112d316332 100644 --- a/packages/sanity/src/presentation/loader/utils.ts +++ b/packages/sanity/src/presentation/loader/utils.ts @@ -1,5 +1,38 @@ +import {toPlainText} from '@portabletext/react' +import {isPortableTextBlock} from '@portabletext/toolkit' import {type ClientPerspective, type QueryParams} from '@sanity/client' +import {type ApplySourceDocumentsUpdateFunction} from '@sanity/client/csm' import {useCallback, useEffect, useMemo, useState, useSyncExternalStore} from 'react' +import {type FIXME} from 'sanity' + +/** + * Used by `applySourceDocuments` + * @internal + */ +export const mapChangedValue: ApplySourceDocumentsUpdateFunction = ( + changedValue: FIXME, + {previousValue}, +) => { + if (typeof previousValue === 'string') { + if (typeof changedValue === 'number') { + // If the string() function was used in the query, we need to convert the source value to a string as well + return `${changedValue}` + } + // If it's an array in the source, but a string in the query response, it could be pt::text + if (Array.isArray(changedValue)) { + if (changedValue.length === 0) { + // If it's empty assume it's PT and return an empty string + return '' + } + // If the array contains any valid block type, assume the GROQ initially used pt::text on it and do the same conversion + if (changedValue.some((node) => typeof node === 'object' && isPortableTextBlock(node))) { + return toPlainText(changedValue) + } + } + } + + return changedValue +} /** * @internal diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f5569de3c07..a1e1cd74f5d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1349,6 +1349,9 @@ importers: '@portabletext/react': specifier: ^3.0.0 version: 3.2.0(react@18.3.1) + '@portabletext/toolkit': + specifier: ^2.0.16 + version: 2.0.16 '@rexxars/react-json-inspector': specifier: ^9.0.1 version: 9.0.1(react@18.3.1)