Fixes #20398: Rely on browser-native form field validation (#20401)
Some checks failed
CodeQL / Analyze (${{ matrix.language }}) (none, actions) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, python) (push) Waiting to run
CI / build (20.x, 3.10) (push) Has been cancelled
CI / build (20.x, 3.11) (push) Has been cancelled
CI / build (20.x, 3.12) (push) Has been cancelled

This commit is contained in:
Jeremy Stretch 2025-09-19 16:13:47 -04:00 committed by GitHub
parent 07a53c8315
commit 6547a16ab6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 5 additions and 40 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1,53 +1,23 @@
import { getElements, scrollTo } from '../util'; import { getElements } from '../util';
function handleFormSubmit(event: Event, form: HTMLFormElement): void { function handleFormSubmit(): void {
// Automatically select all options in any <select> with the "select-all" class. This is useful for // Automatically select all options in any <select> with the "select-all" class. This is useful for
// multi-select fields that are used to add/remove choices. // multi-select fields that are used to add/remove choices.
for (const element of getElements<HTMLOptionElement>('select.select-all option')) { for (const element of getElements<HTMLOptionElement>('select.select-all option')) {
element.selected = true; element.selected = true;
} }
// Track the names of each invalid field.
const invalids = new Set<string>();
for (const element of form.querySelectorAll<FormControls>('*[name]')) {
if (!element.validity.valid) {
invalids.add(element.name);
// If the field is invalid, but doesn't contain the .is-invalid class, add it.
if (!element.classList.contains('is-invalid')) {
element.classList.add('is-invalid');
}
} else {
// If the field is valid, but contains the .is-invalid class, remove it.
if (element.classList.contains('is-invalid')) {
element.classList.remove('is-invalid');
}
}
}
if (invalids.size !== 0) {
// If there are invalid fields, pick the first field and scroll to it.
const firstInvalid = form.elements.namedItem(Array.from(invalids)[0]) as Element;
scrollTo(firstInvalid);
// If the form has invalid fields, don't submit it.
event.preventDefault();
}
} }
/** /**
* Attach an event listener to each form's submitter (button[type=submit]). When called, the * Attach event listeners to each form's submit/reset buttons.
* callback checks the validity of each form field and adds the appropriate Bootstrap CSS class
* based on the field's validity.
*/ */
export function initFormElements(): void { export function initFormElements(): void {
for (const form of getElements('form')) { for (const form of getElements('form')) {
// Find each of the form's submitters. Most object edit forms have a "Create" and // Find each of the form's submit buttons.
// a "Create & Add", so we need to add a listener to both.
const submitters = form.querySelectorAll<HTMLButtonElement>('button[type=submit]'); const submitters = form.querySelectorAll<HTMLButtonElement>('button[type=submit]');
for (const submitter of submitters) { for (const submitter of submitters) {
// Add the event listener to each submitter. // Add the event listener to each submitter.
submitter.addEventListener('click', (event: Event) => handleFormSubmit(event, form)); submitter.addEventListener('click', () => handleFormSubmit());
} }
// Initialize any reset buttons so that when clicked, the page is reloaded without query parameters. // Initialize any reset buttons so that when clicked, the page is reloaded without query parameters.

View File

@ -65,7 +65,6 @@
<div class="col-md-4"> <div class="col-md-4">
{{ form.length_unit }} {{ form.length_unit }}
</div> </div>
<div class="invalid-feedback"></div>
</div> </div>
{% render_field form.tags %} {% render_field form.tags %}
</div> </div>

View File

@ -52,10 +52,6 @@
<div class="form-text text-danger"> <div class="form-text text-danger">
{% for error in field.errors %}{{ error }}{% if not forloop.last %}<br />{% endif %}{% endfor %} {% for error in field.errors %}{{ error }}{% if not forloop.last %}<br />{% endif %}{% endfor %}
</div> </div>
{% elif field.field.required %}
<div class="invalid-feedback">
{% trans "This field is required" %}.
</div>
{% endif %} {% endif %}
{# Help text #} {# Help text #}