Unit test case for React Component using enzyme - reactjs-testutils

I am working on reactjs 5.X version. I am writing UT with enzyme. Below is my reusable component with prop arguments. Not able to find the div tags inside of the const component.Can you help me how to get the div tags using classname or using find function to get div tags.
import React from 'react';
import styles from './democheck.css';
const democheck = ({ input, cb,
required, testCheckbox, checked, label, meta: { error } }) => (
<div id="hierrdivid" className={styles.testDivColumn}>
<div className={styles.testDiv}>
<br />
<span className={styles.testLabel}>
{label}
</span>
{required && <span className={styles.error}>
*
</span>}
<input
{...input}
name={`form-field-${input.name}`}
checked={checked}
id={input.name}
className={testCheckbox ? styles.testCheckboxError :
styles.testCheckbox}
type="checkbox"
onChange={() => {
if (cb) {
cb(document.getElementById(input.name).checked);
}
}}
/>
</div>
<div className={styles.testerrorDiv}>
{testCheckbox &&
<div className={styles.testerrorLabel}>
{label} {error}
</div>}
</div>
</div>
);
democheck.propTypes = {
input: React.PropTypes.objectOf(React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.func
])),
cb: React.PropTypes.func,
label: React.PropTypes.string,
meta: React.PropTypes.shape({}),`enter code here`
required: React.PropTypes.bool,
checked: React.PropTypes.bool,`enter code here`
testCheckbox: React.PropTypes.bool
};

Related

Add parameter at onClose in relation Modal

