Enzyme setState not re-rendering the component - reactjs

I am trying to set a state using enzyme method. After setting a state inside test case I was able to get it back again within the test case which proves that setState is working and I can see the expected output in console. However, inside component I was not able to get state which was set by enzyme because of this my is failing and I am not getting desire view back from component, here is screenshot. You can see there is not data grid table rendered despite having rows data.
table.js
import React, { Component } from 'react';
import styled from 'styled-component';
import ReactDataGrid from 'react-data-grid';
import { Filters } from 'react-data-grid-addons';
import PropTypes from 'prop-types';
export class Table extends Component {
constructor(props) {
super(props);
const {
NumericFilter,
AutoCompleteFilter,
} = Filters;
this.columns = [
{ key: "id", name: "ID", editable: true },
{ key: "title", name: "Title", editable: true },
{ key: "complete", name: "Complete", editable: true }
];
this.state = {
rows: [],
};
}
renderRow = (i) => {
const { rows } = this.state;
return rows[i];
}
render() {
const { className } = this.props;
const { rows } = this.state;
console.log('rows Length', rows.length) // always 0
return (
<div className={className}>
{rows.length
? (
<ReactDataGrid
rowHeight={50}
columns={this.columns}
rowGetter={this.renderRow}
rowsCount={rows.length}
/>
)
: <span id="no-product-message">No Items to be Shown</span>
}
</div>
);
}
}
Table.propTypes = {
className: PropTypes.string,
};
Table.defaultProps = {
className: '',
};
export default styled(Table)`
.react-grid-HeaderCell{
white-space: normal !important;
}
.react-grid-Cell__value{
display: flex;
align-items: center;
}
`;
mountWithTheme
export const mountWithTheme = (children, options) => (
mount(<ThemeProvider theme={theme}>{children}</ThemeProvider>, options)
);
test case
it('should render table if product data is available', () => {
const wrapper = mountWithTheme(<Table />).find(Table);
const instance = wrapper.instance();
jest.spyOn(instance, 'renderRow');
instance.setState({
rows: [{ id: 0, title: "Task 1", complete: 20 }],
});
console.log(instance.state.rows) // [{ id: 0, title: "Task 1", complete: 20 }]
expect(wrapper.find('ReactDataGrid').exists()).toBe(true);
expect(instance.renderRow).toHaveBeenCalledTimes(1);
});

Related

Converting DraftJS class component to Functional component

The following Draftjs code is in class component. The plugins like CreateImage, Focus Plugin, and BlockDndPlugin are being imported from the DraftJS. I would be grateful if somebody can convert the class-based react components into Functional based react components...............................................................................................................................................................................................................................................................................................
import React, { Component } from 'react';
import { convertFromRaw, EditorState } from 'draft-js';
import Editor, { composeDecorators } from '#draft-js-plugins/editor';
import createImagePlugin from '#draft-js-plugins/image';
import createFocusPlugin from '#draft-js-plugins/focus';
import createBlockDndPlugin from '#draft-js-plugins/drag-n-drop';
import editorStyles from './editorStyles.module.css';
const focusPlugin = createFocusPlugin();
const blockDndPlugin = createBlockDndPlugin();
const decorator = composeDecorators(
focusPlugin.decorator,
blockDndPlugin.decorator
);
const imagePlugin = createImagePlugin({ decorator });
const plugins = [blockDndPlugin, focusPlugin, imagePlugin];
/* eslint-disable */
const initialState = {
entityMap: {
0: {
type: 'IMAGE',
mutability: 'IMMUTABLE',
data: {
src: '/images/canada-landscape-small.jpg',
},
},
},
blocks: [
{
key: '9gm3s',
text:
'You can have images in your text field which are draggable. Hover over the image press down your mouse button and drag it to another position inside the editor.',
type: 'unstyled',
depth: 0,
inlineStyleRanges: [],
entityRanges: [],
data: {},
},
{
key: 'ov7r',
text: ' ',
type: 'atomic',
depth: 0,
inlineStyleRanges: [],
entityRanges: [
{
offset: 0,
length: 1,
key: 0,
},
],
data: {},
},
{
key: 'e23a8',
text:
'You can checkout the alignment tool plugin documentation to see how to build a compatible block plugin …',
type: 'unstyled',
depth: 0,
inlineStyleRanges: [],
entityRanges: [],
data: {},
},
],
};
/* eslint-enable */
export default class CustomImageEditor extends Component {
state = {
editorState: EditorState.createWithContent(convertFromRaw(initialState)),
};
onChange = (editorState) => {
this.setState({
editorState,
});
};
focus = () => {
this.editor.focus();
};
render() {
return (
<div>
<div className={editorStyles.editor} onClick={this.focus}>
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
plugins={plugins}
ref={(element) => {
this.editor = element;
}}
/>
</div>
</div>
);
}
}
You can use useState and useRef hooks in FC & Modify your component accordingly..
const CustomImageEditor = () => {
const [editorState, setEditorState] = useState(
EditorState.createWithContent(convertFromRaw(initialState))
);
const editor = useRef();
const onChange = (editorStates) => {
setEditorState(editorStates);
};
const focus = () => {
editor.current.focus();
};
return (
<div>
<div className={editorStyles.editor} onClick={focus}>
<Editor
editorState={editorState}
onChange={onChange}
plugins={plugins}
ref={editor}
/>
</div>
</div>
);
};

