Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PageRegistration & BaseRegistry refactoring #1334

Merged
merged 15 commits into from Nov 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 23 additions & 10 deletions docs/extensions/capabilities/common-capabilities.md
Expand Up @@ -100,13 +100,20 @@ import { ExamplePage } from "./src/example-page"
export default class ExampleRendererExtension extends LensRendererExtension {
globalPages = [
{
path: "/example-route",
hideInMenu: true,
components: {
Page: ExamplePage,
}
}
]

globalPageMenus = [
{
title: "Example page", // used in icon's tooltip
components: {
Icon: () => <Component.Icon material="arrow"/>,
}
}
]
}
```

Expand Down Expand Up @@ -146,11 +153,20 @@ import { ExampleIcon, ExamplePage } from "./src/page"
export default class ExampleExtension extends LensRendererExtension {
clusterPages = [
{
path: "/extension-example",
title: "Example Extension",
routePath: "/extension-example", // optional
exact: true, // optional
components: {
Page: () => <ExamplePage extension={this}/>,
MenuIcon: ExampleIcon,
}
}
]

clusterPageMenus = [
{
url: "/extension-example", // optional
title: "Example Extension",
components: {
Icon: ExampleIcon,
}
}
]
Expand Down Expand Up @@ -199,11 +215,8 @@ export default class ExampleExtension extends LensRendererExtension {
statusBarItems = [
{
item: (
<div
className="flex align-center gaps hover-highlight"
onClick={() => Navigation.navigate("/example-page")}
>
<Component.Icon material="favorite" smallest />
<div className="flex align-center gaps hover-highlight" onClick={() => this.navigate("/example-page")} >
<Component.Icon material="favorite" />
</div>
)
}
Expand Down
4 changes: 1 addition & 3 deletions docs/extensions/get-started/anatomy.md
Expand Up @@ -84,11 +84,9 @@ import React from "react"
export default class ExampleExtension extends LensRendererExtension {
clusterPages = [
{
path: "/extension-example",
title: "Hello World",
routePath: "/extension-example",
components: {
Page: () => <ExamplePage extension={this}/>,
MenuIcon: ExampleIcon,
}
}
]
Expand Down
3 changes: 1 addition & 2 deletions extensions/support-page/main.ts
@@ -1,13 +1,12 @@
import { LensMainExtension } from "@k8slens/extensions";
import { supportPageURL } from "./src/support.route";

export default class SupportPageMainExtension extends LensMainExtension {
appMenus = [
{
parentId: "help",
label: "Support",
click: () => {
this.navigate(supportPageURL());
this.navigate();
}
}
]
Expand Down
21 changes: 7 additions & 14 deletions extensions/support-page/renderer.tsx
@@ -1,28 +1,21 @@
import React from "react";
import { Component, LensRendererExtension, Navigation } from "@k8slens/extensions";
import { supportPageRoute, supportPageURL } from "./src/support.route";
import { Support } from "./src/support";
import { Component, Interface, LensRendererExtension } from "@k8slens/extensions";
import { SupportPage } from "./src/support";

export default class SupportPageRendererExtension extends LensRendererExtension {
globalPages = [
globalPages: Interface.PageRegistration[] = [
{
...supportPageRoute,
url: supportPageURL(),
hideInMenu: true,
components: {
Page: Support,
Page: SupportPage,
}
}
]

statusBarItems = [
statusBarItems: Interface.StatusBarRegistration[] = [
{
item: (
<div
className="SupportPageIcon flex align-center"
onClick={() => Navigation.navigate(supportPageURL())}
>
<Component.Icon interactive material="help" smallest />
<div className="SupportPageIcon flex align-center" onClick={() => this.navigate()}>
<Component.Icon interactive material="help" smallest/>
</div>
)
}
Expand Down
7 changes: 0 additions & 7 deletions extensions/support-page/src/support.route.ts

This file was deleted.

2 changes: 1 addition & 1 deletion extensions/support-page/src/support.scss
@@ -1,4 +1,4 @@
.PageLayout.Support {
.SupportPage {
a[target=_blank] {
text-decoration: none;
border-bottom: 1px solid;
Expand Down
4 changes: 2 additions & 2 deletions extensions/support-page/src/support.tsx
Expand Up @@ -6,12 +6,12 @@ import { observer } from "mobx-react"
import { App, Component } from "@k8slens/extensions";

@observer
export class Support extends React.Component {
export class SupportPage extends React.Component {
render() {
const { PageLayout } = Component;
const { slackUrl, issuesTrackerUrl } = App;
return (
<PageLayout showOnTop className="Support" header={<h2>Support</h2>}>
<PageLayout showOnTop className="SupportPage" header={<h2>Support</h2>}>
<h2>Community Slack Channel</h2>
<p>
Ask a question, see what's being discussed, join the conversation <a href={slackUrl} target="_blank">here</a>
Expand Down
9 changes: 5 additions & 4 deletions integration/__tests__/app.tests.ts
Expand Up @@ -13,6 +13,7 @@ const itif = (condition: boolean) => condition ? it : it.skip

jest.setTimeout(60000)

// FIXME (!): improve / simplify all css-selectors + use [data-test-id="some-id"] (already used in some tests below)
describe("Lens integration tests", () => {
const TEST_NAMESPACE = "integration-tests"

Expand Down Expand Up @@ -394,7 +395,7 @@ describe("Lens integration tests", () => {
if (drawer !== "") {
it(`shows ${drawer} drawer`, async () => {
expect(clusterAdded).toBe(true)
await app.client.click(`.sidebar-nav #${drawerId} span.link-text`)
await app.client.click(`.sidebar-nav [data-test-id="${drawerId}"] span.link-text`)
await app.client.waitUntilTextExists(`a[href^="/${pages[0].href}"]`, pages[0].name)
})
}
Expand All @@ -409,7 +410,7 @@ describe("Lens integration tests", () => {
// hide the drawer
it(`hides ${drawer} drawer`, async () => {
expect(clusterAdded).toBe(true)
await app.client.click(`.sidebar-nav #${drawerId} span.link-text`)
await app.client.click(`.sidebar-nav [data-test-id="${drawerId}"] span.link-text`)
await expect(app.client.waitUntilTextExists(`a[href^="/${pages[0].href}"]`, pages[0].name, 100)).rejects.toThrow()
})
}
Expand All @@ -428,7 +429,7 @@ describe("Lens integration tests", () => {
it(`shows a logs for a pod`, async () => {
expect(clusterAdded).toBe(true)
// Go to Pods page
await app.client.click(".sidebar-nav #workloads span.link-text")
await app.client.click(".sidebar-nav [data-test-id='workloads'] span.link-text")
await app.client.waitUntilTextExists('a[href^="/pods"]', "Pods")
await app.client.click('a[href^="/pods"]')
await app.client.waitUntilTextExists("div.TableCell", "kube-apiserver")
Expand Down Expand Up @@ -479,7 +480,7 @@ describe("Lens integration tests", () => {

it(`creates a pod in ${TEST_NAMESPACE} namespace`, async () => {
expect(clusterAdded).toBe(true)
await app.client.click(".sidebar-nav #workloads span.link-text")
await app.client.click(".sidebar-nav [data-test-id='workloads'] span.link-text")
await app.client.waitUntilTextExists('a[href^="/pods"]', "Pods")
await app.client.click('a[href^="/pods"]')
await app.client.waitUntilTextExists("div.TableCell", "kube-apiserver")
Expand Down
15 changes: 0 additions & 15 deletions src/extensions/dynamic-page.tsx

This file was deleted.

27 changes: 14 additions & 13 deletions src/extensions/extension-loader.ts
Expand Up @@ -56,30 +56,31 @@ export class ExtensionLoader {

loadOnMain() {
logger.info('[EXTENSIONS-LOADER]: load on main')
this.autoInitExtensions((extension: LensMainExtension) => [
registries.menuRegistry.add(...extension.appMenus)
this.autoInitExtensions((ext: LensMainExtension) => [
registries.menuRegistry.add(ext.appMenus, { key: ext })
]);
}

loadOnClusterManagerRenderer() {
logger.info('[EXTENSIONS-LOADER]: load on main renderer (cluster manager)')
this.autoInitExtensions((extension: LensRendererExtension) => [
registries.globalPageRegistry.add(...extension.globalPages),
registries.appPreferenceRegistry.add(...extension.appPreferences),
registries.clusterFeatureRegistry.add(...extension.clusterFeatures),
registries.statusBarRegistry.add(...extension.statusBarItems),
this.autoInitExtensions((ext: LensRendererExtension) => [
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((extension: LensRendererExtension) => [
registries.clusterPageRegistry.add(...extension.clusterPages),
registries.kubeObjectMenuRegistry.add(...extension.kubeObjectMenuItems),
registries.kubeObjectDetailRegistry.add(...extension.kubeObjectDetailItems),
registries.kubeObjectStatusRegistry.add(...extension.kubeObjectStatusTexts)
this.autoInitExtensions((ext: LensRendererExtension) => [
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 })
])

}

protected autoInitExtensions(register: (ext: LensExtension) => Function[]) {
Expand Down
1 change: 1 addition & 0 deletions src/extensions/interfaces/registrations.ts
Expand Up @@ -4,4 +4,5 @@ export type { KubeObjectDetailRegistration, KubeObjectDetailComponents } from ".
export type { KubeObjectMenuRegistration, KubeObjectMenuComponents } from "../registries/kube-object-menu-registry"
export type { KubeObjectStatusRegistration } from "../registries/kube-object-status-registry"
export type { PageRegistration, PageComponents } from "../registries/page-registry"
export type { PageMenuRegistration, PageMenuComponents } from "../registries/page-menu-registry"
export type { StatusBarRegistration } from "../registries/status-bar-registry"
10 changes: 10 additions & 0 deletions src/extensions/lens-extension.ts
@@ -1,5 +1,6 @@
import type { InstalledExtension } from "./extension-manager";
import { action, observable, reaction } from "mobx";
import { compile } from "path-to-regexp"
import logger from "../main/logger";

export type LensExtensionId = string; // path to manifest (package.json)
Expand All @@ -14,6 +15,7 @@ export interface LensExtensionManifest {
}

export class LensExtension {
readonly routePrefix = "/extension/:name"
readonly manifest: LensExtensionManifest;
readonly manifestPath: string;
readonly isBundled: boolean;
Expand Down Expand Up @@ -42,6 +44,14 @@ export class LensExtension {
return this.manifest.description
}

getPageUrl(baseUrl = "") {
return compile(this.routePrefix)({ name: this.name }) + baseUrl;
}

getPageRoute(baseRoute = "") {
return this.routePrefix + baseRoute;
}

@action
async enable() {
if (this.isEnabled) return;
Expand Down
6 changes: 4 additions & 2 deletions src/extensions/lens-main-extension.ts
Expand Up @@ -6,7 +6,9 @@ import { WindowManager } from "../main/window-manager";
export class LensMainExtension extends LensExtension {
@observable.shallow appMenus: MenuRegistration[] = []

async navigate(location: string, frameId?: number) {
await WindowManager.getInstance<WindowManager>().navigate(location, frameId)
async navigate(location?: string, frameId?: number) {
const windowManager = WindowManager.getInstance<WindowManager>();
const url = this.getPageUrl(location); // get full path to extension's page
await windowManager.navigate(url, frameId);
}
}
16 changes: 7 additions & 9 deletions src/extensions/lens-renderer-extension.ts
@@ -1,23 +1,21 @@
import type {
AppPreferenceRegistration, ClusterFeatureRegistration,
KubeObjectMenuRegistration, KubeObjectDetailRegistration,
PageRegistration, StatusBarRegistration, KubeObjectStatusRegistration
} from "./registries"
import type { AppPreferenceRegistration, ClusterFeatureRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration, KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, } from "./registries"
import { observable } from "mobx";
import { LensExtension } from "./lens-extension"
import { ipcRenderer } from "electron"

export class LensRendererExtension extends LensExtension {
@observable.shallow globalPages: PageRegistration[] = []
@observable.shallow clusterPages: PageRegistration[] = []
@observable.shallow kubeObjectStatusTexts: KubeObjectStatusRegistration[] = []
@observable.shallow globalPageMenus: PageMenuRegistration[] = []
@observable.shallow clusterPageMenus: PageMenuRegistration[] = []
@observable.shallow kubeObjectStatusTexts: KubeObjectStatusRegistration[] = []
@observable.shallow appPreferences: AppPreferenceRegistration[] = []
@observable.shallow clusterFeatures: ClusterFeatureRegistration[] = []
@observable.shallow statusBarItems: StatusBarRegistration[] = []
@observable.shallow kubeObjectDetailItems: KubeObjectDetailRegistration[] = []
@observable.shallow kubeObjectMenuItems: KubeObjectMenuRegistration[] = []

navigate(location: string) {
ipcRenderer.emit("renderer:navigate", location)
async navigate(location?: string) {
const { navigate } = await import("../renderer/navigation");
navigate(this.getPageUrl(location));
}
}
4 changes: 2 additions & 2 deletions 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<any>;
Input: React.ComponentType<any>;
}

export interface AppPreferenceRegistration {
export interface AppPreferenceRegistration extends BaseRegistryItem {
title: string;
components: AppPreferenceComponents;
}
Expand Down