-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
39c38e5
commit 1f09904
Showing
2 changed files
with
131 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import React, { FC } from "react"; | ||
import { useFormContext } from "./form-context"; | ||
import { AttributeContext, useAttributeContext } from "./attribute-context"; | ||
|
||
export interface ListGroupChildProps { | ||
/** | ||
* Callback function that will add a new item in to the list. This will use | ||
* the `newItem` function to generate a new item. | ||
*/ | ||
add: () => void; | ||
} | ||
|
||
export interface ListGroupProps { | ||
/** | ||
* The attribute this input is for | ||
*/ | ||
attribute: string; | ||
/** | ||
* Function that will return a new empty item. This will be called whenever | ||
* added a new item to the list. The default will return a new empty object. | ||
*/ | ||
newItem?: () => any; | ||
/** | ||
* Render a list of inputs | ||
*/ | ||
children: (props: ListGroupChildProps) => JSX.Element; | ||
} | ||
|
||
export const ListGroup: FC<ListGroupProps> = ({ children, attribute, newItem }) => { | ||
const { getAttribute, setAttribute } = useFormContext(); | ||
const options = getAttribute(attribute, []); | ||
|
||
if (typeof newItem === "undefined") { | ||
throw new Error("newItem must not be undefined."); | ||
} | ||
|
||
const add = () => { | ||
setAttribute(attribute, [...getAttribute(attribute, []), newItem()]); | ||
}; | ||
|
||
return <AttributeContext.Provider value={{ attribute, options }}>{children({ add })}</AttributeContext.Provider>; | ||
}; | ||
|
||
ListGroup.defaultProps = { | ||
newItem: () => ({}), | ||
}; | ||
|
||
export interface ListOptionChildProps { | ||
/** | ||
* The index this option is at | ||
*/ | ||
index: string; | ||
} | ||
|
||
export interface ListOptionProps { | ||
/** | ||
* Render all the form elements for this list option | ||
*/ | ||
children: (props: ListOptionChildProps) => JSX.Element; | ||
} | ||
|
||
export const ListOption: FC<ListOptionProps> = ({ children }) => { | ||
const { options } = useAttributeContext(); | ||
|
||
return ( | ||
<> | ||
{Object.keys(options).map((index) => | ||
children({ | ||
index, | ||
}) | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
export default ListGroup; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import React from "react"; | ||
import { render } from "@testing-library/react"; | ||
import userEvent from "@testing-library/user-event"; | ||
import Form from "../src/form"; | ||
import { ListGroup, ListOption } from "../src/list-group"; | ||
import { InputGroup } from "../src/input-group"; | ||
|
||
it("will render and submit with a radio list", async () => { | ||
const onSubmit = jest.fn(); | ||
const { getByLabelText, getByText } = render( | ||
<Form initialValues={{}} onSubmit={onSubmit}> | ||
<ListGroup attribute="tags" newItem={() => ""}> | ||
{({ add }) => ( | ||
<div> | ||
<ListOption> | ||
{({ index }) => ( | ||
<div key={index}> | ||
<InputGroup attribute={`tags.${index}`}> | ||
{({ props }) => ( | ||
<div> | ||
<label htmlFor={props.id}>Tag {parseInt(index) + 1}</label> | ||
<input {...props} /> | ||
</div> | ||
)} | ||
</InputGroup> | ||
</div> | ||
)} | ||
</ListOption> | ||
<button type="button" onClick={add}>Add Tag</button> | ||
</div> | ||
)} | ||
</ListGroup> | ||
<button>Submit</button> | ||
</Form> | ||
); | ||
|
||
await userEvent.click(getByText("Add Tag")); | ||
await userEvent.click(getByText("Submit")); | ||
|
||
expect(onSubmit).toBeCalledTimes(1); | ||
expect(onSubmit).toBeCalledWith({ formState: { tags: [""] } }); | ||
|
||
await userEvent.type(getByLabelText("Tag 1"), "Tag One"); | ||
await userEvent.click(getByText("Submit")); | ||
|
||
expect(onSubmit).toBeCalledTimes(2); | ||
expect(onSubmit).toBeCalledWith({ formState: { tags: ["Tag One"] } }); | ||
|
||
await userEvent.click(getByText("Add Tag")); | ||
await userEvent.type(getByLabelText("Tag 2"), "Tag Two"); | ||
await userEvent.click(getByText("Submit")); | ||
|
||
expect(onSubmit).toBeCalledTimes(3); | ||
expect(onSubmit).toBeCalledWith({ formState: { tags: ["Tag One", "Tag Two"] } }); | ||
}); |