diff --git a/.betterer.results b/.betterer.results
index 3b6f2de23a9..5b5be31e246 100644
--- a/.betterer.results
+++ b/.betterer.results
@@ -3513,8 +3513,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use export all (\`export * from ...\`)", "2"],
[0, 0, 0, "Do not use export all (\`export * from ...\`)", "3"],
[0, 0, 0, "Do not use export all (\`export * from ...\`)", "4"],
- [0, 0, 0, "Do not use export all (\`export * from ...\`)", "5"],
- [0, 0, 0, "Do not use export all (\`export * from ...\`)", "6"]
+ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "5"]
],
"public/app/features/datasources/state/navModel.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
diff --git a/public/app/features/connections/Connections.test.tsx b/public/app/features/connections/Connections.test.tsx
index 1d752fda505..42995857cde 100644
--- a/public/app/features/connections/Connections.test.tsx
+++ b/public/app/features/connections/Connections.test.tsx
@@ -1,7 +1,7 @@
-import { render, RenderResult, screen } from '@testing-library/react';
-import { TestProvider } from 'test/helpers/TestProvider';
+import { RenderResult, screen } from '@testing-library/react';
+import { Route, Routes } from 'react-router-dom-v5-compat';
+import { render } from 'test/test-utils';
-import { locationService } from '@grafana/runtime';
import { contextSrv } from 'app/core/services/context_srv';
import { getMockDataSources } from 'app/features/datasources/__mocks__';
import * as api from 'app/features/datasources/api';
@@ -11,7 +11,7 @@ import { getPluginsStateMock } from '../plugins/admin/__mocks__';
import Connections from './Connections';
import { navIndex } from './__mocks__/store.navIndex.mock';
-import { ROUTE_BASE_ID, ROUTES } from './constants';
+import { ROUTES } from './constants';
jest.mock('app/core/services/context_srv');
jest.mock('app/features/datasources/api');
@@ -21,15 +21,17 @@ jest.mock('@grafana/runtime', () => ({
}));
const renderPage = (
- path = `/${ROUTE_BASE_ID}`,
+ path: string = ROUTES.Base,
store = configureStore({ navIndex, plugins: getPluginsStateMock([]) })
): RenderResult => {
- locationService.push(path);
-
return render(
-
-
-
+
+ } />
+ ,
+ {
+ store,
+ historyOptions: { initialEntries: [path] },
+ }
);
};
diff --git a/public/app/features/connections/Connections.tsx b/public/app/features/connections/Connections.tsx
index fd0a97b1125..05330a180b5 100644
--- a/public/app/features/connections/Connections.tsx
+++ b/public/app/features/connections/Connections.tsx
@@ -1,7 +1,5 @@
-import { Route, Switch, useLocation } from 'react-router-dom';
-import { Navigate } from 'react-router-dom-v5-compat';
+import { Navigate, Routes, Route, useLocation } from 'react-router-dom-v5-compat';
-import { DataSourcesRoutesContext } from 'app/features/datasources/state';
import { StoreState, useSelector } from 'app/types';
import { ROUTES } from './constants';
@@ -32,39 +30,43 @@ export default function Connections() {
const isAddNewConnectionPageOverridden = Boolean(navIndex['standalone-plugin-page-/connections/add-new-connection']);
return (
-
-
- {/* Redirect to "Add new connection" by default */}
- } />
-
-
-
-
-
-
- {/* "Add new connection" page - we don't register a route in case a plugin already registers a standalone page for it */}
- {!isAddNewConnectionPageOverridden && (
-
- )}
+
+ {/* Redirect to "Add new connection" by default */}
+ } />
+ {/* The route paths need to be relative to the parent path (ROUTES.Base), so we need to remove that part */}
+ } />
+ } />
+ }
+ />
+ } />
+ }
+ />
- {/* Redirect from earlier routes to updated routes */}
-
+ {/* "Add new connection" page - we don't register a route in case a plugin already registers a standalone page for it */}
+ {!isAddNewConnectionPageOverridden && (
}
+ caseSensitive
+ path={ROUTES.AddNewConnection.replace(ROUTES.Base, '')}
+ element={}
/>
- } />
+ )}
+
+ {/* Redirect from earlier routes to updated routes */}
+ } />
+ } />
+ }
+ />
- {/* Not found */}
- } />
-
-
+ {/* Not found */}
+ } />
+
);
}
diff --git a/public/app/features/connections/hooks/useDataSourceSettingsNav.ts b/public/app/features/connections/hooks/useDataSourceSettingsNav.ts
index 1d94698a9a8..cbf26cc5f09 100644
--- a/public/app/features/connections/hooks/useDataSourceSettingsNav.ts
+++ b/public/app/features/connections/hooks/useDataSourceSettingsNav.ts
@@ -1,4 +1,4 @@
-import { useLocation, useParams } from 'react-router-dom';
+import { useLocation, useParams } from 'react-router-dom-v5-compat';
import { NavModel, NavModelItem } from '@grafana/data';
import { getDataSourceSrv } from '@grafana/runtime';
@@ -9,7 +9,7 @@ import { useGetSingle } from 'app/features/plugins/admin/state/hooks';
import { useSelector } from 'app/types';
export function useDataSourceSettingsNav(pageIdParam?: string) {
- const { uid } = useParams<{ uid: string }>();
+ const { uid = '' } = useParams<{ uid: string }>();
const location = useLocation();
const datasource = useDataSource(uid);
const dataSourceMeta = useDataSourceMeta(datasource.type);
diff --git a/public/app/features/connections/pages/DataSourceDashboardsPage.tsx b/public/app/features/connections/pages/DataSourceDashboardsPage.tsx
index ecda29195d7..9a82b0f0d15 100644
--- a/public/app/features/connections/pages/DataSourceDashboardsPage.tsx
+++ b/public/app/features/connections/pages/DataSourceDashboardsPage.tsx
@@ -1,4 +1,4 @@
-import { useParams } from 'react-router-dom';
+import { useParams } from 'react-router-dom-v5-compat';
import { Page } from 'app/core/components/Page/Page';
import { DataSourceDashboards } from 'app/features/datasources/components/DataSourceDashboards';
@@ -6,7 +6,7 @@ import { DataSourceDashboards } from 'app/features/datasources/components/DataSo
import { useDataSourceSettingsNav } from '../hooks/useDataSourceSettingsNav';
export function DataSourceDashboardsPage() {
- const { uid } = useParams<{ uid: string }>();
+ const { uid = '' } = useParams<{ uid: string }>();
const { navId, pageNav } = useDataSourceSettingsNav('dashboards');
return (
diff --git a/public/app/features/connections/pages/DataSourceDetailsPage.tsx b/public/app/features/connections/pages/DataSourceDetailsPage.tsx
index 9a72ddb5783..9ed3153764b 100644
--- a/public/app/features/connections/pages/DataSourceDetailsPage.tsx
+++ b/public/app/features/connections/pages/DataSourceDetailsPage.tsx
@@ -1,4 +1,4 @@
-import { useParams } from 'react-router-dom';
+import { useParams } from 'react-router-dom-v5-compat';
import { Alert, Badge } from '@grafana/ui';
import { PluginDetailsPage } from 'app/features/plugins/admin/components/PluginDetailsPage';
@@ -8,7 +8,7 @@ import { ROUTES } from '../constants';
export function DataSourceDetailsPage() {
const overrideNavId = 'standalone-plugin-page-/connections/add-new-connection';
- const { id } = useParams<{ id: string }>();
+ const { id = '' } = useParams<{ id: string }>();
const navIndex = useSelector((state: StoreState) => state.navIndex);
const isConnectDataPageOverriden = Boolean(navIndex[overrideNavId]);
const navId = isConnectDataPageOverriden ? overrideNavId : 'connections-add-new-connection'; // The nav id changes (gets a prefix) if it is overriden by a plugin
diff --git a/public/app/features/connections/pages/EditDataSourcePage.tsx b/public/app/features/connections/pages/EditDataSourcePage.tsx
index be105116ca0..fe2d93bc888 100644
--- a/public/app/features/connections/pages/EditDataSourcePage.tsx
+++ b/public/app/features/connections/pages/EditDataSourcePage.tsx
@@ -1,9 +1,9 @@
-import { useLocation, useParams } from 'react-router-dom';
+import { useLocation, useParams } from 'react-router-dom-v5-compat';
import DataSourceTabPage from 'app/features/datasources/components/DataSourceTabPage';
export function EditDataSourcePage() {
- const { uid } = useParams<{ uid: string }>();
+ const { uid = '' } = useParams<{ uid: string }>();
const location = useLocation();
const params = new URLSearchParams(location.search);
const pageId = params.get('page');
diff --git a/public/app/features/connections/routes.tsx b/public/app/features/connections/routes.tsx
index d49a322e8f6..935caa40164 100644
--- a/public/app/features/connections/routes.tsx
+++ b/public/app/features/connections/routes.tsx
@@ -1,12 +1,12 @@
import { SafeDynamicImport } from 'app/core/components/DynamicImports/SafeDynamicImport';
import { RouteDescriptor } from 'app/core/navigation/types';
-import { ROUTE_BASE_ID } from './constants';
+import { ROUTES } from './constants';
export function getRoutes(): RouteDescriptor[] {
return [
{
- path: `/${ROUTE_BASE_ID}`,
+ path: ROUTES.Base,
exact: false,
component: SafeDynamicImport(
() => import(/* webpackChunkName: "Connections"*/ 'app/features/connections/Connections')
diff --git a/public/app/features/connections/tabs/ConnectData/ConnectData.tsx b/public/app/features/connections/tabs/ConnectData/ConnectData.tsx
index 63737f4cdff..bc21502dfcb 100644
--- a/public/app/features/connections/tabs/ConnectData/ConnectData.tsx
+++ b/public/app/features/connections/tabs/ConnectData/ConnectData.tsx
@@ -1,6 +1,5 @@
import { css } from '@emotion/css';
-import { useMemo, useState } from 'react';
-import * as React from 'react';
+import { useMemo, useState, FormEvent, MouseEvent } from 'react';
import { GrafanaTheme2, PluginType } from '@grafana/data';
import { useStyles2, LoadingPlaceholder, EmptyState } from '@grafana/ui';
@@ -38,7 +37,7 @@ export function AddNewConnection() {
const styles = useStyles2(getStyles);
const canCreateDataSources = contextSrv.hasPermission(AccessControlAction.DataSourcesCreate);
- const handleSearchChange = (e: React.FormEvent) => {
+ const handleSearchChange = (e: FormEvent) => {
setQueryParams({
search: e.currentTarget.value.toLowerCase(),
});
@@ -62,7 +61,7 @@ export function AddNewConnection() {
[plugins]
);
- const onClickCardGridItem = (e: React.MouseEvent, item: CardGridItem) => {
+ const onClickCardGridItem = (e: MouseEvent, item: CardGridItem) => {
if (!canCreateDataSources) {
e.preventDefault();
e.stopPropagation();
diff --git a/public/app/features/datasources/components/DataSourceAddButton.tsx b/public/app/features/datasources/components/DataSourceAddButton.tsx
index 8d7de1d5cc0..3d5d9fead3f 100644
--- a/public/app/features/datasources/components/DataSourceAddButton.tsx
+++ b/public/app/features/datasources/components/DataSourceAddButton.tsx
@@ -2,16 +2,14 @@ import { config } from '@grafana/runtime';
import { LinkButton } from '@grafana/ui';
import { contextSrv } from 'app/core/core';
import { Trans } from 'app/core/internationalization';
+import { ROUTES } from 'app/features/connections/constants';
import { AccessControlAction } from 'app/types';
-import { useDataSourcesRoutes } from '../state';
-
export function DataSourceAddButton(): JSX.Element | null {
const canCreateDataSource = contextSrv.hasPermission(AccessControlAction.DataSourcesCreate);
- const dataSourcesRoutes = useDataSourcesRoutes();
return canCreateDataSource ? (
-
+
Add new data source
) : null;
diff --git a/public/app/features/datasources/components/DataSourcesList.tsx b/public/app/features/datasources/components/DataSourcesList.tsx
index bf5a3f2f829..e9cf467c97f 100644
--- a/public/app/features/datasources/components/DataSourcesList.tsx
+++ b/public/app/features/datasources/components/DataSourcesList.tsx
@@ -9,7 +9,8 @@ import { contextSrv } from 'app/core/core';
import { Trans, t } from 'app/core/internationalization';
import { StoreState, AccessControlAction, useSelector } from 'app/types';
-import { getDataSources, getDataSourcesCount, useDataSourcesRoutes, useLoadDataSources } from '../state';
+import { ROUTES } from '../../connections/constants';
+import { getDataSources, getDataSourcesCount, useLoadDataSources } from '../state';
import { trackDataSourcesListViewed } from '../tracking';
import { DataSourcesListCard } from './DataSourcesListCard';
@@ -54,7 +55,6 @@ export function DataSourcesListView({
hasExploreRights,
}: ViewProps) {
const styles = useStyles2(getStyles);
- const dataSourcesRoutes = useDataSourcesRoutes();
const location = useLocation();
useEffect(() => {
@@ -69,7 +69,7 @@ export function DataSourcesListView({
+
Add data source
}
diff --git a/public/app/features/datasources/components/DataSourcesListCard.tsx b/public/app/features/datasources/components/DataSourcesListCard.tsx
index f93c3d91def..0c77ea501aa 100644
--- a/public/app/features/datasources/components/DataSourcesListCard.tsx
+++ b/public/app/features/datasources/components/DataSourcesListCard.tsx
@@ -5,7 +5,7 @@ import { DataSourceSettings, GrafanaTheme2 } from '@grafana/data';
import { config } from '@grafana/runtime';
import { Card, LinkButton, Stack, Tag, useStyles2 } from '@grafana/ui';
-import { useDataSourcesRoutes } from '../state';
+import { ROUTES } from '../../connections/constants';
import { trackCreateDashboardClicked, trackExploreClicked } from '../tracking';
import { constructDataSourceExploreUrl } from '../utils';
@@ -16,8 +16,7 @@ export interface Props {
}
export function DataSourcesListCard({ dataSource, hasWriteRights, hasExploreRights }: Props) {
- const dataSourcesRoutes = useDataSourcesRoutes();
- const dsLink = config.appSubUrl + dataSourcesRoutes.Edit.replace(/:uid/gi, dataSource.uid);
+ const dsLink = config.appSubUrl + ROUTES.DataSourcesEdit.replace(/:uid/gi, dataSource.uid);
const styles = useStyles2(getStyles);
return (
diff --git a/public/app/features/datasources/components/NewDataSource.tsx b/public/app/features/datasources/components/NewDataSource.tsx
index 86501a76477..e24ab0953fa 100644
--- a/public/app/features/datasources/components/NewDataSource.tsx
+++ b/public/app/features/datasources/components/NewDataSource.tsx
@@ -1,4 +1,4 @@
-import { AnyAction } from 'redux';
+import { Action } from 'redux';
import { DataSourcePluginMeta, PluginType } from '@grafana/data';
import { LinkButton, FilterInput } from '@grafana/ui';
@@ -6,6 +6,7 @@ import PageLoader from 'app/core/components/PageLoader/PageLoader';
import { PluginsErrorsInfo } from 'app/features/plugins/components/PluginsErrorsInfo';
import { DataSourcePluginCategory, StoreState, useDispatch, useSelector } from 'app/types';
+import { ROUTES } from '../../connections/constants';
import { DataSourceCategories } from '../components/DataSourceCategories';
import { DataSourceTypeCardList } from '../components/DataSourceTypeCardList';
import {
@@ -13,7 +14,6 @@ import {
useLoadDataSourcePlugins,
getFilteredDataSourcePlugins,
setDataSourceTypeSearchQuery,
- useDataSourcesRoutes,
} from '../state';
export function NewDataSource() {
@@ -45,7 +45,7 @@ export type ViewProps = {
searchQuery: string;
isLoading: boolean;
onAddDataSource: (dataSource: DataSourcePluginMeta) => void;
- onSetSearchQuery: (q: string) => AnyAction;
+ onSetSearchQuery: (q: string) => Action;
};
export function NewDataSourceView({
@@ -56,8 +56,6 @@ export function NewDataSourceView({
onAddDataSource,
onSetSearchQuery,
}: ViewProps) {
- const dataSourcesRoutes = useDataSourcesRoutes();
-
if (isLoading) {
return ;
}
@@ -68,7 +66,7 @@ export function NewDataSourceView({
diff --git a/public/app/features/datasources/state/contexts.ts b/public/app/features/datasources/state/contexts.ts
deleted file mode 100644
index df94279c2f5..00000000000
--- a/public/app/features/datasources/state/contexts.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { createContext } from 'react';
-
-import { DATASOURCES_ROUTES } from '../constants';
-import { DataSourcesRoutes } from '../types';
-
-// The purpose of this context is to be able to override the data-sources routes (used for links for example) used under
-// the app/features/datasources modules, so we can reuse them more easily in different parts of the application (e.g. under Connections)
-export const DataSourcesRoutesContext = createContext(DATASOURCES_ROUTES);
diff --git a/public/app/features/datasources/state/hooks.ts b/public/app/features/datasources/state/hooks.ts
index 11587a60d42..d2c956a2a4b 100644
--- a/public/app/features/datasources/state/hooks.ts
+++ b/public/app/features/datasources/state/hooks.ts
@@ -1,4 +1,4 @@
-import { useContext, useEffect } from 'react';
+import { useEffect } from 'react';
import { DataSourcePluginMeta, DataSourceSettings } from '@grafana/data';
import { cleanUpAction } from 'app/core/actions/cleanUp';
@@ -7,6 +7,7 @@ import { contextSrv } from 'app/core/core';
import { AccessControlAction, useDispatch, useSelector } from 'app/types';
import { ShowConfirmModalEvent } from 'app/types/events';
+import { ROUTES } from '../../connections/constants';
import { DataSourceRights } from '../types';
import { constructDataSourceExploreUrl } from '../utils';
@@ -20,7 +21,6 @@ import {
updateDataSource,
deleteLoadedDataSource,
} from './actions';
-import { DataSourcesRoutesContext } from './contexts';
import { initialDataSourceSettingsState } from './reducers';
import { getDataSource, getDataSourceMeta } from './selectors';
@@ -42,9 +42,8 @@ export const useInitDataSourceSettings = (uid: string) => {
export const useTestDataSource = (uid: string) => {
const dispatch = useDispatch();
- const dataSourcesRoutes = useDataSourcesRoutes();
- return () => dispatch(testDataSource(uid, dataSourcesRoutes.Edit));
+ return () => dispatch(testDataSource(uid, ROUTES.DataSourcesEdit));
};
export const useLoadDataSources = () => {
@@ -77,10 +76,9 @@ export const useLoadDataSourcePlugins = () => {
export const useAddDatasource = () => {
const dispatch = useDispatch();
- const dataSourcesRoutes = useDataSourcesRoutes();
return (plugin: DataSourcePluginMeta) => {
- dispatch(addDataSource(plugin, dataSourcesRoutes.Edit));
+ dispatch(addDataSource(plugin, ROUTES.DataSourcesEdit));
};
};
@@ -136,7 +134,3 @@ export const useDataSourceRights = (uid: string): DataSourceRights => {
hasDeleteRights,
};
};
-
-export const useDataSourcesRoutes = () => {
- return useContext(DataSourcesRoutesContext);
-};
diff --git a/public/app/features/datasources/state/index.ts b/public/app/features/datasources/state/index.ts
index 37a2d6f5cf0..42fae7a84a7 100644
--- a/public/app/features/datasources/state/index.ts
+++ b/public/app/features/datasources/state/index.ts
@@ -1,6 +1,5 @@
export * from './actions';
export * from './buildCategories';
-export * from './contexts';
export * from './hooks';
export * from './navModel';
export * from './reducers';
diff --git a/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.tsx b/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.tsx
index 2c45596367f..22ba118489b 100644
--- a/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.tsx
+++ b/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.tsx
@@ -4,7 +4,8 @@ import * as React from 'react';
import { DataSourcePluginMeta } from '@grafana/data';
import { config } from '@grafana/runtime';
import { Button } from '@grafana/ui';
-import { useDataSourcesRoutes, addDataSource } from 'app/features/datasources/state';
+import { ROUTES } from 'app/features/connections/constants';
+import { addDataSource } from 'app/features/datasources/state';
import { useDispatch } from 'app/types';
import { isDataSourceEditor } from '../../permissions';
@@ -16,15 +17,14 @@ type Props = {
export function GetStartedWithDataSource({ plugin }: Props): React.ReactElement | null {
const dispatch = useDispatch();
- const dataSourcesRoutes = useDataSourcesRoutes();
const onAddDataSource = useCallback(() => {
const meta = {
name: plugin.name,
id: plugin.id,
} as DataSourcePluginMeta;
- dispatch(addDataSource(meta, dataSourcesRoutes.Edit));
- }, [dispatch, plugin, dataSourcesRoutes]);
+ dispatch(addDataSource(meta, ROUTES.DataSourcesEdit));
+ }, [dispatch, plugin]);
if (!isDataSourceEditor()) {
return null;