feat(dashboard): show dataset column labels in View as table (#37140)

This commit is contained in:
Vanessa Giannoni
2026-01-27 18:51:28 -03:00
committed by GitHub
parent 20da4eb86e
commit 2ec3aaaeea
7 changed files with 48 additions and 3 deletions

View File

@@ -457,6 +457,7 @@ const SliceHeaderControls = (
isRequest isRequest
isVisible isVisible
canDownload={!!props.supersetCanCSV} canDownload={!!props.supersetCanCSV}
columnDisplayNames={datasetWithVerboseMap?.verbose_map}
/> />
} }
/> />

View File

@@ -164,6 +164,7 @@ const DataTableTemporalHeaderCell = ({
onTimeColumnChange, onTimeColumnChange,
datasourceId, datasourceId,
isOriginalTimeColumn, isOriginalTimeColumn,
displayLabel,
}: { }: {
columnName: string; columnName: string;
onTimeColumnChange: ( onTimeColumnChange: (
@@ -172,6 +173,7 @@ const DataTableTemporalHeaderCell = ({
) => void; ) => void;
datasourceId?: string; datasourceId?: string;
isOriginalTimeColumn: boolean; isOriginalTimeColumn: boolean;
displayLabel?: string;
}) => { }) => {
const theme = useTheme(); const theme = useTheme();
@@ -215,10 +217,10 @@ const DataTableTemporalHeaderCell = ({
onClick={(e: React.MouseEvent<HTMLElement>) => e.stopPropagation()} onClick={(e: React.MouseEvent<HTMLElement>) => e.stopPropagation()}
/> />
</Popover> </Popover>
{columnName} {displayLabel ?? columnName}
</span> </span>
) : ( ) : (
<span>{columnName}</span> <span>{displayLabel ?? columnName}</span>
); );
}; };
@@ -258,6 +260,7 @@ export const useTableColumns = (
isVisible?: boolean, isVisible?: boolean,
moreConfigs?: { [key: string]: Partial<Column> }, moreConfigs?: { [key: string]: Partial<Column> },
allowHTML?: boolean, allowHTML?: boolean,
columnDisplayNames?: Record<string, string>,
) => { ) => {
const [originalFormattedTimeColumns, setOriginalFormattedTimeColumns] = const [originalFormattedTimeColumns, setOriginalFormattedTimeColumns] =
useState<string[]>(getTimeColumns(datasourceId)); useState<string[]>(getTimeColumns(datasourceId));
@@ -302,6 +305,7 @@ export const useTableColumns = (
.map((key, index) => { .map((key, index) => {
const colType = coltypes?.[index]; const colType = coltypes?.[index];
const firstValue = data[0][key]; const firstValue = data[0][key];
const headerLabel = columnDisplayNames?.[key] ?? key;
const originalFormattedTimeColumnIndex = const originalFormattedTimeColumnIndex =
colType === GenericDataType.Temporal colType === GenericDataType.Temporal
? originalFormattedTimeColumns.indexOf(key) ? originalFormattedTimeColumns.indexOf(key)
@@ -320,9 +324,10 @@ export const useTableColumns = (
datasourceId={datasourceId} datasourceId={datasourceId}
onTimeColumnChange={onTimeColumnChange} onTimeColumnChange={onTimeColumnChange}
isOriginalTimeColumn={isOriginalTimeColumn} isOriginalTimeColumn={isOriginalTimeColumn}
displayLabel={headerLabel}
/> />
) : ( ) : (
key headerLabel
), ),
Cell: ({ value }) => { Cell: ({ value }) => {
if (value === true) { if (value === true) {
@@ -357,6 +362,7 @@ export const useTableColumns = (
datasourceId, datasourceId,
moreConfigs, moreConfigs,
originalFormattedTimeColumns, originalFormattedTimeColumns,
columnDisplayNames,
], ],
); );
}; };

View File

@@ -105,6 +105,7 @@ test('useTableColumns with no options', () => {
"Cell": [Function], "Cell": [Function],
"Header": <DataTableTemporalHeaderCell "Header": <DataTableTemporalHeaderCell
columnName="numtime" columnName="numtime"
displayLabel="numtime"
isOriginalTimeColumn={false} isOriginalTimeColumn={false}
onTimeColumnChange={[Function]} onTimeColumnChange={[Function]}
/>, />,
@@ -168,6 +169,7 @@ test('useTableColumns with options', () => {
"Cell": [Function], "Cell": [Function],
"Header": <DataTableTemporalHeaderCell "Header": <DataTableTemporalHeaderCell
columnName="numtime" columnName="numtime"
displayLabel="numtime"
isOriginalTimeColumn={false} isOriginalTimeColumn={false}
onTimeColumnChange={[Function]} onTimeColumnChange={[Function]}
/>, />,
@@ -194,3 +196,29 @@ test('useTableColumns with options', () => {
}); });
}); });
}); });
test('useTableColumns applies columnDisplayNames to headers', () => {
const columnDisplayNames = {
col01: 'Column One',
[NUMTIME_KEY]: 'Verbose Numtime',
} as Record<string, string>;
const hook = renderHook(() =>
useTableColumns(
colnames,
coltypes,
data,
undefined,
true,
undefined,
undefined,
columnDisplayNames,
),
);
const cols = hook.result.current as JsonObject[];
const col01 = cols.find(c => c.id === 'col01');
const numtime = cols.find(c => c.id === NUMTIME_KEY);
expect(col01?.Header).toBe('Column One');
// Temporal header is a component; ensure it received the displayLabel prop
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
expect(numtime?.Header.props.displayLabel).toBe('Verbose Numtime');
});

