mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
fix(studio): clarify fast database reboot (#49741)
## What kind of change does this PR introduce? Bug fix. Resolves DEPR-657. ## What is the current behavior? The Fast database reboot description suggests the action may fail to recover from some failure modes, which can be read as a risk of the reboot itself. ## What is the new behavior? The description clearly explains that the faster option restarts only the database service, has less downtime than a full project restart, and leaves other project services running. | Before | After | | --- | --- | | <img width="1460" height="512" alt="CleanShot 2026-08-31 at 09 24 49@2x" src="https://github.com/user-attachments/assets/2d4a940c-4d66-4753-99d8-9d0d2b4951af" /> | <img width="1458" height="500" alt="CleanShot 2026-08-31 at 09 31 18@2x" src="https://github.com/user-attachments/assets/f58aa351-5c8c-4a9a-b31d-b771659defd3" /> | ## To test 1. Open a project's **Settings > General** page. 2. Under **Project availability**, tab to **Restart project**, then tab again to the adjacent chevron button. 3. Press Enter and confirm focus moves to **Fast database reboot**. 4. Confirm its description reads: “Restarts only the database service, with less downtime than a full project restart. Other project services remain running.” 5. Confirm the project availability descriptions appear as secondary text beneath their action labels. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Accessibility Improvements** * Improved keyboard navigation with separate tab stops for restart actions and restart-type selection. * Added clearer labeling and focus behavior when choosing a restart type. * **UI Improvements** * Clarified that fast database restarts affect only PostgreSQL while other services continue running. * Improved text contrast on the project settings page. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
b0e31be89a
commit
0b27205ae4
3 files changed
+79
-6
No files matched your search
+71
@@ -0,0 +1,71 @@
|
||||
import { screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { RestartServerButton } from './RestartServerButton'
|
||||
import { customRender } from '@/tests/lib/custom-render'
|
||||
|
||||
const {
|
||||
mockUseAsyncCheckPermissions,
|
||||
mockUseFlag,
|
||||
mockUseIsFeatureEnabled,
|
||||
mockUseSelectedProjectQuery,
|
||||
} = vi.hoisted(() => ({
|
||||
mockUseAsyncCheckPermissions: vi.fn(),
|
||||
mockUseFlag: vi.fn(),
|
||||
mockUseIsFeatureEnabled: vi.fn(),
|
||||
mockUseSelectedProjectQuery: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('common', async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import('common')>()),
|
||||
useFlag: mockUseFlag,
|
||||
}))
|
||||
|
||||
vi.mock('next/router', () => ({
|
||||
useRouter: () => ({ push: vi.fn() }),
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/misc/useCheckPermissions', () => ({
|
||||
useAsyncCheckPermissions: mockUseAsyncCheckPermissions,
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/misc/useIsFeatureEnabled', () => ({
|
||||
useIsFeatureEnabled: mockUseIsFeatureEnabled,
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/misc/useSelectedProject', () => ({
|
||||
useIsAwsK8sCloudProvider: () => false,
|
||||
useIsProjectActive: () => true,
|
||||
useSelectedProjectQuery: mockUseSelectedProjectQuery,
|
||||
}))
|
||||
|
||||
describe('RestartServerButton', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockUseFlag.mockReturnValue(false)
|
||||
mockUseAsyncCheckPermissions.mockReturnValue({ can: true })
|
||||
mockUseIsFeatureEnabled.mockReturnValue({ projectSettingsRestartProject: true })
|
||||
mockUseSelectedProjectQuery.mockReturnValue({
|
||||
data: { ref: 'default', region: 'us-east-1', status: 'ACTIVE_HEALTHY' },
|
||||
})
|
||||
})
|
||||
|
||||
it('uses separate tab stops for the primary action and restart type menu', async () => {
|
||||
const user = userEvent.setup()
|
||||
customRender(<RestartServerButton />)
|
||||
|
||||
const restartProject = screen.getByRole('button', { name: 'Restart project' })
|
||||
const chooseRestartType = screen.getByRole('button', { name: 'Choose restart type' })
|
||||
|
||||
await user.tab()
|
||||
expect(restartProject).toHaveFocus()
|
||||
|
||||
await user.tab()
|
||||
expect(chooseRestartType).toHaveFocus()
|
||||
|
||||
await user.keyboard('{Enter}')
|
||||
expect(await screen.findByRole('menuitem', { name: /Fast database reboot/ })).toHaveFocus()
|
||||
expect(screen.getByText(/Other project services remain running/)).toBeVisible()
|
||||
})
|
||||
})
|
||||
+6
-4
@@ -107,6 +107,7 @@ export const RestartServerButton = () => {
|
||||
{projectSettingsRestartProject ? (
|
||||
<div className="flex w-full @lg:w-auto">
|
||||
<ButtonTooltip
|
||||
type="button"
|
||||
variant="default"
|
||||
className={cn(
|
||||
'flex-1 px-3 hover:z-10 focus-visible:z-10 @lg:flex-none',
|
||||
@@ -141,8 +142,9 @@ export const RestartServerButton = () => {
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
type="button"
|
||||
variant="default"
|
||||
aria-label={`Restart ${entityLabel}`}
|
||||
aria-label="Choose restart type"
|
||||
className="shrink-0 rounded-l-none px-[4px] py-[5px] -ml-px focus-visible:z-10 focus-visible:rounded-l-sm"
|
||||
icon={<ChevronDown />}
|
||||
disabled={!canRestartProject}
|
||||
@@ -158,9 +160,9 @@ export const RestartServerButton = () => {
|
||||
>
|
||||
<div className="space-y-0.5">
|
||||
<p className="block text-foreground">Fast database reboot</p>
|
||||
<p className="block text-foreground-light">
|
||||
Restarts only the database. Faster, but may not be able to recover from all
|
||||
failure modes.
|
||||
<p className="block text-foreground-lighter">
|
||||
Restarts only the database service, with less downtime than a full project
|
||||
restart. Other project services remain running.
|
||||
</p>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
|
||||
@@ -72,7 +72,7 @@ export const Project = () => {
|
||||
<div>
|
||||
<p className="text-sm">{primaryActionLabel}</p>
|
||||
<div className="max-w-[420px]">
|
||||
<p className="text-sm text-foreground-light">{primaryActionDescription}</p>
|
||||
<p className="text-sm text-foreground-lighter">{primaryActionDescription}</p>
|
||||
</div>
|
||||
</div>
|
||||
{isPaused ? (
|
||||
@@ -98,7 +98,7 @@ export const Project = () => {
|
||||
<div>
|
||||
<p className="text-sm">Pause {entityLabel}</p>
|
||||
<div className="max-w-[420px]">
|
||||
<p className="text-sm text-foreground-light">
|
||||
<p className="text-sm text-foreground-lighter">
|
||||
Your {entityLabel} will not be accessible while it is paused.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user