Test react component can't get clientWidth - javascript

I'm trying to develop unit test for my react component with jest and enzyme. So basically my component have resize listener, when resize occured my component will update component state. But i just couldn't get the clientWidth for my react component. Below is some code of my component.
import React, { Component } from "react";
import moment from "moment";
// import PropTypes from "prop-types";
import Table from "./Table";
import Grid from "./Grid";
import ActionBlock from "../ActionBlock";
import ConfirmDialog from './ConfirmDialog';
import ReactTooltip from 'react-tooltip'
import { debounce } from '../../utils';
import styles from './styles.scss';
export default class Pagination extends Component {
constructor(props) {
super(props);
this.state = {
index: props.index,
type: props.type,
config: props.config,
data: props.data,
currentPage: 1,
dataPerPage: 20,
enableActionBlock: props.enableActionBlock,
confirmDialogIndex: null,
confirmDialogActionName: null,
confirmDialogData: null,
width: 0
};
this.handleWindowResize = debounce(this.handleWindowResize.bind(this), 100); //delay trigger resize event
}
componentDidMount() {
this.setState({ width: this.refs.pagination_wrapper.clientWidth })
window.addEventListener('resize', this.handleWindowResize)
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleWindowResize);
}
handleWindowResize = () => {
this.setState({ width: this.refs.pagination_wrapper.clientWidth })
}
render() {
return (
<div ref="pagination_wrapper" className={styles.pagination_wrapper}>
<ReactTooltip />
{this.renderViewType()}
{this.renderConfirmDialog()}
</div>
)
}
}
Pagination.defaultProps = {
enableActionBlock: true,
dataPerPage: 20
};
And below is my test code.
import React from 'react'
import { shallow, mount, render } from 'enzyme';
import Pagination from '../index';
let img = 'https://www.jqueryscript.net/images/Simplest-Responsive-jQuery-Image-Lightbox-Plugin-simple-lightbox.jpg';
let imageStream = 'http://192.168.100.125:8080/';
let imgQuoteError = `http://192.168.100.71/target-data/fr/target-person-images/1111112222233333#Rizkifika-Asanuli'nam/qTD8vYa.jpeg`;
describe('Testing Pagination', () => {
let action = (actionName, indexData) => {
console.log('action APP', actionName, indexData);
}
let dataListProps = {
index: 'id',
type: 'grid',
config: [
{ text: 'Image', type: 'image', textPath: 'image', textColor: 'red', valuePath: 'image' },
{ text: 'Fullname', type: 'string', textPath: 'fullname', valuePath: 'fullname' },
{ text: 'Role', type: 'string', textPath: 'role', valuePath: 'role' },
{ text: 'Datetime', type: 'date', textPath: 'datetime', valuePath: 'datetime' },
{ text: 'Json', type: 'json', textPath: 'json', valuePath: 'json' },
],
data: [
{ id: 305, created_at: '2018-02-23T09:43:08.928Z', rule_detail: { id: 1 }, cam_detail: { id: 2, name: 'kamera huawei' }, vas_detail: { id: 3, name: 'VAS 3' }, image: img },
{ id: 306, created_at: '2018-02-23T09:43:08.928Z', rule_detail: { id: 2, name: '' }, cam_detail: { id: 3, name: 'kamera avigilon' }, vas_detail: { id: 4, name: 'VAS 4' }, image: imageStream },
{ id: 306, created_at: '2018-02-23T09:43:08.928Z', rule_detail: { id: 2, name: null }, cam_detail: { id: 3, name: 'kamera avigilon' }, vas_detail: { id: 4, name: 'VAS 4' }, image: imgQuoteError },
{ id: 306, created_at: '2018-02-23T09:43:08.928Z', rule_detail: { id: 2, name: 'Crowd Behaviour' }, cam_detail: { id: 3, name: 'kamera avigilon' }, vas_detail: { id: 4, name: 'VAS 4' }, image: imageStream },
],
onAction: action,
enableActionBlock: false
}
it('snapshot', () => {
const wrapper = shallow(<Pagination {...dataListProps}/>)
expect(wrapper).toMatchSnapshot();
})
})
I need help for solving this

