From 1683f482f11c0177b3990cbc513948656032f733 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 11 Apr 2022 15:00:45 -0400 Subject: [PATCH] fix statefulsets/dialog/dialog.test.tsx Signed-off-by: Sebastian Malton --- .../k8s-api/endpoints/legacy-globals.ts | 6 ++ .../endpoints/stateful-set.api.injectable.ts | 19 ++++++ .../k8s-api/endpoints/stateful-set.api.ts | 5 -- src/extensions/main-api/k8s-api.ts | 4 +- src/extensions/renderer-api/k8s-api.ts | 4 +- .../dialog.scss} | 0 .../dialog.test.tsx} | 35 +++++++---- .../dialog.tsx} | 58 ++++++++++--------- .../dialog/open.injectable.ts | 21 +++++++ .../dialog/state.injectable.ts | 15 +++++ .../stateful-set-menu.tsx | 41 ++++++++----- .../components/layout/main-layout.tsx | 1 + src/renderer/components/wizard/wizard.tsx | 3 +- .../frames/cluster-frame/cluster-frame.tsx | 2 +- 14 files changed, 151 insertions(+), 63 deletions(-) create mode 100644 src/common/k8s-api/endpoints/stateful-set.api.injectable.ts rename src/renderer/components/+workloads-statefulsets/{statefulset-scale-dialog.scss => dialog/dialog.scss} (100%) rename src/renderer/components/+workloads-statefulsets/{statefulset-scale-dialog.test.tsx => dialog/dialog.test.tsx} (77%) rename src/renderer/components/+workloads-statefulsets/{statefulset-scale-dialog.tsx => dialog/dialog.tsx} (75%) create mode 100644 src/renderer/components/+workloads-statefulsets/dialog/open.injectable.ts create mode 100644 src/renderer/components/+workloads-statefulsets/dialog/state.injectable.ts diff --git a/src/common/k8s-api/endpoints/legacy-globals.ts b/src/common/k8s-api/endpoints/legacy-globals.ts index 3938e1331f..4ede9839ba 100644 --- a/src/common/k8s-api/endpoints/legacy-globals.ts +++ b/src/common/k8s-api/endpoints/legacy-globals.ts @@ -11,6 +11,7 @@ import podApiInjectable from "./pod.api.injectable"; import replicaSetApiInjectable from "./replica-set.api.injectable"; import roleApiInjectable from "./role.api.injectable"; import serviceAccountApiInjectable from "./service-account.api.injectable"; +import statefulSetApiInjectable from "./stateful-set.api.injectable"; /** * @deprecated use `di.inject(clusterRoleBindingApiInjectable)` instead @@ -46,3 +47,8 @@ export const daemonSetApi = asLegacyGlobalForExtensionApi(daemonSetApiInjectable * @deprecated use `di.inject(replicaSetApiInjectable)` instead */ export const replicaSetApi = asLegacyGlobalForExtensionApi(replicaSetApiInjectable); + +/** + * @deprecated use `di.inject(statefulSetApiInjectable)` instead + */ +export const statefulSetApi = asLegacyGlobalForExtensionApi(statefulSetApiInjectable); diff --git a/src/common/k8s-api/endpoints/stateful-set.api.injectable.ts b/src/common/k8s-api/endpoints/stateful-set.api.injectable.ts new file mode 100644 index 0000000000..27ff0130e6 --- /dev/null +++ b/src/common/k8s-api/endpoints/stateful-set.api.injectable.ts @@ -0,0 +1,19 @@ +/** + * 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 assert from "assert"; +import { createStoresAndApisInjectionToken } from "../create-stores-apis.token"; +import { StatefulSetApi } from "./stateful-set.api"; + +const statefulSetApiInjectable = getInjectable({ + id: "stateful-set-api", + instantiate: (di) => { + assert(di.inject(createStoresAndApisInjectionToken), "statefulSetApi is only available in certain environments"); + + return new StatefulSetApi(); + }, +}); + +export default statefulSetApiInjectable; diff --git a/src/common/k8s-api/endpoints/stateful-set.api.ts b/src/common/k8s-api/endpoints/stateful-set.api.ts index 3d2885f4fc..2e263d03bf 100644 --- a/src/common/k8s-api/endpoints/stateful-set.api.ts +++ b/src/common/k8s-api/endpoints/stateful-set.api.ts @@ -7,7 +7,6 @@ import type { DerivedKubeApiOptions, IgnoredKubeApiOptions } from "../kube-api"; import { KubeApi } from "../kube-api"; import { metricsApi } from "./metrics.api"; import type { PodMetricData } from "./pod.api"; -import { isClusterPageContext } from "../../utils/cluster-id-url-parsing"; import type { KubeObjectScope, LabelSelector } from "../kube-object"; import { KubeObject } from "../kube-object"; import type { PodTemplateSpec } from "./types/pod-template-spec"; @@ -121,7 +120,3 @@ export class StatefulSet extends KubeObject container.image); } } - -export const statefulSetApi = isClusterPageContext() - ? new StatefulSetApi() - : undefined as never; diff --git a/src/extensions/main-api/k8s-api.ts b/src/extensions/main-api/k8s-api.ts index 7f95f77bf0..74026ff68f 100644 --- a/src/extensions/main-api/k8s-api.ts +++ b/src/extensions/main-api/k8s-api.ts @@ -10,6 +10,7 @@ import podApiInjectable from "../../common/k8s-api/endpoints/pod.api.injectable" import replicaSetApiInjectable from "../../common/k8s-api/endpoints/replica-set.api.injectable"; import roleApiInjectable from "../../common/k8s-api/endpoints/role.api.injectable"; import serviceAccountApiInjectable from "../../common/k8s-api/endpoints/service-account.api.injectable"; +import statefulSetApiInjectable from "../../common/k8s-api/endpoints/stateful-set.api.injectable"; import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; /** @@ -27,6 +28,7 @@ export const roleApi = asLegacyGlobalForExtensionApi(roleApiInjectable); export const podsApi = asLegacyGlobalForExtensionApi(podApiInjectable); export const daemonSetApi = asLegacyGlobalForExtensionApi(daemonSetApiInjectable); export const replicaSetApi = asLegacyGlobalForExtensionApi(replicaSetApiInjectable); +export const statefulSetApi = asLegacyGlobalForExtensionApi(statefulSetApiInjectable); export { ResourceStack } from "../../common/k8s/resource-stack"; export { apiManager } from "../../common/k8s-api/api-manager"; @@ -37,7 +39,7 @@ export { Pod, PodApi as PodsApi } from "../../common/k8s-api/endpoints/pod.api"; export { Node, nodeApi as nodesApi, NodeApi as NodesApi } from "../../common/k8s-api/endpoints/nodes.api"; export { Deployment, deploymentApi, DeploymentApi } from "../../common/k8s-api/endpoints/deployment.api"; export { DaemonSet } from "../../common/k8s-api/endpoints/daemon-set.api"; -export { StatefulSet, statefulSetApi } from "../../common/k8s-api/endpoints/stateful-set.api"; +export { StatefulSet } from "../../common/k8s-api/endpoints/stateful-set.api"; export { Job, jobApi } from "../../common/k8s-api/endpoints/job.api"; export { CronJob, cronJobApi } from "../../common/k8s-api/endpoints/cron-job.api"; export { ConfigMap, configMapApi } from "../../common/k8s-api/endpoints/configmap.api"; diff --git a/src/extensions/renderer-api/k8s-api.ts b/src/extensions/renderer-api/k8s-api.ts index eff7087b49..54837c3c89 100644 --- a/src/extensions/renderer-api/k8s-api.ts +++ b/src/extensions/renderer-api/k8s-api.ts @@ -15,6 +15,7 @@ import roleApiInjectable from "../../common/k8s-api/endpoints/role.api.injectabl import podApiInjectable from "../../common/k8s-api/endpoints/pod.api.injectable"; import daemonSetApiInjectable from "../../common/k8s-api/endpoints/daemon-set.api.injectable"; import replicaSetApiInjectable from "../../common/k8s-api/endpoints/replica-set.api.injectable"; +import statefulSetApiInjectable from "../../common/k8s-api/endpoints/stateful-set.api.injectable"; export function isAllowedResource(resource: KubeResource | KubeResource[]) { const resources = castArray(resource); @@ -36,6 +37,7 @@ export const roleApi = asLegacyGlobalForExtensionApi(roleApiInjectable); export const podsApi = asLegacyGlobalForExtensionApi(podApiInjectable); export const daemonSetApi = asLegacyGlobalForExtensionApi(daemonSetApiInjectable); export const replicaSetApi = asLegacyGlobalForExtensionApi(replicaSetApiInjectable); +export const statefulSetApi = asLegacyGlobalForExtensionApi(statefulSetApiInjectable); export { BaseKubeJsonApiObjectMetadata, @@ -49,7 +51,7 @@ export { Pod, PodApi as PodsApi } from "../../common/k8s-api/endpoints/pod.api"; export { Node, nodeApi as nodesApi, NodeApi as NodesApi } from "../../common/k8s-api/endpoints/nodes.api"; export { Deployment, deploymentApi, DeploymentApi } from "../../common/k8s-api/endpoints/deployment.api"; export { DaemonSet } from "../../common/k8s-api/endpoints/daemon-set.api"; -export { StatefulSet, statefulSetApi } from "../../common/k8s-api/endpoints/stateful-set.api"; +export { StatefulSet } from "../../common/k8s-api/endpoints/stateful-set.api"; export { Job, jobApi } from "../../common/k8s-api/endpoints/job.api"; export { CronJob, cronJobApi } from "../../common/k8s-api/endpoints/cron-job.api"; export { ConfigMap, configMapApi } from "../../common/k8s-api/endpoints/configmap.api"; diff --git a/src/renderer/components/+workloads-statefulsets/statefulset-scale-dialog.scss b/src/renderer/components/+workloads-statefulsets/dialog/dialog.scss similarity index 100% rename from src/renderer/components/+workloads-statefulsets/statefulset-scale-dialog.scss rename to src/renderer/components/+workloads-statefulsets/dialog/dialog.scss diff --git a/src/renderer/components/+workloads-statefulsets/statefulset-scale-dialog.test.tsx b/src/renderer/components/+workloads-statefulsets/dialog/dialog.test.tsx similarity index 77% rename from src/renderer/components/+workloads-statefulsets/statefulset-scale-dialog.test.tsx rename to src/renderer/components/+workloads-statefulsets/dialog/dialog.test.tsx index 3e3a30b299..c850f8d158 100755 --- a/src/renderer/components/+workloads-statefulsets/statefulset-scale-dialog.test.tsx +++ b/src/renderer/components/+workloads-statefulsets/dialog/dialog.test.tsx @@ -5,10 +5,17 @@ import "@testing-library/jest-dom/extend-expect"; -import { StatefulSet, StatefulSetApi } from "../../../common/k8s-api/endpoints"; -import { StatefulSetScaleDialog } from "./statefulset-scale-dialog"; -import { render, waitFor, fireEvent } from "@testing-library/react"; +import type { StatefulSetApi } from "../../../../common/k8s-api/endpoints"; +import { StatefulSet } from "../../../../common/k8s-api/endpoints"; +import { StatefulSetScaleDialog } from "./dialog"; +import { waitFor, fireEvent } from "@testing-library/react"; import React from "react"; +import type { OpenStatefulSetDialog } from "./open.injectable"; +import { getDiForUnitTesting } from "../../../getDiForUnitTesting"; +import statefulSetApiInjectable from "../../../../common/k8s-api/endpoints/stateful-set.api.injectable"; +import createStoresAndApisInjectable from "../../../create-stores-apis.injectable"; +import openStatefulSetDialogInjectable from "./open.injectable"; +import { type DiRender, renderFor } from "../../test-utils/renderFor"; const dummyStatefulSet = new StatefulSet({ apiVersion: "v1", @@ -81,15 +88,21 @@ const dummyStatefulSet = new StatefulSet({ describe("", () => { let statefulSetApi: StatefulSetApi; + let openStatefulSetDialog: OpenStatefulSetDialog; + let render: DiRender; beforeEach(() => { - statefulSetApi = new StatefulSetApi({ - objectConstructor: StatefulSet, - }); + const di = getDiForUnitTesting({ doGeneralOverrides: true }); + + di.override(createStoresAndApisInjectable, () => true); + + render = renderFor(di); + statefulSetApi = di.inject(statefulSetApiInjectable); + openStatefulSetDialog = di.inject(openStatefulSetDialogInjectable); }); it("renders w/o errors", () => { - const { container } = render(); + const { container } = render(); expect(container).toBeInstanceOf(HTMLElement); }); @@ -100,9 +113,9 @@ describe("", () => { const initReplicas = 1; statefulSetApi.getReplicas = jest.fn().mockImplementationOnce(async () => initReplicas); - const { getByTestId } = render(); + const { getByTestId } = render(); - StatefulSetScaleDialog.open(dummyStatefulSet); + openStatefulSetDialog(dummyStatefulSet); // we need to wait for the StatefulSetScaleDialog to show up // because there is an in which renders null at start. await waitFor(async () => { @@ -120,9 +133,9 @@ describe("", () => { const initReplicas = 1; statefulSetApi.getReplicas = jest.fn().mockImplementationOnce(async () => initReplicas); - const component = render(); + const component = render(); - StatefulSetScaleDialog.open(dummyStatefulSet); + openStatefulSetDialog(dummyStatefulSet); await waitFor(async () => { expect(await component.findByTestId("desired-scale")).toHaveTextContent(`${initReplicas}`); expect(await component.findByTestId("current-scale")).toHaveTextContent(`${initReplicas}`); diff --git a/src/renderer/components/+workloads-statefulsets/statefulset-scale-dialog.tsx b/src/renderer/components/+workloads-statefulsets/dialog/dialog.tsx similarity index 75% rename from src/renderer/components/+workloads-statefulsets/statefulset-scale-dialog.tsx rename to src/renderer/components/+workloads-statefulsets/dialog/dialog.tsx index 20b15132c3..e8470b88e7 100644 --- a/src/renderer/components/+workloads-statefulsets/statefulset-scale-dialog.tsx +++ b/src/renderer/components/+workloads-statefulsets/dialog/dialog.tsx @@ -3,51 +3,45 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import "./statefulset-scale-dialog.scss"; +import "./dialog.scss"; -import type { StatefulSet, StatefulSetApi } from "../../../common/k8s-api/endpoints"; -import { statefulSetApi } from "../../../common/k8s-api/endpoints"; +import type { StatefulSet, StatefulSetApi } from "../../../../common/k8s-api/endpoints"; import React, { Component } from "react"; +import type { IObservableValue } from "mobx"; import { computed, makeObservable, observable } from "mobx"; import { observer } from "mobx-react"; -import type { DialogProps } from "../dialog"; -import { Dialog } from "../dialog"; -import { Wizard, WizardStep } from "../wizard"; -import { Icon } from "../icon"; -import { Slider } from "../slider"; -import { Notifications } from "../notifications"; -import { cssNames } from "../../utils"; +import type { DialogProps } from "../../dialog"; +import { Dialog } from "../../dialog"; +import { Wizard, WizardStep } from "../../wizard"; +import { Icon } from "../../icon"; +import { Slider } from "../../slider"; +import { Notifications } from "../../notifications"; +import { cssNames } from "../../../utils"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import statefulSetApiInjectable from "../../../../common/k8s-api/endpoints/stateful-set.api.injectable"; +import statefulSetDialogStateInjectable from "./state.injectable"; export interface StatefulSetScaleDialogProps extends Partial { - statefulSetApi: StatefulSetApi; } -const dialogState = observable.box(); +interface Dependencies { + statefulSetApi: StatefulSetApi; + state: IObservableValue; +} @observer -export class StatefulSetScaleDialog extends Component { - static defaultProps = { - statefulSetApi, - }; +class NonInjectedStatefulSetScaleDialog extends Component { @observable ready = false; @observable currentReplicas = 0; @observable desiredReplicas = 0; - constructor(props: StatefulSetScaleDialogProps) { + constructor(props: StatefulSetScaleDialogProps & Dependencies) { super(props); makeObservable(this); } - static open(statefulSet: StatefulSet) { - dialogState.set(statefulSet); - } - - static close() { - dialogState.set(undefined); - } - close = () => { - StatefulSetScaleDialog.close(); + this.props.state.set(undefined); }; onOpen = async (statefulSet: StatefulSet) => { @@ -161,8 +155,8 @@ export class StatefulSetScaleDialog extends Component(NonInjectedStatefulSetScaleDialog, { + getProps: (di, props) => ({ + ...props, + statefulSetApi: di.inject(statefulSetApiInjectable), + state: di.inject(statefulSetDialogStateInjectable), + }), +}); diff --git a/src/renderer/components/+workloads-statefulsets/dialog/open.injectable.ts b/src/renderer/components/+workloads-statefulsets/dialog/open.injectable.ts new file mode 100644 index 0000000000..aac97e8277 --- /dev/null +++ b/src/renderer/components/+workloads-statefulsets/dialog/open.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { StatefulSet } from "../../../../common/k8s-api/endpoints"; +import { getInjectable } from "@ogre-tools/injectable"; +import statefulSetDialogStateInjectable from "./state.injectable"; + +export type OpenStatefulSetDialog = (obj: StatefulSet) => void; + +const openStatefulSetDialogInjectable = getInjectable({ + id: "open-stateful-set-dialog", + instantiate: (di): OpenStatefulSetDialog => { + const state = di.inject(statefulSetDialogStateInjectable); + + return (obj) => state.set(obj); + }, +}); + +export default openStatefulSetDialogInjectable; diff --git a/src/renderer/components/+workloads-statefulsets/dialog/state.injectable.ts b/src/renderer/components/+workloads-statefulsets/dialog/state.injectable.ts new file mode 100644 index 0000000000..8d8634a6bf --- /dev/null +++ b/src/renderer/components/+workloads-statefulsets/dialog/state.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 { getInjectable } from "@ogre-tools/injectable"; +import { observable } from "mobx"; +import type { StatefulSet } from "../../../../common/k8s-api/endpoints"; + +const statefulSetDialogStateInjectable = getInjectable({ + id: "stateful-set-dialog-state", + instantiate: () => observable.box(), +}); + +export default statefulSetDialogStateInjectable; diff --git a/src/renderer/components/+workloads-statefulsets/stateful-set-menu.tsx b/src/renderer/components/+workloads-statefulsets/stateful-set-menu.tsx index 41a8b4f168..b4119f86ca 100644 --- a/src/renderer/components/+workloads-statefulsets/stateful-set-menu.tsx +++ b/src/renderer/components/+workloads-statefulsets/stateful-set-menu.tsx @@ -6,22 +6,33 @@ import React from "react"; import type { KubeObjectMenuProps } from "../kube-object-menu"; import type { StatefulSet } from "../../../common/k8s-api/endpoints"; import { MenuItem } from "../menu"; -import { StatefulSetScaleDialog } from "./statefulset-scale-dialog"; import { Icon } from "../icon"; +import type { OpenStatefulSetDialog } from "./dialog/open.injectable"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import openStatefulSetDialogInjectable from "./dialog/open.injectable"; -export function StatefulSetMenu(props: KubeObjectMenuProps) { - const { object, toolbar } = props; +export interface StatefulSetMenuProps extends KubeObjectMenuProps {} - return ( - <> - StatefulSetScaleDialog.open(object)}> - - Scale - - - ); +interface Dependencies { + openStatefulSetDialog: OpenStatefulSetDialog; } + +const NonInjectedStatefulSetMenu = ({ object, toolbar, openStatefulSetDialog }: Dependencies & StatefulSetMenuProps) => ( + <> + openStatefulSetDialog(object)}> + + Scale + + +); + +export const StatefulSetMenu = withInjectables(NonInjectedStatefulSetMenu, { + getProps: (di, props) => ({ + ...props, + openStatefulSetDialog: di.inject(openStatefulSetDialogInjectable), + }), +}); diff --git a/src/renderer/components/layout/main-layout.tsx b/src/renderer/components/layout/main-layout.tsx index 4ce8aabdb5..d123ffab56 100755 --- a/src/renderer/components/layout/main-layout.tsx +++ b/src/renderer/components/layout/main-layout.tsx @@ -19,6 +19,7 @@ export interface MainLayoutProps { sidebar: React.ReactNode; className?: string; footer?: React.ReactNode; + children?: React.ReactNode | React.ReactNode[]; } /** diff --git a/src/renderer/components/wizard/wizard.tsx b/src/renderer/components/wizard/wizard.tsx index 8c26e7a08c..4d7b471754 100755 --- a/src/renderer/components/wizard/wizard.tsx +++ b/src/renderer/components/wizard/wizard.tsx @@ -18,7 +18,6 @@ export interface WizardCommonProps { reset?: () => void; done?: () => void; hideSteps?: boolean; - children?: React.ReactElement>[] | React.ReactElement>; } export interface WizardProps extends WizardCommonProps { @@ -27,6 +26,7 @@ export interface WizardProps extends WizardCommonProps { title?: string; header?: React.ReactNode; onChange?: (step: number) => void; + children?: React.ReactElement>[] | React.ReactElement>; } interface State { @@ -128,6 +128,7 @@ export interface WizardStepProps extends WizardCommonProps { noValidate?: boolean; // no validate form attribute skip?: boolean; // don't render the step scrollable?: boolean; + children?: React.ReactNode | React.ReactNode[]; } interface WizardStepState { diff --git a/src/renderer/frames/cluster-frame/cluster-frame.tsx b/src/renderer/frames/cluster-frame/cluster-frame.tsx index 300a34d5dc..d9f33d29ef 100755 --- a/src/renderer/frames/cluster-frame/cluster-frame.tsx +++ b/src/renderer/frames/cluster-frame/cluster-frame.tsx @@ -9,7 +9,7 @@ import { Redirect } from "react-router"; import { ConfirmDialog } from "../../components/confirm-dialog"; import { DeploymentScaleDialog } from "../../components/+workloads-deployments/deployment-scale-dialog"; import { CronJobTriggerDialog } from "../../components/+workloads-cronjobs/cronjob-trigger-dialog"; -import { StatefulSetScaleDialog } from "../../components/+workloads-statefulsets/statefulset-scale-dialog"; +import { StatefulSetScaleDialog } from "../../components/+workloads-statefulsets/dialog/dialog"; import { ReplicaSetScaleDialog } from "../../components/+workloads-replicasets/replicaset-scale-dialog"; import { CommandContainer } from "../../components/command-palette/command-container"; import { ErrorBoundary } from "../../components/error-boundary";