mirror of
https://github.com/apache/superset.git
synced 2026-04-18 15:44:57 +00:00
feat(charts): add subtitle option and metric customization controls (#32975)
This commit is contained in:
@@ -81,6 +81,8 @@ export default function PopKPI(props: PopKPIProps) {
|
||||
currentTimeRangeFilter,
|
||||
startDateOffset,
|
||||
shift,
|
||||
subtitle,
|
||||
subtitleFontSize,
|
||||
dashboardTimeRange,
|
||||
} = props;
|
||||
|
||||
@@ -140,6 +142,16 @@ export default function PopKPI(props: PopKPIProps) {
|
||||
margin-bottom: ${theme.gridUnit * 4}px;
|
||||
`;
|
||||
|
||||
const SubtitleText = styled.div`
|
||||
${({ theme }) => `
|
||||
font-family: ${theme.typography.families.sansSerif};
|
||||
font-weight: ${theme.typography.weights.medium};
|
||||
text-align: center;
|
||||
margin-top: -10px;
|
||||
margin-bottom: ${theme.gridUnit * 4}px;
|
||||
`}
|
||||
`;
|
||||
|
||||
const getArrowIndicatorColor = () => {
|
||||
if (!comparisonColorEnabled || percentDifferenceNumber === 0) {
|
||||
return theme.colors.grayscale.base;
|
||||
@@ -195,31 +207,40 @@ export default function PopKPI(props: PopKPIProps) {
|
||||
]);
|
||||
|
||||
const SYMBOLS_WITH_VALUES = useMemo(
|
||||
() => [
|
||||
{
|
||||
symbol: '#',
|
||||
value: prevNumber,
|
||||
tooltipText: t('Data for %s', comparisonRange || 'previous range'),
|
||||
columnKey: 'Previous value',
|
||||
},
|
||||
{
|
||||
symbol: '△',
|
||||
value: valueDifference,
|
||||
tooltipText: t('Value difference between the time periods'),
|
||||
columnKey: 'Delta',
|
||||
},
|
||||
{
|
||||
symbol: '%',
|
||||
value: percentDifferenceFormattedString,
|
||||
tooltipText: t('Percentage difference between the time periods'),
|
||||
columnKey: 'Percent change',
|
||||
},
|
||||
],
|
||||
() =>
|
||||
[
|
||||
{
|
||||
defaultSymbol: '#',
|
||||
value: prevNumber,
|
||||
tooltipText: t('Data for %s', comparisonRange || 'previous range'),
|
||||
columnKey: 'Previous value',
|
||||
},
|
||||
{
|
||||
defaultSymbol: '△',
|
||||
value: valueDifference,
|
||||
tooltipText: t('Value difference between the time periods'),
|
||||
columnKey: 'Delta',
|
||||
},
|
||||
{
|
||||
defaultSymbol: '%',
|
||||
value: percentDifferenceFormattedString,
|
||||
tooltipText: t('Percentage difference between the time periods'),
|
||||
columnKey: 'Percent change',
|
||||
},
|
||||
].map(item => {
|
||||
const config = props.columnConfig?.[item.columnKey];
|
||||
return {
|
||||
...item,
|
||||
symbol: config?.displayTypeIcon === false ? '' : item.defaultSymbol,
|
||||
label: config?.customColumnName || item.columnKey,
|
||||
};
|
||||
}),
|
||||
[
|
||||
comparisonRange,
|
||||
prevNumber,
|
||||
valueDifference,
|
||||
percentDifferenceFormattedString,
|
||||
props.columnConfig,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -250,6 +271,15 @@ export default function PopKPI(props: PopKPIProps) {
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{subtitle && (
|
||||
<SubtitleText
|
||||
style={{
|
||||
fontSize: `${subtitleFontSize * height * 0.4}px`,
|
||||
}}
|
||||
>
|
||||
{subtitle}
|
||||
</SubtitleText>
|
||||
)}
|
||||
|
||||
{visibleSymbols.length > 0 && (
|
||||
<div
|
||||
@@ -276,7 +306,7 @@ export default function PopKPI(props: PopKPIProps) {
|
||||
>
|
||||
{visibleSymbols.map((symbol_with_value, index) => (
|
||||
<ComparisonValue
|
||||
key={`comparison-symbol-${symbol_with_value.symbol}`}
|
||||
key={`comparison-symbol-${symbol_with_value.columnKey}`}
|
||||
subheaderFontSize={subheaderFontSize}
|
||||
>
|
||||
<Tooltip
|
||||
@@ -284,15 +314,19 @@ export default function PopKPI(props: PopKPIProps) {
|
||||
placement="top"
|
||||
title={symbol_with_value.tooltipText}
|
||||
>
|
||||
<SymbolWrapper
|
||||
backgroundColor={
|
||||
index > 0 ? backgroundColor : defaultBackgroundColor
|
||||
}
|
||||
textColor={index > 0 ? textColor : defaultTextColor}
|
||||
>
|
||||
{symbol_with_value.symbol}
|
||||
</SymbolWrapper>
|
||||
{symbol_with_value.value}
|
||||
{symbol_with_value.symbol && (
|
||||
<SymbolWrapper
|
||||
backgroundColor={
|
||||
index > 0 ? backgroundColor : defaultBackgroundColor
|
||||
}
|
||||
textColor={index > 0 ? textColor : defaultTextColor}
|
||||
>
|
||||
{symbol_with_value.symbol}
|
||||
</SymbolWrapper>
|
||||
)}
|
||||
{symbol_with_value.value}{' '}
|
||||
{props.columnConfig?.[symbol_with_value.columnKey]
|
||||
?.customColumnName || ''}
|
||||
</Tooltip>
|
||||
</ComparisonValue>
|
||||
))}
|
||||
|
||||
@@ -23,7 +23,12 @@ import {
|
||||
sharedControls,
|
||||
sections,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import { headerFontSize, subheaderFontSize } from '../sharedControls';
|
||||
import {
|
||||
headerFontSize,
|
||||
subheaderFontSize,
|
||||
subtitleControl,
|
||||
subtitleFontSize,
|
||||
} from '../sharedControls';
|
||||
import { ColorSchemeEnum } from './types';
|
||||
|
||||
const config: ControlPanelConfig = {
|
||||
@@ -63,6 +68,8 @@ const config: ControlPanelConfig = {
|
||||
config: { ...headerFontSize.config, default: 0.2 },
|
||||
},
|
||||
],
|
||||
[subtitleControl],
|
||||
[subtitleFontSize],
|
||||
[
|
||||
{
|
||||
...subheaderFontSize,
|
||||
@@ -120,7 +127,11 @@ const config: ControlPanelConfig = {
|
||||
[GenericDataType.Numeric]: [
|
||||
{
|
||||
tab: t('General'),
|
||||
children: [['visible']],
|
||||
children: [
|
||||
['customColumnName'],
|
||||
['displayTypeIcon'],
|
||||
['visible'],
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -89,6 +89,8 @@ export default function transformProps(chartProps: ChartProps) {
|
||||
comparisonColorScheme,
|
||||
comparisonColorEnabled,
|
||||
percentDifferenceFormat,
|
||||
subtitle = '',
|
||||
subtitleFontSize,
|
||||
columnConfig,
|
||||
} = formData;
|
||||
const { data: dataA = [] } = queriesData[0];
|
||||
@@ -183,6 +185,8 @@ export default function transformProps(chartProps: ChartProps) {
|
||||
valueDifference,
|
||||
percentDifferenceFormattedString: percentDifference,
|
||||
boldText,
|
||||
subtitle,
|
||||
subtitleFontSize,
|
||||
headerFontSize: getHeaderFontSize(headerFontSize),
|
||||
subheaderFontSize: getComparisonFontSize(subheaderFontSize),
|
||||
headerText,
|
||||
|
||||
@@ -35,6 +35,8 @@ export interface PopKPIStylesProps {
|
||||
|
||||
export type TableColumnConfig = {
|
||||
visible?: boolean;
|
||||
customColumnName?: string;
|
||||
displayTypeIcon?: boolean;
|
||||
};
|
||||
|
||||
interface PopKPICustomizeProps {
|
||||
@@ -61,6 +63,8 @@ export type PopKPIProps = PopKPIStylesProps &
|
||||
metricName: string;
|
||||
bigNumber: string;
|
||||
prevNumber: string;
|
||||
subtitle?: string;
|
||||
subtitleFontSize: number;
|
||||
valueDifference: string;
|
||||
percentDifferenceFormattedString: string;
|
||||
compType: string;
|
||||
|
||||
@@ -24,7 +24,11 @@ import {
|
||||
Dataset,
|
||||
getStandardizedControls,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import { headerFontSize, subheaderFontSize } from '../sharedControls';
|
||||
import {
|
||||
headerFontSize,
|
||||
subtitleFontSize,
|
||||
subtitleControl,
|
||||
} from '../sharedControls';
|
||||
|
||||
export default {
|
||||
controlPanelSections: [
|
||||
@@ -33,32 +37,13 @@ export default {
|
||||
expanded: true,
|
||||
controlSetRows: [['metric'], ['adhoc_filters']],
|
||||
},
|
||||
{
|
||||
label: t('Display settings'),
|
||||
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],
|
||||
[subtitleControl],
|
||||
[subtitleFontSize],
|
||||
['y_axis_format'],
|
||||
['currency_format'],
|
||||
[
|
||||
|
||||
@@ -47,8 +47,8 @@ export default function transformProps(
|
||||
const {
|
||||
headerFontSize,
|
||||
metric = 'value',
|
||||
subheader = '',
|
||||
subheaderFontSize,
|
||||
subtitle = '',
|
||||
subtitleFontSize,
|
||||
forceTimestampFormatting,
|
||||
timeFormat,
|
||||
yAxisFormat,
|
||||
@@ -59,7 +59,7 @@ export default function transformProps(
|
||||
const { data = [], coltypes = [] } = queriesData[0];
|
||||
const granularity = extractTimegrain(rawFormData as QueryFormData);
|
||||
const metricName = getMetricLabel(metric);
|
||||
const formattedSubheader = subheader;
|
||||
const formattedSubtitle = subtitle;
|
||||
const bigNumber =
|
||||
data.length === 0 ? null : parseMetricValue(data[0][metricName]);
|
||||
|
||||
@@ -105,8 +105,10 @@ export default function transformProps(
|
||||
bigNumber,
|
||||
headerFormatter,
|
||||
headerFontSize,
|
||||
subheaderFontSize,
|
||||
subheader: formattedSubheader,
|
||||
subtitleFontSize,
|
||||
subtitle: formattedSubtitle,
|
||||
subheader: '',
|
||||
subheaderFontSize: subtitleFontSize,
|
||||
onContextMenu,
|
||||
refs,
|
||||
colorThresholdFormatters,
|
||||
|
||||
@@ -229,6 +229,40 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> {
|
||||
return null;
|
||||
}
|
||||
|
||||
renderSubtitle(maxHeight: number) {
|
||||
const { subtitle, width } = this.props;
|
||||
let fontSize = 0;
|
||||
|
||||
if (subtitle) {
|
||||
const container = this.createTemporaryContainer();
|
||||
document.body.append(container);
|
||||
try {
|
||||
fontSize = computeMaxFontSize({
|
||||
text: subtitle,
|
||||
maxWidth: width * 0.9,
|
||||
maxHeight,
|
||||
className: 'subtitle-line',
|
||||
container,
|
||||
});
|
||||
} finally {
|
||||
container.remove();
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="subtitle-line"
|
||||
style={{
|
||||
fontSize,
|
||||
height: maxHeight,
|
||||
}}
|
||||
>
|
||||
{subtitle}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
renderTrendline(maxHeight: number) {
|
||||
const { width, trendLineData, echartOptions, refs } = this.props;
|
||||
|
||||
@@ -282,6 +316,7 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> {
|
||||
kickerFontSize,
|
||||
headerFontSize,
|
||||
subheaderFontSize,
|
||||
subtitleFontSize,
|
||||
} = this.props;
|
||||
const className = this.getClassName();
|
||||
|
||||
@@ -306,6 +341,9 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> {
|
||||
subheaderFontSize * (1 - PROPORTION.TRENDLINE) * height,
|
||||
),
|
||||
)}
|
||||
{this.renderSubtitle(
|
||||
Math.ceil(subtitleFontSize * (1 - PROPORTION.TRENDLINE) * height),
|
||||
)}
|
||||
</div>
|
||||
{this.renderTrendline(chartHeight)}
|
||||
</div>
|
||||
@@ -318,6 +356,7 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> {
|
||||
{this.renderKicker((kickerFontSize || 0) * height)}
|
||||
{this.renderHeader(Math.ceil(headerFontSize * height))}
|
||||
{this.renderSubheader(Math.ceil(subheaderFontSize * height))}
|
||||
{this.renderSubtitle(Math.ceil(subtitleFontSize * height))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -368,7 +407,12 @@ export default styled(BigNumberVis)`
|
||||
|
||||
.subheader-line {
|
||||
line-height: 1em;
|
||||
padding-bottom: 0;
|
||||
padding-bottom: 0.3em;
|
||||
}
|
||||
|
||||
.subtitle-line {
|
||||
line-height: 1em;
|
||||
padding-top: 0.3em;
|
||||
}
|
||||
|
||||
&.is-fallback-value {
|
||||
|
||||
@@ -26,7 +26,12 @@ import {
|
||||
getStandardizedControls,
|
||||
temporalColumnMixin,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import { headerFontSize, subheaderFontSize } from '../sharedControls';
|
||||
import {
|
||||
headerFontSize,
|
||||
subheaderFontSize,
|
||||
subtitleFontSize,
|
||||
subtitleControl,
|
||||
} from '../sharedControls';
|
||||
|
||||
const config: ControlPanelConfig = {
|
||||
controlPanelSections: [
|
||||
@@ -134,6 +139,8 @@ const config: ControlPanelConfig = {
|
||||
['color_picker', null],
|
||||
[headerFontSize],
|
||||
[subheaderFontSize],
|
||||
[subtitleControl],
|
||||
[subtitleFontSize],
|
||||
['y_axis_format'],
|
||||
['currency_format'],
|
||||
[
|
||||
|
||||
@@ -66,6 +66,8 @@ export default function transformProps(
|
||||
metric = 'value',
|
||||
showTimestamp,
|
||||
showTrendLine,
|
||||
subtitle = '',
|
||||
subtitleFontSize,
|
||||
aggregation,
|
||||
startYAxisAtZero,
|
||||
subheader = '',
|
||||
@@ -302,6 +304,8 @@ export default function transformProps(
|
||||
formatTime,
|
||||
formData,
|
||||
headerFontSize,
|
||||
subtitleFontSize,
|
||||
subtitle,
|
||||
subheaderFontSize,
|
||||
mainColor,
|
||||
showTimestamp,
|
||||
|
||||
@@ -55,6 +55,39 @@ export const headerFontSize: CustomControlItem = {
|
||||
},
|
||||
};
|
||||
|
||||
export const subtitleFontSize: CustomControlItem = {
|
||||
name: 'subtitle_font_size',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
label: t('Subtitle Font Size'),
|
||||
renderTrigger: true,
|
||||
clearable: false,
|
||||
default: 0.15,
|
||||
// Values represent the percentage of space a subtitle 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,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
export const subheaderFontSize: CustomControlItem = {
|
||||
name: 'subheader_font_size',
|
||||
config: {
|
||||
@@ -88,3 +121,13 @@ export const subheaderFontSize: CustomControlItem = {
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const subtitleControl: CustomControlItem = {
|
||||
name: 'subtitle',
|
||||
config: {
|
||||
type: 'TextControl',
|
||||
label: t('Subtitle'),
|
||||
renderTrigger: true,
|
||||
description: t('Description text that shows up below your Big Number'),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -78,7 +78,9 @@ export type BigNumberVizProps = {
|
||||
headerFontSize: number;
|
||||
kickerFontSize?: number;
|
||||
subheader: string;
|
||||
subtitle: string;
|
||||
subheaderFontSize: number;
|
||||
subtitleFontSize: number;
|
||||
showTimestamp?: boolean;
|
||||
showTrendLine?: boolean;
|
||||
startYAxisAtZero?: boolean;
|
||||
|
||||
Reference in New Issue
Block a user