Goal:
When you press the button named "yes 1", the value should contain "yes yes" and in the end the console.log should display "test yes yes".
When you press the button named "yes 2", the value should contain "no no" and in the end the console.log should display "test no no".
The display of the value "test yes yes" or "test no no" take place at index.tsx.
The execution or the decision take place at ModalForm.tsx.
Problem:
In technical perspectiv, tried to find a solution by using this code onClick={props.onClose("yes yes")} but it doesn't work.
How do I solve this case?
Stackblitz:
https://stackblitz.com/edit/react-ts-rpltpq
Thank you!
index.html
<div id="root"></div>
<!-- Latest compiled and minified CSS -->
<link
href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/css/bootstrap.min.css"
rel="stylesheet"
/>
<!-- Latest compiled JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/js/bootstrap.bundle.min.js"></script>
index.tsx
import React, { Component, useState, useEffect } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import { ModalForm } from './ModalForm';
import './style.css';
interface dddd {
clientid: string | undefined;
idid: number;
}
const getTest = () => {
console.log('test');
};
const App = () => {
const [clientiddd, setClientid] = useState('ddfdf');
const [idid, setIdid] = useState(0);
return (
<div>
<button
data-bs-toggle="modal"
data-bs-target="#myModalll"
className={'btn btn-outline-dark'}
>
{'data'}
</button>
<br />
<ModalForm clientid={clientiddd} onClose={getTest} />
</div>
);
};
render(<App />, document.getElementById('root'));
ModalForm.tsx
import React, { Component } from 'react';
interface ModalProps {
clientid: string | undefined;
onClose: () => void;
}
export const ModalForm = (props: ModalProps) => {
return (
<div
className="modal"
id="myModalll"
data-bs-backdrop="static"
data-bs-keyboard="false"
tabIndex={-1}
aria-labelledby="staticBackdropLabel"
aria-hidden="true"
>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<h4 className="modal-title">T</h4>
<button
type="button"
className="btn-close btn-close-black"
data-bs-dismiss="modal"
onClick={props.onClose}
></button>
</div>
<div className="modal-body">
TITLE:
<br />
<button
type="button"
data-bs-dismiss="modal"
onClick={props.onClose}
>
yes 1
</button>
<button
type="button"
data-bs-dismiss="modal"
onClick={props.onClose}
>
yes 2
</button>
<br />
</div>
</div>
</div>
</div>
);
};
It's somewhat hard to understand your question, but let me try.
onClick={props.onClose('yes yes')}
What this code does is that it calls props.onClick with yes yes as an argument and assigns the returned value as the onClick listener.
Assume the props.onClose is this:
function onClose() {
console.log('test')
}
What it does here is that it calls this function (it logs test to the console) but since this function is not returning anything, it passes undefined as the onClick here.
If instead your function was this:
function onClose(result) {
return function () {
console.log('test', result)
}
}
Now it would call props.onClose with yes yes and it would return a function. This anonymous function would be passed as the onClick event listener and when you click, it would call that so there would be test yes yes logged only after clicking.
You can as well do it differently, keep your onClose function as it was but introduce result:
function onClose(result) {
console.log('test', result)
}
but now you have to pass this function instead of calling it:
onClick={() => props.onClose('yes yes')}
As you can see, there will always be one anonymous function somewhere in there, it's just a question of where that function is and what is called when. Hope this explanation helps.
https://stackblitz.com/edit/react-ts-nw6upt?file=index.html
index.html
<div id="root"></div>
<!-- Latest compiled and minified CSS -->
<link
href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/css/bootstrap.min.css"
rel="stylesheet"
/>
<!-- Latest compiled JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/js/bootstrap.bundle.min.js"></script>
index.tsx
import React, { Component, useState, useEffect } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
import { ModalForm } from './ModalForm';
interface dddd {
clientid: string | undefined;
idid: number;
}
const getTest = (result: string) => {
console.log('testff ' + result);
};
const App = () => {
const [clientiddd, setClientid] = useState('ddfdf');
const [idid, setIdid] = useState(0);
return (
<div>
<button
data-bs-toggle="modal"
data-bs-target="#myModalll"
className={'btn btn-outline-dark'}
>
{'data'}
</button>
<br />
<ModalForm clientid={clientiddd} onClose={getTest} />
</div>
);
};
render(<App />, document.getElementById('root'));
ModalForm.tsx
import React, { Component } from 'react';
interface ModalProps {
clientid: string | undefined;
onClose: (result: string) => void;
}
export const ModalForm = (props: ModalProps) => {
return (
<div
className="modal"
id="myModalll"
data-bs-backdrop="static"
data-bs-keyboard="false"
tabIndex={-1}
aria-labelledby="staticBackdropLabel"
aria-hidden="true"
>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<h4 className="modal-title">T</h4>
<button
type="button"
className="btn-close btn-close-black"
data-bs-dismiss="modal"
onClick={() => props.onClose('ccc')}
></button>
</div>
<div className="modal-body">
TITLE:
<br />
<button
type="button"
data-bs-dismiss="modal"
onClick={() => props.onClose('aaa')}
>
yes 1
</button>
<button
type="button"
data-bs-dismiss="modal"
onClick={() => props.onClose('bbb')}
>
yes 2
</button>
<br />
</div>
</div>
</div>
</div>
);
};

How to show/hide #foreach statement

