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

Toggle [disabled] on form submitter #386

Merged
merged 1 commit into from
Nov 9, 2021

Conversation

seanpdoyle
Copy link
Contributor

@seanpdoyle seanpdoyle commented Sep 10, 2021

During a form submission, toggle the disabled attribute on prior to
turbo:submit-start, and remove it prior to firing turbo:submit-end.

If callers need to control the submitter's disabled attribute more
finely, they can declare listeners to do so.

Combined with CSS rules, consumer applications can hide and show
submission text similar to RailsUJS's support for data-disable-with:

button                  .show-when-disabled { display: none; }
button[disabled]        .show-when-disabled { display: initial; }

button                  .show-when-enabled { display: initial; }
button[disabled]        .show-when-enabled { display: none; }
<button>
  <span class="show-when-enabled">Submit</span>
  <span class="show-when-disabled">Submitting...</span>
</button>

Styling descendant submitters based on their ancestors is only possible
with <button> elements, since <input type="submit"> do not have text
content or descendants of their own.

@jacobdaddario
Copy link

This is so slick. I don’t know why I thought I couldn’t use the request callbacks to achieve this. The use of CSS selectors to handle the disable behaviors is super cool too.

That said, I wonder if it’s worth trying to implement the behavior for submit given that submit inputs seems to be considered semantic for the Rails form builder. submit is useful for I18n as well thanks to Rails’ built-in localization of helpers.

Also there’s the question of if we want to continue the hack of disable working on links. I figure no since Hotwire focuses on progressive enhancement, but I wanted to throw it out there.

@seanpdoyle
Copy link
Contributor Author

I've recently read Making Disabled Buttons more Inclusive, which highlights some downsides to setting the [disabled] attribute on a <button>. For example, if a keyboard user moves focus to a <button> and "clicks" it by pressing space or enter, toggling [disabled] would dismiss that element's focus, which could be disorienting and could leave the user in an inconsistent state of focus.

To account for that type of experience, we could modify this proposal to instead toggle [aria-disabled="true"]. The bulk of the changes would remain, with the caveat that we'd also need to account for blocking click events when [aria-disabled="true"]. We could account for that in the FormSubmitObserver:

const submitter = event.submitter
const ariaDisabled = submitter?.getAttribute("aria-disabled")

if (ariaDisabled == "true") event.preventDefault()

Any application-side CSS styles that target [disabled] or :disabled (like the ones mentioned in the commit message) could be modified to account for [aria-disabled="true"]:

button                       .show-when-disabled { display: none; }
button[aria-disabled="true"] .show-when-disabled { display: initial; }

button                       .show-when-enabled { display: initial; }
button[aria-disabled="true"] .show-when-enabled { display: none; }

@pinzonjulian
Copy link

pinzonjulian commented Oct 17, 2021

Hey! I've been following all the developments on this topic and based on the article you linked I'm wondering if there are actually two different states a button could be in.

TL;DR: I think Turbo should definitely be responsible for setting aria-disabled and preventing double clicking. As for styling, there might be nuances that are difficult to cover with a single attribute. Read on for ideas.

Before submitting a form
Based on the article, there might be cases where you can use aria-disabled=true before sending a form to signal (alongside some other aid) that you need to fill in a field, or select an option before the button is enabled.

In this case, the button would visually change to be faded or gray for example to signal its state.

Submitting the form
When the form is submitted the button needs two things

  1. For it to be disabled to prevent double clicking
  2. For it to appear as loading.

The problem
Styling a button element based on aria-disabled only asumes that when the button is disabled the circumstances are always the same but in reality they're not.

Idea 1: tagging the button with something else

How about if we add not only aria-disabled="true" but also another specific Turbo attribute for users to hook into in their css?

<button type="submit">
  Create Post
</button>

User presses button
Option 1: busy
<button type="submit"
        aria-disabled="true"
        data-turbo-busy>
  Create Post
</button>

Option 2: form-submitting
<button type="submit"
        aria-disabled="true"
        data-turbo-form-submitting>
  Create Post
</button>

Idea 2: firing an event

Assuming that developer's coding style is purely based on css might not be ideal. People might want to delegate styling to js for more complex interactions. How about firing an event on the submitter alongside the aria attribute so users can listen to that and perform their own actions?

<button type="submit">
  Create Post
</button>

User presses button
Fire same events as turbo's form events: turbo:submit-start and turbo:submit-end

