mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2026-09-19 20:06:10 +00:00
Search modal native submit (#4427)
* UI - Search - Fix search modal navigating to the host root on sub-path deployments
base_path was referenced by search-modal.js but never defined, so searching
always jumped to the host root instead of the X-Forwarded-Prefix sub-path.
* UI - Search modal - submit natively instead of rebuilding the URL in JS
Alternative to defining a `base_path` JS global: give the search form a
server-rendered `action`, so url_for() supplies the reverse-proxy sub-path the
same way every other link on the page already does, and let the browser submit.
Drops the submit handler and the Enter handler from search-modal.js - Enter in
the input reaches the footer's submit button via implicit submission, which also
runs the `required` validation the synthetic `new Event('submit')` skipped.
The hidden tag field is only rendered when a tag is active, so a plain search no
longer carries an empty value.
Also drops the nginx-job grep for the rendered markup - test_search.py already
covers the sub-path case, and asserting on an exact HTML attribute string from a
shell grep breaks on any unrelated edit to that tag.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: ponstream24 <87808547+ponstream24@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
ponstream24
parent
8d938b5966
commit
cd66f9ed54
@@ -7,7 +7,6 @@
|
||||
// The Search button is rendered in the left rail and the mobile drawer.
|
||||
const openSearchButtons = document.querySelectorAll('.js-open-search-modal');
|
||||
const closeSearchButton = document.getElementById('close-search-modal');
|
||||
const searchForm = document.getElementById('search-form');
|
||||
const searchInput = document.getElementById('search-modal-input');
|
||||
|
||||
if (!searchModal || openSearchButtons.length === 0) {
|
||||
@@ -93,43 +92,8 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Handle Enter key in search input
|
||||
if (searchInput) {
|
||||
searchInput.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
if (searchForm) {
|
||||
// Trigger form submission programmatically
|
||||
searchForm.dispatchEvent(new Event('submit'));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle form submission
|
||||
if (searchForm) {
|
||||
searchForm.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Get form data
|
||||
const formData = new FormData(searchForm);
|
||||
const searchQuery = formData.get('q');
|
||||
const tags = formData.get('tags');
|
||||
|
||||
// Build URL
|
||||
const params = new URLSearchParams();
|
||||
if (searchQuery) {
|
||||
params.append('q', searchQuery);
|
||||
}
|
||||
if (tags) {
|
||||
params.append('tags', tags);
|
||||
}
|
||||
|
||||
// Navigate to search results (always redirect to watchlist home)
|
||||
// Use base_path if available (for sub-path deployments like /enlighten-richerx)
|
||||
const basePath = typeof base_path !== 'undefined' ? base_path : '';
|
||||
window.location.href = basePath + '/?' + params.toString();
|
||||
});
|
||||
}
|
||||
// Submission is left to the browser: the form carries a server-rendered action
|
||||
// (correct under a reverse-proxy sub-path) and Enter in the input triggers implicit
|
||||
// submission via the footer's submit button, which also runs `required` validation.
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -317,11 +317,13 @@
|
||||
<h2 class="modal-title" id="search-modal-title">{{ _('Search') }}</h2>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="search-form" method="GET">
|
||||
{# Plain GET submit to the watchlist - url_for() carries the reverse-proxy sub-path
|
||||
(SCRIPT_NAME), so no client-side URL building is needed. #}
|
||||
<form id="search-form" method="GET" action="{{ url_for('watchlist.index') }}">
|
||||
<div class="pure-control-group">
|
||||
<label for="search-modal-input">{% if active_tag_uuid %}{{ _("URL or Title in '%(title)s'", title=active_tag.title) }}{% else %}{{ _('URL or Title') }}{% endif %}</label>
|
||||
<input id="search-modal-input" class="m-d" name="q" placeholder="{{ _('Enter search term...') }}" required type="text" value="" autofocus>
|
||||
<input name="tags" type="hidden" value="{% if active_tag_uuid %}{{active_tag_uuid}}{% endif %}">
|
||||
{% if active_tag_uuid %}<input name="tags" type="hidden" value="{{ active_tag_uuid }}">{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -71,3 +71,12 @@ def test_search_in_tag_limit(client, live_server, measure_memory_usage, datastor
|
||||
assert urls[0].split(' ')[0].encode('utf-8') in res.data, urls[0].encode('utf-8')
|
||||
assert urls[1].split(' ')[0].encode('utf-8') not in res.data, urls[0].encode('utf-8')
|
||||
|
||||
|
||||
def test_search_modal_form_action(client, live_server, measure_memory_usage, datastore_path):
|
||||
# The search modal submits as a plain GET form, so its action has to carry the
|
||||
# reverse-proxy sub-path (SCRIPT_NAME), otherwise search jumps to the host root.
|
||||
res = client.get(url_for("watchlist.index"))
|
||||
assert b'<form id="search-form" method="GET" action="/">' in res.data
|
||||
|
||||
res = client.get("/", base_url="http://localhost/sub-path")
|
||||
assert b'<form id="search-form" method="GET" action="/sub-path/">' in res.data
|
||||
|
||||
Reference in New Issue
Block a user