How to pass an icon as a prop? - javascript

I'm using props and I want to have "optionText" and "optionIcon" the 1st one I'm able to add but I'm not able to implement an icon as a prop
File where I'm creating props
import Icon from "#mui/material/Icon";
function HeaderMenuOptions({ optionText, OptionIcon }) {
return (
<div className="flex items-center text-center">
<Icon>{OptionIcon}</Icon>
<h1 className="py-1 my-1 hover:bg-menu-option-hover hover:text-black cursor-pointer">
{optionText}
</h1>
</div>
);
}
export default HeaderMenuOptions;
file where I'm using said props
<div className="absolute left-72 top-3 rounded-md bg-section w-[10rem] text-center">
<p className="menu-header mt-2">VIEW OPTIONS</p>
<div className="flex items-center justify-center space-x-2 mr-2 cursor-pointer hover:bg-menu-option-hover hover:hover:text-black group">
<HeaderMenuOptions optionText="Night Mode" />
<CheckBoxIcon
defaultChecked
onClick={() => alert("Light mode has not been added yet!")}
className="cursor-pointer text-blue-500"
/>
</div>
<p className="menu-header">MORE STUFF</p>
<HeaderMenuOptions optionText="Premium" OptionIcon={SecurityIcon} />
<HeaderMenuOptions optionText="TEST" />
<HeaderMenuOptions optionText="TEST" />
<HeaderMenuOptions optionText="TEST" />
<HeaderMenuOptions optionText="TEST" />
</div>
can anyone please help me. Thanks

The HeaderMenuOptions looks fine. You need to change the parent component.
You can do something like this.
<HeaderMenuOptions optionText="Premium" OptionIcon={<SecurityIcon />} />

You can use pass icon as JSX.Element from ParentComponent to TargetComponent as the following:
import AddAlertIcon from '#mui/icons-material/AddAlert';
...
const ParentComponent=()=>{
return(
....
<TargetComponent icon={<AddAlertIcon />}>
....
)
}
const TargetComponent = (props: Props) => {
return(
<span>
{props.icon}
</span>
);
}
export type Props= {
icon: JSX.Element;
};

Related

How to use Mantine dropzone with React hook form in Javascript

Is there any solution to use Mantine dropzone with Reack hook form in javascript? I am doing a modal Upload using Tailwind components like this
import { useForm } from 'react-hook-form';
import { Group, Text, useMantineTheme } from '#mantine/core';
import { Dropzone, DropzoneProps, IMAGE_MIME_TYPE } from '#mantine/dropzone';
export default function UploadForm({ isVisible, onClose }) {
const { register, handleSubmit, formState: { errors } } = useForm({});
const onSubmit = data => console.log(data);
if (!isVisible) return <></>;
return (
<div className="bg-white shadow sm:rounded-lg">
<div className="px-4 py-5 sm:p-6">
<h3 className="text-lg leading-6 font-medium text-gray-900">Create new subcategory</h3>
<div className="mt-2 max-w-xl text-sm text-gray-500">
<p>Enter subcategory's name</p>
</div>
<form onSubmit={handleSubmit(onSubmit)} className="mt-5 sm:flex sm:items-center">
<div className="w-full sm:max-w-xs">
<label htmlFor="Name" className="sr-only">
Name
</label>
<input
type="file"
{...register("file", { required: true })}
/>
</div>
<button
type="submit"
className="mt-3 w-full inline-flex items-center justify-center px-4 py-2 border border-transparent shadow-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
>
Save
</button>
</form>
</div>
</div>
);
}
But when i tried to change the input to Mantine dropzone, it always occured error. Is there any ideas?
Thank you so much and sorry for bad English!
I want to have a modal form with only dropzone and submit button, but i'm still confusing with Mantine dropzone
For someone stuck in this problem like me, you can use Controller and FormProvider of react-hook-form to solve this problem, just replace the input tag with the code below
<Controller
control={methods.control}
name="fileupload"
render={({ field }) => (
<Dropzone
onDrop={(files) =>
methods.setValue("fileupload", files)
}
onReject={(files) =>
console.log("rejected files", files)
}
maxSize={3 * 1024 ** 2}
accept={IMAGE_MIME_TYPE}
{...field}
>
<Group
position="center"
spacing="xl"
style={{
minHeight: 220,
pointerEvents: "none",
}}
>
<Dropzone.Accept>
<IconUpload
size={50}
stroke={1.5}
color={
theme.colors[theme.primaryColor][
theme.colorScheme === "dark" ? 4 : 6
]
}
/>
</Dropzone.Accept>
<Dropzone.Reject>
<IconX
size={50}
stroke={1.5}
color={
theme.colors.red[
theme.colorScheme === "dark" ? 4 : 6
]
}
/>
</Dropzone.Reject>
<Dropzone.Idle>
<IconPhoto size={50} stroke={1.5} />
</Dropzone.Idle>
<div>
<Text size="xl" inline>
Drag images here or click to select files
</Text>
<Text
size="sm"
color="dimmed"
inline
mt={7}
>
Attach as many files as you like, each
file should not exceed 5mb
</Text>
</div>
</Group>
</Dropzone>
)}
/>
and wrap all the form in <FormProvider {...methods}></FormProvider>, change submit function to onSubmit={methods.handleSubmit()}

