-
Notifications
You must be signed in to change notification settings - Fork 436
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
Conversation
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 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. |
I've recently read Making Disabled Buttons more Inclusive, which highlights some downsides to setting the To account for that type of experience, we could modify this proposal to instead toggle const submitter = event.submitter
const ariaDisabled = submitter?.getAttribute("aria-disabled")
if (ariaDisabled == "true") event.preventDefault() Any application-side CSS styles that target 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; } |
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 Before submitting a form In this case, the button would visually change to be faded or gray for example to signal its state. Submitting the form
The problem Idea 1: tagging the button with something else How about if we add not only <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 What do you think about these options? |
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 Otherwise, if we can agree that disabling the 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 Since Turbo itself intervenes in Throughout all of this submitter-specific discussion, it's worth highlighting that a |
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
0abd9cd
to
2aa727d
Compare
The 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 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 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 |
I also found something interesting while working with Activestorage direct uploads yesterday. It also sets the Maybe it's something we could think of holistically(?) |
Let's start with |
Document the changes proposed in [hotwired/turbo#386][]. [hotwired/turbo#386]: hotwired/turbo#386
Document the changes proposed in [hotwired/turbo#386][]. [hotwired/turbo#386]: hotwired/turbo#386
The advantage of using |
@tleish that's a good point. Turbo already declares FormSubmitObserver and FormInterceptor classes that listen for If |
Turbo v7.1.0 から自動でボタンのステートを切り替える機能がついた CSS の clsas 名は特に何も規約はなく、単純に button に対して disabled 属性がつくだけ それを利用し CSS セレクタで表示を切り替えている また、disabled 属性は POST 完了後に自動で削除されるため Stimulus で手動リセットしていた処理は削除した hotwired/turbo#386
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.
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
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
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
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
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
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
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
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
During a form submission, toggle the disabled attribute on prior to
turbo:submit-start
, and remove it prior to firingturbo: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
:Styling descendant submitters based on their ancestors is only possible
with
<button>
elements, since<input type="submit">
do not have textcontent or descendants of their own.