Update Collapsible (#3305)

* Update Collapsible

* snapshot tests

* word

* Update docs/widgets/collapsible.md

Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>

* Update docs/widgets/collapsible.md

Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>

* simplify render

---------

Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>
This commit is contained in:
Will McGugan
2023-09-14 16:26:41 +01:00
committed by GitHub
parent ea6bf766e7
commit 1db9ecb302
8 changed files with 641 additions and 685 deletions

View File

@@ -21,7 +21,7 @@ Son of Leto and Jessica.
class CollapsibleApp(App[None]):
"""An example of colllapsible container."""
"""An example of collapsible container."""
BINDINGS = [
("c", "collapse_or_expand(True)", "Collapse All"),

View File

@@ -34,6 +34,16 @@ A classic checkbox control.
```{.textual path="docs/examples/widgets/checkbox.py"}
```
## Collapsible
Content that may be toggled on and off by clicking a title.
[Collapsible reference](./widgets/collapsible.md){ .md-button .md-button--primary }
```{.textual path="docs/examples/widgets/collapsible.py"}
```
## ContentSwitcher

View File

@@ -1,24 +1,25 @@
# Collapsible
!!! tip "Added in version 0.36"
!!! tip "Added in version 0.37"
Widget that wraps its contents in a collapsible container.
A container with a title that can be used to show (expand) or hide (collapse) content, either by clicking or focusing and pressing ++enter++.
- [ ] Focusable
- [x] Focusable
- [x] Container
## Composing
There are two ways to wrap other widgets.
You can pass them as positional arguments to the [Collapsible][textual.widgets.Collapsible] constructor:
You can add content to a Collapsible widget either by passing in children to the constructor, or with a context manager (`with` statement).
Here is an example of using the constructor to add content:
```python
def compose(self) -> ComposeResult:
yield Collapsible(Label("Hello, world."))
```
Alternatively, you can compose other widgets under the context manager:
Here's how the to use it with the context manager:
```python
def compose(self) -> ComposeResult:
@@ -26,9 +27,11 @@ def compose(self) -> ComposeResult:
yield Label("Hello, world.")
```
The second form is generally preferred, but the end result is the same.
## Title
The default title "Toggle" of the `Collapsible` widget can be customized by specifying the parameter `title` of the constructor:
The default title "Toggle" can be customized by setting the `title` parameter of the constructor:
```python
def compose(self) -> ComposeResult:
@@ -38,7 +41,7 @@ def compose(self) -> ComposeResult:
## Initial State
The initial state of the `Collapsible` widget can be customized via the parameter `collapsed` of the constructor:
The initial state of the `Collapsible` widget can be customized via the `collapsed` parameter of the constructor:
```python
def compose(self) -> ComposeResult:
@@ -51,7 +54,7 @@ def compose(self) -> ComposeResult:
## Collapse/Expand Symbols
The symbols `►` and `▼` of the `Collapsible` widget can be customized by specifying the parameters `collapsed_symbol` and `expanded_symbol`, respectively, of the `Collapsible` constructor:
The symbols used to show the collapsed / expanded state can be customized by setting the parameters `collapsed_symbol` and `expanded_symbol`:
```python
def compose(self) -> ComposeResult:
@@ -59,31 +62,19 @@ def compose(self) -> ComposeResult:
yield Label("Hello, world.")
```
=== "Output"
```{.textual path="tests/snapshot_tests/snapshot_apps/collapsible_custom_symbol.py"}
```
=== "collapsible_custom_symbol.py"
```python
--8<-- "tests/snapshot_tests/snapshot_apps/collapsible_custom_symbol.py"
```
## Examples
### Basic example
The following example contains three `Collapsible`s in different states.
=== "All expanded"
```{.textual path="docs/examples/widgets/collapsible.py press="e"}
```{.textual path="docs/examples/widgets/collapsible.py" press="e"}
```
=== "All collapsed"
```{.textual path="docs/examples/widgets/collapsible.py press="c"}
```{.textual path="docs/examples/widgets/collapsible.py" press="c"}
```
=== "Mixed"
@@ -104,49 +95,37 @@ The example below shows nested `Collapsible` widgets and how to set their initia
=== "Output"
```{.textual path="tests/snapshot_tests/snapshot_apps/collapsible_nested.py"}
```{.textual path="docs/examples/widgets/collapsible_nested.py"}
```
=== "collapsible_nested.py"
```python hl_lines="7"
--8<-- "tests/snapshot_tests/snapshot_apps/collapsible_nested.py"
--8<-- "docs/examples/widgets/collapsible_nested.py"
```
### Custom Symbols
The app below shows `Collapsible` widgets with custom expand/collapse symbols.
The following example shows `Collapsible` widgets with custom expand/collapse symbols.
=== "Output"
```{.textual path="tests/snapshot_tests/snapshot_apps/collapsible_custom_symbol.py"}
```{.textual path="docs/examples/widgets/collapsible_custom_symbol.py"}
```
=== "collapsible_custom_symbol.py"
```python
--8<-- "tests/snapshot_tests/snapshot_apps/collapsible_custom_symbol.py"
--8<-- "docs/examples/widgets/collapsible_custom_symbol.py"
```
## Reactive attributes
| Name | Type | Default | Description |
| ----------- | ------ | ------- | -------------------------------------------------------------- |
| Name | Type | Default | Description |
| ----------- | ------ | ------- | ---------------------------------------------------- |
| `collapsed` | `bool` | `True` | Controls the collapsed/expanded state of the widget. |
## Messages
- [Collapsible.Title.Toggle][textual.widgets.Collapsible.Title.Toggle]
<!--
## See also
TODO: Add Accordion widgets later
-->
---
::: textual.widgets.Collapsible
options:

View File

@@ -46,18 +46,18 @@ theme:
- content.code.annotate
- content.code.copy
palette:
- media: "(prefers-color-scheme: light)"
scheme: default
accent: purple
toggle:
icon: material/weather-sunny
name: Switch to dark mode
- media: "(prefers-color-scheme: dark)"
scheme: slate
primary: black
toggle:
icon: material/weather-night
name: Switch to light mode
- media: "(prefers-color-scheme: light)"
scheme: default
accent: purple
toggle:
icon: material/weather-sunny
name: Switch to dark mode
- media: "(prefers-color-scheme: dark)"
scheme: slate
primary: black
toggle:
icon: material/weather-night
name: Switch to light mode
plugins:
search:
@@ -87,6 +87,7 @@ plugins:
- "!^render_lines$"
- "!^get_content_width$"
- "!^get_content_height$"
- "!^compose_add_child$"
watch:
- mkdocs-common.yml
- mkdocs-nav.yml
@@ -98,11 +99,9 @@ plugins:
- "**/_template.md"
- "snippets/*"
extra_css:
- stylesheets/custom.css
extra:
social:
- icon: fontawesome/brands/twitter

View File

@@ -1,211 +1,212 @@
nav:
- Introduction:
- "index.md"
- "getting_started.md"
- "help.md"
- "tutorial.md"
- Guide:
- "guide/index.md"
- "guide/devtools.md"
- "guide/app.md"
- "guide/styles.md"
- "guide/CSS.md"
- "guide/design.md"
- "guide/queries.md"
- "guide/layout.md"
- "guide/events.md"
- "guide/input.md"
- "guide/actions.md"
- "guide/reactivity.md"
- "guide/widgets.md"
- "guide/animation.md"
- "guide/screens.md"
- "guide/workers.md"
- "guide/command_palette.md"
- "widget_gallery.md"
- Reference:
- "reference/index.md"
- CSS Types:
- "css_types/index.md"
- "css_types/border.md"
- "css_types/color.md"
- "css_types/horizontal.md"
- "css_types/integer.md"
- "css_types/name.md"
- "css_types/number.md"
- "css_types/overflow.md"
- "css_types/percentage.md"
- "css_types/scalar.md"
- "css_types/text_align.md"
- "css_types/text_style.md"
- "css_types/vertical.md"
- Events:
- "events/index.md"
- "events/blur.md"
- "events/descendant_blur.md"
- "events/descendant_focus.md"
- "events/enter.md"
- "events/focus.md"
- "events/hide.md"
- "events/key.md"
- "events/leave.md"
- "events/load.md"
- "events/mount.md"
- "events/mouse_capture.md"
- "events/click.md"
- "events/mouse_down.md"
- "events/mouse_move.md"
- "events/mouse_release.md"
- "events/mouse_scroll_down.md"
- "events/mouse_scroll_up.md"
- "events/mouse_up.md"
- "events/paste.md"
- "events/resize.md"
- "events/screen_resume.md"
- "events/screen_suspend.md"
- "events/show.md"
- Styles:
- "styles/align.md"
- "styles/background.md"
- "styles/border.md"
- "styles/border_subtitle_align.md"
- "styles/border_subtitle_background.md"
- "styles/border_subtitle_color.md"
- "styles/border_subtitle_style.md"
- "styles/border_title_align.md"
- "styles/border_title_background.md"
- "styles/border_title_color.md"
- "styles/border_title_style.md"
- "styles/box_sizing.md"
- "styles/color.md"
- "styles/content_align.md"
- "styles/display.md"
- "styles/dock.md"
- "styles/index.md"
- Grid:
- "styles/grid/index.md"
- "styles/grid/column_span.md"
- "styles/grid/grid_columns.md"
- "styles/grid/grid_gutter.md"
- "styles/grid/grid_rows.md"
- "styles/grid/grid_size.md"
- "styles/grid/row_span.md"
- "styles/height.md"
- "styles/layer.md"
- "styles/layers.md"
- "styles/layout.md"
- Links:
- "styles/links/index.md"
- "styles/links/link_background.md"
- "styles/links/link_color.md"
- "styles/links/link_hover_background.md"
- "styles/links/link_hover_color.md"
- "styles/links/link_hover_style.md"
- "styles/links/link_style.md"
- "styles/margin.md"
- "styles/max_height.md"
- "styles/max_width.md"
- "styles/min_height.md"
- "styles/min_width.md"
- "styles/offset.md"
- "styles/opacity.md"
- "styles/outline.md"
- "styles/overflow.md"
- "styles/padding.md"
- Scrollbar colors:
- "styles/scrollbar_colors/index.md"
- "styles/scrollbar_colors/scrollbar_background.md"
- "styles/scrollbar_colors/scrollbar_background_active.md"
- "styles/scrollbar_colors/scrollbar_background_hover.md"
- "styles/scrollbar_colors/scrollbar_color.md"
- "styles/scrollbar_colors/scrollbar_color_active.md"
- "styles/scrollbar_colors/scrollbar_color_hover.md"
- "styles/scrollbar_colors/scrollbar_corner_color.md"
- "styles/scrollbar_gutter.md"
- "styles/scrollbar_size.md"
- "styles/text_align.md"
- "styles/text_opacity.md"
- "styles/text_style.md"
- "styles/tint.md"
- "styles/visibility.md"
- "styles/width.md"
- Widgets:
- "widgets/button.md"
- "widgets/checkbox.md"
- "widgets/content_switcher.md"
- "widgets/data_table.md"
- "widgets/digits.md"
- "widgets/directory_tree.md"
- "widgets/footer.md"
- "widgets/header.md"
- "widgets/index.md"
- "widgets/input.md"
- "widgets/label.md"
- "widgets/list_item.md"
- "widgets/list_view.md"
- "widgets/loading_indicator.md"
- "widgets/log.md"
- "widgets/markdown_viewer.md"
- "widgets/markdown.md"
- "widgets/option_list.md"
- "widgets/placeholder.md"
- "widgets/pretty.md"
- "widgets/progress_bar.md"
- "widgets/radiobutton.md"
- "widgets/radioset.md"
- "widgets/rich_log.md"
- "widgets/rule.md"
- "widgets/select.md"
- "widgets/selection_list.md"
- "widgets/sparkline.md"
- "widgets/static.md"
- "widgets/switch.md"
- "widgets/tabbed_content.md"
- "widgets/tabs.md"
- "widgets/tree.md"
- API:
- "api/index.md"
- "api/app.md"
- "api/await_remove.md"
- "api/binding.md"
- "api/color.md"
- "api/command.md"
- "api/containers.md"
- "api/coordinate.md"
- "api/dom_node.md"
- "api/events.md"
- "api/errors.md"
- "api/filter.md"
- "api/fuzzy_matcher.md"
- "api/geometry.md"
- "api/logger.md"
- "api/logging.md"
- "api/map_geometry.md"
- "api/message_pump.md"
- "api/message.md"
- "api/on.md"
- "api/pilot.md"
- "api/query.md"
- "api/reactive.md"
- "api/screen.md"
- "api/scrollbar.md"
- "api/scroll_view.md"
- "api/strip.md"
- "api/suggester.md"
- "api/system_commands_source.md"
- "api/timer.md"
- "api/types.md"
- "api/validation.md"
- "api/walk.md"
- "api/widget.md"
- "api/work.md"
- "api/worker.md"
- "api/worker_manager.md"
- "How To":
- "how-to/index.md"
- "how-to/center-things.md"
- "how-to/design-a-layout.md"
- "FAQ.md"
- "roadmap.md"
- "Blog":
- blog/index.md
- Introduction:
- "index.md"
- "getting_started.md"
- "help.md"
- "tutorial.md"
- Guide:
- "guide/index.md"
- "guide/devtools.md"
- "guide/app.md"
- "guide/styles.md"
- "guide/CSS.md"
- "guide/design.md"
- "guide/queries.md"
- "guide/layout.md"
- "guide/events.md"
- "guide/input.md"
- "guide/actions.md"
- "guide/reactivity.md"
- "guide/widgets.md"
- "guide/animation.md"
- "guide/screens.md"
- "guide/workers.md"
- "guide/command_palette.md"
- "widget_gallery.md"
- Reference:
- "reference/index.md"
- CSS Types:
- "css_types/index.md"
- "css_types/border.md"
- "css_types/color.md"
- "css_types/horizontal.md"
- "css_types/integer.md"
- "css_types/name.md"
- "css_types/number.md"
- "css_types/overflow.md"
- "css_types/percentage.md"
- "css_types/scalar.md"
- "css_types/text_align.md"
- "css_types/text_style.md"
- "css_types/vertical.md"
- Events:
- "events/index.md"
- "events/blur.md"
- "events/descendant_blur.md"
- "events/descendant_focus.md"
- "events/enter.md"
- "events/focus.md"
- "events/hide.md"
- "events/key.md"
- "events/leave.md"
- "events/load.md"
- "events/mount.md"
- "events/mouse_capture.md"
- "events/click.md"
- "events/mouse_down.md"
- "events/mouse_move.md"
- "events/mouse_release.md"
- "events/mouse_scroll_down.md"
- "events/mouse_scroll_up.md"
- "events/mouse_up.md"
- "events/paste.md"
- "events/resize.md"
- "events/screen_resume.md"
- "events/screen_suspend.md"
- "events/show.md"
- Styles:
- "styles/align.md"
- "styles/background.md"
- "styles/border.md"
- "styles/border_subtitle_align.md"
- "styles/border_subtitle_background.md"
- "styles/border_subtitle_color.md"
- "styles/border_subtitle_style.md"
- "styles/border_title_align.md"
- "styles/border_title_background.md"
- "styles/border_title_color.md"
- "styles/border_title_style.md"
- "styles/box_sizing.md"
- "styles/color.md"
- "styles/content_align.md"
- "styles/display.md"
- "styles/dock.md"
- "styles/index.md"
- Grid:
- "styles/grid/index.md"
- "styles/grid/column_span.md"
- "styles/grid/grid_columns.md"
- "styles/grid/grid_gutter.md"
- "styles/grid/grid_rows.md"
- "styles/grid/grid_size.md"
- "styles/grid/row_span.md"
- "styles/height.md"
- "styles/layer.md"
- "styles/layers.md"
- "styles/layout.md"
- Links:
- "styles/links/index.md"
- "styles/links/link_background.md"
- "styles/links/link_color.md"
- "styles/links/link_hover_background.md"
- "styles/links/link_hover_color.md"
- "styles/links/link_hover_style.md"
- "styles/links/link_style.md"
- "styles/margin.md"
- "styles/max_height.md"
- "styles/max_width.md"
- "styles/min_height.md"
- "styles/min_width.md"
- "styles/offset.md"
- "styles/opacity.md"
- "styles/outline.md"
- "styles/overflow.md"
- "styles/padding.md"
- Scrollbar colors:
- "styles/scrollbar_colors/index.md"
- "styles/scrollbar_colors/scrollbar_background.md"
- "styles/scrollbar_colors/scrollbar_background_active.md"
- "styles/scrollbar_colors/scrollbar_background_hover.md"
- "styles/scrollbar_colors/scrollbar_color.md"
- "styles/scrollbar_colors/scrollbar_color_active.md"
- "styles/scrollbar_colors/scrollbar_color_hover.md"
- "styles/scrollbar_colors/scrollbar_corner_color.md"
- "styles/scrollbar_gutter.md"
- "styles/scrollbar_size.md"
- "styles/text_align.md"
- "styles/text_opacity.md"
- "styles/text_style.md"
- "styles/tint.md"
- "styles/visibility.md"
- "styles/width.md"
- Widgets:
- "widgets/button.md"
- "widgets/checkbox.md"
- "widgets/collapsible.md"
- "widgets/content_switcher.md"
- "widgets/data_table.md"
- "widgets/digits.md"
- "widgets/directory_tree.md"
- "widgets/footer.md"
- "widgets/header.md"
- "widgets/index.md"
- "widgets/input.md"
- "widgets/label.md"
- "widgets/list_item.md"
- "widgets/list_view.md"
- "widgets/loading_indicator.md"
- "widgets/log.md"
- "widgets/markdown_viewer.md"
- "widgets/markdown.md"
- "widgets/option_list.md"
- "widgets/placeholder.md"
- "widgets/pretty.md"
- "widgets/progress_bar.md"
- "widgets/radiobutton.md"
- "widgets/radioset.md"
- "widgets/rich_log.md"
- "widgets/rule.md"
- "widgets/select.md"
- "widgets/selection_list.md"
- "widgets/sparkline.md"
- "widgets/static.md"
- "widgets/switch.md"
- "widgets/tabbed_content.md"
- "widgets/tabs.md"
- "widgets/tree.md"
- API:
- "api/index.md"
- "api/app.md"
- "api/await_remove.md"
- "api/binding.md"
- "api/color.md"
- "api/command.md"
- "api/containers.md"
- "api/coordinate.md"
- "api/dom_node.md"
- "api/events.md"
- "api/errors.md"
- "api/filter.md"
- "api/fuzzy_matcher.md"
- "api/geometry.md"
- "api/logger.md"
- "api/logging.md"
- "api/map_geometry.md"
- "api/message_pump.md"
- "api/message.md"
- "api/on.md"
- "api/pilot.md"
- "api/query.md"
- "api/reactive.md"
- "api/screen.md"
- "api/scrollbar.md"
- "api/scroll_view.md"
- "api/strip.md"
- "api/suggester.md"
- "api/system_commands_source.md"
- "api/timer.md"
- "api/types.md"
- "api/validation.md"
- "api/walk.md"
- "api/widget.md"
- "api/work.md"
- "api/worker.md"
- "api/worker_manager.md"
- "How To":
- "how-to/index.md"
- "how-to/center-things.md"
- "how-to/design-a-layout.md"
- "FAQ.md"
- "roadmap.md"
- "Blog":
- blog/index.md

View File

@@ -1,16 +1,76 @@
from __future__ import annotations
from textual.widget import Widget
from rich.console import RenderableType
from rich.text import Text
from .. import events
from ..app import ComposeResult
from ..containers import Container, Horizontal
from ..binding import Binding
from ..containers import Container
from ..css.query import NoMatches
from ..message import Message
from ..reactive import reactive
from ..widget import Widget
from ..widgets import Label
__all__ = ["Collapsible"]
__all__ = ["Collapsible", "CollapsibleTitle"]
class CollapsibleTitle(Widget, can_focus=True):
"""Title and symbol for the Collapsible."""
DEFAULT_CSS = """
CollapsibleTitle {
width: auto;
height: auto;
padding: 0 1 0 1;
}
CollapsibleTitle:hover {
background: $foreground 10%;
color: $text;
}
CollapsibleTitle:focus {
background: $accent;
}
"""
BINDINGS = [Binding("enter", "toggle", "Toggle collapsible", show=False)]
collapsed = reactive(True)
def __init__(
self,
*,
label: str,
collapsed_symbol: str,
expanded_symbol: str,
collapsed: bool,
) -> None:
super().__init__()
self.collapsed_symbol = collapsed_symbol
self.expanded_symbol = expanded_symbol
self.label = label
self.collapse = collapsed
class Toggle(Message):
"""Request toggle."""
async def _on_click(self, event: events.Click) -> None:
"""Inform ancestor we want to toggle."""
event.stop()
self.post_message(self.Toggle())
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."""
if self.collapsed:
return Text(f"{self.collapsed_symbol} {self.label}")
else:
return Text(f"{self.expanded_symbol} {self.label}")
class Collapsible(Widget):
@@ -23,66 +83,12 @@ class Collapsible(Widget):
width: 1fr;
height: auto;
}
Collapsible.-collapsed > Contents {
display: none;
}
"""
class Title(Horizontal):
DEFAULT_CSS = """
Title {
width: 100%;
height: auto;
}
Title:hover {
background: grey;
}
Title .label {
padding: 0 0 0 1;
}
Title #collapsed-symbol {
display:none;
}
Title.-collapsed #expanded-symbol {
display:none;
}
Title.-collapsed #collapsed-symbol {
display:block;
}
"""
def __init__(
self,
*,
label: str,
collapsed_symbol: str,
expanded_symbol: str,
name: str | None = None,
id: str | None = None,
classes: str | None = None,
disabled: bool = False,
) -> None:
super().__init__(name=name, id=id, classes=classes, disabled=disabled)
self.collapsed_symbol = collapsed_symbol
self.expanded_symbol = expanded_symbol
self.label = label
class Toggle(Message):
"""Request toggle."""
async def _on_click(self, event: events.Click) -> None:
"""Inform ancestor we want to toggle."""
event.stop()
self.post_message(self.Toggle())
def compose(self) -> ComposeResult:
"""Compose right/down arrow and label."""
yield Label(self.expanded_symbol, classes="label", id="expanded-symbol")
yield Label(self.collapsed_symbol, classes="label", id="collapsed-symbol")
yield Label(self.label, classes="label")
class Contents(Container):
DEFAULT_CSS = """
Contents {
@@ -91,9 +97,6 @@ class Collapsible(Widget):
padding: 0 0 0 3;
}
Contents.-collapsed {
display: none;
}
"""
def __init__(
@@ -101,7 +104,7 @@ class Collapsible(Widget):
*children: Widget,
title: str = "Toggle",
collapsed: bool = True,
collapsed_symbol: str = "",
collapsed_symbol: str = "",
expanded_symbol: str = "",
name: str | None = None,
id: str | None = None,
@@ -121,31 +124,39 @@ class Collapsible(Widget):
classes: The CSS classes of the collapsible.
disabled: Whether the collapsible is disabled or not.
"""
self._title = self.Title(
self._title = CollapsibleTitle(
label=title,
collapsed_symbol=collapsed_symbol,
expanded_symbol=expanded_symbol,
collapsed=collapsed,
)
self._contents_list: list[Widget] = list(children)
super().__init__(name=name, id=id, classes=classes, disabled=disabled)
self.collapsed = collapsed
def _on_title_toggle(self, event: Title.Toggle) -> None:
def on_collapsible_title_toggle(self, event: CollapsibleTitle.Toggle) -> None:
event.stop()
self.collapsed = not self.collapsed
def watch_collapsed(self) -> None:
for child in self._nodes:
child.set_class(self.collapsed, "-collapsed")
def _watch_collapsed(self, collapsed: bool) -> None:
"""Update collapsed state when reactive is changed."""
self._update_collapsed(collapsed)
def _update_collapsed(self, collapsed: bool) -> None:
"""Update children to match collapsed state."""
try:
self._title.collapsed = collapsed
self.set_class(collapsed, "-collapsed")
except NoMatches:
pass
def _on_mount(self) -> None:
"""Initialise collapsed state."""
self._update_collapsed(self.collapsed)
def compose(self) -> ComposeResult:
yield from (
child.set_class(self.collapsed, "-collapsed")
for child in (
self._title,
self.Contents(*self._contents_list),
)
)
yield self._title
yield self.Contents(*self._contents_list)
def compose_add_child(self, widget: Widget) -> None:
"""When using the context manager compose syntax, we want to attach nodes to the contents.

File diff suppressed because one or more lines are too long

View File

@@ -2,12 +2,13 @@ from __future__ import annotations
from textual.app import App, ComposeResult
from textual.widgets import Collapsible, Label
from textual.widgets._collapsible import CollapsibleTitle
COLLAPSED_CLASS = "-collapsed"
def get_title(collapsible: Collapsible) -> Collapsible.Title:
return collapsible.get_child_by_type(Collapsible.Title)
def get_title(collapsible: Collapsible) -> CollapsibleTitle:
return collapsible.get_child_by_type(CollapsibleTitle)
def get_contents(collapsible: Collapsible) -> Collapsible.Contents:
@@ -31,9 +32,7 @@ async def test_compose_default_collapsible():
async with CollapsibleApp().run_test() as pilot:
collapsible = pilot.app.query_one(Collapsible)
assert get_title(collapsible).label == "Toggle"
assert get_title(collapsible).has_class(COLLAPSED_CLASS)
assert len(get_contents(collapsible).children) == 1
assert get_contents(collapsible).has_class(COLLAPSED_CLASS)
async def test_compose_empty_collapsible():
@@ -76,32 +75,6 @@ async def test_compose_expanded_collapsible():
assert not get_contents(collapsible).has_class(COLLAPSED_CLASS)
async def test_collapsible_collapsed_title_label():
"""Collapsed title label should be displayed."""
class CollapsibleApp(App[None]):
def compose(self) -> ComposeResult:
yield Collapsible(Label("Some Contents"), collapsed=True)
async with CollapsibleApp().run_test() as pilot:
title = get_title(pilot.app.query_one(Collapsible))
assert not title.get_child_by_id("expanded-symbol").display
assert title.get_child_by_id("collapsed-symbol").display
async def test_collapsible_expanded_title_label():
"""Expanded title label should be displayed."""
class CollapsibleApp(App[None]):
def compose(self) -> ComposeResult:
yield Collapsible(Label("Some Contents"), collapsed=False)
async with CollapsibleApp().run_test() as pilot:
title = get_title(pilot.app.query_one(Collapsible))
assert title.get_child_by_id("expanded-symbol").display
assert not title.get_child_by_id("collapsed-symbol").display
async def test_collapsible_collapsed_contents_display_false():
"""Test default settings of Collapsible with 1 widget in contents."""
@@ -126,22 +99,6 @@ async def test_collapsible_expanded_contents_display_true():
assert get_contents(collapsible).display
async def test_reactive_collapsed():
"""Updating ``collapsed`` should change classes of children."""
class CollapsibleApp(App[None]):
def compose(self) -> ComposeResult:
yield Collapsible(collapsed=False)
async with CollapsibleApp().run_test() as pilot:
collapsible = pilot.app.query_one(Collapsible)
assert not get_title(collapsible).has_class(COLLAPSED_CLASS)
collapsible.collapsed = True
assert get_contents(collapsible).has_class(COLLAPSED_CLASS)
collapsible.collapsed = False
assert not get_title(collapsible).has_class(COLLAPSED_CLASS)
async def test_toggle_title():
"""Clicking title should update ``collapsed``."""
@@ -152,12 +109,9 @@ async def test_toggle_title():
async with CollapsibleApp().run_test() as pilot:
collapsible = pilot.app.query_one(Collapsible)
assert not collapsible.collapsed
assert not get_title(collapsible).has_class(COLLAPSED_CLASS)
await pilot.click(Collapsible.Title)
await pilot.click(CollapsibleTitle)
assert collapsible.collapsed
assert get_contents(collapsible).has_class(COLLAPSED_CLASS)
await pilot.click(Collapsible.Title)
await pilot.click(CollapsibleTitle)
assert not collapsible.collapsed
assert not get_title(collapsible).has_class(COLLAPSED_CLASS)