From 0b27205ae42ac5a66050ff52e8a1c95e25c71d18 Mon Sep 17 00:00:00 2001 From: Danny White <3104761+dnywh@users.noreply.github.com> Date: Mon, 31 Aug 2026 13:01:17 +1000 Subject: [PATCH] fix(studio): clarify fast database reboot (#49741) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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 | | --- | --- | | CleanShot 2026-08-31 at 09 24
49@2x | CleanShot 2026-08-31 at 09 31
18@2x | ## 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. ## 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. --- .../RestartServerButton.test.tsx | 71 +++++++++++++++++++ .../Infrastructure/RestartServerButton.tsx | 10 +-- .../interfaces/Settings/General/Project.tsx | 4 +- 3 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 apps/studio/components/interfaces/Settings/General/Infrastructure/RestartServerButton.test.tsx diff --git a/apps/studio/components/interfaces/Settings/General/Infrastructure/RestartServerButton.test.tsx b/apps/studio/components/interfaces/Settings/General/Infrastructure/RestartServerButton.test.tsx new file mode 100644 index 00000000000..940c2617184 --- /dev/null +++ b/apps/studio/components/interfaces/Settings/General/Infrastructure/RestartServerButton.test.tsx @@ -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()), + 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() + + 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() + }) +}) diff --git a/apps/studio/components/interfaces/Settings/General/Infrastructure/RestartServerButton.tsx b/apps/studio/components/interfaces/Settings/General/Infrastructure/RestartServerButton.tsx index 8eda0ce5e83..db3b604491a 100644 --- a/apps/studio/components/interfaces/Settings/General/Infrastructure/RestartServerButton.tsx +++ b/apps/studio/components/interfaces/Settings/General/Infrastructure/RestartServerButton.tsx @@ -107,6 +107,7 @@ export const RestartServerButton = () => { {projectSettingsRestartProject ? (
{