Completed
Push — master ( c8ba67...05deb1 )
by Alejandro
21s queued 12s
created

SortableBarGraph.render   B

Complexity

Conditions 2

Size

Total Lines 44
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 39
dl 0
loc 44
ccs 8
cts 8
cp 1
rs 8.9439
c 0
b 0
f 0
cc 2
crap 2
1
import React from 'react';
2
import PropTypes from 'prop-types';
3
import { fromPairs, head, keys, pipe, prop, reverse, sortBy, splitEvery, toLower, toPairs, type } from 'ramda';
4
import SortingDropdown from '../utils/SortingDropdown';
5
import PaginationDropdown from '../utils/PaginationDropdown';
6
import { rangeOf } from '../utils/utils';
7
import { roundTen } from '../utils/helpers/numbers';
8
import SimplePaginator from '../common/SimplePaginator';
9
import GraphCard from './GraphCard';
10
11 2
const { max } = Math;
12 8
const toLowerIfString = (value) => type(value) === 'String' ? toLower(value) : value;
13 320
const pickValueFromPair = ([ , value ]) => value;
14
15
export default class SortableBarGraph extends React.Component {
16 2
  static propTypes = {
17
    stats: PropTypes.object.isRequired,
18
    highlightedStats: PropTypes.object,
19
    title: PropTypes.string.isRequired,
20
    sortingItems: PropTypes.object.isRequired,
21
    extraHeaderContent: PropTypes.func,
22
    withPagination: PropTypes.bool,
23
  };
24
25 9
  state = {
26
    orderField: undefined,
27
    orderDir: undefined,
28
    currentPage: 1,
29
    itemsPerPage: Infinity,
30
  };
31
32
  determineStats(stats, sortingItems) {
33 17
    const pairs = toPairs(stats);
34 17
    const sortedPairs = !this.state.orderField ? pairs : sortBy(
35
      pipe(
36
        prop(this.state.orderField === head(keys(sortingItems)) ? 0 : 1),
37
        toLowerIfString
38
      ),
39
      pairs
40
    );
41 17
    const directionalPairs = !this.state.orderDir || this.state.orderDir === 'ASC' ? sortedPairs : reverse(sortedPairs);
42
43 17
    if (directionalPairs.length <= this.state.itemsPerPage) {
44 15
      return { currentPageStats: fromPairs(directionalPairs) };
45
    }
46
47 2
    const pages = splitEvery(this.state.itemsPerPage, directionalPairs);
48
49 2
    return {
50
      currentPageStats: fromPairs(this.determineCurrentPagePairs(pages)),
51
      pagination: this.renderPagination(pages.length),
52
      max: roundTen(max(...directionalPairs.map(pickValueFromPair))),
53
    };
54
  }
55
56
  determineCurrentPagePairs(pages) {
57 2
    const page = pages[this.state.currentPage - 1];
58
59 2
    if (this.state.currentPage < pages.length) {
60 2
      return page;
61
    }
62
63
    const firstPageLength = pages[0].length;
64
65
    // Using the "hidden" key, the chart will just replace the label by an empty string
66
    return [ ...page, ...rangeOf(firstPageLength - page.length, (i) => [ `hidden_${i}`, 0 ]) ];
67
  }
68
69
  renderPagination(pagesCount) {
70 2
    const { currentPage } = this.state;
71 2
    const setCurrentPage = (currentPage) => this.setState({ currentPage });
72
73 2
    return <SimplePaginator currentPage={currentPage} pagesCount={pagesCount} setCurrentPage={setCurrentPage} />;
74
  }
75
76
  render() {
77 17
    const { stats, sortingItems, title, extraHeaderContent, highlightedStats, withPagination = true } = this.props;
78 17
    const { currentPageStats, pagination, max } = this.determineStats(stats, sortingItems);
79 17
    const activeCities = keys(currentPageStats);
80 17
    const computeTitle = () => (
81 8
      <React.Fragment>
82
        {title}
83
        <div className="float-right">
84
          <SortingDropdown
85
            isButton={false}
86
            right
87
            items={sortingItems}
88
            orderField={this.state.orderField}
89
            orderDir={this.state.orderDir}
90 4
            onChange={(orderField, orderDir) => this.setState({ orderField, orderDir, currentPage: 1 })}
91
          />
92
        </div>
93
        {withPagination && keys(stats).length > 50 && (
94
          <div className="float-right">
95
            <PaginationDropdown
96
              toggleClassName="btn-sm paddingless mr-3"
97
              ranges={[ 50, 100, 200, 500 ]}
98
              value={this.state.itemsPerPage}
99 4
              setValue={(itemsPerPage) => this.setState({ itemsPerPage, currentPage: 1 })}
100
            />
101
          </div>
102
        )}
103
        {extraHeaderContent && (
104
          <div className="float-right">
105
            {extraHeaderContent(pagination ? activeCities : undefined)}
106
          </div>
107
        )}
108
      </React.Fragment>
109
    );
110
111 17
    return (
112
      <GraphCard
113
        isBarChart
114
        title={computeTitle}
115
        stats={currentPageStats}
116
        footer={pagination}
117
        max={max}
118
        highlightedStats={highlightedStats}
119
      />
120
    );
121
  }
122
}
123