mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-09-24 23:01:18 +08:00
feat(curriculum): add contact form lab (#56270)
Co-authored-by: Tom <20648924+moT01@users.noreply.github.com> Co-authored-by: Ilenia <26656284+ilenia-magoni@users.noreply.github.com>
This commit is contained in:
@@ -1893,7 +1893,12 @@
|
||||
},
|
||||
"jnqt": { "title": "68", "intro": [] },
|
||||
"vkei": { "title": "69", "intro": [] },
|
||||
"jows": { "title": "70", "intro": [] },
|
||||
"lab-contact-form": {
|
||||
"title": "Design a Contact Form",
|
||||
"intro": [
|
||||
"In this lab, you will design a contact form in HTML and style it using CSS."
|
||||
]
|
||||
},
|
||||
"ipmw": { "title": "71", "intro": [] },
|
||||
"quiz-styling-forms": {
|
||||
"title": "Styling Forms Quiz",
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Introduction to the Design a Contact Form
|
||||
block: lab-contact-form
|
||||
superBlock: front-end-development
|
||||
---
|
||||
|
||||
## Introduction to the Design a Contact Form
|
||||
|
||||
In this lab, you will design a contact form in HTML and style it using CSS.
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "Design a Contact Form",
|
||||
"isUpcomingChange": true,
|
||||
"usesMultifileEditor": true,
|
||||
"dashedName": "lab-contact-form",
|
||||
"order": 70,
|
||||
"superBlock": "front-end-development",
|
||||
"challengeOrder": [{ "id": "66f26c32ec6f90df01a44f60", "title": "Design a Contact Form" }],
|
||||
"helpCategory": "HTML-CSS",
|
||||
"blockType": "lab"
|
||||
}
|
||||
+455
@@ -0,0 +1,455 @@
|
||||
---
|
||||
id: 66f26c32ec6f90df01a44f60
|
||||
title: Design a Contact Form
|
||||
challengeType: 14
|
||||
dashedName: design-a-contact-form
|
||||
demoType: onClick
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
**Objective:** Fulfill the user stories below and get all the tests to pass to complete the lab.
|
||||
|
||||
**User Stories:**
|
||||
|
||||
1. You should have a `div` with a class of `form-container` to hold your form.
|
||||
|
||||
2. You should have a `form` element within the `.form-container` `div`.
|
||||
|
||||
3. Within the `form` element, you should have the following elements and input fields:
|
||||
|
||||
- A `h2` element with some text.
|
||||
- A text input field for the name with the `type` set to `text` and `id`, `name`, `required` attributes.
|
||||
- An email input field with the `type` set to `email` and `id`, `name`, `required` attributes.
|
||||
- A textarea for the message with `id`, `name`, and the `required` attribute.
|
||||
- A `button` element with the text `"Submit"` and a `type` attribute of `submit`.
|
||||
|
||||
4. Each `input` and `textarea` elements should have their corresponding `label` element with a `for` attribute set to the element's `id`.
|
||||
|
||||
5. The `.form-container` `div` should have a background color, and have values for `border-radius`, `padding`, and `width`.
|
||||
|
||||
6. The `label` elements should have a margin and a font color.
|
||||
|
||||
7. The `input` and `textarea` elements should have values for:
|
||||
|
||||
- `width`.
|
||||
- `padding`.
|
||||
- Margin on the bottom.
|
||||
|
||||
8. The `button` element should have a:
|
||||
|
||||
- Background color
|
||||
- Font size apart from the default.
|
||||
|
||||
9. The button should have a hover effect that changes the background color.
|
||||
|
||||
**Note:** Be sure to link your stylesheet in your HTML and apply your CSS.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have a `div` with a class of `form-container`.
|
||||
|
||||
```js
|
||||
const el = document.querySelector('div.form-container');
|
||||
assert.isNotNull(el);
|
||||
```
|
||||
|
||||
You should have a `form` element within the `.form-container` `div`.
|
||||
|
||||
```js
|
||||
const el = document.querySelector('.form-container form');
|
||||
assert.isNotNull(el);
|
||||
```
|
||||
|
||||
Within the `form`, you should have an `h2` element with some text.
|
||||
|
||||
```js
|
||||
const el = document.querySelector('.form-container form h2');
|
||||
assert.isNotEmpty(el.innerText);
|
||||
// assert.isAtLeast(document.querySelector('h1').innerText.length, 1);
|
||||
|
||||
```
|
||||
|
||||
Within the `form`, you should have a text input field with the type set to `text`.
|
||||
|
||||
```js
|
||||
const el = document.querySelector('.form-container form input[type="text"]');
|
||||
assert.isNotNull(el);
|
||||
```
|
||||
|
||||
Your `type=text` input field should have an `id`, a `name`, and a `required` attribute.
|
||||
|
||||
```js
|
||||
const el = document.querySelector('.form-container form input[type="text"]');
|
||||
|
||||
assert(!!el && el.required);
|
||||
assert.isNotEmpty(el?.getAttribute('id'));
|
||||
assert.isNotEmpty(el?.getAttribute('name'));
|
||||
```
|
||||
|
||||
Within the `form`, you should have an email input field with the `type` set to `email`.
|
||||
|
||||
```js
|
||||
const el = document.querySelector('.form-container form input[type="email"]');
|
||||
assert.isNotNull(el);
|
||||
```
|
||||
|
||||
Your `type=email` input field should have an `id`, a `name`, and a `required` attribute.
|
||||
|
||||
```js
|
||||
const el = document.querySelector('.form-container form input[type="email"]');
|
||||
assert.isTrue(el?.required);
|
||||
assert.isNotEmpty(el?.getAttribute('id'));
|
||||
assert.isNotEmpty(el?.getAttribute('name'));
|
||||
```
|
||||
|
||||
Within the `form`, you should have a `textarea` with an `id`, `name`, and a `required` attribute.
|
||||
|
||||
```js
|
||||
const el = document.querySelector('.form-container form textarea');
|
||||
assert.isNotNull(el);
|
||||
assert.isNotNull(document.querySelector('.form-container form textarea[required]'));
|
||||
assert.isNotEmpty(el?.getAttribute('id'));
|
||||
assert.isNotEmpty(el?.getAttribute('name'));
|
||||
```
|
||||
|
||||
Within the `form`, you should have a `button` element with a `type` attribute of `submit`.
|
||||
|
||||
```js
|
||||
const el = document.querySelector('.form-container form button');
|
||||
assert.strictEqual(el?.getAttribute('type'), 'submit');
|
||||
```
|
||||
|
||||
Your button should have a text of `"Submit"`.
|
||||
|
||||
```js
|
||||
const el = document.querySelector('.form-container form button');
|
||||
assert.strictEqual(document.querySelector('.form-container form button').textContent, 'Submit');
|
||||
```
|
||||
|
||||
You should have a `label` element for the `#name` `input` with a `for` attribute matching the `id` value on the corresponding `input`.
|
||||
|
||||
```js
|
||||
const nameInput = document.querySelector('.form-container form input[type="text"]')?.getAttribute('id');
|
||||
|
||||
const labels = document.querySelectorAll('.form-container form label');
|
||||
|
||||
assert.isAbove(labels.length, 0);
|
||||
|
||||
// check if id has a value
|
||||
assert.isNotEmpty(nameInput);
|
||||
let found = false;
|
||||
|
||||
for (let label of labels) {
|
||||
if (label.getAttribute('for') === nameInput) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert.isTrue(found);
|
||||
```
|
||||
|
||||
You should have a `label` element for the `#email` `input` with a `for` attribute matching the `id` value on the corresponding `input`.
|
||||
|
||||
```js
|
||||
const emailInput = document.querySelector('.form-container form input[type="email"]')?.getAttribute('id');
|
||||
const labels = document.querySelectorAll('.form-container form label');
|
||||
|
||||
assert.isAbove(labels.length, 0);
|
||||
// check if id has a value
|
||||
assert.isNotEmpty(emailInput);
|
||||
let found = false;
|
||||
|
||||
for (let label of labels) {
|
||||
if (label.getAttribute('for') === emailInput) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert.isTrue(found);
|
||||
```
|
||||
|
||||
You should have a `label` element for the `#message` `textarea` with a `for` attribute matching the `id` value on the `textarea`.
|
||||
|
||||
```js
|
||||
const messageInput = document.querySelector('.form-container form textarea')?.getAttribute('id');
|
||||
const labels = document.querySelectorAll('.form-container form label');
|
||||
|
||||
assert.isAbove(labels.length, 0);
|
||||
|
||||
// check if id has a value
|
||||
|
||||
assert.isNotEmpty(messageInput);
|
||||
|
||||
assert.exists(document.querySelector(`label[for="${messageInput}"]`));
|
||||
```
|
||||
|
||||
The labels shouldn't be empty.
|
||||
|
||||
```js
|
||||
const labels = document.querySelectorAll('.form-container form label');
|
||||
|
||||
assert.isAbove(labels.length, 0);
|
||||
|
||||
for (let label of labels) {
|
||||
const labelText = label.textContent.trim();
|
||||
assert.isNotEmpty(labelText);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The `form-container` `div` should have a background color.
|
||||
|
||||
```js
|
||||
assert.isNotEmpty(new __helpers.CSSHelp(document).getStyle('.form-container')?.backgroundColor);
|
||||
```
|
||||
|
||||
The `form-container` `div` should have a `border-radius`.
|
||||
|
||||
```js
|
||||
assert.isNotEmpty(new __helpers.CSSHelp(document).getStyle('.form-container')?.borderRadius);
|
||||
```
|
||||
|
||||
The `form-container` `div` should have padding.
|
||||
|
||||
```js
|
||||
assert.isNotEmpty(new __helpers.CSSHelp(document).getStyle('.form-container')?.padding);
|
||||
```
|
||||
|
||||
The `form-container` `div` should have a width.
|
||||
|
||||
```js
|
||||
assert.isNotEmpty(new __helpers.CSSHelp(document).getStyle('.form-container')?.width);
|
||||
```
|
||||
|
||||
Your labels should have a margin and font color.
|
||||
|
||||
```js
|
||||
assert.isNotEmpty(new __helpers.CSSHelp(document).getStyle('label')?.margin);
|
||||
assert.isNotEmpty(new __helpers.CSSHelp(document).getStyle('label')?.color);
|
||||
```
|
||||
|
||||
Your `input` elements should have a width, padding, and a margin on the bottom.
|
||||
|
||||
```js
|
||||
// Helper function to extract properties from multiple selectors
|
||||
function getStyleProperties(selectors, properties) {
|
||||
const result = {};
|
||||
properties.forEach((property) => {
|
||||
result[property] = selectors.map((selector) => selector?.[property]).find(Boolean);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
// Initializing selectors
|
||||
const selectors = [
|
||||
new __helpers.CSSHelp(document).getStyle('input'),
|
||||
new __helpers.CSSHelp(document).getStyle('input, textarea'),
|
||||
new __helpers.CSSHelp(document).getStyle('textarea, input')
|
||||
];
|
||||
|
||||
// Extract width, padding, and margin properties
|
||||
const { width } = getStyleProperties(selectors, ['width']);
|
||||
assert.isNotEmpty(width);
|
||||
|
||||
const {
|
||||
padding,
|
||||
paddingTop,
|
||||
paddingBottom,
|
||||
paddingLeft,
|
||||
paddingRight
|
||||
} = getStyleProperties(selectors, ['padding', 'paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight']);
|
||||
|
||||
// Assert padding properties
|
||||
assert.isOk(
|
||||
padding ||
|
||||
(paddingTop && paddingBottom && paddingLeft && paddingRight)
|
||||
);
|
||||
|
||||
const { marginBottom } = getStyleProperties(selectors, ['marginBottom']);
|
||||
assert.isNotEmpty(marginBottom);
|
||||
|
||||
```
|
||||
|
||||
Your `textarea` element should have a width, padding, margin on the bottom.
|
||||
|
||||
```js
|
||||
// Helper function to extract properties from multiple selectors
|
||||
function getStyleProperties(selectors, properties) {
|
||||
const result = {};
|
||||
properties.forEach((property) => {
|
||||
result[property] = selectors.map((selector) => selector?.[property]).find(Boolean);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
// Initializing selectors
|
||||
const selectors = [
|
||||
new __helpers.CSSHelp(document).getStyle('textarea'),
|
||||
new __helpers.CSSHelp(document).getStyle('input, textarea'),
|
||||
new __helpers.CSSHelp(document).getStyle('textarea, input')
|
||||
];
|
||||
|
||||
// Extract width, padding, and margin properties
|
||||
const { width } = getStyleProperties(selectors, ['width']);
|
||||
assert.isNotEmpty(width);
|
||||
|
||||
const {
|
||||
padding,
|
||||
paddingTop,
|
||||
paddingBottom,
|
||||
paddingLeft,
|
||||
paddingRight
|
||||
} = getStyleProperties(selectors, ['padding', 'paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight']);
|
||||
|
||||
// Assert padding properties
|
||||
assert.isOk(
|
||||
padding ||
|
||||
(paddingTop && paddingBottom && paddingLeft && paddingRight)
|
||||
);
|
||||
|
||||
const { marginBottom } = getStyleProperties(selectors, ['marginBottom']);
|
||||
assert.isNotEmpty(marginBottom);
|
||||
```
|
||||
|
||||
Your `button` element should have a background color.
|
||||
|
||||
```js
|
||||
assert.isNotEmpty(new __helpers.CSSHelp(document).getStyle('button')?.backgroundColor);
|
||||
```
|
||||
|
||||
Your `button` element should have a font size.
|
||||
|
||||
```js
|
||||
assert.isNotEmpty(new __helpers.CSSHelp(document).getStyle('button')?.fontSize);
|
||||
```
|
||||
|
||||
Your `button` element should have a hover effect that changes the background color.
|
||||
|
||||
```js
|
||||
const gs = (s) => new __helpers.CSSHelp(document).getStyle(s);
|
||||
const btn_hover = gs('button:hover')?.backgroundColor;
|
||||
assert.isNotEmpty(btn_hover);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Contact Form</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Styled Form Project</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="form-container">
|
||||
<form>
|
||||
<h2>Contact Us</h2>
|
||||
<label for="name">Name:</label>
|
||||
<input type="text" id="name" name="name" required>
|
||||
|
||||
<label for="email">Email:</label>
|
||||
<input type="email" id="email" name="email" required>
|
||||
|
||||
<label for="message">Message:</label>
|
||||
<textarea id="message" name="message" required></textarea>
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
background: linear-gradient(45deg, #83a4d4, #b6fbff);
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
width: 300px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
form h2 {
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin: 10px 0 5px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
input{
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
padding: 10px 15px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user