Fix stuck disabled submit buttons after failed ajax calls

Submit buttons on Vue forms get stuck disabled after a failed submit, and only a reload clears it.

main.js's auto-disable handler bound its events as one comma-joined string, 'ajax:complete, ajax:beforeSend, submit'. jQuery splits event strings on whitespace, not commas, so two of the three names never registered; only submit was ever live, and that branch only disables. GlButton used to keep disabled in its vdom, so toggling the prop cleared the stuck native attribute as a side effect, until !247432 (merged) moved that state to aria-disabled, and the side effect went with it.

Registering the event names properly isn't enough on its own: ajax:complete is a rails-ujs event, and forms using @submit.prevent with axios never fire it, so the re-enable path has to skip those forms. That's safe because Vue attaches its own submit listener on the <form>, which runs before this <body>-delegated handler, while rails-ujs delegates from document and runs after, so server-rendered and rails-ujs remote forms still disable and still re-enable on ajax:complete.

Evidence

Reproduced on a local GDK with the new pipeline schedule form (pipeline_schedules_form.vue), invalid cron, so the mutation fails and the page stays put:

Before After
616513-before 616513-after

spec/frontend/main_spec.js is new: 9 examples, green on both the default and VUE_VERSION=3 configs, 4 failing against the unfixed handler.

The tradeoff: a form that doesn't track its own in-flight state loses its double-submit guard. Three clicks here now fire three createPipelineSchedule mutations, and two rapid clicks with a valid cron created two identical schedules. This form's isSubmitDisabled only ever came from validity, so the one thing stopping a duplicate was a bug that also stranded the button, and the roughly 30 components already on js-no-auto-disable carry the same exposure today. In-flight state belongs to the component.

jira_connect/version_select_form.vue, flagged in the issue as at risk, isn't: its layout never loads main.js, and jira_connect/subscriptions/index.js calls setConfigs() with no arguments, so GlButton still renders the native attribute there.

js-no-auto-disable opt-outs are untouched, so this reverts on its own. It spans two groups, groupauthentication for the symptom and foundations for the handler and the GlButton contract, so review from both would help.

References

Edited by Adam Ferch

Merge request reports

Loading
Loading