mirror of
https://github.com/apache/superset.git
synced 2026-06-09 09:39:25 +00:00
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:
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
Reference in New Issue
Block a user