Repository: Textualize/textual

Issue:
DirectoryTree.path no longer reactive
The `DirectoryTree.path` variable changing values no longer reloads the widget when it's changed. This behavior changed on the [0.49.0 release](https://github.com/Textualize/textual/releases/tag/v0.49.0). 

I expected the `path` attribute to be reactive, according to the `watch_path` docs:
> If the path is changed the directory tree will be repopulated using the new value as the root.

In the below examples I set a binding on the directory tree, `.` - this action should trigger the directory tree going refreshing to the current root's parent directory:

<details><summary>Example App</summary>
<p>

```py
import pathlib
from typing import Any, ClassVar, List

from rich.syntax import Syntax
from textual import on
from textual.app import App, ComposeResult
from textual.binding import BindingType
from textual.containers import Horizontal, VerticalScroll
from textual.widgets import DirectoryTree, Footer, Header, Static


class DirectoryTreeApp(App):
    """
    Directory Tree + Code Viewer App
    """

    BINDINGS: ClassVar[List[BindingType]] = [
        ("q", "quit", "Quit"),
        (".", "parent_dir", "Parent Directory"),
    ]

    def __init__(self, path: str, *args: Any, **kwargs: Any):
        super().__init__(*args, **kwargs)
        self.path = pathlib.Path(path).resolve()
        self.file_content = Static()
        self.directory_tree = DirectoryTree(path=self.path)

    def compose(self) -> ComposeResult:
        yield Header()
        yield Horizontal(self.directory_tree, VerticalScroll(self.file_content))
        yield Footer()

    @on(DirectoryTree.FileSelected)
    def handle_file_selected(self, message: DirectoryTree.FileSelected) -> None:
        file_content = message.path.read_text(errors="ignore")
        lexer = Syntax.guess_lexer(path=str(message.path))
        code = Syntax(code=file_content, lexer=lexer)
        self.file_content.update(code)

    def action_parent_dir(self) -> None:
        """
        Go to the parent directory
        """
        if self.directory_tree.path != self.directory_tree.path.parent:
            self.directory_tree.path = self.directory_tree.path.parent
            self.notify(message=f"Directory Changed: {self.directory_tree.path}")


app = DirectoryTreeApp(".")

if __name__ == "__main__":
    app.run()
```

</p>
</details> 

<details><summary>textual==0.48.2 screen recording</summary>
<p>

https://github.com/Textualize/textual/assets/49741340/1c38b3d6-6ada-4a63-8b66-0d0ac95b9112

</p>
</details> 

</p>
</details> 

<details><summary>textual==0.49.0 screen recording</summary>
<p>

https://github.com/Textualize/textual/assets/49741340/aed6f0a5-e9f2-4dda-bbe6-5f687aca4edb

</p>
</details> 

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a3534132a..2df2b1993 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 - Fixed `Sparkline` not working with data in a `deque` https://github.com/Textualize/textual/issues/3899
 - Tooltips are now cleared when the related widget is no longer under them https://github.com/Textualize/textual/issues/3045
 - Simplified tree-sitter highlight queries for HTML, which also seems to fix segfault issue https://github.com/Textualize/textual/pull/4195
+- Fixed `DirectoryTree.path` no longer reacting to new values https://github.com/Textualize/textual/issues/4208
 
 ## [0.52.1] - 2024-02-20
 
diff --git a/src/textual/widgets/_directory_tree.py b/src/textual/widgets/_directory_tree.py
index fbddcb75d..16f9d0a10 100644
--- a/src/textual/widgets/_directory_tree.py
+++ b/src/textual/widgets/_directory_tree.py
@@ -332,6 +332,7 @@ class DirectoryTree(Tree[DirEntry]):
         If the path is changed the directory tree will be repopulated using
         the new value as the root.
         """
+        self.reset_node(self.root, str(self.path), DirEntry(self.PATH(self.path)))
         await self.reload()
 
     def process_label(self, label: TextType) -> Text:


Test patch:
diff --git a/tests/directory_tree/test_change_path.py b/tests/directory_tree/test_change_path.py
new file mode 100644
index 000000000..1b73f3c09
--- /dev/null
+++ b/tests/directory_tree/test_change_path.py
@@ -0,0 +1,20 @@
+from pathlib import Path
+
+from textual.app import App, ComposeResult
+from textual.widgets import DirectoryTree
+
+
+class DirectoryTreeApp(App[None]):
+
+    def compose(self) -> ComposeResult:
+        yield DirectoryTree(".")
+
+
+async def test_change_directory_tree_path(tmpdir: Path) -> None:
+    """The DirectoryTree should react to the path changing."""
+
+    async with DirectoryTreeApp().run_test() as pilot:
+        assert pilot.app.query_one(DirectoryTree).root.data.path == Path(".")
+        pilot.app.query_one(DirectoryTree).path = tmpdir
+        await pilot.pause()
+        assert pilot.app.query_one(DirectoryTree).root.data.path == tmpdir