[] = [];
topBarItems: TopBarRegistration[] = [];
additionalCategoryColumns: AdditionalCategoryColumnRegistration[] = [];
+ customCategoryViews: CustomCategoryViewRegistration[] = [];
async navigate(pageId?: string, params?: P) {
const { navigate } = await import("../renderer/navigation");
diff --git a/src/renderer/components/+catalog/__tests__/custom-views.test.ts b/src/renderer/components/+catalog/__tests__/custom-views.test.ts
new file mode 100644
index 0000000000..af2dad0f55
--- /dev/null
+++ b/src/renderer/components/+catalog/__tests__/custom-views.test.ts
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import type { ConfigurableDependencyInjectionContainer } from "@ogre-tools/injectable";
+import { computed } from "mobx";
+import type React from "react";
+import type { LensRendererExtension } from "../../../../extensions/lens-renderer-extension";
+import rendererExtensionsInjectable from "../../../../extensions/renderer-extensions.injectable";
+import { getDiForUnitTesting } from "../../../getDiForUnitTesting";
+import type { CustomCategoryViewRegistration } from "../custom-views";
+import customCategoryViewsInjectable from "../custom-views.injectable";
+
+describe("Custom Category Views", () => {
+ let di: ConfigurableDependencyInjectionContainer;
+
+ beforeEach(() => {
+ di = getDiForUnitTesting();
+ });
+
+ it("should order items correctly over all extensions", () => {
+ const component1 = (): React.ReactElement => null;
+ const component2 = (): React.ReactElement => null;
+
+ di.override(rendererExtensionsInjectable, () => computed(() => [
+ {
+ customCategoryViews: [
+ {
+ components: {
+ View: component1,
+ },
+ group: "foo",
+ kind: "bar",
+ priority: 100,
+ } as CustomCategoryViewRegistration,
+ ],
+ },
+ {
+ customCategoryViews: [
+ {
+ components: {
+ View: component2,
+ },
+ group: "foo",
+ kind: "bar",
+ priority: 95,
+ } as CustomCategoryViewRegistration,
+ ],
+ },
+ ] as LensRendererExtension[]));
+
+ const customCategoryViews = di.inject(customCategoryViewsInjectable);
+ const { after } = customCategoryViews.get().get("foo").get("bar");
+
+ expect(after[0].View).toBe(component2);
+ expect(after[1].View).toBe(component1);
+ });
+
+ it("should put put priority < 50 items in before", () => {
+ const component1 = (): React.ReactElement => null;
+ const component2 = (): React.ReactElement => null;
+
+ di.override(rendererExtensionsInjectable, () => computed(() => [
+ {
+ customCategoryViews: [
+ {
+ components: {
+ View: component1,
+ },
+ group: "foo",
+ kind: "bar",
+ priority: 40,
+ } as CustomCategoryViewRegistration,
+ ],
+ },
+ {
+ customCategoryViews: [
+ {
+ components: {
+ View: component2,
+ },
+ group: "foo",
+ kind: "bar",
+ priority: 95,
+ } as CustomCategoryViewRegistration,
+ ],
+ },
+ ] as LensRendererExtension[]));
+
+ const customCategoryViews = di.inject(customCategoryViewsInjectable);
+ const { before } = customCategoryViews.get().get("foo").get("bar");
+
+ expect(before[0].View).toBe(component1);
+ });
+});
diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx
index 107e02db30..194f35f481 100644
--- a/src/renderer/components/+catalog/catalog.tsx
+++ b/src/renderer/components/+catalog/catalog.tsx
@@ -8,7 +8,7 @@ import styles from "./catalog.module.scss";
import React from "react";
import { disposeOnUnmount, observer } from "mobx-react";
import { ItemListLayout } from "../item-object-list";
-import { action, makeObservable, observable, reaction, runInAction, when } from "mobx";
+import { action, IComputedValue, makeObservable, observable, reaction, runInAction, when } from "mobx";
import type { CatalogEntityStore } from "./catalog-entity-store/catalog-entity.store";
import { navigate } from "../../navigation";
import { MenuItem, MenuActions } from "../menu";
@@ -33,6 +33,9 @@ import catalogPreviousActiveTabStorageInjectable from "./catalog-previous-active
import catalogEntityStoreInjectable from "./catalog-entity-store/catalog-entity-store.injectable";
import type { GetCategoryColumnsParams, CategoryColumns } from "./get-category-columns.injectable";
import getCategoryColumnsInjectable from "./get-category-columns.injectable";
+import type { RegisteredCustomCategoryViewDecl } from "./custom-views.injectable";
+import customCategoryViewsInjectable from "./custom-views.injectable";
+import type { CustomCategoryViewComponents } from "./custom-views";
interface Props extends RouteComponentProps {}
@@ -40,6 +43,7 @@ interface Dependencies {
catalogPreviousActiveTabStorage: { set: (value: string ) => void };
catalogEntityStore: CatalogEntityStore;
getCategoryColumns: (params: GetCategoryColumnsParams) => CategoryColumns;
+ customCategoryViews: IComputedValue