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

michaelsmolina pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 19ec7b48a0f fix: Conditional formatting painting empty cells (#37894)
19ec7b48a0f is described below

commit 19ec7b48a0f94c350306f7379dd8466ae022e80e
Author: Michael S. Molina <[email protected]>
AuthorDate: Thu Feb 12 10:22:00 2026 -0300

    fix: Conditional formatting painting empty cells (#37894)
---
 .../packages/superset-core/src/utils/index.ts      |   1 +
 .../superset-core/src/utils/isBlank.test.ts        |  59 +++++++++++
 .../src/utils/{index.ts => isBlank.ts}             |  10 +-
 .../src/utils/getColorFormatters.ts                |  13 +--
 .../test/utils/getColorFormatters.test.ts          | 111 +++++++++++++++++++++
 .../plugin-chart-table/test/TableChart.test.tsx    |   4 +-
 6 files changed, 186 insertions(+), 12 deletions(-)

diff --git a/superset-frontend/packages/superset-core/src/utils/index.ts 
b/superset-frontend/packages/superset-core/src/utils/index.ts
index 724adae6249..62f2b9bfbdf 100644
--- a/superset-frontend/packages/superset-core/src/utils/index.ts
+++ b/superset-frontend/packages/superset-core/src/utils/index.ts
@@ -17,4 +17,5 @@
  * under the License.
  */
 
+export { default as isBlank } from './isBlank';
 export { default as logging } from './logging';
diff --git a/superset-frontend/packages/superset-core/src/utils/isBlank.test.ts 
b/superset-frontend/packages/superset-core/src/utils/isBlank.test.ts
new file mode 100644
index 00000000000..52a9b340333
--- /dev/null
+++ b/superset-frontend/packages/superset-core/src/utils/isBlank.test.ts
@@ -0,0 +1,59 @@
+/**
+ * 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 isBlank from './isBlank';
+
+test('returns true for null', () => {
+  expect(isBlank(null)).toBe(true);
+});
+
+test('returns true for undefined', () => {
+  expect(isBlank(undefined)).toBe(true);
+});
+
+test('returns true for empty string', () => {
+  expect(isBlank('')).toBe(true);
+});
+
+test('returns true for whitespace-only strings', () => {
+  expect(isBlank(' ')).toBe(true);
+  expect(isBlank('  ')).toBe(true);
+  expect(isBlank('\t')).toBe(true);
+  expect(isBlank('\n')).toBe(true);
+  expect(isBlank(' \t\n ')).toBe(true);
+});
+
+test('returns false for non-empty strings', () => {
+  expect(isBlank('hello')).toBe(false);
+  expect(isBlank(' hello ')).toBe(false);
+});
+
+test('returns true for NaN', () => {
+  expect(isBlank(NaN)).toBe(true);
+});
+
+test('returns false for numbers', () => {
+  expect(isBlank(0)).toBe(false);
+  expect(isBlank(50)).toBe(false);
+  expect(isBlank(-1)).toBe(false);
+});
+
+test('returns false for booleans', () => {
+  expect(isBlank(true)).toBe(false);
+  expect(isBlank(false)).toBe(false);
+});
diff --git a/superset-frontend/packages/superset-core/src/utils/index.ts 
b/superset-frontend/packages/superset-core/src/utils/isBlank.ts
similarity index 72%
copy from superset-frontend/packages/superset-core/src/utils/index.ts
copy to superset-frontend/packages/superset-core/src/utils/isBlank.ts
index 724adae6249..e409dabe529 100644
--- a/superset-frontend/packages/superset-core/src/utils/index.ts
+++ b/superset-frontend/packages/superset-core/src/utils/isBlank.ts
@@ -16,5 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import { isEmpty, isNaN, isNil, isString, trim } from 'lodash';
 
-export { default as logging } from './logging';
+/**
+ * Checks if a value is null, undefined, NaN, or a whitespace-only string.
+ */
+export default function isBlank(value: unknown): boolean {
+  return (
+    isNil(value) || isNaN(value) || (isString(value) && isEmpty(trim(value)))
+  );
+}
diff --git 
a/superset-frontend/packages/superset-ui-chart-controls/src/utils/getColorFormatters.ts
 
b/superset-frontend/packages/superset-ui-chart-controls/src/utils/getColorFormatters.ts
index 3bdb9a0ad6a..dbfa4b28428 100644
--- 
a/superset-frontend/packages/superset-ui-chart-controls/src/utils/getColorFormatters.ts
+++ 
b/superset-frontend/packages/superset-ui-chart-controls/src/utils/getColorFormatters.ts
@@ -17,6 +17,8 @@
  * under the License.
  */
 import memoizeOne from 'memoize-one';
+import { isString, isBoolean } from 'lodash';
+import { isBlank } from '@apache-superset/core';
 import { addAlpha, DataRecord } from '@superset-ui/core';
 import {
   ColorFormatters,
@@ -254,6 +256,9 @@ export const getColorFunction = (
   }
 
   return (value: number | string | boolean | null) => {
+    if (isBlank(value) && operator !== Comparator.IsNull) {
+      return undefined;
+    }
     const compareResult = comparatorFunction(value, columnValues);
     if (compareResult === false) return undefined;
     const { cutoffValue, extremeValue } = compareResult;
@@ -318,11 +323,3 @@ export const getColorFormatters = memoizeOne(
       [],
     ) ?? [],
 );
-
-function isString(value: unknown) {
-  return typeof value === 'string';
-}
-
-function isBoolean(value: unknown) {
-  return typeof value === 'boolean';
-}
diff --git 
a/superset-frontend/packages/superset-ui-chart-controls/test/utils/getColorFormatters.test.ts
 
b/superset-frontend/packages/superset-ui-chart-controls/test/utils/getColorFormatters.test.ts
index e1be6ae36e3..0c0563049fe 100644
--- 
a/superset-frontend/packages/superset-ui-chart-controls/test/utils/getColorFormatters.test.ts
+++ 
b/superset-frontend/packages/superset-ui-chart-controls/test/utils/getColorFormatters.test.ts
@@ -506,6 +506,117 @@ test('getColorFunction IsNotNull', () => {
   expect(colorFunction(null)).toBeUndefined();
 });
 
+test('getColorFunction returns undefined for null values on numeric 
comparators', () => {
+  const operators = [
+    { operator: Comparator.LessThan, targetValue: 50 },
+    { operator: Comparator.LessOrEqual, targetValue: 50 },
+    { operator: Comparator.GreaterThan, targetValue: 50 },
+    { operator: Comparator.GreaterOrEqual, targetValue: 50 },
+    { operator: Comparator.Equal, targetValue: 50 },
+    { operator: Comparator.NotEqual, targetValue: 50 },
+  ];
+  operators.forEach(({ operator, targetValue }) => {
+    const colorFunction = getColorFunction(
+      {
+        operator,
+        targetValue,
+        colorScheme: '#FF0000',
+        column: 'count',
+      },
+      countValues,
+    );
+    expect(colorFunction(null)).toBeUndefined();
+    expect(colorFunction(undefined as unknown as null)).toBeUndefined();
+  });
+});
+
+test('getColorFunction returns undefined for null values on Between 
comparators', () => {
+  const operators = [
+    Comparator.Between,
+    Comparator.BetweenOrEqual,
+    Comparator.BetweenOrLeftEqual,
+    Comparator.BetweenOrRightEqual,
+  ];
+  operators.forEach(operator => {
+    const colorFunction = getColorFunction(
+      {
+        operator,
+        targetValueLeft: -10,
+        targetValueRight: 50,
+        colorScheme: '#FF0000',
+        column: 'count',
+      },
+      countValues,
+    );
+    expect(colorFunction(null)).toBeUndefined();
+    expect(colorFunction(undefined as unknown as null)).toBeUndefined();
+  });
+});
+
+test('getColorFunction returns undefined for null values on None operator', () 
=> {
+  const colorFunction = getColorFunction(
+    {
+      operator: Comparator.None,
+      colorScheme: '#FF0000',
+      column: 'count',
+    },
+    countValues,
+  );
+  expect(colorFunction(null)).toBeUndefined();
+  expect(colorFunction(undefined as unknown as null)).toBeUndefined();
+});
+
+test('getColorFunction returns undefined for null values on string 
comparators', () => {
+  const operators = [
+    Comparator.BeginsWith,
+    Comparator.EndsWith,
+    Comparator.Containing,
+    Comparator.NotContaining,
+  ];
+  operators.forEach(operator => {
+    const colorFunction = getColorFunction(
+      {
+        operator,
+        targetValue: 'test',
+        colorScheme: '#FF0000',
+        column: 'name',
+      },
+      strValues,
+    );
+    expect(colorFunction(null)).toBeUndefined();
+    expect(colorFunction(undefined as unknown as null)).toBeUndefined();
+  });
+});
+
+test('getColorFunction returns undefined for empty and whitespace string 
values', () => {
+  const colorFunction = getColorFunction(
+    {
+      operator: Comparator.LessThan,
+      targetValue: 50,
+      colorScheme: '#FF0000',
+      column: 'count',
+    },
+    countValues,
+  );
+  expect(colorFunction('' as unknown as number)).toBeUndefined();
+  expect(colorFunction('  ' as unknown as number)).toBeUndefined();
+  expect(colorFunction('\t' as unknown as number)).toBeUndefined();
+});
+
+test('getColorFunction IsNull still matches null values', () => {
+  const colorFunction = getColorFunction(
+    {
+      operator: Comparator.IsNull,
+      targetValue: '',
+      colorScheme: '#FF0000',
+      column: 'isMember',
+    },
+    boolValues,
+  );
+  expect(colorFunction(null)).toEqual('#FF0000FF');
+  expect(colorFunction(true)).toBeUndefined();
+});
+
 test('correct column config', () => {
   const columnConfig = [
     {
diff --git 
a/superset-frontend/plugins/plugin-chart-table/test/TableChart.test.tsx 
b/superset-frontend/plugins/plugin-chart-table/test/TableChart.test.tsx
index d907c1e3eb8..3d9da78b875 100644
--- a/superset-frontend/plugins/plugin-chart-table/test/TableChart.test.tsx
+++ b/superset-frontend/plugins/plugin-chart-table/test/TableChart.test.tsx
@@ -614,9 +614,7 @@ describe('plugin-chart-table', () => {
         expect(getComputedStyle(screen.getByTitle('2467063')).background).toBe(
           '',
         );
-        expect(getComputedStyle(screen.getByText('N/A')).background).toBe(
-          'rgba(172, 225, 196, 1)',
-        );
+        expect(getComputedStyle(screen.getByText('N/A')).background).toBe('');
       });
       test('should display original label in grouped headers', () => {
         const props = transformProps(testData.comparison);

Reply via email to