The Old County
The Old County

Reputation: 89

Reactsj and d3v4 integration -- Table and Pie chart application with jest testing

I am trying to create a basic integration example of react, d3 with jest testing. I've created a pie chart and table component - and I am keen to push data through from the main application into both the table and pie chart components. The pie chart should only become populated when the user has clicked on the table.

-- sandbox https://codesandbox.io/s/react-d3-pie-chart-2-ezq6y?file=/src/index.js - I've come up with this example

enter image description here

enter image description here

9th June - current integration.

data will look a little like this

[
    {
      "Country": "Afghanistan",
      "TotalConfirmed": 20917,
      "TotalDeaths": 369,
      "TotalRecovered": 2171,
    },
    {
      "Country": "Albania",
      "TotalConfirmed": 1263,
      "TotalDeaths": 34,
      "TotalRecovered": 945,
    },
    {
      "Country": "Algeria",
      "TotalConfirmed": 10265,
      "TotalDeaths": 715,
      "TotalRecovered": 6799,
    }
]

//App.js

import React from 'react';

import Pie from './Pie/Pie';
import Table from './Table/Table';
import './App.css';


class App extends React.Component {
  componentDidMount() {
    //get data 
  }

  render() {


    const data = [
      {
        "Country": "Afghanistan",
        "TotalConfirmed": 20917,
        "TotalDeaths": 369,
        "TotalRecovered": 2171,
      },
      {
        "Country": "Albania",
        "TotalConfirmed": 1263,
        "TotalDeaths": 34,
        "TotalRecovered": 945,
      },
      {
        "Country": "Algeria",
        "TotalConfirmed": 10265,
        "TotalDeaths": 715,
        "TotalRecovered": 6799,
      }
  ];



    const piedata =[
            {
                label: 'Jam',
                value: 50,
            },
            {
                label: 'Coconut',
                value: 10,
            },
            {
                label: 'Nutmeg',
                value: 20,
            },
            {
                label: 'Tumeric',
                value: 20,
            },
        ];


    return (
      <div className="App">    

        <div className="row">
          <div className="col col-6"> 
            <Table data={data} />
          </div>
          <div className="col col-6">
            <Pie 
              data={piedata} 
              width="300"
              height="300"
              r="110"
              ir="0"
            />
          </div>
        </div>
      </div>
    );
  }
}

export default App;

//Pie.js

import React from 'react';
import * as d3 from "d3";
import ReactDOM from 'react-dom';
import $ from 'jquery';
import './Pie.css';


class Pie extends React.Component {

    constructor(props) {
        super(props);
        this.myRef = React.createRef();
    }

    componentDidMount() {

                var $this = $(this.myRef.current);

                const data  = this.props.data;

                const width = parseInt(this.props.width,10),
                    height = parseInt(this.props.height,10),
                    radius = parseInt(this.props.r,10),
                    innerradius = parseInt(this.props.ir,10);


                var color = d3.scaleOrdinal().range(["#f0cf85", "#e7f0c3", "#a4d4ae", "#32afa9"]);


                var arc = d3.arc()
                    .outerRadius(radius - 10)
                    .innerRadius(innerradius);

                data.forEach(function(d) {
                    d.total = +d.value;
                });

                var pie = d3.pie()
                    .sort(null)
                    .value(function(d) { return d.total; });

                var svg = d3.select($this[0])
                    .append("svg")
                    .attr("width", width)
                    .attr("height", height)
                    .append("g")
                    .attr('class', 'piechart')
                    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

                var segments = svg.append('g').attr('class', 'segments');

                var slices = segments.selectAll(".arc")
                    .data(pie(data))
                    .enter().append("g")
                    .attr("class", "arc");

                slices.append("path")
                    .attr("d", arc)
                    .attr('fill', function(d, i) {
                        return color(i);
                    })

    }

    render() {

        return (
            <div 
                ref={this.myRef} 
                className="Pie"
            />
        );
    }
}
export default Pie;

//Table.js

import React from 'react';
import ReactDOM from 'react-dom';
import $ from 'jquery';
import './Table.css';


class Table extends React.Component {

    constructor(props) {
        super(props);
        this.myRef = React.createRef();
    }

    componentDidMount() {
        var $this = $(this.myRef.current);
    }

