From 91971ededc68a6eb55f1dec626f8c85f42a897f0 Mon Sep 17 00:00:00 2001 From: Ted Conbeer Date: Thu, 31 Oct 2024 18:53:02 +0000 Subject: [PATCH] feat: add param to disable render of rich markup --- CHANGELOG.md | 2 + src/textual_fastdatatable/data_table.py | 8 +- src/textual_fastdatatable/formatter.py | 6 +- .../__snapshots__/test_snapshots.ambr | 158 ++++++++++++++++++ .../data_table_no_render_markup.py | 26 +++ tests/snapshot_tests/test_snapshots.py | 4 + 6 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 tests/snapshot_tests/snapshot_apps/data_table_no_render_markup.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 639f5a4..50e5966 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +- Adds an optional parameter to DataTable to disable rendering of string data as Rich Markup. + ## [0.9.0] - 2024-07-23 - Adds a PolarsBackend implementation of DataTableBackend. You must have `polars` installed to use the PolarsBackend. You can install it using the `polars` extra for this package. diff --git a/src/textual_fastdatatable/data_table.py b/src/textual_fastdatatable/data_table.py index c7896eb..192d6e2 100644 --- a/src/textual_fastdatatable/data_table.py +++ b/src/textual_fastdatatable/data_table.py @@ -552,6 +552,7 @@ def __init__( classes: str | None = None, disabled: bool = False, null_rep: str = "", + render_markup: bool = True, ) -> None: super().__init__(name=name, id=id, classes=classes, disabled=disabled) try: @@ -652,6 +653,8 @@ def __init__( """The type of cursor of the `DataTable`.""" self.null_rep = Text.from_markup(null_rep) """The string used to represent missing data (None or null)""" + self.render_markup = render_markup + """If true, render string data as Rich markup.""" @property def hover_row(self) -> int: @@ -1775,7 +1778,9 @@ def _get_row_renderables(self, row_index: int) -> RowRenderables: empty = self.null_rep formatted_row_cells = [ - cell_formatter(datum, null_rep=empty, col=col) + cell_formatter( + datum, null_rep=empty, col=col, render_markup=self.render_markup + ) for datum, col in zip_longest(ordered_row, self.ordered_columns) ] label = None @@ -1811,6 +1816,7 @@ def _get_cell_renderable( datum, null_rep=self.null_rep, col=self.ordered_columns[column_index], + render_markup=self.render_markup, ) def _render_cell( diff --git a/src/textual_fastdatatable/formatter.py b/src/textual_fastdatatable/formatter.py index 0ba0f7a..23a3461 100644 --- a/src/textual_fastdatatable/formatter.py +++ b/src/textual_fastdatatable/formatter.py @@ -15,7 +15,7 @@ def cell_formatter( - obj: object, null_rep: Text, col: Column | None = None + obj: object, null_rep: Text, col: Column | None = None, render_markup: bool = True ) -> RenderableType: """Convert a cell into a Rich renderable for display. @@ -30,12 +30,14 @@ def cell_formatter( """ if obj is None: return Align(null_rep, align="center") - elif isinstance(obj, str): + elif isinstance(obj, str) and render_markup: try: rich_text: Text | str = Text.from_markup(obj) except MarkupError: rich_text = escape(obj) return rich_text + elif isinstance(obj, str): + return escape(obj) elif isinstance(obj, bool): return Align( f"[dim]{'✓' if obj else 'X'}[/] {obj}{' ' if obj else ''}", diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 1a29b77..25b8d4e 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -1493,6 +1493,164 @@ ''' # --- +# name: test_datatable_no_render_markup + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TableApp + + + + + + + + + +  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  + + + + + + + + + + + + + + + + + + + ''' +# --- # name: test_datatable_no_rows ''' diff --git a/tests/snapshot_tests/snapshot_apps/data_table_no_render_markup.py b/tests/snapshot_tests/snapshot_apps/data_table_no_render_markup.py new file mode 100644 index 0000000..968e954 --- /dev/null +++ b/tests/snapshot_tests/snapshot_apps/data_table_no_render_markup.py @@ -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() diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index 45a1a20..75758bc 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -21,6 +21,10 @@ def test_datatable_row_cursor_render(snap_compare: Callable) -> None: 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_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)