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

Replace hidden <input> with ElementInternals integration #1128

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

seanpdoyle
Copy link
Contributor

@seanpdoyle seanpdoyle commented Jan 30, 2024

Closes #1023

Replace the requirement for an <input type="hidden"> element with direct <form> integration through built-in support for ElementInternals.

According to the Form-associated custom elements section of More capable form controls, various behaviors that the <trix-editor> element was recreating are provided out of the box.

For example, the <input type="hidden">-[input] attribute pairing can be achieved through ElementInternals.setFormValue. Similarly, the <label> element support can be achieved through
ElementInternals.labels.

TODO before merging:

  • Control through browser feature-detection and a Trix.config.editor object

TODO after merging:

@seanpdoyle
Copy link
Contributor Author

@afcapel do you have any thoughts or ideas about the TODO before merging list in the PR description?

@afcapel
Copy link
Contributor

afcapel commented Feb 6, 2024

@seanpdoyle thanks for kicking this off. ElementInternals is certainly the way of the future and it makes the whole setup look much simpler. It's also exciting that we will be able to add attributes such as required to the <trix-editor> without needing any extra JS.

A couple of things to consider:

  • ElementInternals is only available since Safari 16.4 (released 2023-03-27)
  • We should also look for the migration path for existing projects. We probably can update the markup in actiontext to discard the hidden input and start using the <trix-editor> element itself. But what's the story for projects that integrated Trix without actiontext?

Given that, do you think it's possible for the two systems (ElementInternals and hidden input) to coexist for a while to ease the migration?

@seanpdoyle
Copy link
Contributor Author

Given that, do you think it's possible for the two systems to coexist for a while to ease the migration?

I do. I'd created this item in the PR description to vaguely capture that:

  • Control through browser feature-detection and a Trix.config.editor object

I'd originally explored this work by introducing two delegate classes (something like (ElementInternalsDelegate and LegacyDelegate). I abandoned that in favor of demonstrating the possibilities in a straightforward way. Now that the approach is proven, I'm curious of what kind of configuration options are available.

On the JS-only side, a Trix.config value is straightforward. I'm not sure what to name it, but it could be as simple as a Trix.config.legacy = false boolean to use the new stuff.

On the Action Text side, I'm unsure about how to best forward that server-side decision along. A <meta> tag? Guidance to set a global window.Trix.config value? A Rails helper method to do the behind the scenes work for you?

Like you mentioned, there are Form Builder implications to that setting, since using the newer machinery would mean that the <input type="hidden"> is no longer necessary. It'd be crucial to ensure that the setting is respected all the way through from an application's config/ file to its server-generated HTML to its client-side Trix instance.

Do you have any thoughts on what that configuration might entail?

