Test onClick function using Jest within internal component - javascript

I have a parent component for a navigation bar and would like to test an imported Toggle component that acts as a button with an onClick prop.
const Header = ({ brandLogo, links }: HeaderProps) => {
const [navOpen, setNav] = useState(false);
return (
<Container>
<div className='toggle-container'>
<Toggle active={navOpen} onClick={() => setNav(!navOpen)} />
</div>
<div className={navOpen ? 'menu open' : 'menu closed'}>
{links ? (
<ul>
{links.map(link => (
<li key={uuid.v4()} className='page-links'>
<a href={link.href}>{link.textKey}</a>
</li>
))}
</ul>
) : null}
<div className='menu-buttons'>
<Button text='Log In' />
<Button text='Register' />
</div>
</div>
{brandLogo ? (
<a className='brand-logo' href={brandLogo.href}>
{brandLogo.image}
</a>
) : null}
{links ? (
<ul className='links-container'>
{links.map(link => (
<li key={uuid.v4()} className='page-links'>
<a href={link.href}>{link.textKey}</a>
</li>
))}
</ul>
) : null}
<div className='button-container'>
<Button text='Log In' />
<Button text='Register' />
</div>
</Container>
);
};
export default Header;
Segment I would like to test
<Toggle active={navOpen} onClick={() => setNav(!navOpen)} />
I have seen much documentation on testing the onClick functionality on the parent component but not on a child component.
current tests im running are -
describe('Footer Component', () => {
it('Component should render with all config set', () => {
const { asFragment } = render(<Header {...config} />);
expect(asFragment()).toMatchSnapshot();
});
it('Component should only render links if they are present', () => {
const { asFragment } = render(<Header {...onlyLinks} />);
expect(asFragment()).toMatchSnapshot();
});
it('Component should only render links if they are present', () => {
const { asFragment } = render(<Header {...onlyBrandLogo} />);
expect(asFragment()).toMatchSnapshot();
});
});
Any help would be appreciate thanks :)

Related

How to toggle item by id or index ? React.js

I need to opet child component by clicked item. FIrst check code:
<div className="d-flex">
{boardList.map((list) => (
<div className="card m-3 p-3" key={list.id}>
<div className="d-flex flex-column">
<h6> {list.name} </h6>
<ul className="list-group">
{list.cards.map((card) => (
<li className="list-group-item" key={card.id}>
{card.name}
</li>
))}
</ul>
{isVisible ? (
<TodoForm onCloseForm={onCloseForm} />
) : (
<small
className="mt-2"
onClick={showInput}
>
Add new task +
</small>
)}
</div>
</div>
))}
</div>
This is work but when I click on 'Add new task +' a child component opens up to me everywhere. i want only the component with the selected id or index to open.
also component for this :
const [isVisible, setIsVisible] = useState(false);
const [boardList, setBoardList] = useState([]);
useEffect(() => {
axiosInstance
.get("")
.then((res) => {
setBoardList(res.data);
console.log("resp", boardList);
})
.catch((err) => {
console.log(err);
});
}, []);
const showInput = () => {
setIsVisible(true);
};
const onCloseForm = () => {
setIsVisible(false);
};
All the items of the resultant array from boardList.map are depending on the same state isVisible, that's why when you click on one of them all the items mimic the same behaviour.
What you need is to create a component with its own state to encapsulate this part of your code
{isVisible ? (
<TodoForm onCloseForm={onCloseForm} />
) : (
<small
className="mt-2"
onClick={showInput}
>
Add new task +
</small>
)}
This way every instance of this new component would have its own isVisible so they no longer would affect their siblings state.
The component would look like this.
const NewComponent = () => {
const [isVisible, setIsVisible] = useState(false);
return <>
{isVisible ? (
<TodoForm onCloseForm={onCloseForm} />
) : (
<small className="mt-2" onClick={() => setIsVisible(true)}>
Add new task +
</small>
)}
</>
};

How to pass a property from a react component to its parent?

