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

TopBar Update button (#5376)

* UpdateButton skeleton

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

* Show UpdateButton in topbar

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

* Default button styles

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

* Add update icon to menu item

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

* Replace colors for medium & high levels

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

* Fine-tune colors for light theme

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

* Fix keyboard events bug

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

* Hide <UpdateButton> from topbar

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

* Clean up

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

* Using dropdown icon instead of sandwich

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

* Used determenistic id for the button

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

* Respect id prop

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

* Add small space

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

* Using accent color directly from active theme

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

* Update snapshots

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

* Fix open/close Menu errors

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

* Linter fix

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

* Fix linter harder

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

* Clean up tests

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

* Fix act() wrapper console.error

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

* Remove aria-expanded attribute

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
Alex Andreev 2022-05-18 19:36:40 +03:00 committed by GitHub
parent 54b92aa9b6
commit c2b4fed5c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 224 additions and 1 deletions

View File

@ -11,7 +11,7 @@ import { Icon } from "../../icon";
import { observable } from "mobx"; import { observable } from "mobx";
import { ipcRendererOn } from "../../../../common/ipc"; import { ipcRendererOn } from "../../../../common/ipc";
import { watchHistoryState } from "../../../remote-helpers/history-updater"; import { watchHistoryState } from "../../../remote-helpers/history-updater";
import { cssNames } from "../../../utils"; import { cssNames, noop } from "../../../utils";
import topBarItemsInjectable from "./top-bar-items/top-bar-items.injectable"; import topBarItemsInjectable from "./top-bar-items/top-bar-items.injectable";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import type { TopBarRegistration } from "./top-bar-registration"; import type { TopBarRegistration } from "./top-bar-registration";
@ -23,6 +23,7 @@ import type { NavigateToCatalog } from "../../../../common/front-end-routing/rou
import navigateToCatalogInjectable from "../../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; import navigateToCatalogInjectable from "../../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
import catalogRouteInjectable from "../../../../common/front-end-routing/routes/catalog/catalog-route.injectable"; import catalogRouteInjectable from "../../../../common/front-end-routing/routes/catalog/catalog-route.injectable";
import routeIsActiveInjectable from "../../../routes/route-is-active.injectable"; import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
import { UpdateButton } from "../../update-button";
interface Dependencies { interface Dependencies {
navigateToCatalog: NavigateToCatalog; navigateToCatalog: NavigateToCatalog;
@ -129,6 +130,7 @@ const NonInjectedTopBar = observer(({
onClick={goForward} onClick={goForward}
disabled={!nextEnabled.get()} disabled={!nextEnabled.get()}
/> />
<UpdateButton update={noop} />
</div> </div>
<div className={styles.items}> <div className={styles.items}>
{renderRegisteredItems(items.get())} {renderRegisteredItems(items.get())}

View File

@ -0,0 +1,22 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<UpdateButton/> should render if warning level prop passed 1`] = `
<button
class="updateButton"
data-testid="update-button"
data-warning-level="light"
id="update-lens-button"
>
Update
<i
class="Icon icon material focusable"
>
<span
class="icon"
data-icon-name="arrow_drop_down"
>
arrow_drop_down
</span>
</i>
</button>
`;

View File

@ -0,0 +1,77 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { render, act } from "@testing-library/react";
import React from "react";
import { UpdateButton } from "../update-button";
import "@testing-library/jest-dom/extend-expect";
const update = jest.fn();
describe("<UpdateButton/>", () => {
beforeEach(() => {
update.mockClear();
});
it("should not render if no warning level prop passed", () => {
const { queryByTestId } = render(<UpdateButton update={update} />);
expect(queryByTestId("update-button")).not.toBeInTheDocument();
});
it("should render if warning level prop passed", () => {
const { getByTestId } = render(<UpdateButton update={update} warningLevel="light" />);
expect(getByTestId("update-button")).toMatchSnapshot();
});
it("should open menu when clicked", async () => {
const { getByTestId } = render(<UpdateButton update={update} warningLevel="light" />);
const button = getByTestId("update-button");
act(() => button.click());
expect(getByTestId("update-lens-menu-item")).toBeInTheDocument();
});
it("should call update function when menu item clicked", () => {
const { getByTestId } = render(<UpdateButton update={update} warningLevel="light" />);
const button = getByTestId("update-button");
act(() => button.click());
const menuItem = getByTestId("update-lens-menu-item");
menuItem.click();
expect(update).toHaveBeenCalled();
});
it("should have light warning level", () => {
const { getByTestId } = render(<UpdateButton update={update} warningLevel="light" />);
const button = getByTestId("update-button");
expect(button.dataset.warningLevel).toBe("light");
});
it("should have medium warning level", () => {
const { getByTestId } = render(<UpdateButton update={update} warningLevel="medium" />);
const button = getByTestId("update-button");
expect(button.dataset.warningLevel).toBe("medium");
});
it("should have high warning level", () => {
const { getByTestId } = render(<UpdateButton update={update} warningLevel="high" />);
const button = getByTestId("update-button");
expect(button.dataset.warningLevel).toBe("high");
});
});

View File

@ -0,0 +1,6 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export * from "./update-button";

View File

@ -0,0 +1,52 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
.updateButton {
--accent-color: var(--colorOk);
border: 1px solid var(--accent-color);
border-radius: 4px;
color: var(--accent-color);
display: flex;
align-items: center;
padding: 4px 8px;
gap: 6px;
cursor: default;
position: relative;
&:hover::before{
opacity: 0.25;
}
&:focus-visible {
box-shadow: 0 0 0 2px var(--blue);
border-color: transparent;
}
&::before {
content: " ";
position: absolute;
background: var(--accent-color);
width: 100%;
height: 100%;
left: 0;
opacity: 0.15;
z-index: -1;
transition: opacity 0.1s;
}
}
.icon {
width: 16px;
height: 16px;
}
.warningMedium {
--accent-color: var(--colorWarning);
}
.warningHigh {
--accent-color: var(--colorSoftError);
}

View File

@ -0,0 +1,64 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import styles from "./styles.module.scss";
import type { HTMLAttributes } from "react";
import React, { useState } from "react";
import { Menu, MenuItem } from "../menu";
import { cssNames } from "../../utils";
import type { IconProps } from "../icon";
import { Icon } from "../icon";
interface UpdateButtonProps extends HTMLAttributes<HTMLButtonElement> {
warningLevel?: "light" | "medium" | "high";
update: () => void;
}
export function UpdateButton({ warningLevel, update, id }: UpdateButtonProps) {
const buttonId = id ?? "update-lens-button";
const menuIconProps: IconProps = { material: "update", small: true };
const [opened, setOpened] = useState(false);
const toggle = () => {
setOpened(!opened);
};
if (!warningLevel) {
return null;
}
return (
<>
<button
data-testid="update-button"
data-warning-level={warningLevel}
id={buttonId}
className={cssNames(styles.updateButton, {
[styles.warningHigh]: warningLevel === "high",
[styles.warningMedium]: warningLevel === "medium",
})}
>
Update
<Icon material="arrow_drop_down" className={styles.icon}/>
</button>
<Menu
usePortal
htmlFor={buttonId}
isOpen={opened}
close={toggle}
open={toggle}
>
<MenuItem
icon={menuIconProps}
onClick={update}
data-testid="update-lens-menu-item"
>
Relaunch to Update Lens
</MenuItem>
</Menu>
</>
);
}