Fix repeated Turbo dialog streams

Repeated streams reopened a detached template dialog instead of the
existing DOM dialog.
This commit is contained in:
Alexander Brandon Coles
2026-06-13 15:13:40 +01:00
parent 83d9850141
commit 7314e9a10c
2 changed files with 15 additions and 5 deletions
@@ -114,6 +114,12 @@ describe('AttributeHelpTextModalService', () => {
});
it('should handle Turbo Stream dialog response and update dialog', async () => {
const showModalSpy = vi.spyOn(HTMLDialogElement.prototype, 'showModal') as unknown as Mock<() => void>;
showModalSpy.mockImplementation(function showModal(this:HTMLDialogElement) {
this.open = true;
});
expect(document.querySelector('dialog#test3')).toBeFalsy();
await modalService.show('3');
@@ -128,6 +134,7 @@ describe('AttributeHelpTextModalService', () => {
await modalService.show('3');
expect(fetchSpy).toHaveBeenCalledTimes(2);
expect(showModalSpy.mock.contexts.at(-1)).toBe(dialog);
let mutation = await waitForElementMutation(dialog);
@@ -138,6 +145,7 @@ describe('AttributeHelpTextModalService', () => {
await modalService.show('3');
expect(fetchSpy).toHaveBeenCalledTimes(3);
expect(showModalSpy.mock.contexts.at(-1)).toBe(dialog);
mutation = await waitForElementMutation(dialog);
+7 -5
View File
@@ -4,7 +4,7 @@ import { Idiomorph } from 'idiomorph';
export function registerDialogStreamAction() {
StreamActions.closeDialog = function closeDialogStreamAction(this:StreamElement) {
const dialog = document.querySelector(this.target)!;
const additionalData = JSON.parse(this.getAttribute('additional') || '{}') as unknown;
const additionalData = JSON.parse(this.getAttribute('additional') ?? '{}') as unknown;
// dispatching with submitted: true to indicate that the behavior of a successful submission should
// be triggered (i.e. reloading the ui)
@@ -16,10 +16,12 @@ export function registerDialogStreamAction() {
const content = this.templateElement.content;
const dialog = content.querySelector('dialog')!;
const existingElement = document.getElementById(dialog.id);
let dialogToShow = dialog;
if (existingElement && existingElement instanceof HTMLDialogElement) {
if (existingElement instanceof HTMLDialogElement) {
// a dialog with this id already exists: update (morph) its contents.
Idiomorph.morph(existingElement, dialog.innerHTML, { morphStyle: 'innerHTML' });
dialogToShow = existingElement;
} else {
// no dialog with this id exists: append <dialog-helper> to the body.
document.body.append(content);
@@ -39,13 +41,13 @@ export function registerDialogStreamAction() {
}
// Auto-show the modal
dialog.showModal();
dialogToShow.showModal();
// Hack to fix the width calculation of nested elements
// such as the CKEditor toolbar.
setTimeout(() => {
const width = dialog.offsetWidth;
dialog.style.width = `${width + 1}px`;
const width = dialogToShow.offsetWidth;
dialogToShow.style.width = `${width + 1}px`;
}, 250);
};
}