I am creating a "presentation" component with multiple sections, each rendered dynamically.
In the parent component which houses all the different children, I want the "next button" disabled for each part until a certain condition has been met. The button lives in the parent component.
This component does not pass the property:
Child one example:
export function ChildOne() {
const [condition, setCondition] = useState(false);
return (
<div>
<button onClick={() => setCondition(true)}>
hello world
</button>
</div>
);
}
Parent:
import ChildOne, condition from "../child-one"
export default function Parent() {
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne />
)}
</div>
<button isDisabled={condition}>Next</button>
);
}
I'm not sure how to pass the condition property from the child component so that I can use it in the parent component. In addition, is this methodology an anti-pattern? Can I conditionally make the button in the parent disabled based on values from the child component in another way?
Thank you.
try this way
child:
export function ChildOne({setCondition}) {
return (
<div>
<button onClick={() => setCondition(true)}>
hello world
</button>
</div>
);
}
Parent:
import {ChildOne} from "../child-one"
export default function Parent() {
const [condition, setCondition] = useState(false);
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne setCondition={setCondition} />
)}
</div>
<button isDisabled={condition}>Next</button>
);
}
You should use a state in parent component to control disabled for steps. It can use when you have other pages.
export default function Parent() {
const [condition, setCondition] = useState({});
const changeCondition = (val) => {
setCondition({...condition, [page]: val})
}
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne changeCondition={} />
)}
</div>
<button isDisabled={!condition[page]}>Next</button>
);
}
export function ChildOne({changeCondition}) {
return (
<div>
<button onClick={() => {changeCondition(true)}}>
hello world
</button>
</div>
);
}
You could pass the onClick fucntion as a props param.
Child
export function ChildOne({onClick}) {
return (
<div>
<button onClick={onClick}>
hello world
</button>
</div>
);
}
Parent
import ChildOne, condition from "../child-one"
export default function Parent() {
const [condition, setCondition] = useState(false);
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne onClick={() => setCondition(true)} />
)}
</div>
<button isDisabled={condition}>Next</button>
);
}
in your Parent component try this :
import ChildOne, condition from "../child-one"
export default function Parent() {
const [condition, setCondition] = useState(false);
const handleClick = () => setCondition(true)
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne handleClick={handleClick} />
)}
</div>
<button isDisabled={condition}>Next</button>
);
}
and in use children :
export function ChildOne({handleClick}) {
return (
<div>
<button onClick={handleClick}>
hello world
</button>
</div>
);
}

Curly braces as function parameter

