mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Fix StatusBar extension API (#4838)
Co-authored-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
parent
e72c9ff110
commit
8480b2a1e5
@ -2,7 +2,7 @@
|
|||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
export type { StatusBarRegistration } from "../../renderer/components/cluster-manager/status-bar-registration";
|
export type { StatusBarRegistration } from "../../renderer/components/status-bar/status-bar-registration";
|
||||||
export type { KubeObjectMenuRegistration, KubeObjectMenuComponents } from "../../renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-registration";
|
export type { KubeObjectMenuRegistration, KubeObjectMenuComponents } from "../../renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-registration";
|
||||||
export type { AppPreferenceRegistration, AppPreferenceComponents } from "../../renderer/components/+preferences/app-preferences/app-preference-registration";
|
export type { AppPreferenceRegistration, AppPreferenceComponents } from "../../renderer/components/+preferences/app-preferences/app-preference-registration";
|
||||||
export type { KubeObjectDetailRegistration, KubeObjectDetailComponents } from "../registries/kube-object-detail-registry";
|
export type { KubeObjectDetailRegistration, KubeObjectDetailComponents } from "../registries/kube-object-detail-registry";
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import type { CommandRegistration } from "../renderer/components/command-palette
|
|||||||
import type { AppPreferenceRegistration } from "../renderer/components/+preferences/app-preferences/app-preference-registration";
|
import type { AppPreferenceRegistration } from "../renderer/components/+preferences/app-preferences/app-preference-registration";
|
||||||
import type { AdditionalCategoryColumnRegistration } from "../renderer/components/+catalog/custom-category-columns";
|
import type { AdditionalCategoryColumnRegistration } from "../renderer/components/+catalog/custom-category-columns";
|
||||||
import type { CustomCategoryViewRegistration } from "../renderer/components/+catalog/custom-views";
|
import type { CustomCategoryViewRegistration } from "../renderer/components/+catalog/custom-views";
|
||||||
import type { StatusBarRegistration } from "../renderer/components/cluster-manager/status-bar-registration";
|
import type { StatusBarRegistration } from "../renderer/components/status-bar/status-bar-registration";
|
||||||
import type { KubeObjectMenuRegistration } from "../renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-registration";
|
import type { KubeObjectMenuRegistration } from "../renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-registration";
|
||||||
|
|
||||||
export class LensRendererExtension extends LensExtension {
|
export class LensRendererExtension extends LensExtension {
|
||||||
|
|||||||
@ -1,28 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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);
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
.BottomBar {
|
|
||||||
@apply flex px-2 text-white;
|
|
||||||
|
|
||||||
grid-area: bottom-bar;
|
|
||||||
background-color: var(--blue);
|
|
||||||
height: var(--bottom-bar-height);
|
|
||||||
font-size: var(--font-size-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
.item {
|
|
||||||
@apply flex items-center mr-2 h-full px-2 last:mr-0;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
@apply cursor-pointer;
|
|
||||||
|
|
||||||
background-color: #ffffff33;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.onLeft + .onRight {
|
|
||||||
@apply ml-auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.onRight {}
|
|
||||||
@ -1,74 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import styles from "./bottom-bar.module.scss";
|
|
||||||
|
|
||||||
import React from "react";
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
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
|
|
||||||
class NonInjectedBottomBar extends React.Component<Dependencies> {
|
|
||||||
renderRegisteredItem(registration: StatusBarRegistration) {
|
|
||||||
const { item } = registration;
|
|
||||||
|
|
||||||
if (item) {
|
|
||||||
return typeof item === "function" ? item() : item;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <registration.components.Item />;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderRegisteredItems() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{this.props.items.get().map((registration, index) => {
|
|
||||||
if (!registration?.item && !registration?.components?.Item) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={cssNames(styles.item, {
|
|
||||||
[styles.onLeft]: registration.components?.position == "left",
|
|
||||||
[styles.onRight]: registration.components?.position != "left",
|
|
||||||
})}
|
|
||||||
key={index}
|
|
||||||
>
|
|
||||||
{this.renderRegisteredItem(registration)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className={styles.BottomBar}>
|
|
||||||
{this.renderRegisteredItems()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const BottomBar = withInjectables<Dependencies>(
|
|
||||||
NonInjectedBottomBar,
|
|
||||||
|
|
||||||
{
|
|
||||||
getProps: (di, props) => ({
|
|
||||||
items: di.inject(bottomBarItemsInjectable),
|
|
||||||
...props,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
@ -4,14 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.ClusterManager {
|
.ClusterManager {
|
||||||
--bottom-bar-height: 21px;
|
|
||||||
--hotbar-width: 75px;
|
--hotbar-width: 75px;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"topbar topbar"
|
"topbar topbar"
|
||||||
"menu main"
|
"menu main"
|
||||||
"bottom-bar bottom-bar";
|
"status-bar status-bar";
|
||||||
grid-template-rows: auto 1fr min-content;
|
grid-template-rows: auto 1fr min-content;
|
||||||
grid-template-columns: min-content 1fr;
|
grid-template-columns: min-content 1fr;
|
||||||
|
|
||||||
@ -26,10 +25,6 @@
|
|||||||
grid-area: menu;
|
grid-area: menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
.BottomBar {
|
|
||||||
grid-area: bottom-bar;
|
|
||||||
}
|
|
||||||
|
|
||||||
#lens-views {
|
#lens-views {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import "./cluster-manager.scss";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Redirect, Route, Switch } from "react-router";
|
import { Redirect, Route, Switch } from "react-router";
|
||||||
import { disposeOnUnmount, observer } from "mobx-react";
|
import { disposeOnUnmount, observer } from "mobx-react";
|
||||||
import { BottomBar } from "./bottom-bar";
|
import { StatusBar } from "../status-bar/status-bar";
|
||||||
import { Catalog } from "../+catalog";
|
import { Catalog } from "../+catalog";
|
||||||
import { Preferences } from "../+preferences";
|
import { Preferences } from "../+preferences";
|
||||||
import { AddCluster } from "../+add-cluster";
|
import { AddCluster } from "../+add-cluster";
|
||||||
@ -71,7 +71,7 @@ class NonInjectedClusterManager extends React.Component<Dependencies> {
|
|||||||
</Switch>
|
</Switch>
|
||||||
</main>
|
</main>
|
||||||
<HotbarMenu />
|
<HotbarMenu />
|
||||||
<BottomBar />
|
<StatusBar />
|
||||||
<DeleteClusterDialog />
|
<DeleteClusterDialog />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
@ -25,7 +25,7 @@
|
|||||||
.contents {
|
.contents {
|
||||||
grid-area: contents;
|
grid-area: contents;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
height: calc(100vh - var(--bottom-bar-height) - var(--main-layout-header));
|
height: calc(100vh - var(--status-bar-height) - var(--main-layout-header));
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
|
|||||||
@ -0,0 +1,73 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import React from "react";
|
||||||
|
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||||
|
import { computed, IComputedValue } from "mobx";
|
||||||
|
import type { StatusBarItemProps, StatusBarRegistration } from "./status-bar-registration";
|
||||||
|
import statusBarItemsInjectable from "./status-bar-items.injectable";
|
||||||
|
|
||||||
|
export interface RegisteredStatusBarItems {
|
||||||
|
right: React.ComponentType<StatusBarItemProps>[];
|
||||||
|
left: React.ComponentType<StatusBarItemProps>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
registrations: IComputedValue<StatusBarRegistration[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRegisteredStatusBarItems({ registrations }: Dependencies): IComputedValue<RegisteredStatusBarItems> {
|
||||||
|
return computed(() => {
|
||||||
|
const res: RegisteredStatusBarItems = {
|
||||||
|
left: [],
|
||||||
|
right: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const registration of registrations.get()) {
|
||||||
|
if (!registration || typeof registration !== "object") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (registration.item) {
|
||||||
|
const { item } = registration;
|
||||||
|
|
||||||
|
// default for old API is "right"
|
||||||
|
res.right.push(
|
||||||
|
() => (
|
||||||
|
<>
|
||||||
|
{
|
||||||
|
typeof item === "function"
|
||||||
|
? item()
|
||||||
|
: item
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (registration.components) {
|
||||||
|
const { position = "right", Item } = registration.components;
|
||||||
|
|
||||||
|
if (position !== "left" && position !== "right") {
|
||||||
|
throw new TypeError("StatusBarRegistration.components.position must be either 'right' or 'left'");
|
||||||
|
}
|
||||||
|
|
||||||
|
res[position].push(Item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is done so that the first ones registered are closest to the corner
|
||||||
|
res.right.reverse();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const registeredStatusBarItemsInjectable = getInjectable({
|
||||||
|
instantiate: (di) => getRegisteredStatusBarItems({
|
||||||
|
registrations: di.inject(statusBarItemsInjectable),
|
||||||
|
}),
|
||||||
|
|
||||||
|
lifecycle: lifecycleEnum.singleton,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default registeredStatusBarItemsInjectable;
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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";
|
||||||
|
|
||||||
|
const statusBarItemsInjectable = getInjectable({
|
||||||
|
instantiate: (di) => {
|
||||||
|
const extensions = di.inject(rendererExtensionsInjectable);
|
||||||
|
|
||||||
|
return computed(() => (
|
||||||
|
extensions.get()
|
||||||
|
.flatMap(ext => ext.statusBarItems)
|
||||||
|
));
|
||||||
|
},
|
||||||
|
lifecycle: lifecycleEnum.singleton,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default statusBarItemsInjectable;
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The props for StatusBar item component
|
||||||
|
*/
|
||||||
|
export interface StatusBarItemProps {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type defining the registration of a status bar item
|
||||||
|
*/
|
||||||
|
export interface StatusBarComponents {
|
||||||
|
/**
|
||||||
|
* The component for this registrations
|
||||||
|
*/
|
||||||
|
Item: React.ComponentType<StatusBarItemProps>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The side of the bottom bar to place this component.
|
||||||
|
*
|
||||||
|
* @default "right"
|
||||||
|
*/
|
||||||
|
position?: "left" | "right";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type for registering status bar items from the LensRendererExtension
|
||||||
|
*/
|
||||||
|
export interface StatusBarRegistration {
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link StatusBarRegistration.components} instead
|
||||||
|
*/
|
||||||
|
item?: React.ReactNode | (() => React.ReactNode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The newer API, allows for registering a component instead of a ReactNode
|
||||||
|
*/
|
||||||
|
components?: StatusBarComponents;
|
||||||
|
}
|
||||||
40
src/renderer/components/status-bar/status-bar.module.scss
Normal file
40
src/renderer/components/status-bar/status-bar.module.scss
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.StatusBar {
|
||||||
|
--status-bar-height: 21px;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
grid-area: status-bar;
|
||||||
|
background-color: var(--blue);
|
||||||
|
height: var(--status-bar-height);
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
|
||||||
|
display: inline-grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leftSide {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rightSide {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #ffffff33;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,21 +5,18 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import "@testing-library/jest-dom/extend-expect";
|
import "@testing-library/jest-dom/extend-expect";
|
||||||
import { BottomBar } from "./bottom-bar";
|
import { StatusBar } from "./status-bar";
|
||||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||||
import type { DiRender } from "../test-utils/renderFor";
|
import type { DiRender } from "../test-utils/renderFor";
|
||||||
import { renderFor } 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 { computed, IObservableArray, observable } from "mobx";
|
||||||
|
import type { ConfigurableDependencyInjectionContainer } from "@ogre-tools/injectable";
|
||||||
|
import statusBarItemsInjectable from "./status-bar-items.injectable";
|
||||||
|
import type { StatusBarRegistration } from "./status-bar-registration";
|
||||||
import { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
import { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
||||||
import { computed, IObservableArray, observable, runInAction } from "mobx";
|
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||||
import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable";
|
import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable";
|
||||||
|
|
||||||
jest.mock("electron", () => ({
|
|
||||||
app: {
|
|
||||||
getPath: () => "/foo",
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
class SomeTestExtension extends LensRendererExtension {
|
class SomeTestExtension extends LensRendererExtension {
|
||||||
constructor(statusBarItems: IObservableArray<any>) {
|
constructor(statusBarItems: IObservableArray<any>) {
|
||||||
super({
|
super({
|
||||||
@ -36,36 +33,28 @@ class SomeTestExtension extends LensRendererExtension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("<BottomBar />", () => {
|
describe("<StatusBar />", () => {
|
||||||
let render: DiRender;
|
let render: DiRender;
|
||||||
|
let di: ConfigurableDependencyInjectionContainer;
|
||||||
let statusBarItems: IObservableArray<any>;
|
let statusBarItems: IObservableArray<any>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
|
||||||
statusBarItems = observable.array([]);
|
statusBarItems = observable.array([]);
|
||||||
|
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||||
const someTestExtension = new SomeTestExtension(statusBarItems);
|
render = renderFor(di);
|
||||||
|
|
||||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
|
||||||
|
|
||||||
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
|
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
|
||||||
|
di.override(rendererExtensionsInjectable, () => computed(() => [new SomeTestExtension(statusBarItems)]));
|
||||||
di.override(rendererExtensionsInjectable, () => {
|
|
||||||
return computed(() => [someTestExtension]);
|
|
||||||
});
|
|
||||||
|
|
||||||
render = renderFor(di);
|
|
||||||
|
|
||||||
await di.runSetups();
|
await di.runSetups();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders w/o errors", () => {
|
it("renders w/o errors", () => {
|
||||||
const { container } = render(<BottomBar />);
|
const { container } = render(<StatusBar />);
|
||||||
|
|
||||||
expect(container).toBeInstanceOf(HTMLElement);
|
expect(container).toBeInstanceOf(HTMLElement);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
undefined,
|
undefined,
|
||||||
"hello",
|
"hello",
|
||||||
@ -74,25 +63,21 @@ describe("<BottomBar />", () => {
|
|||||||
[],
|
[],
|
||||||
[{}],
|
[{}],
|
||||||
{},
|
{},
|
||||||
])("renders w/o errors when .getItems() returns not type compliant (%p)", val => {
|
])("renders w/o errors when registrations are not type compliant (%p)", val => {
|
||||||
runInAction(() => {
|
statusBarItems.replace([val]);
|
||||||
statusBarItems.replace([val]);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(() => render(<BottomBar />)).not.toThrow();
|
expect(() => render(<StatusBar />)).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders items [{item: React.ReactNode}] (4.0.0-rc.1)", () => {
|
it("renders items [{item: React.ReactNode}] (4.0.0-rc.1)", () => {
|
||||||
const testId = "testId";
|
const testId = "testId";
|
||||||
const text = "heee";
|
const text = "heee";
|
||||||
|
|
||||||
runInAction(() => {
|
di.override(statusBarItemsInjectable, () => computed(() => [
|
||||||
statusBarItems.replace([
|
{ item: <span data-testid={testId} >{text}</span> },
|
||||||
{ item: <span data-testid={testId} >{text}</span> },
|
] as StatusBarRegistration[]));
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
const { getByTestId } = render(<BottomBar />);
|
const { getByTestId } = render(<StatusBar />);
|
||||||
|
|
||||||
expect(getByTestId(testId)).toHaveTextContent(text);
|
expect(getByTestId(testId)).toHaveTextContent(text);
|
||||||
});
|
});
|
||||||
@ -101,51 +86,47 @@ describe("<BottomBar />", () => {
|
|||||||
const testId = "testId";
|
const testId = "testId";
|
||||||
const text = "heee";
|
const text = "heee";
|
||||||
|
|
||||||
runInAction(() => {
|
statusBarItems.replace([{
|
||||||
statusBarItems.replace([
|
item: () => <span data-testid={testId} >{text}</span>,
|
||||||
{ item: () => <span data-testid={testId} >{text}</span> },
|
}]);
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
const { getByTestId } = render(<BottomBar />);
|
const { getByTestId } = render(<StatusBar />);
|
||||||
|
|
||||||
expect(getByTestId(testId)).toHaveTextContent(text);
|
expect(getByTestId(testId)).toHaveTextContent(text);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it("sort positioned items properly", () => {
|
it("sort positioned items properly", () => {
|
||||||
runInAction(() => {
|
statusBarItems.replace([
|
||||||
statusBarItems.replace([
|
{
|
||||||
{
|
components: {
|
||||||
components: {
|
Item: () => <div data-testid="sortedElem">right1</div>,
|
||||||
Item: () => <div data-testid="sortedElem">right</div>,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
components: {
|
{
|
||||||
Item: () => <div data-testid="sortedElem">right</div>,
|
components: {
|
||||||
position: "right",
|
Item: () => <div data-testid="sortedElem">right2</div>,
|
||||||
},
|
position: "right",
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
components: {
|
{
|
||||||
Item: () => <div data-testid="sortedElem">left</div>,
|
components: {
|
||||||
position: "left",
|
Item: () => <div data-testid="sortedElem">left1</div>,
|
||||||
},
|
position: "left",
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
components: {
|
{
|
||||||
Item: () => <div data-testid="sortedElem">left</div>,
|
components: {
|
||||||
position: "left",
|
Item: () => <div data-testid="sortedElem">left2</div>,
|
||||||
},
|
position: "left",
|
||||||
},
|
},
|
||||||
]);
|
},
|
||||||
});
|
]);
|
||||||
|
|
||||||
const { getAllByTestId } = render(<BottomBar />);
|
const { getAllByTestId } = render(<StatusBar />);
|
||||||
const elems = getAllByTestId("sortedElem");
|
const elems = getAllByTestId("sortedElem");
|
||||||
const positions = elems.map(elem => elem.textContent);
|
const positions = elems.map(elem => elem.textContent);
|
||||||
|
|
||||||
expect(positions).toEqual(["left", "left", "right", "right"]);
|
expect(positions).toEqual(["left1", "left2", "right2", "right1"]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
49
src/renderer/components/status-bar/status-bar.tsx
Normal file
49
src/renderer/components/status-bar/status-bar.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import styles from "./status-bar.module.scss";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import registeredStatusBarItemsInjectable, { RegisteredStatusBarItems } from "./registered-status-bar-items.injectable";
|
||||||
|
import type { IComputedValue } from "mobx";
|
||||||
|
|
||||||
|
export interface StatusBarProps {}
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
items: IComputedValue<RegisteredStatusBarItems>
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedStatusBar = observer(({ items }: Dependencies & StatusBarProps) => {
|
||||||
|
const { left, right } = items.get();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.StatusBar}>
|
||||||
|
<div className={styles.leftSide}>
|
||||||
|
{left.map((Item, index) => (
|
||||||
|
<div className={styles.item} key={index}>
|
||||||
|
<Item />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className={styles.rightSide}>
|
||||||
|
{right.map((Item, index) => (
|
||||||
|
<div className={styles.item} key={index}>
|
||||||
|
<Item />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
export const StatusBar = withInjectables<Dependencies, StatusBarProps>(NonInjectedStatusBar, {
|
||||||
|
getProps: (di, props) => ({
|
||||||
|
items: di.inject(registeredStatusBarItemsInjectable),
|
||||||
|
...props,
|
||||||
|
}),
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user