Repository: Textualize/textual

Issue:
`Switch` should stop the `Click` event from bubbling
At the moment `Switch` handles `Click` but then lets it bubble; there's no good reason to do that and it also stops the ability to write something like this:

```python
from textual.app        import App, ComposeResult
from textual.containers import Horizontal
from textual.widgets    import Header, Footer, Label, Switch

class LabeledSwitch( Horizontal ):

    def on_click( self ) -> None:
        self.query_one(Switch).toggle()

class ClickableLabelApp( App[ None ] ):

    def compose( self ) -> ComposeResult:
        yield Header()
        with LabeledSwitch():
            yield Label( "Click me!" )
            yield Switch()
        yield Footer()

if __name__ == "__main__":
    ClickableLabelApp().run()
```

where the idea is to make a compound widget that lets you click on the `Label` or the `Switch` and the `Switch` will toggle -- only it doesn't work if you click on the `Switch` because it ends up double-toggling.

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6d9b2b217..bf9a3b79b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 ### Fixed
 
 - Fixed a crash when a `SelectionList` had a prompt wider than itself https://github.com/Textualize/textual/issues/2900
+- Fixed a bug where `Click` events were bubbling up from `Switch` widgets https://github.com/Textualize/textual/issues/2366
 
 ## [0.30.0] - 2023-07-17
 
diff --git a/src/textual/widgets/_switch.py b/src/textual/widgets/_switch.py
index d19ee168e..eb0568c61 100644
--- a/src/textual/widgets/_switch.py
+++ b/src/textual/widgets/_switch.py
@@ -154,8 +154,9 @@ class Switch(Widget, can_focus=True):
     def get_content_height(self, container: Size, viewport: Size, width: int) -> int:
         return 1
 
-    async def _on_click(self, _: Click) -> None:
+    async def _on_click(self, event: Click) -> None:
         """Toggle the state of the switch."""
+        event.stop()
         self.toggle()
 
     def action_toggle(self) -> None:


Test patch:
diff --git a/tests/test_switch.py b/tests/test_switch.py
new file mode 100644
index 000000000..e0868118f
--- /dev/null
+++ b/tests/test_switch.py
@@ -0,0 +1,18 @@
+from textual.app import App, ComposeResult
+from textual.widgets import Switch
+
+
+async def test_switch_click_doesnt_bubble_up():
+    """Regression test for https://github.com/Textualize/textual/issues/2366"""
+
+    class SwitchApp(App[None]):
+        def compose(self) -> ComposeResult:
+            yield Switch()
+
+        async def on_click(self) -> None:
+            raise AssertionError(
+                "The app should never receive a click event when Switch is clicked."
+            )
+
+    async with SwitchApp().run_test() as pilot:
+        await pilot.click(Switch)