Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate with ElementInternals #1121

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,30 +55,38 @@ Like an HTML `<textarea>`, `<trix-editor>` accepts `autofocus` and `placeholder`

## Integrating With Forms

To submit the contents of a `<trix-editor>` with a form, first define a hidden input field in the form and assign it an `id`. Then reference that `id` in the editor’s `input` attribute.
To label a `<trix-editor>` element, render the element with an `[id]` attribute, then render a `<label>` element with a `[for]` attribute that corresponds to the `[id]`:

```html
<label for="editor">Editor</label>
<trix-editor id="editor"></trix-editor>
```

To submit the contents of a `<trix-editor>` with a `<form>`, render the element with a `[name]` attribute and its initial value as its inner HTML.

```html
<form …>
<input id="x" type="hidden" name="content">
<trix-editor input="x"></trix-editor>
<trix-editor name="content"></trix-editor>
</form>
```

Trix will automatically update the value of the hidden input field with each change to the editor.
To associate the element with a `<form>` that isn't an ancestor, render the element with a `[form]` attribute that references the `<form>` element by its `[id]`:

```html
<form id="a-form-element" …></form>
<trix-editor name="content" form="a-form-element"></trix-editor>
```

## Populating With Stored Content

To populate a `<trix-editor>` with stored content, include that content in the associated input element’s `value` attribute.
To populate a `<trix-editor>` with stored content, include that content as HTML inside the element’s inner HTML.

```html
<form …>
<input id="x" value="Editor content goes here" type="hidden" name="content">
<trix-editor input="x"></trix-editor>
<trix-editor input="x">Editor content goes here</trix-editor>
</form>
```

Always use an associated input element to safely populate an editor. Trix won’t load any HTML content inside a `<trix-editor>…</trix-editor>` tag.

## Styling Formatted Content

To ensure what you see when you edit is what you see when you save, use a CSS class name to scope styles for Trix formatted content. Apply this class name to your `<trix-editor>` element, and to a containing element when you render stored Trix content for display in your application.
Expand Down
8 changes: 7 additions & 1 deletion assets/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
Trix.Inspector.install(event.target);
});

document.addEventListener("trix-change", function(event) {
var input = document.getElementById("input")
input.value = event.target.value
})

