From 5fdff9707fb796d20c7f381fa9bc9dae8332586f Mon Sep 17 00:00:00 2001 From: Danny White <3104761+dnywh@users.noreply.github.com> Date: Fri, 22 May 2026 17:51:23 +1000 Subject: [PATCH] chore(studio): improve download snippet dialog (#46242) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What kind of change does this PR introduce? Chore: component migration and copy update. ## What is the current behavior? `DownloadSnippetModal` and `RenameQueryModal` use an awkward tabs-based layout. The download action is labelled "Download" throughout, and the NPX toggle was incorrectly labelled "NPM". ## What is the new behavior? - Both modals are migrated to the `Dialog` component. - The download dialog replaces the tab layout with a select (Migration / Seed file / SQL file) and a CLI/NPX toggle. - Action language changed from "Download" to "Export" (context menu item, dialog title, select label) — more accurate since the user runs a CLI command rather than triggering a browser download. - NPM toggle label corrected to NPX; internal `npm` property key renamed to `npx` for consistency. | Before | After | | --- | --- | | Add Auth Hook (General) SQL Editor
Pickles Pantry Supabase-C6F22F8B-19FF-486E-8C08-915895495875 | Add Auth Hook (General) SQL
Editor Pickles Pantry Supabase-5B63AD0C-7CD0-4BB2-BEC8-5DBAE94963CE | | Add Auth Hook (General) SQL Editor
Pickles Pantry Supabase-1F28FADA-46D1-4C0A-BE96-6CFF2317FDCF | Add Auth Hook (General) SQL
Editor Pickles Pantry Supabase-652E6E8D-1CD2-4465-BC82-46EDB974CD8C | ## Summary by CodeRabbit * **Refactor** * Updated the SQL query export modal UI from a tabs-based interface to a dropdown-based selection for choosing export formats. * Revised menu and dialog labels to "Export query" for improved clarity. * Enhanced code block presentation in the export modal. [![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46242?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) --------- Co-authored-by: Gildas Garcia <1122076+djhi@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 --- .../SQLEditor/DownloadSnippetModal.tsx | 179 +++++++++++------- .../SQLEditorNavV2/SQLEditorTreeViewItem.tsx | 2 +- e2e/studio/features/sql-editor.spec.ts | 4 +- 3 files changed, 110 insertions(+), 75 deletions(-) diff --git a/apps/studio/components/interfaces/SQLEditor/DownloadSnippetModal.tsx b/apps/studio/components/interfaces/SQLEditor/DownloadSnippetModal.tsx index a112ed61ff2..30efd77a085 100644 --- a/apps/studio/components/interfaces/SQLEditor/DownloadSnippetModal.tsx +++ b/apps/studio/components/interfaces/SQLEditor/DownloadSnippetModal.tsx @@ -4,18 +4,22 @@ import Link from 'next/link' import { useState } from 'react' import { Button, + cn, Dialog, DialogContent, DialogHeader, DialogSection, DialogSectionSeparator, DialogTitle, - Tabs, + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, type DialogProps, } from 'ui' import { CodeBlock } from 'ui-patterns/CodeBlock' -import { Markdown } from '../Markdown' import { generateFileCliCommand, generateMigrationCliCommand, @@ -25,108 +29,139 @@ import { TwoOptionToggle } from '@/components/ui/TwoOptionToggle' import { DOCS_URL } from '@/lib/constants' import { useSqlEditorV2StateSnapshot } from '@/state/sql-editor-v2' +const CLI_DOCS_URL = `${DOCS_URL}/guides/cli/local-development` + export interface DownloadSnippetModalProps extends DialogProps { id: string } +type DownloadFormat = 'migration' | 'seed' | 'sql' + export const DownloadSnippetModal = ({ id, ...props }: DownloadSnippetModalProps) => { const snapV2 = useSqlEditorV2StateSnapshot() const snippet = snapV2.snippets[id]?.snippet const migrationName = snakeCase(snippet?.name) - const [selectedView, setSelectedView] = useState<'CLI' | 'NPM'>('CLI') + const [downloadFormat, setDownloadFormat] = useState('migration') + const [selectedView, setSelectedView] = useState<'CLI' | 'NPX'>('CLI') const SNIPPETS = [ { - id: 'migration', + id: 'migration' as const, label: 'Migration', - title: 'Download as migration', - description: `Download the snippet in a new migration named \`${migrationName}\``, + caption: ( + <> + Run this command from your project directory to download the snippet in a new migration + named {migrationName}. + + ), + docLink: { + label: 'About migrations', + href: `${DOCS_URL}/guides/deployment/database-migrations`, + }, cli: generateMigrationCliCommand(id, migrationName), - npm: generateMigrationCliCommand(id, migrationName, true), + npx: generateMigrationCliCommand(id, migrationName, true), }, { - id: 'seed', + id: 'seed' as const, label: 'Seed file', - title: 'Download as seed file', - description: - 'If your query consists of sample data, append the snippet to the end of `supabase/seed.sql`', + caption: ( + <> + Run this command from your project directory to download the snippet. If your query + consists of sample data, append it to the end of{' '} + supabase/seed.sql. + + ), + docLink: { + label: 'About seeding', + href: `${DOCS_URL}/guides/local-development/seeding-your-database`, + }, cli: generateSeedCliCommand(id), - npm: generateSeedCliCommand(id, true), + npx: generateSeedCliCommand(id, true), }, { - id: 'sql', + id: 'sql' as const, label: 'SQL file', - title: 'Download as SQL file', - description: `Download the snippet directly into a new SQL file named \`${migrationName}.sql\``, + caption: ( + <> + Run this command from your project directory to download the snippet into a new SQL file + named {migrationName}.sql. + + ), cli: generateFileCliCommand(id, migrationName), - npm: generateFileCliCommand(id, migrationName, true), + npx: generateFileCliCommand(id, migrationName, true), }, ] + const selectedSnippet = SNIPPETS.find((s) => s.id === downloadFormat) ?? SNIPPETS[0] + const commandValue = selectedView === 'CLI' ? selectedSnippet.cli : selectedSnippet.npx + return ( - + - Download snippet as local migration file via the Supabase CLI + Export query - - {SNIPPETS.map((snippet) => { - return ( - - -
-
-

{snippet.title}

- -
- - selectedView === 'CLI' ? setSelectedView('NPM') : setSelectedView('CLI') - } - /> -
-
-                    
-                      {selectedView === 'CLI' ? snippet.cli : snippet.npm}
-                    
-                  
-
-
- ) - })} -
- -

