Skip to content

Commit

Permalink
keep state persistent when collection and fields are unmounted (#36) (#…
Browse files Browse the repository at this point in the history
…37)

* keep state persistent when collection and fields are unmounted

* create the PersistStateOnUnmount component to persist the state on onmounting the component

* Persistent state on onmount fixes (#38)

* keep state persistent when collection and fields are unmounted

* create the PersistStateOnUnmount component to persist the state on onmounting the component

* State preseverd on unmount (#39)

* keep state persistent when collection and fields are unmounted

* create the PersistStateOnUnmount component to persist the state on onmounting the component

* update persistent state

* remove comment

* v3.4.1-alpha.0

* v3.4.1-alpha.1

* PERSISTENTSTATEONUNMOUNT unit test PersistentStateOnUnmount

* fix sync validation

* 3.4.1-alpha.2

* fix <PersistStateOnUnmount />

* 3.4.1-alpha.3

* fix radio, checkbox validation message on submit

Co-authored-by: Simone <[email protected]>
Co-authored-by: Antonio <[email protected]>
Co-authored-by: Antonio Pangallo <[email protected]>
  • Loading branch information
4 people authored Oct 31, 2021
1 parent 1f0d93b commit 8c571ab
Show file tree
Hide file tree
Showing 18 changed files with 520 additions and 10 deletions.
15 changes: 14 additions & 1 deletion __tests__/FormSyncValidation.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from "react";
import { cleanup, fireEvent, act } from "@testing-library/react";
import { cleanup, fireEvent, act, render } from "@testing-library/react";

import { FormWithValidationAfterMounted } from "./helpers/components/FormWithValidationAfterMounted";
import Reset from "./helpers/components/Reset";
import { mountForm } from "./helpers/utils/mountForm";

Expand Down Expand Up @@ -38,6 +39,18 @@ describe("Component => Form (sync validation)", () => {
expect(errorLabel.innerHTML).toBe("Mail not Valid");
});

it("should synchronously validate a Form if Fields are added after Form is mounted", () => {
const { getByTestId } = render(<FormWithValidationAfterMounted />);
const addBtn = getByTestId("addBtn");

act(() => {
fireEvent.click(addBtn);
});

const isValid = getByTestId("isValid");
expect(isValid.innerHTML).toBe("false");
});

it("should synchronously validate a Form with touched prop true", () => {
const name = "email";
const value = "[email protected]";
Expand Down
97 changes: 96 additions & 1 deletion __tests__/Input.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
import userEvent from "@testing-library/user-event";
import { Input } from "./../src";
import InputAsync from "./helpers/components/InputAsync";
import Radio from "./helpers/components/Radio";
import CheckBox from "./helpers/components/CheckBox";
import InputSyncValidation from "./helpers/components/InputSyncValidation";
import Submit from "./helpers/components/Submit";
import Reset from "./helpers/components/Reset";
Expand Down Expand Up @@ -396,7 +398,22 @@ describe("Component => Input", () => {
input.blur();
});

const errorLabel = getByTestId("errorLabel");
let errorLabel = getByTestId("errorLabel");
expect(errorLabel).toBeDefined();

act(() => {
input.focus();
fireEvent.change(input, { target: { value: "1234" } });
});

expect(() => getByTestId("errorLabel")).toThrow();

act(() => {
input.focus();
fireEvent.change(input, { target: { value: "" } });
});

errorLabel = getByTestId("errorLabel");
expect(errorLabel).toBeDefined();

const reset = getByTestId("reset");
Expand Down Expand Up @@ -463,6 +480,84 @@ describe("Component => Input", () => {
expect(() => getByTestId("errorLabel")).toThrow();
});

it("should use sync validator functions to validate a Radio input", () => {
const props = { onReset, onChange };
const children = [
<Radio name="radio" key="1" value="1" />,
<Reset key="2" />,
<Submit forceEnable={true} key="3" />
];

const { getByTestId } = mountForm({ children, props });

const submit = getByTestId("submit");
const reset = getByTestId("reset");
const radio = getByTestId("radio");

act(() => {
fireEvent.click(submit);
});

let errorLabel = getByTestId("errorLabel");
expect(errorLabel).toBeDefined();

act(() => {
radio.focus();
fireEvent.click(radio);
});

expect(() => getByTestId("errorLabel")).toThrow();

act(() => {
fireEvent.click(reset);
});
expect(onReset).toHaveBeenCalledWith({}, false);
expect(() => getByTestId("errorLabel")).toThrow();
});

it("should use sync validator functions to validate a Checkbox input", () => {
const props = { onReset, onChange };
const children = [
<CheckBox name="checkbox" key="1" value="1" />,
<Reset key="2" />,
<Submit forceEnable={true} key="3" />
];

const { getByTestId } = mountForm({ children, props });

const submit = getByTestId("submit");
const reset = getByTestId("reset");
const checkBox = getByTestId("checkbox");

act(() => {
fireEvent.click(submit);
});

let errorLabel = getByTestId("errorLabel");
expect(errorLabel).toBeDefined();

act(() => {
checkBox.focus();
fireEvent.click(checkBox);
});

expect(() => getByTestId("errorLabel")).toThrow();

act(() => {
checkBox.focus();
fireEvent.click(checkBox);
});

errorLabel = getByTestId("errorLabel");
expect(errorLabel).toBeDefined();

act(() => {
fireEvent.click(reset);
});
expect(onReset).toHaveBeenCalledWith({}, false);
expect(() => getByTestId("errorLabel")).toThrow();
});

it("should use an async validator function to validate the Input", async () => {
const value = "33";
const name = "test";
Expand Down
120 changes: 120 additions & 0 deletions __tests__/PersistStateOnUnmount.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import React from "react";
import { cleanup, fireEvent, act, render } from "@testing-library/react";

import {
PersistStateOnUnmountHelpers,
initialState
} from "./helpers/components/PersistStateOnUnmountHelpers";

const onInit = jest.fn();
const onChange = jest.fn();
const onReset = jest.fn();
const onSubmit = jest.fn();

afterEach(cleanup);

describe("Component => PersistStateOnUnmount", () => {
beforeEach(() => {
onInit.mockClear();
onChange.mockClear();
onReset.mockClear();
onSubmit.mockClear();
});

it("should Collection state persist on unmonuted", () => {
const stateExpected = {
...initialState,
user: { ...initialState.user, lastname: "hero" }
};
const props = { onInit, onChange };
const { getByTestId } = render(<PersistStateOnUnmountHelpers {...props} />);

const toggleCollection = getByTestId("toggleCollection");
const lastName = getByTestId("lastname");
expect(onInit).toHaveBeenCalledWith(initialState, true);

act(() => {
fireEvent.change(lastName, { target: { value: "hero" } });
});

act(() => {
fireEvent.click(toggleCollection);
});

expect(onChange).toHaveBeenCalledWith(stateExpected, true);

const togglekeepValue = getByTestId("togglekeepValue");

act(() => {
fireEvent.click(togglekeepValue);
});

expect(onChange).toHaveBeenCalledWith(stateExpected, true);
});

it("should Checkbox state persist on unmonuted", () => {
const stateExpected = { ...initialState, other: [, "3"] };
const props = { onInit, onChange };
const { getByTestId } = render(<PersistStateOnUnmountHelpers {...props} />);

const toggleNestedInput = getByTestId("toggleNestedInput");
const other1 = getByTestId("other1");
expect(onInit).toHaveBeenCalledWith(initialState, true);

act(() => {
fireEvent.click(other1);
});

expect(onChange).toHaveBeenCalledWith(stateExpected, true);

act(() => {
fireEvent.click(other1);
});

act(() => {
fireEvent.click(toggleNestedInput);
});

expect(onChange).toHaveBeenCalledWith(initialState, true);
});

it("should Radio state persist on unmonuted", () => {
const stateExpected = { ...initialState, gender: "M" };
const props = { onInit, onChange };
const { getByTestId } = render(<PersistStateOnUnmountHelpers {...props} />);

const toggleNestedRadio = getByTestId("toggleNestedRadio");
const genderm = getByTestId("genderm");
expect(onInit).toHaveBeenCalledWith(initialState, true);

act(() => {
fireEvent.click(genderm);
});

act(() => {
fireEvent.click(toggleNestedRadio);
});

expect(onChange).toHaveBeenCalledWith(stateExpected, true);
});

it("should Select state persist on unmonuted", () => {
const stateExpected = { ...initialState, select: "2" };
const props = { onInit, onChange };
const { getByTestId } = render(<PersistStateOnUnmountHelpers {...props} />);

const toggleSelect = getByTestId("toggleSelect");
const select = getByTestId("select");
expect(onInit).toHaveBeenCalledWith(initialState, true);

act(() => {
fireEvent.change(select, { target: { value: "2" } });
});

act(() => {
fireEvent.click(toggleSelect);
});

expect(onChange).toHaveBeenCalledWith(stateExpected, true);
});
});
21 changes: 21 additions & 0 deletions __tests__/helpers/components/CheckBox.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from "react";
import { Input, useValidation } from "./../../../src";

const required = value => (value ? undefined : "Required");

export default function CheckBox({ name = "checkbox", value }) {
const [status, validation] = useValidation([required]);
return (
<div>
<label>CheckBox: </label>
<Input
name={name}
{...validation}
type="checkbox"
data-testid="checkbox"
value={value}
/>
{status.error && <label data-testid="errorLabel">{status.error}</label>}
</div>
);
}
2 changes: 1 addition & 1 deletion __tests__/helpers/components/CollectionDynamicAdded.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function CollectionDynamicAdded() {
onlyNumberCollection
]);

const [collections, setCollection] = useChildren([]);
const [collections, setCollection] = useChildren();
const index = useRef(0);
const addCollection = () => {
index.current = index.current + 1;
Expand Down
29 changes: 29 additions & 0 deletions __tests__/helpers/components/FormWithValidationAfterMounted.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { useState } from "react";
import Submit from "./Submit";
import { Form, Input, useValidation } from "./../../../src";

const required = value =>
value && value.trim() !== "" ? undefined : "Required";

export const FormWithValidationAfterMounted = () => {
const [input, setInput] = useState(() => null);

const [status, inputValidation] = useValidation([required]);

const onClick = () => {
setInput(<Input key="1" type="text" name="text" {...inputValidation} />);
};

return (
<>
<Form>
{input}
<Submit />
</Form>
<button onClick={onClick} type="button" data-testid="addBtn">
addBtn
</button>
{status.error && <label data-testid="errorLabel">{status.error}</label>}
</>
);
};
Loading

0 comments on commit 8c571ab

Please sign in to comment.