mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Before hook for onRun of Catalog entities (#3911)
Co-authored-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
851274d314
commit
4af796c532
17 files changed
+636
-128
No files matched your search
@@ -23,13 +23,12 @@ import "./catalog-entity-details.scss";
|
||||
import React, { Component } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { Drawer, DrawerItem } from "../drawer";
|
||||
import { catalogEntityRunContext } from "../../api/catalog-entity";
|
||||
import type { CatalogCategory, CatalogEntity } from "../../../common/catalog";
|
||||
import { Icon } from "../icon";
|
||||
import { CatalogEntityDrawerMenu } from "./catalog-entity-drawer-menu";
|
||||
import { CatalogEntityDetailRegistry } from "../../../extensions/registries";
|
||||
import { HotbarIcon } from "../hotbar/hotbar-icon";
|
||||
import type { CatalogEntityItem } from "./catalog-entity.store";
|
||||
import type { CatalogEntityItem } from "./catalog-entity-item";
|
||||
import { isDevelopment } from "../../../common/vars";
|
||||
|
||||
interface Props<T extends CatalogEntity> {
|
||||
@@ -68,8 +67,10 @@ export class CatalogEntityDetails<T extends CatalogEntity> extends Component<Pro
|
||||
material={item.entity.spec.icon?.material}
|
||||
background={item.entity.spec.icon?.background}
|
||||
disabled={!item?.enabled}
|
||||
onClick={() => item.onRun(catalogEntityRunContext)}
|
||||
size={128} />
|
||||
onClick={() => item.onRun()}
|
||||
size={128}
|
||||
data-testid="detail-panel-hot-bar-icon"
|
||||
/>
|
||||
{item?.enabled && (
|
||||
<div className="IconHint">
|
||||
Click to open
|
||||
|
||||
@@ -30,7 +30,7 @@ import { MenuItem } from "../menu";
|
||||
import { ConfirmDialog } from "../confirm-dialog";
|
||||
import { HotbarStore } from "../../../common/hotbar-store";
|
||||
import { Icon } from "../icon";
|
||||
import type { CatalogEntityItem } from "./catalog-entity.store";
|
||||
import type { CatalogEntityItem } from "./catalog-entity-item";
|
||||
|
||||
export interface CatalogEntityDrawerMenuProps<T extends CatalogEntity> extends MenuActionsProps {
|
||||
item: CatalogEntityItem<T> | null | undefined;
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import styles from "./catalog.module.css";
|
||||
import React from "react";
|
||||
import { action, computed } from "mobx";
|
||||
import type { CatalogEntity } from "../../api/catalog-entity";
|
||||
import type { ItemObject } from "../../../common/item.store";
|
||||
import { Badge } from "../badge";
|
||||
import { navigation } from "../../navigation";
|
||||
import { searchUrlParam } from "../input";
|
||||
import { makeCss } from "../../../common/utils/makeCss";
|
||||
import { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import type { CatalogEntityRegistry } from "../../api/catalog-entity-registry";
|
||||
|
||||
const css = makeCss(styles);
|
||||
|
||||
export class CatalogEntityItem<T extends CatalogEntity> implements ItemObject {
|
||||
constructor(public entity: T, private registry: CatalogEntityRegistry) {}
|
||||
|
||||
get kind() {
|
||||
return this.entity.kind;
|
||||
}
|
||||
|
||||
get apiVersion() {
|
||||
return this.entity.apiVersion;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.entity.metadata.name;
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.entity.metadata.name;
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this.entity.metadata.uid;
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@computed get phase() {
|
||||
return this.entity.status.phase;
|
||||
}
|
||||
|
||||
get enabled() {
|
||||
return this.entity.status.enabled ?? true;
|
||||
}
|
||||
|
||||
get labels() {
|
||||
return KubeObject.stringifyLabels(this.entity.metadata.labels);
|
||||
}
|
||||
|
||||
getLabelBadges(onClick?: React.MouseEventHandler<any>) {
|
||||
return this.labels
|
||||
.map(label => (
|
||||
<Badge
|
||||
className={css.badge}
|
||||
key={label}
|
||||
label={label}
|
||||
title={label}
|
||||
onClick={(event) => {
|
||||
navigation.searchParams.set(searchUrlParam.name, label);
|
||||
onClick?.(event);
|
||||
event.stopPropagation();
|
||||
}}
|
||||
expandable={false}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
||||
get source() {
|
||||
return this.entity.metadata.source || "unknown";
|
||||
}
|
||||
|
||||
get searchFields() {
|
||||
return [
|
||||
this.name,
|
||||
this.id,
|
||||
this.phase,
|
||||
`source=${this.source}`,
|
||||
...this.labels,
|
||||
];
|
||||
}
|
||||
|
||||
onRun() {
|
||||
this.registry.onRun(this.entity);
|
||||
}
|
||||
|
||||
@action
|
||||
async onContextMenuOpen(ctx: any) {
|
||||
return this.entity.onContextMenuOpen(ctx);
|
||||
}
|
||||
}
|
||||
@@ -19,106 +19,16 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import styles from "./catalog.module.css";
|
||||
|
||||
import React from "react";
|
||||
import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from "mobx";
|
||||
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
||||
import type { CatalogEntity, CatalogEntityActionContext } from "../../api/catalog-entity";
|
||||
import { ItemObject, ItemStore } from "../../../common/item.store";
|
||||
import { computed, makeObservable, observable, reaction } from "mobx";
|
||||
import { catalogEntityRegistry, CatalogEntityRegistry } from "../../api/catalog-entity-registry";
|
||||
import type { CatalogEntity } from "../../api/catalog-entity";
|
||||
import { ItemStore } from "../../../common/item.store";
|
||||
import { CatalogCategory, catalogCategoryRegistry } from "../../../common/catalog";
|
||||
import { autoBind } from "../../../common/utils";
|
||||
import { Badge } from "../badge";
|
||||
import { navigation } from "../../navigation";
|
||||
import { searchUrlParam } from "../input";
|
||||
import { makeCss } from "../../../common/utils/makeCss";
|
||||
import { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
|
||||
const css = makeCss(styles);
|
||||
|
||||
export class CatalogEntityItem<T extends CatalogEntity> implements ItemObject {
|
||||
constructor(public entity: T) {}
|
||||
|
||||
get kind() {
|
||||
return this.entity.kind;
|
||||
}
|
||||
|
||||
get apiVersion() {
|
||||
return this.entity.apiVersion;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.entity.metadata.name;
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.entity.metadata.name;
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this.entity.metadata.uid;
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@computed get phase() {
|
||||
return this.entity.status.phase;
|
||||
}
|
||||
|
||||
get enabled() {
|
||||
return this.entity.status.enabled ?? true;
|
||||
}
|
||||
|
||||
get labels() {
|
||||
return KubeObject.stringifyLabels(this.entity.metadata.labels);
|
||||
}
|
||||
|
||||
getLabelBadges(onClick?: React.MouseEventHandler<any>) {
|
||||
return this.labels
|
||||
.map(label => (
|
||||
<Badge
|
||||
className={css.badge}
|
||||
key={label}
|
||||
label={label}
|
||||
title={label}
|
||||
onClick={(event) => {
|
||||
navigation.searchParams.set(searchUrlParam.name, label);
|
||||
onClick?.(event);
|
||||
event.stopPropagation();
|
||||
}}
|
||||
expandable={false}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
||||
get source() {
|
||||
return this.entity.metadata.source || "unknown";
|
||||
}
|
||||
|
||||
get searchFields() {
|
||||
return [
|
||||
this.name,
|
||||
this.id,
|
||||
this.phase,
|
||||
`source=${this.source}`,
|
||||
...this.labels,
|
||||
];
|
||||
}
|
||||
|
||||
onRun(ctx: CatalogEntityActionContext) {
|
||||
this.entity.onRun(ctx);
|
||||
}
|
||||
|
||||
@action
|
||||
async onContextMenuOpen(ctx: any) {
|
||||
return this.entity.onContextMenuOpen(ctx);
|
||||
}
|
||||
}
|
||||
import { autoBind, disposer } from "../../../common/utils";
|
||||
import { CatalogEntityItem } from "./catalog-entity-item";
|
||||
|
||||
export class CatalogEntityStore extends ItemStore<CatalogEntityItem<CatalogEntity>> {
|
||||
constructor() {
|
||||
constructor(private registry: CatalogEntityRegistry = catalogEntityRegistry) {
|
||||
super();
|
||||
makeObservable(this);
|
||||
autoBind(this);
|
||||
@@ -129,10 +39,10 @@ export class CatalogEntityStore extends ItemStore<CatalogEntityItem<CatalogEntit
|
||||
|
||||
@computed get entities() {
|
||||
if (!this.activeCategory) {
|
||||
return catalogEntityRegistry.filteredItems.map(entity => new CatalogEntityItem(entity));
|
||||
return this.registry.filteredItems.map(entity => new CatalogEntityItem(entity, this.registry));
|
||||
}
|
||||
|
||||
return catalogEntityRegistry.getItemsForCategory(this.activeCategory, { filtered: true }).map(entity => new CatalogEntityItem(entity));
|
||||
return this.registry.getItemsForCategory(this.activeCategory, { filtered: true }).map(entity => new CatalogEntityItem(entity, this.registry));
|
||||
}
|
||||
|
||||
@computed get selectedItem() {
|
||||
@@ -140,12 +50,10 @@ export class CatalogEntityStore extends ItemStore<CatalogEntityItem<CatalogEntit
|
||||
}
|
||||
|
||||
watch() {
|
||||
const disposers: IReactionDisposer[] = [
|
||||
return disposer(
|
||||
reaction(() => this.entities, () => this.loadAll()),
|
||||
reaction(() => this.activeCategory, () => this.loadAll(), { delay: 100})
|
||||
];
|
||||
|
||||
return () => disposers.forEach((dispose) => dispose());
|
||||
reaction(() => this.activeCategory, () => this.loadAll(), { delay: 100}),
|
||||
);
|
||||
}
|
||||
|
||||
loadAll() {
|
||||
|
||||
@@ -32,4 +32,4 @@
|
||||
.catalog {
|
||||
@apply p-5 font-bold text-2xl;
|
||||
color: var(--textColorAccent);
|
||||
}
|
||||
}
|
||||
@@ -41,11 +41,15 @@ function getCategories() {
|
||||
}
|
||||
|
||||
function getCategoryIcon(category: CatalogCategory) {
|
||||
if (!category.metadata?.icon) return null;
|
||||
const { icon } = category.metadata ?? {};
|
||||
|
||||
return category.metadata.icon.includes("<svg")
|
||||
? <Icon small svg={category.metadata.icon}/>
|
||||
: <Icon small material={category.metadata.icon}/>;
|
||||
if (typeof icon === "string") {
|
||||
return icon.includes("<svg")
|
||||
? <Icon small svg={icon}/>
|
||||
: <Icon small material={icon}/>;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function Item(props: TreeItemProps) {
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { Catalog } from "./catalog";
|
||||
import { createMemoryHistory } from "history";
|
||||
import { mockWindow } from "../../../../__mocks__/windowMock";
|
||||
import { kubernetesClusterCategory } from "../../../common/catalog-entities/kubernetes-cluster";
|
||||
import { catalogCategoryRegistry, CatalogCategoryRegistry } from "../../../common/catalog";
|
||||
import { CatalogEntityRegistry } from "../../../renderer/api/catalog-entity-registry";
|
||||
import { CatalogEntityDetailRegistry } from "../../../extensions/registries";
|
||||
import { CatalogEntityItem } from "./catalog-entity-item";
|
||||
import { CatalogEntityStore } from "./catalog-entity.store";
|
||||
|
||||
mockWindow();
|
||||
|
||||
// avoid TypeError: Cannot read property 'getPath' of undefined
|
||||
jest.mock("@electron/remote", () => {
|
||||
return {
|
||||
app: {
|
||||
getPath: () => {
|
||||
// avoid TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received undefined
|
||||
return "";
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe("<Catalog />", () => {
|
||||
const history = createMemoryHistory();
|
||||
const mockLocation = {
|
||||
pathname: "",
|
||||
search: "",
|
||||
state: "",
|
||||
hash: "",
|
||||
};
|
||||
const mockMatch = {
|
||||
params: {
|
||||
// will be used to match activeCategory
|
||||
// need to be the same as property values in kubernetesClusterCategory
|
||||
group: "entity.k8slens.dev",
|
||||
kind: "KubernetesCluster",
|
||||
},
|
||||
isExact: true,
|
||||
path: "",
|
||||
url: "",
|
||||
};
|
||||
|
||||
const catalogEntityUid = "a_catalogEntity_uid";
|
||||
const catalogEntity = {
|
||||
enabled: true,
|
||||
apiVersion: "api",
|
||||
kind: "kind",
|
||||
metadata: {
|
||||
uid: catalogEntityUid,
|
||||
name: "a catalog entity",
|
||||
labels: {
|
||||
test: "label",
|
||||
},
|
||||
},
|
||||
status: {
|
||||
phase: "",
|
||||
},
|
||||
spec: {},
|
||||
};
|
||||
const catalogEntityItemMethods = {
|
||||
getId: () => catalogEntity.metadata.uid,
|
||||
getName: () => catalogEntity.metadata.name,
|
||||
onContextMenuOpen: () => {},
|
||||
onSettingsOpen: () => {},
|
||||
onRun: () => {},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
CatalogEntityDetailRegistry.createInstance();
|
||||
// mock the return of getting CatalogCategoryRegistry.filteredItems
|
||||
jest
|
||||
.spyOn(catalogCategoryRegistry, "filteredItems", "get")
|
||||
.mockImplementation(() => {
|
||||
return [kubernetesClusterCategory];
|
||||
});
|
||||
|
||||
// we don't care what this.renderList renders in this test case.
|
||||
jest.spyOn(Catalog.prototype, "renderList").mockImplementation(() => {
|
||||
return <span>empty renderList</span>;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
CatalogEntityDetailRegistry.resetInstance();
|
||||
jest.clearAllMocks();
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("can use catalogEntityRegistry.addOnBeforeRun to add hooks for catalog entities", (done) => {
|
||||
const catalogCategoryRegistry = new CatalogCategoryRegistry();
|
||||
const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry);
|
||||
const onRun = jest.fn();
|
||||
const catalogEntityItem = new CatalogEntityItem({
|
||||
...catalogEntity,
|
||||
...catalogEntityItemMethods,
|
||||
onRun,
|
||||
}, catalogEntityRegistry);
|
||||
|
||||
// mock as if there is a selected item > the detail panel opens
|
||||
jest
|
||||
.spyOn(catalogEntityStore, "selectedItem", "get")
|
||||
.mockImplementation(() => catalogEntityItem);
|
||||
|
||||
catalogEntityRegistry.addOnBeforeRun(
|
||||
catalogEntityUid,
|
||||
(entity) => {
|
||||
expect(entity).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"apiVersion": "api",
|
||||
"enabled": true,
|
||||
"getId": [Function],
|
||||
"getName": [Function],
|
||||
"kind": "kind",
|
||||
"metadata": Object {
|
||||
"labels": Object {
|
||||
"test": "label",
|
||||
},
|
||||
"name": "a catalog entity",
|
||||
"uid": "a_catalogEntity_uid",
|
||||
},
|
||||
"onContextMenuOpen": [Function],
|
||||
"onRun": [MockFunction],
|
||||
"onSettingsOpen": [Function],
|
||||
"spec": Object {},
|
||||
"status": Object {
|
||||
"phase": "",
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(onRun).toHaveBeenCalled();
|
||||
done();
|
||||
}, 500);
|
||||
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
render(
|
||||
<Catalog
|
||||
history={history}
|
||||
location={mockLocation}
|
||||
match={mockMatch}
|
||||
catalogEntityStore={catalogEntityStore}
|
||||
/>
|
||||
);
|
||||
|
||||
userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon"));
|
||||
});
|
||||
|
||||
it("onBeforeRun return false => onRun wont be triggered", (done) => {
|
||||
const catalogCategoryRegistry = new CatalogCategoryRegistry();
|
||||
const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry);
|
||||
const onRun = jest.fn();
|
||||
const catalogEntityItem = new CatalogEntityItem({
|
||||
...catalogEntity,
|
||||
...catalogEntityItemMethods,
|
||||
onRun,
|
||||
}, catalogEntityRegistry);
|
||||
|
||||
// mock as if there is a selected item > the detail panel opens
|
||||
jest
|
||||
.spyOn(catalogEntityStore, "selectedItem", "get")
|
||||
.mockImplementation(() => catalogEntityItem);
|
||||
|
||||
catalogEntityRegistry.addOnBeforeRun(
|
||||
catalogEntityUid,
|
||||
() => {
|
||||
setTimeout(() => {
|
||||
expect(onRun).not.toHaveBeenCalled();
|
||||
done();
|
||||
}, 500);
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
render(
|
||||
<Catalog
|
||||
history={history}
|
||||
location={mockLocation}
|
||||
match={mockMatch}
|
||||
catalogEntityStore={catalogEntityStore}
|
||||
/>
|
||||
);
|
||||
|
||||
userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon"));
|
||||
});
|
||||
|
||||
it("addOnBeforeRun throw an exception => onRun wont be triggered", (done) => {
|
||||
const catalogCategoryRegistry = new CatalogCategoryRegistry();
|
||||
const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry);
|
||||
const onRun = jest.fn();
|
||||
const catalogEntityItem = new CatalogEntityItem({
|
||||
...catalogEntity,
|
||||
...catalogEntityItemMethods,
|
||||
onRun,
|
||||
}, catalogEntityRegistry);
|
||||
|
||||
// mock as if there is a selected item > the detail panel opens
|
||||
jest
|
||||
.spyOn(catalogEntityStore, "selectedItem", "get")
|
||||
.mockImplementation(() => catalogEntityItem);
|
||||
|
||||
catalogEntityRegistry.addOnBeforeRun(
|
||||
catalogEntityUid,
|
||||
() => {
|
||||
setTimeout(() => {
|
||||
expect(onRun).not.toHaveBeenCalled();
|
||||
done();
|
||||
}, 500);
|
||||
|
||||
throw new Error("error!");
|
||||
}
|
||||
);
|
||||
|
||||
render(
|
||||
<Catalog
|
||||
history={history}
|
||||
location={mockLocation}
|
||||
match={mockMatch}
|
||||
catalogEntityStore={catalogEntityStore}
|
||||
/>
|
||||
);
|
||||
|
||||
userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon"));
|
||||
});
|
||||
|
||||
it("addOnRunHook return a promise and resolve true => onRun()", (done) => {
|
||||
const catalogCategoryRegistry = new CatalogCategoryRegistry();
|
||||
const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry);
|
||||
const onRun = jest.fn(() => done());
|
||||
const catalogEntityItem = new CatalogEntityItem({
|
||||
...catalogEntity,
|
||||
...catalogEntityItemMethods,
|
||||
onRun,
|
||||
}, catalogEntityRegistry);
|
||||
|
||||
// mock as if there is a selected item > the detail panel opens
|
||||
jest
|
||||
.spyOn(catalogEntityStore, "selectedItem", "get")
|
||||
.mockImplementation(() => catalogEntityItem);
|
||||
|
||||
catalogEntityRegistry.addOnBeforeRun(
|
||||
catalogEntityUid,
|
||||
async () => {
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
render(
|
||||
<Catalog
|
||||
history={history}
|
||||
location={mockLocation}
|
||||
match={mockMatch}
|
||||
catalogEntityStore={catalogEntityStore}
|
||||
/>
|
||||
);
|
||||
|
||||
userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon"));
|
||||
});
|
||||
|
||||
it("addOnRunHook return a promise and resolve false => onRun() wont be triggered", (done) => {
|
||||
const catalogCategoryRegistry = new CatalogCategoryRegistry();
|
||||
const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry);
|
||||
const onRun = jest.fn();
|
||||
const catalogEntityItem = new CatalogEntityItem({
|
||||
...catalogEntity,
|
||||
...catalogEntityItemMethods,
|
||||
onRun,
|
||||
}, catalogEntityRegistry);
|
||||
|
||||
// mock as if there is a selected item > the detail panel opens
|
||||
jest
|
||||
.spyOn(catalogEntityStore, "selectedItem", "get")
|
||||
.mockImplementation(() => catalogEntityItem);
|
||||
|
||||
catalogEntityRegistry.addOnBeforeRun(
|
||||
catalogEntityUid,
|
||||
async () => {
|
||||
expect(onRun).not.toBeCalled();
|
||||
|
||||
setTimeout(() => {
|
||||
expect(onRun).not.toBeCalled();
|
||||
done();
|
||||
}, 500);
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
render(
|
||||
<Catalog
|
||||
history={history}
|
||||
location={mockLocation}
|
||||
match={mockMatch}
|
||||
catalogEntityStore={catalogEntityStore}
|
||||
/>
|
||||
);
|
||||
|
||||
userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon"));
|
||||
});
|
||||
|
||||
it("addOnRunHook return a promise and reject => onRun wont be triggered", (done) => {
|
||||
const catalogCategoryRegistry = new CatalogCategoryRegistry();
|
||||
const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry);
|
||||
const onRun = jest.fn();
|
||||
const catalogEntityItem = new CatalogEntityItem({
|
||||
...catalogEntity,
|
||||
...catalogEntityItemMethods,
|
||||
onRun,
|
||||
}, catalogEntityRegistry);
|
||||
|
||||
// mock as if there is a selected item > the detail panel opens
|
||||
jest
|
||||
.spyOn(catalogEntityStore, "selectedItem", "get")
|
||||
.mockImplementation(() => catalogEntityItem);
|
||||
|
||||
catalogEntityRegistry.addOnBeforeRun(
|
||||
catalogEntityUid,
|
||||
async () => {
|
||||
setTimeout(() => {
|
||||
expect(onRun).not.toHaveBeenCalled();
|
||||
done();
|
||||
}, 500);
|
||||
|
||||
throw new Error("rejection!");
|
||||
}
|
||||
);
|
||||
|
||||
render(
|
||||
<Catalog
|
||||
history={history}
|
||||
location={mockLocation}
|
||||
match={mockMatch}
|
||||
catalogEntityStore={catalogEntityStore}
|
||||
/>
|
||||
);
|
||||
|
||||
userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon"));
|
||||
});
|
||||
});
|
||||
@@ -25,10 +25,11 @@ 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 { CatalogEntityItem, CatalogEntityStore } from "./catalog-entity.store";
|
||||
import { CatalogEntityStore } from "./catalog-entity.store";
|
||||
import type { CatalogEntityItem } from "./catalog-entity-item";
|
||||
import { navigate } from "../../navigation";
|
||||
import { MenuItem, MenuActions } from "../menu";
|
||||
import { CatalogEntityContextMenu, CatalogEntityContextMenuContext, catalogEntityRunContext } from "../../api/catalog-entity";
|
||||
import type { CatalogEntityContextMenu, CatalogEntityContextMenuContext } from "../../api/catalog-entity";
|
||||
import { HotbarStore } from "../../../common/hotbar-store";
|
||||
import { ConfirmDialog } from "../confirm-dialog";
|
||||
import { catalogCategoryRegistry, CatalogEntity } from "../../../common/catalog";
|
||||
@@ -55,7 +56,10 @@ enum sortBy {
|
||||
|
||||
const css = makeCss(styles);
|
||||
|
||||
interface Props extends RouteComponentProps<CatalogViewRouteParam> {}
|
||||
interface Props extends RouteComponentProps<CatalogViewRouteParam> {
|
||||
catalogEntityStore?: CatalogEntityStore;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class Catalog extends React.Component<Props> {
|
||||
@observable private catalogEntityStore?: CatalogEntityStore;
|
||||
@@ -65,8 +69,11 @@ export class Catalog extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
this.catalogEntityStore = new CatalogEntityStore();
|
||||
this.catalogEntityStore = props.catalogEntityStore;
|
||||
}
|
||||
static defaultProps = {
|
||||
catalogEntityStore: new CatalogEntityStore(),
|
||||
};
|
||||
|
||||
get routeActiveTab(): string {
|
||||
const { group, kind } = this.props.match.params ?? {};
|
||||
@@ -126,7 +133,7 @@ export class Catalog extends React.Component<Props> {
|
||||
if (this.catalogEntityStore.selectedItemId) {
|
||||
this.catalogEntityStore.selectedItemId = null;
|
||||
} else {
|
||||
item.onRun(catalogEntityRunContext);
|
||||
item.onRun();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user