fix(edit-content): make WYSIWYG Insert Image dialog dismissable (#35898)#35900
Conversation
Add closable, closeOnEscape and dismissableMask flags to the DotAssetSearchDialogComponent opened from the WYSIWYG field, so the Insert Image dialog can be closed via the X icon, the Esc key, or an overlay-mask click without inserting an image. The existing filter((asset) => !!asset) guard on onClose already prevents content insertion on a no-selection close. Adds a negative-path unit test covering dismissal without selection. Closes #35898
|
Claude finished @oidacra's task in 1m 36s —— View job ReviewThe fix is correct and the tests cover both paths. A few things worth a second look: 1. PR body is out of sync with the code (minor)AC5 is marked unchecked / "needs manual QA" in the description, but commit 2. Out-of-scope styling changeThe
Right now it's a silent shared-component change riding in a fix PR. 3. Replacing the
|
Add Tailwind px-4 (1rem left/right) to the dot-asset-card-list host so the asset list in the Insert Image dialog has horizontal breathing room.
…log close Subscribe to the dialog onClose without the truthy filter so the editor regains focus on every close path (image insert, X, Esc or overlay mask), satisfying AC5 without relying on PrimeNG/TinyMCE default focus behavior. Insertion still only happens when an asset is selected. Unit tests assert editor.focus() is called on both the insert and dismiss branches.
…) (#35900) ## Summary The **Insert Image** dialog opened from a **WYSIWYG field** in the New Edit Content experience had no way to be dismissed — no close (X) icon, no Esc-key support, and clicking the overlay mask did nothing. Users were trapped in the dialog and could only escape by selecting an image (then deleting it). This adds the three PrimeNG `DynamicDialog` dismissal flags to the `dialogService.open(DotAssetSearchDialogComponent, …)` call in `DotWysiwygPluginService.dotImageDialog()`: ```ts closable: true, // renders the X icon in the header closeOnEscape: true, // Esc dismisses the dialog dismissableMask: true, // overlay-mask click dismisses the dialog ``` In PrimeNG 21.1.3 these `DynamicDialogConfig` options all default to `false`, so the dialog previously rendered with no close affordances. This follows the project convention in `core-web/CLAUDE.md` ("All dialogs must have `closable: true` and `closeOnEscape: true`") and mirrors the sibling block-editor asset picker (`new-block-editor/src/lib/editor/config.utils.ts`), which already sets all three flags. No changes were needed to `DotAssetSearchDialogComponent` itself. The `onClose` subscription was also reworked: instead of the previous `filter((asset) => !!asset)` (which produced no callback at all on dismissal), it now subscribes unconditionally — inserting content only when an asset is selected, and calling `editor.focus()` on **every** close path so focus reliably returns to the editor (AC5) without depending on PrimeNG/TinyMCE default focus behavior. Additionally, a small UI tweak adds `host: { class: 'px-4' }` (Tailwind, 1rem left/right) to the shared `DotAssetCardListComponent`, giving the asset list horizontal breathing room. Note this component is also consumed by the block-editor asset picker, so the gutter change applies there too. Closes #35898 ## Acceptance Criteria - [x] AC1 — Insert Image dialog displays a close (X) icon in the header (`closable: true`; verified against PrimeNG 21.1.3 source where the default is `false`) - [x] AC2 — Clicking the X dismisses the dialog without inserting an image (`onClose` only inserts when an asset is selected; covered by the negative-path unit test) - [x] AC3 — Pressing Esc dismisses the dialog without inserting an image (`closeOnEscape: true`) - [x] AC4 — Clicking outside (overlay mask) dismisses the dialog without inserting an image (`dismissableMask: true`) — *intentional scope addition beyond the original issue ACs, justified by the reported behavior in the issue video* - [x] AC5 — After dismissal, keyboard focus returns to the WYSIWYG editor (`editor.focus()` is now called on every close path; asserted in both the insert and dismiss unit tests) - [x] AC6 — After dismissal, the editor content is unchanged (insertion is gated on a selected asset; covered by the negative-path unit test) > AC7 from the original issue (cancel in-progress upload on close) was determined **out of scope**: this dialog is browse-only. Disk uploads use a separate drag-and-drop path (`handleImageDrop`). ## Test Plan - `yarn nx lint edit-content` → 0 errors (pre-existing warnings only, in unrelated files) - `yarn nx test edit-content` → 1867 passed, 0 failures - `yarn nx lint ui` / `yarn nx test ui` → 0 errors, all green (covers the `dot-asset-card-list` change) - Unit test `should open the dialog when the button is clicked` — asserts the full `dialogConfig` (including the three new flags) **and** that `editor.focus()` is called after insertion. - Unit test `should NOT insert content when the dialog is closed without selecting an image` — mocks `onClose` emitting `undefined`, asserts `editor.insertContent` is **not** called **and** `editor.focus()` **is** called (AC5 on the dismiss path). ## Changed Files - `core-web/libs/edit-content/src/lib/fields/dot-edit-content-wysiwyg-field/dot-wysiwyg-plugin/dot-wysiwyg-plugin.service.ts` - `core-web/libs/edit-content/src/lib/fields/dot-edit-content-wysiwyg-field/dot-wysiwyg-plugin/dot-wysiwyg-plugin.service.spec.ts` - `core-web/libs/ui/src/lib/components/dot-asset-search/components/dot-asset-card-list/dot-asset-card-list.component.ts` ## Demo https://github.com/user-attachments/assets/da2813c3-625c-482f-88c9-1be01ee2bcf9
Summary
The Insert Image dialog opened from a WYSIWYG field in the New Edit Content experience had no way to be dismissed — no close (X) icon, no Esc-key support, and clicking the overlay mask did nothing. Users were trapped in the dialog and could only escape by selecting an image (then deleting it).
This adds the three PrimeNG
DynamicDialogdismissal flags to thedialogService.open(DotAssetSearchDialogComponent, …)call inDotWysiwygPluginService.dotImageDialog():In PrimeNG 21.1.3 these
DynamicDialogConfigoptions all default tofalse, so the dialog previously rendered with no close affordances. This follows the project convention incore-web/CLAUDE.md("All dialogs must haveclosable: trueandcloseOnEscape: true") and mirrors the sibling block-editor asset picker (new-block-editor/src/lib/editor/config.utils.ts), which already sets all three flags. No changes were needed toDotAssetSearchDialogComponentitself.The
onClosesubscription was also reworked: instead of the previousfilter((asset) => !!asset)(which produced no callback at all on dismissal), it now subscribes unconditionally — inserting content only when an asset is selected, and callingeditor.focus()on every close path so focus reliably returns to the editor (AC5) without depending on PrimeNG/TinyMCE default focus behavior.Additionally, a small UI tweak adds
host: { class: 'px-4' }(Tailwind, 1rem left/right) to the sharedDotAssetCardListComponent, giving the asset list horizontal breathing room. Note this component is also consumed by the block-editor asset picker, so the gutter change applies there too.Closes #35898
Acceptance Criteria
closable: true; verified against PrimeNG 21.1.3 source where the default isfalse)onCloseonly inserts when an asset is selected; covered by the negative-path unit test)closeOnEscape: true)dismissableMask: true) — intentional scope addition beyond the original issue ACs, justified by the reported behavior in the issue videoeditor.focus()is now called on every close path; asserted in both the insert and dismiss unit tests)Test Plan
yarn nx lint edit-content→ 0 errors (pre-existing warnings only, in unrelated files)yarn nx test edit-content→ 1867 passed, 0 failuresyarn nx lint ui/yarn nx test ui→ 0 errors, all green (covers thedot-asset-card-listchange)should open the dialog when the button is clicked— asserts the fulldialogConfig(including the three new flags) and thateditor.focus()is called after insertion.should NOT insert content when the dialog is closed without selecting an image— mocksonCloseemittingundefined, assertseditor.insertContentis not called andeditor.focus()is called (AC5 on the dismiss path).Changed Files
core-web/libs/edit-content/src/lib/fields/dot-edit-content-wysiwyg-field/dot-wysiwyg-plugin/dot-wysiwyg-plugin.service.tscore-web/libs/edit-content/src/lib/fields/dot-edit-content-wysiwyg-field/dot-wysiwyg-plugin/dot-wysiwyg-plugin.service.spec.tscore-web/libs/ui/src/lib/components/dot-asset-search/components/dot-asset-card-list/dot-asset-card-list.component.tsDemo
CleanShot.2026-06-02.at.11.26.58.mp4
This PR fixes: #35898