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:
parent
8025471eee
commit
0f72c118f2
@ -16,14 +16,15 @@ import { withInjectables } from "@ogre-tools/injectable-react";
|
|||||||
import filteredCategoriesInjectable from "../../../common/catalog/filtered-categories.injectable";
|
import filteredCategoriesInjectable from "../../../common/catalog/filtered-categories.injectable";
|
||||||
import { TreeGroup, TreeItem, TreeView } from "../tree-view/tree-view";
|
import { TreeGroup, TreeItem, TreeView } from "../tree-view/tree-view";
|
||||||
import { browseCatalogTab } from "./catalog-browse-tab";
|
import { browseCatalogTab } from "./catalog-browse-tab";
|
||||||
|
import { HorizontalLine } from "../horizontal-line/horizontal-line";
|
||||||
|
|
||||||
export interface CatalogMenuProps {
|
export interface CatalogMenuProps {
|
||||||
activeTab: string | undefined;
|
activeTab: string | undefined;
|
||||||
onItemClick: (id: string) => void;
|
onItemClick: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCategoryIcon(category: CatalogCategory) {
|
function CategoryIcon(props: { category: CatalogCategory }) {
|
||||||
const { icon } = category.metadata ?? {};
|
const { icon } = props.category.metadata ?? {};
|
||||||
|
|
||||||
if (typeof icon === "string") {
|
if (typeof icon === "string") {
|
||||||
return Icon.isSvg(icon)
|
return Icon.isSvg(icon)
|
||||||
@ -42,41 +43,38 @@ const NonInjectedCatalogMenu = observer(({
|
|||||||
activeTab,
|
activeTab,
|
||||||
filteredCategories,
|
filteredCategories,
|
||||||
onItemClick,
|
onItemClick,
|
||||||
}: CatalogMenuProps & Dependencies) => {
|
}: CatalogMenuProps & Dependencies) => (
|
||||||
console.log(treeStyles);
|
<div className="flex flex-col w-full">
|
||||||
|
<div className={styles.catalog}>Catalog</div>
|
||||||
return (
|
<TreeView>
|
||||||
<div className="flex flex-col w-full">
|
<TreeItem
|
||||||
<div className={styles.catalog}>Catalog</div>
|
classes={treeStyles}
|
||||||
<TreeView>
|
label="Browse"
|
||||||
<TreeItem
|
data-testid="*-tab"
|
||||||
label="Browse"
|
onClick={() => onItemClick("*")}
|
||||||
data-testid="*-tab"
|
selected={activeTab === browseCatalogTab}
|
||||||
onClick={() => onItemClick("*")}
|
/>
|
||||||
selected={activeTab === browseCatalogTab} />
|
<HorizontalLine size="xxs" />
|
||||||
<TreeGroup
|
<TreeGroup
|
||||||
classes={treeStyles}
|
classes={treeStyles}
|
||||||
label={<div className={styles.parent}>Categories</div>}
|
label={<div className={styles.parent}>Categories</div>}
|
||||||
>
|
>
|
||||||
{filteredCategories.get()
|
{filteredCategories.get()
|
||||||
.map(category => (
|
.map(category => (
|
||||||
<TreeItem
|
<TreeItem
|
||||||
key={category.getId()}
|
classes={treeStyles}
|
||||||
label={(
|
key={category.getId()}
|
||||||
<>
|
icon={<CategoryIcon category={category} />}
|
||||||
{getCategoryIcon(category)}
|
label={<CatalogCategoryLabel category={category} />}
|
||||||
<CatalogCategoryLabel category={category} />
|
selected={activeTab === category.getId()}
|
||||||
</>
|
data-testid={`${category.getId()}-tab`}
|
||||||
)}
|
onClick={() => onItemClick(category.getId())}
|
||||||
selected={activeTab === category.getId()}
|
/>
|
||||||
data-testid={`${category.getId()}-tab`}
|
))}
|
||||||
onClick={() => onItemClick(category.getId())} />
|
</TreeGroup>
|
||||||
))}
|
</TreeView>
|
||||||
</TreeGroup>
|
</div>
|
||||||
</TreeView>
|
));
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export const CatalogMenu = withInjectables<Dependencies, CatalogMenuProps>(NonInjectedCatalogMenu, {
|
export const CatalogMenu = withInjectables<Dependencies, CatalogMenuProps>(NonInjectedCatalogMenu, {
|
||||||
getProps: (di, props) => ({
|
getProps: (di, props) => ({
|
||||||
|
|||||||
@ -23,39 +23,8 @@
|
|||||||
.content {
|
.content {
|
||||||
min-height: 26px;
|
min-height: 26px;
|
||||||
line-height: 1.3;
|
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 {
|
.group {
|
||||||
margin-left: 0px;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -177,7 +177,6 @@ class NonInjectedCatalog extends React.Component<Dependencies> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onTabChange = action((tabId: string | null) => {
|
onTabChange = action((tabId: string | null) => {
|
||||||
console.log(tabId);
|
|
||||||
const activeCategory = this.categories.find(category => category.getId() === tabId);
|
const activeCategory = this.categories.find(category => category.getId() === tabId);
|
||||||
|
|
||||||
this.props.emitEvent({
|
this.props.emitEvent({
|
||||||
|
|||||||
@ -20,6 +20,9 @@
|
|||||||
|
|
||||||
$baseline: 8px;
|
$baseline: 8px;
|
||||||
|
|
||||||
|
@include horizontalLineSize('xxs', 0.5 * $baseline);
|
||||||
|
@include horizontalLineSize('xs', 1 * $baseline);
|
||||||
@include horizontalLineSize('sm', 2 * $baseline);
|
@include horizontalLineSize('sm', 2 * $baseline);
|
||||||
@include horizontalLineSize('md', 3 * $baseline);
|
@include horizontalLineSize('md', 3 * $baseline);
|
||||||
|
@include horizontalLineSize('lg', 4 * $baseline);
|
||||||
@include horizontalLineSize('xl', 5 * $baseline);
|
@include horizontalLineSize('xl', 5 * $baseline);
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import styles from "./horizontal-line.module.scss";
|
|||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
|
|
||||||
interface HorizontalLineProps {
|
interface HorizontalLineProps {
|
||||||
size?: "sm" | "md" | "xl";
|
size?: "xxs" | "xs" | "sm" | "md" | "lg" | "xl";
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HorizontalLine = ({ size = "xl" }: HorizontalLineProps = { size: "xl" }) => {
|
export const HorizontalLine = ({ size = "xl" }: HorizontalLineProps = { size: "xl" }) => {
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* 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 type { MouseEventHandler } from "react";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
@ -20,7 +21,7 @@ export interface TreeViewProps {
|
|||||||
export function TreeView(props: TreeViewProps) {
|
export function TreeView(props: TreeViewProps) {
|
||||||
return (
|
return (
|
||||||
<ul
|
<ul
|
||||||
className={props.classes?.root}
|
className={cssNames(props.classes?.root, styles.treeView)}
|
||||||
role="tree"
|
role="tree"
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
@ -31,10 +32,14 @@ export function TreeView(props: TreeViewProps) {
|
|||||||
export interface TreeItemClasses {
|
export interface TreeItemClasses {
|
||||||
root?: string;
|
root?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
|
selected?: string;
|
||||||
|
hover?: string;
|
||||||
|
iconContainer?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TreeItemProps {
|
export interface TreeItemProps {
|
||||||
classes?: TreeItemClasses;
|
classes?: TreeItemClasses;
|
||||||
|
icon?: JSX.Element;
|
||||||
label: JSX.Element | string;
|
label: JSX.Element | string;
|
||||||
testId?: string;
|
testId?: string;
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
@ -42,15 +47,31 @@ export interface TreeItemProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function TreeItem(props: 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 (
|
return (
|
||||||
<li
|
<li
|
||||||
className={cssNames(props.classes?.root, {
|
className={cssNames(props.classes?.root, optionalCssNames, styles.treeItem, {
|
||||||
selected: props.selected ?? false,
|
[styles.selected]: props.selected ?? false,
|
||||||
})}
|
})}
|
||||||
role="treeitem"
|
role="treeitem"
|
||||||
data-testid={props.testId}
|
data-testid={props.testId}
|
||||||
onClick={props.onClick}
|
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}>
|
<div className={props.classes?.label}>
|
||||||
{props.label}
|
{props.label}
|
||||||
</div>
|
</div>
|
||||||
@ -60,7 +81,7 @@ export function TreeItem(props: TreeItemProps) {
|
|||||||
|
|
||||||
export interface TreeGroupClasses {
|
export interface TreeGroupClasses {
|
||||||
root?: string;
|
root?: string;
|
||||||
header?: string;
|
group?: string;
|
||||||
iconContainer?: string;
|
iconContainer?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
contents?: string;
|
contents?: string;
|
||||||
@ -81,12 +102,15 @@ export function TreeGroup(props: TreeGroupProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
className={props.classes?.root}
|
className={cssNames(props.classes?.root, styles.treeGroup)}
|
||||||
role="group"
|
role="group"
|
||||||
data-testid={props.testId}
|
data-testid={props.testId}
|
||||||
>
|
>
|
||||||
<div className={props.classes?.header} onClick={() => setExpanded(!expanded)}>
|
<div
|
||||||
<div className={props.classes?.iconContainer}>
|
className={cssNames(props.classes?.group, styles.group)}
|
||||||
|
onClick={() => setExpanded(!expanded)}
|
||||||
|
>
|
||||||
|
<div className={cssNames(props.classes?.iconContainer, styles.iconContainer)}>
|
||||||
{
|
{
|
||||||
expanded
|
expanded
|
||||||
? props.collapseIcon ?? <Icon material="expand_more" />
|
? props.collapseIcon ?? <Icon material="expand_more" />
|
||||||
@ -97,13 +121,13 @@ export function TreeGroup(props: TreeGroupProps) {
|
|||||||
{props.label}
|
{props.label}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={props.classes?.contents}>
|
<ul
|
||||||
{
|
className={cssNames(props.classes?.contents, styles.contents, {
|
||||||
expanded
|
[styles.expanded]: expanded,
|
||||||
? props.children
|
})}
|
||||||
: null
|
>
|
||||||
}
|
{props.children}
|
||||||
</div>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,6 @@
|
|||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"allowJs": false,
|
"allowJs": false,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"importsNotUsedAsValues": "error",
|
"importsNotUsedAsValues": "error",
|
||||||
"traceResolution": false,
|
"traceResolution": false,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user