User uses a stimulus controller to control behaviour. The controller registers listeners for the submit-start and submit-end events.

<button type="submit"
        aria-disabled="true"
        data-controller="button"
  Create Post
</button>

Maybe this button controller could be part of the Stimulus Standard Library that has been mentioned in other places by @dhh.

What do you think about these options?

@seanpdoyle
Copy link
Contributor Author

seanpdoyle commented Oct 17, 2021

In my opinion, Turbo should either defer this type of behavior entirely to the consumer application, or it should commit to doing one single thing.

If disabling the SubmitEvent.submitter is context-dependent for an application, using Stimulus to declare a pair of turbo:submit-start and turbo:submit-end events like you've outline is a well supported feature, and might benefit from explicit documentation suggesting that approach and outlining how to do it.

Otherwise, if we can agree that disabling the SubmitEvent.submitter is Turbo's responsibility, I think it'd be best to commit to toggling a single attribute. Whether that's [disabled] or [aria-disabled="true"] (with additional click-discarding event listeners) is something that could be hashed out.

I'm not sure if I see the utility in toggling an additional attribute specific to submitting that we'd pair with whichever disabled variation we choose to support. There have been discussions in the past on toggling [aria-busy="true"] instead of [busy] on <turbo-frame> events as they navigate (see #157), as well as toggling a [data-turbo-busy] attribute up the chain from form[data-turbo-busy], to turbo-frame[data-turbo-busy], to the <body> or <html> element (see #199).

Since Turbo itself intervenes in <form> element submissions, it has an opportunity to toggle [aria-busy="true"] on <form> elements in its own turbo:submit-start and turbo:submit-end event listener pairs.

Throughout all of this submitter-specific discussion, it's worth highlighting that a <form> submitted from an end-user enter keystroke while an <input> has focus will result in a SubmitEvent.submitter == null. In that case, the [disabled] versus [aria-disabled="true"] discussion is irrelevant, but the <form aria-busy="true"> toggling discussion is still pertinent.

During a form submission, toggle the [disabled][] attribute on prior to
`turbo:submit-start`, and remove it prior to firing `turbo:submit-end`.

If callers need to control the submitter's [disabled][] attribute more
finely, they can declare listeners to do so.

Combined with CSS rules, consumer applications can hide and show
submission text similar to RailsUJS's support for `data-disable-with`:

```css
button                  .show-when-disabled { display: none; }
button[disabled]        .show-when-disabled { display: initial; }

button                  .show-when-enabled { display: initial; }
button[disabled]        .show-when-enabled { display: none; }
```

```html
<button>
  <span class="show-when-enabled">Submit</span>
  <span class="show-when-disabled">Submitting...</span>
</button>
```

Styling descendant submitters based on their ancestors is only possible
with `<button>` elements, since `<input type="submit">` do not have text
content or descendants of their own.

[disabled]: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled
@pinzonjulian
Copy link

pinzonjulian commented Oct 21, 2021

The SubmitEven.submitter discussion is very interesting

That makes me think that it may be out of Turbo's reach/responsibility to do something too specific about it. I personally think that disabling a button in a busy form is super important for all apps and Hotwire should probably provide a solution but I starting to gear more towards leaving that responsibility to Stimulus. I just added this functionality to an app and I liked having that control; from toggling styles the way I like to being responsible for accessibility. In my case, styles are tied to aria-disabled so this just works but I imagine people might want to handle things differently (like triggering classes with JS):

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["submitter"]

  disableSubmitter(event){
    // Although this should be the correct implementation,
    // there is a bug in safari at the moment that doesn't allow
    // the submitter to be located this way.
    // In the future it's likely that Turbo will have this functionality baked
    // in so we won't have to do it manually.
    // https://github.com/hotwired/turbo-rails/pull/257
    // event.detail.formSubmission.submitter.setAttribute("disabled", "")
    this.submitterTarget.setAttribute("aria-disabled", "true")
    // some other stuff...
  }

  enableSubmitter(event){
    this.submitterTarget.setAttribute("aria-disabled", "false")
    // some other stuff...
  }
}
<%= form_with model: @user,  data: { controller: "form", action: 'turbo:submit-start->form#disableSubmitter turbo:submit-end->form#enableSubmitter') } do |form| %>
 <%= form.button data: { form_target: "submitter" } %>
<%= end %>