I have a Vuejs 3 dropdown reusable component. My problem is that the #foreach statement runs before the component loads so it causes a flash of the foreach results which is very ugly upon refresh or when the page is loading.
To demonstrate please check this gif:
My component in blade:
<Dropdown title="{{ isset($currentCategory) ? ucwords($currentCategory->name) : 'Categories' }}">
<Dropdowncontent>
<Dropdownitems href="/">
All
</Dropdownitems>
<div>
#foreach ($categories as $category)
<Dropdownitems
href="/?category={{ $category->slug }}&{{ http_build_query(request()->except('category')) }}"
class="{{ isset($currentCategory) && $currentCategory->is($category) ? ' selectedCategoryItem' : '' }}">
{{ $category->name }}
</Dropdownitems>
#endforeach
</div>
</Dropdowncontent>
</Dropdown>
I added a div to contain the #foreach statement but i don't know what to do from here. I don't want to use alpineJS as it will defeat the purpose of using Vue (I guess?).
I just need a way to only display this div or the #foreach statement if the component is fully loaded or if the button is pressed or something like that. Any ideas?
-- EDIT --
I tried to hide the links in my 'dropdownitems' vue component and set the default value to false. The links are now hidden but still the blade #foreach statement echoing out the results as text before the component is loaded:
<template>
<a v-if="showLinks" href="" class="demogard categoryItems">
<slot />
</a>
</template>
<script>
export default {
name: "Dropdownitems",
setup() {
const showLinks = false;
return {
showLinks,
};
},
};
</script>
<style></style>
Here is a gif to show the result of that:
-- EDIT --
Here is my dropdown component:
<template>
<div
class="relative"
v-click-outside="onClickOutside"
#click="showCategories"
>
<slot name="toggler">
<button
class="flex max-h-52 w-full overflow-auto py-2 pl-3 pr-9 text-sm font-semibold lg:inline-flex lg:w-32"
>
{{ title }}
</button>
</slot>
<slot />
</div>
</template>
<script>
import vClickOutside from "click-outside-vue3";
import { ref, onMounted, provide } from "vue";
export default {
name: "Dropdown",
props: ["title"],
directives: {
clickOutside: vClickOutside.directive,
},
setup() {
const sharedState = ref(false);
const showCategories = () => {
sharedState.value = !sharedState.value;
};
const onClickOutside = (event) => {
sharedState.value = false;
};
provide("sharedState", sharedState);
return {
sharedState,
showCategories,
onClickOutside,
};
},
};
</script>
<style></style>
As your question, I think you have to add if condition on your dropdown component.
Your dropdown component should be like this
#dropdown.vue
<template>
<div class="dropdown">
<div #click="show = !show">{{title}}</div>
<div v-if="show">
<slot />
</div>
</div>
</template>
<script>
import { ref } from "vue";
export default {
props: ["title"],
setup(props) {
const show = ref(false);
return {
show,
};
},
};
</script>
Demo
---- EDIT ----
#dropdown.vue
<template>
<div
class="relative"
v-click-outside="sharedState = false"
>
<slot name="toggler">
<button
class="flex max-h-52 w-full overflow-auto py-2 pl-3 pr-9 text-sm font-semibold lg:inline-flex lg:w-32"
#click="sharedState = !sharedState"
>
{{ title }}
</button>
</slot>
<div v-if="sharedState">
<slot />
</div>
</div>
</template>
<script>
import vClickOutside from "click-outside-vue3";
import { ref, onMounted, provide } from "vue";
export default {
name: "Dropdown",
props: ["title"],
directives: {
clickOutside: vClickOutside.directive,
},
setup() {
const sharedState = ref(false);
// const showCategories = () => {
// sharedState.value = !sharedState.value;
// };
// const onClickOutside = (event) => {
// sharedState.value = false;
// };
provide("sharedState", sharedState);
return {
sharedState,
//showCategories,
//onClickOutside,
};
},
};
</script>
<style></style>
Try with a #if directive:
Conditional Rendering
from the documentation:
<button #click="awesome = !awesome">Toggle</button>
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
As showed in the example it render the "h1" tag conditionally respect the "awesome" variable.
In this case i will set a default value of "false" and i will turn it to "true" in the mounted hook:
Lifecycle
It's impossible to load Vue before PHP because your webpage only displays when full PHP code is received from the server. Therefore, we're never able to stop PHP or HTML from flashing if we're using them inside a reusable Vue component.
The solution I made is simply passing the value of the foreach loop as a prop to the Vue component in order for it to be displayed from there, not from my blade file.
Here's my code in blade after passing the value of the category name as a prop to my Vue component.
<Dropdown title="{{ isset($currentCategory) ? ucwords($currentCategory->name) : 'Categories' }}">
<Dropdowncontent>
<Dropdownitems href="/" category="All"></Dropdownitems>
#foreach ($categories as $category)
<Dropdownitems
category="{{ $category->name }}"
href="/?category={{ $category->slug }}&{{ http_build_query(request()->except('category')) }}"
class="{{ isset($currentCategory) && $currentCategory->is($category) ? ' selectedCategoryItem' : '' }}">
</Dropdownitems>
#endforeach
</Dropdowncontent>
</Dropdown>
Here is me displaying it from there the Vue dropdown items component:
<template>
<a href="" class="demogard categoryItems">
<slot>{{ category }}</slot>
</a>
</template>
<script>
export default {
name: "Dropdownitems",
props: ["category"],
};
</script>
<style></style>

