jest shallow render find by class excluding - reactjs

I am testing a class like:
const wrapper = shallow(<Features {...props}/>);
expect(wrapper.find('.Feature__Item:not(.Feature__Showmore)').length).toBe(2);
But, I am getting an error like this:
Error: Enzyme::Selector does not support pseudo-element or pseudo-class selectors.
How can I work around this limitation?
Update: Interesting observation, when I wrote the following in the debug console: wrapper.find('.Feature__Item').findWhere(a => a.hasClass('Feature__Showmore') === false).length
The output was:
0:Object {nodeType: "host", type: "li", props: Object, …}
1:Object {nodeType: "function", type: , props: Object, …}
2:Object {nodeType: "host", type: "li", props: Object, …}
3:Object {nodeType: "function", type: , props: Object, …}

I found another way which did the trick:
expect(wrapper.find('.Feature__Item').not('.Feature__Showmore').length).toBe(2);

You could try with findWhere:
expect(wrapper.find('.Feature__Item').findWhere(a => a.hasClass('Feature__Showmore') === false).length).toBe(2);
This will first get all the elements having class Feature__Item and then exclude from this list the ones which don't have Feature__Showmore class.

Related

TypeScript - How to infer a type from array of objects

I am building a React form component with TypeScript and want to type the data returned by my form.
My component accepts a prop called "fields" with the following structure:
const fields = [
{
name: "title",
default: "",
data: undefined,
size: 2
},
{
name: "users",
default: [],
data: [{id: 1, ...}]
size: 8
},
]
I would like to find a way to retrieve the type of the data returned by component based on the "field" variable. So, basically, I want to get something like that:
type FormData = {
title: string;
users: Array<{id: number, ...}>
}
The best solution would be to infer a different type depending on the "data" key. If "data" exists, then the field will have the same type as data, otherwise it is the type of the "default" key. All other keys should be ignored.
I believe I should be using generics to achieve this but I am not even sure this is something possible with TS and I have to admit that I can't figure it out...
Has anyone already faced this situation and found a solution to a similar issue ?
Thank you very much!
Based on the given array fields, we can create the type FormData with the following generic type as long as the fields variable was initialized with as const to stop TypeScript from widening the string literals.
const fields = [
{
name: "title",
default: "",
data: undefined,
size: 2
},
{
name: "users",
default: [],
data: [{id: 1}],
size: 8
},
] as const
type FormData<T extends readonly { name: st
[E in T[number] as E["name"]]: E["data"]
? E["default"]
: E["data"]
}
type Result = FormData<typeof fields>
// type Result = {
// title: "";
// users: readonly [{
// readonly id: 1;
// }];
// }
This might or might not work in the context of your component. But you did not show us the component itself. This is a solution based on the information given in your question.
Playground

Changing the Text of a ReactNative <Text/> component programmatically without using state

Hello StackOverflow Members,
I have a parent react-native component <Cell/> and inside it there is a child <Text/> component. The child component has to be a <Text/> component, i can not change it to another component. See example below...
let renderedCell = (<Cell>
<Text>HELLO PEOPLE</Text>
</Cell>)
I want to programmatically change "HELLO PEOPLE" to "Hello World" without using state via a callback. I have "inspected" the child component and i get this using console.log(renderedCell.props.children)...
{
$$typeof: Symbol(react.element)
type:
$$typeof: Symbol(react.forward_ref)
render: ƒ Text(props, forwardedRef)
displayName: "Text"
propTypes: {ellipsizeMode: ƒ, numberOfLines: ƒ, textBreakStrategy: ƒ, …}
__proto__: Object
key: null
ref: null
props:
style: (3) [(...), (...), (...)]
children: "HELLO PEOPLE"
__proto__: Object
_owner: FiberNode {tag: 1, key: null, stateNode: Table, elementType: ƒ, …}
_store: {validated: true}
_self: null
_source: {fileName: "component.js", lineNumber: 146, columnNumber: 17}
__proto__: Object
}
I have tried, to change the text programmatically from "HELLO PEOPLE" to "Hello World" like this:
renderedCell.props.children.props.children = "Hello World"
but the above does not work... I have tried cloning the element and other things... All I need is to change the text inside the component programmatically on the fly without using states from a callback.
Any suggestions? Is this permitted? Any other way to do it and accomplish the same?
TIA!
Instead of Text component use
<TextInput editable={false} defaultValue={"HELLO PEOPLE"} ref={(ref)=> this.textInputRef=ref} />
then you can directly manipulate the TextInput like this
if(this.textInputRef){
this.textInputRef.setNativeProps({text:"Change Text Here"})
}
Note: You can't directly manipulate a <Text> because it uses an NSTextStorage and not an NSString property. If there was a way to convert a string value to an NSTextStorage value, then we could also manipulate the Text field, but to my knowledge, we can't.

Why do I get TypeError: _this.data.forEach is not a function

