How to use simulate(click) function in react unit test - reactjs

I am beginner level in React test.
I have a component like a small carousel. I want to test if change state(currentPage) when i click. But i didn't. It throws that error message:
Method “simulate” is only meant to be run on a single node. 0 found
instead.
I didnt understand " what is node,which node ? "
That is my test code :
it("check if next button works properly", () => {
const wrapper = shallow(<QuickStartGuide />);
const nextButton = wrapper.find(".quick-start-guide-messages-control-button .next-button");
nextButton.simulate('click');
expect(wrapper.state().currentIndex).toEqual(1);
})
And component :
<div className={containerClassName}>
{isOpen &&
<div className={"quick-start-guide-content-wrapper"}>
<div className="quick-start-guide-header">
<MtButton text={"MINIMIZE"}
hasIcon={true}
onClick={this.handleTriggerButtonClick}
className={"quick-start-guide-minimize-button"}/>
<div className={"quick-start-guide-title"}>{"Quick Start Guide"}</div>
{!!guideMessages.length &&
<Fragment>
<div className={"quick-start-guide-messages"}>
<MtButton className={"quick-start-guide-messages-control-button prev-button"}
hasIcon={true}
onClick={this.handleGoToPrevMessageButtonClick}
isDisabled={!currentGuideMessageIndex}
text={"PREV"}/>
{guideMessages[currentGuideMessageIndex][0]}
<MtButton className={"quick-start-guide-messages-control-button next-button"}
hasIcon={true}
onClick={this.handleGoToNextMessageButtonClick}
isDisabled={currentGuideMessageIndex >= guideMessages.length - 1}
text={"NEXT"}/>
</div>
<div className={"quick-start-guide-indicators"}>
{guideMessages.map((item, index) => (
<IndicatorItem key={item[0].key}
order={index}
onClick={this.handleIndicatorItemClick}
className={classNames({
active: index === currentGuideMessageIndex
})}/>
))}
</div>
</Fragment>}
</div>
<div className={"quick-start-guide-content"}>
<div className={"quick-start-guide-action-buttons"}>
{guideActions.map(([text, handleActionButtonClick, isDisabled = false]) => (
<MtButton key={text}
text={text}
isDisabled={isDisabled}
visualType={BUTTON_VISUAL_TYPES.ROUNDED}
className={"quick-start-guide-action-button"}
onClick={handleActionButtonClick}/>
))}
</div>
</div>
</div>}
<MtButton className={"uick-start-guide-trigger-button"}
text={"GUIDE"}
onClick={this.handleTriggerButtonClick}
hasIcon={true}/>
</div>

The error you're receiving isn't related to the simulate(click) and is an issue with your selector because it's not finding anything to actually click.
Double check that this is the correct selector to use: wrapper.find(".quick-start-guide-messages-control-button .next-button");.
You can use console.log(wrapper.debug()) to check to see what elements are available to select.

Related

set first box clicked as a default at the first load

Im new in reactjs and I create a list of card by mapping in reactjs but I want my first card be clicked as a default at the first load what can i do for this code.
<div className="d-flex">
{data && data.length > 0
? data.map((item, index) => {
return (
<>
<div className="box-stock" onClick={() => selectData(item)}>
<div className="top-stock skewed p-5">
<h1>{item.symbol}</h1>
<strong className="text-center">
<span> (0.25)</span>
{item.stockNum}
</strong>
<Label className="text-center">
EPS:<span className="text-white">{item.EPS}</span>
</Label>
<Label className="text-center">
P/E:<span className="text-white">{item.PE}</span>
</Label>
</div>
</div>
</>
);
})
: 'no data'}
</div>
You can use useEffect hook to run program once it renders. Here, in useEffect pass empty array as a dependency so, that it runs only once.
useEffect(() => {
selectData(data[0])
}, [])
You can add click event on first load using javascript. The button will trigger the event on first load and you will get your first card clicked. Here is the code:
document.getElementsByClassName("box-stock").click()

