diff --git a/apps/studio/components/interfaces/Support/CategoryAndSeverityInfo.tsx b/apps/studio/components/interfaces/Support/CategoryAndSeverityInfo.tsx index b1806a24497..843bbedc875 100644 --- a/apps/studio/components/interfaces/Support/CategoryAndSeverityInfo.tsx +++ b/apps/studio/components/interfaces/Support/CategoryAndSeverityInfo.tsx @@ -91,7 +91,11 @@ function CategorySelector({ form }: CategorySelectorProps) { - + diff --git a/apps/studio/components/interfaces/Support/SupportForm.utils.tsx b/apps/studio/components/interfaces/Support/SupportForm.utils.tsx index b072b3f5fc1..6a7ecbb705d 100644 --- a/apps/studio/components/interfaces/Support/SupportForm.utils.tsx +++ b/apps/studio/components/interfaces/Support/SupportForm.utils.tsx @@ -224,3 +224,34 @@ export async function selectInitialOrgAndProject({ orgSlug: orgs[0]?.slug ?? null, } } + +// Chrome blocks scrollIntoView (triggered by .focus()) when overflow-x: hidden and +// overflow-y: auto are on the same element. Manually scroll the sidebar instead. +export function scrollToRequiredField(formId: string, fieldName: string) { + const form = document.getElementById(formId) + if (!form) return + + // Selects use data-support-field since they have no name attr; inputs/textareas use name. + const el = + form.querySelector(`[data-support-field="${fieldName}"]`) ?? + form.querySelector(`[name="${fieldName}"]`) + if (!(el instanceof HTMLElement)) return + + const scrollContainer = findScrollableParent(el) + if (scrollContainer) { + const offset = el.getBoundingClientRect().top - scrollContainer.getBoundingClientRect().top - 80 + scrollContainer.scrollTo({ top: scrollContainer.scrollTop + offset, behavior: 'smooth' }) + } + + el.focus({ preventScroll: true }) +} + +function findScrollableParent(el: HTMLElement): HTMLElement | null { + let parent = el.parentElement + while (parent && parent !== document.body) { + const { overflowY } = window.getComputedStyle(parent) + if (overflowY === 'auto' || overflowY === 'scroll') return parent + parent = parent.parentElement + } + return null +} diff --git a/apps/studio/components/interfaces/Support/SupportFormV3.tsx b/apps/studio/components/interfaces/Support/SupportFormV3.tsx index 764dc5959eb..d3d86d97e68 100644 --- a/apps/studio/components/interfaces/Support/SupportFormV3.tsx +++ b/apps/studio/components/interfaces/Support/SupportFormV3.tsx @@ -36,6 +36,7 @@ import { getOrgSubscriptionPlan, NO_ORG_MARKER, NO_PROJECT_MARKER, + scrollToRequiredField, } from './SupportForm.utils' import { SupportFormDirectEmailContent } from './SupportFormDirectEmailInfo' import { getProjectAuthConfig } from '@/data/auth/auth-config-query' @@ -148,6 +149,8 @@ export const SupportFormV3 = ({ type: 'manual', message: "Please select the library that you're facing issues with", }) + // setError triggers a re-render; wait for it before querying aria-invalid + requestAnimationFrame(() => scrollToRequiredField('support-form', 'library')) return } @@ -223,7 +226,9 @@ export const SupportFormV3 = ({ submitSupportTicket(payload) } - const handleFormSubmit = form.handleSubmit(onSubmit) + const handleFormSubmit = form.handleSubmit(onSubmit, (errors) => { + scrollToRequiredField('support-form', Object.keys(errors)[0]) + }) const handleSubmitButtonClick: MouseEventHandler = (event) => { handleFormSubmit(event)