You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/08/24 19:11:58 UTC

(superset) branch master updated: fix(explore): honor column Label in filter search and pill (#43404)

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

rusackas 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 c635d0754fc fix(explore): honor column Label in filter search and pill (#43404)
c635d0754fc is described below

commit c635d0754fc3ea35eee1fe95e04140bc90a21749
Author: Joe Li <jo...@preset.io>
AuthorDate: Mon Aug 24 12:11:52 2026 -0700

    fix(explore): honor column Label in filter search and pill (#43404)
---
 .../DndAdhocFilterOption.tsx                       |  2 +-
 .../DndFilterSelect.test.tsx                       | 31 +++++++++++++++++-
 .../FilterControl/AdhocFilter/AdhocFilter.test.ts  | 28 ++++++++++++++++
 .../controls/FilterControl/AdhocFilter/index.ts    | 10 +++---
 ...AdhocFilterEditPopoverSimpleTabContent.test.tsx | 37 ++++++++++++++++++++++
 .../index.tsx                                      |  4 +++
 .../AdhocFilterOption/AdhocFilterOption.test.tsx   | 18 +++++++++++
 .../FilterControl/AdhocFilterOption/index.tsx      |  2 +-
 .../controls/FilterControl/utils/translateToSQL.ts | 34 ++++++++++++++++++--
 9 files changed, 156 insertions(+), 10 deletions(-)

diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndAdhocFilterOption.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndAdhocFilterOption.tsx
index 525b2350496..34517365ee8 100644
--- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndAdhocFilterOption.tsx
+++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndAdhocFilterOption.tsx
@@ -60,7 +60,7 @@ export default function DndAdhocFilterOption({
       <OptionWrapper
         key={index}
         index={index}
-        label={actualTimeRange ?? adhocFilter.getDefaultLabel()}
+        label={actualTimeRange ?? adhocFilter.getDefaultLabel(options)}
         tooltipTitle={title ?? adhocFilter.getTooltipTitle()}
         clickClose={onClickClose}
         onShiftOptions={onShiftOptions}
diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndFilterSelect.test.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndFilterSelect.test.tsx
index cca9e101e8d..dc94dc802bb 100644
--- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndFilterSelect.test.tsx
+++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndFilterSelect.test.tsx
@@ -43,7 +43,7 @@ import {
   DndFilterSelectProps,
 } from 'src/explore/components/controls/DndColumnSelectControl/DndFilterSelect';
 import { PLACEHOLDER_DATASOURCE } from 'src/dashboard/constants';
-import { ExpressionTypes } from '../FilterControl/types';
+import { Clauses, ExpressionTypes } from '../FilterControl/types';
 import { DndItemType } from '../../DndItemType';
 import { Datasource } from '../../../types';
 import {
@@ -137,6 +137,35 @@ test('renders with value', async () => {
   expect(await screen.findByText('COUNT(*)')).toBeInTheDocument();
 });
 
+test('renders the pill using the column verbose_name when one is set', async () => {
+  const value = new AdhocFilter({
+    expressionType: ExpressionTypes.Simple,
+    subject: 'num',
+    operator: '>',
+    comparator: '500',
+    clause: Clauses.Where,
+  });
+  render(
+    setup({
+      value,
+      columns: [
+        {
+          id: 1,
+          type: 'BIGINT',
+          type_generic: GenericDataType.Numeric,
+          column_name: 'num',
+          verbose_name: 'total_count',
+        },
+      ],
+    }),
+    {
+      useDndKit: true,
+      store,
+    },
+  );
+  expect(await screen.findByText('total_count > 500')).toBeInTheDocument();
+});
+
 test('renders options with saved metric', async () => {
   render(
     setup({
diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/AdhocFilter.test.ts b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/AdhocFilter.test.ts
index 18d58848df7..116387ce28c 100644
--- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/AdhocFilter.test.ts
+++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/AdhocFilter.test.ts
@@ -370,4 +370,32 @@ describe('AdhocFilter', () => {
     });
     expect(adhocFilter.getDefaultLabel()).toBe('');
   });
+  test('uses the column verbose_name in the label when one is given', () => {
+    const adhocFilter = new AdhocFilter({
+      expressionType: ExpressionTypes.Simple,
+      subject: 'num',
+      operator: '>',
+      comparator: '500',
+      clause: Clauses.Where,
+    });
+    expect(
+      adhocFilter.getDefaultLabel([
+        { column_name: 'num', verbose_name: 'total_count' },
+      ]),
+    ).toBe('total_count > 500');
+  });
+  test('falls back to the column_name when no verbose_name is set', () => {
+    const adhocFilter = new AdhocFilter({
+      expressionType: ExpressionTypes.Simple,
+      subject: 'num',
+      operator: '>',
+      comparator: '500',
+      clause: Clauses.Where,
+    });
+    expect(
+      adhocFilter.getDefaultLabel([{ column_name: 'num', verbose_name: '' }]),
+    ).toBe('num > 500');
+    expect(adhocFilter.getDefaultLabel([])).toBe('num > 500');
+    expect(adhocFilter.getDefaultLabel()).toBe('num > 500');
+  });
 });
diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/index.ts b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/index.ts
index 649b8135ba3..f5502c348fe 100644
--- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/index.ts
+++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/index.ts
@@ -23,7 +23,7 @@ import {
   OPERATOR_ENUM_TO_OPERATOR_TYPE,
   Operators,
 } from 'src/explore/constants';
-import { translateToSql } from '../utils/translateToSQL';
+import { translateToSql, VerboseColumn } from '../utils/translateToSQL';
 import { Clauses, ExpressionTypes } from '../types';
 
 const CUSTOM_OPERATIONS = [...CUSTOM_OPERATORS].map(
@@ -193,8 +193,8 @@ export default class AdhocFilter {
     );
   }
 
-  getDefaultLabel(): string {
-    const label = this.translateToSql();
+  getDefaultLabel(columns?: VerboseColumn[]): string {
+    const label = this.translateToSql({ columns });
     return label.length < 43 ? label : `${label.substring(0, 40)}...`;
   }
 
@@ -202,8 +202,8 @@ export default class AdhocFilter {
     return this.translateToSql();
   }
 
-  translateToSql(): string {
-    return translateToSql(this as unknown as CoreAdhocFilter);
+  translateToSql(params: { columns?: VerboseColumn[] } = {}): string {
+    return translateToSql(this as unknown as CoreAdhocFilter, params);
   }
 }
 
diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/AdhocFilterEditPopoverSimpleTabContent.test.tsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/AdhocFilterEditPopoverSimpleTabContent.test.tsx
index f9687b49172..8e0f9f16ae8 100644
--- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/AdhocFilterEditPopoverSimpleTabContent.test.tsx
+++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/AdhocFilterEditPopoverSimpleTabContent.test.tsx
@@ -23,6 +23,7 @@ import {
   screen,
   userEvent,
   waitFor,
+  within,
 } from 'spec/helpers/testing-library';
 import thunk from 'redux-thunk';
 import configureStore from 'redux-mock-store';
@@ -914,3 +915,39 @@ test('dropdown should remain open when clicked after filter is configured', asyn
 
   expect(operatorDropdown).toHaveAttribute('aria-expanded', 'true');
 });
+
+test('filters the subject select by column verbose_name as well as column_name', async () => {
+  setup({
+    options: [
+      {
+        type: 'BIGINT',
+        column_name: 'num',
+        verbose_name: 'total_count',
+        id: 1,
+      },
+      {
+        type: 'VARCHAR(255)',
+        column_name: 'name',
+        verbose_name: 'Full Name',
+        id: 2,
+      },
+    ],
+  });
+
+  const combobox = screen.getByRole('combobox', { name: 'Select subject' });
+  userEvent.click(combobox);
+
+  await userEvent.type(combobox, 'total');
+
+  const dropdown = document.querySelector(
+    '.ant-select-dropdown-list',
+  ) as HTMLElement;
+  expect(within(dropdown).getByText('total_count')).toBeInTheDocument();
+  expect(within(dropdown).queryByText('Full Name')).not.toBeInTheDocument();
+
+  await userEvent.clear(combobox);
+  await userEvent.type(combobox, 'num');
+
+  expect(within(dropdown).getByText('total_count')).toBeInTheDocument();
+  expect(within(dropdown).queryByText('Full Name')).not.toBeInTheDocument();
+});
diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx
index c093e703acf..692d84b3135 100644
--- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx
+++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx
@@ -639,7 +639,11 @@ const AdhocFilterEditPopoverSimpleTabContent: FC<Props> = props => {
           ('optionName' in column && column.optionName) ||
           undefined,
         label: renderSubjectOptionLabel(column),
+        column_name: 'column_name' in column ? column.column_name : undefined,
+        verbose_name:
+          'verbose_name' in column ? column.verbose_name : undefined,
       }))}
+      optionFilterProps={['column_name', 'verbose_name']}
       {...subjectSelectProps}
     />
   );
diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterOption/AdhocFilterOption.test.tsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterOption/AdhocFilterOption.test.tsx
index 1469ef1ac64..05b5b2be93f 100644
--- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterOption/AdhocFilterOption.test.tsx
+++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterOption/AdhocFilterOption.test.tsx
@@ -71,6 +71,24 @@ test('should render the control label', async () => {
   expect(await screen.findByText('value > 10')).toBeInTheDocument();
 });
 
+test('should render the control label using the column verbose_name when one is set', async () => {
+  render(
+    setup({
+      ...mockedProps,
+      options: [
+        {
+          type: 'DOUBLE',
+          column_name: 'value',
+          verbose_name: 'total_count',
+          id: 3,
+        },
+      ],
+    }),
+    { useDnd: true, useRedux: true },
+  );
+  expect(await screen.findByText('total_count > 10')).toBeInTheDocument();
+});
+
 test('should render the remove button', async () => {
   render(setup(mockedProps), { useDnd: true, useRedux: true });
   const removeBtn = await screen.findByTestId('remove-control-button');
diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterOption/index.tsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterOption/index.tsx
index ca406143ec3..d521cbc0948 100644
--- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterOption/index.tsx
+++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterOption/index.tsx
@@ -65,7 +65,7 @@ export default function AdhocFilterOption({
       partitionColumn={partitionColumn ?? undefined}
     >
       <OptionControlLabel
-        label={actualTimeRange ?? adhocFilter.getDefaultLabel()}
+        label={actualTimeRange ?? adhocFilter.getDefaultLabel(options)}
         tooltipTitle={title ?? adhocFilter.getTooltipTitle()}
         onRemove={() =>
           onRemoveFilter({
diff --git a/superset-frontend/src/explore/components/controls/FilterControl/utils/translateToSQL.ts b/superset-frontend/src/explore/components/controls/FilterControl/utils/translateToSQL.ts
index df42774bada..6e102cc84f9 100644
--- a/superset-frontend/src/explore/components/controls/FilterControl/utils/translateToSQL.ts
+++ b/superset-frontend/src/explore/components/controls/FilterControl/utils/translateToSQL.ts
@@ -63,9 +63,35 @@ export const OPERATORS_TO_SQL = {
     `= '{{ presto.latest_partition('${datasource.schema}.${datasource.datasource_name}') }}'`,
 };
 
+export interface VerboseColumn {
+  column_name?: string;
+  verbose_name?: string | null;
+}
+
+// Resolves the display label for a filter's subject: the verbose_name of the
+// matching column when one is supplied, falling back to the technical
+// subject used for SQL generation.
+const getDisplaySubject = (
+  subject: string | { column_name?: string } | null | undefined,
+  columns?: VerboseColumn[],
+) => {
+  if (!columns) {
+    return subject ?? undefined;
+  }
+  const columnName =
+    typeof subject === 'object' ? subject?.column_name : subject;
+  const verboseName = columns.find(
+    column => column.column_name === columnName,
+  )?.verbose_name;
+  return verboseName || (subject ?? undefined);
+};
+
 export const translateToSql = (
   adhocFilter: AdhocFilter,
-  { useSimple }: { useSimple: boolean } = { useSimple: false },
+  {
+    useSimple,
+    columns,
+  }: { useSimple?: boolean; columns?: VerboseColumn[] } = {},
 ) => {
   if (isSimpleAdhocFilter(adhocFilter) || useSimple) {
     const { subject, operator } = adhocFilter as SimpleAdhocFilter;
@@ -81,7 +107,11 @@ export const translateToSql = (
           OPERATORS_TO_SQL[operator](adhocFilter)
         : // @ts-expect-error TODO: fix missing operator type `NOT LIKE` and `TEMPORAL RANGE`.
           OPERATORS_TO_SQL[operator];
-    return getSimpleSQLExpression(subject, op, comparator);
+    return getSimpleSQLExpression(
+      getDisplaySubject(subject, columns),
+      op,
+      comparator,
+    );
   }
   if (isFreeFormAdhocFilter(adhocFilter)) {
     return adhocFilter.sqlExpression;