How to make queries in Jest test within context of particular element? - javascript

I'm writing tests in Jest for a React app. Let's say I have a web page that contains multiples of a certain element. In the below case, I have two buttons. I want to make queries in my test about the button that is inside of the div element with a test ID of 2.
Is there a way for me to query for that button within the context of that parent div? Something like:
const secondDiv = screen.getByTestId(2);
const buttonOfInterest = secondDiv.getByRole('button', {name: 'Button'});
I've tried the above before, but it doesn't work.
I know that I could assign test ID's to the buttons, but I think it'd be very helpful in other scenarios to be able to make queries within contexts of particular elements (instead of using screen, which refers to the entire document).

You can use “within” helper function:
import { screen, within } from '#testing-library/dom';
const someDiv = screen.getByTestId('some-id');
const childButton = within(someDiv).getByRole('button');

In addition to genechk's answer:
If you are writing tests for react app you better use
import { screen, within } from '#testing-library/react'
instead of
import { screen, within } from '#testing-library/dom';

Related

Any way to use React refs with methods on the window object that require DOM elements?

I'm writing a React app that embeds lists of Tweets that it pulls from an API. I'm aware that there already exists react-twitter-embed and react-twitter-widgets but both of these proved to be very slow, both in terms of load time and visual performance when scrolling, compared to Twitter's own twttr javascript widgets.
The only problem with using Twitter's widget is that, in order to implement lazy loading such that the app does not actually make the call to Twitter's embed API until the div containing the Tweet is visible on the screen, it seems I have to do some direct DOM references which I understand is not good to do in React.
The "React-flavored" version I attempted first looked something like this:
Tweet component
const tweetDOMId = `twt-${tweet.twtId}`
const divDOMId = `div-${tweet.twtId}`
const tweetRef = createRef()
<div id={divDOMId} ref={tweetRef}>
<blockquote
id={tweetDOMId}
className='twitter-tweet'
data-dnt='true'
data-theme='dark'>
<a href={twtUrl}></a>
</blockquote>
</div>
Parent component
const loadTweet = (tweetRef) => {
window.twttr.widgets.load(tweetRef)
window.twttr.events.bind('rendered', (event) => {
setLoadedTweet(event.target.(...).tweetId)
})
}
However, the Twitter widgets.load() method was unable to find the appropriate element to turn into an embedded tweet this way. Instead, I had to write it like this:
Tweet component
const tweetDOMId = `twt-${tweet.twtId}`
<blockquote
id={tweetDOMId}
className='twitter-tweet'
data-dnt='true'
data-theme='dark'>
<a href={twtUrl}></a>
</blockquote>
Parent component
const loadTweet = (elementId) => {
window.twttr.widgets.load(document.getElementById(`${elementId}`))
window.twttr.events.bind('rendered', (event) => {
setLoadedTweet(event.target.(...).tweetId)
})
}
So I am wondering if there is any way to not use document.getElementById() in this situation? A way to supply window.twttr.widgets.load() with a reference to the element that it needs without querying the DOM?
Using document.getElementById() is what Twitter recommends, but that example in the link is clearly for vanilla js, not React.
You seem to be trying to load the ref directly, instead of ref.current.

Appending JSX in React Native

I'm searching for a way to add a JSX element programmatically in React Native. Not conditionally.
It needs to be independent function, so far I couldn't find anything about this. Let me give you code example;
const appendJSX = () => {
const jsx = <Text> Hello! </Text>
append(jsx) // <---- can we do something like this?
}
let's say I call this function with useEffect and it should add whatever jsx I have inside the function. Up until this point I always see things like pushing inside of an array or something like that.
UPDATE
Equivalent behaviour that works on web;
useEffect(() => {
const div = document.createElement("div");
div.innerText = "appended div"
document.body.append(div)
}, [])
As you can see we don't have to touch any JSX in application. Reaching document.body and appending whatever we want is possible in React Web. But how can we achieve this in React Native?
Not quite sure what you want to do, but as for to add a JSX manually. Here's the answer.
JSX is already part of the language in most of the cases.
const a = <Text />
export default a
Will translates into:
const a = createElement(Text, null, null)
export default a
Therefore in most of common cases, if you continue using React somewhere else, then the variable a holds a React element without any compilation error.
You might wonder what a actually really holds, it's an object:
const a = {
$$typeof: Symbol(ReactElement),
props: null,
type: Text
}
So you can see the only dependencies in above piece is Text and the ReactElement Symbol. As long as you can resolve them, you are good to export this to anywhere. The latter is normally taken care by Babel.
NOTE:
There's a difference between Text and <Text />. If you just want to export a Text which is a function component, there'll tutorial online, also you can dig into any third party library, because essentially that's what they do, export Text so other people can use it.

Calling "document.getElementByID" in React Component vs. "ref"

