Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/api/requests/cms/searchPages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { z } from 'zod'

import { client } from '@/api/utils/api.utils'
import { logger } from '@/lib/logger'

export const searchResultSchema = z.object({
title: z.string(),
sub_title: z.string().optional().nullable(),
meta: z.object({
type: z.string(),
detail_url: z.string(),
html_url: z.string().nullable(),
slug: z.string(),
search_description: z.string().optional(),
}),
})

export const searchResultsSchema = z.object({
items: z.array(searchResultSchema),
meta: z.object({
total_count: z.number(),
}),
})

export type SearchResponse = z.infer<typeof searchResultsSchema>

export const searchPages = async ({ limit, search }: { limit: string; search: string }) => {
try {
const searchParams = new URLSearchParams()
searchParams.set('fields', 'title')
searchParams.set('limit', limit)
searchParams.set('offset', '0')
searchParams.set('search', search)

const { data } = await client<SearchResponse>(`proxy/pages/search`, { searchParams })
const result = searchResultsSchema.safeParse(data)

if (!result.success) {
logger.error('Search Zod Validation error: ', result, data)
}

return result
} catch (error) {
logger.error(error)
return searchResultsSchema.safeParse(error)
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { NextRequest, NextResponse } from 'next/server'

import { getPages } from '@/api/requests/cms/getPages'
import { SearchResponse } from '@/api/requests/cms/searchPages'
import { client } from '@/api/utils/api.utils'

export async function GET(req: NextRequest) {
const searchParams: Record<string, string> = {}
for (const [k, v] of req.nextUrl.searchParams) {
searchParams[k] = v
}
const proxiedResponse = await getPages(searchParams)
// FIXME: should we clean down to expected params here?

Check warning on line 7 in src/app/api/proxy/pages/search/route.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Take the required action to fix the issue indicated by this comment.

See more on https://sonarcloud.io/project/issues?id=UKHSA-Internal_winter-pressures-frontend&issues=AZ13BJ7LtN53tJB8_FNT&open=AZ13BJ7LtN53tJB8_FNT&pullRequest=917

const proxiedResponse = await client<SearchResponse>('pages', { searchParams: req.nextUrl.searchParams })

if (proxiedResponse.data) {
return NextResponse.json(proxiedResponse.data)
Expand Down
80 changes: 36 additions & 44 deletions src/app/components/ui/ukhsa/GovukHeader/GovukHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,52 +11,44 @@ export async function GovukHeader({ serviceTitle }: GovukHeaderProps) {
const { t } = await getServerTranslation('common')
return (
<header className="govuk-header border-none bg-blue" data-module="govuk-header">
<div className="relative ">
<div className="govuk-width-container relative flow-root">
<div className="relative ">
<div className="govuk-width-container relative flow-root">
<div className="govuk-header__logo w-auto">
<Link href="/" className="govuk-header__link govuk-header__link--homepage">
<svg
xmlns="http://www.w3.org/2000/svg"
focusable="false"
role="img"
viewBox="0 0 324 60"
height="30"
width="162"
fill="currentcolor"
className="govuk-header__logotype"
aria-label="GOV.UK"
>
<title>GOV.UK</title>
<g>
<circle cx="20" cy="17.6" r="3.7"></circle>
<circle cx="10.2" cy="23.5" r="3.7"></circle>
<circle cx="3.7" cy="33.2" r="3.7"></circle>
<circle cx="31.7" cy="30.6" r="3.7"></circle>
<circle cx="43.3" cy="17.6" r="3.7"></circle>
<circle cx="53.2" cy="23.5" r="3.7"></circle>
<circle cx="59.7" cy="33.2" r="3.7"></circle>
<circle cx="31.7" cy="30.6" r="3.7"></circle>
<path d="M33.1,9.8c.2-.1.3-.3.5-.5l4.6,2.4v-6.8l-4.6,1.5c-.1-.2-.3-.3-.5-.5l1.9-5.9h-6.7l1.9,5.9c-.2.1-.3.3-.5.5l-4.6-1.5v6.8l4.6-2.4c.1.2.3.3.5.5l-2.6,8c-.9,2.8,1.2,5.7,4.1,5.7h0c3,0,5.1-2.9,4.1-5.7l-2.6-8ZM37,37.9s-3.4,3.8-4.1,6.1c2.2,0,4.2-.5,6.4-2.8l-.7,8.5c-2-2.8-4.4-4.1-5.7-3.8.1,3.1.5,6.7,5.8,7.2,3.7.3,6.7-1.5,7-3.8.4-2.6-2-4.3-3.7-1.6-1.4-4.5,2.4-6.1,4.9-3.2-1.9-4.5-1.8-7.7,2.4-10.9,3,4,2.6,7.3-1.2,11.1,2.4-1.3,6.2,0,4,4.6-1.2-2.8-3.7-2.2-4.2.2-.3,1.7.7,3.7,3,4.2,1.9.3,4.7-.9,7-5.9-1.3,0-2.4.7-3.9,1.7l2.4-8c.6,2.3,1.4,3.7,2.2,4.5.6-1.6.5-2.8,0-5.3l5,1.8c-2.6,3.6-5.2,8.7-7.3,17.5-7.4-1.1-15.7-1.7-24.5-1.7h0c-8.8,0-17.1.6-24.5,1.7-2.1-8.9-4.7-13.9-7.3-17.5l5-1.8c-.5,2.5-.6,3.7,0,5.3.8-.8,1.6-2.3,2.2-4.5l2.4,8c-1.5-1-2.6-1.7-3.9-1.7,2.3,5,5.2,6.2,7,5.9,2.3-.4,3.3-2.4,3-4.2-.5-2.4-3-3.1-4.2-.2-2.2-4.6,1.6-6,4-4.6-3.7-3.7-4.2-7.1-1.2-11.1,4.2,3.2,4.3,6.4,2.4,10.9,2.5-2.8,6.3-1.3,4.9,3.2-1.8-2.7-4.1-1-3.7,1.6.3,2.3,3.3,4.1,7,3.8,5.4-.5,5.7-4.2,5.8-7.2-1.3-.2-3.7,1-5.7,3.8l-.7-8.5c2.2,2.3,4.2,2.7,6.4,2.8-.7-2.3-4.1-6.1-4.1-6.1h10.6,0Z"></path>
</g>
<circle className="govuk-logo-dot" cx="227" cy="36" r="7.3"></circle>
<path d="M94.7,36.1c0,1.9.2,3.6.7,5.4.5,1.7,1.2,3.2,2.1,4.5.9,1.3,2.2,2.4,3.6,3.2,1.5.8,3.2,1.2,5.3,1.2s3.6-.3,4.9-.9c1.3-.6,2.3-1.4,3.1-2.3.8-.9,1.3-2,1.6-3,.3-1.1.5-2.1.5-3v-.4h-11v-6.6h19.5v24h-7.7v-5.4c-.5.8-1.2,1.6-2,2.3-.8.7-1.7,1.3-2.7,1.8-1,.5-2.1.9-3.3,1.2-1.2.3-2.5.4-3.8.4-3.2,0-6-.6-8.4-1.7-2.5-1.1-4.5-2.7-6.2-4.7-1.7-2-3-4.4-3.8-7.1-.9-2.7-1.3-5.6-1.3-8.7s.5-6,1.5-8.7,2.4-5.1,4.2-7.1c1.8-2,4-3.6,6.5-4.7s5.4-1.7,8.6-1.7,4,.2,5.9.7c1.8.5,3.5,1.1,5.1,2,1.5.9,2.9,1.9,4,3.2,1.2,1.2,2.1,2.6,2.8,4.1l-7.7,4.3c-.5-.9-1-1.8-1.6-2.6-.6-.8-1.3-1.5-2.2-2.1-.8-.6-1.7-1-2.8-1.4-1-.3-2.2-.5-3.5-.5-2,0-3.8.4-5.3,1.2s-2.7,1.9-3.6,3.2c-.9,1.3-1.7,2.8-2.1,4.6s-.7,3.5-.7,5.3v.3h0ZM152.9,13.7c3.2,0,6.1.6,8.7,1.7,2.6,1.2,4.7,2.7,6.5,4.7,1.8,2,3.1,4.4,4.1,7.1s1.4,5.6,1.4,8.7-.5,6-1.4,8.7c-.9,2.7-2.3,5.1-4.1,7.1s-4,3.6-6.5,4.7c-2.6,1.1-5.5,1.7-8.7,1.7s-6.1-.6-8.7-1.7c-2.6-1.1-4.7-2.7-6.5-4.7-1.8-2-3.1-4.4-4.1-7.1-.9-2.7-1.4-5.6-1.4-8.7s.5-6,1.4-8.7,2.3-5.1,4.1-7.1c1.8-2,4-3.6,6.5-4.7s5.4-1.7,8.7-1.7h0ZM152.9,50.4c1.9,0,3.6-.4,5-1.1,1.4-.7,2.7-1.7,3.6-3,1-1.3,1.7-2.8,2.2-4.5.5-1.7.8-3.6.8-5.7v-.2c0-2-.3-3.9-.8-5.7-.5-1.7-1.3-3.3-2.2-4.5-1-1.3-2.2-2.3-3.6-3-1.4-.7-3.1-1.1-5-1.1s-3.6.4-5,1.1c-1.5.7-2.7,1.7-3.6,3s-1.7,2.8-2.2,4.5c-.5,1.7-.8,3.6-.8,5.7v.2c0,2.1.3,4,.8,5.7.5,1.7,1.2,3.2,2.2,4.5,1,1.3,2.2,2.3,3.6,3,1.5.7,3.1,1.1,5,1.1ZM189.1,58l-12.3-44h9.8l8.4,32.9h.3l8.2-32.9h9.7l-12.3,44M262.9,50.4c1.3,0,2.5-.2,3.6-.6,1.1-.4,2-.9,2.8-1.7.8-.8,1.4-1.7,1.9-2.9.5-1.2.7-2.5.7-4.1V14h8.6v28.5c0,2.4-.4,4.6-1.3,6.6-.9,2-2.1,3.6-3.7,5-1.6,1.4-3.4,2.4-5.6,3.2-2.2.7-4.5,1.1-7.1,1.1s-4.9-.4-7.1-1.1c-2.2-.7-4-1.8-5.6-3.2s-2.8-3-3.7-5c-.9-2-1.3-4.1-1.3-6.6V14h8.7v27.2c0,1.6.2,2.9.7,4.1.5,1.2,1.1,2.1,1.9,2.9.8.8,1.7,1.3,2.8,1.7s2.3.6,3.6.6h0ZM288.5,14h8.7v19.1l15.5-19.1h10.8l-15.1,17.6,16.1,26.4h-10.2l-11.5-19.7-5.6,6.3v13.5h-8.7"></path>
</svg>
</Link>
</div>
<div className="ukhsa-header govuk-width-container">
<div className="govuk-header__logo w-auto">
<Link href="/" className="govuk-header__link govuk-header__link--homepage">
<svg
xmlns="http://www.w3.org/2000/svg"
focusable="false"
role="img"
viewBox="0 0 324 60"
height="30"
width="162"
fill="currentcolor"
className="govuk-header__logotype"
aria-label="GOV.UK"
>
<title>GOV.UK</title>
<g>
<circle cx="20" cy="17.6" r="3.7"></circle>
<circle cx="10.2" cy="23.5" r="3.7"></circle>
<circle cx="3.7" cy="33.2" r="3.7"></circle>
<circle cx="31.7" cy="30.6" r="3.7"></circle>
<circle cx="43.3" cy="17.6" r="3.7"></circle>
<circle cx="53.2" cy="23.5" r="3.7"></circle>
<circle cx="59.7" cy="33.2" r="3.7"></circle>
<circle cx="31.7" cy="30.6" r="3.7"></circle>
<path d="M33.1,9.8c.2-.1.3-.3.5-.5l4.6,2.4v-6.8l-4.6,1.5c-.1-.2-.3-.3-.5-.5l1.9-5.9h-6.7l1.9,5.9c-.2.1-.3.3-.5.5l-4.6-1.5v6.8l4.6-2.4c.1.2.3.3.5.5l-2.6,8c-.9,2.8,1.2,5.7,4.1,5.7h0c3,0,5.1-2.9,4.1-5.7l-2.6-8ZM37,37.9s-3.4,3.8-4.1,6.1c2.2,0,4.2-.5,6.4-2.8l-.7,8.5c-2-2.8-4.4-4.1-5.7-3.8.1,3.1.5,6.7,5.8,7.2,3.7.3,6.7-1.5,7-3.8.4-2.6-2-4.3-3.7-1.6-1.4-4.5,2.4-6.1,4.9-3.2-1.9-4.5-1.8-7.7,2.4-10.9,3,4,2.6,7.3-1.2,11.1,2.4-1.3,6.2,0,4,4.6-1.2-2.8-3.7-2.2-4.2.2-.3,1.7.7,3.7,3,4.2,1.9.3,4.7-.9,7-5.9-1.3,0-2.4.7-3.9,1.7l2.4-8c.6,2.3,1.4,3.7,2.2,4.5.6-1.6.5-2.8,0-5.3l5,1.8c-2.6,3.6-5.2,8.7-7.3,17.5-7.4-1.1-15.7-1.7-24.5-1.7h0c-8.8,0-17.1.6-24.5,1.7-2.1-8.9-4.7-13.9-7.3-17.5l5-1.8c-.5,2.5-.6,3.7,0,5.3.8-.8,1.6-2.3,2.2-4.5l2.4,8c-1.5-1-2.6-1.7-3.9-1.7,2.3,5,5.2,6.2,7,5.9,2.3-.4,3.3-2.4,3-4.2-.5-2.4-3-3.1-4.2-.2-2.2-4.6,1.6-6,4-4.6-3.7-3.7-4.2-7.1-1.2-11.1,4.2,3.2,4.3,6.4,2.4,10.9,2.5-2.8,6.3-1.3,4.9,3.2-1.8-2.7-4.1-1-3.7,1.6.3,2.3,3.3,4.1,7,3.8,5.4-.5,5.7-4.2,5.8-7.2-1.3-.2-3.7,1-5.7,3.8l-.7-8.5c2.2,2.3,4.2,2.7,6.4,2.8-.7-2.3-4.1-6.1-4.1-6.1h10.6,0Z"></path>
</g>
<circle className="govuk-logo-dot" cx="227" cy="36" r="7.3"></circle>
<path d="M94.7,36.1c0,1.9.2,3.6.7,5.4.5,1.7,1.2,3.2,2.1,4.5.9,1.3,2.2,2.4,3.6,3.2,1.5.8,3.2,1.2,5.3,1.2s3.6-.3,4.9-.9c1.3-.6,2.3-1.4,3.1-2.3.8-.9,1.3-2,1.6-3,.3-1.1.5-2.1.5-3v-.4h-11v-6.6h19.5v24h-7.7v-5.4c-.5.8-1.2,1.6-2,2.3-.8.7-1.7,1.3-2.7,1.8-1,.5-2.1.9-3.3,1.2-1.2.3-2.5.4-3.8.4-3.2,0-6-.6-8.4-1.7-2.5-1.1-4.5-2.7-6.2-4.7-1.7-2-3-4.4-3.8-7.1-.9-2.7-1.3-5.6-1.3-8.7s.5-6,1.5-8.7,2.4-5.1,4.2-7.1c1.8-2,4-3.6,6.5-4.7s5.4-1.7,8.6-1.7,4,.2,5.9.7c1.8.5,3.5,1.1,5.1,2,1.5.9,2.9,1.9,4,3.2,1.2,1.2,2.1,2.6,2.8,4.1l-7.7,4.3c-.5-.9-1-1.8-1.6-2.6-.6-.8-1.3-1.5-2.2-2.1-.8-.6-1.7-1-2.8-1.4-1-.3-2.2-.5-3.5-.5-2,0-3.8.4-5.3,1.2s-2.7,1.9-3.6,3.2c-.9,1.3-1.7,2.8-2.1,4.6s-.7,3.5-.7,5.3v.3h0ZM152.9,13.7c3.2,0,6.1.6,8.7,1.7,2.6,1.2,4.7,2.7,6.5,4.7,1.8,2,3.1,4.4,4.1,7.1s1.4,5.6,1.4,8.7-.5,6-1.4,8.7c-.9,2.7-2.3,5.1-4.1,7.1s-4,3.6-6.5,4.7c-2.6,1.1-5.5,1.7-8.7,1.7s-6.1-.6-8.7-1.7c-2.6-1.1-4.7-2.7-6.5-4.7-1.8-2-3.1-4.4-4.1-7.1-.9-2.7-1.4-5.6-1.4-8.7s.5-6,1.4-8.7,2.3-5.1,4.1-7.1c1.8-2,4-3.6,6.5-4.7s5.4-1.7,8.7-1.7h0ZM152.9,50.4c1.9,0,3.6-.4,5-1.1,1.4-.7,2.7-1.7,3.6-3,1-1.3,1.7-2.8,2.2-4.5.5-1.7.8-3.6.8-5.7v-.2c0-2-.3-3.9-.8-5.7-.5-1.7-1.3-3.3-2.2-4.5-1-1.3-2.2-2.3-3.6-3-1.4-.7-3.1-1.1-5-1.1s-3.6.4-5,1.1c-1.5.7-2.7,1.7-3.6,3s-1.7,2.8-2.2,4.5c-.5,1.7-.8,3.6-.8,5.7v.2c0,2.1.3,4,.8,5.7.5,1.7,1.2,3.2,2.2,4.5,1,1.3,2.2,2.3,3.6,3,1.5.7,3.1,1.1,5,1.1ZM189.1,58l-12.3-44h9.8l8.4,32.9h.3l8.2-32.9h9.7l-12.3,44M262.9,50.4c1.3,0,2.5-.2,3.6-.6,1.1-.4,2-.9,2.8-1.7.8-.8,1.4-1.7,1.9-2.9.5-1.2.7-2.5.7-4.1V14h8.6v28.5c0,2.4-.4,4.6-1.3,6.6-.9,2-2.1,3.6-3.7,5-1.6,1.4-3.4,2.4-5.6,3.2-2.2.7-4.5,1.1-7.1,1.1s-4.9-.4-7.1-1.1c-2.2-.7-4-1.8-5.6-3.2s-2.8-3-3.7-5c-.9-2-1.3-4.1-1.3-6.6V14h8.7v27.2c0,1.6.2,2.9.7,4.1.5,1.2,1.1,2.1,1.9,2.9.8.8,1.7,1.3,2.8,1.7s2.3.6,3.6.6h0ZM288.5,14h8.7v19.1l15.5-19.1h10.8l-15.1,17.6,16.1,26.4h-10.2l-11.5-19.7-5.6,6.3v13.5h-8.7"></path>
</svg>
</Link>
</div>

<div className="float-left flex h-9 w-auto text-center sm:w-3/12">
<Link href="/" className="govuk-header__link govuk-header__service-name m-0 mt-3 h-6">
{serviceTitle}
</Link>
</div>
<Link href="/" className="ukhsa-service-name govuk-header__link govuk-header__service-name">
{serviceTitle}
</Link>

<div className="ukhsa-search float-right mt-3 h-9 sm:w-5/12">
<Search placeholder={t('search.searchTitle')} label={t('search.searchLabel')} />
</div>
</div>
</div>
<div className="ukhsa-search">
<Search placeholder={t('search.searchTitle')} label={t('search.searchLabel')} />
</div>
</div>
</header>
Expand Down
15 changes: 13 additions & 2 deletions src/app/components/ui/ukhsa/Search/Search.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { render } from '@/config/test-utils'

import Search from './Search'

const getPages = jest.mocked(client)
const mockClient = jest.mocked(client)

beforeEach(() => {
jest.clearAllMocks()
getPages.mockResolvedValue({
mockClient.mockResolvedValue({
status: 200,
data: {
meta: { total_count: 1 },
Expand Down Expand Up @@ -51,3 +51,14 @@ test('searching displays results', async () => {
expect(getByRole('textbox').getAttribute('value')).toBe('query')
expect(await findByRole('link', { name: 'Result 1' })).toBeVisible()
})

test('searching uses correct query', async () => {
const query = 'query'
const search = render(<Search placeholder="Search" label="Search" />)
const { getByRole, findByRole } = search
await userEvent.type(getByRole('textbox'), query)
// Wait for the effect to finish
await findByRole('link', { name: 'Result 1' })
expect(mockClient).toHaveBeenCalledTimes(1)
expect(mockClient.mock.calls[0][1]?.searchParams?.toString()).toEqual(`fields=title&limit=5&offset=0&search=${query}`)
})
111 changes: 80 additions & 31 deletions src/app/components/ui/ukhsa/Search/Search.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use client'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { useEffect, useRef, useState } from 'react'
import { useDebounceValue } from 'usehooks-ts'

import { getPages } from '@/api/requests/cms/getPages'
import { searchPages } from '@/api/requests/cms/searchPages'

const DEBOUNCE_MILLISECONDS = 300

Expand All @@ -14,8 +14,6 @@ interface SearchProps {

export function Search({ label, placeholder }: SearchProps) {
const limit = 5

// CSS used to hide the search bar for non-JS users
interface SearchResult {
readonly title: string
readonly meta: {
Expand All @@ -26,50 +24,101 @@ export function Search({ label, placeholder }: SearchProps) {
const [searchInputValue, setSearchInputValue] = useState('')
const [debouncedSearchValue] = useDebounceValue(searchInputValue, DEBOUNCE_MILLISECONDS)
const [searchResults, setSearchResults] = useState<SearchResult[] | undefined>([])
const [isDropdownOpen, setIsDropdownOpen] = useState(false)
const searchContainerRef = useRef<HTMLDivElement>(null)
const searchInputRef = useRef<HTMLInputElement>(null)

const getSearchResults = async ({ query }: { query: string }) => {
const pages = await getPages({ limit: limit.toString(), search: query })
const getSearchResults = async ({ search }: { search: string }) => {
const pages = await searchPages({ limit: limit.toString(), search })
setSearchResults(pages?.data?.items)
}

useEffect(() => {
if (debouncedSearchValue) {
getSearchResults({ query: debouncedSearchValue })
getSearchResults({ search: debouncedSearchValue })
} else {
setSearchResults([])
}
}, [debouncedSearchValue])

useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (searchContainerRef.current && !searchContainerRef.current.contains(event.target as Node)) {
setIsDropdownOpen(false)
}
}

document.addEventListener('mousedown', handleClickOutside)

return () => {
document.removeEventListener('mousedown', handleClickOutside)
}
}, [])

return (
<form method="GET" aria-label={label}>
<div className="govuk-form-group">
<div className="govuk-form-group relative" ref={searchContainerRef}>
<label className="govuk-label" htmlFor="search" hidden={true}>
{placeholder}
</label>
<input
className="govuk-input"
id="search"
name="search"
type="text"
placeholder={placeholder}
value={searchInputValue}
onChange={(event) => {
setSearchInputValue(event.currentTarget.value)
}}
/>
<div className="absolute z-[1000] max-h-[200px] overflow-y-auto bg-white shadow-[0px_15px_15px_0px_rgba(0,0,0,0.35)] sm:w-5/12">
<ul>
{searchResults?.map(({ title, meta: { html_url } }, i) => (
<li
key={`result-${i}`}
value="{i}"
className="govuk-!-padding-3 govuk-!-margin-left-2 govuk-!-margin-right-2 border-b shadow-[0_1px_0_#929191]"
>
<Link href={html_url || ''}>{title}</Link>
</li>
))}
</ul>
<div className="relative">
<input
ref={searchInputRef}
className="govuk-input govuk-!-font-size-16 govuk-!-width-full pr-[2.5rem]"
id="search"
name="search"
type="text"
placeholder={placeholder}
value={searchInputValue}
onFocus={() => {
setIsDropdownOpen(true)
}}
onKeyDown={(event) => {
if (event.key === 'Escape') {
setIsDropdownOpen(false)
}
}}
onChange={(event) => {
setSearchInputValue(event.currentTarget.value)
setIsDropdownOpen(true)
}}
/>
{searchInputValue && (
<button
type="button"
aria-label="Clear search"
className="absolute inset-y-0 right-0 mt-[2px] flex h-[36px] items-center justify-center px-3 text-black"
style={{ fontSize: '1.5rem', lineHeight: 1 }}
onClick={() => {
setSearchInputValue('')
setSearchResults([])
setIsDropdownOpen(false)
searchInputRef.current?.focus()
}}
>
&times;
</button>
)}
</div>
{isDropdownOpen && searchResults && searchResults.length > 0 && (
<div className="absolute z-[1000] max-h-[200px] w-full overflow-y-auto bg-white shadow-[0px_15px_15px_0px_rgba(0,0,0,0.35)]">
<ul>
{searchResults.map(({ title, meta: { html_url } }, i) => (
<li
key={`result-${i}`}
value="{i}"
className="govuk-!-margin-left-2 govuk-!-margin-right-2 border-b shadow-[0_1px_0_#929191]"
>
<Link className="govuk-link group block focus:border-b-0 focus:outline-none" href={html_url || ''}>
<div className="size-full border-2 border-transparent p-3 group-focus:border-black group-focus:bg-yellow">
{title}
</div>
</Link>
</li>
))}
</ul>
</div>
)}
<noscript>
<style>{'input#search {display: none; }'}</style>
</noscript>
Expand Down
Loading
Loading