How can I test a popover from chakra-ui - reactjs

I need to test a popover from chakra-ui in a React App.
I tried with this. But It does not find the popover. If I try by text, then I cannot assert if it is visible.
it('show a popover when hover terms and conditions', () => {
render(<SummaryForm />);
const link = screen.getByText(/terms and conditions/i);
const popover = screen.getByRole('dialog');
expect(popover.parentNode).not.toBeVisible();
userEvent.click(link);
expect(popover.parentNode).toBeVisible();
});

Try using the hidden option of the API:
const popover = screen.getByRole('dialog', {hidden: true})
ChakraUI renders a wrapper div around the section that has the dialog role. You can see this by using screen.debug() if you are using the testing-library. Notice the wrapper controls the visibility of said section, which starts as hidden, with styling elements and aria tags.
Using the hidden option allows you to look amognst the elements that aren't visible in the accessibility tree.
Since you want to test the popover, you should know there are some issues with modifying and checking the visibility of the popover when using jest-dom.

The chakra Modal applies a transform style to toggle its visibility. toBeVisible only checks a limited set of style attributes - transform is not one of them - so you might have to check for those instead, for example:
For invisibility:
expect(screen.getByRole('dialog')).toHaveStyle('transform: translateX(0px) translateY(0.18967%) translateZ(0);')

try toBeInTheDocument() or toMatchSnapshot()

Related

How can i write a test using React Testing Library to check if hover changes the components style?

I am testing my application, and encountered a problem. When trying to test whether a row in my Dropdown component applies an effect on hover, I noticed I was not able to check elements for their background color, which I find odd.
Trying to use the jest-dom matcher "toHaveStyle()", the following is an example where I cannot for the life of me get it to work.
dropdown.test.tsx
test('Should contain clickable elements that change style when hovered', () => {
const dropElement1 = screen.getByLabelText('testLabel1');
expect(dropElement1).toHaveStyle('background: white');
});
Error
I have also tried this by using 'background-color', by using the hex value (another interesting bug is that PrettyDom converts hex to RGB), or by adding ; to the declaration in toHaveStyle().
I am certain that the element is indeed white, and I can't understand what is going wrong. If my approach is bad practice and you have a better idea of how to check this, or you have a solution to my problem, please, let me know!
Your testing case can't find the dropElement1 styles because it's a drop-down menu and not opened since you just render the Dropdonw component.
You need to simulate a mouse hover or clicking action on the DropDown menu and then expect to have styles property for it.
import React from "react";
import { render, screen, fireEvent, waitFor } from "#testing-library/react";
import { Dropdown } from "./Dropdown";
test('Should contain clickable elements that change style when hovered', async () => {
render(<Dropdown />);
fireEvent.mouseEnter(screen.getByText('drop-down-btn'));
await waitFor(() => screen.getByTestId('dropdown-menu'))
expect(screen.getByLabelText('testLabel1')).toHaveStyle('background: white');
});
Note: as you have not posted the Dropdown component, I put some sample names for getting your toggles and drop-down menu. also, you can read about the mouse events on the react-testing-library. you can also use mouseOver but it depends on your drop-down menu implementation.

#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

React change class of component without using state

How can I change the className or style of a div without using state or any third party libraries? Lets say I click on a button, and I need to change the background color of a div how can I do that?
<Affix onChange={() => change css or class} offsetTop={60}>
<div>...</div> // Change css of this div
</Affix>
You can change any attribute or property of a Component (Element) in React by using basic javascript functions.
onClick={(e) => {
e.currentTarget.setAttribute("src", newUrl);
}
Will change an image the moment you click on it, without using Ref or State.
event.currentTarget will give you the reference to the component that fired that particular React.MouseEventHandler event, and with the Element's reference, you can manipulate it at will.
This is particularly useful when you need to change an attribute in a component in a map loop without needing to keep track of it.
Edit:
A friend of mine just gave me a better one for classes in specific:
e.currentTarget.classList.add('my_custom_klass')
You can either do it manually using state:
const [myClass, setMyClass] = useState('bgColor-white');
return (
<Affix onChange={() => setMyClass('bgColor-black')} offsetTop={60}>
<div className={myClass}>...</div> // Change css of this div
</Affix>
)
Or you can use a library that handles dynamic styling. I use and recommend styled-components

React global click track handler

I am new to react and working on a legacy codebase. Am wondering if we can write a global button click handler for click tracking.
The jQuery equivalent of this would be something like,
utilities.js :
var subscribeForClickTracking = function() {
$( "button" ).click((event) => { console.log($(event.target).html()) })
$( "p" ).click((event) => { console.log($(event.target).html()) })
}
In all the html files, will add reference to utiliies.js and this snippet of code.
$(document).ready(function () {
subscribeForClickTracking();
});
I have surfed about this and reached similar so questions, like
Higher Order React Component for Click Tracking
and https://www.freecodecamp.org/news/how-to-track-user-interactions-in-your-react-app-b82f0bc4c7ff/
But these solutions involve modifying the button's implementation, which would lead to huge change. (For a html form with 50+ buttons).
Is there an alternate approach to achieve something similar to the above jQuery approach.
Thanks in advance.
No, you can not do that. The reason is that React prevents you from doing global things to avoid side effects.
I think the proper React way would be to create your own Button component.
First create a new component :
export default Button = (props) => <button ...props />
Then, you can import and use Button instead of button in any component.
Then in your Button component, you can override your onClick method like this :
<button
...props
onClick={() => {
// doWhatYouWantHere;
props.onClick()
/>
However, as React is JavaScript, you can still use vanilla JavaScript to attach an event to every button

ReactJS & Material UI - ClickawayListener is not working properly in the Shadow DOM

I'm trying to build Micro Front End applications using ReactJS and Material UI framework. As a part of it, I was trying to embed a react application into the main React App using ShadowDOM.
I got the application running and working except when opening a popover, dialogs, modal, or date picker the ClickAwayListener is not functioning as expected meaning not closing.
Please suggest a way to fix this or show me a workaround to get the application running.
Code Sandbox
Found a workaround:
First, create a click listener on your shadow root to trigger a CustomEvent called closeModal.
const shadowRoot = document.getElementById('root').attachShadow({open: true});
let mountPoint = document.createElement('div');
mountPoint.id = "portal";
mountPoint.addEventListener('click', (e) => {
let event = new CustomEvent('closeModal',{bubbles: true, cancelable: false});
shadowRoot.dispatchEvent(event);
});
ReactDOM.render(themeProvider, mountPoint);
Then, when the popover or modal or date picker or dialog opens, create another event listener
document.getElementById('root').shadowRoot.addEventListener('closeModal', this.handleClose);
Once the modal is closed by the handleClose function, remove the event listener
document.getElementById('root').shadowRoot.removeEventListener('closeModal', this.handleClose);
Thats it.
I don't see where ClickAwayListener is being used in your CodeSandbox link, but I would guess that this issue is because you didn't wrap the inner element in an element that can accept a ref. Try wrapping the child element in a <div> and see where that gets you. :)

Resources