How to behaviour test a Component which uses React Bootstrap with Jest - reactjs

In pseudo code:
MyComponent: React.createClass
doThis: () ->
//do something
render: () ->
<div>
<button className='something' onClick={clickHandler()}>click this button</button>
<ReactBootstrap.Pagination onSelect=(this.doThis) items=3 />
</div>
//tests
component = TestUtils.renderIntoDocument <MyComponent>
//test1
el = TestUtils.findRenderedDOMComponentWithClass component, 'something'
TestUtils.Simulate.click el
//test2
el = TestUtils.srcyRenderedDOMComponentsWithTag component, 'li'
TestUtils.Simulate.click el[0]
In test1 the click is fired. In test2 doThis() is not called
In both cases I definitely have a dom node and am firing the click on it. onSelect is the correct prop to use to pass to ReactBoostrap.Pagination. It works fine in the browser.
The Pagination class in Bootstrap uses onClick and seems to attach it to the li element it renders so I think I am targeting the correct element. (Edit: looking at Bootstrap-react's test for the Pagination component that targets the a tag which is rendered inside the li https://github.com/react-bootstrap/react-bootstrap/blob/master/test/PaginationSpec.js. However; I tried that too so I don't think that is my problem).
It seems to be to do with trying to target a dom node rendered by a child component. But I have no idea how to proceed. (Edit: or perhaps it is specific to react-bootstrap? Maybe I need to not mock some dependency...?)

The answer, for me, was to not mock 'classnames' - which is a dependency of react-bootstrap:
jest.dontMock 'classnames'
The answer was given by: http://racingtadpole.com/blog/test-react-with-jest/ so thanks to racingtadpole.

Related

#testing-library/React : Clicking outside of component not working

I'm using react testing library to test my component built with FluentUI.
Here is link:
https://codesandbox.io/s/keen-borg-2tqmj?file=/src/App.spec.js
The code is basically a pasted snippet of the example code of Dialog component from FluentUI docs site. The behavior that I'm testing is:
User opens the dialog
User clicks outside of the dialog
onDimiss prop of the component should be fired.
It works when I'm playing with it manually however it seems that I failed to simulate a click outside of the component with testing library.
I tried using userEvent.click(document.body) as mentioned in this post but got no luck
Does anyone has any idea how to make test work?
It is not working because the Dialog component is not listening for the onClick event on the body, so what you need to do in this case is to find the actual element that is being clicked, observing the dom you'll find that the overlay is a div with some overlay classes on it.
<div
class="ms-Modal is-open ms-Dialog root-94"
role="document"
>
<div
aria-hidden="true"
class="ms-Overlay ms-Overlay--dark root-99"
/>
The problem now is to find a way to select it. Unfortunately, you cannot select elements in RTL by their className, so you need to use another selector; in this case, we can get the parent element by the role and then access the first child.
const onDismiss = jest.fn();
const { getByRole } = render(<App onDismiss={onDismiss} />);
UserEvent.click(screen.getByText("Open Dialog"));
const document = getByRole("document");
UserEvent.click(document.firstChild);
expect(onDismiss).toHaveBeenCalled();
https://codesandbox.io/s/hungry-joliot-tjcph?file=/src/App.spec.js

reactJS adding an event listener to a navbar tile

I am running the current version of reactJS with hooks. I have three code modules in my app: header.js which creates a navbar and exports it to app.js which adds some other objects and exports all of this to index.js.
I am trying to add an event listener to the individual tiles in the navbar so that I can redirect to the appropriate page.
code
var listenerElement = document.getElementById("Tile1");
if (listenerElement !== null) {
listenerElement.addEventListener("click", navbarClicked) ;
console.log(listenerElement);
} else {
console.log("Element with ID=Tile1 not found");
}
<div id="Tile1" className="linkcontainer">Home</div>
/code
However, I cannot find an appropriate place to add the event-listener and the element with ID "Tile1" is never found - perhaps because it hasn't been rendered as yet?
The element in question is only rendered by index.js but I can't add a function after the reactDOM.render block in index.js - I get an error "not a react function"
Any suggestions would be much appreciated :-)
For react, you should use "refs" to link to a specific aspect of the navabar.
But as you are just trying to have something affect the navbar, you should use the "onClick" property for the div or iconButton or similar.
for example in a function component:
function handleClick() {
console.log("clicked");}
<nav> <iconButton onClick={handleClick} > button </iconButton> </nav>
https://reactjs.org/docs/handling-events.html
In react, you don't work with the DOM directly. React makes a copy of the DOM called virtual DOM and then compares them to update the DOM. You should add your event listener using props.
So instead of:
document.getElementById("Tile1").addEventListener("click", navbarClicked);
you should do <div onClick={navbarClicked} id="Tile1" className="linkcontainer">Home</div>