As pointed by Xarvalus. If wanna access refs, the component have to be mounted first by using mount from import { shallow, mount, render } from 'enzyme';.
But it will have bug (RangeError: Invalid string length). So to overcome this, we have to convert enzyme to json by using import toJson from 'enzyme-to-json';
full working code
import React from 'react';
import { shallow, mount, render } from 'enzyme';
import toJson from 'enzyme-to-json';
import Pagination from '../index';
describe('Testing Pagination', () => {
it('snapshot', () => {
const wrapper = mount(<Pagination {...dataListProps}/>)
expect(toJson(wrapper)).toMatchSnapshot();
})
})
reference issue

You can access the window object inside your component, and so retrieve the window.innerWidth field which is, I guess, what you're looking for.

Related

How to return JSON object in Javascript?

I am having issues with returning a JSON object. When I render the webpage, nothing shows up. Does anyone know how to fix this? Sorry, I am new to Javascrtipt.
import React, { useEffect, useState, useContext } from 'react'
export const MarketData = () => {
var obj = {
width: '100%',
height: '100%',
symbolsGroups: [
{
name: 'Indices',
originalName: 'Indices',
symbols: [
{
name: 'INDEX:DEU30',
displayName: 'DAX Index',
},
{
name: 'FOREXCOM:UKXGBP',
displayName: 'FTSE 100',
},
],
},
...
],
showSymbolLogo: true,
colorTheme: 'dark',
isTransparent: false,
locale: 'en',
largeChartUrl:
'https://bondintelligence.cloud.looker.com/extensions/bond_intelligence_webpage::helloworld-js/',
}
return (
<>
<text>{obj}</text>
</>
)
}
You can use JSON.stringify()
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
The third argument in JSON.stringify() provides new lines and indentation. If only the first argument is provided, the string will be one long line.
Your example with fix (I changed your <text> to <p> as I have never heard of a <text> HTML element):
import React, { useEffect, useState, useContext } from 'react'
export const MarketData = () => {
var obj = {
width: '100%',
height: '100%',
symbolsGroups: [
{
name: 'Indices',
originalName: 'Indices',
symbols: [
{
name: 'INDEX:DEU30',
displayName: 'DAX Index',
},
{
name: 'FOREXCOM:UKXGBP',
displayName: 'FTSE 100',
},
],
},
...
],
showSymbolLogo: true,
colorTheme: 'dark',
isTransparent: false,
locale: 'en',
largeChartUrl:
'https://bondintelligence.cloud.looker.com/extensions/bond_intelligence_webpage::helloworld-js/',
}
var objAsString = JSON.stringify(obj, null, 2)
return (
<>
<p>{objAsString}</p>
</>
)
}

Getting setState is not defined no-undef error using React hooks