Row level operations on react-table: React Js

I am trying to implement a feature where in on click of each row, I need to get the info of the row that is been clicked and corresponding row color should be changed to something else. And when I select multiple rows using Ctrl+mouse-click the corresponding rows color should also get changed, and need to get the info of corresponding rows in the form of array. I need to have a common getTrProps function that should implement both of this . Can someone help me out with this
Codesandbox: https://codesandbox.io/s/react-table-row-table-0bbyi
App
import * as React from "react";
import { render } from "react-dom";
import DataGrid from "./DataGrid";
import { Column } from "react-table";
interface IProps {}
interface IState {
data: IUser[];
columns: Column<IUser>[];
}
export interface IUser {
firstName: string;
status: "Submitted" | "Pending" | "Approved";
age: string;
}
export interface IColum {
Header: string;
accessor: string;
}
class App extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
data: [],
columns: []
};
}
componentDidMount() {
this.getData();
this.getColumns();
}
getData = () => {
const data: IUser[] = [
{ firstName: "Jack", status: "Submitted", age: "14" },
{ firstName: "Simon", status: "Pending", age: "15" },
{ firstName: "Pete", status: "Approved", age: "17" }
];
this.setState({ data });
};
getColumns = () => {
const columns: IColum[] = [
{
Header: "First Name",
accessor: "firstName"
},
{
Header: "Status",
accessor: "status"
},
{
Header: "Age",
accessor: "age"
}
];
this.setState({ columns });
};
onClickRow = (rowInfo: IUser) => {
console.log("You clicked: " + JSON.stringify(rowInfo));
};
render() {
return (
<>
<DataGrid
data={this.state.data}
columns={this.state.columns}
rowClicked={this.onClickRow}
/>
<DataGrid data={this.state.data} columns={this.state.columns} />
</>
);
}
}
DataGrid
import * as React from "react";
import ReactTable, {
RowInfo,
Column,
ComponentPropsGetterR
} from "react-table";
import "react-table/react-table.css";
import { IUser, IColum } from ".";
interface IProps {
data: IUser[];
columns: Column<IUser>[];
// The ? makes it optional
rowClicked?: (user: IUser) => void;
}
export default class DataGrid extends React.Component<IProps> {
rowClick: ComponentPropsGetterR = (state: any, rowInfo: RowInfo) => {
const rowClicked = this.props.rowClicked;
return rowClicked
? {
onClick: () => rowClicked(rowInfo.original as IUser)
}
: {};
};
render() {
return (
<ReactTable
data={this.props.data}
columns={this.props.columns}
getTrProps={this.rowClick}
/>
);
}
}
Here is Your Final Answer :
https://codesandbox.io/s/react-table-row-table-3xwxi
you can now hold Ctrl Key and Select as many row as you want and you can toggle between.
and if you don't hold the key you can select one
you can see each time you choose a row color of the row Changes.
and you have all the data in this.state.allData.
and all of this in typescript as you want from your sandbox.

Data does't appear in datatables

Data tidak tampil pada datatables
import React, { Component } from 'react';
import Linkify from 'react-linkify';
import './CobaData.css';
import './css/jquery.dataTables.css';
const $ = require('jquery')
$.Datatable = require('datatables.net')
export default class CobaData extends Component {
constructor(props) {
super(props);
}
render() {
let data = this.props.data;
let printData = data;
console.log(printData);
let table = $('#exc').dataTable(
{
data: printData,
columns: [
{ title: "ID" },
{ title: "Nama" },
{ title: "Nomor KTP" },
{ title: "Nomor HP" },
],
searchable: false,
orderable: false,
targets: 0,
}
);
console.log(table);
return (
<div>
<table className="display" width="100%" id="exc" >
</table>
</div>
);
}
}
data can't display datatables, when in console.log(printData) can be read in 'console'
before I used a normal table can be read on the table, but when using datatables on the data can not be read on the table
Using jQuery in a react app is not a good approach.
Instead I would recommend you to use react-data-table-component.
And then your Component would be like this:
import React, { Component } from 'react';
import DataTable from 'react-data-table-component';
import Linkify from 'react-linkify';
import './CobaData.css';
const columns = [
{
name: 'ID',
selector: 'id_karyawan'
},
{
name: 'Nama',
selector: 'nama'
},
{
name: 'Nomor KTP',
selector: 'KTP'
},
{
name: 'Nomor HP',
selector: 'no_hp'
},
];
export default class CobaData extends Component {
render() {
let data = this.props.data;
return (
<DataTable
title="Arnold Movies"
columns={columns}
data={data}
/>
);
}
}

