Skip to content

Commit

Permalink
Merge pull request #3409 from joshbduncan/disabled-trees
Browse files Browse the repository at this point in the history
fix Tree(disabled=True) breaking app
  • Loading branch information
davep authored Sep 27, 2023
2 parents d766bb9 + 6698bbb commit fb81ee9
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Fixed

- `Pilot.click`/`Pilot.hover` can't use `Screen` as a selector https://github.com/Textualize/textual/issues/3395
- App exception when a `Tree` is initialized/mounted with `disabled=True` https://github.com/Textualize/textual/issues/3407

### Added

Expand Down
4 changes: 2 additions & 2 deletions src/textual/widgets/_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,8 +597,6 @@ def __init__(
disabled: Whether the tree is disabled or not.
"""

super().__init__(name=name, id=id, classes=classes, disabled=disabled)

text_label = self.process_label(label)

self._updates = 0
Expand All @@ -610,6 +608,8 @@ def __init__(
self._tree_lines_cached: list[_TreeLine] | None = None
self._cursor_node: TreeNode[TreeDataType] | None = None

super().__init__(name=name, id=id, classes=classes, disabled=disabled)

@property
def cursor_node(self) -> TreeNode[TreeDataType] | None:
"""The currently selected node, or ``None`` if no selection."""
Expand Down
118 changes: 118 additions & 0 deletions tests/tree/test_tree_availability.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
from __future__ import annotations

from typing import Any

from textual import on
from textual.app import App, ComposeResult
from textual.widgets import Tree


class TreeApp(App[None]):
"""Test tree app."""

def __init__(self, disabled: bool, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self.disabled = disabled
self.messages: list[tuple[str, str]] = []

def compose(self) -> ComposeResult:
"""Compose the child widgets."""
yield Tree("Root", disabled=self.disabled, id="test-tree")

def on_mount(self) -> None:
self.query_one(Tree).root.add("Child")
self.query_one(Tree).focus()

def record(
self,
event: Tree.NodeSelected[None]
| Tree.NodeExpanded[None]
| Tree.NodeCollapsed[None]
| Tree.NodeHighlighted[None],
) -> None:
self.messages.append(
(event.__class__.__name__, event.node.tree.id or "Unknown")
)

@on(Tree.NodeSelected)
def node_selected(self, event: Tree.NodeSelected[None]) -> None:
self.record(event)

@on(Tree.NodeExpanded)
def node_expanded(self, event: Tree.NodeExpanded[None]) -> None:
self.record(event)

@on(Tree.NodeCollapsed)
def node_collapsed(self, event: Tree.NodeCollapsed[None]) -> None:
self.record(event)

@on(Tree.NodeHighlighted)
def node_highlighted(self, event: Tree.NodeHighlighted[None]) -> None:
self.record(event)


async def test_creating_disabled_tree():
"""Mounting a disabled `Tree` should result in the base `Widget`
having a `disabled` property equal to `True`"""
app = TreeApp(disabled=True)
async with app.run_test() as pilot:
tree = app.query_one(Tree)
assert not tree.focusable
assert tree.disabled
assert tree.cursor_line == 0
await pilot.click("#test-tree")
await pilot.pause()
await pilot.press("down")
await pilot.pause()
assert tree.cursor_line == 0


async def test_creating_enabled_tree():
"""Mounting an enabled `Tree` should result in the base `Widget`
having a `disabled` property equal to `False`"""
app = TreeApp(disabled=False)
async with app.run_test() as pilot:
tree = app.query_one(Tree)
assert tree.focusable
assert not tree.disabled
assert tree.cursor_line == 0
await pilot.click("#test-tree")
await pilot.pause()
await pilot.press("down")
await pilot.pause()
assert tree.cursor_line == 1


async def test_disabled_tree_node_selected_message() -> None:
"""Clicking the root node disclosure triangle on a disabled tree
should result in no messages being emitted."""
app = TreeApp(disabled=True)
async with app.run_test() as pilot:
tree = app.query_one(Tree)
# try clicking on a disabled tree
await pilot.click("#test-tree")
await pilot.pause()
assert not pilot.app.messages
# make sure messages DO flow after enabling a disabled tree
tree.disabled = False
await pilot.click("#test-tree")
await pilot.pause()
assert pilot.app.messages == [("NodeExpanded", "test-tree")]


async def test_enabled_tree_node_selected_message() -> None:
"""Clicking the root node disclosure triangle on an enabled tree
should result in an `NodeExpanded` message being emitted."""
app = TreeApp(disabled=False)
async with app.run_test() as pilot:
tree = app.query_one(Tree)
# try clicking on an enabled tree
await pilot.click("#test-tree")
await pilot.pause()
assert pilot.app.messages == [("NodeExpanded", "test-tree")]
tree.disabled = True
# make sure messages DO NOT flow after disabling an enabled tree
app.messages = []
await pilot.click("#test-tree")
await pilot.pause()
assert not pilot.app.messages

0 comments on commit fb81ee9

Please sign in to comment.