I am just getting started with React. So I generated a new React app with npx create-react-app . and it generated me a what I think is functional React hooks components. I guess this is the 2020 version.
But I ran into a problem when I tried to update my state. I basically wanted to toggle the completed property of the selected todo item. But when I called the setTodos method it gave me this error:
index.js:1 ./src/App.js
Line 27:5: 'setTodos' is not defined no-undef
import React, { useState } from 'react'
import Todos from './components/Todos.js'
function App()
{
const [todos, setTodos] = useState([
{ id: 1, title: 'First todo item', completed: false },
{ id: 2, title: 'Second todo item', completed: true },
{ id: 3, title: 'Third todo item', completed: false },
])
return (
<div>
<Todos
todos={todos}
markComplete={ (event, todo) => markComplete(event, todo) }
/>
</div>
)
}
function markComplete(event, todo)
{
// this works
console.log('You clicked todo with id: ' + todo.id + ' and title: ' + todo.title)
// setTodos is not defined...?
setTodos({
id: 1,
title: 'Test',
completed: true,
})
}
export default App
setTodos is only in scope within the function it is defined in, in this case the App component. Move markComplete into your component.
import React, { useState } from 'react'
import Todos from './components/Todos.js'
function App() {
const [todos, setTodos] = useState([
{ id: 1, title: 'First todo item', completed: false },
{ id: 2, title: 'Second todo item', completed: true },
{ id: 3, title: 'Third todo item', completed: false },
]);
function markComplete(event, todo) {
console.log('You clicked todo with id: ' + todo.id + ' and title: ' + todo.title)
setTodos({
id: 1,
title: 'Test',
completed: true,
})
}
return (
<div>
<Todos
todos={todos}
markComplete={ (event, todo) => markComplete(event, todo) }
/>
</div>
)
}
export default App
Put markComplete in the same function scope as setTodos
import React, { useState } from 'react'
import Todos from './components/Todos.js'
function App()
{
function markComplete(event, todo)
{
setTodos({
id: 1,
title: 'Test',
completed: true,
})
}
const [todos, setTodos] = useState([
{ id: 1, title: 'First todo item', completed: false },
{ id: 2, title: 'Second todo item', completed: true },
{ id: 3, title: 'Third todo item', completed: false },
])
return (
<div>
<Todos
todos={todos}
markComplete={ (event, todo) => markComplete(event, todo) }
/>
</div>
)
}
export default App
State
state use only inside a component.
state change a value inside a component.
So you must put the function markComplete inside your component App.
import React, { useState } from 'react'
import Todos from './components/Todos.js'
function App()
{
const [todos, setTodos] = useState([
{ id: 1, title: 'First todo item', completed: false },
{ id: 2, title: 'Second todo item', completed: true },
{ id: 3, title: 'Third todo item', completed: false },
])
const markComplete = (event, todo) =>
{
setTodos({
id: 1,
title: 'Test',
completed: true,
})
}
return (
<div>
<Todos
todos={todos}
markComplete={ (event, todo) => markComplete(event, todo) }
/>
</div>
)
}
export default App

How to show the data I got from API to react-material datatable