How to pass icons as props in react with typescript

I am new to typescript and started a simple project. I have a sidebar component that is made up of several sidebarNavigationItem components. Each Sidebar item has an Icon and Title, as shown below.
Sidebar.tsx
import SidevarNavigationItem from "./SidevarNavigationItem"
import { HomeIcon } from "#heroicons/react/24/solid"
import { StarIcon } from "#heroicons/react/24/solid"
const Sidebar = () => {
return (
<nav
id="sidebar"
className="p-2 mt-5 max-w-2xl xl:min-w-fit sticky top-20 flex-grow-0.2 text-center h-screen"
>
<div id="navigation">
<SidevarNavigationItem Icon={<HomeIcon />} name="Homepage" source="/" />
<SidevarNavigationItem
Icon={<StarIcon />}
name="Tournaments"
source="/tournaments"
/>
</div>
</nav>
)
}
export default Sidebar
SidebarNavigationItem.tsx
import Link from "next/link"
import { FunctionComponent } from "react"
interface Props {
Icon: React.ElementType
name: string
source: string
}
const SidevarNavigationItem: FunctionComponent<Props> = ({
Icon,
name,
source,
}) => {
return (
<Link href={source}>
<div className="flex items-center cursor-pointer text-3xl space-x-5 justify-center">
{Icon && <Icon classname="h-8 w-8 text-red-500" />}
<p className="hidden sm:inline-flex font-medium">{name}</p>
</div>
</Link>
)
}
export default SidevarNavigationItem
The issue is that I can't style the icons that have been passed down as props when the type is JSX.Element. Changing the type of the Icon to React.ElementType will let me add styles but causes an error in the parent that says
Type 'Element' is not assignable to type 'ElementType'
What do I do?
You need to pass the component reference itself instead of its rendered jsx.
So, in your Sidebar.tsx component, you need to replace these lines:
Icon={<HomeIcon />}
Icon={<StarIcon />}
with these lines respectively:
Icon={HomeIcon}
Icon={StarIcon}
The final code should be like this:
<div id="navigation">
<SidevarNavigationItem Icon={HomeIcon} name="Homepage" source="/" />
<SidevarNavigationItem
Icon={StarIcon}
name="Tournaments"
source="/tournaments"
/>
</div>

How to get child click on paremt in react js

