diff --git a/.azure-pipelines-k8s-matrix.yml b/.azure-pipelines-k8s-matrix.yml index 77129460a3..cdcc18b740 100644 --- a/.azure-pipelines-k8s-matrix.yml +++ b/.azure-pipelines-k8s-matrix.yml @@ -15,7 +15,7 @@ trigger: none jobs: - job: Linux pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-18.04 strategy: matrix: kube_1.16: @@ -46,7 +46,7 @@ jobs: curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 sudo install minikube-linux-amd64 /usr/local/bin/minikube sudo minikube start --driver=none --kubernetes-version $(kubernetes_version) - # Although the kube and minikube config files are in placed $HOME they are owned by root + sudo mv /root/.kube /root/.minikube $HOME sudo chown -R $USER $HOME/.kube $HOME/.minikube displayName: Install integration test dependencies - script: make node_modules diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 3e1b43f875..3c928430ed 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -109,7 +109,7 @@ jobs: - job: Linux pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-18.04 strategy: matrix: node_14.x: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b319c97770..7590f42289 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,7 +2,7 @@ name: Test on: pull_request: branches: - - "*" + - "**" push: branches: - master @@ -12,7 +12,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-16.04, macos-11, windows-2019] + os: [ubuntu-18.04, macos-11, windows-2019] node-version: [14.x] steps: - name: Checkout Release from lens @@ -67,15 +67,9 @@ jobs: - run: make test-extensions name: Run In-tree Extension tests - - run: | - sudo apt-get update - sudo apt-get install libgconf-2-4 conntrack -y - curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 - sudo install minikube-linux-amd64 /usr/local/bin/minikube - sudo minikube start --driver=none - # Although the kube and minikube config files are in placed $HOME they are owned by root - sudo chown -R $USER $HOME/.kube $HOME/.minikube - name: Install integration test dependencies + - name: Install integration test dependencies + id: minikube + uses: medyagh/setup-minikube@5a9a7104d7322fa40424de8855c84685e89cefd7 if: runner.os == 'Linux' - run: xvfb-run --auto-servernum --server-args='-screen 0, 1600x900x24' make integration diff --git a/RELEASE_GUIDE.md b/RELEASE_GUIDE.md index fde9786459..b11a47c134 100644 --- a/RELEASE_GUIDE.md +++ b/RELEASE_GUIDE.md @@ -2,7 +2,9 @@ Lens releases are built by CICD automatically on git tags. The typical release process flow is the following: -1. From a clean and up to date `master` run `make release-version ` where `` is one of the following: +1. If doing a patch release checkout the `release/vMAJOR.MINOR` branch for the appropriate `MAJOR`/`MINOR` version and manually `cherry-pick` the PRs required for the patch that were commited to master. If there are any conflicts they must be resolved manually. If necessary, get assistance from the PR authors. + +1. From a clean and up to date `master` (or `release/vMAJOR.MINOR` if doing a patch release) run `make release-version ` where `` is one of the following: - `major` - `minor` - `patch` @@ -17,15 +19,15 @@ Lens releases are built by CICD automatically on git tags. The typical release p - `rc` This assumes origin is set to https://github.com/lensapp/lens.git. If not then set GIT_REMOTE to the remote that is set to https://github.com/lensapp/lens.git. For example run `GIT_REMOTE=upstream make release-version ...` -1. Open the PR (git should have printed a link to GitHub in the console) with the contents of all the accepted PRs since the last release. The PR description needs to be filled with the draft release description. From https://github.com/lensapp/lens click on Releases, the draft release should be first in the list, click `Edit` and copy/paste the markdown to the PR description. Add the `skip-changelog` label and click `Create Pull Request`. -1. After the PR is accepted and passes CI (and before merging), go to the same branch and run `make tag-release` (set GIT_REMOTE if necessary). This additionally runs the azure jobs to build the binaries and put them on S3. +1. Open the PR (git should have printed a link to GitHub in the console) with the contents of all the accepted PRs since the last release. The PR description needs to be filled with the draft release description. From https://github.com/lensapp/lens click on Releases, the draft release should be first in the list, click `Edit` and copy/paste the markdown to the PR description. Add the `skip-changelog` label and click `Create Pull Request`. If this is a patch release be sure to set the PR base branch to `release/vMAJOR.MINOR` instead of `master`. +1. After the PR is accepted and passes CI (and before merging), go to the same branch and run `make tag-release` (set GIT_REMOTE if necessary). This additionally triggers the azure jobs to build the binaries and put them on S3. 1. If the CI fails at this stage the problem needs to be fixed. Sometimes an azure job fails due to outside service issues (e.g. Apple signing occasionally fails), in which case the specific azure job can be rerun from https://dev.azure.com/lensapp/lensapp/_build. Otherwise changes to the codebase may need to be done and committed to the release branch and pushed to https://github.com/lensapp/lens. CI will run again. As well the release tag needs to be manually set to this new commit. You can do something like: - `git push origin :refs/tags/vX.Y.Z-beta.N` (removes the tag from https://github.com/lensapp/lens) - `git tag -fa vX.Y.Z-beta.N` (move the tag locally to the current commit) - `git push origin --tags` (update the tags on https://github.com/lensapp/lens to reflect this local change) 1. Once CI passes again go to the releases tab on GitHub. You can use the existing draft release prepared by k8slens-bot (select the correct tag). Or you can create a new release from the tag that was created, and make sure that the change log is the same as that of the PR, and the title is the tag. Either way, click the prerelease checkbox if this is not a new major, minor, or patch version before clicking `Publish release`. -1. Merge the release PR after the release is published. GitHub should delete the branch once it is merged. +1. Merge the release PR after the release is published. If it is a patch release then there is no need to squash the cherry-picked commits as part of the merge. GitHub should delete the branch once it is merged. 1. If you have just released a new major or minor version then create a new `release/vMAJOR.MINOR` branch from that same tag and push it to https://github.com/lensapp/lens. Given the commit of the merged release PR from the master branch you can do this like `git push origin :refs/heads/release/vX.Y` @@ -37,4 +39,4 @@ Other tasks - generate a changelog from the prerelease descriptions (for major/minor releases) - announce the release on lens and lens-hq slack channels (release is announced automatically on the community slack lens channel through the above publishing process) - announce on lens-hq that master is open for PR merges for the next release (for major/minor releases) - - update issues on github (bump those that did not make it into the release to a subsequent release) (for major/minor releases) \ No newline at end of file + - update issues on github (bump those that did not make it into the release to a subsequent release) (for major/minor/patch releases) \ No newline at end of file diff --git a/extensions/metrics-cluster-feature/package-lock.json b/extensions/metrics-cluster-feature/package-lock.json index 981cb43dff..1d2e2217cb 100644 --- a/extensions/metrics-cluster-feature/package-lock.json +++ b/extensions/metrics-cluster-feature/package-lock.json @@ -6645,9 +6645,9 @@ } }, "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, "to-arraybuffer": { diff --git a/extensions/node-menu/package-lock.json b/extensions/node-menu/package-lock.json index 02d953b830..88ada2da7e 100644 --- a/extensions/node-menu/package-lock.json +++ b/extensions/node-menu/package-lock.json @@ -6608,9 +6608,9 @@ } }, "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, "to-arraybuffer": { diff --git a/extensions/pod-menu/package-lock.json b/extensions/pod-menu/package-lock.json index 3abe5a107f..b2c2e736fe 100644 --- a/extensions/pod-menu/package-lock.json +++ b/extensions/pod-menu/package-lock.json @@ -6561,9 +6561,9 @@ } }, "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, "to-arraybuffer": { diff --git a/package.json b/package.json index 0ff7e1727c..6b19e77edc 100644 --- a/package.json +++ b/package.json @@ -216,14 +216,14 @@ "md5-file": "^5.0.0", "mobx": "^6.3.0", "mobx-observable-history": "^2.0.1", - "mobx-react": "^7.1.0", + "mobx-react": "^7.2.0", "mock-fs": "^4.14.0", "moment": "^2.29.1", "moment-timezone": "^0.5.33", "monaco-editor": "^0.26.1", "node-fetch": "^2.6.1", "node-pty": "^0.10.1", - "npm": "^6.14.8", + "npm": "^6.14.15", "openid-client": "^3.15.2", "p-limit": "^3.1.0", "path-to-regexp": "^6.1.0", @@ -348,7 +348,6 @@ "jest-canvas-mock": "^2.3.1", "jest-fetch-mock": "^3.0.3", "jest-mock-extended": "^1.0.16", - "json-to-pretty-yaml": "^1.2.2", "make-plural": "^6.2.2", "mini-css-extract-plugin": "^1.6.0", "node-gyp": "7.1.2", @@ -368,7 +367,7 @@ "react-select-event": "^5.1.0", "react-table": "^7.7.0", "react-window": "^1.8.5", - "sass": "^1.39.0", + "sass": "^1.41.1", "sass-loader": "^8.0.2", "sharp": "^0.29.0", "style-loader": "^2.0.0", diff --git a/src/common/catalog-entities/kubernetes-cluster.ts b/src/common/catalog-entities/kubernetes-cluster.ts index b2ff284069..3335d3803d 100644 --- a/src/common/catalog-entities/kubernetes-cluster.ts +++ b/src/common/catalog-entities/kubernetes-cluster.ts @@ -51,6 +51,7 @@ export interface KubernetesClusterSpec extends CatalogEntitySpec { material?: string; background?: string; }; + accessibleNamespaces?: string[]; } export interface KubernetesClusterMetadata extends CatalogEntityMetadata { diff --git a/src/common/catalog-entities/web-link.ts b/src/common/catalog-entities/web-link.ts index d54aa8533a..8309945f24 100644 --- a/src/common/catalog-entities/web-link.ts +++ b/src/common/catalog-entities/web-link.ts @@ -57,6 +57,10 @@ export class WebLink extends CatalogEntity(this) + ?.emit("contextMenuOpen", this, context); } } diff --git a/src/common/k8s-api/kube-object.store.ts b/src/common/k8s-api/kube-object.store.ts index dc428eb08b..f90eaab8e5 100644 --- a/src/common/k8s-api/kube-object.store.ts +++ b/src/common/k8s-api/kube-object.store.ts @@ -159,7 +159,7 @@ export abstract class KubeObjectStore extends ItemStore this.loadedNamespaces = namespaces; return Promise // load resources per namespace - .all(namespaces.map(namespace => api.list({ namespace, reqInit }))) + .all(namespaces.map(namespace => api.list({ namespace, reqInit }, this.query))) .then(items => items.flat().filter(Boolean)); } } diff --git a/src/common/logger.ts b/src/common/logger.ts index 825fa2493b..ca975871b8 100644 --- a/src/common/logger.ts +++ b/src/common/logger.ts @@ -59,24 +59,24 @@ if (ipcMain) { ), }) ); + + if (!isTestEnv) { + transports.push( + new winston.transports.File({ + handleExceptions: false, + level: logLevel, + filename: "lens.log", + dirname: getPath("logs"), + maxsize: 16 * 1024, + maxFiles: 16, + tailable: true, + }) + ); + } } else { transports.push(new BrowserConsole()); } -if (!isTestEnv) { - transports.push( - new winston.transports.File({ - handleExceptions: false, - level: logLevel, - filename: "lens.log", - dirname: getPath("logs"), - maxsize: 16 * 1024, - maxFiles: 16, - tailable: true, - }) - ); -} - export default winston.createLogger({ format: format.simple(), transports, diff --git a/src/extensions/registries/base-registry.ts b/src/extensions/registries/base-registry.ts index af00fb48fd..9b596172c9 100644 --- a/src/extensions/registries/base-registry.ts +++ b/src/extensions/registries/base-registry.ts @@ -25,7 +25,7 @@ import { Singleton } from "../../common/utils"; import { LensExtension } from "../lens-extension"; export class BaseRegistry extends Singleton { - private items = observable.map([], {deep: false}); + private items = observable.map([], { deep: false }); constructor() { super(); diff --git a/src/extensions/registries/command-registry.ts b/src/extensions/registries/command-registry.ts index 5c38e32c71..3916b0f721 100644 --- a/src/extensions/registries/command-registry.ts +++ b/src/extensions/registries/command-registry.ts @@ -25,9 +25,9 @@ import { BaseRegistry } from "./base-registry"; import type { LensExtension } from "../lens-extension"; import type { CatalogEntity } from "../../common/catalog"; -export type CommandContext = { +export interface CommandContext { entity?: CatalogEntity; -}; +} export interface CommandRegistration { id: string; diff --git a/src/main/cluster-manager.ts b/src/main/cluster-manager.ts index fdc3bf3e1a..307b4592f1 100644 --- a/src/main/cluster-manager.ts +++ b/src/main/cluster-manager.ts @@ -193,7 +193,10 @@ export class ClusterManager extends Singleton { } else { cluster.kubeConfigPath = entity.spec.kubeconfigPath; cluster.contextName = entity.spec.kubeconfigContext; - cluster.accessibleNamespaces = entity.spec.accessibleNamespaces ?? []; + + if (entity.spec.accessibleNamespace) { + cluster.accessibleNamespaces = entity.spec.accessibleNamespaces; + } this.updateEntityFromCluster(cluster); } diff --git a/src/main/kubectl.ts b/src/main/kubectl.ts index fbfd044dab..4695e8272e 100644 --- a/src/main/kubectl.ts +++ b/src/main/kubectl.ts @@ -275,7 +275,7 @@ export class Kubectl { return false; }); - isValid = !await this.checkBinary(this.path, false); + isValid = await this.checkBinary(this.path, false); } if (!isValid) { diff --git a/src/main/window-manager.ts b/src/main/window-manager.ts index be6d71498e..80e3224fd4 100644 --- a/src/main/window-manager.ts +++ b/src/main/window-manager.ts @@ -31,7 +31,6 @@ import { IpcRendererNavigationEvents } from "../renderer/navigation/events"; import logger from "./logger"; import { productName } from "../common/vars"; import { LensProxy } from "./lens-proxy"; -import * as path from "path"; function isHideable(window: BrowserWindow | null): boolean { return Boolean(window && !window.isDestroyed()); @@ -85,7 +84,6 @@ export class WindowManager extends Singleton { titleBarStyle: "hiddenInset", backgroundColor: "#1e2124", webPreferences: { - preload: path.join(__static, "build", "preload.js"), nodeIntegration: true, nodeIntegrationInSubFrames: true, enableRemoteModule: true, diff --git a/src/renderer/api/catalog-entity-registry.ts b/src/renderer/api/catalog-entity-registry.ts index 7b09ff273b..d731e2562a 100644 --- a/src/renderer/api/catalog-entity-registry.ts +++ b/src/renderer/api/catalog-entity-registry.ts @@ -31,7 +31,7 @@ import { once } from "lodash"; export type EntityFilter = (entity: CatalogEntity) => any; export class CatalogEntityRegistry { - @observable.ref activeEntity: CatalogEntity; + @observable protected activeEntityId: string | undefined = undefined; protected _entities = observable.map([], { deep: true }); protected filters = observable.set([], { deep: false, @@ -46,6 +46,22 @@ export class CatalogEntityRegistry { makeObservable(this); } + get activeEntity(): CatalogEntity | null { + return this._entities.get(this.activeEntityId) || null; + } + + set activeEntity(raw: CatalogEntity | string | null) { + if (raw) { + const id = typeof raw === "string" + ? raw + : raw.getId(); + + this.activeEntityId = id; + } else { + this.activeEntityId = undefined; + } + } + init() { ipcRendererOn("catalog:items", (event, items: (CatalogEntityData & CatalogEntityKindData)[]) => { this.updateItems(items); diff --git a/src/renderer/api/terminal-api.ts b/src/renderer/api/terminal-api.ts index 347dfac224..59e248fba5 100644 --- a/src/renderer/api/terminal-api.ts +++ b/src/renderer/api/terminal-api.ts @@ -58,7 +58,6 @@ export class TerminalApi extends WebSocketApi { public onReady = new EventEmitter<[]>(); @observable public isReady = false; - @observable public shellRunCommandsFinished = false; public readonly url: string; constructor(protected options: TerminalApiQuery) { @@ -92,7 +91,6 @@ export class TerminalApi extends WebSocketApi { connect() { this.emitStatus("Connecting ..."); this.onData.addListener(this._onReady, { prepend: true }); - this.onData.addListener(this._onShellRunCommandsFinished); super.connect(this.url); } @@ -109,24 +107,6 @@ export class TerminalApi extends WebSocketApi { this.onReady.removeAllListeners(); } - _onShellRunCommandsFinished = (data: string) => { - if (!data) { - return; - } - - /** - * This is a heuistic for ditermining when a shell has finished executing - * its own rc file (or RunCommands file) such as `.bashrc` or `.zshrc`. - * - * This heuistic assumes that the prompt line of a terminal is a single line - * and ends with a whitespace character. - */ - if (data.match(/\r?\n/) === null && data.match(/\s$/)) { - this.shellRunCommandsFinished = true; - this.onData.removeListener(this._onShellRunCommandsFinished); - } - }; - @boundMethod protected _onReady(data: string) { if (!data) return true; diff --git a/src/renderer/components/+add-cluster/add-cluster.tsx b/src/renderer/components/+add-cluster/add-cluster.tsx index e987c1d4bb..81a75019fa 100644 --- a/src/renderer/components/+add-cluster/add-cluster.tsx +++ b/src/renderer/components/+add-cluster/add-cluster.tsx @@ -62,7 +62,7 @@ export class AddCluster extends React.Component { @observable kubeContexts = observable.map(); @observable customConfig = ""; @observable isWaiting = false; - @observable errorText: string; + @observable errors: string[] = []; constructor(props: {}) { super(props); @@ -75,7 +75,7 @@ export class AddCluster extends React.Component { @computed get allErrors(): string[] { return [ - this.errorText, + ...this.errors, ...iter.map(this.kubeContexts.values(), ({ error }) => error) ].filter(Boolean); } @@ -85,7 +85,14 @@ export class AddCluster extends React.Component { const { config, error } = loadConfigFromString(this.customConfig.trim() || "{}"); this.kubeContexts.replace(getContexts(config)); - this.errorText = error?.toString(); + + if (error) { + this.errors.push(error.toString()); + } + + if (config.contexts.length === 0) { + this.errors.push('No contexts defined, either missing the "contexts" field, or it is empty.'); + } }, 500); @action @@ -124,7 +131,7 @@ export class AddCluster extends React.Component { value={this.customConfig} onChange={value => { this.customConfig = value; - this.errorText = ""; + this.errors.length = 0; this.refreshContexts(); }} /> diff --git a/src/renderer/components/+apps-helm-charts/helm-charts.tsx b/src/renderer/components/+apps-helm-charts/helm-charts.tsx index 8a47654adc..da5f9f8c5a 100644 --- a/src/renderer/components/+apps-helm-charts/helm-charts.tsx +++ b/src/renderer/components/+apps-helm-charts/helm-charts.tsx @@ -55,6 +55,14 @@ export class HelmCharts extends Component { return helmChartStore.getByName(chartName, repo); } + onDetails = (chart: HelmChart) => { + if (chart === this.selectedChart) { + this.hideDetails(); + } else { + this.showDetails(chart); + } + }; + showDetails = (chart: HelmChart) => { if (!chart) { navigation.push(helmChartsURL()); @@ -121,7 +129,7 @@ export class HelmCharts extends Component { { className: "menu" } ]} detailsItem={this.selectedChart} - onDetails={this.showDetails} + onDetails={this.onDetails} /> {this.selectedChart && ( { }); } + onDetails = (item: HelmRelease) => { + if (item === this.selectedRelease) { + this.hideDetails(); + } else { + this.showDetails(item); + } + }; + showDetails = (item: HelmRelease) => { navigation.push(releaseURL({ params: { @@ -169,7 +177,7 @@ export class HelmReleases extends Component { message: this.renderRemoveDialogMessage(selectedItems) })} detailsItem={this.selectedRelease} - onDetails={this.showDetails} + onDetails={this.onDetails} /> { } onDetails = (item: CatalogEntityItem) => { - this.catalogEntityStore.selectedItemId = item.getId(); + if (this.catalogEntityStore.selectedItemId === item.getId()) { + this.catalogEntityStore.selectedItemId = null; + } else { + this.catalogEntityStore.selectedItemId = item.getId(); + } }; onMenuItemClick(menuItem: CatalogEntityContextMenu) { diff --git a/src/renderer/components/+cluster/cluster-issues.tsx b/src/renderer/components/+cluster/cluster-issues.tsx index 9b5f73b9a6..fb5b98bc28 100644 --- a/src/renderer/components/+cluster/cluster-issues.tsx +++ b/src/renderer/components/+cluster/cluster-issues.tsx @@ -33,7 +33,7 @@ import { boundMethod, cssNames, prevDefault } from "../../utils"; import type { ItemObject } from "../../../common/item.store"; import { Spinner } from "../spinner"; import { ThemeStore } from "../../theme.store"; -import { kubeSelectedUrlParam, showDetails } from "../kube-detail-params"; +import { kubeSelectedUrlParam, toggleDetails } from "../kube-detail-params"; import { kubeWatchApi } from "../../../common/k8s-api/kube-watch-api"; import { apiManager } from "../../../common/k8s-api/api-manager"; @@ -124,7 +124,7 @@ export class ClusterIssues extends React.Component { key={getId()} sortItem={warning} selected={selfLink === kubeSelectedUrlParam.get()} - onClick={prevDefault(() => showDetails(selfLink))} + onClick={prevDefault(() => toggleDetails(selfLink))} > {message} diff --git a/src/renderer/components/+entity-settings/entity-settings.scss b/src/renderer/components/+entity-settings/entity-settings.module.css similarity index 76% rename from src/renderer/components/+entity-settings/entity-settings.scss rename to src/renderer/components/+entity-settings/entity-settings.module.css index 30c01b8a67..604ecd5c5b 100644 --- a/src/renderer/components/+entity-settings/entity-settings.scss +++ b/src/renderer/components/+entity-settings/entity-settings.module.css @@ -19,26 +19,12 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -.EntitySettings { - $spacing: $padding * 3; - - - // TODO: move sub-component styles to separate files - .admin-note { - font-size: small; - opacity: 0.5; - margin-left: $margin; - } - - .button-area { - margin-top: $margin * 2; - } - - .file-loader { - margin-top: $margin * 2; - } - - .Input, .Select { - margin-top: $padding; - } -} +.entityName { + @apply font-bold overflow-hidden; + word-break: break-word; + color: var(--textColorAccent); + display: -webkit-box; + /* Simulate text-overflow:ellipsis styles but for multiple text lines */ + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; +} \ No newline at end of file diff --git a/src/renderer/components/+entity-settings/entity-settings.tsx b/src/renderer/components/+entity-settings/entity-settings.tsx index f656b6fa9d..bcb5895413 100644 --- a/src/renderer/components/+entity-settings/entity-settings.tsx +++ b/src/renderer/components/+entity-settings/entity-settings.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import "./entity-settings.scss"; +import styles from "./entity-settings.module.css"; import React from "react"; import { observable, makeObservable } from "mobx"; @@ -98,7 +98,9 @@ export class EntitySettings extends React.Component { source={this.entity.metadata.source} src={this.entity.spec.icon?.src} /> -

