search: make the navigation form work from every page (AUD-151)
The nav form carried hx-get="/search" hx-target="#search-results", but #search-results exists only on /search. On every other page htmx found no target and silently discarded the response: a search box that looks like it works and does nothing. - SearchForm is now a plain GET with no htmx attributes (navigation and the landing page), so it navigates to /search?q=… from any page and with JavaScript disabled. - SearchFormLive keeps the in-place swap on the search page, where the target does exist. For HX-Request the server now returns the block's *contents* (SearchResultsBody), so the fragment no longer nests a second element with id="search-results" inside the live region. - Empty query shows a prompt instead of an empty list. A zero-match query says "Nothing found" and offers the six areas plus the evidence index. - The result count is role="status" inside the existing aria-live container, so a screen reader reads "4 results for …" rather than a run of titles. Tests (internal/server, internal/site): - the nav form carries no htmx attribute on nine pages, and no built page carries an hx-target whose element is missing (walks all of dist/); - search submitted from /, /edge/ and /platform/ lands on a page with results with no HX-Request header, i.e. the JavaScript-disabled path; - empty query, zero match and the announced count; - a content change followed by a build is visible in dist/search.db. That is the deploy path: deploy/deploy.sh runs `make dist` before it stages a release, and search.Build removes and rewrites the index every build. DoD: search from home, /edge and /platform lands on results; works with JavaScript disabled; zero-match query shows a message; content change followed by a deploy is reflected in search; no htmx request targets a missing element. (cherry picked from commit a56c795768e1e9b10204aae04c3612d7a96775a7)
This commit is contained in:
@@ -413,9 +413,10 @@ func (s *Server) fragmentNotFound(w http.ResponseWriter, msg string) {
|
||||
_, _ = io.WriteString(w, `<div class="stage-empty"><p class="stage-empty-title">`+msg+`</p><p class="stage-hint">The <a href="/evidence/">evidence index</a> lists every fragment in this build.</p></div>`)
|
||||
}
|
||||
|
||||
// handleSearch runs a keyword search. With HTMX it returns just the results
|
||||
// block; without JavaScript the same handler returns the whole page, so the form
|
||||
// works in both cases.
|
||||
// handleSearch runs a keyword search. With HTMX it returns just the contents of
|
||||
// the results block, which the search page's own form swaps into #search-results;
|
||||
// without JavaScript the same handler returns the whole page, so the form works
|
||||
// in both cases.
|
||||
func (s *Server) handleSearch(w http.ResponseWriter, r *http.Request) {
|
||||
q := strings.TrimSpace(r.URL.Query().Get("q"))
|
||||
failed := false
|
||||
@@ -451,7 +452,9 @@ func (s *Server) handleSearch(w http.ResponseWriter, r *http.Request) {
|
||||
Render(context.Context, io.Writer) error
|
||||
} = templates.SearchPage(view)
|
||||
if r.Header.Get("HX-Request") == "true" {
|
||||
c = templates.SearchResults(view)
|
||||
// The element that holds #search-results stays where it is, so the
|
||||
// fragment carries the contents and not the wrapper.
|
||||
c = templates.SearchResultsBody(view)
|
||||
}
|
||||
if err := c.Render(r.Context(), &buf); err != nil {
|
||||
s.log.Error("render search", "error", err)
|
||||
|
||||
Reference in New Issue
Block a user