)
}
diff --git a/extensions/support-page/renderer.tsx b/extensions/support-page/renderer.tsx
index b46eaa67aa..64b46b76b7 100644
--- a/extensions/support-page/renderer.tsx
+++ b/extensions/support-page/renderer.tsx
@@ -13,7 +13,7 @@ export default class SupportPageRendererExtension extends LensRendererExtension
}
]
- statusBarItems = [
+ statusBarItems: Interface.StatusBarRegistration[] = [
{
item: (
this.navigate(pageUrl)}>
diff --git a/package.json b/package.json
index 4e7bdaee23..ddcfb9452c 100644
--- a/package.json
+++ b/package.json
@@ -241,8 +241,6 @@
"openid-client": "^3.15.2",
"path-to-regexp": "^6.1.0",
"proper-lockfile": "^4.1.1",
- "react": "^16.14.0",
- "react-router": "^5.2.0",
"request": "^2.88.2",
"request-promise-native": "^1.0.8",
"semver": "^7.3.2",
@@ -363,9 +361,11 @@
"postinstall-postinstall": "^2.1.0",
"progress-bar-webpack-plugin": "^2.1.0",
"raw-loader": "^4.0.1",
+ "react": "^16.14.0",
"react-beautiful-dnd": "^13.0.0",
"react-dom": "^16.13.1",
"react-refresh": "^0.9.0",
+ "react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-select": "^3.1.0",
"react-window": "^1.8.5",
diff --git a/src/extensions/extension-loader.ts b/src/extensions/extension-loader.ts
index f38c7c3ae9..b75fd5147d 100644
--- a/src/extensions/extension-loader.ts
+++ b/src/extensions/extension-loader.ts
@@ -57,29 +57,29 @@ export class ExtensionLoader {
loadOnMain() {
logger.info('[EXTENSIONS-LOADER]: load on main')
this.autoInitExtensions((ext: LensMainExtension) => [
- registries.menuRegistry.add(ext.appMenus, { ext })
+ registries.menuRegistry.add(ext.appMenus, { key: ext })
]);
}
loadOnClusterManagerRenderer() {
logger.info('[EXTENSIONS-LOADER]: load on main renderer (cluster manager)')
this.autoInitExtensions((ext: LensRendererExtension) => [
- registries.globalPageRegistry.add(ext.globalPages, { ext }),
- registries.globalPageMenuRegistry.add(ext.globalPageMenus, { ext }),
- registries.appPreferenceRegistry.add(ext.appPreferences, { ext }),
- registries.clusterFeatureRegistry.add(ext.clusterFeatures, { ext }),
- registries.statusBarRegistry.add(ext.statusBarItems, { ext }),
+ registries.globalPageRegistry.add(ext.globalPages, { key: ext }),
+ registries.globalPageMenuRegistry.add(ext.globalPageMenus, { key: ext }),
+ registries.appPreferenceRegistry.add(ext.appPreferences, { key: ext }),
+ registries.clusterFeatureRegistry.add(ext.clusterFeatures, { key: ext }),
+ registries.statusBarRegistry.add(ext.statusBarItems, { key: ext }),
]);
}
loadOnClusterRenderer() {
logger.info('[EXTENSIONS-LOADER]: load on cluster renderer (dashboard)')
this.autoInitExtensions((ext: LensRendererExtension) => [
- registries.clusterPageRegistry.add(ext.clusterPages, { ext }),
- registries.clusterPageMenuRegistry.add(ext.clusterPageMenus, { ext }),
- registries.kubeObjectMenuRegistry.add(ext.kubeObjectMenuItems, { ext }),
- registries.kubeObjectDetailRegistry.add(ext.kubeObjectDetailItems, { ext }),
- registries.kubeObjectStatusRegistry.add(ext.kubeObjectStatusTexts, { ext })
+ registries.clusterPageRegistry.add(ext.clusterPages, { key: ext }),
+ registries.clusterPageMenuRegistry.add(ext.clusterPageMenus, { key: ext }),
+ registries.kubeObjectMenuRegistry.add(ext.kubeObjectMenuItems, { key: ext }),
+ registries.kubeObjectDetailRegistry.add(ext.kubeObjectDetailItems, { key: ext }),
+ registries.kubeObjectStatusRegistry.add(ext.kubeObjectStatusTexts, { key: ext })
])
}
diff --git a/src/extensions/lens-extension.ts b/src/extensions/lens-extension.ts
index 3931831fbf..ca135a0b39 100644
--- a/src/extensions/lens-extension.ts
+++ b/src/extensions/lens-extension.ts
@@ -44,11 +44,11 @@ export class LensExtension {
return this.manifest.description
}
- getPageUrl(baseUrl: string) {
- return compile(this.routePrefix)({ name: this.name }) + baseUrl
+ getPageUrl(baseUrl = "") {
+ return compile(this.routePrefix)({ name: this.name }) + baseUrl;
}
- getPageRoute(baseRoute: string) {
+ getPageRoute(baseRoute = "") {
return this.routePrefix + baseRoute;
}
diff --git a/src/extensions/lens-renderer-extension.ts b/src/extensions/lens-renderer-extension.ts
index e330293105..86be06e17e 100644
--- a/src/extensions/lens-renderer-extension.ts
+++ b/src/extensions/lens-renderer-extension.ts
@@ -1,11 +1,7 @@
-import type {
- AppPreferenceRegistration, ClusterFeatureRegistration,
- KubeObjectMenuRegistration, KubeObjectDetailRegistration, StatusBarRegistration, KubeObjectStatusRegistration,
- PageRegistration, PageMenuRegistration,
-} from "./registries"
+import type { AppPreferenceRegistration, ClusterFeatureRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration, KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, } from "./registries"
+import { ipcRenderer } from "electron"
import { observable } from "mobx";
import { LensExtension } from "./lens-extension"
-import { ipcRenderer } from "electron"
export class LensRendererExtension extends LensExtension {
@observable.shallow globalPages: PageRegistration[] = []
@@ -20,6 +16,6 @@ export class LensRendererExtension extends LensExtension {
@observable.shallow kubeObjectMenuItems: KubeObjectMenuRegistration[] = []
navigate(location: string) {
- ipcRenderer.emit("renderer:navigate", location)
+ ipcRenderer.emit("renderer:navigate", this.getPageUrl(location))
}
}
diff --git a/src/extensions/registries/app-preference-registry.ts b/src/extensions/registries/app-preference-registry.ts
index 6c54911f82..dc15ec3e20 100644
--- a/src/extensions/registries/app-preference-registry.ts
+++ b/src/extensions/registries/app-preference-registry.ts
@@ -1,12 +1,12 @@
import type React from "react"
-import { BaseRegistry } from "./base-registry";
+import { BaseRegistry, BaseRegistryItem } from "./base-registry";
export interface AppPreferenceComponents {
Hint: React.ComponentType
;
Input: React.ComponentType;
}
-export interface AppPreferenceRegistration {
+export interface AppPreferenceRegistration extends BaseRegistryItem {
title: string;
components: AppPreferenceComponents;
}
diff --git a/src/extensions/registries/base-registry.ts b/src/extensions/registries/base-registry.ts
index 1f362862d1..76678791f9 100644
--- a/src/extensions/registries/base-registry.ts
+++ b/src/extensions/registries/base-registry.ts
@@ -1,16 +1,24 @@
// Base class for extensions-api registries
import { action, observable } from "mobx";
import { LensExtension } from "../lens-extension";
+import { getRandId } from "../../common/utils";
+
+export type BaseRegistryKey = LensExtension | null;
+export type BaseRegistryItemId = string | symbol;
+
+export interface BaseRegistryItem {
+ id?: BaseRegistryItemId; // uniq id, generated automatically when not provided
+}
export interface BaseRegistryAddMeta {
- ext?: LensExtension | null;
+ key?: BaseRegistryKey;
merge?: boolean
}
-export class BaseRegistry {
- private items = observable.map([], { deep: false });
+export class BaseRegistry {
+ private items = observable.map([], { deep: false });
- getItems(): (T & { extension?: LensExtension })[] {
+ getItems(): (T & { extension?: LensExtension | null })[] {
return Array.from(this.items).map(([ext, items]) => {
return items.map(item => ({
...item,
@@ -19,29 +27,39 @@ export class BaseRegistry {
}).flat()
}
- @action
- add(items: T | T[], { ext = null, merge = true }: BaseRegistryAddMeta = {}) {
- const itemsList: T[] = Array.isArray(items) ? items : [items];
- if (merge && this.items.has(ext)) {
- const newItems = new Set(this.items.get(ext));
- itemsList.forEach(item => newItems.add(item))
- this.items.set(ext, [...newItems]);
- } else {
- this.items.set(ext, itemsList);
+ getById(itemId: BaseRegistryItemId, key?: BaseRegistryKey): T {
+ const byId = (item: BaseRegistryItem) => item.id === itemId;
+ if (key) {
+ return this.items.get(key)?.find(byId)
}
- return () => this.remove(itemsList, ext)
+ return this.getItems().find(byId);
}
@action
- remove(items: T[], key: LensExtension = null) {
+ add(items: T | T[], { key = null, merge = true }: BaseRegistryAddMeta = {}) {
+ const normalizedItems = (Array.isArray(items) ? items : [items]).map((item: T) => {
+ item.id = item.id || getRandId();
+ return item;
+ });
+ if (merge && this.items.has(key)) {
+ const newItems = new Set(this.items.get(key));
+ normalizedItems.forEach(item => newItems.add(item))
+ this.items.set(key, [...newItems]);
+ } else {
+ this.items.set(key, normalizedItems);
+ }
+ return () => this.remove(normalizedItems, key)
+ }
+
+ @action
+ remove(items: T[], key: BaseRegistryKey = null) {
const storedItems = this.items.get(key);
- if (storedItems) {
- const newItems = storedItems.filter(item => !items.includes(item)); // works because of {deep: false};
- if (newItems.length > 0) {
- this.items.set(key, newItems)
- } else {
- this.items.delete(key);
- }
+ if (!storedItems) return;
+ const newItems = storedItems.filter(item => !items.includes(item)); // works because of {deep: false};
+ if (newItems.length > 0) {
+ this.items.set(key, newItems)
+ } else {
+ this.items.delete(key);
}
}
}
diff --git a/src/extensions/registries/cluster-feature-registry.ts b/src/extensions/registries/cluster-feature-registry.ts
index 0b579fd639..17a4ef1eef 100644
--- a/src/extensions/registries/cluster-feature-registry.ts
+++ b/src/extensions/registries/cluster-feature-registry.ts
@@ -1,17 +1,18 @@
import type React from "react"
-import { BaseRegistry } from "./base-registry";
+import { BaseRegistry, BaseRegistryItem } from "./base-registry";
import { ClusterFeature } from "../cluster-feature";
export interface ClusterFeatureComponents {
Description: React.ComponentType;
}
-export interface ClusterFeatureRegistration {
+export interface ClusterFeatureRegistration extends BaseRegistryItem {
title: string;
components: ClusterFeatureComponents
feature: ClusterFeature
}
-export class ClusterFeatureRegistry extends BaseRegistry {}
+export class ClusterFeatureRegistry extends BaseRegistry {
+}
export const clusterFeatureRegistry = new ClusterFeatureRegistry()
diff --git a/src/extensions/registries/kube-object-detail-registry.ts b/src/extensions/registries/kube-object-detail-registry.ts
index 32fea66b81..63d9cfa30d 100644
--- a/src/extensions/registries/kube-object-detail-registry.ts
+++ b/src/extensions/registries/kube-object-detail-registry.ts
@@ -1,11 +1,11 @@
import React from "react"
-import { BaseRegistry } from "./base-registry";
+import { BaseRegistry, BaseRegistryItem } from "./base-registry";
export interface KubeObjectDetailComponents {
Details: React.ComponentType;
}
-export interface KubeObjectDetailRegistration {
+export interface KubeObjectDetailRegistration extends BaseRegistryItem {
kind: string;
apiVersions: string[];
components: KubeObjectDetailComponents;
diff --git a/src/extensions/registries/kube-object-menu-registry.ts b/src/extensions/registries/kube-object-menu-registry.ts
index 8f527d6a3d..3e0cfb0251 100644
--- a/src/extensions/registries/kube-object-menu-registry.ts
+++ b/src/extensions/registries/kube-object-menu-registry.ts
@@ -1,11 +1,11 @@
import React from "react"
-import { BaseRegistry } from "./base-registry";
+import { BaseRegistry, BaseRegistryItem } from "./base-registry";
export interface KubeObjectMenuComponents {
MenuItem: React.ComponentType;
}
-export interface KubeObjectMenuRegistration {
+export interface KubeObjectMenuRegistration extends BaseRegistryItem {
kind: string;
apiVersions: string[];
components: KubeObjectMenuComponents;
diff --git a/src/extensions/registries/kube-object-status-registry.ts b/src/extensions/registries/kube-object-status-registry.ts
index 74fd8145d2..3a516a82e1 100644
--- a/src/extensions/registries/kube-object-status-registry.ts
+++ b/src/extensions/registries/kube-object-status-registry.ts
@@ -1,7 +1,7 @@
import { KubeObject, KubeObjectStatus } from "../renderer-api/k8s-api";
-import { BaseRegistry } from "./base-registry";
+import { BaseRegistry, BaseRegistryItem } from "./base-registry";
-export interface KubeObjectStatusRegistration {
+export interface KubeObjectStatusRegistration extends BaseRegistryItem {
kind: string;
apiVersions: string[];
resolve: (object: KubeObject) => KubeObjectStatus;
diff --git a/src/extensions/registries/page-menu-registry.ts b/src/extensions/registries/page-menu-registry.ts
index 41680dabf9..ea2f04d288 100644
--- a/src/extensions/registries/page-menu-registry.ts
+++ b/src/extensions/registries/page-menu-registry.ts
@@ -2,28 +2,26 @@
import type React from "react";
import type { IconProps } from "../../renderer/components/icon";
-import { BaseRegistry } from "./base-registry";
-import { matchPath } from "react-router";
+import { BaseRegistry, BaseRegistryItem, BaseRegistryItemId } from "./base-registry";
-export interface PageMenuRegistration {
- url: string;
+export interface PageMenuRegistration extends BaseRegistryItem {
+ id: BaseRegistryItemId; // required id from page-registry item to match with
+ url?: string; // when not provided initial extension's path used, e.g. "/extension/lens-extension-name"
title: React.ReactNode;
components: PageMenuComponents;
- subMenus?: Omit[];
+ subMenus?: PageSubMenuRegistration[];
+}
+
+export interface PageSubMenuRegistration {
+ url: string;
+ title: React.ReactNode;
}
export interface PageMenuComponents {
Icon: React.ComponentType;
}
-export class PageMenuRegistry extends BaseRegistry {
- getByMatchingRoute(routePath: string | string[], exact?: boolean) {
- return this.getItems().find(item => !!matchPath(item.url, {
- path: routePath,
- exact,
- }))
- }
-
+export class PageMenuRegistry extends BaseRegistry {
getItems() {
return super.getItems().map(item => {
item.url = item.extension.getPageUrl(item.url)
diff --git a/src/extensions/registries/page-registry.ts b/src/extensions/registries/page-registry.ts
index 886a00ea6a..0169f99de5 100644
--- a/src/extensions/registries/page-registry.ts
+++ b/src/extensions/registries/page-registry.ts
@@ -1,14 +1,19 @@
// Extensions-api -> Custom page registration
import React from "react";
-import { matchPath } from "react-router";
-import { BaseRegistry } from "./base-registry";
+import { BaseRegistry, BaseRegistryItem } from "./base-registry";
-export interface PageRegistration {
- routePath: string; // react-router's path, e.g. "/page/:id"
+export interface PageRegistration extends BaseRegistryItem {
+ routePath?: string; // additional (suffix) route path to base extension's route: "/extension/:name"
exact?: boolean; // route matching flag, see: https://reactrouter.com/web/api/NavLink/exact-bool
components: PageComponents;
- subPages?: Omit[];
+ subPages?: SubPageRegistration[];
+}
+
+export interface SubPageRegistration {
+ routePath: string; // required for sub-pages
+ exact?: boolean;
+ components: PageComponents;
}
export interface PageComponents {
@@ -16,12 +21,6 @@ export interface PageComponents {
}
export class PageRegistry extends BaseRegistry {
- getByMatchingUrl(baseUrl: string) {
- return this.getItems().find(({ routePath: path, exact }) => {
- return !!matchPath(baseUrl, { path, exact });
- })
- }
-
getItems() {
return super.getItems().map(item => {
item.routePath = item.extension.getPageRoute(item.routePath)
diff --git a/src/extensions/registries/status-bar-registry.ts b/src/extensions/registries/status-bar-registry.ts
index 88c4132d30..c1029e6d68 100644
--- a/src/extensions/registries/status-bar-registry.ts
+++ b/src/extensions/registries/status-bar-registry.ts
@@ -1,9 +1,9 @@
// Extensions API -> Status bar customizations
import React from "react";
-import { BaseRegistry } from "./base-registry";
+import { BaseRegistry, BaseRegistryItem } from "./base-registry";
-export interface StatusBarRegistration {
+export interface StatusBarRegistration extends BaseRegistryItem {
item?: React.ReactNode;
}
diff --git a/src/renderer/components/app.tsx b/src/renderer/components/app.tsx
index e1cdb467a9..45b7d565f2 100755
--- a/src/renderer/components/app.tsx
+++ b/src/renderer/components/app.tsx
@@ -74,17 +74,17 @@ export class App extends React.Component {
}
renderExtensionRoutes() {
- return clusterPageRegistry.getItems().map(({ components: { Page }, exact, routePath, subPages }) => {
+ return clusterPageRegistry.getItems().map(({ id: pageId, components: { Page }, exact, routePath, subPages }) => {
const Component = () => {
if (subPages) {
const tabs: TabLayoutRoute[] = subPages.map(({ exact, routePath, components: { Page } }) => {
- const matchingUrl = clusterPageMenuRegistry.getByMatchingRoute(routePath, exact)
- if (!matchingUrl) return;
+ const menuItem = clusterPageMenuRegistry.getById(pageId);
+ if (!menuItem) return;
return {
routePath, exact,
component: Page,
- url: matchingUrl.url,
- title: matchingUrl.title,
+ url: menuItem.url,
+ title: menuItem.title,
}
}).filter(Boolean);
if (tabs.length > 0) {
diff --git a/src/renderer/components/cluster-manager/clusters-menu.tsx b/src/renderer/components/cluster-manager/clusters-menu.tsx
index 25c514254c..7844399227 100644
--- a/src/renderer/components/cluster-manager/clusters-menu.tsx
+++ b/src/renderer/components/cluster-manager/clusters-menu.tsx
@@ -149,8 +149,8 @@ export class ClustersMenu extends React.Component {
)}