Files
supabase/apps/studio/components/interfaces/SignIn/SessionTimeoutModal.tsx
T
Danny White 325a5a0da7 fix(studio): polish extension installation dialogs (#51463)
## Problem

The enable-extension dialog in #51440 needs clearer installation copy
and a consistent disclosure for custom-schema guidance. On narrow
screens, footer buttons touch and dialog content can scroll sideways.

## Solution

Polish the dialog on top of #51440:

- Explain fixed-schema requirements and hide custom-schema guidance when
the extension requires a specific schema.
- Share the disclosure interaction with the session-expired and
API-hardening dialogs through `ui-patterns/DialogDisclosure`, documented
with a live design-system example.
- Fix narrow-screen overflow and add vertical spacing to shared dialog
footers.
- Refine inline-code styling and update the PostGIS instructions and E2E
button locator.


| After |
| --- |
| <img width="852" height="710" alt="CleanShot 2026-10-09 at 12 17
47@2x"
src="https://github.com/user-attachments/assets/59f6f1e7-9884-4cdb-b678-62b7f88a8ec5"
/> |
| _Standard extension_ |
| <img width="836" height="1092" alt="52254"
src="https://github.com/user-attachments/assets/dbf53d3e-6583-4c3b-8f43-577e0723d9a7"
/> |
| _Standard extension expanded accordion_ |
| <img width="836" height="604" alt="CleanShot 2026-10-09 at 12 20
04@2x"
src="https://github.com/user-attachments/assets/c8e3961f-2683-440c-a2a2-48ccd53c4a17"
/> |
| _Extension with fixed schema_ |

## Review instructions

1. Open `/project/<ref>/database/extensions`. Turn on the toggle for a
disabled `postgres_fdw` extension. Expand **Install in a different
schema** and check its hover, keyboard focus and full-width click
target. Repeat with `pg_cron` or `pgmq`: the intro must say **must**,
and custom-schema guidance must be absent. Cancel without enabling.
2. At 320 px and 375 px, check the extension dialog's alignment,
inline-code rendering, wrapped SQL and stacked **Enable** / **Cancel**
spacing. Confirm the page does not scroll sideways.
3. On a platform project with the Data API enabled, open
`/project/<ref>/integrations/data_api/settings`. Under **Harden Data
API**, click **Harden Data API**. Expand both sections and check the
same trigger behaviour. Confirm completed sections still show their
check icons. Close without applying either action.
4. The session-expired dialog has no direct route. It is rendered by
`apps/studio/hooks/misc/withAuth.tsx` after authentication loading
exceeds 10 seconds. For repeatable local review, temporarily pass
`visible={true}` to its `SessionTimeoutModal` callsite, then open
`/projects`. Expand **Having trouble?** by clicking the text or chevron
and using Enter / Space. Check the help text and controls. Close the
dialog without signing out or clearing site data, then undo the
temporary prop change.
5. In the design system, open
[`/docs/components/dialog#optional-details`](https://design-system-git-dnywh-choreextension-dialog-polish-supabase.vercel.app/design-system/docs/components/dialog#optional-details).
Click **Export results**, then expand **What’s included** with the mouse
and keyboard. The demo imports the same `ui-patterns/DialogDisclosure`
used by Studio.
6. Open another dialog and an alert dialog at a narrow width. Check the
shared 8 px gap between stacked footer buttons. Review inline-code
examples on `/docs/components/typography` for the shared styling change.
2026-10-12 10:18:45 +11:00

112 lines
3.6 KiB
TypeScript

import * as Sentry from '@sentry/nextjs'
import { SupportCategories } from '@supabase/shared-types/out/constants'
import { safeLocalStorage, safeSessionStorage } from 'common'
import { useEffect } from 'react'
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
Button,
DialogSection,
DialogSectionSeparator,
} from 'ui'
import {
DialogDisclosure,
DialogDisclosureContent,
DialogDisclosureTrigger,
} from 'ui-patterns/DialogDisclosure'
import { SupportLink } from '../Support/SupportLink'
import { InlineLink, InlineLinkClassName } from '@/components/ui/InlineLink'
interface SessionTimeoutModalProps {
visible: boolean
onClose: () => void
redirectToSignIn: () => void
/** Optional context so the support form can pre-populate when opened from this dialog */
supportContext?: { projectRef?: string; orgSlug?: string }
}
export const SessionTimeoutModal = ({
visible,
onClose,
redirectToSignIn,
supportContext,
}: SessionTimeoutModalProps) => {
useEffect(() => {
if (visible) {
Sentry.captureException(new Error('Session error detected'))
}
}, [visible])
const handleClearStorage = () => {
safeLocalStorage.clear()
safeSessionStorage.clear()
window.location.reload()
}
return (
<AlertDialog
open={visible}
onOpenChange={(open) => {
if (!open) onClose()
}}
>
<AlertDialogContent size="small">
<AlertDialogHeader>
<AlertDialogTitle>Session expired</AlertDialogTitle>
<AlertDialogDescription>Please sign in again to continue.</AlertDialogDescription>
</AlertDialogHeader>
<DialogSectionSeparator />
<DialogDisclosure>
<DialogDisclosureTrigger className="py-4 px-5">Having trouble?</DialogDisclosureTrigger>
<DialogDisclosureContent>
<DialogSection className="px-5 pt-1 text-sm">
<div className="space-y-3 text-foreground-light">
<p>
Try a different browser or disable extensions that block network requests. If the
problem persists:
</p>
<Button size="tiny" onClick={handleClearStorage}>
Clear site data and reload
</Button>
<p>
Still stuck?{' '}
<SupportLink
className={InlineLinkClassName}
queryParams={{
subject: 'Session expired',
category: SupportCategories.LOGIN_ISSUES,
...(supportContext?.projectRef && {
projectRef: supportContext.projectRef,
}),
...(supportContext?.orgSlug && { orgSlug: supportContext.orgSlug }),
}}
onClick={onClose}
>
Contact support
</SupportLink>{' '}
and include a{' '}
<InlineLink href="https://github.com/orgs/supabase/discussions/36540">
HAR file
</InlineLink>{' '}
from your session to help us investigate.
</p>
</div>
</DialogSection>
</DialogDisclosureContent>
</DialogDisclosure>
<AlertDialogFooter>
<AlertDialogCancel>Close</AlertDialogCancel>
<AlertDialogAction onClick={redirectToSignIn}>Sign in again</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}