1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Fix: remove hotbar name from status bar (#4679)

* Show tooltip with hotbar name manually

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Remove this.refreshPosition() call

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Remove hotbar name from status bar

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Convert HotbarSelector to use css modules

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Remove unused export

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Fixing linter

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Clean up

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Invert invisible prop

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Linter fix

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
Alex Andreev 2022-01-19 14:31:15 +03:00 committed by GitHub
parent e862d5bf1d
commit 74d92d09d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 85 additions and 167 deletions

View File

@ -98,9 +98,6 @@ export async function bootstrap(di: DependencyInjectionContainer) {
logger.info(`${logPrefix} initializing IpcRendererListeners`); logger.info(`${logPrefix} initializing IpcRendererListeners`);
initializers.initIpcRendererListeners(extensionLoader); initializers.initIpcRendererListeners(extensionLoader);
logger.info(`${logPrefix} initializing StatusBarRegistry`);
initializers.initStatusBarRegistry();
extensionLoader.init(); extensionLoader.init();
const extensionDiscovery = di.inject(extensionDiscoveryInjectable); const extensionDiscovery = di.inject(extensionDiscoveryInjectable);

View File

@ -1,36 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import React from "react";
import { observer } from "mobx-react";
import { Icon } from "../icon";
import hotbarManagerInjectable from "../../../common/hotbar-store.injectable";
import { HotbarSwitchCommand } from "../hotbar/hotbar-switch-command";
import { withInjectables } from "@ogre-tools/injectable-react";
import commandOverlayInjectable from "../command-palette/command-overlay.injectable";
interface Dependencies {
openCommandOverlay: (component: React.ReactElement) => void;
activeHotbarName: () => string | undefined;
}
const NonInjectedActiveHotbarName = observer(({ openCommandOverlay, activeHotbarName }: Dependencies) => (
<div
className="flex items-center"
data-testid="current-hotbar-name"
onClick={() => openCommandOverlay(<HotbarSwitchCommand />)}
>
<Icon material="bookmarks" className="mr-2" size={14} />
{activeHotbarName()}
</div>
));
export const ActiveHotbarName = withInjectables<Dependencies>(NonInjectedActiveHotbarName, {
getProps: (di, props) => ({
activeHotbarName: () => di.inject(hotbarManagerInjectable).getActive()?.name,
openCommandOverlay: di.inject(commandOverlayInjectable).open,
...props,
}),
});

View File

@ -4,68 +4,24 @@
*/ */
import React from "react"; import React from "react";
import mockFs from "mock-fs"; import { render } from "@testing-library/react";
import { fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect"; import "@testing-library/jest-dom/extend-expect";
import { BottomBar } from "./bottom-bar"; import { BottomBar } from "./bottom-bar";
import { StatusBarRegistry } from "../../../extensions/registries"; import { StatusBarRegistry } from "../../../extensions/registries";
import hotbarManagerInjectable from "../../../common/hotbar-store.injectable";
import { HotbarSwitchCommand } from "../hotbar/hotbar-switch-command";
import { ActiveHotbarName } from "./active-hotbar-name";
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
import { DiRender, renderFor } from "../test-utils/renderFor";
import type { DependencyInjectionContainer } from "@ogre-tools/injectable";
import commandOverlayInjectable from "../command-palette/command-overlay.injectable";
import { getEmptyHotbar } from "../../../common/hotbar-types";
jest.mock("electron", () => ({ jest.mock("electron", () => ({
app: { app: {
getName: () => "lens", getPath: () => "/foo",
setName: jest.fn(),
setPath: jest.fn(),
getPath: () => "tmp",
},
ipcMain: {
handle: jest.fn(),
on: jest.fn(),
removeAllListeners: jest.fn(),
off: jest.fn(),
send: jest.fn(),
}, },
})); }));
const foobarHotbar = getEmptyHotbar("foobar");
describe("<BottomBar />", () => { describe("<BottomBar />", () => {
let di: DependencyInjectionContainer; beforeEach(() => {
let render: DiRender;
beforeEach(async () => {
const mockOpts = {
"tmp": {
"test-store.json": JSON.stringify({}),
},
};
di = getDiForUnitTesting({ doGeneralOverrides: true });
mockFs(mockOpts);
render = renderFor(di);
di.override(hotbarManagerInjectable, () => ({
getActive: () => foobarHotbar,
} as any));
await di.runSetups();
StatusBarRegistry.createInstance(); StatusBarRegistry.createInstance();
}); });
afterEach(() => { afterEach(() => {
StatusBarRegistry.resetInstance(); StatusBarRegistry.resetInstance();
mockFs.restore();
}); });
it("renders w/o errors", () => { it("renders w/o errors", () => {
@ -111,33 +67,6 @@ describe("<BottomBar />", () => {
expect(getByTestId(testId)).toHaveTextContent(text); expect(getByTestId(testId)).toHaveTextContent(text);
}); });
it("shows active hotbar name", () => {
StatusBarRegistry.getInstance().getItems = jest.fn().mockImplementationOnce(() => [
{ item: () => <ActiveHotbarName/> },
]);
const { getByTestId } = render(<BottomBar />);
expect(getByTestId("current-hotbar-name")).toHaveTextContent("foobar");
});
it("opens command palette on click", () => {
const mockOpen = jest.fn();
di.override(commandOverlayInjectable, () => ({
open: mockOpen,
}) as any);
StatusBarRegistry.getInstance().getItems = jest.fn().mockImplementationOnce(() => [
{ item: () => <ActiveHotbarName/> },
]);
const { getByTestId } = render(<BottomBar />);
const activeHotbar = getByTestId("current-hotbar-name");
fireEvent.click(activeHotbar);
expect(mockOpen).toHaveBeenCalledWith(<HotbarSwitchCommand />);
});
it("sort positioned items properly", () => { it("sort positioned items properly", () => {
StatusBarRegistry.getInstance().getItems = jest.fn().mockImplementationOnce(() => [ StatusBarRegistry.getInstance().getItems = jest.fn().mockImplementationOnce(() => [

View File

@ -4,6 +4,8 @@
*/ */
.HotbarSelector { .HotbarSelector {
display: flex;
align-items: center;
height: 26px; height: 26px;
background-color: var(--layoutBackground); background-color: var(--layoutBackground);
position: relative; position: relative;
@ -17,7 +19,13 @@
top: -20px; top: -20px;
} }
.SelectorIndex { .HotbarIndex {
display: flex;
flex-grow: 1;
align-items: center;
}
.Badge {
cursor: pointer; cursor: pointer;
background: var(--secondaryBackground); background: var(--secondaryBackground);
width: 100%; width: 100%;

View File

@ -3,21 +3,18 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import "./hotbar-selector.scss"; import styles from "./hotbar-selector.module.scss";
import React from "react"; import React, { useRef, useState } from "react";
import { Icon } from "../icon"; import { Icon } from "../icon";
import { Badge } from "../badge"; import { Badge } from "../badge";
import hotbarManagerInjectable from "../../../common/hotbar-store.injectable"; import hotbarManagerInjectable from "../../../common/hotbar-store.injectable";
import { HotbarSwitchCommand } from "./hotbar-switch-command"; import { HotbarSwitchCommand } from "./hotbar-switch-command";
import { TooltipPosition } from "../tooltip"; import { Tooltip, TooltipPosition } from "../tooltip";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { Hotbar } from "../../../common/hotbar-types"; import type { Hotbar } from "../../../common/hotbar-types";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import commandOverlayInjectable from "../command-palette/command-overlay.injectable"; import commandOverlayInjectable from "../command-palette/command-overlay.injectable";
import { cssNames } from "../../utils";
export interface HotbarSelectorProps {
hotbar: Hotbar;
}
interface Dependencies { interface Dependencies {
hotbarManager: { hotbarManager: {
@ -29,25 +26,63 @@ interface Dependencies {
openCommandOverlay: (component: React.ReactElement) => void; openCommandOverlay: (component: React.ReactElement) => void;
} }
const NonInjectedHotbarSelector = observer(({ hotbar, hotbarManager, openCommandOverlay }: HotbarSelectorProps & Dependencies) => ( export interface HotbarSelectorProps extends Partial<Dependencies> {
<div className="HotbarSelector flex align-center"> hotbar: Hotbar;
<Icon material="play_arrow" className="previous box" onClick={() => hotbarManager.switchToPrevious()} /> }
<div className="box grow flex align-center">
const NonInjectedHotbarSelector = observer(({ hotbar, hotbarManager, openCommandOverlay }: HotbarSelectorProps & Dependencies) => {
const [tooltipVisible, setTooltipVisible] = useState(false);
const tooltipTimeout = useRef<NodeJS.Timeout>();
function clearTimer() {
clearTimeout(tooltipTimeout.current);
}
function onTooltipShow() {
setTooltipVisible(true);
clearTimer();
tooltipTimeout.current = setTimeout(() => setTooltipVisible(false), 1500);
}
function onArrowClick(switchTo: () => void) {
onTooltipShow();
switchTo();
}
function onMouseEvent(event: React.MouseEvent) {
clearTimer();
setTooltipVisible(event.type == "mouseenter");
}
return (
<div className={styles.HotbarSelector}>
<Icon
material="play_arrow"
className={cssNames(styles.Icon, styles.previous)}
onClick={() => onArrowClick(hotbarManager.switchToPrevious)}
/>
<div className={styles.HotbarIndex}>
<Badge <Badge
id="hotbarIndex" id="hotbarIndex"
small small
label={hotbarManager.getDisplayIndex(hotbarManager.getActive())} label={hotbarManager.getDisplayIndex(hotbarManager.getActive())}
onClick={() => openCommandOverlay(<HotbarSwitchCommand />)} onClick={() => openCommandOverlay(<HotbarSwitchCommand />)}
tooltip={{ className={styles.Badge}
preferredPositions: [TooltipPosition.TOP, TooltipPosition.TOP_LEFT], onMouseEnter={onMouseEvent}
children: hotbar.name, onMouseLeave={onMouseEvent}
}}
className="SelectorIndex"
/> />
<Tooltip
visible={tooltipVisible}
targetId="hotbarIndex"
preferredPositions={[TooltipPosition.TOP, TooltipPosition.TOP_LEFT]}
>
{hotbar.name}
</Tooltip>
</div> </div>
<Icon material="play_arrow" className="next box" onClick={() => hotbarManager.switchToNext()} /> <Icon material="play_arrow" className={styles.Icon} onClick={() => onArrowClick(hotbarManager.switchToNext)} />
</div> </div>
)); );
});
export const HotbarSelector = withInjectables<Dependencies, HotbarSelectorProps>(NonInjectedHotbarSelector, { export const HotbarSelector = withInjectables<Dependencies, HotbarSelectorProps>(NonInjectedHotbarSelector, {
getProps: (di, props) => ({ getProps: (di, props) => ({

View File

@ -25,14 +25,15 @@
pointer-events: none; pointer-events: none;
transition: opacity 150ms 150ms ease-in-out; transition: opacity 150ms 150ms ease-in-out;
z-index: 100000; z-index: 100000;
opacity: 1;
box-shadow: 0 8px 16px rgba(0,0,0,0.24); box-shadow: 0 8px 16px rgba(0,0,0,0.24);
&.invisible {
left: 0; left: 0;
top: 0; top: 0;
opacity: 0; opacity: 0;
visibility: hidden; visibility: hidden;
&.visible {
opacity: 1;
visibility: visible;
} }
&:empty { &:empty {

View File

@ -54,7 +54,7 @@ export class Tooltip extends React.Component<TooltipProps> {
@observable.ref elem: HTMLElement; @observable.ref elem: HTMLElement;
@observable activePosition: TooltipPosition; @observable activePosition: TooltipPosition;
@observable isVisible = !!this.props.visible; @observable isVisible = false;
constructor(props: TooltipProps) { constructor(props: TooltipProps) {
super(props); super(props);
@ -78,6 +78,10 @@ export class Tooltip extends React.Component<TooltipProps> {
this.hoverTarget.addEventListener("mouseleave", this.onLeaveTarget); this.hoverTarget.addEventListener("mouseleave", this.onLeaveTarget);
} }
componentDidUpdate() {
this.refreshPosition();
}
componentWillUnmount() { componentWillUnmount() {
this.hoverTarget.removeEventListener("mouseenter", this.onEnterTarget); this.hoverTarget.removeEventListener("mouseenter", this.onEnterTarget);
this.hoverTarget.removeEventListener("mouseleave", this.onLeaveTarget); this.hoverTarget.removeEventListener("mouseleave", this.onLeaveTarget);
@ -210,9 +214,9 @@ export class Tooltip extends React.Component<TooltipProps> {
} }
render() { render() {
const { style, formatters, usePortal, children } = this.props; const { style, formatters, usePortal, children, visible } = this.props;
const className = cssNames("Tooltip", this.props.className, formatters, this.activePosition, { const className = cssNames("Tooltip", this.props.className, formatters, this.activePosition, {
invisible: !this.isVisible, visible: visible ?? this.isVisible,
formatter: !!formatters, formatter: !!formatters,
}); });
const tooltip = ( const tooltip = (

View File

@ -12,4 +12,3 @@ export * from "./kube-object-menu-registry";
export * from "./registries"; export * from "./registries";
export * from "./workloads-overview-detail-registry"; export * from "./workloads-overview-detail-registry";
export * from "./catalog-category-registry"; export * from "./catalog-category-registry";
export * from "./status-bar-registry";

View File

@ -1,19 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import React from "react";
import { StatusBarRegistry } from "../../extensions/registries";
import { ActiveHotbarName } from "../components/cluster-manager/active-hotbar-name";
export function initStatusBarRegistry() {
StatusBarRegistry.getInstance().add([
{
components: {
Item: () => <ActiveHotbarName/>,
position: "left",
},
},
]);
}