diff --git a/CHANGELOG.md b/CHANGELOG.md
index adb5bfde9..6bb413d79 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Fix empty ListView preventing bindings from firing https://github.com/Textualize/textual/pull/2281
- Fix `get_component_styles` returning incorrect values on first call when combined with pseudoclasses https://github.com/Textualize/textual/pull/2304
- Fixed `active_message_pump.get` sometimes resulting in a `LookupError` https://github.com/Textualize/textual/issues/2301
+- Fixed issue arising when active tab was changed too quickly in succession https://github.com/Textualize/textual/pull/2305
## [0.19.1] - 2023-04-10
diff --git a/src/textual/widgets/_tabbed_content.py b/src/textual/widgets/_tabbed_content.py
index ffaa1112b..3792ed533 100644
--- a/src/textual/widgets/_tabbed_content.py
+++ b/src/textual/widgets/_tabbed_content.py
@@ -83,9 +83,6 @@ class TabbedContent(Widget):
}
"""
- active: reactive[str] = reactive("", init=False)
- """The ID of the active tab, or empty string if none are active."""
-
class TabActivated(Message):
"""Posted when the active tab changes."""
@@ -116,21 +113,16 @@ class TabbedContent(Widget):
self._initial = initial
super().__init__()
- def validate_active(self, active: str) -> str:
- """It doesn't make sense for `active` to be an empty string.
+ @property
+ def active(self) -> str:
+ """The ID of the active tab, or empty string if none are active."""
+ return self.get_child_by_type(Tabs).active
- Args:
- active: Attribute to be validated.
-
- Returns:
- Value of `active`.
-
- Raises:
- ValueError: If the active attribute is set to empty string.
- """
+ @active.setter
+ def active(self, active: str) -> None:
if not active:
raise ValueError("'active' tab must not be empty string.")
- return active
+ self.get_child_by_type(Tabs).active = active
def compose(self) -> ComposeResult:
"""Compose the tabbed content."""
@@ -186,7 +178,6 @@ class TabbedContent(Widget):
switcher = self.get_child_by_type(ContentSwitcher)
assert isinstance(event.tab, ContentTab)
switcher.current = event.tab.id
- self.active = event.tab.id
self.post_message(
TabbedContent.TabActivated(
tabbed_content=self,
@@ -197,7 +188,3 @@ class TabbedContent(Widget):
def _on_tabs_cleared(self, event: Tabs.Cleared) -> None:
"""All tabs were removed."""
event.stop()
-
- def watch_active(self, active: str) -> None:
- """Switch tabs when the active attributes changes."""
- self.get_child_by_type(Tabs).active = active
diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
index 5cd124486..c80298cc6 100644
--- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
+++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
@@ -19676,6 +19676,165 @@
'''
# ---
+# name: test_quickly_change_tabs
+ '''
+
+
+ '''
+# ---
# name: test_radio_button_example
'''