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

Add CatalogCategory filter to the extension API (#3718)

* Add CatalogCategory filter to the extension API.

Signed-off-by: Panu Horsmalahti <phorsmalahti@mirantis.com>

* Fix test.

Signed-off-by: Panu Horsmalahti <phorsmalahti@mirantis.com>
This commit is contained in:
Panu Horsmalahti 2021-09-13 15:46:28 +03:00 committed by GitHub
parent e2b096fae1
commit 936927af98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 96 additions and 8 deletions

View File

@ -72,4 +72,34 @@ describe("CatalogCategoryRegistry", () => {
d2(); d2();
expect(registry.items.length).toBe(0); expect(registry.items.length).toBe(0);
}); });
it("doesn't return items that are filtered out", () => {
const registry = new TestCatalogCategoryRegistry();
registry.add(new TestCatalogCategory());
registry.add(new TestCatalogCategory2());
expect(registry.items.length).toBe(2);
expect(registry.filteredItems.length).toBe(2);
const disposer = registry.addCatalogCategoryFilter(category => category.metadata.name === "Test Category");
expect(registry.items.length).toBe(2);
expect(registry.filteredItems.length).toBe(1);
const disposer2 = registry.addCatalogCategoryFilter(category => category.metadata.name === "foo");
expect(registry.items.length).toBe(2);
expect(registry.filteredItems.length).toBe(0);
disposer();
expect(registry.items.length).toBe(2);
expect(registry.filteredItems.length).toBe(0);
disposer2();
expect(registry.items.length).toBe(2);
expect(registry.filteredItems.length).toBe(2);
});
}); });

View File

