Remove unnecessary CatalogEntityItem (#4582)

* Remove unnecessary CatalogEntityItem

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Using simple notation to set search url params

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Fix styles file name

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

Co-authored-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
Sebastian MaltonandAlex Andreev authored and GitHub committed 2021-12-24 13:49:34 +03:00
1 parent 2d279a6b99
commit 8082501bb3
8 files changed
+219 -110

No files matched your search

@@ -27,14 +27,15 @@ import type { CatalogCategory, CatalogEntity } from "../../../common/catalog";
import { Icon } from "../icon";
import { CatalogEntityDrawerMenu } from "./catalog-entity-drawer-menu";
import { CatalogEntityDetailRegistry } from "../../../extensions/registries";
import type { CatalogEntityItem } from "./catalog-entity-item";
import { isDevelopment } from "../../../common/vars";
import { cssNames } from "../../utils";
import { Avatar } from "../avatar";
import { getLabelBadges } from "./helpers";
interface Props<T extends CatalogEntity> {
item: CatalogEntityItem<T> | null | undefined;
entity: T;
hideDetails(): void;
onRun: () => void;
}
@observer
@@ -47,32 +48,30 @@ export class CatalogEntityDetails<T extends CatalogEntity> extends Component<Pro
}
}
renderContent(item: CatalogEntityItem<T>) {
const detailItems = CatalogEntityDetailRegistry.getInstance().getItemsForKind(item.kind, item.apiVersion);
const details = detailItems.map(({ components }, index) => {
return <components.Details entity={item.entity} key={index}/>;
});
const showDetails = detailItems.find((item) => item.priority > 999) === undefined;
renderContent(entity: T) {
const { onRun, hideDetails } = this.props;
const detailItems = CatalogEntityDetailRegistry.getInstance().getItemsForKind(entity.kind, entity.apiVersion);
const details = detailItems.map(({ components }, index) => <components.Details entity={entity} key={index} />);
const showDefaultDetails = detailItems.find((item) => item.priority > 999) === undefined;
return (
<>
{showDetails && (
{showDefaultDetails && (
<div className="flex">
<div className={styles.entityIcon}>
<Avatar
title={item.name}
colorHash={`${item.name}-${item.source}`}
title={entity.getName()}
colorHash={`${entity.getName()}-${entity.getSource()}`}
size={128}
src={item.entity.spec.icon?.src}
src={entity.spec.icon?.src}
data-testid="detail-panel-hot-bar-icon"
background={item.entity.spec.icon?.background}
onClick={() => item.onRun()}
background={entity.spec.icon?.background}
onClick={onRun}
className={styles.avatar}
>
{item.entity.spec.icon?.material && <Icon material={item.entity.spec.icon?.material}/>}
{entity.spec.icon?.material && <Icon material={entity.spec.icon?.material}/>}
</Avatar>
{item?.enabled && (
{entity.isEnabled() && (
<div className={styles.hint}>
Click to open
</div>
@@ -80,23 +79,23 @@ export class CatalogEntityDetails<T extends CatalogEntity> extends Component<Pro
</div>
<div className={cssNames("box grow", styles.metadata)}>
<DrawerItem name="Name">
{item.name}
{entity.getName()}
</DrawerItem>
<DrawerItem name="Kind">
{item.kind}
{entity.kind}
</DrawerItem>
<DrawerItem name="Source">
{item.source}
{entity.getSource()}
</DrawerItem>
<DrawerItem name="Status">
{item.phase}
{entity.status.phase}
</DrawerItem>
<DrawerItem name="Labels">
{...item.getLabelBadges(this.props.hideDetails)}
{getLabelBadges(entity, hideDetails)}
</DrawerItem>
{isDevelopment && (
<DrawerItem name="Id">
{item.getId()}
{entity.getId()}
</DrawerItem>
)}
</div>
@@ -110,19 +109,18 @@ export class CatalogEntityDetails<T extends CatalogEntity> extends Component<Pro
}
render() {
const { item, hideDetails } = this.props;
const title = `${item.kind}: ${item.name}`;
const { entity, hideDetails } = this.props;
return (
<Drawer
className={styles.entityDetails}
usePortal={true}
open={true}
title={title}
toolbar={<CatalogEntityDrawerMenu item={item} key={item.getId()} />}
title={`${entity.kind}: ${entity.getName()}`}
toolbar={<CatalogEntityDrawerMenu entity={entity} key={entity.getId()} />}
onClose={hideDetails}
>
{item && this.renderContent(item)}
{this.renderContent(entity)}
</Drawer>
);
}
@@ -29,11 +29,10 @@ import { navigate } from "../../navigation";
import { MenuItem } from "../menu";
import { ConfirmDialog } from "../confirm-dialog";
import { Icon } from "../icon";
import type { CatalogEntityItem } from "./catalog-entity-item";
import { HotbarToggleMenuItem } from "./hotbar-toggle-menu-item";
export interface CatalogEntityDrawerMenuProps<T extends CatalogEntity> extends MenuActionsProps {
item: CatalogEntityItem<T> | null | undefined;
entity: T;
}
@observer
@@ -50,7 +49,7 @@ export class CatalogEntityDrawerMenu<T extends CatalogEntity> extends React.Comp
menuItems: [],
navigate: (url: string) => navigate(url),
};
this.props.item?.onContextMenuOpen(this.contextMenu);
this.props.entity?.onContextMenuOpen(this.contextMenu);
}
onMenuItemClick(menuItem: CatalogEntityContextMenu) {
@@ -108,9 +107,9 @@ export class CatalogEntityDrawerMenu<T extends CatalogEntity> extends React.Comp
}
render() {
const { className, item: entity, ...menuProps } = this.props;
const { className, entity, ...menuProps } = this.props;
if (!this.contextMenu || !entity.enabled) {
if (!this.contextMenu || !entity.isEnabled()) {
return null;
}
@@ -120,7 +119,7 @@ export class CatalogEntityDrawerMenu<T extends CatalogEntity> extends React.Comp
toolbar
{...menuProps}
>
{this.getMenuItems(entity.entity)}
{this.getMenuItems(entity)}
</MenuActions>
);
}
@@ -25,9 +25,8 @@ import type { CatalogEntity } from "../../api/catalog-entity";
import { ItemStore } from "../../../common/item.store";
import { CatalogCategory, catalogCategoryRegistry } from "../../../common/catalog";
import { autoBind, disposer } from "../../../common/utils";
import { CatalogEntityItem } from "./catalog-entity-item";
export class CatalogEntityStore extends ItemStore<CatalogEntityItem<CatalogEntity>> {
export class CatalogEntityStore extends ItemStore<CatalogEntity> {
constructor(private registry: CatalogEntityRegistry = catalogEntityRegistry) {
super();
makeObservable(this);
@@ -39,10 +38,10 @@ export class CatalogEntityStore extends ItemStore<CatalogEntityItem<CatalogEntit
@computed get entities() {
if (!this.activeCategory) {
return this.registry.filteredItems.map(entity => new CatalogEntityItem(entity, this.registry));
return this.registry.filteredItems;
}
return this.registry.getItemsForCategory(this.activeCategory, { filtered: true }).map(entity => new CatalogEntityItem(entity, this.registry));
return this.registry.getItemsForCategory(this.activeCategory, { filtered: true });
}
@computed get selectedItem() {
@@ -68,4 +67,8 @@ export class CatalogEntityStore extends ItemStore<CatalogEntityItem<CatalogEntit
// concurrency is true to fix bug if catalog filter is removed and added at the same time
return this.loadItems(() => this.entities, undefined, true);
}
onRun(entity: CatalogEntity): void {
this.registry.onRun(entity);
}
}
@@ -29,7 +29,6 @@ import { kubernetesClusterCategory } from "../../../common/catalog-entities/kube
import { catalogCategoryRegistry, CatalogCategoryRegistry, CatalogEntity, CatalogEntityActionContext, CatalogEntityData } 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";
import { AppPaths } from "../../../common/app-paths";
@@ -130,7 +129,7 @@ describe("<Catalog />", () => {
const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry);
const onRun = jest.fn();
const catalogEntityItem = new CatalogEntityItem(createMockCatalogEntity(onRun), catalogEntityRegistry);
const catalogEntityItem = createMockCatalogEntity(onRun);
// mock as if there is a selected item > the detail panel opens
jest
@@ -166,7 +165,7 @@ describe("<Catalog />", () => {
const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry);
const onRun = jest.fn();
const catalogEntityItem = new CatalogEntityItem(createMockCatalogEntity(onRun), catalogEntityRegistry);
const catalogEntityItem = createMockCatalogEntity(onRun);
// mock as if there is a selected item > the detail panel opens
jest
@@ -200,7 +199,7 @@ describe("<Catalog />", () => {
const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry);
const onRun = jest.fn();
const catalogEntityItem = new CatalogEntityItem(createMockCatalogEntity(onRun), catalogEntityRegistry);
const catalogEntityItem = createMockCatalogEntity(onRun);
// mock as if there is a selected item > the detail panel opens
jest
@@ -235,7 +234,7 @@ describe("<Catalog />", () => {
const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry);
const onRun = jest.fn(() => done());
const catalogEntityItem = new CatalogEntityItem(createMockCatalogEntity(onRun), catalogEntityRegistry);
const catalogEntityItem = createMockCatalogEntity(onRun);
// mock as if there is a selected item > the detail panel opens
jest
@@ -265,7 +264,7 @@ describe("<Catalog />", () => {
const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry);
const onRun = jest.fn();
const catalogEntityItem = new CatalogEntityItem(createMockCatalogEntity(onRun), catalogEntityRegistry);
const catalogEntityItem = createMockCatalogEntity(onRun);
// mock as if there is a selected item > the detail panel opens
jest
@@ -302,7 +301,7 @@ describe("<Catalog />", () => {
const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry);
const onRun = jest.fn();
const catalogEntityItem = new CatalogEntityItem(createMockCatalogEntity(onRun), catalogEntityRegistry);
const catalogEntityItem = createMockCatalogEntity(onRun);
// mock as if there is a selected item > the detail panel opens
jest
+45 -35
View File
@@ -26,7 +26,6 @@ import { disposeOnUnmount, observer } from "mobx-react";
import { ItemListLayout } from "../item-object-list";
import { action, makeObservable, observable, reaction, runInAction, when } from "mobx";
import { CatalogEntityStore } from "./catalog-entity.store";
import type { CatalogEntityItem } from "./catalog-entity-item";
import { navigate } from "../../navigation";
import { MenuItem, MenuActions } from "../menu";
import type { CatalogEntityContextMenu, CatalogEntityContextMenuContext } from "../../api/catalog-entity";
@@ -45,6 +44,8 @@ import { RenderDelay } from "../render-delay/render-delay";
import { Icon } from "../icon";
import { HotbarToggleMenuItem } from "./hotbar-toggle-menu-item";
import { Avatar } from "../avatar";
import { KubeObject } from "../../../common/k8s-api/kube-object";
import { getLabelBadges } from "./helpers";
export const previousActiveTab = createStorage("catalog-previous-active-tab", browseCatalogTab);
@@ -125,19 +126,19 @@ export class Catalog extends React.Component<Props> {
}));
}
addToHotbar(item: CatalogEntityItem<CatalogEntity>): void {
HotbarStore.getInstance().addToHotbar(item.entity);
addToHotbar(entity: CatalogEntity): void {
HotbarStore.getInstance().addToHotbar(entity);
}
removeFromHotbar(item: CatalogEntityItem<CatalogEntity>): void {
HotbarStore.getInstance().removeFromHotbar(item.getId());
removeFromHotbar(entity: CatalogEntity): void {
HotbarStore.getInstance().removeFromHotbar(entity.getId());
}
onDetails = (item: CatalogEntityItem<CatalogEntity>) => {
onDetails = (entity: CatalogEntity) => {
if (this.catalogEntityStore.selectedItemId) {
this.catalogEntityStore.selectedItemId = null;
} else {
item.onRun();
this.catalogEntityStore.onRun(entity);
}
};
@@ -179,16 +180,16 @@ export class Catalog extends React.Component<Props> {
);
}
renderItemMenu = (item: CatalogEntityItem<CatalogEntity>) => {
renderItemMenu = (entity: CatalogEntity) => {
const onOpen = () => {
this.contextMenu.menuItems = [];
item.onContextMenuOpen(this.contextMenu);
entity.onContextMenuOpen(this.contextMenu);
};
return (
<MenuActions onOpen={onOpen}>
<MenuItem key="open-details" onClick={() => this.catalogEntityStore.selectedItemId = item.getId()}>
<MenuItem key="open-details" onClick={() => this.catalogEntityStore.selectedItemId = entity.getId()}>
View Details
</MenuItem>
{
@@ -200,7 +201,7 @@ export class Catalog extends React.Component<Props> {
}
<HotbarToggleMenuItem
key="hotbar-toggle"
entity={item.entity}
entity={entity}
addContent="Add to Hotbar"
removeContent="Remove from Hotbar"
/>
@@ -208,29 +209,29 @@ export class Catalog extends React.Component<Props> {
);
};
renderName(item: CatalogEntityItem<CatalogEntity>) {
const isItemInHotbar = HotbarStore.getInstance().isAddedToActive(item.entity);
renderName(entity: CatalogEntity) {
const isItemInHotbar = HotbarStore.getInstance().isAddedToActive(entity);
return (
<>
<Avatar
title={item.getName()}
colorHash={`${item.getName()}-${item.source}`}
src={item.entity.spec.icon?.src}
background={item.entity.spec.icon?.background}
title={entity.getName()}
colorHash={`${entity.getName()}-${entity.getSource()}`}
src={entity.spec.icon?.src}
background={entity.spec.icon?.background}
className={styles.catalogAvatar}
size={24}
>
{item.entity.spec.icon?.material && <Icon material={item.entity.spec.icon?.material} small/>}
{entity.spec.icon?.material && <Icon material={entity.spec.icon?.material} small/>}
</Avatar>
<span>{item.name}</span>
<span>{entity.getName()}</span>
<Icon
small
className={styles.pinIcon}
material={!isItemInHotbar && "push_pin"}
svg={isItemInHotbar ? "push_off" : "push_pin"}
tooltip={isItemInHotbar ? "Remove from Hotbar" : "Add to Hotbar"}
onClick={prevDefault(() => isItemInHotbar ? this.removeFromHotbar(item) : this.addToHotbar(item))}
onClick={prevDefault(() => isItemInHotbar ? this.removeFromHotbar(entity) : this.addToHotbar(entity))}
/>
</>
);
@@ -253,13 +254,19 @@ export class Catalog extends React.Component<Props> {
isConfigurable={true}
store={this.catalogEntityStore}
sortingCallbacks={{
[sortBy.name]: item => item.name,
[sortBy.source]: item => item.source,
[sortBy.status]: item => item.phase,
[sortBy.kind]: item => item.kind,
[sortBy.name]: entity => entity.getName(),
[sortBy.source]: entity => entity.getSource(),
[sortBy.status]: entity => entity.status.phase,
[sortBy.kind]: entity => entity.kind,
}}
searchFilters={[
entity => entity.searchFields,
entity => [
entity.getName(),
entity.getId(),
entity.status.phase,
`source=${entity.getSource()}`,
...KubeObject.stringifyLabels(entity.metadata.labels),
],
]}
renderTableHeader={[
{ title: "Name", className: styles.entityName, sortBy: sortBy.name, id: "name" },
@@ -268,15 +275,15 @@ export class Catalog extends React.Component<Props> {
{ title: "Labels", className: `${styles.labelsCell} scrollable`, id: "labels" },
{ title: "Status", className: styles.statusCell, sortBy: sortBy.status, id: "status" },
].filter(Boolean)}
customizeTableRowProps={item => ({
disabled: !item.enabled,
customizeTableRowProps={entity => ({
disabled: !entity.isEnabled(),
})}
renderTableContents={item => [
this.renderName(item),
!activeCategory && item.kind,
item.source,
item.getLabelBadges(),
<span key="phase" className={item.phase}>{item.phase}</span>,
renderTableContents={entity => [
this.renderName(entity),
!activeCategory && entity.kind,
entity.getSource(),
getLabelBadges(entity),
<span key="phase" className={entity.status.phase}>{entity.status.phase}</span>,
].filter(Boolean)}
onDetails={this.onDetails}
renderItemMenu={this.renderItemMenu}
@@ -289,16 +296,19 @@ export class Catalog extends React.Component<Props> {
return null;
}
const selectedEntity = this.catalogEntityStore.selectedItem;
return (
<MainLayout sidebar={this.renderNavigation()}>
<div className="p-6 h-full">
{this.renderList()}
</div>
{
this.catalogEntityStore.selectedItem
selectedEntity
? <CatalogEntityDetails
item={this.catalogEntityStore.selectedItem}
entity={selectedEntity}
hideDetails={() => this.catalogEntityStore.selectedItemId = null}
onRun={() => this.catalogEntityStore.onRun(selectedEntity)}
/>
: (
<RenderDelay>
@@ -0,0 +1,49 @@
/**
* 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.scss";
import React from "react";
import { KubeObject } from "../../../common/k8s-api/kube-object";
import type { CatalogEntity } from "../../api/catalog-entity";
import { Badge } from "../badge";
import { searchUrlParam } from "../input";
/**
* @param entity The entity to render badge labels for
*/
export function getLabelBadges(entity: CatalogEntity, onClick?: (evt: React.MouseEvent<any, MouseEvent>) => void) {
return KubeObject.stringifyLabels(entity.metadata.labels)
.map(label => (
<Badge
scrollable
className={styles.badge}
key={label}
label={label}
title={label}
onClick={(event) => {
searchUrlParam.set(label);
onClick?.(event);
event.stopPropagation();
}}
expandable={false}
/>
));
}