web/flows: use aria-invalid attribute to better show invalid input fields (#7661)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
78f47a8726
commit
2c6ac73e0a
|
@ -6,19 +6,20 @@ import { customElement, property } from "lit/decorators.js";
|
||||||
|
|
||||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||||
|
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
|
|
||||||
import { ErrorDetail } from "@goauthentik/api";
|
import { ErrorDetail } from "@goauthentik/api";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is used in two places outside of Flow, and in both cases is used primarily to
|
* This is used in two places outside of Flow, and in both cases is used primarily to
|
||||||
* display content, not take input. It displays the TOPT QR code, and the static
|
* display content, not take input. It displays the TOTP QR code, and the static
|
||||||
* recovery tokens. But it's used a lot in Flow.
|
* recovery tokens. But it's used a lot in Flow.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@customElement("ak-form-element")
|
@customElement("ak-form-element")
|
||||||
export class FormElement extends AKElement {
|
export class FormElement extends AKElement {
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [PFForm, PFFormControl];
|
return [PFBase, PFForm, PFFormControl];
|
||||||
}
|
}
|
||||||
|
|
||||||
@property()
|
@property()
|
||||||
|
@ -28,7 +29,16 @@ export class FormElement extends AKElement {
|
||||||
required = false;
|
required = false;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
errors?: ErrorDetail[];
|
set errors(value: ErrorDetail[] | undefined) {
|
||||||
|
this._errors = value;
|
||||||
|
const hasError = (value || []).length > 0;
|
||||||
|
this.querySelectorAll("input").forEach((input) => {
|
||||||
|
input.setAttribute("aria-invalid", hasError.toString());
|
||||||
|
});
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
_errors?: ErrorDetail[];
|
||||||
|
|
||||||
updated(): void {
|
updated(): void {
|
||||||
this.querySelectorAll<HTMLInputElement>("input[autofocus]").forEach((input) => {
|
this.querySelectorAll<HTMLInputElement>("input[autofocus]").forEach((input) => {
|
||||||
|
@ -45,8 +55,12 @@ export class FormElement extends AKElement {
|
||||||
: html``}
|
: html``}
|
||||||
</label>
|
</label>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
${(this.errors || []).map((error) => {
|
${(this._errors || []).map((error) => {
|
||||||
return html`<p class="pf-c-form__helper-text pf-m-error">${error.string}</p>`;
|
return html`<p class="pf-c-form__helper-text pf-m-error">
|
||||||
|
<span class="pf-c-form__helper-text-icon">
|
||||||
|
<i class="fas fa-exclamation-circle" aria-hidden="true"></i> </span
|
||||||
|
>${error.string}
|
||||||
|
</p>`;
|
||||||
})}
|
})}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,11 @@ export class PasswordStage extends BaseStage<PasswordChallenge, PasswordChalleng
|
||||||
|
|
||||||
timer?: number;
|
timer?: number;
|
||||||
|
|
||||||
|
hasError(field: string): boolean {
|
||||||
|
const errors = (this.challenge?.responseErrors || {})[field];
|
||||||
|
return (errors || []).length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
renderInput(): HTMLInputElement {
|
renderInput(): HTMLInputElement {
|
||||||
this.input = document.createElement("input");
|
this.input = document.createElement("input");
|
||||||
this.input.type = "password";
|
this.input.type = "password";
|
||||||
|
@ -38,6 +43,7 @@ export class PasswordStage extends BaseStage<PasswordChallenge, PasswordChalleng
|
||||||
this.input.classList.add("pf-c-form-control");
|
this.input.classList.add("pf-c-form-control");
|
||||||
this.input.required = true;
|
this.input.required = true;
|
||||||
this.input.value = PasswordManagerPrefill.password || "";
|
this.input.value = PasswordManagerPrefill.password || "";
|
||||||
|
this.input.setAttribute("aria-invalid", this.hasError("password").toString());
|
||||||
// This is somewhat of a crude way to get autofocus, but in most cases the `autofocus` attribute
|
// This is somewhat of a crude way to get autofocus, but in most cases the `autofocus` attribute
|
||||||
// isn't enough, due to timing within shadow doms and such.
|
// isn't enough, due to timing within shadow doms and such.
|
||||||
this.timer = window.setInterval(() => {
|
this.timer = window.setInterval(() => {
|
||||||
|
|
Reference in New Issue