seanpdoyle added a commit to seanpdoyle/trix that referenced this pull request Feb 7, 2024
In preparation for [basecamp#1128][], this commit introduces a module-private
`Delegate` class to serve as a representation of what form integration
requires for the `<trix-editor>` custom element. The structure of the
`Delegate` class mirrors that of the `TrixEditorElement` from which its
contents are extracted.

First, there are the properties that mimic those of most form controls,
including:

* `labels`
* `form`
* `name`
* `value`
* `defaultValue`

With the exception of `labels`, property access is mostly proxied
through the associated `<input type="hidden">` element (accessed through
its own `inputElement` property).

Next, the `Delegate` defines methods that correspond to the Custom
Element lifecycle events, including:

* `connectedCallback`
* `disconnectedCallback`
* `setFormValue`

The connected and disconnected callbacks mirror that of the
`TrixEditorElement` itself. These callbacks attach and remove event
listeners for `click` and `reset` events.

The `setFormValue` is named to correspond with
[ElementInternals.setFormValue][]. Along with introducing this callback
method, this commit renames the `TrixEditorElement.setInputElementValue`
method to `TrixEditorElement.setFormValue`.

In addition to renaming `setInputElementValue`, this commit also defines
`TrixEditorElement.formResetCallback`, then implements
`TrixEditorElement.reset` as an alias. The name mirrors the
[ElementInternals.formResetCallback][].

[basecamp#1128]: basecamp#1128
[ElementInternals.setFormValue]: https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setFormValue
[ElementInternals.formResetCallback]: https://web.dev/articles/more-capable-form-controls#void_formresetcallback
seanpdoyle added a commit to seanpdoyle/trix that referenced this pull request Feb 7, 2024
In preparation for [basecamp#1128][], this commit introduces a module-private
`Delegate` class to serve as a representation of what form integration
requires for the `<trix-editor>` custom element. The structure of the
`Delegate` class mirrors that of the `TrixEditorElement` from which its
contents are extracted.

First, there are the properties that mimic those of most form controls,
including:

* `labels`
* `form`
* `name`
* `value`
* `defaultValue`

With the exception of `labels`, property access is mostly proxied
through the associated `<input type="hidden">` element (accessed through
its own `inputElement` property).

Next, the `Delegate` defines methods that correspond to the Custom
Element lifecycle events, including:

* `connectedCallback`
* `disconnectedCallback`
* `setFormValue`

The connected and disconnected callbacks mirror that of the
`TrixEditorElement` itself. These callbacks attach and remove event
listeners for `click` and `reset` events.

The `setFormValue` is named to correspond with
[ElementInternals.setFormValue][]. Along with introducing this callback
method, this commit renames the `TrixEditorElement.setInputElementValue`
method to `TrixEditorElement.setFormValue`.

In addition to renaming `setInputElementValue`, this commit also defines
`TrixEditorElement.formResetCallback`, then implements
`TrixEditorElement.reset` as an alias. The name mirrors the
[ElementInternals.formResetCallback][].

[basecamp#1128]: basecamp#1128
[ElementInternals.setFormValue]: https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setFormValue
[ElementInternals.formResetCallback]: https://web.dev/articles/more-capable-form-controls#void_formresetcallback
seanpdoyle added a commit to seanpdoyle/trix that referenced this pull request Feb 7, 2024
In preparation for [basecamp#1128][], this commit introduces a module-private
`Delegate` class to serve as a representation of what form integration
requires for the `<trix-editor>` custom element. The structure of the
`Delegate` class mirrors that of the `TrixEditorElement` from which its
contents are extracted.

First, there are the properties that mimic those of most form controls,
including:

* `labels`
* `form`
* `name`
* `value`
* `defaultValue`
* `type`

With the exception of `labels`, property access is mostly proxied
through the associated `<input type="hidden">` element (accessed through
its own `inputElement` property).

Next, the `Delegate` defines methods that correspond to the Custom
Element lifecycle events, including:

* `connectedCallback`
* `disconnectedCallback`
* `setFormValue`

The connected and disconnected callbacks mirror that of the
`TrixEditorElement` itself. These callbacks attach and remove event
listeners for `click` and `reset` events.

The `setFormValue` is named to correspond with
[ElementInternals.setFormValue][]. Along with introducing this callback
method, this commit renames the `TrixEditorElement.setInputElementValue`
method to `TrixEditorElement.setFormValue`.

In addition to renaming `setInputElementValue`, this commit also defines
`TrixEditorElement.formResetCallback`, then implements
`TrixEditorElement.reset` as an alias. The name mirrors the
[ElementInternals.formResetCallback][].

[basecamp#1128]: basecamp#1128
[ElementInternals.setFormValue]: https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setFormValue
[ElementInternals.formResetCallback]: https://web.dev/articles/more-capable-form-controls#void_formresetcallback
seanpdoyle added a commit to seanpdoyle/trix that referenced this pull request Feb 7, 2024
In preparation for [basecamp#1128][], this commit introduces a module-private
`Delegate` class to serve as a representation of what form integration
requires for the `<trix-editor>` custom element. The structure of the
`Delegate` class mirrors that of the `TrixEditorElement` from which its
contents are extracted.

First, there are the properties that mimic those of most form controls,
including:

* `labels`
* `form`
* `name`
* `value`
* `defaultValue`
* `type`

With the exception of `labels`, property access is mostly proxied
through the associated `<input type="hidden">` element (accessed through
its own `inputElement` property).

Next, the `Delegate` defines methods that correspond to the Custom
Element lifecycle events, including:

* `connectedCallback`
* `disconnectedCallback`
* `setFormValue`

The connected and disconnected callbacks mirror that of the
`TrixEditorElement` itself. These callbacks attach and remove event
listeners for `click` and `reset` events.

The `setFormValue` is named to correspond with
[ElementInternals.setFormValue][]. Along with introducing this callback
method, this commit renames the `TrixEditorElement.setInputElementValue`
method to `TrixEditorElement.setFormValue`.

In addition to renaming `setInputElementValue`, this commit also defines
`TrixEditorElement.formResetCallback` (along with other empty
callbacks), then implements `TrixEditorElement.reset` as an alias. The
name mirrors the [ElementInternals.formResetCallback][].

[basecamp#1128]: basecamp#1128
[ElementInternals.setFormValue]: https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setFormValue
[ElementInternals.formResetCallback]: https://web.dev/articles/more-capable-form-controls#void_formresetcallback
seanpdoyle added a commit to seanpdoyle/trix that referenced this pull request Feb 7, 2024
In preparation for [basecamp#1128][], this commit introduces a module-private
`Delegate` class to serve as a representation of what form integration
requires for the `<trix-editor>` custom element. The structure of the
`Delegate` class mirrors that of the `TrixEditorElement` from which its
contents are extracted.

First, there are the properties that mimic those of most form controls,
including:

* `labels`
* `form`
* `name`
* `value`
* `defaultValue`
* `type`

With the exception of `labels`, property access is mostly proxied
through the associated `<input type="hidden">` element (accessed through
its own `inputElement` property).

Next, the `Delegate` defines methods that correspond to the Custom
Element lifecycle events, including:

* `connectedCallback`
* `disconnectedCallback`
* `setFormValue`

The connected and disconnected callbacks mirror that of the
`TrixEditorElement` itself. These callbacks attach and remove event
listeners for `click` and `reset` events.

The `setFormValue` is named to correspond with
[ElementInternals.setFormValue][]. Along with introducing this callback
method, this commit renames the `TrixEditorElement.setInputElementValue`
method to `TrixEditorElement.setFormValue`.

In addition to renaming `setInputElementValue`, this commit also defines
`TrixEditorElement.formResetCallback` (along with other empty
callbacks), then implements `TrixEditorElement.reset` as an alias. The
name mirrors the [ElementInternals.formResetCallback][].

[basecamp#1128]: basecamp#1128
[ElementInternals.setFormValue]: https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setFormValue
[ElementInternals.formResetCallback]: https://web.dev/articles/more-capable-form-controls#void_formresetcallback
seanpdoyle added a commit to seanpdoyle/trix that referenced this pull request Feb 7, 2024
In preparation for [basecamp#1128][], this commit introduces a module-private
`Delegate` class to serve as a representation of what form integration
requires for the `<trix-editor>` custom element. The structure of the
`Delegate` class mirrors that of the `TrixEditorElement` from which its
contents are extracted.

First, there are the properties that mimic those of most form controls,
including:

* `labels`
* `form`
* `name`
* `value`
* `defaultValue`
* `type`

With the exception of `labels`, property access is mostly proxied
through the associated `<input type="hidden">` element (accessed through
its own `inputElement` property).

Next, the `Delegate` defines methods that correspond to the Custom
Element lifecycle events, including:

* `connectedCallback`
* `disconnectedCallback`
* `setFormValue`

The connected and disconnected callbacks mirror that of the
`TrixEditorElement` itself. These callbacks attach and remove event
listeners for `click` and `reset` events.

The `setFormValue` is named to correspond with
[ElementInternals.setFormValue][]. Along with introducing this callback
method, this commit renames the `TrixEditorElement.setInputElementValue`
method to `TrixEditorElement.setFormValue`.

In addition to renaming `setInputElementValue`, this commit also defines
`TrixEditorElement.formResetCallback` (along with other empty
callbacks), then implements `TrixEditorElement.reset` as an alias. The
name mirrors the [ElementInternals.formResetCallback][].

[basecamp#1128]: basecamp#1128
[ElementInternals.setFormValue]: https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setFormValue
[ElementInternals.formResetCallback]: https://web.dev/articles/more-capable-form-controls#void_formresetcallback
seanpdoyle added a commit to seanpdoyle/trix that referenced this pull request Feb 7, 2024
In preparation for [basecamp#1128][], this commit introduces a module-private
`Delegate` class to serve as a representation of what form integration
requires for the `<trix-editor>` custom element. The structure of the
`Delegate` class mirrors that of the `TrixEditorElement` from which its
contents are extracted.

First, there are the properties that mimic those of most form controls,
including:

* `labels`
* `form`
* `name`
* `value`
* `defaultValue`
* `type`

With the exception of `labels`, property access is mostly proxied
through the associated `<input type="hidden">` element (accessed through
its own `inputElement` property).

Next, the `Delegate` defines methods that correspond to the Custom
Element lifecycle events, including:

* `connectedCallback`
* `disconnectedCallback`
* `setFormValue`

The connected and disconnected callbacks mirror that of the
`TrixEditorElement` itself. These callbacks attach and remove event
listeners for `click` and `reset` events.

The `setFormValue` is named to correspond with
[ElementInternals.setFormValue][]. Along with introducing this callback
method, this commit renames the `TrixEditorElement.setInputElementValue`
method to `TrixEditorElement.setFormValue`.

In addition to renaming `setInputElementValue`, this commit also defines
`TrixEditorElement.formResetCallback` (along with other empty
callbacks), then implements `TrixEditorElement.reset` as an alias. The
name mirrors the [ElementInternals.formResetCallback][].

[basecamp#1128]: basecamp#1128
[ElementInternals.setFormValue]: https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setFormValue
[ElementInternals.formResetCallback]: https://web.dev/articles/more-capable-form-controls#void_formresetcallback
@seanpdoyle seanpdoyle force-pushed the element-internals branch 6 times, most recently from 3e68af9 to 92dd79e Compare February 9, 2024 04:29
seanpdoyle added a commit to seanpdoyle/trix that referenced this pull request Feb 9, 2024
In preparation for [basecamp#1128][], this commit introduces a module-private
`Delegate` class to serve as a representation of what form integration
requires for the `<trix-editor>` custom element. The structure of the
`Delegate` class mirrors that of the `TrixEditorElement` from which its
contents are extracted.

First, there are the properties that mimic those of most form controls,
including:

* `labels`
* `form`
* `name`
* `value`
* `defaultValue`
* `type`

With the exception of `labels`, property access is mostly proxied
through the associated `<input type="hidden">` element (accessed through
its own `inputElement` property).

Next, the `Delegate` defines methods that correspond to the Custom
Element lifecycle events, including:

* `connectedCallback`
* `disconnectedCallback`
* `setFormValue`

The connected and disconnected callbacks mirror that of the
`TrixEditorElement` itself. These callbacks attach and remove event
listeners for `click` and `reset` events.

The `setFormValue` is named to correspond with
[ElementInternals.setFormValue][]. Along with introducing this callback
method, this commit renames the `TrixEditorElement.setInputElementValue`
method to `TrixEditorElement.setFormValue`.

In addition to renaming `setInputElementValue`, this commit also defines
`TrixEditorElement.formResetCallback` (along with other empty
callbacks), then implements `TrixEditorElement.reset` as an alias. The
name mirrors the [ElementInternals.formResetCallback][].

[basecamp#1128]: basecamp#1128
[ElementInternals.setFormValue]: https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setFormValue
[ElementInternals.formResetCallback]: https://web.dev/articles/more-capable-form-controls#void_formresetcallback
In preparation for [basecamp#1128][], this commit introduces a module-private
`Delegate` class to serve as a representation of what form integration
requires for the `<trix-editor>` custom element. The structure of the
`Delegate` class mirrors that of the `TrixEditorElement` from which its
contents are extracted.

First, there are the properties that mimic those of most form controls,
including:

* `labels`
* `form`
* `name`
* `value`
* `defaultValue`
* `type`

With the exception of `labels`, property access is mostly proxied
through the associated `<input type="hidden">` element (accessed through
its own `inputElement` property).

Next, the `Delegate` defines methods that correspond to the Custom
Element lifecycle events, including:

* `connectedCallback`
* `disconnectedCallback`
* `setFormValue`

The connected and disconnected callbacks mirror that of the
`TrixEditorElement` itself. These callbacks attach and remove event
listeners for `click` and `reset` events.

The `setFormValue` is named to correspond with
[ElementInternals.setFormValue][]. Along with introducing this callback
method, this commit renames the `TrixEditorElement.setInputElementValue`
method to `TrixEditorElement.setFormValue`.

In addition to renaming `setInputElementValue`, this commit also defines
`TrixEditorElement.formResetCallback` (along with other [empty
form callbacks][]), then implements `TrixEditorElement.reset` as an alias. The
name mirrors the [ElementInternals.formResetCallback][].

[basecamp#1128]: basecamp#1128
[ElementInternals.setFormValue]: https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setFormValue
[empty form callbacks]: https://webkit.org/blog/13711/elementinternals-and-form-associated-custom-elements/
[ElementInternals.formResetCallback]: https://web.dev/articles/more-capable-form-controls#void_formresetcallback
@seanpdoyle seanpdoyle marked this pull request as ready for review February 9, 2024 04:45
@seanpdoyle
Copy link
Contributor Author

seanpdoyle commented Feb 9, 2024

@afcapel I've explored a Trix.config.editor.formAssociated JavaScript configuration that's pre-populated by the presence of a <meta name="trix-editor-formAssociated" content="true"> element in the <head>.

The configuration key is provisional open to renaming. The idea is that Action Text could provide a configuration key that would then utilize the new yield :head standard in the layout to push that node when Element Internals is enabled.

To set the stage for these changes and to ease the particulars of the Element Internals code review, I've opened #1132 to introduce the idea of a delegate and to make some of the less controversial changes ahead of these.

Both #1132 and #1128 are ready for review.

Closes [basecamp#1023][]

Replace the requirement for an `<input type="hidden">` element with
direct `<form>` integration through built-in support for
[ElementInternals][].

According to the [Form-associated custom elements][] section of [More
capable form controls][], various behaviors that the `<trix-editor>`
element was recreating are provided out of the box.

For example, the `<input type="hidden">`-`[input]` attribute pairing can
be achieved through [ElementInternals.setFormValue][]. Similarly, the
`<label>` element support can be achieved through
[ElementInternals.labels][].

[basecamp#1023]: basecamp#1023
[ElementInternals]: https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals
[Form-associated custom elements]: https://web.dev/articles/more-capable-form-controls#form-associated_custom_elements
[More capable form controls]: https://web.dev/articles/more-capable-form-controls
[ElementInternals.setFormValue]: https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setFormValue
[ElementInternals.labels]: https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/labels
@MatheusRich
Copy link

@afcapel, any chance this can be re-evaluated? It is a common pain when working with Trix. Is there anything we can do to move this forward somehow?

@seanpdoyle seanpdoyle changed the title Integrate with ElementInternals Replace hidden <input> with ElementInternals integration Sep 28, 2024
@seanpdoyle
Copy link
Contributor Author

The changes proposed by this PR (in their current form), are rather ambitious, and involve changing the structure of the element. They also require coordination with rails/rails.

I've opened #1188 as a supplementary, less ambitious proposal that doesn't require much change to the package internals, and does not require coordination with rails/rails.

@seanpdoyle seanpdoyle mentioned this pull request Oct 3, 2024
3 tasks
seanpdoyle added a commit to seanpdoyle/trix that referenced this pull request Oct 3, 2024
In preparation for [basecamp#1128][], this commit introduces a module-private
`Delegate` class to serve as a representation of what form integration
requires for the `<trix-editor>` custom element. The structure of the
`Delegate` class mirrors that of the `TrixEditorElement` from which its
contents are extracted.

First, there are the properties that mimic those of most form controls,
including:

* `labels`
* `form`
* `name`
* `value`
* `defaultValue`
* `type`

With the exception of `labels`, property access is mostly proxied
through the associated `<input type="hidden">` element (accessed through
its own `inputElement` property).

Next, the `Delegate` defines methods that correspond to the Custom
Element lifecycle events, including:

* `connectedCallback`
* `disconnectedCallback`
* `setFormValue`

The connected and disconnected callbacks mirror that of the
`TrixEditorElement` itself. These callbacks attach and remove event
listeners for `click` and `reset` events.

The `setFormValue` is named to correspond with
[ElementInternals.setFormValue][]. Along with introducing this callback
method, this commit renames the `TrixEditorElement.setInputElementValue`
method to `TrixEditorElement.setFormValue`.

In addition to renaming `setInputElementValue`, this commit also defines
`TrixEditorElement.formResetCallback` (along with other [empty
form callbacks][]), then implements `TrixEditorElement.reset` as an alias. The
name mirrors the [ElementInternals.formResetCallback][].

[basecamp#1128]: basecamp#1128
[ElementInternals.setFormValue]: https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setFormValue
[empty form callbacks]: https://webkit.org/blog/13711/elementinternals-and-form-associated-custom-elements/
[ElementInternals.formResetCallback]: https://web.dev/articles/more-capable-form-controls#void_formresetcallback
@seanpdoyle seanpdoyle marked this pull request as draft October 4, 2024 04:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Does Trix plan to support HTMLElement.attachInternals?
3 participants