From 6f9c867660c4b76941fe090d01a86a2446719714 Mon Sep 17 00:00:00 2001 From: Paul Marbach Date: Thu, 20 Nov 2025 13:49:46 -0500 Subject: [PATCH] Timeseries: More nuanced editing of linear threshold to avoid crashes (#112301) * Timeseries: More nuanced editing of linear threshold to avoid crashes * remove useEffect import * remove unused import * update error and invalid field handling to call out error case --- .../src/options/builder/axis.test.tsx | 33 ++++++++++ .../grafana-ui/src/options/builder/axis.tsx | 64 ++++++++++++++++--- public/locales/cs-CZ/grafana.json | 6 +- public/locales/de-DE/grafana.json | 6 +- public/locales/en-US/grafana.json | 8 ++- public/locales/es-ES/grafana.json | 6 +- public/locales/fr-FR/grafana.json | 6 +- public/locales/hu-HU/grafana.json | 6 +- public/locales/id-ID/grafana.json | 6 +- public/locales/it-IT/grafana.json | 6 +- public/locales/ja-JP/grafana.json | 6 +- public/locales/ko-KR/grafana.json | 6 +- public/locales/nl-NL/grafana.json | 6 +- public/locales/pl-PL/grafana.json | 6 +- public/locales/pt-BR/grafana.json | 6 +- public/locales/pt-PT/grafana.json | 6 +- public/locales/ru-RU/grafana.json | 6 +- public/locales/sv-SE/grafana.json | 6 +- public/locales/tr-TR/grafana.json | 6 +- public/locales/zh-Hans/grafana.json | 6 +- public/locales/zh-Hant/grafana.json | 6 +- 21 files changed, 168 insertions(+), 45 deletions(-) create mode 100644 packages/grafana-ui/src/options/builder/axis.test.tsx diff --git a/packages/grafana-ui/src/options/builder/axis.test.tsx b/packages/grafana-ui/src/options/builder/axis.test.tsx new file mode 100644 index 00000000000..0b749ec6f63 --- /dev/null +++ b/packages/grafana-ui/src/options/builder/axis.test.tsx @@ -0,0 +1,33 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { ScaleDistribution } from '@grafana/schema'; + +import { ScaleDistributionEditor } from './axis'; + +describe('ScaleDistributionEditor', () => { + describe('Symlog', () => { + it('linear threshold should not dispatch a change for 0', async () => { + const onChange = jest.fn(); + const origValue = { type: ScaleDistribution.Symlog, log: 10 }; + + render(); + + // so annoying that this doesn't work. + // const el = await screen.findByLabelText('Linear threshold'); + const el = screen.getByTestId('input-wrapper').querySelector('input')!; + + await userEvent.type(el, '0'); + expect(onChange).not.toHaveBeenCalled(); + + await userEvent.type(el, '.'); + expect(onChange).not.toHaveBeenCalled(); + + await userEvent.type(el, '5'); + expect(onChange).toHaveBeenCalledWith({ linearThreshold: 0.5, ...origValue }); + + await userEvent.clear(el); + expect(onChange).toHaveBeenCalledWith(origValue); + }); + }); +}); diff --git a/packages/grafana-ui/src/options/builder/axis.tsx b/packages/grafana-ui/src/options/builder/axis.tsx index d3b6a74ee07..83237cb310c 100644 --- a/packages/grafana-ui/src/options/builder/axis.tsx +++ b/packages/grafana-ui/src/options/builder/axis.tsx @@ -1,3 +1,5 @@ +import { useState } from 'react'; + import { FieldConfigEditorBuilder, FieldType, @@ -126,12 +128,31 @@ const LOG_DISTRIBUTION_OPTIONS: Array> = [ }, ]; +const isValidLinearThreshold = (value: number): string | undefined => { + if (Number.isNaN(value)) { + return t('grafana-ui.axis-builder.linear-threshold.warning.nan', 'Linear threshold must be a number'); + } + if (value === 0) { + return t('grafana-ui.axis-builder.linear-threshold.warning.zero', 'Linear threshold cannot be zero'); + } + return; +}; + /** * @internal */ -export const ScaleDistributionEditor = ({ value, onChange }: StandardEditorProps) => { +export const ScaleDistributionEditor = ({ + value, + onChange, +}: Pick, 'value' | 'onChange'>) => { const type = value?.type ?? ScaleDistribution.Linear; const log = value?.log ?? 2; + + const [localLinearThreshold, setLocalLinearThreshold] = useState( + value?.linearThreshold != null ? String(value.linearThreshold) : '' + ); + const [linearThresholdWarning, setLinearThresholdWarning] = useState(); + const DISTRIBUTION_OPTIONS: Array> = [ { label: t('grafana-ui.builder.axis.scale-distribution-editor.distribution-options.label-linear', 'Linear'), @@ -161,7 +182,7 @@ export const ScaleDistributionEditor = ({ value, onChange }: StandardEditorProps }} /> {(type === ScaleDistribution.Log || type === ScaleDistribution.Symlog) && ( - + { + if (ev.currentTarget.value) { + setLinearThresholdWarning(isValidLinearThreshold(Number(ev.currentTarget.value))); + } + }} onChange={(v) => { - onChange({ - ...value, - linearThreshold: Number(v.currentTarget.value), - }); + setLocalLinearThreshold(v.currentTarget.value); + if (v.currentTarget.value === '') { + const newValue = { ...value }; + delete newValue.linearThreshold; + onChange(newValue); + setLinearThresholdWarning(undefined); + return; + } + + const asNumber = Number(v.currentTarget.value); + if (isValidLinearThreshold(asNumber) == null) { + setLinearThresholdWarning(undefined); + onChange({ + ...value, + linearThreshold: asNumber, + }); + } }} /> diff --git a/public/locales/cs-CZ/grafana.json b/public/locales/cs-CZ/grafana.json index 70cca883b8b..cc3b5e994c4 100644 --- a/public/locales/cs-CZ/grafana.json +++ b/public/locales/cs-CZ/grafana.json @@ -8661,7 +8661,9 @@ "saving": "Ukládání <1>" }, "axis-builder": { - "linear-threshold": "Lineární práh", + "linear-threshold": { + "label": "Lineární práh" + }, "log-base": "Základ protokolu" }, "builder": { @@ -14694,4 +14696,4 @@ "label-points": "Body" } } -} \ No newline at end of file +} diff --git a/public/locales/de-DE/grafana.json b/public/locales/de-DE/grafana.json index d491c81700b..a4eb397e4f7 100644 --- a/public/locales/de-DE/grafana.json +++ b/public/locales/de-DE/grafana.json @@ -8589,7 +8589,9 @@ "saving": "<1> wird gespeichert" }, "axis-builder": { - "linear-threshold": "Linearer Schwellenwert", + "linear-threshold": { + "label": "Linearer Schwellenwert" + }, "log-base": "Logarithmische Basis" }, "builder": { @@ -14580,4 +14582,4 @@ "label-points": "Punkte" } } -} \ No newline at end of file +} diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index d29fe101bd6..ba66da11bbf 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -8589,7 +8589,13 @@ "saving": "Saving <1>" }, "axis-builder": { - "linear-threshold": "Linear threshold", + "linear-threshold": { + "label": "Linear threshold", + "warning": { + "nan": "Linear threshold must be a number", + "zero": "Linear threshold cannot be zero" + } + }, "log-base": "Log base" }, "builder": { diff --git a/public/locales/es-ES/grafana.json b/public/locales/es-ES/grafana.json index adae8f28bf6..933d56ff352 100644 --- a/public/locales/es-ES/grafana.json +++ b/public/locales/es-ES/grafana.json @@ -8589,7 +8589,9 @@ "saving": "Guardando <1>" }, "axis-builder": { - "linear-threshold": "Umbral lineal", + "linear-threshold": { + "label": "Umbral lineal" + }, "log-base": "Base de registro" }, "builder": { @@ -14580,4 +14582,4 @@ "label-points": "Puntos" } } -} \ No newline at end of file +} diff --git a/public/locales/fr-FR/grafana.json b/public/locales/fr-FR/grafana.json index e0648accd63..9bedeb3235c 100644 --- a/public/locales/fr-FR/grafana.json +++ b/public/locales/fr-FR/grafana.json @@ -8589,7 +8589,9 @@ "saving": "Enregistrement en cours <1>" }, "axis-builder": { - "linear-threshold": "Seuil linéaire", + "linear-threshold": { + "label": "Seuil linéaire" + }, "log-base": "Base du journal" }, "builder": { @@ -14580,4 +14582,4 @@ "label-points": "Points" } } -} \ No newline at end of file +} diff --git a/public/locales/hu-HU/grafana.json b/public/locales/hu-HU/grafana.json index 80e9b74c7a7..bccbc9c4437 100644 --- a/public/locales/hu-HU/grafana.json +++ b/public/locales/hu-HU/grafana.json @@ -8589,7 +8589,9 @@ "saving": "Mentés: <1>" }, "axis-builder": { - "linear-threshold": "Lineáris küszöbérték", + "linear-threshold": { + "label": "Lineáris küszöbérték" + }, "log-base": "Naplóbázis" }, "builder": { @@ -14580,4 +14582,4 @@ "label-points": "Pontok" } } -} \ No newline at end of file +} diff --git a/public/locales/id-ID/grafana.json b/public/locales/id-ID/grafana.json index 1d0d348f979..e2653a0bbee 100644 --- a/public/locales/id-ID/grafana.json +++ b/public/locales/id-ID/grafana.json @@ -8553,7 +8553,9 @@ "saving": "Menyimpan <1>" }, "axis-builder": { - "linear-threshold": "Ambang linear", + "linear-threshold": { + "label": "Ambang linear" + }, "log-base": "Basis log" }, "builder": { @@ -14523,4 +14525,4 @@ "label-points": "Poin" } } -} \ No newline at end of file +} diff --git a/public/locales/it-IT/grafana.json b/public/locales/it-IT/grafana.json index 31e179978f2..4d9670ae838 100644 --- a/public/locales/it-IT/grafana.json +++ b/public/locales/it-IT/grafana.json @@ -8589,7 +8589,9 @@ "saving": "Salvataggio in corso di <1>" }, "axis-builder": { - "linear-threshold": "Soglia lineare", + "linear-threshold": { + "label": "Soglia lineare" + }, "log-base": "Base logaritmica" }, "builder": { @@ -14580,4 +14582,4 @@ "label-points": "Punti" } } -} \ No newline at end of file +} diff --git a/public/locales/ja-JP/grafana.json b/public/locales/ja-JP/grafana.json index 22a4029f62d..ba8d530e4a0 100644 --- a/public/locales/ja-JP/grafana.json +++ b/public/locales/ja-JP/grafana.json @@ -8553,7 +8553,9 @@ "saving": "<1>を保存しています" }, "axis-builder": { - "linear-threshold": "線形のしきい値", + "linear-threshold": { + "label": "線形のしきい値" + }, "log-base": "ログベース" }, "builder": { @@ -14523,4 +14525,4 @@ "label-points": "ポイント" } } -} \ No newline at end of file +} diff --git a/public/locales/ko-KR/grafana.json b/public/locales/ko-KR/grafana.json index 3174cc0be90..debe88cc4ac 100644 --- a/public/locales/ko-KR/grafana.json +++ b/public/locales/ko-KR/grafana.json @@ -8553,7 +8553,9 @@ "saving": "<1> 저장 중" }, "axis-builder": { - "linear-threshold": "선형 임계값", + "linear-threshold": { + "label": "선형 임계값" + }, "log-base": "로그 베이스" }, "builder": { @@ -14523,4 +14525,4 @@ "label-points": "포인트" } } -} \ No newline at end of file +} diff --git a/public/locales/nl-NL/grafana.json b/public/locales/nl-NL/grafana.json index ebef81b6199..46a2279ecfb 100644 --- a/public/locales/nl-NL/grafana.json +++ b/public/locales/nl-NL/grafana.json @@ -8589,7 +8589,9 @@ "saving": "Opslaan <1>" }, "axis-builder": { - "linear-threshold": "Lineaire drempel", + "linear-threshold": { + "label": "Lineaire drempel" + }, "log-base": "Logboekbase" }, "builder": { @@ -14580,4 +14582,4 @@ "label-points": "Punten" } } -} \ No newline at end of file +} diff --git a/public/locales/pl-PL/grafana.json b/public/locales/pl-PL/grafana.json index 4ea5e15c281..977a67b7d6c 100644 --- a/public/locales/pl-PL/grafana.json +++ b/public/locales/pl-PL/grafana.json @@ -8661,7 +8661,9 @@ "saving": "Zapisywanie <1>" }, "axis-builder": { - "linear-threshold": "Próg liniowy", + "linear-threshold": { + "label": "Próg liniowy" + }, "log-base": "Baza logów" }, "builder": { @@ -14694,4 +14696,4 @@ "label-points": "Punkty" } } -} \ No newline at end of file +} diff --git a/public/locales/pt-BR/grafana.json b/public/locales/pt-BR/grafana.json index c6b347e785f..efeee889595 100644 --- a/public/locales/pt-BR/grafana.json +++ b/public/locales/pt-BR/grafana.json @@ -8589,7 +8589,9 @@ "saving": "Salvando <1>" }, "axis-builder": { - "linear-threshold": "Limite linear", + "linear-threshold": { + "label": "Limite linear" + }, "log-base": "Base de log" }, "builder": { @@ -14580,4 +14582,4 @@ "label-points": "Pontos" } } -} \ No newline at end of file +} diff --git a/public/locales/pt-PT/grafana.json b/public/locales/pt-PT/grafana.json index 26db09f82dd..fe3265ca211 100644 --- a/public/locales/pt-PT/grafana.json +++ b/public/locales/pt-PT/grafana.json @@ -8589,7 +8589,9 @@ "saving": "A guardar <1>" }, "axis-builder": { - "linear-threshold": "Limite linear", + "linear-threshold": { + "label": "Limite linear" + }, "log-base": "Base de registo" }, "builder": { @@ -14580,4 +14582,4 @@ "label-points": "Pontos" } } -} \ No newline at end of file +} diff --git a/public/locales/ru-RU/grafana.json b/public/locales/ru-RU/grafana.json index dec4add29ca..e4f9fe26d4f 100644 --- a/public/locales/ru-RU/grafana.json +++ b/public/locales/ru-RU/grafana.json @@ -8661,7 +8661,9 @@ "saving": "Сохранение <1>" }, "axis-builder": { - "linear-threshold": "Линейный порог", + "linear-threshold": { + "label": "Линейный порог" + }, "log-base": "База журналов" }, "builder": { @@ -14694,4 +14696,4 @@ "label-points": "Точки" } } -} \ No newline at end of file +} diff --git a/public/locales/sv-SE/grafana.json b/public/locales/sv-SE/grafana.json index 65178bb1cc1..aee0dd73037 100644 --- a/public/locales/sv-SE/grafana.json +++ b/public/locales/sv-SE/grafana.json @@ -8589,7 +8589,9 @@ "saving": "Sparar <1>" }, "axis-builder": { - "linear-threshold": "Lineär tröskel", + "linear-threshold": { + "label": "Lineär tröskel" + }, "log-base": "Loggbas" }, "builder": { @@ -14580,4 +14582,4 @@ "label-points": "Poäng" } } -} \ No newline at end of file +} diff --git a/public/locales/tr-TR/grafana.json b/public/locales/tr-TR/grafana.json index 9674e80d4de..d70e3969044 100644 --- a/public/locales/tr-TR/grafana.json +++ b/public/locales/tr-TR/grafana.json @@ -8589,7 +8589,9 @@ "saving": "<1> kaydediliyor" }, "axis-builder": { - "linear-threshold": "Doğrusal eşik", + "linear-threshold": { + "label": "Doğrusal eşik" + }, "log-base": "Günlük tabanı" }, "builder": { @@ -14580,4 +14582,4 @@ "label-points": "Noktalar" } } -} \ No newline at end of file +} diff --git a/public/locales/zh-Hans/grafana.json b/public/locales/zh-Hans/grafana.json index d0302660f99..72c6cd12396 100644 --- a/public/locales/zh-Hans/grafana.json +++ b/public/locales/zh-Hans/grafana.json @@ -8553,7 +8553,9 @@ "saving": "正在保存<1>" }, "axis-builder": { - "linear-threshold": "线性阈值", + "linear-threshold": { + "label": "线性阈值" + }, "log-base": "记录基础" }, "builder": { @@ -14523,4 +14525,4 @@ "label-points": "点" } } -} \ No newline at end of file +} diff --git a/public/locales/zh-Hant/grafana.json b/public/locales/zh-Hant/grafana.json index ad6c511d1f8..46758e840b5 100644 --- a/public/locales/zh-Hant/grafana.json +++ b/public/locales/zh-Hant/grafana.json @@ -8553,7 +8553,9 @@ "saving": "正在儲存<1>" }, "axis-builder": { - "linear-threshold": "線性臨界值", + "linear-threshold": { + "label": "線性臨界值" + }, "log-base": "日誌基礎" }, "builder": { @@ -14523,4 +14525,4 @@ "label-points": "點" } } -} \ No newline at end of file +}