This is an automated email from the ASF dual-hosted git repository.

arivero pushed a commit to branch table-time-comparison-offset
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 08173d95ae7d505ac62194b0cde00bee14fc2be8
Author: Lily Kuang <[email protected]>
AuthorDate: Tue Apr 2 13:19:53 2024 -0700

    feat(plugin): color option for table with time comparison (#27716)
    
    - cherry picked from afe7eaf8b3d16ef8aefdc5b2bf8f0953a59d9b60
---
 .../plugins/plugin-chart-table/src/TableChart.tsx  |  66 +++++++-
 .../plugin-chart-table/src/controlPanel.tsx        | 171 ++++++++++++++++-----
 .../plugin-chart-table/src/transformProps.ts       | 134 +++++++++++++++-
 .../plugins/plugin-chart-table/src/types.ts        |  13 ++
 .../ConditionalFormattingControl.tsx               |   3 +
 .../FormattingPopover.tsx                          |   2 +
 .../FormattingPopoverContent.tsx                   |  39 ++++-
 .../controls/ConditionalFormattingControl/types.ts |   2 +
 8 files changed, 383 insertions(+), 47 deletions(-)

diff --git a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx 
b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx
index 5c0e99b203..dc8221821a 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,
@@ -250,6 +254,8 @@ export default function TableChart<D extends DataRecord = 
DataRecord>(
     onContextMenu,
     emitCrossFilters,
     isUsingTimeComparison,
+    basicColorFormatters,
+    basicColorColumnFormatters,
   } = props;
   const comparisonColumns = [
     { key: 'all', label: t('Display all') },
@@ -693,7 +699,13 @@ export default function TableChart<D extends DataRecord = 
DataRecord>(
         Array.isArray(columnColorFormatters) &&
         columnColorFormatters.length > 0;
 
+      const hasBasicColorFormatters =
+        isUsingTimeComparison &&
+        Array.isArray(basicColorFormatters) &&
+        basicColorFormatters.length > 0;
+
       const valueRange =
+        !hasBasicColorFormatters &&
         !hasColumnColorFormatters &&
         (config.showCellBars === undefined
           ? showCellBars
@@ -727,6 +739,17 @@ export default function TableChart<D extends DataRecord = 
DataRecord>(
           const html = isHtml && allowRenderHtml ? { __html: text } : 
undefined;
 
           let backgroundColor;
+          let arrow = '';
+          const originKey = column.key.substring(column.label.length).trim();
+          if (!hasColumnColorFormatters && hasBasicColorFormatters) {
+            backgroundColor =
+              basicColorFormatters[row.index][originKey]?.backgroundColor;
+            arrow =
+              column.label === comparisonLabels[0]
+                ? basicColorFormatters[row.index][originKey]?.mainArrow
+                : '';
+          }
+
           if (hasColumnColorFormatters) {
             columnColorFormatters!
               .filter(formatter => formatter.column === column.key)
@@ -741,6 +764,19 @@ export default function TableChart<D extends DataRecord = 
DataRecord>(
               });
           }
 
+          if (
+            basicColorColumnFormatters &&
+            basicColorColumnFormatters?.length > 0
+          ) {
+            backgroundColor =
+              basicColorColumnFormatters[row.index][column.key]
+                ?.backgroundColor || backgroundColor;
+            arrow =
+              column.label === comparisonLabels[0]
+                ? basicColorColumnFormatters[row.index][column.key]?.mainArrow
+                : '';
+          }
+
           const StyledCell = styled.td`
             text-align: ${sharedStyle.textAlign};
             white-space: ${value instanceof Date ? 'nowrap' : undefined};
@@ -772,6 +808,28 @@ export default function TableChart<D extends DataRecord = 
DataRecord>(
               `}
           `;
 
+          let arrowStyles = css`
+            color: ${basicColorFormatters &&
+            basicColorFormatters[row.index][originKey]?.arrowColor ===
+              ColorSchemeEnum.Green
+              ? theme.colors.success.base
+              : theme.colors.error.base};
+            margin-right: ${theme.gridUnit}px;
+          `;
+
+          if (
+            basicColorColumnFormatters &&
+            basicColorColumnFormatters?.length > 0
+          ) {
+            arrowStyles = css`
+              color: ${basicColorColumnFormatters[row.index][column.key]
+                ?.arrowColor === ColorSchemeEnum.Green
+                ? theme.colors.success.base
+                : theme.colors.error.base};
+              margin-right: ${theme.gridUnit}px;
+            `;
+          }
+
           const cellProps = {
             'aria-labelledby': `header-${column.key}`,
             role: 'cell',
@@ -841,10 +899,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 f5f28d5d38..5859a6a0bf 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx
@@ -49,6 +49,7 @@ import {
 
 import { isEmpty } from 'lodash';
 import { PAGE_SIZE_OPTIONS } from './consts';
+import { ColorSchemeEnum } from './types';
 
 function getQueryMode(controls: ControlStateMapping): QueryMode {
   const mode = controls?.query_mode?.value;
@@ -145,6 +146,33 @@ const percentMetricsControl: typeof sharedControls.metrics 
= {
   validators: [],
 };
 
+const processComparisonColumns = (columns: any[], suffix: string) =>
+  columns
+    .map(col => {
+      if (!col.label.includes(suffix)) {
+        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: [
     {
@@ -400,11 +428,70 @@ const config: ControlPanelConfig = {
               description: t('Whether to include a client-side search box'),
             },
           },
+        ],
+        [
+          {
+            name: 'allow_rearrange_columns',
+            config: {
+              type: 'CheckboxControl',
+              label: t('Allow columns to be rearranged'),
+              renderTrigger: true,
+              default: false,
+              description: t(
+                "Allow end user to drag-and-drop column headers to rearrange 
them. Note their changes won't persist for the next time they open the chart.",
+              ),
+              visibility: ({ controls }) =>
+                isEmpty(controls?.time_compare?.value),
+            },
+          },
+        ],
+        [
+          {
+            name: 'allow_render_html',
+            config: {
+              type: 'CheckboxControl',
+              label: t('Render columns in HTML format'),
+              renderTrigger: true,
+              default: true,
+              description: t('Render data in HTML format if applicable.'),
+            },
+          },
+        ],
+        [
+          {
+            name: 'column_config',
+            config: {
+              type: 'ColumnConfigControl',
+              label: t('Customize columns'),
+              description: t('Further customize how to display each column'),
+              width: 400,
+              height: 320,
+              renderTrigger: true,
+              shouldMapStateToProps() {
+                return true;
+              },
+              mapStateToProps(explore, _, chart) {
+                return {
+                  queryResponse: chart?.queriesResponse?.[0] as
+                    | ChartDataResponseResult
+                    | undefined,
+                };
+              },
+            },
+          },
+        ],
+      ],
+    },
+    {
+      label: t('Visual formatting'),
+      expanded: true,
+      controlSetRows: [
+        [
           {
             name: 'show_cell_bars',
             config: {
               type: 'CheckboxControl',
-              label: t('Cell bars'),
+              label: t('Show Cell bars'),
               renderTrigger: true,
               default: true,
               description: t(
@@ -426,11 +513,13 @@ const config: ControlPanelConfig = {
               ),
             },
           },
+        ],
+        [
           {
             name: 'color_pn',
             config: {
               type: 'CheckboxControl',
-              label: t('Color +/-'),
+              label: t('add colors to cell bars for +/-'),
               renderTrigger: true,
               default: true,
               description: t(
@@ -441,52 +530,41 @@ const config: ControlPanelConfig = {
         ],
         [
           {
-            name: 'allow_rearrange_columns',
+            name: 'comparison_color_enabled',
             config: {
               type: 'CheckboxControl',
-              label: t('Allow columns to be rearranged'),
+              label: t('basic conditional formatting'),
               renderTrigger: true,
+              visibility: ({ controls }) =>
+                !isEmpty(controls?.time_compare?.value),
               default: false,
               description: t(
-                "Allow end user to drag-and-drop column headers to rearrange 
them. Note their changes won't persist for the next time they open the chart.",
+                'This will be applied to the whole table. Arrows (↑ and ↓) 
will be added to ' +
+                  'main columns for increase and decrease. Basic conditional 
formatting can be ' +
+                  'overwritten by conditional formatting below.',
               ),
-              visibility: ({ controls }) =>
-                isEmpty(controls?.time_compare?.value),
-            },
-          },
-        ],
-        [
-          {
-            name: 'allow_render_html',
-            config: {
-              type: 'CheckboxControl',
-              label: t('Render columns in HTML format'),
-              renderTrigger: true,
-              default: true,
-              description: t('Render data in HTML format if applicable.'),
             },
           },
         ],
         [
           {
-            name: 'column_config',
+            name: 'comparison_color_scheme',
             config: {
-              type: 'ColumnConfigControl',
-              label: t('Customize columns'),
-              description: t('Further customize how to display each column'),
-              width: 400,
-              height: 320,
+              type: 'SelectControl',
+              label: t('color type'),
+              default: ColorSchemeEnum.Green,
               renderTrigger: true,
-              shouldMapStateToProps() {
-                return true;
-              },
-              mapStateToProps(explore, _, chart) {
-                return {
-                  queryResponse: chart?.queriesResponse?.[0] as
-                    | ChartDataResponseResult
-                    | undefined,
-                };
-              },
+              choices: [
+                [ColorSchemeEnum.Green, 'Green for increase, red for 
decrease'],
+                [ColorSchemeEnum.Red, 'Red for increase, green for decrease'],
+              ],
+              visibility: ({ controls }) =>
+                !isEmpty(controls?.time_compare?.value) &&
+                Boolean(controls?.comparison_color_enabled?.value),
+              description: t(
+                'Adds color to the chart symbols based on the positive or ' +
+                  'negative change from the comparison value.',
+              ),
             },
           },
         ],
@@ -496,7 +574,17 @@ const config: ControlPanelConfig = {
             config: {
               type: 'ConditionalFormattingControl',
               renderTrigger: true,
-              label: t('Conditional formatting'),
+              label: t('Custom Conditional Formatting'),
+              extraColorChoices: [
+                {
+                  value: ColorSchemeEnum.Green,
+                  label: t('Green for increase, red for decrease'),
+                },
+                {
+                  value: ColorSchemeEnum.Red,
+                  label: t('Red for increase, green for decrease'),
+                },
+              ],
               description: t(
                 'Apply conditional color formatting to numeric columns',
               ),
@@ -524,9 +612,18 @@ const config: ControlPanelConfig = {
                           label: verboseMap[colname] ?? colname,
                         }))
                     : [];
+                const columnOptions = explore?.controls?.time_compare?.value
+                  ? processComparisonColumns(
+                      numericColumns || [],
+                      ensureIsArray(
+                        explore?.controls?.time_compare?.value,
+                      )[0]?.toString() || '',
+                    )
+                  : 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 cf70bfed16..a9ead497c3 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-table/src/transformProps.ts
@@ -36,6 +36,7 @@ import {
 } from '@superset-ui/core';
 import {
   ColorFormatters,
+  ConditionalFormattingConfig,
   getColorFormatters,
 } from '@superset-ui/chart-controls';
 
@@ -43,6 +44,8 @@ import { isEmpty } from 'lodash';
 import isEqualColumns from './utils/isEqualColumns';
 import DateWithFormatter from './utils/DateWithFormatter';
 import {
+  BasicColorFormatterType,
+  ColorSchemeEnum,
   DataColumnMeta,
   TableChartProps,
   TableChartTransformedProps,
@@ -390,9 +393,124 @@ const transformProps = (
     allow_rearrange_columns: allowRearrangeColumns,
     allow_render_html: allowRenderHtml,
     time_compare,
+    comparison_color_enabled: comparisonColorEnabled = false,
+    comparison_color_scheme: comparisonColorScheme = ColorSchemeEnum.Green,
   } = formData;
   const isUsingTimeComparison =
     !isEmpty(time_compare) && queryMode === QueryMode.Aggregate;
+
+  const calculateBasicStyle = (
+    percentDifferenceNum: number,
+    colorOption: ColorSchemeEnum,
+  ) => {
+    if (percentDifferenceNum === 0) {
+      return {
+        arrow: '',
+        arrowColor: '',
+        // eslint-disable-next-line theme-colors/no-literal-colors
+        backgroundColor: '#FFBFA133',
+      };
+    }
+    const isPositive = percentDifferenceNum > 0;
+    const arrow = isPositive ? '↑' : '↓';
+    const arrowColor =
+      colorOption === ColorSchemeEnum.Green
+        ? isPositive
+          ? ColorSchemeEnum.Green
+          : ColorSchemeEnum.Red
+        : isPositive
+          ? ColorSchemeEnum.Red
+          : ColorSchemeEnum.Green;
+    const backgroundColor =
+      colorOption === 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, backgroundColor };
+  };
+
+  const getBasicColorFormatter = memoizeOne(function getBasicColorFormatter(
+    originalData: DataRecord[] | undefined,
+    originalColumns: DataColumnMeta[],
+    selectedColumns?: ConditionalFormattingConfig[],
+  ) {
+    // Transform data
+    const relevantColumns = selectedColumns
+      ? originalColumns.filter(col =>
+          selectedColumns.some(scol => scol?.column?.includes(col.key)),
+        )
+      : originalColumns;
+
+    return originalData?.map(originalItem => {
+      const item: { [key: string]: BasicColorFormatterType } = {};
+      relevantColumns.forEach(origCol => {
+        if (
+          (origCol.isMetric || origCol.isPercentMetric) &&
+          !origCol.key.includes(ensureIsArray(time_compare)[0]) &&
+          origCol.isNumeric
+        ) {
+          const originalValue = originalItem[origCol.key] || 0;
+          const comparisonValue = origCol.isMetric
+            ? originalItem?.[
+                `${origCol.key}__${ensureIsArray(time_compare)[0]}`
+              ] || 0
+            : originalItem[
+                `%${origCol.key.slice(1)}__${ensureIsArray(time_compare)[0]}`
+              ] || 0;
+          const { percentDifferenceNum } = calculateDifferences(
+            originalValue as number,
+            comparisonValue as number,
+          );
+
+          if (selectedColumns) {
+            selectedColumns.forEach(col => {
+              if (col?.column?.includes(origCol.key)) {
+                const { arrow, arrowColor, backgroundColor } =
+                  calculateBasicStyle(
+                    percentDifferenceNum,
+                    col.colorScheme || comparisonColorScheme,
+                  );
+                item[col.column] = {
+                  mainArrow: arrow,
+                  arrowColor,
+                  backgroundColor,
+                };
+              }
+            });
+          } else {
+            const { arrow, arrowColor, backgroundColor } = calculateBasicStyle(
+              percentDifferenceNum,
+              comparisonColorScheme,
+            );
+            item[`${origCol.key}`] = {
+              mainArrow: arrow,
+              arrowColor,
+              backgroundColor,
+            };
+          }
+        }
+      });
+      return item;
+    });
+  });
+
+  const getBasicColorFormatterForColumn = (
+    originalData: DataRecord[] | undefined,
+    originalColumns: DataColumnMeta[],
+    conditionalFormatting?: ConditionalFormattingConfig[],
+  ) => {
+    const selectedColumns = conditionalFormatting?.filter(
+      (config: ConditionalFormattingConfig) =>
+        config.column &&
+        (config.colorScheme === ColorSchemeEnum.Green ||
+          config.colorScheme === ColorSchemeEnum.Red),
+    );
+
+    return selectedColumns?.length
+      ? getBasicColorFormatter(originalData, originalColumns, selectedColumns)
+      : undefined;
+  };
+
   const timeGrain = extractTimegrain(formData);
   const comparisonSuffix = isUsingTimeComparison
     ? ensureIsArray(time_compare)[0]
@@ -431,12 +549,22 @@ const transformProps = (
         ? processComparisonTotals(comparisonSuffix, totalQuery?.data)
         : totalQuery?.data[0]
       : undefined;
-  const columnColorFormatters =
-    getColorFormatters(conditionalFormatting, data) ?? defaultColorFormatters;
 
   const passedData = isUsingTimeComparison ? comparisonData || [] : data;
   const passedColumns = isUsingTimeComparison ? comparisonColumns : columns;
 
+  const basicColorFormatters =
+    comparisonColorEnabled && getBasicColorFormatter(baseQuery?.data, columns);
+  const columnColorFormatters =
+    getColorFormatters(conditionalFormatting, passedData) ??
+    defaultColorFormatters;
+
+  const basicColorColumnFormatters = getBasicColorFormatterForColumn(
+    baseQuery?.data,
+    columns,
+    conditionalFormatting,
+  );
+
   return {
     height,
     width,
@@ -469,6 +597,8 @@ const transformProps = (
     allowRenderHtml,
     onContextMenu,
     isUsingTimeComparison,
+    basicColorFormatters,
+    basicColorColumnFormatters,
   };
 };
 
diff --git a/superset-frontend/plugins/plugin-chart-table/src/types.ts 
b/superset-frontend/plugins/plugin-chart-table/src/types.ts
index 2abba6815d..7aa14d06ac 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/types.ts
+++ b/superset-frontend/plugins/plugin-chart-table/src/types.ts
@@ -102,6 +102,12 @@ export interface TableChartProps extends ChartProps {
   queriesData: ChartDataResponseResult[];
 }
 
+export type BasicColorFormatterType = {
+  backgroundColor: string;
+  arrowColor: string;
+  mainArrow: string;
+};
+
 export interface TableChartTransformedProps<D extends DataRecord = DataRecord> 
{
   timeGrain?: TimeGranularity;
   height: number;
@@ -137,6 +143,13 @@ export interface TableChartTransformedProps<D extends 
DataRecord = DataRecord> {
     filters?: ContextMenuFilters,
   ) => void;
   isUsingTimeComparison?: boolean;
+  basicColorFormatters?: { [Key: string]: BasicColorFormatterType }[];
+  basicColorColumnFormatters?: { [Key: string]: BasicColorFormatterType }[];
+}
+
+export enum ColorSchemeEnum {
+  'Green' = 'Green',
+  'Red' = 'Red',
 }
 
 export default {};
diff --git 
a/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/ConditionalFormattingControl.tsx
 
b/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/ConditionalFormattingControl.tsx
index 6c4ae9f412..9bf0c90f7c 100644
--- 
a/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/ConditionalFormattingControl.tsx
+++ 
b/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/ConditionalFormattingControl.tsx
@@ -71,6 +71,7 @@ const ConditionalFormattingControl = ({
   columnOptions,
   verboseMap,
   removeIrrelevantConditions,
+  extraColorChoices,
   ...props
 }: ConditionalFormattingControlProps) => {
   const theme = useTheme();
@@ -155,6 +156,7 @@ const ConditionalFormattingControl = ({
                 onEdit(newConfig, index)
               }
               destroyTooltipOnHide
+              extraColorChoices={extraColorChoices}
             >
               <OptionControlContainer withCaret>
                 <Label>{createLabel(config)}</Label>
@@ -170,6 +172,7 @@ const ConditionalFormattingControl = ({
           columns={columnOptions}
           onChange={onSave}
           destroyTooltipOnHide
+          extraColorChoices={extraColorChoices}
         >
           <AddControlLabel>
             <Icons.PlusSmall iconColor={theme.colors.grayscale.light1} />
diff --git 
a/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/FormattingPopover.tsx
 
b/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/FormattingPopover.tsx
index c44ca76235..fda09d4e61 100644
--- 
a/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/FormattingPopover.tsx
+++ 
b/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/FormattingPopover.tsx
@@ -27,6 +27,7 @@ export const FormattingPopover = ({
   onChange,
   config,
   children,
+  extraColorChoices,
   ...props
 }: FormattingPopoverProps) => {
   const [visible, setVisible] = useState(false);
@@ -47,6 +48,7 @@ export const FormattingPopover = ({
           onChange={handleSave}
           config={config}
           columns={columns}
+          extraColorChoices={extraColorChoices}
         />
       }
       visible={visible}
diff --git 
a/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/FormattingPopoverContent.tsx
 
b/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/FormattingPopoverContent.tsx
index 533e185475..1f17019d8c 100644
--- 
a/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/FormattingPopoverContent.tsx
+++ 
b/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/FormattingPopoverContent.tsx
@@ -16,8 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React from 'react';
+import React, { useState } from 'react';
 import { styled, SupersetTheme, t, useTheme } from '@superset-ui/core';
+import { ColorSchemeEnum } from '@superset-ui/plugin-chart-table';
 import {
   Comparator,
   MultipleValueComparators,
@@ -123,21 +124,24 @@ const shouldFormItemUpdate = (
   isOperatorMultiValue(prevValues.operator) !==
     isOperatorMultiValue(currentValues.operator);
 
-const operatorField = (
+const operatorField = (showOnlyNone?: boolean) => (
   <FormItem
     name="operator"
     label={t('Operator')}
     rules={rulesRequired}
     initialValue={operatorOptions[0].value}
   >
-    <Select ariaLabel={t('Operator')} options={operatorOptions} />
+    <Select
+      ariaLabel={t('Operator')}
+      options={showOnlyNone ? [operatorOptions[0]] : operatorOptions}
+    />
   </FormItem>
 );
 
 const renderOperatorFields = ({ getFieldValue }: GetFieldValue) =>
   isOperatorNone(getFieldValue('operator')) ? (
     <Row gutter={12}>
-      <Col span={6}>{operatorField}</Col>
+      <Col span={6}>{operatorField()}</Col>
     </Row>
   ) : isOperatorMultiValue(getFieldValue('operator')) ? (
     <Row gutter={12}>
@@ -186,13 +190,26 @@ export const FormattingPopoverContent = ({
   config,
   onChange,
   columns = [],
+  extraColorChoices = [],
 }: {
   config?: ConditionalFormattingConfig;
   onChange: (config: ConditionalFormattingConfig) => void;
   columns: { label: string; value: string }[];
+  extraColorChoices?: { label: string; value: string }[];
 }) => {
   const theme = useTheme();
   const colorScheme = colorSchemeOptions(theme);
+  const [showOperatorFields, setShowOperatorFields] = useState(
+    config === undefined ||
+      (config?.colorScheme !== ColorSchemeEnum.Green &&
+        config?.colorScheme !== ColorSchemeEnum.Red),
+  );
+  const handleChange = (event: any) => {
+    setShowOperatorFields(
+      !(event === ColorSchemeEnum.Green || event === ColorSchemeEnum.Red),
+    );
+  };
+
   return (
     <Form
       onFinish={onChange}
@@ -218,12 +235,22 @@ export const FormattingPopoverContent = ({
             rules={rulesRequired}
             initialValue={colorScheme[0].value}
           >
-            <Select ariaLabel={t('Color scheme')} options={colorScheme} />
+            <Select
+              onChange={event => handleChange(event)}
+              ariaLabel={t('Color scheme')}
+              options={[...colorScheme, ...extraColorChoices]}
+            />
           </FormItem>
         </Col>
       </Row>
       <FormItem noStyle shouldUpdate={shouldFormItemUpdate}>
-        {renderOperatorFields}
+        {showOperatorFields ? (
+          renderOperatorFields
+        ) : (
+          <Row gutter={12}>
+            <Col span={6}>{operatorField(true)}</Col>
+          </Row>
+        )}
       </FormItem>
       <FormItem>
         <JustifyEnd>
diff --git 
a/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/types.ts
 
b/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/types.ts
index 4edadf99b4..c352ca818b 100644
--- 
a/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/types.ts
+++ 
b/superset-frontend/src/explore/components/controls/ConditionalFormattingControl/types.ts
@@ -38,6 +38,7 @@ export type ConditionalFormattingControlProps = 
ControlComponentProps<
   verboseMap: Record<string, string>;
   label: string;
   description: string;
+  extraColorChoices?: { label: string; value: string }[];
 };
 
 export type FormattingPopoverProps = PopoverProps & {
@@ -46,4 +47,5 @@ export type FormattingPopoverProps = PopoverProps & {
   config?: ConditionalFormattingConfig;
   title: string;
   children: ReactNode;
+  extraColorChoices?: { label: string; value: string }[];
 };

Reply via email to