mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
File diff suppressed because one or more lines are too long
29
tests/snapshot_tests/snapshot_apps/alignment_containers.py
Normal file
29
tests/snapshot_tests/snapshot_apps/alignment_containers.py
Normal file
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
App to test alignment containers.
|
||||
"""
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import Center, Middle
|
||||
from textual.widgets import Button
|
||||
|
||||
|
||||
class AlignContainersApp(App[None]):
|
||||
CSS = """
|
||||
Center {
|
||||
tint: $primary 10%;
|
||||
}
|
||||
Middle {
|
||||
tint: $secondary 10%;
|
||||
}
|
||||
"""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
with Center():
|
||||
yield Button.success("center")
|
||||
with Middle():
|
||||
yield Button.error("middle")
|
||||
|
||||
|
||||
app = AlignContainersApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
@@ -1,10 +1,9 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import Vertical
|
||||
from textual.containers import VerticalScroll
|
||||
from textual.widgets import Header, Footer, Label, Input
|
||||
|
||||
|
||||
class InputWidthAutoApp(App[None]):
|
||||
|
||||
CSS = """
|
||||
Input.auto {
|
||||
width: auto;
|
||||
|
||||
@@ -2,6 +2,7 @@ from textual.app import App, ComposeResult
|
||||
from textual.containers import Horizontal
|
||||
from textual.widgets import Button
|
||||
|
||||
|
||||
class WidgetDisableTestApp(App[None]):
|
||||
CSS = """
|
||||
Horizontal {
|
||||
@@ -28,5 +29,6 @@ class WidgetDisableTestApp(App[None]):
|
||||
yield Button(variant="warning")
|
||||
yield Button(variant="error")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
WidgetDisableTestApp().run()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from rich.text import Text
|
||||
|
||||
from textual.app import App, ComposeResult, RenderResult
|
||||
from textual.containers import Vertical
|
||||
from textual.containers import VerticalScroll
|
||||
from textual.widgets import Header, Footer
|
||||
from textual.widget import Widget
|
||||
|
||||
@@ -32,7 +32,7 @@ class Tester(Widget, can_focus=True):
|
||||
class StyleBugApp(App[None]):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Header()
|
||||
with Vertical():
|
||||
with VerticalScroll():
|
||||
for n in range(40):
|
||||
yield Tester(n)
|
||||
yield Footer()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import Horizontal, Vertical
|
||||
from textual.containers import Horizontal, VerticalScroll
|
||||
from textual.widgets import Static
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ class StaticText(Static):
|
||||
|
||||
|
||||
class FRApp(App):
|
||||
|
||||
CSS = """
|
||||
StaticText {
|
||||
height: 1fr;
|
||||
@@ -39,7 +38,7 @@ class FRApp(App):
|
||||
"""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Vertical(
|
||||
yield VerticalScroll(
|
||||
StaticText("HEADER", id="header"),
|
||||
Horizontal(
|
||||
StaticText("foo", id="foo"),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.screen import Screen
|
||||
from textual.widgets import Header, Footer, Label
|
||||
from textual.containers import Vertical, Container
|
||||
from textual.containers import VerticalScroll, Container
|
||||
|
||||
|
||||
class Overlay(Container):
|
||||
@@ -9,12 +9,12 @@ class Overlay(Container):
|
||||
yield Label("This should float over the top")
|
||||
|
||||
|
||||
class Body1(Vertical):
|
||||
class Body1(VerticalScroll):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Label("My God! It's full of stars! " * 300)
|
||||
|
||||
|
||||
class Body2(Vertical):
|
||||
class Body2(VerticalScroll):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Label("My God! It's full of stars! " * 300)
|
||||
|
||||
@@ -36,7 +36,6 @@ class Bad(Screen):
|
||||
|
||||
|
||||
class Layers(App[None]):
|
||||
|
||||
CSS = """
|
||||
Screen {
|
||||
layers: base higher;
|
||||
|
||||
56
tests/snapshot_tests/snapshot_apps/layout_containers.py
Normal file
56
tests/snapshot_tests/snapshot_apps/layout_containers.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""
|
||||
App to test layout containers.
|
||||
"""
|
||||
|
||||
from typing import Iterable
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import (
|
||||
Grid,
|
||||
Horizontal,
|
||||
HorizontalScroll,
|
||||
Vertical,
|
||||
VerticalScroll,
|
||||
)
|
||||
from textual.widget import Widget
|
||||
from textual.widgets import Button, Input, Label
|
||||
|
||||
|
||||
def sub_compose() -> Iterable[Widget]:
|
||||
yield Button.success("Accept")
|
||||
yield Button.error("Decline")
|
||||
yield Input()
|
||||
yield Label("\n\n".join([str(n * 1_000_000) for n in range(10)]))
|
||||
|
||||
|
||||
class MyApp(App[None]):
|
||||
CSS = """
|
||||
Grid {
|
||||
grid-size: 2 2;
|
||||
grid-rows: 1fr;
|
||||
grid-columns: 1fr;
|
||||
}
|
||||
Grid > Widget {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
Input {
|
||||
width: 80;
|
||||
}
|
||||
"""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
with Grid():
|
||||
with Horizontal():
|
||||
yield from sub_compose()
|
||||
with HorizontalScroll():
|
||||
yield from sub_compose()
|
||||
with Vertical():
|
||||
yield from sub_compose()
|
||||
with VerticalScroll():
|
||||
yield from sub_compose()
|
||||
|
||||
|
||||
app = MyApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
@@ -1,7 +1,7 @@
|
||||
from rich.text import Text
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import Vertical
|
||||
from textual.containers import VerticalScroll
|
||||
from textual.widget import Widget
|
||||
from textual.widgets import TextLog
|
||||
|
||||
@@ -21,32 +21,32 @@ class ScrollViewApp(App):
|
||||
Screen {
|
||||
align: center middle;
|
||||
}
|
||||
|
||||
|
||||
TextLog {
|
||||
width:13;
|
||||
height:10;
|
||||
height:10;
|
||||
}
|
||||
|
||||
Vertical{
|
||||
VerticalScroll {
|
||||
width:13;
|
||||
height: 10;
|
||||
overflow: scroll;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
MyWidget {
|
||||
width:13;
|
||||
height:auto;
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield TextLog()
|
||||
yield Vertical(MyWidget())
|
||||
yield VerticalScroll(MyWidget())
|
||||
|
||||
def on_ready(self) -> None:
|
||||
self.query_one(TextLog).write("\n".join(f"{n} 0123456789" for n in range(20)))
|
||||
self.query_one(Vertical).scroll_end(animate=False)
|
||||
self.query_one(VerticalScroll).scroll_end(animate=False)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import Vertical
|
||||
from textual.containers import VerticalScroll
|
||||
from textual.widgets import Static
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ class NestedAutoApp(App[None]):
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
self._static = Static("", id="my-static")
|
||||
yield Vertical(
|
||||
Vertical(
|
||||
yield VerticalScroll(
|
||||
VerticalScroll(
|
||||
self._static,
|
||||
id="my-static-wrapper",
|
||||
),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import Vertical
|
||||
from textual.containers import VerticalScroll
|
||||
from textual.widgets import Label, Static
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ class Box(Static):
|
||||
|
||||
|
||||
class OffsetsApp(App):
|
||||
|
||||
CSS = """
|
||||
|
||||
#box1 {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from textual.app import App
|
||||
from textual.containers import Vertical
|
||||
from textual.containers import VerticalScroll
|
||||
from textual.widgets import Static
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ class Visibility(App):
|
||||
Screen {
|
||||
layout: horizontal;
|
||||
}
|
||||
Vertical {
|
||||
VerticalScroll {
|
||||
width: 1fr;
|
||||
border: solid red;
|
||||
}
|
||||
@@ -30,13 +30,12 @@ class Visibility(App):
|
||||
"""
|
||||
|
||||
def compose(self):
|
||||
|
||||
yield Vertical(
|
||||
yield VerticalScroll(
|
||||
Static("foo"),
|
||||
Static("float", classes="float"),
|
||||
id="container1",
|
||||
)
|
||||
yield Vertical(
|
||||
yield VerticalScroll(
|
||||
Static("bar"),
|
||||
Static("float", classes="float"),
|
||||
id="container2",
|
||||
|
||||
@@ -47,6 +47,14 @@ def test_dock_layout_sidebar(snap_compare):
|
||||
assert snap_compare(LAYOUT_EXAMPLES_DIR / "dock_layout2_sidebar.py")
|
||||
|
||||
|
||||
def test_layout_containers(snap_compare):
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "layout_containers.py")
|
||||
|
||||
|
||||
def test_alignment_containers(snap_compare):
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "alignment_containers.py")
|
||||
|
||||
|
||||
# --- Widgets - rendering and basic interactions ---
|
||||
# Each widget should have a canonical example that is display in the docs.
|
||||
# When adding a new widget, ideally we should also create a snapshot test
|
||||
|
||||
100
tests/test_containers.py
Normal file
100
tests/test_containers.py
Normal file
@@ -0,0 +1,100 @@
|
||||
"""Test basic functioning of some containers."""
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import (
|
||||
Center,
|
||||
Horizontal,
|
||||
HorizontalScroll,
|
||||
Middle,
|
||||
Vertical,
|
||||
VerticalScroll,
|
||||
)
|
||||
from textual.widgets import Label
|
||||
|
||||
|
||||
async def test_horizontal_vs_horizontalscroll_scrolling():
|
||||
"""Check the default scrollbar behaviours for `Horizontal` and `HorizontalScroll`."""
|
||||
|
||||
class HorizontalsApp(App[None]):
|
||||
CSS = """
|
||||
Screen {
|
||||
layout: vertical;
|
||||
}
|
||||
"""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
with Horizontal():
|
||||
for _ in range(10):
|
||||
yield Label("How is life going? " * 3 + " | ")
|
||||
with HorizontalScroll():
|
||||
for _ in range(10):
|
||||
yield Label("How is life going? " * 3 + " | ")
|
||||
|
||||
WIDTH = 80
|
||||
HEIGHT = 24
|
||||
app = HorizontalsApp()
|
||||
async with app.run_test(size=(WIDTH, HEIGHT)):
|
||||
horizontal = app.query_one(Horizontal)
|
||||
horizontal_scroll = app.query_one(HorizontalScroll)
|
||||
assert horizontal.size.height == horizontal_scroll.size.height
|
||||
assert horizontal.scrollbars_enabled == (False, False)
|
||||
assert horizontal_scroll.scrollbars_enabled == (False, True)
|
||||
|
||||
|
||||
async def test_vertical_vs_verticalscroll_scrolling():
|
||||
"""Check the default scrollbar behaviours for `Vertical` and `VerticalScroll`."""
|
||||
|
||||
class VerticalsApp(App[None]):
|
||||
CSS = """
|
||||
Screen {
|
||||
layout: horizontal;
|
||||
}
|
||||
"""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
with Vertical():
|
||||
for _ in range(10):
|
||||
yield Label("How is life going?\n" * 3 + "\n\n")
|
||||
with VerticalScroll():
|
||||
for _ in range(10):
|
||||
yield Label("How is life going?\n" * 3 + "\n\n")
|
||||
|
||||
WIDTH = 80
|
||||
HEIGHT = 24
|
||||
app = VerticalsApp()
|
||||
async with app.run_test(size=(WIDTH, HEIGHT)):
|
||||
vertical = app.query_one(Vertical)
|
||||
vertical_scroll = app.query_one(VerticalScroll)
|
||||
assert vertical.size.width == vertical_scroll.size.width
|
||||
assert vertical.scrollbars_enabled == (False, False)
|
||||
assert vertical_scroll.scrollbars_enabled == (True, False)
|
||||
|
||||
|
||||
async def test_center_container():
|
||||
"""Check the size of the container `Center`."""
|
||||
|
||||
class CenterApp(App[None]):
|
||||
def compose(self) -> ComposeResult:
|
||||
with Center():
|
||||
yield Label("<>\n<>\n<>")
|
||||
|
||||
app = CenterApp()
|
||||
async with app.run_test():
|
||||
center = app.query_one(Center)
|
||||
assert center.size.width == app.size.width
|
||||
assert center.size.height == 3
|
||||
|
||||
|
||||
async def test_middle_container():
|
||||
"""Check the size of the container `Middle`."""
|
||||
|
||||
class MiddleApp(App[None]):
|
||||
def compose(self) -> ComposeResult:
|
||||
with Middle():
|
||||
yield Label("1234")
|
||||
|
||||
app = MiddleApp()
|
||||
async with app.run_test():
|
||||
middle = app.query_one(Middle)
|
||||
assert middle.size.width == 4
|
||||
assert middle.size.height == app.size.height
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Test Widget.disabled."""
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import Vertical
|
||||
from textual.containers import VerticalScroll
|
||||
from textual.widgets import (
|
||||
Button,
|
||||
DataTable,
|
||||
@@ -21,7 +21,7 @@ class DisableApp(App[None]):
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
"""Compose the child widgets."""
|
||||
yield Vertical(
|
||||
yield VerticalScroll(
|
||||
Button(),
|
||||
DataTable(),
|
||||
DirectoryTree("."),
|
||||
@@ -56,7 +56,7 @@ async def test_enabled_widgets_have_enabled_pseudo_class() -> None:
|
||||
async def test_all_individually_disabled() -> None:
|
||||
"""Post-disable all widgets should report being disabled."""
|
||||
async with DisableApp().run_test() as pilot:
|
||||
for node in pilot.app.screen.query("Vertical > *"):
|
||||
for node in pilot.app.screen.query("VerticalScroll > *"):
|
||||
node.disabled = True
|
||||
assert all(
|
||||
node.disabled for node in pilot.app.screen.query("#test-container > *")
|
||||
@@ -77,7 +77,7 @@ async def test_disabled_widgets_have_disabled_pseudo_class() -> None:
|
||||
async def test_disable_via_container() -> None:
|
||||
"""All child widgets should appear (to CSS) as disabled by a container being disabled."""
|
||||
async with DisableApp().run_test() as pilot:
|
||||
pilot.app.screen.query_one("#test-container", Vertical).disabled = True
|
||||
pilot.app.screen.query_one("#test-container", VerticalScroll).disabled = True
|
||||
assert all(
|
||||
node.has_pseudo_class("disabled") and not node.has_pseudo_class("enabled")
|
||||
for node in pilot.app.screen.query("#test-container > *")
|
||||
|
||||
@@ -151,11 +151,11 @@ def test_focus_next_and_previous_with_type_selector_without_self():
|
||||
|
||||
screen = app.screen
|
||||
|
||||
from textual.containers import Horizontal, Vertical
|
||||
from textual.containers import Horizontal, VerticalScroll
|
||||
from textual.widgets import Button, Input, Switch
|
||||
|
||||
screen._add_children(
|
||||
Vertical(
|
||||
VerticalScroll(
|
||||
Horizontal(
|
||||
Input(id="w3"),
|
||||
Switch(id="w4"),
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
"""Regression test for #1616 https://github.com/Textualize/textual/issues/1616"""
|
||||
import pytest
|
||||
|
||||
from textual.app import App
|
||||
from textual.containers import Vertical
|
||||
from textual.containers import VerticalScroll
|
||||
|
||||
|
||||
async def test_overflow_change_updates_virtual_size_appropriately():
|
||||
class MyApp(App):
|
||||
def compose(self):
|
||||
yield Vertical()
|
||||
yield VerticalScroll()
|
||||
|
||||
app = MyApp()
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
vertical = app.query_one(Vertical)
|
||||
vertical = app.query_one(VerticalScroll)
|
||||
|
||||
height = vertical.virtual_size.height
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""See https://github.com/Textualize/textual/issues/1355 as the motivation for these tests."""
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import Vertical
|
||||
from textual.containers import VerticalScroll
|
||||
from textual.widget import Widget
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class VisibleTester(App[None]):
|
||||
"""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Vertical(
|
||||
yield VerticalScroll(
|
||||
Widget(id="keep"), Widget(id="hide-via-code"), Widget(id="hide-via-css")
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user