Footer in react-table is giving 'Reference Error-data is not defined'

I am making API call and displaying the results in React-Table. What i want to show additionally is in footer i want to show sum of column values for column totalCount. I have given below my code and i am getting
ReferenceError: data is not defined in
Footer: <span>{_.sum(_.map(data, d => d.totalCount))}</span>
My Sample Json data from API would be
[
{
"receiveDate": "2019-01-28",
"hour": "00",
"integrationName": "lms_job",
"totalCount": 61
},
{
"receiveDate": "2019-01-28",
"hour": "01",
"integrationName": "lms_job",
"totalCount": 43
}
]
If i move const columns block inside the class BalancingTable extends React.Component, then it works as expected. But I have also other code which is to download data in CSV and that logic demands me to put the const columns out side of the app.
So If I remove these 2 lines, code still works with out footer logic.
Cell: props => <span>{props.value}</span>,
Footer: <span>{_.sum(_.map(data, d => d.totalCount))}</span>
As i wanted to keep const columns outside of the app, Is there a way to resolve this issue which provides sum of columns as needed?
import React from 'react'
import { bindActionCreators } from 'redux'
import PropTypes, { object } from 'prop-types'
import { withStyles } from '#material-ui/core/styles'
import Button from '#material-ui/core/Button'
import axios from 'axios'
import ReactTable from 'react-table'
import '../FirmJson/ReactTableCustom.css'
import _ from "lodash";
const styles = theme => ({
})
const columns = [
{
Header: 'Date',
accessor: 'receiveDate',
width: 80,
},
{
Header: 'Hour',
accessor: 'hour',
width: 50,
},
{
Header: 'Integration',
accessor: 'integrationName',
width: 140,
},
{
Header: 'Total Count',
accessor: 'totalCount',
width: 105,
id: 'totalCount',
Cell: props => <span>{props.value}</span>,
Footer: <span>{_.sum(_.map(data, d => d.totalCount))}</span>
},
]
class BalancingTable extends React.Component {
constructor (props) {
super(props)
this.state = {
data: [],
isLoaded: false,
}
this.handleSubmit = this.handleSubmit.bind(this)
}
handleSubmit (index) {
let url = 'https://myapp.company.com/balancing/lms/2019-01-29'
axios.get(url).then(response => {
this.setState({
data: response.data,
isLoaded: true,
})
}).catch()
}
render () {
var {isLoaded,data} = this.state
const { classes } = this.props
if (isLoaded) {
return (
<div>
<div>
<ReactTable ref={(r) => this.reactTable = r}
columns={columns}
data={data}
filterable
defaultFilterMethod={(filter, row) =>
String(row[filter.id]).toLowerCase().includes(filter.value.toLowerCase())}
style={{
minWidth: '800px',
border: '50px',
}}
/>
</div>
</div>
)
}
else
{
return(
<div>
<Button variant="raised" color="primary" aria-label="ret" onClick={this.handleSubmit}>
Submit
</Button>
</div>
)
}
}
}
BalancingTable.propTypes = {
classes: PropTypes.object.isRequired,
}
export default (withStyles(styles)(BalancingTable))

Upgrading react-virtualized gives error: Super expression must either be null or a function, not undefined

