diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index 8a290e5b35b..07b58a28c91 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -88,7 +88,6 @@ Alpha features might be changed or removed without prior notice. | `showDashboardValidationWarnings` | Show warnings when dashboards do not validate against the schema | | `mysqlAnsiQuotes` | Use double quotes to escape keyword in a MySQL query | | `showTraceId` | Show trace ids for requests | -| `datasourceOnboarding` | Enable data source onboarding page | | `authnService` | Use new auth service to perform authentication | | `alertingBacktesting` | Rule backtesting API for alerting | | `editPanelCSVDragAndDrop` | Enables drag and drop for CSV and Excel files | diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index adab6ecb1eb..d957965e103 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -59,7 +59,6 @@ export interface FeatureToggles { nestedFolders?: boolean; accessTokenExpirationCheck?: boolean; showTraceId?: boolean; - datasourceOnboarding?: boolean; emptyDashboardPage?: boolean; authnService?: boolean; disablePrometheusExemplarSampling?: boolean; diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 36dab1f1e2d..36067a82c2e 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -277,12 +277,6 @@ var ( State: FeatureStateAlpha, Owner: grafanaObservabilityLogsSquad, }, - { - Name: "datasourceOnboarding", - Description: "Enable data source onboarding page", - State: FeatureStateAlpha, - Owner: grafanaDashboardsSquad, - }, { Name: "emptyDashboardPage", Description: "Enable the redesigned user interface of a dashboard page that includes no panels", diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index b735609fdda..6be0c87d088 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -40,7 +40,6 @@ accessControlOnCall,beta,@grafana/grafana-authnz-team,false,false,false,false nestedFolders,beta,@grafana/backend-platform,false,false,false,false accessTokenExpirationCheck,stable,@grafana/grafana-authnz-team,false,false,false,false showTraceId,alpha,@grafana/observability-logs,false,false,false,false -datasourceOnboarding,alpha,@grafana/dashboards-squad,false,false,false,false emptyDashboardPage,stable,@grafana/dashboards-squad,false,false,false,true authnService,alpha,@grafana/grafana-authnz-team,false,false,false,false disablePrometheusExemplarSampling,stable,@grafana/observability-metrics,false,false,false,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index 3c9ffae7143..1d1341e3f41 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -171,10 +171,6 @@ const ( // Show trace ids for requests FlagShowTraceId = "showTraceId" - // FlagDatasourceOnboarding - // Enable data source onboarding page - FlagDatasourceOnboarding = "datasourceOnboarding" - // FlagEmptyDashboardPage // Enable the redesigned user interface of a dashboard page that includes no panels FlagEmptyDashboardPage = "emptyDashboardPage" diff --git a/public/app/features/dashboard/containers/NewDashboardPage.tsx b/public/app/features/dashboard/containers/NewDashboardPage.tsx deleted file mode 100644 index f33fb8f5076..00000000000 --- a/public/app/features/dashboard/containers/NewDashboardPage.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { useState } from 'react'; - -import { config } from '@grafana/runtime'; -import { t } from 'app/core/internationalization'; -import { GrafanaRouteComponentProps } from 'app/core/navigation/types'; -import { EmptyStateNoDatasource } from 'app/features/datasources/components/EmptyStateNoDatasource'; -import { useLoadDataSources } from 'app/features/datasources/state'; -import { useSelector } from 'app/types'; - -import DashboardPage from './DashboardPage'; - -export default function NewDashboardPage(props: GrafanaRouteComponentProps) { - const { isLoading } = useLoadDataSources(); - - const { hasDatasource } = useSelector((state) => ({ - hasDatasource: state.dataSources.dataSourcesCount > 0, - })); - const [createDashboard, setCreateDashboard] = useState(false); - const showDashboardPage = hasDatasource || createDashboard || !config.featureToggles.datasourceOnboarding; - - return showDashboardPage ? ( - - ) : ( - setCreateDashboard(true)} - loading={isLoading} - title={t('datasource-onboarding.welcome', 'Welcome to Grafana dashboards!')} - CTAText={t('datasource-onboarding.sampleData', 'Or set up a new dashboard with sample data')} - navId="dashboards/browse" - pageNav={{ text: t('datasource-onboarding.new-dashboard', 'New dashboard'), url: '/dashboard/new' }} - /> - ); -} diff --git a/public/app/features/datasources/components/EmptyStateNoDatasource.test.tsx b/public/app/features/datasources/components/EmptyStateNoDatasource.test.tsx deleted file mode 100644 index ae0bf357126..00000000000 --- a/public/app/features/datasources/components/EmptyStateNoDatasource.test.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { fireEvent, render, screen, waitFor } from '@testing-library/react'; -import { rest } from 'msw'; -import { setupServer } from 'msw/node'; -import React from 'react'; -import { DeepPartial } from 'react-hook-form'; -import { TestProvider } from 'test/helpers/TestProvider'; - -import { DataSourcePluginMeta } from '@grafana/data'; -import * as runtime from '@grafana/runtime'; -import { contextSrv } from 'app/core/services/context_srv'; -import 'whatwg-fetch'; - -import { EmptyStateNoDatasource } from './EmptyStateNoDatasource'; - -let reportInteractionSpy: jest.SpyInstance; -const server = setupServer(); - -beforeEach(() => { - jest.spyOn(contextSrv, 'hasRole').mockReturnValue(true); - reportInteractionSpy = jest.spyOn(runtime, 'reportInteraction'); - server.resetHandlers(); -}); - -afterEach(() => { - jest.restoreAllMocks(); -}); - -beforeAll(() => { - server.listen({ onUnhandledRequest: 'bypass' }); -}); - -afterAll(() => { - server.close(); -}); - -describe('EmptyStateNoDatasource', () => { - it('correctly tracks user interactions', async () => { - server.use( - rest.get('/api/plugins', (_, res, ctx) => { - return res( - ctx.json>>([ - { id: 'prometheus', name: 'Prometheus', info: { logos: { small: 'prometheus.png' } } }, - { id: 'mysql', name: 'MySQL', info: { logos: { small: 'mysql.png' } } }, - { id: 'elasticsearch', name: 'Elasticsearch', info: { logos: { small: 'elasticsearch.png' } } }, - { id: 'influxdb', name: 'InfluxDB', info: { logos: { small: 'influxdb.png' } } }, - { id: 'graphite', name: 'Graphite', info: { logos: { small: 'graphite.png' } } }, - { id: 'stackdriver', name: 'StackDriver', info: { logos: { small: 'stackdriver.png' } } }, - { id: 'cloudwatch', name: 'CloudWatch', info: { logos: { small: 'cloudwatch.png' } } }, - { - id: 'grafana-azure-monitor-datasource', - name: 'Azure Monitor', - info: { logos: { small: 'grafana-azure-monitor-datasource.png' } }, - }, - ]) - ); - }) - ); - render( - - - - ); - - await waitFor(() => { - expect(screen.getByRole('heading', { name: 'A Title' })).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Prometheus' })).toBeInTheDocument(); - }); - - fireEvent.click(screen.getByRole('button', { name: 'Prometheus' })); - expect(reportInteractionSpy).toHaveBeenLastCalledWith('dashboards_connectds_ds_clicked'); - - fireEvent.click(screen.getByRole('link', { name: 'View all' })); - expect(reportInteractionSpy).toHaveBeenCalledWith('dashboards_connectds_viewall_clicked'); - - fireEvent.click(screen.getByRole('button', { name: 'CTA' })); - expect(reportInteractionSpy).toHaveBeenLastCalledWith('dashboards_connectds_sampledata_clicked'); - }); -}); diff --git a/public/app/features/datasources/components/EmptyStateNoDatasource.tsx b/public/app/features/datasources/components/EmptyStateNoDatasource.tsx deleted file mode 100644 index a0f4bbf5226..00000000000 --- a/public/app/features/datasources/components/EmptyStateNoDatasource.tsx +++ /dev/null @@ -1,215 +0,0 @@ -import { css } from '@emotion/css'; -import React, { ComponentProps } from 'react'; -import { useAsync } from 'react-use'; - -import { DataSourcePluginMeta, GrafanaTheme2, PageLayoutType } from '@grafana/data'; -import { reportInteraction } from '@grafana/runtime'; -import { Icon, useStyles2 } from '@grafana/ui'; -import { Page } from 'app/core/components/Page/Page'; -import { useGrafana } from 'app/core/context/GrafanaContext'; -import { contextSrv } from 'app/core/core'; -import { t } from 'app/core/internationalization'; -import { useAddDatasource } from 'app/features/datasources/state'; - -const topDatasources = [ - 'prometheus', - 'mysql', - 'elasticsearch', - 'influxdb', - 'graphite', - 'stackdriver', - 'cloudwatch', - 'grafana-azure-monitor-datasource', -]; - -interface Props extends Pick, 'navId' | 'pageNav'> { - title: string; - CTAText: string; - onCTAClick?: () => void; - loading?: boolean; -} - -export function EmptyStateNoDatasource({ onCTAClick, loading = false, title, CTAText, navId, pageNav }: Props) { - const styles = useStyles2(getStyles); - const { backend } = useGrafana(); - const { value: datasources, loading: loadingDatasources } = useAsync(async () => { - const datasourceMeta: DataSourcePluginMeta[] = await backend.get('/api/plugins', { - enabled: 1, - type: 'datasource', - }); - - const byId = datasourceMeta.reduce>((prev, cur) => { - prev[cur.id] = cur; - return prev; - }, {}); - - return topDatasources.map((d) => byId[d]); - }, []); - - const onAddDatasource = useAddDatasource(); - - if (loading) { - return null; - } - - return ( - -
-

{title}

-
-

- {t('datasource-onboarding.explanation', "To visualize your data, you'll need to connect it first.")} -

-
- {contextSrv.hasRole('Admin') ? ( - <> -

- {t('datasource-onboarding.preferred', 'Connect your preferred data source:')} -

- {!loadingDatasources && datasources !== undefined && ( - - )} - - ) : ( -

- {t('datasource-onboarding.contact-admin', 'Please contact your administrator to configure data sources.')} -

- )} - -
-
- ); -} - -function getStyles(theme: GrafanaTheme2) { - return { - wrapper: css({ - display: 'flex', - flexDirection: 'column', - flexGrow: 1, - alignItems: 'center', - justifyContent: 'center', - }), - title: css({ - textAlign: 'center', - fontSize: theme.typography.pxToRem(32), - fontWeight: theme.typography.fontWeightBold, - }), - description: css({ - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - gap: theme.spacing(1), - maxWidth: '50vw', - marginBottom: theme.spacing(8), - color: theme.colors.text.secondary, - }), - explanation: css({ - marginBottom: '0px', - textAlign: 'center', - fontSize: theme.typography.pxToRem(19), - }), - preferredDataSource: css({ - marginBottom: theme.spacing(3), - fontSize: theme.typography.pxToRem(19), - fontWeight: theme.typography.fontWeightRegular, - }), - datasources: css({ - display: 'grid', - gridTemplateColumns: `repeat(auto-fit, minmax(${theme.spacing(28)}, 1fr))`, - gap: theme.spacing(2), - listStyle: 'none', - width: '100%', - maxWidth: theme.spacing(88), - - '> li': { - display: 'flex', - alignItems: 'center', - '> button': { - display: 'flex', - alignItems: 'center', - width: '100%', - height: theme.spacing(7), - gap: theme.spacing(2), - margin: '0px', - padding: `calc(${theme.spacing(2)} - 1px)`, - lineHeight: theme.spacing(3), - border: `1px solid ${theme.colors.border.weak}`, - borderRadius: theme.shape.borderRadius(1), - background: theme.colors.background.primary, - fontSize: theme.typography.pxToRem(19), - color: 'inherit', - }, - }, - }), - logo: css({ - width: theme.spacing(2), - height: theme.spacing(2), - objectFit: 'contain', - }), - datasourceName: css({ - marginRight: 'auto', - }), - arrowIcon: css({ - color: theme.colors.text.link, - }), - viewAll: css({ - display: 'flex', - flexGrow: 1, - alignItems: 'center', - justifyContent: 'center', - padding: theme.spacing(2), - lineHeight: theme.spacing(3), - fontSize: theme.typography.pxToRem(19), - color: theme.colors.text.link, - textDecoration: 'underline', - textUnderlinePosition: 'under', - }), - ctaButton: css({ - display: 'flex', - alignItems: 'center', - gap: theme.spacing(1), - margin: '0px', - marginTop: theme.spacing(8), - padding: theme.spacing(0), - border: 'none', - background: 'inherit', - fontSize: theme.typography.h6.fontSize, - color: theme.colors.text.secondary, - }), - }; -} diff --git a/public/app/routes/routes.tsx b/public/app/routes/routes.tsx index e5f15e64f83..fd1b47580bb 100644 --- a/public/app/routes/routes.tsx +++ b/public/app/routes/routes.tsx @@ -50,7 +50,7 @@ export function getAppRoutes(): RouteDescriptor[] { pageClass: 'page-dashboard', routeName: DashboardRoutes.New, component: SafeDynamicImport( - () => import(/* webpackChunkName: "DashboardPage" */ '../features/dashboard/containers/NewDashboardPage') + () => import(/* webpackChunkName: "DashboardPage" */ '../features/dashboard/containers/DashboardPage') ), }, {