mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'master' into fix/consistent-inputs
This commit is contained in:
commit
fdf7011d20
@ -3,7 +3,7 @@
|
|||||||
"productName": "OpenLens",
|
"productName": "OpenLens",
|
||||||
"description": "OpenLens - Open Source IDE for Kubernetes",
|
"description": "OpenLens - Open Source IDE for Kubernetes",
|
||||||
"homepage": "https://github.com/lensapp/lens",
|
"homepage": "https://github.com/lensapp/lens",
|
||||||
"version": "5.3.0",
|
"version": "5.4.0-alpha.1",
|
||||||
"main": "static/build/main.js",
|
"main": "static/build/main.js",
|
||||||
"copyright": "© 2021 OpenLens Authors",
|
"copyright": "© 2021 OpenLens Authors",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -304,7 +304,7 @@
|
|||||||
"@types/react-dom": "^17.0.11",
|
"@types/react-dom": "^17.0.11",
|
||||||
"@types/react-router-dom": "^5.3.2",
|
"@types/react-router-dom": "^5.3.2",
|
||||||
"@types/react-select": "3.1.2",
|
"@types/react-select": "3.1.2",
|
||||||
"@types/react-table": "^7.7.8",
|
"@types/react-table": "^7.7.9",
|
||||||
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
||||||
"@types/react-window": "^1.8.5",
|
"@types/react-window": "^1.8.5",
|
||||||
"@types/readable-stream": "^2.3.12",
|
"@types/readable-stream": "^2.3.12",
|
||||||
|
|||||||
131
src/common/k8s-api/__tests__/nodes.test.ts
Normal file
131
src/common/k8s-api/__tests__/nodes.test.ts
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { Node } from "../endpoints";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
describe("Nodes tests", () => {
|
||||||
|
describe("getRoleLabels()", () => {
|
||||||
|
it("should return empty string if labels is not present", () => {
|
||||||
|
const node = new Node({
|
||||||
|
apiVersion: "foo",
|
||||||
|
kind: "Node",
|
||||||
|
metadata: {
|
||||||
|
name: "bar",
|
||||||
|
resourceVersion: "1",
|
||||||
|
uid: "bat",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(node.getRoleLabels()).toBe("");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return empty string if labels is empty object", () => {
|
||||||
|
const node = new Node({
|
||||||
|
apiVersion: "foo",
|
||||||
|
kind: "Node",
|
||||||
|
metadata: {
|
||||||
|
name: "bar",
|
||||||
|
resourceVersion: "1",
|
||||||
|
uid: "bat",
|
||||||
|
labels: {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(node.getRoleLabels()).toBe("");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return rest of keys with substring node-role.kubernetes.io/", () => {
|
||||||
|
const node = new Node({
|
||||||
|
apiVersion: "foo",
|
||||||
|
kind: "Node",
|
||||||
|
metadata: {
|
||||||
|
name: "bar",
|
||||||
|
resourceVersion: "1",
|
||||||
|
uid: "bat",
|
||||||
|
labels: {
|
||||||
|
"node-role.kubernetes.io/foobar": "bat",
|
||||||
|
"hellonode-role.kubernetes.io/foobar1": "bat",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(node.getRoleLabels()).toBe("foobar, foobar1");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return rest of keys with substring node-role.kubernetes.io/ after last /", () => {
|
||||||
|
const node = new Node({
|
||||||
|
apiVersion: "foo",
|
||||||
|
kind: "Node",
|
||||||
|
metadata: {
|
||||||
|
name: "bar",
|
||||||
|
resourceVersion: "1",
|
||||||
|
uid: "bat",
|
||||||
|
labels: {
|
||||||
|
"node-role.kubernetes.io/foobar": "bat",
|
||||||
|
"hellonode-role.kubernetes.io//////foobar1": "bat",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(node.getRoleLabels()).toBe("foobar, foobar1");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return value of label kubernetes.io/role if present", () => {
|
||||||
|
const node = new Node({
|
||||||
|
apiVersion: "foo",
|
||||||
|
kind: "Node",
|
||||||
|
metadata: {
|
||||||
|
name: "bar",
|
||||||
|
resourceVersion: "1",
|
||||||
|
uid: "bat",
|
||||||
|
labels: {
|
||||||
|
"kubernetes.io/role": "master",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(node.getRoleLabels()).toBe("master");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return value of label node.kubernetes.io/role if present", () => {
|
||||||
|
const node = new Node({
|
||||||
|
apiVersion: "foo",
|
||||||
|
kind: "Node",
|
||||||
|
metadata: {
|
||||||
|
name: "bar",
|
||||||
|
resourceVersion: "1",
|
||||||
|
uid: "bat",
|
||||||
|
labels: {
|
||||||
|
"node.kubernetes.io/role": "master",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(node.getRoleLabels()).toBe("master");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("all sources should be joined together", () => {
|
||||||
|
const node = new Node({
|
||||||
|
apiVersion: "foo",
|
||||||
|
kind: "Node",
|
||||||
|
metadata: {
|
||||||
|
name: "bar",
|
||||||
|
resourceVersion: "1",
|
||||||
|
uid: "bat",
|
||||||
|
labels: {
|
||||||
|
"aksjhdkjahsdnode-role.kubernetes.io/foobar": "bat",
|
||||||
|
"kubernetes.io/role": "master",
|
||||||
|
"node.kubernetes.io/role": "master-v2-max",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(node.getRoleLabels()).toBe("foobar, master, master-v2-max");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -140,6 +140,12 @@ function* getTrueConditionTypes(conditions: IterableIterator<NodeCondition> | It
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This regex is used in the `getRoleLabels()` method bellow, but placed here
|
||||||
|
* as factoring out regexes is best practice.
|
||||||
|
*/
|
||||||
|
const nodeRoleLabelKeyMatcher = /^.*node-role.kubernetes.io\/+(?<role>.+)$/;
|
||||||
|
|
||||||
export class Node extends KubeObject {
|
export class Node extends KubeObject {
|
||||||
static kind = "Node";
|
static kind = "Node";
|
||||||
static namespaced = false;
|
static namespaced = false;
|
||||||
@ -165,17 +171,29 @@ export class Node extends KubeObject {
|
|||||||
return this.spec.taints || [];
|
return this.spec.taints || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
getRoleLabels() {
|
getRoleLabels(): string {
|
||||||
if (!this.metadata?.labels || typeof this.metadata.labels !== "object") {
|
const { labels } = this.metadata;
|
||||||
|
|
||||||
|
if (!labels || typeof labels !== "object") {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const roleLabels = Object.keys(this.metadata.labels)
|
const roleLabels: string[] = [];
|
||||||
.filter(key => key.includes("node-role.kubernetes.io"))
|
|
||||||
.map(key => key.match(/([^/]+$)/)[0]); // all after last slash
|
|
||||||
|
|
||||||
if (this.metadata.labels["kubernetes.io/role"] != undefined) {
|
for (const labelKey of Object.keys(labels)) {
|
||||||
roleLabels.push(this.metadata.labels["kubernetes.io/role"]);
|
const match = nodeRoleLabelKeyMatcher.exec(labelKey);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
roleLabels.push(match.groups.role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof labels["kubernetes.io/role"] === "string") {
|
||||||
|
roleLabels.push(labels["kubernetes.io/role"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof labels["node.kubernetes.io/role"] === "string") {
|
||||||
|
roleLabels.push(labels["node.kubernetes.io/role"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return roleLabels.join(", ");
|
return roleLabels.join(", ");
|
||||||
|
|||||||
@ -179,12 +179,13 @@ export async function getHistory(name: string, namespace: string, kubeconfigPath
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function rollback(name: string, namespace: string, revision: number, kubeconfigPath: string) {
|
export async function rollback(name: string, namespace: string, revision: number, kubeconfigPath: string) {
|
||||||
return JSON.parse(await execHelm([
|
await execHelm([
|
||||||
"rollback",
|
"rollback",
|
||||||
name,
|
name,
|
||||||
|
`${revision}`,
|
||||||
"--namespace", namespace,
|
"--namespace", namespace,
|
||||||
"--kubeconfig", kubeconfigPath,
|
"--kubeconfig", kubeconfigPath,
|
||||||
]));
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getResources(name: string, namespace: string, kubeconfigPath: string, kubectlPath: string) {
|
async function getResources(name: string, namespace: string, kubeconfigPath: string, kubectlPath: string) {
|
||||||
|
|||||||
@ -102,9 +102,7 @@ class HelmService {
|
|||||||
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
|
const proxyKubeconfig = await cluster.getProxyKubeconfigPath();
|
||||||
|
|
||||||
logger.debug("Rollback release");
|
logger.debug("Rollback release");
|
||||||
const output = rollback(releaseName, namespace, revision, proxyKubeconfig);
|
await rollback(releaseName, namespace, revision, proxyKubeconfig);
|
||||||
|
|
||||||
return { message: output };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -71,9 +71,9 @@ export class HelmApiRoute {
|
|||||||
const { cluster, params, payload, response } = request;
|
const { cluster, params, payload, response } = request;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await helmService.rollback(cluster, params.release, params.namespace, payload.revision);
|
await helmService.rollback(cluster, params.release, params.namespace, payload.revision);
|
||||||
|
|
||||||
respondJson(response, result);
|
response.end();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.debug(error);
|
logger.debug(error);
|
||||||
respondText(response, error?.toString() || "Error rolling back chart", 422);
|
respondText(response, error?.toString() || "Error rolling back chart", 422);
|
||||||
|
|||||||
@ -94,6 +94,7 @@ export class NodeShellSession extends ShellSession {
|
|||||||
tolerations: [{
|
tolerations: [{
|
||||||
operator: "Exists",
|
operator: "Exists",
|
||||||
}],
|
}],
|
||||||
|
priorityClassName: "system-node-critical",
|
||||||
containers: [{
|
containers: [{
|
||||||
name: "shell",
|
name: "shell",
|
||||||
image: this.cluster.nodeShellImage,
|
image: this.cluster.nodeShellImage,
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import releaseInjectable from "./release.injectable";
|
|||||||
const releaseDetailsInjectable = getInjectable({
|
const releaseDetailsInjectable = getInjectable({
|
||||||
instantiate: (di) =>
|
instantiate: (di) =>
|
||||||
asyncComputed(async () => {
|
asyncComputed(async () => {
|
||||||
const release = di.inject(releaseInjectable).value.get();
|
const release = di.inject(releaseInjectable).get();
|
||||||
|
|
||||||
return await getRelease(release.name, release.namespace);
|
return await getRelease(release.name, release.namespace);
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import "./release-details.scss";
|
|||||||
|
|
||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
import groupBy from "lodash/groupBy";
|
import groupBy from "lodash/groupBy";
|
||||||
import { computed, makeObservable, observable } from "mobx";
|
import { computed, IComputedValue, makeObservable, observable } from "mobx";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import kebabCase from "lodash/kebabCase";
|
import kebabCase from "lodash/kebabCase";
|
||||||
import type { HelmRelease, IReleaseDetails, IReleaseUpdateDetails, IReleaseUpdatePayload } from "../../../../common/k8s-api/endpoints/helm-releases.api";
|
import type { HelmRelease, IReleaseDetails, IReleaseUpdateDetails, IReleaseUpdatePayload } from "../../../../common/k8s-api/endpoints/helm-releases.api";
|
||||||
@ -39,7 +39,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
release: IAsyncComputed<HelmRelease>
|
release: IComputedValue<HelmRelease>
|
||||||
releaseDetails: IAsyncComputed<IReleaseDetails>
|
releaseDetails: IAsyncComputed<IReleaseDetails>
|
||||||
releaseValues: IAsyncComputed<string>
|
releaseValues: IAsyncComputed<string>
|
||||||
updateRelease: (name: string, namespace: string, payload: IReleaseUpdatePayload) => Promise<IReleaseUpdateDetails>
|
updateRelease: (name: string, namespace: string, payload: IReleaseUpdatePayload) => Promise<IReleaseUpdateDetails>
|
||||||
@ -59,7 +59,7 @@ class NonInjectedReleaseDetails extends Component<Props & Dependencies> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@computed get release() {
|
@computed get release() {
|
||||||
return this.props.release.value.get();
|
return this.props.release.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get details() {
|
@computed get details() {
|
||||||
|
|||||||
@ -12,7 +12,13 @@ import userSuppliedValuesAreShownInjectable from "./user-supplied-values-are-sho
|
|||||||
const releaseValuesInjectable = getInjectable({
|
const releaseValuesInjectable = getInjectable({
|
||||||
instantiate: (di) =>
|
instantiate: (di) =>
|
||||||
asyncComputed(async () => {
|
asyncComputed(async () => {
|
||||||
const release = di.inject(releaseInjectable).value.get();
|
const release = di.inject(releaseInjectable).get();
|
||||||
|
|
||||||
|
// TODO: Figure out way to get rid of defensive code
|
||||||
|
if (!release) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
const userSuppliedValuesAreShown = di.inject(userSuppliedValuesAreShownInjectable).value;
|
const userSuppliedValuesAreShown = di.inject(userSuppliedValuesAreShownInjectable).value;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -6,14 +6,14 @@ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
|||||||
import { matches } from "lodash/fp";
|
import { matches } from "lodash/fp";
|
||||||
import releasesInjectable from "../releases.injectable";
|
import releasesInjectable from "../releases.injectable";
|
||||||
import releaseRouteParametersInjectable from "./release-route-parameters.injectable";
|
import releaseRouteParametersInjectable from "./release-route-parameters.injectable";
|
||||||
import { asyncComputed } from "@ogre-tools/injectable-react";
|
import { computed } from "mobx";
|
||||||
|
|
||||||
const releaseInjectable = getInjectable({
|
const releaseInjectable = getInjectable({
|
||||||
instantiate: (di) => {
|
instantiate: (di) => {
|
||||||
const releases = di.inject(releasesInjectable);
|
const releases = di.inject(releasesInjectable);
|
||||||
const releaseRouteParameters = di.inject(releaseRouteParametersInjectable);
|
const releaseRouteParameters = di.inject(releaseRouteParametersInjectable);
|
||||||
|
|
||||||
return asyncComputed(async () => {
|
return computed(() => {
|
||||||
const { name, namespace } = releaseRouteParameters.get();
|
const { name, namespace } = releaseRouteParameters.get();
|
||||||
|
|
||||||
if (!name || !namespace) {
|
if (!name || !namespace) {
|
||||||
|
|||||||
@ -3,10 +3,29 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||||
import { updateRelease } from "../../../../common/k8s-api/endpoints/helm-releases.api";
|
import {
|
||||||
|
IReleaseUpdatePayload,
|
||||||
|
updateRelease,
|
||||||
|
} from "../../../../common/k8s-api/endpoints/helm-releases.api";
|
||||||
|
import releasesInjectable from "../releases.injectable";
|
||||||
|
|
||||||
const updateReleaseInjectable = getInjectable({
|
const updateReleaseInjectable = getInjectable({
|
||||||
instantiate: () => updateRelease,
|
instantiate: (di) => {
|
||||||
|
const releases = di.inject(releasesInjectable);
|
||||||
|
|
||||||
|
return async (
|
||||||
|
name: string,
|
||||||
|
namespace: string,
|
||||||
|
payload: IReleaseUpdatePayload,
|
||||||
|
) => {
|
||||||
|
const result = await updateRelease(name, namespace, payload);
|
||||||
|
|
||||||
|
releases.invalidate();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
lifecycle: lifecycleEnum.singleton,
|
lifecycle: lifecycleEnum.singleton,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -43,6 +43,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.pinIcon {
|
.pinIcon {
|
||||||
|
--color-active: var(--textColorAccent);
|
||||||
|
|
||||||
transition: none;
|
transition: none;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
margin-left: var(--padding);
|
margin-left: var(--padding);
|
||||||
|
|||||||
@ -9,19 +9,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.quota-entries {
|
.quota-entries {
|
||||||
margin: -$margin * 0.5;
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-left: -1px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
&:empty {
|
&:empty {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quota {
|
.quota {
|
||||||
--flex-gap: #{$padding};
|
|
||||||
border: 1px solid var(--halfGray);
|
border: 1px solid var(--halfGray);
|
||||||
border-radius: $radius;
|
border-radius: $radius;
|
||||||
margin: $margin * 0.5;
|
|
||||||
padding: $padding * 0.5 $padding;
|
padding: $padding * 0.5 $padding;
|
||||||
transition: all 150ms ease;
|
transition: all 150ms ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: inset 0 0 0 1px var(--borderColor);
|
box-shadow: inset 0 0 0 1px var(--borderColor);
|
||||||
|
|||||||
@ -124,7 +124,7 @@ export class Events extends React.Component<Props> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { store, visibleItems } = this;
|
const { store } = this;
|
||||||
const { compact, compactLimit, className, ...layoutProps } = this.props;
|
const { compact, compactLimit, className, ...layoutProps } = this.props;
|
||||||
|
|
||||||
const events = (
|
const events = (
|
||||||
@ -137,7 +137,7 @@ export class Events extends React.Component<Props> {
|
|||||||
renderHeaderTitle="Events"
|
renderHeaderTitle="Events"
|
||||||
customizeHeader={this.customizeHeader}
|
customizeHeader={this.customizeHeader}
|
||||||
isSelectable={false}
|
isSelectable={false}
|
||||||
items={visibleItems}
|
getItems={() => this.visibleItems}
|
||||||
virtual={!compact}
|
virtual={!compact}
|
||||||
tableProps={{
|
tableProps={{
|
||||||
sortSyncWithUrl: false,
|
sortSyncWithUrl: false,
|
||||||
|
|||||||
@ -136,7 +136,7 @@ export class NonInjectedUpgradeChart extends React.Component<Props & Dependencie
|
|||||||
const { tabId, release, value, error, onChange, onError, upgrade, versions, version } = this;
|
const { tabId, release, value, error, onChange, onError, upgrade, versions, version } = this;
|
||||||
const { className } = this.props;
|
const { className } = this.props;
|
||||||
|
|
||||||
if (!release || this.props.upgradeChartTabStore.isReady(tabId) || !version) {
|
if (!release || !this.props.upgradeChartTabStore.isReady(tabId) || !version) {
|
||||||
return <Spinner center />;
|
return <Spinner center />;
|
||||||
}
|
}
|
||||||
const currentVersion = release.getVersion();
|
const currentVersion = release.getVersion();
|
||||||
|
|||||||
@ -111,7 +111,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
color: var(--textColorAccent);
|
color: var(--color-active);
|
||||||
box-shadow: 0 0 0 2px var(--iconActiveBackground);
|
box-shadow: 0 0 0 2px var(--iconActiveBackground);
|
||||||
background-color: var(--iconActiveBackground);
|
background-color: var(--iconActiveBackground);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import "./item-list-layout.scss";
|
|||||||
|
|
||||||
import React, { ReactNode } from "react";
|
import React, { ReactNode } from "react";
|
||||||
import { computed, makeObservable } from "mobx";
|
import { computed, makeObservable } from "mobx";
|
||||||
import { observer } from "mobx-react";
|
import { Observer, observer } from "mobx-react";
|
||||||
import { ConfirmDialog, ConfirmDialogParams } from "../confirm-dialog";
|
import { ConfirmDialog, ConfirmDialogParams } from "../confirm-dialog";
|
||||||
import { Table, TableCell, TableCellProps, TableHead, TableProps, TableRow, TableRowProps, TableSortCallbacks } from "../table";
|
import { Table, TableCell, TableCellProps, TableHead, TableProps, TableRow, TableRowProps, TableSortCallbacks } from "../table";
|
||||||
import { boundMethod, cssNames, IClassName, isReactNode, prevDefault, stopPropagation } from "../../utils";
|
import { boundMethod, cssNames, IClassName, isReactNode, prevDefault, stopPropagation } from "../../utils";
|
||||||
@ -70,6 +70,10 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
|
|||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
getRow(uid: string) {
|
getRow(uid: string) {
|
||||||
|
return (
|
||||||
|
<div key={uid}>
|
||||||
|
<Observer>
|
||||||
|
{() => {
|
||||||
const {
|
const {
|
||||||
isSelectable, renderTableHeader, renderTableContents, renderItemMenu,
|
isSelectable, renderTableHeader, renderTableContents, renderItemMenu,
|
||||||
store, hasDetailsView, onDetails,
|
store, hasDetailsView, onDetails,
|
||||||
@ -83,7 +87,6 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<TableRow
|
||||||
key={itemId}
|
|
||||||
nowrap
|
nowrap
|
||||||
searchItem={item}
|
searchItem={item}
|
||||||
sortItem={item}
|
sortItem={item}
|
||||||
@ -98,13 +101,17 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
|
|||||||
onClick={prevDefault(() => store.toggleSelection(item))}
|
onClick={prevDefault(() => store.toggleSelection(item))}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{
|
{renderTableContents(item).map((content, index) => {
|
||||||
renderTableContents(item).map((content, index) => {
|
const cellProps: TableCellProps = isReactNode(content)
|
||||||
const cellProps: TableCellProps = isReactNode(content) ? { children: content } : content;
|
? { children: content }
|
||||||
|
: content;
|
||||||
const headCell = renderTableHeader?.[index];
|
const headCell = renderTableHeader?.[index];
|
||||||
|
|
||||||
if (copyClassNameFromHeadCells && headCell) {
|
if (copyClassNameFromHeadCells && headCell) {
|
||||||
cellProps.className = cssNames(cellProps.className, headCell.className);
|
cellProps.className = cssNames(
|
||||||
|
cellProps.className,
|
||||||
|
headCell.className,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!headCell || this.showColumn(headCell)) {
|
if (!headCell || this.showColumn(headCell)) {
|
||||||
@ -112,8 +119,7 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
})
|
})}
|
||||||
}
|
|
||||||
{renderItemMenu && (
|
{renderItemMenu && (
|
||||||
<TableCell className="menu">
|
<TableCell className="menu">
|
||||||
<div onClick={stopPropagation}>
|
<div onClick={stopPropagation}>
|
||||||
@ -123,6 +129,10 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
|
|||||||
)}
|
)}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
|
}}
|
||||||
|
</Observer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
@ -190,12 +200,16 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
|
|||||||
return (
|
return (
|
||||||
<TableHead showTopLine nowrap>
|
<TableHead showTopLine nowrap>
|
||||||
{isSelectable && (
|
{isSelectable && (
|
||||||
|
<Observer>
|
||||||
|
{() => (
|
||||||
<TableCell
|
<TableCell
|
||||||
checkbox
|
checkbox
|
||||||
isChecked={store.isSelectedAll(enabledItems)}
|
isChecked={store.isSelectedAll(enabledItems)}
|
||||||
onClick={prevDefault(() => store.toggleSelectionAll(enabledItems))}
|
onClick={prevDefault(() => store.toggleSelectionAll(enabledItems))}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
</Observer>
|
||||||
|
)}
|
||||||
{renderTableHeader.map((cellProps, index) => (
|
{renderTableHeader.map((cellProps, index) => (
|
||||||
this.showColumn(cellProps) && (
|
this.showColumn(cellProps) && (
|
||||||
<TableCell key={cellProps.id ?? index} {...cellProps} />
|
<TableCell key={cellProps.id ?? index} {...cellProps} />
|
||||||
@ -213,7 +227,6 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
|
|||||||
store, hasDetailsView, addRemoveButtons = {}, virtual, sortingCallbacks,
|
store, hasDetailsView, addRemoveButtons = {}, virtual, sortingCallbacks,
|
||||||
detailsItem, className, tableProps = {}, tableId,
|
detailsItem, className, tableProps = {}, tableId,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { selectedItems } = store;
|
|
||||||
const selectedItemId = detailsItem && detailsItem.getId();
|
const selectedItemId = detailsItem && detailsItem.getId();
|
||||||
const classNames = cssNames(className, "box", "grow", ThemeStore.getInstance().activeTheme.type);
|
const classNames = cssNames(className, "box", "grow", ThemeStore.getInstance().activeTheme.type);
|
||||||
|
|
||||||
@ -234,11 +247,18 @@ export class ItemListLayoutContent<I extends ItemObject> extends React.Component
|
|||||||
{this.renderTableHeader()}
|
{this.renderTableHeader()}
|
||||||
{this.renderItems()}
|
{this.renderItems()}
|
||||||
</Table>
|
</Table>
|
||||||
|
|
||||||
|
<Observer>
|
||||||
|
{() => (
|
||||||
<AddRemoveButtons
|
<AddRemoveButtons
|
||||||
onRemove={selectedItems.length ? this.removeItemsDialog : null}
|
onRemove={
|
||||||
removeTooltip={`Remove selected items (${selectedItems.length})`}
|
store.selectedItems.length ? this.removeItemsDialog : null
|
||||||
|
}
|
||||||
|
removeTooltip={`Remove selected items (${store.selectedItems.length})`}
|
||||||
{...addRemoveButtons}
|
{...addRemoveButtons}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
</Observer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,7 +46,6 @@ export function hideDetails() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getDetailsUrl(selfLink: string, resetSelected = false, mergeGlobals = true) {
|
export function getDetailsUrl(selfLink: string, resetSelected = false, mergeGlobals = true) {
|
||||||
console.debug("getDetailsUrl", { selfLink, resetSelected, mergeGlobals });
|
|
||||||
const params = new URLSearchParams(mergeGlobals ? navigation.searchParams : "");
|
const params = new URLSearchParams(mergeGlobals ? navigation.searchParams : "");
|
||||||
|
|
||||||
params.set(kubeDetailsUrlParam.name, selfLink);
|
params.set(kubeDetailsUrlParam.name, selfLink);
|
||||||
|
|||||||
@ -1762,10 +1762,10 @@
|
|||||||
"@types/react-dom" "*"
|
"@types/react-dom" "*"
|
||||||
"@types/react-transition-group" "*"
|
"@types/react-transition-group" "*"
|
||||||
|
|
||||||
"@types/react-table@^7.7.8":
|
"@types/react-table@^7.7.9":
|
||||||
version "7.7.8"
|
version "7.7.9"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-table/-/react-table-7.7.8.tgz#b1aa5fb7a54432969262d2306b87fdbb9a5ee647"
|
resolved "https://registry.yarnpkg.com/@types/react-table/-/react-table-7.7.9.tgz#ea82875775fc6ee71a28408dcc039396ae067c92"
|
||||||
integrity sha512-OMhbPlf+uUGte3M1WdArEKeBkyQ1XJxKvFYs+o1dGGGyaSVIqxPPQmBZ6Skkw0V9y0F/kOY7rnTD8r9GbfpBOg==
|
integrity sha512-ejP/J20Zlj9VmuLh73YgYkW2xOSFTW39G43rPH93M4mYWdMmqv66lCCr+axZpkdtlNLGjvMG2CwzT4S6abaeGQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user