diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7843dca6c87..a8dc5d1a68b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -501,7 +501,6 @@ i18next.config.ts @grafana/grafana-frontend-platform /e2e-playwright/various-suite/filter-annotations.spec.ts @grafana/dashboards-squad /e2e-playwright/various-suite/frontend-sandbox-app.spec.ts @grafana/plugins-platform-frontend /e2e-playwright/various-suite/frontend-sandbox-datasource.spec.ts @grafana/plugins-platform-frontend -/e2e-playwright/various-suite/gauge.spec.ts @grafana/dataviz-squad /e2e-playwright/various-suite/grafana-datasource-random-walk.spec.ts @grafana/grafana-frontend-platform /e2e-playwright/various-suite/graph-auto-migrate.spec.ts @grafana/dataviz-squad /e2e-playwright/various-suite/inspect-drawer.spec.ts @grafana/dashboards-squad diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests.v42.json index 92a865b0b10..a705218273e 100644 --- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests.v42.json +++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests.v42.json @@ -628,6 +628,20 @@ } ], "title": "Only nulls and no user set min \u0026 max", + "transformations": [ + { + "id": "convertFieldType", + "options": { + "conversions": [ + { + "destinationType": "number", + "targetField": "A-series" + } + ], + "fields": {} + } + } + ], "type": "gauge" }, { @@ -1179,4 +1193,4 @@ "title": "Panel Tests - Gauge", "uid": "_5rDmaQiz", "weekStart": "" -} \ No newline at end of file +} diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_new.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_new.v42.json index cb130445efc..f24ec9a61a0 100644 --- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_new.v42.json +++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_new.v42.json @@ -1760,6 +1760,22 @@ "startValue": 0 } ], + "transformations": [ + { + "id": "calculateField", + "options": { + "mode": "unary", + "reduce": { + "reducer": "sum" + }, + "replaceFields": true, + "unary": { + "operator": "round", + "fieldName": "A-series" + } + } + } + ], "title": "Active gateways", "type": "radialbar" }, @@ -1843,6 +1859,22 @@ "startValue": 0 } ], + "transformations": [ + { + "id": "calculateField", + "options": { + "mode": "unary", + "reduce": { + "reducer": "sum" + }, + "replaceFields": true, + "unary": { + "operator": "round", + "fieldName": "A-series" + } + } + } + ], "title": "Active pods", "type": "radialbar" }, diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_old_to_new.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_old_to_new.v42.json index dda0fbcd432..a75a58b7530 100644 --- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_old_to_new.v42.json +++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_old_to_new.v42.json @@ -485,6 +485,7 @@ }, "id": 12, "options": { + "displayName": "My gauge", "minVizHeight": 75, "minVizWidth": 75, "orientation": "auto", diff --git a/devenv/dev-dashboards/panel-gauge/gauge_tests.json b/devenv/dev-dashboards/panel-gauge/gauge_tests.json index 0c8e2651e67..f32ace420b4 100644 --- a/devenv/dev-dashboards/panel-gauge/gauge_tests.json +++ b/devenv/dev-dashboards/panel-gauge/gauge_tests.json @@ -600,6 +600,20 @@ "stringInput": "null,null" } ], + "transformations": [ + { + "id": "convertFieldType", + "options": { + "fields": {}, + "conversions": [ + { + "targetField": "A-series", + "destinationType": "number" + } + ] + } + } + ], "title": "Only nulls and no user set min & max", "type": "gauge" }, diff --git a/devenv/dev-dashboards/panel-gauge/gauge_tests_new.json b/devenv/dev-dashboards/panel-gauge/gauge_tests_new.json index 65cc0b0a5ae..ff69226fdf5 100644 --- a/devenv/dev-dashboards/panel-gauge/gauge_tests_new.json +++ b/devenv/dev-dashboards/panel-gauge/gauge_tests_new.json @@ -1718,6 +1718,22 @@ "startValue": 0 } ], + "transformations": [ + { + "id": "calculateField", + "options": { + "mode": "unary", + "reduce": { + "reducer": "sum" + }, + "replaceFields": true, + "unary": { + "operator": "round", + "fieldName": "A-series" + } + } + } + ], "title": "Active gateways", "type": "radialbar" }, @@ -1799,6 +1815,22 @@ "startValue": 0 } ], + "transformations": [ + { + "id": "calculateField", + "options": { + "mode": "unary", + "reduce": { + "reducer": "sum" + }, + "replaceFields": true, + "unary": { + "operator": "round", + "fieldName": "A-series" + } + } + } + ], "title": "Active pods", "type": "radialbar" }, diff --git a/devenv/dev-dashboards/panel-gauge/gauge_tests_old_to_new.json b/devenv/dev-dashboards/panel-gauge/gauge_tests_old_to_new.json index bee1ece914e..3ca00ad5b63 100644 --- a/devenv/dev-dashboards/panel-gauge/gauge_tests_old_to_new.json +++ b/devenv/dev-dashboards/panel-gauge/gauge_tests_old_to_new.json @@ -474,6 +474,7 @@ }, "id": 12, "options": { + "displayName": "My gauge", "minVizHeight": 75, "minVizWidth": 75, "orientation": "auto", diff --git a/e2e-playwright/panels-suite/gauge.spec.ts b/e2e-playwright/panels-suite/gauge.spec.ts new file mode 100644 index 00000000000..cb4b26e9850 --- /dev/null +++ b/e2e-playwright/panels-suite/gauge.spec.ts @@ -0,0 +1,101 @@ +import { test, expect } from '@grafana/plugin-e2e'; + +// this test requires a larger viewport so all gauge panels load properly +test.use({ + featureToggles: { newGauge: true }, + viewport: { width: 1280, height: 3000 }, +}); + +const OLD_GAUGES_DASHBOARD_UID = '_5rDmaQiz'; +const NEW_GAUGES_DASHBOARD_UID = 'panel-tests-gauge-new'; + +test.describe( + 'Gauge Panel', + { + tag: ['@panels', '@gauge'], + }, + () => { + test('successfully migrates all gauge panels', async ({ gotoDashboardPage, selectors }) => { + const dashboardPage = await gotoDashboardPage({ uid: OLD_GAUGES_DASHBOARD_UID }); + + // check that gauges are rendered + const gaugeElements = dashboardPage.getByGrafanaSelector( + selectors.components.Panels.Visualization.Gauge.Container + ); + await expect(gaugeElements).toHaveCount(16); + + // check that no panel errors exist + const errorInfo = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.headerCornerInfo('error')); + await expect(errorInfo).toBeHidden(); + }); + + test('renders new gauge panels', async ({ gotoDashboardPage, selectors }) => { + // open Panel Tests - Gauge + const dashboardPage = await gotoDashboardPage({ uid: NEW_GAUGES_DASHBOARD_UID }); + + // check that gauges are rendered + const gaugeElements = dashboardPage.getByGrafanaSelector( + selectors.components.Panels.Visualization.Gauge.Container + ); + await expect(gaugeElements).toHaveCount(32); + + // check that no panel errors exist + const errorInfo = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.headerCornerInfo('error')); + await expect(errorInfo).toBeHidden(); + }); + + test('renders sparklines in gauge panels', async ({ gotoDashboardPage, page }) => { + await gotoDashboardPage({ + uid: NEW_GAUGES_DASHBOARD_UID, + queryParams: new URLSearchParams({ editPanel: '11' }), + }); + + await expect(page.locator('.uplot')).toHaveCount(5); + }); + + test('"no data"', async ({ gotoDashboardPage, selectors }) => { + const dashboardPage = await gotoDashboardPage({ + uid: NEW_GAUGES_DASHBOARD_UID, + queryParams: new URLSearchParams({ editPanel: '36' }), + }); + + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Visualization.Gauge.Container), + 'that the gauge does not appear' + ).toBeHidden(); + + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.PanelDataErrorMessage), + 'that the empty text appears' + ).toHaveText('No data'); + + // update the "No value" option and see if the panel updates + const noValueOption = dashboardPage + .getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel('Standard options No value')) + .locator('input'); + + await noValueOption.fill('My empty value'); + await noValueOption.blur(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Visualization.Gauge.Container), + 'that the empty text shows up in an empty gauge' + ).toHaveText('My empty value'); + + // test the "no numeric fields" message on the next panel + const dashboardPage2 = await gotoDashboardPage({ + uid: NEW_GAUGES_DASHBOARD_UID, + queryParams: new URLSearchParams({ editPanel: '37' }), + }); + + await expect( + dashboardPage2.getByGrafanaSelector(selectors.components.Panels.Visualization.Gauge.Container), + 'that the gauge does not appear' + ).toBeHidden(); + + await expect( + dashboardPage2.getByGrafanaSelector(selectors.components.Panels.Panel.PanelDataErrorMessage), + 'that the empty text appears' + ).toHaveText('Data is missing a number field'); + }); + } +); diff --git a/e2e-playwright/various-suite/gauge.spec.ts b/e2e-playwright/various-suite/gauge.spec.ts deleted file mode 100644 index 3e2d6023c95..00000000000 --- a/e2e-playwright/various-suite/gauge.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { test, expect } from '@grafana/plugin-e2e'; - -// this test requires a larger viewport so all gauge panels load properly -test.use({ - viewport: { width: 1280, height: 1080 }, -}); - -test.describe( - 'Gauge Panel', - { - tag: ['@various'], - }, - () => { - test('Gauge rendering e2e tests', async ({ gotoDashboardPage, selectors, page }) => { - // open Panel Tests - Gauge - const dashboardPage = await gotoDashboardPage({ uid: '_5rDmaQiz' }); - - // check that gauges are rendered - const gaugeElements = page.locator('.flot-base'); - await expect(gaugeElements).toHaveCount(16); - - // check that no panel errors exist - const errorInfo = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.headerCornerInfo('error')); - await expect(errorInfo).toBeHidden(); - }); - } -); diff --git a/packages/grafana-e2e-selectors/src/selectors/components.ts b/packages/grafana-e2e-selectors/src/selectors/components.ts index de04398a3e1..0e7e7bf6150 100644 --- a/packages/grafana-e2e-selectors/src/selectors/components.ts +++ b/packages/grafana-e2e-selectors/src/selectors/components.ts @@ -535,6 +535,11 @@ export const versionedComponents = { '12.3.0': 'data-testid viz-tooltip-wrapper', }, }, + Gauge: { + Container: { + '12.4.0': 'data-testid gauge container', + }, + }, }, }, VizLegend: { diff --git a/packages/grafana-schema/src/raw/composable/newgauge/panelcfg/x/NewGaugePanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/newgauge/panelcfg/x/NewGaugePanelCfg_types.gen.ts index 7c29eb7b463..c0f8481a7f5 100644 --- a/packages/grafana-schema/src/raw/composable/newgauge/panelcfg/x/NewGaugePanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/newgauge/panelcfg/x/NewGaugePanelCfg_types.gen.ts @@ -35,6 +35,7 @@ export interface Options extends common.SingleStatBaseOptions { showThresholdLabels: boolean; showThresholdMarkers: boolean; sparkline?: boolean; + textMode?: ('auto' | 'value_and_name' | 'value' | 'name' | 'none'); } export const defaultOptions: Partial = { @@ -48,4 +49,5 @@ export const defaultOptions: Partial = { showThresholdLabels: false, showThresholdMarkers: true, sparkline: true, + textMode: 'auto', }; diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialGauge.story.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.story.tsx index b0574ef3f87..5c5faa64db7 100644 --- a/packages/grafana-ui/src/components/RadialGauge/RadialGauge.story.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.story.tsx @@ -32,24 +32,6 @@ const meta: Meta = { controls: { exclude: ['theme', 'values', 'vizCount'], }, - a11y: { - config: { - rules: [ - { - id: 'scrollable-region-focusable', - selector: 'body', - enabled: false, - }, - // NOTE: this is necessary due to a false positive with the filered svg glow in one of the examples. - // The color-contrast in this component should be accessible! - { - id: 'color-contrast', - selector: 'text', - enabled: false, - }, - ], - }, - }, }, args: { barWidthFactor: 0.2, diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx index 1251a364230..452dd394ab1 100644 --- a/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx @@ -2,6 +2,7 @@ import { css, cx } from '@emotion/css'; import { useId } from 'react'; import { DisplayValueAlignmentFactors, FALLBACK_COLOR, FieldDisplay, GrafanaTheme2, TimeRange } from '@grafana/data'; +import { selectors } from '@grafana/e2e-selectors'; import { t } from '@grafana/i18n'; import { useStyles2, useTheme2 } from '../../themes/ThemeContext'; @@ -275,7 +276,11 @@ export function RadialGauge(props: RadialGaugeProps) { } return ( -
+
{body}
); diff --git a/packages/grafana-ui/src/components/RadialGauge/effects.tsx b/packages/grafana-ui/src/components/RadialGauge/effects.tsx index c48307f177e..53a255d4a45 100644 --- a/packages/grafana-ui/src/components/RadialGauge/effects.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/effects.tsx @@ -1,4 +1,4 @@ -import { GrafanaTheme2 } from '@grafana/data'; +import { colorManipulator, GrafanaTheme2 } from '@grafana/data'; import { RadialGaugeDimensions } from './types'; @@ -25,13 +25,14 @@ export function GlowGradient({ id, barWidth }: GlowGradientProps) { ); } -const CENTER_GLOW_OPACITY = 0.15; +const CENTER_GLOW_OPACITY = 0.25; export function CenterGlowGradient({ gaugeId, color }: { gaugeId: string; color: string }) { + const transparentColor = colorManipulator.alpha(color, CENTER_GLOW_OPACITY); return ( - - + + ); } @@ -44,13 +45,14 @@ export interface CenterGlowProps { export function MiddleCircleGlow({ dimensions, gaugeId, color }: CenterGlowProps) { const gradientId = `circle-glow-${gaugeId}`; + const transparentColor = color ? colorManipulator.alpha(color, CENTER_GLOW_OPACITY) : color; return ( <> - - + + @@ -86,9 +88,9 @@ export function SpotlightGradient({ return ( - - - {roundedBars && } + + + {roundedBars && } ); } diff --git a/packages/grafana-ui/src/components/VizRepeater/VizRepeater.tsx b/packages/grafana-ui/src/components/VizRepeater/VizRepeater.tsx index a1fb6c6a1c3..735e198f0bc 100644 --- a/packages/grafana-ui/src/components/VizRepeater/VizRepeater.tsx +++ b/packages/grafana-ui/src/components/VizRepeater/VizRepeater.tsx @@ -167,7 +167,8 @@ export class VizRepeater extends PureComponent ); } @@ -65,9 +66,7 @@ export function RadialBarPanel({ if (hasLinks && getLinks) { return ( - {(api) => { - return renderComponent(valueProps, api); - }} + {(api) => renderComponent(valueProps, api)} ); } diff --git a/public/app/plugins/panel/radialbar/__snapshots__/GaugeMigrations.test.ts.snap b/public/app/plugins/panel/radialbar/__snapshots__/migrations.test.ts.snap similarity index 100% rename from public/app/plugins/panel/radialbar/__snapshots__/GaugeMigrations.test.ts.snap rename to public/app/plugins/panel/radialbar/__snapshots__/migrations.test.ts.snap diff --git a/public/app/plugins/panel/radialbar/GaugeMigrations.test.ts b/public/app/plugins/panel/radialbar/migrations.test.ts similarity index 82% rename from public/app/plugins/panel/radialbar/GaugeMigrations.test.ts rename to public/app/plugins/panel/radialbar/migrations.test.ts index 8e870517f65..a09f1435e90 100644 --- a/public/app/plugins/panel/radialbar/GaugeMigrations.test.ts +++ b/public/app/plugins/panel/radialbar/migrations.test.ts @@ -1,7 +1,7 @@ import { PanelModel } from '@grafana/data'; import { FieldColorModeId } from '@grafana/schema/dist/esm/index.gen'; -import { gaugePanelMigrationHandler, gaugePanelChangedHandler } from './GaugeMigrations'; +import { gaugePanelMigrationHandler, gaugePanelChangedHandler } from './migrations'; describe('Gauge Panel Migrations', () => { it('from old gauge', () => { @@ -32,6 +32,51 @@ describe('Gauge Panel Migrations', () => { expect(result.sparkline).toBe(false); }); + it.each([ + { + textMode: 'value_and_name', + displayName: 'My gauge', + }, + { + textMode: undefined, + displayName: '', + }, + { + textMode: undefined, + displayName: null, + }, + { + textMode: undefined, + displayName: undefined, + }, + ])('sets the the text mode to "$textMode" for "$displayName"', ({ textMode, displayName }) => { + const panel = { + id: 2, + options: { + displayName, + reduceOptions: { + calcs: ['lastNotNull'], + }, + showThresholdLabels: false, + showThresholdMarkers: true, + }, + fieldConfig: { + defaults: { + color: { + mode: FieldColorModeId.Fixed, + fixedColor: 'blue', + }, + }, + overrides: [], + }, + pluginVersion: '12.3.0', + type: 'gauge', + } as Omit; + + const result = gaugePanelMigrationHandler(panel as PanelModel); + expect(result.textMode).toEqual(textMode); + }); + it('does not overwrite new gauge', () => { const panel = { id: 2, diff --git a/public/app/plugins/panel/radialbar/GaugeMigrations.ts b/public/app/plugins/panel/radialbar/migrations.ts similarity index 91% rename from public/app/plugins/panel/radialbar/GaugeMigrations.ts rename to public/app/plugins/panel/radialbar/migrations.ts index a64bc93b1cc..ec7840fe779 100644 --- a/public/app/plugins/panel/radialbar/GaugeMigrations.ts +++ b/public/app/plugins/panel/radialbar/migrations.ts @@ -21,6 +21,11 @@ export function gaugePanelMigrationHandler(panel: PanelModel): Partial< newOptions.sparkline = false; newOptions.effects = { gradient: false }; + // if a display name is set, set the appropriate text mode + if ('displayName' in newOptions && newOptions.displayName && newOptions.displayName !== '') { + newOptions.textMode = 'value_and_name'; + } + // Remove deprecated sizing options if ('sizing' in newOptions) { delete newOptions.sizing; diff --git a/public/app/plugins/panel/radialbar/module.tsx b/public/app/plugins/panel/radialbar/module.tsx index 3dfcb3e3d5f..f7afa0ef2c9 100644 --- a/public/app/plugins/panel/radialbar/module.tsx +++ b/public/app/plugins/panel/radialbar/module.tsx @@ -5,8 +5,8 @@ import { commonOptionsBuilder } from '@grafana/ui'; import { addOrientationOption, addStandardDataReduceOptions } from '../stat/common'; import { EffectsEditor } from './EffectsEditor'; -import { gaugePanelChangedHandler, gaugePanelMigrationHandler, shouldMigrateGauge } from './GaugeMigrations'; import { RadialBarPanel } from './RadialBarPanel'; +import { gaugePanelChangedHandler, gaugePanelMigrationHandler, shouldMigrateGauge } from './migrations'; import { defaultGaugePanelEffects, defaultOptions, Options } from './panelcfg.gen'; import { radialBarSuggestionsSupplier } from './suggestions'; @@ -99,6 +99,22 @@ export const plugin = new PanelPlugin(RadialBarPanel) showIf: (options) => options.barShape === 'rounded' && options.segmentCount === 1, }); + builder.addSelect({ + path: 'textMode', + name: t('radialbar.config.text-mode', 'Text mode'), + category, + settings: { + options: [ + { value: 'auto', label: t('radialbar.config.text-mode-auto', 'Auto') }, + { value: 'value_and_name', label: t('radialbar.config.text-mode-value-and-name', 'Value and Name') }, + { value: 'value', label: t('radialbar.config.text-mode-value', 'Value') }, + { value: 'name', label: t('radialbar.config.text-mode-name', 'Name') }, + { value: 'none', label: t('radialbar.config.text-mode-none', 'None') }, + ], + }, + defaultValue: defaultOptions.textMode, + }); + builder.addBooleanSwitch({ path: 'sparkline', name: t('radialbar.config.sparkline', 'Show sparkline'), diff --git a/public/app/plugins/panel/radialbar/panelcfg.cue b/public/app/plugins/panel/radialbar/panelcfg.cue index c959512c0b8..6e5fd7eec21 100644 --- a/public/app/plugins/panel/radialbar/panelcfg.cue +++ b/public/app/plugins/panel/radialbar/panelcfg.cue @@ -42,6 +42,7 @@ composableKinds: PanelCfg: { barWidthFactor: number | *0.5 barShape: "flat" | "rounded" | *"flat" endpointMarker?: "point" | "glow" | "none" | *"point" + textMode?: "auto" | "value_and_name" | "value" | "name" | "none" | *"auto" effects: GaugePanelEffects | *{} } @cuetsy(kind="interface") } diff --git a/public/app/plugins/panel/radialbar/panelcfg.gen.ts b/public/app/plugins/panel/radialbar/panelcfg.gen.ts index e050a044f77..594747136ef 100644 --- a/public/app/plugins/panel/radialbar/panelcfg.gen.ts +++ b/public/app/plugins/panel/radialbar/panelcfg.gen.ts @@ -33,6 +33,7 @@ export interface Options extends common.SingleStatBaseOptions { showThresholdLabels: boolean; showThresholdMarkers: boolean; sparkline?: boolean; + textMode?: ('auto' | 'value_and_name' | 'value' | 'name' | 'none'); } export const defaultOptions: Partial = { @@ -46,4 +47,5 @@ export const defaultOptions: Partial = { showThresholdLabels: false, showThresholdMarkers: true, sparkline: true, + textMode: 'auto', }; diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 7430957c560..10967a7331c 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -12519,6 +12519,12 @@ "shape-circle": "Circle", "shape-gauge": "Arc", "sparkline": "Show sparkline", + "text-mode": "Text mode", + "text-mode-auto": "Auto", + "text-mode-name": "Name", + "text-mode-none": "None", + "text-mode-value": "Value", + "text-mode-value-and-name": "Value and Name", "threshold-labels": "Show threshold labels", "threshold-markers": "Show thresholds" }