I'm new when using materialUI table in react.js, I want to try using react-material table but I got lost as how can I show my data in the table, Let say I have 28 data and in fact it already show the right number in the pagination but the data itself doesn't show anything. this is the documentation link for react-material table Check this.
I already read several topic about this but all of them using tableRow, tableHead, and etc.
this is my component code:
import React, { Component } from 'react';
import MaterialTable from 'material-table';
import { history } from '../../../../Helpers/history';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { orderActions } from '../../../../Actions/orderActions';
import { withStyles } from '#material-ui/core/styles';
// Component
import './tabledata.css';
const styles = theme => ({
'#global': {
body: {
backgroundColor: theme.palette.common.white,
},
},
});
class Tabledata extends Component {
constructor(props) {
super(props);
// const { orders } = this.props;
this.state = {
columns: [
{ title: 'Nama Pemesanan', field: 'name' },
{ title: 'Status', field: 'status' },
{ title: 'Estimasi Pengerjaan (Hari)', field: 'estimate', type: 'numeric' },
{ title: 'Jumlah Pesanan (pcs)', field: 'unit', type: 'numeric' },
{ title: 'Harga (Rp)', field: 'price', type: 'numeric' },
],
data: [
{
id: 2,
name: 'lala',
status: 'Penyablonan',
estimate: 8,
unit: 36,
price: '36.000.000',
},
],
}
}
componentDidMount() {
if(localStorage.getItem('auth')) {
const { dispatch } = this.props;
dispatch(orderActions.getAllOrder());
// history.push('/dashboard');
}
}
componentWillReceiveProps(newProps){
this.setState({ loading: newProps.loading }); // remove the loading progress when logged in or fail to log in
}
handleChange = prop => event => {
this.setState({ [prop]: event.target.value });
};
change(data){
console.log("Check ID : ", data);
}
render(){
const { orders } = this.props;
console.log("test console : ", orders)
return (
<div className="tabledata-order">
<div className="row item-section">
<div className="col">
<div className="card">
<div className="card-body">
<MaterialTable
title="Daftar Pesanan"
columns={this.state.columns}
key={orders._id}
data={orders}
/>
</div>
</div>
</div>
</div>
</div>
);
}
}
Tabledata.propTypes = {
classes: PropTypes.object.isRequired
};
const mapStateToProps = (state) => {
const { orders } = state.orderPage;
return {
orders
};
}
const connectedTableDataPage = withRouter(connect(mapStateToProps, '', '', {
pure: false
}) (withStyles(styles)(Tabledata)));
export { connectedTableDataPage as Tabledata };
As you can see, this material table have a component like this
<MaterialTable
title="Daftar Pesanan"
columns={this.state.columns}
key={orders._id}
data={orders}
/>
As you can see, in the bottom of the image you can see 1-5 of 28 and in my console there is exactly 28 data but the table itself doesn't show any data
can someone help me? how can I show the data in orders and this is the example of the image json that I have:
Finally I can fix this problem, this answer for you who have facing the same problem with react-material table if your data doesn't show but it show in console.log. you must check the field in column
this.state = {
columns: [
{ title: 'Nama Pemesanan', field: 'name' },
{ title: 'Status', field: 'status' },
{ title: 'Estimasi Pengerjaan (Hari)', field: 'estimate', type: 'numeric' },
{ title: 'Jumlah Pesanan (pcs)', field: 'unit', type: 'numeric' },
{ title: 'Harga (Rp)', field: 'price', type: 'numeric' },
],
data: [
{
id: 2,
name: 'lala',
status: 'Penyablonan',
estimate: 8,
unit: 36,
price: '36.000.000',
},
],
}
let say, json that I got have city, color, and weight then you must state the column field as such:
this.state = {
columns: [
{ title: 'detail Address', field: 'city' },
{ title: 'color', field: 'color' },
{ title: 'weight', field: 'weight' },
],
}
and for the MaterialTable you can just put all the variable you have like this
<MaterialTable
title="Daftar Pesanan"
columns={this.state.columns}
key={orders._id}
data={orders}
/>
and you can get the data like I show you below
I hope this answer can help you who have a hard time with react-material table

Changing state in useEffect doesn't change interface

