Files
supabase/apps/studio/components/interfaces/Support/ClientLibraryInfo.tsx
Monica Khoury fff5928056 fix: scroll to required fields on support form submit (#49147)
Fixes FE-4130. 

## What is the current behavior?

When submitting the support form with required fields missing, the form
does not scroll to or focus the required empty field. Users have to
manually find which field they missed.

## What is the new behavior?

On submit, the form automatically scrolls to and focuses the first
required field that's missing a value - including "What issue are you
having?" and "Which library are you having issues with?".

## Additional context

The scroll wasn't working due to a Chrome bug where scrollIntoView is
blocked when overflow-x: hidden and overflow-y: auto are on the same
element (the sidebar scroll container). The fix manually walks the DOM
to find the scrollable parent and calls scrollTo() directly. Dropdown
fields (Radix Selects) were also unfindable via the usual name
attribute, so data-support-field attributes are used as a stable DOM
hook for those.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Bug Fixes

* Improved support form validation by automatically scrolling to the
first invalid or missing required field.
* Added smooth scrolling and focus behavior to help users quickly
correct form errors.
* Ensured the client library field is brought into view when required
information is missing.
* Improved field targeting for category and client library validation
messages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Ali Waseem <waseema393@gmail.com>
2026-08-18 15:27:01 +03:00

138 lines
4.6 KiB
TypeScript

// End of third-party imports
import { CLIENT_LIBRARIES } from 'common/constants'
import { ExternalLink } from 'lucide-react'
import Link from 'next/link'
import type { UseFormReturn } from 'react-hook-form'
import {
Button,
cn,
FormControl,
FormField,
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import type { ExtendedSupportCategories } from './Support.constants'
import type { SupportFormValues } from './SupportForm.schema'
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
interface ClientLibraryInfoProps {
form: UseFormReturn<SupportFormValues>
category: ExtendedSupportCategories
library: string | undefined
}
export function ClientLibraryInfo({ form, category, library }: ClientLibraryInfoProps) {
const showClientLibraries = useIsFeatureEnabled('support:show_client_libraries')
if (!showClientLibraries) return null
if (category !== 'Problem') return null
return (
<div className="flex flex-col gap-y-1">
<FormField
name="library"
control={form.control}
render={({ field }) => (
<FormItemLayout layout="vertical" label="Which library are you having issues with">
<FormControl>
<Select {...field} defaultValue={field.value} onValueChange={field.onChange}>
<SelectTrigger
data-support-field="library"
className="w-full"
aria-label="Select a library"
>
<SelectValue placeholder="Select a library" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{CLIENT_LIBRARIES.map((option) => (
<SelectItem key={option.language} value={option.language}>
{option.language}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
</FormControl>
</FormItemLayout>
)}
/>
{library && library.length > 0 && <LibrarySuggestions library={library} />}
</div>
)
}
interface LibrarySuggestionsProps {
library: string
}
const LibrarySuggestions = ({ library }: LibrarySuggestionsProps) => {
const selectedLibrary = CLIENT_LIBRARIES.find((lib) => lib.language === library)
const selectedClientLibraries = selectedLibrary?.libraries.filter((library) =>
library.name.includes('supabase-')
)
return (
<div className="flex flex-col gap-y-4">
<div className="space-y-2">
<p className="text-sm text-foreground-light">
Found an issue or a bug? Try searching our GitHub issues or submit a new one.
</p>
</div>
<div className="flex items-center space-x-4 overflow-x-auto">
{selectedClientLibraries?.map((lib) => {
const libraryLanguage = library === 'Dart (Flutter)' ? lib.name.split('-')[1] : library
return (
<div
key={lib.name}
className="w-[230px] min-w-[230px] min-h-[128px] rounded-sm border border-control bg-surface-100 space-y-3 px-4 py-3"
>
<div className="space-y-1">
<p className="text-sm">{lib.name}</p>
<p className="text-sm text-foreground-light">
For issues regarding the {libraryLanguage} client library
</p>
</div>
<div>
<Button
asChild
variant="default"
icon={<ExternalLink size={14} strokeWidth={1.5} />}
>
<Link href={lib.url} target="_blank" rel="noreferrer">
View GitHub issues
</Link>
</Button>
</div>
</div>
)
})}
<div
className={cn(
'px-4 py-3 rounded-sm border border-control bg-surface-100',
'w-[230px] min-w-[230px] min-h-[128px] flex flex-col justify-between space-y-3'
)}
>
<div className="space-y-1">
<p className="text-sm">supabase</p>
<p className="text-sm text-foreground-light">For any issues about our API</p>
</div>
<div>
<Button asChild variant="default" icon={<ExternalLink size={14} strokeWidth={1.5} />}>
<Link href="https://github.com/supabase/supabase" target="_blank" rel="noreferrer">
View GitHub issues
</Link>
</Button>
</div>
</div>
</div>
</div>
)
}