onClick inside of a <img - reactjs

Not able to perform a test for a click event that is inside of an image tag.
Here is the code that I am trying to test it. Not able to test both click events due to setState
<td className="centertext">
<input type='radio' name='req' onClick={() => {this.handleRequestClick(titles[i][j].RequestId, titles[i][j].Name, titles[i][j].Content)}} />
</td>
<td className="modal-add-window-td">{i}</td>
<td className="modal-add-window-td">
<div>{titles[i][j].Name}</div>
</td>
<td className="modal-add-window-td">
<img className='request-info'src={infoSVG} alt='Info' onClick={() => {this.showDescription(titles[i][j].Name, titles[i][j].Description)}} />
</td>
Here is what I have tried to so far but getting the following msg:
Method “props” is meant to be run on 1 node. 0 found instead.
Using Jest and enzyme - memory router and mount the components
it("Test click event on show Description", () => {
wrapper.find('AddViewModal').setState({
titles:[[{
Requests:{
Name:""
}}
]]
}),
wrapper.update();
expect(wrapper.find('AddViewModal').find('img').props().src).toEqual('../../Images/chartImages/info.svg')
//wrapper.find('AddViewModal').find('img[alt="Info"]').simulate('click')
});

did you try findWhere?
element.findWhere(node => node.hasClass("request-info"))
If it doesn't, can you verify if findWhere traverses the node you're targeting?

