Skip to content

Commit

Permalink
Fix initial value undefined issue when passed as prop for useField - …
Browse files Browse the repository at this point in the history
…Input - Select - Colletction (#26)

Co-authored-by: Antonio <[email protected]>
  • Loading branch information
iusehooks and Antonio committed Jan 8, 2021
1 parent 2f0a9f8 commit b6494b3
Show file tree
Hide file tree
Showing 7 changed files with 281 additions and 41 deletions.
67 changes: 66 additions & 1 deletion __tests__/Form.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from "./helpers/components/ComplexForm";
import { mountForm } from "./helpers/utils/mountForm";

import { Input } from "./../src";
import { Input, Select, Collection, TextArea } from "./../src";

const dataTestid = "email";
const typeInput = "text";
Expand Down Expand Up @@ -92,6 +92,71 @@ describe("Component => Form", () => {
expect(onInit).toHaveReturnedWith(initialState);
});

it("should override a initialized the Form state if Fields contain the value prop", () => {
const initialState = {
text: "foo",
number: 1,
checkbox: "1",
radio: "2",
range: 2,
selectSingle: "1",
selectMultiple: ["3"],
object: { text: "foo" },
object1: { text: "foo" },
textarea: "foo",
array: ["foo"],
array1: ["foo"]
};
const props = { initialState, onInit };
const children = [
<Input type="text" name="text" value="BeBo" key="1" />,
<Input type="number" name="number" value={10} key="2" />,
<Input type="checkbox" name="checkbox" value="4" key="3" checked />,
<Input type="radio" name="radio" value="6" key="4" checked />,
<Input type="range" min="0" max="120" name="range" value={7} key="5" />,
<Select name="selectSingle" value="2" key="6">
<option value="" />
<option value="1">1</option>
<option value="2">2</option>
</Select>,
<Select key="7" multiple name="selectMultiple" value={["1", "2"]}>
<option value="" />
<option value="1">1</option>
<option value="2">2</option>
<option value="2">3</option>
</Select>,
<Collection key="8" object name="object">
<Input type="text" name="text" value="BeBo" />,
</Collection>,
<Collection key="9" object name="object1" value={{ text: "BeBo" }}>
<Input type="text" name="text" />,
</Collection>,
<TextArea name="textarea" value="BeBo" key="10" />,
<Collection key="11" array name="array" value={["foo"]}>
<Input type="text" value="BeBo" />,
</Collection>,
<Collection key="12" array name="array1" value={["BeBo"]}>
<Input type="text" />,
</Collection>
];
mountForm({ props, children });

expect(onInit).toHaveReturnedWith({
text: "BeBo",
number: 10,
checkbox: "4",
radio: "6",
range: 7,
selectSingle: "2",
selectMultiple: ["1", "2"],
object: { text: "BeBo" },
object1: { text: "BeBo" },
textarea: "BeBo",
array: ["BeBo"],
array1: ["BeBo"]
});
});

it("should reset the Form state", () => {
const initialState = {
user: { name: "foo", lastname: "anything", email: "[email protected]" }
Expand Down
36 changes: 26 additions & 10 deletions __tests__/helpers/components/CustomField.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
import React from "react";
import React, { useEffect } from "react";
import { useField, withIndex } from "./../../../src";

export const CustomField = withIndex(({ name, value, ...restAttr }) => {
const props = useField({ type: "custom", name, value });
const onChange = () => props.onChange({ target: { value: "5" } });
return (
<button type="button" onClick={onChange} {...restAttr}>
Change Value
</button>
);
});
export const CustomField = withIndex(
({
type = "custom",
name,
value,
jestFN = () => {},
valueToChange = "5"
}) => {
const props = useField({ type, name, value });
const onChange = () => props.onChange({ target: { value: valueToChange } });
useEffect(() => {
jestFN(props.value);
}, []);
return (
<>
<pre>
<code data-testid="output">{JSON.stringify(props.value)}</code>
</pre>
<button type="button" data-testid="buttonChange" onClick={onChange}>
Change Value
</button>
</>
);
}
);
9 changes: 9 additions & 0 deletions __tests__/helpers/components/InputCustom.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,12 @@ export const InputCustom = withIndex(
return <input {...restAttr} {...props}></input>;
}
);

export const InputUseField = withIndex(({ type, name, value }) => {
const props = useField({ type, name, value });
return (
<pre>
<code>{JSON.stringify(props.value)}</code>
</pre>
);
});
157 changes: 153 additions & 4 deletions __tests__/hooks/useField.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,25 +50,174 @@ describe("Hooks => useField", () => {
expect(onChange).toHaveBeenCalledWith({ number: "50", number2: "5" }, true);
});

it("should render a Field of type custom with an initial value", () => {
it("should render a Field of type custom with an initial value passed as prop", () => {
const name = "custom";
const props = { onInit, onChange };
const initial = { a: "test" };
const jestFN = jest.fn();

const children = [
<CustomField key="1" data-testid={name} name={name} value={initial} />
<CustomField key="1" name={name} value={initial} jestFN={jestFN} />
];
const { getByTestId } = mountForm({ children, props });

expect(jestFN).toHaveBeenCalledWith(initial);

expect(onInit).toHaveBeenCalledWith({ [name]: initial }, true);

const buttonChange = getByTestId("buttonChange");
act(() => {
fireEvent.click(buttonChange);
});

expect(onChange).toHaveBeenCalledWith({ [name]: "5" }, true);
});

it("should render a Field of type text with an initial value passed as prop", () => {
const name = "text";
const props = { onInit, onChange };
const initial = "test";
const jestFN = jest.fn();

const children = [
<CustomField
key="1"
type="text"
name={name}
value={initial}
jestFN={jestFN}
/>
];
const { getByTestId } = mountForm({ children, props });

expect(jestFN).toHaveBeenCalledWith(initial);

expect(onInit).toHaveBeenCalledWith({ [name]: initial }, true);

const buttonChange = getByTestId("buttonChange");
act(() => {
fireEvent.click(buttonChange);
});

expect(onChange).toHaveBeenCalledWith({ [name]: "5" }, true);
});

it("should render a Field of type radio with an initial value passed as prop", () => {
const name = "radio";
const props = { onChange };
const initial = "5";
const jestFN = jest.fn();

const children = [
<CustomField
key="1"
type="radio"
name={name}
value={initial}
jestFN={jestFN}
/>
];
const { getByTestId } = mountForm({ children, props });

expect(jestFN).toHaveBeenCalledWith(initial);

const buttonChange = getByTestId("buttonChange");
act(() => {
fireEvent.click(buttonChange);
});

expect(onChange).toHaveBeenCalledWith({ [name]: "5" }, true);
});

it("should render a Field of type checkbox with an initial value passed as prop", () => {
const name = "checkbox";
const props = { onChange };
const initial = "5";
const jestFN = jest.fn();

const children = [
<CustomField
key="1"
type="checkbox"
name={name}
value={initial}
jestFN={jestFN}
/>
];
const { getByTestId } = mountForm({ children, props });

expect(jestFN).toHaveBeenCalledWith(initial);

const buttonChange = getByTestId("buttonChange");
act(() => {
fireEvent.click(buttonChange);
});

expect(onChange).toHaveBeenCalledWith({ [name]: "5" }, true);
});

it("should render a Field of type select single with an initial value passed as prop", () => {
const name = "select";
const props = { onInit, onChange };
const initial = "5";
const jestFN = jest.fn();

const children = [
<CustomField
key="1"
type="select"
data-testid="buttonChange"
name={name}
value={initial}
jestFN={jestFN}
/>
];
const { getByTestId } = mountForm({ children, props });

expect(jestFN).toHaveBeenCalledWith(initial);

expect(onInit).toHaveBeenCalledWith({ [name]: initial }, true);

const custom = getByTestId("custom");
const buttonChange = getByTestId("buttonChange");
act(() => {
fireEvent.click(custom);
fireEvent.click(buttonChange);
});

expect(onChange).toHaveBeenCalledWith({ [name]: "5" }, true);
});

it("should render a Field of type select multiple with an initial value passed as prop", () => {
const name = "select";
const props = { onInit, onChange };
const initial = ["5", "6"];
const valueAfterClick = ["7", "8"];

const jestFN = jest.fn();

const children = [
<CustomField
key="1"
type="select"
name={name}
valueToChange={valueAfterClick}
value={initial}
jestFN={jestFN}
/>
];
const { getByTestId } = mountForm({ children, props });

expect(jestFN).toHaveBeenCalledWith(initial);

expect(onInit).toHaveBeenCalledWith({ [name]: initial }, true);

const buttonChange = getByTestId("buttonChange");
act(() => {
fireEvent.click(buttonChange);
});

expect(onChange).toHaveBeenCalledWith({ [name]: valueAfterClick }, true);
});

it("should change a Field value due to an action", () => {
const props = { onChange };
const children = [
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "usetheform",
"version": "3.2.0",
"version": "3.2.1",
"description": "React library for composing declarative forms in React and managing their state.",
"main": "./build/index.js",
"module": "./build/es/index.js",
Expand Down
46 changes: 25 additions & 21 deletions src/hooks/useField.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,29 +72,33 @@ export function useField(props) {
context
);

if (type === "checkbox" || type === "radio") {
if (!isMounted.current && initialValueRef.current) {
valueField.current = initialValueRef.current;
checkedField.current =
type === "checkbox"
? state[nameProp.current] !== undefined ||
(!isMounted.current && initialChecked === true)
: state[nameProp.current] === initialValueRef.current;
} else if (type === "select") {
valueField.current =
state[nameProp.current] !== undefined
? state[nameProp.current]
: !multiple
? ""
: [];
} else if (type === "file") {
valueField.current = state[nameProp.current];
fileField.current =
state[nameProp.current] !== undefined ? fileField.current : "";
} else if (type === "custom") {
valueField.current = state[nameProp.current];
} else {
valueField.current =
state[nameProp.current] !== undefined ? state[nameProp.current] : "";
if (type === "checkbox" || type === "radio") {
valueField.current = initialValueRef.current;
checkedField.current =
type === "checkbox"
? state[nameProp.current] !== undefined ||
(!isMounted.current && initialChecked === true)
: state[nameProp.current] === initialValueRef.current;
} else if (type === "select") {
valueField.current =
state[nameProp.current] !== undefined
? state[nameProp.current]
: !multiple
? ""
: [];
} else if (type === "file") {
valueField.current = state[nameProp.current];
fileField.current =
state[nameProp.current] !== undefined ? fileField.current : "";
} else if (type === "custom") {
valueField.current = state[nameProp.current];
} else {
valueField.current =
state[nameProp.current] !== undefined ? state[nameProp.current] : "";
}
}

const { current: applyReducers } = useRef(chainReducers(reducers));
Expand Down
5 changes: 1 addition & 4 deletions src/hooks/useObject.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,7 @@ export function useObject(props) {

// getValue from parent context
if (!isMounted.current) {
state.current =
context.state[nameProp.current] !== undefined
? context.state[nameProp.current]
: init;
state.current = initValue || context.state[nameProp.current] || init;
} else {
state.current =
context.state[nameProp.current] || (isArray ? initArray : initObject);
Expand Down

0 comments on commit b6494b3

Please sign in to comment.