diff --git a/app/components/form/fields/ListboxField.tsx b/app/components/form/fields/ListboxField.tsx index 6ffefbea60..965a92081e 100644 --- a/app/components/form/fields/ListboxField.tsx +++ b/app/components/form/fields/ListboxField.tsx @@ -79,6 +79,7 @@ export function ListboxField< name={name} hasError={fieldState.error !== undefined} isLoading={isLoading} + buttonRef={field.ref} /> diff --git a/app/pages/project/floating-ips/FloatingIpsPage.tsx b/app/pages/project/floating-ips/FloatingIpsPage.tsx index ea187488f8..4377bac75a 100644 --- a/app/pages/project/floating-ips/FloatingIpsPage.tsx +++ b/app/pages/project/floating-ips/FloatingIpsPage.tsx @@ -22,6 +22,7 @@ import { import { IpGlobal16Icon, IpGlobal24Icon } from '@oxide/design-system/icons/react' import { DocsPopover } from '~/components/DocsPopover' +import { ListboxField } from '~/components/form/fields/ListboxField' import { HL } from '~/components/HL' import { getProjectSelector, useProjectSelector } from '~/hooks' import { confirmAction } from '~/stores/confirm-action' @@ -34,7 +35,6 @@ import { Columns } from '~/table/columns/common' import { PAGE_SIZE, useQueryTable } from '~/table/QueryTable' import { CreateLink } from '~/ui/lib/CreateButton' import { EmptyMessage } from '~/ui/lib/EmptyMessage' -import { Listbox } from '~/ui/lib/Listbox' import { Message } from '~/ui/lib/Message' import { Modal } from '~/ui/lib/Modal' import { PageHeader, PageTitle } from '~/ui/lib/PageHeader' @@ -268,6 +268,7 @@ const AttachFloatingIpModal = ({ }, }) const form = useForm({ defaultValues: { instanceId: '' } }) + const instanceId = form.watch('instanceId') return ( @@ -280,30 +281,27 @@ const AttachFloatingIpModal = ({ The selected instance will be reachable at {address} } - > + />
- ({ value: i.id, label: i.name }))} label="Instance" - onChange={(e) => { - form.setValue('instanceId', e) - }} required placeholder="Select an instance" - selected={form.watch('instanceId')} /> floatingIpAttach.mutate({ path: { floatingIp }, query: { project }, - body: { kind: 'instance', parent: form.getValues('instanceId') }, + body: { kind: 'instance', parent: instanceId }, }) } onDismiss={onDismiss} diff --git a/app/ui/lib/Listbox.tsx b/app/ui/lib/Listbox.tsx index 458717fd22..32289a81b4 100644 --- a/app/ui/lib/Listbox.tsx +++ b/app/ui/lib/Listbox.tsx @@ -13,7 +13,7 @@ import { ListboxOptions, } from '@headlessui/react' import cn from 'classnames' -import type { ReactNode } from 'react' +import { type ReactNode, type Ref } from 'react' import { SelectArrows6Icon } from '@oxide/design-system/icons/react' @@ -42,6 +42,8 @@ export interface ListboxProps { description?: React.ReactNode required?: boolean isLoading?: boolean + /** Necessary if you want RHF to be able to focus it on error */ + buttonRef?: Ref } export const Listbox = ({ @@ -59,6 +61,7 @@ export const Listbox = ({ required, disabled, isLoading = false, + buttonRef, ...props }: ListboxProps) => { const selectedItem = selected && items.find((i) => i.value === selected) @@ -100,6 +103,7 @@ export const Listbox = ({ : 'bg-default', isDisabled && hasError && '!border-error-secondary' )} + ref={buttonRef} {...props} >