Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Core] Add option to make tree node disabled #3354

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/core/src/components/tree/_tree.scss
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,20 @@ $tree-icon-spacing: ($tree-row-height - $pt-icon-size-standard) / 2 !default;
}
}

.#{$ns}-tree-node.#{$ns}-disabled {
.#{$ns}-tree-node-content {
background-color: inherit;
cursor: not-allowed;
color: $pt-text-color-disabled;
}

.#{$ns}-tree-node-caret,
.#{$ns}-tree-node-icon {
cursor: not-allowed;
color: $pt-text-color-disabled;
}
}

.#{$ns}-tree-node.#{$ns}-tree-node-selected > .#{$ns}-tree-node-content {
background-color: $pt-intent-primary;

Expand Down
37 changes: 24 additions & 13 deletions packages/core/src/components/tree/treeNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ export interface ITreeNode<T = {}> extends IProps {
*/
childNodes?: Array<ITreeNode<T>>;

/**
* Whether this tree node is non-interactive. Enabling this prop will ignore
* mouse event handlers (in particular click, down, enter, leave).
*/
disabled?: boolean;

/**
* Whether the caret to expand/collapse a node should be shown.
* If not specified, this will be true if the node has children and false otherwise.
Expand Down Expand Up @@ -86,10 +92,11 @@ export class TreeNode<T = {}> extends React.Component<ITreeNodeProps<T>, {}> {
}

public render() {
const { children, className, icon, isExpanded, isSelected, label } = this.props;
const { children, className, disabled, icon, isExpanded, isSelected, label } = this.props;
const classes = classNames(
Classes.TREE_NODE,
{
[Classes.DISABLED]: disabled,
[Classes.TREE_NODE_SELECTED]: isSelected,
[Classes.TREE_NODE_EXPANDED]: isExpanded,
},
Expand All @@ -101,17 +108,20 @@ export class TreeNode<T = {}> extends React.Component<ITreeNodeProps<T>, {}> {
`${Classes.TREE_NODE_CONTENT}-${this.props.depth}`,
);

const eventHandlers =
disabled === true
? {}
: {
onClick: this.handleClick,
onContextMenu: this.handleContextMenu,
onDoubleClick: this.handleDoubleClick,
onMouseEnter: this.handleMouseEnter,
onMouseLeave: this.handleMouseLeave,
};

return (
<li className={classes}>
<div
className={contentClasses}
onClick={this.handleClick}
onContextMenu={this.handleContextMenu}
onDoubleClick={this.handleDoubleClick}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
ref={this.handleContentRef}
>
<div className={contentClasses} ref={this.handleContentRef} {...eventHandlers}>
{this.maybeRenderCaret()}
<Icon className={Classes.TREE_NODE_ICON} icon={icon} />
<span className={Classes.TREE_NODE_LABEL}>{label}</span>
Expand All @@ -123,13 +133,14 @@ export class TreeNode<T = {}> extends React.Component<ITreeNodeProps<T>, {}> {
}

private maybeRenderCaret() {
const { hasCaret = React.Children.count(this.props.children) > 0 } = this.props;
const { children, isExpanded, disabled, hasCaret = React.Children.count(children) > 0 } = this.props;
if (hasCaret) {
const caretClasses = classNames(
Classes.TREE_NODE_CARET,
this.props.isExpanded ? Classes.TREE_NODE_CARET_OPEN : Classes.TREE_NODE_CARET_CLOSED,
isExpanded ? Classes.TREE_NODE_CARET_OPEN : Classes.TREE_NODE_CARET_CLOSED,
);
return <Icon className={caretClasses} onClick={this.handleCaretClick} icon={"chevron-right"} />;
const onClick = disabled === true ? undefined : this.handleCaretClick;
return <Icon className={caretClasses} onClick={onClick} icon={"chevron-right"} />;
}
return <span className={Classes.TREE_NODE_CARET_NONE} />;
}
Expand Down
61 changes: 61 additions & 0 deletions packages/core/test/tree/treeTests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,67 @@ describe("<Tree>", () => {
assert.deepEqual(onNodeMouseLeave.args[0][1], [2]);
});

it("if disabled, event callbacks are not fired", () => {
const onNodeClick = spy();
const onNodeCollapse = spy();
const onNodeContextMenu = spy();
const onNodeDoubleClick = spy();
const onNodeExpand = spy();
const onNodeMouseEnter = spy();
const onNodeMouseLeave = spy();

const contents = createDefaultContents();
contents[0].disabled = true;
contents[0].hasCaret = true;
contents[0].isExpanded = false;

const tree = renderTree({
contents,
onNodeClick,
onNodeCollapse,
onNodeContextMenu,
onNodeDoubleClick,
onNodeExpand,
onNodeMouseEnter,
onNodeMouseLeave,
});

const treeNode = tree.find(`.${Classes.TREE_NODE}.c0`);
const treeNodeContent = treeNode.find(`.${Classes.TREE_NODE_CONTENT}`);
const treeNodeCaret = treeNodeContent.find(`.${Classes.TREE_NODE_CARET}`).first();

treeNodeContent.simulate("click");
assert.isTrue(onNodeClick.notCalled);

treeNodeContent.simulate("dblclick");
assert.isTrue(onNodeDoubleClick.notCalled);

treeNodeContent.simulate("contextmenu");
assert.isTrue(onNodeContextMenu.notCalled);

treeNodeContent.simulate("mouseenter");
assert.isTrue(onNodeMouseEnter.notCalled);

treeNodeContent.simulate("mouseleave");
assert.isTrue(onNodeMouseLeave.notCalled);

treeNodeCaret.simulate("click");
assert.isTrue(onNodeExpand.notCalled);

treeNodeCaret.simulate("click");
assert.isTrue(onNodeCollapse.notCalled);
});

it("disabled nodes are rendered correctly", () => {
const contents = createDefaultContents();
contents[0].disabled = true;

const tree = renderTree({ contents });
const disabledTreeNode = tree.find(`.${Classes.TREE_NODE}.c0.${Classes.DISABLED}`);

assert.equal(disabledTreeNode.length, 1);
});

it("icons are rendered correctly if present", () => {
const contents = createDefaultContents();
contents[1].icon = "document";
Expand Down
7 changes: 7 additions & 0 deletions packages/docs-app/src/examples/core-examples/treeExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,5 +122,12 @@ const INITIAL_STATE: ITreeNode[] = [
},
],
},
{
id: 2,
hasCaret: true,
icon: "folder-close",
label: "Super secret files",
disabled: true,
},
];
/* tslint:enable:object-literal-sort-keys */