From a3de3c9dde226c7419a02e22c511999ea4b92f04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 22 Mar 2023 17:08:41 +0100 Subject: [PATCH] Table: Fixes table panel gauge alignment (#64994) * Table: Fixes tables guage alignment issues * Removed console.log * Fixing --- .betterer.results | 2 +- .../grafana-data/src/field/fieldDisplay.ts | 32 ++++++------ packages/grafana-data/src/types/dataFrame.ts | 8 ++- .../grafana-data/src/types/displayValue.ts | 2 +- .../src/components/Table/BarGaugeCell.tsx | 49 ++++++++++++++++++- 5 files changed, 71 insertions(+), 22 deletions(-) diff --git a/.betterer.results b/.betterer.results index 5024b59fac9..5f5c8d7acf4 100644 --- a/.betterer.results +++ b/.betterer.results @@ -1,5 +1,5 @@ // BETTERER RESULTS V2. -// +// // If this file contains merge conflicts, use `betterer merge` to automatically resolve them: // https://phenomnomnominal.github.io/betterer/docs/results-file/#merge // diff --git a/packages/grafana-data/src/field/fieldDisplay.ts b/packages/grafana-data/src/field/fieldDisplay.ts index ac4c8f8d7b9..f21393f21d0 100644 --- a/packages/grafana-data/src/field/fieldDisplay.ts +++ b/packages/grafana-data/src/field/fieldDisplay.ts @@ -315,36 +315,32 @@ export function hasLinks(field: Field): boolean { } export function getDisplayValueAlignmentFactors(values: FieldDisplay[]): DisplayValueAlignmentFactors { - const info: DisplayValueAlignmentFactors = { - title: '', - text: '', - }; - - let prefixLength = 0; - let suffixLength = 0; + let maxTitle = ''; + let maxText = ''; + let maxPrefix = ''; + let maxSuffix = ''; for (let i = 0; i < values.length; i++) { const v = values[i].display; - if (v.text && v.text.length > info.text.length) { - info.text = v.text; + if (v.text && v.text.length > maxText.length) { + maxText = v.text; } - if (v.title && v.title.length > info.title.length) { - info.title = v.title; + if (v.title && v.title.length > maxTitle.length) { + maxTitle = v.title; } - if (v.prefix && v.prefix.length > prefixLength) { - info.prefix = v.prefix; - prefixLength = v.prefix.length; + if (v.prefix && v.prefix.length > maxPrefix.length) { + maxPrefix = v.prefix; } - if (v.suffix && v.suffix.length > suffixLength) { - info.suffix = v.suffix; - suffixLength = v.suffix.length; + if (v.suffix && v.suffix.length > maxSuffix.length) { + maxSuffix = v.suffix; } } - return info; + + return { text: maxText, title: maxTitle, suffix: maxSuffix, prefix: maxPrefix }; } function createNoValuesFieldDisplay(options: GetFieldDisplayValuesOptions): FieldDisplay { diff --git a/packages/grafana-data/src/types/dataFrame.ts b/packages/grafana-data/src/types/dataFrame.ts index 48ca9eb4188..5ee8a3d30db 100644 --- a/packages/grafana-data/src/types/dataFrame.ts +++ b/packages/grafana-data/src/types/dataFrame.ts @@ -1,7 +1,7 @@ import { ScopedVars } from './ScopedVars'; import { QueryResultBase, Labels, NullValueMode } from './data'; import { DataLink, LinkModel } from './dataLink'; -import { DecimalCount, DisplayProcessor, DisplayValue } from './displayValue'; +import { DecimalCount, DisplayProcessor, DisplayValue, DisplayValueAlignmentFactors } from './displayValue'; import { FieldColor } from './fieldColor'; import { ThresholdsConfig } from './thresholds'; import { ValueMapping } from './valueMapping'; @@ -204,6 +204,12 @@ export interface FieldState { * this would applied more than one time. */ nullThresholdApplied?: boolean; + + /** + * Can be used by visualizations to cache max display value lengths to aid alignment. + * It's up to each visualization to calculate and set this. + */ + alignmentFactors?: DisplayValueAlignmentFactors; } /** @public */ diff --git a/packages/grafana-data/src/types/displayValue.ts b/packages/grafana-data/src/types/displayValue.ts index a0de2ccc2d6..f0b9457e34b 100644 --- a/packages/grafana-data/src/types/displayValue.ts +++ b/packages/grafana-data/src/types/displayValue.ts @@ -32,7 +32,7 @@ export interface DisplayValue extends FormattedValue { * Used to align widths and heights when displaying multiple DisplayValues */ export interface DisplayValueAlignmentFactors extends FormattedValue { - title: string; + title?: string; } export type DecimalCount = number | null | undefined; diff --git a/packages/grafana-ui/src/components/Table/BarGaugeCell.tsx b/packages/grafana-ui/src/components/Table/BarGaugeCell.tsx index 4e6870f0a5f..7ef0a425c8c 100644 --- a/packages/grafana-ui/src/components/Table/BarGaugeCell.tsx +++ b/packages/grafana-ui/src/components/Table/BarGaugeCell.tsx @@ -1,7 +1,15 @@ import { isFunction } from 'lodash'; import React from 'react'; -import { ThresholdsConfig, ThresholdsMode, VizOrientation, getFieldConfigWithMinMax } from '@grafana/data'; +import { + ThresholdsConfig, + ThresholdsMode, + VizOrientation, + getFieldConfigWithMinMax, + DisplayValueAlignmentFactors, + Field, + DisplayValue, +} from '@grafana/data'; import { BarGaugeDisplayMode, BarGaugeValueMode } from '@grafana/schema'; import { BarGauge } from '../BarGauge/BarGauge'; @@ -57,6 +65,7 @@ export const BarGaugeCell = (props: TableCellProps) => { }; const hasLinks = Boolean(getLinks().length); + const alignmentFactors = getAlignmentFactor(field, displayValue, cell.row.index); const renderComponent = (menuProps: DataLinksContextMenuApi) => { const { openMenu, targetClassName } = menuProps; @@ -71,6 +80,7 @@ export const BarGaugeCell = (props: TableCellProps) => { value={displayValue} orientation={VizOrientation.Horizontal} theme={tableStyles.theme} + alignmentFactors={alignmentFactors} onClick={openMenu} className={targetClassName} itemSpacing={1} @@ -92,3 +102,40 @@ export const BarGaugeCell = (props: TableCellProps) => { ); }; + +/** + * Getting gauge values to align is very tricky without looking at all values and passing them trough display processor. For very large tables that + * could pretty expensive. So this is kind of a compromise. We look at the first 1000 rows and cache the longest value. + * If we have a cached value we just check if the current value is longer and update the alignmentFactor. This can obviously still lead to + * unaligned gauges but it should a lot less common. + **/ +function getAlignmentFactor(field: Field, displayValue: DisplayValue, rowIndex: number): DisplayValueAlignmentFactors { + let alignmentFactor = field.state?.alignmentFactors; + + if (alignmentFactor) { + // check if current alignmentFactor is still the longest + if (alignmentFactor.text.length < displayValue.text.length) { + alignmentFactor.text = displayValue.text; + } + return alignmentFactor; + } else { + // look at the next 100 rows + alignmentFactor = { ...displayValue }; + const maxIndex = Math.min(field.values.length, rowIndex + 1000); + + for (let i = rowIndex + 1; i < maxIndex; i++) { + const nextDisplayValue = field.display!(field.values.get(i)); + if (nextDisplayValue.text.length > alignmentFactor.text.length) { + alignmentFactor.text = displayValue.text; + } + } + + if (field.state) { + field.state.alignmentFactors = alignmentFactor; + } else { + field.state = { alignmentFactors: alignmentFactor }; + } + + return alignmentFactor; + } +}