You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by li...@apache.org on 2024/03/27 16:28:03 UTC
(superset) 02/04: implement basic color formatter
This is an automated email from the ASF dual-hosted git repository.
lilykuang pushed a commit to branch table-time-comparison-color
in repository https://gitbox.apache.org/repos/asf/superset.git
commit c71c472b976df316cc6a4b901ec2ee9a3a83e454
Author: lilykuang <ji...@gmail.com>
AuthorDate: Mon Mar 25 20:22:14 2024 -0700
implement basic color formatter
---
.../plugins/plugin-chart-table/src/TableChart.tsx | 37 +++++++++-
.../plugin-chart-table/src/controlPanel.tsx | 75 ++++++++++++++++++++-
.../plugin-chart-table/src/transformProps.ts | 78 +++++++++++++++++++++-
.../plugins/plugin-chart-table/src/types.ts | 6 ++
4 files changed, 188 insertions(+), 8 deletions(-)
diff --git a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx
index c274296ccd..ef564fdf27 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx
+++ b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx
@@ -59,7 +59,11 @@ import {
} from '@ant-design/icons';
import { isEmpty } from 'lodash';
-import { DataColumnMeta, TableChartTransformedProps } from './types';
+import {
+ ColorSchemeEnum,
+ DataColumnMeta,
+ TableChartTransformedProps,
+} from './types';
import DataTable, {
DataTableProps,
SearchInputProps,
@@ -248,6 +252,7 @@ export default function TableChart<D extends DataRecord = DataRecord>(
onContextMenu,
emitCrossFilters,
enableTimeComparison,
+ basicColorFormatter,
} = props;
const comparisonColumns = [
{ key: 'all', label: t('Display all') },
@@ -692,7 +697,11 @@ export default function TableChart<D extends DataRecord = DataRecord>(
Array.isArray(columnColorFormatters) &&
columnColorFormatters.length > 0;
+ const hasBasicColorFormatter =
+ Array.isArray(basicColorFormatter) && basicColorFormatter.length > 0;
+
const valueRange =
+ !hasBasicColorFormatter &&
!hasColumnColorFormatters &&
(config.showCellBars === undefined
? showCellBars
@@ -728,6 +737,17 @@ export default function TableChart<D extends DataRecord = DataRecord>(
const html = isHtml ? { __html: text } : undefined;
let backgroundColor;
+ let arrow = '';
+ const originKey = column.key.substring(column.label.length).trim();
+ if (!hasColumnColorFormatters && hasBasicColorFormatter) {
+ backgroundColor =
+ basicColorFormatter[row.index][originKey]?.backgroundColor;
+ arrow =
+ column.label === comparisonLabels[0]
+ ? basicColorFormatter[row.index][originKey]?.mainArrow
+ : '';
+ }
+
if (hasColumnColorFormatters) {
columnColorFormatters!
.filter(formatter => formatter.column === column.key)
@@ -773,6 +793,15 @@ export default function TableChart<D extends DataRecord = DataRecord>(
`}
`;
+ const arrowStyles = css`
+ color: ${basicColorFormatter &&
+ basicColorFormatter[row.index][originKey]?.arrowColor ===
+ ColorSchemeEnum.Green
+ ? theme.colors.success.base
+ : theme.colors.error.base};
+ margin-right: ${theme.gridUnit}px;
+ `;
+
const cellProps = {
// show raw number in title in case of numeric values
title: typeof value === 'number' ? String(value) : undefined,
@@ -838,10 +867,14 @@ export default function TableChart<D extends DataRecord = DataRecord>(
className="dt-truncate-cell"
style={columnWidth ? { width: columnWidth } : undefined}
>
+ {arrow && <span css={arrowStyles}>{arrow}</span>}
{text}
</div>
) : (
- text
+ <>
+ {arrow && <span css={arrowStyles}>{arrow}</span>}
+ {text}
+ </>
)}
</StyledCell>
);
diff --git a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx
index 93402978b7..6fcdcd825d 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx
@@ -50,7 +50,8 @@ import {
getStandardizedControls,
} from '@superset-ui/chart-controls';
-import { PAGE_SIZE_OPTIONS } from './consts';
+import { COMPARISON_PREFIX, PAGE_SIZE_OPTIONS } from './consts';
+import { ColorSchemeEnum } from './types';
function getQueryMode(controls: ControlStateMapping): QueryMode {
const mode = controls?.query_mode?.value;
@@ -153,6 +154,33 @@ const percentMetricsControl: typeof sharedControls.metrics = {
validators: [],
};
+const processComparisonColumns = (columns: any[]) =>
+ columns
+ .map(col => {
+ if (!col.label.includes(COMPARISON_PREFIX)) {
+ return [
+ {
+ label: `${t('Main')} ${col.label}`,
+ value: `${t('Main')} ${col.value}`,
+ },
+ {
+ label: `# ${col.label}`,
+ value: `# ${col.value}`,
+ },
+ {
+ label: `△ ${col.label}`,
+ value: `△ ${col.value}`,
+ },
+ {
+ label: `% ${col.label}`,
+ value: `% ${col.value}`,
+ },
+ ];
+ }
+ return [];
+ })
+ .flat();
+
const config: ControlPanelConfig = {
controlPanelSections: [
{
@@ -568,13 +596,47 @@ const config: ControlPanelConfig = {
},
},
],
+ [
+ {
+ name: 'comparison_color_enabled',
+ config: {
+ type: 'CheckboxControl',
+ label: t('basic conditional formatting for comparison '),
+ renderTrigger: true,
+ default: false,
+ description: t('Add color for positive/negative change'),
+ },
+ },
+ ],
+ [
+ {
+ name: 'comparison_color_scheme',
+ config: {
+ type: 'SelectControl',
+ label: t('color scheme for comparison'),
+ default: ColorSchemeEnum.Green,
+ renderTrigger: true,
+ choices: [
+ [ColorSchemeEnum.Green, 'Green for increase, red for decrease'],
+ [ColorSchemeEnum.Red, 'Red for increase, green for decrease'],
+ ],
+ visibility: ({ controls }) =>
+ controls?.comparison_color_enabled?.value === true,
+ description: t(
+ 'Adds color to the chart symbols based on the positive or ' +
+ 'negative change from the comparison value.',
+ ),
+ },
+ },
+ ],
[
{
name: 'conditional_formatting',
config: {
type: 'ConditionalFormattingControl',
renderTrigger: true,
- label: t('Conditional formatting'),
+ label: t('Custom Conditional Formatting'),
+ multi: true,
description: t(
'Apply conditional color formatting to numeric columns',
),
@@ -602,9 +664,16 @@ const config: ControlPanelConfig = {
label: verboseMap[colname] ?? colname,
}))
: [];
+
+ const columnOptions = Boolean(
+ explore?.controls?.enable_time_comparison?.value,
+ )
+ ? processComparisonColumns(numericColumns || [])
+ : numericColumns;
+
return {
removeIrrelevantConditions: chartStatus === 'success',
- columnOptions: numericColumns,
+ columnOptions,
verboseMap,
};
},
diff --git a/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts b/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts
index 64fb198016..8a39f4b4dd 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts
@@ -43,6 +43,7 @@ import {
import isEqualColumns from './utils/isEqualColumns';
import DateWithFormatter from './utils/DateWithFormatter';
import {
+ ColorSchemeEnum,
DataColumnMeta,
TableChartProps,
TableChartTransformedProps,
@@ -374,7 +375,74 @@ const transformProps = (
conditional_formatting: conditionalFormatting,
allow_rearrange_columns: allowRearrangeColumns,
enable_time_comparison: enableTimeComparison = false,
+ comparison_color_enabled: comparisonColorEnabled = false,
+ comparison_color_scheme: comparisonColorScheme = ColorSchemeEnum.Green,
} = formData;
+
+ const calculateColorAndArrow = (percentDifferenceNum: number) => {
+ if (percentDifferenceNum === 0) {
+ return {
+ arrow: '',
+ arrowColor: '',
+ basicBackgroundColor: 'rgba(255, 191, 161, 0.2)',
+ };
+ }
+ const isPositive = percentDifferenceNum > 0;
+ const arrow = isPositive ? '↑' : '↓';
+ const arrowColor =
+ comparisonColorScheme === ColorSchemeEnum.Green
+ ? isPositive
+ ? ColorSchemeEnum.Green
+ : ColorSchemeEnum.Red
+ : isPositive
+ ? ColorSchemeEnum.Red
+ : ColorSchemeEnum.Green;
+ const basicBackgroundColor =
+ comparisonColorScheme === ColorSchemeEnum.Green
+ ? `rgba(${isPositive ? '0,150,0' : '150,0,0'},0.2)`
+ : `rgba(${isPositive ? '150,0,0' : '0,150,0'},0.2)`;
+
+ return { arrow, arrowColor, basicBackgroundColor };
+ };
+
+ const getBasicColorFormatter = memoizeOne(
+ function processComparisonDataRecords(
+ originalData: DataRecord[] | undefined,
+ originalColumns: DataColumnMeta[],
+ ) {
+ // Transform data
+ return originalData?.map(originalItem => {
+ const item: any = {};
+ originalColumns.forEach(origCol => {
+ if (
+ (origCol.isMetric || origCol.isPercentMetric) &&
+ !origCol.key.includes(COMPARISON_PREFIX) &&
+ origCol.isNumeric
+ ) {
+ const originalValue = originalItem[origCol.key] || 0;
+ const comparisonValue = origCol.isMetric
+ ? originalItem?.[`${COMPARISON_PREFIX}${origCol.key}`] || 0
+ : originalItem[`%${COMPARISON_PREFIX}${origCol.key.slice(1)}`] ||
+ 0;
+ const { percentDifferenceNum } = calculateDifferences(
+ originalValue as number,
+ comparisonValue as number,
+ );
+
+ const { arrow, arrowColor, basicBackgroundColor } =
+ calculateColorAndArrow(percentDifferenceNum);
+
+ item[`${origCol.key}`] = {};
+ item[`${origCol.key}`].mainArrow = arrow;
+ item[`${origCol.key}`].arrowColor = arrowColor;
+ item[`${origCol.key}`].backgroundColor = basicBackgroundColor;
+ }
+ });
+ return item;
+ });
+ },
+ );
+
const canUseTimeComparison =
enableTimeComparison &&
isFeatureEnabled(FeatureFlag.ChartPluginsExperimental) &&
@@ -407,15 +475,18 @@ const transformProps = (
showTotals && queryMode === QueryMode.Aggregate
? totalQuery?.data[0]
: undefined;
- const columnColorFormatters =
- getColorFormatters(conditionalFormatting, data) ?? defaultColorFormatters;
const comparisonTotals = processComparisonTotals(totals);
-
const passedData = canUseTimeComparison ? comparisonData || [] : data;
const passedTotals = canUseTimeComparison ? comparisonTotals : totals;
const passedColumns = canUseTimeComparison ? comparisonColumns : columns;
+ const basicColorFormatter =
+ comparisonColorEnabled && getBasicColorFormatter(baseQuery?.data, columns);
+ const columnColorFormatters =
+ getColorFormatters(conditionalFormatting, passedData) ??
+ defaultColorFormatters;
+
return {
height,
width,
@@ -447,6 +518,7 @@ const transformProps = (
allowRearrangeColumns,
onContextMenu,
enableTimeComparison: canUseTimeComparison,
+ basicColorFormatter,
};
};
diff --git a/superset-frontend/plugins/plugin-chart-table/src/types.ts b/superset-frontend/plugins/plugin-chart-table/src/types.ts
index 1806eddb1a..f4ca8473f1 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/types.ts
+++ b/superset-frontend/plugins/plugin-chart-table/src/types.ts
@@ -137,6 +137,12 @@ export interface TableChartTransformedProps<D extends DataRecord = DataRecord> {
filters?: ContextMenuFilters,
) => void;
enableTimeComparison?: boolean;
+ basicColorFormatter?: any;
+}
+
+export enum ColorSchemeEnum {
+ 'Green' = 'Green',
+ 'Red' = 'Red',
}
export default {};