From 769c7f668ff96e72c54fa39bf5f1ced9fce464f6 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Fri, 13 Sep 2024 13:29:22 +0100 Subject: [PATCH] Auto-height table repopulate fix (#4992) --- CHANGELOG.md | 1 + src/textual/widgets/_data_table.py | 5 + ...t_datatable_auto_height_future_updates.svg | 154 ++++++++++++++++++ tests/snapshot_tests/test_snapshots.py | 44 ++++- 4 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 tests/snapshot_tests/__snapshots__/test_snapshots/test_datatable_auto_height_future_updates.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e9199b3e8..780c788d6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed `RichLog` writing at wrong width when `write` occurs before width is known (e.g. in `compose` or `on_mount`) https://github.com/Textualize/textual/pull/4978 - Fixed `RichLog.write` incorrectly shrinking width to `RichLog.min_width` when `shrink=True` (now shrinks to fit content area instead) https://github.com/Textualize/textual/pull/4978 - Fixed flicker when setting `dark` reactive on startup https://github.com/Textualize/textual/pull/4989 +- Fixed `DataTable` cached height issue on re-populating the table when using auto-height rows https://github.com/Textualize/textual/pull/4992 ## [0.79.1] - 2024-08-31 diff --git a/src/textual/widgets/_data_table.py b/src/textual/widgets/_data_table.py index fb6a30270d..26fccef377 100644 --- a/src/textual/widgets/_data_table.py +++ b/src/textual/widgets/_data_table.py @@ -818,6 +818,7 @@ def _y_offsets(self) -> list[tuple[RowKey, int]]: for row in self.ordered_rows: y_offsets += [(row.key, y) for y in range(row.height)] self._offset_cache[self._update_count] = y_offsets + return y_offsets @property @@ -1397,6 +1398,7 @@ def _update_dimensions(self, new_rows: Iterable[RowKey]) -> None: # If there are rows that need to have their height computed, render them correctly # so that we can cache this rendering for later. if auto_height_rows: + self._offset_cache.clear() render_cell = self._render_cell # This method renders & caches. should_highlight = self._should_highlight cursor_type = self.cursor_type @@ -1451,6 +1453,9 @@ def _update_dimensions(self, new_rows: Iterable[RowKey]) -> None: ] ) + self._line_cache.clear() + self._styles_cache.clear() + data_cells_width = sum( column.get_render_width(self) for column in self.columns.values() ) diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_datatable_auto_height_future_updates.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_datatable_auto_height_future_updates.svg new file mode 100644 index 0000000000..89a5dc877f --- /dev/null +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_datatable_auto_height_future_updates.svg @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ExampleApp + + + + + + + + + + ┌──────────────────────────────────────────────────────────────────────────────┐ + foo  bar  + 1    abc  + + 2    def  + 3    ghi  + + 4    jkl  +└──────────────────────────────────────────────────────────────────────────────┘ + + + + + + + + + + + + + + + + + + diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index b4caa1c5e9..e022f72942 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -12,7 +12,7 @@ from textual.containers import Vertical from textual.pilot import Pilot from textual.screen import Screen -from textual.widgets import Button, Header, Input, RichLog, TextArea, Footer +from textual.widgets import Button, Header, DataTable, Input, RichLog, TextArea, Footer from textual.widgets import Switch from textual.widgets import Label from textual.widgets.text_area import BUILTIN_LANGUAGES, Selection, TextAreaTheme @@ -193,6 +193,48 @@ def test_datatable_add_row_auto_height_sorted(snap_compare): ) +def test_datatable_auto_height_future_updates(snap_compare): + """https://github.com/Textualize/textual/issues/4928 meant that when height=None, + in add_row, future updates to the table would be incorrect. + + In this test, every 2nd row is auto height and every other row is height 2. + The table is cleared then fully repopulated with the same 4 rows. All 4 rows + should be visible and rendered at heights 2, 1, 2, 1. + """ + ROWS = [ + ("foo", "bar"), + (1, "abc"), + (2, "def"), + (3, "ghi"), + (4, "jkl"), + ] + + class ExampleApp(App[None]): + CSS = "DataTable { border: solid red; }" + + def compose(self) -> ComposeResult: + yield DataTable() + + def on_mount(self) -> None: + table = self.query_one(DataTable) + table.add_columns(*ROWS[0]) + self.populate_table() + + def key_r(self) -> None: + self.populate_table() + + def populate_table(self) -> None: + table = self.query_one(DataTable) + table.clear() + for i, row in enumerate(ROWS[1:]): + table.add_row( + *row, + height=None if i % 2 == 1 else 2, + ) + + assert snap_compare(ExampleApp(), press=["r"]) + + def test_datatable_cell_padding(snap_compare): # Check that horizontal cell padding is respected. assert snap_compare(SNAPSHOT_APPS_DIR / "data_table_cell_padding.py")