mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'master' into export-kube-json-api-class
This commit is contained in:
commit
82c27c10ee
6
.idea/jsLibraryMappings.xml
generated
Normal file
6
.idea/jsLibraryMappings.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<file url="PROJECT" libraries="{ogre-tools}" />
|
||||
</component>
|
||||
</project>
|
||||
1
.idea/lens.iml
generated
1
.idea/lens.iml
generated
@ -15,5 +15,6 @@
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="ogre-tools" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
16
.idea/libraries/ogre_tools.xml
generated
Normal file
16
.idea/libraries/ogre_tools.xml
generated
Normal file
@ -0,0 +1,16 @@
|
||||
<component name="libraryTable">
|
||||
<library name="ogre-tools" type="javaScript">
|
||||
<properties>
|
||||
<sourceFilesUrls>
|
||||
<item url="file://$PROJECT_DIR$/node_modules/@ogre-tools/injectable-react/ogre-tools-injectable-react.d.ts" />
|
||||
<item url="file://$PROJECT_DIR$/node_modules/@ogre-tools/injectable/ogre-tools-injectable.d.ts" />
|
||||
</sourceFilesUrls>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="file://$PROJECT_DIR$/node_modules/@ogre-tools/injectable-react/ogre-tools-injectable-react.d.ts" />
|
||||
<root url="file://$PROJECT_DIR$/node_modules/@ogre-tools/injectable/ogre-tools-injectable.d.ts" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
34
package.json
34
package.json
@ -72,7 +72,12 @@
|
||||
"setupFiles": [
|
||||
"<rootDir>/src/jest.setup.ts",
|
||||
"jest-canvas-mock"
|
||||
]
|
||||
],
|
||||
"globals": {
|
||||
"ts-jest": {
|
||||
"isolatedModules": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"build": {
|
||||
"generateUpdatesFilesForAllChannels": true,
|
||||
@ -193,8 +198,8 @@
|
||||
"@kubernetes/client-node": "^0.16.1",
|
||||
"@sentry/electron": "^2.5.4",
|
||||
"@sentry/integrations": "^6.15.0",
|
||||
"@ogre-tools/injectable": "^1.2.1",
|
||||
"@ogre-tools/injectable-react": "^1.2.1",
|
||||
"@ogre-tools/injectable": "^1.3.0",
|
||||
"@ogre-tools/injectable-react": "^1.3.1",
|
||||
"abort-controller": "^3.0.0",
|
||||
"auto-bind": "^4.0.0",
|
||||
"autobind-decorator": "^2.4.0",
|
||||
@ -227,7 +232,7 @@
|
||||
"mobx-react": "^7.2.1",
|
||||
"mock-fs": "^4.14.0",
|
||||
"moment": "^2.29.1",
|
||||
"moment-timezone": "^0.5.33",
|
||||
"moment-timezone": "^0.5.34",
|
||||
"monaco-editor": "^0.29.1",
|
||||
"monaco-editor-webpack-plugin": "^5.0.0",
|
||||
"node-fetch": "lensapp/node-fetch#2.x",
|
||||
@ -238,7 +243,7 @@
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-material-ui-carousel": "^2.3.5",
|
||||
"react-material-ui-carousel": "^2.3.8",
|
||||
"react-router": "^5.2.0",
|
||||
"react-virtualized-auto-sizer": "^1.0.6",
|
||||
"readable-stream": "^3.6.0",
|
||||
@ -260,12 +265,13 @@
|
||||
"ws": "^7.5.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@async-fn/jest": "^1.5.0",
|
||||
"@material-ui/core": "^4.12.3",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@material-ui/lab": "^4.0.0-alpha.60",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
|
||||
"@sentry/types": "^6.14.1",
|
||||
"@testing-library/jest-dom": "^5.15.0",
|
||||
"@testing-library/jest-dom": "^5.16.1",
|
||||
"@testing-library/react": "^11.2.7",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@types/byline": "^4.2.33",
|
||||
@ -280,7 +286,7 @@
|
||||
"@types/html-webpack-plugin": "^3.2.6",
|
||||
"@types/http-proxy": "^1.17.7",
|
||||
"@types/jest": "^26.0.24",
|
||||
"@types/js-yaml": "^4.0.4",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/jsdom": "^16.2.13",
|
||||
"@types/jsonpath": "^0.2.0",
|
||||
"@types/lodash": "^4.14.177",
|
||||
@ -312,9 +318,9 @@
|
||||
"@types/tcp-port-used": "^1.0.0",
|
||||
"@types/tempy": "^0.3.0",
|
||||
"@types/triple-beam": "^1.3.2",
|
||||
"@types/url-parse": "^1.4.4",
|
||||
"@types/url-parse": "^1.4.5",
|
||||
"@types/uuid": "^8.3.3",
|
||||
"@types/webpack": "^4.41.31",
|
||||
"@types/webpack": "^4.41.32",
|
||||
"@types/webpack-dev-server": "^3.11.6",
|
||||
"@types/webpack-env": "^1.16.3",
|
||||
"@types/webpack-node-externals": "^1.7.1",
|
||||
@ -353,7 +359,7 @@
|
||||
"node-gyp": "7.1.2",
|
||||
"node-loader": "^1.0.3",
|
||||
"nodemon": "^2.0.14",
|
||||
"playwright": "^1.15.2",
|
||||
"playwright": "^1.17.1",
|
||||
"postcss": "^8.3.11",
|
||||
"postcss-loader": "4.3.0",
|
||||
"postinstall-postinstall": "^2.1.0",
|
||||
@ -367,9 +373,9 @@
|
||||
"react-select-event": "^5.1.0",
|
||||
"react-table": "^7.7.0",
|
||||
"react-window": "^1.8.6",
|
||||
"sass": "^1.43.4",
|
||||
"sass": "^1.44.0",
|
||||
"sass-loader": "^8.0.2",
|
||||
"sharp": "^0.29.2",
|
||||
"sharp": "^0.29.3",
|
||||
"style-loader": "^2.0.0",
|
||||
"tailwindcss": "^2.2.19",
|
||||
"ts-jest": "26.5.6",
|
||||
@ -385,9 +391,9 @@
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^4.46.0",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-dev-server": "^3.11.2",
|
||||
"webpack-dev-server": "^3.11.3",
|
||||
"webpack-node-externals": "^1.7.2",
|
||||
"xterm": "^4.14.1",
|
||||
"xterm": "^4.15.0",
|
||||
"xterm-addon-fit": "^0.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,6 +26,12 @@ import { KubeObject } from "../kube-object";
|
||||
import AbortController from "abort-controller";
|
||||
import { delay } from "../../utils/delay";
|
||||
import { PassThrough } from "stream";
|
||||
import { ApiManager, apiManager } from "../api-manager";
|
||||
import { Ingress, Pod } from "../endpoints";
|
||||
|
||||
jest.mock("../api-manager");
|
||||
|
||||
const mockApiManager = apiManager as jest.Mocked<ApiManager>;
|
||||
|
||||
class TestKubeObject extends KubeObject {
|
||||
static kind = "Pod";
|
||||
@ -33,7 +39,11 @@ class TestKubeObject extends KubeObject {
|
||||
static apiBase = "/api/v1/pods";
|
||||
}
|
||||
|
||||
class TestKubeApi extends KubeApi<TestKubeObject> { }
|
||||
class TestKubeApi extends KubeApi<TestKubeObject> {
|
||||
public async checkPreferredVersion() {
|
||||
return super.checkPreferredVersion();
|
||||
}
|
||||
}
|
||||
|
||||
describe("forRemoteCluster", () => {
|
||||
it("builds api client for KubeObject", async () => {
|
||||
@ -184,6 +194,94 @@ describe("KubeApi", () => {
|
||||
expect(kubeApi.apiGroup).toEqual("extensions");
|
||||
});
|
||||
|
||||
describe("checkPreferredVersion", () => {
|
||||
it("registers with apiManager if checkPreferredVersion changes apiVersionPreferred", async () => {
|
||||
expect.hasAssertions();
|
||||
|
||||
const api = new TestKubeApi({
|
||||
objectConstructor: Ingress,
|
||||
checkPreferredVersion: true,
|
||||
fallbackApiBases: ["/apis/extensions/v1beta1/ingresses"],
|
||||
request: {
|
||||
get: jest.fn()
|
||||
.mockImplementationOnce((path: string) => {
|
||||
expect(path).toBe("/apis/networking.k8s.io/v1");
|
||||
|
||||
throw new Error("no");
|
||||
})
|
||||
.mockImplementationOnce((path: string) => {
|
||||
expect(path).toBe("/apis/extensions/v1beta1");
|
||||
|
||||
return {
|
||||
resources: [
|
||||
{
|
||||
name: "ingresses",
|
||||
},
|
||||
],
|
||||
};
|
||||
})
|
||||
.mockImplementationOnce((path: string) => {
|
||||
expect(path).toBe("/apis/extensions");
|
||||
|
||||
return {
|
||||
preferredVersion: {
|
||||
version: "v1beta1",
|
||||
},
|
||||
};
|
||||
}),
|
||||
} as any,
|
||||
});
|
||||
|
||||
await api.checkPreferredVersion();
|
||||
|
||||
expect(api.apiVersionPreferred).toBe("v1beta1");
|
||||
expect(mockApiManager.registerApi).toBeCalledWith("/apis/extensions/v1beta1/ingresses", expect.anything());
|
||||
});
|
||||
|
||||
it("registers with apiManager if checkPreferredVersion changes apiVersionPreferred with non-grouped apis", async () => {
|
||||
expect.hasAssertions();
|
||||
|
||||
const api = new TestKubeApi({
|
||||
objectConstructor: Pod,
|
||||
checkPreferredVersion: true,
|
||||
fallbackApiBases: ["/api/v1beta1/pods"],
|
||||
request: {
|
||||
get: jest.fn()
|
||||
.mockImplementationOnce((path: string) => {
|
||||
expect(path).toBe("/api/v1");
|
||||
|
||||
throw new Error("no");
|
||||
})
|
||||
.mockImplementationOnce((path: string) => {
|
||||
expect(path).toBe("/api/v1beta1");
|
||||
|
||||
return {
|
||||
resources: [
|
||||
{
|
||||
name: "pods",
|
||||
},
|
||||
],
|
||||
};
|
||||
})
|
||||
.mockImplementationOnce((path: string) => {
|
||||
expect(path).toBe("/api");
|
||||
|
||||
return {
|
||||
preferredVersion: {
|
||||
version: "v1beta1",
|
||||
},
|
||||
};
|
||||
}),
|
||||
} as any,
|
||||
});
|
||||
|
||||
await api.checkPreferredVersion();
|
||||
|
||||
expect(api.apiVersionPreferred).toBe("v1beta1");
|
||||
expect(mockApiManager.registerApi).toBeCalledWith("/api/v1beta1/pods", expect.anything());
|
||||
});
|
||||
});
|
||||
|
||||
describe("patch", () => {
|
||||
let api: TestKubeApi;
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@ export interface IKubeApiLinkRef {
|
||||
apiPrefix?: string;
|
||||
apiVersion: string;
|
||||
resource: string;
|
||||
name: string;
|
||||
name?: string;
|
||||
namespace?: string;
|
||||
}
|
||||
|
||||
@ -145,15 +145,18 @@ function _parseKubeApi(path: string): IKubeApiParsed {
|
||||
};
|
||||
}
|
||||
|
||||
export function createKubeApiURL(ref: IKubeApiLinkRef): string {
|
||||
const { apiPrefix = "/apis", resource, apiVersion, name } = ref;
|
||||
let { namespace } = ref;
|
||||
export function createKubeApiURL({ apiPrefix = "/apis", resource, apiVersion, name, namespace }: IKubeApiLinkRef): string {
|
||||
const parts = [apiPrefix, apiVersion];
|
||||
|
||||
if (namespace) {
|
||||
namespace = `namespaces/${namespace}`;
|
||||
parts.push("namespaces", namespace);
|
||||
}
|
||||
|
||||
return [apiPrefix, apiVersion, namespace, resource, name]
|
||||
.filter(v => v)
|
||||
.join("/");
|
||||
parts.push(resource);
|
||||
|
||||
if (name) {
|
||||
parts.push(name);
|
||||
}
|
||||
|
||||
return parts.join("/");
|
||||
}
|
||||
|
||||
@ -38,9 +38,14 @@ import AbortController from "abort-controller";
|
||||
import { Agent, AgentOptions } from "https";
|
||||
import type { Patch } from "rfc6902";
|
||||
|
||||
/**
|
||||
* The options used for creating a `KubeApi`
|
||||
*/
|
||||
export interface IKubeApiOptions<T extends KubeObject> {
|
||||
/**
|
||||
* base api-path for listing all resources, e.g. "/api/v1/pods"
|
||||
*
|
||||
* If not specified then will be the one on the `objectConstructor`
|
||||
*/
|
||||
apiBase?: string;
|
||||
|
||||
@ -52,11 +57,33 @@ export interface IKubeApiOptions<T extends KubeObject> {
|
||||
*/
|
||||
fallbackApiBases?: string[];
|
||||
|
||||
objectConstructor: KubeObjectConstructor<T>;
|
||||
request?: KubeJsonApi;
|
||||
isNamespaced?: boolean;
|
||||
kind?: string;
|
||||
/**
|
||||
* If `true` then will check all declared apiBases against the kube api server
|
||||
* for the first accepted one.
|
||||
*/
|
||||
checkPreferredVersion?: boolean;
|
||||
|
||||
/**
|
||||
* The constructor for the kube objects returned from the API
|
||||
*/
|
||||
objectConstructor: KubeObjectConstructor<T>;
|
||||
|
||||
/**
|
||||
* The api instance to use for making requests
|
||||
*
|
||||
* @default apiKube
|
||||
*/
|
||||
request?: KubeJsonApi;
|
||||
|
||||
/**
|
||||
* @deprecated should be specified by `objectConstructor`
|
||||
*/
|
||||
isNamespaced?: boolean;
|
||||
|
||||
/**
|
||||
* @deprecated should be specified by `objectConstructor`
|
||||
*/
|
||||
kind?: string;
|
||||
}
|
||||
|
||||
export interface IKubeApiQueryParams {
|
||||
@ -249,11 +276,11 @@ export interface DeleteResourceDescriptor extends ResourceDescriptor {
|
||||
|
||||
export class KubeApi<T extends KubeObject> {
|
||||
readonly kind: string;
|
||||
readonly apiBase: string;
|
||||
readonly apiPrefix: string;
|
||||
readonly apiGroup: string;
|
||||
readonly apiVersion: string;
|
||||
readonly apiVersionPreferred?: string;
|
||||
apiBase: string;
|
||||
apiPrefix: string;
|
||||
apiGroup: string;
|
||||
apiVersionPreferred?: string;
|
||||
readonly apiResource: string;
|
||||
readonly isNamespaced: boolean;
|
||||
|
||||
@ -264,23 +291,18 @@ export class KubeApi<T extends KubeObject> {
|
||||
private watchId = 1;
|
||||
|
||||
constructor(protected options: IKubeApiOptions<T>) {
|
||||
const {
|
||||
objectConstructor,
|
||||
request = apiKube,
|
||||
kind = options.objectConstructor?.kind,
|
||||
isNamespaced = options.objectConstructor?.namespaced,
|
||||
} = options || {};
|
||||
|
||||
const { objectConstructor, request, kind, isNamespaced } = options;
|
||||
const { apiBase, apiPrefix, apiGroup, apiVersion, resource } = parseKubeApi(options.apiBase || objectConstructor.apiBase);
|
||||
|
||||
this.kind = kind;
|
||||
this.isNamespaced = isNamespaced;
|
||||
this.options = options;
|
||||
this.kind = kind ?? objectConstructor.kind;
|
||||
this.isNamespaced = isNamespaced ?? objectConstructor.namespaced ?? false;
|
||||
this.apiBase = apiBase;
|
||||
this.apiPrefix = apiPrefix;
|
||||
this.apiGroup = apiGroup;
|
||||
this.apiVersion = apiVersion;
|
||||
this.apiResource = resource;
|
||||
this.request = request;
|
||||
this.request = request ?? apiKube;
|
||||
this.objectConstructor = objectConstructor;
|
||||
|
||||
this.parseResponse = this.parseResponse.bind(this);
|
||||
@ -353,21 +375,16 @@ export class KubeApi<T extends KubeObject> {
|
||||
const { apiPrefix, apiGroup } = await this.getPreferredVersionPrefixGroup();
|
||||
|
||||
// The apiPrefix and apiGroup might change due to fallbackApiBases, so we must override them
|
||||
Object.defineProperty(this, "apiPrefix", {
|
||||
value: apiPrefix,
|
||||
});
|
||||
Object.defineProperty(this, "apiGroup", {
|
||||
value: apiGroup,
|
||||
});
|
||||
this.apiPrefix = apiPrefix;
|
||||
this.apiGroup = apiGroup;
|
||||
|
||||
const res = await this.request.get<IKubePreferredVersion>(`${this.apiPrefix}/${this.apiGroup}`);
|
||||
const url = [apiPrefix, apiGroup].filter(Boolean).join("/");
|
||||
const res = await this.request.get<IKubePreferredVersion>(url);
|
||||
|
||||
Object.defineProperty(this, "apiVersionPreferred", {
|
||||
value: res?.preferredVersion?.version ?? null,
|
||||
});
|
||||
this.apiVersionPreferred = res?.preferredVersion?.version ?? null;
|
||||
|
||||
if (this.apiVersionPreferred) {
|
||||
Object.defineProperty(this, "apiBase", { value: this.getUrl() });
|
||||
this.apiBase = this.computeApiBase();
|
||||
apiManager.registerApi(this.apiBase, this);
|
||||
}
|
||||
}
|
||||
@ -385,7 +402,15 @@ export class KubeApi<T extends KubeObject> {
|
||||
return this.list(params, { limit: 1 });
|
||||
}
|
||||
|
||||
getUrl({ name, namespace = "default" }: Partial<ResourceDescriptor> = {}, query?: Partial<IKubeApiQueryParams>) {
|
||||
private computeApiBase(): string {
|
||||
return createKubeApiURL({
|
||||
apiPrefix: this.apiPrefix,
|
||||
apiVersion: this.apiVersionWithGroup,
|
||||
resource: this.apiResource,
|
||||
});
|
||||
}
|
||||
|
||||
getUrl({ name, namespace }: Partial<ResourceDescriptor> = {}, query?: Partial<IKubeApiQueryParams>) {
|
||||
const resourcePath = createKubeApiURL({
|
||||
apiPrefix: this.apiPrefix,
|
||||
apiVersion: this.apiVersionWithGroup,
|
||||
|
||||
@ -30,13 +30,13 @@ export enum RoutingErrorType {
|
||||
MISSING_EXTENSION = "missing-ext",
|
||||
}
|
||||
|
||||
export class RoutingError extends Error {
|
||||
export class RoutingError<Query> extends Error {
|
||||
/**
|
||||
* Will be set if the routing error originated in an extension route table
|
||||
*/
|
||||
public extensionName?: string;
|
||||
|
||||
constructor(public type: RoutingErrorType, public url: Url) {
|
||||
constructor(public type: RoutingErrorType, public url: Url<Query>) {
|
||||
super("routing error");
|
||||
}
|
||||
|
||||
|
||||
@ -91,7 +91,7 @@ export abstract class LensProtocolRouter extends Singleton {
|
||||
* @param url the parsed URL that initiated the `lens://` protocol
|
||||
* @returns true if a route has been found
|
||||
*/
|
||||
protected _routeToInternal(url: Url): RouteAttempt {
|
||||
protected _routeToInternal(url: Url<Record<string, string>>): RouteAttempt {
|
||||
return this._route(this.internalRoutes.entries(), url);
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ export abstract class LensProtocolRouter extends Singleton {
|
||||
* @param routes the array of path schemas, handler pairs to match against
|
||||
* @param url the url (in its current state)
|
||||
*/
|
||||
protected _findMatchingRoute(routes: Iterable<[string, RouteHandler]>, url: Url): null | [match<Record<string, string>>, RouteHandler] {
|
||||
protected _findMatchingRoute(routes: Iterable<[string, RouteHandler]>, url: Url<Record<string, string>>): null | [match<Record<string, string>>, RouteHandler] {
|
||||
const matches: [match<Record<string, string>>, RouteHandler][] = [];
|
||||
|
||||
for (const [schema, handler] of routes) {
|
||||
@ -128,7 +128,7 @@ export abstract class LensProtocolRouter extends Singleton {
|
||||
* @param routes the array of (path schemas, handler) pairs to match against
|
||||
* @param url the url (in its current state)
|
||||
*/
|
||||
protected _route(routes: Iterable<[string, RouteHandler]>, url: Url, extensionName?: string): RouteAttempt {
|
||||
protected _route(routes: Iterable<[string, RouteHandler]>, url: Url<Record<string, string>>, extensionName?: string): RouteAttempt {
|
||||
const route = this._findMatchingRoute(routes, url);
|
||||
|
||||
if (!route) {
|
||||
@ -166,7 +166,7 @@ export abstract class LensProtocolRouter extends Singleton {
|
||||
* @param url the protocol request URI that was "open"-ed
|
||||
* @returns either the found name or the instance of `LensExtension`
|
||||
*/
|
||||
protected async _findMatchingExtensionByName(url: Url): Promise<LensExtension | string> {
|
||||
protected async _findMatchingExtensionByName(url: Url<Record<string, string>>): Promise<LensExtension | string> {
|
||||
interface ExtensionUrlMatch {
|
||||
[EXTENSION_PUBLISHER_MATCH]: string;
|
||||
[EXTENSION_NAME_MATCH]: string;
|
||||
@ -222,7 +222,7 @@ export abstract class LensProtocolRouter extends Singleton {
|
||||
* Note: this function modifies its argument, do not reuse
|
||||
* @param url the protocol request URI that was "open"-ed
|
||||
*/
|
||||
protected async _routeToExtension(url: Url): Promise<RouteAttempt> {
|
||||
protected async _routeToExtension(url: Url<Record<string, string>>): Promise<RouteAttempt> {
|
||||
const extension = await this._findMatchingExtensionByName(url);
|
||||
|
||||
if (typeof extension === "string") {
|
||||
|
||||
@ -26,6 +26,12 @@ import { BaseRegistry } from "./base-registry";
|
||||
|
||||
interface StatusBarComponents {
|
||||
Item?: React.ComponentType;
|
||||
/**
|
||||
* The side of the bottom bar to place this component.
|
||||
*
|
||||
* @default "right"
|
||||
*/
|
||||
position?: "left" | "right";
|
||||
}
|
||||
|
||||
interface StatusBarRegistrationV2 {
|
||||
|
||||
@ -39,7 +39,7 @@ export interface FallbackHandler {
|
||||
* @returns `true` if it should be routed internally to Lens, `false` if to an extension
|
||||
* @throws if `host` is not valid
|
||||
*/
|
||||
function checkHost(url: URLParse): boolean {
|
||||
function checkHost<Query>(url: URLParse<Query>): boolean {
|
||||
switch (url.host) {
|
||||
case "app":
|
||||
return true;
|
||||
@ -112,7 +112,7 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected async _findMatchingExtensionByName(url: URLParse): Promise<LensExtension | string> {
|
||||
protected async _findMatchingExtensionByName(url: URLParse<Record<string, string>>): Promise<LensExtension | string> {
|
||||
const firstAttempt = await super._findMatchingExtensionByName(url);
|
||||
|
||||
if (typeof firstAttempt !== "string") {
|
||||
@ -126,7 +126,7 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter {
|
||||
return "";
|
||||
}
|
||||
|
||||
protected _routeToInternal(url: URLParse): RouteAttempt {
|
||||
protected _routeToInternal(url: URLParse<Record<string, string>>): RouteAttempt {
|
||||
const rawUrl = url.toString(); // for sending to renderer
|
||||
const attempt = super._routeToInternal(url);
|
||||
|
||||
@ -135,7 +135,7 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter {
|
||||
return attempt;
|
||||
}
|
||||
|
||||
protected async _routeToExtension(url: URLParse): Promise<RouteAttempt> {
|
||||
protected async _routeToExtension(url: URLParse<Record<string, string>>): Promise<RouteAttempt> {
|
||||
const rawUrl = url.toString(); // for sending to renderer
|
||||
|
||||
/**
|
||||
|
||||
@ -104,13 +104,13 @@ export async function bootstrap(comp: () => Promise<AppComponent>, di: Dependenc
|
||||
logger.info(`${logPrefix} initializing WelcomeMenuRegistry`);
|
||||
initializers.initWelcomeMenuRegistry();
|
||||
|
||||
logger.info(`${logPrefix} initializing WorkloadsOverviewDetailRegist`);
|
||||
logger.info(`${logPrefix} initializing WorkloadsOverviewDetailRegistry`);
|
||||
initializers.initWorkloadsOverviewDetailRegistry();
|
||||
|
||||
logger.info(`${logPrefix} initializing CatalogEntityDetailRegistry`);
|
||||
initializers.initCatalogEntityDetailRegistry();
|
||||
|
||||
logger.info(`${logPrefix} initializing CatalogCategoryRegistryEntrie`);
|
||||
logger.info(`${logPrefix} initializing CatalogCategoryRegistryEntries`);
|
||||
initializers.initCatalogCategoryRegistryEntries();
|
||||
|
||||
logger.info(`${logPrefix} initializing Catalog`);
|
||||
@ -119,6 +119,9 @@ export async function bootstrap(comp: () => Promise<AppComponent>, di: Dependenc
|
||||
logger.info(`${logPrefix} initializing IpcRendererListeners`);
|
||||
initializers.initIpcRendererListeners();
|
||||
|
||||
logger.info(`${logPrefix} initializing StatusBarRegistry`);
|
||||
initializers.initStatusBarRegistry();
|
||||
|
||||
ExtensionLoader.createInstance().init();
|
||||
ExtensionDiscovery.createInstance().init();
|
||||
|
||||
|
||||
@ -57,9 +57,7 @@ export class CrdResources extends React.Component<Props> {
|
||||
}
|
||||
|
||||
@computed get store() {
|
||||
if (!this.crd) return null;
|
||||
|
||||
return apiManager.getStore(this.crd.getResourceApiBase());
|
||||
return apiManager.getStore(this.crd?.getResourceApiBase());
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@ -29,15 +29,14 @@ import { CRDResourceStore } from "./crd-resource.store";
|
||||
import { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
|
||||
function initStore(crd: CustomResourceDefinition) {
|
||||
const apiBase = crd.getResourceApiBase();
|
||||
const kind = crd.getResourceKind();
|
||||
const isNamespaced = crd.isNamespaced();
|
||||
const api = apiManager.getApi(apiBase) ?? new KubeApi({
|
||||
objectConstructor: KubeObject,
|
||||
apiBase,
|
||||
kind,
|
||||
isNamespaced,
|
||||
});
|
||||
const objectConstructor = class extends KubeObject {
|
||||
static readonly kind = crd.getResourceKind();
|
||||
static readonly namespaced = crd.isNamespaced();
|
||||
static readonly apiBase = crd.getResourceApiBase();
|
||||
};
|
||||
|
||||
const api = apiManager.getApi(objectConstructor.apiBase)
|
||||
?? new KubeApi({ objectConstructor });
|
||||
|
||||
if (!apiManager.getStore(api)) {
|
||||
apiManager.registerStore(new CRDResourceStore(api));
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { Icon } from "../icon";
|
||||
import { HotbarStore } from "../../../common/hotbar-store";
|
||||
import { CommandOverlay } from "../command-palette";
|
||||
import { HotbarSwitchCommand } from "../hotbar/hotbar-switch-command";
|
||||
|
||||
export const ActiveHotbarName = observer(() => {
|
||||
return (
|
||||
<div
|
||||
className="flex items-center"
|
||||
data-testid="current-hotbar-name"
|
||||
onClick={() => CommandOverlay.open(<HotbarSwitchCommand />)}
|
||||
>
|
||||
<Icon material="bookmarks" className="mr-2" size={14}/>
|
||||
{HotbarStore.getInstance().getActive()?.name}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@ -20,22 +20,26 @@
|
||||
*/
|
||||
|
||||
.BottomBar {
|
||||
$spacing: $padding * 0.5;
|
||||
--flex-gap: #{$spacing};
|
||||
@apply flex px-2 text-white;
|
||||
|
||||
grid-area: bottom-bar;
|
||||
background-color: var(--blue);
|
||||
padding: 0 2px;
|
||||
height: var(--bottom-bar-height);
|
||||
font-size: var(--font-size-small);
|
||||
}
|
||||
|
||||
.extensions {
|
||||
font-size: var(--font-size-small);
|
||||
color: white;
|
||||
.item {
|
||||
padding: $padding * 0.25 $padding * 0.5;
|
||||
&:hover {
|
||||
background-color: #ffffff33;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.item {
|
||||
@apply flex items-center mr-2 h-full px-2 last:mr-0;
|
||||
|
||||
&:hover {
|
||||
@apply cursor-pointer;
|
||||
|
||||
background-color: #ffffff33;
|
||||
}
|
||||
}
|
||||
|
||||
.onLeft + .onRight {
|
||||
@apply ml-auto;
|
||||
}
|
||||
|
||||
.onRight {}
|
||||
@ -20,25 +20,58 @@
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { render } from "@testing-library/react";
|
||||
import mockFs from "mock-fs";
|
||||
import { render, fireEvent } from "@testing-library/react";
|
||||
import "@testing-library/jest-dom/extend-expect";
|
||||
import { BottomBar } from "./bottom-bar";
|
||||
import { StatusBarRegistry } from "../../../extensions/registries";
|
||||
import { HotbarStore } from "../../../common/hotbar-store";
|
||||
import { AppPaths } from "../../../common/app-paths";
|
||||
import { CommandOverlay } from "../command-palette";
|
||||
import { HotbarSwitchCommand } from "../hotbar/hotbar-switch-command";
|
||||
import { ActiveHotbarName } from "./active-hotbar-name";
|
||||
|
||||
jest.mock("electron", () => ({
|
||||
app: {
|
||||
getPath: () => "/foo",
|
||||
jest.mock("../command-palette", () => ({
|
||||
CommandOverlay: {
|
||||
open: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
import { BottomBar } from "./bottom-bar";
|
||||
import { StatusBarRegistry } from "../../../extensions/registries";
|
||||
AppPaths.init();
|
||||
|
||||
jest.mock("electron", () => ({
|
||||
app: {
|
||||
getName: () => "lens",
|
||||
setName: jest.fn(),
|
||||
setPath: jest.fn(),
|
||||
getPath: () => "tmp",
|
||||
},
|
||||
ipcMain: {
|
||||
handle: jest.fn(),
|
||||
on: jest.fn(),
|
||||
removeAllListeners: jest.fn(),
|
||||
off: jest.fn(),
|
||||
send: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe("<BottomBar />", () => {
|
||||
beforeEach(() => {
|
||||
const mockOpts = {
|
||||
"tmp": {
|
||||
"test-store.json": JSON.stringify({}),
|
||||
},
|
||||
};
|
||||
|
||||
mockFs(mockOpts);
|
||||
StatusBarRegistry.createInstance();
|
||||
HotbarStore.createInstance();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
StatusBarRegistry.resetInstance();
|
||||
HotbarStore.resetInstance();
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it("renders w/o errors", () => {
|
||||
@ -87,4 +120,73 @@ describe("<BottomBar />", () => {
|
||||
|
||||
expect(await getByTestId(testId)).toHaveTextContent(text);
|
||||
});
|
||||
|
||||
it("show default hotbar name", () => {
|
||||
StatusBarRegistry.getInstance().getItems = jest.fn().mockImplementationOnce(() => [
|
||||
{ item: () => <ActiveHotbarName/> },
|
||||
]);
|
||||
const { getByTestId } = render(<BottomBar />);
|
||||
|
||||
expect(getByTestId("current-hotbar-name")).toHaveTextContent("default");
|
||||
});
|
||||
|
||||
it("show active hotbar name", () => {
|
||||
StatusBarRegistry.getInstance().getItems = jest.fn().mockImplementationOnce(() => [
|
||||
{ item: () => <ActiveHotbarName/> },
|
||||
]);
|
||||
const { getByTestId } = render(<BottomBar />);
|
||||
|
||||
HotbarStore.getInstance().add({
|
||||
id: "new",
|
||||
name: "new",
|
||||
}, { setActive: true });
|
||||
|
||||
expect(getByTestId("current-hotbar-name")).toHaveTextContent("new");
|
||||
});
|
||||
|
||||
it("opens command palette on click", () => {
|
||||
StatusBarRegistry.getInstance().getItems = jest.fn().mockImplementationOnce(() => [
|
||||
{ item: () => <ActiveHotbarName/> },
|
||||
]);
|
||||
const { getByTestId } = render(<BottomBar />);
|
||||
const activeHotbar = getByTestId("current-hotbar-name");
|
||||
|
||||
fireEvent.click(activeHotbar);
|
||||
|
||||
expect(CommandOverlay.open).toHaveBeenCalledWith(<HotbarSwitchCommand />);
|
||||
});
|
||||
|
||||
it("sort positioned items properly", () => {
|
||||
StatusBarRegistry.getInstance().getItems = jest.fn().mockImplementationOnce(() => [
|
||||
{
|
||||
components: {
|
||||
Item: () => <div data-testid="sortedElem">right</div>,
|
||||
},
|
||||
},
|
||||
{
|
||||
components: {
|
||||
Item: () => <div data-testid="sortedElem">right</div>,
|
||||
position: "right",
|
||||
},
|
||||
},
|
||||
{
|
||||
components: {
|
||||
Item: () => <div data-testid="sortedElem">left</div>,
|
||||
position: "left",
|
||||
},
|
||||
},
|
||||
{
|
||||
components: {
|
||||
Item: () => <div data-testid="sortedElem">left</div>,
|
||||
position: "left",
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const { getAllByTestId } = render(<BottomBar />);
|
||||
const elems = getAllByTestId("sortedElem");
|
||||
const positions = elems.map(elem => elem.textContent);
|
||||
|
||||
expect(positions).toEqual(["left", "left", "right", "right"]);
|
||||
});
|
||||
});
|
||||
|
||||
@ -19,11 +19,12 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import "./bottom-bar.scss";
|
||||
import styles from "./bottom-bar.module.css";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { StatusBarRegistration, StatusBarRegistry } from "../../../extensions/registries";
|
||||
import { cssNames } from "../../utils";
|
||||
|
||||
@observer
|
||||
export class BottomBar extends React.Component {
|
||||
@ -44,26 +45,36 @@ export class BottomBar extends React.Component {
|
||||
return null;
|
||||
}
|
||||
|
||||
items.sort(function sortLeftPositionFirst(a, b) {
|
||||
return a.components?.position?.localeCompare(b.components?.position);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="extensions box grow flex gaps justify-flex-end">
|
||||
<>
|
||||
{items.map((registration, index) => {
|
||||
if (!registration?.item && !registration?.components?.Item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex align-center gaps item" key={index}>
|
||||
<div
|
||||
className={cssNames(styles.item, {
|
||||
[styles.onLeft]: registration.components?.position == "left",
|
||||
[styles.onRight]: registration.components?.position != "left",
|
||||
})}
|
||||
key={index}
|
||||
>
|
||||
{this.renderRegisteredItem(registration)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="BottomBar flex gaps">
|
||||
<div className={styles.BottomBar}>
|
||||
{this.renderRegisteredItems()}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
.ClusterManager {
|
||||
--bottom-bar-height: 22px;
|
||||
--bottom-bar-height: 21px;
|
||||
--hotbar-width: 75px;
|
||||
|
||||
display: grid;
|
||||
|
||||
@ -140,6 +140,7 @@ export class ConfirmDialog extends React.Component<ConfirmDialogProps> {
|
||||
isOpen={dialogState.isOpen}
|
||||
onClose={this.onClose}
|
||||
close={this.close}
|
||||
{...(dialogState.isOpen ? { "data-testid":"confirmation-dialog" } : {})}
|
||||
>
|
||||
<div className="confirm-content">
|
||||
{icon} {message}
|
||||
@ -158,6 +159,7 @@ export class ConfirmDialog extends React.Component<ConfirmDialogProps> {
|
||||
label={labelOk}
|
||||
onClick={prevDefault(this.ok)}
|
||||
waiting={this.isSaving}
|
||||
data-testid="confirm"
|
||||
{...okButtonProps}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -41,6 +41,7 @@ export interface DialogProps {
|
||||
modal?: boolean;
|
||||
pinned?: boolean;
|
||||
animated?: boolean;
|
||||
"data-testid"?: string;
|
||||
}
|
||||
|
||||
interface DialogState {
|
||||
@ -149,7 +150,7 @@ export class Dialog extends React.PureComponent<DialogProps, DialogState> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { modal, animated, pinned } = this.props;
|
||||
const { modal, animated, pinned, "data-testid": testId } = this.props;
|
||||
let { className } = this.props;
|
||||
|
||||
className = cssNames("Dialog flex center", className, { modal, pinned });
|
||||
@ -158,6 +159,7 @@ export class Dialog extends React.PureComponent<DialogProps, DialogState> {
|
||||
className={className}
|
||||
onClick={stopPropagation}
|
||||
ref={this.ref}
|
||||
data-testid={testId}
|
||||
>
|
||||
<div className="box" ref={e => this.contentElem = e}>
|
||||
{this.props.children}
|
||||
|
||||
@ -35,7 +35,7 @@ import { InfoPanel } from "./info-panel";
|
||||
import * as resourceApplierApi from "../../../common/k8s-api/endpoints/resource-applier.api";
|
||||
import { Notifications } from "../notifications";
|
||||
import logger from "../../../common/logger";
|
||||
import type { KubeJsonApiData, KubeJsonApiError } from "../../../common/k8s-api/kube-json-api";
|
||||
import type { KubeJsonApiData } from "../../../common/k8s-api/kube-json-api";
|
||||
import { getDetailsUrl } from "../kube-detail-params";
|
||||
import { apiManager } from "../../../common/k8s-api/api-manager";
|
||||
import { prevDefault } from "../../utils";
|
||||
@ -130,9 +130,7 @@ export class CreateResource extends React.Component<Props> {
|
||||
</p>,
|
||||
);
|
||||
} catch (error) {
|
||||
const failureReason = (error as KubeJsonApiError).reason.toString();
|
||||
|
||||
Notifications.error(failureReason);
|
||||
Notifications.error(error?.toString() ?? "Unknown error occured");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -43,7 +43,8 @@
|
||||
|
||||
.Icon {
|
||||
--size: 16px;
|
||||
padding: 0 4px;
|
||||
padding: 0 4px 0 0px;
|
||||
margin: 0px 4px;
|
||||
|
||||
&:hover {
|
||||
box-shadow: none;
|
||||
|
||||
@ -0,0 +1,371 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`kube-object-menu given kube object renders 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div>
|
||||
<ul
|
||||
class="Animate opacity Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
|
||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||
>
|
||||
<li>
|
||||
Some menu item
|
||||
</li>
|
||||
<li
|
||||
class="MenuItem"
|
||||
data-testid="menu-action-remove"
|
||||
tabindex="0"
|
||||
>
|
||||
<i
|
||||
class="Icon material interactive focusable"
|
||||
id="tooltip_target_4"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
class="icon"
|
||||
data-icon-name="delete"
|
||||
>
|
||||
delete
|
||||
</span>
|
||||
<div />
|
||||
</i>
|
||||
<span
|
||||
class="title"
|
||||
>
|
||||
Delete
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="Animate opacity-scale Dialog flex center ConfirmDialog modal"
|
||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||
/>
|
||||
<div
|
||||
class="Tooltip narrow invisible formatter"
|
||||
>
|
||||
Delete
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`kube-object-menu given kube object when removing kube object renders 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div>
|
||||
<ul
|
||||
class="Animate opacity Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
|
||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||
>
|
||||
<li>
|
||||
Some menu item
|
||||
</li>
|
||||
<li
|
||||
class="MenuItem"
|
||||
data-testid="menu-action-remove"
|
||||
tabindex="0"
|
||||
>
|
||||
<i
|
||||
class="Icon material interactive focusable"
|
||||
id="tooltip_target_8"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
class="icon"
|
||||
data-icon-name="delete"
|
||||
>
|
||||
delete
|
||||
</span>
|
||||
<div />
|
||||
</i>
|
||||
<span
|
||||
class="title"
|
||||
>
|
||||
Delete
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="Animate opacity-scale Dialog flex center ConfirmDialog modal"
|
||||
data-testid="confirmation-dialog"
|
||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||
>
|
||||
<div
|
||||
class="box"
|
||||
>
|
||||
<div
|
||||
class="confirm-content"
|
||||
>
|
||||
<i
|
||||
class="Icon material focusable big"
|
||||
>
|
||||
<span
|
||||
class="icon"
|
||||
data-icon-name="warning"
|
||||
>
|
||||
warning
|
||||
</span>
|
||||
</i>
|
||||
|
||||
<div>
|
||||
<p>
|
||||
Remove
|
||||
some-kind
|
||||
|
||||
<b>
|
||||
some-namespace/some-name
|
||||
</b>
|
||||
from
|
||||
<b>
|
||||
Some name
|
||||
</b>
|
||||
?
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="confirm-buttons"
|
||||
>
|
||||
<button
|
||||
class="Button cancel plain"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
class="Button ok primary"
|
||||
data-testid="confirm"
|
||||
type="button"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="Tooltip narrow invisible formatter"
|
||||
>
|
||||
Delete
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`kube-object-menu given kube object with namespace when removing kube object, renders confirmation dialog with namespace 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div>
|
||||
<ul
|
||||
class="Animate opacity Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
|
||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||
>
|
||||
<li>
|
||||
Some menu item
|
||||
</li>
|
||||
<li
|
||||
class="MenuItem"
|
||||
data-testid="menu-action-remove"
|
||||
tabindex="0"
|
||||
>
|
||||
<i
|
||||
class="Icon material interactive focusable"
|
||||
id="tooltip_target_33"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
class="icon"
|
||||
data-icon-name="delete"
|
||||
>
|
||||
delete
|
||||
</span>
|
||||
<div />
|
||||
</i>
|
||||
<span
|
||||
class="title"
|
||||
>
|
||||
Delete
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="Animate opacity-scale Dialog flex center ConfirmDialog modal"
|
||||
data-testid="confirmation-dialog"
|
||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||
>
|
||||
<div
|
||||
class="box"
|
||||
>
|
||||
<div
|
||||
class="confirm-content"
|
||||
>
|
||||
<i
|
||||
class="Icon material focusable big"
|
||||
>
|
||||
<span
|
||||
class="icon"
|
||||
data-icon-name="warning"
|
||||
>
|
||||
warning
|
||||
</span>
|
||||
</i>
|
||||
|
||||
<div>
|
||||
<p>
|
||||
Remove
|
||||
some-kind
|
||||
|
||||
<b>
|
||||
some-namespace/some-name
|
||||
</b>
|
||||
from
|
||||
<b>
|
||||
Some name
|
||||
</b>
|
||||
?
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="confirm-buttons"
|
||||
>
|
||||
<button
|
||||
class="Button cancel plain"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
class="Button ok primary"
|
||||
data-testid="confirm"
|
||||
type="button"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="Tooltip narrow invisible formatter"
|
||||
>
|
||||
Delete
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`kube-object-menu given kube object without namespace when removing kube object, renders confirmation dialog without namespace 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div>
|
||||
<ul
|
||||
class="Animate opacity Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
|
||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||
>
|
||||
<li>
|
||||
Some menu item
|
||||
</li>
|
||||
<li
|
||||
class="MenuItem"
|
||||
data-testid="menu-action-remove"
|
||||
tabindex="0"
|
||||
>
|
||||
<i
|
||||
class="Icon material interactive focusable"
|
||||
id="tooltip_target_41"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
class="icon"
|
||||
data-icon-name="delete"
|
||||
>
|
||||
delete
|
||||
</span>
|
||||
<div />
|
||||
</i>
|
||||
<span
|
||||
class="title"
|
||||
>
|
||||
Delete
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="Animate opacity-scale Dialog flex center ConfirmDialog modal"
|
||||
data-testid="confirmation-dialog"
|
||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||
>
|
||||
<div
|
||||
class="box"
|
||||
>
|
||||
<div
|
||||
class="confirm-content"
|
||||
>
|
||||
<i
|
||||
class="Icon material focusable big"
|
||||
>
|
||||
<span
|
||||
class="icon"
|
||||
data-icon-name="warning"
|
||||
>
|
||||
warning
|
||||
</span>
|
||||
</i>
|
||||
|
||||
<div>
|
||||
<p>
|
||||
Remove
|
||||
some-kind
|
||||
|
||||
<b>
|
||||
some-name
|
||||
</b>
|
||||
from
|
||||
<b>
|
||||
Some name
|
||||
</b>
|
||||
?
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="confirm-buttons"
|
||||
>
|
||||
<button
|
||||
class="Button cancel plain"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
class="Button ok primary"
|
||||
data-testid="confirm"
|
||||
type="button"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="Tooltip narrow invisible formatter"
|
||||
>
|
||||
Delete
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`kube-object-menu given no kube object, renders 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<ul
|
||||
class="Animate opacity Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
|
||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||
/>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import type { ApiManager } from "../../../../common/k8s-api/api-manager";
|
||||
import { apiManager } from "../../../../common/k8s-api/api-manager";
|
||||
import type { Injectable } from "@ogre-tools/injectable";
|
||||
import { lifecycleEnum } from "@ogre-tools/injectable";
|
||||
|
||||
const apiManagerInjectable: Injectable<ApiManager> = {
|
||||
getDependencies: () => ({}),
|
||||
instantiate: () => apiManager,
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
};
|
||||
|
||||
export default apiManagerInjectable;
|
||||
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import type { Injectable } from "@ogre-tools/injectable";
|
||||
import { lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import type { Cluster } from "../../../../main/cluster";
|
||||
import clusterInjectable from "./cluster.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
cluster: Cluster;
|
||||
}
|
||||
|
||||
const clusterNameInjectable: Injectable<string | undefined, Dependencies> = {
|
||||
getDependencies: di => ({
|
||||
cluster: di.inject(clusterInjectable),
|
||||
}),
|
||||
|
||||
instantiate: ({ cluster }) => cluster?.name,
|
||||
|
||||
lifecycle: lifecycleEnum.transient,
|
||||
};
|
||||
|
||||
export default clusterNameInjectable;
|
||||
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import { getActiveClusterEntity } from "../../../api/catalog-entity-registry";
|
||||
|
||||
import type { Injectable } from "@ogre-tools/injectable";
|
||||
import { lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import type { Cluster } from "../../../../main/cluster";
|
||||
|
||||
const clusterInjectable: Injectable<Cluster | null> = {
|
||||
getDependencies: () => ({}),
|
||||
instantiate: () => getActiveClusterEntity(),
|
||||
lifecycle: lifecycleEnum.transient,
|
||||
};
|
||||
|
||||
export default clusterInjectable;
|
||||
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import { editResourceTab } from "../../dock/edit-resource.store";
|
||||
import type { Injectable } from "@ogre-tools/injectable";
|
||||
import { lifecycleEnum } from "@ogre-tools/injectable";
|
||||
|
||||
const editResourceTabInjectable: Injectable<typeof editResourceTab> = {
|
||||
getDependencies: () => ({}),
|
||||
instantiate: () => editResourceTab,
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
};
|
||||
|
||||
export default editResourceTabInjectable;
|
||||
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import { hideDetails } from "../../kube-detail-params";
|
||||
import type { Injectable } from "@ogre-tools/injectable";
|
||||
import { lifecycleEnum } from "@ogre-tools/injectable";
|
||||
|
||||
export const hideDetailsInjectable: Injectable<typeof hideDetails> = {
|
||||
getDependencies: () => ({}),
|
||||
instantiate: () => hideDetails,
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
};
|
||||
|
||||
export default hideDetailsInjectable;
|
||||
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import type { KubeObjectMenuRegistry } from "../../../../../extensions/registries";
|
||||
import type { KubeObject } from "../../../../../common/k8s-api/kube-object";
|
||||
|
||||
export interface Dependencies {
|
||||
kubeObjectMenuRegistry: KubeObjectMenuRegistry;
|
||||
}
|
||||
|
||||
export interface InstantiationParameter {
|
||||
kubeObject: KubeObject;
|
||||
}
|
||||
|
||||
export const getKubeObjectMenuItems = (
|
||||
{ kubeObjectMenuRegistry }: Dependencies,
|
||||
{ kubeObject }: InstantiationParameter,
|
||||
) => {
|
||||
if (!kubeObject) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return kubeObjectMenuRegistry
|
||||
.getItemsForKind(kubeObject.kind, kubeObject.apiVersion)
|
||||
.map(item => item.components.MenuItem);
|
||||
};
|
||||
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import { Injectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import kubeObjectMenuRegistryInjectable from "./kube-object-menu-registry.injectable";
|
||||
|
||||
import {
|
||||
InstantiationParameter,
|
||||
Dependencies,
|
||||
getKubeObjectMenuItems,
|
||||
} from "./get-kube-object-menu-items";
|
||||
|
||||
const kubeObjectMenuItemsInjectable: Injectable<
|
||||
ReturnType<typeof getKubeObjectMenuItems>,
|
||||
Dependencies,
|
||||
InstantiationParameter
|
||||
> = {
|
||||
getDependencies: di => ({
|
||||
kubeObjectMenuRegistry: di.inject(kubeObjectMenuRegistryInjectable),
|
||||
}),
|
||||
|
||||
instantiate: getKubeObjectMenuItems,
|
||||
|
||||
lifecycle: lifecycleEnum.transient,
|
||||
};
|
||||
|
||||
export default kubeObjectMenuItemsInjectable;
|
||||
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import { KubeObjectMenuRegistry } from "../../../../../extensions/registries";
|
||||
import type { Injectable } from "@ogre-tools/injectable";
|
||||
import { lifecycleEnum } from "@ogre-tools/injectable";
|
||||
|
||||
const kubeObjectMenuRegistryInjectable: Injectable<KubeObjectMenuRegistry> = {
|
||||
getDependencies: () => ({}),
|
||||
instantiate: () => KubeObjectMenuRegistry.getInstance(),
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
};
|
||||
|
||||
export default kubeObjectMenuRegistryInjectable;
|
||||
@ -19,4 +19,5 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
export * from "./kube-object-menu";
|
||||
export type { KubeObjectMenuProps } from "./kube-object-menu";
|
||||
export { KubeObjectMenu } from "./kube-object-menu-container";
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import { getInjectedComponent } from "@ogre-tools/injectable-react";
|
||||
import KubeObjectMenuInjectable from "./kube-object-menu.injectable";
|
||||
|
||||
export const KubeObjectMenu = getInjectedComponent(KubeObjectMenuInjectable);
|
||||
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import {
|
||||
KubeObjectMenu,
|
||||
KubeObjectMenuDependencies,
|
||||
KubeObjectMenuProps,
|
||||
} from "./kube-object-menu";
|
||||
|
||||
import type { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import { lifecycleEnum, Injectable } from "@ogre-tools/injectable";
|
||||
import apiManagerInjectable from "./dependencies/api-manager.injectable";
|
||||
import clusterNameInjectable from "./dependencies/cluster-name.injectable";
|
||||
import editResourceTabInjectable from "./dependencies/edit-resource-tab.injectable";
|
||||
import hideDetailsInjectable from "./dependencies/hide-details.injectable";
|
||||
import kubeObjectMenuItemsInjectable from "./dependencies/kube-object-menu-items/kube-object-menu-items.injectable";
|
||||
|
||||
const KubeObjectMenuInjectable: Injectable<
|
||||
JSX.Element,
|
||||
KubeObjectMenuDependencies<KubeObject>,
|
||||
KubeObjectMenuProps<KubeObject>
|
||||
> = {
|
||||
getDependencies: (di, props) => ({
|
||||
clusterName: di.inject(clusterNameInjectable),
|
||||
apiManager: di.inject(apiManagerInjectable),
|
||||
editResourceTab: di.inject(editResourceTabInjectable),
|
||||
hideDetails: di.inject(hideDetailsInjectable),
|
||||
|
||||
kubeObjectMenuItems: di.inject(kubeObjectMenuItemsInjectable, {
|
||||
kubeObject: props.object,
|
||||
}),
|
||||
}),
|
||||
|
||||
instantiate: (dependencies, props) => (
|
||||
<KubeObjectMenu {...dependencies} {...props} />
|
||||
),
|
||||
|
||||
lifecycle: lifecycleEnum.transient,
|
||||
};
|
||||
|
||||
export default KubeObjectMenuInjectable;
|
||||
@ -0,0 +1,284 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { screen } from "@testing-library/react";
|
||||
import "@testing-library/jest-dom/extend-expect";
|
||||
import { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import type { ConfigurableDependencyInjectionContainer } from "@ogre-tools/injectable";
|
||||
import type { KubeObjectMenuRegistration } from "../../../extensions/registries";
|
||||
import { KubeObjectMenuRegistry } from "../../../extensions/registries";
|
||||
import { ConfirmDialog } from "../confirm-dialog";
|
||||
import asyncFn, { AsyncFnMock } from "@async-fn/jest";
|
||||
import { getDiForUnitTesting } from "../getDiForUnitTesting";
|
||||
|
||||
import clusterInjectable from "./dependencies/cluster.injectable";
|
||||
import hideDetailsInjectable from "./dependencies/hide-details.injectable";
|
||||
import editResourceTabInjectable from "./dependencies/edit-resource-tab.injectable";
|
||||
import { TabKind } from "../dock/dock.store";
|
||||
import kubeObjectMenuRegistryInjectable from "./dependencies/kube-object-menu-items/kube-object-menu-registry.injectable";
|
||||
import { DiRender, renderFor } from "../test-utils/renderFor";
|
||||
import type { Cluster } from "../../../main/cluster";
|
||||
import type { ApiManager } from "../../../common/k8s-api/api-manager";
|
||||
import apiManagerInjectable from "./dependencies/api-manager.injectable";
|
||||
import { KubeObjectMenu } from "./index";
|
||||
|
||||
describe("kube-object-menu", () => {
|
||||
let di: ConfigurableDependencyInjectionContainer;
|
||||
let render: DiRender;
|
||||
|
||||
beforeEach(() => {
|
||||
di = getDiForUnitTesting();
|
||||
|
||||
// TODO: Remove global shared state
|
||||
KubeObjectMenuRegistry.resetInstance();
|
||||
KubeObjectMenuRegistry.createInstance();
|
||||
|
||||
render = renderFor(di);
|
||||
|
||||
di.override(clusterInjectable, {
|
||||
name: "Some name",
|
||||
} as Cluster);
|
||||
|
||||
di.override(apiManagerInjectable, {
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars-ts
|
||||
getStore: api => undefined,
|
||||
} as ApiManager);
|
||||
|
||||
di.override(hideDetailsInjectable, () => {});
|
||||
|
||||
di.override(editResourceTabInjectable, () => ({
|
||||
id: "irrelevant",
|
||||
kind: TabKind.TERMINAL,
|
||||
pinned: false,
|
||||
title: "irrelevant",
|
||||
}));
|
||||
|
||||
addDynamicMenuItem({
|
||||
di,
|
||||
apiVersions: ["some-api-version"],
|
||||
kind: "some-kind",
|
||||
});
|
||||
|
||||
addDynamicMenuItem({
|
||||
di,
|
||||
apiVersions: ["some-unrelated-api-version"],
|
||||
kind: "some-kind",
|
||||
});
|
||||
|
||||
addDynamicMenuItem({
|
||||
di,
|
||||
apiVersions: ["some-api-version"],
|
||||
kind: "some-unrelated-kind",
|
||||
});
|
||||
});
|
||||
|
||||
it("given no cluster, does not crash", () => {
|
||||
di.override(clusterInjectable, null);
|
||||
|
||||
expect(() => {
|
||||
render(<KubeObjectMenu object={null} toolbar={true} />);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it("given no kube object, renders", () => {
|
||||
const { baseElement } = render(
|
||||
<KubeObjectMenu object={null} toolbar={true} />,
|
||||
);
|
||||
|
||||
expect(baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("given kube object", () => {
|
||||
let baseElement: Element;
|
||||
let removeActionMock: AsyncFnMock<Function>;
|
||||
|
||||
beforeEach(async () => {
|
||||
const objectStub = KubeObject.create({
|
||||
apiVersion: "some-api-version",
|
||||
kind: "some-kind",
|
||||
metadata: {
|
||||
uid: "some-uid",
|
||||
name: "some-name",
|
||||
resourceVersion: "some-resource-version",
|
||||
namespace: "some-namespace",
|
||||
},
|
||||
});
|
||||
|
||||
removeActionMock = asyncFn();
|
||||
|
||||
({ baseElement } = render(
|
||||
<div>
|
||||
<ConfirmDialog />
|
||||
|
||||
<KubeObjectMenu
|
||||
object={objectStub}
|
||||
toolbar={true}
|
||||
removeAction={removeActionMock}
|
||||
/>
|
||||
</div>,
|
||||
));
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
expect(baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("does not open a confirmation dialog yet", () => {
|
||||
expect(screen.queryByTestId("confirmation-dialog")).toBeNull();
|
||||
});
|
||||
|
||||
describe("when removing kube object", () => {
|
||||
beforeEach(() => {
|
||||
const menuItem = screen.getByTestId("menu-action-remove");
|
||||
|
||||
userEvent.click(menuItem);
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
expect(baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("opens a confirmation dialog", () => {
|
||||
screen.getByTestId("confirmation-dialog");
|
||||
});
|
||||
|
||||
describe("when remove is confirmed", () => {
|
||||
beforeEach(() => {
|
||||
const confirmRemovalButton = screen.getByTestId("confirm");
|
||||
|
||||
userEvent.click(confirmRemovalButton);
|
||||
});
|
||||
|
||||
it("calls for removal of the kube object", () => {
|
||||
expect(removeActionMock).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it("does not close the confirmation dialog yet", () => {
|
||||
screen.getByTestId("confirmation-dialog");
|
||||
});
|
||||
|
||||
it("when removal resolves, closes the confirmation dialog", async () => {
|
||||
await removeActionMock.resolve();
|
||||
|
||||
expect(screen.queryByTestId("confirmation-dialog")).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("given kube object with namespace", () => {
|
||||
let baseElement: Element;
|
||||
|
||||
beforeEach(async () => {
|
||||
const objectStub = KubeObject.create({
|
||||
apiVersion: "some-api-version",
|
||||
kind: "some-kind",
|
||||
metadata: {
|
||||
uid: "some-uid",
|
||||
name: "some-name",
|
||||
resourceVersion: "some-resource-version",
|
||||
namespace: "some-namespace",
|
||||
},
|
||||
});
|
||||
|
||||
({ baseElement } = render(
|
||||
<div>
|
||||
<ConfirmDialog />
|
||||
|
||||
<KubeObjectMenu
|
||||
object={objectStub}
|
||||
toolbar={true}
|
||||
removeAction={() => {}}
|
||||
/>
|
||||
</div>,
|
||||
));
|
||||
});
|
||||
|
||||
it("when removing kube object, renders confirmation dialog with namespace", () => {
|
||||
const menuItem = screen.getByTestId("menu-action-remove");
|
||||
|
||||
userEvent.click(menuItem);
|
||||
|
||||
expect(baseElement).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("given kube object without namespace", () => {
|
||||
let baseElement: Element;
|
||||
|
||||
beforeEach(async () => {
|
||||
const objectStub = KubeObject.create({
|
||||
apiVersion: "some-api-version",
|
||||
kind: "some-kind",
|
||||
metadata: {
|
||||
uid: "some-uid",
|
||||
name: "some-name",
|
||||
resourceVersion: "some-resource-version",
|
||||
namespace: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
({ baseElement } = render(
|
||||
<div>
|
||||
<ConfirmDialog />
|
||||
|
||||
<KubeObjectMenu
|
||||
object={objectStub}
|
||||
toolbar={true}
|
||||
removeAction={() => {}}
|
||||
/>
|
||||
</div>,
|
||||
));
|
||||
});
|
||||
|
||||
it("when removing kube object, renders confirmation dialog without namespace", () => {
|
||||
const menuItem = screen.getByTestId("menu-action-remove");
|
||||
|
||||
userEvent.click(menuItem);
|
||||
|
||||
expect(baseElement).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const addDynamicMenuItem = ({
|
||||
di,
|
||||
apiVersions,
|
||||
kind,
|
||||
}: {
|
||||
di: ConfigurableDependencyInjectionContainer;
|
||||
apiVersions: string[];
|
||||
kind: string;
|
||||
}) => {
|
||||
const MenuItemComponent: React.FC = () => <li>Some menu item</li>;
|
||||
|
||||
const dynamicMenuItemStub: KubeObjectMenuRegistration = {
|
||||
apiVersions,
|
||||
kind,
|
||||
components: { MenuItem: MenuItemComponent },
|
||||
};
|
||||
|
||||
const kubeObjectMenuRegistry = di.inject(kubeObjectMenuRegistryInjectable);
|
||||
|
||||
kubeObjectMenuRegistry.add(dynamicMenuItemStub);
|
||||
};
|
||||
@ -22,25 +22,37 @@
|
||||
import React from "react";
|
||||
import { boundMethod, cssNames } from "../../utils";
|
||||
import type { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import { editResourceTab } from "../dock/edit-resource.store";
|
||||
import { MenuActions, MenuActionsProps } from "../menu/menu-actions";
|
||||
import { hideDetails } from "../kube-detail-params";
|
||||
import { apiManager } from "../../../common/k8s-api/api-manager";
|
||||
import { KubeObjectMenuRegistry } from "../../../extensions/registries/kube-object-menu-registry";
|
||||
import { MenuActions, MenuActionsProps } from "../menu";
|
||||
import identity from "lodash/identity";
|
||||
import type { ApiManager } from "../../../common/k8s-api/api-manager";
|
||||
|
||||
export interface KubeObjectMenuProps<T> extends MenuActionsProps {
|
||||
object: T | null | undefined;
|
||||
export interface KubeObjectMenuDependencies<TKubeObject> {
|
||||
apiManager: ApiManager;
|
||||
kubeObjectMenuItems: React.ElementType[];
|
||||
clusterName: string;
|
||||
hideDetails: () => void;
|
||||
editResourceTab: (kubeObject: TKubeObject) => void;
|
||||
}
|
||||
|
||||
export interface KubeObjectMenuProps<TKubeObject> extends MenuActionsProps {
|
||||
object: TKubeObject | null | undefined;
|
||||
editable?: boolean;
|
||||
removable?: boolean;
|
||||
}
|
||||
|
||||
export class KubeObjectMenu<T extends KubeObject> extends React.Component<KubeObjectMenuProps<T>> {
|
||||
export interface KubeObjectMenuPropsAndDependencies<TKubeObject>
|
||||
extends KubeObjectMenuProps<TKubeObject>,
|
||||
KubeObjectMenuDependencies<TKubeObject> {}
|
||||
|
||||
export class KubeObjectMenu<
|
||||
TKubeObject extends KubeObject,
|
||||
> extends React.Component<KubeObjectMenuPropsAndDependencies<TKubeObject>> {
|
||||
get store() {
|
||||
const { object } = this.props;
|
||||
|
||||
if (!object) return null;
|
||||
|
||||
return apiManager.getStore(object.selfLink);
|
||||
return this.props.apiManager.getStore(object.selfLink);
|
||||
}
|
||||
|
||||
get isEditable() {
|
||||
@ -53,13 +65,13 @@ export class KubeObjectMenu<T extends KubeObject> extends React.Component<KubeOb
|
||||
|
||||
@boundMethod
|
||||
async update() {
|
||||
hideDetails();
|
||||
editResourceTab(this.props.object);
|
||||
this.props.hideDetails();
|
||||
this.props.editResourceTab(this.props.object);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
async remove() {
|
||||
hideDetails();
|
||||
this.props.hideDetails();
|
||||
const { object, removeAction } = this.props;
|
||||
|
||||
if (removeAction) await removeAction();
|
||||
@ -74,28 +86,27 @@ export class KubeObjectMenu<T extends KubeObject> extends React.Component<KubeOb
|
||||
return null;
|
||||
}
|
||||
|
||||
const breadcrumbParts = [object.getNs(), object.getName()];
|
||||
|
||||
const breadcrumb = breadcrumbParts.filter(identity).join("/");
|
||||
|
||||
return (
|
||||
<p>Remove {object.kind} <b>{object.getName()}</b>?</p>
|
||||
<p>
|
||||
Remove {object.kind} <b>{breadcrumb}</b> from <b>{this.props.clusterName}</b>?
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
getMenuItems(): React.ReactChild[] {
|
||||
const { object, toolbar } = this.props;
|
||||
|
||||
if (!object) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return KubeObjectMenuRegistry
|
||||
.getInstance()
|
||||
.getItemsForKind(object.kind, object.apiVersion)
|
||||
.map(({ components: { MenuItem }}, index) => (
|
||||
<MenuItem
|
||||
object={object}
|
||||
key={`menu-item-${index}`}
|
||||
toolbar={toolbar}
|
||||
/>
|
||||
));
|
||||
return this.props.kubeObjectMenuItems.map((MenuItem, index) => (
|
||||
<MenuItem
|
||||
object={object}
|
||||
toolbar={toolbar}
|
||||
key={`menu-item-${index}`}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@ -30,7 +30,6 @@ import { Icon, IconProps } from "../icon";
|
||||
import { Menu, MenuItem, MenuProps } from "../menu";
|
||||
import uniqueId from "lodash/uniqueId";
|
||||
import isString from "lodash/isString";
|
||||
import { RenderDelay } from "../render-delay/render-delay";
|
||||
|
||||
export interface MenuActionsProps extends Partial<MenuProps> {
|
||||
className?: string;
|
||||
@ -125,32 +124,31 @@ export class MenuActions extends React.Component<MenuActionsProps> {
|
||||
return (
|
||||
<>
|
||||
{this.renderTriggerIcon()}
|
||||
<RenderDelay>
|
||||
<Menu
|
||||
htmlFor={this.id}
|
||||
isOpen={this.isOpen} open={this.toggle} close={this.toggle}
|
||||
className={menuClassName}
|
||||
usePortal={autoClose}
|
||||
closeOnScroll={autoClose}
|
||||
closeOnClickItem={autoCloseOnSelect ?? autoClose }
|
||||
closeOnClickOutside={autoClose}
|
||||
{...menuProps}
|
||||
>
|
||||
{children}
|
||||
{updateAction && (
|
||||
<MenuItem onClick={updateAction}>
|
||||
<Icon material="edit" interactive={toolbar} tooltip="Edit"/>
|
||||
<span className="title">Edit</span>
|
||||
</MenuItem>
|
||||
)}
|
||||
{removeAction && (
|
||||
<MenuItem onClick={this.remove}>
|
||||
<Icon material="delete" interactive={toolbar} tooltip="Delete"/>
|
||||
<span className="title">Delete</span>
|
||||
</MenuItem>
|
||||
)}
|
||||
</Menu>
|
||||
</RenderDelay>
|
||||
|
||||
<Menu
|
||||
htmlFor={this.id}
|
||||
isOpen={this.isOpen} open={this.toggle} close={this.toggle}
|
||||
className={menuClassName}
|
||||
usePortal={autoClose}
|
||||
closeOnScroll={autoClose}
|
||||
closeOnClickItem={autoCloseOnSelect ?? autoClose }
|
||||
closeOnClickOutside={autoClose}
|
||||
{...menuProps}
|
||||
>
|
||||
{children}
|
||||
{updateAction && (
|
||||
<MenuItem onClick={updateAction}>
|
||||
<Icon material="edit" interactive={toolbar} tooltip="Edit"/>
|
||||
<span className="title">Edit</span>
|
||||
</MenuItem>
|
||||
)}
|
||||
{removeAction && (
|
||||
<MenuItem onClick={this.remove} data-testid="menu-action-remove">
|
||||
<Icon material="delete" interactive={toolbar} tooltip="Delete"/>
|
||||
<span className="title">Delete</span>
|
||||
</MenuItem>
|
||||
)}
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
42
src/renderer/components/test-utils/renderFor.tsx
Normal file
42
src/renderer/components/test-utils/renderFor.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import {
|
||||
render as testingLibraryRender,
|
||||
RenderResult,
|
||||
} from "@testing-library/react";
|
||||
|
||||
import type { ConfigurableDependencyInjectionContainer } from "@ogre-tools/injectable";
|
||||
|
||||
import { DiContextProvider } from "@ogre-tools/injectable-react";
|
||||
|
||||
export type DiRender = (ui: React.ReactElement) => RenderResult;
|
||||
|
||||
type DiRenderFor = (
|
||||
di: ConfigurableDependencyInjectionContainer,
|
||||
) => DiRender;
|
||||
|
||||
export const renderFor: DiRenderFor = di => ui =>
|
||||
testingLibraryRender(
|
||||
<DiContextProvider value={{ di }}>{ui}</DiContextProvider>,
|
||||
);
|
||||
|
||||
@ -61,11 +61,11 @@ export function initCatalogCategoryRegistryEntries() {
|
||||
ctx.menuItems.push(
|
||||
{
|
||||
icon: "create_new_folder",
|
||||
title: "Sync kubeconfig folders(s)",
|
||||
title: "Sync kubeconfig folder(s)",
|
||||
defaultAction: true,
|
||||
onClick: async () => {
|
||||
await PathPicker.pick({
|
||||
label: "Sync folders(s)",
|
||||
label: "Sync folder(s)",
|
||||
buttonLabel: "Sync",
|
||||
properties: ["showHiddenFiles", "multiSelections", "openDirectory"],
|
||||
onPick: addSyncEntries,
|
||||
|
||||
@ -30,3 +30,4 @@ export * from "./registries";
|
||||
export * from "./welcome-menu-registry";
|
||||
export * from "./workloads-overview-detail-registry";
|
||||
export * from "./catalog-category-registry";
|
||||
export * from "./status-bar-registry";
|
||||
|
||||
35
src/renderer/initializers/status-bar-registry.tsx
Normal file
35
src/renderer/initializers/status-bar-registry.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { StatusBarRegistry } from "../../extensions/registries";
|
||||
import { ActiveHotbarName } from "../components/cluster-manager/active-hotbar-name";
|
||||
|
||||
export function initStatusBarRegistry() {
|
||||
StatusBarRegistry.getInstance().add([
|
||||
{
|
||||
components: {
|
||||
Item: () => <ActiveHotbarName/>,
|
||||
position: "left",
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
||||
254
yarn.lock
254
yarn.lock
@ -7,6 +7,11 @@
|
||||
resolved "https://registry.yarnpkg.com/7zip-bin/-/7zip-bin-5.1.1.tgz#9274ec7460652f9c632c59addf24efb1684ef876"
|
||||
integrity sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==
|
||||
|
||||
"@async-fn/jest@^1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@async-fn/jest/-/jest-1.5.0.tgz#dda820de3e48eca6d5b17b00a01bdb060dfcc233"
|
||||
integrity sha512-IeuTZj1TdoBS64fpNfHG9efw6dW3UQuIXfEv0qhxyNro3368kKz9E/eO46zvPmQBxbBCox3RCbKexAJH1WAY+Q==
|
||||
|
||||
"@babel/code-frame@7.12.11":
|
||||
version "7.12.11"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
|
||||
@ -14,13 +19,20 @@
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.10.4"
|
||||
|
||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.8.3":
|
||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.10.4":
|
||||
version "7.14.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb"
|
||||
integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.14.5"
|
||||
|
||||
"@babel/code-frame@^7.8.3":
|
||||
version "7.16.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431"
|
||||
integrity sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.16.0"
|
||||
|
||||
"@babel/core@^7.1.0", "@babel/core@^7.7.5":
|
||||
version "7.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.10.2.tgz#bd6786046668a925ac2bd2fd95b579b92a23b36a"
|
||||
@ -148,6 +160,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2"
|
||||
integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.15.7":
|
||||
version "7.15.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389"
|
||||
integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==
|
||||
|
||||
"@babel/helpers@^7.10.1":
|
||||
version "7.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.1.tgz#a6827b7cb975c9d9cef5fd61d919f60d8844a973"
|
||||
@ -175,6 +192,15 @@
|
||||
chalk "^2.0.0"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/highlight@^7.16.0":
|
||||
version "7.16.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.0.tgz#6ceb32b2ca4b8f5f361fb7fd821e3fddf4a1725a"
|
||||
integrity sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.15.7"
|
||||
chalk "^2.0.0"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/parser@^7.1.0", "@babel/parser@^7.10.1", "@babel/parser@^7.10.2":
|
||||
version "7.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0"
|
||||
@ -953,19 +979,19 @@
|
||||
dependencies:
|
||||
lodash "^4.17.21"
|
||||
|
||||
"@ogre-tools/injectable-react@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-1.2.1.tgz#886fbb9f9816d68daf41b6fd7ff5def6eae833b4"
|
||||
integrity sha512-kr9Q2T/VyhtUG8EbfzpFPk2ndwKQl9WHzqEfp8fasAXMNmUfUnyWs6iPNoJiuy2gh4/CNBvlFB8c647ls6/jUA==
|
||||
"@ogre-tools/injectable-react@^1.3.1":
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-1.3.1.tgz#dec3829ac8cf295c32cfe636ca2cd39a495d56ce"
|
||||
integrity sha512-5jHL9Zcb3QkrttdzqJpN6iCXaV2+fEuDNigwH6NJ3uyV1iQWuRIctnlXxfa9qtZESwaAz7o0hAwkyqEl7YSA4g==
|
||||
dependencies:
|
||||
"@ogre-tools/fp" "^1.0.2"
|
||||
"@ogre-tools/injectable" "^1.2.1"
|
||||
"@ogre-tools/injectable" "^1.3.0"
|
||||
lodash "^4.17.21"
|
||||
|
||||
"@ogre-tools/injectable@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@ogre-tools/injectable/-/injectable-1.2.1.tgz#f3eb481806dd6e53af8d9d37f8b20f3c0d875a60"
|
||||
integrity sha512-bfTlnT08uDydE0i5GxJ9SIoRKfNYVabQRrZfBraZi2rs3zx+DOpcZrJjhjDoSCzIr6C2azySuyxn1h8x8CMUPw==
|
||||
"@ogre-tools/injectable@^1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@ogre-tools/injectable/-/injectable-1.3.0.tgz#87d329a81575c9345b3af5c1afb0b45537f8f70e"
|
||||
integrity sha512-rBy8HSExUy1r53ATvk823GXevwultKuSn3mmyRlIj7opJDVRp7Usx0bvOPs+X169jmAZNzsT6HBXbDLXt4Jl4A==
|
||||
dependencies:
|
||||
"@ogre-tools/fp" "^1.0.2"
|
||||
lodash "^4.17.21"
|
||||
@ -1184,14 +1210,14 @@
|
||||
lz-string "^1.4.4"
|
||||
pretty-format "^26.6.2"
|
||||
|
||||
"@testing-library/jest-dom@^5.15.0":
|
||||
version "5.15.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.15.0.tgz#4f5295dbc476a14aec3b07176434b3d51aae5da7"
|
||||
integrity sha512-lOMuQidnL1tWHLEWIhL6UvSZC1Qt3OkNe1khvi2h6xFiqpe5O8arYs46OU0qyUGq0cSTbroQyMktYNXu3a7sAA==
|
||||
"@testing-library/jest-dom@^5.16.1":
|
||||
version "5.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.1.tgz#3db7df5ae97596264a7da9696fe14695ba02e51f"
|
||||
integrity sha512-ajUJdfDIuTCadB79ukO+0l8O+QwN0LiSxDaYUTI4LndbbUsGi6rWU1SCexXzBA2NSjlVB9/vbkasQIL3tmPBjw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@types/testing-library__jest-dom" "^5.9.1"
|
||||
aria-query "^4.2.2"
|
||||
aria-query "^5.0.0"
|
||||
chalk "^3.0.0"
|
||||
css "^3.0.0"
|
||||
css.escape "^1.5.1"
|
||||
@ -1518,10 +1544,10 @@
|
||||
jest-diff "^26.0.0"
|
||||
pretty-format "^26.0.0"
|
||||
|
||||
"@types/js-yaml@^4.0.1", "@types/js-yaml@^4.0.4":
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.4.tgz#cc38781257612581a1a0eb25f1709d2b06812fce"
|
||||
integrity sha512-AuHubXUmg0AzkXH0Mx6sIxeY/1C110mm/EkE/gB1sTRz3h2dao2W/63q42SlVST+lICxz5Oki2hzYA6+KnnieQ==
|
||||
"@types/js-yaml@^4.0.1", "@types/js-yaml@^4.0.5":
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138"
|
||||
integrity sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==
|
||||
|
||||
"@types/jsdom@^16.2.13":
|
||||
version "16.2.13"
|
||||
@ -1532,7 +1558,12 @@
|
||||
"@types/parse5" "*"
|
||||
"@types/tough-cookie" "*"
|
||||
|
||||
"@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6":
|
||||
"@types/json-schema@^7.0.4":
|
||||
version "7.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
|
||||
integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
|
||||
|
||||
"@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6":
|
||||
version "7.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
|
||||
integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
|
||||
@ -1951,10 +1982,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/underscore/-/underscore-1.10.0.tgz#5cb0dff2a5f616fc8e0c61b482bf01fa20a03cec"
|
||||
integrity sha512-ZAbqul7QAKpM2h1PFGa5ETN27ulmqtj0QviYHasw9LffvXZvVHuraOx/FOsIPPDNGZN0Qo1nASxxSfMYOtSoCw==
|
||||
|
||||
"@types/url-parse@^1.4.4":
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/url-parse/-/url-parse-1.4.4.tgz#ebeb0ec8b581318739cf73e9f9b186f610764255"
|
||||
integrity sha512-KtQLad12+4T/NfSxpoDhmr22+fig3T7/08QCgmutYA6QSznSRmEtuL95GrhVV40/0otTEdFc+etRcCTqhh1q5Q==
|
||||
"@types/url-parse@^1.4.5":
|
||||
version "1.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/url-parse/-/url-parse-1.4.5.tgz#bda0b813cc8f79d767dc8ca645fdabc238c6ae77"
|
||||
integrity sha512-8Wje3itJpk/FX+QItca9vjNLjGx5jlEYBw/CpMi03Fphk2DSVeZDUqWTE81BeCI5Bl6Z+zmA1O9L/8e3ZUSeLg==
|
||||
|
||||
"@types/uuid@^8.3.3":
|
||||
version "8.3.3"
|
||||
@ -1998,10 +2029,10 @@
|
||||
"@types/source-list-map" "*"
|
||||
source-map "^0.7.3"
|
||||
|
||||
"@types/webpack@*", "@types/webpack@^4", "@types/webpack@^4.41.31", "@types/webpack@^4.41.8":
|
||||
version "4.41.31"
|
||||
resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.31.tgz#c35f252a3559ddf9c85c0d8b0b42019025e581aa"
|
||||
integrity sha512-/i0J7sepXFIp1ZT7FjUGi1eXMCg8HCCzLJEQkKsOtbJFontsJLolBcDC+3qxn5pPwiCt1G0ZdRmYRzNBtvpuGQ==
|
||||
"@types/webpack@*", "@types/webpack@^4", "@types/webpack@^4.41.32", "@types/webpack@^4.41.8":
|
||||
version "4.41.32"
|
||||
resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.32.tgz#a7bab03b72904070162b2f169415492209e94212"
|
||||
integrity sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
"@types/tapable" "^1"
|
||||
@ -2362,7 +2393,7 @@ agent-base@4, agent-base@^4.3.0:
|
||||
dependencies:
|
||||
es6-promisify "^5.0.0"
|
||||
|
||||
agent-base@6:
|
||||
agent-base@6, agent-base@^6.0.2:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
||||
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
|
||||
@ -2447,7 +2478,12 @@ ansi-escapes@^4.2.1:
|
||||
dependencies:
|
||||
type-fest "^0.11.0"
|
||||
|
||||
ansi-html@0.0.7, ansi-html@^0.0.7:
|
||||
ansi-html-community@0.0.8:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41"
|
||||
integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==
|
||||
|
||||
ansi-html@^0.0.7:
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e"
|
||||
integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4=
|
||||
@ -2617,6 +2653,11 @@ aria-query@^4.2.2:
|
||||
"@babel/runtime" "^7.10.2"
|
||||
"@babel/runtime-corejs3" "^7.10.2"
|
||||
|
||||
aria-query@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.0.tgz#210c21aaf469613ee8c9a62c7f86525e058db52c"
|
||||
integrity sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==
|
||||
|
||||
arr-diff@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
|
||||
@ -3879,11 +3920,16 @@ commander@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
|
||||
integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==
|
||||
|
||||
commander@^6.0.0, commander@^6.1.0:
|
||||
commander@^6.0.0:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
|
||||
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
|
||||
|
||||
commander@^8.2.0:
|
||||
version "8.3.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
|
||||
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
|
||||
|
||||
commondir@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||
@ -4462,13 +4508,6 @@ decompress-response@^3.3.0:
|
||||
dependencies:
|
||||
mimic-response "^1.0.0"
|
||||
|
||||
decompress-response@^4.2.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986"
|
||||
integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==
|
||||
dependencies:
|
||||
mimic-response "^2.0.0"
|
||||
|
||||
decompress-response@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc"
|
||||
@ -7160,6 +7199,11 @@ immer@^9.0.6:
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.6.tgz#7a96bf2674d06c8143e327cbf73539388ddf1a73"
|
||||
integrity sha512-G95ivKpy+EvVAnAab4fVa4YGYn24J1SpEktnJX7JJ45Bd7xqME/SCplFzYFmTbrkwZbQ4xJK1xMTUYBkN6pWsQ==
|
||||
|
||||
immutable@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23"
|
||||
integrity sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==
|
||||
|
||||
import-cwd@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-3.0.0.tgz#20845547718015126ea9b3676b7592fb8bd4cf92"
|
||||
@ -9246,9 +9290,9 @@ mem@^1.1.0:
|
||||
mimic-fn "^1.0.0"
|
||||
|
||||
memfs@^3.1.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.2.2.tgz#5de461389d596e3f23d48bb7c2afb6161f4df40e"
|
||||
integrity sha512-RE0CwmIM3CEvpcdK3rZ19BC4E6hv9kADkMN5rPduRak58cNArWLi/9jFLsa4rhsjfVxMP3v0jO7FHXq7SvFY5Q==
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.3.0.tgz#4da2d1fc40a04b170a56622c7164c6be2c4cbef2"
|
||||
integrity sha512-BEE62uMfKOavX3iG7GYX43QJ+hAeeWnwIAuJ/R6q96jaMtiLzhsxHJC8B1L7fK7Pt/vXDRwb3SG/yBpNGDPqzg==
|
||||
dependencies:
|
||||
fs-monkey "1.0.3"
|
||||
|
||||
@ -9383,11 +9427,6 @@ mimic-response@^1.0.0, mimic-response@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
|
||||
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
|
||||
|
||||
mimic-response@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43"
|
||||
integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
|
||||
|
||||
mimic-response@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
|
||||
@ -9549,10 +9588,10 @@ modern-normalize@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/modern-normalize/-/modern-normalize-1.1.0.tgz#da8e80140d9221426bd4f725c6e11283d34f90b7"
|
||||
integrity sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA==
|
||||
|
||||
moment-timezone@^0.5.33:
|
||||
version "0.5.33"
|
||||
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.33.tgz#b252fd6bb57f341c9b59a5ab61a8e51a73bbd22c"
|
||||
integrity sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==
|
||||
moment-timezone@^0.5.34:
|
||||
version "0.5.34"
|
||||
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.34.tgz#a75938f7476b88f155d3504a9343f7519d9a405c"
|
||||
integrity sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==
|
||||
dependencies:
|
||||
moment ">= 2.9.0"
|
||||
|
||||
@ -9701,12 +9740,12 @@ no-case@^3.0.3:
|
||||
lower-case "^2.0.1"
|
||||
tslib "^1.10.0"
|
||||
|
||||
node-abi@^2.21.0:
|
||||
version "2.30.0"
|
||||
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.30.0.tgz#8be53bf3e7945a34eea10e0fc9a5982776cf550b"
|
||||
integrity sha512-g6bZh3YCKQRdwuO/tSZZYJAw622SjsRfJ2X0Iy4sSOHZ34/sPPdVBn8fev2tj7njzLwuqPw9uMtGsGkO5kIQvg==
|
||||
node-abi@^3.3.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.5.0.tgz#26e8b7b251c3260a5ac5ba5aef3b4345a0229248"
|
||||
integrity sha512-LtHvNIBgOy5mO8mPEUtkCW/YCRWYEKshIvqhe1GHHyXEHEB5mgICyYnAcl4qan3uFeRROErKGzatFHPf6kDxWw==
|
||||
dependencies:
|
||||
semver "^5.4.1"
|
||||
semver "^7.3.5"
|
||||
|
||||
node-addon-api@^1.6.3:
|
||||
version "1.7.2"
|
||||
@ -10873,12 +10912,12 @@ pkg-up@^3.1.0:
|
||||
dependencies:
|
||||
find-up "^3.0.0"
|
||||
|
||||
playwright@^1.15.2:
|
||||
version "1.15.2"
|
||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.15.2.tgz#b350056d1fffbe5de5b1bdaca6ab73e35758baad"
|
||||
integrity sha512-+Z+7ckihyxR6rK5q8DWC6eUbKARfXpyxpjNcoJfgwSr64lAOzjhyFQiPC/JkdIqhsLgZjxpWfl1S7fLb+wPkgA==
|
||||
playwright-core@=1.17.1:
|
||||
version "1.17.1"
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.17.1.tgz#a16e0f89284a0ed8ae6d77e1c905c84b8a2ba022"
|
||||
integrity sha512-C3c8RpPiC3qr15fRDN6dx6WnUkPLFmST37gms2aoHPDRvp7EaGDPMMZPpqIm/QWB5J40xDrQCD4YYHz2nBTojQ==
|
||||
dependencies:
|
||||
commander "^6.1.0"
|
||||
commander "^8.2.0"
|
||||
debug "^4.1.1"
|
||||
extract-zip "^2.0.1"
|
||||
https-proxy-agent "^5.0.0"
|
||||
@ -10889,10 +10928,19 @@ playwright@^1.15.2:
|
||||
proper-lockfile "^4.1.1"
|
||||
proxy-from-env "^1.1.0"
|
||||
rimraf "^3.0.2"
|
||||
socks-proxy-agent "^6.1.0"
|
||||
stack-utils "^2.0.3"
|
||||
ws "^7.4.6"
|
||||
yauzl "^2.10.0"
|
||||
yazl "^2.5.1"
|
||||
|
||||
playwright@^1.17.1:
|
||||
version "1.17.1"
|
||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.17.1.tgz#a6d63302ee40f41283c4bf869de261c4743a787c"
|
||||
integrity sha512-DisCkW9MblDJNS3rG61p8LiLA2WA7IY/4A4W7DX4BphWe/HuWjKmGQptuk4NVIh5UuSwXpW/jaH2+ZgjHs3GMA==
|
||||
dependencies:
|
||||
playwright-core "=1.17.1"
|
||||
|
||||
plist@^3.0.1:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.2.tgz#74bbf011124b90421c22d15779cee60060ba95bc"
|
||||
@ -11057,10 +11105,10 @@ postinstall-postinstall@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3"
|
||||
integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ==
|
||||
|
||||
prebuild-install@^6.1.4:
|
||||
version "6.1.4"
|
||||
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.1.4.tgz#ae3c0142ad611d58570b89af4986088a4937e00f"
|
||||
integrity sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==
|
||||
prebuild-install@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.0.tgz#3c5ce3902f1cb9d6de5ae94ca53575e4af0c1574"
|
||||
integrity sha512-IvSenf33K7JcgddNz2D5w521EgO+4aMMjFt73Uk9FRzQ7P+QZPKrp7qPsDydsSwjGt3T5xRNnM1bj1zMTD5fTA==
|
||||
dependencies:
|
||||
detect-libc "^1.0.3"
|
||||
expand-template "^2.0.3"
|
||||
@ -11068,11 +11116,11 @@ prebuild-install@^6.1.4:
|
||||
minimist "^1.2.3"
|
||||
mkdirp-classic "^0.5.3"
|
||||
napi-build-utils "^1.0.1"
|
||||
node-abi "^2.21.0"
|
||||
node-abi "^3.3.0"
|
||||
npmlog "^4.0.1"
|
||||
pump "^3.0.0"
|
||||
rc "^1.2.7"
|
||||
simple-get "^3.0.3"
|
||||
simple-get "^4.0.0"
|
||||
tar-fs "^2.0.0"
|
||||
tunnel-agent "^0.6.0"
|
||||
|
||||
@ -11474,10 +11522,10 @@ react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
|
||||
integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==
|
||||
|
||||
react-material-ui-carousel@^2.3.5:
|
||||
version "2.3.5"
|
||||
resolved "https://registry.yarnpkg.com/react-material-ui-carousel/-/react-material-ui-carousel-2.3.5.tgz#c6b4514333564ff4d49d2447f6d67f6c7651e813"
|
||||
integrity sha512-laiysIaA47rX0/lIVjprX5MHrDyjLxgzAsyBnG8OKgO2ad5o3Pg88PAmu6RlMy/L/H6PKFYQ8H5bAqpeZETeYw==
|
||||
react-material-ui-carousel@^2.3.8:
|
||||
version "2.3.8"
|
||||
resolved "https://registry.yarnpkg.com/react-material-ui-carousel/-/react-material-ui-carousel-2.3.8.tgz#6d6c088be22301fa91863383f35795d4e917a2a5"
|
||||
integrity sha512-0qAWiKBQI4GCjuCWZANdDuHPW8k8N3qZo3tCn9OxpVNO+M9lq7Xzak63AyPwmRDRw93l97LYhbwQIMohvpaMeg==
|
||||
dependencies:
|
||||
auto-bind "^2.1.1"
|
||||
react-swipeable "^6.1.0"
|
||||
@ -12183,12 +12231,13 @@ sass-loader@^8.0.2:
|
||||
schema-utils "^2.6.1"
|
||||
semver "^6.3.0"
|
||||
|
||||
sass@^1.32.13, sass@^1.43.4:
|
||||
version "1.43.4"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.43.4.tgz#68c7d6a1b004bef49af0d9caf750e9b252105d1f"
|
||||
integrity sha512-/ptG7KE9lxpGSYiXn7Ar+lKOv37xfWsZRtFYal2QHNigyVQDx685VFT/h7ejVr+R8w7H4tmUgtulsKl5YpveOg==
|
||||
sass@^1.32.13, sass@^1.44.0:
|
||||
version "1.44.0"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.44.0.tgz#619aa0a2275c097f9af5e6b8fe8a95e3056430fb"
|
||||
integrity sha512-0hLREbHFXGQqls/K8X+koeP+ogFRPF4ZqetVB19b7Cst9Er8cOR0rc6RU7MaI4W1JmUShd1BPgPoeqmmgMMYFw==
|
||||
dependencies:
|
||||
chokidar ">=3.0.0 <4.0.0"
|
||||
immutable "^4.0.0"
|
||||
|
||||
sax@^1.2.4, sax@~1.2.4:
|
||||
version "1.2.4"
|
||||
@ -12407,17 +12456,17 @@ shallow-clone@^3.0.0:
|
||||
dependencies:
|
||||
kind-of "^6.0.2"
|
||||
|
||||
sharp@^0.29.2:
|
||||
version "0.29.2"
|
||||
resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.29.2.tgz#e8c003cd9cb321585b32dbda6eed3baa7d6f2308"
|
||||
integrity sha512-XWRdiYLIJ3tDUejRyG24KERnJzMfIoyiJBntd2S6/uj3NEeNgRFRLgiBlvPxMa8aml14dKKD98yHinSNKp1xzQ==
|
||||
sharp@^0.29.3:
|
||||
version "0.29.3"
|
||||
resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.29.3.tgz#0da183d626094c974516a48fab9b3e4ba92eb5c2"
|
||||
integrity sha512-fKWUuOw77E4nhpyzCCJR1ayrttHoFHBT2U/kR/qEMRhvPEcluG4BKj324+SCO1e84+knXHwhJ1HHJGnUt4ElGA==
|
||||
dependencies:
|
||||
color "^4.0.1"
|
||||
detect-libc "^1.0.3"
|
||||
node-addon-api "^4.2.0"
|
||||
prebuild-install "^6.1.4"
|
||||
prebuild-install "^7.0.0"
|
||||
semver "^7.3.5"
|
||||
simple-get "^3.1.0"
|
||||
simple-get "^4.0.0"
|
||||
tar-fs "^2.1.1"
|
||||
tunnel-agent "^0.6.0"
|
||||
|
||||
@ -12496,12 +12545,12 @@ simple-concat@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
|
||||
integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
|
||||
|
||||
simple-get@^3.0.3, simple-get@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.0.tgz#b45be062435e50d159540b576202ceec40b9c6b3"
|
||||
integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==
|
||||
simple-get@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.0.tgz#73fa628278d21de83dadd5512d2cc1f4872bd675"
|
||||
integrity sha512-ZalZGexYr3TA0SwySsr5HlgOOinS4Jsa8YB2GJ6lUNAazyAu4KG/VmzMTwAt2YVXzzVj8QmefmAonZIK2BSGcQ==
|
||||
dependencies:
|
||||
decompress-response "^4.2.0"
|
||||
decompress-response "^6.0.0"
|
||||
once "^1.3.1"
|
||||
simple-concat "^1.0.0"
|
||||
|
||||
@ -12607,6 +12656,23 @@ socks-proxy-agent@^4.0.0:
|
||||
agent-base "~4.2.1"
|
||||
socks "~2.3.2"
|
||||
|
||||
socks-proxy-agent@^6.1.0:
|
||||
version "6.1.1"
|
||||
resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz#e664e8f1aaf4e1fb3df945f09e3d94f911137f87"
|
||||
integrity sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==
|
||||
dependencies:
|
||||
agent-base "^6.0.2"
|
||||
debug "^4.3.1"
|
||||
socks "^2.6.1"
|
||||
|
||||
socks@^2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e"
|
||||
integrity sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==
|
||||
dependencies:
|
||||
ip "^1.1.5"
|
||||
smart-buffer "^4.1.0"
|
||||
|
||||
socks@~2.3.2:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.3.tgz#01129f0a5d534d2b897712ed8aceab7ee65d78e3"
|
||||
@ -14231,12 +14297,12 @@ webpack-dev-middleware@^3.7.2:
|
||||
range-parser "^1.2.1"
|
||||
webpack-log "^2.0.0"
|
||||
|
||||
webpack-dev-server@^3.11.2:
|
||||
version "3.11.2"
|
||||
resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz#695ebced76a4929f0d5de7fd73fafe185fe33708"
|
||||
integrity sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ==
|
||||
webpack-dev-server@^3.11.3:
|
||||
version "3.11.3"
|
||||
resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz#8c86b9d2812bf135d3c9bce6f07b718e30f7c3d3"
|
||||
integrity sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA==
|
||||
dependencies:
|
||||
ansi-html "0.0.7"
|
||||
ansi-html-community "0.0.8"
|
||||
bonjour "^3.5.0"
|
||||
chokidar "^2.1.8"
|
||||
compression "^1.7.4"
|
||||
@ -14605,10 +14671,10 @@ xterm-addon-fit@^0.5.0:
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-fit/-/xterm-addon-fit-0.5.0.tgz#2d51b983b786a97dcd6cde805e700c7f913bc596"
|
||||
integrity sha512-DsS9fqhXHacEmsPxBJZvfj2la30Iz9xk+UKjhQgnYNkrUIN5CYLbw7WEfz117c7+S86S/tpHPfvNxJsF5/G8wQ==
|
||||
|
||||
xterm@^4.14.1:
|
||||
version "4.14.1"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.14.1.tgz#6884cb8fb3b83353b1a98139ea23daedf8e35796"
|
||||
integrity sha512-jgzNg5BuGPwq5/M4dGnmbghZvHx2jaj+9crSEt15bV34Za49VziBmCu7zIy88zUKKiGTxeo7aVzirFSJArIMFw==
|
||||
xterm@^4.15.0:
|
||||
version "4.15.0"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.15.0.tgz#e52038507eba7e0d36d47f81e29fe548c82b9561"
|
||||
integrity sha512-Ik1GoSq1yqKZQ2LF37RPS01kX9t4TP8gpamUYblD09yvWX5mEYuMK4CcqH6+plgiNEZduhTz/UrcaWs97gOlOw==
|
||||
|
||||
y18n@^3.2.1:
|
||||
version "3.2.2"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user