| 
1 | 1 | from textual.app import App, ComposeResult  | 
2 |  | -from textual.containers import Container, ScrollableContainer  | 
 | 2 | +from textual.containers import Container, ScrollableContainer, Vertical  | 
3 | 3 | from textual.widget import Widget  | 
4 | 4 | from textual.widgets import Button, Label  | 
5 | 5 | from textual.widgets._placeholder import Placeholder  | 
@@ -507,3 +507,44 @@ def compose(self) -> ComposeResult:  | 
507 | 507 |         assert isinstance(app.focused, Focusable)  | 
508 | 508 |         # Check focus chain  | 
509 | 509 |         assert app.screen.focus_chain == [app.query_one(Focusable)]  | 
 | 510 | + | 
 | 511 | + | 
 | 512 | +async def test_trap_focus():  | 
 | 513 | +    class TrapApp(App):  | 
 | 514 | +        CSS = """  | 
 | 515 | +        Screen {  | 
 | 516 | +            layout: horizontal;  | 
 | 517 | +        }  | 
 | 518 | +        """  | 
 | 519 | + | 
 | 520 | +        def compose(self) -> ComposeResult:  | 
 | 521 | +            with Vertical(id="left"):  | 
 | 522 | +                yield Button("1", id="one")  | 
 | 523 | +                yield Button("2", id="two")  | 
 | 524 | +            with Vertical(id="right"):  | 
 | 525 | +                yield Button("A", id="a")  | 
 | 526 | +                yield Button("B", id="b")  | 
 | 527 | + | 
 | 528 | +    app = TrapApp()  | 
 | 529 | +    async with app.run_test():  | 
 | 530 | +        # Normal focus chain reports all focusable widgets  | 
 | 531 | +        focus_ids = [node.id for node in app.screen.focus_chain]  | 
 | 532 | +        assert focus_ids == ["one", "two", "a", "b"]  | 
 | 533 | + | 
 | 534 | +        # Trap the focus on the left container  | 
 | 535 | +        # Since Button#one is focused, the focus chain will be limited to the left vertical  | 
 | 536 | +        app.screen.query_one("#left").trap_focus()  | 
 | 537 | +        focus_ids = [node.id for node in app.screen.focus_chain]  | 
 | 538 | +        assert focus_ids == ["one", "two"]  | 
 | 539 | + | 
 | 540 | +        # Trap focus on the right container  | 
 | 541 | +        # Since the right container doesn't contain a focused widget, we would expect no change  | 
 | 542 | +        app.screen.query_one("#right").trap_focus()  | 
 | 543 | +        focus_ids = [node.id for node in app.screen.focus_chain]  | 
 | 544 | +        assert focus_ids == ["one", "two"]  | 
 | 545 | + | 
 | 546 | +        # Un-trap the focus on the left container  | 
 | 547 | +        # Should restore original focus chain  | 
 | 548 | +        app.screen.query_one("#left").trap_focus(False)  | 
 | 549 | +        focus_ids = [node.id for node in app.screen.focus_chain]  | 
 | 550 | +        assert focus_ids == ["one", "two", "a", "b"]  | 
0 commit comments