diff --git a/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx b/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx index e6a5c67bfd8..a261e441041 100644 --- a/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx +++ b/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx @@ -63,6 +63,7 @@ interface IState { */ export default class AccessSecretStorageDialog extends React.PureComponent { private fileUpload = React.createRef(); + private inputRef = React.createRef(); public constructor(props: IProps) { super(props); @@ -178,7 +179,10 @@ export default class AccessSecretStorageDialog extends React.PureComponent | React.MouseEvent): Promise => { ev.preventDefault(); - if (this.state.passPhrase.length <= 0) return; + if (this.state.passPhrase.length <= 0) { + this.inputRef.current?.focus(); + return; + } this.setState({ keyMatches: null }); const input = { passphrase: this.state.passPhrase }; @@ -187,6 +191,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent { // Handle displaying feedback on validity let fieldTooltip; if (tooltipContent || this.state.feedback) { + let role: React.AriaRole; + if (tooltipContent) { + role = "tooltip"; + } else { + role = this.state.valid ? "status" : "alert"; + } + fieldTooltip = ( ); } diff --git a/src/components/views/elements/Tooltip.tsx b/src/components/views/elements/Tooltip.tsx index 98ee56b1595..4a98b0a3300 100644 --- a/src/components/views/elements/Tooltip.tsx +++ b/src/components/views/elements/Tooltip.tsx @@ -51,6 +51,8 @@ export interface ITooltipProps { id?: string; // If the parent is over this width, act as if it is only this wide maxParentWidth?: number; + // aria-role passed to the tooltip + role?: React.AriaRole; } type State = Partial>; @@ -186,7 +188,7 @@ export default class Tooltip extends React.PureComponent { style.display = this.props.visible ? "block" : "none"; const tooltip = ( -
+
{this.props.label}
diff --git a/test/components/views/dialogs/AccessSecretStorageDialog-test.tsx b/test/components/views/dialogs/AccessSecretStorageDialog-test.tsx index 4ce83370801..7d66f39dfb5 100644 --- a/test/components/views/dialogs/AccessSecretStorageDialog-test.tsx +++ b/test/components/views/dialogs/AccessSecretStorageDialog-test.tsx @@ -134,5 +134,7 @@ describe("AccessSecretStorageDialog", () => { "👎 Unable to access secret storage. Please verify that you entered the correct Security Phrase.", ), ).toBeInTheDocument(); + + expect(screen.getByPlaceholderText("Security Phrase")).toHaveFocus(); }); }); diff --git a/test/components/views/elements/Field-test.tsx b/test/components/views/elements/Field-test.tsx index 199f1c25577..ce826282aca 100644 --- a/test/components/views/elements/Field-test.tsx +++ b/test/components/views/elements/Field-test.tsx @@ -15,7 +15,7 @@ limitations under the License. */ import React from "react"; -import { render, screen } from "@testing-library/react"; +import { act, fireEvent, render, screen } from "@testing-library/react"; import Field from "../../../../src/components/views/elements/Field"; @@ -51,4 +51,61 @@ describe("Field", () => { expect(screen.getByRole("textbox")).not.toHaveAttribute("placeholder", "my placeholder"); }); }); + + describe("Feedback", () => { + it("Should mark the feedback as alert if invalid", async () => { + render( + Promise.resolve({ valid: false, feedback: "Invalid" })} + />, + ); + + // When invalid + await act(async () => { + fireEvent.focus(screen.getByRole("textbox")); + }); + + // Expect 'alert' role + expect(screen.queryByRole("alert")).toBeInTheDocument(); + }); + + it("Should mark the feedback as status if valid", async () => { + render( + Promise.resolve({ valid: true, feedback: "Valid" })} + />, + ); + + // When valid + await act(async () => { + fireEvent.focus(screen.getByRole("textbox")); + }); + + // Expect 'status' role + expect(screen.queryByRole("status")).toBeInTheDocument(); + }); + + it("Should mark the feedback as tooltip if custom tooltip set", async () => { + render( + Promise.resolve({ valid: true, feedback: "Valid" })} + tooltipContent="Tooltip" + />, + ); + + // When valid or invalid and 'tooltipContent' set + await act(async () => { + fireEvent.focus(screen.getByRole("textbox")); + }); + + // Expect 'tooltip' role + expect(screen.queryByRole("tooltip")).toBeInTheDocument(); + }); + }); });