packages/issue-dashboard-widgets/widgets/distribution-reports/app/report-chart.js (292 lines of code) (raw):
import './style/report-chart.scss';
import React from 'react';
import PropTypes from 'prop-types';
import ButtonGroup from '@jetbrains/ring-ui/components/button-group/button-group';
import Button from '@jetbrains/ring-ui/components/button/button';
import {i18n} from 'hub-dashboard-addons/dist/localization';
import FilterFieldValue from '../../../../lib/reporting-components/filter-field-value/filter-field-value';
import ReportModel from '../../../../lib/reporting-components/report-model/report-model';
import ReportChartSortOrder from './report-chart-sort-order';
import PieChartPresentation from './pie-chart-presentation';
import BarsChartPresentation from './bars-chart-presentation';
import MatrixPresentation from './matrix-presentation';
import DistributionReportModel from './distribution-report-model';
import './nv-flex-pie-chart';
const X_AXIS_HEIGHT = 22;
class ReportChart extends React.Component {
static PresentationModes = {
Bars: 'DEFAULT',
Table: 'MATRIX',
Pie: 'PIE'
};
static LineHeight = 22; // eslint-disable-line no-magic-numbers
static getBarsChartHeight = columns =>
ReportChart.LineHeight * columns.length + X_AXIS_HEIGHT;
static propTypes = {
reportData: PropTypes.object,
presentationMode: PropTypes.string,
reportMainSortOrder: PropTypes.string,
reportSecondarySortOrder: PropTypes.string,
reportMainAxisLabel: PropTypes.string,
reportSecondaryAxisLabel: PropTypes.string,
aggregationTitle: PropTypes.string,
onChangeSortOrders: PropTypes.func,
onChangePresentationMode: PropTypes.func,
homeUrl: PropTypes.string
};
constructor(props) {
super(props);
this.state = {
reportData: props.reportData,
reportMainSortOrder: props.reportMainSortOrder,
reportSecondarySortOrder: props.reportSecondarySortOrder,
reportMainAxisLabel: props.reportMainAxisLabel,
reportSecondaryAxisLabel: props.reportSecondaryAxisLabel
};
}
// eslint-disable-next-line camelcase
UNSAFE_componentWillReceiveProps(props) {
if (props.reportData) {
this.setState(
{reportData: props.reportData},
() => this.drawBarChart()
);
}
}
onChangeMainSortOrder = newMainSortOrder => {
this.setState({
reportMainSortOrder: newMainSortOrder
}, () =>
this.props.onChangeSortOrders && this.props.onChangeSortOrders(
newMainSortOrder, this.state.reportSecondarySortOrder
)
);
};
onChangeSecondarySortOrder = newSecondarySortOrder => {
this.setState({
reportSecondarySortOrder: newSecondarySortOrder
}, () =>
this.props.onChangeSortOrders && this.props.onChangeSortOrders(
this.state.reportMainSortOrder, newSecondarySortOrder
)
);
};
isActiveLineIndex = idx =>
(this.state.activeLineIdx === idx &&
this.props.presentationMode === ReportChart.PresentationModes.Table);
setActiveLineIndex = activeLineIdx =>
this.setState({activeLineIdx});
clearActiveLineIndex = () =>
this.setState({activeLineIdx: null});
getOnLineMouseOverCallback = lineIdx =>
() => this.setActiveLineIndex(lineIdx);
renderLineLabel(column, idx) {
const isActiveIdx = this.isActiveLineIndex(idx);
return (
<div
key={`report-label-${column.name}`}
className={`report-chart__line-label${isActiveIdx ? ' report-chart__line-label_active' : ''}`}
onMouseOver={this.getOnLineMouseOverCallback(idx)}
>
<FilterFieldValue
value={column}
homeUrl={this.props.homeUrl}
/>
</div>
);
}
renderLineSize(column, idx) {
const sizePresentation = `${ReportModel.getSizePresentation(column.size)}`;
if (`${ReportModel.getSizeValue(column.size)}` !== sizePresentation) {
return '';
}
const isActiveIdx = this.isActiveLineIndex(idx);
return (
<div
className={`report-chart__size${isActiveIdx ? ' report-chart__size_active' : ''}`}
onMouseOver={this.getOnLineMouseOverCallback(idx)}
key={`report-label-size-${idx}`}
>
{sizePresentation}
</div>
);
}
renderLinePercents(column, totalCount, idx) {
const toPercentsMultiplier = 100;
const getSizeInPercents = size => (totalCount
? `${Math.round(ReportModel.getSizeValue(size) / totalCount * toPercentsMultiplier)}%`
: '');
const isActiveIdx = this.isActiveLineIndex(idx);
return (
<div
className={`report-chart__size-in-percents${isActiveIdx ? ' report-chart__size-in-percents_active' : ''}`}
onMouseOver={this.getOnLineMouseOverCallback(idx)}
key={`report-label-percents-${idx}`}
>
{getSizeInPercents(column.size)}
</div>
);
}
renderPieChart() {
return (
<PieChartPresentation
reportData={this.props.reportData}
homeUrl={this.props.homeUrl}
/>
);
}
renderBarsChart(height) {
return (
<BarsChartPresentation
reportData={this.props.reportData}
height={height}
homeUrl={this.props.homeUrl}
/>
);
}
renderTable() {
return (
<MatrixPresentation
reportData={this.props.reportData}
homeUrl={this.props.homeUrl}
activeLineIdx={this.state.activeLineIdx}
onActivateLine={this.setActiveLineIndex}
onResetActiveLine={this.clearActiveLineIndex}
/>
);
}
renderChartBody(chartHeight) {
if (this.props.presentationMode === ReportChart.PresentationModes.Bars) {
return this.renderBarsChart(chartHeight);
}
if (this.props.presentationMode === ReportChart.PresentationModes.Pie) {
return this.renderPieChart();
}
return this.renderTable();
}
renderLinesLabels() {
const reportData = this.state.reportData || {};
const columns = reportData.ycolumns || reportData.columns || [];
const chartHeight = ReportChart.getBarsChartHeight(columns);
const totalCount = ReportModel.getSizeValue(reportData.total);
return (
<div>
<div className="report-chart__body-wrapper">
<div
className="report-chart__labels"
style={{height: chartHeight}}
onMouseLeave={this.clearActiveLineIndex}
>
<div className="report-chart__labels-column">
{
columns.map((column, idx) =>
this.renderLineLabel(column, idx)
)
}
</div>
<div className="report-chart__labels-column">
{
columns.map((column, idx) =>
this.renderLineSize(column, idx)
)
}
</div>
<div className="report-chart__labels-column">
{
columns.map((column, idx) =>
this.renderLinePercents(column, totalCount, idx)
)
}
</div>
</div>
{this.renderChartBody(chartHeight)}
</div>
</div>
);
}
renderChartArea() {
if (this.props.presentationMode === ReportChart.PresentationModes.Pie) {
return this.renderPieChart();
}
return this.renderLinesLabels();
}
render() {
const {presentationMode: mode} = this.props;
const reportData = this.state.reportData || {};
const title = `${this.props.aggregationTitle || i18n('Total')}: ${ReportModel.getSizePresentation(reportData.total)}`;
const isTwoDimensionalChart =
DistributionReportModel.isStackedChart(reportData);
const getOnChangeReportPresentationCallback = presentationMode =>
() => this.props.onChangePresentationMode(
presentationMode
);
return (
<div className="report-chart">
<div
className="report-chart__title"
style={{height: ReportChart.LineHeight, lineHeight: `${ReportChart.LineHeight}px`}}
>
<div className="report-chart__label report-chart__label_title">
{title}
</div>
<div className="report-chart__title-settings">
<ButtonGroup>
<Button
className="report-chart__chart-type-switcher"
active={mode === ReportChart.PresentationModes.Bars}
onClick={getOnChangeReportPresentationCallback(
ReportChart.PresentationModes.Bars
)}
>
{i18n('Bars')}
</Button>
{
isTwoDimensionalChart && (
<Button
className="report-chart__chart-type-switcher"
active={mode === ReportChart.PresentationModes.Table}
onClick={getOnChangeReportPresentationCallback(
ReportChart.PresentationModes.Table
)}
>
{i18n('Table')}
</Button>
)}
{
!isTwoDimensionalChart && (
<Button
className="report-chart__chart-type-switcher"
active={mode === ReportChart.PresentationModes.Pie}
onClick={getOnChangeReportPresentationCallback(
ReportChart.PresentationModes.Pie
)}
>
{i18n('Pie chart')}
</Button>
)}
</ButtonGroup>
</div>
</div>
<div className="report-chart__sort-order-bar">
<div className="report-chart__sort-order report-chart__sort-order_main">
<ReportChartSortOrder
sortOrder={this.state.reportMainSortOrder}
onChange={this.onChangeMainSortOrder}
orientation={ReportChartSortOrder.Orientation.Vertical}
/>
{
this.state.reportMainAxisLabel && (
<span className="report-chart__sort-order-label">
{`(${this.state.reportMainAxisLabel})`}
</span>
)}
</div>
<div className="report-chart__sort-order report-chart__sort-order_secondary">
<ReportChartSortOrder
sortOrder={this.state.reportSecondarySortOrder}
onChange={this.onChangeSecondarySortOrder}
orientation={ReportChartSortOrder.Orientation.Horizontal}
/>
{
this.state.reportSecondaryAxisLabel && (
<span className="report-chart__sort-order-label">
{`(${this.state.reportSecondaryAxisLabel})`}
</span>
)}
</div>
</div>
{this.renderChartArea()}
</div>
);
}
}
export default ReportChart;