Explore: Use Datasource Onboarding page when visiting without any datasource set up (#60399)

* allow DatasourceOnboarding title and cta text customization

* Explore: use Datasource Onboarding page when visiting without any datasource set up

* move & rename DatasourceOnboarding

* Rename component
This commit is contained in:
Giordano Ricci 2022-12-29 15:38:40 +00:00 committed by GitHub
parent 15d32546ea
commit 9ff3bf4849
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 71 additions and 31 deletions

View File

@ -3771,9 +3771,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "7"],
[0, 0, 0, "Do not use any type assertions.", "8"]
],
"public/app/features/explore/Wrapper.test.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/features/explore/spec/helper/setup.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],

View File

@ -2,12 +2,13 @@ import React, { useState } from 'react';
import { useEffectOnce } from 'react-use';
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 { loadDataSources } from 'app/features/datasources/state';
import { useDispatch, useSelector } from 'app/types';
import DashboardPage from './DashboardPage';
import { DatasourceOnboarding } from './DatasourceOnboarding';
export default function NewDashboardPage(props: GrafanaRouteComponentProps) {
const dispatch = useDispatch();
@ -25,6 +26,13 @@ export default function NewDashboardPage(props: GrafanaRouteComponentProps) {
return showDashboardPage ? (
<DashboardPage {...props} />
) : (
<DatasourceOnboarding onNewDashboard={() => setCreateDashboard(true)} loading={loading} />
<EmptyStateNoDatasource
onCTAClick={() => setCreateDashboard(true)}
loading={loading}
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('dashboard', 'New dashboard'), url: '/dashboard/new' }}
/>
);
}

View File

