mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
PageRegistration / PageMenuRegistration fixes & more simplification (#1386)
* PageRegistration & PageMenuRegistration fixes & more simplification, fix #1383 Signed-off-by: Roman <ixrock@gmail.com> * clean up test, deprecate page.routePath Signed-off-by: Roman <ixrock@gmail.com> * fix: proper `isActive` state matching for page-menu-target to page considering current document location Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
parent
596e60a6f3
commit
13dbdac2f2
@ -6,7 +6,7 @@ export default class SupportPageMainExtension extends LensMainExtension {
|
|||||||
parentId: "help",
|
parentId: "help",
|
||||||
label: "Support",
|
label: "Support",
|
||||||
click: () => {
|
click: () => {
|
||||||
this.navigate("/support");
|
this.navigate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -5,8 +5,6 @@ import { SupportPage } from "./src/support";
|
|||||||
export default class SupportPageRendererExtension extends LensRendererExtension {
|
export default class SupportPageRendererExtension extends LensRendererExtension {
|
||||||
globalPages: Interface.PageRegistration[] = [
|
globalPages: Interface.PageRegistration[] = [
|
||||||
{
|
{
|
||||||
id: "support",
|
|
||||||
routePath: "/support",
|
|
||||||
components: {
|
components: {
|
||||||
Page: SupportPage,
|
Page: SupportPage,
|
||||||
}
|
}
|
||||||
@ -16,7 +14,7 @@ export default class SupportPageRendererExtension extends LensRendererExtension
|
|||||||
statusBarItems: Interface.StatusBarRegistration[] = [
|
statusBarItems: Interface.StatusBarRegistration[] = [
|
||||||
{
|
{
|
||||||
item: (
|
item: (
|
||||||
<div className="SupportPageIcon flex align-center" onClick={() => this.navigate("/support")}>
|
<div className="SupportPageIcon flex align-center" onClick={() => this.navigate()}>
|
||||||
<Component.Icon interactive material="help" smallest/>
|
<Component.Icon interactive material="help" smallest/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -2,14 +2,18 @@ import type { MenuRegistration } from "./registries/menu-registry";
|
|||||||
import { observable } from "mobx";
|
import { observable } from "mobx";
|
||||||
import { LensExtension } from "./lens-extension"
|
import { LensExtension } from "./lens-extension"
|
||||||
import { WindowManager } from "../main/window-manager";
|
import { WindowManager } from "../main/window-manager";
|
||||||
import { getPageUrl } from "./registries/page-registry"
|
import { getExtensionPageUrl } from "./registries/page-registry"
|
||||||
|
|
||||||
export class LensMainExtension extends LensExtension {
|
export class LensMainExtension extends LensExtension {
|
||||||
@observable.shallow appMenus: MenuRegistration[] = []
|
@observable.shallow appMenus: MenuRegistration[] = []
|
||||||
|
|
||||||
async navigate(location?: string, frameId?: number) {
|
async navigate<P extends object>(pageId?: string, params?: P, frameId?: number) {
|
||||||
const windowManager = WindowManager.getInstance<WindowManager>();
|
const windowManager = WindowManager.getInstance<WindowManager>();
|
||||||
const url = getPageUrl(this, location); // get full path to extension's page
|
const pageUrl = getExtensionPageUrl({
|
||||||
await windowManager.navigate(url, frameId);
|
extensionId: this.name,
|
||||||
|
pageId: pageId,
|
||||||
|
params: params ?? {}, // compile to url with params
|
||||||
|
});
|
||||||
|
await windowManager.navigate(pageUrl, frameId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { AppPreferenceRegistration, ClusterFeatureRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration, KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, } from "./registries"
|
import type { AppPreferenceRegistration, ClusterFeatureRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration, KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, } from "./registries"
|
||||||
import { observable } from "mobx";
|
import { observable } from "mobx";
|
||||||
import { LensExtension } from "./lens-extension"
|
import { LensExtension } from "./lens-extension"
|
||||||
import { getPageUrl } from "./registries/page-registry"
|
import { getExtensionPageUrl } from "./registries/page-registry"
|
||||||
|
|
||||||
export class LensRendererExtension extends LensExtension {
|
export class LensRendererExtension extends LensExtension {
|
||||||
@observable.shallow globalPages: PageRegistration[] = []
|
@observable.shallow globalPages: PageRegistration[] = []
|
||||||
@ -15,8 +15,13 @@ export class LensRendererExtension extends LensExtension {
|
|||||||
@observable.shallow kubeObjectDetailItems: KubeObjectDetailRegistration[] = []
|
@observable.shallow kubeObjectDetailItems: KubeObjectDetailRegistration[] = []
|
||||||
@observable.shallow kubeObjectMenuItems: KubeObjectMenuRegistration[] = []
|
@observable.shallow kubeObjectMenuItems: KubeObjectMenuRegistration[] = []
|
||||||
|
|
||||||
async navigate(location?: string) {
|
async navigate<P extends object>(pageId?: string, params?: P) {
|
||||||
const { navigate } = await import("../renderer/navigation");
|
const { navigate } = await import("../renderer/navigation");
|
||||||
navigate(getPageUrl(this, location));
|
const pageUrl = getExtensionPageUrl({
|
||||||
|
extensionId: this.name,
|
||||||
|
pageId: pageId,
|
||||||
|
params: params ?? {}, // compile to url with params
|
||||||
|
});
|
||||||
|
navigate(pageUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { getPageUrl, globalPageRegistry } from "../page-registry"
|
import { getExtensionPageUrl, globalPageRegistry, PageRegistration } from "../page-registry"
|
||||||
import { LensExtension } from "../../lens-extension"
|
import { LensExtension } from "../../lens-extension"
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
@ -18,20 +18,19 @@ describe("getPageUrl", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("returns a page url for extension", () => {
|
it("returns a page url for extension", () => {
|
||||||
expect(getPageUrl(ext)).toBe("/extension/foo-bar")
|
expect(getExtensionPageUrl({ extensionId: ext.name })).toBe("/extension/foo-bar")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("allows to pass base url as parameter", () => {
|
it("allows to pass base url as parameter", () => {
|
||||||
expect(getPageUrl(ext, "/test")).toBe("/extension/foo-bar/test")
|
expect(getExtensionPageUrl({ extensionId: ext.name, pageId: "/test" })).toBe("/extension/foo-bar/test")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("removes @", () => {
|
it("removes @", () => {
|
||||||
ext.manifest.name = "@foo/bar"
|
expect(getExtensionPageUrl({ extensionId: "@foo/bar" })).toBe("/extension/foo-bar")
|
||||||
expect(getPageUrl(ext)).toBe("/extension/foo-bar")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("adds / prefix", () => {
|
it("adds / prefix", () => {
|
||||||
expect(getPageUrl(ext, "test")).toBe("/extension/foo-bar/test")
|
expect(getExtensionPageUrl({ extensionId: ext.name, pageId: "test" })).toBe("/extension/foo-bar/test")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -57,12 +56,24 @@ describe("globalPageRegistry", () => {
|
|||||||
id: "another-page",
|
id: "another-page",
|
||||||
components: {
|
components: {
|
||||||
Page: () => React.createElement('Text')
|
Page: () => React.createElement('Text')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
components: {
|
||||||
|
Page: () => React.createElement('Default')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
], ext)
|
], ext)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("getByPageMenuTarget", () => {
|
describe("getByPageMenuTarget", () => {
|
||||||
|
it("matching to first registered page without id", () => {
|
||||||
|
const page = globalPageRegistry.getByPageMenuTarget({ extensionId: ext.name })
|
||||||
|
expect(page.id).toEqual(undefined);
|
||||||
|
expect(page.extensionId).toEqual(ext.name);
|
||||||
|
expect(page.routePath).toEqual(getExtensionPageUrl({ extensionId: ext.name }));
|
||||||
|
});
|
||||||
|
|
||||||
it("returns matching page", () => {
|
it("returns matching page", () => {
|
||||||
const page = globalPageRegistry.getByPageMenuTarget({
|
const page = globalPageRegistry.getByPageMenuTarget({
|
||||||
pageId: "test-page",
|
pageId: "test-page",
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
// Base class for extensions-api registries
|
// Base class for extensions-api registries
|
||||||
import { action, observable } from "mobx";
|
import { action, observable } from "mobx";
|
||||||
|
import { LensExtension } from "../lens-extension";
|
||||||
|
|
||||||
export class BaseRegistry<T = any> {
|
export class BaseRegistry<T = object, I extends T = T> {
|
||||||
private items = observable<T>([], { deep: false });
|
private items = observable<T>([], { deep: false });
|
||||||
|
|
||||||
getItems(): T[] {
|
getItems(): I[] {
|
||||||
return this.items.toJS();
|
return this.items.toJS() as I[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
add(items: T | T[], ext?: LensExtension): () => void; // allow method overloading with required "ext"
|
||||||
@action
|
@action
|
||||||
add(items: T | T[]) {
|
add(items: T | T[]) {
|
||||||
const normalizedItems = (Array.isArray(items) ? items : [items])
|
const normalizedItems = (Array.isArray(items) ? items : [items])
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
// Extensions-api -> Register page menu items
|
// Extensions-api -> Register page menu items
|
||||||
|
import type { IconProps } from "../../renderer/components/icon";
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { action } from "mobx";
|
import { action } from "mobx";
|
||||||
import type { IconProps } from "../../renderer/components/icon";
|
|
||||||
import { BaseRegistry } from "./base-registry";
|
import { BaseRegistry } from "./base-registry";
|
||||||
import { LensExtension } from "../lens-extension";
|
import { LensExtension } from "../lens-extension";
|
||||||
|
|
||||||
export interface PageMenuTarget {
|
export interface PageMenuTarget<P extends object = any> {
|
||||||
pageId: string;
|
|
||||||
extensionId?: string;
|
extensionId?: string;
|
||||||
params?: object;
|
pageId?: string;
|
||||||
|
params?: P;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PageMenuRegistration {
|
export interface PageMenuRegistration {
|
||||||
@ -22,19 +21,19 @@ export interface PageMenuComponents {
|
|||||||
Icon: React.ComponentType<IconProps>;
|
Icon: React.ComponentType<IconProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PageMenuRegistry<T extends PageMenuRegistration> extends BaseRegistry<T> {
|
export class PageMenuRegistry extends BaseRegistry<PageMenuRegistration, Required<PageMenuRegistration>> {
|
||||||
|
|
||||||
@action
|
@action
|
||||||
add(items: T[], ext?: LensExtension) {
|
add(items: PageMenuRegistration[], ext: LensExtension) {
|
||||||
const normalizedItems = items.map((menu) => {
|
const normalizedItems = items.map(menuItem => {
|
||||||
if (menu.target && !menu.target.extensionId) {
|
menuItem.target = {
|
||||||
menu.target.extensionId = ext.name
|
extensionId: ext.name,
|
||||||
}
|
...(menuItem.target || {}),
|
||||||
return menu
|
};
|
||||||
|
return menuItem
|
||||||
})
|
})
|
||||||
return super.add(normalizedItems);
|
return super.add(normalizedItems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const globalPageMenuRegistry = new PageMenuRegistry<PageMenuRegistration>();
|
export const globalPageMenuRegistry = new PageMenuRegistry();
|
||||||
export const clusterPageMenuRegistry = new PageMenuRegistry<PageMenuRegistration>();
|
export const clusterPageMenuRegistry = new PageMenuRegistry();
|
||||||
|
|||||||
@ -1,59 +1,96 @@
|
|||||||
// Extensions-api -> Custom page registration
|
// Extensions-api -> Custom page registration
|
||||||
|
import type { PageMenuTarget } from "./page-menu-registry";
|
||||||
import React from "react";
|
import type React from "react";
|
||||||
|
import path from "path";
|
||||||
import { action } from "mobx";
|
import { action } from "mobx";
|
||||||
import { compile } from "path-to-regexp";
|
import { compile } from "path-to-regexp";
|
||||||
import { BaseRegistry } from "./base-registry";
|
import { BaseRegistry } from "./base-registry";
|
||||||
import { LensExtension } from "../lens-extension"
|
import { LensExtension } from "../lens-extension";
|
||||||
import type { PageMenuTarget } from "./page-menu-registry";
|
import logger from "../../main/logger";
|
||||||
|
|
||||||
export interface PageRegistration {
|
export interface PageRegistration {
|
||||||
id: string; // will be automatically prefixed with extension name
|
/**
|
||||||
routePath?: string; // additional (suffix) route path to base extension's route: "/extension/:name"
|
* Page ID or additional route path to indicate uniqueness within current extension registered pages
|
||||||
exact?: boolean; // route matching flag, see: https://reactrouter.com/web/api/NavLink/exact-bool
|
* Might contain special url placeholders, e.g. "/users/:userId?" (? - marks as optional param)
|
||||||
|
* When not provided, first registered page without "id" would be used for page-menus without target.pageId for same extension
|
||||||
|
*/
|
||||||
|
id?: string;
|
||||||
|
/**
|
||||||
|
* Alias to page ID which assume to be used as path with possible :param placeholders
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
routePath?: string;
|
||||||
|
/**
|
||||||
|
* Strict route matching to provided page-id, read also: https://reactrouter.com/web/api/NavLink/exact-bool
|
||||||
|
* In case when more than one page registered at same extension "pageId" is required to identify different pages,
|
||||||
|
* It might be useful to provide `exact: true` in some cases to avoid overlapping routes.
|
||||||
|
* Without {exact:true} second page never matches since first page-id/route already includes partial route.
|
||||||
|
* @example const pages = [
|
||||||
|
* {id: "/users", exact: true},
|
||||||
|
* {id: "/users/:userId?"}
|
||||||
|
* ]
|
||||||
|
* Pro-tip: registering pages in opposite order will make same effect without "exact".
|
||||||
|
*/
|
||||||
|
exact?: boolean;
|
||||||
components: PageComponents;
|
components: PageComponents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RegisteredPage extends PageRegistration {
|
||||||
|
extensionId: string; // required for compiling registered page to url with page-menu-target to compare
|
||||||
|
routePath: string; // full route-path to registered extension page
|
||||||
|
}
|
||||||
|
|
||||||
export interface PageComponents {
|
export interface PageComponents {
|
||||||
Page: React.ComponentType<any>;
|
Page: React.ComponentType<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const routePrefix = "/extension/:name"
|
export function sanitizeExtensionName(name: string) {
|
||||||
|
|
||||||
export function sanitizeExtensioName(name: string) {
|
|
||||||
return name.replace("@", "").replace("/", "-")
|
return name.replace("@", "").replace("/", "-")
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPageUrl(ext: LensExtension, baseUrl = "") {
|
export function getExtensionPageUrl<P extends object>({ extensionId, pageId = "", params }: PageMenuTarget<P>): string {
|
||||||
if (baseUrl !== "" && !baseUrl.startsWith("/")) {
|
const extensionBaseUrl = compile(`/extension/:name`)({
|
||||||
baseUrl = "/" + baseUrl
|
name: sanitizeExtensionName(extensionId), // compile only with extension-id first and define base path
|
||||||
|
});
|
||||||
|
const extPageRoutePath = path.join(extensionBaseUrl, pageId); // page-id might contain route :param-s, so don't compile yet
|
||||||
|
if (params) {
|
||||||
|
return compile(extPageRoutePath)(params); // might throw error when required params not passed
|
||||||
}
|
}
|
||||||
const validUrlName = sanitizeExtensioName(ext.name);
|
return extPageRoutePath;
|
||||||
return compile(routePrefix)({ name: validUrlName }) + baseUrl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PageRegistry<T extends PageRegistration> extends BaseRegistry<T> {
|
export class PageRegistry extends BaseRegistry<PageRegistration, RegisteredPage> {
|
||||||
|
|
||||||
@action
|
@action
|
||||||
add(items: T[], ext?: LensExtension) {
|
add(items: PageRegistration[], ext: LensExtension) {
|
||||||
const normalizedItems = items.map((page) => {
|
let registeredPages: RegisteredPage[] = [];
|
||||||
if (!page.routePath) {
|
try {
|
||||||
page.routePath = `/${page.id}`
|
registeredPages = items.map(page => ({
|
||||||
}
|
...page,
|
||||||
page.routePath = getPageUrl(ext, page.routePath)
|
extensionId: ext.name,
|
||||||
return page
|
routePath: getExtensionPageUrl({ extensionId: ext.name, pageId: page.id ?? page.routePath }),
|
||||||
|
}))
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`[EXTENSION]: page-registration failed`, {
|
||||||
|
items,
|
||||||
|
extension: ext,
|
||||||
|
error: String(err),
|
||||||
})
|
})
|
||||||
return super.add(normalizedItems);
|
}
|
||||||
|
return super.add(registeredPages);
|
||||||
}
|
}
|
||||||
|
|
||||||
getByPageMenuTarget(target: PageMenuTarget) {
|
getUrl<P extends object>({ extensionId, id: pageId }: RegisteredPage, params?: P) {
|
||||||
if (!target) {
|
return getExtensionPageUrl({ extensionId, pageId, params });
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
const routePath = `/extension/${sanitizeExtensioName(target.extensionId)}/`
|
|
||||||
return this.getItems().find((page) => page.routePath.startsWith(routePath) && page.id === target.pageId) || null
|
getByPageMenuTarget(target: PageMenuTarget = {}): RegisteredPage | null {
|
||||||
|
const targetUrl = getExtensionPageUrl(target);
|
||||||
|
return this.getItems().find(({ id: pageId, extensionId }) => {
|
||||||
|
const pageUrl = getExtensionPageUrl({ extensionId, pageId, params: target.params }); // compiled with provided params
|
||||||
|
return targetUrl === pageUrl;
|
||||||
|
}) || null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const globalPageRegistry = new PageRegistry<PageRegistration>();
|
export const globalPageRegistry = new PageRegistry();
|
||||||
export const clusterPageRegistry = new PageRegistry<PageRegistration>();
|
export const clusterPageRegistry = new PageRegistry();
|
||||||
|
|||||||
@ -29,7 +29,6 @@ import { CustomResources } from "./+custom-resources/custom-resources";
|
|||||||
import { crdRoute } from "./+custom-resources";
|
import { crdRoute } from "./+custom-resources";
|
||||||
import { isAllowedResource } from "../../common/rbac";
|
import { isAllowedResource } from "../../common/rbac";
|
||||||
import { MainLayout } from "./layout/main-layout";
|
import { MainLayout } from "./layout/main-layout";
|
||||||
import { TabLayout, TabLayoutRoute } from "./layout/tab-layout";
|
|
||||||
import { ErrorBoundary } from "./error-boundary";
|
import { ErrorBoundary } from "./error-boundary";
|
||||||
import { Terminal } from "./dock/terminal";
|
import { Terminal } from "./dock/terminal";
|
||||||
import { getHostedCluster, getHostedClusterId } from "../../common/cluster-store";
|
import { getHostedCluster, getHostedClusterId } from "../../common/cluster-store";
|
||||||
@ -37,7 +36,6 @@ import logger from "../../main/logger";
|
|||||||
import { clusterIpc } from "../../common/cluster-ipc";
|
import { clusterIpc } from "../../common/cluster-ipc";
|
||||||
import { webFrame } from "electron";
|
import { webFrame } from "electron";
|
||||||
import { clusterPageRegistry } from "../../extensions/registries/page-registry";
|
import { clusterPageRegistry } from "../../extensions/registries/page-registry";
|
||||||
import { clusterPageMenuRegistry } from "../../extensions/registries";
|
|
||||||
import { extensionLoader } from "../../extensions/extension-loader";
|
import { extensionLoader } from "../../extensions/extension-loader";
|
||||||
import { appEventBus } from "../../common/event-bus";
|
import { appEventBus } from "../../common/event-bus";
|
||||||
import whatInput from 'what-input';
|
import whatInput from 'what-input';
|
||||||
@ -75,10 +73,7 @@ export class App extends React.Component {
|
|||||||
|
|
||||||
renderExtensionRoutes() {
|
renderExtensionRoutes() {
|
||||||
return clusterPageRegistry.getItems().map(({ components: { Page }, exact, routePath }) => {
|
return clusterPageRegistry.getItems().map(({ components: { Page }, exact, routePath }) => {
|
||||||
const Component = () => {
|
return <Route key={routePath} path={routePath} exact={exact} component={Page}/>
|
||||||
return <Page/>
|
|
||||||
};
|
|
||||||
return <Route key={routePath} path={routePath} exact={exact} component={Component}/>
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import { ClusterIcon } from "../cluster-icon";
|
|||||||
import { Icon } from "../icon";
|
import { Icon } from "../icon";
|
||||||
import { autobind, cssNames, IClassName } from "../../utils";
|
import { autobind, cssNames, IClassName } from "../../utils";
|
||||||
import { Badge } from "../badge";
|
import { Badge } from "../badge";
|
||||||
import { isActiveRoute, navigate } from "../../navigation";
|
import { navigate, navigation } from "../../navigation";
|
||||||
import { addClusterURL } from "../+add-cluster";
|
import { addClusterURL } from "../+add-cluster";
|
||||||
import { clusterSettingsURL } from "../+cluster-settings";
|
import { clusterSettingsURL } from "../+cluster-settings";
|
||||||
import { landingURL } from "../+landing-page";
|
import { landingURL } from "../+landing-page";
|
||||||
@ -22,8 +22,7 @@ import { Tooltip } from "../tooltip";
|
|||||||
import { ConfirmDialog } from "../confirm-dialog";
|
import { ConfirmDialog } from "../confirm-dialog";
|
||||||
import { clusterIpc } from "../../../common/cluster-ipc";
|
import { clusterIpc } from "../../../common/cluster-ipc";
|
||||||
import { clusterViewURL } from "./cluster-view.route";
|
import { clusterViewURL } from "./cluster-view.route";
|
||||||
import { globalPageMenuRegistry, globalPageRegistry } from "../../../extensions/registries";
|
import { getExtensionPageUrl, globalPageMenuRegistry, globalPageRegistry } from "../../../extensions/registries";
|
||||||
import { compile } from "path-to-regexp";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
className?: IClassName;
|
className?: IClassName;
|
||||||
@ -152,13 +151,15 @@ export class ClustersMenu extends React.Component<Props> {
|
|||||||
{globalPageMenuRegistry.getItems().map(({ title, target, components: { Icon } }) => {
|
{globalPageMenuRegistry.getItems().map(({ title, target, components: { Icon } }) => {
|
||||||
const registeredPage = globalPageRegistry.getByPageMenuTarget(target);
|
const registeredPage = globalPageRegistry.getByPageMenuTarget(target);
|
||||||
if (!registeredPage) return;
|
if (!registeredPage) return;
|
||||||
const { routePath, exact } = registeredPage;
|
const { extensionId, id: pageId } = registeredPage;
|
||||||
|
const pageUrl = getExtensionPageUrl({ extensionId, pageId, params: target.params });
|
||||||
|
const isActive = pageUrl === navigation.location.pathname;
|
||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
key={routePath}
|
key={pageUrl}
|
||||||
tooltip={title}
|
tooltip={title}
|
||||||
active={isActiveRoute({ path: routePath, exact })}
|
active={isActive}
|
||||||
onClick={() => navigate(compile(routePath)(target.params))}
|
onClick={() => navigate(pageUrl)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|||||||
@ -26,11 +26,10 @@ import { Network } from "../+network";
|
|||||||
import { crdStore } from "../+custom-resources/crd.store";
|
import { crdStore } from "../+custom-resources/crd.store";
|
||||||
import { CrdList, crdResourcesRoute, crdRoute, crdURL } from "../+custom-resources";
|
import { CrdList, crdResourcesRoute, crdRoute, crdURL } from "../+custom-resources";
|
||||||
import { CustomResources } from "../+custom-resources/custom-resources";
|
import { CustomResources } from "../+custom-resources/custom-resources";
|
||||||
import { isActiveRoute } from "../../navigation";
|
import { isActiveRoute, navigation } from "../../navigation";
|
||||||
import { isAllowedResource } from "../../../common/rbac"
|
import { isAllowedResource } from "../../../common/rbac"
|
||||||
import { Spinner } from "../spinner";
|
import { Spinner } from "../spinner";
|
||||||
import { clusterPageMenuRegistry, clusterPageRegistry } from "../../../extensions/registries";
|
import { clusterPageMenuRegistry, clusterPageRegistry, getExtensionPageUrl } from "../../../extensions/registries";
|
||||||
import { compile } from "path-to-regexp";
|
|
||||||
|
|
||||||
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
|
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
|
||||||
type SidebarContextValue = {
|
type SidebarContextValue = {
|
||||||
@ -195,15 +194,14 @@ export class Sidebar extends React.Component<Props> {
|
|||||||
{clusterPageMenuRegistry.getItems().map(({ title, target, components: { Icon } }) => {
|
{clusterPageMenuRegistry.getItems().map(({ title, target, components: { Icon } }) => {
|
||||||
const registeredPage = clusterPageRegistry.getByPageMenuTarget(target);
|
const registeredPage = clusterPageRegistry.getByPageMenuTarget(target);
|
||||||
if (!registeredPage) return;
|
if (!registeredPage) return;
|
||||||
const { routePath, exact } = registeredPage;
|
const { extensionId, id: pageId } = registeredPage;
|
||||||
const url = compile(routePath)(target.params)
|
const pageUrl = getExtensionPageUrl({ extensionId, pageId, params: target.params });
|
||||||
|
const isActive = pageUrl === navigation.location.pathname;
|
||||||
return (
|
return (
|
||||||
<SidebarNavItem
|
<SidebarNavItem
|
||||||
key={url}
|
key={pageUrl} url={pageUrl}
|
||||||
url={url}
|
text={title} icon={<Icon/>}
|
||||||
text={title}
|
isActive={isActive}
|
||||||
icon={<Icon/>}
|
|
||||||
isActive={isActiveRoute({ path: routePath, exact })}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|||||||
@ -22,8 +22,12 @@ export function navigate(location: LocationDescriptor) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function matchParams<P>(route: string | string[] | RouteProps) {
|
||||||
|
return matchPath<P>(navigation.location.pathname, route);
|
||||||
|
}
|
||||||
|
|
||||||
export function isActiveRoute(route: string | string[] | RouteProps): boolean {
|
export function isActiveRoute(route: string | string[] | RouteProps): boolean {
|
||||||
return !!matchPath(navigation.location.pathname, route);
|
return !!matchParams(route);
|
||||||
}
|
}
|
||||||
|
|
||||||
// common params for all pages
|
// common params for all pages
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user