@ -20,12 +20,18 @@
*/ */
import { action, computed, observable, makeObservable } from "mobx"; import { action, computed, observable, makeObservable } from "mobx";
import { Disposer, ExtendedMap } from "../utils"; import { Disposer, ExtendedMap, iter } from "../utils";
import { CatalogCategory, CatalogEntityData, CatalogEntityKindData } from "./catalog-entity"; import { CatalogCategory, CatalogEntityData, CatalogEntityKindData } from "./catalog-entity";
import { once } from "lodash";
export type CategoryFilter = (category: CatalogCategory) => any;
export class CatalogCategoryRegistry { export class CatalogCategoryRegistry {
protected categories = observable.set<CatalogCategory>(); protected categories = observable.set<CatalogCategory>();
protected groupKinds = new ExtendedMap<string, ExtendedMap<string, CatalogCategory>>(); protected groupKinds = new ExtendedMap<string, ExtendedMap<string, CatalogCategory>>();
protected filters = observable.set<CategoryFilter>([], {
deep: false,
});
constructor() { constructor() {
makeObservable(this); makeObservable(this);
@ -47,6 +53,17 @@ export class CatalogCategoryRegistry {
return Array.from(this.categories); return Array.from(this.categories);
} }
@computed get filteredItems() {
return Array.from(
iter.reduce(
this.filters,
iter.filter,
this.items,
)
);
}
getForGroupKind<T extends CatalogCategory>(group: string, kind: string): T | undefined { getForGroupKind<T extends CatalogCategory>(group: string, kind: string): T | undefined {
return this.groupKinds.get(group)?.get(kind) as T; return this.groupKinds.get(group)?.get(kind) as T;
} }
@ -80,6 +97,17 @@ export class CatalogCategoryRegistry {
getByName(name: string) { getByName(name: string) {
return this.items.find(category => category.metadata?.name == name); return this.items.find(category => category.metadata?.name == name);
} }
/**
* Add a new filter to the set of category filters
* @param fn The function that should return a truthy value if that category should be displayed
* @returns A function to remove that filter
*/
addCatalogCategoryFilter(fn: CategoryFilter): Disposer {
this.filters.add(fn);
return once(() => void this.filters.delete(fn));
}
} }
export const catalogCategoryRegistry = new CatalogCategoryRegistry(); export const catalogCategoryRegistry = new CatalogCategoryRegistry();

View File

@ -26,6 +26,7 @@ import { getExtensionPageUrl } from "./registries/page-registry";
import type { CatalogEntity } from "../common/catalog"; import type { CatalogEntity } from "../common/catalog";
import type { Disposer } from "../common/utils"; import type { Disposer } from "../common/utils";
import { catalogEntityRegistry, EntityFilter } from "../renderer/api/catalog-entity-registry"; import { catalogEntityRegistry, EntityFilter } from "../renderer/api/catalog-entity-registry";
import { catalogCategoryRegistry, CategoryFilter } from "../renderer/api/catalog-category-registry";
export class LensRendererExtension extends LensExtension { export class LensRendererExtension extends LensExtension {
globalPages: registries.PageRegistration[] = []; globalPages: registries.PageRegistration[] = [];
@ -63,8 +64,8 @@ export class LensRendererExtension extends LensExtension {
} }
/** /**
* Add a filtering function for the catalog. This will be removed if the extension is disabled. * Add a filtering function for the catalog entities. This will be removed if the extension is disabled.
* @param fn The function which should return a truthy value for those entities which should be kepted * @param fn The function which should return a truthy value for those entities which should be kept.
* @returns A function to clean up the filter * @returns A function to clean up the filter
*/ */
addCatalogFilter(fn: EntityFilter): Disposer { addCatalogFilter(fn: EntityFilter): Disposer {
@ -74,4 +75,17 @@ export class LensRendererExtension extends LensExtension {
return dispose; return dispose;
} }
/**
* Add a filtering function for the catalog catogries. This will be removed if the extension is disabled.
* @param fn The function which should return a truthy value for those categories which should be kept.
* @returns A function to clean up the filter
*/
addCatalogCategoryFilter(fn: CategoryFilter): Disposer {
const dispose = catalogCategoryRegistry.addCatalogCategoryFilter(fn);
this[Disposers].push(dispose);
return dispose;
}
} }

View File

@ -20,3 +20,4 @@
*/ */
export { catalogCategoryRegistry } from "../../common/catalog"; export { catalogCategoryRegistry } from "../../common/catalog";
export type { CategoryFilter } from "../../common/catalog";

View File

@ -29,6 +29,7 @@ import { Icon } from "../icon";
import { StylesProvider } from "@material-ui/core"; import { StylesProvider } from "@material-ui/core";
import { cssNames } from "../../utils"; import { cssNames } from "../../utils";
import type { CatalogCategory } from "../../api/catalog-entity"; import type { CatalogCategory } from "../../api/catalog-entity";
import { observer } from "mobx-react";
type Props = { type Props = {
activeItem: string; activeItem: string;
@ -36,7 +37,7 @@ type Props = {
}; };
function getCategories() { function getCategories() {
return catalogCategoryRegistry.items; return catalogCategoryRegistry.filteredItems;
} }
function getCategoryIcon(category: CatalogCategory) { function getCategoryIcon(category: CatalogCategory) {
@ -53,7 +54,7 @@ function Item(props: TreeItemProps) {
); );
} }
export function CatalogMenu(props: Props) { export const CatalogMenu = observer((props: Props) => {
return ( return (
// Overwrite Material UI styles with injectFirst https://material-ui.com/guides/interoperability/#controlling-priority-4 // Overwrite Material UI styles with injectFirst https://material-ui.com/guides/interoperability/#controlling-priority-4
<StylesProvider injectFirst> <StylesProvider injectFirst>
@ -88,4 +89,4 @@ export function CatalogMenu(props: Props) {
</div> </div>
</StylesProvider> </StylesProvider>
); );
} });

View File

@ -90,8 +90,8 @@ export class Catalog extends React.Component<Props> {
previousActiveTab.set(this.routeActiveTab); previousActiveTab.set(this.routeActiveTab);
try { try {
await when(() => (routeTab === "" || !!catalogCategoryRegistry.items.find(i => i.getId() === routeTab)), { timeout: 5_000 }); // we need to wait because extensions might take a while to load await when(() => (routeTab === "" || !!catalogCategoryRegistry.filteredItems.find(i => i.getId() === routeTab)), { timeout: 5_000 }); // we need to wait because extensions might take a while to load
const item = catalogCategoryRegistry.items.find(i => i.getId() === routeTab); const item = catalogCategoryRegistry.filteredItems.find(i => i.getId() === routeTab);
runInAction(() => { runInAction(() => {
this.activeTab = routeTab; this.activeTab = routeTab;
@ -103,6 +103,20 @@ export class Catalog extends React.Component<Props> {
} }
}, {fireImmediately: true}), }, {fireImmediately: true}),
]); ]);
// If active category is filtered out, automatically switch to the first category
disposeOnUnmount(this, reaction(() => catalogCategoryRegistry.filteredItems, () => {
if (!catalogCategoryRegistry.filteredItems.find(item => item.getId() === this.catalogEntityStore.activeCategory.getId())) {
const item = catalogCategoryRegistry.filteredItems[0];
runInAction(() => {
if (item) {
this.activeTab = item.getId();
this.catalogEntityStore.activeCategory = item;
}
});
}
}));
} }
addToHotbar(item: CatalogEntityItem<CatalogEntity>): void { addToHotbar(item: CatalogEntityItem<CatalogEntity>): void {
HotbarStore.getInstance().addToHotbar(item.entity); HotbarStore.getInstance().addToHotbar(item.entity);