From b90d2f861256a26aa658fa2e6ad5ef05dc357fe3 Mon Sep 17 00:00:00 2001 From: lucaronin Date: Mon, 20 Apr 2026 11:04:29 +0200 Subject: [PATCH] fix: reorder welcome onboarding actions --- src/components/WelcomeScreen.test.tsx | 18 +++---- src/components/WelcomeScreen.tsx | 48 +++++++++---------- tests/smoke/create-empty-vault-flows.spec.ts | 4 +- .../fresh-start-telemetry-onboarding.spec.ts | 11 +++-- tests/smoke/offline-onboarding-status.spec.ts | 6 ++- 5 files changed, 48 insertions(+), 39 deletions(-) diff --git a/src/components/WelcomeScreen.test.tsx b/src/components/WelcomeScreen.test.tsx index d5f78436..33ee502d 100644 --- a/src/components/WelcomeScreen.test.tsx +++ b/src/components/WelcomeScreen.test.tsx @@ -41,16 +41,18 @@ describe('WelcomeScreen', () => { expect(brandIcon).toHaveAttribute('src', tolariaIcon) }) - it('shows all three option buttons', () => { + it('shows the onboarding actions in the guided-first order', () => { render() - expect(screen.getByTestId('welcome-create-new')).toHaveTextContent('Create empty vault') - expect(screen.getByTestId('welcome-open-folder')).toHaveTextContent('Open existing vault') - expect(screen.getByTestId('welcome-create-vault')).toHaveTextContent('Get started with a template') + + const optionButtons = screen.getAllByRole('button') + expect(optionButtons[0]).toBe(screen.getByTestId('welcome-create-vault')) + expect(optionButtons[1]).toBe(screen.getByTestId('welcome-create-new')) + expect(optionButtons[2]).toBe(screen.getByTestId('welcome-open-folder')) }) it('focuses the first action for keyboard users', () => { render() - expect(screen.getByTestId('welcome-create-new')).toHaveFocus() + expect(screen.getByTestId('welcome-create-vault')).toHaveFocus() }) it('shows the simplified template option description', () => { @@ -109,13 +111,13 @@ describe('WelcomeScreen', () => { }) it('cycles onboarding actions with Tab and activates the selected action with Enter', () => { - const onOpenFolder = vi.fn() - render() + const onCreateEmptyVault = vi.fn() + render() fireEvent.keyDown(window, { key: 'Tab' }) fireEvent.keyDown(window, { key: 'Enter' }) - expect(onOpenFolder).toHaveBeenCalledOnce() + expect(onCreateEmptyVault).toHaveBeenCalledOnce() }) it('disables all buttons while creating', () => { diff --git a/src/components/WelcomeScreen.tsx b/src/components/WelcomeScreen.tsx index d5365a12..a7cddb67 100644 --- a/src/components/WelcomeScreen.tsx +++ b/src/components/WelcomeScreen.tsx @@ -310,18 +310,18 @@ function useWelcomeActionButtons({ > & { busy: boolean }) { - const primaryActionRef = useRef(null) - const openFolderActionRef = useRef(null) const templateActionRef = useRef(null) + const createEmptyActionRef = useRef(null) + const openFolderActionRef = useRef(null) const actionButtonRefs = useMemo( - () => [primaryActionRef, openFolderActionRef, templateActionRef], + () => [templateActionRef, createEmptyActionRef, openFolderActionRef], [], ) const actions = useMemo( () => ([ + { disabled: isOffline, run: onCreateVault }, { disabled: false, run: onCreateEmptyVault }, { disabled: false, run: onOpenFolder }, - { disabled: isOffline, run: onCreateVault }, ]), [isOffline, onCreateEmptyVault, onCreateVault, onOpenFolder], ) @@ -330,7 +330,7 @@ function useWelcomeActionButtons({ if (busy) return // WKWebView can ignore `autoFocus`; move focus explicitly so keyboard-only - // onboarding always starts on "Create empty vault". + // onboarding always starts on the guided template flow. focusWelcomeAction(actionButtonRefs, 0) }, [actionButtonRefs, busy, mode]) @@ -362,9 +362,9 @@ function useWelcomeActionButtons({ }, [actionButtonRefs, actions, busy]) return { - primaryActionRef, - openFolderActionRef, templateActionRef, + createEmptyActionRef, + openFolderActionRef, } } @@ -382,7 +382,7 @@ export function WelcomeScreen({ }: WelcomeScreenProps) { const busy = creatingAction !== null const presentation = getWelcomeScreenPresentation(mode, defaultVaultPath, isOffline) - const { primaryActionRef, openFolderActionRef, templateActionRef } = useWelcomeActionButtons({ + const { templateActionRef, createEmptyActionRef, openFolderActionRef } = useWelcomeActionButtons({ mode, busy, isOffline, @@ -417,6 +417,21 @@ export function WelcomeScreen({
+ } + iconBg="var(--accent-purple-light, #F3E8FF)" + label="Get started with a template" + description={presentation.templateDescription} + loadingLabel="Downloading template…" + loadingDescription="Cloning the Getting Started vault template" + onClick={onCreateVault} + disabled={busy || isOffline} + loading={creatingAction === 'template'} + testId="welcome-create-vault" + autoFocus + buttonRef={templateActionRef} + /> + } iconBg="var(--accent-blue-light, #EBF4FF)" @@ -428,8 +443,7 @@ export function WelcomeScreen({ disabled={busy} loading={creatingAction === 'empty'} testId="welcome-create-new" - autoFocus - buttonRef={primaryActionRef} + buttonRef={createEmptyActionRef} /> - - } - iconBg="var(--accent-purple-light, #F3E8FF)" - label="Get started with a template" - description={presentation.templateDescription} - loadingLabel="Downloading template…" - loadingDescription="Cloning the Getting Started vault template" - onClick={onCreateVault} - disabled={busy || isOffline} - loading={creatingAction === 'template'} - testId="welcome-create-vault" - buttonRef={templateActionRef} - />
{creatingAction === 'template' && ( diff --git a/tests/smoke/create-empty-vault-flows.spec.ts b/tests/smoke/create-empty-vault-flows.spec.ts index f10ecf6c..ac8a74eb 100644 --- a/tests/smoke/create-empty-vault-flows.spec.ts +++ b/tests/smoke/create-empty-vault-flows.spec.ts @@ -181,8 +181,10 @@ test('keyboard onboarding can create an empty vault and the first note', async ( await expect(page.getByTestId('welcome-screen')).toBeVisible() await expect(page.getByTestId('welcome-create-new')).toContainText('Create empty vault') - await expect(page.getByTestId('welcome-create-new')).toBeFocused() + await expect(page.getByTestId('welcome-create-vault')).toBeFocused() + await page.keyboard.press('Tab') + await expect(page.getByTestId('welcome-create-new')).toBeFocused() await page.keyboard.press('Enter') await expect(page.getByTestId('note-list-container')).toBeVisible({ timeout: 5_000 }) diff --git a/tests/smoke/fresh-start-telemetry-onboarding.spec.ts b/tests/smoke/fresh-start-telemetry-onboarding.spec.ts index 472810d7..e0228e4d 100644 --- a/tests/smoke/fresh-start-telemetry-onboarding.spec.ts +++ b/tests/smoke/fresh-start-telemetry-onboarding.spec.ts @@ -59,7 +59,7 @@ test('accepting telemetry consent on a fresh start opens the vault choice wizard await expect(page.getByTestId('welcome-screen')).toBeVisible() await expect(page.getByTestId('welcome-open-folder')).toBeVisible() - await expect(page.getByTestId('welcome-create-new')).toBeFocused() + await expect(page.getByTestId('welcome-create-vault')).toBeFocused() }) test('telemetry consent still leaves the welcome wizard fully keyboard navigable @smoke', async ({ page }) => { @@ -76,6 +76,9 @@ test('telemetry consent still leaves the welcome wizard fully keyboard navigable await page.keyboard.press('Enter') await expect(page.getByTestId('welcome-screen')).toBeVisible() + await expect(page.getByTestId('welcome-create-vault')).toBeFocused() + + await page.keyboard.press('Tab') await expect(page.getByTestId('welcome-create-new')).toBeFocused() await page.keyboard.press('Tab') @@ -92,10 +95,10 @@ test('telemetry consent still leaves the welcome wizard fully keyboard navigable await expect(page.getByTestId('welcome-screen')).toBeVisible() - await page.keyboard.press('Tab') - await expect(page.getByTestId('welcome-create-vault')).toBeFocused() await page.keyboard.press('Shift+Tab') - await expect(page.getByTestId('welcome-open-folder')).toBeFocused() + await expect(page.getByTestId('welcome-create-new')).toBeFocused() + await page.keyboard.press('Shift+Tab') + await expect(page.getByTestId('welcome-create-vault')).toBeFocused() }) for (const action of ['accept', 'decline'] as const) { diff --git a/tests/smoke/offline-onboarding-status.spec.ts b/tests/smoke/offline-onboarding-status.spec.ts index bd0aad4d..378dfbab 100644 --- a/tests/smoke/offline-onboarding-status.spec.ts +++ b/tests/smoke/offline-onboarding-status.spec.ts @@ -3,6 +3,7 @@ import { test, expect, type Page } from '@playwright/test' async function installOnboardingMocks(page: Page, offline: boolean) { await page.addInitScript((isOffline: boolean) => { localStorage.clear() + let createdVaultPath: string | null = null let ref: Record | null = null @@ -16,11 +17,12 @@ async function installOnboardingMocks(page: Page, offline: boolean) { hidden_defaults: [], }) ref.get_default_vault_path = () => '/Users/mock/Documents/Getting Started' - ref.check_vault_exists = () => false + ref.check_vault_exists = (args: { path?: string }) => args.path === createdVaultPath ref.create_getting_started_vault = (args: { targetPath?: string | null }) => { if (args.targetPath !== '/Users/mock/Documents/Getting Started') { throw new Error(`Unexpected Getting Started target: ${args.targetPath}`) } + createdVaultPath = args.targetPath return args.targetPath } }, @@ -65,6 +67,6 @@ test('status bar keeps a Getting Started clone entry available after onboarding' await expect(page.getByTestId('claude-onboarding-screen')).toBeVisible() await page.getByTestId('claude-onboarding-continue').click() await expect(page.locator('[data-testid="note-list-container"]')).toBeVisible() - await page.getByTitle('Switch vault').click() + await page.getByTestId('status-vault-trigger').click() await expect(page.getByTestId('vault-menu-clone-getting-started')).toBeVisible() })