I have a child and parent compoinents as follows,
When I click on the div I am not able to get the alert in parent component but when I keep button in child component I can able to get the aler. Not sure what I am doing, Can any one please help. Thanks.
Child Comp:
const PrimaryNavigation = ({ className, data, childToParent }: IPrimaryNavigation) => {
return (
<div className={`${className} w-full bg-black grid grid-cols-12 gap-4 py-12 pl-12`}>
<div className="relative col-span-2 md:col-span-1 w-5 h-5 md:w-6 md:h-6 cursor-pointer" onClick={() => { childToParent() }}>>
<Image src={searchSvg} layout="fill" objectFit="contain" alt="hamburgerIcon" />
</div>
<div className="relative col-span-2 md:col-span-1 md:w-6 md:h-6 w-5 h-5 cursor-pointer" >
<Image src={hamburgerSvg} layout="fill" objectFit="contain" alt="searchIcon" />
</div>
</div>
)
}
Parent Comp:
const Header = ({ data }: IHeader) => {
const childToParent = () => {
alert('hi')
}
return (
<div>
<header className="w-full md-0 md:md-6 bg-black grid grid-cols-12 gap-4">
<PrimaryNavigation className="col-span-11" data={data.primaryNavigationCollection.items} childToParent={childToParent} />
</header>
</div>
)
}
export default Header

How to properly conditionally render child component in react

I have the following components : (AddBookPanel contains the AddBookForm)
I need to have the ability to display the form on click of the 'AddBookButton', and also
have the ability to hide the form while clicking the 'x' button (img in AddBookForm component), however the component doesn't seem to load properly, what could be wrong here? What do you think is the best way to organize this?
import { AddBookButton } from './AddBookButton';
import { AddBookForm } from './AddBookForm';
import { useState } from 'react';
export const AddBookPanel = () => {
const [addBookPressed, setAddBookPressed] = useState(false);
return (
<div>
<div onClick={() => setAddBookPressed(!addBookPressed)}>
<AddBookButton />
</div>
<AddBookForm initialFormActive={addBookPressed} />
</div>
);
};
import { useState } from 'react';
interface AddBookFormProps {
initialFormActive: boolean;
}
export const AddBookForm: React.FC<AddBookFormProps> = ({
initialFormActive,
}) => {
const [isFormActive, setIsFormActive] = useState(initialFormActive);
return (
<>
{isFormActive && (
<div className="fixed box-border h-2/3 w-1/3 border-4 left-1/3 top-24 bg-white border-slate-600 shadow-xl">
<div className="flex justify-between bg-slate-400">
<div>
<p className="text-4xl my-5 mx-6">Add a new Book</p>
</div>
<div className="h-16 w-16 my-3 mx-3">
<button onClick={() => setIsFormActive(!isFormActive)}>
<img
src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/OOjs_UI_icon_close.svg/1200px-OOjs_UI_icon_close.svg.png"
alt="close-button"
className="object-fit"
></img>
</button>
</div>
</div>
<div className="flex-col">
<div className="flex justify-between my-10 ml-5 text-xl">
<input type="text" placeholder="book name"></input>
</div>
</div>
</div>
)}
</>
);
};
You're always rendering the AddBookForm. That means in the initial render the useState is called with false. Since you never call setIsFormActive it is now stuck as "do not render".
Just don't use the useState and use the property initialFormActive directly. And maybe rename it to isFormActive. Let the parent handle the state change.
import { AddBookButton } from './AddBookButton';
import { AddBookForm } from './AddBookForm';
import { useState } from 'react';
export const AddBookPanel = () => {
const [addBookPressed, setAddBookPressed] = useState(false);
return (
<div>
<div onClick={() => setAddBookPressed(!addBookPressed)}>
<AddBookButton />
</div>
<AddBookForm initialFormActive={addBookPressed} onClose={() => setAddBookPressed(false)} />
</div>
);
};
import { useState } from 'react';
interface AddBookFormProps {
initialFormActive: boolean;
onColse: () => void;
}
export const AddBookForm: React.FC<AddBookFormProps> = ({
initialFormActive,
onClose,
}) => {
// const [isFormActive, setIsFormActive] = useState(initialFormActive); don't use this frozen state, allow the parent to control visibility
return (
<>
{initialFormActive && (
<div className="fixed box-border h-2/3 w-1/3 border-4 left-1/3 top-24 bg-white border-slate-600 shadow-xl">
<div className="flex justify-between bg-slate-400">
<div>
<p className="text-4xl my-5 mx-6">Add a new Book</p>
</div>
<div className="h-16 w-16 my-3 mx-3">
<button onClick={() => onClose()}>
<img
src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/OOjs_UI_icon_close.svg/1200px-OOjs_UI_icon_close.svg.png"
alt="close-button"
className="object-fit"
></img>
</button>
</div>
</div>
<div className="flex-col">
<div className="flex justify-between my-10 ml-5 text-xl">
<input type="text" placeholder="book name"></input>
</div>
</div>
</div>
)}
</>
);
};
Problem with your code is in how you tied initialFormActive to inner state of AddBookForm component. When looking at useState inside AddBookForm it is clear that prop initialFormActive only affects your initial state, and when looking at your parent component I can clearly see that you passed false as value for initialFormActive prop, thus initializing state inside AddBookForm with false value(form will stay hidden). Then you expect when you click on button, and when prop value changes(becomes true), that your <AddBookForm /> will become visible, but it will not because you just used initial value(false) from prop and after that completely decoupled from prop value change.
Since you want to control visibility outside of component, then you should attach two props to AddBookForm, isVisible and toggleVisibiliy. And do something like this:
export const AddBookPanel = () => {
const [addBookPressed, setAddBookPressed] = useState(false);
const toggleAddBookPressed = () => setAddBookPressed(p => !p);
return (
<div>
<div onClick={() => setAddBookPressed(!addBookPressed)}>
<AddBookButton />
</div>
<AddBookForm isVisible={addBookPressed} toggleVisibility={toggleAddBookPressed } />
</div>
);
};
export const AddBookForm: React.FC<AddBookFormProps> = ({
isVisible, toggleVisibility
}) => {
return (
<>
{isVisible&& (
<div className="fixed box-border h-2/3 w-1/3 border-4 left-1/3 top-24 bg-white border-slate-600 shadow-xl">
<div className="flex justify-between bg-slate-400">
<div>
<p className="text-4xl my-5 mx-6">Add a new Book</p>
</div>
<div className="h-16 w-16 my-3 mx-3">
<button onClick={toggleVisibility}>
<img
src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/OOjs_UI_icon_close.svg/1200px-OOjs_UI_icon_close.svg.png"
alt="close-button"
className="object-fit"
></img>
</button>
</div>
</div>
<div className="flex-col">
<div className="flex justify-between my-10 ml-5 text-xl">
<input type="text" placeholder="book name"></input>
</div>
</div>
</div>
)}
</>
);
};

