Main checkbox selecting all cards when checked - reactjs

I am building a component where I am grouping cards by regions. I have a checkbox by the region name and when its checked I want to be able to select all the cards in the region.
This is how my cards are displayed by region:
<div className="pl-14 text-black font-nunito mt-8 ml-3 text-2xl">
{_.map(_.keysIn(groups), (region) => {
return (
<>
<div className="flex justify-between">
<div className="flex">
<Checkbox
className="bg-transparent shadow-none text-black hover:bg-transparent"
color="primary"
/>
<p className="mt-1">{_.capitalize(region)}</p>
</div>
<div className="pr-16">
<Button variant="text" className="text-lightBlue">
View More
</Button>
</div>
</div>
<UserCards groupedUser={groups[region]} />
</>
);
})}
</div>
And my cards look like this:
const UserCards = ({ groupedUser }) => {
return groupedUser.map((user) => (
<div className="cardContainer">
<div className="card shadow-customShadow">
<Checkbox
key={user.email}
className="bg-transparent shadow-none text-black mb-14 hover:bg-transparent"
color="primary"
onChange={(event) => {
if (event.target.checked) {
const data = [...userEmails];
data.push(user.email);
setUserEmails(data);
} else {
const data = [...userEmails];
const index = data.indexOf(user.email);
data.splice(index, 1);
setUserEmails(data);
}
}}
checked={_.includes(userEmails, user.email)}
/>
<div>
<div className="mt-2 text-lg">
{information stored here}
</div>
<div className="mt-2 text-lg">
{information stored here}
</div>
</div>
</div>
</div>
));
};
How can I tell the region check box to check all the cards?

Related

What ways are there to do an individual hover on elements that were iterated in React?

