Adding upstream version 0.12.0.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
d887bee5ca
commit
148efc9122
69 changed files with 12923 additions and 0 deletions
21
tests/snapshot_tests/LICENSE
Normal file
21
tests/snapshot_tests/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 Will McGugan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
2924
tests/snapshot_tests/__snapshots__/test_snapshots.ambr
Normal file
2924
tests/snapshot_tests/__snapshots__/test_snapshots.ambr
Normal file
File diff suppressed because one or more lines are too long
1814
tests/snapshot_tests/_snapshots_backup/test_snapshots.ambr
Normal file
1814
tests/snapshot_tests/_snapshots_backup/test_snapshots.ambr
Normal file
File diff suppressed because one or more lines are too long
185
tests/snapshot_tests/snapshot_apps/auto-table.py
Normal file
185
tests/snapshot_tests/snapshot_apps/auto-table.py
Normal file
|
@ -0,0 +1,185 @@
|
|||
from textual.app import App
|
||||
from textual.containers import Container, Horizontal, ScrollableContainer, Vertical
|
||||
from textual.screen import Screen
|
||||
from textual.widgets import Header, Label
|
||||
from textual_fastdatatable import ArrowBackend, DataTable
|
||||
|
||||
|
||||
class LabeledBox(Container):
|
||||
DEFAULT_CSS = """
|
||||
LabeledBox {
|
||||
layers: base_ top_;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
LabeledBox > Container {
|
||||
layer: base_;
|
||||
border: round $primary;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
layout: vertical;
|
||||
}
|
||||
|
||||
LabeledBox > Label {
|
||||
layer: top_;
|
||||
offset-x: 2;
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(self, title, *args, **kwargs):
|
||||
self.__label = Label(title)
|
||||
|
||||
super().__init__(self.__label, Container(*args, **kwargs))
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
return self.__label
|
||||
|
||||
|
||||
class StatusTable(DataTable):
|
||||
def __init__(self) -> None:
|
||||
backend = ArrowBackend.from_pydict(
|
||||
{
|
||||
"Foo": ["ABCDEFGH"] * 50,
|
||||
"Bar": ["0123456789"] * 50,
|
||||
"Baz": ["IJKLMNOPQRSTUVWXYZ"] * 50,
|
||||
}
|
||||
)
|
||||
super().__init__(backend=backend)
|
||||
|
||||
self.cursor_type = "row"
|
||||
self.show_cursor = False
|
||||
|
||||
|
||||
class Status(LabeledBox):
|
||||
DEFAULT_CSS = """
|
||||
Status {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
Status Container {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
Status StatusTable {
|
||||
width: auto;
|
||||
height: 100%;
|
||||
margin-top: 1;
|
||||
scrollbar-gutter: stable;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(self, name: str):
|
||||
self.__name = name
|
||||
self.__table = StatusTable()
|
||||
|
||||
super().__init__(f" {self.__name} ", self.__table)
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self.__name
|
||||
|
||||
@property
|
||||
def table(self) -> StatusTable:
|
||||
return self.__table
|
||||
|
||||
|
||||
class Rendering(LabeledBox):
|
||||
DEFAULT_CSS = """
|
||||
#issue-info {
|
||||
height: auto;
|
||||
border-bottom: dashed #632CA6;
|
||||
}
|
||||
|
||||
#statuses-box {
|
||||
height: 1fr;
|
||||
width: auto;
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.__info = Label("test")
|
||||
|
||||
super().__init__(
|
||||
"",
|
||||
ScrollableContainer(
|
||||
Horizontal(self.__info, id="issue-info"),
|
||||
Horizontal(*[Status(str(i)) for i in range(4)], id="statuses-box"),
|
||||
id="issues-box",
|
||||
),
|
||||
)
|
||||
|
||||
@property
|
||||
def info(self) -> Label:
|
||||
return self.__info
|
||||
|
||||
|
||||
class Sidebar(LabeledBox):
|
||||
DEFAULT_CSS = """
|
||||
#sidebar-status {
|
||||
height: auto;
|
||||
border-bottom: dashed #632CA6;
|
||||
}
|
||||
|
||||
#sidebar-options {
|
||||
height: 1fr;
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.__status = Label("ok")
|
||||
self.__options = Vertical()
|
||||
|
||||
super().__init__(
|
||||
"",
|
||||
Container(self.__status, id="sidebar-status"),
|
||||
Container(self.__options, id="sidebar-options"),
|
||||
)
|
||||
|
||||
@property
|
||||
def status(self) -> Label:
|
||||
return self.__status
|
||||
|
||||
@property
|
||||
def options(self) -> Vertical:
|
||||
return self.__options
|
||||
|
||||
|
||||
class MyScreen(Screen):
|
||||
DEFAULT_CSS = """
|
||||
#main-content {
|
||||
layout: grid;
|
||||
grid-size: 2;
|
||||
grid-columns: 1fr 5fr;
|
||||
grid-rows: 1fr;
|
||||
}
|
||||
|
||||
#main-content-sidebar {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#main-content-rendering {
|
||||
height: 100%;
|
||||
}
|
||||
"""
|
||||
|
||||
def compose(self):
|
||||
yield Header()
|
||||
yield Container(
|
||||
Container(Sidebar(), id="main-content-sidebar"),
|
||||
Container(Rendering(), id="main-content-rendering"),
|
||||
id="main-content",
|
||||
)
|
||||
|
||||
|
||||
class MyApp(App):
|
||||
async def on_mount(self):
|
||||
self.install_screen(MyScreen(), "myscreen")
|
||||
await self.push_screen("myscreen")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = MyApp()
|
||||
app.run()
|
26
tests/snapshot_tests/snapshot_apps/data_table.py
Normal file
26
tests/snapshot_tests/snapshot_apps/data_table.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
from textual.app import App, ComposeResult
|
||||
from textual_fastdatatable import ArrowBackend, DataTable
|
||||
|
||||
ROWS = [
|
||||
("lane", "swimmer", "country", "time"),
|
||||
(4, "Joseph Schooling", "Singapore", 50.39),
|
||||
(2, "Michael Phelps", "United States", 51.14),
|
||||
(5, "Chad le Clos", "South Africa", 51.14),
|
||||
(6, "László Cseh", "Hungary", 51.14),
|
||||
(3, "Li Zhuhao", "China", 51.26),
|
||||
(8, "Mehdy Metella", "France", 51.58),
|
||||
(7, "Tom Shields", "United States", 51.73),
|
||||
(1, "Aleksandr Sadovnikov", "Russia", 51.84),
|
||||
(10, "Darren Burns", "Scotland", 51.84),
|
||||
]
|
||||
|
||||
|
||||
class TableApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
backend = ArrowBackend.from_records(ROWS, has_header=True)
|
||||
yield DataTable(backend=backend)
|
||||
|
||||
|
||||
app = TableApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
36
tests/snapshot_tests/snapshot_apps/data_table_add_column.py
Normal file
36
tests/snapshot_tests/snapshot_apps/data_table_add_column.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
from textual.app import App, ComposeResult
|
||||
from textual.binding import Binding
|
||||
from textual_fastdatatable import ArrowBackend, DataTable
|
||||
|
||||
MOVIES = [
|
||||
"Severance",
|
||||
"Foundation",
|
||||
"Dark",
|
||||
"The Boys",
|
||||
"The Last of Us",
|
||||
"Lost in Space",
|
||||
"Altered Carbon",
|
||||
]
|
||||
|
||||
|
||||
class AddColumn(App):
|
||||
BINDINGS = [
|
||||
Binding(key="c", action="add_column", description="Add Column"),
|
||||
]
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
backend = ArrowBackend.from_pydict({"Movies": MOVIES})
|
||||
table = DataTable(backend=backend)
|
||||
|
||||
column_idx = table.add_column("No Default")
|
||||
table.add_column("With Default", default="ABC")
|
||||
table.add_column("Long Default", default="01234567890123456789")
|
||||
|
||||
# Ensure we can update a cell
|
||||
table.update_cell(2, column_idx, "Hello!")
|
||||
yield table
|
||||
|
||||
|
||||
app = AddColumn()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
|
@ -0,0 +1,24 @@
|
|||
from rich.panel import Panel
|
||||
from rich.text import Text
|
||||
from textual.app import App
|
||||
from textual_fastdatatable import DataTable
|
||||
|
||||
|
||||
class AutoHeightRowsApp(App[None]):
|
||||
def compose(self):
|
||||
table = DataTable()
|
||||
self.column = table.add_column("N")
|
||||
table.add_column("Column", width=10)
|
||||
table.add_row(3, "hey there", height=None)
|
||||
table.add_row(1, Text("hey there"), height=None)
|
||||
table.add_row(5, Text("long string", overflow="fold"), height=None)
|
||||
table.add_row(2, Panel.fit("Hello\nworld"), height=None)
|
||||
table.add_row(4, "1\n2\n3\n4\n5\n6\n7", height=None)
|
||||
yield table
|
||||
|
||||
def key_s(self):
|
||||
self.query_one(DataTable).sort(self.column)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
AutoHeightRowsApp().run()
|
|
@ -0,0 +1,35 @@
|
|||
import csv
|
||||
import io
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual_fastdatatable import ArrowBackend, DataTable
|
||||
|
||||
CSV = """lane,swimmer,country,time
|
||||
4,Joseph Schooling,Singapore,50.39
|
||||
2,Michael Phelps,United States,51.14
|
||||
5,Chad le Clos,South Africa,51.14
|
||||
6,László Cseh,Hungary,51.14
|
||||
3,Li Zhuhao,China,51.26
|
||||
8,Mehdy Metella,France,51.58
|
||||
7,Tom Shields,United States,51.73
|
||||
1,Aleksandr Sadovnikov,Russia,51.84"""
|
||||
|
||||
|
||||
class TableApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
rows = csv.reader(io.StringIO(CSV))
|
||||
labels = next(rows)
|
||||
data = [row for row in rows]
|
||||
backend = ArrowBackend.from_pydict(
|
||||
{label: [row[i] for row in data] for i, label in enumerate(labels)}
|
||||
)
|
||||
table = DataTable(
|
||||
backend=backend, cursor_type="column", fixed_columns=1, fixed_rows=1
|
||||
)
|
||||
table.focus()
|
||||
yield table
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = TableApp()
|
||||
app.run()
|
35
tests/snapshot_tests/snapshot_apps/data_table_max_width.py
Normal file
35
tests/snapshot_tests/snapshot_apps/data_table_max_width.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
import csv
|
||||
import io
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual_fastdatatable import ArrowBackend, DataTable
|
||||
|
||||
CSV = """lane,swimmer,country,time
|
||||
4,Joseph Schooling,Singapore,50.39
|
||||
2,Michael Phelps,United States,51.14
|
||||
5,Chad le Clos,South Africa,51.14
|
||||
6,László Cseh,Hungary,51.14
|
||||
3,Li Zhuhao,China,51.26
|
||||
8,Mehdy Metella,France,51.58
|
||||
7,Tom Shields,United States,51.73
|
||||
1,Aleksandr Sadovnikov,Russia,51.84"""
|
||||
|
||||
|
||||
class TableApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
rows = csv.reader(io.StringIO(CSV))
|
||||
labels = next(rows)
|
||||
data = [row for row in rows]
|
||||
backend = ArrowBackend.from_pydict(
|
||||
{label: [row[i] for row in data] for i, label in enumerate(labels)}
|
||||
)
|
||||
table = DataTable(
|
||||
backend=backend, cursor_type="range", max_column_content_width=8
|
||||
)
|
||||
table.focus()
|
||||
yield table
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = TableApp()
|
||||
app.run()
|
|
@ -0,0 +1,26 @@
|
|||
from textual.app import App, ComposeResult
|
||||
from textual_fastdatatable import ArrowBackend, DataTable
|
||||
|
||||
ROWS = [
|
||||
("lane", "swimmer", "country", "time"),
|
||||
(4, "[Joseph Schooling]", "Singapore", 50.39),
|
||||
(2, "[red]Michael Phelps[/]", "United States", 51.14),
|
||||
(5, "[bold]Chad le Clos[/]", "South Africa", 51.14),
|
||||
(6, "László Cseh", "Hungary", 51.14),
|
||||
(3, "Li Zhuhao", "China", 51.26),
|
||||
(8, "Mehdy Metella", "France", 51.58),
|
||||
(7, "Tom Shields", "United States", 51.73),
|
||||
(1, "Aleksandr Sadovnikov", "Russia", 51.84),
|
||||
(10, "Darren Burns", "Scotland", 51.84),
|
||||
]
|
||||
|
||||
|
||||
class TableApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
backend = ArrowBackend.from_records(ROWS, has_header=True)
|
||||
yield DataTable(backend=backend, render_markup=False)
|
||||
|
||||
|
||||
app = TableApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
|
@ -0,0 +1,22 @@
|
|||
from textual.app import App, ComposeResult
|
||||
from textual_fastdatatable import ArrowBackend, DataTable
|
||||
|
||||
ROWS = [
|
||||
("lane", "swimmer", "country", "time"),
|
||||
(3, "Li Zhuhao", "China", 51.26),
|
||||
("eight", None, "France", 51.58),
|
||||
("seven", "Tom Shields", "United States", None),
|
||||
(1, "Aleksandr Sadovnikov", "Russia", 51.84),
|
||||
(None, "Darren Burns", "Scotland", 51.84),
|
||||
]
|
||||
|
||||
|
||||
class TableApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
backend = ArrowBackend.from_records(ROWS, has_header=True)
|
||||
yield DataTable(backend=backend, null_rep="[dim]∅ null[/]")
|
||||
|
||||
|
||||
app = TableApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
|
@ -0,0 +1,33 @@
|
|||
import csv
|
||||
import io
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual_fastdatatable import ArrowBackend, DataTable
|
||||
|
||||
CSV = """lane,swimmer,country,time
|
||||
4,Joseph Schooling,Singapore,50.39
|
||||
2,Michael Phelps,United States,51.14
|
||||
5,Chad le Clos,South Africa,51.14
|
||||
6,László Cseh,Hungary,51.14
|
||||
3,Li Zhuhao,China,51.26
|
||||
8,Mehdy Metella,France,51.58
|
||||
7,Tom Shields,United States,51.73
|
||||
1,Aleksandr Sadovnikov,Russia,51.84"""
|
||||
|
||||
|
||||
class TableApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
rows = csv.reader(io.StringIO(CSV))
|
||||
labels = next(rows)
|
||||
data = [row for row in rows]
|
||||
backend = ArrowBackend.from_pydict(
|
||||
{label: [row[i] for row in data] for i, label in enumerate(labels)}
|
||||
)
|
||||
table = DataTable(backend=backend, cursor_type="range")
|
||||
table.focus()
|
||||
yield table
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = TableApp()
|
||||
app.run()
|
45
tests/snapshot_tests/snapshot_apps/data_table_remove_row.py
Normal file
45
tests/snapshot_tests/snapshot_apps/data_table_remove_row.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
from textual.app import App, ComposeResult
|
||||
from textual.binding import Binding
|
||||
from textual_fastdatatable import ArrowBackend, DataTable
|
||||
|
||||
ROWS = [
|
||||
("lane", "swimmer", "country", "time"),
|
||||
(5, "Chad le Clos", "South Africa", 51.14),
|
||||
(4, "Joseph Schooling", "Singapore", 50.39),
|
||||
(2, "Michael Phelps", "United States", 51.14),
|
||||
(6, "László Cseh", "Hungary", 51.14),
|
||||
(3, "Li Zhuhao", "China", 51.26),
|
||||
(8, "Mehdy Metella", "France", 51.58),
|
||||
(7, "Tom Shields", "United States", 51.73),
|
||||
(10, "Darren Burns", "Scotland", 51.84),
|
||||
(1, "Aleksandr Sadovnikov", "Russia", 51.84),
|
||||
]
|
||||
|
||||
|
||||
class TableApp(App):
|
||||
"""Snapshot app for testing removal of rows.
|
||||
Removes several rows, so we can check that the display of the
|
||||
DataTable updates as expected."""
|
||||
|
||||
BINDINGS = [
|
||||
Binding("r", "remove_row", "Remove Row"),
|
||||
]
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
backend = ArrowBackend.from_records(ROWS, has_header=True)
|
||||
yield DataTable(backend=backend)
|
||||
|
||||
def on_mount(self) -> None:
|
||||
table = self.query_one(DataTable)
|
||||
table.focus()
|
||||
|
||||
def action_remove_row(self):
|
||||
table = self.query_one(DataTable)
|
||||
table.remove_row(2)
|
||||
table.remove_row(4)
|
||||
table.remove_row(6)
|
||||
|
||||
|
||||
app = TableApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
36
tests/snapshot_tests/snapshot_apps/data_table_row_cursor.py
Normal file
36
tests/snapshot_tests/snapshot_apps/data_table_row_cursor.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
import csv
|
||||
import io
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual_fastdatatable import ArrowBackend, DataTable
|
||||
|
||||
CSV = """lane,swimmer,country,time
|
||||
4,Joseph Schooling,Singapore,50.39
|
||||
2,Michael Phelps,United States,51.14
|
||||
5,Chad le Clos,South Africa,51.14
|
||||
6,László Cseh,Hungary,51.14
|
||||
3,Li Zhuhao,China,51.26
|
||||
8,Mehdy Metella,France,51.58
|
||||
7,Tom Shields,United States,51.73
|
||||
1,Aleksandr Sadovnikov,Russia,51.84"""
|
||||
|
||||
|
||||
class TableApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
rows = csv.reader(io.StringIO(CSV))
|
||||
labels = next(rows)
|
||||
data = [row for row in rows]
|
||||
backend = ArrowBackend.from_pydict(
|
||||
{label: [row[i] for row in data] for i, label in enumerate(labels)}
|
||||
)
|
||||
table = DataTable(backend=backend)
|
||||
table.focus()
|
||||
table.cursor_type = "row"
|
||||
table.fixed_columns = 1
|
||||
table.fixed_rows = 1
|
||||
yield table
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = TableApp()
|
||||
app.run()
|
37
tests/snapshot_tests/snapshot_apps/data_table_row_labels.py
Normal file
37
tests/snapshot_tests/snapshot_apps/data_table_row_labels.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
from textual.app import App, ComposeResult
|
||||
from textual_fastdatatable import DataTable
|
||||
|
||||
ROWS = [
|
||||
("lane", "swimmer", "country", "time"),
|
||||
(5, "Chad le Clos", "South Africa", 51.14),
|
||||
(4, "Joseph Schooling", "Singapore", 50.39),
|
||||
(2, "Michael Phelps", "United States", 51.14),
|
||||
(6, "László Cseh", "Hungary", 51.14),
|
||||
(3, "Li Zhuhao", "China", 51.26),
|
||||
(8, "Mehdy Metella", "France", 51.58),
|
||||
(7, "Tom Shields", "United States", 51.73),
|
||||
(10, "Darren Burns", "Scotland", 51.84),
|
||||
(1, "Aleksandr Sadovnikov", "Russia", 51.84),
|
||||
]
|
||||
|
||||
|
||||
class TableApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield DataTable()
|
||||
|
||||
def on_mount(self) -> None:
|
||||
table = self.query_one(DataTable)
|
||||
table.fixed_rows = 1
|
||||
table.fixed_columns = 1
|
||||
table.focus()
|
||||
rows = iter(ROWS)
|
||||
column_labels = next(rows)
|
||||
for column in column_labels:
|
||||
table.add_column(column, key=column)
|
||||
for index, row in enumerate(rows):
|
||||
table.add_row(*row, label=str(index))
|
||||
|
||||
|
||||
app = TableApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
40
tests/snapshot_tests/snapshot_apps/data_table_sort.py
Normal file
40
tests/snapshot_tests/snapshot_apps/data_table_sort.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
from textual.app import App, ComposeResult
|
||||
from textual.binding import Binding
|
||||
from textual_fastdatatable import ArrowBackend, DataTable
|
||||
|
||||
# Shuffled around a bit to exercise sorting.
|
||||
ROWS = [
|
||||
("lane", "swimmer", "country", "time"),
|
||||
(5, "Chad le Clos", "South Africa", 51.14),
|
||||
(4, "Joseph Schooling", "Singapore", 50.39),
|
||||
(2, "Michael Phelps", "United States", 51.14),
|
||||
(6, "László Cseh", "Hungary", 51.14),
|
||||
(3, "Li Zhuhao", "China", 51.26),
|
||||
(8, "Mehdy Metella", "France", 51.58),
|
||||
(7, "Tom Shields", "United States", 51.73),
|
||||
(10, "Darren Burns", "Scotland", 51.84),
|
||||
(1, "Aleksandr Sadovnikov", "Russia", 51.84),
|
||||
]
|
||||
|
||||
|
||||
class TableApp(App):
|
||||
BINDINGS = [
|
||||
Binding("s", "sort", "Sort"),
|
||||
]
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
backend = ArrowBackend.from_records(ROWS, has_header=True)
|
||||
yield DataTable(backend=backend)
|
||||
|
||||
def on_mount(self) -> None:
|
||||
table = self.query_one(DataTable)
|
||||
table.focus()
|
||||
|
||||
def action_sort(self):
|
||||
table = self.query_one(DataTable)
|
||||
table.sort([("time", "ascending"), ("lane", "ascending")])
|
||||
|
||||
|
||||
app = TableApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
65
tests/snapshot_tests/snapshot_apps/data_table_style_order.py
Normal file
65
tests/snapshot_tests/snapshot_apps/data_table_style_order.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import Label
|
||||
from textual_fastdatatable import ArrowBackend, DataTable
|
||||
from typing_extensions import Literal
|
||||
|
||||
data = [
|
||||
"Severance",
|
||||
"Foundation",
|
||||
"Dark",
|
||||
]
|
||||
|
||||
|
||||
def make_datatable(
|
||||
foreground_priority: Literal["css", "renderable"],
|
||||
background_priority: Literal["css", "renderable"],
|
||||
) -> DataTable:
|
||||
backend = ArrowBackend.from_pydict(
|
||||
{"Movies": [f"[red on blue]{row}" for row in data]}
|
||||
)
|
||||
table = DataTable(
|
||||
backend=backend,
|
||||
cursor_foreground_priority=foreground_priority,
|
||||
cursor_background_priority=background_priority,
|
||||
)
|
||||
table.zebra_stripes = True
|
||||
return table
|
||||
|
||||
|
||||
class DataTableCursorStyles(App):
|
||||
"""Regression test snapshot app which ensures that styles
|
||||
are layered on top of each other correctly in the DataTable.
|
||||
In this example, the colour of the text in the cells under
|
||||
the cursor should not be red, because the CSS should be applied
|
||||
on top."""
|
||||
|
||||
CSS = """
|
||||
DataTable {margin-bottom: 1;}
|
||||
DataTable > .datatable--cursor {
|
||||
color: $secondary;
|
||||
background: $success;
|
||||
text-style: bold italic;
|
||||
}
|
||||
"""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
priorities: list[
|
||||
tuple[Literal["css", "renderable"], Literal["css", "renderable"]]
|
||||
] = [
|
||||
("css", "css"),
|
||||
("css", "renderable"),
|
||||
("renderable", "renderable"),
|
||||
("renderable", "css"),
|
||||
]
|
||||
for foreground, background in priorities:
|
||||
yield Label(f"Foreground is {foreground!r}, background is {background!r}:")
|
||||
table = make_datatable(foreground, background)
|
||||
yield table
|
||||
|
||||
|
||||
app = DataTableCursorStyles()
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
|
@ -0,0 +1,58 @@
|
|||
from pathlib import Path
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual_fastdatatable import ArrowBackend, DataTable
|
||||
|
||||
CSS_PATH = (Path(__file__) / "../datatable_hot_reloading.tcss").resolve()
|
||||
|
||||
# Write some CSS to the file before the app loads.
|
||||
# Then, the test will clear all the CSS to see if the
|
||||
# hot reloading applies the changes correctly.
|
||||
CSS_PATH.write_text(
|
||||
"""\
|
||||
DataTable > .datatable--cursor {
|
||||
background: purple;
|
||||
}
|
||||
|
||||
DataTable > .datatable--fixed {
|
||||
background: red;
|
||||
}
|
||||
|
||||
DataTable > .datatable--fixed-cursor {
|
||||
background: blue;
|
||||
}
|
||||
|
||||
DataTable > .datatable--header {
|
||||
background: yellow;
|
||||
}
|
||||
|
||||
DataTable > .datatable--odd-row {
|
||||
background: pink;
|
||||
}
|
||||
|
||||
DataTable > .datatable--even-row {
|
||||
background: brown;
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
class DataTableHotReloadingApp(App[None]):
|
||||
CSS_PATH = CSS_PATH
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
data = {
|
||||
# orig test set A width=10, we fake it with spaces
|
||||
"A ": ["one", "three", "five"],
|
||||
"B": ["two", "four", "six"],
|
||||
}
|
||||
backend = ArrowBackend.from_pydict(data)
|
||||
yield DataTable(backend, zebra_stripes=True, cursor_type="row", fixed_columns=1)
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self.query_one(DataTable)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = DataTableHotReloadingApp()
|
||||
app.run()
|
|
@ -0,0 +1 @@
|
|||
/* This file is purposefully empty. */
|
12
tests/snapshot_tests/snapshot_apps/empty.py
Normal file
12
tests/snapshot_tests/snapshot_apps/empty.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from textual.app import App, ComposeResult
|
||||
from textual_fastdatatable import DataTable
|
||||
|
||||
|
||||
class TableApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield DataTable()
|
||||
|
||||
|
||||
app = TableApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
17
tests/snapshot_tests/snapshot_apps/empty_add_col.py
Normal file
17
tests/snapshot_tests/snapshot_apps/empty_add_col.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
from textual.app import App, ComposeResult
|
||||
from textual_fastdatatable import DataTable
|
||||
|
||||
|
||||
class TableApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield DataTable()
|
||||
|
||||
def on_mount(self) -> None:
|
||||
table = self.query_one(DataTable)
|
||||
table.add_column("Foo")
|
||||
table.add_rows([("1",), ("2",)])
|
||||
|
||||
|
||||
app = TableApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
16
tests/snapshot_tests/snapshot_apps/from_parquet.py
Normal file
16
tests/snapshot_tests/snapshot_apps/from_parquet.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
from pathlib import Path
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual_fastdatatable import DataTable
|
||||
|
||||
|
||||
class TableApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield DataTable(
|
||||
data=Path(__file__).parent.parent.parent / "data" / "lap_times_100.parquet"
|
||||
)
|
||||
|
||||
|
||||
app = TableApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
|
@ -0,0 +1,20 @@
|
|||
from textual.app import App, ComposeResult
|
||||
from textual_fastdatatable import DataTable
|
||||
|
||||
DATA = {
|
||||
"Foo": list(range(50)),
|
||||
"Bar": ["0123456789"] * 50,
|
||||
"Baz": ["IJKLMNOPQRSTUVWXYZ"] * 50,
|
||||
}
|
||||
|
||||
|
||||
class TableApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield DataTable(
|
||||
data=DATA, column_labels=["[red]Not Foo[/red]", "Zig", "[reverse]Zag[/]"]
|
||||
)
|
||||
|
||||
|
||||
app = TableApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
25
tests/snapshot_tests/snapshot_apps/from_records.py
Normal file
25
tests/snapshot_tests/snapshot_apps/from_records.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
from textual.app import App, ComposeResult
|
||||
from textual_fastdatatable import DataTable
|
||||
|
||||
ROWS = [
|
||||
("lane", "swimmer", "country", "time"),
|
||||
(4, "Joseph Schooling", "Singapore", 50.39),
|
||||
(2, "Michael Phelps", "United States", 51.14),
|
||||
(5, "Chad le Clos", "South Africa", 51.14),
|
||||
(6, "László Cseh", "Hungary", 51.14),
|
||||
(3, "Li Zhuhao", "China", 51.26),
|
||||
(8, "Mehdy Metella", "France", 51.58),
|
||||
(7, "Tom Shields", "United States", 51.73),
|
||||
(1, "Aleksandr Sadovnikov", "Russia", 51.84),
|
||||
(10, "Darren Burns", "Scotland", 51.84),
|
||||
]
|
||||
|
||||
|
||||
class TableApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield DataTable(data=ROWS)
|
||||
|
||||
|
||||
app = TableApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
12
tests/snapshot_tests/snapshot_apps/no_rows.py
Normal file
12
tests/snapshot_tests/snapshot_apps/no_rows.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from textual.app import App, ComposeResult
|
||||
from textual_fastdatatable import DataTable
|
||||
|
||||
|
||||
class TableApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield DataTable(column_labels=["foo [red]foo[/red]", "bar"])
|
||||
|
||||
|
||||
app = TableApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
12
tests/snapshot_tests/snapshot_apps/no_rows_empty_sequence.py
Normal file
12
tests/snapshot_tests/snapshot_apps/no_rows_empty_sequence.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from textual.app import App, ComposeResult
|
||||
from textual_fastdatatable import DataTable
|
||||
|
||||
|
||||
class TableApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield DataTable(column_labels=["foo [red]foo[/red]", "bar"])
|
||||
|
||||
|
||||
app = TableApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
119
tests/snapshot_tests/test_snapshots.py
Normal file
119
tests/snapshot_tests/test_snapshots.py
Normal file
|
@ -0,0 +1,119 @@
|
|||
from pathlib import Path
|
||||
from typing import Callable
|
||||
|
||||
import pytest
|
||||
|
||||
# These paths should be relative to THIS directory.
|
||||
SNAPSHOT_APPS_DIR = Path("./snapshot_apps")
|
||||
|
||||
|
||||
def test_auto_table(snap_compare: Callable) -> None:
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "auto-table.py", terminal_size=(120, 40))
|
||||
|
||||
|
||||
def test_datatable_render(snap_compare: Callable) -> None:
|
||||
press = ["down", "down", "right", "up", "left"]
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "data_table.py", press=press)
|
||||
|
||||
|
||||
def test_datatable_row_cursor_render(snap_compare: Callable) -> None:
|
||||
press = ["up", "left", "right", "down", "down"]
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "data_table_row_cursor.py", press=press)
|
||||
|
||||
|
||||
def test_datatable_no_render_markup(snap_compare: Callable) -> None:
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "data_table_no_render_markup.py")
|
||||
|
||||
|
||||
def test_datatable_null_mixed_cols(snap_compare: Callable) -> None:
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "data_table_null_mixed_cols.py")
|
||||
|
||||
|
||||
def test_datatable_range_cursor_render(snap_compare: Callable) -> None:
|
||||
press = ["right", "down", "shift+right", "shift+down", "shift+down"]
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "data_table_range_cursor.py", press=press)
|
||||
|
||||
|
||||
def test_datatable_column_cursor_render(snap_compare: Callable) -> None:
|
||||
press = ["left", "up", "down", "right", "right"]
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "data_table_column_cursor.py", press=press)
|
||||
|
||||
|
||||
def test_datatable_max_width_render(snap_compare: Callable) -> None:
|
||||
press = ["right", "down", "shift+right", "shift+down", "shift+down"]
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "data_table_max_width.py", press=press)
|
||||
|
||||
|
||||
def test_datatable_sort_multikey(snap_compare: Callable) -> None:
|
||||
press = ["down", "right", "s"] # Also checks that sort doesn't move cursor.
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "data_table_sort.py", press=press)
|
||||
|
||||
|
||||
def test_datatable_remove_row(snap_compare: Callable) -> None:
|
||||
press = ["r"]
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "data_table_remove_row.py", press=press)
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Don't support row labels.")
|
||||
def test_datatable_labels_and_fixed_data(snap_compare: Callable) -> None:
|
||||
# Ensure that we render correctly when there are fixed rows/cols and labels.
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "data_table_row_labels.py")
|
||||
|
||||
|
||||
# skip, don't xfail; see: https://github.com/Textualize/pytest-textual-snapshot/issues/6
|
||||
@pytest.mark.skip(
|
||||
reason=(
|
||||
"The data in this test includes markup; the backend doesn't"
|
||||
"know these have zero width, so we draw the column wider than we used to"
|
||||
)
|
||||
)
|
||||
def test_datatable_style_ordering(snap_compare: Callable) -> None:
|
||||
# Regression test for https -> None://github.com/Textualize/textual/issues/2061
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "data_table_style_order.py")
|
||||
|
||||
|
||||
def test_datatable_add_column(snap_compare: Callable) -> None:
|
||||
# Checking adding columns after adding rows
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "data_table_add_column.py")
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="No multi-height rows. No Rich objects.")
|
||||
def test_datatable_add_row_auto_height(snap_compare: Callable) -> None:
|
||||
# Check that rows added with auto height computation look right.
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "data_table_add_row_auto_height.py")
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="No multi-height rows. No Rich objects.")
|
||||
def test_datatable_add_row_auto_height_sorted(snap_compare: Callable) -> None:
|
||||
# Check that rows added with auto height computation look right.
|
||||
assert snap_compare(
|
||||
SNAPSHOT_APPS_DIR / "data_table_add_row_auto_height.py", press=["s"]
|
||||
)
|
||||
|
||||
|
||||
def test_datatable_empty(snap_compare: Callable) -> None:
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "empty.py")
|
||||
|
||||
|
||||
def test_datatable_empty_add_col(snap_compare: Callable) -> None:
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "empty_add_col.py")
|
||||
|
||||
|
||||
def test_datatable_no_rows(snap_compare: Callable) -> None:
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "no_rows.py")
|
||||
|
||||
|
||||
def test_datatable_no_rows_empty_sequence(snap_compare: Callable) -> None:
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "no_rows_empty_sequence.py")
|
||||
|
||||
|
||||
def test_datatable_from_parquet(snap_compare: Callable) -> None:
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "from_parquet.py")
|
||||
|
||||
|
||||
def test_datatable_from_records(snap_compare: Callable) -> None:
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "from_records.py")
|
||||
|
||||
|
||||
def test_datatable_from_pydict(snap_compare: Callable) -> None:
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "from_pydict_with_col_labels.py")
|
Loading…
Add table
Add a link
Reference in a new issue