Skip to content

Commit 989bf70

Browse files
committed
Make Trix compatible with morphing
Morphing the trix editor will leave it in an unusuable state because morphing will replace the bootstrapping elements without triggering the connection logic. The solution is based on adding an attribute to flag the editor as initialized and detect when that attribute is modified. See hotwired/turbo-rails#533 (comment)
1 parent 7bf3e5a commit 989bf70

File tree

4 files changed

+47
-1
lines changed

4 files changed

+47
-1
lines changed

Diff for: karma.conf.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ if (process.env.SAUCE_ACCESS_KEY) {
7070
browserName: "safari",
7171
platform: "ios",
7272
device: "iPhone X Simulator",
73-
version: "13.0"
73+
version: "latest"
7474
},
7575
sl_android_9: {
7676
base: "SauceLabs",

Diff for: src/test/system.js

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import "test/system/html_replacement_test"
1515
import "test/system/installation_process_test"
1616
import "test/system/level_2_input_test"
1717
import "test/system/list_formatting_test"
18+
import "test/system/morphing_test"
1819
import "test/system/mutation_input_test"
1920
import "test/system/pasting_test"
2021
import "test/system/text_formatting_test"

Diff for: src/test/system/morphing_test.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { assert, expectDocument, test, testGroup } from "test/test_helper"
2+
import { nextFrame } from "../test_helpers/timing_helpers"
3+
4+
testGroup("morphing", { template: "editor_empty" }, () => {
5+
test("removing the 'initialized' attribute will reset the editor and recreate toolbar", async () => {
6+
const element = getEditorElement()
7+
8+
assert.ok(element.hasAttribute("initialized"))
9+
10+
const originalToolbar = element.toolbarElement
11+
element.toolbarElement.remove()
12+
element.removeAttribute("toolbar")
13+
element.removeAttribute("initialized")
14+
15+
await nextFrame()
16+
assert.ok(element.hasAttribute("initialized"))
17+
assert.ok(element.toolbarElement)
18+
assert.notEqual(originalToolbar, element.toolbarElement)
19+
})
20+
})

Diff for: src/trix/elements/trix_editor_element.js

+25
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,8 @@ class LegacyDelegate {
348348
export default class TrixEditorElement extends HTMLElement {
349349
static formAssociated = "ElementInternals" in window
350350

351+
static observedAttributes = [ "initialized" ]
352+
351353
#delegate
352354

353355
constructor() {
@@ -410,6 +412,7 @@ export default class TrixEditorElement extends HTMLElement {
410412
} else if (this.parentNode) {
411413
const toolbarId = `trix-toolbar-${this.trixId}`
412414
this.setAttribute("toolbar", toolbarId)
415+
this.toggleAttribute("internal-toolbar", true)
413416
const element = makeElement("trix-toolbar", { id: toolbarId })
414417
this.parentNode.insertBefore(element, this)
415418
return element
@@ -453,6 +456,14 @@ export default class TrixEditorElement extends HTMLElement {
453456
this.editor?.loadHTML(this.defaultValue)
454457
}
455458

459+
// Element callbacks
460+
461+
attributeChangedCallback(name, oldValue, newValue) {
462+
if (name === "initialized" && oldValue != null && oldValue !== newValue) {
463+
requestAnimationFrame(() => this.reconnect())
464+
}
465+
}
466+
456467
// Controller delegate methods
457468

458469
notify(message, data) {
@@ -485,6 +496,8 @@ export default class TrixEditorElement extends HTMLElement {
485496
}
486497
this.editorController.registerSelectionManager()
487498
this.#delegate.connectedCallback()
499+
500+
this.toggleAttribute("initialized", true)
488501
autofocus(this)
489502
}
490503
}
@@ -494,6 +507,18 @@ export default class TrixEditorElement extends HTMLElement {
494507
this.#delegate.disconnectedCallback()
495508
}
496509

510+
async reconnect() {
511+
this.removeInternalToolbar()
512+
this.disconnectedCallback()
513+
this.connectedCallback()
514+
}
515+
516+
removeInternalToolbar() {
517+
if (this.toolbarElement?.hasAttribute("internal-toolbar")) {
518+
this.toolbarElement.remove()
519+
}
520+
}
521+
497522
// Form support
498523

499524
checkValidity() {

0 commit comments

Comments
 (0)