Joshenlim/fe 4063 set up incremental default opt in for database connections (#49132)

## Context

As per PR title - sets up incremental default opt in for the Database
Connections feature preview

Database Connections preview banner should still only show up if it's
never been dismissed before, but the CTA's changed to "Explore" rather
than "Enable" if the user's default opted in

Related discussion here:
https://github.com/orgs/supabase/discussions/48639

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

* **New Features**
  * Database Connections preview is now enabled by default.
  * Added clearer handling for preview state and initialization.
* Banner actions open Database Connections when enabled, or the feature
preview when disabled.
* **Bug Fixes**
* Improved banner and menu visibility while preview settings initialize.
* Preserved banner dismissal behavior after a previous preference
change.
* Improved navigation consistency across Database Connections entry
points.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Joshen Lim authored and GitHub committed 2026-08-17 16:11:07 +08:00
1 parent 14fe0c0cc8
commit dfb0603a36
9 files changed
+60 -28

No files matched your search

@@ -13,6 +13,7 @@ import {
} from 'react'
import { useFeaturePreviews } from './useFeaturePreviews'
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
import { IS_PLATFORM } from '@/lib/constants'
import { EMPTY_OBJ } from '@/lib/void'
@@ -152,9 +153,18 @@ export const useIsMarketplaceEnabled = () => {
}
export const useIsDatabaseConnectionsEnabled = () => {
const { flags } = useFeaturePreviewContext()
const isDatabaseConnectionsEnabled = useFlag('topForPostgres')
return isDatabaseConnectionsEnabled && flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_DATABASE_CONNECTIONS]
const { flags, isInitialized } = useFeaturePreviewContext()
const [localStorageFlag] = useLocalStorageQuery<boolean | null>(
LOCAL_STORAGE_KEYS.UI_PREVIEW_DATABASE_CONNECTIONS,
null
)
const previouslyToggled = localStorageFlag !== null
return {
enabled: flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_DATABASE_CONNECTIONS],
isInitialized,
previouslyToggled,
}
}
export const useFeaturePreviewModal = () => {
@@ -133,8 +133,8 @@ export const useFeaturePreviews = (): FeaturePreview[] => {
discussionsUrl: 'https://github.com/orgs/supabase/discussions/48639',
isNew: true,
isPlatformOnly: false,
isDefaultOptIn: false,
enabled: isDatabaseConnectionsEnabled,
isDefaultOptIn: isDatabaseConnectionsEnabled,
enabled: true,
getRoute: (ref?: string) => `/project/${ref}/observability/connections`,
bannerId: 'database-connections-banner',
},
@@ -1,4 +1,4 @@
import { LOCAL_STORAGE_KEYS, useFeatureFlags, useParams } from 'common'
import { LOCAL_STORAGE_KEYS, useParams } from 'common'
import { usePathname } from 'next/navigation'
import { PropsWithChildren, useEffect, useRef } from 'react'
@@ -24,7 +24,6 @@ const ObservabilityLayoutContent = ({
}: PropsWithChildren<ObservabilityLayoutProps>) => {
const { ref } = useParams()
const pathname = usePathname()
const { hasLoaded } = useFeatureFlags()
const { addBanner, dismissBanner } = useBannerStack()
const { isIndexAdvisorAvailable, isIndexAdvisorEnabled } = useIndexAdvisorStatus()
@@ -33,7 +32,7 @@ const ObservabilityLayoutContent = ({
false
)
const isDatabaseConnectionsEnabled = useIsDatabaseConnectionsEnabled()
const { isInitialized, previouslyToggled } = useIsDatabaseConnectionsEnabled()
const [isDatabaseConnectionsBannerDismissed, , { isSuccess: isLocalStorageReady }] =
useLocalStorageQuery(LOCAL_STORAGE_KEYS.DATABASE_CONNECTIONS_BANNER_DISMISSED(ref ?? ''), false)
@@ -42,10 +41,10 @@ const ObservabilityLayoutContent = ({
useEffect(() => {
if (
!hasLoaded ||
!isInitialized ||
!isLocalStorageReady ||
isDatabaseConnectionsBannerDismissed ||
isDatabaseConnectionsEnabled
previouslyToggled
)
return
@@ -56,11 +55,11 @@ const ObservabilityLayoutContent = ({
content: <BannerDatabaseConnections />,
})
}, [
hasLoaded,
addBanner,
dismissBanner,
isInitialized,
isDatabaseConnectionsBannerDismissed,
isDatabaseConnectionsEnabled,
previouslyToggled,
isLocalStorageReady,
])
@@ -1,4 +1,3 @@
import { renderHook } from '@testing-library/react'
import { useFlag, useParams } from 'common'
import { beforeEach, describe, expect, it, vi } from 'vitest'
@@ -9,6 +8,7 @@ import {
import { useSupamonitorStatus } from '@/components/interfaces/QueryPerformance/hooks/useSupamonitorStatus'
import { useContentQuery } from '@/data/content/content-query'
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
import { customRenderHook as renderHook } from '@/tests/lib/custom-render'
import { routerMock } from '@/tests/lib/route-mock'
const { REF, mockIsPlatform } = vi.hoisted(() => ({
@@ -49,7 +49,7 @@ export const useGenerateObservabilityMenu = () => {
const { isSupamonitorEnabled } = useSupamonitorStatus()
const showOverview = useFlag('observabilityOverview')
const isDatabaseConnectionsEnabled = useIsDatabaseConnectionsEnabled()
const { enabled: isDatabaseConnectionsEnabled } = useIsDatabaseConnectionsEnabled()
const storageSupported = useIsFeatureEnabled('project_storage:all')
const baseUrl = `/project/${ref}/observability`
@@ -41,7 +41,7 @@ export const SQLEditorMenu = () => {
const { data: project } = useSelectedProjectQuery()
const snapV2 = useSqlEditorV2StateSnapshot()
const isDatabaseConnectionsEnabled = useIsDatabaseConnectionsEnabled()
const { enabled: isDatabaseConnectionsEnabled } = useIsDatabaseConnectionsEnabled()
const sqlEditorLogsSource = useFlag('sqlEditorLogsSource')
const otelLegacyLogs = useFlag('otelLegacyLogs')
const canCreateLogsSnippet = sqlEditorLogsSource && otelLegacyLogs
@@ -1,10 +1,14 @@
import { LOCAL_STORAGE_KEYS } from 'common'
import { useParams } from 'common/hooks'
import Link from 'next/link'
import { Badge, Button, WarningIcon } from 'ui'
import { BannerCard } from '../BannerCard'
import { useBannerStack } from '../BannerStackProvider'
import { useFeaturePreviewModal } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import {
useFeaturePreviewModal,
useIsDatabaseConnectionsEnabled,
} from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
import { useTrack } from '@/lib/telemetry/track'
@@ -19,6 +23,8 @@ export const BannerDatabaseConnections = () => {
false
)
const { enabled: isEnabled } = useIsDatabaseConnectionsEnabled()
return (
<BannerCard
onDismiss={() => {
@@ -72,16 +78,33 @@ export const BannerDatabaseConnections = () => {
</p>
</div>
<div className="flex gap-2">
<Button
variant="default"
size="tiny"
onClick={() => {
track('database_connections_banner_cta_button_clicked', { isEnabled: false })
selectFeaturePreview(LOCAL_STORAGE_KEYS.UI_PREVIEW_DATABASE_CONNECTIONS)
}}
>
Enable Database Connections
</Button>
{isEnabled ? (
<Button
asChild
variant="default"
size="tiny"
onClick={() => {
setIsDismissed(true)
dismissBanner('database-connections-banner')
track('database_connections_banner_cta_button_clicked', { isEnabled })
}}
>
<Link href={`/project/${ref}/observability/connections`}>
Explore Database Connections
</Link>
</Button>
) : (
<Button
variant="default"
size="tiny"
onClick={() => {
selectFeaturePreview(LOCAL_STORAGE_KEYS.UI_PREVIEW_DATABASE_CONNECTIONS)
track('database_connections_banner_cta_button_clicked', { isEnabled: false })
}}
>
Enable Database Connections
</Button>
)}
</div>
</div>
</BannerCard>
@@ -33,7 +33,7 @@ export const DatabaseConnections: NextPageWithLayout = () => {
const { data: project } = useSelectedProjectQuery()
const { openSidebar } = useSidebarManagerSnapshot()
const aiSnap = useAiAssistantStateSnapshot()
const isDatabaseConnectionsEnabled = useIsDatabaseConnectionsEnabled()
const { enabled: isDatabaseConnectionsEnabled } = useIsDatabaseConnectionsEnabled()
const { selectFeaturePreview } = useFeaturePreviewModal()
const [live, setLive] = useState(true)
+1 -1
View File
@@ -66,7 +66,7 @@ export const customRender = (component: React.ReactElement, renderOptions?: Cust
})
}
export const customRenderHook = (hook: () => any, renderOptions?: CustomRenderOpts) => {
export const customRenderHook = <T,>(hook: () => T, renderOptions?: CustomRenderOpts) => {
return renderHook(hook, {
wrapper: ({ children }) =>
CustomWrapper({