chore(explore): Migrate BigNumber to v1 api [ID-28][ID-55] (#17587)
* chore(explore): Migrate BigNumber to v1 api * Move to echarts * Use Echarts trendline * Fix imports * Fix parsing dates as strings * Add from_dttm and to_dttm to v1 chart response * Fix post processing * Fix timeRangeFixed * Fix tests * Remove from and to dttm from cache * Cleanup date formatting * Fix storybook * Fix missing types * Fix timestamp with timezone * Add types to demo's tsconfig * bug fix * fix import * Fix cypress tests * add sort * add resample to handle missing values properly * Sync ChartDataResponseResult schema with ts interface * Lint fix * Add migration * Fix migration * Remove pass * Re-raise the exception in migration * Typo fix * Update revision Co-authored-by: Ville Brofeldt <ville.v.brofeldt@gmail.com>
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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 { buildQueryContext, QueryFormData } from '@superset-ui/core';
|
||||
|
||||
export default function buildQuery(formData: QueryFormData) {
|
||||
return buildQueryContext(formData, baseQueryObject => [baseQueryObject]);
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* 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 { smartDateFormatter, t } from '@superset-ui/core';
|
||||
import {
|
||||
ControlPanelConfig,
|
||||
D3_FORMAT_DOCS,
|
||||
D3_TIME_FORMAT_OPTIONS,
|
||||
sections,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import { headerFontSize, subheaderFontSize } from '../sharedControls';
|
||||
|
||||
export default {
|
||||
controlPanelSections: [
|
||||
sections.legacyTimeseriesTime,
|
||||
{
|
||||
label: t('Query'),
|
||||
expanded: true,
|
||||
controlSetRows: [['metric'], ['adhoc_filters']],
|
||||
},
|
||||
{
|
||||
label: t('Options'),
|
||||
expanded: true,
|
||||
tabOverride: 'data',
|
||||
controlSetRows: [
|
||||
[
|
||||
{
|
||||
name: 'subheader',
|
||||
config: {
|
||||
type: 'TextControl',
|
||||
label: t('Subheader'),
|
||||
renderTrigger: true,
|
||||
description: t(
|
||||
'Description text that shows up below your Big Number',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('Chart Options'),
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
[headerFontSize],
|
||||
[subheaderFontSize],
|
||||
['y_axis_format'],
|
||||
[
|
||||
{
|
||||
name: 'time_format',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
label: t('Date format'),
|
||||
renderTrigger: true,
|
||||
choices: D3_TIME_FORMAT_OPTIONS,
|
||||
description: D3_FORMAT_DOCS,
|
||||
default: smartDateFormatter.id,
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'force_timestamp_formatting',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Force date format'),
|
||||
renderTrigger: true,
|
||||
default: false,
|
||||
description: t(
|
||||
'Use date formatting even when metric value is not a timestamp',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
],
|
||||
controlOverrides: {
|
||||
y_axis_format: {
|
||||
label: t('Number format'),
|
||||
},
|
||||
},
|
||||
} as ControlPanelConfig;
|
||||
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 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 { t, ChartMetadata, ChartPlugin } from '@superset-ui/core';
|
||||
import controlPanel from './controlPanel';
|
||||
import transformProps from './transformProps';
|
||||
import buildQuery from './buildQuery';
|
||||
import example1 from './images/BigNumber.jpg';
|
||||
import example2 from './images/BigNumber2.jpg';
|
||||
import thumbnail from './images/thumbnail.png';
|
||||
import { BigNumberTotalChartProps, BigNumberTotalFormData } from '../types';
|
||||
|
||||
const metadata = new ChartMetadata({
|
||||
category: t('KPI'),
|
||||
description: t(
|
||||
'Showcases a single metric front-and-center. Big number is best used to call attention to a KPI or the one thing you want your audience to focus on.',
|
||||
),
|
||||
exampleGallery: [
|
||||
{ url: example1, caption: t('A Big Number') },
|
||||
{ url: example2, caption: t('With a subheader') },
|
||||
],
|
||||
name: t('Big Number'),
|
||||
tags: [
|
||||
t('Additive'),
|
||||
t('Business'),
|
||||
t('Formattable'),
|
||||
t('Legacy'),
|
||||
t('Percentages'),
|
||||
t('Popular'),
|
||||
t('Report'),
|
||||
t('Description'),
|
||||
],
|
||||
thumbnail,
|
||||
});
|
||||
|
||||
export default class BigNumberTotalChartPlugin extends ChartPlugin<
|
||||
BigNumberTotalFormData,
|
||||
BigNumberTotalChartProps
|
||||
> {
|
||||
constructor() {
|
||||
super({
|
||||
loadChart: () => import('../BigNumberViz'),
|
||||
metadata,
|
||||
buildQuery,
|
||||
transformProps,
|
||||
controlPanel,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* 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 {
|
||||
getNumberFormatter,
|
||||
GenericDataType,
|
||||
getMetricLabel,
|
||||
extractTimegrain,
|
||||
QueryFormData,
|
||||
} from '@superset-ui/core';
|
||||
import { BigNumberTotalChartProps } from '../types';
|
||||
import { getDateFormatter, parseMetricValue } from '../utils';
|
||||
|
||||
export default function transformProps(chartProps: BigNumberTotalChartProps) {
|
||||
const { width, height, queriesData, formData, rawFormData } = chartProps;
|
||||
const {
|
||||
headerFontSize,
|
||||
metric = 'value',
|
||||
subheader = '',
|
||||
subheaderFontSize,
|
||||
forceTimestampFormatting,
|
||||
timeFormat,
|
||||
yAxisFormat,
|
||||
} = formData;
|
||||
const { data = [], coltypes = [] } = queriesData[0];
|
||||
const granularity = extractTimegrain(rawFormData as QueryFormData);
|
||||
const metricName = getMetricLabel(metric);
|
||||
const formattedSubheader = subheader;
|
||||
const bigNumber =
|
||||
data.length === 0 ? null : parseMetricValue(data[0][metricName]);
|
||||
|
||||
let metricEntry;
|
||||
if (chartProps.datasource && chartProps.datasource.metrics) {
|
||||
metricEntry = chartProps.datasource.metrics.find(
|
||||
metricItem => metricItem.metric_name === metric,
|
||||
);
|
||||
}
|
||||
|
||||
const formatTime = getDateFormatter(
|
||||
timeFormat,
|
||||
granularity,
|
||||
metricEntry?.d3format,
|
||||
);
|
||||
|
||||
const headerFormatter =
|
||||
coltypes[0] === GenericDataType.TEMPORAL ||
|
||||
coltypes[0] === GenericDataType.STRING ||
|
||||
forceTimestampFormatting
|
||||
? formatTime
|
||||
: getNumberFormatter(yAxisFormat ?? metricEntry?.d3format ?? undefined);
|
||||
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
bigNumber,
|
||||
headerFormatter,
|
||||
headerFontSize,
|
||||
subheaderFontSize,
|
||||
subheader: formattedSubheader,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
/**
|
||||
* 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 {
|
||||
t,
|
||||
getNumberFormatter,
|
||||
NumberFormatter,
|
||||
smartDateVerboseFormatter,
|
||||
TimeFormatter,
|
||||
computeMaxFontSize,
|
||||
BRAND_COLOR,
|
||||
styled,
|
||||
} from '@superset-ui/core';
|
||||
import { EChartsCoreOption } from 'echarts';
|
||||
import Echart from '../components/Echart';
|
||||
import { TimeSeriesDatum } from './types';
|
||||
|
||||
const defaultNumberFormatter = getNumberFormatter();
|
||||
|
||||
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
|
||||
TRENDLINE: 0.3,
|
||||
};
|
||||
|
||||
type BigNumberVisProps = {
|
||||
className?: string;
|
||||
width: number;
|
||||
height: number;
|
||||
bigNumber?: number | null;
|
||||
bigNumberFallback?: TimeSeriesDatum;
|
||||
headerFormatter: NumberFormatter | TimeFormatter;
|
||||
formatTime: TimeFormatter;
|
||||
headerFontSize: number;
|
||||
kickerFontSize: number;
|
||||
subheader: string;
|
||||
subheaderFontSize: number;
|
||||
showTimestamp?: boolean;
|
||||
showTrendLine?: boolean;
|
||||
startYAxisAtZero?: boolean;
|
||||
timeRangeFixed?: boolean;
|
||||
timestamp?: number;
|
||||
trendLineData?: TimeSeriesDatum[];
|
||||
mainColor: string;
|
||||
echartOptions: EChartsCoreOption;
|
||||
};
|
||||
|
||||
class BigNumberVis extends React.PureComponent<BigNumberVisProps> {
|
||||
static defaultProps = {
|
||||
className: '',
|
||||
headerFormatter: defaultNumberFormatter,
|
||||
formatTime: smartDateVerboseFormatter,
|
||||
headerFontSize: PROPORTION.HEADER,
|
||||
kickerFontSize: PROPORTION.KICKER,
|
||||
mainColor: BRAND_COLOR,
|
||||
showTimestamp: false,
|
||||
showTrendLine: false,
|
||||
startYAxisAtZero: true,
|
||||
subheader: '',
|
||||
subheaderFontSize: PROPORTION.SUBHEADER,
|
||||
timeRangeFixed: false,
|
||||
};
|
||||
|
||||
getClassName() {
|
||||
const { className, showTrendLine, bigNumberFallback } = this.props;
|
||||
const names = `superset-legacy-chart-big-number ${className} ${
|
||||
bigNumberFallback ? 'is-fallback-value' : ''
|
||||
}`;
|
||||
if (showTrendLine) return names;
|
||||
return `${names} no-trendline`;
|
||||
}
|
||||
|
||||
createTemporaryContainer() {
|
||||
const container = document.createElement('div');
|
||||
container.className = this.getClassName();
|
||||
container.style.position = 'absolute'; // so it won't disrupt page layout
|
||||
container.style.opacity = '0'; // and not visible
|
||||
return container;
|
||||
}
|
||||
|
||||
renderFallbackWarning() {
|
||||
const { bigNumberFallback, formatTime, showTimestamp } = this.props;
|
||||
if (!bigNumberFallback || showTimestamp) return null;
|
||||
return (
|
||||
<span
|
||||
className="alert alert-warning"
|
||||
role="alert"
|
||||
title={t(
|
||||
`Last available value seen on %s`,
|
||||
formatTime(bigNumberFallback[0]),
|
||||
)}
|
||||
>
|
||||
{t('Not up to date')}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
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, headerFormatter, width } = this.props;
|
||||
const text = bigNumber === null ? t('No data') : headerFormatter(bigNumber);
|
||||
|
||||
const container = this.createTemporaryContainer();
|
||||
document.body.append(container);
|
||||
const fontSize = computeMaxFontSize({
|
||||
text,
|
||||
maxWidth: width,
|
||||
maxHeight,
|
||||
className: 'header-line',
|
||||
container,
|
||||
});
|
||||
container.remove();
|
||||
|
||||
return (
|
||||
<div
|
||||
className="header-line"
|
||||
style={{
|
||||
fontSize,
|
||||
height: maxHeight,
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderSubheader(maxHeight: number) {
|
||||
const { bigNumber, subheader, width, bigNumberFallback } = this.props;
|
||||
let fontSize = 0;
|
||||
|
||||
const NO_DATA_OR_HASNT_LANDED = t(
|
||||
'No data after filtering or data is NULL for the latest time record',
|
||||
);
|
||||
const NO_DATA = t(
|
||||
'Try applying different filters or ensuring your datasource has data',
|
||||
);
|
||||
let text = subheader;
|
||||
if (bigNumber === null) {
|
||||
text = bigNumberFallback ? NO_DATA : NO_DATA_OR_HASNT_LANDED;
|
||||
}
|
||||
if (text) {
|
||||
const container = this.createTemporaryContainer();
|
||||
document.body.append(container);
|
||||
fontSize = computeMaxFontSize({
|
||||
text,
|
||||
maxWidth: width,
|
||||
maxHeight,
|
||||
className: 'subheader-line',
|
||||
container,
|
||||
});
|
||||
container.remove();
|
||||
|
||||
return (
|
||||
<div
|
||||
className="subheader-line"
|
||||
style={{
|
||||
fontSize,
|
||||
height: maxHeight,
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
renderTrendline(maxHeight: number) {
|
||||
const { width, trendLineData, echartOptions } = this.props;
|
||||
|
||||
// if can't find any non-null values, no point rendering the trendline
|
||||
if (!trendLineData?.some(d => d[1] !== null)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Echart
|
||||
width={Math.floor(width)}
|
||||
height={maxHeight}
|
||||
echartOptions={echartOptions}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
showTrendLine,
|
||||
height,
|
||||
kickerFontSize,
|
||||
headerFontSize,
|
||||
subheaderFontSize,
|
||||
} = this.props;
|
||||
const className = this.getClassName();
|
||||
|
||||
if (showTrendLine) {
|
||||
const chartHeight = Math.floor(PROPORTION.TRENDLINE * height);
|
||||
const allTextHeight = height - chartHeight;
|
||||
|
||||
return (
|
||||
<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,
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
{this.renderTrendline(chartHeight)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default styled(BigNumberVis)`
|
||||
font-family: ${({ theme }) => theme.typography.families.sansSerif};
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
&.no-trendline .subheader-line {
|
||||
padding-bottom: 0.3em;
|
||||
}
|
||||
|
||||
.text-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
.alert {
|
||||
font-size: ${({ theme }) => theme.typography.sizes.s};
|
||||
margin: -0.5em 0 0.4em;
|
||||
line-height: 1;
|
||||
padding: 2px 4px 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
line-height: 1em;
|
||||
span {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.subheader-line {
|
||||
font-weight: ${({ theme }) => theme.typography.weights.light};
|
||||
line-height: 1em;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
&.is-fallback-value {
|
||||
.kicker,
|
||||
.header-line,
|
||||
.subheader-line {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.superset-data-ui-tooltip {
|
||||
z-index: 1000;
|
||||
background: #000;
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* 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 {
|
||||
buildQueryContext,
|
||||
DTTM_ALIAS,
|
||||
PostProcessingResample,
|
||||
QueryFormData,
|
||||
} from '@superset-ui/core';
|
||||
import {
|
||||
rollingWindowOperator,
|
||||
TIME_COLUMN,
|
||||
} from '@superset-ui/chart-controls';
|
||||
|
||||
const TIME_GRAIN_MAP: Record<string, string> = {
|
||||
PT1S: 'S',
|
||||
PT1M: 'min',
|
||||
PT5M: '5min',
|
||||
PT10M: '10min',
|
||||
PT15M: '15min',
|
||||
PT30M: '30min',
|
||||
PT1H: 'H',
|
||||
P1D: 'D',
|
||||
P1M: 'M',
|
||||
P3M: 'Q',
|
||||
P1Y: 'A',
|
||||
// TODO: these need to be mapped carefully, as the first day of week
|
||||
// can vary from engine to engine
|
||||
// P1W: 'W',
|
||||
// '1969-12-28T00:00:00Z/P1W': 'W',
|
||||
// '1969-12-29T00:00:00Z/P1W': 'W',
|
||||
// 'P1W/1970-01-03T00:00:00Z': 'W',
|
||||
// 'P1W/1970-01-04T00:00:00Z': 'W',
|
||||
};
|
||||
|
||||
export default function buildQuery(formData: QueryFormData) {
|
||||
return buildQueryContext(formData, baseQueryObject => {
|
||||
const rollingProc = rollingWindowOperator(formData, baseQueryObject);
|
||||
if (rollingProc) {
|
||||
rollingProc.options = { ...rollingProc.options, is_pivot_df: false };
|
||||
}
|
||||
const { time_grain_sqla } = formData;
|
||||
let resampleProc: PostProcessingResample | undefined;
|
||||
if (rollingProc && time_grain_sqla) {
|
||||
const rule = TIME_GRAIN_MAP[time_grain_sqla];
|
||||
if (rule) {
|
||||
resampleProc = {
|
||||
operation: 'resample',
|
||||
options: {
|
||||
method: 'asfreq',
|
||||
rule,
|
||||
fill_value: null,
|
||||
time_column: TIME_COLUMN,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
return [
|
||||
{
|
||||
...baseQueryObject,
|
||||
is_timeseries: true,
|
||||
post_processing: [
|
||||
{
|
||||
operation: 'sort',
|
||||
options: {
|
||||
columns: {
|
||||
[DTTM_ALIAS]: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
resampleProc,
|
||||
rollingProc,
|
||||
],
|
||||
},
|
||||
];
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* 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 { smartDateFormatter, t } from '@superset-ui/core';
|
||||
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';
|
||||
|
||||
const config: ControlPanelConfig = {
|
||||
controlPanelSections: [
|
||||
sections.legacyTimeseriesTime,
|
||||
{
|
||||
label: t('Query'),
|
||||
expanded: true,
|
||||
controlSetRows: [['metric'], ['adhoc_filters']],
|
||||
},
|
||||
{
|
||||
label: t('Options'),
|
||||
tabOverride: 'data',
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
[
|
||||
{
|
||||
name: 'compare_lag',
|
||||
config: {
|
||||
type: 'TextControl',
|
||||
label: t('Comparison Period Lag'),
|
||||
isInt: true,
|
||||
description: t(
|
||||
'Based on granularity, number of time periods to compare against',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'compare_suffix',
|
||||
config: {
|
||||
type: 'TextControl',
|
||||
label: t('Comparison suffix'),
|
||||
description: t('Suffix to apply after the percentage display'),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'show_timestamp',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Show Timestamp'),
|
||||
renderTrigger: true,
|
||||
default: false,
|
||||
description: t('Whether to display the timestamp'),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'show_trend_line',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Show Trend Line'),
|
||||
renderTrigger: true,
|
||||
default: true,
|
||||
description: t('Whether to display the trend line'),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'start_y_axis_at_zero',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Start y-axis at 0'),
|
||||
renderTrigger: true,
|
||||
default: true,
|
||||
description: t(
|
||||
'Start y-axis at zero. Uncheck to start y-axis at minimum value in the data.',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'time_range_fixed',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Fix to selected Time Range'),
|
||||
description: t(
|
||||
'Fix the trend line to the full time range specified in case filtered results do not include the start or end dates',
|
||||
),
|
||||
renderTrigger: true,
|
||||
visibility(props) {
|
||||
const { time_range: timeRange } = props.form_data;
|
||||
// only display this option when a time range is selected
|
||||
return !!timeRange && timeRange !== 'No filter';
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('Chart Options'),
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
['color_picker', null],
|
||||
[headerFontSize],
|
||||
[subheaderFontSize],
|
||||
['y_axis_format'],
|
||||
[
|
||||
{
|
||||
name: 'time_format',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
label: t('Date format'),
|
||||
renderTrigger: true,
|
||||
choices: D3_TIME_FORMAT_OPTIONS,
|
||||
description: D3_FORMAT_DOCS,
|
||||
default: smartDateFormatter.id,
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'force_timestamp_formatting',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Force date format'),
|
||||
renderTrigger: true,
|
||||
default: false,
|
||||
description: t(
|
||||
'Use date formatting even when metric value is not a timestamp',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('Advanced Analytics'),
|
||||
expanded: false,
|
||||
controlSetRows: [
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
[<h1 className="section-header">{t('Rolling Window')}</h1>],
|
||||
[
|
||||
{
|
||||
name: 'rolling_type',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
label: t('Rolling Function'),
|
||||
default: 'None',
|
||||
choices: formatSelectOptions([
|
||||
'None',
|
||||
'mean',
|
||||
'sum',
|
||||
'std',
|
||||
'cumsum',
|
||||
]),
|
||||
description: t(
|
||||
'Defines a rolling window function to apply, works along ' +
|
||||
'with the [Periods] text box',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'rolling_periods',
|
||||
config: {
|
||||
type: 'TextControl',
|
||||
label: t('Periods'),
|
||||
isInt: true,
|
||||
description: t(
|
||||
'Defines the size of the rolling window function, ' +
|
||||
'relative to the time granularity selected',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'min_periods',
|
||||
config: {
|
||||
type: 'TextControl',
|
||||
label: t('Min Periods'),
|
||||
isInt: true,
|
||||
description: t(
|
||||
'The minimum number of rolling periods required to show ' +
|
||||
'a value. For instance if you do a cumulative sum on 7 days ' +
|
||||
'you may want your "Min Period" to be 7, so that all data points ' +
|
||||
'shown are the total of 7 periods. This will hide the "ramp up" ' +
|
||||
'taking place over the first 7 periods',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
],
|
||||
controlOverrides: {
|
||||
y_axis_format: {
|
||||
label: t('Number format'),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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 { t, ChartMetadata, ChartPlugin } from '@superset-ui/core';
|
||||
import controlPanel from './controlPanel';
|
||||
import transformProps from './transformProps';
|
||||
import buildQuery from './buildQuery';
|
||||
import example from './images/Big_Number_Trendline.jpg';
|
||||
import thumbnail from './images/thumbnail.png';
|
||||
import {
|
||||
BigNumberWithTrendlineChartProps,
|
||||
BigNumberWithTrendlineFormData,
|
||||
} from '../types';
|
||||
|
||||
const metadata = new ChartMetadata({
|
||||
category: t('KPI'),
|
||||
description: t(
|
||||
'Showcases a single number accompanied by a simple line chart, to call attention to an important metric along with its change over time or other dimension.',
|
||||
),
|
||||
exampleGallery: [{ url: example }],
|
||||
name: t('Big Number with Trendline'),
|
||||
tags: [
|
||||
t('Advanced-Analytics'),
|
||||
t('Formattable'),
|
||||
t('Line'),
|
||||
t('Percentages'),
|
||||
t('Popular'),
|
||||
t('Report'),
|
||||
t('Description'),
|
||||
t('Trend'),
|
||||
],
|
||||
thumbnail,
|
||||
});
|
||||
|
||||
export default class BigNumberWithTrendlineChartPlugin extends ChartPlugin<
|
||||
BigNumberWithTrendlineFormData,
|
||||
BigNumberWithTrendlineChartProps
|
||||
> {
|
||||
constructor() {
|
||||
super({
|
||||
loadChart: () => import('../BigNumberViz'),
|
||||
metadata,
|
||||
buildQuery,
|
||||
transformProps,
|
||||
controlPanel,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
/**
|
||||
* 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 {
|
||||
extractTimegrain,
|
||||
getNumberFormatter,
|
||||
NumberFormats,
|
||||
QueryFormData,
|
||||
GenericDataType,
|
||||
getMetricLabel,
|
||||
t,
|
||||
smartDateVerboseFormatter,
|
||||
NumberFormatter,
|
||||
TimeFormatter,
|
||||
} from '@superset-ui/core';
|
||||
import { EChartsCoreOption, graphic } from 'echarts';
|
||||
import {
|
||||
BigNumberDatum,
|
||||
BigNumberWithTrendlineChartProps,
|
||||
TimeSeriesDatum,
|
||||
} from '../types';
|
||||
import { getDateFormatter, parseMetricValue } from '../utils';
|
||||
|
||||
const defaultNumberFormatter = getNumberFormatter();
|
||||
export function renderTooltipFactory(
|
||||
formatDate: TimeFormatter = smartDateVerboseFormatter,
|
||||
formatValue: NumberFormatter | TimeFormatter = defaultNumberFormatter,
|
||||
) {
|
||||
return function renderTooltip(params: { data: TimeSeriesDatum }[]) {
|
||||
return `
|
||||
${formatDate(params[0].data[0])}
|
||||
<br />
|
||||
<strong>
|
||||
${
|
||||
params[0].data[1] === null ? t('N/A') : formatValue(params[0].data[1])
|
||||
}
|
||||
</strong>
|
||||
`;
|
||||
};
|
||||
}
|
||||
|
||||
const TIME_COLUMN = '__timestamp';
|
||||
const formatPercentChange = getNumberFormatter(
|
||||
NumberFormats.PERCENT_SIGNED_1_POINT,
|
||||
);
|
||||
|
||||
export default function transformProps(
|
||||
chartProps: BigNumberWithTrendlineChartProps,
|
||||
) {
|
||||
const { width, height, queriesData, formData, rawFormData } = chartProps;
|
||||
const {
|
||||
colorPicker,
|
||||
compareLag: compareLag_,
|
||||
compareSuffix = '',
|
||||
timeFormat,
|
||||
headerFontSize,
|
||||
metric = 'value',
|
||||
showTimestamp,
|
||||
showTrendLine,
|
||||
startYAxisAtZero,
|
||||
subheader = '',
|
||||
subheaderFontSize,
|
||||
forceTimestampFormatting,
|
||||
yAxisFormat,
|
||||
timeRangeFixed,
|
||||
} = formData;
|
||||
const granularity = extractTimegrain(rawFormData as QueryFormData);
|
||||
const {
|
||||
data = [],
|
||||
colnames = [],
|
||||
coltypes = [],
|
||||
from_dttm: fromDatetime,
|
||||
to_dttm: toDatetime,
|
||||
} = queriesData[0];
|
||||
const metricName = getMetricLabel(metric);
|
||||
const compareLag = Number(compareLag_) || 0;
|
||||
let formattedSubheader = subheader;
|
||||
|
||||
const { r, g, b } = colorPicker;
|
||||
const mainColor = `rgb(${r}, ${g}, ${b})`;
|
||||
|
||||
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;
|
||||
|
||||
const metricColtypeIndex = colnames.findIndex(name => name === metricName);
|
||||
const metricColtype =
|
||||
metricColtypeIndex > -1 ? coltypes[metricColtypeIndex] : null;
|
||||
|
||||
if (data.length > 0) {
|
||||
const sortedData = (data as BigNumberDatum[])
|
||||
.map(d => [d[TIME_COLUMN], parseMetricValue(d[metricName])])
|
||||
// sort in time descending order
|
||||
.sort((a, b) => (a[0] !== null && b[0] !== null ? b[0] - a[0] : 0));
|
||||
|
||||
bigNumber = sortedData[0][1];
|
||||
timestamp = sortedData[0][0];
|
||||
|
||||
if (bigNumber === null) {
|
||||
bigNumberFallback = sortedData.find(d => d[1] !== null);
|
||||
bigNumber = bigNumberFallback ? bigNumberFallback[1] : null;
|
||||
timestamp = bigNumberFallback ? bigNumberFallback[0] : null;
|
||||
}
|
||||
|
||||
if (compareLag > 0) {
|
||||
const compareIndex = compareLag;
|
||||
if (compareIndex < sortedData.length) {
|
||||
const compareValue = sortedData[compareIndex][1];
|
||||
// compare values must both be non-nulls
|
||||
if (bigNumber !== null && compareValue !== null && compareValue !== 0) {
|
||||
percentChange = (bigNumber - compareValue) / Math.abs(compareValue);
|
||||
formattedSubheader = `${formatPercentChange(
|
||||
percentChange,
|
||||
)} ${compareSuffix}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
sortedData.reverse();
|
||||
trendLineData = showTrendLine ? sortedData : undefined;
|
||||
}
|
||||
|
||||
let className = '';
|
||||
if (percentChange > 0) {
|
||||
className = 'positive';
|
||||
} else if (percentChange < 0) {
|
||||
className = 'negative';
|
||||
}
|
||||
|
||||
let metricEntry;
|
||||
if (chartProps.datasource && chartProps.datasource.metrics) {
|
||||
metricEntry = chartProps.datasource.metrics.find(
|
||||
metricEntry => metricEntry.metric_name === metric,
|
||||
);
|
||||
}
|
||||
|
||||
const formatTime = getDateFormatter(
|
||||
timeFormat,
|
||||
granularity,
|
||||
metricEntry?.d3format,
|
||||
);
|
||||
|
||||
const headerFormatter =
|
||||
metricColtype === GenericDataType.TEMPORAL ||
|
||||
metricColtype === GenericDataType.STRING ||
|
||||
forceTimestampFormatting
|
||||
? formatTime
|
||||
: getNumberFormatter(yAxisFormat ?? metricEntry?.d3format ?? undefined);
|
||||
|
||||
if (trendLineData && timeRangeFixed && fromDatetime) {
|
||||
const toDatetimeOrToday = toDatetime ?? Date.now();
|
||||
if (!trendLineData[0][0] || trendLineData[0][0] > fromDatetime) {
|
||||
trendLineData.unshift([fromDatetime, null]);
|
||||
}
|
||||
if (
|
||||
!trendLineData[trendLineData.length - 1][0] ||
|
||||
trendLineData[trendLineData.length - 1][0]! < toDatetimeOrToday
|
||||
) {
|
||||
trendLineData.push([toDatetimeOrToday, null]);
|
||||
}
|
||||
}
|
||||
|
||||
const echartOptions: EChartsCoreOption = trendLineData
|
||||
? {
|
||||
series: [
|
||||
{
|
||||
data: trendLineData,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
showSymbol: false,
|
||||
color: mainColor,
|
||||
areaStyle: {
|
||||
color: new graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: mainColor,
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'white',
|
||||
},
|
||||
]),
|
||||
},
|
||||
},
|
||||
],
|
||||
xAxis: {
|
||||
min: trendLineData[0][0],
|
||||
max: trendLineData[trendLineData.length - 1][0],
|
||||
show: false,
|
||||
type: 'value',
|
||||
},
|
||||
yAxis: {
|
||||
scale: !startYAxisAtZero,
|
||||
show: false,
|
||||
},
|
||||
grid: {
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'axis',
|
||||
confine: true,
|
||||
formatter: renderTooltipFactory(formatTime, headerFormatter),
|
||||
},
|
||||
aria: {
|
||||
enabled: true,
|
||||
label: {
|
||||
description: `Big number visualization ${subheader}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
: {};
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
bigNumber,
|
||||
bigNumberFallback,
|
||||
className,
|
||||
headerFormatter,
|
||||
formatTime,
|
||||
headerFontSize,
|
||||
subheaderFontSize,
|
||||
mainColor,
|
||||
showTimestamp,
|
||||
showTrendLine,
|
||||
startYAxisAtZero,
|
||||
subheader: formattedSubheader,
|
||||
timestamp,
|
||||
trendLineData,
|
||||
echartOptions,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.18.0](https://github.com/apache-superset/superset-ui/compare/v0.17.87...v0.18.0) (2021-08-30)
|
||||
|
||||
**Note:** Version bump only for package @superset-ui/legacy-preset-chart-big-number
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.17.62](https://github.com/apache-superset/superset-ui/compare/v0.17.61...v0.17.62) (2021-07-02)
|
||||
|
||||
**Note:** Version bump only for package @superset-ui/legacy-preset-chart-big-number
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.17.61](https://github.com/apache-superset/superset-ui/compare/v0.17.60...v0.17.61) (2021-07-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **legacy-preset-chart-big-number:** example images got mixed up ([#1196](https://github.com/apache-superset/superset-ui/issues/1196)) ([60215b5](https://github.com/apache-superset/superset-ui/commit/60215b5a4c5ae416fc27418c276276ed38ab7f19))
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { default as BigNumberChartPlugin } from './BigNumberWithTrendline';
|
||||
export { default as BigNumberTotalChartPlugin } from './BigNumberTotal';
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// These are control configurations that are shared ONLY within the BigNumberWithTrendline viz plugin repo.
|
||||
import { t } from '@superset-ui/core';
|
||||
import { CustomControlItem } from '@superset-ui/chart-controls';
|
||||
|
||||
export const headerFontSize: CustomControlItem = {
|
||||
name: 'header_font_size',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
label: t('Big Number Font Size'),
|
||||
renderTrigger: true,
|
||||
clearable: false,
|
||||
default: 0.4,
|
||||
// Values represent the percentage of space a header should take
|
||||
options: [
|
||||
{
|
||||
label: t('Tiny'),
|
||||
value: 0.2,
|
||||
},
|
||||
{
|
||||
label: t('Small'),
|
||||
value: 0.3,
|
||||
},
|
||||
{
|
||||
label: t('Normal'),
|
||||
value: 0.4,
|
||||
},
|
||||
{
|
||||
label: t('Large'),
|
||||
value: 0.5,
|
||||
},
|
||||
{
|
||||
label: t('Huge'),
|
||||
value: 0.6,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const subheaderFontSize: CustomControlItem = {
|
||||
name: 'subheader_font_size',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
label: t('Subheader Font Size'),
|
||||
renderTrigger: true,
|
||||
clearable: false,
|
||||
default: 0.15,
|
||||
// Values represent the percentage of space a subheader should take
|
||||
options: [
|
||||
{
|
||||
label: t('Tiny'),
|
||||
value: 0.125,
|
||||
},
|
||||
{
|
||||
label: t('Small'),
|
||||
value: 0.15,
|
||||
},
|
||||
{
|
||||
label: t('Normal'),
|
||||
value: 0.2,
|
||||
},
|
||||
{
|
||||
label: t('Large'),
|
||||
value: 0.3,
|
||||
},
|
||||
{
|
||||
label: t('Huge'),
|
||||
value: 0.4,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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 {
|
||||
ChartDataResponseResult,
|
||||
ChartProps,
|
||||
QueryFormData,
|
||||
QueryFormMetric,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
export interface BigNumberDatum {
|
||||
[key: string]: number | null;
|
||||
}
|
||||
|
||||
export type BigNumberTotalFormData = QueryFormData & {
|
||||
metric?: QueryFormMetric;
|
||||
yAxisFormat?: string;
|
||||
forceTimestampFormatting?: boolean;
|
||||
};
|
||||
|
||||
export type BigNumberWithTrendlineFormData = BigNumberTotalFormData & {
|
||||
colorPicker: {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
};
|
||||
compareLag?: string | number;
|
||||
};
|
||||
|
||||
export type BigNumberTotalChartProps = ChartProps & {
|
||||
formData: BigNumberTotalFormData;
|
||||
queriesData: (ChartDataResponseResult & {
|
||||
data?: BigNumberDatum[];
|
||||
})[];
|
||||
};
|
||||
|
||||
export type BigNumberWithTrendlineChartProps = BigNumberTotalChartProps & {
|
||||
formData: BigNumberWithTrendlineFormData;
|
||||
};
|
||||
|
||||
export type TimeSeriesDatum = [number, number | null];
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 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 moment from 'moment';
|
||||
import {
|
||||
getTimeFormatter,
|
||||
getTimeFormatterForGranularity,
|
||||
smartDateFormatter,
|
||||
TimeGranularity,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
export const parseMetricValue = (metricValue: number | string | null) => {
|
||||
if (typeof metricValue === 'string') {
|
||||
const dateObject = moment.utc(metricValue, moment.ISO_8601, true);
|
||||
if (dateObject.isValid()) {
|
||||
return dateObject.valueOf();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return metricValue;
|
||||
};
|
||||
|
||||
export const getDateFormatter = (
|
||||
timeFormat: string,
|
||||
granularity?: TimeGranularity,
|
||||
fallbackFormat?: string | null,
|
||||
) =>
|
||||
timeFormat === smartDateFormatter.id
|
||||
? getTimeFormatterForGranularity(granularity)
|
||||
: getTimeFormatter(timeFormat ?? fallbackFormat);
|
||||
@@ -32,6 +32,7 @@ export { default as EchartsRadarChartPlugin } from './Radar';
|
||||
export { default as EchartsFunnelChartPlugin } from './Funnel';
|
||||
export { default as EchartsTreeChartPlugin } from './Tree';
|
||||
export { default as EchartsTreemapChartPlugin } from './Treemap';
|
||||
export { BigNumberChartPlugin, BigNumberTotalChartPlugin } from './BigNumber';
|
||||
|
||||
export { default as BoxPlotTransformProps } from './BoxPlot/transformProps';
|
||||
export { default as FunnelTransformProps } from './Funnel/transformProps';
|
||||
|
||||