Table: Fixes table panel gauge alignment (#64994)

* Table: Fixes tables guage alignment issues

* Removed console.log

* Fixing
This commit is contained in:
Torkel Ödegaard 2023-03-22 17:08:41 +01:00 committed by GitHub
parent 09e3faaa4c
commit a3de3c9dde
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 71 additions and 22 deletions

View File

@ -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
//

View File

@ -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 {

View File

@ -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 */

View File

@ -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;

View File

@ -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) => {
</div>
);
};
/**
* 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;
}
}