diff --git a/src/common/__tests__/catalog-entity.test.ts b/src/common/__tests__/catalog-entity.test.tsx similarity index 52% rename from src/common/__tests__/catalog-entity.test.ts rename to src/common/__tests__/catalog-entity.test.tsx index 3785ed1d49..e6d2e939c7 100644 --- a/src/common/__tests__/catalog-entity.test.ts +++ b/src/common/__tests__/catalog-entity.test.tsx @@ -3,15 +3,18 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import React from "react"; import { CatalogCategory, CatalogCategorySpec } from "../catalog"; -class TestCatalogCategory extends CatalogCategory { +class TestCatalogCategoryWithoutBadge extends CatalogCategory { public readonly apiVersion = "catalog.k8slens.dev/v1alpha1"; public readonly kind = "CatalogCategory"; + public metadata = { name: "Test Category", icon: "", }; + public spec: CatalogCategorySpec = { group: "entity.k8slens.dev", versions: [], @@ -21,10 +24,28 @@ class TestCatalogCategory extends CatalogCategory { }; } +class TestCatalogCategoryWithBadge extends TestCatalogCategoryWithoutBadge { + getBadge() { + return (
Test Badge
); + } +} + describe("CatalogCategory", () => { it("returns name", () => { - const category = new TestCatalogCategory(); + const category = new TestCatalogCategoryWithoutBadge(); expect(category.getName()).toEqual("Test Category"); }); + + it("doesn't return badge by default", () => { + const category = new TestCatalogCategoryWithoutBadge(); + + expect(category.getBadge()).toEqual(null); + }); + + it("returns a badge", () => { + const category = new TestCatalogCategoryWithBadge(); + + expect(category.getBadge()).toBeTruthy(); + }); }); diff --git a/src/common/catalog/catalog-entity.ts b/src/common/catalog/catalog-entity.ts index 2704bed2e0..7bc396d377 100644 --- a/src/common/catalog/catalog-entity.ts +++ b/src/common/catalog/catalog-entity.ts @@ -180,6 +180,15 @@ export abstract class CatalogCategory extends (EventEmitter as new () => TypedEm return this.metadata.name; } + /** + * Get the badge of this category. + * Defaults to no badge. + * The badge is displayed next to the Category name in the Catalog Category menu + */ + public getBadge(): React.ReactNode { + return null; + } + /** * Add a filter for menu items of catalogAddMenu * @param fn The function that should return a truthy value if that menu item should be displayed diff --git a/src/renderer/components/+catalog/__tests__/catalog-category-label.test.tsx b/src/renderer/components/+catalog/__tests__/catalog-category-label.test.tsx new file mode 100644 index 0000000000..015929b643 --- /dev/null +++ b/src/renderer/components/+catalog/__tests__/catalog-category-label.test.tsx @@ -0,0 +1,53 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { render, screen } from "@testing-library/react"; +import React from "react"; +import "@testing-library/jest-dom/extend-expect"; +import { CatalogCategory, CatalogCategorySpec } from "../../../../common/catalog"; +import { CatalogCategoryLabel } from "../catalog-category-label"; + +class CatalogCategoryWithoutBadge extends CatalogCategory { + public readonly apiVersion = "catalog.k8slens.dev/v1alpha1"; + public readonly kind = "CatalogCategory"; + + public metadata = { + name: "Test Category", + icon: "", + }; + + public spec: CatalogCategorySpec = { + group: "entity.k8slens.dev", + versions: [], + names: { + kind: "Test", + }, + }; +} + +class CatalogCategoryWithBadge extends CatalogCategoryWithoutBadge { + getBadge() { + return (
Test Badge
); + } +} + +describe("CatalogCategoryLabel", () => { + it("renders without a badge", async () => { + const category = new CatalogCategoryWithoutBadge(); + + render(); + + expect(await screen.findByText("Test Category")).toBeInTheDocument(); + }); + + it("renders with a badge", async () => { + const category = new CatalogCategoryWithBadge(); + + render(); + + expect(await screen.findByText("Test Category")).toBeInTheDocument(); + expect(await screen.findByText("Test Badge")).toBeInTheDocument(); + }); +}); diff --git a/src/renderer/components/+catalog/catalog-category-label.tsx b/src/renderer/components/+catalog/catalog-category-label.tsx new file mode 100644 index 0000000000..170d8e92b6 --- /dev/null +++ b/src/renderer/components/+catalog/catalog-category-label.tsx @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import React from "react"; +import type { CatalogCategory } from "../../../common/catalog/catalog-entity"; + +interface CatalogCategoryLabelProps { + category: CatalogCategory; +} + +/** + * Display label for Catalog Category for the Catalog menu + */ +export const CatalogCategoryLabel = ({ category }: CatalogCategoryLabelProps) => { + const badge = category.getBadge(); + + return ( +
+
{category.metadata.name}
+ {badge ? (
{badge}
) : null} +
+ ); +}; diff --git a/src/renderer/components/+catalog/catalog-menu.tsx b/src/renderer/components/+catalog/catalog-menu.tsx index 2cc543c25f..36590b82a8 100644 --- a/src/renderer/components/+catalog/catalog-menu.tsx +++ b/src/renderer/components/+catalog/catalog-menu.tsx @@ -14,6 +14,7 @@ import { StylesProvider } from "@material-ui/core"; import { cssNames } from "../../utils"; import type { CatalogCategory } from "../../api/catalog-entity"; import { observer } from "mobx-react"; +import { CatalogCategoryLabel } from "./catalog-category-label"; export interface CatalogMenuProps { activeItem: string; @@ -66,7 +67,7 @@ export const CatalogMenu = observer((props: CatalogMenuProps) => { icon={getCategoryIcon(category)} key={category.getId()} nodeId={category.getId()} - label={category.metadata.name} + label={} data-testid={`${category.getId()}-tab`} onClick={() => props.onItemClick(category.getId())} /> diff --git a/src/renderer/themes/lens-dark.json b/src/renderer/themes/lens-dark.json index f30d48197e..f0b8a97282 100644 --- a/src/renderer/themes/lens-dark.json +++ b/src/renderer/themes/lens-dark.json @@ -29,6 +29,7 @@ "sidebarActiveColor": "#ffffff", "sidebarSubmenuActiveColor": "#ffffff", "sidebarItemHoverBackground": "#3a3e44", + "badgeBackgroundColor": "#ffba44", "buttonPrimaryBackground": "#3d90ce", "buttonDefaultBackground": "#414448", "buttonLightBackground": "#f1f1f1", diff --git a/src/renderer/themes/lens-light.json b/src/renderer/themes/lens-light.json index 06ef6d79b2..d240d7df8a 100644 --- a/src/renderer/themes/lens-light.json +++ b/src/renderer/themes/lens-light.json @@ -29,6 +29,7 @@ "sidebarSubmenuActiveColor": "#3d90ce", "sidebarBackground": "#e8e8e8", "sidebarItemHoverBackground": "#f0f2f5", + "badgeBackgroundColor": "#ffba44", "buttonPrimaryBackground": "#3d90ce", "buttonDefaultBackground": "#414448", "buttonLightBackground": "#f1f1f1", diff --git a/src/renderer/themes/theme-vars.css b/src/renderer/themes/theme-vars.css index e0a695b72d..ffae94e401 100644 --- a/src/renderer/themes/theme-vars.css +++ b/src/renderer/themes/theme-vars.css @@ -29,6 +29,7 @@ --sidebarActiveColor: #ffffff; --sidebarSubmenuActiveColor: #ffffff; --sidebarItemHoverBackground: #3a3e44; +--badgeBackgroundColor: #ffba44; --buttonPrimaryBackground: #3d90ce; --buttonDefaultBackground: #414448; --buttonLightBackground: #f1f1f1; @@ -73,6 +74,8 @@ --dockEditorComment: #808080; --dockEditorActiveLineBackground: #3a3d41; --dockBadgeBackground: #36393e; +--dockTabBorderColor: #43424d; +--dockTabActiveBackground: #3a3e45; --logsBackground: #000000; --logsForeground: #ffffff; --logRowHoverBackground: #35373a; @@ -125,7 +128,7 @@ --inputControlHoverBorder: #474a4f; --lineProgressBackground: #414448; --radioActiveBackground: #36393e; ---menuActiveBackground: #36393e; +--menuActiveBackground: #3d90ce; --menuSelectedOptionBgc: #36393e; --canvasBackground: #24292e; --scrollBarColor: #5f6064;