packages/core-api: move router component inside app and configure it with base path

This commit is contained in:
Patrik Oldsberg
2020-06-06 15:55:46 +02:00
parent 2edcd94793
commit c40a2a5e61
7 changed files with 51 additions and 29 deletions
+3 -6
View File
@@ -16,7 +16,6 @@
import { createApp, AlertDisplay, OAuthRequestDialog } from '@backstage/core';
import React, { FC } from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import Root from './components/Root';
import * as plugins from './plugins';
import apis from './apis';
@@ -33,11 +32,9 @@ const App: FC<{}> = () => (
<AppProvider>
<AlertDisplay />
<OAuthRequestDialog />
<Router>
<Root>
<AppComponent />
</Root>
</Router>
<Root>
<AppComponent />
</Root>
</AppProvider>
);
@@ -1,7 +1,6 @@
import { makeStyles } from '@material-ui/core';
import { createApp } from '@backstage/core';
import React, { FC } from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import * as plugins from './plugins';
const useStyles = makeStyles(theme => ({
@@ -33,9 +32,7 @@ const App: FC<{}> = () => {
useStyles();
return (
<AppProvider>
<Router>
<AppComponent />
</Router>
<AppComponent />
</AppProvider>
);
};
+34 -6
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import React, { ComponentType, FC } from 'react';
import React, { ComponentType, FC, useMemo } from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import { AppContextProvider } from './AppContext';
import { BackstageApp, AppComponents, AppConfigLoader } from './types';
@@ -161,30 +161,58 @@ export class PrivateAppImpl implements BackstageApp {
getProvider(): ComponentType<{}> {
const Provider: FC<{}> = ({ children }) => {
const appThemeApi = useMemo(
() => AppThemeSelector.createWithStorage(this.themes),
[],
);
// Keeping this synchronous when a config loader isn't set simplifies tests a lot
const hasConfig = Boolean(this.configLoader);
const config = useAsync(this.configLoader || (() => Promise.resolve([])));
let childNode = children;
let noConfigNode = undefined;
if (hasConfig && config.loading) {
const { Progress } = this.components;
childNode = <Progress />;
noConfigNode = <Progress />;
} else if (config.error) {
const { BootErrorPage } = this.components;
childNode = <BootErrorPage step="load-config" error={config.error} />;
noConfigNode = (
<BootErrorPage step="load-config" error={config.error} />
);
}
// Before the config is loaded we can't use a router, so exit early
if (noConfigNode) {
return (
<ApiProvider apis={ApiRegistry.from([[appThemeApiRef, appThemeApi]])}>
<AppThemeProvider>{noConfigNode}</AppThemeProvider>
</ApiProvider>
);
}
const configReader = ConfigReader.fromConfigs(config.value ?? []);
const appApis = ApiRegistry.from([
[appThemeApiRef, AppThemeSelector.createWithStorage(this.themes)],
[configApiRef, ConfigReader.fromConfigs(config.value ?? [])],
[configApiRef, configReader],
]);
const apis = new ApiAggregator(this.apis, appApis);
const { Router } = this.components;
let { pathname } = new URL(
configReader.getString('app.baseUrl') ?? '/',
'http://dummy.dev', // baseUrl can be specified as just a path
);
if (pathname.endsWith('/')) {
pathname = pathname.replace(/\/$/, '');
}
return (
<ApiProvider apis={apis}>
<AppContextProvider app={this}>
<AppThemeProvider>{childNode}</AppThemeProvider>
<AppThemeProvider>
<Router basename={pathname}>{children}</Router>
</AppThemeProvider>
</AppContextProvider>
</ApiProvider>
);
+1
View File
@@ -30,6 +30,7 @@ export type AppComponents = {
NotFoundErrorPage: ComponentType<{}>;
BootErrorPage: ComponentType<BootErrorPageProps>;
Progress: ComponentType<{}>;
Router: ComponentType<{ basename?: string }>;
};
/**
+4 -3
View File
@@ -22,7 +22,7 @@ import privateExports, {
BootErrorPageProps,
AppConfigLoader,
} from '@backstage/core-api';
import { BrowserRouter as Router } from 'react-router-dom';
import { BrowserRouter, MemoryRouter } from 'react-router-dom';
import { ErrorPage } from '../layout/ErrorPage';
import Progress from '../components/Progress';
@@ -87,9 +87,9 @@ export function createApp(options?: AppOptions) {
}
// TODO: figure out a nicer way to handle routing on the error page, when it can be done.
return (
<Router>
<MemoryRouter>
<ErrorPage status="501" statusMessage={message} />
</Router>
</MemoryRouter>
);
};
@@ -100,6 +100,7 @@ export function createApp(options?: AppOptions) {
NotFoundErrorPage: DefaultNotFoundPage,
BootErrorPage: DefaultBootErrorPage,
Progress: Progress,
Router: BrowserRouter,
...options?.components,
};
const themes = options?.themes ?? [
+4 -7
View File
@@ -17,7 +17,6 @@
import { hot } from 'react-hot-loader/root';
import React, { FC, ComponentType, ReactNode } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import BookmarkIcon from '@material-ui/icons/Bookmark';
import {
createApp,
@@ -94,12 +93,10 @@ class DevAppBuilder {
<AlertDisplay />
<OAuthRequestDialog />
{this.rootChildren}
<BrowserRouter>
<SidebarPage>
{sidebar}
<AppComponent />
</SidebarPage>
</BrowserRouter>
<SidebarPage>
{sidebar}
<AppComponent />
</SidebarPage>
</AppProvider>
);
};
@@ -55,6 +55,9 @@ export function wrapInTestApp(
NotFoundErrorPage,
BootErrorPage,
Progress,
Router: ({ children }) => (
<MemoryRouter initialEntries={routeEntries} children={children} />
),
},
icons: defaultSystemIcons,
plugins: [],
@@ -79,9 +82,7 @@ export function wrapInTestApp(
return (
<AppProvider>
<MemoryRouter initialEntries={routeEntries}>
<Route component={Wrapper} />
</MemoryRouter>
<Route component={Wrapper} />
</AppProvider>
);
}