ReactJS: Button Enable disable with change color - reactjs

here I have a problem related to making the disable and enable button logic functions.
So, when the user selects the menu in the select option, the button that was originally disabled becomes enable and the color of the button changes too.
how to create such a function? Thanks.
before choosing, the button is disabled.
User selects menu.
The user after selecting the menu, the button changes to enable and the button color also changes.
My Code =
const ButtonModal = () => {
const [openModal, setOpenModal] = useState(false);
const [selectedOption, setSelectedOption] = useState(null);
const handleModal = () => setOpenModal(!openModal);
return (
<>
<button
onClick={handleModal}
className="bg-merah text-white font-bold text-sm rounded-2xl w-48 h-10 py-2 mb-6"
>
Perbarui Kompetitor
</button>
<Modal
center
open={openModal}
onClose={handleModal}
showCloseIcon={false}
>
<section className="grid justify-items-end">
<AiOutlineClose
size={20}
onClick={handleModal}
className="cursor-pointer"
/>
</section>
<div className="flex items-center justify-center mb-4">
<section className="inline-flex gap-2">
<p className="font-bold text-center">Pembaruan Data Kompetitor</p>
</section>
</div>
<p>
Tambah data dengan memilih nama kompetitor yang tersedia atau masukkan
data baru.
</p>
<p className="font-bold mt-6">Nama Kompetitor</p>
<div class="flex justify-center">
<div class="mb-3 w-600 mr-6 mt-2">
<select
onChange={(e) => setSelectedOption(e.target.value)}
class="form-select appearance-none
block
w-full
px-3
py-1.5
mb-1
text-base
font-normal
text-gray-700
bg-white bg-clip-padding bg-no-repeat
border border-solid border-gray-300
rounded
transition
ease-in-out
m-0
focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none"
aria-label="Default select example"
>
<option selected>Pilih nama kompetitor</option>
<option value="biznet">Biznet</option>
<option value="cbn">CBN</option>
<option value="first_media">First Media</option>
<option value="iconet">Iconet</option>
<option value="oxygen">Oxygen</option>
<option value="my_republik">My Republik</option>
<option value="other">Lainnya</option>
</select>
</div>
</div>
{selectedOption === "other" && (
<div class="mb-6">
<input
class="w-[37.5rem] bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Masukan nama kompetitor..."
required
/>
</div>
)}
<div className="flex items-center justify-center">
<button
onClick={handleModal}
type="button"
class="inline-block px-6 py-2 border-2 border-red-600 text-red-600 font-medium text-xs leading-tight rounded hover:bg-black hover:bg-opacity-5 focus:outline-none focus:ring-0 transition duration-150 ease-in-out"
>
Kembali
</button>
<button
type="button"
class="inline-block ml-6 px-6 py-2.5 bg-gray-200 text-gray-700 font-medium text-xs leading-tight rounded shadow-md hover:bg-gray-300 hover:shadow-lg focus:bg-gray-300 focus:shadow-lg focus:outline-none focus:ring-0 active:bg-gray-400 active:shadow-lg transition duration-150 ease-in-out"
disabled
>
Lanjutkan
</button>
</div>
</Modal>
</>
);
};

Bind the button's disabled attribute to the selectedOption state - is this what you're after?
And do the same for changing colour, using a ternary to set the value.
<button
onClick={handleModal}
disabled={!selectedOption}
style={{ background: selectedOption ? 'red': 'transparent' }}
className="bg-merah text-white font-bold text-sm rounded-2xl w-48 h-10 py-2 mb-6"
/>
EDIT: I think I saw you mention in a comment that it's the other button. Just set them on the other button then.
Also edited to bind to the more appropriate state selectedOption.
disabled={!selectedOption}
style={{ background: selectedOption ? 'red': 'transparent' }}

Related

Jest how to test react component which partial elements are lazy load?

