Progress Bar
Implement a progress bar that updates based on a value input by the user.
Solution
Enter a string with at least 50 characters
Explanation
The HTML Structure
A progress container will have two overlapping elements. A background div
that has a constant fixed width and a progress div
that will change size based on a calculation.
Beneath that is the text area
to enter the content to be counted.
Counting the characters
The text area
has an onChange
attribute linked to a handleCount
function.
The function calculates the characters by measuring the length of the value. This is tracked in useState
, with count
.
Animating the progress bar
To update the width of the progress div
a small calculation is done on progressStyle
to see how many pixels should be added for every extra unit of the count.
The width of this element has been fixed to 400px
and an upper limit
of 50 characters
. This means every 1 unit
is equivalent to 8px
.
Bonus animation
For extra benefit to the user experience
, isComplete
is a state to track when the 50 character limit
has been reached. Then short-circuit evaluation
controls the display of the 'complete' alert.
Code
import { useState, CSSProperties } from "react";
import styles from "./Solution.module.css";
const Solution = () => {
const [count, setCount] = useState(0);
const [isComplete, setIsComplete] = useState(false);
const progressStyle: CSSProperties = {
// 400px / 50 characters = 8 -> calc proportion of length
width: `${8 * count}px`,
};
const handleCount: React.ChangeEventHandler<HTMLTextAreaElement> = (
event
) => {
const newCount = event.target.value.length;
setCount(newCount);
setIsComplete(newCount >= 50);
};
return (
<div>
<h3>Enter a string with at least 50 characters</h3>
<div className={styles.progressContainer}>
<div className={styles.progressBG}></div>
<div style={progressStyle} className={styles.progress}></div>
</div>
<textarea
className={styles.input}
onChange={handleCount}
cols={46}
rows={3}
/>
{isComplete && <h3 className={styles.completeMsg}>Complete!</h3>}
</div>
);
};
export default Solution;
Styling
.input {
padding: 5px;
margin: 50px 0 20px;
}
.progressContainer {
position: relative;
}
.progress {
position: absolute;
margin-top: 10px;
height: 20px;
max-width: 400px;
background-color: #7b8ade;
border: 1px solid black;
}
.progressBG {
position: absolute;
margin-top: 10px;
height: 20px;
width: 400px;
border: 1px solid black;
}
.completeMsg {
color: red;
}