-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
coral: implement form component [#141] #159
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
3dfe26b
feat: Add app directory and restructure related files.
programmiri b092a88
feat: Change unused vars to error for linting.
programmiri d9b1725
feat: Add Form component based on aiven-core implementation.
programmiri 1eb49c5
feat: Add userEvent package.
programmiri d93e8a6
feat: Add tests for Form based on aiven-core.
programmiri ae65396
feat: Introduce explicit typing for Form.
programmiri 5c4c952
feat: Update lodash usage to match dependencies.
programmiri bf72dab
Remove comment about aiven-core mirroring
programmiri d0c8092
Remove unused library.
programmiri 99b346f
Rename and restructure pages files.
programmiri 6b6bc0e
Add login page to routing.
programmiri 2d27275
Reorder lint rules after rebase.
programmiri File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
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
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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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,153 @@ | ||
import { Button } from "@aivenio/design-system"; | ||
import type { RenderResult } from "@testing-library/react"; | ||
import { cleanup, render, screen, waitFor } from "@testing-library/react"; | ||
import userEvent from "@testing-library/user-event"; | ||
import React from "react"; | ||
import type { DeepPartial, FieldValues } from "react-hook-form"; | ||
import { | ||
Form, | ||
SubmitErrorHandler, | ||
SubmitHandler, | ||
TextInput, | ||
useForm, | ||
} from "src/app/components/Form"; | ||
import { z, ZodSchema } from "zod"; | ||
|
||
type WrapperProps<T extends FieldValues> = { | ||
schema: ZodSchema; | ||
defaultValues?: DeepPartial<T>; | ||
onSubmit: SubmitHandler<T>; | ||
onError: SubmitErrorHandler<T>; | ||
}; | ||
|
||
const Wrapper = <T extends FieldValues>({ | ||
schema, | ||
defaultValues, | ||
onSubmit, | ||
onError, | ||
children, | ||
}: React.PropsWithChildren<WrapperProps<T>>): React.ReactElement => { | ||
const form = useForm<T>({ schema, defaultValues }); | ||
return ( | ||
<Form onSubmit={onSubmit} onError={onError} {...form}> | ||
{children} | ||
</Form> | ||
); | ||
}; | ||
|
||
describe("Form", () => { | ||
const onSubmit = jest.fn(); | ||
const onError = jest.fn(); | ||
let results: RenderResult; | ||
let user: ReturnType<typeof userEvent.setup>; | ||
|
||
beforeEach(() => { | ||
user = userEvent.setup(); | ||
}); | ||
|
||
afterEach(() => { | ||
cleanup(); | ||
onSubmit.mockClear(); | ||
onError.mockClear(); | ||
}); | ||
|
||
const renderForm = <T extends FieldValues>( | ||
children: React.ReactNode, | ||
{ | ||
schema, | ||
defaultValues, | ||
}: { schema: ZodSchema; defaultValues?: DeepPartial<T> } | ||
) => { | ||
return render( | ||
<Wrapper<T> | ||
schema={schema} | ||
defaultValues={defaultValues} | ||
onSubmit={onSubmit} | ||
onError={onError} | ||
> | ||
{children} | ||
<Button type="submit" title="Submit" /> | ||
</Wrapper> | ||
); | ||
}; | ||
|
||
const typeText = async (value: string) => { | ||
const el = screen.getByRole("textbox"); | ||
await user.clear(el); | ||
await user.type(el, value); | ||
}; | ||
|
||
const submit = async () => { | ||
await user.click(screen.getByRole("button", { name: "Submit" })); | ||
}; | ||
|
||
const assertSubmitted = (data: Record<string, string>) => { | ||
expect(onSubmit).toHaveBeenCalledWith(data, expect.anything()); | ||
}; | ||
|
||
describe("<Form>", () => { | ||
const schema = z.object({ name: z.string().min(3, "error") }); | ||
type Schema = z.infer<typeof schema>; | ||
|
||
beforeEach(() => { | ||
results = renderForm( | ||
<TextInput<Schema> name="name" labelText="TextInput" />, | ||
{ schema } | ||
); | ||
}); | ||
|
||
it("should call onSubmit() after submit if there are no validation errors", async () => { | ||
await user.type(screen.getByLabelText("TextInput"), "abc"); | ||
await submit(); | ||
await waitFor(() => | ||
expect(onSubmit).toHaveBeenCalledWith( | ||
{ name: "abc" }, | ||
expect.anything() | ||
) | ||
); | ||
}); | ||
|
||
it("should call onError() after submit if there are validation errors", async () => { | ||
await user.type(screen.getByLabelText("TextInput"), "a"); | ||
await submit(); | ||
await waitFor(() => expect(onError).toHaveBeenCalled()); | ||
expect(onError.mock.calls[0][0]).toMatchObject({ | ||
name: { message: "error" }, | ||
}); | ||
}); | ||
}); | ||
|
||
describe("<TextInput>", () => { | ||
const schema = z.object({ name: z.string().min(3, "error") }); | ||
type Schema = z.infer<typeof schema>; | ||
|
||
beforeEach(() => { | ||
results = renderForm( | ||
<TextInput<Schema> name="name" labelText="TextInput" />, | ||
{ schema } | ||
); | ||
}); | ||
|
||
it("should render <TextInput>", () => { | ||
expect(results.container).toMatchSnapshot(); | ||
}); | ||
|
||
it("should render label", () => { | ||
expect(screen.queryByLabelText("TextInput")).toBeVisible(); | ||
}); | ||
|
||
it("should sync value to form state", async () => { | ||
await typeText("value{tab}"); | ||
await submit(); | ||
assertSubmitted({ name: "value" }); | ||
}); | ||
|
||
it("should render errors after blur event and hide them after valid input", async () => { | ||
await typeText("a{tab}"); | ||
await waitFor(() => expect(screen.queryByText("error")).toBeVisible()); | ||
|
||
await typeText("abc{tab}"); | ||
await waitFor(() => expect(screen.queryByText("error")).toBeNull()); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think want to even try to keep this up to date with current implementation so lets keep the examples abstract.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But we can do this later 👍