Skip to content

Commit

Permalink
fix(react-components): TreeView: Add info icon for clicking for furth…
Browse files Browse the repository at this point in the history
…er info. Also add support for long names. (#4847)

* Add info

* XFix treenode

* Update TreeView.stories.tsx

* Add Generics

* Update parent
  • Loading branch information
nilscognite authored Nov 5, 2024
1 parent 7a1313b commit ec92a1b
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 43 deletions.
2 changes: 1 addition & 1 deletion react-components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cognite/reveal-react-components",
"version": "0.65.1",
"version": "0.65.2",
"exports": {
".": {
"import": "./dist/index.js",
Expand Down
52 changes: 29 additions & 23 deletions react-components/src/architecture/base/treeView/TreeNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { type IconName } from '../utilities/IconName';
import { type ITreeNode } from './ITreeNode';
import { CheckBoxState, type TreeNodeAction, type IconColor, type LoadNodesAction } from './types';

export class TreeNode implements ITreeNode {
export class TreeNode<T = any> implements ITreeNode {
// ==================================================
// INSTANCE FIELDS
// ==================================================
Expand All @@ -24,12 +24,13 @@ export class TreeNode implements ITreeNode {
private _isLoadingSiblings: boolean = false;
private _needLoadChildren = false;
private _needLoadSiblings = false;
public userData: T | undefined = undefined;

protected _children: TreeNode[] | undefined = undefined;
protected _parent: TreeNode | undefined = undefined;
protected _children: Array<TreeNode<T>> | undefined = undefined;
protected _parent: TreeNode<T> | undefined = undefined;

// ==================================================
// INSTANCE PROPERTIES (Some are implementation of ITreeNode)
// INSTANCE PROPERTIES (Some are implementation of ITreeNode<T>)
// ==================================================

public get label(): string {
Expand Down Expand Up @@ -171,26 +172,31 @@ export class TreeNode implements ITreeNode {
return this._children !== undefined && this._children.length > 0;
}

public get parent(): TreeNode<T> | undefined {
return this._parent;
}

// ==================================================
// INSTANCE METHODS: Parent children methods
// ==================================================

public getRoot(): TreeNode {
if (this._parent !== undefined) {
return this._parent.getRoot();
// eslint-disable-next-line @typescript-eslint/prefer-return-this-type
public getRoot(): TreeNode<T> {
if (this.parent !== undefined) {
return this.parent.getRoot();
}
return this;
}

public addChild(child: TreeNode): void {
public addChild(child: TreeNode<T>): void {
if (this._children === undefined) {
this._children = [];
}
this._children.push(child);
child._parent = this;
}

public insertChild(index: number, child: TreeNode): void {
public insertChild(index: number, child: TreeNode<T>): void {
if (this._children === undefined) {
this._children = [];
}
Expand All @@ -213,7 +219,7 @@ export class TreeNode implements ITreeNode {
if (!(child instanceof TreeNode)) {
continue;
}
this.addChild(child);
this.addChild(child as TreeNode<T>);
}
}
this.needLoadChildren = false;
Expand All @@ -226,7 +232,7 @@ export class TreeNode implements ITreeNode {
if (siblings === undefined || siblings.length === 0) {
return;
}
const parent = this._parent;
const parent = this.parent;
if (parent === undefined || parent._children === undefined) {
return;
}
Expand All @@ -240,7 +246,7 @@ export class TreeNode implements ITreeNode {
continue;
}
index++;
parent.insertChild(index, child);
parent.insertChild(index, child as TreeNode<T>);
}
this.needLoadSiblings = false;
parent.update();
Expand All @@ -250,8 +256,8 @@ export class TreeNode implements ITreeNode {
// INSTANCE METHODS: Get selection and checked nodes
// ==================================================

public getSelectedNodes(): TreeNode[] {
const nodes: TreeNode[] = [];
public getSelectedNodes(): Array<TreeNode<T>> {
const nodes: Array<TreeNode<T>> = [];
for (const child of this.getThisAndDescendants()) {
if (child.isSelected) {
nodes.push(child);
Expand All @@ -260,8 +266,8 @@ export class TreeNode implements ITreeNode {
return nodes;
}

public getCheckedNodes(): TreeNode[] {
const nodes: TreeNode[] = [];
public getCheckedNodes(): Array<TreeNode<T>> {
const nodes: Array<TreeNode<T>> = [];
for (const child of this.getThisAndDescendants()) {
if (child.checkBoxState === CheckBoxState.All) {
nodes.push(child);
Expand All @@ -274,11 +280,11 @@ export class TreeNode implements ITreeNode {
// INSTANCE METHODS: Iterators
// ==================================================

public *getChildren(loadNodes?: LoadNodesAction): Generator<TreeNode> {
public *getChildren(loadNodes?: LoadNodesAction): Generator<TreeNode<T>> {
if (this.isLoadingChildren) {
loadNodes = undefined;
}
const canLoad = this.isParent && this._parent !== undefined;
const canLoad = this.isParent && this.parent !== undefined;
if (canLoad && loadNodes !== undefined && this.needLoadChildren) {
void this.loadChildren(loadNodes);
}
Expand All @@ -290,7 +296,7 @@ export class TreeNode implements ITreeNode {
}
}

public *getDescendants(): Generator<TreeNode> {
public *getDescendants(): Generator<TreeNode<T>> {
for (const child of this.getChildren()) {
yield child;
for (const descendant of child.getDescendants()) {
Expand All @@ -299,18 +305,18 @@ export class TreeNode implements ITreeNode {
}
}

public *getThisAndDescendants(): Generator<TreeNode> {
public *getThisAndDescendants(): Generator<TreeNode<T>> {
yield this;
for (const descendant of this.getDescendants()) {
yield descendant;
}
}

public *getAncestors(): Generator<TreeNode> {
let ancestor = this._parent;
public *getAncestors(): Generator<TreeNode<T>> {
let ancestor = this.parent;
while (ancestor !== undefined) {
yield ancestor;
ancestor = ancestor._parent;
ancestor = ancestor.parent;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,9 @@ export const DomainObjectPanel = (): ReactElement => {

useEffect(() => {
DomainObjectPanelUpdater.setDomainObjectDelegate(setCurrentDomainObjectInfo);

// Set in the get string on the copy command if any
}, [setCurrentDomainObjectInfo, commands]);

// Fore the getString to be updated
// Force the getString to be updated
if (commands !== undefined && info !== undefined) {
for (const command of commands) {
if (command instanceof CopyToClipboardCommand)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
FlipVerticalIcon,
GrabIcon,
type IconProps,
InfoIcon,
LocationIcon,
PerspectiveAltIcon,
PerspectiveIcon,
Expand Down Expand Up @@ -89,6 +90,7 @@ const defaultMappings: Array<[IconName, IconType]> = [
['FlipHorizontal', FlipHorizontalIcon],
['FlipVertical', FlipVerticalIcon],
['Grab', GrabIcon],
['Info', InfoIcon],
['Location', LocationIcon],
['Perspective', PerspectiveIcon],
['PerspectiveAlt', PerspectiveAltIcon],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
SELECTED_TEXT_COLOR,
TEXT_COLOR
} from './utilities/constants';
import { TreeViewInfo } from './components/TreeViewInfo';

// ==================================================
// MAIN COMPONENT
Expand All @@ -51,6 +52,7 @@ export const TreeViewNode = ({
const hasHover = props.hasHover ?? true;
const hasCheckBoxes = props.hasCheckboxes ?? false;
const hasIcons = props.hasIcons ?? false;
const hasInfo = props.hasInfo ?? false;
const marginLeft = level * gapToChildren + 'px';

// This force to update the component when the node changes
Expand Down Expand Up @@ -92,6 +94,7 @@ export const TreeViewNode = ({
{hasIcons && <TreeNodeIcon node={node} color={color} />}
<TreeViewLabel node={node} props={props} />
</div>
{hasInfo && <TreeViewInfo node={node} props={props} />}
</div>
{children !== undefined &&
children.map((node, index) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export type TreeViewProps = {
hoverBackgroundColor?: string;
caretColor?: string;
hoverCaretColor?: string;
infoColor?: string;
hoverInfoColor?: string;

// Sizes
gapBetweenItems?: number;
Expand All @@ -29,12 +31,15 @@ export type TreeViewProps = {
hasHover?: boolean; // Default true, If this is set, it uses the hover color for the mouse over effect
hasCheckboxes?: boolean; // Default is false
hasIcons?: boolean; // Default is false
hasInfo?: boolean; // Default is false
loadingLabel?: string; // Default is 'Loading...'
loadMoreLabel?: string; // Default is 'Load more...'
maxLabelLength?: number;

// Event handlers
onSelectNode?: TreeNodeAction;
onCheckNode?: TreeNodeAction;
onClickInfo?: TreeNodeAction;
loadNodes?: LoadNodesAction;

// The root node of the tree, the root is not rendered.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const TreeNodeCaret = ({
props: TreeViewProps;
}): ReactElement => {
const [isHoverOver, setHoverOver] = useState(false);
const color = getCaretColor(node, props, isHoverOver);
const color = getColor(node, props, isHoverOver);
const size = props.caretSize ?? CARET_SIZE;
const sizePx = size + 'px';
const style = { color, marginTop: '0px', width: sizePx, height: sizePx };
Expand All @@ -50,14 +50,7 @@ export const TreeNodeCaret = ({
return <CaretDownIcon style={style} />;
};

function getCaretColor(
node: ITreeNode,
props: TreeViewProps,
isHoverOver: boolean
): string | undefined {
if (!node.isParent) {
return 'transparent';
}
function getColor(node: ITreeNode, props: TreeViewProps, isHoverOver: boolean): string | undefined {
if (isHoverOver) {
return props.hoverCaretColor ?? HOVER_CARET_COLOR;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*!
* Copyright 2024 Cognite AS
*/

/* eslint-disable react/prop-types */

import { useState, type ReactElement } from 'react';
import { type TreeViewProps } from '../TreeViewProps';
import { type ITreeNode } from '../../../../architecture/base/treeView/ITreeNode';
import { InfoIcon } from '@cognite/cogs.js';
import { HOVER_INFO_COLOR, INFO_COLOR } from '../utilities/constants';

// ==================================================
// MAIN COMPONENT
// ==================================================

export const TreeViewInfo = ({
node,
props
}: {
node: ITreeNode;
props: TreeViewProps;
}): ReactElement => {
const Icon = InfoIcon;
const [isHoverOver, setHoverOver] = useState(false);
const color = getColor(props, isHoverOver);
return (
<Icon
style={{ color, marginTop: '3px', marginLeft: '6px' }}
onClick={() => {
onClickInfo(node);
}}
onMouseEnter={() => {
setHoverOver(true);
}}
onMouseLeave={() => {
setHoverOver(false);
}}
/>
);

function onClickInfo(node: ITreeNode): void {
if (props.onClickInfo === undefined) {
return;
}
props.onClickInfo(node);
}
};

function getColor(props: TreeViewProps, isHoverOver: boolean): string | undefined {
if (isHoverOver) {
return props.hoverInfoColor ?? HOVER_INFO_COLOR;
}
return props.infoColor ?? INFO_COLOR;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { type ReactElement } from 'react';
import { type TreeViewProps } from '../TreeViewProps';
import { type ITreeNode } from '../../../../architecture/base/treeView/ITreeNode';
import { LOADING_LABEL } from '../utilities/constants';
import { LOADING_LABEL, MAX_LABEL_LENGTH } from '../utilities/constants';

// ==================================================
// MAIN COMPONENT
Expand All @@ -20,7 +20,16 @@ export const TreeViewLabel = ({
node: ITreeNode;
props: TreeViewProps;
}): ReactElement => {
const label = node.isLoadingChildren ? (props.loadingLabel ?? LOADING_LABEL) : node.label;
let label: string;
if (node.isLoadingChildren) {
label = props.loadingLabel ?? LOADING_LABEL;
} else {
label = node.label;
const maxLabelLength = props.maxLabelLength ?? MAX_LABEL_LENGTH;
if (label.length > maxLabelLength) {
label = label.substring(0, maxLabelLength) + '...';
}
}
if (node.hasBoldLabel) {
return <b>{label}</b>;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ export const HOVER_TEXT_COLOR = 'black';
export const HOVER_BACKGROUND_COLOR = 'lightgray';
export const CARET_COLOR = 'gray';
export const HOVER_CARET_COLOR = 'highlight';
export const INFO_COLOR = 'black';
export const HOVER_INFO_COLOR = 'highlight';
export const CARET_SIZE = 20;
export const GAP_TO_CHILDREN = 16;
export const GAP_BETWEEN_ITEMS = 4;
export const LOADING_LABEL = 'Loading ...';
export const LOAD_MORE_LABEL = 'Load more ...';
export const BACKGROUND_COLOR = 'white';
export const MAX_LABEL_LENGTH = 25;
Loading

0 comments on commit ec92a1b

Please sign in to comment.