From f7d08a9dcd72c69439931105a1d9bdeeaeaef3e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacek=20Jagie=C5=82=C5=82o?= Date: Mon, 25 Feb 2019 02:56:34 +0100 Subject: [PATCH] [Core] Add option to make tree node disabled (#3354) --- packages/core/src/components/tree/_tree.scss | 14 +++++ .../core/src/components/tree/treeNode.tsx | 37 +++++++---- packages/core/test/tree/treeTests.tsx | 61 +++++++++++++++++++ .../examples/core-examples/treeExample.tsx | 7 +++ 4 files changed, 106 insertions(+), 13 deletions(-) diff --git a/packages/core/src/components/tree/_tree.scss b/packages/core/src/components/tree/_tree.scss index 86fa7502a7..6a8816722c 100644 --- a/packages/core/src/components/tree/_tree.scss +++ b/packages/core/src/components/tree/_tree.scss @@ -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; diff --git a/packages/core/src/components/tree/treeNode.tsx b/packages/core/src/components/tree/treeNode.tsx index c6f4b0cf53..cfcb9f27bc 100644 --- a/packages/core/src/components/tree/treeNode.tsx +++ b/packages/core/src/components/tree/treeNode.tsx @@ -19,6 +19,12 @@ export interface ITreeNode extends IProps { */ childNodes?: Array>; + /** + * 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. @@ -86,10 +92,11 @@ export class TreeNode extends React.Component, {}> { } 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, }, @@ -101,17 +108,20 @@ export class TreeNode extends React.Component, {}> { `${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 (
  • -
    +
    {this.maybeRenderCaret()} {label} @@ -123,13 +133,14 @@ export class TreeNode extends React.Component, {}> { } 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 ; + const onClick = disabled === true ? undefined : this.handleCaretClick; + return ; } return ; } diff --git a/packages/core/test/tree/treeTests.tsx b/packages/core/test/tree/treeTests.tsx index db426d063b..40f7f25329 100644 --- a/packages/core/test/tree/treeTests.tsx +++ b/packages/core/test/tree/treeTests.tsx @@ -144,6 +144,67 @@ describe("", () => { 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"; diff --git a/packages/docs-app/src/examples/core-examples/treeExample.tsx b/packages/docs-app/src/examples/core-examples/treeExample.tsx index 49ae720aa7..873b6bc6f5 100644 --- a/packages/docs-app/src/examples/core-examples/treeExample.tsx +++ b/packages/docs-app/src/examples/core-examples/treeExample.tsx @@ -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 */