I upgraded from react-virtualized 8.11 --> 9.1, and receiving the above error. Accounting for the docs breaking changes:
1- I'm using React version 0.15.X
2- I'm not using the CellMeasurer component.
Have any of you experienced a breaking change upgrading to react-virtualized 9 outside of the above mentioned? I have made no other changes when upgrading.
VirtualizedTable.js
import React, { Component, PropTypes } from 'react';
import { AutoSizer, Table, Column, defaultTableRowRenderer } from 'react-virtualized';
import classnames from 'classnames';
import { Input } from 'react-bootstrap';
import 'react-virtualized/styles.css';
// import '../sass/components/virtualized-table.scss';
export default class VirtualizedTable extends Component {
static propTypes = {
schema: PropTypes.shape({
instanceType: PropTypes.string,
searchable: PropTypes.bool,
skeleton: PropTypes.arrayOf(PropTypes.shape({
key: PropTypes.string,
label: PropTypes.string,
display: PropTypes.string,
sortable: PropTypes.bool
}))
}).isRequired,
data: PropTypes.arrayOf(PropTypes.object).isRequired,
onRowClick: PropTypes.func,
rowClassName: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
rowRenderFn: PropTypes.func,
statusRenderFn: PropTypes.func,
actionsRenderFn: PropTypes.func,
sortBy: PropTypes.string,
sortDirection: PropTypes.oneOf(['ASC', 'DESC']),
maxHeight: PropTypes.number
};
constructor(props) {
super(props);
this.state = {
sortBy: 'name',
sortDirection: 'ASC',
tableFilter: ''
};
this.sort = this.sort.bind(this);
this.onFilterChange = this.onFilterChange.bind(this);
}
componentDidMount() {
if (this.props.sortBy) {
this.setState({
sortBy: this.props.sortBy
});
}
if (this.props.sortDirection) {
this.setState({
sortDirection: this.props.sortDirection
});
}
}
sort({ sortBy, sortDirection }) {
if (this.state.sortBy !== sortBy) {
this.setState({ sortBy });
} else {
this.setState({ sortBy, sortDirection });
}
}
onFilterChange (e) {
this.setState({ tableFilter: e.target.value });
}
escapeRegex (str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
}
render() {
const { showFilter } = this.props;
let sortedList = this.props.data;
if (this.state.tableFilter) {
let regex = new RegExp(this.escapeRegex(this.state.tableFilter), 'i');
sortedList = this.props.data.filter(
item => {
let bool = false;
for (let key in item) {
bool = (regex.test(item[key]));
if (bool) break;
}
if (bool) return item;
}
);
}
if (this.state.sortBy) {
sortedList = sortedList.sort(
(a, b) => typeof a[this.state.sortBy] === 'string' ?
a[this.state.sortBy] < b[this.state.sortBy]
:
a[this.state.sortBy] - b[this.state.sortBy]
);
if (this.state.sortDirection === 'DESC') {
sortedList.reverse();
}
}
let columns = this.props.schema.skeleton.filter(item => item.display !== 'hidden');
const rowHeight = this.props.rowHeight || 40;
const headerHeight = this.props.headerHight || 40;
return (
<div className='table-container'>
{
(showFilter) ?
<div className='ac-filter-container' style={{ width: '15%' }}>
<Input
type='text'
onChange={this.onFilterChange.bind(this)}
value={this.state.tableFilter}
placeholder={this.props.filterText || 'Filter results...'}
/>
</div>
:
null
}
<AutoSizer disableHeight>
{({ width: autoWidth }) => {
// Use Static width if provided - NOTE: For Testing Purposes
const width = this.props.width || autoWidth;
return (
<Table
className={classnames('table', {'collapsed': width < 1000})}
width={width}
height={(sortedList.length + 1) * rowHeight > this.props.maxHeight ? this.props.maxHeight : (sortedList.length + 1) * rowHeight}
headerHeight={headerHeight}
rowHeight={rowHeight}
rowClassName={this.props.rowClassName}
rowRenderer={this.props.rowRenderFn || defaultTableRowRenderer}
rowCount={sortedList.length}
rowGetter={({ index }) => sortedList[index]}
sort={this.sort}
sortBy={this.state.sortBy}
sortDirection={this.state.sortDirection}
onRowClick={({index}) => this.props.onRowClick(this.props.data[index])}
>
{
columns.map((column, i) => {
return (
<Column
key={i}
label={column.label}
dataKey={column.key}
disableSort={column.sortable}
width={width / columns.length}
/>
);
})
}
</Table>
);
}}
</AutoSizer>
</div>
);
}
}
TablContainer.js
import React, {Component} from 'react';
// Components
import VirtualizedTable from '../../components/VirtualizedTable3';
import {objects} from '../objects';
import shouldComponentUpdate from '../../utils/shouldComponentUpdate';
export default class TableContainer extends Component {
constructor(props) {
super(props);
this.shouldComponentUpdate = shouldComponentUpdate.bind(this);
}
render() {
let schema = {
instanceType: 'integer',
searchable: true,
skeleton: [{
key: 'id',
label: 'id',
display: 'true',
sotrable: true
}, {
key: 'name',
label: 'name',
display: 'true',
sotrable: true
}]
};
return (
<div className='container'>
<h1>React Virtualized Table</h1>
{
<VirtualizedTable
schema={schema}
data={objects}
onRowClick={() => console.log('U did it!')}
sortByDefault='id'
sortDirection='DESC'
/>
}
</div>
);
}
}
The 9.0.0 release notes call out 2 breaking changes: CellMeasurer and the required/supported React version:
Supported React versions have changed. Previously all React 14 and 15 releases were supported. Going forward only React 15.3 and newer will be supported. This was done to drop a peer dependency on react-addons-shallow-compare and replace it with React.PureComponent as the docs suggest.
It would appear that you're not using the required React version. (NPM/Yarn should be warning you about an invalid peer dependency when you install.)

Resources