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

Introduce new TreeView for use in CatalogMenu to fix tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2023-02-23 15:34:39 -05:00
parent 8025471eee
commit 0f72c118f2
8 changed files with 140 additions and 85 deletions

View File

@ -16,14 +16,15 @@ import { withInjectables } from "@ogre-tools/injectable-react";
import filteredCategoriesInjectable from "../../../common/catalog/filtered-categories.injectable";
import { TreeGroup, TreeItem, TreeView } from "../tree-view/tree-view";
import { browseCatalogTab } from "./catalog-browse-tab";
import { HorizontalLine } from "../horizontal-line/horizontal-line";
export interface CatalogMenuProps {
activeTab: string | undefined;
onItemClick: (id: string) => void;
}
function getCategoryIcon(category: CatalogCategory) {
const { icon } = category.metadata ?? {};
function CategoryIcon(props: { category: CatalogCategory }) {
const { icon } = props.category.metadata ?? {};
if (typeof icon === "string") {
return Icon.isSvg(icon)
@ -42,41 +43,38 @@ const NonInjectedCatalogMenu = observer(({
activeTab,
filteredCategories,
onItemClick,
}: CatalogMenuProps & Dependencies) => {
console.log(treeStyles);
return (
<div className="flex flex-col w-full">
<div className={styles.catalog}>Catalog</div>
<TreeView>
<TreeItem
label="Browse"
data-testid="*-tab"
onClick={() => onItemClick("*")}
selected={activeTab === browseCatalogTab} />
<TreeGroup
classes={treeStyles}
label={<div className={styles.parent}>Categories</div>}
>
{filteredCategories.get()
.map(category => (
<TreeItem
key={category.getId()}
label={(
<>
{getCategoryIcon(category)}
<CatalogCategoryLabel category={category} />
</>
)}
selected={activeTab === category.getId()}
data-testid={`${category.getId()}-tab`}
onClick={() => onItemClick(category.getId())} />
))}
</TreeGroup>
</TreeView>
</div>
);
});
}: CatalogMenuProps & Dependencies) => (
<div className="flex flex-col w-full">
<div className={styles.catalog}>Catalog</div>
<TreeView>
<TreeItem
classes={treeStyles}
label="Browse"
data-testid="*-tab"
onClick={() => onItemClick("*")}
selected={activeTab === browseCatalogTab}
/>
<HorizontalLine size="xxs" />
<TreeGroup
classes={treeStyles}
label={<div className={styles.parent}>Categories</div>}
>
{filteredCategories.get()
.map(category => (
<TreeItem
classes={treeStyles}
key={category.getId()}
icon={<CategoryIcon category={category} />}
label={<CatalogCategoryLabel category={category} />}
selected={activeTab === category.getId()}
data-testid={`${category.getId()}-tab`}
onClick={() => onItemClick(category.getId())}
/>
))}
</TreeGroup>
</TreeView>
</div>
));
export const CatalogMenu = withInjectables<Dependencies, CatalogMenuProps>(NonInjectedCatalogMenu, {
getProps: (di, props) => ({

View File

@ -23,39 +23,8 @@
.content {
min-height: 26px;
line-height: 1.3;
padding: 2px var(--padding) 2px 0;
&:hover {
background-color: var(--sidebarItemHoverBackground);
border-radius: 2px;
}
&:active {
color: white;
background-color: var(--blue);
}
}
.group {
margin-left: 0px;
.iconContainer {
margin-left: 28px;
margin-top: 2px;
align-self: flex-start;
}
}
.selected {
& > *:first-child {
background-color: var(--blue);
color: white;
border-radius: 2px;
}
}
.iconContainer {
width: 21px;
margin-left: 5px;
margin-right: 0;
}

View File

@ -177,7 +177,6 @@ class NonInjectedCatalog extends React.Component<Dependencies> {
}
onTabChange = action((tabId: string | null) => {
console.log(tabId);
const activeCategory = this.categories.find(category => category.getId() === tabId);
this.props.emitEvent({

View File

@ -20,6 +20,9 @@
$baseline: 8px;
@include horizontalLineSize('xxs', 0.5 * $baseline);
@include horizontalLineSize('xs', 1 * $baseline);
@include horizontalLineSize('sm', 2 * $baseline);
@include horizontalLineSize('md', 3 * $baseline);
@include horizontalLineSize('lg', 4 * $baseline);
@include horizontalLineSize('xl', 5 * $baseline);

View File

@ -7,7 +7,7 @@ import styles from "./horizontal-line.module.scss";
import { cssNames } from "../../utils";
interface HorizontalLineProps {
size?: "sm" | "md" | "xl";
size?: "xxs" | "xs" | "sm" | "md" | "lg" | "xl";
}
export const HorizontalLine = ({ size = "xl" }: HorizontalLineProps = { size: "xl" }) => {

View File

@ -0,0 +1,63 @@
.treeItem {
display: flex;
flex-direction: row;
padding: 2px var(--padding) 2px 0;
cursor: pointer;
&:hover {
background-color: var(--sidebarItemHoverBackground);
}
&.selected:hover {
background-color: var(--blue);
}
}
.selected {
background-color: var(--blue);
color: white;
border-radius: 2px;
width: 100%;
}
.treeGroup {
display: flex;
flex-direction: column;
cursor: pointer;
}
.contents {
padding-left: 25px;
transition: all 300ms ease;
overflow: hidden;
&.expanded {
max-height: 100%;
}
&:not(.expanded) {
max-height: 0;
}
}
.selected {
color: white;
background-color: var(--blue);
}
.group {
display: flex;
flex-direction: row;
}
.iconContainer {
align-self: flex-start;
width: 21px;
margin-left: 5px;
margin-right: 5px;
}
.treeView {
display: flex;
flex-direction: column;
}

View File

@ -3,6 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import styles from "./tree-view.module.scss";
import type { MouseEventHandler } from "react";
import React, { useState } from "react";
import { cssNames } from "../../utils";
@ -20,7 +21,7 @@ export interface TreeViewProps {
export function TreeView(props: TreeViewProps) {
return (
<ul
className={props.classes?.root}
className={cssNames(props.classes?.root, styles.treeView)}
role="tree"
>
{props.children}
@ -31,10 +32,14 @@ export function TreeView(props: TreeViewProps) {
export interface TreeItemClasses {
root?: string;
label?: string;
selected?: string;
hover?: string;
iconContainer?: string;
}
export interface TreeItemProps {
classes?: TreeItemClasses;
icon?: JSX.Element;
label: JSX.Element | string;
testId?: string;
selected?: boolean;
@ -42,15 +47,31 @@ export interface TreeItemProps {
}
export function TreeItem(props: TreeItemProps) {
const [hovering, setHovering] = useState(false);
const optionalCssNames: Partial<Record<string, any>> = {};
if (props.classes?.selected) {
optionalCssNames[props.classes.selected] = props.selected ?? false;
}
if (props.classes?.hover) {
optionalCssNames[props.classes.hover] = hovering;
}
return (
<li
className={cssNames(props.classes?.root, {
selected: props.selected ?? false,
className={cssNames(props.classes?.root, optionalCssNames, styles.treeItem, {
[styles.selected]: props.selected ?? false,
})}
role="treeitem"
data-testid={props.testId}
onClick={props.onClick}
onMouseOver={() => setHovering(true)}
onMouseLeave={() => setHovering(false)}
>
<div className={cssNames(props.classes?.iconContainer, styles.iconContainer)}>
{props.icon}
</div>
<div className={props.classes?.label}>
{props.label}
</div>
@ -60,7 +81,7 @@ export function TreeItem(props: TreeItemProps) {
export interface TreeGroupClasses {
root?: string;
header?: string;
group?: string;
iconContainer?: string;
label?: string;
contents?: string;
@ -81,12 +102,15 @@ export function TreeGroup(props: TreeGroupProps) {
return (
<li
className={props.classes?.root}
className={cssNames(props.classes?.root, styles.treeGroup)}
role="group"
data-testid={props.testId}
>
<div className={props.classes?.header} onClick={() => setExpanded(!expanded)}>
<div className={props.classes?.iconContainer}>
<div
className={cssNames(props.classes?.group, styles.group)}
onClick={() => setExpanded(!expanded)}
>
<div className={cssNames(props.classes?.iconContainer, styles.iconContainer)}>
{
expanded
? props.collapseIcon ?? <Icon material="expand_more" />
@ -97,13 +121,13 @@ export function TreeGroup(props: TreeGroupProps) {
{props.label}
</div>
</div>
<div className={props.classes?.contents}>
{
expanded
? props.children
: null
}
</div>
<ul
className={cssNames(props.classes?.contents, styles.contents, {
[styles.expanded]: expanded,
})}
>
{props.children}
</ul>
</li>
);
}

View File

@ -21,7 +21,6 @@
"skipLibCheck": true,
"allowJs": false,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"importsNotUsedAsValues": "error",
"traceResolution": false,
"resolveJsonModule": true,