Files
twenty/packages
Thomas Trompette 933ae9c20b fix(page-layout): render standalone rich text widget on record pages (#23435)
Fixes #21093

## Problem

A `STANDALONE_RICH_TEXT` widget can be created on a record page layout
through the metadata API, and `getPageLayoutWidgets` returns it, but the
record page renders nothing for it.

`StandaloneRichTextWidget` only resolved a target id when `layoutType
=== PageLayoutType.DASHBOARD`, then bailed out with `return null`
whenever that id was undefined. On a record page it never rendered.

## Why the guard was there

It was correct when it was written. In #16437 the widget used the full
`BLOCK_SCHEMA` and uploaded files:

```ts
return await uploadAttachmentFile(file, {
  id: dashboardId,
  targetObjectNameSingular: CoreObjectNameSingular.Dashboard,
});
```

Without a dashboard id there was nowhere to attach an upload, and
dashboards were the only layout type in play, so refusing to render was
a coherent stance.

#17934 then disabled file upload because the urls were not signed. It
swapped in `DASHBOARD_BLOCK_SCHEMA`, dropped `useUploadAttachmentFile`
and `prepareBodyWithSignedUrls`, and added `filterSupportedBlocks` to
strip file blocks out of previously saved bodies. It left behind the
attachments query, the `attachments` prop and the `useAttachmentSync`
call.

After that, `dashboardId` had one consumer left: a filter that could no
longer match anything. The `return null` underneath it was guarding
nothing. Record page layouts then made the widget reachable outside
dashboards, and the stale guard blanked it.

## Fix

The body lives on the widget configuration, not on the target record, so
the widget needs no record id to display.

The leftover attachment fetch has nothing to act on:

- `DASHBOARD_BLOCK_SCHEMA` declares only paragraph, heading, lists,
checklist, codeBlock, table and quote. No image, file, video or audio
block.
- The three sync utils all key off `ATTACHMENT_BLOCK_TYPES = ['image',
'file', 'video', 'audio']`, so they return empty for any body this
editor can produce.
- No `uploadFile` option, no `onPaste` handler, and
`filterSupportedBlocks` strips unsupported blocks on load, so such a
block cannot get in.

So rather than generalise the attachment filter to every object type,
this removes it: the `useFindManyRecords` call, the `attachments` prop,
and the `useAttachmentSync` call in `StandaloneRichTextEditorContent`.
It finishes the cleanup #17934 started. `useAttachmentSync` is untouched
and still used by `RichTextFieldEditor`, which does support file blocks.

Net result is a pure deletion, and the widget renders on every layout
type.

If image blocks are ever added back to `DASHBOARD_BLOCK_SCHEMA`,
attachment sync will need to come back with them.

## Testing

Local instance, widget created through `createPageLayoutWidget` on the
default Company record page layout.

- On the unpatched component the widget is absent from the page.
- With the fix it renders read-only in the record page column.
- Also verified with the payload shape from the issue (`markdown` set,
`blocknote: null`); the server converts it to blocknote on write, so it
renders too.
- Verified on `calendarEvent`, an object with no `attachments` relation.
Renders clean, no console or GraphQL errors. Generalising the old filter
instead would have sent `targetCalendarEventId` and hit `Object
attachment doesn't have any "targetCalendarEventId" field.`
- Dashboard rendering unchanged, and editing still round-trips: typed
into the widget in dashboard edit mode, hit Save, confirmed the new body
in `core.pageLayoutWidget`.
2026-07-29 06:56:19 +00:00
..