diff --git a/docs/examples/widgets/clock.py b/docs/examples/widgets/clock.py new file mode 100644 index 0000000000..6b18af3c85 --- /dev/null +++ b/docs/examples/widgets/clock.py @@ -0,0 +1,31 @@ +from datetime import datetime + +from textual.app import App, ComposeResult +from textual.widgets import Digits + + +class ClockApp(App): + CSS = """ + Screen { + align: center middle; + } + #clock { + width: auto; + } + """ + + def compose(self) -> ComposeResult: + yield Digits("", id="clock") + + def on_ready(self) -> None: + self.update_clock() + self.set_interval(1, self.update_clock) + + def update_clock(self) -> None: + clock = datetime.now().time() + self.query_one(Digits).update(f"{clock:%T}") + + +if __name__ == "__main__": + app = ClockApp() + app.run() diff --git a/docs/examples/widgets/digits.py b/docs/examples/widgets/digits.py new file mode 100644 index 0000000000..b7e13f0a5a --- /dev/null +++ b/docs/examples/widgets/digits.py @@ -0,0 +1,22 @@ +from textual.app import App, ComposeResult +from textual.widgets import Digits + + +class DigitApp(App): + CSS = """ + Screen { + align: center middle; + } + #pi { + border: double green; + width: auto; + } + """ + + def compose(self) -> ComposeResult: + yield Digits("3.141,592,653,5897", id="pi") + + +if __name__ == "__main__": + app = DigitApp() + app.run() diff --git a/docs/widget_gallery.md b/docs/widget_gallery.md index 1286d78e0f..da06823945 100644 --- a/docs/widget_gallery.md +++ b/docs/widget_gallery.md @@ -52,6 +52,15 @@ A powerful data table, with configurable cursors. ```{.textual path="docs/examples/widgets/data_table.py"} ``` +## Digits + +Display numbers in tall characters. + +[Digits reference](./widgets/digits.md){ .md-button .md-button--primary } + +```{.textual path="docs/examples/widgets/digits.py"} +``` + ## DirectoryTree A tree view of files and folders. diff --git a/docs/widgets/digits.md b/docs/widgets/digits.md new file mode 100644 index 0000000000..6dd33044ce --- /dev/null +++ b/docs/widgets/digits.md @@ -0,0 +1,66 @@ +# Digits + +!!! tip "Added in version 0.33.0" + +A widget to display numerical values in tall multi-line characters. + +The digits 0-9 are supported, in addition to the following characters `+`, `-`, `^`, `:`, and `×`. +Other characters will be displayed in a regular size font. + +You can set the text to be displayed in the constructor, or call [`update()`][textual.widgets.Digits.update] to change the text after the widget has been mounted. + +!!! note "This widget will respect the [text-align](../styles/text_align.md) rule." + +- [ ] Focusable +- [ ] Container + + +## Example + +The following example displays a few digits of Pi: + +=== "Output" + + ```{.textual path="docs/examples/widgets/digits.py"} + ``` + +=== "digits.py" + + ```python + --8<-- "docs/examples/widgets/digits.py" + ``` + +Here's another example which uses `Digits` to display the current time: + + +=== "Output" + + ```{.textual path="docs/examples/widgets/clock.py"} + ``` + +=== "clock.py" + + ```python + --8<-- "docs/examples/widgets/clock.py" + ``` + +## Reactive attributes + +This widget has no reactive attributes. + +## Bindings + +This widget has no bindings. + +## Component classes + +This widget has no component classes. + + + +--- + + +::: textual.widgets.Digits + options: + heading_level: 2 diff --git a/mkdocs-nav.yml b/mkdocs-nav.yml index d97cda74ed..adacbb6723 100644 --- a/mkdocs-nav.yml +++ b/mkdocs-nav.yml @@ -1,204 +1,205 @@ 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" - - "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/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/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/containers.md" - - "api/coordinate.md" - - "api/dom_node.md" - - "api/events.md" - - "api/errors.md" - - "api/filter.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/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" - - "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" + - "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/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/containers.md" + - "api/coordinate.md" + - "api/dom_node.md" + - "api/events.md" + - "api/errors.md" + - "api/filter.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/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" + - "roadmap.md" + - "Blog": + - blog/index.md diff --git a/src/textual/renderables/digits.py b/src/textual/renderables/digits.py new file mode 100644 index 0000000000..f22943e414 --- /dev/null +++ b/src/textual/renderables/digits.py @@ -0,0 +1,128 @@ +from __future__ import annotations + +from rich.console import Console, ConsoleOptions, RenderResult +from rich.measure import Measurement +from rich.segment import Segment +from rich.style import Style, StyleType + +DIGITS = " 0123456789+-^x:" +DIGITS3X3 = """\ + + + +┏━┓ +┃ ┃ +┗━┛ + ┓ + ┃ +╺┻╸ +╺━┓ +┏━┛ +┗━╸ +╺━┓ + ━┫ +╺━┛ +╻ ╻ +┗━┫ + ╹ +┏━╸ +┗━┓ +╺━┛ +┏━╸ +┣━┓ +┗━┛ +╺━┓ + ┃ + ╹ +┏━┓ +┣━┫ +┗━┛ +┏━┓ +┗━┫ +╺━┛ + +╺╋╸ + + +╺━╸ + + ^ + + + + × + + + : + +""".splitlines() + + +class Digits: + """Renders a 3X3 unicode 'font' for numerical values. + + Args: + text: Text to display. + style: Style to apply to the digits. + + """ + + def __init__(self, text: str, style: StyleType = "") -> None: + self._text = text + self._style = style + + def __rich_console__( + self, console: Console, options: ConsoleOptions + ) -> RenderResult: + style = console.get_style(self._style) + yield from self.render(style) + + def render(self, style: Style) -> RenderResult: + """Render with the given style + + Args: + style: Rich Style. + + Returns: + Result of render. + """ + digit_pieces: list[list[str]] = [[], [], []] + row1 = digit_pieces[0].append + row2 = digit_pieces[1].append + row3 = digit_pieces[2].append + + for character in self._text: + try: + position = DIGITS.index(character) * 3 + except ValueError: + row1(" ") + row2(" ") + row3(character) + else: + row1(DIGITS3X3[position].ljust(3)) + row2(DIGITS3X3[position + 1].ljust(3)) + row3(DIGITS3X3[position + 2].ljust(3)) + + new_line = Segment.line() + for line in digit_pieces: + yield Segment("".join(line), style) + yield new_line + + @classmethod + def get_width(cls, text: str) -> int: + """Calculate the width without rendering. + + Args: + text: Text which may be displayed in the `Digits` widget. + + Returns: + width of the text (in cells). + """ + width = sum(3 if character in DIGITS else 1 for character in text) + return width + + def __rich_measure__( + self, console: Console, options: ConsoleOptions + ) -> Measurement: + width = self.get_width(self._text) + return Measurement(width, width) diff --git a/src/textual/widgets/__init__.py b/src/textual/widgets/__init__.py index d21bcd47a2..8c71dfa7fd 100644 --- a/src/textual/widgets/__init__.py +++ b/src/textual/widgets/__init__.py @@ -14,6 +14,7 @@ from ._checkbox import Checkbox from ._content_switcher import ContentSwitcher from ._data_table import DataTable + from ._digits import Digits from ._directory_tree import DirectoryTree from ._footer import Footer from ._header import Header @@ -48,6 +49,7 @@ "Checkbox", "ContentSwitcher", "DataTable", + "Digits", "DirectoryTree", "Footer", "Header", diff --git a/src/textual/widgets/__init__.pyi b/src/textual/widgets/__init__.pyi index bae8df27c5..86f17d13cb 100644 --- a/src/textual/widgets/__init__.pyi +++ b/src/textual/widgets/__init__.pyi @@ -3,6 +3,7 @@ from ._button import Button as Button from ._checkbox import Checkbox as Checkbox from ._content_switcher import ContentSwitcher as ContentSwitcher from ._data_table import DataTable as DataTable +from ._digits import Digits as Digits from ._directory_tree import DirectoryTree as DirectoryTree from ._footer import Footer as Footer from ._header import Header as Header diff --git a/src/textual/widgets/_digits.py b/src/textual/widgets/_digits.py new file mode 100644 index 0000000000..799811ac70 --- /dev/null +++ b/src/textual/widgets/_digits.py @@ -0,0 +1,102 @@ +from __future__ import annotations + +from typing import cast + +from rich.align import Align, AlignMethod +from rich.console import RenderableType + +from ..geometry import Size +from ..renderables.digits import Digits as DigitsRenderable +from ..widget import Widget + + +class Digits(Widget): + """A widget to display numerical values using a 3x3 grid of unicode characters. + + Args: + value: Value to display in widget. + name: The name of the widget. + id: The ID of the widget in the DOM. + classes: The CSS classes of the widget. + disabled: Whether the widget is disabled or not. + + """ + + DEFAULT_CSS = """ + Digits { + width: 1fr; + height: auto; + text-align: left; + text-style: bold; + box-sizing: border-box; + } + """ + + def __init__( + self, + value: str = "", + *, + name: str | None = None, + id: str | None = None, + classes: str | None = None, + disabled: bool = False, + ) -> None: + if not isinstance(value, str): + raise TypeError("value must be a str") + super().__init__(name=name, id=id, classes=classes, disabled=disabled) + self._value = value + + @property + def value(self) -> str: + """The current value displayed in the Digits.""" + return self._value + + def update(self, value: str) -> None: + """Update the Digits with a new value. + + Args: + value: New value to display. + + Raises: + ValueError: If the value isn't a `str`. + """ + if not isinstance(value, str): + raise TypeError("value must be a str") + layout_required = len(value) != len(self._value) or ( + DigitsRenderable.get_width(self._value) != DigitsRenderable.get_width(value) + ) + self._value = value + self.refresh(layout=layout_required) + + def render(self) -> RenderableType: + """Render digits.""" + rich_style = self.rich_style + digits = DigitsRenderable(self._value, rich_style) + text_align = self.styles.text_align + align = "left" if text_align not in {"left", "center", "right"} else text_align + return Align(digits, cast(AlignMethod, align), rich_style) + + def get_content_width(self, container: Size, viewport: Size) -> int: + """Called by textual to get the width of the content area. + + Args: + container: Size of the container (immediate parent) widget. + viewport: Size of the viewport. + + Returns: + The optimal width of the content. + """ + return DigitsRenderable.get_width(self._value) + + def get_content_height(self, container: Size, viewport: Size, width: int) -> int: + """Called by Textual to get the height of the content area. + + Args: + container: Size of the container (immediate parent) widget. + viewport: Size of the viewport. + width: Width of renderable. + + Returns: + The height of the content. + """ + return 3 # Always 3 lines diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 803455113a..1802220c43 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -13719,6 +13719,162 @@ ''' # --- +# name: test_digits + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DigitApp + + + + + + + + + + ╺━┓  ┓ ╻ ╻╺━┓╺━┓ +  ━┫  ┃ ┗━┫┏━┛  ┃ + ╺━┛.╺┻╸  ╹┗━╸  ╹ +    ┏━┓ ┓ ╺━┓╺━┓╻ ╻┏━╸┏━╸╺━┓┏━┓┏━┓         +    ┃ ┃ ┃ ┏━┛ ━┫┗━┫┗━┓┣━┓  ┃┣━┫┗━┫╺╋╸╺━╸   +    ┗━┛╺┻╸┗━╸╺━┛  ╹╺━┛┗━┛  ╹┗━┛╺━┛      ., + ╺━┓    ┓ ┏━┓ ^ ╻ ╻ +  ━┫ ×  ┃ ┃ ┃   ┗━┫ + ╺━┛   ╺┻╸┗━┛     ╹ + + + + + + + + + + + + + + + + + + + + ''' +# --- # name: test_disabled_widgets ''' diff --git a/tests/snapshot_tests/snapshot_apps/digits.py b/tests/snapshot_tests/snapshot_apps/digits.py new file mode 100644 index 0000000000..f4c1be6e01 --- /dev/null +++ b/tests/snapshot_tests/snapshot_apps/digits.py @@ -0,0 +1,26 @@ +from textual.app import App, ComposeResult +from textual.widgets import Digits + + +class DigitApp(App): + CSS = """ + #digits1 { + text-align: left; + } + #digits2 { + text-align:center; + } + #digits3 { + text-align:right; + } + """ + + def compose(self) -> ComposeResult: + yield Digits("3.1427", id="digits1") + yield Digits(" 0123456789+-.,", id="digits2") + yield Digits("3x10^4", id="digits3") + + +if __name__ == "__main__": + app = DigitApp() + app.run() diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index 5c2d07b24e..9d2257f667 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -239,15 +239,21 @@ def test_option_list_build(snap_compare): def test_option_list_replace_prompt_from_single_line_to_single_line(snap_compare): - assert snap_compare(SNAPSHOT_APPS_DIR / "option_list_multiline_options.py", press=["1"]) + assert snap_compare( + SNAPSHOT_APPS_DIR / "option_list_multiline_options.py", press=["1"] + ) def test_option_list_replace_prompt_from_single_line_to_two_lines(snap_compare): - assert snap_compare(SNAPSHOT_APPS_DIR / "option_list_multiline_options.py", press=["2"]) + assert snap_compare( + SNAPSHOT_APPS_DIR / "option_list_multiline_options.py", press=["2"] + ) def test_option_list_replace_prompt_from_two_lines_to_three_lines(snap_compare): - assert snap_compare(SNAPSHOT_APPS_DIR / "option_list_multiline_options.py", press=["3"]) + assert snap_compare( + SNAPSHOT_APPS_DIR / "option_list_multiline_options.py", press=["3"] + ) def test_progress_bar_indeterminate(snap_compare): @@ -622,3 +628,7 @@ def test_text_log_blank_write(snap_compare) -> None: def test_nested_fr(snap_compare) -> None: # https://github.com/Textualize/textual/pull/3059 assert snap_compare(SNAPSHOT_APPS_DIR / "nested_fr.py") + + +def test_digits(snap_compare) -> None: + assert snap_compare(SNAPSHOT_APPS_DIR / "digits.py")