Skip to content

Commit

Permalink
chore: use @ariakit/react for new country picker (#306)
Browse files Browse the repository at this point in the history
  • Loading branch information
karl-run authored Aug 26, 2024
1 parent 33045e6 commit 4d4bc12
Show file tree
Hide file tree
Showing 12 changed files with 107 additions and 129 deletions.
4 changes: 0 additions & 4 deletions src/app/api/country/_types.ts

This file was deleted.

8 changes: 5 additions & 3 deletions src/app/api/country/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@
import { NextResponse } from 'next/server'

import { isUserLoggedIn } from '../../../auth/rsc'
import { countries } from '../../../utils/countries'

import { countriesResponse } from './_countries'

/**
* @Deprecated Will be removed in a different deploy for zero downtime
*/
export async function GET(): Promise<NextResponse> {
const authed = await isUserLoggedIn()
if (!authed) {
return NextResponse.json({ message: 'Not logged in' }, { status: 401 })
}

return NextResponse.json(countriesResponse)
return NextResponse.json(countries)
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useController } from 'react-hook-form'
import { UtenlanskFormValues } from '../../Sykmelding/SykmeldingForm'
import FieldError from '../FieldError/FieldError'

import CountryCombobox from './CountryCombobox/CountryCombobox'
import CountryCombobox from './country-combobox/CountryCombobox'
import styles from './CountryPicker.module.css'

interface Props {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ describe('CountryTypeahead', () => {
<CountryCombobox onSelect={mockSelect} initialValue={null} onChange={() => void 0} />,
)

await waitForPickerToBeLoaded()
await userEvent.type(screen.getByRole('combobox', { name: 'Landet sykmeldingen ble skrevet' }), 'Zim')
await userEvent.click(screen.getByRole('option', { name: 'Zimbabwe' }))

Expand All @@ -24,7 +23,6 @@ describe('CountryTypeahead', () => {
const mockSelect = vi.fn()
render(<CountryCombobox onSelect={mockSelect} initialValue={null} onChange={() => void 0} />)

await waitForPickerToBeLoaded()
await userEvent.type(screen.getByRole('combobox', { name: 'Landet sykmeldingen ble skrevet' }), 'Zim')
await userEvent.click(screen.getByRole('option', { name: 'Zimbabwe' }))

Expand All @@ -35,17 +33,16 @@ describe('CountryTypeahead', () => {
const mockSelect = vi.fn()
render(<CountryCombobox onSelect={mockSelect} initialValue={null} onChange={() => void 0} />)

await waitForPickerToBeLoaded()
await userEvent.type(screen.getByRole('combobox', { name: 'Landet sykmeldingen ble skrevet' }), 'No')
const results = screen.getAllByRole('option')

expect(results).toHaveLength(4)
expect(results[0]).toHaveTextContent('Nord-Korea')
expect(results[1]).toHaveTextContent('Norge')
expect(results[2]).toHaveTextContent('Libanon')
expect(results[0]).toHaveTextContent('Libanon')
expect(results[1]).toHaveTextContent('Nord-Korea')
expect(results[2]).toHaveTextContent('Norge')
expect(results[3]).toHaveTextContent('San Marino')

await userEvent.click(results[1])
await userEvent.click(results[2])

expect(mockSelect).toHaveBeenCalledWith('NOR')
})
Expand All @@ -54,7 +51,6 @@ describe('CountryTypeahead', () => {
const mockSelect = vi.fn()
render(<CountryCombobox onSelect={mockSelect} initialValue="NOR" onChange={() => void 0} />)

await waitForPickerToBeLoaded()
await waitFor(() =>
expect(screen.getByRole('combobox', { name: 'Landet sykmeldingen ble skrevet' })).toHaveValue('Norge'),
)
Expand All @@ -65,16 +61,9 @@ describe('CountryTypeahead', () => {
const mockSelect = vi.fn()
render(<CountryCombobox onSelect={mockSelect} initialValue={null} onChange={() => void 0} />)

await waitForPickerToBeLoaded()
await userEvent.type(screen.getByRole('combobox', { name: 'Landet sykmeldingen ble skrevet' }), 'IkkeEtLand')

expect(screen.getByText('Ingen treff')).toBeInTheDocument()
expect(screen.getByText('Fant ingen land med navn eller kode "IkkeEtLand"')).toBeInTheDocument()
expect(mockSelect).not.toHaveBeenCalled()
})
})

async function waitForPickerToBeLoaded(): Promise<void> {
await waitFor(() =>
expect(screen.getByRole('combobox', { name: 'Landet sykmeldingen ble skrevet' })).not.toBeDisabled(),
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React, { ReactElement, startTransition, useState } from 'react'

import {
AkselifiedCombobox,
AkselifiedComboboxDisclosure,
AkselifiedComboboxItem,
AkselifiedComboboxNonInteractiveFeedbackItem,
AkselifiedComboboxNonSelectables,
AkselifiedComboboxPopover,
AkselifiedComboboxWrapper,
} from '../../combobox/AkselifiedCombobox'
import { countries } from '../../../../utils/countries'

type Props = {
id?: string
onSelect: (countryCode: string | null) => void
onChange: () => void
initialValue: string | null
}

function CountryCombobox({ id, onChange, onSelect, initialValue }: Props): ReactElement {
const defaultCountry = countries.find((country) => country.code === initialValue)?.name
const [searchValue, setSearchValue] = useState(defaultCountry ?? '')
const suggestions = !searchValue.trim()
? countries
: countries.filter(
(country) =>
country.name.toLowerCase().includes(searchValue.toLowerCase()) ||
country.code.toLowerCase().includes(searchValue.toLowerCase()),
)

return (
<AkselifiedComboboxWrapper
defaultValue={defaultCountry ?? undefined}
labelId="country-typeahead-label"
label="Landet sykmeldingen ble skrevet"
setValue={(value) => {
const country = countries.find((country) => country.name === value)

startTransition(() => {
if (country) {
onSelect(country.code)
setSearchValue(country.name)
} else {
onChange()
setSearchValue(value)
}
})
}}
>
<AkselifiedCombobox id={id} aria-labelledby="country-typeahead-label" placeholder="Søk etter land">
<AkselifiedComboboxDisclosure />
</AkselifiedCombobox>
<AkselifiedComboboxPopover>
<AkselifiedComboboxNonSelectables>
{suggestions.length === 0 && (
<AkselifiedComboboxNonInteractiveFeedbackItem>
{`Fant ingen land med navn eller kode "${searchValue}"`}
</AkselifiedComboboxNonInteractiveFeedbackItem>
)}
</AkselifiedComboboxNonSelectables>
{suggestions.length > 0 &&
suggestions.map((value) => (
<AkselifiedComboboxItem key={value.code} value={value.name}>
{value.name}
</AkselifiedComboboxItem>
))}
</AkselifiedComboboxPopover>
</AkselifiedComboboxWrapper>
)
}

export default CountryCombobox
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
AkselifiedComboboxItem,
AkselifiedComboboxLoading,
AkselifiedComboboxNonInteractiveFeedbackItem,
AkselifiedComboboxNonSelectables,
AkselifiedComboboxPopover,
AkselifiedComboboxWrapper,
} from '../../combobox/AkselifiedCombobox'
Expand Down Expand Up @@ -79,7 +80,7 @@ function DiagnoseCombobox({
<AkselifiedComboboxDisclosure loading={data != null && isLoading} />
</AkselifiedCombobox>
<AkselifiedComboboxPopover>
<div className="navds-combobox__list_non-selectables" role="status">
<AkselifiedComboboxNonSelectables>
{data == null && isLoading && <AkselifiedComboboxLoading />}
{(searchValue.trim() === '' || (data == null && !isLoading)) && (
<AkselifiedComboboxNonInteractiveFeedbackItem>
Expand All @@ -96,7 +97,7 @@ function DiagnoseCombobox({
{`Feil ved henting av ${system}-kode. Prøv igjen senere.`}
</AkselifiedComboboxNonInteractiveFeedbackItem>
)}
</div>
</AkselifiedComboboxNonSelectables>
{searchValue &&
suggestions.length > 0 &&
suggestions.map((value) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@ import { BodyShort } from '@navikt/ds-react'
import { ReactElement } from 'react'

import type { UtenlandskAdresse } from '../../../../graphql/queries/graphql.generated'
import { useCountrySuggestions } from '../../CountryPicker/CountryCombobox/CountryCombobox'
import { SmallTextSkeleton } from '../../../skeleton/Skeletons'
import { countries } from '../../../../utils/countries'

type UtenlandskAdresseProps = {
utenlandskAdresse: UtenlandskAdresse
}

function UtenlandskAdresse({ utenlandskAdresse }: UtenlandskAdresseProps): ReactElement {
const [loadingCountries, countries] = useCountrySuggestions()
const country = countries.find((country) => country.code === utenlandskAdresse.landkode)?.name

return (
Expand All @@ -20,7 +18,6 @@ function UtenlandskAdresse({ utenlandskAdresse }: UtenlandskAdresseProps): React
{utenlandskAdresse.postkode && (
<BodyShort>{`${utenlandskAdresse.postkode} ${utenlandskAdresse.bySted ?? ''}`}</BodyShort>
)}
{loadingCountries && <SmallTextSkeleton />}
{country && <BodyShort>{country}</BodyShort>}
</>
)
Expand Down
10 changes: 9 additions & 1 deletion src/components/FormComponents/combobox/AkselifiedCombobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function AkselifiedComboboxWrapper({
)
}

export function AkselifiedComboboxDisclosure({ loading }: { loading: boolean }): ReactElement {
export function AkselifiedComboboxDisclosure({ loading }: { loading?: boolean }): ReactElement {
return (
<Combobox.ComboboxDisclosure
className={styles.disclosure}
Expand Down Expand Up @@ -110,4 +110,12 @@ export function AkselifiedComboboxLoading(): ReactElement {
)
}

export function AkselifiedComboboxNonSelectables({ children }: PropsWithChildren): ReactElement {
return (
<div className="navds-combobox__list_non-selectables" role="status">
{children}
</div>
)
}

export { useComboboxStore } from '@ariakit/react/combobox'
5 changes: 2 additions & 3 deletions src/mocks/handlers-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { HttpResponse, RequestHandler, http } from 'msw'
import { api } from '../utils/apiUtils'
import { searchSystem } from '../app/api/diagnose/[system]/_search-system'
import { DiagnoseSearchResult } from '../app/api/diagnose/[system]/_types'
import { countriesResponse } from '../app/api/country/_countries'
import { Country } from '../app/api/country/_types'
import { countries } from '../utils/countries'

/**
* These are only used in MSW during tests, normally we use the real API routes in next
Expand All @@ -21,6 +20,6 @@ export const handlers: RequestHandler[] = [
return HttpResponse.json<DiagnoseSearchResult>({ suggestions: searchSystem(system, value) })
}),
http.get(api('/api/country'), async () => {
return HttpResponse.json<Country[]>(countriesResponse)
return HttpResponse.json(countries)
}),
]
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import * as R from 'remeda'

import countries from './countries-norwegian.json'
import { Country } from './_types'
import countriesJson from './countries-norwegian.json'

export const countriesResponse: Country[] = R.pipe(
countries,
export type Country = {
code: string
name: string
}

export const countries: Country[] = R.pipe(
countriesJson,
R.map((country) => ({
code: country.alpha3.toUpperCase(),
name: country.name,
Expand Down

0 comments on commit 4d4bc12

Please sign in to comment.