mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 04:01:49 +08:00
Connections: Support standalone plugin pages (#57772)
* feat(Connections): create sub-pages wrapped by `<Page>` * feat(Connections): rename the Connections page and update routes * feat(Connections): use new url for editing datasources * refactor(Connections): remove unused tab components * feat(Connections): update routes and nav titles * tests: fix tests for Connections * tests: fix typo in backend tests
This commit is contained in:
@ -264,8 +264,8 @@ func TestAddAppLinks(t *testing.T) {
|
|||||||
treeRoot.AddSection(service.buildDataConnectionsNavLink(reqCtx))
|
treeRoot.AddSection(service.buildDataConnectionsNavLink(reqCtx))
|
||||||
connectionsNode := treeRoot.FindById("connections")
|
connectionsNode := treeRoot.FindById("connections")
|
||||||
require.Equal(t, "Connections", connectionsNode.Text)
|
require.Equal(t, "Connections", connectionsNode.Text)
|
||||||
require.Equal(t, "Connect Data", connectionsNode.Children[1].Text)
|
require.Equal(t, "Connect data", connectionsNode.Children[1].Text)
|
||||||
require.Equal(t, "connections-connect-data", connectionsNode.Children[1].Id) // Original "Connect Data" page
|
require.Equal(t, "connections-connect-data", connectionsNode.Children[1].Id) // Original "Connect data" page
|
||||||
require.Equal(t, "", connectionsNode.Children[1].PluginID)
|
require.Equal(t, "", connectionsNode.Children[1].PluginID)
|
||||||
|
|
||||||
err := service.addAppLinks(&treeRoot, reqCtx)
|
err := service.addAppLinks(&treeRoot, reqCtx)
|
||||||
@ -273,8 +273,8 @@ func TestAddAppLinks(t *testing.T) {
|
|||||||
// Check if the standalone plugin page appears under the section where we registered it
|
// Check if the standalone plugin page appears under the section where we registered it
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, "Connections", connectionsNode.Text)
|
require.Equal(t, "Connections", connectionsNode.Text)
|
||||||
require.Equal(t, "Connect Data", connectionsNode.Children[1].Text)
|
require.Equal(t, "Connect data", connectionsNode.Children[1].Text)
|
||||||
require.Equal(t, "standalone-plugin-page-/connections/connect-data", connectionsNode.Children[1].Id) // Overridden "Connect Data" page
|
require.Equal(t, "standalone-plugin-page-/connections/connect-data", connectionsNode.Children[1].Id) // Overridden "Connect data" page
|
||||||
require.Equal(t, "test-app3", connectionsNode.Children[1].PluginID)
|
require.Equal(t, "test-app3", connectionsNode.Children[1].PluginID)
|
||||||
|
|
||||||
// Check if the standalone plugin page does not appear under the app section anymore
|
// Check if the standalone plugin page does not appear under the app section anymore
|
||||||
|
@ -534,29 +534,37 @@ func (s *ServiceImpl) buildDataConnectionsNavLink(c *models.ReqContext) *navtree
|
|||||||
var children []*navtree.NavLink
|
var children []*navtree.NavLink
|
||||||
var navLink *navtree.NavLink
|
var navLink *navtree.NavLink
|
||||||
|
|
||||||
baseId := "connections"
|
baseUrl := s.cfg.AppSubURL + "/connections"
|
||||||
baseUrl := s.cfg.AppSubURL + "/" + baseId
|
|
||||||
|
|
||||||
|
// Your connections
|
||||||
children = append(children, &navtree.NavLink{
|
children = append(children, &navtree.NavLink{
|
||||||
Id: baseId + "-datasources",
|
Id: "connections-your-connections",
|
||||||
Text: "Data sources",
|
Text: "Your connections",
|
||||||
Icon: "database",
|
SubTitle: "Manage your existing connections",
|
||||||
SubTitle: "Add and configure data sources",
|
Url: baseUrl + "/your-connections",
|
||||||
Url: baseUrl + "/datasources",
|
// Datasources
|
||||||
|
Children: []*navtree.NavLink{{
|
||||||
|
Id: "connections-your-connections-datasources",
|
||||||
|
Text: "Datasources",
|
||||||
|
SubTitle: "Manage your existing datasource connections",
|
||||||
|
Url: baseUrl + "/your-connections/datasources",
|
||||||
|
}},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Connect data
|
||||||
children = append(children, &navtree.NavLink{
|
children = append(children, &navtree.NavLink{
|
||||||
Id: baseId + "-connect-data",
|
Id: "connections-connect-data",
|
||||||
Text: "Connect Data",
|
Text: "Connect data",
|
||||||
Icon: "plug",
|
SubTitle: "Browse and create new connections",
|
||||||
SubTitle: "Manage data sources",
|
Url: s.cfg.AppSubURL + "/connections/connect-data",
|
||||||
Url: baseUrl + "/connect-data",
|
Children: []*navtree.NavLink{},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Connections (main)
|
||||||
navLink = &navtree.NavLink{
|
navLink = &navtree.NavLink{
|
||||||
Text: "Connections",
|
Text: "Connections",
|
||||||
Icon: "link",
|
Icon: "link",
|
||||||
Id: baseId,
|
Id: "connections",
|
||||||
Url: baseUrl,
|
Url: baseUrl,
|
||||||
Children: children,
|
Children: children,
|
||||||
Section: navtree.NavSectionCore,
|
Section: navtree.NavSectionCore,
|
||||||
|
89
public/app/features/connections/Connections.test.tsx
Normal file
89
public/app/features/connections/Connections.test.tsx
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import { render, RenderResult, screen } from '@testing-library/react';
|
||||||
|
import React from 'react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import { Router } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { locationService } from '@grafana/runtime';
|
||||||
|
import { getMockDataSources } from 'app/features/datasources/__mocks__';
|
||||||
|
import * as api from 'app/features/datasources/api';
|
||||||
|
import { configureStore } from 'app/store/configureStore';
|
||||||
|
|
||||||
|
import { getPluginsStateMock } from '../plugins/admin/__mocks__';
|
||||||
|
|
||||||
|
import Connections from './Connections';
|
||||||
|
import { navIndex } from './__mocks__/store.navIndex.mock';
|
||||||
|
import { ROUTE_BASE_ID, ROUTES } from './constants';
|
||||||
|
|
||||||
|
jest.mock('app/features/datasources/api');
|
||||||
|
|
||||||
|
const renderPage = (
|
||||||
|
path = `/${ROUTE_BASE_ID}`,
|
||||||
|
store = configureStore({ navIndex, plugins: getPluginsStateMock([]) })
|
||||||
|
): RenderResult => {
|
||||||
|
locationService.push(path);
|
||||||
|
|
||||||
|
return render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<Router history={locationService.getHistory()}>
|
||||||
|
<Connections />
|
||||||
|
</Router>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Connections', () => {
|
||||||
|
const mockDatasources = getMockDataSources(3);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(api.getDataSources as jest.Mock) = jest.fn().mockResolvedValue(mockDatasources);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('shows the "Data sources" page by default', async () => {
|
||||||
|
renderPage();
|
||||||
|
|
||||||
|
expect(await screen.findByText('Datasources')).toBeVisible();
|
||||||
|
expect(await screen.findByText('Manage your existing datasource connections')).toBeVisible();
|
||||||
|
expect(await screen.findByRole('link', { name: /add data source/i })).toBeVisible();
|
||||||
|
expect(await screen.findByText(mockDatasources[0].name)).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders the correct tab even if accessing it with a "sub-url"', async () => {
|
||||||
|
renderPage(ROUTES.ConnectData);
|
||||||
|
|
||||||
|
expect(await screen.findByText('Connect data')).toBeVisible();
|
||||||
|
expect(await screen.findByText('Browse and create new connections')).toBeVisible();
|
||||||
|
|
||||||
|
// Should not render the "Your datasources" page
|
||||||
|
expect(screen.queryByText('Manage your existing datasource connections')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders the "Connect data" page using a plugin in case it is a standalone plugin page', async () => {
|
||||||
|
// We are overriding the navIndex to have the "Connect data" page registered by a plugin
|
||||||
|
const standalonePluginPage = {
|
||||||
|
id: 'standalone-plugin-page-/connections/connect-data',
|
||||||
|
text: 'Connect data',
|
||||||
|
subTitle: 'Browse and create new connections',
|
||||||
|
url: '/connections/connect-data',
|
||||||
|
pluginId: 'grafana-easystart-app',
|
||||||
|
};
|
||||||
|
const connections = {
|
||||||
|
...navIndex.connections,
|
||||||
|
children: navIndex.connections.children?.map((child) => {
|
||||||
|
if (child.id === 'connections-connect-data') {
|
||||||
|
return standalonePluginPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
const store = configureStore({
|
||||||
|
navIndex: { ...navIndex, connections, [standalonePluginPage.id]: standalonePluginPage },
|
||||||
|
plugins: getPluginsStateMock([]),
|
||||||
|
});
|
||||||
|
|
||||||
|
renderPage(ROUTES.ConnectData, store);
|
||||||
|
|
||||||
|
// We expect not to see the same text as if it was rendered by core.
|
||||||
|
expect(screen.queryByText('No results matching your query were found.')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
43
public/app/features/connections/Connections.tsx
Normal file
43
public/app/features/connections/Connections.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { Route, Switch } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { DataSourcesRoutesContext } from 'app/features/datasources/state';
|
||||||
|
import { StoreState, useSelector } from 'app/types';
|
||||||
|
|
||||||
|
import { ROUTES } from './constants';
|
||||||
|
import {
|
||||||
|
ConnectDataPage,
|
||||||
|
DataSourceDetailsPage,
|
||||||
|
DataSourcesListPage,
|
||||||
|
EditDataSourcePage,
|
||||||
|
NewDataSourcePage,
|
||||||
|
} from './pages';
|
||||||
|
|
||||||
|
export default function Connections() {
|
||||||
|
const navIndex = useSelector((state: StoreState) => state.navIndex);
|
||||||
|
const isConnectDataPageOverriden = Boolean(navIndex['standalone-plugin-page-/connections/connect-data']);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DataSourcesRoutesContext.Provider
|
||||||
|
value={{
|
||||||
|
New: ROUTES.DataSourcesNew,
|
||||||
|
List: ROUTES.DataSources,
|
||||||
|
Edit: ROUTES.DataSourcesEdit,
|
||||||
|
Dashboards: ROUTES.DataSourcesDashboards,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Switch>
|
||||||
|
<Route exact path={ROUTES.Base} component={DataSourcesListPage} />
|
||||||
|
<Route exact path={ROUTES.YourConnections} component={DataSourcesListPage} />
|
||||||
|
<Route exact path={ROUTES.DataSources} component={DataSourcesListPage} />
|
||||||
|
<Route exact path={ROUTES.DataSourcesDetails} component={DataSourceDetailsPage} />
|
||||||
|
<Route exact path={ROUTES.DataSourcesNew} component={NewDataSourcePage} />
|
||||||
|
<Route exact path={ROUTES.DataSourcesEdit} component={EditDataSourcePage} />
|
||||||
|
{!isConnectDataPageOverriden && <Route path={ROUTES.ConnectData} component={ConnectDataPage} />}
|
||||||
|
|
||||||
|
{/* Default page */}
|
||||||
|
<Route component={DataSourcesListPage} />
|
||||||
|
</Switch>
|
||||||
|
</DataSourcesRoutesContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
@ -1,59 +0,0 @@
|
|||||||
import { render, RenderResult, screen } from '@testing-library/react';
|
|
||||||
import React from 'react';
|
|
||||||
import { Provider } from 'react-redux';
|
|
||||||
import { Router } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { locationService } from '@grafana/runtime';
|
|
||||||
import { getMockDataSources } from 'app/features/datasources/__mocks__';
|
|
||||||
import * as api from 'app/features/datasources/api';
|
|
||||||
import { configureStore } from 'app/store/configureStore';
|
|
||||||
|
|
||||||
import { getPluginsStateMock } from '../plugins/admin/__mocks__';
|
|
||||||
|
|
||||||
import ConnectionsPage from './ConnectionsPage';
|
|
||||||
import { navIndex } from './__mocks__/store.navIndex.mock';
|
|
||||||
import { ROUTE_BASE_ID, ROUTES } from './constants';
|
|
||||||
|
|
||||||
jest.mock('app/features/datasources/api');
|
|
||||||
|
|
||||||
const renderPage = (path = `/${ROUTE_BASE_ID}`): RenderResult => {
|
|
||||||
// @ts-ignore
|
|
||||||
const store = configureStore({ navIndex, plugins: getPluginsStateMock([]) });
|
|
||||||
locationService.push(path);
|
|
||||||
|
|
||||||
return render(
|
|
||||||
<Provider store={store}>
|
|
||||||
<Router history={locationService.getHistory()}>
|
|
||||||
<ConnectionsPage />
|
|
||||||
</Router>
|
|
||||||
</Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('Connections Page', () => {
|
|
||||||
const mockDatasources = getMockDataSources(3);
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
(api.getDataSources as jest.Mock) = jest.fn().mockResolvedValue(mockDatasources);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('shows all tabs', async () => {
|
|
||||||
renderPage();
|
|
||||||
|
|
||||||
expect(await screen.findByLabelText('Tab Data sources')).toBeVisible();
|
|
||||||
expect(await screen.findByLabelText('Tab Connect Data')).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('shows the "Data sources" tab by default', async () => {
|
|
||||||
renderPage();
|
|
||||||
|
|
||||||
expect(await screen.findByRole('link', { name: /add data source/i })).toBeVisible();
|
|
||||||
expect(await screen.findByText(mockDatasources[0].name)).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('renders the correct tab even if accessing it with a "sub-url"', async () => {
|
|
||||||
renderPage(`${ROUTES.ConnectData}`);
|
|
||||||
|
|
||||||
expect(screen.queryByText('No results matching your query were found.')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,41 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import { Route, Switch } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { Page } from 'app/core/components/Page/Page';
|
|
||||||
import { DataSourcesList } from 'app/features/datasources/components/DataSourcesList';
|
|
||||||
import { NewDataSource } from 'app/features/datasources/components/NewDataSource';
|
|
||||||
import { DataSourcesRoutesContext } from 'app/features/datasources/state';
|
|
||||||
|
|
||||||
import { ROUTES } from './constants';
|
|
||||||
import { useNavModel } from './hooks/useNavModel';
|
|
||||||
import { ConnectData } from './tabs/ConnectData';
|
|
||||||
import { DataSourcesEdit } from './tabs/DataSourcesEdit';
|
|
||||||
|
|
||||||
export default function ConnectionsPage() {
|
|
||||||
const navModel = useNavModel();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DataSourcesRoutesContext.Provider
|
|
||||||
value={{
|
|
||||||
New: ROUTES.DataSourcesNew,
|
|
||||||
List: ROUTES.DataSources,
|
|
||||||
Edit: ROUTES.DataSourcesEdit,
|
|
||||||
Dashboards: ROUTES.DataSourcesDashboards,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Page navModel={navModel}>
|
|
||||||
<Page.Contents>
|
|
||||||
<Switch>
|
|
||||||
<Route path={ROUTES.DataSourcesNew} component={NewDataSource} />
|
|
||||||
<Route path={ROUTES.DataSourcesEdit} component={DataSourcesEdit} />
|
|
||||||
<Route path={ROUTES.DataSources} component={DataSourcesList} />
|
|
||||||
<Route path={ROUTES.ConnectData} component={ConnectData} />
|
|
||||||
|
|
||||||
{/* Default page */}
|
|
||||||
<Route component={DataSourcesList} />
|
|
||||||
</Switch>
|
|
||||||
</Page.Contents>
|
|
||||||
</Page>
|
|
||||||
</DataSourcesRoutesContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -5,9 +5,18 @@ export const CLOUD_ONBOARDING_APP_ID = 'grafana-easystart-app';
|
|||||||
export const ROUTE_BASE_ID = 'connections';
|
export const ROUTE_BASE_ID = 'connections';
|
||||||
|
|
||||||
export const ROUTES = {
|
export const ROUTES = {
|
||||||
DataSources: `/${ROUTE_BASE_ID}/datasources`,
|
Base: `/${ROUTE_BASE_ID}`,
|
||||||
DataSourcesNew: `/${ROUTE_BASE_ID}/datasources/new`,
|
|
||||||
DataSourcesEdit: `/${ROUTE_BASE_ID}/datasources/edit/:uid`,
|
// Your Connections
|
||||||
|
YourConnections: `/${ROUTE_BASE_ID}/your-connections`,
|
||||||
|
|
||||||
|
// Your Connections / Datasources
|
||||||
|
DataSources: `/${ROUTE_BASE_ID}/your-connections/datasources`,
|
||||||
|
DataSourcesNew: `/${ROUTE_BASE_ID}/your-connections/datasources/new`,
|
||||||
|
DataSourcesEdit: `/${ROUTE_BASE_ID}/your-connections/datasources/edit/:uid`,
|
||||||
DataSourcesDashboards: `/${ROUTE_BASE_ID}/datasources/edit/:uid/dashboards`,
|
DataSourcesDashboards: `/${ROUTE_BASE_ID}/datasources/edit/:uid/dashboards`,
|
||||||
|
|
||||||
|
// Connect Data
|
||||||
ConnectData: `/${ROUTE_BASE_ID}/connect-data`,
|
ConnectData: `/${ROUTE_BASE_ID}/connect-data`,
|
||||||
|
DataSourcesDetails: `/${ROUTE_BASE_ID}/connect-data/datasources/:id`,
|
||||||
} as const;
|
} as const;
|
||||||
|
15
public/app/features/connections/pages/ConnectDataPage.tsx
Normal file
15
public/app/features/connections/pages/ConnectDataPage.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { Page } from 'app/core/components/Page/Page';
|
||||||
|
|
||||||
|
import { ConnectData } from '../tabs/ConnectData';
|
||||||
|
|
||||||
|
export function ConnectDataPage() {
|
||||||
|
return (
|
||||||
|
<Page navId={'connections-connect-data'}>
|
||||||
|
<Page.Contents>
|
||||||
|
<ConnectData />
|
||||||
|
</Page.Contents>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { Page } from 'app/core/components/Page/Page';
|
||||||
|
import { StoreState, useSelector } from 'app/types';
|
||||||
|
|
||||||
|
export function DataSourceDetailsPage() {
|
||||||
|
const overrideNavId = 'standalone-plugin-page-/connections/connect-data';
|
||||||
|
const navIndex = useSelector((state: StoreState) => state.navIndex);
|
||||||
|
const isConnectDataPageOverriden = Boolean(navIndex[overrideNavId]);
|
||||||
|
const navId = isConnectDataPageOverriden ? overrideNavId : 'connections-connect-data'; // The nav id changes (gets a prefix) if it is overriden by a plugin
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page
|
||||||
|
navId={navId}
|
||||||
|
pageNav={{
|
||||||
|
text: 'Datasource details',
|
||||||
|
subTitle: 'This is going to be the details page for a datasource',
|
||||||
|
active: true,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Page.Contents>Data Source Details (no exposed component from plugins yet)</Page.Contents>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { Page } from 'app/core/components/Page/Page';
|
||||||
|
import { DataSourcesList } from 'app/features/datasources/components/DataSourcesList';
|
||||||
|
|
||||||
|
export function DataSourcesListPage() {
|
||||||
|
return (
|
||||||
|
<Page navId={'connections-your-connections-datasources'}>
|
||||||
|
<Page.Contents>
|
||||||
|
<DataSourcesList />
|
||||||
|
</Page.Contents>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
}
|
27
public/app/features/connections/pages/EditDataSourcePage.tsx
Normal file
27
public/app/features/connections/pages/EditDataSourcePage.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { useLocation, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { Page } from 'app/core/components/Page/Page';
|
||||||
|
import { EditDataSource } from 'app/features/datasources/components/EditDataSource';
|
||||||
|
import { useDataSource } from 'app/features/datasources/state/hooks';
|
||||||
|
import { useGetSingle } from 'app/features/plugins/admin/state/hooks';
|
||||||
|
|
||||||
|
export function EditDataSourcePage() {
|
||||||
|
const { uid } = useParams<{ uid: string }>();
|
||||||
|
const location = useLocation();
|
||||||
|
const datasource = useDataSource(uid);
|
||||||
|
const datasourcePlugin = useGetSingle(datasource.type);
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
const pageId = params.get('page');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page
|
||||||
|
navId={'connections-your-connections-datasources'}
|
||||||
|
pageNav={{ text: datasource.name, subTitle: `Type: ${datasourcePlugin?.name}`, active: true }}
|
||||||
|
>
|
||||||
|
<Page.Contents>
|
||||||
|
<EditDataSource uid={uid} pageId={pageId} />
|
||||||
|
</Page.Contents>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
}
|
17
public/app/features/connections/pages/NewDataSourcePage.tsx
Normal file
17
public/app/features/connections/pages/NewDataSourcePage.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { Page } from 'app/core/components/Page/Page';
|
||||||
|
import { NewDataSource } from 'app/features/datasources/components/NewDataSource';
|
||||||
|
|
||||||
|
export function NewDataSourcePage() {
|
||||||
|
return (
|
||||||
|
<Page
|
||||||
|
navId={'connections-your-connections-datasources'}
|
||||||
|
pageNav={{ text: 'Add data source', subTitle: 'Choose a data source type', active: true }}
|
||||||
|
>
|
||||||
|
<Page.Contents>
|
||||||
|
<NewDataSource />
|
||||||
|
</Page.Contents>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
}
|
5
public/app/features/connections/pages/index.tsx
Normal file
5
public/app/features/connections/pages/index.tsx
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export { ConnectDataPage } from './ConnectDataPage';
|
||||||
|
export { DataSourceDetailsPage } from './DataSourceDetailsPage';
|
||||||
|
export { DataSourcesListPage } from './DataSourcesListPage';
|
||||||
|
export { EditDataSourcePage } from './EditDataSourcePage';
|
||||||
|
export { NewDataSourcePage } from './NewDataSourcePage';
|
@ -11,7 +11,7 @@ export function getRoutes(): RouteDescriptor[] {
|
|||||||
path: `/${ROUTE_BASE_ID}`,
|
path: `/${ROUTE_BASE_ID}`,
|
||||||
exact: false,
|
exact: false,
|
||||||
component: SafeDynamicImport(
|
component: SafeDynamicImport(
|
||||||
() => import(/* webpackChunkName: "DataConnectionsPage"*/ 'app/features/connections/ConnectionsPage')
|
() => import(/* webpackChunkName: "Connections"*/ 'app/features/connections/Connections')
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -4,6 +4,8 @@ import React, { useMemo, useState } from 'react';
|
|||||||
import { useStyles2, LoadingPlaceholder } from '@grafana/ui';
|
import { useStyles2, LoadingPlaceholder } from '@grafana/ui';
|
||||||
import { useGetAllWithFilters } from 'app/features/plugins/admin/state/hooks';
|
import { useGetAllWithFilters } from 'app/features/plugins/admin/state/hooks';
|
||||||
|
|
||||||
|
import { ROUTES } from '../../constants';
|
||||||
|
|
||||||
import { CardGrid } from './CardGrid';
|
import { CardGrid } from './CardGrid';
|
||||||
import { CategoryHeader } from './CategoryHeader';
|
import { CategoryHeader } from './CategoryHeader';
|
||||||
import { NoResults } from './NoResults';
|
import { NoResults } from './NoResults';
|
||||||
@ -31,7 +33,7 @@ export function ConnectData() {
|
|||||||
id: plugin.id,
|
id: plugin.id,
|
||||||
name: plugin.name,
|
name: plugin.name,
|
||||||
logo: plugin.info.logos.small,
|
logo: plugin.info.logos.small,
|
||||||
url: `plugins/${plugin.id}`,
|
url: ROUTES.DataSourcesDetails.replace(':id', plugin.id),
|
||||||
})),
|
})),
|
||||||
[plugins]
|
[plugins]
|
||||||
);
|
);
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
|
||||||
import { EditDataSource } from 'app/features/datasources/components/EditDataSource';
|
|
||||||
|
|
||||||
export interface Props extends GrafanaRouteComponentProps<{ uid: string }> {}
|
|
||||||
|
|
||||||
export function DataSourcesEdit(props: Props) {
|
|
||||||
const uid = props.match.params.uid;
|
|
||||||
const params = new URLSearchParams(props.location.search);
|
|
||||||
const pageId = params.get('page');
|
|
||||||
|
|
||||||
return <EditDataSource uid={uid} pageId={pageId} />;
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export * from './DataSourcesEdit';
|
|
Reference in New Issue
Block a user