Get all values of custom input inside v-for vuejs

I created a vuejs custom input that I wanted to use to dynamically display inputs by using props within the custom input. I haven't shown them here because it would be too long.
By clicking on the submit button, which is also part of the custom input, I wanna be able to get the values of each input, but for some reason, I have only been able to get the value of the last input.
What am I doing wrong?
Custom input:
<template>
<div class="form-input">
<label :label="label" :for="name" v-if="label && type !='submit' ">{{label}} <span v-if="required">*</span></label>
<a v-if="multiple" href="#" class="btn">Upload</a>
<input v-model="inputVal" :multiple="multiple" v-if="type != 'textarea' && type != 'submit'" class="form-control" :required="required" :class="classes" :type="type" :name="name" :placeholder="placeHolder">
<textarea v-model="inputVal" :multiple="multiple" v-else-if="type != 'submit'" class="form-control" :required="required" :class="classes" :type="type" :name="name" :placeholder="placeHolder"></textarea>
<button :multiple="multiple" :name="name" v-else type="submit">{{label}}</button>
</div>
</template>
<script>
export default {
name: "Input",
data () {
return {
inputVal: null
}
},
watch: {
inputVal: {
handler: function(newValue, oldValue) {
this.$emit('input', newValue);
},
deep: true,
}
}
}
</script>
Form where custom input is used:
<template>
<div class="form container">
<form v-on:submit.prevent="sendMail" method="post" class="d-flex row shadow bg-dark border-right border-dark">
<h3 class="col-12">Contact me</h3>
<Input v-model="formInput" v-for="input in inputs" v-bind:key="input.name" :label="input.label" :multiple="input.multiple" :type="input.type" :name="input.name" :class="input.classes" :required="input.required"></Input>
</form>
</div>
</template>
<script>
import Input from "../components/Input";
export default {
name: "Contact",
components: {Input},
data() {
return {
formInput: null,
}
},
methods: {
sendMail () {
console.log(this.formInput);
}
}
}
</script>
The issue I see in your code is, you are using only one variable "formInput" ( in case of Contact component ) and "inputVal" ( in case of Input component ) but you have number of input fields from where you need data right.
The simplest way to deal with these kind of cases is to create a datastructure and loop through that.
For eg.
// Contact component ( i am making it simple to make you understand the scenario )
<template>
<div class="form container">
<form v-on:submit.prevent="sendMail" method="post" class="d-flex row shadow bg-dark border-right border-dark">
<h3 class="col-12">Contact me</h3>
<!-- we are looping through our data structure and binding each inputVal to this input -->
<input v-for="(input, i) in formInputs" :key="i" v-model="input.inputVal">
</form>
</div>
</template>
<script>
import Input from "../components/Input";
export default {
name: "Contact",
components: {Input},
data() {
return {
formInputs: [
{inputVal: ''},
{inputVal: ''},
{inputVal: ''},
],
}
},
methods: {
sendMail () {
// You can extract the data from formInputs as per your need
}
}
}
</script>

React TypeError: orders.map is not a function