React - Each child in a list should have a unique 'key' prop

As my first react project, I decided to try and make a Pokedex.
I have an array of Pokemon and that I pass into a List component and use the .map() function to render. I understand that the root-most element of that the .map() function returns needs a unique key and I understand that it is ideal if the key can be truly unique to the item so that if the list is sorted, items have the same key. Therefore, I figured using the 'id' of the pokemon would be ideal. I believe I have done that but I cannot get rid of the warning in console. I'd really appreciate a hand with this.
export default class List extends React.Component {
render() {
const { list, nav } = this.props;
return (
<div className="list">
{list.map((pokemon) => (
<PokemonItem key={pokemon.id} navigation={nav} pokemon={pokemon} />
))}
</div>
);
}
}
PokemonItem Render Method
render() {
const { pokemon, navigation } = this.props;
return (
<div onClick={() => {
navigation.navigate("Details", { pokemon });
}}
className={"list-item bg_" + pokemon.types[0]}>
<div className="header">
<div className="name">{pokemon.name}</div>
<div className="id">#{this.getId(pokemon.id)}</div>
</div>
<div className="body">
<div className="types">
{pokemon.types.map((type) => {
return <div className="type">{type}</div>;
})}
</div>
<div className="sprite">
<img src={pokemon.imgURL} alt={pokemon.name} title={pokemon.name}></img>
</div>
</div>
<div className="background-image">
<img src={PkBall} alt="" />
</div>
</div>
);
}
Warning message showing in console
Checking your PokemonItem it reveals that the reason may be laying in this piece of code:
{pokemon.types.map((type) => {
return <div className="type">{type}</div>;
})}
This is easily fixed by adding the key attribute:
{pokemon.types.map((type) => {
return <div className="type" key={type.id}>{type}</div>;
})}
You need to add a key in every item returned from a map in order to avoid this error. Also I advice you to add the console output related to your question in the body so it's easier to pinpoint the errors.
After the edit of the OP's question the warning occurs here:
<div className="types">
{pokemon.types.map((type) => {
return <div className="type">{type}</div>;
})}
</div>
The key-property is not set for div and should be done like in the first method. If type is unique you can use this as key.

Unexpected Behavior After State Change in React Component

