Replace StatusBarRegistry with reactive solution (#4728)

This commit is contained in:
Janne Savolainen authored and GitHub committed 2022-01-28 08:40:58 -05:00
1 parent 8d8491a035
commit 09b33633f7
9 files changed
+135 -64

No files matched your search

@@ -0,0 +1,28 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
import { computed } from "mobx";
import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable";
import type { StatusBarRegistration } from "./status-bar-registration";
const bottomBarItemsInjectable = getInjectable({
instantiate: (di) => {
const extensions = di.inject(rendererExtensionsInjectable);
return computed(() =>
extensions
.get()
.flatMap((extension) => extension.statusBarItems)
.sort(leftItemsBeforeRight),
);
},
lifecycle: lifecycleEnum.singleton,
});
export default bottomBarItemsInjectable;
const leftItemsBeforeRight = (firstItem: StatusBarRegistration, secondItem: StatusBarRegistration) =>
firstItem.components?.position?.localeCompare(secondItem.components?.position);
@@ -4,10 +4,15 @@
*/
import React from "react";
import { render } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";
import { BottomBar } from "./bottom-bar";
import { StatusBarRegistry } from "../../../extensions/registries";
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
import type { DiRender } from "../test-utils/renderFor";
import { renderFor } from "../test-utils/renderFor";
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
import { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
import { computed, IObservableArray, observable, runInAction } from "mobx";
import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable";
jest.mock("electron", () => ({
app: {
@@ -15,13 +20,43 @@ jest.mock("electron", () => ({
},
}));
describe("<BottomBar />", () => {
beforeEach(() => {
StatusBarRegistry.createInstance();
});
class SomeTestExtension extends LensRendererExtension {
constructor(statusBarItems: IObservableArray<any>) {
super({
id: "some-id",
absolutePath: "irrelevant",
isBundled: false,
isCompatible: false,
isEnabled: false,
manifest: { name: "some-id", version: "some-version" },
manifestPath: "irrelevant",
});
afterEach(() => {
StatusBarRegistry.resetInstance();
this.statusBarItems = statusBarItems;
}
}
describe("<BottomBar />", () => {
let render: DiRender;
let statusBarItems: IObservableArray<any>;
beforeEach(async () => {
statusBarItems = observable.array([]);
const someTestExtension = new SomeTestExtension(statusBarItems);
const di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
di.override(rendererExtensionsInjectable, () => {
return computed(() => [someTestExtension]);
});
render = renderFor(di);
await di.runSetups();
});
it("renders w/o errors", () => {
@@ -30,6 +65,7 @@ describe("<BottomBar />", () => {
expect(container).toBeInstanceOf(HTMLElement);
});
it.each([
undefined,
"hello",
@@ -39,7 +75,10 @@ describe("<BottomBar />", () => {
[{}],
{},
])("renders w/o errors when .getItems() returns not type compliant (%p)", val => {
StatusBarRegistry.getInstance().getItems = jest.fn().mockImplementationOnce(() => val);
runInAction(() => {
statusBarItems.replace([val]);
});
expect(() => render(<BottomBar />)).not.toThrow();
});
@@ -47,9 +86,12 @@ describe("<BottomBar />", () => {
const testId = "testId";
const text = "heee";
StatusBarRegistry.getInstance().getItems = jest.fn().mockImplementationOnce(() => [
{ item: <span data-testid={testId} >{text}</span> },
]);
runInAction(() => {
statusBarItems.replace([
{ item: <span data-testid={testId} >{text}</span> },
]);
});
const { getByTestId } = render(<BottomBar />);
expect(getByTestId(testId)).toHaveTextContent(text);
@@ -59,9 +101,12 @@ describe("<BottomBar />", () => {
const testId = "testId";
const text = "heee";
StatusBarRegistry.getInstance().getItems = jest.fn().mockImplementationOnce(() => [
{ item: () => <span data-testid={testId} >{text}</span> },
]);
runInAction(() => {
statusBarItems.replace([
{ item: () => <span data-testid={testId} >{text}</span> },
]);
});
const { getByTestId } = render(<BottomBar />);
expect(getByTestId(testId)).toHaveTextContent(text);
@@ -69,31 +114,33 @@ describe("<BottomBar />", () => {
it("sort positioned items properly", () => {
StatusBarRegistry.getInstance().getItems = jest.fn().mockImplementationOnce(() => [
{
components: {
Item: () => <div data-testid="sortedElem">right</div>,
runInAction(() => {
statusBarItems.replace([
{
components: {
Item: () => <div data-testid="sortedElem">right</div>,
},
},
},
{
components: {
Item: () => <div data-testid="sortedElem">right</div>,
position: "right",
{
components: {
Item: () => <div data-testid="sortedElem">right</div>,
position: "right",
},
},
},
{
components: {
Item: () => <div data-testid="sortedElem">left</div>,
position: "left",
{
components: {
Item: () => <div data-testid="sortedElem">left</div>,
position: "left",
},
},
},
{
components: {
Item: () => <div data-testid="sortedElem">left</div>,
position: "left",
{
components: {
Item: () => <div data-testid="sortedElem">left</div>,
position: "left",
},
},
},
]);
]);
});
const { getAllByTestId } = render(<BottomBar />);
const elems = getAllByTestId("sortedElem");
@@ -7,11 +7,18 @@ import styles from "./bottom-bar.module.scss";
import React from "react";
import { observer } from "mobx-react";
import { StatusBarRegistration, StatusBarRegistry } from "../../../extensions/registries";
import { cssNames } from "../../utils";
import { withInjectables } from "@ogre-tools/injectable-react";
import bottomBarItemsInjectable from "./bottom-bar-items.injectable";
import type { IComputedValue } from "mobx";
import type { StatusBarRegistration } from "./status-bar-registration";
interface Dependencies {
items: IComputedValue<StatusBarRegistration[]>
}
@observer
export class BottomBar extends React.Component {
class NonInjectedBottomBar extends React.Component<Dependencies> {
renderRegisteredItem(registration: StatusBarRegistration) {
const { item } = registration;
@@ -23,19 +30,9 @@ export class BottomBar extends React.Component {
}
renderRegisteredItems() {
const items = StatusBarRegistry.getInstance().getItems();
if (!Array.isArray(items)) {
return null;
}
items.sort(function sortLeftPositionFirst(a, b) {
return a.components?.position?.localeCompare(b.components?.position);
});
return (
<>
{items.map((registration, index) => {
{this.props.items.get().map((registration, index) => {
if (!registration?.item && !registration?.components?.Item) {
return null;
}
@@ -64,3 +61,14 @@ export class BottomBar extends React.Component {
);
}
}
export const BottomBar = withInjectables<Dependencies>(
NonInjectedBottomBar,
{
getProps: (di, props) => ({
items: di.inject(bottomBarItemsInjectable),
...props,
}),
},
);
@@ -0,0 +1,24 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
interface StatusBarComponents {
Item?: React.ComponentType;
/**
* The side of the bottom bar to place this component.
*
* @default "right"
*/
position?: "left" | "right";
}
interface StatusBarRegistrationV2 {
components?: StatusBarComponents; // has to be optional for backwards compatability
}
export interface StatusBarRegistration extends StatusBarRegistrationV2 {
/**
* @deprecated use components.Item instead
*/
item?: React.ReactNode;
}
-1
View File
@@ -14,6 +14,5 @@ export function initRegistries() {
registries.KubeObjectDetailRegistry.createInstance();
registries.KubeObjectMenuRegistry.createInstance();
registries.KubeObjectStatusRegistry.createInstance();
registries.StatusBarRegistry.createInstance();
registries.WorkloadsOverviewDetailRegistry.createInstance();
}