I'm a begginer in React. I'm trying to fetch an array of data about orders and that is working and then map it to display specific information about each order.
I'm getting TypeError: orders.map is not a function exception in my application.
Here's my code:
class Orders extends Component {
state = {
orders: []
};
componentDidMount() {
axios
.get("https://localhost:9090/orders")
.then(res => {
this.setState({ orders: res.data });
console.log(res.data);
});
}
render() {
const { orders } = this.state;
const orderList =
this.state.orders.length > 0 ? (
orders.map(o => {
return (
<div key={o.orderId}>
<p>
{o.isbn}
</p>
</div>
);
})
) : (
<div className="row p-5 m-5">
<div className="offset-sm-5 col-sm-2 text-center">
<span className="text-grey r">Loading...</span>
</div>
</div>
);
return <div className="container">{orderList}</div>;
}}
What's interesting, I have a similar code, that is working. The only difference is basically what it's fetching. Here's the code:
class BookList extends Component {
state = {
books: []
};
componentDidMount() {
console.log(this.props.match.params.search_term);
axios
.get("https://localhost:8080/search?searchTerm=" + search_term)
.then(res => {
this.setState({ books: res.data });
console.log(res.data);
});
}
render() {
const { books } = this.state;
const booksList =
this.state.books.length > 0 ? (
books.map(b => {
return (
<div key={b.isbn} className="card">
<div className="card-body">
<h5 className="card-title">
<Link to={"/details/" + b.isbn}>{b.title}</Link>
</h5>
<div className="card-subtitle text-muted">
{b.author} ({b.year}) /{" "}
<span className=" text-danger">{b.category}</span>
</div>
<p />
</div>
</div>
);
})
) : (
<div className="row p-5 m-5">
<div className="offset-sm-5 col-sm-2 text-center">
<span className="text-grey r">Loading...</span>
</div>
</div>
);
return <div className="container">{booksList}</div>;
}}
I can't find the difference that would cause that exception. It is an array in both cases.
Any suggestions?
EDIT:
here's the output of response data:
response data of order
response data of bokstore
From them images it looks like the orders are processed as plain text and not parsed to JSON. Check that your back-end specifies the required headers Content-Type: application/json so that axios will parse the data correctly.
Alternatively you could parse the text client-side with JSON.parse(res.data)

Using redux-form I'm losing focus after typing the first character

