Files
supabase__supabase/apps/studio/tests/pages/sign-up.test.tsx
Danny White 1131e3e2ce fix(ui): default Button variant to default instead of primary (#50160)
## What kind of change does this PR introduce?

Bug fix / design-system alignment for the legacy `Button` from `ui`.

## What is the current behavior?

Omitting `variant` on the legacy `Button` falls back to brand-green
`primary`. That makes accidental greens easy, and it is hard to spot the
real main action on busy pages.

## What is the new behavior?

- Legacy `Button` now defaults to neutral `default`
- Intentional primary CTAs (create, save, submit, marketing CTAs, and
matching `ButtonTooltip` usages) now set `variant="primary"` so their
appearance is unchanged
- Neutral actions that previously relied on the old fallback (cancel,
close, back, dashboard nav, and similar) become grey/white
- Design-system docs updated; regression tests cover the new default

`Button_Shadcn_` is unchanged. It already uses its own CVA default.

This is PR 1 of 2 in a stack. PR 2 drops now-redundant
`variant="default"` props.

## To test

Studio (http://localhost:8082):

- `/sign-in`: Sign in stays green
- Open a project → Database → Tables: New table stays green
- Auth → Users → Invite: Invite user stays green; Cancel / dismiss
controls stay neutral
- Project Settings → General: edit a field so Cancel and Save appear.
Cancel is neutral, Save is green

Design system (http://localhost:3003):

- Components → Button: default demo is neutral; primary demo is green;
featured preview is the default variant

Marketing (optional):

- www header: Start your project stays green; logged-in Dashboard is
neutral

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

- **Style**
- Buttons now default to a neutral style, while primary actions across
Studio, documentation, marketing pages, forms, dialogs, and error states
use prominent primary styling.
- Updated button examples and previews clarify the distinction between
default and primary variants.
  - Event registration now includes a directional arrow icon.

- **Tests**
- Added coverage confirming default button styling and explicit primary
styling behave as expected.
- Updated related test fixtures to use primary styling where
appropriate.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-10 11:23:17 +10:00

103 lines
3.8 KiB
TypeScript

import { screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { useState } from 'react'
import { Button } from 'ui'
import { beforeEach, describe, expect, test, vi } from 'vitest'
import {
CHATGPT_IDENTITY_PROVIDER,
GITHUB_IDENTITY_PROVIDER,
type ExternalIdentityProviderConfig,
} from '@/lib/external-identity-providers'
import SignUpPage from '@/pages/sign-up'
import { customRender } from '@/tests/lib/custom-render'
const mocks = vi.hoisted(() => ({
focusProvider: undefined as ExternalIdentityProviderConfig | undefined,
}))
vi.mock('@/components/interfaces/SignIn/SignInWithExternalProvider', () => ({
SignInWithExternalProvider: ({ provider }: { provider: ExternalIdentityProviderConfig }) => (
<Button variant="primary">Continue with {provider.displayName}</Button>
),
}))
vi.mock('@/components/interfaces/SignIn/SignUpForm', () => ({
SignUpForm: ({ onSuccess }: { onSuccess?: () => void }) => {
const [isSubmitted, setIsSubmitted] = useState(false)
return (
<>
<Button
variant="primary"
onClick={() => {
setIsSubmitted(true)
onSuccess?.()
}}
>
Complete email sign-up
</Button>
{isSubmitted && <div>Check your email</div>}
</>
)
},
}))
vi.mock('@/hooks/misc/useEnabledIdentityProviders', () => ({
useEnabledIdentityProviders: () => [GITHUB_IDENTITY_PROVIDER, CHATGPT_IDENTITY_PROVIDER],
}))
vi.mock('@/hooks/misc/useInboundBranding', () => ({
useInboundBranding: () => ({ focusProvider: mocks.focusProvider }),
}))
vi.mock('@/hooks/misc/useIsFeatureEnabled', () => ({
useIsFeatureEnabled: () => ({
dashboardAuthSignUp: true,
dashboardAuthSignInWithSso: false,
dashboardAuthSignInWithEmail: true,
}),
}))
describe('SignUpPage', () => {
beforeEach(() => {
mocks.focusProvider = undefined
})
test('hides social sign-up options after email sign-up succeeds', async () => {
const user = userEvent.setup()
customRender(<SignUpPage dehydratedState={undefined} />)
expect(screen.getByRole('button', { name: 'Continue with GitHub' })).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'Continue with ChatGPT' })).toBeInTheDocument()
expect(screen.getByText('or')).toBeInTheDocument()
expect(screen.queryByText('Check your email')).not.toBeInTheDocument()
await user.click(screen.getByRole('button', { name: 'Complete email sign-up' }))
expect(screen.queryByRole('button', { name: 'Continue with GitHub' })).not.toBeInTheDocument()
expect(screen.queryByRole('button', { name: 'Continue with ChatGPT' })).not.toBeInTheDocument()
expect(screen.queryByText('or')).not.toBeInTheDocument()
expect(screen.getByText('Check your email')).toBeInTheDocument()
expect(screen.getByRole('link', { name: 'Sign in' })).toHaveAttribute('href', '/sign-in')
})
test('hides the focused provider after email sign-up succeeds', async () => {
const user = userEvent.setup()
mocks.focusProvider = GITHUB_IDENTITY_PROVIDER
customRender(<SignUpPage dehydratedState={undefined} />)
expect(screen.getByRole('button', { name: 'Continue with GitHub' })).toBeInTheDocument()
expect(screen.queryByText('Check your email')).not.toBeInTheDocument()
await user.click(screen.getByRole('button', { name: 'Show other options' }))
await user.click(screen.getByRole('button', { name: 'Complete email sign-up' }))
expect(screen.queryByRole('button', { name: 'Continue with GitHub' })).not.toBeInTheDocument()
expect(screen.queryByRole('button', { name: 'Continue with ChatGPT' })).not.toBeInTheDocument()
expect(screen.queryByText('or')).not.toBeInTheDocument()
expect(screen.getByText('Check your email')).toBeInTheDocument()
expect(screen.getByRole('link', { name: 'Sign in' })).toHaveAttribute('href', '/sign-in')
})
})