blob: fc1e0077ee9c254a4e850a3f6abd5cd87236c268 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import {
Sparkline,
LineSeries,
PointSeries,
HorizontalReferenceLine,
VerticalReferenceLine,
WithTooltip,
} from '@data-ui/sparkline';
import { getTextDimension, formatNumber } from '@superset-ui/core';
const propTypes = {
className: PropTypes.string,
width: PropTypes.number,
height: PropTypes.number,
data: PropTypes.array.isRequired,
ariaLabel: PropTypes.string,
numberFormat: PropTypes.string,
yAxisBounds: PropTypes.array,
showYAxis: PropTypes.bool,
renderTooltip: PropTypes.func,
};
const defaultProps = {
className: '',
width: 300,
height: 50,
ariaLabel: '',
numberFormat: undefined,
yAxisBounds: [null, null],
showYAxis: false,
renderTooltip() {
return <div />;
},
};
const MARGIN = {
top: 8,
right: 8,
bottom: 8,
left: 8,
};
const tooltipProps = {
style: {
opacity: 0.8,
},
offsetTop: 0,
};
function getSparklineTextWidth(text) {
return (
getTextDimension({
text,
style: {
fontSize: '12px',
fontWeight: 200,
letterSpacing: 0.4,
},
}).width + 5
);
}
function isValidBoundValue(value) {
return (
value !== null &&
value !== undefined &&
value !== '' &&
!Number.isNaN(value)
);
}
class SparklineCell extends React.Component {
renderHorizontalReferenceLine(value, label) {
return (
<HorizontalReferenceLine
reference={value}
labelPosition="right"
renderLabel={() => label}
stroke="#bbb"
strokeDasharray="3 3"
strokeWidth={1}
/>
);
}
render() {
const {
width,
height,
data,
ariaLabel,
numberFormat,
yAxisBounds,
showYAxis,
renderTooltip,
} = this.props;
const yScale = {};
let hasMinBound = false;
let hasMaxBound = false;
if (yAxisBounds) {
const [minBound, maxBound] = yAxisBounds;
hasMinBound = isValidBoundValue(minBound);
if (hasMinBound) {
yScale.min = minBound;
}
hasMaxBound = isValidBoundValue(maxBound);
if (hasMaxBound) {
yScale.max = maxBound;
}
}
let min;
let max;
let minLabel;
let maxLabel;
let labelLength = 0;
if (showYAxis) {
const [minBound, maxBound] = yAxisBounds;
min = hasMinBound
? minBound
: data.reduce((acc, current) => Math.min(acc, current), data[0]);
max = hasMaxBound
? maxBound
: data.reduce((acc, current) => Math.max(acc, current), data[0]);
minLabel = formatNumber(numberFormat, min);
maxLabel = formatNumber(numberFormat, max);
labelLength = Math.max(
getSparklineTextWidth(minLabel),
getSparklineTextWidth(maxLabel),
);
}
const margin = {
...MARGIN,
right: MARGIN.right + labelLength,
};
return (
<WithTooltip
tooltipProps={tooltipProps}
hoverStyles={null}
renderTooltip={renderTooltip}
>
{({ onMouseLeave, onMouseMove, tooltipData }) => (
<Sparkline
ariaLabel={ariaLabel}
width={width}
height={height}
margin={margin}
data={data}
onMouseLeave={onMouseLeave}
onMouseMove={onMouseMove}
{...yScale}
>
{showYAxis && this.renderHorizontalReferenceLine(min, minLabel)}
{showYAxis && this.renderHorizontalReferenceLine(max, maxLabel)}
<LineSeries showArea={false} stroke="#767676" />
{tooltipData && (
<VerticalReferenceLine
reference={tooltipData.index}
strokeDasharray="3 3"
strokeWidth={1}
/>
)}
{tooltipData && (
<PointSeries
points={[tooltipData.index]}
fill="#767676"
strokeWidth={1}
/>
)}
</Sparkline>
)}
</WithTooltip>
);
}
}
SparklineCell.propTypes = propTypes;
SparklineCell.defaultProps = defaultProps;
export default SparklineCell;