I'm using redux-form and on blur validation. After I type the first character into an input element, it loses focus and I have to click in it again to continue typing. It only does this with the first character. Subsequent characters types remains focuses. Here's my basic sign in form example:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import * as actions from '../actions/authActions';
require('../../styles/signin.scss');
class SignIn extends Component {
handleFormSubmit({ email, password }) {
this.props.signinUser({ email, password }, this.props.location);
}
renderAlert() {
if (this.props.errorMessage) {
return (
<div className="alert alert-danger">
{this.props.errorMessage}
</div>
);
} else if (this.props.location.query.error) {
return (
<div className="alert alert-danger">
Authorization required!
</div>
);
}
}
render() {
const { message, handleSubmit, prestine, reset, submitting } = this.props;
const renderField = ({ input, label, type, meta: { touched, invalid, error } }) => (
<div class={`form-group ${touched && invalid ? 'has-error' : ''}`}>
<label for={label} className="sr-only">{label}</label>
<input {...input} placeholder={label} type={type} className="form-control" />
<div class="text-danger">
{touched ? error: ''}
</div>
</div>
);
return (
<div className="row">
<div className="col-md-4 col-md-offset-4">
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))} className="form-signin">
<h2 className="form-signin-heading">
Please sign in
</h2>
{this.renderAlert()}
<Field name="email" type="text" component={renderField} label="Email Address" />
<Field name="password" type="password" component={renderField} label="Password" />
<button action="submit" className="btn btn-lg btn-primary btn-block">Sign In</button>
</form>
</div>
</div>
);
}
}
function validate(values) {
const errors = {};
if (!values.email) {
errors.email = 'Enter a username';
}
if (!values.password) {
errors.password = 'Enter a password'
}
return errors;
}
function mapStateToProps(state) {
return { errorMessage: state.auth.error }
}
SignIn = reduxForm({
form: 'signin',
validate: validate
})(SignIn);
export default connect(mapStateToProps, actions)(SignIn);
This happens because you're re-defining renderField as a new component every time you render which means it looks like a new component to React so it'll unmount the original one and re-mounts the new one.
You'll need to hoist it up:
const renderField = ({ input, label, type, meta: { touched, invalid, error } }) => (
<div class={`form-group ${touched && invalid ? 'has-error' : ''}`}>
<label for={label} className="sr-only">{label}</label>
<input {...input} placeholder={label} type={type} className="form-control" />
<div class="text-danger">
{touched ? error: ''}
</div>
</div>
);
class SignIn extends Component {
...
render() {
const { message, handleSubmit, prestine, reset, submitting } = this.props;
return (
<div className="row">
<div className="col-md-4 col-md-offset-4">
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))} className="form-signin">
<h2 className="form-signin-heading">
Please sign in
</h2>
{this.renderAlert()}
<Field name="email" type="text" component={renderField} label="Email Address" />
<Field name="password" type="password" component={renderField} label="Password" />
<button action="submit" className="btn btn-lg btn-primary btn-block">Sign In</button>
</form>
</div>
</div>
);
}
}
...
As #riscarrott mentioned, put renderField outside of component class .
But I am still losing focus .. And after testing, I concluded the re-rendering is done because of using curried function (return another function, and not return element . directly) .
const const renderField = (InputComponent = 'input') => ({ input, label, type, meta: { touched, invalid, error } }) => (
<div class={`form-group ${touched && invalid ? 'has-error' : ''}`}>
<label for={label} className="sr-only">{label}</label>
<InputComponent {...input} placeholder={label} type={type} className="form-control" />
<div class="text-danger">
{touched ? error: ''}
</div>
</div>
);
Then, if your renderField is a curried function :
then , don't do 😔😔😔😔:
//.....
<Field name="email" type="text" component={renderField('input')} label="Email Address" />
<Field name="desc" component={renderField('textarea')} label="Email Address" />
But , do the following 🙂🙂🙂🙂 :
// outside component class
const InputField = renderField('input');
const TextAreaField = renderField('textarea');
// inside component class
<Field name="email" type="text" component={InputField} label="Email Address" />
<Field name="desc" component={TextAreaField} label="Email Address" />
What worked for me was refactoring arrowFunction-based Component to class-based Component as the behavior of InputForm components was weird. Every time the value of each input was changed they all rerendered even after splitting each inputType to separated components. There was nothing else left to fix but changing main component to class-based. I guess it may be caused by redux-form itself.
This can also happen if you have defined styled-components inside your render function.
You should define them outside your class.
Like this:
const Row = styled.div`
justify-content:center;
`;
const Card = styled.div`
width:18rem;
padding:1rem;
`;
class Login extends Component{
i have the same problem. i resolved mine by changing the component to Class component and i removed all the css style config from render().
I had the same problem. I solved it when I added my react redux form to the store in the createForms():
export const ConfigureStore = () => {
const store = createStore(
combineReducers({
tasks: Tasks,
task: Task,
image: Image,
admin: Admin,
pageId: PageID,
fieldValues: FieldValues,
formValues: FormValues,
...createForms({
createTask: initialTask,
editTask: initialEditTask
})
}),
applyMiddleware(thunk, logger)
);
return store;
}
I had the same problem, and none of the answers worked for me.
But thanks to Advem's answer I got an idea of what could be wrong:
My form required accordion UI, and for that I had state variable in it:
const ConveyorNotificationSettingsForm = (props) => {
const {handleSubmit, formValues, dirty, reset, submitting} = props;
const [expandedSection, setExpandedSection] = useState(null);
...
with only one expanded section, that with its index equal to expandedSection .
After I extracted the accordion to a separate functional component and moved useState there, the problem was gone.
actually, this is a problem with the function component. I used a class-based component with redux form and my problem solved. I don't know the exact reason but redux form re-renders when we enter the first word and losses focus. use class-based components whenever you want to use redux form.
class StreamCreate extends React.Component{
rendorInput(formProps){
return <input {...formProps.input} />;
}
render(){
return (
<Container maxWidth="lg">
<form action="">
<Field name="title" component={this.rendorInput}/>
<Field name="description" component={this.rendorInput} />
</form>
</Container>
)
}
}
export default reduxForm({
form: 'createStream'
})( StreamCreate);

Resources