@ -1,5 +1,5 @@
import { css } from '@emotion/css';
import React from 'react';
import React, { ComponentProps } from 'react';
import { useAsync } from 'react-use';
import { DataSourcePluginMeta, GrafanaTheme2, PageLayoutType } from '@grafana/data';
@ -21,13 +21,14 @@ const topDatasources = [
'grafana-azure-monitor-datasource',
];
export function DatasourceOnboarding({
onNewDashboard,
loading = false,
}: {
onNewDashboard?: () => void;
interface Props extends Pick<ComponentProps<typeof Page>, '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 { value: datasources, loading: loadingDatasources } = useAsync(async () => {
const datasourceMeta: DataSourcePluginMeta[] = await getBackendSrv().get('/api/plugins', {
@ -50,13 +51,9 @@ export function DatasourceOnboarding({
}
return (
<Page
navId="dashboards/browse"
pageNav={{ text: t('dashboard', 'New dashboard'), url: '/dashboard/new' }}
layout={PageLayoutType.Canvas}
>
<Page navId={navId} pageNav={pageNav} layout={PageLayoutType.Canvas}>
<div className={styles.wrapper}>
<h1 className={styles.title}>{t('datasource-onboarding.welcome', 'Welcome to Grafana dashboards!')}</h1>
<h1 className={styles.title}>{title}</h1>
<div className={styles.description}>
<h4 className={styles.explanation}>
{t('datasource-onboarding.explanation', "To visualize your data, you'll need to connect it first.")}
@ -100,8 +97,8 @@ export function DatasourceOnboarding({
{t('datasource-onboarding.contact-admin', 'Please contact your administrator to configure data sources.')}
</h4>
)}
<button onClick={onNewDashboard} className={styles.createNew}>
<span>{t('datasource-onboarding.sampleData', 'Or set up a new dashboard with sample data')}</span>
<button onClick={onCTAClick} className={styles.ctaButton}>
<span>{CTAText}</span>
<Icon name="arrow-right" size="lg" />
</button>
</div>
@ -193,7 +190,7 @@ function getStyles(theme: GrafanaTheme2) {
textDecoration: 'underline',
textUnderlinePosition: 'under',
}),
createNew: css({
ctaButton: css({
display: 'flex',
alignItems: 'center',
gap: theme.spacing(1),

View File

@ -0,0 +1,39 @@
import React, { useState } from 'react';
import { useEffectOnce } from 'react-use';
import { config } from '@grafana/runtime';
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
import { EmptyStateNoDatasource } from 'app/features/datasources/components/EmptyStateNoDatasource';
import { ExploreQueryParams, useDispatch, useSelector } from 'app/types';
import { loadDataSources } from '../datasources/state';
import { ExplorePage } from './ExplorePage';
export default function EmptyStateWrapper(props: GrafanaRouteComponentProps<{}, ExploreQueryParams>) {
const dispatch = useDispatch();
useEffectOnce(() => {
if (config.featureToggles.datasourceOnboarding) {
dispatch(loadDataSources());
}
});
const { hasDatasource, loading } = useSelector((state) => ({
hasDatasource: state.dataSources.dataSourcesCount > 0,
loading: !state.dataSources.hasFetched,
}));
const [showOnboarding, setShowOnboarding] = useState(config.featureToggles.datasourceOnboarding);
const showExplorePage = hasDatasource || !showOnboarding;
return showExplorePage ? (
<ExplorePage {...props} />
) : (
<EmptyStateNoDatasource
onCTAClick={() => setShowOnboarding(false)}
loading={loading}
title="Welcome to Grafana Explore!"
CTAText="Or explore sample data"
navId="explore"
/>
);
}

View File

@ -1,6 +1,7 @@
import { fireEvent, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import React, { ComponentProps } from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';
import { serializeStateToUrlParam } from '@grafana/data';
import { locationService, config } from '@grafana/runtime';
@ -27,13 +28,13 @@ jest.mock('app/core/core', () => {
jest.mock('react-virtualized-auto-sizer', () => {
return {
__esModule: true,
default(props: any) {
return <div>{props.children({ width: 1000 })}</div>;
default(props: ComponentProps<typeof AutoSizer>) {
return <div>{props.children({ width: 1000, height: 1000 })}</div>;
},
};
});
describe('Wrapper', () => {
describe('ExplorePage', () => {
afterEach(() => {
tearDown();
});

View File

@ -31,7 +31,7 @@ const styles = {
`,
};
function Wrapper(props: GrafanaRouteComponentProps<{}, ExploreQueryParams>) {
export function ExplorePage(props: GrafanaRouteComponentProps<{}, ExploreQueryParams>) {
useExplorePageTitle();
const dispatch = useDispatch();
const queryParams = props.queryParams;
@ -173,5 +173,3 @@ const useExplorePageTitle = () => {
document.title = `${navModel.main.text} - ${datasources.join(' | ')} - ${Branding.AppTitle}`;
};
export default Wrapper;

View File

@ -29,7 +29,7 @@ import { LokiDatasource } from '../../../../plugins/datasource/loki/datasource';
import { LokiQuery } from '../../../../plugins/datasource/loki/types';
import { ExploreId } from '../../../../types';
import { initialUserState } from '../../../profile/state/reducers';
import Wrapper from '../../Wrapper';
import { ExplorePage } from '../../ExplorePage';
type DatasourceSetup = { settings: DataSourceInstanceSettings; api: DataSourceApi };
@ -120,7 +120,7 @@ export function setupExplore(options?: SetupOptions): {
locationService.partial(urlParams);
}
const route = { component: Wrapper };
const route = { component: ExplorePage };
const { unmount, container } = render(
<Provider store={storeState}>

View File

@ -216,7 +216,7 @@ export function getAppRoutes(): RouteDescriptor[] {
),
component: SafeDynamicImport(() =>
config.exploreEnabled
? import(/* webpackChunkName: "explore" */ 'app/features/explore/Wrapper')
? import(/* webpackChunkName: "explore" */ 'app/features/explore/EmptyStateWrapper')
: import(/* webpackChunkName: "explore-feature-toggle-page" */ 'app/features/explore/FeatureTogglePage')
),
},