Button Function Not calling From Another file in React

I need to call CutomerDashboard.js file's "toggleIsTrucated" function and "isTruncated" to CustomerNotice.js files button onClick and text change places, How can I call that?
(In this customer dashboard file I'm creating a Read function to show some extent of notice text)
import React, {useState,useEffect} from 'react';
import { Input, Row, Col, Button } from 'antd';
import {fetchDashboardMetrics} from "./DashboardApi";
import {items} from "./DashboardItems";
import axios from 'axios';
import CustomerNotice from "./CustomerNotice";
function Read ({children}) {
const text = children;
const [isTruncated, setIsTrucated] = useState(true);
const result = isTruncated ? text.slice(0,90) : text;
function toggleIsTrucated(){
setIsTrucated(!isTruncated);
}
return (
<div>
{result}....
</div>
);
}
const CustomerDashboard = () => {
const [features, setFeatures] = useState(items);
const source = axios.CancelToken.source()
const [notice, setNotice] = useState(<Read>Customer Notice: Optimism Is Invaluable For The Meaningful Life. With A Firm Belief In A Positive Future You Can Throw Yourself Into The Service Of That Which Is Larger Than You Are. -Martin Seligman-</Read>);
const [noticeVisibility, setNoticeVisibility] = useState(true);
useEffect(() => {
fetchDashboardMetrics(features, setFeatures,source.token)
return (() => {
source.cancel();
})
}, []);
return (
<>
<div className='md:pl-8 sm:pl-0'>
<div className='my-5 '>
<p className='mb-8'>My Account - Dashboard Overview</p>
{noticeVisibility && <CustomerNotice notice={notice} setNoticeVisibility={setNoticeVisibility}/>}
</div>
<ul role="list" className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{features.map((feature) => (
<li key={feature.name} className="col-span-1 bg-white rounded-lg shadow divide-y divide-gray-200 relative">
<div className="w-full flex items-center justify-between p-6 space-x-6">
<div className="flex-1 truncate">
<div className="flex items-center space-x-3 justify-between">
<h3 className="text-gray-900 text-lg truncate">{feature.name}</h3>
{feature.isNew && (
<div className="absolute -top-2 -right-2 p-1 px-4 text-white text-sm bg-red-500">
New
</div>
)}
</div>
</div>
</div>
<div>
<div className={'mx-4 mt-2 mb-3 '}>
{feature.details.map((singleDetail) => {
return (
<div className={'flex justify-between text-base'}>
<span>{singleDetail.name}</span>
<span>{singleDetail.value}</span>
</div>
)
})}
</div>
</div>
</li>
))}
</ul>
</div>
</>
)
}
export default CustomerDashboard;
import React, {useState,useEffect} from 'react';
import {XIcon} from "#heroicons/react/solid";
const CustomerNotice = ({notice, setNoticeVisibility}) => {
return (
<div>
<div className="mt-8 pb-2 sm:pb-5">
<div className="max-w-7xl mx-auto px-2 sm:px-6 lg:px-8">
<div className="p-2 rounded-lg bg-orange-600 shadow-lg sm:p-3">
<div className="flex items-center justify-between flex-wrap">
<div className="w-0 flex-1 flex items-center">
<p className="ml-3 font-medium text-white truncate">
<span className="md:inline">{notice}</span>
</p>
</div>
<div className="order-3 mt-2 flex-shrink-0 w-full sm:order-2 sm:mt-0 sm:w-auto">
<a
href="#"
className="flex items-center justify-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-orange-600 bg-white hover:bg-orange-50"
>
<button onClick={toggleIsTrucated}>{isTruncated ? "Read More" : "Read Less"}</button>
</a>
</div>
<div className="order-2 flex-shrink-0 sm:order-3 sm:ml-2">
<button
onClick={() => setNoticeVisibility(false)}
type="button"
className="-mr-1 flex p-2 rounded-md hover:bg-orange-500 focus:outline-none focus:ring-2 focus:ring-white"
>
<span className="sr-only">Dismiss</span>
<XIcon className="h-6 w-6 text-white" aria-hidden="true"/>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default CustomerNotice;
If this is not possible please suggest me a possible way.
Instead of doing a bunch of hacks, I would recommend simplifying the structure of your components.
import { useState } from 'react'
export default function CustomerDashboard() {
// I am not sure why you want to keep notice in state,
// because in your example you did not call setNotice
const [notice, setNotice] = useState(`
Customer Notice: Optimism Is Invaluable For The Meaningful Life.
With A Firm Belief In A Positive Future You Can Throw Yourself Into The Service
Of That Which Is Larger Than You Are. -Martin Seligman
`)
const [isNoticeVisible, setIsNoticeVisible] = useState(true)
return (
<div>
<h1>My Account - Dashboard Overview</h1>
{isNoticeVisible && (
<CustomerNotice
notice={notice}
setIsNoticeVisible={setIsNoticeVisible}
/>
)}
</div>
)
}
function CustomerNotice(props) {
const { notice, setIsNoticeVisible } = props
const [isTruncated, setIsTruncated] = useState(true)
function toggleIsTruncated() {
setIsTruncated(!isTruncated)
}
return (
<div>
<Read text={notice} isTruncated={isTruncated} />
<button onClick={toggleIsTruncated}>
{isTruncated ? 'Read More' : 'Read Less'}
</button>
<button onClick={() => setIsNoticeVisible(false)}>Dismiss</button>
</div>
)
}
function Read(props) {
const { text, isTruncated } = props
const result = isTruncated ? text.slice(0, 90) : text
return <div>{result}....</div>
}
List of the things that were bad in your code.
Keeping the component instance in the state. It is hard to manage. Even your simple case proves that.
Keeping the toggleIsTruncated function inside the Read component. I think we should keep it outside and pass only 2 props to the Read component. I enable exposed only two things
const { text, isTruncated } = props
As you can see it is easy to maintain and allow us to do whatever we want.
PS. If my review and example were helpful please leave the thumbs up.

Categories

Resources