{this.entity.metadata.name}

+
+ {this.entity.metadata.name} +
{ groups.map((group, groupIndex) => ( @@ -138,7 +140,6 @@ export class EntitySettings extends React.Component { return ( diff --git a/src/renderer/components/activate-entity-command/activate-entity-command.tsx b/src/renderer/components/activate-entity-command/activate-entity-command.tsx new file mode 100644 index 0000000000..110afd6b3c --- /dev/null +++ b/src/renderer/components/activate-entity-command/activate-entity-command.tsx @@ -0,0 +1,58 @@ +/** + * 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 { computed } from "mobx"; +import { observer } from "mobx-react"; +import React from "react"; +import { CatalogEntity, catalogEntityRunContext } from "../../api/catalog-entity"; +import { catalogEntityRegistry } from "../../api/catalog-entity-registry"; +import { CommandOverlay } from "../command-palette"; +import { Select } from "../select"; + +@observer +export class ActivateEntityCommand extends React.Component { + @computed get options() { + return catalogEntityRegistry.items.map(entity => ({ + label: `${entity.kind}: ${entity.getName()}`, + value: entity, + })); + } + + onSelect(entity: CatalogEntity): void { + entity.onRun?.(catalogEntityRunContext); + CommandOverlay.close(); + } + + render() { + return ( +