    rows(data) {
        return  (data.map((listValue, index) => {
          return (
            <tr key={index}>
              <td>{listValue.Country}</td>
              <td>{listValue.TotalConfirmed}</td>
              <td>{listValue.TotalDeaths}</td>
              <td>{listValue.TotalRecovered}</td>
            </tr>
          );
        }));
    }

    render() {
        return (
        <div 
            ref={this.myRef} 
            className="Table"
        >

          <table>
            <thead>
                <tr>
                    {Object.keys(this.props.data[0]).map((key, index) => {
                        return (<th key={index}>{key}</th>);
                    })}
                </tr>
            </thead>
            <tbody>
             {this.rows(this.props.data)}
            </tbody>
          </table>

        </div>
        );
    }
}
export default Table;

are these tests correct?

App.test.js

import React from 'react';
import { shallow } from 'enzyme';
import App from './App';
import Table from './Table/Table';
import Pie from './Pie/Pie';
import allData from './data.json';

fetch = jest.fn().mockImplementation(() => {
    const data = allData;

    data.json = jest.fn().mockImplementation(() => Promise.resolve(data));
    return Promise.resolve(data);
});
describe('App', () => {
    const wrapper = shallow(<App />);

    it('initial render', () => {
        expect(wrapper.find(Table).prop('cols')).toHaveLength(4);

        expect(wrapper.find(Pie).prop('data')).toEqual([
            {
                label: '',
                value: 100,
            },
        ]);
    });

    it('should update pie data when row is selected', () => {
        const row = wrapper.state('data')[0];
        const pieDataRefactor = wrapper.instance().pieDataRefactor(row);
        console.log(pieDataRefactor);
        wrapper.instance().rowSelected(row);

        expect(wrapper.find(Pie).prop('data')).toEqual(pieDataRefactor);
    });
});

Table.test.js

import React from 'react';
import Table from './Table';
import allData from '../data.json';
import { shallow } from 'enzyme';

const columns = [
    'Country',
    'CountryCode',
    'Slug',
    'NewConfirmed',
    'TotalConfirmed',
    'NewDeaths',
    'TotalDeaths',
    'NewRecovered',
    'TotalRecovered',
    'Date',
];
describe('Table', () => {
    const defaultProps = {
        cols: columns,
        data: allData.Countries,
        rowSelected: jest.fn(),
    };

    const wrapper = shallow(<Table {...defaultProps} />);

    it('should render table', () => {
        expect(wrapper.find('.Table th')).toHaveLength(10);
        expect(wrapper.find('.Table tbody tr')).toHaveLength(allData.Countries.length);
    });

    it('should call rowSelected', () => {
        const row = wrapper.find('.Table tbody tr').at(0);

        row.simulate('click');

        expect(defaultProps.rowSelected).toHaveBeenCalled();
        expect(defaultProps.rowSelected).toHaveBeenCalledWith(allData.Countries[0]);
    });
});

Pie.test.js

import React from 'react';
import Pie from './Pie';
import $ from 'jquery';
import * as d3 from 'd3';
import { mount } from 'enzyme';
describe('Pie', () => {
    const defaultProps = {
        data: [
            { label: 'TotalConfirmed', value: 20917 },
            { label: 'TotalDeaths', value: 369 },
            { label: 'TotalRecovered', value: 2171 },
        ],
        height: 200,
        width: 200,
        r: 30,
        ir: 10,
    };

    // const wrapper = mount(<Pie {...defaultProps} />);

    it('should render Pie', () => {
        const arcSpy = jest.spyOn(d3, 'arc');
        const selectSpy = jest.spyOn(d3, 'select');
        const scaleOrdinalSpy = jest.spyOn(d3, 'scaleOrdinal');
        const pieSpy = jest.spyOn(d3, 'pie');

        const wrapper = mount(<Pie {...defaultProps} />);
        expect(wrapper.find('.Pie')).toHaveLength(1);

        expect(arcSpy).toHaveBeenCalled();
        expect(pieSpy).toHaveBeenCalled();
        expect(selectSpy).toHaveBeenCalled();
        expect(scaleOrdinalSpy).toHaveBeenCalled();
    });
});

Upvotes: 1

Views: 856

Answers (1)

k5md
k5md

Reputation: 26

You forgot to use didUpdate listener, that's why the pie chart didn't update correctly. One should also clear d3-related stuff container (in this case) before each redraw.

https://codesandbox.io/s/react-d3-pie-chart-2-qezz7?file=/src/Pie/Pie.js

Upvotes: 1

Related Questions