diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx
index b4d01e067b..669838ddd6 100644
--- a/packages/app/src/App.tsx
+++ b/packages/app/src/App.tsx
@@ -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<{}> = () => (
-
-
-
-
-
+
+
+
);
diff --git a/packages/cli/templates/default-app/packages/app/src/App.tsx b/packages/cli/templates/default-app/packages/app/src/App.tsx
index 84bc1b2e7d..140d1c1660 100644
--- a/packages/cli/templates/default-app/packages/app/src/App.tsx
+++ b/packages/cli/templates/default-app/packages/app/src/App.tsx
@@ -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 (
-
-
-
+
);
};
diff --git a/packages/core-api/src/app/App.tsx b/packages/core-api/src/app/App.tsx
index 575dcfc59d..7cc37c2544 100644
--- a/packages/core-api/src/app/App.tsx
+++ b/packages/core-api/src/app/App.tsx
@@ -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 = ;
+ noConfigNode = ;
} else if (config.error) {
const { BootErrorPage } = this.components;
- childNode = ;
+ noConfigNode = (
+
+ );
}
+ // Before the config is loaded we can't use a router, so exit early
+ if (noConfigNode) {
+ return (
+
+ {noConfigNode}
+
+ );
+ }
+
+ 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 (
- {childNode}
+
+ {children}
+
);
diff --git a/packages/core-api/src/app/types.ts b/packages/core-api/src/app/types.ts
index 4d685a6312..664c5238bc 100644
--- a/packages/core-api/src/app/types.ts
+++ b/packages/core-api/src/app/types.ts
@@ -30,6 +30,7 @@ export type AppComponents = {
NotFoundErrorPage: ComponentType<{}>;
BootErrorPage: ComponentType;
Progress: ComponentType<{}>;
+ Router: ComponentType<{ basename?: string }>;
};
/**
diff --git a/packages/core/src/api-wrappers/createApp.tsx b/packages/core/src/api-wrappers/createApp.tsx
index 8ed5cddad7..f69451249b 100644
--- a/packages/core/src/api-wrappers/createApp.tsx
+++ b/packages/core/src/api-wrappers/createApp.tsx
@@ -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 (
-
+
-
+
);
};
@@ -100,6 +100,7 @@ export function createApp(options?: AppOptions) {
NotFoundErrorPage: DefaultNotFoundPage,
BootErrorPage: DefaultBootErrorPage,
Progress: Progress,
+ Router: BrowserRouter,
...options?.components,
};
const themes = options?.themes ?? [
diff --git a/packages/dev-utils/src/devApp/render.tsx b/packages/dev-utils/src/devApp/render.tsx
index aa952f1ca5..8900ecd54b 100644
--- a/packages/dev-utils/src/devApp/render.tsx
+++ b/packages/dev-utils/src/devApp/render.tsx
@@ -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 {
{this.rootChildren}
-
-
- {sidebar}
-
-
-
+
+ {sidebar}
+
+
);
};
diff --git a/packages/test-utils/src/testUtils/appWrappers.tsx b/packages/test-utils/src/testUtils/appWrappers.tsx
index 2450604976..6fa6f62d8c 100644
--- a/packages/test-utils/src/testUtils/appWrappers.tsx
+++ b/packages/test-utils/src/testUtils/appWrappers.tsx
@@ -55,6 +55,9 @@ export function wrapInTestApp(
NotFoundErrorPage,
BootErrorPage,
Progress,
+ Router: ({ children }) => (
+
+ ),
},
icons: defaultSystemIcons,
plugins: [],
@@ -79,9 +82,7 @@ export function wrapInTestApp(
return (
-
-
-
+
);
}