mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
EditDataSources: Add EditDataSourceActions to EditDataSourcePages (#64487)
* add EditDataSourceActions to EditDataSourcePages * fix tests * EditDSPage: do not show buttons in header if topnav is off * remove delete button from the header * EditDSPage: hide buttons from footer when topnav is on * update tests * rename ActionProps to Props * wrap setting of feature toggle in act in jest test * fix jest test by using waitFor
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
|
||||
import { config } from '@grafana/runtime';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { EditDataSource } from 'app/features/datasources/components/EditDataSource';
|
||||
import { EditDataSourceActions } from 'app/features/datasources/components/EditDataSourceActions';
|
||||
|
||||
import { useDataSourceSettingsNav } from '../hooks/useDataSourceSettingsNav';
|
||||
|
||||
@@ -14,7 +16,11 @@ export function EditDataSourcePage() {
|
||||
const { navId, pageNav } = useDataSourceSettingsNav();
|
||||
|
||||
return (
|
||||
<Page navId={navId} pageNav={pageNav}>
|
||||
<Page
|
||||
navId={navId}
|
||||
pageNav={pageNav}
|
||||
actions={config.featureToggles.topnav ? <EditDataSourceActions uid={uid} /> : undefined}
|
||||
>
|
||||
<Page.Contents>
|
||||
<EditDataSource uid={uid} pageId={pageId} />
|
||||
</Page.Contents>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { Button, LinkButton } from '@grafana/ui';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
import { AccessControlAction } from 'app/types';
|
||||
@@ -19,21 +20,25 @@ export function ButtonRow({ canSave, canDelete, onDelete, onSubmit, onTest, expl
|
||||
|
||||
return (
|
||||
<div className="gf-form-button-row">
|
||||
<Button variant="secondary" fill="solid" type="button" onClick={() => history.back()}>
|
||||
Back
|
||||
</Button>
|
||||
{!config.featureToggles.topnav && (
|
||||
<Button variant="secondary" fill="solid" type="button" onClick={() => history.back()}>
|
||||
Back
|
||||
</Button>
|
||||
)}
|
||||
<LinkButton variant="secondary" fill="solid" href={exploreUrl} disabled={!canExploreDataSources}>
|
||||
Explore
|
||||
</LinkButton>
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
disabled={!canDelete}
|
||||
onClick={onDelete}
|
||||
aria-label={selectors.pages.DataSource.delete}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
{!config.featureToggles.topnav && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
disabled={!canDelete}
|
||||
onClick={onDelete}
|
||||
aria-label={selectors.pages.DataSource.delete}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
)}
|
||||
{canSave && (
|
||||
<Button
|
||||
type="submit"
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { Button, LinkButton, useStyles2 } from '@grafana/ui';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
import { AccessControlAction } from 'app/types';
|
||||
|
||||
import { useDataSource, useDataSourceRights, useDeleteLoadedDataSource } from '../state';
|
||||
import { trackCreateDashboardClicked, trackExploreClicked } from '../tracking';
|
||||
import { constructDataSourceExploreUrl } from '../utils';
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
button: css({
|
||||
marginLeft: theme.spacing(2),
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
interface Props {
|
||||
uid: string;
|
||||
}
|
||||
|
||||
export function EditDataSourceActions({ uid }: Props) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const dataSource = useDataSource(uid);
|
||||
const onDelete = useDeleteLoadedDataSource();
|
||||
|
||||
const { readOnly, hasDeleteRights } = useDataSourceRights(uid);
|
||||
const hasExploreRights = contextSrv.hasPermission(AccessControlAction.DataSourcesExplore);
|
||||
|
||||
const canDelete = !readOnly && hasDeleteRights;
|
||||
|
||||
return (
|
||||
<>
|
||||
<LinkButton
|
||||
icon="apps"
|
||||
fill="outline"
|
||||
variant="secondary"
|
||||
href={`dashboard/new-with-ds/${dataSource.uid}`}
|
||||
onClick={() => {
|
||||
trackCreateDashboardClicked({
|
||||
grafana_version: config.buildInfo.version,
|
||||
datasource_uid: dataSource.uid,
|
||||
plugin_name: dataSource.typeName,
|
||||
path: location.pathname,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Build a dashboard
|
||||
</LinkButton>
|
||||
|
||||
{hasExploreRights && (
|
||||
<LinkButton
|
||||
icon="compass"
|
||||
fill="outline"
|
||||
variant="secondary"
|
||||
className={styles.button}
|
||||
href={constructDataSourceExploreUrl(dataSource)}
|
||||
onClick={() => {
|
||||
trackExploreClicked({
|
||||
grafana_version: config.buildInfo.version,
|
||||
datasource_uid: dataSource.uid,
|
||||
plugin_name: dataSource.typeName,
|
||||
path: location.pathname,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Explore
|
||||
</LinkButton>
|
||||
)}
|
||||
|
||||
<Button type="button" variant="destructive" disabled={!canDelete} onClick={onDelete} className={styles.button}>
|
||||
Delete
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import { screen, render } from '@testing-library/react';
|
||||
import { screen, render, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { Store } from 'redux';
|
||||
import { TestProvider } from 'test/helpers/TestProvider';
|
||||
|
||||
import { LayoutModes } from '@grafana/data';
|
||||
import { setAngularLoader } from '@grafana/runtime';
|
||||
import config from 'app/core/config';
|
||||
import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps';
|
||||
import { configureStore } from 'app/store/configureStore';
|
||||
|
||||
@@ -57,6 +58,7 @@ describe('<EditDataSourcePage>', () => {
|
||||
const dataSourceMeta = getMockDataSourceMeta();
|
||||
const dataSourceSettings = getMockDataSourceSettingsState();
|
||||
let store: Store;
|
||||
const topnavValue = config.featureToggles.topnav;
|
||||
|
||||
beforeAll(() => {
|
||||
setAngularLoader({
|
||||
@@ -94,7 +96,12 @@ describe('<EditDataSourcePage>', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
config.featureToggles.topnav = topnavValue;
|
||||
});
|
||||
|
||||
it('should render the edit page without an issue', async () => {
|
||||
config.featureToggles.topnav = false;
|
||||
setup(uid, store);
|
||||
|
||||
expect(screen.queryByText('Loading ...')).not.toBeInTheDocument();
|
||||
@@ -106,9 +113,23 @@ describe('<EditDataSourcePage>', () => {
|
||||
expect(screen.queryByRole('button', { name: /Back/i })).toBeVisible();
|
||||
expect(screen.queryByRole('button', { name: /Delete/i })).toBeVisible();
|
||||
expect(screen.queryByRole('button', { name: /Save (.*) test/i })).toBeVisible();
|
||||
expect(screen.queryByText('Explore')).toBeVisible();
|
||||
expect(screen.queryByRole('link', { name: /Explore/i })).toBeVisible();
|
||||
|
||||
// wait for the rest of the async processes to finish
|
||||
expect(await screen.findByText(name)).toBeVisible();
|
||||
});
|
||||
|
||||
it('should show updated action buttons when topnav is on', async () => {
|
||||
config.featureToggles.topnav = true;
|
||||
setup(uid, store);
|
||||
|
||||
await waitFor(() => {
|
||||
// Buttons
|
||||
expect(screen.queryAllByRole('button', { name: /Back/i })).toHaveLength(0);
|
||||
expect(screen.queryByRole('button', { name: /Delete/i })).toBeVisible();
|
||||
expect(screen.queryByRole('button', { name: /Save (.*) test/i })).toBeVisible();
|
||||
expect(screen.queryByRole('link', { name: /Build a dashboard/i })).toBeVisible();
|
||||
expect(screen.queryAllByRole('link', { name: /Explore/i })).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
import { config } from '@grafana/runtime';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
||||
|
||||
import { EditDataSource } from '../components/EditDataSource';
|
||||
import { EditDataSourceActions } from '../components/EditDataSourceActions';
|
||||
import { useDataSourceSettingsNav } from '../state';
|
||||
|
||||
export interface Props extends GrafanaRouteComponentProps<{ uid: string }> {}
|
||||
@@ -15,7 +17,11 @@ export function EditDataSourcePage(props: Props) {
|
||||
const nav = useDataSourceSettingsNav(uid, pageId);
|
||||
|
||||
return (
|
||||
<Page navId="datasources" pageNav={nav.main}>
|
||||
<Page
|
||||
navId="datasources"
|
||||
pageNav={nav.main}
|
||||
actions={config.featureToggles.topnav ? <EditDataSourceActions uid={uid} /> : undefined}
|
||||
>
|
||||
<Page.Contents>
|
||||
<EditDataSource uid={uid} pageId={pageId} />
|
||||
</Page.Contents>
|
||||
|
||||
Reference in New Issue
Block a user