web: more a11y for search
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
import { ImageFragmentDefinition, stripHtml } from "@/lib/common";
|
|
||||||
import { formatDate, formatOccurrenceMonths } from "@/lib/date";
|
|
||||||
import { unmaskFragment } from "@/gql";
|
import { unmaskFragment } from "@/gql";
|
||||||
import type { ImageFragment, SearchQuery } from "@/gql/graphql";
|
import type { ImageFragment, SearchQuery } from "@/gql/graphql";
|
||||||
import styles from "./searchContainer.module.scss";
|
import { ImageFragmentDefinition, stripHtml } from "@/lib/common";
|
||||||
import { Image } from "../general/Image";
|
import { formatDate, formatOccurrenceMonths } from "@/lib/date";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { Image } from "../general/Image";
|
||||||
|
import styles from "./searchContainer.module.scss";
|
||||||
|
|
||||||
export type SearchResult = SearchQuery["results"][number];
|
export type SearchResult = SearchQuery["results"][number];
|
||||||
|
|
||||||
@@ -78,12 +78,18 @@ function getResultSnippet(result: SupportedResult): string | null {
|
|||||||
|
|
||||||
export function SearchResults({ results }: { results: SearchResult[] }) {
|
export function SearchResults({ results }: { results: SearchResult[] }) {
|
||||||
if (!results.length) {
|
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);
|
const supportedResults = results.filter(isSupported);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p className={styles.resultsCounter}>{results.length} resultater</p>
|
<p className={styles.resultsCounter} aria-live="polite">
|
||||||
|
{results.length} resultater
|
||||||
|
</p>
|
||||||
{supportedResults.map((result) => (
|
{supportedResults.map((result) => (
|
||||||
<ResultRow key={result.id} result={result} />
|
<ResultRow key={result.id} result={result} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
"use client";
|
"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 { 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 { Icon } from "../general/Icon";
|
||||||
|
import { PageHeader } from "../general/PageHeader";
|
||||||
|
import styles from "./searchContainer.module.scss";
|
||||||
|
|
||||||
export function SearchShell({
|
export function SearchShell({
|
||||||
initialQuery,
|
initialQuery,
|
||||||
@@ -35,22 +41,38 @@ export function SearchShell({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.searchContainer}>
|
<div className={styles.searchContainer}>
|
||||||
<PageHeader heading="Søk" />
|
<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}>
|
<div className={styles.searchField}>
|
||||||
|
<label htmlFor="search-query" className="sr-only">
|
||||||
|
Søk
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
name="query"
|
id="search-query"
|
||||||
|
name="q"
|
||||||
type="text"
|
type="text"
|
||||||
autoFocus
|
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setInputValue(e.target.value);
|
setInputValue(e.target.value);
|
||||||
pushQuery(e.target.value);
|
pushQuery(e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className={styles.searchIcon}>
|
<div className={styles.searchIcon} aria-hidden="true">
|
||||||
<Icon type="search" />
|
<Icon type="search" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user