mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-04-16 13:00:49 -04:00
feat(curriculum): adding superhero application form (React forms) workshop (#59932)
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Co-authored-by: Tom <20648924+moT01@users.noreply.github.com>
This commit is contained in:
@@ -3539,9 +3539,11 @@
|
||||
"In these lecture videos, you will learn about working with forms in React."
|
||||
]
|
||||
},
|
||||
"sgau": {
|
||||
"title": "276",
|
||||
"intro": []
|
||||
"workshop-superhero-application-form": {
|
||||
"title": "Build a Superhero Application Form",
|
||||
"intro": [
|
||||
"In this workshop, you will build a superhero application form."
|
||||
]
|
||||
},
|
||||
"lab-event-rsvp": {
|
||||
"title": "Build an Event RSVP",
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Introduction to the Build a Superhero Application Form
|
||||
block: workshop-superhero-application-form
|
||||
superBlock: full-stack-developer
|
||||
---
|
||||
|
||||
## Introduction to the Build a Superhero Application Form
|
||||
|
||||
This is workshop will cover how to work with forms in React.
|
||||
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"name": "Build a Superhero Application Form",
|
||||
"isUpcomingChange": true,
|
||||
"usesMultifileEditor": true,
|
||||
"hasEditableBoundaries": true,
|
||||
"blockType": "workshop",
|
||||
"blockLayout": "challenge-grid",
|
||||
"dashedName": "workshop-superhero-application-form",
|
||||
"superBlock": "full-stack-developer",
|
||||
"challengeOrder": [
|
||||
{
|
||||
"id": "6808fe91a5cb0c3355f23027",
|
||||
"title": "Step 1"
|
||||
},
|
||||
{
|
||||
"id": "680900675ae3d54ee19590c3",
|
||||
"title": "Step 2"
|
||||
},
|
||||
{
|
||||
"id": "680fc849a6f2be0a8597c593",
|
||||
"title": "Step 3"
|
||||
},
|
||||
{
|
||||
"id": "680900675ae3d54ee19590c4",
|
||||
"title": "Step 4"
|
||||
},
|
||||
{
|
||||
"id": "680900675ae3d54ee19590c5",
|
||||
"title": "Step 5"
|
||||
},
|
||||
{
|
||||
"id": "680900675ae3d54ee19590c6",
|
||||
"title": "Step 6"
|
||||
},
|
||||
{
|
||||
"id": "680900675ae3d54ee19590c7",
|
||||
"title": "Step 7"
|
||||
},
|
||||
{
|
||||
"id": "680900675ae3d54ee19590c8",
|
||||
"title": "Step 8"
|
||||
},
|
||||
{
|
||||
"id": "680900675ae3d54ee19590c9",
|
||||
"title": "Step 9"
|
||||
},
|
||||
{
|
||||
"id": "680900675ae3d54ee19590ca",
|
||||
"title": "Step 10"
|
||||
},
|
||||
{
|
||||
"id": "680900675ae3d54ee19590cb",
|
||||
"title": "Step 11"
|
||||
},
|
||||
{
|
||||
"id": "680900675ae3d54ee19590cc",
|
||||
"title": "Step 12"
|
||||
},
|
||||
{
|
||||
"id": "680900675ae3d54ee19590cd",
|
||||
"title": "Step 13"
|
||||
},
|
||||
{
|
||||
"id": "680900675ae3d54ee19590ce",
|
||||
"title": "Step 14"
|
||||
},
|
||||
{
|
||||
"id": "68148d280ee30e5a567a0e2d",
|
||||
"title": "Step 15"
|
||||
},
|
||||
{
|
||||
"id": "680900675ae3d54ee19590cf",
|
||||
"title": "Step 16"
|
||||
},
|
||||
{
|
||||
"id": "680900675ae3d54ee19590d0",
|
||||
"title": "Step 17"
|
||||
},
|
||||
{
|
||||
"id": "68149b101d905a6fc2fcd6d8",
|
||||
"title": "Step 18"
|
||||
},
|
||||
{
|
||||
"id": "680900675ae3d54ee19590d1",
|
||||
"title": "Step 19"
|
||||
},
|
||||
{
|
||||
"id": "680900675ae3d54ee19590d2",
|
||||
"title": "Step 20"
|
||||
},
|
||||
{
|
||||
"id": "680900675ae3d54ee19590d3",
|
||||
"title": "Step 21"
|
||||
}
|
||||
],
|
||||
"helpCategory": "JavaScript"
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
---
|
||||
id: 6808fe91a5cb0c3355f23027
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In this workshop, you'll learn how to work with forms in React by building a superhero application form.
|
||||
|
||||
Some boilerplate has been provided, you just need to create the rest of the React component.
|
||||
|
||||
Begin by returning a `div` element with a `className` of `form-wrap`. Inside the `div`, create an `h2` element with the text `Superhero Application Form`. Below that, create a `p` element with the text `Please complete all fields`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should return a `div` element with the `className` of `form-wrap`.
|
||||
|
||||
```js
|
||||
assert.exists(document?.querySelector("div.form-wrap"));
|
||||
```
|
||||
|
||||
You should create an `h2` element inside your `div` element.
|
||||
|
||||
```js
|
||||
assert.exists(document?.querySelector(".form-wrap > h2"));
|
||||
```
|
||||
|
||||
Your `h2` element should have the text `Superhero Application Form`.
|
||||
|
||||
```js
|
||||
const h2El = document?.querySelector("h2")
|
||||
assert.equal(h2El?.textContent, "Superhero Application Form");
|
||||
```
|
||||
|
||||
You should create a `p` element inside your `div` element. Make sure it comes after the `h2`.
|
||||
|
||||
```js
|
||||
assert.exists(document?.querySelector(".form-wrap > h2 + p"));
|
||||
```
|
||||
|
||||
Your `p` element should have the text `Please complete all fields`.
|
||||
|
||||
```js
|
||||
const pEl = document?.querySelector("p")
|
||||
assert.equal(pEl?.textContent, "Please complete all fields");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,196 @@
|
||||
---
|
||||
id: 680900675ae3d54ee19590c3
|
||||
title: Step 2
|
||||
challengeType: 0
|
||||
dashedName: step-2
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Before building out the form itself, you will create the state variables you need. They are `heroName`, `realName`, `powerSource`, and `powers`.
|
||||
|
||||
For now, create the variables and setters for `heroName` and `realName`. Both should have an intitial value of empty strings.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should use the array destructuring syntax to set a `heroName` state variable and a `setHeroName` setter.
|
||||
|
||||
```js
|
||||
assert.match(code, /(const|let)\s+\[\s*heroName\s*,\s*setHeroName\s*\]/);
|
||||
```
|
||||
|
||||
Your `heroName` and `setHeroName` should use the `useState` hook.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const abuseState = __helpers.spyOn(React, "useState");
|
||||
const script = [...document.querySelectorAll("script")].find((s) => s.dataset.src === "index.jsx").innerText;
|
||||
const exports = {};
|
||||
const _a = eval(script);
|
||||
const _b = await __helpers.prepTestComponent(exports.SuperheroForm);
|
||||
|
||||
assert.isAtLeast(abuseState.calls.length, 1);
|
||||
}
|
||||
```
|
||||
|
||||
Your `useState` hook for `heroName` should have an initial value of empty string.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const abuseState = __helpers.spyOn(React, "useState");
|
||||
const script = [...document.querySelectorAll("script")].find((s) => s.dataset.src === "index.jsx").innerText;
|
||||
const exports = {};
|
||||
const _a = eval(script);
|
||||
const _b = await __helpers.prepTestComponent(exports.SuperheroForm);
|
||||
|
||||
assert.equal(abuseState.calls[0]?.[0], "");
|
||||
}
|
||||
```
|
||||
|
||||
You should use the array destructuring syntax to set a `realName` state variable and a `setRealName` setter.
|
||||
|
||||
```js
|
||||
assert.match(code, /(const|let)\s+\[\s*realName\s*,\s*setRealName\s*\]/);
|
||||
```
|
||||
|
||||
Your `realName` and `setRealName` should use the `useState` hook.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const abuseState = __helpers.spyOn(React, "useState");
|
||||
const script = [...document.querySelectorAll("script")].find((s) => s.dataset.src === "index.jsx").innerText;
|
||||
const exports = {};
|
||||
const _a = eval(script);
|
||||
const _b = await __helpers.prepTestComponent(exports.SuperheroForm);
|
||||
|
||||
assert.isAtLeast(abuseState.calls.length, 2);
|
||||
}
|
||||
```
|
||||
|
||||
Your `useState` hook for `realName` should have an initial value of empty string.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const abuseState = __helpers.spyOn(React, "useState");
|
||||
const script = [...document.querySelectorAll("script")].find((s) => s.dataset.src === "index.jsx").innerText;
|
||||
const exports = {};
|
||||
const _a = eval(script);
|
||||
const _b = await __helpers.prepTestComponent(exports.SuperheroForm);
|
||||
|
||||
assert.equal(abuseState.calls[1]?.[0], "");
|
||||
}
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,148 @@
|
||||
---
|
||||
id: 680900675ae3d54ee19590c4
|
||||
title: Step 4
|
||||
challengeType: 0
|
||||
dashedName: step-4
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Below the paragraph, create a `form` element. Within it, add a `div` with a `className` of `section`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should create a `form` element.
|
||||
|
||||
```js
|
||||
assert.exists(document?.querySelector("form"));
|
||||
```
|
||||
|
||||
You should create a `div` element inside your `form` element.
|
||||
|
||||
```js
|
||||
assert.exists(document?.querySelector("form > div"));
|
||||
```
|
||||
|
||||
Your `div` element should have a `className` of `section`.
|
||||
|
||||
```js
|
||||
assert.exists(document?.querySelector("form > div.section"));
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,162 @@
|
||||
---
|
||||
id: 680900675ae3d54ee19590c5
|
||||
title: Step 5
|
||||
challengeType: 0
|
||||
dashedName: step-5
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Time to create the labels and inputs. In the form, create a `label` with the text `Hero Name` and an `input` element of type `text` inside it.
|
||||
|
||||
Connect the `input` for `Hero Name` to the `heroName` state variable by giving it a `value` attribute set to `heroName`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should create a `label` element.
|
||||
|
||||
```js
|
||||
assert.exists(document?.querySelector("label"));
|
||||
```
|
||||
|
||||
Your `label` element should have `Hero Name` as its text.
|
||||
|
||||
```js
|
||||
const labelEl = document.querySelector("label")
|
||||
assert.equal(labelEl?.textContent, "Hero Name");
|
||||
```
|
||||
|
||||
You should create an `input` of type `text` element inside your `label` element.
|
||||
|
||||
```js
|
||||
const inputEl = document.querySelector("label > input");
|
||||
assert.equal(inputEl?.getAttribute("type"), "text");
|
||||
```
|
||||
|
||||
Your `input` element should have its value set to the `heroName` state variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /value\s*=\s*\{\s*heroName\s*\}/)
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
<form>
|
||||
<div className='section'>
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,151 @@
|
||||
---
|
||||
id: 680900675ae3d54ee19590c6
|
||||
title: Step 6
|
||||
challengeType: 0
|
||||
dashedName: step-6
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
You may notice you can't type in the `input`. If you open up the console you will see a warning.
|
||||
|
||||
To remove the warning, use the `onChange` event to update `heroName`. The value for the `onChange` event should be an arrow function with `e` for the parameter. The arrow function should implicitly return `setHeroName(e.target.value)`.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `input` element should have an `onChange` attribute.
|
||||
|
||||
```js
|
||||
assert.match(code, /onChange\s*=\s*\{/)
|
||||
```
|
||||
|
||||
You should use the `onChange` to set the value of the `heroName` state variable so you can type in the input field.
|
||||
|
||||
```js
|
||||
assert.match(code, /onChange\s*=\s*\{\s*(e|\(\s*e\s*\))\s*=>\s*setHeroName\s*\(\s*e\s*\.\s*target\s*\.\s*value\s*\)\s*\}/)
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
<form>
|
||||
<div className='section'>
|
||||
--fcc-editable-region--
|
||||
<label>
|
||||
Hero Name
|
||||
<input type='text' value={heroName} />
|
||||
</label>
|
||||
--fcc-editable-region--
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,177 @@
|
||||
---
|
||||
id: 680900675ae3d54ee19590c7
|
||||
title: Step 7
|
||||
challengeType: 0
|
||||
dashedName: step-7
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Create another `label` with the text `Real Name` and an `input` of type `password` within it. Give the `input` a `value` of `realName`.
|
||||
|
||||
Then use the `onChange` event to update `realName` in the same way you updated `heroName`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should create a `label` element.
|
||||
|
||||
```js
|
||||
const labels = document.querySelectorAll("label");
|
||||
assert.lengthOf(labels, 2);
|
||||
```
|
||||
|
||||
Your `label` element should have `Real Name` as its text.
|
||||
|
||||
```js
|
||||
const labels = document.querySelectorAll("label");
|
||||
assert.equal(labels[1]?.textContent.trim(), "Real Name");
|
||||
```
|
||||
|
||||
You should create an `input` of type `password` inside your `label` element.
|
||||
|
||||
```js
|
||||
const inputEl = document.querySelectorAll("label > input");
|
||||
assert.equal(inputEl[1]?.getAttribute("type"), "password");
|
||||
```
|
||||
|
||||
Your `input` element should have its value set to the `realName` state variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /value\s*=\s*\{\s*realName\s*\}/)
|
||||
```
|
||||
|
||||
You should use the `onChange` event to set the value of the `realName` state variable so you can type in the `input` field.
|
||||
|
||||
```js
|
||||
assert.match(code, /onChange\s*=\s*\{\s*(e|\(\s*e\s*\))\s*=>\s*setRealName\s*\(\s*e\s*\.\s*target\s*\.\s*value\s*\)\s*\}/)
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
<form>
|
||||
<div className='section'>
|
||||
<label>
|
||||
Hero Name
|
||||
<input
|
||||
type='text'
|
||||
value={heroName}
|
||||
onChange={e => setHeroName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,171 @@
|
||||
---
|
||||
id: 680900675ae3d54ee19590c8
|
||||
title: Step 8
|
||||
challengeType: 0
|
||||
dashedName: step-8
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Next, create a `label` with the `className` of `section` and `column`, and the text `How did you get your powers?`. Inside the `label`, create a `select` element.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should create a `label` element with a `className` of `section` and `column`.
|
||||
|
||||
```js
|
||||
const labelEl = document?.querySelector("label.section.column");
|
||||
assert.exists(labelEl);
|
||||
```
|
||||
|
||||
Your `label` element should have the text `How did you get your powers?`.
|
||||
|
||||
```js
|
||||
const labelEl = document?.querySelector("label.section.column");
|
||||
assert.equal(labelEl?.textContent, "How did you get your powers?")
|
||||
```
|
||||
|
||||
You should create a `select` element inside your `label` element.
|
||||
|
||||
```js
|
||||
const selectEl = document?.querySelector("label > select");
|
||||
assert.exists(selectEl);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
<form>
|
||||
<div className='section'>
|
||||
<label>
|
||||
Hero Name
|
||||
<input
|
||||
type='text'
|
||||
value={heroName}
|
||||
onChange={e => setHeroName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Real Name
|
||||
<input
|
||||
type='password'
|
||||
value={realName}
|
||||
onChange={e => setRealName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,214 @@
|
||||
---
|
||||
id: 680900675ae3d54ee19590c9
|
||||
title: Step 9
|
||||
challengeType: 0
|
||||
dashedName: step-9
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Inside your `select` element, you need to create options for the user to choose as their power source. The `select` should have seven options listed below:
|
||||
|
||||
| Power Source |
|
||||
|-------------------------------|
|
||||
| `Select one` |
|
||||
| `Bitten by a strange creature` |
|
||||
| `Radioactive exposure` |
|
||||
| `Science experiment` |
|
||||
| `Alien heritage ` |
|
||||
| `Ancient artifact discovery` |
|
||||
| `Other` |
|
||||
|
||||
# --hints--
|
||||
|
||||
Your should create the first `option` element with the text `Select one` inside your `select` element.
|
||||
|
||||
```js
|
||||
const optionEls = document?.querySelectorAll("select > option");
|
||||
assert.equal(optionEls[0]?.textContent, "Select one");
|
||||
```
|
||||
|
||||
Your should create a second `option` element with the text `Bitten by a strange creature` inside your `select` element.
|
||||
|
||||
```js
|
||||
const optionEls = document?.querySelectorAll("select > option");
|
||||
assert.equal(optionEls[1]?.textContent, "Bitten by a strange creature");
|
||||
```
|
||||
|
||||
Your should create a third `option` element with the text `Radioactive exposure` inside your `select` element.
|
||||
|
||||
```js
|
||||
const optionEls = document?.querySelectorAll("select > option");
|
||||
assert.equal(optionEls[2]?.textContent, "Radioactive exposure");
|
||||
```
|
||||
|
||||
Your should create a fourth `option` element with the text `Science experiment` inside your `select` element.
|
||||
|
||||
```js
|
||||
const optionEls = document?.querySelectorAll("select > option");
|
||||
assert.equal(optionEls[3]?.textContent, "Science experiment");
|
||||
```
|
||||
|
||||
Your should create a fifth `option` element with the text `Alien heritage` inside your `select` element.
|
||||
|
||||
```js
|
||||
const optionEls = document?.querySelectorAll("select > option");
|
||||
assert.equal(optionEls[4]?.textContent, "Alien heritage");
|
||||
```
|
||||
|
||||
Your should create a sixth `option` element with the text `Ancient artifact discovery` inside your `select` element.
|
||||
|
||||
```js
|
||||
const optionEls = document?.querySelectorAll("select > option");
|
||||
assert.equal(optionEls[5]?.textContent, "Ancient artifact discovery");
|
||||
```
|
||||
|
||||
Your should create a seventh `option` element with the text `Other` inside your `select` element.
|
||||
|
||||
```js
|
||||
const optionEls = document?.querySelectorAll("select > option");
|
||||
assert.equal(optionEls[6]?.textContent, "Other");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
<form>
|
||||
<div className='section'>
|
||||
<label>
|
||||
Hero Name
|
||||
<input
|
||||
type='text'
|
||||
value={heroName}
|
||||
onChange={e => setHeroName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Real Name
|
||||
<input
|
||||
type='password'
|
||||
value={realName}
|
||||
onChange={e => setRealName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label className='section column'>
|
||||
How did you get your powers?
|
||||
--fcc-editable-region--
|
||||
<select>
|
||||
|
||||
</select>
|
||||
--fcc-editable-region--
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,204 @@
|
||||
---
|
||||
id: 680900675ae3d54ee19590ca
|
||||
title: Step 10
|
||||
challengeType: 0
|
||||
dashedName: step-10
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
To account for the setter function, you will have to attach a `value` attribute and an `onChange` event to the `select` and a `value` attribute to each of the `option` elements.
|
||||
|
||||
That's a long process, right? So the best way to handle this is to have an array of the option values and map over it to create the `option` elements.
|
||||
|
||||
A `powerSourceOptions` array has been added at the top of your component. Give it a look.
|
||||
|
||||
For now, remove all the options inside the `select` element except for the first one. In the next step you will map over the array.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have the first option element in your code.
|
||||
|
||||
```js
|
||||
const optionEl = document?.querySelectorAll("select > option");
|
||||
assert.equal(optionEl[0]?.textContent, "Select one");
|
||||
```
|
||||
|
||||
You should remove all the option elements apart from the first one.
|
||||
|
||||
```js
|
||||
const optionEls = document?.querySelectorAll("select > option");
|
||||
assert.lengthOf(optionEls, 1)
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const powerSourceOptions = [
|
||||
'Bitten by a strange creature',
|
||||
'Radioactive exposure',
|
||||
'Science experiment',
|
||||
'Alien heritage',
|
||||
'Ancient artifact discovery',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
<form>
|
||||
<div className='section'>
|
||||
<label>
|
||||
Hero Name
|
||||
<input
|
||||
type='text'
|
||||
value={heroName}
|
||||
onChange={e => setHeroName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Real Name
|
||||
<input
|
||||
type='password'
|
||||
value={realName}
|
||||
onChange={e => setRealName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label className='section column'>
|
||||
How did you get your powers?
|
||||
<select>
|
||||
--fcc-editable-region--
|
||||
<option value=''>
|
||||
Select one
|
||||
</option>
|
||||
<option>
|
||||
Bitten by a strange creature
|
||||
</option>
|
||||
<option>
|
||||
Radioactive exposure
|
||||
</option>
|
||||
<option>
|
||||
Science experiment
|
||||
</option>
|
||||
<option>
|
||||
Alien heritage
|
||||
</option>
|
||||
<option>
|
||||
Ancient artifact discovery
|
||||
</option>
|
||||
<option>
|
||||
Other
|
||||
</option>
|
||||
--fcc-editable-region--
|
||||
</select>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,234 @@
|
||||
---
|
||||
id: 680900675ae3d54ee19590cb
|
||||
title: Step 11
|
||||
challengeType: 0
|
||||
dashedName: step-11
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In a prior lecture video, you learned how to render a list of options using the `map()` method like this:
|
||||
|
||||
```jsx
|
||||
function FruitList() {
|
||||
const fruits = ["Apple", "Banana", "Cherry", "Date"];
|
||||
return (
|
||||
<ul>
|
||||
{fruits.map((fruit, index) => (
|
||||
<li key={`${fruit}-${index}`}>{fruit}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Remember that the `key` must always be unique. It helps React identify which items have changed, and been added or removed.
|
||||
|
||||
After the first `option`, use an arrow function to map through the `powerSourceOptions` array using `source` as the parameter.
|
||||
|
||||
Then inside the `map()` method, create an `option` element with a `key` of `source` and a `value` of `source`. Lastly, use `{source}` to display the name of each power source in the dropdown.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should map through `powerSourceOptions`.
|
||||
|
||||
```js
|
||||
assert.match(code, /\{\s*powerSourceOptions\s*\.\s*map\s*\(/)
|
||||
```
|
||||
|
||||
You should pass in `source` as the parameter of the map.
|
||||
|
||||
```js
|
||||
assert.match(code, /\{\s*powerSourceOptions\s*\.\s*map\s*\(\s*(source|\(\s*source\s*\))\s*=>/)
|
||||
```
|
||||
|
||||
You should create an `option` element with a `key` of `source` and `value` of `source`.
|
||||
|
||||
```js
|
||||
assert.match(code, /<\s*option\s+(key\s*=\s*{\s*source\s*}\s*value\s*=\s*{\s*source\s*}|value\s*=\s*{\s*source\s*}\s*key\s*=\s*{\s*source\s*})\s*>\s*{\s*source\s*}\s*<\/\s*option\s*>/)
|
||||
```
|
||||
|
||||
You should have a total of seven `option` elements.
|
||||
|
||||
```js
|
||||
const optionElements = document?.querySelectorAll("option");
|
||||
assert.lengthOf(optionElements, 7);
|
||||
```
|
||||
|
||||
For the last six `option` elements, each text content should correspond to one of the power source options from the `powerSourceOptions` array.
|
||||
|
||||
```js
|
||||
const optionElements = document?.querySelectorAll("option");
|
||||
|
||||
const powerSourceOptions = [
|
||||
"Bitten by a strange creature",
|
||||
"Radioactive exposure",
|
||||
"Science experiment",
|
||||
"Alien heritage",
|
||||
"Ancient artifact discovery",
|
||||
"Other"
|
||||
];
|
||||
|
||||
for (let i = 0; i < powerSourceOptions.length; i++) {
|
||||
const option = optionElements[i+1];
|
||||
const expected = powerSourceOptions[i];
|
||||
|
||||
assert.strictEqual(option?.textContent, expected);
|
||||
assert.strictEqual(option.value, expected);
|
||||
}
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const powerSourceOptions = [
|
||||
'Bitten by a strange creature',
|
||||
'Radioactive exposure',
|
||||
'Science experiment',
|
||||
'Alien heritage',
|
||||
'Ancient artifact discovery',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
<form>
|
||||
<div className='section'>
|
||||
<label>
|
||||
Hero Name
|
||||
<input
|
||||
type='text'
|
||||
value={heroName}
|
||||
onChange={e => setHeroName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Real Name
|
||||
<input
|
||||
type='password'
|
||||
value={realName}
|
||||
onChange={e => setRealName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label className='section column'>
|
||||
How did you get your powers?
|
||||
<select>
|
||||
<option>
|
||||
Select one
|
||||
</option>
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
</select>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,185 @@
|
||||
---
|
||||
id: 680900675ae3d54ee19590cc
|
||||
title: Step 12
|
||||
challengeType: 0
|
||||
dashedName: step-12
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The values of the `powerSourceOptions` now show up as the options for the `select` element.
|
||||
|
||||
Now, use `powerSource` as the value of the `select`. Then, add an `onChange` to set that value using the `setPowerSource` setter function and `e.target.value` like you did with the other form elements.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should use `{powerSource}` as the value of the `select` element.
|
||||
|
||||
```js
|
||||
assert.match(code, /<\s*select\s+[\s\S]*value\s*=\s*\{\s*powerSource\s*\}/)
|
||||
```
|
||||
|
||||
You should set the value of the `select` element with `onChange` and `setPowerSource`.
|
||||
|
||||
```js
|
||||
assert.match(code, /<\s*select\s+[\s\S]*onChange\s*=\s*\{\s*(e|\(\s*e\s*\))\s*=>\s*setPowerSource\s*\(\s*e\s*\.\s*target\s*\.\s*value\s*\)/)
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const powerSourceOptions = [
|
||||
'Bitten by a strange creature',
|
||||
'Radioactive exposure',
|
||||
'Science experiment',
|
||||
'Alien heritage',
|
||||
'Ancient artifact discovery',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
<form>
|
||||
<div className='section'>
|
||||
<label>
|
||||
Hero Name
|
||||
<input
|
||||
type='text'
|
||||
value={heroName}
|
||||
onChange={e => setHeroName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Real Name
|
||||
<input
|
||||
type='password'
|
||||
value={realName}
|
||||
onChange={e => setRealName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label className='section column'>
|
||||
How did you get your powers?
|
||||
--fcc-editable-region--
|
||||
<select>
|
||||
<option value=''>
|
||||
Select one
|
||||
</option>
|
||||
{powerSourceOptions.map(source => (
|
||||
<option key={source} value={source}>
|
||||
{source}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
--fcc-editable-region--
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,188 @@
|
||||
---
|
||||
id: 680900675ae3d54ee19590cd
|
||||
title: Step 13
|
||||
challengeType: 0
|
||||
dashedName: step-13
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The next `input` is a checkbox to mark all super hero powers that apply to the user.
|
||||
|
||||
Create a `label` element with a `className` of `section` and `column`, and the text of `List your powers (select all that apply):`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You shold create a `label` element with a `className` of `section` and `column`.
|
||||
|
||||
```js
|
||||
const labelEl = document?.querySelectorAll("label.section.column");
|
||||
assert.equal(labelEl?.length, 2)
|
||||
```
|
||||
|
||||
Your `label` element should have the text `List your powers (select all that apply):`.
|
||||
|
||||
```js
|
||||
const labelEl = document?.querySelectorAll("label.section.column");
|
||||
assert.equal(labelEl[1]?.textContent, "List your powers (select all that apply):")
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const powerSourceOptions = [
|
||||
'Bitten by a strange creature',
|
||||
'Radioactive exposure',
|
||||
'Science experiment',
|
||||
'Alien heritage',
|
||||
'Ancient artifact discovery',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
<form>
|
||||
<div className='section'>
|
||||
<label>
|
||||
Hero Name
|
||||
<input
|
||||
type='text'
|
||||
value={heroName}
|
||||
onChange={e => setHeroName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Real Name
|
||||
<input
|
||||
type='password'
|
||||
value={realName}
|
||||
onChange={e => setRealName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label className='section column'>
|
||||
How did you get your powers?
|
||||
<select value={powerSource} onChange={e => setPowerSource(e.target.value)}>
|
||||
<option value=''>
|
||||
Select one
|
||||
</option>
|
||||
{powerSourceOptions.map(source => (
|
||||
<option key={source} value={source}>
|
||||
{source}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,217 @@
|
||||
---
|
||||
id: 680900675ae3d54ee19590ce
|
||||
title: Step 14
|
||||
challengeType: 0
|
||||
dashedName: step-14
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
There will be six checkboxes, so it's also best to create an array for them and map through them to display them as checkboxes. For that, a `powersOptions` array has been provided for you.
|
||||
|
||||
Use an arrow function to map through the `powersOptions` array using parameter of `power`. Inside the map, create a `label` element with a `key` of `power`, and a text of `Hello`.
|
||||
|
||||
After that, you should see six `Hello` text. You will fill in the elements of the array in the next step.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should map through `powersOptions`.
|
||||
|
||||
```js
|
||||
assert.match(code, /\{\s*powersOptions\s*\.\s*map\s*\(/)
|
||||
```
|
||||
|
||||
You should use `power` as the parameter of your map.
|
||||
|
||||
```js
|
||||
assert.match(code, /{\s*powersOptions\s*\.\s*map\s*\(\s*(power|\(\s*power\s*\))\s*=>/)
|
||||
```
|
||||
|
||||
You should create a `label` element with a `key` of `power`.
|
||||
|
||||
```js
|
||||
assert.match(code, /\{\s*powersOptions\s*\.\s*map\s*\([\s\S]*<\s*label\s+key\s*=\s*\{\s*power\s*\}\s*>[\s\S]*<\s*\/\s*label\s*>/)
|
||||
```
|
||||
|
||||
Your `label` element should have the text `Hello`.
|
||||
|
||||
```js
|
||||
const labelEls = document?.querySelectorAll("label")
|
||||
const targetLabelEls = Array.from(labelEls).slice(4)
|
||||
|
||||
for (const label of targetLabelEls) {
|
||||
assert.equal(label.textContent, "Hello");
|
||||
}
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const powerSourceOptions = [
|
||||
'Bitten by a strange creature',
|
||||
'Radioactive exposure',
|
||||
'Science experiment',
|
||||
'Alien heritage',
|
||||
'Ancient artifact discovery',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const powersOptions = [
|
||||
'Super Strength',
|
||||
'Super Speed',
|
||||
'Flight',
|
||||
'Invisibility',
|
||||
'Telekinesis',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
<form>
|
||||
<div className='section'>
|
||||
<label>
|
||||
Hero Name
|
||||
<input
|
||||
type='text'
|
||||
value={heroName}
|
||||
onChange={e => setHeroName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Real Name
|
||||
<input
|
||||
type='password'
|
||||
value={realName}
|
||||
onChange={e => setRealName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label className='section column'>
|
||||
How did you get your powers?
|
||||
<select value={powerSource} onChange={e => setPowerSource(e.target.value)}>
|
||||
<option value=''>
|
||||
Select one
|
||||
</option>
|
||||
{powerSourceOptions.map(source => (
|
||||
<option key={source} value={source}>
|
||||
{source}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className='section column'>
|
||||
List your powers (select all that apply):
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,208 @@
|
||||
---
|
||||
id: 680900675ae3d54ee19590cf
|
||||
title: Step 16
|
||||
challengeType: 0
|
||||
dashedName: step-16
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
To mark the checkboxes, add a `checked` attribute to the checkbox `input`. Use the `includes` method to verify if the current `power` is in the `powers` array.
|
||||
|
||||
The `onChange` will be a separate function, set it to `handlePowersChange` for now. This will lead to an error you will fix in the next step.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should set the checkbox `checked` state to reflect whether the current `power` value exists in the powers array.
|
||||
|
||||
```js
|
||||
assert.match(code, /checked\s*=\s*\{\s*powers\s*\.\s*includes\s*\(\s*power\s*\)\s*\}/)
|
||||
```
|
||||
|
||||
You should set the checkbox `onChange` attribute to `{handlePowersChange}`.
|
||||
|
||||
```js
|
||||
assert.match(code, /onChange\s*=\s*\{\s*handlePowersChange\s*\}/)
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const powerSourceOptions = [
|
||||
'Bitten by a strange creature',
|
||||
'Radioactive exposure',
|
||||
'Science experiment',
|
||||
'Alien heritage',
|
||||
'Ancient artifact discovery',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const powersOptions = [
|
||||
'Super Strength',
|
||||
'Super Speed',
|
||||
'Flight',
|
||||
'Invisibility',
|
||||
'Telekinesis',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
<form>
|
||||
<div className='section'>
|
||||
<label>
|
||||
Hero Name
|
||||
<input
|
||||
type='text'
|
||||
value={heroName}
|
||||
onChange={e => setHeroName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Real Name
|
||||
<input
|
||||
type='password'
|
||||
value={realName}
|
||||
onChange={e => setRealName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label className='section column'>
|
||||
How did you get your powers?
|
||||
<select value={powerSource} onChange={e => setPowerSource(e.target.value)}>
|
||||
<option value=''>
|
||||
Select one
|
||||
</option>
|
||||
{powerSourceOptions.map(source => (
|
||||
<option key={source} value={source}>
|
||||
{source}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className='section column'>
|
||||
List your powers (select all that apply):
|
||||
|
||||
{powersOptions.map(power => (
|
||||
<label key={power}>
|
||||
--fcc-editable-region--
|
||||
<input
|
||||
type='checkbox'
|
||||
value={power}
|
||||
|
||||
/>
|
||||
--fcc-editable-region--
|
||||
<span>{power}</span>
|
||||
</label>
|
||||
))}
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,211 @@
|
||||
---
|
||||
id: 680900675ae3d54ee19590d0
|
||||
title: Step 17
|
||||
challengeType: 0
|
||||
dashedName: step-17
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
To fix the error, create a `handlePowersChange` arrow function with an `e` parameter.
|
||||
|
||||
Inside the function, destructure `value` and `checked` from `e.target` to get the value of the checkbox and whether it is checked or not.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should create an `handlePowersChange` function with an `e` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /(const|let)\s+handlePowersChange\s*=\s*(e|\(\s*e\s*\))\s*=>\s*{/)
|
||||
```
|
||||
|
||||
You should destructure `value` and `checked` from `e.target`.
|
||||
|
||||
```js
|
||||
assert.match(code, /(const|let)\s+handlePowersChange\s*=\s*(e|\(\s*e\s*\))\s*=>\s*{\s*(const|let)\s*{\s*(value\s*,\s*checked|checked\s*,\s*value)\s*}\s*=\s*e\s*\.\s*target/)
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const powerSourceOptions = [
|
||||
'Bitten by a strange creature',
|
||||
'Radioactive exposure',
|
||||
'Science experiment',
|
||||
'Alien heritage',
|
||||
'Ancient artifact discovery',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const powersOptions = [
|
||||
'Super Strength',
|
||||
'Super Speed',
|
||||
'Flight',
|
||||
'Invisibility',
|
||||
'Telekinesis',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
<form>
|
||||
<div className='section'>
|
||||
<label>
|
||||
Hero Name
|
||||
<input
|
||||
type='text'
|
||||
value={heroName}
|
||||
onChange={e => setHeroName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Real Name
|
||||
<input
|
||||
type='password'
|
||||
value={realName}
|
||||
onChange={e => setRealName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label className='section column'>
|
||||
How did you get your powers?
|
||||
<select value={powerSource} onChange={e => setPowerSource(e.target.value)}>
|
||||
<option value=''>
|
||||
Select one
|
||||
</option>
|
||||
{powerSourceOptions.map(source => (
|
||||
<option key={source} value={source}>
|
||||
{source}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className='section column'>
|
||||
List your powers (select all that apply):
|
||||
|
||||
{powersOptions.map(power => (
|
||||
<label key={power}>
|
||||
<input
|
||||
type='checkbox'
|
||||
value={power}
|
||||
checked={powers.includes(power)}
|
||||
onChange={handlePowersChange}
|
||||
/>
|
||||
<span>{power}</span>
|
||||
</label>
|
||||
))}
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,216 @@
|
||||
---
|
||||
id: 680900675ae3d54ee19590d1
|
||||
title: Step 19
|
||||
challengeType: 0
|
||||
dashedName: step-19
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Next, tell the form how to submit. Add a `method` attribute with a value of `post` and an `action` attribute with a value of `https://superhero-application-form.freecodecamp.org` to the form element.
|
||||
|
||||
Now, submitting form will send it to that URL. You will do that in the next step.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `form` element should have a `method` attribute set to `post`.
|
||||
|
||||
```js
|
||||
const formEl = document?.querySelector("form")
|
||||
assert.equal(formEl?.getAttribute("method"), "post")
|
||||
```
|
||||
|
||||
Your `form` elmement should have an `action` attribute set to `https://superhero-application-form.freecodecamp.org`.
|
||||
|
||||
```js
|
||||
const formEl = document?.querySelector("form")
|
||||
assert.equal(formEl?.getAttribute("action"), "https://superhero-application-form.freecodecamp.org")
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const powerSourceOptions = [
|
||||
'Bitten by a strange creature',
|
||||
'Radioactive exposure',
|
||||
'Science experiment',
|
||||
'Alien heritage',
|
||||
'Ancient artifact discovery',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const powersOptions = [
|
||||
'Super Strength',
|
||||
'Super Speed',
|
||||
'Flight',
|
||||
'Invisibility',
|
||||
'Telekinesis',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
const handlePowersChange = e => {
|
||||
const { value, checked } = e.target;
|
||||
setPowers(checked ? [...powers, value] : powers.filter(p => p !== value));
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
--fcc-editable-region--
|
||||
<form>
|
||||
--fcc-editable-region--
|
||||
<div className='section'>
|
||||
<label>
|
||||
Hero Name
|
||||
<input
|
||||
type='text'
|
||||
value={heroName}
|
||||
onChange={e => setHeroName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Real Name
|
||||
<input
|
||||
type='password'
|
||||
value={realName}
|
||||
onChange={e => setRealName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label className='section column'>
|
||||
How did you get your powers?
|
||||
<select value={powerSource} onChange={e => setPowerSource(e.target.value)}>
|
||||
<option value=''>
|
||||
Select one
|
||||
</option>
|
||||
{powerSourceOptions.map(source => (
|
||||
<option key={source} value={source}>
|
||||
{source}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className='section column'>
|
||||
List your powers (select all that apply):
|
||||
|
||||
{powersOptions.map(power => (
|
||||
<label key={power}>
|
||||
<input
|
||||
type='checkbox'
|
||||
value={power}
|
||||
checked={powers.includes(power)}
|
||||
onChange={handlePowersChange}
|
||||
/>
|
||||
<span>{power}</span>
|
||||
</label>
|
||||
))}
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,222 @@
|
||||
---
|
||||
id: 680900675ae3d54ee19590d2
|
||||
title: Step 20
|
||||
challengeType: 0
|
||||
dashedName: step-20
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Create a `button` element with the `className` of `submit-btn` and a `type` of `submit`. Give the button the text `Join the League`.
|
||||
|
||||
After you do that, try to submit the form and see what happens.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should create a `button` element.
|
||||
|
||||
```js
|
||||
assert.exists(document.querySelector("button"));
|
||||
```
|
||||
|
||||
Your button element should have a `className` attribute set to `submit-btn`.
|
||||
|
||||
```js
|
||||
assert.exists(document.querySelector("button.submit-btn"));
|
||||
```
|
||||
|
||||
Your `button` element should have `Join the League` as its text.
|
||||
|
||||
```js
|
||||
const btnEl = document.querySelector("button")
|
||||
assert.equal(btnEl.textContent, "Join the League");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const powerSourceOptions = [
|
||||
'Bitten by a strange creature',
|
||||
'Radioactive exposure',
|
||||
'Science experiment',
|
||||
'Alien heritage',
|
||||
'Ancient artifact discovery',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const powersOptions = [
|
||||
'Super Strength',
|
||||
'Super Speed',
|
||||
'Flight',
|
||||
'Invisibility',
|
||||
'Telekinesis',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
const handlePowersChange = e => {
|
||||
const { value, checked } = e.target;
|
||||
setPowers(checked ? [...powers, value] : powers.filter(p => p !== value));
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
<form method='post' action='https://superhero-application-form.freecodecamp.org'>
|
||||
<div className='section'>
|
||||
<label>
|
||||
Hero Name
|
||||
<input
|
||||
type='text'
|
||||
value={heroName}
|
||||
onChange={e => setHeroName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Real Name
|
||||
<input
|
||||
type='password'
|
||||
value={realName}
|
||||
onChange={e => setRealName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label className='section column'>
|
||||
How did you get your powers?
|
||||
<select value={powerSource} onChange={e => setPowerSource(e.target.value)}>
|
||||
<option value=''>
|
||||
Select one
|
||||
</option>
|
||||
{powerSourceOptions.map(source => (
|
||||
<option key={source} value={source}>
|
||||
{source}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className='section column'>
|
||||
List your powers (select all that apply):
|
||||
|
||||
{powersOptions.map(power => (
|
||||
<label key={power}>
|
||||
<input
|
||||
type='checkbox'
|
||||
value={power}
|
||||
checked={powers.includes(power)}
|
||||
onChange={handlePowersChange}
|
||||
/>
|
||||
<span>{power}</span>
|
||||
</label>
|
||||
))}
|
||||
</label>
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,424 @@
|
||||
---
|
||||
id: 680900675ae3d54ee19590d3
|
||||
title: Step 21
|
||||
challengeType: 0
|
||||
dashedName: step-21
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Did you notice that you can submit the form without filling in the inputs?
|
||||
|
||||
To stop that, add the `disabled` attribute to disable the submit button if `heroName`, `realName`, or `powerSource` are falsy, or if the length of `powers` is `0`.
|
||||
|
||||
Now, the button will be disabled if something isn't filled in. Fill in the form with your superhero information and submit it to join the league.
|
||||
|
||||
With that, you superhero application form is complete.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should not modify the existing content of the button.
|
||||
|
||||
```js
|
||||
assert.match(code, /<\s*button\s+className\s*=\s*('|")\s*submit-btn\s*\1\s*type\s*=\s*('|")\s*submit\s*\2/)
|
||||
```
|
||||
|
||||
Your `button` element should have a `disabled` property.
|
||||
|
||||
```js
|
||||
assert.match(code, /<\s*button\s+className\s*=\s*('|")\s*submit-btn\s*\1\s*type\s*=\s*('|")\s*submit\s*\2\s*disabled/)
|
||||
```
|
||||
|
||||
You should disable the `button` if any of `heroName`, `realName`, and `powerSource` is false, or if the length of `powers` is `0`.
|
||||
|
||||
```js
|
||||
assert.match(code, /disabled\s*=\s*\{\s*(?:!heroName\s*\|\|\s*!realName\s*\|\|\s*!powerSource\s*\|\|\s*powers\.length\s*===\s*0|!heroName\s*\|\|\s*!realName\s*\|\|\s*powers\.length\s*===\s*0\s*\|\|\s*!powerSource|!heroName\s*\|\|\s*!powerSource\s*\|\|\s*!realName\s*\|\|\s*powers\.length\s*===\s*0|!heroName\s*\|\|\s*!powerSource\s*\|\|\s*powers\.length\s*===\s*0\s*\|\|\s*!realName|!heroName\s*\|\|\s*powers\.length\s*===\s*0\s*\|\|\s*!realName\s*\|\|\s*!powerSource|!heroName\s*\|\|\s*powers\.length\s*===\s*0\s*\|\|\s*!powerSource\s*\|\|\s*!realName|!realName\s*\|\|\s*!heroName\s*\|\|\s*!powerSource\s*\|\|\s*powers\.length\s*===\s*0|!realName\s*\|\|\s*!heroName\s*\|\|\s*powers\.length\s*===\s*0\s*\|\|\s*!powerSource|!realName\s*\|\|\s*!powerSource\s*\|\|\s*!heroName\s*\|\|\s*powers\.length\s*===\s*0|!realName\s*\|\|\s*!powerSource\s*\|\|\s*powers\.length\s*===\s*0\s*\|\|\s*!heroName|!realName\s*\|\|\s*powers\.length\s*===\s*0\s*\|\|\s*!heroName\s*\|\|\s*!powerSource|!realName\s*\|\|\s*powers\.length\s*===\s*0\s*\|\|\s*!powerSource\s*\|\|\s*!heroName|!powerSource\s*\|\|\s*!heroName\s*\|\|\s*!realName\s*\|\|\s*powers\.length\s*===\s*0|!powerSource\s*\|\|\s*!heroName\s*\|\|\s*powers\.length\s*===\s*0\s*\|\|\s*!realName|!powerSource\s*\|\|\s*!realName\s*\|\|\s*!heroName\s*\|\|\s*powers\.length\s*===\s*0|!powerSource\s*\|\|\s*!realName\s*\|\|\s*powers\.length\s*===\s*0\s*\|\|\s*!heroName|!powerSource\s*\|\|\s*powers\.length\s*===\s*0\s*\|\|\s*!heroName\s*\|\|\s*!realName|!powerSource\s*\|\|\s*powers\.length\s*===\s*0\s*\|\|\s*!realName\s*\|\|\s*!heroName|powers\.length\s*===\s*0\s*\|\|\s*!heroName\s*\|\|\s*!realName\s*\|\|\s*!powerSource|powers\.length\s*===\s*0\s*\|\|\s*!heroName\s*\|\|\s*!powerSource\s*\|\|\s*!realName|powers\.length\s*===\s*0\s*\|\|\s*!realName\s*\|\|\s*!heroName\s*\|\|\s*!powerSource|powers\.length\s*===\s*0\s*\|\|\s*!realName\s*\|\|\s*!powerSource\s*\|\|\s*!heroName|powers\.length\s*===\s*0\s*\|\|\s*!powerSource\s*\|\|\s*!heroName\s*\|\|\s*!realName|powers\.length\s*===\s*0\s*\|\|\s*!powerSource\s*\|\|\s*!realName\s*\|\|\s*!heroName)\s*\}/)
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const powerSourceOptions = [
|
||||
'Bitten by a strange creature',
|
||||
'Radioactive exposure',
|
||||
'Science experiment',
|
||||
'Alien heritage',
|
||||
'Ancient artifact discovery',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const powersOptions = [
|
||||
'Super Strength',
|
||||
'Super Speed',
|
||||
'Flight',
|
||||
'Invisibility',
|
||||
'Telekinesis',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
const handlePowersChange = e => {
|
||||
const { value, checked } = e.target;
|
||||
setPowers(checked ? [...powers, value] : powers.filter(p => p !== value));
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
<form method='post' action='https://superhero-application-form.freecodecamp.org'>
|
||||
<div className='section'>
|
||||
<label>
|
||||
Hero Name
|
||||
<input
|
||||
type='text'
|
||||
value={heroName}
|
||||
onChange={e => setHeroName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Real Name
|
||||
<input
|
||||
type='password'
|
||||
value={realName}
|
||||
onChange={e => setRealName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label className='section column'>
|
||||
How did you get your powers?
|
||||
<select value={powerSource} onChange={e => setPowerSource(e.target.value)}>
|
||||
<option value=''>
|
||||
Select one
|
||||
</option>
|
||||
{powerSourceOptions.map(source => (
|
||||
<option key={source} value={source}>
|
||||
{source}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className='section column'>
|
||||
List your powers (select all that apply):
|
||||
|
||||
{powersOptions.map(power => (
|
||||
<label key={power}>
|
||||
<input
|
||||
type='checkbox'
|
||||
value={power}
|
||||
checked={powers.includes(power)}
|
||||
onChange={handlePowersChange}
|
||||
/>
|
||||
<span>{power}</span>
|
||||
</label>
|
||||
))}
|
||||
</label>
|
||||
--fcc-editable-region--
|
||||
<button
|
||||
className='submit-btn'
|
||||
type='submit'
|
||||
|
||||
>
|
||||
Join the League
|
||||
</button>
|
||||
--fcc-editable-region--
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const powerSourceOptions = [
|
||||
'Bitten by a strange creature',
|
||||
'Radioactive exposure',
|
||||
'Science experiment',
|
||||
'Alien heritage',
|
||||
'Ancient artifact discovery',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const powersOptions = [
|
||||
'Super Strength',
|
||||
'Super Speed',
|
||||
'Flight',
|
||||
'Invisibility',
|
||||
'Telekinesis',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
const handlePowersChange = e => {
|
||||
const { value, checked } = e.target;
|
||||
setPowers(checked ? [...powers, value] : powers.filter(p => p !== value));
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
<form method='post' action='https://superhero-application-form.freecodecamp.org'>
|
||||
<div className='section'>
|
||||
<label>
|
||||
Hero Name
|
||||
<input
|
||||
type='text'
|
||||
value={heroName}
|
||||
onChange={e => setHeroName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Real Name
|
||||
<input
|
||||
type='password'
|
||||
value={realName}
|
||||
onChange={e => setRealName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label className='section column'>
|
||||
How did you get your powers?
|
||||
<select value={powerSource} onChange={e => setPowerSource(e.target.value)}>
|
||||
<option value=''>
|
||||
Select one
|
||||
</option>
|
||||
{powerSourceOptions.map(source => (
|
||||
<option key={source} value={source}>
|
||||
{source}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className='section column'>
|
||||
List your powers (select all that apply):
|
||||
|
||||
{powersOptions.map(power => (
|
||||
<label key={power}>
|
||||
<input
|
||||
type='checkbox'
|
||||
value={power}
|
||||
checked={powers.includes(power)}
|
||||
onChange={handlePowersChange}
|
||||
/>
|
||||
<span>{power}</span>
|
||||
</label>
|
||||
))}
|
||||
</label>
|
||||
<button
|
||||
className='submit-btn'
|
||||
type='submit'
|
||||
disabled={
|
||||
!heroName || !realName || !powerSource || powers.length === 0
|
||||
}
|
||||
>
|
||||
Join the League
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,198 @@
|
||||
---
|
||||
id: 680fc849a6f2be0a8597c593
|
||||
title: Step 3
|
||||
challengeType: 0
|
||||
dashedName: step-3
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now, create the state variables and setters for `powerSource` and `powers`. `powerSource` should have an initial value of empty string, and `powers` should have an initial value of an empty array.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should use array destructuring to set a `powerSource` state variable and a `setPowerSource` setter.
|
||||
|
||||
```js
|
||||
assert.match(code, /(const|let)\s+\[\s*powerSource\s*,\s*setPowerSource\s*\]/);
|
||||
```
|
||||
|
||||
Your `powerSource` and `setPowerSource` should use the `useState` hook.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const abuseState = __helpers.spyOn(React, "useState");
|
||||
const script = [...document.querySelectorAll("script")].find((s) => s.dataset.src === "index.jsx").innerText;
|
||||
const exports = {};
|
||||
const _a = eval(script);
|
||||
const _b = await __helpers.prepTestComponent(exports.SuperheroForm);
|
||||
|
||||
assert.isAtLeast(abuseState.calls.length, 3);
|
||||
}
|
||||
```
|
||||
|
||||
Your `useState` hook for `powerSource` should have an initial value of empty string.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const abuseState = __helpers.spyOn(React, "useState");
|
||||
const script = [...document.querySelectorAll("script")].find((s) => s.dataset.src === "index.jsx").innerText;
|
||||
const exports = {};
|
||||
const _a = eval(script);
|
||||
const _b = await __helpers.prepTestComponent(exports.SuperheroForm);
|
||||
|
||||
assert.equal(abuseState.calls[2]?.[0], "");
|
||||
}
|
||||
```
|
||||
|
||||
You should use array destructuring to set a `powers` state variable and a `setPowers` setter.
|
||||
|
||||
```js
|
||||
assert.match(code, /(const|let)\s+\[\s*powers\s*,\s*setPowers\s*\]/);
|
||||
```
|
||||
|
||||
Your `powers` and `setPowers` should use the `useState` hook.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const abuseState = __helpers.spyOn(React, "useState");
|
||||
const script = [...document.querySelectorAll("script")].find((s) => s.dataset.src === "index.jsx").innerText;
|
||||
const exports = {};
|
||||
const _a = eval(script);
|
||||
const _b = await __helpers.prepTestComponent(exports.SuperheroForm);
|
||||
|
||||
assert.isAtLeast(abuseState.calls.length, 4);
|
||||
}
|
||||
```
|
||||
|
||||
Your `useState` hook for `powers` should have an initial value of empty array.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const abuseState = __helpers.spyOn(React, "useState");
|
||||
const script = [...document.querySelectorAll("script")].find((s) => s.dataset.src === "index.jsx").innerText;
|
||||
const exports = {};
|
||||
const _a = eval(script);
|
||||
const _b = await __helpers.prepTestComponent(exports.SuperheroForm);
|
||||
|
||||
console.log("State calls:", abuseState.calls)
|
||||
|
||||
assert.deepEqual(abuseState.calls[3]?.[0], []);
|
||||
}
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,208 @@
|
||||
---
|
||||
id: 68148d280ee30e5a567a0e2d
|
||||
title: Step 15
|
||||
challengeType: 0
|
||||
dashedName: step-15
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Remove the `Hello` text inside the `label`, then create an `input` of type `checkbox` and a `value` of `power`.
|
||||
|
||||
Finally, display the values from the array next to each checkbox by creating a `span` element just before the closing `label` with a text content of `{power}`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should remove the `Hello` text from the `label` element.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /hello/i)
|
||||
```
|
||||
|
||||
You should create an `input` with its `type` set to `checkbox` and a `value` set to `power` as the first child of the label.
|
||||
|
||||
```js
|
||||
assert.match(code, /<\s*label\s+key\s*=\s*{\s*power\s*}\s*>\s*<\s*input\s+type\s*=\s*("|')checkbox\1\s*value\s*=\s*\{\s*power\s*\}\s*\/>[\s\S]*<\/\s*label\s*>/)
|
||||
```
|
||||
|
||||
You should create a `span` element with `{power}` for its text content as the last child of the label.
|
||||
|
||||
```js
|
||||
assert.match(code, /<\s*label\s+key\s*=\s*{\s*power\s*}\s*>[\s\S]*<\s*span\s*>\s*{\s*power\s*}\s*<\/\s*span\s*>\s*<\/\s*label\s*>/)
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const powerSourceOptions = [
|
||||
'Bitten by a strange creature',
|
||||
'Radioactive exposure',
|
||||
'Science experiment',
|
||||
'Alien heritage',
|
||||
'Ancient artifact discovery',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const powersOptions = [
|
||||
'Super Strength',
|
||||
'Super Speed',
|
||||
'Flight',
|
||||
'Invisibility',
|
||||
'Telekinesis',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
<form>
|
||||
<div className='section'>
|
||||
<label>
|
||||
Hero Name
|
||||
<input
|
||||
type='text'
|
||||
value={heroName}
|
||||
onChange={e => setHeroName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Real Name
|
||||
<input
|
||||
type='password'
|
||||
value={realName}
|
||||
onChange={e => setRealName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label className='section column'>
|
||||
How did you get your powers?
|
||||
<select value={powerSource} onChange={e => setPowerSource(e.target.value)}>
|
||||
<option value=''>
|
||||
Select one
|
||||
</option>
|
||||
{powerSourceOptions.map(source => (
|
||||
<option key={source} value={source}>
|
||||
{source}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className='section column'>
|
||||
List your powers (select all that apply):
|
||||
--fcc-editable-region--
|
||||
{powersOptions.map(power => (
|
||||
<label key={power}>
|
||||
Hello
|
||||
</label>
|
||||
))}
|
||||
--fcc-editable-region--
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,208 @@
|
||||
---
|
||||
id: 68149b101d905a6fc2fcd6d8
|
||||
title: Step 18
|
||||
challengeType: 0
|
||||
dashedName: step-18
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Below the destructured `value` and `checked`, call the `setPowers` function to update the list of checked powers.
|
||||
|
||||
The function needs an array argument. In the function parameter, use a ternary operator to check if `checked` is true. If it is, spread in the existing `powers` array into a new array and add the `value` to it. If it is not true, filter out the `value` from `powers` with `powers.filter(p => p !== value)`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should use a ternary operator to check if `checked` is true. If it is, spread in the existing `powers` and `value` into an array. If it is not, filter out the `value` from `powers`.
|
||||
|
||||
```js
|
||||
assert.match(code, /(const|let)\s*{\s*(value\s*,\s*checked|checked\s*,\s*value)\s*}\s*=\s*e\s*\.\s*target\s*;?\s*setPowers\s*\(\s*checked\s*\?\s*\[\s*\.\.\.powers\s*,\s*value\s*\]\s*:\s*powers\s*\.\s*filter\s*\(\s*(p|\(\s*p\s*\))\s*=>\s*p\s*!==?\s*value\s*\)\s*\)/)
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Superhero Application Form</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.3/babel.min.js"></script>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
src="index.jsx"
|
||||
></script>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
data-plugins="transform-modules-umd"
|
||||
type="text/babel"
|
||||
data-presets="react"
|
||||
data-type="module"
|
||||
>
|
||||
import { SuperheroForm } from './index.jsx';
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<SuperheroForm />);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: linear-gradient(30deg, #ff9999 50%, #6699ff 50%)
|
||||
}
|
||||
|
||||
.form-wrap {
|
||||
background-color: white;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 1px solid black;
|
||||
box-shadow: 5px 5px 10px black;
|
||||
}
|
||||
|
||||
.form-wrap h2,
|
||||
.form-wrap p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-wrap p {
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: 1px solid black
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const { useState } = React;
|
||||
|
||||
export const SuperheroForm = () => {
|
||||
|
||||
const powerSourceOptions = [
|
||||
'Bitten by a strange creature',
|
||||
'Radioactive exposure',
|
||||
'Science experiment',
|
||||
'Alien heritage',
|
||||
'Ancient artifact discovery',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const powersOptions = [
|
||||
'Super Strength',
|
||||
'Super Speed',
|
||||
'Flight',
|
||||
'Invisibility',
|
||||
'Telekinesis',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const [heroName, setHeroName] = useState('');
|
||||
const [realName, setRealName] = useState('');
|
||||
const [powerSource, setPowerSource] = useState('');
|
||||
const [powers, setPowers] = useState([]);
|
||||
|
||||
--fcc-editable-region--
|
||||
const handlePowersChange = e => {
|
||||
const { value, checked } = e.target;
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
return (
|
||||
<div className='form-wrap'>
|
||||
<h2>Superhero Application Form</h2>
|
||||
<p>Please complete all fields</p>
|
||||
<form>
|
||||
<div className='section'>
|
||||
<label>
|
||||
Hero Name
|
||||
<input
|
||||
type='text'
|
||||
value={heroName}
|
||||
onChange={e => setHeroName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Real Name
|
||||
<input
|
||||
type='password'
|
||||
value={realName}
|
||||
onChange={e => setRealName(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label className='section column'>
|
||||
How did you get your powers?
|
||||
<select value={powerSource} onChange={e => setPowerSource(e.target.value)}>
|
||||
<option value=''>
|
||||
Select one
|
||||
</option>
|
||||
{powerSourceOptions.map(source => (
|
||||
<option key={source} value={source}>
|
||||
{source}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className='section column'>
|
||||
List your powers (select all that apply):
|
||||
|
||||
{powersOptions.map(power => (
|
||||
<label key={power}>
|
||||
<input
|
||||
type='checkbox'
|
||||
value={power}
|
||||
checked={powers.includes(power)}
|
||||
onChange={handlePowersChange}
|
||||
/>
|
||||
<span>{power}</span>
|
||||
</label>
|
||||
))}
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
@@ -1078,6 +1078,9 @@
|
||||
{
|
||||
"dashedName": "quiz-react-state-and-hooks"
|
||||
},
|
||||
{
|
||||
"dashedName": "workshop-superhero-application-form"
|
||||
},
|
||||
{
|
||||
"dashedName": "lecture-working-with-forms-in-react"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user