I want to pass emailID as the second parameter to . Can you help me to understand how to pass additional parameter in Curly braces as a function parameter and how to access it in AccountMenuSidebar.
Sorry for asking this basic question.I am a newbie to Javascript and React.
class Invoices extends Component {
render() {
var emailID="guest#somedomain.com";
const accountLinks = [
{
text: 'Account Information',
url: '/account/user-information',
icon: 'icon-user',
},
{
text: 'Notifications',
url: '/account/notifications',
icon: 'icon-alarm-ringing',
},
];
return (
<section className="ps-my-account ps-page--account">
<div className="container">
<div className="row">
<div className="col-lg-4">
<div className="ps-page__left">
<AccountMenuSidebar data={accountLinks} /> // Want to pass email id as second argument here
</div>
</div>
</div>
</div>
</section>
);
}
}
export default Invoices;
const Accountbar = ({ data }) => (
<aside className="ps-widget--account-dashboard">
<p>{email}</p>
<div className="ps-widget__content">
<ul>
{data.map((link) => (
<li key={link.text} className={link.active ? 'active' : ''}>
<Link href={link.url}>
<a>
<i className={link.icon}></i>
{link.text}
</a>
</Link>
</li>
))}
</ul>
</div>
</aside>
);
export default Accountbar;
<AccountMenuSidebar data={accountLinks} email={emailID} />
and
const Accountbar = (data , emaildID) => (...
or
const Accountbar = (props) => (...
and then you can use props like this...
<ul>
{props.data.map((link) => (
<li key={link.text} className={link.active ? 'active' : ''}>
<Link href={link.url}>
<a>
<i className={link.icon}></i>
{link.text}
</a>
</Link>
</li>
))}
</ul>
When you pass the props from Invoices, you usually acces them like this in AccountMenuSidebar:
<AccountMenuSidebar data={accountLinks} />
const AccountMenuSidebar = (props) => {
return (
<p>{props.data}</p>
)
}
However, using destructuring, which lets you directly unpack variables from an object in JavaScript, you can access the props like this instead:
<AccountMenuSidebar data={accountLinks} />
const AccountMenuSidebar = ({ data }) => {
return (
<p>{data}</p>
)
}
So if you want to send another prop, you can access it the same way, i.e.
<AccountMenuSidebar data={accountLinks} email={email} />
const AccountMenuSidebar = (props) => {
return (
<>
<p>{props.data}</p>
<p>{props.email}</p>
</>
)
}
or using destructuring:
<AccountMenuSidebar data={accountLinks} email={email} />
const AccountMenuSidebar = ({ data, email }) => {
return (
<>
<p>{data}</p>
<p>{email}</p>
</>
)
}

How to update parent's component using state from child component in React?

I have 3 components.
In ListCard.js, I map cards array and based on the card the user click on, I call handleChangeCardData to update the modal's text.
My question is: How do I update/change the modal's text when my handleChangeCardData function is inside ListCard.js and my modal is on the same level. (Both are in Board.js)
Board.js
const [cardTitle, setCardTitle] = useState("");
return (
{columns.map((column, index) => (
<div className="column__container" key={index}>
<div className="column__header">
<div className="columnHeader__name">
<p>{column.name ? column.name : "..."}</p>
</div>
<div className="columnHeader__button">
<button
className="btn btn-sm --create-card-btn"
data-bs-toggle="modal"
data-bs-target="#modal-card"
onClick={() => setColumnId(column.id)}
>
New item
</button>
</div>
</div>
<Droppable droppableId={column.id}>
{(provided, snapshot) => (
<div
className="column"
ref={provided.innerRef}
{...provided.droppableProps}
>
<ListCard columnId={column.id} />
{provided.placeholder}
</div>
)}
</Droppable>
</div>
))}
<ViewCardModal cardTitle={cardTitle} />
)
LisCard.js
const handleChangeCardData = (cardTitle) => {
setCardTitle(cardTitle);
}
return (
{cards.map((card, index) => (
<>
<div key={index}>
<Draggable draggableId={card.id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<div
className="card --listcard-card"
onClick={() => handleChangeCardData(card.title)}
data-bs-toggle="modal"
data-bs-target="#modal-card-details"
style={{ border: `2px solid ${card.color}` }}
>
<div className="card-body">
<p>{card.title}</p>
</div>
</div>
</div>
)}
</Draggable>
</div>
</>
))}
)
ViewCardModal.js
function ViewCardModal(props) {
return (
<div>{props.cardTitle}</div>
)
}
In general, lift state up. In this case, it sounds like that means moving the state into Board and then passing that state to whatever child components need it (as a prop), and the state setter to whatever (other) child components need it.
Here's a minimal example of lifting state up. I haven't tried to recreate the full complexity of your example, just to provide an example of Parent having state that ChildA uses and ChildB sets:
const {useState} = React;
const ChildA = React.memo(({counter}) => {
console.log("ChildA rendered");
return <div>Counter = {counter}</div>;
});
const ChildB = React.memo(({setCounter}) => {
console.log("ChildB rendered");
return <input
type="button"
value="Increment Counter"
onClick={() => setCounter(c => c + 1)}
/>;
});
const Parent = () => {
const [counter, setCounter] = useState(0);
return (
<div>
<ChildA counter={counter} />
<ChildB setCounter={setCounter} />
</div>
);
};
ReactDOM.render(<Parent />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.development.js"></script>
If there are several levels of hierarchy between where the state is being held and a descendant component that needs it, you might use context instead of props (although you might also look at component composition instead). See those links for details.
you cant do that directly, but must use props.
in list:
onClick={() => props.onClick(card.title)}
in board:
handleChangeCardData = (cardTitle) => {
setCardTitle(cardTitle);
}
<ListCard columnId={column.id} onClick={(e)=>handleChangeCardData(e)}/>
Inside ListCard:
const ListCard = ({setCardTitle}) => {...}
onClick={() => setCardTitle(card.title)}
In the parent:
<ListCard columnId={column.id} setCardTitle={setCardTitle} />

Hiding items in React through states

So I want when I press the button in the Button Component everything in the 'li section' disappears as well as in the ImageComponent but it not working I would like to know what my mistake is. ButtonComponent is rendered somewhere else.
App Component/Parent
function App({ hideButton }) {
return (
<div className="App">
<ImageComponent hideButton={hideButton} />
</div>
);
}
// ButtonComponent
function ButtonComponent() {
const [hideButton, setHideButton] = React.useState(false)
function handleClick() {
setHideButton(true)
}
return (
{
!hideButton && (
<li>
<img className="image"src="./icons/>
<Button onClick={handleClick} variant="outlined" className="button__rightpage" >Hide</Button>
<caption className="text"> Hide</caption>
</li >
)
}
)
}
// ImageComponent
const ImageComponent = ({ hideButton }) => {
return (
<div>
{
!hideButton && (
<div>
<img src='icons/icon.png' />
<caption>Image </caption>
</div>
)
}
</div>
)
}
you need to lift up the state to the most parent Component be accessible to the ButtonCommponent and the ImageComponent. in this Case App Component. however, if the ButtonComponent is rendered out of the hierarchy under the App Component tree, you should use the context API.
create a context and share the state on it and it will be accessible on the application level.
//#1. create context.
export const HiddenContext = React.createContext(false);
//#2. create the provider and share the state with it.
function HiddenProvider({ children }) {
const [hideButton, setHideButton] = React.useState(false);
function handleClick() {
setHideButton(true);
}
return (
<HiddenContext.Provider value={{ hideButton, handleClick }}>
{children}
</HiddenContext.Provider>
);
}
//#3. render the provider component to the most top parent component
export default function App() {
const { hideButton } = React.useContext(HiddenContext);
return (
<HiddenProvider>
<div className="App">
<ImageComponent hideButton={hideButton} />
<OtherComponentRenderTheButton />
</div>
</HiddenProvider>
);
}
// other component that render the button
function OtherComponentRenderTheButton() {
return <ButtonComponent />;
}
//ButtonComponent
function ButtonComponent() {
const { hideButton, handleClick } = React.useContext(HiddenContext);
return (
<React.Fragment>
{!hideButton && (
<li>
<img className="image" src="./icons" alt="" />
<Button
onClick={handleClick}
variant="outlined"
className="button__rightpage"
>
Hide
</Button>
<caption className="text"> Hide</caption>
</li>
)}
</React.Fragment>
);
}
// ImageComponent
const ImageComponent = () => {
const { hideButton } = React.useContext(HiddenContext);
return (
<div>
{!hideButton && (
<React.Fragment>
<img src="icons/icon.png" alt="" />
<caption>Image </caption>
</React.Fragment>
)}
</div>
);
};
working demo

Categories