From 10ffc9a754b17ba067350ad0bc34edce48d8b4df Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Wed, 8 Sep 2021 00:44:41 -0700 Subject: [PATCH] Tables: optionally show field type icon in tables (#38855) --- .../src/components/MatchersUI/utils.ts | 15 +++---------- .../src/components/Table/HeaderRow.tsx | 11 +++++++--- .../grafana-ui/src/components/Table/Table.tsx | 4 +++- packages/grafana-ui/src/types/icon.ts | 22 +++++++++++++++++++ .../PanelEditor/PanelEditorTableView.tsx | 1 + .../app/features/inspector/InspectDataTab.tsx | 2 +- public/app/plugins/panel/table/TablePanel.tsx | 1 + public/app/plugins/panel/table/models.cue | 1 + public/app/plugins/panel/table/models.gen.ts | 2 ++ 9 files changed, 42 insertions(+), 17 deletions(-) diff --git a/packages/grafana-ui/src/components/MatchersUI/utils.ts b/packages/grafana-ui/src/components/MatchersUI/utils.ts index d402e2cf5e8..913994a10f0 100644 --- a/packages/grafana-ui/src/components/MatchersUI/utils.ts +++ b/packages/grafana-ui/src/components/MatchersUI/utils.ts @@ -1,6 +1,6 @@ import { useMemo } from 'react'; -import { DataFrame, Field, FieldType, getFieldDisplayName, SelectableValue } from '@grafana/data'; -import { IconName } from '../..'; +import { DataFrame, Field, getFieldDisplayName, SelectableValue } from '@grafana/data'; +import { getFieldTypeIcon } from '../../types'; /** * @internal @@ -62,15 +62,6 @@ export function useFieldDisplayNames(data: DataFrame[], filter?: (field: Field) }, [data, filter]); } -const fieldTypeIcons: { [key in FieldType]: IconName } = { - time: 'clock-nine', - string: 'font', - number: 'calculator-alt', - boolean: 'toggle-on', - trace: 'info-circle', - other: 'brackets-curly', -}; - /** * @internal */ @@ -93,7 +84,7 @@ export function useSelectOptions( options.push({ value: name, label: name, - icon: field ? fieldTypeIcons[field.type] : undefined, + icon: field ? getFieldTypeIcon(field) : undefined, }); } for (const name of displayNames.raw) { diff --git a/packages/grafana-ui/src/components/Table/HeaderRow.tsx b/packages/grafana-ui/src/components/Table/HeaderRow.tsx index 15339c470bb..1b513d5baf9 100644 --- a/packages/grafana-ui/src/components/Table/HeaderRow.tsx +++ b/packages/grafana-ui/src/components/Table/HeaderRow.tsx @@ -6,14 +6,16 @@ import { getTableStyles, TableStyles } from './styles'; import { useStyles2 } from '../../themes'; import { Filter } from './Filter'; import { Icon } from '../Icon/Icon'; +import { getFieldTypeIcon } from '../../types'; export interface HeaderRowProps { headerGroups: HeaderGroup[]; data: DataFrame; + showTypeIcons?: boolean; } export const HeaderRow = (props: HeaderRowProps) => { - const { headerGroups, data } = props; + const { headerGroups, data, showTypeIcons } = props; const e2eSelectorsTable = selectors.components.Panels.Visualization.Table; const tableStyles = useStyles2(getTableStyles); @@ -30,7 +32,7 @@ export const HeaderRow = (props: HeaderRowProps) => { role="row" > {headerGroup.headers.map((column: Column, index: number) => - renderHeaderCell(column, tableStyles, data.fields[index]) + renderHeaderCell(column, tableStyles, data.fields[index], showTypeIcons) )} ); @@ -39,7 +41,7 @@ export const HeaderRow = (props: HeaderRowProps) => { ); }; -function renderHeaderCell(column: any, tableStyles: TableStyles, field?: Field) { +function renderHeaderCell(column: any, tableStyles: TableStyles, field?: Field, showTypeIcons?: boolean) { const headerProps = column.getHeaderProps(); if (column.canResize) { @@ -58,6 +60,9 @@ function renderHeaderCell(column: any, tableStyles: TableStyles, field?: Field) className={tableStyles.headerCellLabel} title={column.render('Header')} > + {showTypeIcons && ( + + )}
{column.render('Header')}
{column.isSorted && (column.isSortedDesc ? : )} diff --git a/packages/grafana-ui/src/components/Table/Table.tsx b/packages/grafana-ui/src/components/Table/Table.tsx index 2def06e4e7a..cc6772b9047 100644 --- a/packages/grafana-ui/src/components/Table/Table.tsx +++ b/packages/grafana-ui/src/components/Table/Table.tsx @@ -38,6 +38,7 @@ export interface Props { /** Minimal column width specified in pixels */ columnMinWidth?: number; noHeader?: boolean; + showTypeIcons?: boolean; resizable?: boolean; initialSortBy?: TableSortByFieldState[]; onColumnResize?: TableColumnResizeActionCallback; @@ -124,6 +125,7 @@ export const Table: FC = memo((props: Props) => { resizable = true, initialSortBy, footerValues, + showTypeIcons, } = props; const tableStyles = useStyles2(getTableStyles); @@ -204,7 +206,7 @@ export const Table: FC = memo((props: Props) => {
- {!noHeader && } + {!noHeader && } {rows.length > 0 ? ( type BrandIconNames = 'google' | 'microsoft' | 'github' | 'gitlab' | 'okta'; export type IconName = ReturnType[number] | BrandIconNames; + +/** Get the icon for a given field type */ +export function getFieldTypeIcon(field?: Field): IconName { + if (field) { + switch (field.type) { + case FieldType.time: + return 'clock-nine'; + case FieldType.string: + return 'font'; + case FieldType.number: + return 'calculator-alt'; + case FieldType.boolean: + return 'toggle-on'; + case FieldType.trace: + return 'info-circle'; + case FieldType.other: + return 'brackets-curly'; + } + } + return 'question-circle'; +} diff --git a/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.tsx b/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.tsx index ebddedc2918..ccb12b4fd06 100644 --- a/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.tsx +++ b/public/app/features/dashboard/components/PanelEditor/PanelEditorTableView.tsx @@ -19,6 +19,7 @@ export function PanelEditorTableView({ width, height, panel, dashboard }: Props) const [options, setOptions] = useState({ frameIndex: 0, showHeader: true, + showTypeIcons: true, }); // Subscribe to panel event diff --git a/public/app/features/inspector/InspectDataTab.tsx b/public/app/features/inspector/InspectDataTab.tsx index 32121249543..8fceda77382 100644 --- a/public/app/features/inspector/InspectDataTab.tsx +++ b/public/app/features/inspector/InspectDataTab.tsx @@ -228,7 +228,7 @@ export class InspectDataTab extends PureComponent { return (
- +
); }} diff --git a/public/app/plugins/panel/table/TablePanel.tsx b/public/app/plugins/panel/table/TablePanel.tsx index 120fe04039b..bd50f95fdba 100644 --- a/public/app/plugins/panel/table/TablePanel.tsx +++ b/public/app/plugins/panel/table/TablePanel.tsx @@ -86,6 +86,7 @@ export class TablePanel extends Component { width={width} data={frame} noHeader={!options.showHeader} + showTypeIcons={options.showTypeIcons} resizable={true} initialSortBy={options.sortBy} onSortByChange={this.onSortByChange} diff --git a/public/app/plugins/panel/table/models.cue b/public/app/plugins/panel/table/models.cue index 0d798f8576d..3739d3eeb0d 100644 --- a/public/app/plugins/panel/table/models.cue +++ b/public/app/plugins/panel/table/models.cue @@ -25,6 +25,7 @@ Family: { PanelOptions: { frameIndex: number | *0 showHeader: bool | *true + showTypeIcons: bool | *false sortBy?: [...ui.TableSortByFieldState] } PanelFieldConfig: { diff --git a/public/app/plugins/panel/table/models.gen.ts b/public/app/plugins/panel/table/models.gen.ts index ffd1c1d178f..e2c504875a3 100644 --- a/public/app/plugins/panel/table/models.gen.ts +++ b/public/app/plugins/panel/table/models.gen.ts @@ -14,12 +14,14 @@ export const modelVersion = Object.freeze([1, 0]); export interface PanelOptions { frameIndex: number; showHeader: boolean; + showTypeIcons?: boolean; sortBy?: TableSortByFieldState[]; } export const defaultPanelOptions: PanelOptions = { frameIndex: 0, showHeader: true, + showTypeIcons: false, }; export interface PanelFieldConfig {