From 1ffdb6c89fc03566b483aa2b5ac2962f2098b6c9 Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Fri, 26 May 2023 13:32:25 +0300 Subject: [PATCH] Table component injection tokens (#7754) Signed-off-by: Alex Andreev --- open-lens/src/renderer/index.ts | 6 ++-- package-lock.json | 18 ++++++++++++ packages/core/package.json | 1 + .../table/table-component.injectable.ts | 15 ++++++++++ .../core/src/features/table/table-feature.ts | 14 ++++++++++ .../pod-distruption-budgets.test.tsx | 7 +++++ .../components/item-object-list/content.tsx | 15 +++++++--- .../item-object-list/list-layout.tsx | 6 +++- .../kube-object-list-layout.test.tsx | 7 +++++ .../kube-object-list-layout.tsx | 3 +- .../test-utils/get-application-builder.tsx | 2 ++ packages/core/src/renderer/library.ts | 1 + packages/logger/src/logger.injectable.ts | 5 +++- packages/logger/src/logger.test.ts | 13 +++++---- packages/logger/src/transports.ts | 8 ++++-- packages/table/README.md | 3 ++ packages/table/index.ts | 28 +++++++++++++++++++ packages/table/package.json | 28 +++++++++++++++++++ packages/table/tsconfig.json | 4 +++ packages/table/webpack.config.js | 1 + 20 files changed, 167 insertions(+), 18 deletions(-) create mode 100644 packages/core/src/features/table/table-component.injectable.ts create mode 100644 packages/core/src/features/table/table-feature.ts create mode 100644 packages/table/README.md create mode 100644 packages/table/index.ts create mode 100644 packages/table/package.json create mode 100644 packages/table/tsconfig.json create mode 100644 packages/table/webpack.config.js diff --git a/open-lens/src/renderer/index.ts b/open-lens/src/renderer/index.ts index 5981151da9..49662da117 100644 --- a/open-lens/src/renderer/index.ts +++ b/open-lens/src/renderer/index.ts @@ -9,7 +9,8 @@ import { rendererExtensionApi as Renderer, commonExtensionApi as Common, registerLensCore, - metricsFeature + metricsFeature, + tableFeature, } from "@k8slens/core/renderer"; import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration"; import { registerFeature } from "@k8slens/feature-core"; @@ -49,7 +50,8 @@ runInAction(() => { keyboardShortcutsFeature, reactApplicationFeature, routingFeature, - metricsFeature + metricsFeature, + tableFeature, ); autoRegister({ diff --git a/package-lock.json b/package-lock.json index 3cccd40daa..cf1f675640 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3833,6 +3833,10 @@ "resolved": "packages/utility-features/startable-stoppable", "link": true }, + "node_modules/@k8slens/table-tokens": { + "resolved": "packages/table", + "link": true + }, "node_modules/@k8slens/test-utils": { "resolved": "packages/utility-features/test-utils", "link": true @@ -34324,6 +34328,7 @@ "@k8slens/routing": "^1.0.0-alpha.5", "@k8slens/run-many": "^1.0.0-alpha.1", "@k8slens/startable-stoppable": "^1.0.0-alpha.1", + "@k8slens/table-tokens": "^6.5.0-alpha.7", "@k8slens/tooltip": "^1.0.0-alpha.5", "@k8slens/utilities": "^1.0.0-alpha.1", "@kubernetes/client-node": "^0.18.1", @@ -35304,6 +35309,19 @@ "rimraf": "^4.4.1" } }, + "packages/table": { + "name": "@k8slens/table-tokens", + "version": "6.5.0-alpha.7", + "license": "MIT", + "devDependencies": { + "@k8slens/webpack": "^6.5.0-alpha.8", + "rimraf": "^4.4.1" + }, + "peerDependencies": { + "@ogre-tools/injectable": "^16.1.0", + "react": "^17.0.2" + } + }, "packages/technical-features/application/agnostic": { "name": "@k8slens/application", "version": "6.5.0-alpha.9", diff --git a/packages/core/package.json b/packages/core/package.json index 7c8052fb12..51ca511003 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -230,6 +230,7 @@ "@k8slens/routing": "^1.0.0-alpha.5", "@k8slens/run-many": "^1.0.0-alpha.1", "@k8slens/startable-stoppable": "^1.0.0-alpha.1", + "@k8slens/table-tokens": "^6.5.0-alpha.7", "@k8slens/tooltip": "^1.0.0-alpha.5", "@k8slens/utilities": "^1.0.0-alpha.1", "@kubernetes/client-node": "^0.18.1", diff --git a/packages/core/src/features/table/table-component.injectable.ts b/packages/core/src/features/table/table-component.injectable.ts new file mode 100644 index 0000000000..976b941f38 --- /dev/null +++ b/packages/core/src/features/table/table-component.injectable.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { tableComponentInjectionToken } from "@k8slens/table-tokens"; +import { getInjectable } from "@ogre-tools/injectable"; +import { Table } from "../../renderer/components/table/table"; + +const tableComponentInjectable = getInjectable({ + id: "table-component", + instantiate: () => ({ Component: Table }), + injectionToken: tableComponentInjectionToken, +}); + +export default tableComponentInjectable; diff --git a/packages/core/src/features/table/table-feature.ts b/packages/core/src/features/table/table-feature.ts new file mode 100644 index 0000000000..3ecb298443 --- /dev/null +++ b/packages/core/src/features/table/table-feature.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getFeature } from "@k8slens/feature-core"; +import tableComponentInjectable from "./table-component.injectable"; + +export const tableFeature = getFeature({ + id: "core-table-feature", + + register: (di) => { + di.register(tableComponentInjectable); + }, +}); diff --git a/packages/core/src/renderer/components/config-pod-disruption-budgets/__tests__/pod-distruption-budgets.test.tsx b/packages/core/src/renderer/components/config-pod-disruption-budgets/__tests__/pod-distruption-budgets.test.tsx index c423962e68..33ab10f4aa 100644 --- a/packages/core/src/renderer/components/config-pod-disruption-budgets/__tests__/pod-distruption-budgets.test.tsx +++ b/packages/core/src/renderer/components/config-pod-disruption-budgets/__tests__/pod-distruption-budgets.test.tsx @@ -18,6 +18,9 @@ import { Cluster } from "../../../../common/cluster/cluster"; import hostedClusterInjectable from "../../../cluster-frame-context/hosted-cluster.injectable"; import userPreferencesStateInjectable from "../../../../features/user-preferences/common/state.injectable"; import type { DiContainer } from "@ogre-tools/injectable"; +import { registerFeature } from "@k8slens/feature-core"; +import { runInAction } from "mobx"; +import { tableFeature } from "../../../library"; describe("", () => { let di: DiContainer; @@ -69,6 +72,10 @@ describe("", () => { }), } as any, })); + + runInAction(() => { + registerFeature(di, tableFeature); + }); }); describe("PDB with minAvailable 0", () => { diff --git a/packages/core/src/renderer/components/item-object-list/content.tsx b/packages/core/src/renderer/components/item-object-list/content.tsx index de19c1c058..6e5c37dbb2 100644 --- a/packages/core/src/renderer/components/item-object-list/content.tsx +++ b/packages/core/src/renderer/components/item-object-list/content.tsx @@ -11,14 +11,14 @@ import { computed, makeObservable } from "mobx"; import { Observer, observer } from "mobx-react"; import type { ConfirmDialogParams } from "../confirm-dialog"; import type { TableProps, TableRowProps, TableSortCallbacks } from "../table"; -import { Table, TableCell, TableHead, TableRow } from "../table"; +import { TableCell, TableHead, TableRow } from "../table"; import type { IClassName, StrictReactNode } from "@k8slens/utilities"; import { cssNames, isDefined, isReactNode, noop, prevDefault, stopPropagation } from "@k8slens/utilities"; import type { AddRemoveButtonsProps } from "../add-remove-buttons"; import { AddRemoveButtons } from "../add-remove-buttons"; import { NoItems } from "../no-items"; import { Spinner } from "../spinner"; -import type { ItemObject, TableCellProps } from "@k8slens/list-layout"; +import type { GeneralKubeObjectListLayoutColumn, ItemObject, TableCellProps } from "@k8slens/list-layout"; import type { Filter, PageFiltersStore } from "./page-filters/store"; import type { LensTheme } from "../../themes/lens-theme"; import { MenuActions } from "../menu/menu-actions"; @@ -35,6 +35,8 @@ import type { ToggleTableColumnVisibility } from "../../../features/user-prefere import toggleTableColumnVisibilityInjectable from "../../../features/user-preferences/common/toggle-table-column-visibility.injectable"; import type { IsTableColumnHidden } from "../../../features/user-preferences/common/is-table-column-hidden.injectable"; import isTableColumnHiddenInjectable from "../../../features/user-preferences/common/is-table-column-hidden.injectable"; +import type { TableComponent } from "@k8slens/table-tokens"; +import { tableComponentInjectionToken } from "@k8slens/table-tokens"; export interface ItemListLayoutContentProps { getFilters: () => Filter[]; @@ -54,6 +56,7 @@ export interface ItemListLayoutContentProps Partial>; addRemoveButtons?: Partial; virtual?: boolean; + columns?: GeneralKubeObjectListLayoutColumn[]; // item details view hasDetailsView?: boolean; @@ -79,6 +82,7 @@ interface Dependencies { openConfirmDialog: OpenConfirmDialog; toggleTableColumnVisibility: ToggleTableColumnVisibility; isTableColumnHidden: IsTableColumnHidden; + table: TableComponent; } @observer @@ -299,6 +303,7 @@ class NonInjectedItemListLayoutContent< const { store, hasDetailsView, addRemoveButtons = {}, virtual, sortingCallbacks, detailsItem, className, tableProps = {}, tableId, getItems, activeTheme, + table, } = this.props; const selectedItemId = detailsItem && detailsItem.getId(); const classNames = cssNames(className, "box", "grow", activeTheme.get().type); @@ -307,8 +312,9 @@ class NonInjectedItemListLayoutContent< return (
- {this.renderTableHeader()} {this.renderItems()} -
+ {() => ( @@ -385,5 +391,6 @@ export const ItemListLayoutContent = withInjectables(props: ItemListLayoutContentProps) => React.ReactElement; diff --git a/packages/core/src/renderer/components/item-object-list/list-layout.tsx b/packages/core/src/renderer/components/item-object-list/list-layout.tsx index 32f46e7a2a..fd1d34eac0 100644 --- a/packages/core/src/renderer/components/item-object-list/list-layout.tsx +++ b/packages/core/src/renderer/components/item-object-list/list-layout.tsx @@ -13,7 +13,9 @@ import type { TableProps, TableRowProps, TableSortCallbacks } from "../table"; import type { IClassName, StrictReactNode, SingleOrMany } from "@k8slens/utilities"; import { cssNames, noop } from "@k8slens/utilities"; import type { AddRemoveButtonsProps } from "../add-remove-buttons"; -import type { ItemObject, TableCellProps } from "@k8slens/list-layout"; +import type { ItemObject, TableCellProps, + GeneralKubeObjectListLayoutColumn, +} from "@k8slens/list-layout"; import type { SearchInputUrlProps } from "../input"; import type { PageFiltersStore } from "./page-filters/store"; import { FilterType } from "./page-filters/store"; @@ -98,6 +100,7 @@ export type ItemListLayoutProps; customizeHeader?: HeaderCustomizer | HeaderCustomizer[]; + columns?: GeneralKubeObjectListLayoutColumn[]; // items list configuration isReady?: boolean; // show loading indicator while not ready @@ -304,6 +307,7 @@ class NonInjectedItemListLayout + columns={this.props.columns} getItems={() => this.items} getFilters={() => this.filters} tableId={this.props.tableId} diff --git a/packages/core/src/renderer/components/kube-object-list-layout/kube-object-list-layout.test.tsx b/packages/core/src/renderer/components/kube-object-list-layout/kube-object-list-layout.test.tsx index ee52707837..9239310005 100644 --- a/packages/core/src/renderer/components/kube-object-list-layout/kube-object-list-layout.test.tsx +++ b/packages/core/src/renderer/components/kube-object-list-layout/kube-object-list-layout.test.tsx @@ -23,6 +23,9 @@ import type { PodStore } from "../workloads-pods/store"; import { Cluster } from "../../../common/cluster/cluster"; import isTableColumnHiddenInjectable from "../../../features/user-preferences/common/is-table-column-hidden.injectable"; import { podListLayoutColumnInjectionToken } from "@k8slens/list-layout"; +import { registerFeature } from "@k8slens/feature-core"; +import { runInAction } from "mobx"; +import { tableFeature } from "../../library"; describe("kube-object-list-layout", () => { let di: DiContainer; @@ -54,6 +57,10 @@ describe("kube-object-list-layout", () => { get: () => ({}), })); + runInAction(() => { + registerFeature(di, tableFeature); + }); + podStore = di.inject(podStoreInjectable); }); diff --git a/packages/core/src/renderer/components/kube-object-list-layout/kube-object-list-layout.tsx b/packages/core/src/renderer/components/kube-object-list-layout/kube-object-list-layout.tsx index fe8b87dc68..87fd84504e 100644 --- a/packages/core/src/renderer/components/kube-object-list-layout/kube-object-list-layout.tsx +++ b/packages/core/src/renderer/components/kube-object-list-layout/kube-object-list-layout.tsx @@ -42,7 +42,7 @@ export interface KubeObjectListLayoutProps< // eslint-disable-next-line unused-imports/no-unused-vars-ts, @typescript-eslint/no-unused-vars A extends KubeApi, D extends KubeJsonApiDataFor, -> extends Omit, "getItems" | "dependentStores" | "preloadStores"> { +> extends Omit, "getItems" | "dependentStores" | "preloadStores" | "columns"> { items?: K[]; getItems?: () => K[]; store: KubeItemListStore; @@ -193,6 +193,7 @@ class NonInjectedKubeObjectListLayout< getItems={() => this.props.items || store.contextItems} preloadStores={false} // loading handled in kubeWatchApi.subscribeStores() detailsItem={this.selectedItem} + columns={targetColumns as GeneralKubeObjectListLayoutColumn[]} customizeHeader={[ ({ filters, searchProps, info, ...headerPlaceHolders }) => ({ filters: ( diff --git a/packages/core/src/renderer/components/test-utils/get-application-builder.tsx b/packages/core/src/renderer/components/test-utils/get-application-builder.tsx index c2822c4fb5..ba69f8015e 100644 --- a/packages/core/src/renderer/components/test-utils/get-application-builder.tsx +++ b/packages/core/src/renderer/components/test-utils/get-application-builder.tsx @@ -72,6 +72,7 @@ import { sendMessageToChannelInjectionToken } from "@k8slens/messaging"; import { getMessageBridgeFake } from "@k8slens/messaging-fake-bridge"; import { historyInjectionToken } from "@k8slens/routing"; import writeJsonSyncInjectable from "../../../common/fs/write-json-sync.injectable"; +import { tableFeature } from "../../library"; type MainDiCallback = (container: { mainDi: DiContainer }) => void | Promise; type WindowDiCallback = (container: { windowDi: DiContainer }) => void | Promise; @@ -256,6 +257,7 @@ export const getApplicationBuilder = () => { registerFeature( windowDi, applicationFeature, + tableFeature, ); windowDi.register(rendererExtensionsStateInjectable); diff --git a/packages/core/src/renderer/library.ts b/packages/core/src/renderer/library.ts index 8ed023c559..2d239e7393 100644 --- a/packages/core/src/renderer/library.ts +++ b/packages/core/src/renderer/library.ts @@ -22,3 +22,4 @@ export * as ReactRouterDom from "react-router-dom"; export * as rendererExtensionApi from "../extensions/renderer-api"; export * as commonExtensionApi from "../extensions/common-api"; export { metricsFeature } from "../features/metrics/metrics-feature"; +export { tableFeature } from "../features/table/table-feature"; diff --git a/packages/logger/src/logger.injectable.ts b/packages/logger/src/logger.injectable.ts index 5a2b95e2a4..2148de334e 100644 --- a/packages/logger/src/logger.injectable.ts +++ b/packages/logger/src/logger.injectable.ts @@ -63,7 +63,10 @@ export const logSillyInjectionToken = getInjectionToken({ const screamingKebabCase = (str: string) => pipeline(str, kebabCase, toUpper); -const getLogFunctionFor = (scenario: keyof Logger, namespace: string | undefined) => { +const getLogFunctionFor = ( + scenario: keyof Logger, + namespace: string | undefined +) => { const prefix = namespace ? `[${screamingKebabCase(namespace.replace(/-feature$/, ""))}]: ` : ""; diff --git a/packages/logger/src/logger.test.ts b/packages/logger/src/logger.test.ts index 1904cc0b05..d7282bb242 100644 --- a/packages/logger/src/logger.test.ts +++ b/packages/logger/src/logger.test.ts @@ -90,7 +90,6 @@ describe("logger", () => { instantiate: (di) => di.inject(injectionToken), }); - const someFeature = getFeature({ id: "some-feature", @@ -124,11 +123,13 @@ describe("logger", () => { registerFeature(di, loggerFeature); - di.register(getInjectable({ - id: "some-transport", - instantiate: () => new TransportStream({ log }), - injectionToken: loggerTransportInjectionToken, - })) + di.register( + getInjectable({ + id: "some-transport", + instantiate: () => new TransportStream({ log }), + injectionToken: loggerTransportInjectionToken, + }) + ); const logger = di.inject(loggerInjectable); diff --git a/packages/logger/src/transports.ts b/packages/logger/src/transports.ts index 1407eb91b8..510dcbd7da 100644 --- a/packages/logger/src/transports.ts +++ b/packages/logger/src/transports.ts @@ -6,6 +6,8 @@ import { getInjectionToken } from "@ogre-tools/injectable"; import type TransportStream from "winston-transport"; -export const loggerTransportInjectionToken = getInjectionToken({ - id: "logger-transport", -}); +export const loggerTransportInjectionToken = getInjectionToken( + { + id: "logger-transport", + } +); diff --git a/packages/table/README.md b/packages/table/README.md new file mode 100644 index 0000000000..e66efd11b8 --- /dev/null +++ b/packages/table/README.md @@ -0,0 +1,3 @@ +# Description + +The package exports tokens needed for external table configuration. diff --git a/packages/table/index.ts b/packages/table/index.ts new file mode 100644 index 0000000000..b6f785dfb2 --- /dev/null +++ b/packages/table/index.ts @@ -0,0 +1,28 @@ +import { getInjectionToken } from "@ogre-tools/injectable"; +import type { KubeObject } from "@k8slens/kube-object/src/kube-object"; +import type { + BaseKubeObjectListLayoutColumn, + GeneralKubeObjectListLayoutColumn, + SpecificKubeListLayoutColumn, +} from "@k8slens/list-layout"; + +type Column = ( + | BaseKubeObjectListLayoutColumn + | SpecificKubeListLayoutColumn + | GeneralKubeObjectListLayoutColumn +); + +export interface TableComponentProps { + tableId?: string; + columns?: Column[]; + save?: (state: object) => void; + load?: (tableId: string) => object; +} + +export interface TableComponent { + Component: React.ComponentType; +} + +export const tableComponentInjectionToken = getInjectionToken({ + id: "table-component-injection-token", +}); diff --git a/packages/table/package.json b/packages/table/package.json new file mode 100644 index 0000000000..5e8d5db658 --- /dev/null +++ b/packages/table/package.json @@ -0,0 +1,28 @@ +{ + "name": "@k8slens/table-tokens", + "version": "6.5.0-alpha.7", + "description": "Injection token exporter for table components", + "license": "MIT", + "type": "commonjs", + "private": false, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "clean": "rimraf dist/", + "build": "lens-webpack-build" + }, + "devDependencies": { + "@k8slens/webpack": "^6.5.0-alpha.8", + "rimraf": "^4.4.1" + }, + "peerDependencies": { + "@ogre-tools/injectable": "^16.1.0" + } +} diff --git a/packages/table/tsconfig.json b/packages/table/tsconfig.json new file mode 100644 index 0000000000..1819203dc1 --- /dev/null +++ b/packages/table/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@k8slens/typescript/config/base.json", + "include": ["**/*.ts"] +} diff --git a/packages/table/webpack.config.js b/packages/table/webpack.config.js new file mode 100644 index 0000000000..3183f30179 --- /dev/null +++ b/packages/table/webpack.config.js @@ -0,0 +1 @@ +module.exports = require("@k8slens/webpack").configForNode;