RenderImages = (): React.ReactElement => {
let selected = this.state.results.filter(x=>this.state.selectedGroups.includes(x.domain))
console.log(selected)
return(
<div className="results_wrapper">
{selected.map((r,i)=>{
let openState = (this.state.selectedImage==i)?true:false;
return(
<RenderPanel panelType={PanelType.large} openState={openState} title={r.domain+'.TheCommonVein.net'} preview={(openIt)=>(
<div className="result" onClick={openIt} style={{ boxShadow: theme.effects.elevation8}}>
<img src={r.url} />
</div>
)} content={(closeIt)=>(
<div className="panel_wrapper">
<div className="panel_content">{r.content}</div>
{this.RenderPostLink(r.domain,r.parent)}
<div onClick={()=>{
closeIt();
this.setState({selectedImage:2})
console.log('wtfff'+this.state.selectedImage)
}
}>Next</div>
<img src={r.url} />
</div>
)}/>
)
})}
</div>
)
}
When I change the state of 'selectedImage', I expect the variable 'openState' to render differently within my map() function. But it does not do anything.
Console.log shows that the state did successfully change.
And what is even stranger, is if I run "this.setState({selectedImage:2})" within componentsDidMount(), then everything renders exactly as expected.
Why is this not responding to my state change?
Update
I have tried setting openState in my component state variable, but this does not help either:
RenderImages = (): React.ReactElement => {
let selected = this.state.results.filter(x=>this.state.selectedGroups.includes(x.domain))
console.log(selected)
let html = selected.map((r,i)=>{
return(
<RenderPanel key={i} panelType={PanelType.large} openState={this.state.openState[i]} title={r.domain+'.TheCommonVein.net'} preview={(openIt)=>(
<div className="result" onClick={openIt} style={{ boxShadow: theme.effects.elevation8}}>
<img src={r.url} />
</div>
)} content={(closeIt)=>(
<div className="panel_wrapper">
<div className="panel_content">{r.content}</div>
{this.RenderPostLink(r.domain,r.parent)}
<div onClick={()=>{
closeIt();
let openState = this.state.openState.map(()=>false)
let index = i+1
openState[index] = true;
this.setState({openState:openState},()=>console.log(this.state.openState[i+1]))
}
}>Next</div>
<img src={r.url} />
</div>
)}/>
)
})
return(
<div className="results_wrapper">
{html}
</div>
)
}
https://codesandbox.io/s/ecstatic-bas-1v3p9?file=/src/Search.tsx
To test, just hit enter at the search box. Then click on 1 of 3 of the results. When you click 'Next', it should close the pane, and open the next one. That is what I'm trying to accomplish here.
#Spitz was on the right path with his answer, though didn't follow through to the full solution.
The issue you are having is that the panel's useBoolean doesn't update it's state based on the openState value passed down.
If you add the following code to panel.tsx, then everything will work as you described:
React.useEffect(()=>{
if(openState){
openPanel()
}else{
dismissPanel();
}
},[openState, openPanel,dismissPanel])
What this is doing is setting up an effect to synchronize the isOpen state in the RenderPanel with the openState that's passed as a prop to the RenderPanel. That way while the panel controls itself for the most part, if the parent changes the openState, it'll update.
Working sandbox
I believe it's because you set openState in your map function, after it has already run. I understand you think the function should rerender and then the loop will run once more, but I think you'll need to set openState in a function outside of render.
The problem is that even though you can access this.state from the component, which is a member of a class component, there's nothing that would make the component re-render. Making components inside other components is an anti-pattern and produces unexpected effects - as you've seen.
The solution here is to either move RenderImages into a separate component altogether and pass required data via props or context, or turn it into a normal function and call it as a function in the parent component's render().
The latter would mean instead of <RenderImages/>, you'd do this.RenderImages(). And also since it's not a component anymore but just a function that returns JSX, I'd probably rename it to renderImages.
I tire to look at it again and again, but couldn't wrap my head around why it wasn't working with any clean approach.
That being said, I was able to make it work with a "hack", that is to explicitly call openIt method for selectedImage after rendering is completed.
RenderImages = (): React.ReactElement => {
let selected = this.state.results.filter((x) =>
this.state.selectedGroups.includes(x.domain)
);
return (
<div className="results_wrapper">
{selected.map((r, i) => {
let openState = this.state.selectedImage === i ? true : false;
return (
<RenderPanel
key={i}
panelType={PanelType.medium}
openState={openState}
title={r.domain + ".TheCommonVein.net"}
preview={(openIt) => {
/* This is where I am making explicit call */
if (openState) {
setTimeout(() => openIt());
}
/* changes end */
return (
<div
className="result"
onClick={openIt}
style={{ boxShadow: theme.effects.elevation8 }}
>
<img src={r.url} />
</div>
);
}}
content={(closeIt) => (
<div className="panel_wrapper">
<div className="panel_content">{r.content}</div>
{this.RenderPostLink(r.domain, r.parent)}
<div
onClick={() => {
closeIt();
this.setState({
selectedImage: i + 1
});
}}
>
[Next>>]
</div>
<img src={r.url} />
</div>
)}
/>
);
})}
</div>
);
};
take a look at this codesandbox.

React toggle button based on API return

