diff --git a/src/renderer/components/layout/top-bar/top-bar.tsx b/src/renderer/components/layout/top-bar/top-bar.tsx index 69397ccf18..adc5457747 100644 --- a/src/renderer/components/layout/top-bar/top-bar.tsx +++ b/src/renderer/components/layout/top-bar/top-bar.tsx @@ -11,7 +11,7 @@ import { Icon } from "../../icon"; import { observable } from "mobx"; import { ipcRendererOn } from "../../../../common/ipc"; 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 { withInjectables } from "@ogre-tools/injectable-react"; 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 catalogRouteInjectable from "../../../../common/front-end-routing/routes/catalog/catalog-route.injectable"; import routeIsActiveInjectable from "../../../routes/route-is-active.injectable"; +import { UpdateButton } from "../../update-button"; interface Dependencies { navigateToCatalog: NavigateToCatalog; @@ -129,6 +130,7 @@ const NonInjectedTopBar = observer(({ onClick={goForward} disabled={!nextEnabled.get()} /> +
{renderRegisteredItems(items.get())} diff --git a/src/renderer/components/update-button/__tests__/__snapshots__/update-button.test.tsx.snap b/src/renderer/components/update-button/__tests__/__snapshots__/update-button.test.tsx.snap new file mode 100644 index 0000000000..3492065df4 --- /dev/null +++ b/src/renderer/components/update-button/__tests__/__snapshots__/update-button.test.tsx.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should render if warning level prop passed 1`] = ` + +`; diff --git a/src/renderer/components/update-button/__tests__/update-button.test.tsx b/src/renderer/components/update-button/__tests__/update-button.test.tsx new file mode 100644 index 0000000000..ff52035a48 --- /dev/null +++ b/src/renderer/components/update-button/__tests__/update-button.test.tsx @@ -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("", () => { + beforeEach(() => { + update.mockClear(); + }); + + it("should not render if no warning level prop passed", () => { + const { queryByTestId } = render(); + + expect(queryByTestId("update-button")).not.toBeInTheDocument(); + }); + + it("should render if warning level prop passed", () => { + const { getByTestId } = render(); + + expect(getByTestId("update-button")).toMatchSnapshot(); + }); + + it("should open menu when clicked", async () => { + const { getByTestId } = render(); + + 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(); + + 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(); + + const button = getByTestId("update-button"); + + expect(button.dataset.warningLevel).toBe("light"); + }); + + it("should have medium warning level", () => { + const { getByTestId } = render(); + + const button = getByTestId("update-button"); + + expect(button.dataset.warningLevel).toBe("medium"); + }); + + it("should have high warning level", () => { + const { getByTestId } = render(); + + const button = getByTestId("update-button"); + + expect(button.dataset.warningLevel).toBe("high"); + }); +}); diff --git a/src/renderer/components/update-button/index.ts b/src/renderer/components/update-button/index.ts new file mode 100644 index 0000000000..006a7f0d81 --- /dev/null +++ b/src/renderer/components/update-button/index.ts @@ -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"; diff --git a/src/renderer/components/update-button/styles.module.scss b/src/renderer/components/update-button/styles.module.scss new file mode 100644 index 0000000000..ad4d39a7dd --- /dev/null +++ b/src/renderer/components/update-button/styles.module.scss @@ -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); +} diff --git a/src/renderer/components/update-button/update-button.tsx b/src/renderer/components/update-button/update-button.tsx new file mode 100644 index 0000000000..6da453de3d --- /dev/null +++ b/src/renderer/components/update-button/update-button.tsx @@ -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 { + 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 ( + <> + + + + Relaunch to Update Lens + + + + ); +}