mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge pull request #2508 from davep/add-options
Add `add_options` to `OptionList`
This commit is contained in:
@@ -25,6 +25,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
- Method `DataTable.move_cursor` https://github.com/Textualize/textual/issues/2472
|
||||
|
||||
### Added
|
||||
|
||||
- Added `OptionList.add_options` https://github.com/Textualize/textual/pull/2508
|
||||
|
||||
## [0.23.0] - 2023-05-03
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -4,7 +4,6 @@ from rich.table import Table
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import Footer, Header, OptionList
|
||||
from textual.widgets.option_list import Option, Separator
|
||||
|
||||
COLONIES: tuple[tuple[str, str, str, str], ...] = (
|
||||
("Aerilon", "Demeter", "1.2 Billion", "Gaoth"),
|
||||
|
||||
@@ -7,7 +7,7 @@ forms of bounce-bar menu.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import ClassVar, NamedTuple
|
||||
from typing import ClassVar, Iterable, NamedTuple
|
||||
|
||||
from rich.console import RenderableType
|
||||
from rich.repr import Result
|
||||
@@ -508,6 +508,31 @@ class OptionList(ScrollView, can_focus=True):
|
||||
# list, set the virtual size.
|
||||
self.virtual_size = Size(self.scrollable_content_region.width, len(self._lines))
|
||||
|
||||
def add_options(self, items: Iterable[NewOptionListContent]) -> Self:
|
||||
"""Add new options to the end of the option list.
|
||||
|
||||
Args:
|
||||
items: The new items to add.
|
||||
|
||||
Returns:
|
||||
The `OptionList` instance.
|
||||
|
||||
Raises:
|
||||
DuplicateID: If there is an attempt to use a duplicate ID.
|
||||
"""
|
||||
# Only work if we have items to add; but don't make a fuss out of
|
||||
# zero items to add, just carry on like nothing happened.
|
||||
if items:
|
||||
# Turn any incoming values into valid content for the list.
|
||||
content = [self._make_content(item) for item in items]
|
||||
self._contents.extend(content)
|
||||
# Pull out the content that is genuine options and add them to the
|
||||
# list of options.
|
||||
self._options.extend([item for item in content if isinstance(item, Option)])
|
||||
self._refresh_content_tracking(force=True)
|
||||
self.refresh()
|
||||
return self
|
||||
|
||||
def add_option(self, item: NewOptionListContent = None) -> Self:
|
||||
"""Add a new option to the end of the option list.
|
||||
|
||||
@@ -520,15 +545,7 @@ class OptionList(ScrollView, can_focus=True):
|
||||
Raises:
|
||||
DuplicateID: If there is an attempt to use a duplicate ID.
|
||||
"""
|
||||
# Turn any incoming value into valid content for the list.
|
||||
content = self._make_content(item)
|
||||
self._contents.append(content)
|
||||
# If the content is a genuine option, add it to the list of options.
|
||||
if isinstance(content, Option):
|
||||
self._options.append(content)
|
||||
self._refresh_content_tracking(force=True)
|
||||
self.refresh()
|
||||
return self
|
||||
return self.add_options([item])
|
||||
|
||||
def _remove_option(self, index: int) -> None:
|
||||
"""Remove an option from the option list.
|
||||
|
||||
@@ -106,6 +106,14 @@ async def test_add_later() -> None:
|
||||
assert option_list.option_count == 6
|
||||
option_list.add_option(Option("even more"))
|
||||
assert option_list.option_count == 7
|
||||
option_list.add_options(
|
||||
[Option("more still"), "Yet more options", "so many options!"]
|
||||
)
|
||||
assert option_list.option_count == 10
|
||||
option_list.add_option(None)
|
||||
assert option_list.option_count == 10
|
||||
option_list.add_options([])
|
||||
assert option_list.option_count == 10
|
||||
|
||||
|
||||
async def test_create_with_duplicate_id() -> None:
|
||||
|
||||
File diff suppressed because one or more lines are too long
45
tests/snapshot_tests/snapshot_apps/option_list.py
Normal file
45
tests/snapshot_tests/snapshot_apps/option_list.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from rich.text import Text
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import Horizontal
|
||||
from textual.widgets import OptionList
|
||||
from textual.widgets.option_list import Option
|
||||
|
||||
|
||||
class OptionListApp(App[None]):
|
||||
|
||||
def compose( self ) -> ComposeResult:
|
||||
with Horizontal():
|
||||
yield OptionList(
|
||||
"One",
|
||||
Option("Two"),
|
||||
None,
|
||||
Text.from_markup("[red]Three[/]")
|
||||
)
|
||||
yield OptionList(id="later-individual")
|
||||
yield OptionList(id="later-at-once")
|
||||
|
||||
def on_mount(self) -> None:
|
||||
options: list[None | str | Text | Option] = [
|
||||
"One",
|
||||
Option("Two"),
|
||||
None,
|
||||
Text.from_markup("[red]Three[/]"),
|
||||
]
|
||||
option_list = self.query_one("#later-individual", OptionList)
|
||||
for option in options:
|
||||
option_list.add_option(option)
|
||||
option_list.highlighted = 0
|
||||
option_list = self.query_one("#later-at-once", OptionList)
|
||||
option_list.add_options([
|
||||
"One",
|
||||
Option("Two"),
|
||||
None,
|
||||
Text.from_markup("[red]Three[/]"),
|
||||
])
|
||||
option_list.highlighted = 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
OptionListApp().run()
|
||||
@@ -203,6 +203,8 @@ def test_option_list(snap_compare):
|
||||
assert snap_compare(WIDGET_EXAMPLES_DIR / "option_list_options.py")
|
||||
assert snap_compare(WIDGET_EXAMPLES_DIR / "option_list_tables.py")
|
||||
|
||||
def test_option_list_build(snap_compare):
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "option_list.py")
|
||||
|
||||
def test_progress_bar_indeterminate(snap_compare):
|
||||
assert snap_compare(WIDGET_EXAMPLES_DIR / "progress_bar_isolated_.py", press=["f"])
|
||||
|
||||
Reference in New Issue
Block a user