feat: connect sheet deep linking (#44021)

## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Supabase Dashboard - Connect

## What is the current behavior?

On the new `<ConnectSheet />` component their is no deep linking like
the previous `<Connect />` component

## What is the new behavior?

Deep linking added onto framework and other options, Example local
links:


http://localhost:8082/project/default?showConnect=true&connectTab=framework&framework=nextjs&using=pages

http://localhost:8082/project/default?showConnect=true&connectTab=mcp&mcpClient=goose



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

* **New Features**
* Connect Sheet supports URL query parameters for pre-configuring
connection settings (framework, using, method, type, mcpClient).
  * Legacy tab identifiers are accepted for compatibility.

* **Improvements**
* Opening, switching, and closing the Connect Sheet now more reliably
syncs and clears related parameters to avoid stale state.

* **Tests**
* Added end-to-end tests covering deep-linking, legacy aliases, and
parameter clearing on close/mode change.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
hallidayo
2026-04-13 10:00:19 +01:00
committed by GitHub
parent e541719345
commit 9791f65a18
2 changed files with 207 additions and 6 deletions
+130
View File
@@ -54,3 +54,133 @@ test.describe('Connect', async () => {
await expect(page).toHaveURL(/showConnect=true/)
})
})
test.describe('Connect Sheet deep linking', async () => {
test('pre-selects framework and variant from URL params', async ({ page, ref }) => {
await page.goto(
toUrl(
`/project/${ref}?showConnect=true&connectTab=framework&framework=nextjs&using=pages`
)
)
await expect(
page.getByRole('heading', { name: 'Connect to your project' }),
'ConnectSheet should open from deep link'
).toBeVisible({ timeout: 30000 })
await expect(
page.getByRole('combobox').filter({ hasText: 'Next.js' }),
'Framework select should show Next.js'
).toBeVisible()
await expect(
page.getByRole('combobox').filter({ hasText: 'Pages Router' }),
'Variant select should show Pages Router'
).toBeVisible()
})
test('supports legacy frameworks tab alias', async ({ page, ref }) => {
await page.goto(
toUrl(`/project/${ref}?showConnect=true&connectTab=frameworks&framework=nextjs`)
)
await expect(
page.getByRole('heading', { name: 'Connect to your project' }),
'ConnectSheet should open with legacy tab alias'
).toBeVisible({ timeout: 30000 })
await expect(
page.getByRole('combobox').filter({ hasText: 'Next.js' }),
'Framework select should show Next.js via legacy connectTab alias'
).toBeVisible()
})
test('pre-selects ORM from URL params', async ({ page, ref }) => {
// Use drizzle (non-default) to verify the param takes effect
await page.goto(toUrl(`/project/${ref}?showConnect=true&connectTab=orm&framework=drizzle`))
await expect(
page.getByRole('heading', { name: 'Connect to your project' }),
'ConnectSheet should open from deep link'
).toBeVisible({ timeout: 30000 })
await expect(
page.locator('[data-state="checked"]').filter({ hasText: 'Drizzle' }),
'Drizzle radio should be selected'
).toBeVisible()
})
test('pre-selects MCP client from URL params', async ({ page, ref }) => {
await page.goto(toUrl(`/project/${ref}?showConnect=true&connectTab=mcp&mcpClient=goose`))
await expect(
page.getByRole('heading', { name: 'Connect to your project' }),
'ConnectSheet should open from deep link'
).toBeVisible({ timeout: 30000 })
await expect(
page.getByRole('combobox').filter({ hasText: 'Goose' }),
'MCP client select should show Goose'
).toBeVisible()
})
test('pre-selects direct connection method from URL params', async ({ page, ref }) => {
await page.goto(
toUrl(`/project/${ref}?showConnect=true&connectTab=direct&method=transaction`)
)
await expect(
page.getByRole('heading', { name: 'Connect to your project' }),
'ConnectSheet should open from deep link'
).toBeVisible({ timeout: 30000 })
await expect(
page.locator('[data-state="checked"]').filter({ hasText: 'Transaction pooler' }),
'Transaction pooler radio should be selected'
).toBeVisible()
})
test('closing the sheet clears all deep-link params from URL', async ({ page, ref }) => {
await page.goto(
toUrl(
`/project/${ref}?showConnect=true&connectTab=framework&framework=nextjs&using=pages`
)
)
await expect(
page.getByRole('heading', { name: 'Connect to your project' }),
'ConnectSheet should open'
).toBeVisible({ timeout: 30000 })
await page.keyboard.press('Escape')
await expect(
page.getByRole('heading', { name: 'Connect to your project' }),
'ConnectSheet should close'
).not.toBeVisible({ timeout: 10000 })
await expect(page, 'connectTab param should be removed').not.toHaveURL(/connectTab/)
await expect(page, 'framework param should be removed').not.toHaveURL(/[?&]framework=/)
await expect(page, 'using param should be removed').not.toHaveURL(/using=/)
})
test('changing mode clears previous mode params from URL', async ({ page, ref }) => {
await page.goto(
toUrl(`/project/${ref}?showConnect=true&connectTab=framework&framework=nextjs`)
)
await expect(
page.getByRole('heading', { name: 'Connect to your project' }),
'ConnectSheet should open'
).toBeVisible({ timeout: 30000 })
await expect(page, 'framework param should be in URL initially').toHaveURL(/framework=nextjs/)
await page.getByRole('button', { name: /ORM/ }).click()
await expect(page, 'framework param should be cleared after mode change').not.toHaveURL(
/framework=nextjs/
)
await expect(page, 'connectTab should update to orm').toHaveURL(/connectTab=orm/)
})
})