document.addEventListener("trix-attachment-add", function(event) {
var attachment = event.attachment;
if (attachment.file) {
Expand Down Expand Up @@ -72,7 +77,8 @@
</head>
<body>
<main>
<trix-editor autofocus class="trix-content" input="input"></trix-editor>
<label for="editor">Input</label>
<trix-editor autofocus id="editor" class="trix-content"></trix-editor>
<details id="output">
<summary>Output</summary>
<textarea readonly id="input"></textarea>
Expand Down
41 changes: 19 additions & 22 deletions src/test/system/custom_element_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ testGroup("Custom element API", { template: "editor_empty" }, () => {

test("editor resets to its original value on form reset", async () => {
const element = getEditorElement()
const { form } = element.inputElement
const { form } = element

await typeCharacters("hello")
form.reset()
Expand All @@ -451,7 +451,7 @@ testGroup("Custom element API", { template: "editor_empty" }, () => {

test("editor resets to last-set value on form reset", async () => {
const element = getEditorElement()
const { form } = element.inputElement
const { form } = element

element.value = "hi"
await typeCharacters("hello")
Expand All @@ -461,7 +461,7 @@ testGroup("Custom element API", { template: "editor_empty" }, () => {

test("editor respects preventDefault on form reset", async () => {
const element = getEditorElement()
const { form } = element.inputElement
const { form } = element
const preventDefault = (event) => event.preventDefault()

await typeCharacters("hello")
Expand All @@ -473,27 +473,24 @@ testGroup("Custom element API", { template: "editor_empty" }, () => {
})
})

testGroup("<label> support", { template: "editor_with_labels" }, () => {
test("associates all label elements", () => {
const labels = [ document.getElementById("label-1"), document.getElementById("label-3") ]
assert.deepEqual(getEditorElement().labels, labels)
})
testGroup("HTML sanitization", { template: "editor_unsafe_html" }, () => {
test("editor sanitizes initial value", async () => {
const element = getEditorElement()

test("focuses when <label> clicked", () => {
document.getElementById("label-1").click()
assert.equal(getEditorElement(), document.activeElement)
})
expectDocument("safe\n")

test("focuses when <label> descendant clicked", () => {
document.getElementById("label-1").querySelector("span").click()
assert.equal(getEditorElement(), document.activeElement)
assert.equal(element.innerHTML, "<div><!--block-->safe</div>")
})
})

testGroup("<label> support", { template: "editor_with_labels" }, () => {
test("associates all label elements", () => {
const element = getEditorElement()
const labels = Array.from(element.labels)
const controls = labels.map((label) => label.control)

test("does not focus when <label> controls another element", () => {
const label = document.getElementById("label-2")
assert.notEqual(getEditorElement(), label.control)
label.click()
assert.notEqual(getEditorElement(), document.activeElement)
assert.deepEqual(labels, [ document.getElementById("label-1"), document.getElementById("label-3") ])
assert.deepEqual(controls, [ element, element ])
})
})

Expand All @@ -505,8 +502,8 @@ testGroup("form property references its <form>", { template: "editors_with_forms
})

test("transitively accesses its related <input> element's <form>", () => {
const form = document.getElementById("input-form")
const editor = document.getElementById("editor-with-input-form")
const form = document.getElementById("attribute-form")
const editor = document.getElementById("editor-with-attribute-form")
assert.equal(editor.form, form)
})

Expand Down
10 changes: 1 addition & 9 deletions src/test/system/installation_process_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,21 @@ testGroup("Installation process", { template: "editor_html" }, () => {
})

testGroup("Installation process without specified elements", { template: "editor_empty" }, () =>
test("creates identified toolbar and input elements", () => {
test("creates identified toolbar", () => {
const editorElement = getEditorElement()

const toolbarId = editorElement.getAttribute("toolbar")
assert.ok(/trix-toolbar-\d+/.test(toolbarId), `toolbar id not assert.ok ${JSON.stringify(toolbarId)}`)
const toolbarElement = document.getElementById(toolbarId)
assert.ok(toolbarElement, "toolbar element not assert.ok")
assert.equal(editorElement.toolbarElement, toolbarElement)

const inputId = editorElement.getAttribute("input")
assert.ok(/trix-input-\d+/.test(inputId), `input id not assert.ok ${JSON.stringify(inputId)}`)
const inputElement = document.getElementById(inputId)
assert.ok(inputElement, "input element not assert.ok")
assert.equal(editorElement.inputElement, inputElement)
})
)

testGroup("Installation process with specified elements", { template: "editor_with_toolbar_and_input" }, () => {
test("uses specified elements", () => {
const editorElement = getEditorElement()
assert.equal(editorElement.toolbarElement, document.getElementById("my_toolbar"))
assert.equal(editorElement.inputElement, document.getElementById("my_input"))
assert.equal(editorElement.value, "<div>Hello world</div>")
})

Expand All @@ -58,7 +51,6 @@ testGroup("Installation process with specified elements", { template: "editor_wi

const editorElement = getEditorElement()
assert.equal(editorElement.toolbarElement, document.getElementById("my_toolbar"))
assert.equal(editorElement.inputElement, document.getElementById("my_input"))
assert.equal(editorElement.value, "<div>Hello world</div>")
})
})
4 changes: 2 additions & 2 deletions src/test/test_helpers/fixtures/editor_html.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export default () =>
`<input id="my_input" type="hidden" value="&lt;div&gt;Hello world&lt;/div&gt;">
<trix-editor input="my_input" autofocus placeholder="Say hello..."></trix-editor>`
`<trix-editor autofocus placeholder="Say hello..."><div>Hello world</div></trix-editor>
`
2 changes: 2 additions & 0 deletions src/test/test_helpers/fixtures/editor_unsafe_html.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export default () => `<trix-editor autofocus><div>safe</div><script>alert("unsafe")</script></trix-editor>
`
3 changes: 1 addition & 2 deletions src/test/test_helpers/fixtures/editor_with_image.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { TEST_IMAGE_URL } from "./test_image_url"

export default () =>
`<trix-editor input="my_input" autofocus placeholder="Say hello..."></trix-editor>
<input id="my_input" type="hidden" value="ab&lt;img src=&quot;${TEST_IMAGE_URL}&quot; width=&quot;10&quot; height=&quot;10&quot;&gt;">`
`<trix-editor autofocus placeholder="Say hello...">ab<img src="${TEST_IMAGE_URL}" width="10" height="10"></trix-editor>`
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export default () =>
`<ul id="my_editor">
<li><trix-toolbar id="my_toolbar"></trix-toolbar></li>
<li><trix-editor toolbar="my_toolbar" input="my_input" autofocus placeholder="Say hello..."></trix-editor></li>
<li><input id="my_input" type="hidden" value="&lt;div&gt;Hello world&lt;/div&gt;"></li>
<li><trix-editor toolbar="my_toolbar" autofocus placeholder="Say hello..."><div>Hello world</div></trix-editor></li>
</ul>`
6 changes: 2 additions & 4 deletions src/test/test_helpers/fixtures/editors_with_forms.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ export default () =>
<trix-editor id="editor-with-ancestor-form"></trix-editor>
</form>

<form id="input-form">
<input type="hidden" id="hidden-input">
</form>
<trix-editor id="editor-with-input-form" input="hidden-input"></trix-editor>
<form id="attribute-form"></form>
<trix-editor id="editor-with-attribute-form" form="attribute-form"></trix-editor>

<trix-editor id="editor-with-no-form"></trix-editor>`
2 changes: 2 additions & 0 deletions src/test/test_helpers/fixtures/fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import StringPiece from "trix/models/string_piece"
import editorDefaultAriaLabel from "./editor_default_aria_label"
import editorEmpty from "./editor_empty"
import editorHtml from "./editor_html"
import editorUnsafeHtml from "./editor_unsafe_html"
import editorInTable from "./editor_in_table"
import editorWithBlockStyles from "./editor_with_block_styles"
import editorWithBoldStyles from "./editor_with_bold_styles"
Expand All @@ -25,6 +26,7 @@ export const fixtureTemplates = {
"editor_default_aria_label": editorDefaultAriaLabel,
"editor_empty": editorEmpty,
"editor_html": editorHtml,
"editor_unsafe_html": editorUnsafeHtml,
"editor_in_table": editorInTable,
"editor_with_block_styles": editorWithBlockStyles,
"editor_with_bold_styles": editorWithBoldStyles,
Expand Down
2 changes: 1 addition & 1 deletion src/trix/controllers/editor_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ export default class EditorController extends Controller {
updateInputElement() {
const element = this.compositionController.getSerializableElement()
const value = serializeToContentType(element, "text/html")
return this.editorElement.setInputElementValue(value)
return this.editorElement.setFormValue(value)
}

notifyEditorElement(message, data) {
Expand Down
Loading
Loading