Newly created preact Component retains UI state of old component (different from react)

I have a simple Preact component which contains a checkbox:
class Cb extends Component {
render() {
return (<div>Checkbox: <input type="checkbox" /></div>);
}
}
In a parent Component, this Cb is conditionally added like this:
{ this.state.show ? <Cb /> : <div>Nothing</div> }
Now for the strange part: If you follow these steps:
Check the checkbox
Toggle state.show in the parent Component, removing the Cb
Toggle state.show in the parent Component again, creating a new Cb
Then the newly created checkbox is still checked!
How is this possible? The checkbox is truely removed and a completely new Cb instance is created (I checked using log messages in the constructor). Where is this state stored?
Additional weirdness: The behavior is different in React (there, the newly created checkbox is not checked).
Here are two Codepens with the same code in Preact and React, where you can compare the behaviors.
This has to do with how Preact recycles components. There is a GitHub issue along the same lines as what your question is. Now to fix the problem you will have to reset the checked value in componentWillUnmount
componentWillUnmount () {
this.cb.checked = false;
}
Where this.cb is a ref to the checkbox.
Working Codepen with the modification.
I made the checkbox a ref because there is another issue with componentWillUnMount and using querySelector (and its also shorter to type)
Edit: As commented this is only valid for preact 8.x. preact X removes component recycling.

Webpack & Css-loader. How to create dynamic className for React?

The project has the following reference, which returns a string:
const left = slide.imageLeft; // introLeft
And further renders it inside React Component. But it returns as a string styles.imageLeft and since webpack doest convert it into corresponding bundled class like 914u923asdsajdlj1l23 the styles are not applied.
<div className={`styles.${left}`}> </div>
P.S I did try to eval, but it drops 2 errors.
There is an internal error in the React performance measurement code. Did not expect componentDidMount timer to start while render timer is still in progress for another instance.
And
ReferenceError: styles is not defined
Can you please suggest the possible ways to achieve dynamic class generation for css-loader.
You have to define the style within the render(), or within the component definition, like this
render: function(){
var myStyle = {
// your style rules go here
};
return(
<div style={myStyle}>
{this.props.children}
</div>
)
}
in a way, this is already dynamic, because all you have to do is to change to style and it'll make sure that the component will re-render on update

React - how to do jQuery style find() to find children of a DOM node?

I've got a component that returns this:
return (
<div className="audio-widget">
<div
className={this.props.className}
onWheel={this.handleWheel}
/>
{controls}
</div>
)
I need to do the equivalent of:
handleWheel(event) {
let $canvas = $('.audio-widget').find('canvas');
[...]
}
The canvas is drawn programatically by a 3rd party script, so I can't just slap an ID on it (especially since this is a component and there are several per page).
Excuse the extreme n00b question, I'm brand new to React. Thanks!
This is a good use case for the ref prop. If you give a component a ref prop, e.g. ref="foo", upon rendering that component will be available as e.g. this.refs.foo. Then you can get the rendered DOM node with React.findDOMNode.
render() {
// ...
return (
<div ref="audioWidget" className="audio-widget">
<div
className={this.props.className}
onWheel={this.handleWheel}
/>
{controls}
</div>
);
}
handleWheel(event) {
let canvas = React.findDOMNode(this.refs.audioWidget)
.querySelector('canvas');
// ...
}
You can learn more about refs here: https://facebook.github.io/react/docs/more-about-refs.html
I think, it's a bad way to direct access 'canvas' with selector, the reason is as follows:
When you component changed, the React will do some diff works, then update your dom with a best way, that means, the node you get with selector may be changed to another node just for 'the best way'.
You can build a 'Child Component', and do something in the 'handleWheel' of the 'Parent Component', then communicate with the 'Child Component' through the 'props' for 'Child Component'

Resources