We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
🎛️ Migrate header search Stimulus Controller
> ℹ️ **Part of the [Stimulus  🎛️ RFC 78](https://github.com/wagtail/rfcs/pull/78)**

### Is your proposal related to a problem?

We have a core.js implementations of JavaScript code that, when a matching search input receives changes, will trigger an async request to the relevant search results listing. Once the endpoint returns with HTML, it will be patched into the results container HTML element.

### Describe the solution you'd like

* Create a stimulus controller `w-search` that will replace the existing ad-hoc JS implementation
* The behaviour should be exactly the same as current state but using Stimulus data attributes for the behaviour & classes declaration (note: likely we will drop the `autofocus` and may not re-introduce the `slide` jQuery animation)
* Controller should be written in TypeScript
* Ensure that the existing unit tests are created to reflect this new behaviour
* We will need to document an upgrade consideration that the previous `window.headerSearch` approach will not work in a future release.
* We may want to introduce a console warning once all the Wagtail usage of `window.headerSearch` has been removed
* Nice to have - a Storybook story for this component

### Additional context

* Implementation https://github.com/wagtail/wagtail/blob/main/client/src/entrypoints/admin/core.js#L251-L306
* There is a very similar (almost cut & paste) of logic that is used in the chooser modals for searching here https://github.com/wagtail/wagtail/blob/main/client/src/includes/chooserModal.js#L109-L176 (the Stimulus will likely replace this but may be out of scope for this issue

### Potential approach

#### Support `input` only usage (with using `window.headerSearch` config)

```JS
window.headerSearch = {
    url: "{% url 'wagtailimages:listing_results' %}",
    targetOutput: "#image-results"
}
```

```html
<div class="w-field__input" data-field-input="">
    <svg class="icon icon-search w-field__icon" aria-hidden="true">
        <use href="#icon-search"></use>
    </svg>
    <input
        type="text"
        name="q"
        placeholder="Search images"
        data-controller="w-search"
        data-action="change->w-search#search cut->w-search#search keyup->w-search#search paste->w-search#search"
        id="id_q"
    />
</div>
```

#### Support `input` only usage

```html
<div class="w-field__input" data-field-input="">
    <svg class="icon icon-search w-field__icon" aria-hidden="true">
        <use href="#icon-search"></use>
    </svg>
    <input
        type="text"
        name="q"
        placeholder="Search images"
        data-controller="w-search"
        data-action="change->w-search#search cut->w-search#search keyup->w-search#search paste->w-search#search"
        id="id_q"
        data-w-search-results-value="#image-results"
        data-w-search-url-value="/admin/images/results/"
    />
</div>
```

#### Support controlled form with search input as a target

```html
<form
  class="col search-form"
  action="/admin/images/"
  method="get"
  novalidate=""
  role="search"
  data-controller="w-search"
  data-w-search-url-value="/admin/images/results/"
>
  <div class="w-field__wrapper w-mb-0" data-field-wrapper="">
    <label class="w-field__label w-sr-only" for="id_q" id="id_q-label">Search term</label>
    <div class="w-field w-field--char_field w-field--text_input">
      <div class="w-field__input" data-field-input="">
        <svg class="icon icon-search w-field__icon" aria-hidden="true"><use href="#icon-search"></use></svg>
        <input
          type="text"
          name="q"
          placeholder="Search images"
          data-w-search-target="input"
          data-action="change->w-search#search cut->w-search#search keyup->w-search#search paste->w-search#search"
          id="id_q"
        />
      </div>
    </div>
  </div>
  <div class="visuallyhidden"><input disabled="" type="submit" aria-hidden="true" /></div>
</form>
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `wagtail/admin/forms/search.py`
Content:
```
1 from django import forms
2 from django.utils.translation import gettext as _
3 from django.utils.translation import gettext_lazy
4 
5 
6 class SearchForm(forms.Form):
7     def __init__(self, *args, **kwargs):
8         placeholder = kwargs.pop("placeholder", _("Search"))
9         super().__init__(*args, **kwargs)
10         self.fields["q"].widget.attrs = {"placeholder": placeholder}
11 
12     q = forms.CharField(
13         label=gettext_lazy("Search term"),
14         widget=forms.TextInput(),
15         required=False,
16     )
17
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/wagtail/admin/forms/search.py b/wagtail/admin/forms/search.py
--- a/wagtail/admin/forms/search.py
+++ b/wagtail/admin/forms/search.py
@@ -7,7 +7,10 @@
     def __init__(self, *args, **kwargs):
         placeholder = kwargs.pop("placeholder", _("Search"))
         super().__init__(*args, **kwargs)
-        self.fields["q"].widget.attrs = {"placeholder": placeholder}
+        self.fields["q"].widget.attrs = {
+            "placeholder": placeholder,
+            "data-w-swap-target": "input",
+        }
 
     q = forms.CharField(
         label=gettext_lazy("Search term"),

{"golden_diff": "diff --git a/wagtail/admin/forms/search.py b/wagtail/admin/forms/search.py\n--- a/wagtail/admin/forms/search.py\n+++ b/wagtail/admin/forms/search.py\n@@ -7,7 +7,10 @@\n     def __init__(self, *args, **kwargs):\n         placeholder = kwargs.pop(\"placeholder\", _(\"Search\"))\n         super().__init__(*args, **kwargs)\n-        self.fields[\"q\"].widget.attrs = {\"placeholder\": placeholder}\n+        self.fields[\"q\"].widget.attrs = {\n+            \"placeholder\": placeholder,\n+            \"data-w-swap-target\": \"input\",\n+        }\n \n     q = forms.CharField(\n         label=gettext_lazy(\"Search term\"),\n", "issue": "\ud83c\udf9b\ufe0f Migrate header search Stimulus Controller\n> \u2139\ufe0f **Part of the [Stimulus  \ud83c\udf9b\ufe0f RFC 78](https://github.com/wagtail/rfcs/pull/78)**\r\n\r\n### Is your proposal related to a problem?\r\n\r\nWe have a core.js implementations of JavaScript code that, when a matching search input receives changes, will trigger an async request to the relevant search results listing. Once the endpoint returns with HTML, it will be patched into the results container HTML element.\r\n\r\n### Describe the solution you'd like\r\n\r\n* Create a stimulus controller `w-search` that will replace the existing ad-hoc JS implementation\r\n* The behaviour should be exactly the same as current state but using Stimulus data attributes for the behaviour & classes declaration (note: likely we will drop the `autofocus` and may not re-introduce the `slide` jQuery animation)\r\n* Controller should be written in TypeScript\r\n* Ensure that the existing unit tests are created to reflect this new behaviour\r\n* We will need to document an upgrade consideration that the previous `window.headerSearch` approach will not work in a future release.\r\n* We may want to introduce a console warning once all the Wagtail usage of `window.headerSearch` has been removed\r\n* Nice to have - a Storybook story for this component\r\n\r\n### Additional context\r\n\r\n* Implementation https://github.com/wagtail/wagtail/blob/main/client/src/entrypoints/admin/core.js#L251-L306\r\n* There is a very similar (almost cut & paste) of logic that is used in the chooser modals for searching here https://github.com/wagtail/wagtail/blob/main/client/src/includes/chooserModal.js#L109-L176 (the Stimulus will likely replace this but may be out of scope for this issue\r\n\r\n### Potential approach\r\n\r\n#### Support `input` only usage (with using `window.headerSearch` config)\r\n\r\n```JS\r\nwindow.headerSearch = {\r\n    url: \"{% url 'wagtailimages:listing_results' %}\",\r\n    targetOutput: \"#image-results\"\r\n}\r\n```\r\n\r\n```html\r\n<div class=\"w-field__input\" data-field-input=\"\">\r\n    <svg class=\"icon icon-search w-field__icon\" aria-hidden=\"true\">\r\n        <use href=\"#icon-search\"></use>\r\n    </svg>\r\n    <input\r\n        type=\"text\"\r\n        name=\"q\"\r\n        placeholder=\"Search images\"\r\n        data-controller=\"w-search\"\r\n        data-action=\"change->w-search#search cut->w-search#search keyup->w-search#search paste->w-search#search\"\r\n        id=\"id_q\"\r\n    />\r\n</div>\r\n```\r\n\r\n#### Support `input` only usage\r\n\r\n```html\r\n<div class=\"w-field__input\" data-field-input=\"\">\r\n    <svg class=\"icon icon-search w-field__icon\" aria-hidden=\"true\">\r\n        <use href=\"#icon-search\"></use>\r\n    </svg>\r\n    <input\r\n        type=\"text\"\r\n        name=\"q\"\r\n        placeholder=\"Search images\"\r\n        data-controller=\"w-search\"\r\n        data-action=\"change->w-search#search cut->w-search#search keyup->w-search#search paste->w-search#search\"\r\n        id=\"id_q\"\r\n        data-w-search-results-value=\"#image-results\"\r\n        data-w-search-url-value=\"/admin/images/results/\"\r\n    />\r\n</div>\r\n```\r\n\r\n#### Support controlled form with search input as a target\r\n\r\n```html\r\n<form\r\n  class=\"col search-form\"\r\n  action=\"/admin/images/\"\r\n  method=\"get\"\r\n  novalidate=\"\"\r\n  role=\"search\"\r\n  data-controller=\"w-search\"\r\n  data-w-search-url-value=\"/admin/images/results/\"\r\n>\r\n  <div class=\"w-field__wrapper w-mb-0\" data-field-wrapper=\"\">\r\n    <label class=\"w-field__label w-sr-only\" for=\"id_q\" id=\"id_q-label\">Search term</label>\r\n    <div class=\"w-field w-field--char_field w-field--text_input\">\r\n      <div class=\"w-field__input\" data-field-input=\"\">\r\n        <svg class=\"icon icon-search w-field__icon\" aria-hidden=\"true\"><use href=\"#icon-search\"></use></svg>\r\n        <input\r\n          type=\"text\"\r\n          name=\"q\"\r\n          placeholder=\"Search images\"\r\n          data-w-search-target=\"input\"\r\n          data-action=\"change->w-search#search cut->w-search#search keyup->w-search#search paste->w-search#search\"\r\n          id=\"id_q\"\r\n        />\r\n      </div>\r\n    </div>\r\n  </div>\r\n  <div class=\"visuallyhidden\"><input disabled=\"\" type=\"submit\" aria-hidden=\"true\" /></div>\r\n</form>\r\n```\n", "before_files": [{"content": "from django import forms\nfrom django.utils.translation import gettext as _\nfrom django.utils.translation import gettext_lazy\n\n\nclass SearchForm(forms.Form):\n    def __init__(self, *args, **kwargs):\n        placeholder = kwargs.pop(\"placeholder\", _(\"Search\"))\n        super().__init__(*args, **kwargs)\n        self.fields[\"q\"].widget.attrs = {\"placeholder\": placeholder}\n\n    q = forms.CharField(\n        label=gettext_lazy(\"Search term\"),\n        widget=forms.TextInput(),\n        required=False,\n    )\n", "path": "wagtail/admin/forms/search.py"}], "after_files": [{"content": "from django import forms\nfrom django.utils.translation import gettext as _\nfrom django.utils.translation import gettext_lazy\n\n\nclass SearchForm(forms.Form):\n    def __init__(self, *args, **kwargs):\n        placeholder = kwargs.pop(\"placeholder\", _(\"Search\"))\n        super().__init__(*args, **kwargs)\n        self.fields[\"q\"].widget.attrs = {\n            \"placeholder\": placeholder,\n            \"data-w-swap-target\": \"input\",\n        }\n\n    q = forms.CharField(\n        label=gettext_lazy(\"Search term\"),\n        widget=forms.TextInput(),\n        required=False,\n    )\n", "path": "wagtail/admin/forms/search.py"}]}