diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationTypeSelection.test.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationTypeSelection.test.tsx index 8b7d4dcb9d7..a84bf5b1ae4 100644 --- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationTypeSelection.test.tsx +++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationTypeSelection.test.tsx @@ -76,10 +76,10 @@ describe('DestinationTypeSelection', () => { expect(await screen.findByText('Select a destination type')).toBeInTheDocument() }) - test('renders the Pipelines group with BigQuery when the flag is enabled', async () => { + test('groups destinations by release stage', async () => { mockBigQueryEnabled.mockReturnValue(true) - mockIcebergEnabled.mockReturnValue(false) - mockDucklakeEnabled.mockReturnValue(false) + mockIcebergEnabled.mockReturnValue(true) + mockDucklakeEnabled.mockReturnValue(true) mockSnowflakeEnabled.mockReturnValue(false) mockClickHouseEnabled.mockReturnValue(false) addBackgroundMocks() @@ -88,8 +88,13 @@ describe('DestinationTypeSelection', () => { fireEvent.click(await screen.findByRole('combobox')) - expect(await screen.findByText('Pipelines')).toBeInTheDocument() + expect(await screen.findByText('Public Alpha')).toBeInTheDocument() + expect(screen.getByText('Early Access')).toBeInTheDocument() + expect(screen.getByText('Deprecated')).toBeInTheDocument() expect(screen.getByText('BigQuery')).toBeInTheDocument() + expect(screen.getByText('DuckLake')).toBeInTheDocument() + expect(screen.getByText('Analytics Bucket')).toBeInTheDocument() + expect(screen.queryByText('Pipelines')).not.toBeInTheDocument() }) test('hides destinations behind disabled feature flags', async () => { diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationTypeSelection.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationTypeSelection.tsx index 7e99d4b6323..9c7c902b7b7 100644 --- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationTypeSelection.tsx +++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationTypeSelection.tsx @@ -1,11 +1,11 @@ import { parseAsInteger, parseAsStringEnum, useQueryState } from 'nuqs' import { - Badge, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, + SelectSeparator, SelectTrigger, } from 'ui' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' @@ -30,13 +30,9 @@ interface DestinationTypeOption { enabled: boolean } -const STAGE_BADGE_VARIANT: Record< - NonNullable, - 'warning' | 'destructive' | 'default' -> = { - 'Early Access': 'warning', - Deprecated: 'destructive', - 'Public Alpha': 'default', +interface DestinationTypeGroup { + label: NonNullable + options: DestinationTypeOption[] } export const DestinationTypeSelection = () => { @@ -72,46 +68,64 @@ export const DestinationTypeSelection = () => { const isOptionVisible = (value: DestinationType, hasAccess: boolean) => editMode ? destinationType === value : hasAccess - const options: DestinationTypeOption[] = ( - [ - { - value: 'Analytics Bucket', - label: 'Analytics Bucket', - description: 'Write Apache Iceberg tables to Supabase Storage for analytics workflows', - stage: 'Deprecated', - enabled: isOptionVisible('Analytics Bucket', etlEnableIceberg), - }, - { - value: 'BigQuery', - label: 'BigQuery', - description: "Replicate changes to Google Cloud's data warehouse for analytics and BI", - stage: 'Public Alpha', - enabled: isOptionVisible('BigQuery', etlEnableBigQuery), - }, - { - value: 'DuckLake', - label: 'DuckLake', - description: 'Replicate changes to a DuckLake catalog backed by S3-compatible storage', - stage: 'Early Access', - enabled: isOptionVisible('DuckLake', etlEnableDucklake), - }, - { - value: 'Snowflake', - label: 'Snowflake', - description: - 'Replicate changes to Snowflake for warehouse analytics and downstream data workflows', - stage: 'Early Access', - enabled: isOptionVisible('Snowflake', etlEnableSnowflake), - }, - { - value: 'ClickHouse', - label: 'ClickHouse', - description: 'Stream changes to a ClickHouse cluster for fast columnar analytics', - stage: 'Early Access', - enabled: isOptionVisible('ClickHouse', etlEnableClickHouse), - }, - ] satisfies DestinationTypeOption[] - ).filter((option) => option.enabled) + const groups: DestinationTypeGroup[] = [ + { + label: 'Public Alpha', + options: [ + { + value: 'BigQuery', + label: 'BigQuery', + description: "Replicate changes to Google Cloud's data warehouse for analytics and BI", + stage: 'Public Alpha', + enabled: isOptionVisible('BigQuery', etlEnableBigQuery), + }, + ], + }, + { + label: 'Early Access', + options: [ + { + value: 'DuckLake', + label: 'DuckLake', + description: 'Replicate changes to a DuckLake catalog backed by S3-compatible storage', + stage: 'Early Access', + enabled: isOptionVisible('DuckLake', etlEnableDucklake), + }, + { + value: 'Snowflake', + label: 'Snowflake', + description: + 'Replicate changes to Snowflake for warehouse analytics and downstream data workflows', + stage: 'Early Access', + enabled: isOptionVisible('Snowflake', etlEnableSnowflake), + }, + { + value: 'ClickHouse', + label: 'ClickHouse', + description: 'Stream changes to a ClickHouse cluster for fast columnar analytics', + stage: 'Early Access', + enabled: isOptionVisible('ClickHouse', etlEnableClickHouse), + }, + ], + }, + { + label: 'Deprecated', + options: [ + { + value: 'Analytics Bucket', + label: 'Analytics Bucket', + description: 'Write Apache Iceberg tables to Supabase Storage for analytics workflows', + stage: 'Deprecated', + enabled: isOptionVisible('Analytics Bucket', etlEnableIceberg), + }, + ], + }, + ] + + const visibleGroups = groups + .map((group) => ({ ...group, options: group.options.filter((option) => option.enabled) })) + .filter((group) => group.options.length > 0) + const options = visibleGroups.flatMap((group) => group.options) const selectedOption = options.find((option) => option.value === destinationType) @@ -154,24 +168,18 @@ export const DestinationTypeSelection = () => { size={20} className="shrink-0 text-foreground-light" /> -
- {selectedOption.label} - {selectedOption.stage && ( - - {selectedOption.stage} - - )} -
+ {selectedOption.label} ) : ( Select a destination type )} - {options.length > 0 && ( - - Pipelines - {options.map((option) => ( + {visibleGroups.map((group, index) => ( + + {index > 0 && } + {group.label} + {group.options.map((option) => (
{ className="shrink-0 text-foreground-light" />
-
- {option.label} - {option.stage && ( - - {option.stage} - - )} -
+ {option.label} {option.description} @@ -196,7 +197,7 @@ export const DestinationTypeSelection = () => { ))} - )} + ))}