I am trying to retrieve data from the backend. These are the relevant parts of my code:
API call
getData(PrimaryId:number):Observable<DataDto[]>{
return this.httpClient.get(`${this.prefix}/<xyz>/${PrimaryId}/xyz`) as Observable<DataDto[]>
}
Component TypeScript
onRetrieveClicked() {
this.xyzService.getData(this.PrimaryId).subscribe(
(xyz: DataDto[]) => {
this.xyz = xyz
console.log(this.xyz)
console.log(this.xyz.forEach((data)=>data.name)
})
}
First console.log output
{content: Array(1), pageable: {…}, totalPages: 1, totalElements: 1, last: true, …}
content: Array(1)
0: {name: max, name: null, asset: null, …}
length: 1
..........
But when I try to print only the name in the second console, it says that forEach is not a function. How can I solve this
edit
Dto model
export interface DataDto {
name: string
asset: abcDto
status: StatusDto
tasks: efgDto[]
nextDate: string
}
The xyz variable that you type as DataDto[], an array, is actually an object. This can be seen in your console.log, an array would be enclosed in [], not {}
is an object --> {
content: Array(1), pageable: {…}, totalPages: 1, totalElements: 1, last: true, …}
content: Array(1)
0: {name: max, name: null, asset: null, …}
length: 1
}
The data you are looking for is most likely the response object's content so add an import for import {map} from 'rxjs/operators'; and transform the data you've gotten from the response:
this.xyzService.getData(this.PrimaryId).pipe(
map((xyzResponse: any) => xyzResponse.content)
).subscribe(
(xyz: DataDto[]) => {
this.xyz = xyz;
console.log(this.xyz);
let dataNames = xyz.map(data => data.name);
console.log(dataNames);
}
I've typed xyzResponse as any but you could ofcourse create a reusable type for it if the API always returns the object with content, pageable, totalPages, ...
Rxjs is the library that Angular uses to handle asynchronous programming, such as HTTP calls or component events. Rxjs chains asynchronous manipulations together in a pipe (hence the .pipe call). Inside of this pipe rxjs expects a chain of operators that will perform operations on the asynchronous data, one by one. The map operator takes the input value and returns a new value so that the value you subscribe to has been transformed from the HTTP response to the .content field of the HTTP response.
Working in this way fixes all TypeScript compiler errors and allows you to chain additional calls later, like retrying if the API times out, or catching errors, or merging in other HTTP calls.
It seems that your this.xyz is not an array, but has an array property called content, you should modify your response object in order to accept it.
You can check if your objects are arrays with the following method
Array.isArray(obj)
Update your code to this.
this.xyzService.getData(this.PrimaryId).subscribe(
(xyz: NewObject) => {
this.xyz = xyz
console.log(this.xyz)
//If you have doubts of what is comming is nice to check if your property is an array
if(Array.isArray(this.xhy.content) {
console.log(this.xyz.content.forEach((data)=>data.name) });
}
}
Create a new object in order to support your response
class NewObject {
content: Array<DataDto>
// other values here
}
Another approach is like #Robin says in the comment
this.xyzService.getData(this.PrimaryId).subscribe((xyz: {content: DataDto[]}) =>
{
this.xyz = xyz
console.log(this.xyz)
//If you have doubts of what is comming is nice to check if your property is an array
if(Array.isArray(this.xhy.content) {
console.log(this.xyz.content.forEach((data)=>data.name) });
}
}
It's because you are trying to loop through an object instead of array
I think you can try this:
console.log(this.xyz.content.forEach((data)=>data.name) })

Reactjs # symbol in parameter: item.enclosure.#attributes how to fix, that this is possible?

I have a .map function in my code and Im getting data from a json output but one of the item parameters is: #attribute but I cant have an # symbol in the parameter so is there any way I can fix this?
I coudn't really find anything about this because I'm not sure what to search for except for # symbol in parameter.
this is the code I used to get the data:
this.state.news.channel.item.map(function (item, i) {
console.log(item.enclosure.#attributes)
{#attributes: {…}}
#attributes:
length: "0"
type: "image/jpeg"
url: "https://media.nu.nl/m/aoqxps0aijuy_sqr256.jpg/barnier-eu-niet-onder-de-indruk-van-no-deal-brexit-dreigement.jpg"
__proto__: Object
__proto__: Object
So how can I fix that I can have an # in the parameter and it properly gets the data from the json.
Try this:
this.state.news.channel.item.map(function (item, i) {
console.log(item.enclosure["#attributes"])
});
Retrieving the value of a property
We can retrieve the value of a property using 2 different syntaxes.
const car = {
brand: {
name: 'Ford'
},
color: 'blue',
'the color': 'blue'
}
The first is dot notation:
car.color
The second, which is mandatory for properties with invalid names, is to use square brackets:
car['the color']
or even mixing:
car.brand['name']
car['brand'].name

use react render a nested object in jsx

I am trying to render a nested object from a firestore database in react jsx with the map function. The 'text' property like 'I will save document" is contained within what appears to be an object(document) that contains an array[blocks] that contains another array[inlineStyleRanges] that contains an object (text). I am trying to render the text object and I am having a hard time figuring out the syntax to make it render correctly.
The object has this structure:
(4) [{…}, {…}, {…}, {…}]
0:
description: "Lorem ipsum dolor sit amet, suspendisse velit"
document:
blocks: Array(1)
0:
data: {}
depth: 0
entityRanges: []
inlineStyleRanges: []
key: "8u7m4"
text: "I will save this document."
type: "unstyled"
__proto__: Object
length: 1
__proto__: Array(0)
entityMap: {}
__proto__: Object
id: Timestamp {seconds: 1551856294, nanoseconds: 879000000}
title: "TCC/NIBF SUMMARY SHEET"
__proto__: Object
my attempted render method looks like this:
render() {
const urlID = this.state.urlID;
const results = this.state.documents;
const postList = results.map((result, index) => {
if (urlID === result.id.seconds.toString()) {
return (
<Card key={result.id.seconds}>
<CardBody>
<CardTitle>File Name: {result.title}</CardTitle>
<CardText>File Description: {result.description}</CardText>
{/*<CardText>File Document Text: {result.document... }</CardText>*/}
</CardBody>
</Card>
);
} else {
return null;
}
});
return <div>{postList}</div>;
}
I used the new es6 Map function to make a new object map that I turned into an array using the spread operator and then looked at the structure of the object as an array in chrome dev tools.
After studying it, I found the correct syntax:
<CardText>File text: {result.document.blocks["0"].text}</CardText>

Resources