Run this command from your project directory

-
- + +
+

Export as

+ +
+
+
+

Run with

+ + selectedView === 'CLI' ? setSelectedView('NPX') : setSelectedView('CLI') + } + /> +
+ +

+ {selectedSnippet.caption} +

+
+
+ +
+ {selectedSnippet.docLink && ( + + )} - diff --git a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorTreeViewItem.tsx b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorTreeViewItem.tsx index 5536305da67..7635f8045d4 100644 --- a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorTreeViewItem.tsx +++ b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorTreeViewItem.tsx @@ -411,7 +411,7 @@ export const SQLEditorTreeViewItem = ({ onFocusCapture={(e) => e.stopPropagation()} > - Download as migration file + Export query )} {onSelectDelete !== undefined && isOwner && ( diff --git a/e2e/studio/features/sql-editor.spec.ts b/e2e/studio/features/sql-editor.spec.ts index 21a302d20a5..44f2a6500ca 100644 --- a/e2e/studio/features/sql-editor.spec.ts +++ b/e2e/studio/features/sql-editor.spec.ts @@ -786,11 +786,11 @@ hello world`) await expect(page.getByText('result found')).toBeVisible() await searchBar.fill('') // clear search bar - // download as migration file + // export query as migration file await privateSnippetSection .getByTitle(sqlSnippetName, { exact: true }) .click({ button: 'right' }) - await page.getByRole('menuitem', { name: 'Download as migration file' }).click() + await page.getByRole('menuitem', { name: 'Export query' }).click() await expect(page.getByText('supabase migration new')).toBeVisible() await page.getByRole('button', { name: 'Close' }).click()