I am currently working on a React application.
As you can see in the following code, there are two buttons. I would like to show the grey button if the user hasn't created a Request (default). If the user has created a Request the button should be green.
Problem:
The main issue is, that the map function (API) no returning any value for "no Requests" so I am not able to identify "no Requests". That means that .isEmpty,.length, .indexOf,... and also the "if-else" is not working because there is nothing to validate.
const greenButton = (
<Button color="green" onClick={e => DeleteRequest(e, rest.id)}>Request</Button>
);
const greyButton = (
<Button color="grey" onClick={e => CreateRequest(e, props.reservationID)}>Request</Button>
);
return (
<div>
{greyButton}
{requests.map((rest, i) => (
<div key={i}>
{rest.requester === username
? <div>{greenButton}{rest.requester} <i
aria-hidden="true"
className="delete icon"
onClick={e => DeleteRequest(e, rest.id)} />
</div> : <div />}
</div>
))}
</div>
);
Result in the UI:
API:
Any Ideas? (If you need more information, I am happy to provide more details)
conditional rendering
Use a ternary on requests being a defined/truthy object and has a truthy length property. In the true branch map the requests, grey button in false branch. This covers requests being either initially (or returned from the API) undefined or an empty array [].
return (
<div>
{requests && requests.length ? requests.map((rest, i) => (
<div key={i}>
{rest.requester === username
? <div>{greenButton}{rest.requester} <i
aria-hidden="true"
className="delete icon"
onClick={e => DeleteRequest(e, rest.id)} />
</div> : <div />}
</div>
)) : (
{greyButton}
)}
</div>
);

How to render my Modal window and all the information contained inside ( in React)?

My application renders twelve random people fetched from a different website. Everything works fine apart from my modal component(it should render more information about the person you clicked). For some reason whenever I try to render it I get this error 'Modal.js:9 Uncaught TypeError: Cannot read property 'medium' of undefined' and more errors comes with it. I am printing props.modalInfo from the Modal component to the console and it does have all the information I need, but for some reasons it shows that props.modalInfo is undefined when I try to render it. I have never done modal box in React (I am a beginner). Could someone explain me how I can render my Modal and pass all the data successfully? Thank you in advance!
handleClick(id) {
this.setState((prevState) => {
const modalInfoToPass = prevState.employeeList.filter(employee =>
{
if(`${employee.name.first} ${employee.name.last}` === id){
// get only and only one object that fulfils the
// condition
return employee;
}
})
return {
displayModal: true,
// update the modalInfo state
modalInfo: modalInfoToPass
}
})
}
render(){
return (
<div className='container'>
<Header />
<main>
{
this.state.loading ? <h2 className='load-page'>Loading...</h2> :
this.state.employeeList.map(employee =>
<Employee key={`${employee.name.title}
${employee.name.last}`}
employeeInfo={employee}
**handleClick={this.handleClick}**
/>)
}
</main>
<Footer />
**{this.state.displayModal && <Modal modalInfo={this.state.modalInfo} />}**
</div>
);
}
function Modal(props) {
**console.log(props.modalInfo);**
return (
<div className='bg-modal'>
<div className='modal-content'>
<div className='modal-image'>
<img src={props.modalInfo.picture.medium} alt={`${props.modalInfo.name.title} ${props.modalInfo.name.first}`}/>
</div>
<div className='modal-info'>
<p className='name'>{props.modalInfo.name.first} {props.modalInfo.name.last}</p>
<p className='email'>{props.modalInfo.email}</p>
<p className='place'>{props.modalInfo.location.city}</p>
</div>
<hr />
<div className='modal-more-info'>
<p className='number'>{props.modalInfo.cell}</p>
<p className='address'>{`${props.modalInfo.location.street}, ${props.modalInfo.location.state}`}</p>
<p className='postcode'>{props.modalInfo.location.postcode}</p>
<p className='birthday'>{props.modalInfo.dob.date}</p>
</div>
</div>
</div>
);
}
What is id and is it on an employee? If it isn't available, you could just pass what you're filtering for in your handleClick:
handleClick={()=>this.handleClick(`${employee.name.first} ${employee.name.last}`)}
Or, you could just pass the employee:
handleClick={()=>this.handleClick(employee)}
and modify your handler:
handleClick(employee) {
this.setState({modalInfo: employee, displayModal: true})
}

Resources