Repository: Textualize/textual

Issue:
Make moving a child before or after itself, a no-op
If we try to use `move_child` to move a widget to after/before itself, should we get an error or is that a no-op?
(Add tests to tests/test_widget_child_moving.py accordingly.)

Context: this arose from writing some functionality in a TODO app that sorts TODO items according to their due date and reorders them when the due date is edited. From this point of view, it makes sense for moving to after/before itself to be a no-op.
Otherwise, the sorting code needs to take extra steps to make sure we don't try to move things to where they already are.

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8d6821f92..aeaaf466a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 ### Changed
 
 - App `title` and `sub_title` attributes can be set to any type https://github.com/Textualize/textual/issues/2521
+- Using `Widget.move_child` where the target and the child being moved are the same is now a no-op https://github.com/Textualize/textual/issues/1743
 
 ### Fixed
 
diff --git a/src/textual/widget.py b/src/textual/widget.py
index 4fc364ebf..b116f5393 100644
--- a/src/textual/widget.py
+++ b/src/textual/widget.py
@@ -795,19 +795,15 @@ class Widget(DOMNode):
 
         Args:
             child: The child widget to move.
-            before: Optional location to move before. An `int` is the index
-                of the child to move before, a `str` is a `query_one` query to
-                find the widget to move before.
-            after: Optional location to move after. An `int` is the index
-                of the child to move after, a `str` is a `query_one` query to
-                find the widget to move after.
+            before: Child widget or location index to move before.
+            after: Child widget or location index to move after.
 
         Raises:
             WidgetError: If there is a problem with the child or target.
 
         Note:
-            Only one of ``before`` or ``after`` can be provided. If neither
-            or both are provided a ``WidgetError`` will be raised.
+            Only one of `before` or `after` can be provided. If neither
+            or both are provided a `WidgetError` will be raised.
         """
 
         # One or the other of before or after are required. Can't do
@@ -817,6 +813,10 @@ class Widget(DOMNode):
         elif before is not None and after is not None:
             raise WidgetError("Only one of `before` or `after` can be handled.")
 
+        # We short-circuit the no-op, otherwise it will error later down the road.
+        if child is before or child is after:
+            return
+
         def _to_widget(child: int | Widget, called: str) -> Widget:
             """Ensure a given child reference is a Widget."""
             if isinstance(child, int):


Test patch:
diff --git a/tests/test_widget_child_moving.py b/tests/test_widget_child_moving.py
index 520ef7810..f2d40a4aa 100644
--- a/tests/test_widget_child_moving.py
+++ b/tests/test_widget_child_moving.py
@@ -42,22 +42,18 @@ async def test_move_child_to_outside() -> None:
             pilot.app.screen.move_child(child, before=Widget())
 
 
-@pytest.mark.xfail(
-    strict=True, reason="https://github.com/Textualize/textual/issues/1743"
-)
 async def test_move_child_before_itself() -> None:
     """Test moving a widget before itself."""
+
     async with App().run_test() as pilot:
         child = Widget(Widget())
         await pilot.app.mount(child)
         pilot.app.screen.move_child(child, before=child)
 
 
-@pytest.mark.xfail(
-    strict=True, reason="https://github.com/Textualize/textual/issues/1743"
-)
 async def test_move_child_after_itself() -> None:
     """Test moving a widget after itself."""
+    # Regression test for https://github.com/Textualize/textual/issues/1743
     async with App().run_test() as pilot:
         child = Widget(Widget())
         await pilot.app.mount(child)