Make sure you get the right state otherwise node wont be found
it("Test click event on show Description", () => {
baseProps.onClick.mockClear();
wrapper.find('Component').setState({
Requests: [{
Name: 'testing'
}]
})
wrapper.update();
wrapper.find('Componentl').find('img').simulate('click')

Related

React Testing Library toBeInTheDocument failing despite being in the DOM

I have a test which basically contains the following code:
const row = await screen.findByText('Test Subject');
try {
expect(row).toBeInTheDocument();
} catch (e) {
screen.debug();
throw e;
}
The debug is for trying to track this down. findByText succeeds, and does find the element. I can log this out, and it appears. But when I do the expect, it fails SOMETIMES. It's unpredictable. I'm pretty certain the DOM isn't changing between calls, as I have logs out on every render and nothing logs out between the find and the assert.
I won't print the whole DOM tree, but on the catch debug, I can clearly see the row.
<tr
class="sc-jgrIVw bDPrDC"
>
<td
class="sc-jeqYYF hinrdE"
>
<div
class="sc-clIAKW jvtlES"
>
05-Dec-2022 12:08
</div>
</td>
<td
class="sc-jeqYYF ijUrhn"
>
<div
class="sc-clIAKW jvtlES"
>
<div
title="Test"
>
Test
</div>
</div>
</td>
<td
class="sc-jeqYYF ijUrhn"
>
<div
class="sc-clIAKW jvtlES"
>
Test Subject
</div>
</td>
</tr>
So as far as I can tell, RTL knows my DOM is there, the DOM log shows its there, but toBeInTheDocument throws. What could I be missing?

How to add and remove dynamic rows with "Select-Drop down", "Text Field", "Text-Area auto size" in React JS- Functional component

I am new to React JS. I need to develop the add or remove the control dynamically (rows).
I have two "Text field" control, one "Select (Dropdown)" control, "Text Area " control, and one button (Add button) conrol in the first row.
When I click the Add button in the second row is automatically added with first row controls. (Text field, select, text area, button). The first-row value has not changed and the second-row field is empty. How can I do this in react js functional component with React material UI with yup validation (controller-based component)?
I got some sample code in the code sandbox. But, Code is written in-class components without yup validation and without use state. I attached the link below:
https://codesandbox.io/s/3vk7jxv69p
import React from "react";
import { render } from "react-dom";
class App extends React.Component {
state = {
rows: [{}]
};
handleChange = idx => e => {
const { name, value } = e.target;
const rows = [...this.state.rows];
rows[idx] = {
[name]: value
};
this.setState({
rows
});
};
handleAddRow = () => {
const item = {
name: "",
mobile: ""
};
this.setState({
rows: [...this.state.rows, item]
});
};
handleRemoveRow = () => {
this.setState({
rows: this.state.rows.slice(0, -1)
});
};
handleRemoveSpecificRow = (idx) => () => {
const rows = [...this.state.rows]
rows.splice(idx, 1)
this.setState({ rows })
}
render() {
return (
<div>
<div className="container">
<div className="row clearfix">
<div className="col-md-12 column">
<table
className="table table-bordered table-hover"
id="tab_logic"
>
<thead>
<tr>
<th className="text-center"> # </th>
<th className="text-center"> Name </th>
<th className="text-center"> Mobile </th>
<th />
</tr>
</thead>
<tbody>
{this.state.rows.map((item, idx) => (
<tr id="addr0" key={idx}>
<td>{idx}</td>
<td>
<input
type="text"
name="name"
value={this.state.rows[idx].name}
onChange={this.handleChange(idx)}
className="form-control"
/>
</td>
<td>
<input
type="text"
name="mobile"
value={this.state.rows[idx].mobile}
onChange={this.handleChange(idx)}
className="form-control"
/>
</td>
<td>
<button
className="btn btn-outline-danger btn-sm"
onClick={this.handleRemoveSpecificRow(idx)}
>
Remove
</button>
</td>
</tr>
))}
</tbody>
</table>
<button onClick={this.handleAddRow} className="btn btn-primary">
Add Row
</button>
<button
onClick={this.handleRemoveRow}
className="btn btn-danger float-right"
>
Delete Last Row
</button>
</div>
</div>
</div>
</div>
);
}
}
render(<App />, document.getElementById("root"));
I expected the same output but, I want to use a functional component with yup validation (React Material UI and Controller-based component). who knows this answer let me know.
How can I do this in react js functional component with React material UI with yup validation
It sounds like there are a number of things you could benefit from learning more about before copy-pasting some code you find here, but I hope this helps.
"in react js functional component"
For this, I'd recommend this quick read from Digital Ocean. Essentially, you need to understand that instead of state and lifecycle events being managed within the class, you will need to implement React Hooks. With the component you have here, this should be very easy once you've read the two links.
"with React material UI"
This just requires changing out the HTML/CSS table with Material UI components. A simple replacement would be keeping the table structure, but using MUI form elements like their button and text field. If you are looking for a fuller and more robust update, I'd also recommend MUI Data Grid.
"with yup validation"
This really should just require installing it in your project, and implementing it exactly as the docs say. You will just have to define the schema and check that all rows are valid via something like const tableIsValid = Promise.all(state.map(row => schema.isValid)).every(x => x), though you will want to split the results up by row rather than executing them all at once if you'd like to inform the user which row is invalid.

Links to external resource are empty in React

In a React app I have a Table tbody of which looks the following way:
<tbody>
{
this.state.datasets.map((datasetName, idx) => {
return (<tr>
<td>
<div className={"pretty p-default p-curve"}>
<input id = {"datasetCheckBox" + idx}
checked = {this.state.checkBoxSelected === datasetName}
type="radio" name="datasetName"
onChange={(e) => this.checkBoxSelected(datasetName, e)}/>
<div className={"state p-primary"}>
<label>{idx}</label>
</div>
</div>
</td>
<td>{datasetName}</td>
<td>{datasetsJSON["datasets"][datasetName]["region"]}</td>
<td>{datasetsJSON["datasets"][datasetName]["authors"]}</td>
<td>
<a href={datasetsJSON["datasets"][datasetName]["link"]}>{datasetName}</a>
</td>
</tr>)
}, this)
}
</tbody>
The link is shown with an empty href even though other parameters of the table from datasetsJSON["datasets"][datasetName] are set correctly. I tried using React Router and the only piece of code that redirected to links is in the answer here:
React-Router External link
But in the answer to the question above if I set path="/", when the table loads, it is just redirecting me straight away to the first link. I would expect then that for each link the path should be different, but supplying datasetsJSON["datasets"][datasetName]["link"] as a path does not work either. Why is the simple link a empty? How could I fix it?
I solved it by using onClick method instead of href link attribute. In the table I have:
<td onClick = {() => this.articleClicked(datasetName)}>
<span role="button"><a>{datasetName}</a></span>
</td>
Then, onClick method:
articleClicked = (datasetName) => {
let url = datasetsJSON["datasets"][datasetName]["link"];
window.open(url);
}
<span role="button"> is needed for the cursor to show hand when the user is hovering over the link. It is bootstrap thing.

How to pass VideoId from a list to React Modal Video

I am trying to play youtube video in a modal window on button click in my react app. I am using "react-modal-window" node module for the modal. I am getting an array of youtube video Ids and passing the ids to modal in the following way but only the last id from the array is taken by the modal window. However if I try to print the video id, am getting respective ids. Can any one please help in fixing this?
<tr>
<th></th>
{filteredProductVideoId.map(k =>
<td key={k}>VideoID: {k}
<ModalVideo channel='youtube' isOpen={this.state.isOpen} videoId={k}
onClose={() => this.setState({isOpen: false})} />
<button onClick={this.openModal}>Watch Video</button>
</td>)
}
</tr>
To view the output, please click this link -https://7jrz33j35x.codesandbox.io/ Select all four products and click compare button. A tabular view of comparison displays. In the last row there is a "watch video" button for each product. Beside the button, respective id got displayed(for debugging purpose) but on clicking watch video, the video with last id from the array is played
pass the clicked button video id via state.. like below
this.state = {
//use this props inside modal video view
currentVideoId : null
}
this.updateVideoId(currentVideoId){
this.setState({currentVideoId})
}
//your jsx change button onClick like below
<tr>
<th></th>
{filteredProductVideoId.map(k =>
<td key={k}>VideoID: {k}
<ModalVideo channel='youtube' isOpen={this.state.isOpen} videoId={k}
onClose={() => this.setState({isOpen: false})} />
<button onClick={()=>{this.updateVideoId(k)}}>Watch Video</button>
</td>)
}
</tr>

react ref with focus() doesn't work without setTimeout (my example)

I have encounter this problem, the .focus() only works with setTimeout if i take it out and it stop working. can anyone explain me what's the reason for that, possible i am doing it incorrectly and how can i fix the problem.
componentDidMount() {
React.findDOMNode(this.refs.titleInput).getElementsByTagName('input')[0].focus();
}
works example with setTimeout
componentDidMount() {
setTimeout(() => {
React.findDOMNode(this.refs.titleInput).getElementsByTagName('input')[0].focus();
}, 1);
}
JXS
<input ref="titleInput" type="text" />
and i have followed this example React set focus on input after render
render function
render() {
const {title, description, tagtext, siteName} = (this.state.selected !== undefined) ? this.state.selected : {};
const hasContentChangedYet = this.hasContentChangedYet(title, description);
return (
<div>
<h2 className={styles.formMainHeader}>Edit Meta-Data Form</h2>
<table className={styles.formBlock}>
<tbody>
<tr>
<td className={styles.tagEditLabel}>
Tag
</td>
<td className={styles.inputFieldDisableContainer}>
{tagtext}
</td>
</tr>
<tr>
<td className={styles.tagEditLabel}>
Site
</td>
<td className={styles.inputFieldDisableContainer}>
{siteName}
</td>
</tr>
<tr>
<td className={styles.tagEditLabel}>
Title
</td>
<td className={styles.inputFieldContainer}>
<ReactInputField
ref="titleInput"
id="title"
defaultValue={(title) ? title : ''}
onChange={this.onInputChange}
placeholder="Title"
clearTool={true} />
</td>
</tr>
<tr>
<td className={styles.tagEditLabel}>
Description
</td>
<td className={styles.inputFieldContainer}>
<ReactInputField
id="description"
defaultValue={(description) ? description : ''}
onChange={this.onInputChange}
placeholder="Description"
clearTool={true} />
</td>
</tr>
</tbody>
</table>
<div className={styles.formFooter}>
<button id="save-button" className={styles.saveButton} disabled={!hasContentChangedYet} onClick={() => this.handleSavePressed()}>
Save
</button>
<button id="form-cancel-button" className={styles.cancelButton} onClick={this.actions.form.cancelUpdateToTagData}>
Cancel
</button>
</div>
</div>
);
}
After seeing the update to the question, I realise that you have deeply nested HTML passed to the render function, and the input element of your interest will indeed not be available at the time of the componentDidMount call on the ancestor element. As stated in the React v0.13 Change Log:
ref resolution order has changed slightly such that a ref to a component is available immediately after its componentDidMount method is called; this change should be observable only if your component calls a parent component's callback within your componentDidMount, which is an anti-pattern and should be avoided regardless
This is your case. So either you have to break down the HTML structure into separately rendered elements, as described here, and then you would access the input element in its own componentDidMount callback; or you just stick with the timer hack you have.
Use of componentDidMount makes sure the code runs only when the component on which it is called is mounted (see quote from docs further down).
Note that calling React.findDOMNode is discouraged:
In most cases, you can attach a ref to the DOM node and avoid using findDOMNode at all.
Note
findDOMNode() is an escape hatch used to access the underlying DOM node. In most cases, use of this escape hatch is discouraged because it pierces the component abstraction.
findDOMNode() only works on mounted components (that is, components that have been placed in the DOM). If you try to call this on a component that has not been mounted yet (like calling findDOMNode() in render() on a component that has yet to be created) an exception will be thrown.
And from the docs on the ref string attribute:
Assign a ref attribute to anything returned from render such as:
<input ref="myInput" />
In some other code (typically event handler code), access the backing instance via this.refs as in:
var input = this.refs.myInput;
var inputValue = input.value;
var inputRect = input.getBoundingClientRect();
Alternatively, you could eliminate the need of the code, and use the JSX autoFocus attribute:
<ReactInputField
ref="titleInput"
autoFocus
... />
Using setTimeout() is a bad idea and using componentDidMount() is irrelevant. You may find the answer to your question in the following example:
In a parent component I render a primereact Dialog with an InputText in it:
<Dialog visible={this.state.visible} ...>
<InputText ref={(nameInp) => {this.nameInp = nameInp}} .../>
...
</Dialog>
Initially, this.state.visible is false and the Dialog is hidden.
To show the Dialog, I re-render the parent component by calling showDlg(), where nameInp is the ref to InputText:
showDlg() {
this.setState({visible:true}, ()=>{
this.nameInp.element.focus();
});
}
The input element gets the focus only after rendering has been accomplished and the setState callback function called.
Instead of using the setState callback, in some cases you may simply use:
componentDidUpdate(){
this.nameInp.element.focus();
}
However, componentDidUpdate() is being called every time you (re)render the component, including in case the InputText is hidden.
See also: https://reactjs.org/docs/react-component.html#setstate

Resources