I am trying to open a modal by changing the style properties of the modal, so it is visible. I works perfectly when I write a function like this:
modalAction(action) {
switch (action) {
case "open": {
const modal = document.getElementById("login-popup");
modal.style.opacity = 1;
modal.style.visibility = "visible";
}
}
}
Yet I have never seen react code like this. Insead I read a lot about refs that use Code like this:
<div className="login-popup" ref={(ref) => {this.loginPopup = ref}} id="login-popup">
But as far as I see it using refs will only alow me to access a virtual DOM elements in the same (or nested) components. The modal is a component of itself and is not nested it is calles in.
I could solve my problem using redux but writing a reducer-case as well as an action and producing an additional piece of state seems overkill for my sort of problem.
How can I access the modals style properties the right way if the modal is neither nested nor in the same component where it is called from? Thx

Aurelia #children with dynamic data

Suppose I have a custom element in which I iterate over an array of data and render custom subelements.
<my-list action-handler.bind="actionHandler">
<my-element item.bind="element" repeat.for="element of data"></my-element>
</my-list>
In my-list.html I render the contents with <contents></contents> and in general the real code is a bit more complex and has replaceable template parts, but that's not the problem.
I want the actionHandler that I bind to MyList to be available in all of the children. For the sake of elegancy, we did this in my-list.js:
#children('*:not(div)') listItems;
//...
attached(){
this.listItems.forEach((e) => {
e.actionHandler = this.actionHandler;
});
}
And everything worked just fine until we started to load data dynamically. In this case listItems is empty since the element is initialized before the child elements re-rendered.
So, the question is:
How would I make #children be re-calculated?
I understand that we could bind the actionHandler to all children when we were repeating over them, but this would start to look really ugly when we add some more logic like
Thanks!
If <my-element> has a dependency on it's parent <my-list> element's actionHandler, you can express that declaratively and let Aurelia's dependency injection system do the rest. Here's a couple of ways to do that...
If <my-element> will always be within a <my-list> (doesn't necessarily need to be directly within):
import {inject} from 'aurelia-dependency-injection';
#inject(MyList)
export class MyElement {
constructor(list) {
this.actionHandler = list.actionHandler;
}
}
If you need access <my-element>'s closest parent custom element, regardless of what it is:
import {inject, Parent} from 'aurelia-dependency-injection';
#inject(Parent.of(Element))
export class MyElement {
constructor(parent) {
this.actionHandler = parent.actionHandler;
}
}
I know this wasn't exactly what you were asking but I'm proposing this in case it's a simpler way to meet your requirements.
The Aurelia Cheat Sheet is telling that:
#children(selector) - Decorates a property to create an array on your class that has its items automatically synchronized based on a query selector against the element's immediate child content.
so i guess it is not possible to use #children with dynamic data.
You might should go with the Parent approach, like the previous answer is suggesting.

How can I access child components values from a parent component when they are added dynamically?

Current Working Example
I am creating a search form that has a varying number of input elements based on the users selection from a select box.
I have broken this up into three components, a wrapper called SearchContainer, a select box called SearchSelect, and the inputs within components called SearchWithTwo and SearchWithOne just for the sake of the example.
App
└─SearchContainer Form
│ SearchSelect
│ ... any one of the multiple search inputs (SearchWithOne, SearchWithTwo)
When a user changes the value of the select box the related component which contains the inputs is loaded. The component could have anywhere from one to ten inputs. All the examples I've seen mention using ref which would be great if my inputs weren't changing.
I currently have it working by using the following in the onSubmit handler for SearchContainer
handleSubmit: function(e) {
e.preventDefault();
var form = this.getDOMNode();
[].forEach.call(form.elements, function(e){
// get the values
});
// submit the values to get results.
}
However this doesn't feel like the proper way to be doing this. Is there a better recommended way to iterate through the children components and read their state? Or can I somehow pass the children into the parent state and get the values that way?
I think I have a solution in the form of a fork of your fiddle, and I'll cover the main ideas below.
First, I'm no React expert, but I like the idea of it, and I know it's gaining popularity so I want to learn more. What I don't know is the right way to use composition or inheritance to reduce the code duplication shown below.
Basically, my idea is to add a method to each search class that exposes its state to calling classes. This is implemented here as a very simple function inside the createClass call:
getData: function() {
return this.state;
},
It's so simple, there has to be a way to create a base class or mixin class with this method and then inherit/compose over it with other classes. I'm just not sure how. For now, you can just copy-paste these lines wherever it makes sense to expose a component's state.
Keep in mind, this is anathema to the Flux architecture everyone loves. In Flux, state always comes from a source object which lives outside the React components.
Anyway, abandoning larger architecture concerns for now, you can just grab that state variable by calling getData in the handleSubmit method. No DOM traversal required:
handleSubmit: function(e) {
e.preventDefault();
var form = this.getDOMNode(),
fd = new FormData(form);
var submitData = this.state.SearchBox.getData();
// submit the values to get results.
},

Categories

Resources