Skip to content

Commit

Permalink
[Core] Add option to make tree node disabled (#3354)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jacek Jagiełło authored and adidahiya committed Feb 25, 2019
1 parent 7816c70 commit f7d08a9
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 13 deletions.
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 @@ -138,6 +138,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 */

1 comment on commit f7d08a9

@blueprint-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Previews: documentation | landing | table

Please sign in to comment.