The code below comes from https://headlessui.com/react/popover.
As you can see, it uses a function to render Popover.Button.
In enzyme, no matter use shallow or mount, the result tree without elements wrapped by React.Fragment.
So I want to ask is there a way to render the whole dom in enzyme or #testing-library/react
export default function Example() {
return (
<div className="fixed top-16 w-full max-w-sm px-4">
<Popover className="relative">
{({ open }) => (
<>
<Popover.Button
className={`
${open ? '' : 'text-opacity-90'}
group inline-flex items-center rounded-md bg-orange-700 px-3 py-2 text-base font-medium text-white hover:text-opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75`}
>
<span>Solutions</span>
<ChevronDownIcon
className={`${open ? '' : 'text-opacity-70'}
ml-2 h-5 w-5 text-orange-300 transition duration-150 ease-in-out group-hover:text-opacity-80`}
aria-hidden="true"
/>
</Popover.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute left-1/2 z-10 mt-3 w-screen max-w-sm -translate-x-1/2 transform px-4 sm:px-0 lg:max-w-3xl">
<div className="overflow-hidden rounded-lg shadow-lg ring-1 ring-black ring-opacity-5">
<div className="relative grid gap-8 bg-white p-7 lg:grid-cols-2">
{solutions.map((item) => (
<a
key={item.name}
href={item.href}
className="-m-3 flex items-center rounded-lg p-2 transition duration-150 ease-in-out hover:bg-gray-50 focus:outline-none focus-visible:ring focus-visible:ring-orange-500 focus-visible:ring-opacity-50"
>
<div className="flex h-10 w-10 shrink-0 items-center justify-center text-white sm:h-12 sm:w-12">
<item.icon aria-hidden="true" />
</div>
<div className="ml-4">
<p className="text-sm font-medium text-gray-900">
{item.name}
</p>
<p className="text-sm text-gray-500">
{item.description}
</p>
</div>
</a>
))}
</div>
<div className="bg-gray-50 p-4">
<a
href="##"
className="flow-root rounded-md px-2 py-2 transition duration-150 ease-in-out hover:bg-gray-100 focus:outline-none focus-visible:ring focus-visible:ring-orange-500 focus-visible:ring-opacity-50"
>
<span className="flex items-center">
<span className="text-sm font-medium text-gray-900">
Documentation
</span>
</span>
<span className="block text-sm text-gray-500">
Start integrating products and tools
</span>
</a>
</div>
</div>
</Popover.Panel>
</Transition>
</>
)}
</Popover>
</div>
)
}

Reactjs: Form logic

I'm having problems with the logic form.
So, I have a select form. Like this =>
So what I want is, when the user clicks on the "other" option, an input form will appear.
The question is, how to make the logic function?
My Code =
const ButtonModal = () => {
const [openModal, setOpenModal] = useState(false);
const [selectedOption, setSelectedOption] = useState(null);
const handleModal = () => setOpenModal(!openModal);
return (
<>
<button
onClick={handleModal}
className="bg-merah text-white font-bold text-sm rounded-2xl w-48 h-10 py-2 mb-6"
>
Perbarui Kompetitor
</button>
<Modal
center
open={openModal}
onClose={handleModal}
showCloseIcon={false}
>
<section className="grid justify-items-end">
<AiOutlineCloseCircle size={30} onClick={handleModal} />
</section>
<div className="flex items-center justify-center mb-4">
<section className="inline-flex gap-2">
<p className="font-bold text-center">Pembaruan Data Kompetitor</p>
</section>
</div>
<p>
Tambah data dengan memilih nama kompetitor yang tersedia atau masukkan
data baru.
</p>
<p className="font-bold mt-6">Nama Kompetitor</p>
<div class="flex justify-center">
<div class="mb-3 w-600 mr-6 mt-2">
<select
onChange={(e) => setSelectedOption(e.target.value)}
class="form-select appearance-none
block
w-full
px-3
py-1.5
mb-1
text-base
font-normal
text-gray-700
bg-white bg-clip-padding bg-no-repeat
border border-solid border-gray-300
rounded
transition
ease-in-out
m-0
focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none"
aria-label="Default select example"
>
<option selected>Choose a competitor's name</option>
<option value="biznet">Biznet</option>
<option value="cbn">CBN</option>
<option value="first_media">First Media</option>
<option value="iconet">Iconet</option>
<option value="oxygen">Oxygen</option>
<option value="my_republik">My Republik</option>
<option value="other">Other</option>
</select>
</div>
</div>
{selectedOption === "other" && (
<div class="mb-6">
<input
class="w-[37.5rem] bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Masukan nama kompetitor..."
required
/>
</div>
)}
<div className="flex items-center justify-center">
<button
type="button"
class="inline-block px-6 py-2 border-2 border-red-600 text-red-600 font-medium text-xs leading-tight rounded hover:bg-black hover:bg-opacity-5 focus:outline-none focus:ring-0 transition duration-150 ease-in-out"
>
Kembali
</button>
<button
type="button"
class="inline-block ml-6 px-6 py-2.5 bg-gray-200 text-gray-700 font-medium text-xs leading-tight rounded shadow-md hover:bg-gray-300 hover:shadow-lg focus:bg-gray-300 focus:shadow-lg focus:outline-none focus:ring-0 active:bg-gray-400 active:shadow-lg transition duration-150 ease-in-out"
>
Ya, Keluar
</button>
</div>
</Modal>
</>
);
};