What I personally don't like about this approach is that it's very verbose. I could probably cut a couple of characters by changing disableSubmitter to toggleSubmitter and having it set the listener for submit-end?.

Also, maybe I'm doing something wrong but I'd like for the actions to be declared on the button rather than the form. Is that possible?

I think having an answer for questions like these in the context of Stimulus might be worth exploring to understand if this functionality is better off over there. I still love the idea of this being part of a Stimulus Standard Library. Having a stdlib mixin called something like... submitDisabler to add this features to a controller of your own might be interesting

@pinzonjulian
Copy link

I also found something interesting while working with Activestorage direct uploads yesterday. It also sets the disabled attribute upon form submission while it waits for the blob ids to return back from the server and upon success it doesn't remove the disabled attribute.

Maybe it's something we could think of holistically(?)

@dhh dhh merged commit 7b54d10 into hotwired:main Nov 9, 2021
@dhh
Copy link
Member

dhh commented Nov 9, 2021

Let's start with disabled, so we stay congruent with ActiveStorage as well. Then we can explore options for aria-disabled separately.

@seanpdoyle seanpdoyle deleted the form-submitter-disabled branch November 9, 2021 13:40
seanpdoyle added a commit to seanpdoyle/turbo-site that referenced this pull request Nov 9, 2021
seanpdoyle added a commit to seanpdoyle/turbo-site that referenced this pull request Nov 9, 2021
@tleish
Copy link
Contributor

tleish commented Nov 23, 2021

The advantage of using disabled is it automatically prevents users who like to double-click buttons from submitting the form multiple times. When using aria-disabled, you must also add additional javascript to check the state of aria-disabled to prevent double clicking. If considering aria-disabled in the future, consider how to make it easy to also prevent double clicking.

@seanpdoyle
Copy link
Contributor Author

@tleish that's a good point.

Turbo already declares FormSubmitObserver and FormInterceptor classes that listen for submit events.

If [aria-disabled="true"] were toggled, those listeners could be extended to ignore or cancel submissions where event.submitter.getAttribute("aria-disabled") == "true".

zaru added a commit to zaru/hotwire-spa-demo that referenced this pull request Nov 25, 2021
Turbo v7.1.0 から自動でボタンのステートを切り替える機能がついた
CSS の clsas 名は特に何も規約はなく、単純に button に対して disabled 属性がつくだけ
それを利用し CSS セレクタで表示を切り替えている

また、disabled 属性は POST 完了後に自動で削除されるため Stimulus で手動リセットしていた処理は削除した

