From 8eb25a01646693436b09d4fa90521320a40d02c0 Mon Sep 17 00:00:00 2001 From: Gareth Date: Tue, 9 Dec 2025 17:18:06 +0900 Subject: [PATCH 001/409] OpenTSDB: Support all query options in the backend (#114822) * update backend to support all query options * update backend tests * move formatDownsampleInterval to utils --- pkg/tsdb/opentsdb/opentsdb.go | 31 ++- pkg/tsdb/opentsdb/opentsdb_test.go | 201 ++++++++++++++++++ pkg/tsdb/opentsdb/types.go | 5 +- pkg/tsdb/opentsdb/utils.go | 31 +++ .../plugins/datasource/opentsdb/datasource.ts | 24 +++ 5 files changed, 287 insertions(+), 5 deletions(-) create mode 100644 pkg/tsdb/opentsdb/utils.go diff --git a/pkg/tsdb/opentsdb/opentsdb.go b/pkg/tsdb/opentsdb/opentsdb.go index 8b34b21cbaf..d00242f6432 100644 --- a/pkg/tsdb/opentsdb/opentsdb.go +++ b/pkg/tsdb/opentsdb/opentsdb.go @@ -62,6 +62,7 @@ type QueryModel struct { IsCounter bool `json:"isCounter"` CounterMax string `json:"counterMax"` CounterResetValue string `json:"counterResetValue"` + ExplicitTags bool `json:"explicitTags"` } func newInstanceSettings(httpClientProvider *httpclient.Provider) datasource.InstanceFactoryFunc { @@ -236,8 +237,19 @@ func createInitialFrame(val OpenTsdbCommon, length int, refID string) *data.Fram labels[label] = value } + tagKeys := make([]string, 0, len(val.Tags)+len(val.AggregateTags)) + for tagKey := range val.Tags { + tagKeys = append(tagKeys, tagKey) + } + sort.Strings(tagKeys) + tagKeys = append(tagKeys, val.AggregateTags...) + frame := data.NewFrameOfFieldTypes(val.Metric, length, data.FieldTypeTime, data.FieldTypeFloat64) - frame.Meta = &data.FrameMeta{Type: data.FrameTypeTimeSeriesMulti, TypeVersion: data.FrameTypeVersion{0, 1}} + frame.Meta = &data.FrameMeta{ + Type: data.FrameTypeTimeSeriesMulti, + TypeVersion: data.FrameTypeVersion{0, 1}, + Custom: map[string]any{"tagKeys": tagKeys}, + } frame.RefID = refID timeField := frame.Fields[0] timeField.Name = data.TimeSeriesTimeFieldName @@ -355,10 +367,19 @@ func (s *Service) buildMetric(query backend.DataQuery) map[string]any { if !model.DisableDownsampling { downsampleInterval := model.DownsampleInterval if downsampleInterval == "" { - downsampleInterval = "1m" // default value for blank + if ms := query.Interval.Milliseconds(); ms > 0 { + downsampleInterval = FormatDownsampleInterval(ms) + } else { + downsampleInterval = "1m" + } + } else if strings.Contains(downsampleInterval, ".") && strings.HasSuffix(downsampleInterval, "s") { + if val, err := strconv.ParseFloat(strings.TrimSuffix(downsampleInterval, "s"), 64); err == nil { + downsampleInterval = strconv.FormatInt(int64(val*1000), 10) + "ms" + } } + downsample := downsampleInterval + "-" + model.DownsampleAggregator - if model.DownsampleFillPolicy != "none" { + if model.DownsampleFillPolicy != "" && model.DownsampleFillPolicy != "none" { metric["downsample"] = downsample + "-" + model.DownsampleFillPolicy } else { metric["downsample"] = downsample @@ -408,6 +429,10 @@ func (s *Service) buildMetric(query backend.DataQuery) map[string]any { metric["filters"] = model.Filters } + if model.ExplicitTags { + metric["explicitTags"] = true + } + return metric } diff --git a/pkg/tsdb/opentsdb/opentsdb_test.go b/pkg/tsdb/opentsdb/opentsdb_test.go index b959e9efa26..a0150b9e75f 100644 --- a/pkg/tsdb/opentsdb/opentsdb_test.go +++ b/pkg/tsdb/opentsdb/opentsdb_test.go @@ -70,6 +70,164 @@ func TestCheckHealth(t *testing.T) { } } +func TestBuildMetric(t *testing.T) { + service := &Service{} + + t.Run("Metric with no downsampleInterval should use query interval", func(t *testing.T) { + query := backend.DataQuery{ + JSON: []byte(` + { + "metric": "cpu.average.percent", + "aggregator": "avg", + "disableDownsampling": false, + "downsampleInterval": "", + "downsampleAggregator": "avg", + "downsampleFillPolicy": "none" + }`, + ), + Interval: 30 * time.Second, + } + + metric := service.buildMetric(query) + require.Equal(t, "30s-avg", metric["downsample"], "should use query interval formatted as seconds") + }) + + t.Run("Metric with downsampleInterval converts decimal seconds to milliseconds", func(t *testing.T) { + query := backend.DataQuery{ + JSON: []byte(` + { + "metric": "cpu.average.percent", + "aggregator": "avg", + "disableDownsampling": false, + "downsampleInterval": "0.5s", + "downsampleAggregator": "avg", + "downsampleFillPolicy": "none" + }`, + ), + } + + metric := service.buildMetric(query) + require.Equal(t, "500ms-avg", metric["downsample"], "should convert 0.5s to 500ms") + }) + + t.Run("Metric with no downsampleInterval uses milliseconds for sub-second query interval", func(t *testing.T) { + query := backend.DataQuery{ + JSON: []byte(` + { + "metric": "cpu.average.percent", + "aggregator": "avg", + "disableDownsampling": false, + "downsampleInterval": "", + "downsampleAggregator": "avg", + "downsampleFillPolicy": "none" + }`, + ), + Interval: 500 * time.Millisecond, + } + + metric := service.buildMetric(query) + require.Equal(t, "500ms-avg", metric["downsample"], "should use query interval formatted as milliseconds") + }) + + t.Run("Metric with no downsampleInterval uses minutes for longer intervals", func(t *testing.T) { + query := backend.DataQuery{ + JSON: []byte(` + { + "metric": "cpu.average.percent", + "aggregator": "avg", + "disableDownsampling": false, + "downsampleInterval": "", + "downsampleAggregator": "sum", + "downsampleFillPolicy": "none" + }`, + ), + Interval: 5 * time.Minute, + } + + metric := service.buildMetric(query) + require.Equal(t, "5m-sum", metric["downsample"], "should use query interval formatted as minutes") + }) + + t.Run("Metric with no downsampleInterval uses hours for multi-hour intervals", func(t *testing.T) { + query := backend.DataQuery{ + JSON: []byte(` + { + "metric": "cpu.average.percent", + "aggregator": "avg", + "disableDownsampling": false, + "downsampleInterval": "", + "downsampleAggregator": "max", + "downsampleFillPolicy": "none" + }`, + ), + Interval: 2 * time.Hour, + } + + metric := service.buildMetric(query) + require.Equal(t, "2h-max", metric["downsample"], "should use query interval formatted as hours") + }) + + t.Run("Metric with no downsampleInterval uses days for multi-day intervals", func(t *testing.T) { + query := backend.DataQuery{ + JSON: []byte(` + { + "metric": "cpu.average.percent", + "aggregator": "avg", + "disableDownsampling": false, + "downsampleInterval": "", + "downsampleAggregator": "min", + "downsampleFillPolicy": "none" + }`, + ), + Interval: 48 * time.Hour, + } + + metric := service.buildMetric(query) + require.Equal(t, "2d-min", metric["downsample"], "should use query interval formatted as days") + }) + + t.Run("Build metric with explicitTags enabled", func(t *testing.T) { + query := backend.DataQuery{ + JSON: []byte(` + { + "metric": "cpu.average.percent", + "aggregator": "avg", + "disableDownsampling": true, + "explicitTags": true, + "tags": { + "host": "server01" + } + }`, + ), + } + + metric := service.buildMetric(query) + require.True(t, metric["explicitTags"].(bool), "explicitTags should be true") + + metricTags := metric["tags"].(map[string]any) + require.Equal(t, "server01", metricTags["host"]) + }) + + t.Run("Build metric with explicitTags disabled does not include explicitTags", func(t *testing.T) { + query := backend.DataQuery{ + JSON: []byte(` + { + "metric": "cpu.average.percent", + "aggregator": "avg", + "disableDownsampling": true, + "explicitTags": false, + "tags": { + "host": "server01" + } + }`, + ), + } + + metric := service.buildMetric(query) + require.Nil(t, metric["explicitTags"], "explicitTags should not be present when false") + }) +} + func TestOpenTsdbExecutor(t *testing.T) { service := &Service{} @@ -119,6 +277,7 @@ func TestOpenTsdbExecutor(t *testing.T) { testFrame.Meta = &data.FrameMeta{ Type: data.FrameTypeTimeSeriesMulti, TypeVersion: data.FrameTypeVersion{0, 1}, + Custom: map[string]any{"tagKeys": []string{"app", "env"}}, } testFrame.RefID = "A" tsdbVersion := float32(4) @@ -160,6 +319,7 @@ func TestOpenTsdbExecutor(t *testing.T) { testFrame.Meta = &data.FrameMeta{ Type: data.FrameTypeTimeSeriesMulti, TypeVersion: data.FrameTypeVersion{0, 1}, + Custom: map[string]any{"tagKeys": []string{"app", "env"}}, } testFrame.RefID = "A" tsdbVersion := float32(3) @@ -232,6 +392,7 @@ func TestOpenTsdbExecutor(t *testing.T) { testFrame.Meta = &data.FrameMeta{ Type: data.FrameTypeTimeSeriesMulti, TypeVersion: data.FrameTypeVersion{0, 1}, + Custom: map[string]any{"tagKeys": []string{"app", "env"}}, } testFrame.RefID = "A" tsdbVersion := float32(3) @@ -275,6 +436,7 @@ func TestOpenTsdbExecutor(t *testing.T) { testFrame.Meta = &data.FrameMeta{ Type: data.FrameTypeTimeSeriesMulti, TypeVersion: data.FrameTypeVersion{0, 1}, + Custom: map[string]any{"tagKeys": []string{"app", "env"}}, } testFrame.RefID = myRefid @@ -290,6 +452,45 @@ func TestOpenTsdbExecutor(t *testing.T) { } }) + t.Run("tagKeys are returned sorted alphabetically in frame metadata", func(t *testing.T) { + response := ` + [ + { + "metric": "cpu.usage", + "dps": [ + [1405544146, 75.5] + ], + "tags" : { + "zone": "us-east-1", + "host": "server01", + "app": "api", + "env": "production" + } + } + ]` + + tsdbVersion := float32(4) + + resp := http.Response{Body: io.NopCloser(strings.NewReader(response))} + resp.StatusCode = 200 + result, err := service.parseResponse(logger, &resp, "A", tsdbVersion) + require.NoError(t, err) + + frame := result.Responses["A"].Frames[0] + require.NotNil(t, frame.Meta, "frame metadata should not be nil") + require.NotNil(t, frame.Meta.Custom, "frame custom metadata should not be nil") + + customMeta, ok := frame.Meta.Custom.(map[string]any) + require.True(t, ok, "custom metadata should be a map") + + tagKeys, ok := customMeta["tagKeys"].([]string) + require.True(t, ok, "tagKeys should be present and be a string slice") + require.Len(t, tagKeys, 4, "should have 4 tag keys") + + expectedTagKeys := []string{"app", "env", "host", "zone"} + require.Equal(t, expectedTagKeys, tagKeys, "tagKeys should be sorted alphabetically") + }) + t.Run("Build metric with downsampling enabled", func(t *testing.T) { query := backend.DataQuery{ JSON: []byte(` diff --git a/pkg/tsdb/opentsdb/types.go b/pkg/tsdb/opentsdb/types.go index 19d2ba75197..89aed49baa8 100644 --- a/pkg/tsdb/opentsdb/types.go +++ b/pkg/tsdb/opentsdb/types.go @@ -7,8 +7,9 @@ type OpenTsdbQuery struct { } type OpenTsdbCommon struct { - Metric string `json:"metric"` - Tags map[string]string `json:"tags"` + Metric string `json:"metric"` + Tags map[string]string `json:"tags"` + AggregateTags []string `json:"aggregateTags"` } type OpenTsdbResponse struct { diff --git a/pkg/tsdb/opentsdb/utils.go b/pkg/tsdb/opentsdb/utils.go new file mode 100644 index 00000000000..ae57b3e787a --- /dev/null +++ b/pkg/tsdb/opentsdb/utils.go @@ -0,0 +1,31 @@ +package opentsdb + +import ( + "strconv" + "time" +) + +func FormatDownsampleInterval(ms int64) string { + duration := time.Duration(ms) * time.Millisecond + + seconds := int64(duration / time.Second) + if seconds < 60 { + if seconds < 1 { + return strconv.FormatInt(ms, 10) + "ms" + } + return strconv.FormatInt(seconds, 10) + "s" + } + + minutes := int64(duration / time.Minute) + if minutes < 60 { + return strconv.FormatInt(minutes, 10) + "m" + } + + hours := int64(duration / time.Hour) + if hours < 24 { + return strconv.FormatInt(hours, 10) + "h" + } + + days := int64(duration / (24 * time.Hour)) + return strconv.FormatInt(days, 10) + "d" +} diff --git a/public/app/plugins/datasource/opentsdb/datasource.ts b/public/app/plugins/datasource/opentsdb/datasource.ts index 14c24b5c34d..1413daacfc2 100644 --- a/public/app/plugins/datasource/opentsdb/datasource.ts +++ b/public/app/plugins/datasource/opentsdb/datasource.ts @@ -18,6 +18,7 @@ import { catchError, map } from 'rxjs/operators'; import { AnnotationEvent, + DataFrame, DataQueryRequest, DataQueryResponse, dateMath, @@ -78,6 +79,20 @@ export default class OpenTsDatasource extends DataSourceWithBackend): Observable { + if (config.featureToggles.opentsdbBackendMigration) { + const hasValidTargets = options.targets.some((target) => target.metric && !target.hide); + if (!hasValidTargets) { + return of({ data: [] }); + } + + return super.query(options).pipe( + map((response) => { + this._saveTagKeysFromFrames(response.data); + return response; + }) + ); + } + // migrate annotations if (options.targets.some((target: OpenTsdbQuery) => target.fromAnnotations)) { const streams: Array> = []; @@ -265,6 +280,15 @@ export default class OpenTsDatasource extends DataSourceWithBackend { From de42ff2f759aff64fa1a53bf2ba29acce1871cca Mon Sep 17 00:00:00 2001 From: Victor Marin Date: Tue, 9 Dec 2025 10:24:05 +0200 Subject: [PATCH 002/409] Dashboards: Fix versions tab not showing in dashboard settings after making dashboard editable (#114963) * fix showing versions tab on dashboard settings after making dashboard editable * Update public/app/features/dashboard-scene/scene/NavToolbarActions.tsx Co-authored-by: Marc M. <146180665+grafakus@users.noreply.github.com> * Update public/app/features/dashboard-scene/scene/new-toolbar/actions/MakeDashboardEditableButton.tsx Co-authored-by: Marc M. <146180665+grafakus@users.noreply.github.com> --------- Co-authored-by: Marc M. <146180665+grafakus@users.noreply.github.com> --- .../scene/NavToolbarActions.test.tsx | 14 +++++++++++++- .../dashboard-scene/scene/NavToolbarActions.tsx | 2 +- .../actions/MakeDashboardEditableButton.test.tsx | 16 ++++++++++++++-- .../actions/MakeDashboardEditableButton.tsx | 2 +- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/public/app/features/dashboard-scene/scene/NavToolbarActions.test.tsx b/public/app/features/dashboard-scene/scene/NavToolbarActions.test.tsx index e276bf80e46..d40fcfea59f 100644 --- a/public/app/features/dashboard-scene/scene/NavToolbarActions.test.tsx +++ b/public/app/features/dashboard-scene/scene/NavToolbarActions.test.tsx @@ -186,6 +186,17 @@ describe('NavToolbarActions', () => { }); }); }); + + describe('where dashboard is not editable', () => { + it('should set dashboard to editable on make editable button press', async () => { + const { dashboard } = setup({}, true); + await userEvent.click(await screen.findByTestId(selectors.components.NavToolbar.editDashboard.editButton)); + + expect(dashboard.state.editable).toBe(true); + expect(dashboard.state.meta.canEdit).toBe(true); + expect(dashboard.state.meta.canSave).toBe(true); + }); + }); }); describe('Given new sharing button', () => { @@ -214,7 +225,7 @@ describe('NavToolbarActions', () => { }); }); -function setup(meta?: DashboardMeta) { +function setup(meta?: DashboardMeta, editable?: boolean) { const dashboard = new DashboardScene({ $timeRange: new SceneTimeRange({ from: 'now-6h', to: 'now' }), meta: { @@ -229,6 +240,7 @@ function setup(meta?: DashboardMeta) { ...meta, }, title: 'hello', + editable: editable || true, uid: 'dash-1', body: DefaultGridLayoutManager.fromVizPanels([ new VizPanel({ diff --git a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx index 959913c8f8f..eb063b47090 100644 --- a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx +++ b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx @@ -351,7 +351,7 @@ export function ToolbarActions({ dashboard }: Props) { onClick={() => { trackDashboardSceneEditButtonClicked(dashboard.state.uid); dashboard.onEnterEditMode(); - dashboard.setState({ editable: true, meta: { ...meta, canEdit: true } }); + dashboard.setState({ meta: { ...meta, canEdit: true, canSave: true } }); }} tooltip={t('dashboard.toolbar.enter-edit-mode.tooltip', 'This dashboard was marked as read only')} key="edit" diff --git a/public/app/features/dashboard-scene/scene/new-toolbar/actions/MakeDashboardEditableButton.test.tsx b/public/app/features/dashboard-scene/scene/new-toolbar/actions/MakeDashboardEditableButton.test.tsx index 87788a2b187..02a635cf576 100644 --- a/public/app/features/dashboard-scene/scene/new-toolbar/actions/MakeDashboardEditableButton.test.tsx +++ b/public/app/features/dashboard-scene/scene/new-toolbar/actions/MakeDashboardEditableButton.test.tsx @@ -34,10 +34,11 @@ setPluginImportUtils({ getPanelPluginFromCache: (id: string) => undefined, }); -export function buildTestScene(isEditing = false) { +export function buildTestScene(isEditing?: boolean, editable?: boolean) { const testScene = new DashboardScene({ $timeRange: new SceneTimeRange({ from: 'now-6h', to: 'now' }), - isEditing: isEditing, + isEditing: isEditing || false, + editable: editable || true, body: new DefaultGridLayoutManager({ grid: new SceneGridLayout({ children: [new DashboardGridItem({ body: new VizPanel({ key: 'panel-1', pluginId: 'text' }) })], @@ -76,4 +77,15 @@ describe('MakeDashboardEditableButton', () => { expect(DashboardInteractions.editButtonClicked).toHaveBeenCalledWith({ outlineExpanded: false }); }); }); + + it('should set state correctly', async () => { + const scene = buildTestScene(false, false); + + render(); + await userEvent.click(await screen.findByTestId(selectors.components.NavToolbar.editDashboard.editButton)); + + expect(scene.state.editable).toBe(true); + expect(scene.state.meta.canEdit).toBe(true); + expect(scene.state.meta.canSave).toBe(true); + }); }); diff --git a/public/app/features/dashboard-scene/scene/new-toolbar/actions/MakeDashboardEditableButton.tsx b/public/app/features/dashboard-scene/scene/new-toolbar/actions/MakeDashboardEditableButton.tsx index 3e6386d46e7..b93aad22034 100644 --- a/public/app/features/dashboard-scene/scene/new-toolbar/actions/MakeDashboardEditableButton.tsx +++ b/public/app/features/dashboard-scene/scene/new-toolbar/actions/MakeDashboardEditableButton.tsx @@ -13,7 +13,7 @@ export const MakeDashboardEditableButton = ({ dashboard }: ToolbarActionProps) = onClick={() => { trackDashboardSceneEditButtonClicked(dashboard.state.uid); dashboard.onEnterEditMode(); - dashboard.setState({ editable: true, meta: { ...dashboard.state.meta, canEdit: true } }); + dashboard.setState({ meta: { ...dashboard.state.meta, canEdit: true, canSave: true } }); }} tooltip={t('dashboard.toolbar.new.enter-edit-mode.tooltip', 'This dashboard was marked as read only')} variant="secondary" From ca342afb255271491fb454973164d38a94e83fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 9 Dec 2025 09:59:40 +0100 Subject: [PATCH 003/409] AppChrome: Add proper menu icon for menu, logo icon becomes home (#114713) * AppChrome: Add proper menu icon for menu, logo icon becomes home * Update * Update * fix merge issue * Aligning icons * Simplify styling and fix issues * fixes * style fix * Fixed unit test * review updates * update * Update * Remove feature highlight * fix lint * remove unused parts --- .../prometheus-variable-editor.spec.ts | 2 +- .../ToolbarButton/ToolbarButton.tsx | 2 +- .../components/AppChrome/AppChromeService.tsx | 2 +- .../AppChrome/MegaMenu/FeatureHighlight.tsx | 34 ----------- .../AppChrome/MegaMenu/MegaMenu.tsx | 8 +-- .../AppChrome/MegaMenu/MegaMenuHeader.tsx | 39 ++++--------- .../AppChrome/MegaMenu/MegaMenuItem.tsx | 26 +++------ .../AppChrome/MegaMenu/MegaMenuItemText.tsx | 10 ++-- .../AppChrome/TopBar/SingleTopBar.tsx | 6 +- .../app/core/components/Branding/Branding.tsx | 56 ++++++++++++++++++- .../core/components/Breadcrumbs/utils.test.ts | 10 +--- .../app/core/components/Breadcrumbs/utils.ts | 10 +--- public/app/core/components/Page/Page.test.tsx | 1 + .../app/core/components/Page/usePageTitle.ts | 10 +++- .../containers/DashboardPage.test.tsx | 6 +- 15 files changed, 100 insertions(+), 122 deletions(-) delete mode 100644 public/app/core/components/AppChrome/MegaMenu/FeatureHighlight.tsx diff --git a/e2e-playwright/various-suite/prometheus-variable-editor.spec.ts b/e2e-playwright/various-suite/prometheus-variable-editor.spec.ts index 16a722854aa..82b293da581 100644 --- a/e2e-playwright/various-suite/prometheus-variable-editor.spec.ts +++ b/e2e-playwright/various-suite/prometheus-variable-editor.spec.ts @@ -95,7 +95,7 @@ test.describe( await createNewPanelButton.click(); // Close the data source picker modal - const closeButton = page.getByRole('button', { name: 'Close menu' }); + const closeButton = page.getByRole('button', { name: 'Close', exact: true }); await closeButton.click({ force: true }); // Select prom data source from the data source list diff --git a/packages/grafana-ui/src/components/ToolbarButton/ToolbarButton.tsx b/packages/grafana-ui/src/components/ToolbarButton/ToolbarButton.tsx index c1f99b0db93..2193103a90f 100644 --- a/packages/grafana-ui/src/components/ToolbarButton/ToolbarButton.tsx +++ b/packages/grafana-ui/src/components/ToolbarButton/ToolbarButton.tsx @@ -206,7 +206,7 @@ const getStyles = (theme: GrafanaTheme2) => { background: 'transparent', border: `1px solid transparent`, - '&:hover, &:focus': { + '&:hover': { color: theme.colors.text.primary, background: theme.colors.action.hover, }, diff --git a/public/app/core/components/AppChrome/AppChromeService.tsx b/public/app/core/components/AppChrome/AppChromeService.tsx index f3ab1b63c1b..e340e8276b8 100644 --- a/public/app/core/components/AppChrome/AppChromeService.tsx +++ b/public/app/core/components/AppChrome/AppChromeService.tsx @@ -119,7 +119,7 @@ export class AppChromeService { }; private getUpdatedHistory(newState: AppChromeState): HistoryEntry[] { - const breadcrumbs = buildBreadcrumbs(newState.sectionNav.node, newState.pageNav, { text: 'Home', url: '/' }, true); + const breadcrumbs = buildBreadcrumbs(newState.sectionNav.node, newState.pageNav, { text: 'Home', url: '/' }); const newPageNav = newState.pageNav || newState.sectionNav.node; let entries = store.getObject(HISTORY_LOCAL_STORAGE_KEY, []); diff --git a/public/app/core/components/AppChrome/MegaMenu/FeatureHighlight.tsx b/public/app/core/components/AppChrome/MegaMenu/FeatureHighlight.tsx deleted file mode 100644 index ca660e8750c..00000000000 --- a/public/app/core/components/AppChrome/MegaMenu/FeatureHighlight.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { css } from '@emotion/css'; -import type { JSX } from 'react'; - -import { GrafanaTheme2 } from '@grafana/data'; -import { useStyles2 } from '@grafana/ui'; - -export interface Props { - children: JSX.Element; -} - -export const FeatureHighlight = ({ children }: Props): JSX.Element => { - const styles = useStyles2(getStyles); - return ( - <> - {children} - - - ); -}; - -const getStyles = (theme: GrafanaTheme2) => { - return { - highlight: css({ - backgroundColor: theme.colors.success.main, - borderRadius: theme.shape.radius.circle, - width: '6px', - height: '6px', - display: 'inline-block;', - position: 'absolute', - top: '50%', - transform: 'translateY(-50%)', - }), - }; -}; diff --git a/public/app/core/components/AppChrome/MegaMenu/MegaMenu.tsx b/public/app/core/components/AppChrome/MegaMenu/MegaMenu.tsx index dead4bd810e..f4ec7598fa2 100644 --- a/public/app/core/components/AppChrome/MegaMenu/MegaMenu.tsx +++ b/public/app/core/components/AppChrome/MegaMenu/MegaMenu.tsx @@ -62,10 +62,6 @@ export const MegaMenu = memo( const activeItem = getActiveItem(navItems, state.sectionNav.node, location.pathname); - const handleMegaMenu = () => { - chrome.setMegaMenuOpen(!state.megaMenuOpen); - }; - const handleDockedMenu = () => { chrome.setMegaMenuDocked(!state.megaMenuDocked); if (state.megaMenuDocked) { @@ -108,7 +104,7 @@ export const MegaMenu = memo( return (
- +