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

feat(collapsible): make title a reactive attribute #3830

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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

- Added `get_loading_widget` to Widget and App customize the loading widget. https://github.com/Textualize/textual/pull/3816
- Added messages `Collapsible.Expanded` and `Collapsible.Collapsed` that inherit from `Collapsible.Toggled`. https://github.com/Textualize/textual/issues/3824
- Added `Collapsible.title` reactive attribute https://github.com/Textualize/textual/pull/3830

## [0.44.1] - 2023-12-4

Expand Down
7 changes: 4 additions & 3 deletions docs/widgets/collapsible.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,10 @@ The following example shows `Collapsible` widgets with custom expand/collapse sy

## Reactive Attributes

| Name | Type | Default | Description |
| ----------- | ------ | ------- | ---------------------------------------------------- |
| `collapsed` | `bool` | `True` | Controls the collapsed/expanded state of the widget. |
| Name | Type | Default | Description |
| ----------- | ------ | ------------| ---------------------------------------------------- |
| `collapsed` | `bool` | `True` | Controls the collapsed/expanded state of the widget. |
| `title` | `str` | `"Toggle"` | Title of the collapsed/expanded contents. |

## Messages

Expand Down
33 changes: 23 additions & 10 deletions src/textual/widgets/_collapsible.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
from __future__ import annotations

from rich.console import RenderableType
from rich.text import Text

from .. import events
from ..app import ComposeResult
from ..binding import Binding
Expand All @@ -11,11 +8,12 @@
from ..message import Message
from ..reactive import reactive
from ..widget import Widget
from ..widgets import Static

__all__ = ["Collapsible", "CollapsibleTitle"]


class CollapsibleTitle(Widget, can_focus=True):
class CollapsibleTitle(Static, can_focus=True):
"""Title and symbol for the Collapsible."""

DEFAULT_CSS = """
Expand Down Expand Up @@ -44,6 +42,7 @@ class CollapsibleTitle(Widget, can_focus=True):
"""

collapsed = reactive(True)
label = reactive("Toggle")

def __init__(
self,
Expand All @@ -57,7 +56,9 @@ def __init__(
self.collapsed_symbol = collapsed_symbol
self.expanded_symbol = expanded_symbol
self.label = label
self.collapse = collapsed
self.collapsed = collapsed
self._collapsed_label = f"{collapsed_symbol} {label}"
self._expanded_label = f"{expanded_symbol} {label}"

class Toggle(Message):
"""Request toggle."""
Expand All @@ -71,18 +72,26 @@ def action_toggle(self) -> None:
"""Toggle the state of the parent collapsible."""
self.post_message(self.Toggle())

def render(self) -> RenderableType:
"""Compose right/down arrow and label."""
def _watch_label(self, label: str) -> None:
self._collapsed_label = f"{self.collapsed_symbol} {label}"
self._expanded_label = f"{self.expanded_symbol} {label}"
if self.collapsed:
return Text(f"{self.collapsed_symbol} {self.label}")
self.update(self._collapsed_label)
else:
self.update(self._expanded_label)

def _watch_collapsed(self, collapsed: bool) -> None:
if collapsed:
self.update(self._collapsed_label)
else:
return Text(f"{self.expanded_symbol} {self.label}")
self.update(self._expanded_label)


class Collapsible(Widget):
"""A collapsible container."""

collapsed = reactive(True)
title = reactive("Toggle")

DEFAULT_CSS = """
Collapsible {
Expand Down Expand Up @@ -169,14 +178,15 @@ def __init__(
classes: The CSS classes of the collapsible.
disabled: Whether the collapsible is disabled or not.
"""
super().__init__(name=name, id=id, classes=classes, disabled=disabled)
self._title = CollapsibleTitle(
label=title,
collapsed_symbol=collapsed_symbol,
expanded_symbol=expanded_symbol,
collapsed=collapsed,
)
self.title = title
self._contents_list: list[Widget] = list(children)
super().__init__(name=name, id=id, classes=classes, disabled=disabled)
self.collapsed = collapsed

def _on_collapsible_title_toggle(self, event: CollapsibleTitle.Toggle) -> None:
Expand Down Expand Up @@ -214,3 +224,6 @@ def compose_add_child(self, widget: Widget) -> None:
widget: A Widget to add.
"""
self._contents_list.append(widget)

def _watch_title(self, title: str) -> None:
self._title.label = title
12 changes: 12 additions & 0 deletions tests/test_collapsible.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,15 @@ def on_collapsible_collapsed(self) -> None:

assert pilot.app.query_one(Collapsible).collapsed
assert len(hits) == 1


async def test_collapsible_title_reactive_change():
class CollapsibleApp(App[None]):
def compose(self) -> ComposeResult:
yield Collapsible(title="Old title")

async with CollapsibleApp().run_test() as pilot:
collapsible = pilot.app.query_one(Collapsible)
assert get_title(collapsible).label == "Old title"
collapsible.title = "New title"
assert get_title(collapsible).label == "New title"
Loading