From 6562581ede650f3db6a9c406da3a90ac9eeab922 Mon Sep 17 00:00:00 2001 From: Taewoo Kim Date: Thu, 11 May 2023 11:38:07 -0400 Subject: [PATCH] restore basicSettings component and apply feature toggle --- .betterer.results | 3 + .../hooks/useDataSourceSettingsNav.ts | 34 ++++++++ .../connections/pages/EditDataSourcePage.tsx | 20 ++++- .../components/BasicSettings.test.tsx | 27 ++++++ .../datasources/components/BasicSettings.tsx | 85 +++++++++++++++++++ .../components/EditDataSource.test.tsx | 2 + .../datasources/components/EditDataSource.tsx | 29 ++++++- .../datasources/pages/EditDataSourcePage.tsx | 19 ++++- 8 files changed, 216 insertions(+), 3 deletions(-) create mode 100644 public/app/features/connections/hooks/useDataSourceSettingsNav.ts create mode 100644 public/app/features/datasources/components/BasicSettings.test.tsx create mode 100644 public/app/features/datasources/components/BasicSettings.tsx diff --git a/.betterer.results b/.betterer.results index 94f122c1f93..22be1f2f322 100644 --- a/.betterer.results +++ b/.betterer.results @@ -2593,6 +2593,9 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "10"], [0, 0, 0, "Unexpected any. Specify a different type.", "11"] ], + "public/app/features/datasources/components/BasicSettings.tsx:5381": [ + [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] + ], "public/app/features/datasources/components/DataSourceReadOnlyMessage.tsx:5381": [ [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] ], diff --git a/public/app/features/connections/hooks/useDataSourceSettingsNav.ts b/public/app/features/connections/hooks/useDataSourceSettingsNav.ts new file mode 100644 index 00000000000..30b7b6addf2 --- /dev/null +++ b/public/app/features/connections/hooks/useDataSourceSettingsNav.ts @@ -0,0 +1,34 @@ +import { useLocation, useParams } from 'react-router-dom'; + +import { + useDataSource, + useDataSourceSettingsNav as useDataSourceSettingsNavOriginal, +} from 'app/features/datasources/state/hooks'; +import { useGetSingle } from 'app/features/plugins/admin/state/hooks'; + +// We are extending the original useDataSourceSettingsNav in the following ways: +// - changing the URL of the nav items to point to Connections +// - setting the parent nav item +export function useDataSourceSettingsNav(pageId?: string) { + const { uid } = useParams<{ uid: string }>(); + const location = useLocation(); + const datasource = useDataSource(uid); + const datasourcePlugin = useGetSingle(datasource.type); + const params = new URLSearchParams(location.search); + const nav = useDataSourceSettingsNavOriginal(uid, pageId || params.get('page')); + const pageNav = { + ...nav.main, + text: datasource.name, + subTitle: `Type: ${datasourcePlugin?.name}`, + active: true, + children: (nav.main.children || []).map((navModelItem) => ({ + ...navModelItem, + url: navModelItem.url?.replace('datasources/edit/', '/connections/datasources/edit/'), + })), + }; + + return { + navId: 'connections-datasources', + pageNav, + }; +} diff --git a/public/app/features/connections/pages/EditDataSourcePage.tsx b/public/app/features/connections/pages/EditDataSourcePage.tsx index ac8e2596501..e4faf9f3fd9 100644 --- a/public/app/features/connections/pages/EditDataSourcePage.tsx +++ b/public/app/features/connections/pages/EditDataSourcePage.tsx @@ -1,13 +1,31 @@ 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 DataSourceTabPage from 'app/features/datasources/components/DataSourceTabPage'; +import { EditDataSource } from 'app/features/datasources/components/EditDataSource'; +import { EditDataSourceActions } from 'app/features/datasources/components/EditDataSourceActions'; + +import { useDataSourceSettingsNav } from '../hooks/useDataSourceSettingsNav'; export function EditDataSourcePage() { const { uid } = useParams<{ uid: string }>(); const location = useLocation(); const params = new URLSearchParams(location.search); const pageId = params.get('page'); + const dataSourcePageHeader = config.featureToggles.dataSourcePageHeader; + const { navId, pageNav } = useDataSourceSettingsNav(); - return ; + if (dataSourcePageHeader) { + return ; + } + + return ( + }> + + + + + ); } diff --git a/public/app/features/datasources/components/BasicSettings.test.tsx b/public/app/features/datasources/components/BasicSettings.test.tsx new file mode 100644 index 00000000000..7a869dc8beb --- /dev/null +++ b/public/app/features/datasources/components/BasicSettings.test.tsx @@ -0,0 +1,27 @@ +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { selectors } from '@grafana/e2e-selectors'; + +import { BasicSettings, Props } from './BasicSettings'; + +const setup = () => { + const props: Props = { + dataSourceName: 'Graphite', + isDefault: false, + onDefaultChange: jest.fn(), + onNameChange: jest.fn(), + alertingSupported: false, + }; + + return render(); +}; + +describe('', () => { + it('should render component', () => { + setup(); + + expect(screen.getByRole('textbox', { name: selectors.pages.DataSource.name })).toBeInTheDocument(); + expect(screen.getByRole('checkbox', { name: /Default/ })).toBeInTheDocument(); + }); +}); diff --git a/public/app/features/datasources/components/BasicSettings.tsx b/public/app/features/datasources/components/BasicSettings.tsx new file mode 100644 index 00000000000..71342bb1721 --- /dev/null +++ b/public/app/features/datasources/components/BasicSettings.tsx @@ -0,0 +1,85 @@ +import { css } from '@emotion/css'; +import React from 'react'; + +import { GrafanaTheme2 } from '@grafana/data'; +import { selectors } from '@grafana/e2e-selectors'; +import { InlineField, InlineSwitch, Input, Badge, useStyles2 } from '@grafana/ui'; + +export interface Props { + dataSourceName: string; + isDefault: boolean; + onNameChange: (name: string) => void; + onDefaultChange: (value: boolean) => void; + alertingSupported: boolean; + disabled?: boolean; +} + +export function BasicSettings({ + dataSourceName, + isDefault, + onDefaultChange, + onNameChange, + alertingSupported, + disabled, +}: Props) { + return ( + <> + + +
+
+ {/* Name */} +
+ + onNameChange(event.currentTarget.value)} + required + aria-label={selectors.pages.DataSource.name} + /> + +
+ + {/* Is Default */} + + ) => { + onDefaultChange(event.currentTarget.checked); + }} + /> + +
+
+ + ); +} + +export function AlertingEnabled({ enabled }: { enabled: boolean }) { + const styles = useStyles2(getStyles); + return ( +
+ {enabled ? ( + + ) : ( + + )} +
+ ); +} + +const getStyles = (theme: GrafanaTheme2) => ({ + badge: css` + margin-bottom: ${theme.spacing(2)}; + `, +}); diff --git a/public/app/features/datasources/components/EditDataSource.test.tsx b/public/app/features/datasources/components/EditDataSource.test.tsx index 1e864b0a32a..166e836a667 100644 --- a/public/app/features/datasources/components/EditDataSource.test.tsx +++ b/public/app/features/datasources/components/EditDataSource.test.tsx @@ -37,6 +37,8 @@ const setup = (props?: Partial) => { dataSourceRights={{ readOnly: false, hasWriteRights: true, hasDeleteRights: true }} exploreUrl={'/explore'} onDelete={jest.fn()} + onDefaultChange={jest.fn()} + onNameChange={jest.fn()} onOptionsChange={jest.fn()} onTest={jest.fn()} onUpdate={jest.fn()} diff --git a/public/app/features/datasources/components/EditDataSource.tsx b/public/app/features/datasources/components/EditDataSource.tsx index eb5f2f475ac..4a46f3d6b7a 100644 --- a/public/app/features/datasources/components/EditDataSource.tsx +++ b/public/app/features/datasources/components/EditDataSource.tsx @@ -6,12 +6,14 @@ import { DataSourcePluginMeta, DataSourceSettings as DataSourceSettingsType, } from '@grafana/data'; -import { getDataSourceSrv } from '@grafana/runtime'; +import { getDataSourceSrv, config } from '@grafana/runtime'; import PageLoader from 'app/core/components/PageLoader/PageLoader'; import { DataSourceSettingsState, useDispatch } from 'app/types'; import { dataSourceLoaded, + setDataSourceName, + setIsDefault, useDataSource, useDataSourceExploreUrl, useDataSourceMeta, @@ -24,6 +26,7 @@ import { } from '../state'; import { DataSourceRights } from '../types'; +import { BasicSettings } from './BasicSettings'; import { ButtonRow } from './ButtonRow'; import { CloudInfoBox } from './CloudInfoBox'; import { DataSourceLoadError } from './DataSourceLoadError'; @@ -53,6 +56,8 @@ export function EditDataSource({ uid, pageId }: Props) { const onDelete = useDeleteLoadedDataSource(); const onTest = useTestDataSource(uid); const onUpdate = useUpdateDatasource(); + const onDefaultChange = (value: boolean) => dispatch(setIsDefault(value)); + const onNameChange = (name: string) => dispatch(setDataSourceName(name)); const onOptionsChange = (ds: DataSourceSettingsType) => dispatch(dataSourceLoaded(ds)); return ( @@ -64,6 +69,8 @@ export function EditDataSource({ uid, pageId }: Props) { dataSourceRights={dataSourceRights} exploreUrl={exploreUrl} onDelete={onDelete} + onDefaultChange={onDefaultChange} + onNameChange={onNameChange} onOptionsChange={onOptionsChange} onTest={onTest} onUpdate={onUpdate} @@ -79,6 +86,8 @@ export type ViewProps = { dataSourceRights: DataSourceRights; exploreUrl: string; onDelete: () => void; + onDefaultChange: (isDefault: boolean) => AnyAction; + onNameChange: (name: string) => AnyAction; onOptionsChange: (dataSource: DataSourceSettingsType) => AnyAction; onTest: () => void; onUpdate: (dataSource: DataSourceSettingsType) => Promise; @@ -92,6 +101,8 @@ export function EditDataSourceView({ dataSourceRights, exploreUrl, onDelete, + onDefaultChange, + onNameChange, onOptionsChange, onTest, onUpdate, @@ -102,6 +113,11 @@ export function EditDataSourceView({ const dsi = getDataSourceSrv()?.getInstanceSettings(dataSource.uid); + const hasAlertingEnabled = Boolean(dsi?.meta?.alerting ?? false); + const isAlertManagerDatasource = dsi?.type === 'alertmanager'; + const alertingSupported = hasAlertingEnabled || isAlertManagerDatasource; + const dataSourcePageHeader = config.featureToggles.dataSourcePageHeader; + const onSubmit = async (e: React.MouseEvent | React.FormEvent) => { e.preventDefault(); try { @@ -142,6 +158,17 @@ export function EditDataSourceView({ + {!dataSourcePageHeader && ( + + )} + {plugin && ( {} @@ -10,8 +15,20 @@ export function EditDataSourcePage(props: Props) { const uid = props.match.params.uid; const params = new URLSearchParams(props.location.search); const pageId = params.get('page'); + const dataSourcePageHeader = config.featureToggles.dataSourcePageHeader; + const nav = useDataSourceSettingsNav(uid, pageId); - return ; + if (dataSourcePageHeader) { + return ; + } + + return ( + }> + + + + + ); } export default EditDataSourcePage;