I'm rendering elements with the map function in react, the problem is that when hovering, the effect is applied to all the elements and what I'm looking for is an individual effect for each card.
This is my code:
const Card = () => {
const [isHovering, setIsHovering] = useState(false);
const handleMouseOver = () => {
setIsHovering(true);
};
const handleMouseOut = () => {
setIsHovering(false);
};
return (
<>
{productsList.map((product) => (
<div key={product.id}>
<div className="relative mb-4" onMouseOver={handleMouseOver} onMouseOut={handleMouseOut}>
<img src={product.img} alt="product" className="w-fit h-fit jshover cursor-pointer" />
{isHovering ? <CardOptions /> : ""}
</div>
<div className="flex justify-center flex-col px-3">
<h3 className="captialize font-sans font-bold text-black mb-3">Adicolor Classics Joggers</h3>
<div className="flex justify-between ">
<span className="capitalize font-normal font-sans text-[#777777]">Dress</span>
<span className="font-sans font-bold">$63.85</span>
</div>
</div>
</div>
))}
</>
)
}
I am iterating an external array of objects with the information of each card.
As seen in the image, I hover my mouse over a card and the "shop now" box appears on all cards.
What would be the best way to do it?
Without iteration of course it worked, but then using React is pointless.
Edit: [SOLVED] The state has to have the index of the iteration of the function. So the conditional rendering has to be conditioned on the index and not on a boolean value.
Like this:
const Card = () => {
const [isHovering, setIsHovering] = useState(-1);
const handleMouseOver = (item) => {
setIsHovering(item);
};
const handleMouseOut = () => {
setIsHovering(-1);
};
return (
<>
{productsList.map((product, index) => (
<div key={product.id}>
<div className="relative mb-4" onMouseOver={() => {
handleMouseOver(index);
}} onMouseOut={handleMouseOut}>
<img src={product.img} alt="product" className="w-fit h-fit jshover cursor-pointer" />
{isHovering === index ? <CardOptions /> : ""}
</div>
<div className="flex justify-center flex-col px-3">
<h3 className="captialize font-sans font-bold text-black mb-3">{product.title}</h3>
<div className="flex justify-between ">
<span className="capitalize font-normal font-sans text-[#777777]">{product.category}</span>
<span className="font-sans font-bold">{product.price}</span>
</div>
</div>
</div>
))}
</>
)

Formik check all checkboxes

The below code is checking the checkboxes one at a time each time select-all is clicked.
I've used the same code in SelectAllStudents elsewhere in my application not using Formik and it works fine. It processes each item in checkboxes.forEach but only checks the last item in the list on each click and I need to know how to get this to check all on a single click
const selectAllStudents = (contactType: string) => {
const checkboxes = document.querySelectorAll("#studentEmail");
var nativeInputValueSetter = Object?.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
"checked"
)?.set;
checkboxes.forEach((checkbox) => {
nativeInputValueSetter?.call(checkbox, true);
let event = new Event("click", { bubbles: true })
checkbox.dispatchEvent(event);
});
})
this is the return markup from <StudentCheckBoxes / >
{students!.map((student, idx) => {
return (
<div key={idx+student.personId}>
<div className="inline-flex items-center">
<Field
type="checkbox"
className="w-4 h-4"
value={student.studentId.toString()}
name={"students"}
id={"studentEmail"}
/>
<span className="ml-2 text-sm">
{student.preferredName} {student.surname}
</span>
</div>
</div>
);
})}
Initial part of the form up to the select-all button
return (
<div>
<Formik
initialValues={initialValues}
onSubmit={onSubmit}
validateOnChange={true}
validationSchema={validationSchema}
className="text-gray-700"
>
{({ values, errors }) => (
<Form className="container pl-1 pr-3 pb-3 mx-auto">
{/* uncomment below to output form and error values */}
<pre>{JSON.stringify(values, null, 2)}</pre>
<pre>{JSON.stringify(errors, null, 2)}</pre>
<div className="flex-col text-center w-full mb-6 hidden 2xl:block">
<h1 className="sm:text-3xl text-2xl font-medium title-font mb-4 text-gray-900">
Email
</h1>
<p className="">Send E-Mail to Group</p>
</div>
{!loadingStudents && filteredStudents.length > 0 && (
<div className="grid grid-cols-2 mb-3">
<StudentCheckboxes students={students} contactType={contactType} />
</div>
)}
<div className="mb-2 ml-3 text-error-red error">
{errors.students}
</div>
<p onClick={() => selectAllStudents(contactType)} className="mb-4 text-sm underline cursor-pointer">Select All</p>

button only clicks once, at one div element

this is the onclick function
export function changeColorButton() {
document.getElementById("friendDiv").style.background = "grey";
}
this is the output file. I want every button to be clickable and give background grey
{data.projects.map((project, key) => {
return (
<div id="friendDiv" className="relative">
<div key={key} className="flex flex-row items-center space-x-6 mb-6">
<img src={project.image} />
<div>
<h1 key={key} class=" text-xl font-bold">
{project.name}
</h1>
</div>
<button
className="absolute right-10 bg-bgButtonAddPeople p-2"
onClick={changeColorButton}
>
Legg til
</button>
</div>
</div>
);
});
}
You would just need to componentize the div element that is being mapped. So you can follow an approach like this.
Each FriendDiv element would have its own instance of changeColorButton, so it would apply the color to its own element.
FriendDiv.js
const FriendDiv = ({ key, project }) => {
const [isGrey, setIsGrey] = useState(false);
const changeColorButton = () => {
setIsGrey(!isGrey);
};
return (
<div
style={{ backgroundColor: isGrey ? 'grey' : 'default-color}}
id="friendDiv"
className="relative"
>
<div key={key} className="flex flex-row items-center space-x-6 mb-6">
<img src={project.image} />
<div>
<h1 key={key} class=" text-xl font-bold">
{project.name}
</h1>
</div>
<button
className="absolute right-10 bg-bgButtonAddPeople p-2"
onClick={changeColorButton}
>
Legg til
</button>
</div>
</div>
);
};
App.js
const App = () => {
return data.projects.map((project, key) => {
return <FriendDiv project={project} key={key} />;
});
};

how set Scroll to the top of the page after page change?

I have a problem, which I have no idea about. when the user changes the page the user stays at the bottom of the page. I want to take the user to the top of the page. I use to navigate by Id but it's not working. How can I solve this?
const handleSelect = (e) => {
navigate('#manageItem')
setLimit(e.target.value)
}
return (
<div id='manageItem' div className='container mx-auto' >
<PageTitle title={'Manage items-Laptop Stockroom'} />
{isLoading ? <Spinner /> : <div className='grid grid-cols-1 gap-16 mt-16 md:grid-cols-3 p-4'>
{
products.map(laptop => <SingleProduct
key={laptop._id}
laptop={laptop}
/>
)
}
</div>}
<div className='flex justify-center gap-2 my-12 items-center'>
<div className='flex gap-2'>
{
[...Array(page).keys()].map(number => <>
<button
onClick={() => setActive(number)}
key={number}
className={`border-2 p-1 border-blue-500 font-semibold ${active === number ? 'bg-blue-500' : ''}`}
> {number + 1}</button>
</>)
}
</div>
<div>
<select onChange={handleSelect}>
<option value="6">6</option>
<option value="12">12</option>
<option value="18">18</option>
</select>
</div>
</div>
</div >
);
};
export default ManageItems;
you can do smth simple like
const scrollToTop = () => {
window.scrollTo(0, 0);
}

How to open one dropdown item?

friends, I have array of questions, and a dropdown list for them... i want to open any question, but all questions are opening together... please help
const FAQ = () => {
const [isOpenAnswer, setIsOpenAnswer] = useState(false)
const toggle = (id) => {
questions.forEach((q) => {
if(q.id === id){
setIsOpenAnswer((prevState) => !prevState)
}
})
}
return <Layout>
<div className="questionsBox pb-5">
<h2 className="title pt-4 pb-4" >Frequently Asked Questions</h2>
{questions.map((q, index) => {
return <div className="question pl-1 pt-3 pb-3 pr-1" key={index}>
<div className="d-flex justify-content-between">
<span className="questionTitle">{q.question}</span>
<img className="questionIcon"
src={Plus} alt="plus"
onClick={() => toggle(q.id)}
/>
</div>
{isOpenAnswer && <p className="answer pt-2 pb-2">
{q.answer}
{q.source}
</p>}
</div>
})}
</div>
</Layout>
}
Use a Javascript object to track which unique q.id is being set to true.
const FAQ = () => {
const [isOpenAnswer, setIsOpenAnswer] = useState({})
const toggle = (id) => {
setIsOpenAnswer(prevState => ({
...prevState,
[id]: !prevState[id],
});
}
return <Layout>
<div className="questionsBox pb-5">
<h2 className="title pt-4 pb-4" >Frequently Asked Questions</h2>
{questions.map((q, index) => {
return <div className="question pl-1 pt-3 pb-3 pr-1" key={index}>
<div className="d-flex justify-content-between">
<span className="questionTitle">{q.question}</span>
<img className="questionIcon"
src={Plus} alt="plus"
onClick={() => toggle(q.id)}
/>
</div>
{isOpenAnswer[q.id] && <p className="answer pt-2 pb-2">
{q.answer}
{q.source}
</p>}
</div>
})}
</div>
</Layout>
}
You're using the same prop for all of them here:
{isOpenAnswer && <p className="answer pt-2 pb-2">
{q.answer}
{q.source}
</p>}
Try saving something unique in state to identify what you're supposed to be showing, e.g.,
{selectedQuestionId && /* the rest */ }
and set the selectedQuestionId where you're currently setting isOpenAnswer .

Resources