From 048d164a3c2dc563d1c5e79d906650ebc72a7ffb Mon Sep 17 00:00:00 2001 From: Andres Martinez Gotor Date: Mon, 14 Apr 2025 15:40:16 +0200 Subject: [PATCH] Advisor: Add redirection notice in data sources and plugins pages (#103894) --- .../AdvisorRedirectNotice.test.tsx | 74 +++++++++++++++++++ .../AdvisorRedirectNotice.tsx | 69 +++++++++++++++++ .../connections/pages/DataSourcesListPage.tsx | 2 + .../features/plugins/admin/pages/Browse.tsx | 2 + public/locales/cs-CZ/grafana.json | 5 ++ public/locales/de-DE/grafana.json | 5 ++ public/locales/en-US/grafana.json | 5 ++ public/locales/es-ES/grafana.json | 5 ++ public/locales/fr-FR/grafana.json | 5 ++ public/locales/hu-HU/grafana.json | 5 ++ public/locales/id-ID/grafana.json | 5 ++ public/locales/it-IT/grafana.json | 5 ++ public/locales/ja-JP/grafana.json | 5 ++ public/locales/ko-KR/grafana.json | 5 ++ public/locales/nl-NL/grafana.json | 5 ++ public/locales/pl-PL/grafana.json | 5 ++ public/locales/pt-BR/grafana.json | 5 ++ public/locales/pt-PT/grafana.json | 5 ++ public/locales/ru-RU/grafana.json | 5 ++ public/locales/sv-SE/grafana.json | 5 ++ public/locales/tr-TR/grafana.json | 5 ++ public/locales/zh-Hans/grafana.json | 5 ++ public/locales/zh-Hant/grafana.json | 5 ++ 23 files changed, 242 insertions(+) create mode 100644 public/app/features/connections/components/AdvisorRedirectNotice/AdvisorRedirectNotice.test.tsx create mode 100644 public/app/features/connections/components/AdvisorRedirectNotice/AdvisorRedirectNotice.tsx diff --git a/public/app/features/connections/components/AdvisorRedirectNotice/AdvisorRedirectNotice.test.tsx b/public/app/features/connections/components/AdvisorRedirectNotice/AdvisorRedirectNotice.test.tsx new file mode 100644 index 00000000000..f6b530d54c8 --- /dev/null +++ b/public/app/features/connections/components/AdvisorRedirectNotice/AdvisorRedirectNotice.test.tsx @@ -0,0 +1,74 @@ +import { render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { config } from '@grafana/runtime'; +import { contextSrv } from 'app/core/core'; + +import { AdvisorRedirectNotice } from './AdvisorRedirectNotice'; + +const originalFeatureToggleValue = config.featureToggles.grafanaAdvisor; +jest.mock('@grafana/runtime/internal', () => ({ + UserStorage: jest.fn().mockImplementation(() => ({ + getItem: jest.fn().mockResolvedValue('true'), + setItem: jest.fn().mockResolvedValue(undefined), + })), +})); + +describe('AdvisorRedirectNotice', () => { + beforeEach(() => { + config.featureToggles.grafanaAdvisor = true; + contextSrv.isGrafanaAdmin = true; + }); + + afterEach(() => { + jest.clearAllMocks(); + config.featureToggles.grafanaAdvisor = originalFeatureToggleValue; + }); + + it('should not render when user is not admin', async () => { + contextSrv.isGrafanaAdmin = false; + render(); + expect(screen.queryByRole('status')).not.toBeInTheDocument(); + }); + + it('should not render when feature flag is disabled', async () => { + config.featureToggles.grafanaAdvisor = false; + render(); + expect(screen.queryByRole('status')).not.toBeInTheDocument(); + }); + it('should render notice with correct content', async () => { + render(); + + await waitFor(() => { + expect(screen.getByRole('status')).toBeInTheDocument(); + expect( + screen.getByText(/Try the new Advisor to uncover potential issues with your data sources and plugins./i) + ).toBeInTheDocument(); + expect(screen.getByText(/Go to Advisor/i)).toBeInTheDocument(); + }); + }); + + it('should dismiss notice when close button is clicked', async () => { + const { rerender } = render(); + + await waitFor(() => { + expect( + screen.getByText(/Try the new Advisor to uncover potential issues with your data sources and plugins./i) + ).toBeInTheDocument(); + }); + + const closeButton = screen.getByRole('button', { name: /close alert/i }); + + await userEvent.click(closeButton); + + await waitFor(() => { + expect( + screen.queryByText(/Try the new Advisor to uncover potential issues with your data sources and plugins./i) + ).not.toBeInTheDocument(); + }); + + // Re-render the component and check that the notice is not rendered + rerender(); + expect(screen.queryByRole('status')).not.toBeInTheDocument(); + }); +}); diff --git a/public/app/features/connections/components/AdvisorRedirectNotice/AdvisorRedirectNotice.tsx b/public/app/features/connections/components/AdvisorRedirectNotice/AdvisorRedirectNotice.tsx new file mode 100644 index 00000000000..b83c864d26c --- /dev/null +++ b/public/app/features/connections/components/AdvisorRedirectNotice/AdvisorRedirectNotice.tsx @@ -0,0 +1,69 @@ +import { css } from '@emotion/css'; +import { useEffect, useState } from 'react'; + +import { GrafanaTheme2 } from '@grafana/data'; +import { config } from '@grafana/runtime'; +import { UserStorage } from '@grafana/runtime/internal'; +import { Alert, LinkButton, useStyles2 } from '@grafana/ui'; +import { contextSrv } from 'app/core/core'; +import { t, Trans } from 'app/core/internationalization'; + +const getStyles = (theme: GrafanaTheme2) => ({ + alertContent: css({ + display: 'flex', + flexDirection: 'row', + padding: 0, + justifyContent: 'space-between', + alignItems: 'center', + }), + alertParagraph: css({ + margin: theme.spacing(0, 1, 0, 0), + lineHeight: theme.spacing(theme.components.height.sm), + }), +}); +const userStorage = new UserStorage('advisor-redirect-notice'); + +export function AdvisorRedirectNotice() { + const styles = useStyles2(getStyles); + const hasAdminRights = contextSrv.hasRole('Admin') || contextSrv.isGrafanaAdmin; + const canUseAdvisor = hasAdminRights && config.featureToggles.grafanaAdvisor; + const [showNotice, setShowNotice] = useState(false); + useEffect(() => { + if (canUseAdvisor) { + userStorage.getItem('showNotice').then((showNotice) => { + if (showNotice !== 'false') { + setShowNotice(true); + } + }); + } + }, [canUseAdvisor]); + + return showNotice ? ( + { + userStorage.setItem('showNotice', 'false'); + setShowNotice(false); + }} + > +
+

+ + Try the new Advisor to uncover potential issues with your data sources and plugins. + +

+ + Go to Advisor + +
+
+ ) : ( + <> + ); +} diff --git a/public/app/features/connections/pages/DataSourcesListPage.tsx b/public/app/features/connections/pages/DataSourcesListPage.tsx index 29f24b7e548..7a794ae3b52 100644 --- a/public/app/features/connections/pages/DataSourcesListPage.tsx +++ b/public/app/features/connections/pages/DataSourcesListPage.tsx @@ -1,4 +1,5 @@ import { Page } from 'app/core/components/Page/Page'; +import { AdvisorRedirectNotice } from 'app/features/connections/components/AdvisorRedirectNotice/AdvisorRedirectNotice'; import { DataSourceAddButton } from 'app/features/datasources/components/DataSourceAddButton'; import { DataSourcesList } from 'app/features/datasources/components/DataSourcesList'; import { getDataSourcesCount } from 'app/features/datasources/state'; @@ -11,6 +12,7 @@ export function DataSourcesListPage() { return ( + diff --git a/public/app/features/plugins/admin/pages/Browse.tsx b/public/app/features/plugins/admin/pages/Browse.tsx index 38b9447cf0c..f28fbf04331 100644 --- a/public/app/features/plugins/admin/pages/Browse.tsx +++ b/public/app/features/plugins/admin/pages/Browse.tsx @@ -8,6 +8,7 @@ import { Select, RadioButtonGroup, useStyles2, Tooltip, Field, TextLink } from ' import { Page } from 'app/core/components/Page/Page'; import { t, Trans } from 'app/core/internationalization'; import { getNavModel } from 'app/core/selectors/navModel'; +import { AdvisorRedirectNotice } from 'app/features/connections/components/AdvisorRedirectNotice/AdvisorRedirectNotice'; import { ROUTES as CONNECTIONS_ROUTES } from 'app/features/connections/constants'; import { useSelector } from 'app/types'; @@ -94,6 +95,7 @@ export default function Browse() { return ( + diff --git a/public/locales/cs-CZ/grafana.json b/public/locales/cs-CZ/grafana.json index 9a3be9433d8..7853e231d69 100644 --- a/public/locales/cs-CZ/grafana.json +++ b/public/locales/cs-CZ/grafana.json @@ -2461,6 +2461,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "", diff --git a/public/locales/de-DE/grafana.json b/public/locales/de-DE/grafana.json index 68f04d561bc..176ef2e7439 100644 --- a/public/locales/de-DE/grafana.json +++ b/public/locales/de-DE/grafana.json @@ -2439,6 +2439,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "", diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 81e27ce8e1f..ef32abaf262 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -2428,6 +2428,11 @@ } }, "connections": { + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "Link to Advisor", + "body": "Try the new Advisor to uncover potential issues with your data sources and plugins.", + "go-to-advisor": "Go to Advisor" + }, "connect-data": { "category-header-label": "Data sources", "empty-message": "No results matching your query were found", diff --git a/public/locales/es-ES/grafana.json b/public/locales/es-ES/grafana.json index fa0d8edff9f..19c3af352b4 100644 --- a/public/locales/es-ES/grafana.json +++ b/public/locales/es-ES/grafana.json @@ -2439,6 +2439,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "", diff --git a/public/locales/fr-FR/grafana.json b/public/locales/fr-FR/grafana.json index aa6527cbe8b..4e56096b718 100644 --- a/public/locales/fr-FR/grafana.json +++ b/public/locales/fr-FR/grafana.json @@ -2439,6 +2439,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "", diff --git a/public/locales/hu-HU/grafana.json b/public/locales/hu-HU/grafana.json index 189bba03083..53fb35fc5bf 100644 --- a/public/locales/hu-HU/grafana.json +++ b/public/locales/hu-HU/grafana.json @@ -2439,6 +2439,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "", diff --git a/public/locales/id-ID/grafana.json b/public/locales/id-ID/grafana.json index a6675d86cd9..4ef5da6fd26 100644 --- a/public/locales/id-ID/grafana.json +++ b/public/locales/id-ID/grafana.json @@ -2428,6 +2428,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "", diff --git a/public/locales/it-IT/grafana.json b/public/locales/it-IT/grafana.json index 59cc05e04b4..dd01ba3a4b4 100644 --- a/public/locales/it-IT/grafana.json +++ b/public/locales/it-IT/grafana.json @@ -2439,6 +2439,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "", diff --git a/public/locales/ja-JP/grafana.json b/public/locales/ja-JP/grafana.json index 4424bc9e379..da468a92cb4 100644 --- a/public/locales/ja-JP/grafana.json +++ b/public/locales/ja-JP/grafana.json @@ -2428,6 +2428,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "", diff --git a/public/locales/ko-KR/grafana.json b/public/locales/ko-KR/grafana.json index f5c14460621..f1ee5195336 100644 --- a/public/locales/ko-KR/grafana.json +++ b/public/locales/ko-KR/grafana.json @@ -2428,6 +2428,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "", diff --git a/public/locales/nl-NL/grafana.json b/public/locales/nl-NL/grafana.json index b5558c64718..d426e9481a7 100644 --- a/public/locales/nl-NL/grafana.json +++ b/public/locales/nl-NL/grafana.json @@ -2439,6 +2439,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "", diff --git a/public/locales/pl-PL/grafana.json b/public/locales/pl-PL/grafana.json index 10e6dfbe6e2..3607101cbbb 100644 --- a/public/locales/pl-PL/grafana.json +++ b/public/locales/pl-PL/grafana.json @@ -2461,6 +2461,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "", diff --git a/public/locales/pt-BR/grafana.json b/public/locales/pt-BR/grafana.json index c740464450e..bc26c67789c 100644 --- a/public/locales/pt-BR/grafana.json +++ b/public/locales/pt-BR/grafana.json @@ -2439,6 +2439,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "", diff --git a/public/locales/pt-PT/grafana.json b/public/locales/pt-PT/grafana.json index 81377d0a25e..73c4f0ae7b4 100644 --- a/public/locales/pt-PT/grafana.json +++ b/public/locales/pt-PT/grafana.json @@ -2439,6 +2439,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "", diff --git a/public/locales/ru-RU/grafana.json b/public/locales/ru-RU/grafana.json index 9cef5305f76..e7eb6cf837a 100644 --- a/public/locales/ru-RU/grafana.json +++ b/public/locales/ru-RU/grafana.json @@ -2461,6 +2461,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "", diff --git a/public/locales/sv-SE/grafana.json b/public/locales/sv-SE/grafana.json index 4ac5f942e3a..ba8ba39e500 100644 --- a/public/locales/sv-SE/grafana.json +++ b/public/locales/sv-SE/grafana.json @@ -2439,6 +2439,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "", diff --git a/public/locales/tr-TR/grafana.json b/public/locales/tr-TR/grafana.json index 582d189bd10..babe29122b3 100644 --- a/public/locales/tr-TR/grafana.json +++ b/public/locales/tr-TR/grafana.json @@ -2439,6 +2439,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "", diff --git a/public/locales/zh-Hans/grafana.json b/public/locales/zh-Hans/grafana.json index c6062637472..cb9b7f7a56e 100644 --- a/public/locales/zh-Hans/grafana.json +++ b/public/locales/zh-Hans/grafana.json @@ -2428,6 +2428,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "", diff --git a/public/locales/zh-Hant/grafana.json b/public/locales/zh-Hant/grafana.json index a3050722954..7fde3421ece 100644 --- a/public/locales/zh-Hant/grafana.json +++ b/public/locales/zh-Hant/grafana.json @@ -2428,6 +2428,11 @@ "body": "", "go-to-connections": "" }, + "advisor-redirect-notice": { + "aria-label-link-to-advisor": "", + "body": "", + "go-to-advisor": "" + }, "no-access-modal": { "connection-contact-grafana-admin": "", "editor-warning": "",