Skip to content

Commit

Permalink
[challenge] add challenge on form handling
Browse files Browse the repository at this point in the history
  • Loading branch information
PhantomKnight287 committed May 16, 2024
1 parent c4b483d commit c0bcef1
Show file tree
Hide file tree
Showing 7 changed files with 348 additions and 0 deletions.
1 change: 1 addition & 0 deletions challenges/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dependencies": {
"@repo/config": "workspace:^",
"@testing-library/react": "^14.2.0",
"@testing-library/user-event": "^14.5.2",
"@webcontainer/api": "^1.1.8",
"jest": "^29.7.0",
"typescript": "^5",
Expand Down
13 changes: 13 additions & 0 deletions challenges/react/form-handling/challenge.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "../../schema.json",
"id": "form-handling",
"setup_commands": ["pnpm i", "clear"],
"test_runner": "vitest",
"track_slug": "react",
"author": ["phantomknight287"],
"description": "Learn how to create forms in react",
"difficulty": "beginner",
"label": "Form Handling",
"playground_needed": true,
"prerequisites": ["react/state", "react/props"]
}
134 changes: 134 additions & 0 deletions challenges/react/form-handling/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
Form handling is a crucial part of any web application to keep track of many things. For example: Amazon uses it to keep track of your credit/debit cards, your delivery address and many other things.

Forms in react are of 2 types:

- Controlled
- UnControlled

## UnControlled

Forms where the form data is handled by DOM itself and values are accessed using `useRef`. We will study about this method later once we learn about [`useRef`](https://react.dev/reference/react/useRef).

## Controlled

Forms where the form data is handled by the React's state. We will study this method thoroughly.

You must know about [`State`](/tracks/react/challenge/state) before moving forward.

Lets imagine a scenario where you have to create a form where a user will enter his name and password to login. Below is the design of how we will implement this.

- We will first create state for values
- We will create required input elements
- Add event listeners on input elements and update value on change
- Submit the form and perform actions based on input

Lets see how this will look in code

```jsx
import { useState } from "react";

export default function LoginForm() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");

const handleSubmit = (event) => {
alert(`Welcome back ${username}`);
};

return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="username">Username:</label>
<input
type="text"
id="username"
name="username"
value={username}
onChange={(event) => setUsername(event.target.value)}
/>
</div>
<div>
<label htmlFor="password">Password:</label>
<input
type="password"
id="password"
name="password"
value={password}
onChange={(event) => setPassword(event.target.value)}
/>
</div>
<button type="submit">Login</button>
</form>
);
}
```

This will render 2 input elements in DOM, you can enter your username and password then submit the form by pressing Login button(or by pressing enter once you are done entering values). It will show an alert showing you your username.

But, did you notice one thing? The page refreshed and our data is done gone. This is not what you want to happen in real life right? We need a way to somehow stop page from reloading.

We can do this in our `handleSubmit` function. It gets an `event` as parameter which has a `preventDefault` function on it. You can read more about it [here](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault).

Now our code is gonna look like this.

```jsx
import { useState } from "react";

export default function LoginForm() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");

const handleSubmit = (event) => {
event.preventDefault();
alert(`Welcome back ${username}`);
};

return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="username">Username:</label>
<input
type="text"
id="username"
name="username"
value={username}
onChange={(event) => setUsername(event.target.value)}
/>
</div>
<div>
<label htmlFor="password">Password:</label>
<input
type="password"
id="password"
name="password"
value={password}
onChange={(event) => setPassword(event.target.value)}
/>
</div>
<button type="submit">Login</button>
</form>
);
}
```

And this works as expected.

Try to add a hide and unhide button in password input as an exercise 😉

If you managed to do that you can submit your implementation in [Github Discussions](https://github.com/PhantomKnight287/frameground/discussions/7)

## Challenge

There are two files in `components` folder.

- Create a component, exported default, which renders 2 input field for title and description. It must take a function called `onSubmit` as prop and pass the submit event, name and description as its parameters.

- Create a component, exported default, which renders 1 select field and 1 name field for role and name. It must take a function called `onSubmit` as prop and pass the submit event, name and role as its parameters.

More details are present in files.

<Callout type="error" title="Warning" >

You must add all props to input elements listed in description present in files else tests will fail.

</Callout>
67 changes: 67 additions & 0 deletions challenges/react/form-handling/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { it, describe, expect, vi } from "vitest";
import { render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

//@ts-expect-error
import CreateTodo from "./src/components/create-todo";
//@ts-expect-error
import CreateUser from "./src/components/create-user";

describe("Tests for CreateTodo form", () => {
const onSubmit = vi.fn();
//@ts-expect-error
const rendered = render(<CreateTodo onSubmit={onSubmit} />);
const nameInput = rendered.getByTestId("name");
const descriptionInput = rendered.getByTestId("description");
const submitButton = rendered.getByTestId("submit_button");
const user = userEvent.setup();

it("Must have a user input", () => {
expect(nameInput).toBeTruthy();
});
it("Value of name must change", async () => {
await user.type(nameInput, "Gurpal");
//@ts-expect-error
expect(nameInput.value).toBe("Gurpal");
});

it("Value of description must change", async () => {
await user.type(descriptionInput, "Owner of FrameGround");
//@ts-expect-error
expect(descriptionInput.value).toBe("Owner of FrameGround");
});

it("Form must submit", async () => {
await user.click(submitButton);
expect(onSubmit).toHaveBeenCalledOnce();
});
});

describe("Tests for CreateUser form", () => {
const onSubmit = vi.fn();
//@ts-expect-error
const rendered = render(<CreateUser onSubmit={onSubmit} />);
const nameInput = rendered.getByTestId("name_input");
const roleSelect = rendered.getByTestId("role_select");
const submitButton = rendered.getByTestId("submit");
const user = userEvent.setup();

it("Must have a user input", () => {
expect(nameInput).toBeTruthy();
});
it("Value of name must change", async () => {
await user.type(nameInput, "Gurpal");
//@ts-expect-error
expect(nameInput.value).toBe("Gurpal");
});

it("Default value of role must be 'user'", async () => {
//@ts-expect-error
expect(roleSelect.value).toBe("user");
});

it("Form must submit", async () => {
await user.click(submitButton);
expect(onSubmit).toHaveBeenCalledOnce();
});
});
119 changes: 119 additions & 0 deletions challenges/react/form-handling/index.ts

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions challenges/react/form-handling/terminal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import type { ITerminalOptions } from "xterm";
export default {} satisfies ITerminalOptions
12 changes: 12 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit c0bcef1

Please sign in to comment.