web: more a11y for search

This commit is contained in:
2026-05-25 23:25:51 +02:00
parent b5c9188488
commit 2c8f8a218c
2 changed files with 54 additions and 26 deletions
+12 -6
View File
@@ -1,10 +1,10 @@
import { ImageFragmentDefinition, stripHtml } from "@/lib/common";
import { formatDate, formatOccurrenceMonths } from "@/lib/date";
import { unmaskFragment } from "@/gql";
import type { ImageFragment, SearchQuery } from "@/gql/graphql";
import styles from "./searchContainer.module.scss";
import { Image } from "../general/Image";
import { ImageFragmentDefinition, stripHtml } from "@/lib/common";
import { formatDate, formatOccurrenceMonths } from "@/lib/date";
import Link from "next/link";
import { Image } from "../general/Image";
import styles from "./searchContainer.module.scss";
export type SearchResult = SearchQuery["results"][number];
@@ -78,12 +78,18 @@ function getResultSnippet(result: SupportedResult): string | null {
export function SearchResults({ results }: { results: SearchResult[] }) {
if (!results.length) {
return <div className={styles.noResults}>Ingen resultater</div>;
return (
<div className={styles.noResults} aria-live="polite">
Ingen resultater
</div>
);
}
const supportedResults = results.filter(isSupported);
return (
<div>
<p className={styles.resultsCounter}>{results.length} resultater</p>
<p className={styles.resultsCounter} aria-live="polite">
{results.length} resultater
</p>
{supportedResults.map((result) => (
<ResultRow key={result.id} result={result} />
))}
+42 -20
View File
@@ -1,11 +1,17 @@
"use client";
import { useEffect, useRef, useState, useTransition, type ReactNode } from "react";
import { useDebouncedCallback } from "use-debounce";
import { useRouter } from "next/navigation";
import { PageHeader } from "../general/PageHeader";
import { getSearchPath } from "@/lib/common";
import styles from "./searchContainer.module.scss";
import { useRouter } from "next/navigation";
import {
type ReactNode,
useEffect,
useRef,
useState,
useTransition,
} from "react";
import { useDebouncedCallback } from "use-debounce";
import { Icon } from "../general/Icon";
import { PageHeader } from "../general/PageHeader";
import styles from "./searchContainer.module.scss";
export function SearchShell({
initialQuery,
@@ -35,22 +41,38 @@ export function SearchShell({
return (
<div className={styles.searchContainer}>
<PageHeader heading="Søk" />
<div className={styles.searchField}>
<input
name="query"
type="text"
autoFocus
value={inputValue}
onChange={(e) => {
setInputValue(e.target.value);
pushQuery(e.target.value);
}}
/>
<div className={styles.searchIcon}>
<Icon type="search" />
<PageHeader heading={initialQuery ? `Søk: «${initialQuery}»` : "Søk"} />
<form
action="/sok"
method="get"
onSubmit={(e) => {
e.preventDefault();
pushQuery.cancel();
lastPushedRef.current = inputValue;
startTransition(() => {
replace(getSearchPath(inputValue));
});
}}
>
<div className={styles.searchField}>
<label htmlFor="search-query" className="sr-only">
Søk
</label>
<input
id="search-query"
name="q"
type="text"
value={inputValue}
onChange={(e) => {
setInputValue(e.target.value);
pushQuery(e.target.value);
}}
/>
<div className={styles.searchIcon} aria-hidden="true">
<Icon type="search" />
</div>
</div>
</div>
</form>
{children}
</div>
);