View File

@@ -55,6 +55,7 @@ export const ResultsPaneOnDashboard = ({
isVisible, isVisible,
dataSize = 50, dataSize = 50,
canDownload, canDownload,
columnDisplayNames,
}: ResultsPaneProps) => { }: ResultsPaneProps) => {
const resultsPanes = useResultsPane({ const resultsPanes = useResultsPane({
errorMessage, errorMessage,
@@ -66,6 +67,7 @@ export const ResultsPaneOnDashboard = ({
dataSize, dataSize,
isVisible, isVisible,
canDownload, canDownload,
columnDisplayNames,
}); });
if (resultsPanes.length === 1) { if (resultsPanes.length === 1) {

View File

@@ -39,6 +39,7 @@ export const SingleQueryResultPane = ({
dataSize = 50, dataSize = 50,
isVisible, isVisible,
canDownload, canDownload,
columnDisplayNames,
}: SingleQueryResultPaneProp) => { }: SingleQueryResultPaneProp) => {
const [filterText, setFilterText] = useState(''); const [filterText, setFilterText] = useState('');
@@ -52,6 +53,7 @@ export const SingleQueryResultPane = ({
isVisible, isVisible,
{}, // moreConfig {}, // moreConfig
true, // allowHTML true, // allowHTML
columnDisplayNames,
); );
const filteredData = useFilteredTableData(filterText, data); const filteredData = useFilteredTableData(filterText, data);

View File

@@ -55,6 +55,7 @@ export const useResultsPane = ({
isVisible, isVisible,
dataSize = 50, dataSize = 50,
canDownload, canDownload,
columnDisplayNames,
}: ResultsPaneProps): ReactElement[] => { }: ResultsPaneProps): ReactElement[] => {
const metadata = getChartMetadataRegistry().get( const metadata = getChartMetadataRegistry().get(
queryFormData?.viz_type || queryFormData?.vizType, queryFormData?.viz_type || queryFormData?.vizType,
@@ -164,6 +165,7 @@ export const useResultsPane = ({
datasourceId={queryFormData.datasource} datasourceId={queryFormData.datasource}
isVisible={isVisible} isVisible={isVisible}
canDownload={canDownload} canDownload={canDownload}
columnDisplayNames={columnDisplayNames}
/> />
</StyledDiv> </StyledDiv>
)); ));

View File

@@ -49,6 +49,8 @@ export interface ResultsPaneProps {
// reload OriginalFormattedTimeColumns from localStorage when isVisible is true // reload OriginalFormattedTimeColumns from localStorage when isVisible is true
isVisible: boolean; isVisible: boolean;
canDownload: boolean; canDownload: boolean;
// Optional map of column/metric name -> verbose label
columnDisplayNames?: Record<string, string>;
} }
export interface SamplesPaneProps { export interface SamplesPaneProps {
@@ -88,4 +90,6 @@ export interface SingleQueryResultPaneProp extends QueryResultInterface {
// reload OriginalFormattedTimeColumns from localStorage when isVisible is true // reload OriginalFormattedTimeColumns from localStorage when isVisible is true
isVisible: boolean; isVisible: boolean;
canDownload: boolean; canDownload: boolean;
// Optional map of column/metric name -> verbose label
columnDisplayNames?: Record<string, string>;
} }