Skip to content

Commit

Permalink
feat: scroll to top of form when non-field errors are present MAASENG…
Browse files Browse the repository at this point in the history
…-3515 (#5503)

- When a non-field (i.e. backend) error is received after submitting a form, the error message will now automatically be scrolled into view

---
Fixes [MAASENG-3515](https://warthogs.atlassian.net/browse/MAASENG-3515)
  • Loading branch information
ndv99 authored Jul 18, 2024
1 parent 3cc9cbd commit 90df80f
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Field, Formik } from "formik";
import { Provider } from "react-redux";
import { MemoryRouter } from "react-router-dom";
import configureStore from "redux-mock-store";
import type { Mock } from "vitest";
import * as Yup from "yup";

import FormikFormContent from "./FormikFormContent";
Expand Down Expand Up @@ -31,7 +32,12 @@ vi.mock("react-router-dom", async () => {

describe("FormikFormContent", () => {
let state: RootState;
let scrollIntoViewSpy: Mock;

beforeEach(() => {
scrollIntoViewSpy = vi.fn();
window.HTMLElement.prototype.scrollIntoView = scrollIntoViewSpy;

state = factory.rootState({
config: factory.configState({
items: [
Expand Down Expand Up @@ -101,6 +107,18 @@ describe("FormikFormContent", () => {
expect(screen.getByText("Uh oh!")).toBeInTheDocument();
});

it("scrolls non-field errors into view when present", () => {
renderWithBrowserRouter(
<Formik initialValues={{}} onSubmit={vi.fn()}>
<FormikFormContent errors="Uh oh!">Content</FormikFormContent>
</Formik>,
{ state }
);

expect(screen.getByText("Uh oh!")).toBeInTheDocument();
expect(scrollIntoViewSpy).toHaveBeenCalledTimes(1);
});

it("can display non-field errors from the __all__ key", () => {
renderWithBrowserRouter(
<Formik initialValues={{}} onSubmit={vi.fn()}>
Expand Down
13 changes: 11 additions & 2 deletions src/app/base/components/FormikFormContent/FormikFormContent.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ReactNode, AriaAttributes } from "react";
import React, { useEffect, useRef } from "react";
import React, { useEffect, useId, useRef } from "react";

import { ContentSection } from "@canonical/maas-react-components";
import { Form, Notification } from "@canonical/react-components";
Expand Down Expand Up @@ -110,6 +110,7 @@ const FormikFormContent = <V extends object, E = null>({
allowUnchanged,
});
const [hasSaved, resetHasSaved] = useCycled(saved);
const notificationId = useId();
const hasErrors = !!errors;

// Run onValuesChanged function whenever formik values change.
Expand Down Expand Up @@ -168,10 +169,18 @@ const FormikFormContent = <V extends object, E = null>({
}
}, [navigate, savedRedirect, saved]);

useEffect(() => {
if (nonFieldError) {
document
.getElementById(notificationId)
?.scrollIntoView({ behavior: "smooth" });
}
}, [nonFieldError, notificationId]);

const renderFormContent = () => (
<>
{!!nonFieldError && (
<Notification severity="negative" title="Error:">
<Notification id={notificationId} severity="negative" title="Error:">
{nonFieldError}
</Notification>
)}
Expand Down
7 changes: 6 additions & 1 deletion src/setupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,26 @@ const fetchMocker = createFetchMock(vi);

fetchMocker.enableMocks();
const originalObserver = window.ResizeObserver;
const originalScrollIntoView = window.HTMLElement.prototype.scrollIntoView;

beforeAll(() => {
// disable act warnings
global.IS_REACT_ACT_ENVIRONMENT = false;
});

// mock ResizeObserver for MainToolbar
beforeEach(() => {
// mock ResizeObserver for MainToolbar
window.ResizeObserver = vi.fn(() => ({
observe: vi.fn(),
unobserve: vi.fn(),
disconnect: vi.fn(),
}));

// mock scrollIntoView for FormikFormContent
window.HTMLElement.prototype.scrollIntoView = vi.fn();
});

afterEach(() => {
window.ResizeObserver = originalObserver;
window.HTMLElement.prototype.scrollIntoView = originalScrollIntoView;
});

0 comments on commit 90df80f

Please sign in to comment.