I'm using the useState and useEffect hooks in react to render a form. But when I'm updating the form using the useEffect hook. The form doesn't re-render.
import React, { useState, useEffect } from 'react';
import { makeStyles } from "#material-ui/core/styles";
import GridItem from "components/Grid/GridItem.js";
import GridContainer from "components/Grid/GridContainer.js";
import Card from "components/Card/Card.js";
import CardHeader from "components/Card/CardHeader.js";
import CardBody from "components/Card/CardBody.js";
import Input from "components/UI/Input/Input";
import Button from "components/CustomButtons/Button.js";
import styles from "styles/styles";
import falconAPI from "falcon-api";
const useStyles = makeStyles(styles);
export default function AddWarehouse(props) {
const classes = useStyles();
// State management hooks
const [form, setForm] = useState({
warehouseType: {
elementType: 'select',
elementConfig: {
options: [
{ value: '4', displayValue: 'Showroom' }
]
},
value: '1',
validation: {},
valid: true
},
territory: {
elementType: 'select',
elementConfig: {
options: [
{ value: '1', displayValue: 'Kandy' },
{ value: '2', displayValue: 'Jaffna' },
{ value: '3', displayValue: 'Colombo' },
{ value: '4', displayValue: 'Galle' }
]
},
value: '1',
validation: {},
valid: true
},
name: {
elementType: 'input',
elementConfig: {
type: 'text',
placeholder: 'Name'
},
value: '',
validation: {
required: true
},
valid: false,
touched: false
},
address: {
elementType: 'input',
elementConfig: {
type: 'text',
placeholder: 'Address'
},
value: '',
validation: {
required: true
},
valid: false,
touched: false
},
telephone: {
elementType: 'input',
elementConfig: {
type: 'text',
placeholder: 'Telephone'
},
value: '',
validation: {
required: true,
},
valid: false,
touched: false
},
});
// Life cycle hooks
useEffect(() => {
falconAPI.post('/warehouse/type/all')
.then(response => {
const warehouseTypes = response.data.message;
const updatedWarehouseTypes = []
warehouseTypes.map(warehouseType => {
updatedWarehouseTypes.push({
value: warehouseType.id,
displayValue: warehouseType.name
});
})
const updatedForm = { ...form };
updatedForm.warehouseType.options = updatedWarehouseTypes;
setForm(updatedForm);
})
.catch(error => {
});
}, []);
const inputChangedHandler = (e) => {
}
const submitFormHandler = (e) => {
console.log(form);
}
const formElementsArray = [];
for (let key in form){
formElementsArray.push({
id: key,
config: form[key]
})
}
return (
<GridContainer>
<GridItem xs={12} sm={12} md={12}>
<Card>
<CardHeader color="success">
<h4 className={classes.cardTitleWhite}>{props.title}</h4>
</CardHeader>
<CardBody>
{formElementsArray.map(formElement => (
<Input
key={formElement.id}
elementType={formElement.config.elementType}
elementConfig={formElement.config.elementConfig}
value={formElement.config.value}
invalid={!formElement.config.valid}
shouldValidate={formElement.config.validation}
touched={formElement.config.touched}
changed={(event) => inputChangedHandler(event, formElement.id)} />
))}
<Button onClick={submitFormHandler}>Add Model</Button>
</CardBody>
</Card>
</GridItem>
</GridContainer>
);
}
In the useEffect hook, the api call update the form therefore re-rendering the warehouse type select input but the select input does not re-render. What could be the cause for this.
You need to copy the nested values too:
{
warehouseType: {
elementType: 'select',
elementConfig: {
options: [
{ value: '4', displayValue: 'Showroom' }
]
},
value: '1',
validation: {},
valid: true
},
...
const updatedForm = { ...form };
updatedForm.warehouseType.options = updatedWarehouseTypes;
setForm(updatedForm);
You also missed elementConfig in there. updatedForm.warehouseTypes.elementConfig.options
But it's still a good idea to copy the nested values too.
const updatedForm = {
...form,
warehouseType: {...form.warehouseType,
elementConfig: {...form.elementConfig,
options:updatedWarehouseTypes
}}};

Jest test fails - returns undefined when testing component creation

I am writing a test in Jest to confirm that a component is created, I am working in angular. It however returns undefined when I run the test and states:
OrderDetailsDeliveryTabComponent › should create
expect(received).toBeTruthy()
Received: undefined
I have imported the relevant modules into the test and I believe I am calling the component correctly into test.
I've not encountered this error before
import { ComponentFixture } from '#angular/core/testing';
import { NO_ERRORS_SCHEMA } from '#angular/core';
import { OrderDetailsDeliveryTableComponent } from './order-details-
delivery-table.component';
import { ConfigureFn, configureTests, MockOrderDetailsService} from
'#lib/testing';
import { OrderDetailsService } from '#c3services/order-editing/order-details.service';
describe('OrderDetailsDeliveryTabComponent', () => {
let component: OrderDetailsDeliveryTableComponent;
let fixture: ComponentFixture<OrderDetailsDeliveryTableComponent>;
beforeEach(async () => {
const configure: ConfigureFn = testBed => {
testBed.configureTestingModule({
declarations: [OrderDetailsDeliveryTableComponent],
providers: [{ provide: OrderDetailsService, useValue:
MockOrderDetailsService }],
schemas: [NO_ERRORS_SCHEMA]
});
};
const testBed = await configureTests(configure);
fixture = testBed.createComponent(OrderDetailsDeliveryTableComponent);
component = fixture.componentInstance;
component.associatedExistingDespatchData = [
{
item: 'CS119NVP30F',
quantity: 1,
despatched: '04-08-2017 05:57',
method: 'Standard',
trackingNumber: '',
deliveryAddress: '14 Beetham Court',
returnsTrackingNumber: '',
fromLocation: 'Tackbrook Wahrehouse',
service: 'UK',
type: 'D',
reference: '160501255D0001'
}
];
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
I expect the it statement to return true.
The component is this one:
import { AssociatedExistingDespatch } from
'./../../../models/OrderDetails.model';
import { Component, OnInit } from '#angular/core';
import { TableColumn, TextAlignment, DataType } from
'#sharedModels/TableData.interface';
import { Subscription } from 'rxjs';
import { OrderDetailsService } from '#c3services/order-editing/order-details.service';
type AssociatedExistingDespatchData = {
deliveryAddress: string;
despatched: string;
fromLocation: string;
item: string;
quantity: number;
method: string;
reference: string;
returnsTrackingNumber: string;
service: string;
trackingNumber: string;
type: string;
};
const emptyDespatchNote = [
{
deliveryAddress: '',
despatched: '',
fromLocation: '',
item: '',
quantity: null,
method: '',
reference: '',
returnsTrackingNumber: '',
service: '',
trackingNumber: '',
type: ''
}
];
#Component({
selector: 'order-details-delivery-table',
templateUrl: './order-details-delivery-table.component.html',
styleUrls: ['./order-details-delivery-table.component.css']
})
export class OrderDetailsDeliveryTableComponent implements OnInit {
associatatedExistingDespatchesSubscription: Subscription[] = [];
loadingDetails: boolean;
orderDetailsDeliveryData: AssociatedExistingDespatch[];
associatedExistingDespatchData: AssociatedExistingDespatchData[] = emptyDespatchNote;
constructor(public orderDetailsService: OrderDetailsService) {}
ngOnInit() {
this.associatatedExistingDespatchesSubscription.push(
this.orderDetailsService.orderDetails.subscribe(({ loadingDetails, data }) => {
this.loadingDetails = loadingDetails;
if (!loadingDetails && data) {
this.orderDetailsDeliveryData =
this.orderDetailsService.getAssociatedExistingDespatches(data);
} else {
this.orderDetailsDeliveryData = emptyDespatchNote;
}
})
);
}
orderDetailsDeliveryHeader: TableColumn[] = [
{ value: 'item', label: 'Item', columnWidth: '100px' },
{
value: 'quantity',
label: 'Qty',
columnWidth: '35px',
textAlign: TextAlignment.Center
},
{
value: 'despatched',
label: 'Despatched',
type: DataType.Date,
columnWidth: '100px'
},
{
value: 'method',
label: 'Method',
columnWidth: '95px'
},
{ value: 'trackingNumber', label: 'Tracking No.', columnWidth: '100px' },
{
value: 'deliveryAddress',
label: 'Delivery Address',
columnWidth: '150px'
},
{
value: 'returnsTrackingNumber',
label: 'Returns Tracking No.',
columnWidth: '130px'
},
{ value: 'fromLocation', label: 'From Location', columnWidth: '150px' },
{ value: 'service', label: 'Service', columnWidth: '90px' },
{ value: 'type', label: 'Type', columnWidth: '40px' },
{ value: 'reference', label: 'Reference', columnWidth: '120px' }
];
}
You didnt call compileComponents(), you can try this :
beforeEach(async () => {
const configure: ConfigureFn = testBed => {
testBed.configureTestingModule({
declarations: [OrderDetailsDeliveryTableComponent],
providers: [{ provide: OrderDetailsService, useValue:
MockOrderDetailsService }],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
};

Categories

Resources