Access JSX react variables in a button

I have mapped a list of responses from my backend mongodb to my frontend. For each response, I have also added a button. I want to add an voteResponse function for my vote button. How can I continue to have access to my {response.respondee} and {response.response} in my voteResponse function?
{responses.map((response) => (
<div
key={response._id}
className='py-8 px-12 mb-12 bg-gray-50 border-b border-gray-100 transform transition duration-300 ease-in-out hover:-translate-y-2'
>
<NextLink href={`/responses/${response.response}`} passHref>
<div>
<div className='inline-block text-gray-900 mb-4'></div>
<h3 className='text-lg leading-normal mb-2 font-semibold text-black'>
{response.response}
</h3>
<p className='text-gray-500'>
{response.respondee.slice(0, 5)}...
{response.respondee.slice(38)}
</p>
</div>
</NextLink>
<div>
<button
type='submit'
className='inline-flex justify-center mt-3 py-1 px-3 border border-transparent shadow text-base font-medium rounded-md text-white bg-cyan-600 hover:bg-cyan-700 focus:outline-none'
onClick={voteResponse}
>
Vote
</button>
</div>
</div>
))}
You can do this by 2 ways. First is by making a separate component which is being rendered in map function and pass response in it through props then each component will have access to it's response.response and response.respondee.
funtion Response({response}){
const voteResponse = () =>{
console.log(response.response, response.respondee)
}
return(
<div key={response._id} className='py-8 px-12 mb-12 bg-gray-50 border-b border-gray-100 transform transition duration-300 ease-in-out hover:-translate-y-2'>
<NextLink href={`/responses/${response.response}`} passHref>
<div>
<div className='inline-block text-gray-900 mb-4'></div>
<h3 className='text-lg leading-normal mb-2 font-semibold text-black'>
{response.response}
</h3>
<p className='text-gray-500'>
{response.respondee.slice(0, 5)}... {response.respondee.slice(38)}
</p>
</div>
</NextLink>
<div>
<button type='submit' className='inline-flex justify-center mt-3 py-1 px-3 border border-transparent shadow text-base font-medium rounded-md text-white bg-cyan-600 hover:bg-cyan-700 focus:outline-none' onClick={voteResponse}>
Vote
</button>
</div>
</div>
)
}
//use it like this
{responses.map((response) => (
<Response response={response} key={response._id} />
))}
Second way to pass the index in voteResponse function and then through that index you can access the those variables like responses[index].response and responses[index].respondee.
{responses.map((response, index) => (
<div key={response._id} className='py-8 px-12 mb-12 bg-gray-50 border-b border-gray-100 transform transition duration-300 ease-in-out hover:-translate-y-2'>
<NextLink href={`/responses/${response.response}`} passHref>
<div>
<div className='inline-block text-gray-900 mb-4'></div>
<h3 className='text-lg leading-normal mb-2 font-semibold text-black'>
{response.response}
</h3>
<p className='text-gray-500'>
{response.respondee.slice(0, 5)}... {response.respondee.slice(38)}
</p>
</div>
</NextLink>
<div>
<button type='submit' className='inline-flex justify-center mt-3 py-1 px-3 border border-transparent shadow text-base font-medium rounded-md text-white bg-cyan-600 hover:bg-cyan-700 focus:outline-none' onClick={voteResponse}>
Vote
</button>
</div>
</div>
))}
Pass value to onClick prop: onClick={() => voteResponse(response)} and use it as parameter of voteResponse function:
const voteResponse = (res) => {
console.log("response.respondee is: ", res.respondee) //<=== response.respondee value is res.respondee value
console.log("response.response is: ", res.response)
}
{responses.map((response) => (
<div
key={response._id}
className='py-8 px-12 mb-12 bg-gray-50 border-b border-gray-100 transform transition duration-300 ease-in-out hover:-translate-y-2'
>
<NextLink href={`/responses/${response.response}`} passHref>
<div>
<div className='inline-block text-gray-900 mb-4'></div>
<h3 className='text-lg leading-normal mb-2 font-semibold text-black'>
{response.response}
</h3>
<p className='text-gray-500'>
{response.respondee.slice(0, 5)}...
{response.respondee.slice(38)}
</p>
</div>
</NextLink>
<div>
<button
type='submit'
className='inline-flex justify-center mt-3 py-1 px-3 border border-transparent shadow text-base font-medium rounded-md text-white bg-cyan-600 hover:bg-cyan-700 focus:outline-none'
onClick={() => voteResponse(response)
>
Vote
</button>
</div>
</div>
))}

How can I get a modal to be larger on desktop?

I am trying to get the modal on the site I'm working on to open larger on a desktop and to change to a vertical view in mobile, like these examples which I designed in Figma:
.
Any help with what I could change in the code to reflect these images would be amazing.
{showModal ? (
<>
<div
className="justify-center items-center flex overflow-x-hidden overflow-y-auto fixed inset-0 z-50 outline-none focus:outline-none"
onClick={() => setShowModal(false)}
>
<div className="relative w-auto my-6 mx-auto max-w-3xl grid grid-cols-2 ">
{/* which grid? number one */}
<div className="border-0 rounded-lg shadow-lg relative flex flex-col w-full bg-white outline-none focus:outline-none">
<Image
src={ImageLink}
alt="Work?"
width="600"
height="600"
layout="responsive"
onClick={() => setShowModal(true)}
/>
</div>
{/*content*/}
<div className="border-0 rounded-lg shadow-lg relative flex flex-col w-full bg-white outline-none focus:outline-none">
{/*header*/}
<div className="flex items-start justify-between p-5 border-b border-solid border-gray-300 rounded-t">
<h3 className="text-3xl font-semibold">
{Name} </h3>
<button
className="p-1 ml-auto bg-transparent border-0 text-black opacity-5 float-right text-3xl leading-none font-semibold outline-none focus:outline-none"
onClick={() => setShowModal(false)}
>
<span className="bg-transparent text-black opacity-5 h-6 w-6 text-2xl block outline-none focus:outline-none">
×
</span>
</button>
</div>
{/*body*/}
<div className="relative p-6 flex-auto">
<p className="my-4 text-gray-600 text-lg leading-relaxed">
{Description}
</p>
</div>
{/*footer*/}
<div className="flex items-center justify-end p-6 border-t border-solid border-gray-300 rounded-b">
<button
className="text-red-500 background-transparent font-bold uppercase px-6 py-2 text-sm outline-none focus:outline-none mr-1 mb-1"
type="button"
style={{ transition: "all .15s ease" }}
// onClick={() => setShowModal(false)}
>
Contact
</button>
<button
className="bg-green-500 text-white active:bg-green-600 font-bold uppercase text-sm px-6 py-3 rounded shadow hover:shadow-lg outline-none focus:outline-none mr-1 mb-1"
type="button"
style={{ transition: "all .15s ease" }}
// onClick={() => setShowModal(false)}
>
Make an Offer </button>
</div>
</div>
</div>
</div>
<div className="opacity-25 fixed inset-0 z-40 bg-black"></div>
</>
) : null}
</>
);
}
I'm just stuck on tailwind documentation - trying to use the grid system, it just doesn't want to work and would love to understand where i'm going wrong.
Here is a link to the current site to see how it looks currently
https://greenr-two.vercel.app/example
thanks in advance,
It's a simple change.
On mobile, grid-cols-1 with col-span-1 will give 100% to image and div.
On Desktop, md:grid-cols-3 with image as col-span-2 will consume 66% (2/3 * 100) . Whereas div with col-span-1 will consume 33% (1/3 * 100).
<div class="grid grid-cols-1 md:grid-cols-3">
<image class="col-span-1 md:col-span-2" />
<div class="col-span-1"></div>
</div>

