feat(charts): add subtitle option and metric customization controls (#32975)

This commit is contained in:
Levis Mbote
2025-04-10 18:24:24 +03:00
committed by GitHub
parent 164a07e2be
commit d75ff9e784
19 changed files with 690 additions and 401 deletions

View File

@@ -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>
))}

View File

@@ -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'],
],
},
],
},

View File

@@ -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,

View File

@@ -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;

View File

@@ -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'],
[

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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'],
[

View File

@@ -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,

View File

@@ -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'),
},
};

View File

@@ -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;