feat(legacy-preset-chart-big-number): add timestamp above number (#1278)

* feat(legacy-preset-chart-big-number): add timestamp above number

* add timestamp formatting

* revert to fallback warning when no timestamp

* fix smart date formatting

* fix test

Co-authored-by: Ivan Krsnik <ivan.krsnik@unipart.io>
This commit is contained in:
krsnik93
2021-08-10 18:13:14 +01:00
committed by Yongjie Zhao
parent 13519fd56e
commit c4bc66a1bf
4 changed files with 102 additions and 7 deletions

View File

@@ -41,6 +41,7 @@ const CHART_MARGIN = {
const PROPORTION = {
// text size: proportion of the chart container sans trendline
KICKER: 0.1,
HEADER: 0.3,
SUBHEADER: 0.125,
// trendline size: proportion of the whole chart container
@@ -80,11 +81,14 @@ type BigNumberVisProps = {
fromDatetime?: number;
toDatetime?: number;
headerFontSize: number;
kickerFontSize: number;
subheader: string;
subheaderFontSize: number;
showTimestamp?: boolean;
showTrendLine?: boolean;
startYAxisAtZero?: boolean;
timeRangeFixed?: boolean;
timestamp?: number;
trendLineData?: TimeSeriesDatum[];
mainColor: string;
};
@@ -97,7 +101,9 @@ class BigNumberVis extends React.PureComponent<BigNumberVisProps, {}> {
formatNumber: defaultNumberFormatter,
formatTime: smartDateVerboseFormatter,
headerFontSize: PROPORTION.HEADER,
kickerFontSize: PROPORTION.KICKER,
mainColor: BRAND_COLOR,
showTimestamp: false,
showTrendLine: false,
startYAxisAtZero: true,
subheader: '',
@@ -123,8 +129,8 @@ class BigNumberVis extends React.PureComponent<BigNumberVisProps, {}> {
}
renderFallbackWarning() {
const { bigNumberFallback, formatTime } = this.props;
if (!bigNumberFallback) return null;
const { bigNumberFallback, formatTime, showTimestamp } = this.props;
if (!bigNumberFallback || showTimestamp) return null;
return (
<span
className="alert alert-warning"
@@ -136,6 +142,36 @@ class BigNumberVis extends React.PureComponent<BigNumberVisProps, {}> {
);
}
renderKicker(maxHeight: number) {
const { timestamp, showTimestamp, formatTime, width } = this.props;
if (!showTimestamp) return null;
const text = timestamp === null ? '' : formatTime(timestamp);
const container = this.createTemporaryContainer();
document.body.append(container);
const fontSize = computeMaxFontSize({
text,
maxWidth: width,
maxHeight,
className: 'kicker',
container,
});
container.remove();
return (
<div
className="kicker"
style={{
fontSize,
height: maxHeight,
}}
>
{text}
</div>
);
}
renderHeader(maxHeight: number) {
const { bigNumber, formatNumber, width } = this.props;
const text = bigNumber === null ? t('No data') : formatNumber(bigNumber);
@@ -273,7 +309,7 @@ class BigNumberVis extends React.PureComponent<BigNumberVisProps, {}> {
}
render() {
const { showTrendLine, height, headerFontSize, subheaderFontSize } = this.props;
const { showTrendLine, height, kickerFontSize, headerFontSize, subheaderFontSize } = this.props;
const className = this.getClassName();
if (showTrendLine) {
@@ -284,6 +320,7 @@ class BigNumberVis extends React.PureComponent<BigNumberVisProps, {}> {
<div className={className}>
<div className="text-container" style={{ height: allTextHeight }}>
{this.renderFallbackWarning()}
{this.renderKicker(Math.ceil(kickerFontSize * (1 - PROPORTION.TRENDLINE) * height))}
{this.renderHeader(Math.ceil(headerFontSize * (1 - PROPORTION.TRENDLINE) * height))}
{this.renderSubheader(
Math.ceil(subheaderFontSize * (1 - PROPORTION.TRENDLINE) * height),
@@ -296,6 +333,8 @@ class BigNumberVis extends React.PureComponent<BigNumberVisProps, {}> {
return (
<div className={className} style={{ height }}>
{this.renderFallbackWarning()}
{this.renderKicker(kickerFontSize * height)}
{this.renderHeader(Math.ceil(headerFontSize * height))}
{this.renderSubheader(Math.ceil(subheaderFontSize * height))}
</div>
@@ -328,6 +367,12 @@ export default styled(BigNumberVis)`
}
}
.kicker {
font-weight: ${({ theme }) => theme.typography.weights.light};
line-height: 1em;
padding-bottom: 2em;
}
.header-line {
font-weight: ${({ theme }) => theme.typography.weights.normal};
position: relative;
@@ -345,6 +390,7 @@ export default styled(BigNumberVis)`
}
&.is-fallback-value {
.kicker,
.header-line,
.subheader-line {
opacity: 0.5;

View File

@@ -17,7 +17,13 @@
* under the License.
*/
import { t } from '@superset-ui/core';
import { ControlPanelConfig, formatSelectOptions, sections } from '@superset-ui/chart-controls';
import {
ControlPanelConfig,
D3_FORMAT_DOCS,
D3_TIME_FORMAT_OPTIONS,
formatSelectOptions,
sections,
} from '@superset-ui/chart-controls';
import React from 'react';
import { headerFontSize, subheaderFontSize } from '../sharedControls';
@@ -56,6 +62,36 @@ const config: ControlPanelConfig = {
},
],
['y_axis_format'],
[
{
name: 'show_timestamp',
config: {
type: 'CheckboxControl',
label: t('Show Timestamp'),
renderTrigger: true,
default: false,
description: t('Whether to display the timestamp'),
},
},
],
[
{
name: 'time_format',
config: {
type: 'SelectControl',
freeForm: true,
label: t('Timestamp format'),
renderTrigger: true,
choices: D3_TIME_FORMAT_OPTIONS,
default: '%d-%m-%Y %H:%M:%S',
description: D3_FORMAT_DOCS,
visibility(props) {
const { show_timestamp } = props.form_data;
return !!show_timestamp;
},
},
},
],
[
{
name: 'show_trend_line',

View File

@@ -19,12 +19,14 @@
import * as color from 'd3-color';
import {
extractTimegrain,
getTimeFormatterForGranularity,
getNumberFormatter,
getTimeFormatter,
getTimeFormatterForGranularity,
NumberFormats,
ChartProps,
LegacyQueryData,
QueryFormData,
smartDateFormatter,
} from '@superset-ui/core';
const TIME_COLUMN = '__timestamp';
@@ -63,8 +65,10 @@ export default function transformProps(chartProps: BigNumberChartProps) {
colorPicker,
compareLag: compareLag_,
compareSuffix = '',
timeFormat,
headerFontSize,
metric = 'value',
showTimestamp,
showTrendLine,
startYAxisAtZero,
subheader = '',
@@ -90,6 +94,7 @@ export default function transformProps(chartProps: BigNumberChartProps) {
let trendLineData;
let percentChange = 0;
let bigNumber = data.length === 0 ? null : data[0][metricName];
let timestamp = data.length === 0 ? null : data[0][TIME_COLUMN];
let bigNumberFallback;
if (data.length > 0) {
@@ -99,9 +104,12 @@ export default function transformProps(chartProps: BigNumberChartProps) {
.sort((a, b) => (a.x !== null && b.x !== null ? b.x - a.x : 0));
bigNumber = sortedData[0].y;
timestamp = sortedData[0].x;
if (bigNumber === null) {
bigNumberFallback = sortedData.find(d => d.y !== null);
bigNumber = bigNumberFallback ? bigNumberFallback.y : null;
timestamp = bigNumberFallback ? bigNumberFallback.x : null;
}
if (compareLag > 0) {
@@ -139,7 +147,10 @@ export default function transformProps(chartProps: BigNumberChartProps) {
}
const formatNumber = getNumberFormatter(yAxisFormat);
const formatTime = getTimeFormatterForGranularity(granularity);
const formatTime =
timeFormat === smartDateFormatter.id
? getTimeFormatterForGranularity(granularity)
: getTimeFormatter(timeFormat);
return {
width,
@@ -152,9 +163,11 @@ export default function transformProps(chartProps: BigNumberChartProps) {
headerFontSize,
subheaderFontSize,
mainColor,
showTimestamp,
showTrendLine: supportAndShowTrendLine,
startYAxisAtZero,
subheader: formattedSubheader,
timestamp,
trendLineData,
fromDatetime,
toDatetime,

View File

@@ -117,7 +117,7 @@ describe('BigNumber', () => {
expect(transformed.bigNumberFallback).not.toBeNull();
// should successfully formatTime by ganularity
expect(transformed.formatTime(new Date('2020-01-01'))).toStrictEqual('2020 Q1');
expect(transformed.formatTime(new Date('2020-01-01'))).toStrictEqual('2020-01-01 00:00:00');
});
it('should respect datasource d3 format', () => {