Conditionally render download button in react

I have a download button from which the user can download the assets. but I want only authenticated users can download. If the user is not authenticated and click on the download button I want to show them a modal with a login button on it. I have a modal component. I am using React.
Please help me.
Getting currently authenticated user from the firebase.
const {currentUser} = useAuth();
This is my code for the download button.
<a className="bg-secondary text-white font-bold py-3
px-5 rounded text-2xl focus:outline-none mt-3 block w-48" href="YOUR_URL">
<i className="animate-bounce fas fa-arrow-down mr-2"></i>
Download
</a>
Modal Code:
import React from "react";
import { useState } from "react";
export default function Modal() {
const [showModal, setShowModal] = useState(true);
console.log('modal');
return (
<>
{showModal ? (
<>
<div
className="justify-center items-center flex overflow-x-hidden overflow-y-auto fixed inset-0 z-50 outline-none focus:outline-none"
onClick={() => setShowModal(false)}
>
<div className="relative w-auto my-6 mx-auto max-w-3xl">
{/*content*/}
<div className="border-0 rounded-lg shadow-lg relative flex flex-col w-full bg-white outline-none focus:outline-none">
{/*header*/}
<div className="flex items-start justify-between p-5 border-b border-solid border-gray-300 rounded-t">
<h3 className="text-3xl font-semibold">
Modal Title
</h3>
<button
className="p-1 ml-auto bg-transparent border-0 text-black opacity-5 float-right text-3xl leading-none font-semibold outline-none focus:outline-none"
onClick={() => setShowModal(false)}
>
<span className="bg-transparent text-black opacity-5 h-6 w-6 text-2xl block outline-none focus:outline-none">
×
</span>
</button>
</div>
{/*body*/}
<div className="relative p-6 flex-auto">
<p className="my-4 text-gray-600 text-lg leading-relaxed">
I always felt like I could do anything. That’s the main
thing people are controlled by! Thoughts- their perception
of themselves! They're slowed down by their perception of
themselves. If you're taught you can’t do anything, you
won’t do anything. I was taught I could do everything.
</p>
</div>
{/*footer*/}
<div className="flex items-center justify-end p-6 border-t border-solid border-gray-300 rounded-b">
<button
className="text-red-500 background-transparent font-bold uppercase px-6 py-2 text-sm outline-none focus:outline-none mr-1 mb-1"
type="button"
style={{ transition: "all .15s ease" }}
onClick={() => setShowModal(false)}
>
Close
</button>
<button
className="bg-green-500 text-white active:bg-green-600 font-bold uppercase text-sm px-6 py-3 rounded shadow hover:shadow-lg outline-none focus:outline-none mr-1 mb-1"
type="button"
style={{ transition: "all .15s ease" }}
onClick={() => setShowModal(false)}
>
Save Changes
</button>
</div>
</div>
</div>
</div>
<div className="opacity-25 fixed inset-0 z-40 bg-black"></div>
</>
) : null}
</>
);
}
You could use button instead of a and use onClick to check if currentUser exists when you click button.
This is final code:
function x {
const {currentUser} = useAuth();
const downloadAssets = () => {
if(currentUser){
window.open("url assets here", '_blank', 'noopener,noreferrer')
}else{
// Open modal here
alert("User not authenticated");
}
}
return {
<>
...
<button onClick={downloadAssets}><i className="animate-bounce fas fa-arrow-down mr-2"></I>Download</button>
...
</>
}

Resources