123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- // Import to ensure echarts components are loaded.
- import './components/markPoint';
- import * as React from 'react';
- import set from 'lodash/set';
- import {getFormattedDate} from 'app/utils/dates';
- import theme from 'app/utils/theme';
- import BarChart, {BarChartSeries} from './barChart';
- import BaseChart from './baseChart';
- import {truncationFormatter} from './utils';
- type Marker = {
- name: string;
- value: string | number | Date;
- color: string;
- symbolSize?: number;
- };
- const defaultProps = {
- /**
- * Colors to use on the chart.
- */
- colors: [theme.gray200, theme.purple300, theme.purple300] as string[],
- /**
- * Show max/min values on yAxis
- */
- labelYAxisExtents: false,
- /**
- * Whether not the series should be stacked.
- *
- * Some of our stats endpoints return data where the 'total' series includes
- * breakdown data (issues). For these results `stacked` should be false.
- * Other endpoints return decomposed results that need to be stacked (outcomes).
- */
- stacked: false,
- };
- type ChartProps = React.ComponentProps<typeof BaseChart>;
- type BarChartProps = React.ComponentProps<typeof BarChart>;
- type Props = Omit<ChartProps, 'series'> &
- typeof defaultProps & {
- /**
- * A list of series to be rendered as markLine components on the chart
- * This is often used to indicate start/end markers on the xAxis
- */
- markers?: Marker[];
- /**
- * Whether timestamps are should be shown in UTC or local timezone.
- */
- utc?: boolean;
- /**
- * A list of colors to use on hover.
- * By default hover state will shift opacity from 0.6 to 1.0.
- * You can use this prop to also shift colors on hover.
- */
- emphasisColors?: string[];
- /**
- * Delay time for hiding tooltip, in ms.
- */
- hideDelay?: number;
- /**
- * Function to format tooltip values
- */
- tooltipFormatter?: (value: number) => string;
- series?: BarChartProps['series'];
- };
- class MiniBarChart extends React.Component<Props> {
- static defaultProps = defaultProps;
- render() {
- const {
- markers,
- emphasisColors,
- colors,
- series: _series,
- labelYAxisExtents,
- stacked,
- series,
- hideDelay,
- tooltipFormatter,
- ...props
- } = this.props;
- const {ref: _ref, ...barChartProps} = props;
- let chartSeries: BarChartSeries[] = [];
- // Ensure bars overlap and that empty values display as we're disabling the axis lines.
- if (series && series.length) {
- chartSeries = series.map((original, i: number) => {
- const updated = {
- ...original,
- cursor: 'normal',
- type: 'bar',
- } as BarChartSeries;
- if (i === 0) {
- updated.barMinHeight = 1;
- if (stacked === false) {
- updated.barGap = '-100%';
- }
- }
- if (stacked) {
- updated.stack = 'stack1';
- }
- set(updated, 'itemStyle.color', colors[i]);
- set(updated, 'itemStyle.opacity', 0.6);
- set(updated, 'itemStyle.emphasis.opacity', 1.0);
- set(updated, 'itemStyle.emphasis.color', emphasisColors?.[i] ?? colors[i]);
- return updated;
- });
- }
- if (markers) {
- const markerTooltip = {
- show: true,
- trigger: 'item',
- formatter: ({data}) => {
- const time = getFormattedDate(data.coord[0], 'MMM D, YYYY LT', {
- local: !this.props.utc,
- });
- const name = truncationFormatter(data.name, props?.xAxis?.truncate);
- return [
- '<div class="tooltip-series">',
- `<div><span class="tooltip-label"><strong>${name}</strong></span></div>`,
- '</div>',
- '<div class="tooltip-date">',
- time,
- '</div>',
- '</div>',
- '<div class="tooltip-arrow"></div>',
- ].join('');
- },
- };
- const markPoint = {
- data: markers.map((marker: Marker) => ({
- name: marker.name,
- coord: [marker.value, 0],
- tooltip: markerTooltip,
- symbol: 'circle',
- symbolSize: marker.symbolSize ?? 8,
- itemStyle: {
- color: marker.color,
- borderColor: '#ffffff',
- },
- })),
- };
- chartSeries[0].markPoint = markPoint;
- }
- const yAxisOptions = labelYAxisExtents
- ? {
- showMinLabel: true,
- showMaxLabel: true,
- interval: Infinity,
- }
- : {
- axisLabel: {
- show: false,
- },
- };
- const chartOptions = {
- tooltip: {
- trigger: 'axis' as const,
- hideDelay,
- valueFormatter: tooltipFormatter
- ? (value: number) => tooltipFormatter(value)
- : undefined,
- },
- yAxis: {
- max(value) {
- // This keeps small datasets from looking 'scary'
- // by having full bars for < 10 values.
- return Math.max(10, value.max);
- },
- splitLine: {
- show: false,
- },
- ...yAxisOptions,
- },
- grid: {
- // Offset to ensure there is room for the marker symbols at the
- // default size.
- top: labelYAxisExtents ? 6 : 0,
- bottom: markers || labelYAxisExtents ? 4 : 0,
- left: markers ? 4 : 0,
- right: markers ? 4 : 0,
- },
- xAxis: {
- axisLine: {
- show: false,
- },
- axisTick: {
- show: false,
- alignWithLabel: true,
- },
- axisLabel: {
- show: false,
- },
- axisPointer: {
- type: 'line' as const,
- label: {
- show: false,
- },
- lineStyle: {
- width: 0,
- },
- },
- },
- options: {
- animation: false,
- },
- };
- return <BarChart series={chartSeries} {...chartOptions} {...barChartProps} />;
- }
- }
- export default MiniBarChart;
|