Files
Danny White 476d4a5851 refactor(ui): drop redundant Button variant="default" props (#50161)
## What kind of change does this PR introduce?

Mechanical cleanup on top of the Button default-variant change (#50160).

## What is the current behavior?

Many callsites still pass `variant="default"` even though that is now
the component default.

## What is the new behavior?

Removes redundant static `variant="default"` from legacy `Button` and
`ButtonTooltip` callsites. Keeps explicit defaults where they document
the API:

- `button-default.tsx` and `button-sizes.tsx` demos
- `DocsButton`, which pins neutral styling at the wrapper boundary

## To test

Studio:

- [Auth → Rate
Limits](https://studio-staging-2s957kwc4-supabase.vercel.app/dashboard/project/_/auth/rate-limits):
dirty the form so Cancel appears; Cancel stays neutral, Save stays green
- [Project Settings → API
Keys](https://studio-staging-2s957kwc4-supabase.vercel.app/dashboard/project/_/settings/api-keys):
`DocsButton` in the header actions stays neutral

Design system:

- [Design system →
Button](https://design-system-git-dnywh-dc924ac1-supabase.vercel.app/design-system/docs/components/button):
`button-default` / `button-sizes` still show explicit default styling;
Primary (green) is restricted to the Primary section (and `asChild`)

WWW:

- [www → Brand
assets](https://zone-www-dot-com-git-dnywh-dc924ac1-supabase.vercel.app/brand-assets):
Download logo kit / Download button kit stay neutral
2026-09-11 17:05:26 +10:00

252 lines
9.4 KiB
TypeScript

import { ChevronDown, PlusIcon, RefreshCw } from 'lucide-react'
import { useMemo, useState, type ComponentProps, type ReactNode } from 'react'
import { useWatch, type FieldValues, type Path, type UseFormReturn } from 'react-hook-form'
import {
Button,
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
FormControl,
FormField,
Popover,
PopoverContent,
PopoverTrigger,
} from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { useGitHubAuthorizationQuery } from '@/data/integrations/github-authorization-query'
import { useGitHubRepositoriesQuery } from '@/data/integrations/github-repositories-query'
import { openInstallGitHubIntegrationWindow } from '@/lib/github'
import { EMPTY_ARR } from '@/lib/void'
export type GitHubRepository = {
id: string
name: string
installation_id: number
default_branch: string
}
export const GITHUB_ICON = (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98 96" className="w-5 shrink-0">
<title>GitHub icon</title>
<path
fill="currentColor"
fillRule="evenodd"
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"
clipRule="evenodd"
/>
</svg>
)
export const useGitHubRepositoryOptions = () => {
const {
data: gitHubAuthorization,
isPending: isLoadingGitHubAuthorization,
refetch: refetchGitHubAuthorization,
} = useGitHubAuthorizationQuery()
const {
data: githubReposData,
isPending: isLoadingGitHubRepos,
refetch: refetchGitHubRepositories,
} = useGitHubRepositoriesQuery({
enabled: Boolean(gitHubAuthorization),
})
const githubRepos = useMemo<GitHubRepository[]>(
() =>
githubReposData?.repositories?.map((repo) => ({
id: repo.id.toString(),
name: repo.name,
installation_id: repo.installation_id,
default_branch: repo.default_branch || 'main',
})) ?? EMPTY_ARR,
[githubReposData]
)
const refetchGitHubAuthorizationAndRepositories = () => {
setTimeout(() => {
refetchGitHubAuthorization()
refetchGitHubRepositories()
}, 2000)
}
return {
gitHubAuthorization,
githubRepos,
hasPartialResponseDueToSSO: githubReposData?.partial_response_due_to_sso ?? false,
isLoading: isLoadingGitHubAuthorization || isLoadingGitHubRepos,
refetch: refetchGitHubAuthorizationAndRepositories,
}
}
interface GitHubRepositoryFieldProps<TFormValues extends FieldValues> {
form: UseFormReturn<TFormValues>
name: Path<TFormValues>
label: string
description?: ReactNode
layout?: ComponentProps<typeof FormItemLayout>['layout']
disabled?: boolean
selectedRepositoryName?: string
installationIdField?: Path<TFormValues>
repositoryNameField?: Path<TFormValues>
repositories: GitHubRepository[]
gitHubAuthorization: unknown | null
hasPartialResponseDueToSSO?: boolean
isLoading?: boolean
placeholder?: string
refetch: () => void
onConnectClick?: () => void
onRepositorySelect?: (repo: GitHubRepository) => void
}
export const GitHubRepositoryField = <TFormValues extends FieldValues>({
form,
name,
label,
description,
layout = 'horizontal',
disabled = false,
selectedRepositoryName,
installationIdField,
repositoryNameField,
repositories,
gitHubAuthorization,
hasPartialResponseDueToSSO = false,
isLoading = false,
placeholder = 'Choose GitHub repository',
refetch,
onConnectClick,
onRepositorySelect,
}: GitHubRepositoryFieldProps<TFormValues>) => {
const [isRepoSelectorOpen, setIsRepoSelectorOpen] = useState(false)
const currentRepositoryId = useWatch({ control: form.control, name }) as string | undefined
const selectedRepository = repositories.find((repo) => repo.id === currentRepositoryId)
return (
<FormField
control={form.control}
name={name}
render={({ field }) => (
<FormItemLayout id={name} label={label} layout={layout} description={description}>
{gitHubAuthorization === null ? (
<FormControl>
<Button
id={name}
size="tiny"
type="button"
disabled={disabled}
onClick={() => {
onConnectClick?.()
openInstallGitHubIntegrationWindow('authorize', refetch)
}}
icon={GITHUB_ICON}
>
Connect GitHub
</Button>
</FormControl>
) : (
<Popover open={isRepoSelectorOpen} onOpenChange={setIsRepoSelectorOpen}>
<PopoverTrigger asChild>
<FormControl>
<Button
id={name}
type="button"
className="justify-start h-[34px] w-full [&>div:last-child]:ml-auto"
disabled={disabled || isLoading}
loading={isLoading}
icon={GITHUB_ICON}
iconRight={<ChevronDown />}
>
{selectedRepository?.name ||
selectedRepositoryName ||
(isLoading ? 'Loading GitHub repositories...' : placeholder)}
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" side="bottom" align="start" sameWidthAsTrigger>
<Command>
<CommandInput placeholder="Search repositories..." />
<CommandList className="!max-h-[220px]">
<CommandEmpty>No repositories found.</CommandEmpty>
{repositories.length > 0 ? (
<CommandGroup>
{repositories.map((repo) => (
<CommandItem
key={repo.id}
value={`${repo.name.replaceAll('"', '')}-${repo.id}`}
className="flex gap-2 items-center"
onSelect={() => {
field.onChange(repo.id)
if (installationIdField !== undefined) {
form.setValue(installationIdField, repo.installation_id as never, {
shouldDirty: true,
})
}
if (repositoryNameField !== undefined) {
form.setValue(repositoryNameField, repo.name as never, {
shouldDirty: true,
})
}
onRepositorySelect?.(repo)
setIsRepoSelectorOpen(false)
}}
>
{GITHUB_ICON}
<span className="truncate" title={repo.name}>
{repo.name}
</span>
</CommandItem>
))}
</CommandGroup>
) : null}
<CommandGroup>
<CommandItem
className="flex gap-2 items-center cursor-pointer"
onSelect={() => {
setIsRepoSelectorOpen(false)
openInstallGitHubIntegrationWindow('install', refetch)
}}
>
<PlusIcon size={16} />
Add GitHub Repositories
</CommandItem>
</CommandGroup>
{hasPartialResponseDueToSSO && (
<>
<CommandSeparator />
<CommandGroup>
<CommandItem
className="flex gap-2 items-start cursor-pointer"
onSelect={() => {
setIsRepoSelectorOpen(false)
openInstallGitHubIntegrationWindow('authorize', refetch)
}}
>
<RefreshCw size={16} className="mt-0.5 shrink-0" />
<div className="text-xs text-foreground-light">
Re-authorize GitHub with SSO to show all repositories
</div>
</CommandItem>
</CommandGroup>
</>
)}
</CommandList>
</Command>
</PopoverContent>
</Popover>
)}
</FormItemLayout>
)}
/>
)
}