Skip to content

Commit c75f7b4

Browse files
committed
use individual route for each KP app (#65977)
* use individual route for each KP app * cleanup
1 parent 49be10c commit c75f7b4

6 files changed

Lines changed: 48 additions & 22 deletions

File tree

src/core/public/application/application_service.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ export class ApplicationService {
198198
appBasePath: basePath.prepend(app.appRoute!),
199199
mount: wrapMount(plugin, app),
200200
unmountBeforeMounting: false,
201+
legacy: false,
201202
});
202203
},
203204
registerLegacyApp: app => {
@@ -232,6 +233,7 @@ export class ApplicationService {
232233
appBasePath,
233234
mount,
234235
unmountBeforeMounting: true,
236+
legacy: true,
235237
});
236238
},
237239
registerAppUpdater: (appUpdater$: Observable<AppUpdater>) =>

src/core/public/application/integration_tests/router.test.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { createRenderer, createAppMounter, createLegacyAppMounter, getUnmounter
2727
import { AppStatus } from '../types';
2828
import { ScopedHistory } from '../scoped_history';
2929

30-
describe('AppContainer', () => {
30+
describe('AppRouter', () => {
3131
let mounters: MockedMounterMap<EitherApp>;
3232
let globalHistory: History;
3333
let appStatuses$: BehaviorSubject<Map<string, AppStatus>>;
@@ -78,6 +78,16 @@ describe('AppContainer', () => {
7878
history.push('/subpath');
7979
},
8080
}),
81+
createAppMounter({
82+
appId: 'app5',
83+
html: '<div>App 5</div>',
84+
appRoute: '/app/my-app/app5',
85+
}),
86+
createAppMounter({
87+
appId: 'app6',
88+
html: '<div>App 6</div>',
89+
appRoute: '/app/my-app/app6',
90+
}),
8191
] as Array<MockedMounterTuple<EitherApp>>);
8292
globalHistory = createMemoryHistory();
8393
appStatuses$ = mountersToAppStatus$();
@@ -282,6 +292,16 @@ describe('AppContainer', () => {
282292
expect(unmount).not.toHaveBeenCalled();
283293
});
284294

295+
it('allows multiple apps with the same `/app/appXXX` appRoute prefix', async () => {
296+
await navigate('/app/my-app/app5/path');
297+
expect(mounters.get('app5')!.mounter.mount).toHaveBeenCalledTimes(1);
298+
expect(mounters.get('app6')!.mounter.mount).toHaveBeenCalledTimes(0);
299+
300+
await navigate('/app/my-app/app6/another-path');
301+
expect(mounters.get('app5')!.mounter.mount).toHaveBeenCalledTimes(1);
302+
expect(mounters.get('app6')!.mounter.mount).toHaveBeenCalledTimes(1);
303+
});
304+
285305
it('should not remount when when changing pages within app using hash history', async () => {
286306
globalHistory = createHashHistory();
287307
update = createRenderer(

src/core/public/application/integration_tests/utils.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export const createAppMounter = ({
6161
mounter: {
6262
appRoute,
6363
appBasePath: appRoute,
64+
legacy: false,
6465
mount: jest.fn(async (params: AppMountParameters) => {
6566
const { appBasePath: basename, element } = params;
6667
Object.assign(element, {
@@ -88,6 +89,7 @@ export const createLegacyAppMounter = (
8889
appRoute: `/app/${appId.split(':')[0]}`,
8990
appBasePath: `/app/${appId.split(':')[0]}`,
9091
unmountBeforeMounting: true,
92+
legacy: true,
9193
mount: legacyMount,
9294
},
9395
unmount: jest.fn(),

src/core/public/application/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ export type Mounter<T = App | LegacyApp> = SelectivePartial<
549549
appRoute: string;
550550
appBasePath: string;
551551
mount: T extends LegacyApp ? LegacyAppMounter : AppMounter;
552+
legacy: boolean;
552553
unmountBeforeMounting: T extends LegacyApp ? true : boolean;
553554
},
554555
T extends LegacyApp ? never : 'unmountBeforeMounting'

src/core/public/application/ui/app_container.test.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ describe('AppContainer', () => {
5454
appBasePath: '/base-path',
5555
appRoute: '/some-route',
5656
unmountBeforeMounting: false,
57+
legacy: false,
5758
mount: async ({ element }: AppMountParameters) => {
5859
await promise;
5960
const container = document.createElement('div');
@@ -138,9 +139,10 @@ describe('AppContainer', () => {
138139
it('should call setIsMounting(false) if mounting throws', async () => {
139140
const [waitPromise, resolvePromise] = createResolver();
140141
const mounter = {
141-
appBasePath: '/base-path',
142+
appBasePath: '/base-path/some-route',
142143
appRoute: '/some-route',
143144
unmountBeforeMounting: false,
145+
legacy: false,
144146
mount: async ({ element }: AppMountParameters) => {
145147
await waitPromise;
146148
throw new Error(`Mounting failed!`);

src/core/public/application/ui/app_router.tsx

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -55,26 +55,25 @@ export const AppRouter: FunctionComponent<Props> = ({
5555
return (
5656
<Router history={history}>
5757
<Switch>
58-
{[...mounters].flatMap(([appId, mounter]) =>
59-
// Remove /app paths from the routes as they will be handled by the
60-
// "named" route parameter `:appId` below
61-
mounter.appBasePath.startsWith('/app')
62-
? []
63-
: [
64-
<Route
65-
key={mounter.appRoute}
66-
path={mounter.appRoute}
67-
render={({ match: { url } }) => (
68-
<AppContainer
69-
appPath={url}
70-
appStatus={appStatuses.get(appId) ?? AppStatus.inaccessible}
71-
createScopedHistory={createScopedHistory}
72-
{...{ appId, mounter, setAppLeaveHandler, setIsMounting }}
73-
/>
74-
)}
75-
/>,
76-
]
77-
)}
58+
{[...mounters]
59+
// legacy apps can have multiple sub-apps registered with the same route
60+
// which needs additional logic that is handled in the catch-all route below
61+
.filter(([_, mounter]) => !mounter.legacy)
62+
.map(([appId, mounter]) => (
63+
<Route
64+
key={mounter.appRoute}
65+
path={mounter.appRoute}
66+
render={({ match: { url } }) => (
67+
<AppContainer
68+
appPath={url}
69+
appStatus={appStatuses.get(appId) ?? AppStatus.inaccessible}
70+
createScopedHistory={createScopedHistory}
71+
{...{ appId, mounter, setAppLeaveHandler, setIsMounting }}
72+
/>
73+
)}
74+
/>
75+
))}
76+
{/* handler for legacy apps and used as a catch-all to display 404 page on not existing /app/appId apps*/}
7877
<Route
7978
path="/app/:appId"
8079
render={({

0 commit comments

Comments
 (0)