From 29c7103c11ecceb6a2129638b92c63598fffba97 Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Fri, 15 Nov 2024 19:30:07 +0000 Subject: [PATCH] fix(tabbed content): prevent duplicate ids error --- CHANGELOG.md | 1 + src/textual/widgets/_tabbed_content.py | 6 +++++- tests/test_tabbed_content.py | 20 ++++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b82e5c51fc..d181988a04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed duplicated key displays in the help panel https://github.com/Textualize/textual/issues/5037 - Fixed `TextArea` mouse selection with tab characters https://github.com/Textualize/textual/issues/5212 +- Fixed crash in `TabbedContent` when removing then adding panes https://github.com/Textualize/textual/issues/5215 ### Added diff --git a/src/textual/widgets/_tabbed_content.py b/src/textual/widgets/_tabbed_content.py index 2355c82275..9fd84c21e0 100644 --- a/src/textual/widgets/_tabbed_content.py +++ b/src/textual/widgets/_tabbed_content.py @@ -331,6 +331,7 @@ def __init__( self.titles = [self.render_str(title) for title in titles] self._tab_content: list[Widget] = [] self._initial = initial + self._tabs_counter = 0 super().__init__(name=name, id=id, classes=classes, disabled=disabled) @property @@ -383,6 +384,8 @@ def compose(self) -> ComposeResult: for content in pane_content ] + self._tabs_counter = len(tabs) + # Yield the tabs, and ensure they're linked to this TabbedContent. # It's important to associate the Tabs with the TabbedContent, so that this # TabbedContent can determine whether a message received from a Tabs instance @@ -418,12 +421,13 @@ def add_pane( Only one of `before` or `after` can be provided. If both are provided an exception is raised. """ + self._tabs_counter += 1 if isinstance(before, TabPane): before = before.id if isinstance(after, TabPane): after = after.id tabs = self.get_child_by_type(ContentTabs) - pane = self._set_id(pane, tabs.tab_count + 1) + pane = self._set_id(pane, self._tabs_counter) assert pane.id is not None pane.display = False return AwaitComplete( diff --git a/tests/test_tabbed_content.py b/tests/test_tabbed_content.py index 57a8874ebf..06c4693138 100644 --- a/tests/test_tabbed_content.py +++ b/tests/test_tabbed_content.py @@ -914,3 +914,23 @@ def compose(self) -> ComposeResult: await pilot.pause() assert app.query_one("Tab#duplicate").disabled is True assert app.query_one("TabPane#duplicate").disabled is False + + +async def test_remove_and_add_pane_no_duplicate_id_error(): + """Ensure that removing then adding panes does not raise a `DuplicateIds` exception. + + Regression test for https://github.com/Textualize/textual/issues/5215 + """ + + class TabbedApp(App[None]): + def compose(self) -> ComposeResult: + with TabbedContent(): + yield Label("tab-1") + yield Label("tab-2") + + app = TabbedApp() + async with app.run_test() as pilot: + # If no exception is raised, the test will pass + tabbed_content = pilot.app.query_one(TabbedContent) + await tabbed_content.remove_pane("tab-1") + await tabbed_content.add_pane(TabPane("Tab 3", Label("tab-3")))