core-api: restructure route ref types to use a symbol to determine type + deprecate more fields

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2021-02-28 15:22:16 +01:00
parent cfc83cac16
commit 762c7c5421
7 changed files with 69 additions and 57 deletions
+1 -2
View File
@@ -50,14 +50,13 @@ import {
} from '../extensions/traversal';
import { IconComponent, IconComponentMap, IconKey } from '../icons';
import { BackstagePlugin } from '../plugin';
import { RouteRef } from '../routing';
import { RouteRef, ExternalRouteRef } from '../routing';
import {
routeObjectCollector,
routeParentCollector,
routePathCollector,
} from '../routing/collectors';
import { RoutingProvider, validateRoutes } from '../routing/hooks';
import { ExternalRouteRef } from '../routing/RouteRef';
import { AppContextProvider } from './AppContext';
import { AppIdentity } from './AppIdentity';
import { AppThemeProvider } from './AppThemeProvider';
+1 -2
View File
@@ -15,9 +15,8 @@
*/
import { ComponentType } from 'react';
import { RouteRef } from '../routing';
import { RouteRef, ExternalRouteRef } from '../routing';
import { AnyApiFactory } from '../apis/system';
import { ExternalRouteRef } from '../routing/RouteRef';
export type RouteOptions = {
// Whether the route path must match exactly, defaults to true.
+33 -25
View File
@@ -14,19 +14,31 @@
* limitations under the License.
*/
import { RouteRef } from './types';
import { RouteRef, ExternalRouteRef, routeRefType } from './types';
import { IconComponent } from '../icons';
// TODO(Rugvip): Remove this once we get rid of the deprecated fields, it's not exported
export type RouteRefConfig<Params extends { [param in string]: string }> = {
params?: Array<keyof Params>;
/** @deprecated Route refs no longer decide their own path */
path?: string;
icon?: IconComponent;
title: string;
};
export class AbsoluteRouteRef<Params extends { [param in string]: string }> {
constructor(private readonly config: RouteRefConfig<Params>) {}
class RouteRefBaseBase {
constructor(type: string, id: string) {
this.toString = () => `routeRef{type=${type},id=${id}}`;
}
}
export class RouteRefImpl<
Params extends { [param in string]: string }
> extends RouteRefBaseBase {
readonly [routeRefType] = 'absolute';
constructor(private readonly config: RouteRefConfig<Params>) {
super('absolute', config.title);
}
get icon() {
return this.config.icon;
@@ -40,10 +52,6 @@ export class AbsoluteRouteRef<Params extends { [param in string]: string }> {
get title() {
return this.config.title;
}
toString() {
return `routeRef{title=${this.title}}`;
}
}
export function createRouteRef<
@@ -58,22 +66,27 @@ export function createRouteRef<
params?: ParamKey[];
/** @deprecated Route refs no longer decide their own path */
path?: string;
/** @deprecated Route refs no longer decide their own icon */
icon?: IconComponent;
/** @deprecated Route refs no longer decide their own title */
title: string;
}): RouteRef<Params> {
return new AbsoluteRouteRef<Params>(config);
return new RouteRefImpl<Params>(config);
}
export class ExternalRouteRef<Optional extends boolean = true> {
readonly optional: boolean;
export class ExternalRouteRefImpl<
Optional extends boolean
> extends RouteRefBaseBase {
readonly [routeRefType] = 'external';
private constructor({ id, optional }: ExternalRouteRefOptions<Optional>) {
this.toString = () => `externalRouteRef{${id}}`;
this.optional = Boolean(optional);
constructor(id: string, readonly optional: Optional) {
super('external', id);
}
}
export type ExternalRouteRefOptions<Optional extends boolean = false> = {
export function createExternalRouteRef<
Optional extends boolean = false
>(options: {
/**
* An identifier for this route, used to identify it in error messages
*/
@@ -86,14 +99,9 @@ export type ExternalRouteRefOptions<Optional extends boolean = false> = {
* if they aren't, `useRouteRef` will return `undefined`.
*/
optional?: Optional;
};
export function createExternalRouteRef<Optional extends boolean = false>(
options: ExternalRouteRefOptions<Optional>,
): ExternalRouteRef<Optional> {
return new ((ExternalRouteRef as unknown) as {
new (options: ExternalRouteRefOptions<Optional>): ExternalRouteRef<
Optional
>;
})(options);
}): ExternalRouteRef<Optional> {
return new ExternalRouteRefImpl<Optional>(
options.id,
Boolean(options.optional) as Optional,
);
}
+1 -2
View File
@@ -38,10 +38,9 @@ import {
import {
createRouteRef,
createExternalRouteRef,
ExternalRouteRef,
RouteRefConfig,
} from './RouteRef';
import { AnyRouteRef, RouteRef } from './types';
import { AnyRouteRef, RouteRef, ExternalRouteRef } from './types';
const mockConfig = (extra?: Partial<RouteRefConfig<{}>>) => ({
path: '/unused',
+6 -2
View File
@@ -15,9 +15,13 @@
*/
import React, { createContext, ReactNode, useContext, useMemo } from 'react';
import { AnyRouteRef, BackstageRouteObject, RouteRef } from './types';
import {
AnyRouteRef,
BackstageRouteObject,
RouteRef,
ExternalRouteRef,
} from './types';
import { generatePath, matchRoutes, useLocation } from 'react-router-dom';
import { ExternalRouteRef } from './RouteRef';
// The extra TS magic here is to require a single params argument if the RouteRef
// had at least one param defined, but require 0 arguments if there are no params defined.
+2 -5
View File
@@ -19,12 +19,9 @@ export type {
AbsoluteRouteRef,
ConcreteRoute,
MutableRouteRef,
ExternalRouteRef,
} from './types';
export { FlatRoutes } from './FlatRoutes';
export {
createRouteRef,
createExternalRouteRef,
ExternalRouteRef,
} from './RouteRef';
export { createRouteRef, createExternalRouteRef } from './RouteRef';
export type { RouteRefConfig } from './RouteRef';
export { useRouteRef } from './hooks';
+25 -19
View File
@@ -15,35 +15,41 @@
*/
import { IconComponent } from '../icons';
import { ExternalRouteRef } from './RouteRef';
import { getGlobalSingleton } from '../lib/globalObject';
// @ts-ignore, we're just embedding the Params type for usage in other places
export type RouteRef<Params extends { [param in string]: string } = {}> = {
// TODO(Rugvip): Remove path, look up via registry instead
export const routeRefType: unique symbol = getGlobalSingleton<any>(
'route-ref-type',
() => Symbol('route-ref-type'),
);
export type RouteRef<Params extends { [param in string]: string } = any> = {
[routeRefType]: 'absolute';
params?: keyof Params;
// TODO(Rugvip): Remove all of these once plugins don't rely on the path
/** @deprecated paths are no longer accessed directly from RouteRefs, use useRouteRef instead */
path: string;
/** @deprecated icons are no longer accessed via RouteRefs */
icon?: IconComponent;
title: string;
/** @deprecated titles are no longer accessed via RouteRefs */
title?: string;
};
export type ExternalRouteRef<Optional extends boolean = any> = {
[routeRefType]: 'external';
optional?: Optional;
};
export type AnyRouteRef = RouteRef<any> | ExternalRouteRef<any>;
/**
* This type should not be used
* @deprecated
*/
// TODO(Rugvip): None of these should be found in the wild anymore, remove in next minor release
/** @deprecated */
export type ConcreteRoute = {};
/**
* This type should not be used, use RouteRef instead
* @deprecated
*/
/** @deprecated */
export type AbsoluteRouteRef = RouteRef<{}>;
/**
* This type should not be used, use RouteRef instead
* @deprecated
*/
/** @deprecated */
export type MutableRouteRef = RouteRef<{}>;
// A duplicate of the react-router RouteObject, but with routeRef added