mirror of
https://github.com/supabase/supabase.git
synced 2026-06-29 11:57:37 -04:00
498d051d88
## What kind of change does this PR introduce? Feature. Resolves FE-3417. ## What is the current behavior? Project Settings has a top-level `G then ,` shortcut, but its subnavigation and repeated key/log drain actions do not have scoped keyboard shortcuts or visible shortcut tooltips. | Area | Current behaviour | | --- | --- | | Project Settings sidebar | Routes are click-only once users are inside Settings. | | API/JWT keys | Creation buttons do not expose keyboard shortcuts. | | Log Drains | Add/save destination actions do not expose keyboard shortcuts. | ## What is the new behavior? Adds scoped Project Settings navigation chords, shortcut tooltips on the sidebar rows, and page/action shortcuts for API keys, JWT standby keys, and Log Drains. | Area | New shortcut coverage | | --- | --- | | Project Settings sidebar | `S then G/C/I/N/W/K/J/L/A/D` for eligible in-section routes. | | API Keys | `Shift+P` and `Shift+S` open the publishable/secret key dialogs; `Mod+Enter` submits the open dialog. | | JWT Keys | `Shift+N` opens Create standby key; `Mod+Enter` submits the open dialog. | | Log Drains | `Shift+N` adds a destination when the primary action is available; `Mod+Enter` saves the open destination sheet. | <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added keyboard shortcuts for Project Settings navigation and for actions in API Keys, JWT Keys, and Log Drains (open, create/submit). * **Improvements** * Dialogs and forms now support keyboard-triggered open and submit actions with improved enable/disable gating and updated settings menu composition; shortcuts appear in the shortcuts reference. * **Tests** * Added tests covering shortcut wiring and shortcut-driven open/submit behaviors across dialogs and action panels. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46352?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Ali Waseem <waseema393@gmail.com>
117 lines
3.5 KiB
TypeScript
117 lines
3.5 KiB
TypeScript
import { render, screen } from '@testing-library/react'
|
|
import userEvent from '@testing-library/user-event'
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
import { CreatePublishableAPIKeyDialog } from './CreatePublishableAPIKeyDialog'
|
|
import { CreateSecretAPIKeyDialog } from './CreateSecretAPIKeyDialog'
|
|
import { SHORTCUT_IDS } from '@/state/shortcuts/registry'
|
|
|
|
const { mockSetVisible, mockShortcut, mockUseQueryState } = vi.hoisted(() => ({
|
|
mockSetVisible: vi.fn(),
|
|
mockShortcut: vi.fn(({ children }: any) => <div data-testid="shortcut">{children}</div>),
|
|
mockUseQueryState: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('next/navigation', async () => {
|
|
const actual = await vi.importActual<typeof import('next/navigation')>('next/navigation')
|
|
return {
|
|
...actual,
|
|
useParams: () => ({ ref: 'project-ref' }),
|
|
}
|
|
})
|
|
|
|
vi.mock('nuqs', async () => {
|
|
const actual = await vi.importActual<typeof import('nuqs')>('nuqs')
|
|
return {
|
|
...actual,
|
|
useQueryState: mockUseQueryState,
|
|
}
|
|
})
|
|
|
|
vi.mock('@/components/ui/Shortcut', () => ({
|
|
Shortcut: mockShortcut,
|
|
}))
|
|
|
|
vi.mock('@/data/api-keys/api-key-create-mutation', () => ({
|
|
useAPIKeyCreateMutation: () => ({ mutate: vi.fn(), isPending: false }),
|
|
}))
|
|
|
|
describe('API key create dialogs', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
mockUseQueryState.mockReturnValue(['', mockSetVisible])
|
|
})
|
|
|
|
it('registers and surfaces the publishable key shortcut on the visible trigger', async () => {
|
|
const user = userEvent.setup()
|
|
|
|
render(<CreatePublishableAPIKeyDialog />)
|
|
|
|
expect(mockShortcut).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
id: SHORTCUT_IDS.API_KEYS_NEW_PUBLISHABLE,
|
|
onTrigger: expect.any(Function),
|
|
side: 'bottom',
|
|
}),
|
|
undefined
|
|
)
|
|
|
|
await user.click(screen.getByRole('button', { name: 'New publishable key' }))
|
|
|
|
expect(mockSetVisible).toHaveBeenCalledWith('publishable')
|
|
})
|
|
|
|
it('registers and surfaces the publishable key submit shortcut on the primary action', () => {
|
|
mockUseQueryState.mockReturnValue(['publishable', mockSetVisible])
|
|
|
|
render(<CreatePublishableAPIKeyDialog />)
|
|
|
|
expect(mockShortcut).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
id: SHORTCUT_IDS.API_KEYS_CREATE_PUBLISHABLE,
|
|
onTrigger: expect.any(Function),
|
|
options: { enabled: true },
|
|
side: 'top',
|
|
}),
|
|
undefined
|
|
)
|
|
expect(screen.getByRole('button', { name: 'Create Publishable API key' })).toBeInTheDocument()
|
|
})
|
|
|
|
it('registers and surfaces the secret key shortcut on the visible trigger', async () => {
|
|
const user = userEvent.setup()
|
|
|
|
render(<CreateSecretAPIKeyDialog />)
|
|
|
|
expect(mockShortcut).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
id: SHORTCUT_IDS.API_KEYS_NEW_SECRET,
|
|
onTrigger: expect.any(Function),
|
|
side: 'bottom',
|
|
}),
|
|
undefined
|
|
)
|
|
|
|
await user.click(screen.getByRole('button', { name: 'New secret key' }))
|
|
|
|
expect(mockSetVisible).toHaveBeenCalledWith('secret')
|
|
})
|
|
|
|
it('registers and surfaces the secret key submit shortcut on the primary action', () => {
|
|
mockUseQueryState.mockReturnValue(['secret', mockSetVisible])
|
|
|
|
render(<CreateSecretAPIKeyDialog />)
|
|
|
|
expect(mockShortcut).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
id: SHORTCUT_IDS.API_KEYS_CREATE_SECRET,
|
|
onTrigger: expect.any(Function),
|
|
options: { enabled: true },
|
|
side: 'top',
|
|
}),
|
|
undefined
|
|
)
|
|
expect(screen.getByRole('button', { name: 'Create API key' })).toBeInTheDocument()
|
|
})
|
|
})
|