hotwired/turbo#386
shouichi added a commit to shouichi/rails that referenced this pull request Jul 11, 2023
Rails 7 uses Turbo/Stimulus by default. Turbo adds disabled attributes
when submitting form (see hotwired/turbo#386).
As the PR describes, we can change submission text when form is
submitted.

```
button                  .show-when-disabled { display: none; }
button[disabled]        .show-when-disabled { display: initial; }

button                  .show-when-enabled { display: initial; }
button[disabled]        .show-when-enabled { display: none; }
```

```
<button>
  <span class="show-when-enabled">Submit</span>
  <span class="show-when-disabled">Submitting...</span>
</button>
```

This is only possible with the button tag. Because input tag does not
allow child elements.

It also seems like that button tags are preferred way nowadays.

> https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/button
> Note: While <input> elements of type button are still perfectly valid
> HTML, the newer <button> element is now the favored way to create
> buttons. Given that a <button>'s label text is inserted between the
> opening and closing tags, you can include HTML in the label, even
> images.
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Mar 2, 2024
Follow-up to [hotwired#386][]

While Turbo's support for disabling a Form Submissions `<input
type="submit">` or `<button>` element is rooted in [Rails UJS][], it
degrades (and has always degraded) the accessibility of those
experiences.

To learn more about the risks involved, read the [Don’t Disable
Submits][] section of Adrian Roselli's [Don't Disable Form Controls][]
along with the additional resources mentioned therein.

The risk of degrading accessibility is especially true for Morph-enabled
Form Submissions. If a form submission will trigger a morphing Page
Refresh with the submitter focused, it's likely that the focus *is
intended to* remain on the submitter.

With the current `[disabled]` behavior, that is not possible without a
bespoke event handler like:

```js
addEventListener("submit", ({ target, submitter }) => {
  if (submitter) {
    target.addEventListener("turbo:submit-start", () => {
      submitter.disabled = false
      submitter.focus()
    }, { once: true })
  }
})
```

This commit introduces a `Turbo.config.submitter` object with two
pre-defined keys: `"disabled"` (the default until we can deprecate it),
and `"aria-disabled"`.

When applications specify either `Turbo.config.submitter = "disabled"`
or `Turbo.config.submitter = "aria-disabled"`, they will be able to
leverage those pre-packed hooks. Otherwise, they can provide their own
object with `beforeSubmit(submitter)` and `afterSubmit(submitter)`
functions.

[hotwired#386]: hotwired#386
[Rails UJS]: https://guides.rubyonrails.org/v6.0/working_with_javascript_in_rails.html#automatic-disabling
[Don't Disable Form Controls]: https://adrianroselli.com/2024/02/dont-disable-form-controls.html#Submit
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Mar 2, 2024
Follow-up to [hotwired#386][]

While Turbo's support for disabling a Form Submissions `<input
type="submit">` or `<button>` element is rooted in [Rails UJS][], it
degrades (and has always degraded) the accessibility of those
experiences.

To learn more about the risks involved, read the [Don’t Disable
Submits][] section of Adrian Roselli's [Don't Disable Form Controls][]
along with the additional resources mentioned therein.

The risk of degrading accessibility is especially true for Morph-enabled
Form Submissions. If a form submission will trigger a morphing Page
Refresh with the submitter focused, it's likely that the focus *is
intended to* remain on the submitter.

With the current `[disabled]` behavior, that is not possible without a
bespoke event handler like:

```js
addEventListener("submit", ({ target, submitter }) => {
  if (submitter) {
    target.addEventListener("turbo:submit-start", () => {
      submitter.disabled = false
      submitter.focus()
    }, { once: true })
  }
})
```

This commit introduces a `Turbo.config.submitter` object with two
pre-defined keys: `"disabled"` (the default until we can deprecate it),
and `"aria-disabled"`.

When applications specify either `Turbo.config.submitter = "disabled"`
or `Turbo.config.submitter = "aria-disabled"`, they will be able to
leverage those pre-packed hooks. Otherwise, they can provide their own
object with `beforeSubmit(submitter)` and `afterSubmit(submitter)`
functions.

[hotwired#386]: hotwired#386
[Rails UJS]: https://guides.rubyonrails.org/v6.0/working_with_javascript_in_rails.html#automatic-disabling
[Don't Disable Form Controls]: https://adrianroselli.com/2024/02/dont-disable-form-controls.html#Submit
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Mar 3, 2024
Follow-up to [hotwired#386][]

While Turbo's support for disabling a Form Submissions `<input
type="submit">` or `<button>` element is rooted in [Rails UJS][], it
degrades (and has always degraded) the accessibility of those
experiences.

To learn more about the risks involved, read the [Don’t Disable
Submits][] section of Adrian Roselli's [Don't Disable Form Controls][]
along with the additional resources mentioned therein.

The risk of degrading accessibility is especially true for Morph-enabled
Form Submissions. If a form submission will trigger a morphing Page
Refresh with the submitter focused, it's likely that the focus *is
intended to* remain on the submitter.

With the current `[disabled]` behavior, that is not possible without a
bespoke event handler like:

```js
addEventListener("submit", ({ target, submitter }) => {
  if (submitter) {
    target.addEventListener("turbo:submit-start", () => {
      submitter.disabled = false
      submitter.focus()
    }, { once: true })
  }
})
```

This commit introduces a `Turbo.config.submitter` object with two
pre-defined keys: `"disabled"` (the default until we can deprecate it),
and `"aria-disabled"`.

When applications specify either `Turbo.config.submitter = "disabled"`
or `Turbo.config.submitter = "aria-disabled"`, they will be able to
leverage those pre-packed hooks. Otherwise, they can provide their own
object with `beforeSubmit(submitter)` and `afterSubmit(submitter)`
functions.

[hotwired#386]: hotwired#386
[Rails UJS]: https://guides.rubyonrails.org/v6.0/working_with_javascript_in_rails.html#automatic-disabling
[Don't Disable Form Controls]: https://adrianroselli.com/2024/02/dont-disable-form-controls.html#Submit
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Mar 3, 2024
Follow-up to [hotwired#386][]

While Turbo's support for disabling a Form Submissions `<input
type="submit">` or `<button>` element is rooted in [Rails UJS][], it
degrades (and has always degraded) the accessibility of those
experiences.

To learn more about the risks involved, read the [Don't Disable
Submits][] section of Adrian Roselli's [Don't Disable Form Controls][]
along with the additional resources mentioned therein.

The risk of degrading accessibility is especially true for Morph-enabled
Form Submissions. If a form submission will trigger a morphing Page
Refresh with the submitter focused, it's likely that the focus *is
intended to* remain on the submitter.

With the current `[disabled]` behavior, that is not possible without a
bespoke event handler like:

```js
addEventListener("submit", ({ target, submitter }) => {
  if (submitter) {
    target.addEventListener("turbo:submit-start", () => {
      submitter.disabled = false
      submitter.focus()
    }, { once: true })
  }
})
```

This commit introduces a `Turbo.config.forms.submitter` object with two
pre-defined keys: `"disabled"` (the default until we can deprecate it),
and `"aria-disabled"`.

When applications specify either `Turbo.config.forms.submitter =
"disabled"` or `Turbo.config.forms.submitter = "aria-disabled"`, they
will be able to leverage those pre-packed hooks. Otherwise, they can
provide their own object with `beforeSubmit(submitter)` and
`afterSubmit(submitter)` functions.

[hotwired#386]: hotwired#386
[Rails UJS]: https://guides.rubyonrails.org/v6.0/working_with_javascript_in_rails.html#automatic-disabling
[Don't Disable Form Controls]: https://adrianroselli.com/2024/02/dont-disable-form-controls.html
[Don't Disable Submits]: https://adrianroselli.com/2024/02/dont-disable-form-controls.html#Submit
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Mar 3, 2024
Follow-up to [hotwired#386][]

While Turbo's support for disabling a Form Submissions `<input
type="submit">` or `<button>` element is rooted in [Rails UJS][], it
degrades (and has always degraded) the accessibility of those
experiences.

To learn more about the risks involved, read the [Don't Disable
Submits][] section of Adrian Roselli's [Don't Disable Form Controls][]
along with the additional resources mentioned therein.

The risk of degrading accessibility is especially true for Morph-enabled
Form Submissions. If a form submission will trigger a morphing Page
Refresh with the submitter focused, it's likely that the focus *is
intended to* remain on the submitter.

With the current `[disabled]` behavior, that is not possible without a
bespoke event handler like:

```js
addEventListener("submit", ({ target, submitter }) => {
  if (submitter) {
    target.addEventListener("turbo:submit-start", () => {
      submitter.disabled = false
      submitter.focus()
    }, { once: true })
  }
})
```

This commit introduces a `Turbo.config.forms.submitter` object with two
pre-defined keys: `"disabled"` (the default until we can deprecate it),
and `"aria-disabled"`.

When applications specify either `Turbo.config.forms.submitter =
"disabled"` or `Turbo.config.forms.submitter = "aria-disabled"`, they
will be able to leverage those pre-packed hooks. Otherwise, they can
provide their own object with `beforeSubmit(submitter)` and
`afterSubmit(submitter)` functions.

[hotwired#386]: hotwired#386
[Rails UJS]: https://guides.rubyonrails.org/v6.0/working_with_javascript_in_rails.html#automatic-disabling
[Don't Disable Form Controls]: https://adrianroselli.com/2024/02/dont-disable-form-controls.html
[Don't Disable Submits]: https://adrianroselli.com/2024/02/dont-disable-form-controls.html#Submit
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Mar 3, 2024
Follow-up to [hotwired#386][]

While Turbo's support for disabling a Form Submissions `<input
type="submit">` or `<button>` element is rooted in [Rails UJS][], it
degrades (and has always degraded) the accessibility of those
experiences.

To learn more about the risks involved, read the [Don't Disable
Submits][] section of Adrian Roselli's [Don't Disable Form Controls][]
along with the additional resources mentioned therein.

The risk of degrading accessibility is especially true for Morph-enabled
Form Submissions. If a form submission will trigger a morphing Page
Refresh with the submitter focused, it's likely that the focus *is
intended to* remain on the submitter.

With the current `[disabled]` behavior, that is not possible without a
bespoke event handler like:

```js
addEventListener("submit", ({ target, submitter }) => {
  if (submitter) {
    target.addEventListener("turbo:submit-start", () => {
      submitter.disabled = false
      submitter.focus()
    }, { once: true })
  }
})
```

This commit introduces a `Turbo.config.forms.submitter` object with two
pre-defined keys: `"disabled"` (the default until we can deprecate it),
and `"aria-disabled"`.

When applications specify either `Turbo.config.forms.submitter =
"disabled"` or `Turbo.config.forms.submitter = "aria-disabled"`, they
will be able to leverage those pre-packed hooks. Otherwise, they can
provide their own object with `beforeSubmit(submitter)` and
`afterSubmit(submitter)` functions.

[hotwired#386]: hotwired#386
[Rails UJS]: https://guides.rubyonrails.org/v6.0/working_with_javascript_in_rails.html#automatic-disabling
[Don't Disable Form Controls]: https://adrianroselli.com/2024/02/dont-disable-form-controls.html
[Don't Disable Submits]: https://adrianroselli.com/2024/02/dont-disable-form-controls.html#Submit
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Mar 3, 2024
Follow-up to [hotwired#386][]

While Turbo's support for disabling a Form Submissions `<input
type="submit">` or `<button>` element is rooted in [Rails UJS][], it
degrades (and has always degraded) the accessibility of those
experiences.

To learn more about the risks involved, read the [Don't Disable
Submits][] section of Adrian Roselli's [Don't Disable Form Controls][]
along with the additional resources mentioned therein.

The risk of degrading accessibility is especially true for Morph-enabled
Form Submissions. If a form submission will trigger a morphing Page
Refresh with the submitter focused, it's likely that the focus *is
intended to* remain on the submitter.

With the current `[disabled]` behavior, that is not possible without a
bespoke event handler like:

```js
addEventListener("submit", ({ target, submitter }) => {
  if (submitter) {
    target.addEventListener("turbo:submit-start", () => {
      submitter.disabled = false
      submitter.focus()
    }, { once: true })
  }
})
```

This commit introduces a `Turbo.config.forms.submitter` object with two
pre-defined keys: `"disabled"` (the default until we can deprecate it),
and `"aria-disabled"`.

When applications specify either `Turbo.config.forms.submitter =
"disabled"` or `Turbo.config.forms.submitter = "aria-disabled"`, they
will be able to leverage those pre-packed hooks. Otherwise, they can
provide their own object with `beforeSubmit(submitter)` and
`afterSubmit(submitter)` functions.

[hotwired#386]: hotwired#386
[Rails UJS]: https://guides.rubyonrails.org/v6.0/working_with_javascript_in_rails.html#automatic-disabling
[Don't Disable Form Controls]: https://adrianroselli.com/2024/02/dont-disable-form-controls.html
[Don't Disable Submits]: https://adrianroselli.com/2024/02/dont-disable-form-controls.html#Submit
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Mar 29, 2024
Follow-up to [hotwired#386][]

While Turbo's support for disabling a Form Submissions `<input
type="submit">` or `<button>` element is rooted in [Rails UJS][], it
degrades (and has always degraded) the accessibility of those
experiences.

To learn more about the risks involved, read the [Don't Disable
Submits][] section of Adrian Roselli's [Don't Disable Form Controls][]
along with the additional resources mentioned therein.

The risk of degrading accessibility is especially true for Morph-enabled
Form Submissions. If a form submission will trigger a morphing Page
Refresh with the submitter focused, it's likely that the focus *is
intended to* remain on the submitter.

With the current `[disabled]` behavior, that is not possible without a
bespoke event handler like:

```js
addEventListener("submit", ({ target, submitter }) => {
  if (submitter) {
    target.addEventListener("turbo:submit-start", () => {
      submitter.disabled = false
      submitter.focus()
    }, { once: true })
  }
})
```

This commit introduces a `Turbo.config.forms.submitter` object with two
pre-defined keys: `"disabled"` (the default until we can deprecate it),
and `"aria-disabled"`.

When applications specify either `Turbo.config.forms.submitter =
"disabled"` or `Turbo.config.forms.submitter = "aria-disabled"`, they
will be able to leverage those pre-packed hooks. Otherwise, they can
provide their own object with `beforeSubmit(submitter)` and
`afterSubmit(submitter)` functions.

[hotwired#386]: hotwired#386
[Rails UJS]: https://guides.rubyonrails.org/v6.0/working_with_javascript_in_rails.html#automatic-disabling
[Don't Disable Form Controls]: https://adrianroselli.com/2024/02/dont-disable-form-controls.html
[Don't Disable Submits]: https://adrianroselli.com/2024/02/dont-disable-form-controls.html#Submit
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants