From 500ac15703c371afdd7a64600e446f66d004b6a3 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 12 May 2023 09:46:48 -0400 Subject: [PATCH] chore: Extract sidebar item injection token into separate package - Add unit tests to cover custom resources sidebar items Signed-off-by: Sebastian Malton --- open-lens/package.json | 1 + open-lens/src/main/index.ts | 11 +- open-lens/src/renderer/index.ts | 2 + package-lock.json | 26 + packages/cluster-sidebar/.eslintrc.js | 6 + packages/cluster-sidebar/.prettierrc | 1 + packages/cluster-sidebar/index.ts | 8 + packages/cluster-sidebar/jest.config.js | 1 + packages/cluster-sidebar/package.json | 48 + packages/cluster-sidebar/src/feature.ts | 14 + .../src/sidebar-items.injectable.ts | 62 + packages/cluster-sidebar/src/tokens.ts | 37 + packages/cluster-sidebar/tsconfig.json | 4 + packages/cluster-sidebar/webpack.config.js | 1 + packages/core/package.json | 1 + .../crd-list/crd-list-route.injectable.ts | 21 - .../custom-resource-definitions.injectable.ts | 24 + .../custom-resources-route.injectable.ts | 4 +- ...custom-resource-definitions.injectable.ts} | 4 +- ...navigate-to-custom-resources.injectable.ts | 5 +- .../ingress-classes-route.injectable.ts | 24 + .../ingress-classeses-route.injectable.ts | 30 - .../navigate-to-ingress-classes.injectable.ts | 4 +- .../orderable/orderable.ts | 25 - ...lication-menu-item-composite.injectable.ts | 61 +- .../application-menu-item-injection-token.ts | 2 +- .../custom-resources-in-sidebar.test.tsx.snap | 3847 +++++++++++++++++ .../custom-resources-in-sidebar.test.tsx | 244 ++ .../cluster/order-of-sidebar-items.test.tsx | 134 +- ...debar-and-tab-navigation-for-core.test.tsx | 58 +- .../visibility-of-sidebar-items.test.tsx | 25 +- .../preference-item-injection-token.ts | 4 +- .../preference-items/preference-tab-root.tsx | 5 +- .../preferences-composite.injectable.ts | 35 +- .../request-api-resources.injectable.ts | 2 +- .../entity-details/detail-items.injectable.ts | 2 +- ...ster-overview-sidebar-items.injectable.tsx | 39 +- .../components/cluster/cluster-overview.tsx | 9 +- .../internal-commands.injectable.tsx | 2 +- .../sidebar-items.injectable.tsx | 36 +- .../leases-sidebar-items.injectable.tsx | 35 +- .../limit-ranges-sidebar-items.injectable.tsx | 35 +- .../config-maps-sidebar-items.injectable.tsx | 35 +- ...configurations-sidebar-items.injectable.ts | 41 +- ...ption-budgets-sidebar-items.injectable.tsx | 35 +- .../priority-classes-items.injectable.tsx | 35 +- ...source-quotas-sidebar-items.injectable.tsx | 35 +- .../runtime-classes-items.injectable.tsx | 38 - ...ntime-classes-sidebar-items.injectable.tsx | 33 + .../secrets-sidebar-items.injectable.tsx | 35 +- ...configurations-sidebar-items.injectable.ts | 41 +- ...-auto-scalers-sidebar-items.injectable.tsx | 36 +- .../config-sidebar-items.injectable.tsx | 36 +- .../crd-list-route-component.injectable.ts | 2 +- ...roups-sidebar-items-computed.injectable.ts | 86 + ...ps-sidebar-items-registrator.injectable.ts | 29 + ...rce-definitions-sidebar-item.injectable.ts | 30 + ...stom-resource-sidebar-items.injectable.tsx | 59 - ...om-resources-route-component.injectable.ts | 2 +- ...m-resources-route-parameters.injectable.ts | 2 +- ...tom-resources-sidebar-items.injectable.tsx | 24 + ...-items-for-definition-groups.injectable.ts | 81 - .../entity-settings/active-tabs.injectable.ts | 2 +- .../entity-settings/settings.injectable.ts | 2 +- .../events-sidebar-items.injectable.tsx | 39 +- .../helm-charts-sidebar-items.injectable.tsx | 35 +- ...helm-releases-sidebar-items.injectable.tsx | 35 +- .../helm/helm-sidebar-items.injectable.tsx | 36 +- ...on-sidebar-item-registrator.injectable.tsx | 114 +- .../layout/siblings-in-tab-layout.tsx | 2 +- .../components/layout/sidebar-item.tsx | 142 +- .../layout/sidebar-items.injectable.ts | 80 - .../renderer/components/layout/sidebar.tsx | 4 +- .../components/layout/tab-layout-2.tsx | 2 +- .../namespaces-sidebar-items.injectable.tsx | 39 +- .../endpoints-sidebar-items.injectable.tsx | 35 +- ...ngress-class-route-component.injectable.ts | 9 +- ...ingress-classes-sidebar-item.injectable.ts | 30 + .../ingresses-sidebar-items.injectable.ts | 30 + .../ingresses-sidebar-items.injectable.tsx | 56 - ...work-policies-sidebar-items.injectable.tsx | 35 +- ...port-forwards-sidebar-items.injectable.tsx | 36 +- .../services-sidebar-items.injectable.tsx | 35 +- .../network-sidebar-items.injectable.tsx | 36 +- .../nodes/nodes-sidebar-items.injectable.tsx | 39 +- ...rity-policies-sidebar-items.injectable.tsx | 35 +- ...orage-classes-sidebar-items.injectable.tsx | 35 +- ...volume-claims-sidebar-items.injectable.tsx | 35 +- ...stent-volumes-sidebar-items.injectable.tsx | 35 +- .../storage-sidebar-items.injectable.tsx | 36 +- ...role-bindings-sidebar-items.injectable.tsx | 35 +- ...cluster-roles-sidebar-items.injectable.tsx | 35 +- ...role-bindings-sidebar-items.injectable.tsx | 35 +- .../roles/roles-sidebar-items.injectable.tsx | 36 +- ...vice-accounts-sidebar-items.injectable.tsx | 35 +- ...er-management-sidebar-items.injectable.tsx | 36 +- .../cron-jobs-sidebar-items.injectable.tsx | 36 +- .../daemonsets-sidebar-items.injectable.tsx | 35 +- .../deployments-sidebar-items.injectable.tsx | 35 +- .../jobs-sidebar-items.injectable.tsx | 35 +- ...oads-overview-sidebar-items.injectable.tsx | 35 +- .../workloads/workloads.injectable.ts | 2 +- .../pods-sidebar-items.injectable.ts | 35 +- .../replicasets-sidebar-items.injectable.tsx | 35 +- ...on-controller-sidebar-items.injectable.tsx | 35 +- .../statefulsets-sidebar-items.injectable.tsx | 35 +- .../workloads-sidebar-items.injectable.tsx | 36 +- .../core/src/renderer/getDiForUnitTesting.tsx | 2 + .../routes/sibling-tabs.injectable.ts | 15 +- packages/utility-features/utilities/index.ts | 3 +- .../{computed-or.ts => computed-helpers.ts} | 4 + .../utility-features/utilities/src/iter.ts | 19 +- .../utilities/src/orderable.ts | 14 + 113 files changed, 5645 insertions(+), 1681 deletions(-) create mode 100644 packages/cluster-sidebar/.eslintrc.js create mode 100644 packages/cluster-sidebar/.prettierrc create mode 100644 packages/cluster-sidebar/index.ts create mode 100644 packages/cluster-sidebar/jest.config.js create mode 100644 packages/cluster-sidebar/package.json create mode 100644 packages/cluster-sidebar/src/feature.ts create mode 100644 packages/cluster-sidebar/src/sidebar-items.injectable.ts create mode 100644 packages/cluster-sidebar/src/tokens.ts create mode 100644 packages/cluster-sidebar/tsconfig.json create mode 100644 packages/cluster-sidebar/webpack.config.js delete mode 100644 packages/core/src/common/front-end-routing/routes/cluster/custom-resources/crd-list/crd-list-route.injectable.ts create mode 100644 packages/core/src/common/front-end-routing/routes/cluster/custom-resources/custom-resource-definitions.injectable.ts rename packages/core/src/common/front-end-routing/routes/cluster/custom-resources/{custom-resources => }/custom-resources-route.injectable.ts (80%) rename packages/core/src/common/front-end-routing/routes/cluster/custom-resources/{crd-list/navigate-to-crd-list.injectable.ts => navigate-to-custom-resource-definitions.injectable.ts} (75%) rename packages/core/src/common/front-end-routing/routes/cluster/custom-resources/{custom-resources => }/navigate-to-custom-resources.injectable.ts (77%) create mode 100644 packages/core/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classes-route.injectable.ts delete mode 100644 packages/core/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classeses-route.injectable.ts delete mode 100644 packages/core/src/common/utils/composable-responsibilities/orderable/orderable.ts create mode 100644 packages/core/src/features/cluster/__snapshots__/custom-resources-in-sidebar.test.tsx.snap create mode 100644 packages/core/src/features/cluster/custom-resources-in-sidebar.test.tsx delete mode 100644 packages/core/src/renderer/components/config-runtime-classes/runtime-classes-items.injectable.tsx create mode 100644 packages/core/src/renderer/components/config-runtime-classes/runtime-classes-sidebar-items.injectable.tsx create mode 100644 packages/core/src/renderer/components/custom-resources/custom-resource-definition-groups-sidebar-items-computed.injectable.ts create mode 100644 packages/core/src/renderer/components/custom-resources/custom-resource-definition-groups-sidebar-items-registrator.injectable.ts create mode 100644 packages/core/src/renderer/components/custom-resources/custom-resource-definitions-sidebar-item.injectable.ts delete mode 100644 packages/core/src/renderer/components/custom-resources/custom-resource-sidebar-items.injectable.tsx create mode 100644 packages/core/src/renderer/components/custom-resources/custom-resources-sidebar-items.injectable.tsx delete mode 100644 packages/core/src/renderer/components/custom-resources/sidebar-items-for-definition-groups.injectable.ts delete mode 100644 packages/core/src/renderer/components/layout/sidebar-items.injectable.ts create mode 100644 packages/core/src/renderer/components/network-ingresses/ingress-classes-sidebar-item.injectable.ts create mode 100644 packages/core/src/renderer/components/network-ingresses/ingresses-sidebar-items.injectable.ts delete mode 100644 packages/core/src/renderer/components/network-ingresses/ingresses-sidebar-items.injectable.tsx rename packages/utility-features/utilities/src/{computed-or.ts => computed-helpers.ts} (73%) create mode 100644 packages/utility-features/utilities/src/orderable.ts diff --git a/open-lens/package.json b/open-lens/package.json index 278fa6980d..1e2d4a6062 100644 --- a/open-lens/package.json +++ b/open-lens/package.json @@ -184,6 +184,7 @@ "@k8slens/application-for-electron-main": "^6.5.0", "@k8slens/button": "^1.0.0", "@k8slens/cluster-settings": "^6.5.0", + "@k8slens/cluster-sidebar": "^1.0.0-alpha.0", "@k8slens/core": "^6.5.0", "@k8slens/ensure-binaries": "^6.5.0", "@k8slens/error-boundary": "^1.0.0", diff --git a/open-lens/src/main/index.ts b/open-lens/src/main/index.ts index 190fbe7201..6eaac3e1db 100644 --- a/open-lens/src/main/index.ts +++ b/open-lens/src/main/index.ts @@ -25,13 +25,14 @@ runInAction(() => { registerLensCore(di, environment); registerFeature(di, - loggerFeature, + loggerFeature, ); - registerFeature(di, - applicationFeature, - applicationFeatureForElectronMain, - messagingFeatureForMain, + registerFeature( + di, + applicationFeature, + applicationFeatureForElectronMain, + messagingFeatureForMain, ); try { diff --git a/open-lens/src/renderer/index.ts b/open-lens/src/renderer/index.ts index 9c2c90d6bb..19ce93e220 100644 --- a/open-lens/src/renderer/index.ts +++ b/open-lens/src/renderer/index.ts @@ -28,6 +28,7 @@ import { reactApplicationFeature } from "@k8slens/react-application"; import { routingFeature } from "@k8slens/routing"; import { loggerFeature } from "@k8slens/logger"; import { animateFeature } from "@k8slens/animate"; +import { clusterSidebarFeature } from "@k8slens/cluster-sidebar"; const environment = "renderer"; @@ -54,6 +55,7 @@ runInAction(() => { routingFeature, metricsFeature, animateFeature, + clusterSidebarFeature, ); autoRegister({ diff --git a/package-lock.json b/package-lock.json index 150ae619f2..80e6a9edaa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3702,6 +3702,10 @@ "resolved": "packages/cluster-settings", "link": true }, + "node_modules/@k8slens/cluster-sidebar": { + "resolved": "packages/cluster-sidebar", + "link": true + }, "node_modules/@k8slens/computed-channel": { "resolved": "packages/technical-features/messaging/computed-channel", "link": true @@ -33993,6 +33997,7 @@ "@k8slens/application-for-electron-main": "^6.5.0", "@k8slens/button": "^1.0.0", "@k8slens/cluster-settings": "^6.5.0", + "@k8slens/cluster-sidebar": "^1.0.0-alpha.0", "@k8slens/core": "^6.5.0", "@k8slens/ensure-binaries": "^6.5.0", "@k8slens/error-boundary": "^1.0.0", @@ -34200,6 +34205,26 @@ "@ogre-tools/injectable": "^17.1.0" } }, + "packages/cluster-sidebar": { + "name": "@k8slens/cluster-sidebar", + "version": "1.0.0-alpha.0", + "license": "MIT", + "devDependencies": { + "@k8slens/eslint-config": "^6.5.0-alpha.3", + "@k8slens/jest": "^6.5.0-alpha.5", + "@k8slens/typescript": "^6.5.0-alpha.2", + "@k8slens/webpack": "^6.5.0-alpha.5" + }, + "peerDependencies": { + "@k8slens/feature-core": "^6.5.0-alpha.4", + "@k8slens/utilities": "^1.0.0-alpha.3", + "@ogre-tools/injectable": "^15.8.1", + "@ogre-tools/injectable-extension-for-auto-registration": "^15.8.1", + "@ogre-tools/injectable-extension-for-mobx": "^15.8.1", + "mobx": "^6.9.0", + "react": "^17.0.2" + } + }, "packages/core": { "name": "@k8slens/core", "version": "6.5.0", @@ -34318,6 +34343,7 @@ "@k8slens/application-for-electron-main": "^6.5.0-alpha.0", "@k8slens/button": "^1.0.0-alpha.5", "@k8slens/cluster-settings": "^6.5.0-alpha.1", + "@k8slens/cluster-sidebar": "^1.0.0-alpha.0", "@k8slens/error-boundary": "^1.0.0-alpha.5", "@k8slens/event-emitter": "^1.0.0-alpha.1", "@k8slens/icon": "^1.0.0-alpha.7", diff --git a/packages/cluster-sidebar/.eslintrc.js b/packages/cluster-sidebar/.eslintrc.js new file mode 100644 index 0000000000..f404cf0ace --- /dev/null +++ b/packages/cluster-sidebar/.eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + extends: "@k8slens/eslint-config/eslint", + parserOptions: { + project: "./tsconfig.json", + }, + }; diff --git a/packages/cluster-sidebar/.prettierrc b/packages/cluster-sidebar/.prettierrc new file mode 100644 index 0000000000..edd47b479e --- /dev/null +++ b/packages/cluster-sidebar/.prettierrc @@ -0,0 +1 @@ +"@k8slens/eslint-config/prettier" diff --git a/packages/cluster-sidebar/index.ts b/packages/cluster-sidebar/index.ts new file mode 100644 index 0000000000..4d072e91b2 --- /dev/null +++ b/packages/cluster-sidebar/index.ts @@ -0,0 +1,8 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +export * from "./src/tokens"; +export * from "./src/feature"; +export { default as sidebarItemsInjectable } from "./src/sidebar-items.injectable"; diff --git a/packages/cluster-sidebar/jest.config.js b/packages/cluster-sidebar/jest.config.js new file mode 100644 index 0000000000..38d54ab7b6 --- /dev/null +++ b/packages/cluster-sidebar/jest.config.js @@ -0,0 +1 @@ +module.exports = require("@k8slens/jest").monorepoPackageConfig(__dirname).configForReact; diff --git a/packages/cluster-sidebar/package.json b/packages/cluster-sidebar/package.json new file mode 100644 index 0000000000..34a6a8a212 --- /dev/null +++ b/packages/cluster-sidebar/package.json @@ -0,0 +1,48 @@ +{ + "name": "@k8slens/cluster-sidebar", + "private": false, + "version": "1.0.0-alpha.0", + "description": "Injection tokens for adding new sidebar items within the Cluster View", + "type": "commonjs", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "files": [ + "dist" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/lensapp/lens.git" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "author": { + "name": "OpenLens Authors", + "email": "info@k8slens.dev" + }, + "license": "MIT", + "homepage": "https://github.com/lensapp/lens", + "scripts": { + "build": "lens-webpack-build", + "clean": "rimraf dist/", + "test": "jest --coverage --runInBand", + "lint": "lens-lint", + "lint:fix": "lens-lint --fix" + }, + "peerDependencies": { + "@k8slens/feature-core": "^6.5.0-alpha.4", + "@k8slens/utilities": "^1.0.0-alpha.3", + "@ogre-tools/injectable": "^15.8.1", + "@ogre-tools/injectable-extension-for-auto-registration": "^15.8.1", + "@ogre-tools/injectable-extension-for-mobx": "^15.8.1", + "mobx": "^6.9.0", + "react": "^17.0.2" + }, + "devDependencies": { + "@k8slens/eslint-config": "^6.5.0-alpha.3", + "@k8slens/jest": "^6.5.0-alpha.5", + "@k8slens/typescript": "^6.5.0-alpha.2", + "@k8slens/webpack": "^6.5.0-alpha.5" + } +} diff --git a/packages/cluster-sidebar/src/feature.ts b/packages/cluster-sidebar/src/feature.ts new file mode 100644 index 0000000000..d51e46c524 --- /dev/null +++ b/packages/cluster-sidebar/src/feature.ts @@ -0,0 +1,14 @@ +import { getFeature } from "@k8slens/feature-core"; +import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration"; + +export const clusterSidebarFeature = getFeature({ + id: "cluster-side-feature", + + register: (di) => { + autoRegister({ + di, + targetModule: module, + getRequireContexts: () => [require.context("./", true, /\.injectable\.(ts|tsx)$/)], + }); + }, +}); diff --git a/packages/cluster-sidebar/src/sidebar-items.injectable.ts b/packages/cluster-sidebar/src/sidebar-items.injectable.ts new file mode 100644 index 0000000000..6cef399fdd --- /dev/null +++ b/packages/cluster-sidebar/src/sidebar-items.injectable.ts @@ -0,0 +1,62 @@ +/* eslint-disable prettier/prettier */ + +import { getInjectable } from "@ogre-tools/injectable"; +import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; +import { HierarchicalSidebarItem, sidebarItemInjectionToken, SidebarItemRegistration } from "./tokens"; +import { computed } from "mobx"; +import { byOrderNumber } from "@k8slens/utilities"; + +const getSidebarItemsHierarchy = ( + registrations: SidebarItemRegistration[], + parentId: string | null, +): HierarchicalSidebarItem[] => ( + registrations + .filter((item) => item.parentId === parentId) + .map(({ + isActive, + isVisible, + ...registration + }) => { + const children = getSidebarItemsHierarchy(registrations, registration.id); + + return { + ...registration, + children, + isVisible: computed(() => { + if (children.length === 0) { + if (isVisible) { + return isVisible.get(); + } + + return true; + } + + return children.some((child) => child.isVisible.get()); + }), + isActive: computed(() => { + if (children.length === 0) { + if (isActive) { + return isActive.get(); + } + + return false; + } + + return children.some((child) => child.isActive.get()); + }), + }; + }) + .sort(byOrderNumber) +); + +const sidebarItemsInjectable = getInjectable({ + id: "sidebar-items", + instantiate: (di) => { + const computedInjectMany = di.inject(computedInjectManyInjectable); + const sidebarItemRegistrations = computedInjectMany(sidebarItemInjectionToken); + + return computed(() => getSidebarItemsHierarchy(sidebarItemRegistrations.get(), null)); + }, +}); + +export default sidebarItemsInjectable; diff --git a/packages/cluster-sidebar/src/tokens.ts b/packages/cluster-sidebar/src/tokens.ts new file mode 100644 index 0000000000..3edbd0468e --- /dev/null +++ b/packages/cluster-sidebar/src/tokens.ts @@ -0,0 +1,37 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectionToken } from "@ogre-tools/injectable"; +import type { IComputedValue } from "mobx"; +import type { StrictReactNode } from "@k8slens/utilities"; + +export interface SidebarItemRegistration { + id: string; + parentId: string | null; + title: StrictReactNode; + onClick: () => void; + getIcon?: () => StrictReactNode; + isActive?: IComputedValue; + isVisible?: IComputedValue; + orderNumber: number; +} + +export interface SidebarItem { + id: string; + parentId: string | null; + title: StrictReactNode; + onClick: () => void; + getIcon?: () => StrictReactNode; + isActive: IComputedValue; + isVisible: IComputedValue; +} + +export interface HierarchicalSidebarItem extends SidebarItem { + children: HierarchicalSidebarItem[]; +} + +export const sidebarItemInjectionToken = getInjectionToken({ + id: "sidebar-item-injection-token", +}); diff --git a/packages/cluster-sidebar/tsconfig.json b/packages/cluster-sidebar/tsconfig.json new file mode 100644 index 0000000000..2b0f0e5603 --- /dev/null +++ b/packages/cluster-sidebar/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@k8slens/typescript/config/base.json", + "include": ["**/*.ts"] + } diff --git a/packages/cluster-sidebar/webpack.config.js b/packages/cluster-sidebar/webpack.config.js new file mode 100644 index 0000000000..1cda407f5a --- /dev/null +++ b/packages/cluster-sidebar/webpack.config.js @@ -0,0 +1 @@ +module.exports = require("@k8slens/webpack").configForReact; diff --git a/packages/core/package.json b/packages/core/package.json index becbf86a71..49cf673309 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -211,6 +211,7 @@ "@k8slens/application-for-electron-main": "^6.5.0-alpha.0", "@k8slens/button": "^1.0.0-alpha.5", "@k8slens/cluster-settings": "^6.5.0-alpha.1", + "@k8slens/cluster-sidebar": "^1.0.0-alpha.0", "@k8slens/error-boundary": "^1.0.0-alpha.5", "@k8slens/event-emitter": "^1.0.0-alpha.1", "@k8slens/kube-api": "^1.0.0-alpha.1", diff --git a/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/crd-list/crd-list-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/crd-list/crd-list-route.injectable.ts deleted file mode 100644 index 854b3a8c3a..0000000000 --- a/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/crd-list/crd-list-route.injectable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { computed } from "mobx"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; - -const crdListRouteInjectable = getInjectable({ - id: "crd-list-route", - - instantiate: () => ({ - path: "/crd/definitions", - clusterFrame: true, - isEnabled: computed(() => true), - }), - - injectionToken: frontEndRouteInjectionToken, -}); - -export default crdListRouteInjectable; diff --git a/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/custom-resource-definitions.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/custom-resource-definitions.injectable.ts new file mode 100644 index 0000000000..74251a6499 --- /dev/null +++ b/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/custom-resource-definitions.injectable.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { shouldShowResourceInjectionToken } from "../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; +import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token"; + +const customResourceDefinitionsRouteInjectable = getInjectable({ + id: "custom-resource-definitions-route", + + instantiate: (di) => ({ + path: "/crd/definitions", + clusterFrame: true, + isEnabled: di.inject(shouldShowResourceInjectionToken, { + group: "apiextensions.k8s.io", + apiName: "customresourcedefinitions", + }), + }), + + injectionToken: frontEndRouteInjectionToken, +}); + +export default customResourceDefinitionsRouteInjectable; diff --git a/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/custom-resources/custom-resources-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/custom-resources-route.injectable.ts similarity index 80% rename from packages/core/src/common/front-end-routing/routes/cluster/custom-resources/custom-resources/custom-resources-route.injectable.ts rename to packages/core/src/common/front-end-routing/routes/cluster/custom-resources/custom-resources-route.injectable.ts index cc7d9b40be..5f0c35c76d 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/custom-resources/custom-resources-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/custom-resources-route.injectable.ts @@ -4,8 +4,8 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import type { Route } from "../../../../front-end-route-injection-token"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; +import type { Route } from "../../../front-end-route-injection-token"; +import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token"; export interface CustomResourcesPathParameters { group?: string; diff --git a/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/crd-list/navigate-to-crd-list.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/navigate-to-custom-resource-definitions.injectable.ts similarity index 75% rename from packages/core/src/common/front-end-routing/routes/cluster/custom-resources/crd-list/navigate-to-crd-list.injectable.ts rename to packages/core/src/common/front-end-routing/routes/cluster/custom-resources/navigate-to-custom-resource-definitions.injectable.ts index bd78dd1174..adf22eff34 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/crd-list/navigate-to-crd-list.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/navigate-to-custom-resource-definitions.injectable.ts @@ -3,8 +3,8 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import crdListRouteInjectable from "./crd-list-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; +import { navigateToRouteInjectionToken } from "../../../navigate-to-route-injection-token"; +import crdListRouteInjectable from "./custom-resource-definitions.injectable"; const navigateToCrdListInjectable = getInjectable({ id: "navigate-to-crd-list", diff --git a/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/custom-resources/navigate-to-custom-resources.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/navigate-to-custom-resources.injectable.ts similarity index 77% rename from packages/core/src/common/front-end-routing/routes/cluster/custom-resources/custom-resources/navigate-to-custom-resources.injectable.ts rename to packages/core/src/common/front-end-routing/routes/cluster/custom-resources/navigate-to-custom-resources.injectable.ts index 9df8cd6e2c..d8206324e2 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/custom-resources/navigate-to-custom-resources.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/custom-resources/navigate-to-custom-resources.injectable.ts @@ -3,9 +3,9 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; +import { navigateToRouteInjectionToken } from "../../../navigate-to-route-injection-token"; import type { CustomResourcesPathParameters } from "./custom-resources-route.injectable"; import customResourcesRouteInjectable from "./custom-resources-route.injectable"; -import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; const navigateToCustomResourcesInjectable = getInjectable({ id: "navigate-to-custom-resources", @@ -14,8 +14,7 @@ const navigateToCustomResourcesInjectable = getInjectable({ const navigateToRoute = di.inject(navigateToRouteInjectionToken); const route = di.inject(customResourcesRouteInjectable); - return (parameters?: CustomResourcesPathParameters) => - navigateToRoute(route, { parameters }); + return (parameters?: CustomResourcesPathParameters) => navigateToRoute(route, { parameters }); }, }); diff --git a/packages/core/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classes-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classes-route.injectable.ts new file mode 100644 index 0000000000..6ea8ffd736 --- /dev/null +++ b/packages/core/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classes-route.injectable.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; + +const ingressClassesRouteInjectable = getInjectable({ + id: "ingress-classes-route", + + instantiate: (di) => ({ + path: "/ingress-classes", + clusterFrame: true, + isEnabled: di.inject(shouldShowResourceInjectionToken, { + apiName: "ingressclasses", + group: "networking.k8s.io", + }), + }), + + injectionToken: frontEndRouteInjectionToken, +}); + +export default ingressClassesRouteInjectable; diff --git a/packages/core/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classeses-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classeses-route.injectable.ts deleted file mode 100644 index efde1d8d7a..0000000000 --- a/packages/core/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classeses-route.injectable.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; -import { - shouldShowResourceInjectionToken, -} from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; - -const ingressClassesesRouteInjectable = getInjectable({ - id: "ingress-classes-route", - - instantiate: (di) => { - const isEnabled = di.inject(shouldShowResourceInjectionToken, { - apiName: "ingressclasses", - group: "networking.k8s.io", - }); - - return { - path: "/ingress-classes", - clusterFrame: true, - isEnabled, - }; - }, - - injectionToken: frontEndRouteInjectionToken, -}); - -export default ingressClassesesRouteInjectable; diff --git a/packages/core/src/common/front-end-routing/routes/cluster/network/ingress-class/navigate-to-ingress-classes.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/network/ingress-class/navigate-to-ingress-classes.injectable.ts index 22c666d5ae..1cc241ef7e 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/network/ingress-class/navigate-to-ingress-classes.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/network/ingress-class/navigate-to-ingress-classes.injectable.ts @@ -4,14 +4,14 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; -import ingressClassesesRouteInjectable from "./ingress-classeses-route.injectable"; +import ingressClassesRouteInjectable from "./ingress-classes-route.injectable"; const navigateToIngressesInjectable = getInjectable({ id: "navigate-to-ingress-classes", instantiate: (di) => { const navigateToRoute = di.inject(navigateToRouteInjectionToken); - const route = di.inject(ingressClassesesRouteInjectable); + const route = di.inject(ingressClassesRouteInjectable); return () => navigateToRoute(route); }, diff --git a/packages/core/src/common/utils/composable-responsibilities/orderable/orderable.ts b/packages/core/src/common/utils/composable-responsibilities/orderable/orderable.ts deleted file mode 100644 index 2b4d95b3f4..0000000000 --- a/packages/core/src/common/utils/composable-responsibilities/orderable/orderable.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { sortBy } from "lodash/fp"; - -export interface Orderable { - readonly orderNumber: number; -} - -export type MaybeOrderable = Orderable | object; - -export const orderByOrderNumber = (maybeOrderables: T[]) => - sortBy( - (orderable) => - "orderNumber" in orderable - ? orderable.orderNumber - : Number.MAX_SAFE_INTEGER, - maybeOrderables, - ); - -export const byOrderNumber = (left: T, right: T) => ( - left.orderNumber - right.orderNumber -); diff --git a/packages/core/src/features/application-menu/main/application-menu-item-composite.injectable.ts b/packages/core/src/features/application-menu/main/application-menu-item-composite.injectable.ts index b9012fb67b..53a34338ba 100644 --- a/packages/core/src/features/application-menu/main/application-menu-item-composite.injectable.ts +++ b/packages/core/src/features/application-menu/main/application-menu-item-composite.injectable.ts @@ -4,18 +4,17 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import applicationMenuItemsInjectable from "./application-menu-items.injectable"; -import type { Composite } from "../../../common/utils/composite/get-composite/get-composite"; import { getCompositeFor } from "../../../common/utils/composite/get-composite/get-composite"; import { computed } from "mobx"; -import { pipeline } from "@ogre-tools/fp"; import type { ApplicationMenuItemTypes } from "./menu-items/application-menu-item-injection-token"; import type { RootComposite } from "../../../common/utils/composite/interfaces"; import type { Discriminable } from "../../../common/utils/composable-responsibilities/discriminable/discriminable"; -import { orderByOrderNumber } from "../../../common/utils/composable-responsibilities/orderable/orderable"; import logErrorInjectable from "../../../common/log-error.injectable"; import { isShown } from "../../../common/utils/composable-responsibilities/showable/showable"; +import type { Orderable } from "@k8slens/utilities"; +import { byOrderNumber } from "@k8slens/utilities"; -export type MenuItemRoot = Discriminable<"root"> & RootComposite<"root">; +export type MenuItemRoot = Discriminable<"root"> & RootComposite<"root"> & Orderable; const applicationMenuItemCompositeInjectable = getInjectable({ id: "application-menu-item-composite", @@ -24,40 +23,30 @@ const applicationMenuItemCompositeInjectable = getInjectable({ const menuItems = di.inject(applicationMenuItemsInjectable); const logError = di.inject(logErrorInjectable); - return computed((): Composite => { - const items = menuItems.get(); + const getComposite = getCompositeFor({ + getId: (x) => x.id, + getParentId: (x) => x.parentId, + transformChildren: (children) => ( + [...children] + .sort(byOrderNumber) + .filter(isShown) + ), + handleMissingParentIds: ({ missingParentIds }) => { + const ids = missingParentIds.join('", "'); - return pipeline( - [ - { - parentId: undefined, - id: "root", - kind: "root", - } as const, - - ...items, - ], - - getCompositeFor({ - getId: (x) => x.id, - getParentId: (x) => x.parentId, - transformChildren: (children) => - pipeline( - children, - orderByOrderNumber, - (children) => children.filter(isShown), - ), - - handleMissingParentIds: ({ missingParentIds }) => { - logError( - `[MENU]: cannot render menu item for missing parentIds: "${missingParentIds.join( - '", "', - )}"`, - ); - }, - }), - ); + logError(`[MENU]: cannot render menu item for missing parentIds: "${ids}"`); + }, }); + + return computed(() => getComposite([ + { + parentId: undefined, + id: "root", + kind: "root", + orderNumber: -Infinity, + } as const, + ...menuItems.get(), + ])); }, }); diff --git a/packages/core/src/features/application-menu/main/menu-items/application-menu-item-injection-token.ts b/packages/core/src/features/application-menu/main/menu-items/application-menu-item-injection-token.ts index b90e5e1612..1da1bc00b7 100644 --- a/packages/core/src/features/application-menu/main/menu-items/application-menu-item-injection-token.ts +++ b/packages/core/src/features/application-menu/main/menu-items/application-menu-item-injection-token.ts @@ -8,7 +8,7 @@ import type { SetOptional } from "type-fest"; import type { ChildOfParentComposite, ParentOfChildComposite } from "../../../../common/utils/composite/interfaces"; import type { MaybeShowable } from "../../../../common/utils/composable-responsibilities/showable/showable"; import type { Discriminable } from "../../../../common/utils/composable-responsibilities/discriminable/discriminable"; -import type { Orderable } from "../../../../common/utils/composable-responsibilities/orderable/orderable"; +import type { Orderable } from "@k8slens/utilities"; export interface MayHaveKeyboardShortcut { keyboardShortcut?: string; diff --git a/packages/core/src/features/cluster/__snapshots__/custom-resources-in-sidebar.test.tsx.snap b/packages/core/src/features/cluster/__snapshots__/custom-resources-in-sidebar.test.tsx.snap new file mode 100644 index 0000000000..7ec3ae37ee --- /dev/null +++ b/packages/core/src/features/cluster/__snapshots__/custom-resources-in-sidebar.test.tsx.snap @@ -0,0 +1,3847 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`cluster - custom resources in sidebar renders 1`] = ` +
+
+
+
+
+ +
+ + + close + + +
+ Close +
+
+
+
+
+
+
+
+