Table: Fixes broken link styles after recent cell options PR (#61582)

* Table: Fixes broken link styles after recent cell options PR

* Share migration and fix bar gauge as well

* Remove unused import

* Review fixes

* Fixed test

Co-authored-by: Kyle Cunningham <kyle@codeincarnate.com>
This commit is contained in:
Torkel Ödegaard 2023-01-17 17:08:23 +01:00 committed by GitHub
parent bb7410aa09
commit 8620909006
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 168 additions and 179 deletions

View File

@ -1488,7 +1488,8 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
[0, 0, 0, "Unexpected any. Specify a different type.", "12"],
[0, 0, 0, "Unexpected any. Specify a different type.", "13"],
[0, 0, 0, "Unexpected any. Specify a different type.", "14"]
[0, 0, 0, "Unexpected any. Specify a different type.", "14"],
[0, 0, 0, "Do not use any type assertions.", "15"]
],
"packages/grafana-ui/src/components/Tags/Tag.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]

View File

@ -206,7 +206,6 @@ FieldTextAlignment: "auto" | "left" | "right" | "center" @cuetsy(kind="type")
// The color-background-solid, gradient-gauge, and lcd-gauge
// modes are deprecated in favor of new cell subOptions
TableCellDisplayMode: "auto" | "color-text" | "color-background" | "color-background-solid" | "gradient-gauge" | "lcd-gauge" | "json-view" | "basic" | "image" | "gauge" @cuetsy(kind="enum",memberNames="Auto|ColorText|ColorBackground|ColorBackgroundSolid|GradientGauge|LcdGauge|JSONView|BasicGauge|Image|Gauge")
// : TableCellDisplayMode @cuetsy(kind="enum")
// Display mode to the "Colored Background" display
// mode for table cells. Either displays a solid color (basic mode)
@ -260,26 +259,41 @@ VizLegendOptions: {
// for the bar gauge component of Grafana UI
BarGaugeDisplayMode: "basic" | "lcd" | "gradient" @cuetsy(kind="enum")
// Interface for table cell types that have no additional options.
// Auto mode table cell options
TableAutoCellOptions: {
type: TableCellDisplayMode
type: TableCellDisplayMode & "auto"
} @cuetsy(kind="interface")
// Allows for the table cell gauge display type to set the gauge mode.
// Colored text cell options
TableColorTextCellOptions: {
type: TableCellDisplayMode & "color-text"
} @cuetsy(kind="interface")
// Json view cell options
TableJsonViewCellOptions: {
type: TableCellDisplayMode & "json-view"
} @cuetsy(kind="interface")
// Json view cell options
TableImageCellOptions: {
type: TableCellDisplayMode & "image"
} @cuetsy(kind="interface")
// Gauge cell options
TableBarGaugeCellOptions: {
type: TableCellDisplayMode & "gauge"
mode: BarGaugeDisplayMode
mode?: BarGaugeDisplayMode
} @cuetsy(kind="interface")
// Allows for the background display mode to be set for the color background cell.
// Colored background cell options
TableColoredBackgroundCellOptions: {
type: TableCellDisplayMode & "color-background"
mode: TableCellBackgroundDisplayMode
mode?: TableCellBackgroundDisplayMode
} @cuetsy(kind="interface")
// Table cell options. Each cell has a display mode
// and other potential options for that display.
TableCellOptions: TableAutoCellOptions | TableBarGaugeCellOptions | TableColoredBackgroundCellOptions @cuetsy(kind="type")
TableCellOptions: TableAutoCellOptions | TableBarGaugeCellOptions | TableColoredBackgroundCellOptions | TableColorTextCellOptions | TableImageCellOptions | TableJsonViewCellOptions @cuetsy(kind="type")
// Field options for each field within a table (e.g 10, "The String", 64.20, etc.)
// Generally defines alignment, filtering capabilties, display options, etc.

View File

@ -489,25 +489,46 @@ export enum BarGaugeDisplayMode {
}
/**
* Interface for table cell types that have no additional options.
* Auto mode table cell options
*/
export interface TableAutoCellOptions {
type: TableCellDisplayMode;
type: TableCellDisplayMode.Auto;
}
/**
* Allows for the table cell gauge display type to set the gauge mode.
* Colored text cell options
*/
export interface TableColorTextCellOptions {
type: TableCellDisplayMode.ColorText;
}
/**
* Json view cell options
*/
export interface TableJsonViewCellOptions {
type: TableCellDisplayMode.JSONView;
}
/**
* Json view cell options
*/
export interface TableImageCellOptions {
type: TableCellDisplayMode.Image;
}
/**
* Gauge cell options
*/
export interface TableBarGaugeCellOptions {
mode: BarGaugeDisplayMode;
mode?: BarGaugeDisplayMode;
type: TableCellDisplayMode.Gauge;
}
/**
* Allows for the background display mode to be set for the color background cell.
* Colored background cell options
*/
export interface TableColoredBackgroundCellOptions {
mode: TableCellBackgroundDisplayMode;
mode?: TableCellBackgroundDisplayMode;
type: TableCellDisplayMode.ColorBackground;
}
@ -515,7 +536,7 @@ export interface TableColoredBackgroundCellOptions {
* Table cell options. Each cell has a display mode
* and other potential options for that display.
*/
export type TableCellOptions = (TableAutoCellOptions | TableBarGaugeCellOptions | TableColoredBackgroundCellOptions);
export type TableCellOptions = (TableAutoCellOptions | TableBarGaugeCellOptions | TableColoredBackgroundCellOptions | TableColorTextCellOptions | TableImageCellOptions | TableJsonViewCellOptions);
/**
* Field options for each field within a table (e.g 10, "The String", 64.20, etc.)

View File

@ -8,6 +8,7 @@ import { BarGauge } from '../BarGauge/BarGauge';
import { DataLinksContextMenu, DataLinksContextMenuApi } from '../DataLinks/DataLinksContextMenu';
import { TableCellProps, TableCellDisplayMode } from './types';
import { getCellOptions } from './utils';
const defaultScale: ThresholdsConfig = {
mode: ThresholdsMode.Absolute,
@ -39,28 +40,9 @@ export const BarGaugeCell: FC<TableCellProps> = (props) => {
// Set default display mode
let barGaugeMode: BarGaugeDisplayMode = BarGaugeDisplayMode.Gradient;
// Support deprecated settings
const usingDeprecatedSettings = field.config.custom.displayMode !== undefined;
// If we're using the old settings format we read the displayMode directly from
// the cell options
if (usingDeprecatedSettings) {
if (
(field.config.custom && field.config.custom.cellOptions.displayMode === TableCellDisplayMode.Gauge) ||
(field.config.custom && field.config.custom.cellOptions.displayMode === BarGaugeDisplayMode.Lcd)
) {
barGaugeMode = BarGaugeDisplayMode.Lcd;
} else if (
(field.config.custom && field.config.custom.cellOptions.displayMode === TableCellDisplayMode.Gauge) ||
(field.config.custom && field.config.custom.cellOptions.displayMode === BarGaugeDisplayMode.Basic)
) {
barGaugeMode = BarGaugeDisplayMode.Basic;
}
}
// Otherwise in the case of sub-options we read specifically from the sub-options
// object in order to get the display mode
else {
barGaugeMode = field.config.custom.cellOptions.mode;
const cellOptions = getCellOptions(field);
if (cellOptions.type === TableCellDisplayMode.Gauge) {
barGaugeMode = cellOptions.mode ?? BarGaugeDisplayMode.Gradient;
}
const getLinks = () => {

View File

@ -2,8 +2,8 @@ import { cx } from '@emotion/css';
import React, { FC, ReactElement } from 'react';
import tinycolor from 'tinycolor2';
import { DisplayValue, Field, formattedValueToString } from '@grafana/data';
import { TableCellBackgroundDisplayMode } from '@grafana/schema';
import { DisplayValue, formattedValueToString } from '@grafana/data';
import { TableCellBackgroundDisplayMode, TableCellOptions } from '@grafana/schema';
import { getCellLinks, getTextColorForAlphaBackground } from '../../utils';
import { DataLinksContextMenu } from '../DataLinks/DataLinksContextMenu';
@ -11,6 +11,7 @@ import { DataLinksContextMenu } from '../DataLinks/DataLinksContextMenu';
import { CellActions } from './CellActions';
import { TableStyles } from './styles';
import { TableCellDisplayMode, TableCellProps, TableFieldOptions } from './types';
import { getCellOptions } from './utils';
export const DefaultCell: FC<TableCellProps> = (props) => {
const { field, cell, tableStyles, row, cellProps } = props;
@ -27,7 +28,8 @@ export const DefaultCell: FC<TableCellProps> = (props) => {
const showFilters = field.config.filterable;
const showActions = (showFilters && cell.value !== undefined) || inspectEnabled;
const cellStyle = getCellStyle(tableStyles, field, displayValue, inspectEnabled);
const cellOptions = getCellOptions(field);
const cellStyle = getCellStyle(tableStyles, cellOptions, displayValue, inspectEnabled);
const hasLinks = Boolean(getCellLinks(field, row)?.length);
return (
@ -38,7 +40,7 @@ export const DefaultCell: FC<TableCellProps> = (props) => {
<DataLinksContextMenu links={() => getCellLinks(field, row) || []}>
{(api) => {
return (
<div onClick={api.openMenu} className={getLinkStyle(tableStyles, field, api.targetClassName)}>
<div onClick={api.openMenu} className={getLinkStyle(tableStyles, cellOptions, api.targetClassName)}>
{value}
</div>
);
@ -53,49 +55,24 @@ export const DefaultCell: FC<TableCellProps> = (props) => {
function getCellStyle(
tableStyles: TableStyles,
field: Field,
cellOptions: TableCellOptions,
displayValue: DisplayValue,
disableOverflowOnHover = false
) {
// How much to darken elements depends upon if we're in dark mode
const darkeningFactor = tableStyles.theme.isDark ? 1 : -0.7;
// See if we're using deprecated settings
const usingDeprecatedSettings = field.config.custom?.displayMode !== undefined;
// Setup color variables
let textColor: string | undefined = undefined;
let bgColor: string | undefined = undefined;
// Set colors using deprecated settings format
if (usingDeprecatedSettings) {
if (field.config.custom?.displayMode === TableCellDisplayMode.ColorText) {
textColor = displayValue.color;
} else if (field.config.custom?.displayMode === TableCellDisplayMode.ColorBackground) {
if (cellOptions.type === TableCellDisplayMode.ColorText) {
textColor = displayValue.color;
} else if (cellOptions.type === TableCellDisplayMode.ColorBackground) {
if (cellOptions.mode === TableCellBackgroundDisplayMode.Basic) {
textColor = getTextColorForAlphaBackground(displayValue.color!, tableStyles.theme.isDark);
bgColor = tinycolor(displayValue.color).toRgbString();
} else if (
field.config.custom?.displayMode === TableCellDisplayMode.ColorBackground &&
field.config.custom?.backgroundDisplayMode === TableCellBackgroundDisplayMode.Gradient
) {
const bgColor2 = tinycolor(displayValue.color)
.darken(10 * darkeningFactor)
.spin(5);
textColor = getTextColorForAlphaBackground(displayValue.color!, tableStyles.theme.isDark);
bgColor = `linear-gradient(120deg, ${bgColor2.toRgbString()}, ${displayValue.color})`;
}
}
// Set colors using updated sub-options format
else {
const cellDisplayMode = field.config.custom?.cellOptions?.mode;
const cellDisplayType = field.config.custom?.cellOptions?.type;
if (cellDisplayType === TableCellDisplayMode.ColorText) {
textColor = displayValue.color;
} else if (cellDisplayMode === TableCellBackgroundDisplayMode.Basic) {
textColor = getTextColorForAlphaBackground(displayValue.color!, tableStyles.theme.isDark);
bgColor = tinycolor(displayValue.color).toRgbString();
} else if (cellDisplayMode === TableCellBackgroundDisplayMode.Gradient) {
} else if (cellOptions.mode === TableCellBackgroundDisplayMode.Gradient) {
const bgColor2 = tinycolor(displayValue.color)
.darken(10 * darkeningFactor)
.spin(5);
@ -113,8 +90,8 @@ function getCellStyle(
return disableOverflowOnHover ? tableStyles.cellContainerNoOverflow : tableStyles.cellContainer;
}
function getLinkStyle(tableStyles: TableStyles, field: Field, targetClassName: string | undefined) {
if (field.config.custom?.displayMode === TableCellDisplayMode.Auto) {
function getLinkStyle(tableStyles: TableStyles, cellOptions: TableCellOptions, targetClassName: string | undefined) {
if (cellOptions.type === TableCellDisplayMode.Auto) {
return cx(tableStyles.cellLink, targetClassName);
}

View File

@ -16,6 +16,12 @@ import {
GrafanaTheme2,
ArrayVector,
} from '@grafana/data';
import {
BarGaugeDisplayMode,
TableAutoCellOptions,
TableCellBackgroundDisplayMode,
TableCellOptions,
} from '@grafana/schema';
import { BarGaugeCell } from './BarGaugeCell';
import { DefaultCell } from './DefaultCell';
@ -344,3 +350,65 @@ export function createFooterCalculationValues(rows: Row[]): any[number] {
return values;
}
const defaultCellOptions: TableAutoCellOptions = { type: TableCellDisplayMode.Auto };
export function getCellOptions(field: Field): TableCellOptions {
if (field.config.custom?.displayMode) {
return migrateTableDisplayModeToCellOptions(field.config.custom?.displayMode);
}
if (!field.config.custom?.cellOptions) {
return defaultCellOptions;
}
return (field.config.custom as TableFieldOptions).cellOptions;
}
/**
* Migrates table cell display mode to new object format.
*
* @param displayMode The display mode of the cell
* @returns TableCellOptions object in the correct format
* relative to the old display mode.
*/
export function migrateTableDisplayModeToCellOptions(displayMode: TableCellDisplayMode): TableCellOptions {
switch (displayMode) {
// In the case of the gauge we move to a different option
case 'basic':
case 'gradient-gauge':
case 'lcd-gauge':
let gaugeMode = BarGaugeDisplayMode.Basic;
if (displayMode === 'gradient-gauge') {
gaugeMode = BarGaugeDisplayMode.Gradient;
} else if (displayMode === 'lcd-gauge') {
gaugeMode = BarGaugeDisplayMode.Lcd;
}
return {
type: TableCellDisplayMode.Gauge,
mode: gaugeMode,
};
// Also true in the case of the color background
case 'color-background':
case 'color-background-solid':
let mode = TableCellBackgroundDisplayMode.Basic;
// Set the new mode field, somewhat confusingly the
// color-background mode is for gradient display
if (displayMode === 'color-background') {
mode = TableCellBackgroundDisplayMode.Gradient;
}
return {
type: TableCellDisplayMode.ColorBackground,
mode: mode,
};
default:
return {
// @ts-ignore
type: displayMode,
};
}
}

View File

@ -26,8 +26,8 @@ import {
import { labelsToFieldsTransformer } from '@grafana/data/src/transformations/transformers/labelsToFields';
import { mergeTransformer } from '@grafana/data/src/transformations/transformers/merge';
import { getDataSourceSrv, setDataSourceSrv } from '@grafana/runtime';
import { BarGaugeDisplayMode, TableCellBackgroundDisplayMode, TableCellOptions } from '@grafana/schema';
import { AxisPlacement, GraphFieldConfig, TableCellDisplayMode } from '@grafana/ui';
import { AxisPlacement, GraphFieldConfig } from '@grafana/ui';
import { migrateTableDisplayModeToCellOptions } from '@grafana/ui/src/components/Table/utils';
import { getAllOptionEditors, getAllStandardFieldConfigs } from 'app/core/components/OptionsUI/registry';
import { config } from 'app/core/config';
import {
@ -818,7 +818,7 @@ export class DashboardMigrator {
// Update field configuration
if (displayMode !== undefined) {
// Migrate any options for the panel
panel.fieldConfig.defaults.custom.cellOptions = migrateTableCellConfig(displayMode);
panel.fieldConfig.defaults.custom.cellOptions = migrateTableDisplayModeToCellOptions(displayMode);
// Delete the legacy field
delete panel.fieldConfig.defaults.custom.displayMode;
@ -831,7 +831,8 @@ export class DashboardMigrator {
if (panel.fieldConfig.overrides[i].properties[j].id === 'custom.displayMode') {
panel.fieldConfig.overrides[i].properties[j].id = 'custom.cellOptions';
panel.fieldConfig.overrides[i].properties[j].value = migrateTableCellConfig(overrideDisplayMode);
panel.fieldConfig.overrides[i].properties[j].value =
migrateTableDisplayModeToCellOptions(overrideDisplayMode);
}
}
}
@ -1364,50 +1365,3 @@ function ensureXAxisVisibility(panel: PanelModel) {
return panel;
}
/**
* Migrates table cell display mode to new object format.
*
* @param displayMode The display mode of the cell
* @returns TableCellOptions object in the correct format
* relative to the old display mode.
*/
function migrateTableCellConfig(displayMode: TableCellDisplayMode): TableCellOptions {
switch (displayMode) {
// In the case of the gauge we move to a different option
case 'basic':
case 'gradient-gauge':
case 'lcd-gauge':
let gaugeMode = BarGaugeDisplayMode.Basic;
if (displayMode === 'gradient-gauge') {
gaugeMode = BarGaugeDisplayMode.Gradient;
} else if (displayMode === 'lcd-gauge') {
gaugeMode = BarGaugeDisplayMode.Lcd;
}
return {
type: TableCellDisplayMode.Gauge,
mode: gaugeMode,
};
// Also true in the case of the color background
case 'color-background':
case 'color-background-solid':
let mode = TableCellBackgroundDisplayMode.Basic;
// Set the new mode field, somewhat confusingly the
// color-background mode is for gradient display
if (displayMode === 'color-background') {
mode = TableCellBackgroundDisplayMode.Gradient;
}
return {
type: TableCellDisplayMode.ColorBackground,
mode: mode,
};
default:
return {
type: displayMode,
};
}
}

View File

@ -1,22 +1,13 @@
import { merge } from 'lodash';
import React, { ReactNode, useState } from 'react';
import React, { useState } from 'react';
import { SelectableValue } from '@grafana/data';
import { TableCellOptions } from '@grafana/schema';
import { Field, HorizontalGroup, Select, TableCellDisplayMode } from '@grafana/ui';
import { Field, Select, TableCellDisplayMode } from '@grafana/ui';
import { BarGaugeCellOptionsEditor } from './cells/BarGaugeCellOptionsEditor';
import { ColorBackgroundCellOptionsEditor } from './cells/ColorBackgroundCellOptionsEditor';
const cellDisplayModeOptions = [
{ value: TableCellDisplayMode.Auto, label: 'Auto' },
{ value: TableCellDisplayMode.ColorText, label: 'Colored text' },
{ value: TableCellDisplayMode.ColorBackground, label: 'Colored background' },
{ value: TableCellDisplayMode.Gauge, label: 'Gauge' },
{ value: TableCellDisplayMode.JSONView, label: 'JSON View' },
{ value: TableCellDisplayMode.Image, label: 'Image' },
];
// The props that any cell type editor are expected
// to handle. In this case the generic type should
// be a discriminated interface of TableCellOptions
@ -25,29 +16,6 @@ export interface TableCellEditorProps<T> {
onChange: (value: T) => void;
}
// Maps display modes to editor components
interface ComponentMap {
[key: string]: Function;
}
// Maps cell type to options for caching
interface SettingMap {
[key: string]: TableCellOptions;
}
/*
Map of display modes to editor components
Additional cell types can be placed here
---
A cell editor is expected to be a functional
component that accepts options and displays
them in a form.
*/
const displayModeComponentMap: ComponentMap = {
[TableCellDisplayMode.Gauge]: BarGaugeCellOptionsEditor,
[TableCellDisplayMode.ColorBackground]: ColorBackgroundCellOptionsEditor,
};
interface Props {
value: TableCellOptions;
onChange: (v: TableCellOptions) => void;
@ -55,25 +23,21 @@ interface Props {
export const TableCellOptionEditor = ({ value, onChange }: Props) => {
const cellType = value.type;
let editor: ReactNode | null = null;
let [settingCache, setSettingCache] = useState<SettingMap>({});
const currentMode = cellDisplayModeOptions.find((o) => o.value!.type === cellType)!;
let [settingCache, setSettingCache] = useState<Record<string, TableCellOptions>>({});
// Update display mode on change
const onCellTypeChange = (v: SelectableValue<TableCellDisplayMode>) => {
const onCellTypeChange = (v: SelectableValue<TableCellOptions>) => {
if (v.value !== undefined) {
// Set the new type of cell starting
// with default settings
value = {
type: v.value,
};
value = v.value;
// When changing cell type see if there were previously stored
// settings and merge those with the changed value
if (settingCache[v.value] !== undefined && Object.keys(settingCache[v.value]).length > 1) {
settingCache[v.value] = merge(value, settingCache[v.value]);
setSettingCache(settingCache);
onChange(settingCache[v.value]);
return;
if (settingCache[value.type] !== undefined && Object.keys(settingCache[value.type]).length > 1) {
value = merge(value, settingCache[value.type]);
}
onChange(value);
@ -88,19 +52,27 @@ export const TableCellOptionEditor = ({ value, onChange }: Props) => {
onChange(settingCache[value.type]);
};
// Setup specific cell editor
if (cellType !== undefined && displayModeComponentMap[cellType] !== undefined) {
let Comp: Function = displayModeComponentMap[cellType];
editor = <Comp cellOptions={value} onChange={onCellOptionsChange} />;
}
// Setup and inject editor
return (
<>
<Field>
<Select options={cellDisplayModeOptions} value={cellType} onChange={onCellTypeChange} />
<Select options={cellDisplayModeOptions} value={currentMode} onChange={onCellTypeChange} />
</Field>
<HorizontalGroup>{editor}</HorizontalGroup>
{cellType === TableCellDisplayMode.Gauge && (
<BarGaugeCellOptionsEditor cellOptions={value} onChange={onCellOptionsChange} />
)}
{cellType === TableCellDisplayMode.ColorBackground && (
<ColorBackgroundCellOptionsEditor cellOptions={value} onChange={onCellOptionsChange} />
)}
</>
);
};
const cellDisplayModeOptions: Array<SelectableValue<TableCellOptions>> = [
{ value: { type: TableCellDisplayMode.Auto }, label: 'Auto' },
{ value: { type: TableCellDisplayMode.ColorText }, label: 'Colored text' },
{ value: { type: TableCellDisplayMode.ColorBackground }, label: 'Colored background' },
{ value: { type: TableCellDisplayMode.Gauge }, label: 'Gauge' },
{ value: { type: TableCellDisplayMode.JSONView }, label: 'JSON View' },
{ value: { type: TableCellDisplayMode.Image }, label: 'Image' },
];