From 7151f44784194a4caab0d2bf0414de06363d8ed1 Mon Sep 17 00:00:00 2001 From: Henrik Kurelid Date: Mon, 20 Apr 2026 21:56:04 +0200 Subject: [PATCH 1/2] feat: move action buttons to mobile drawer to fix toolbar overflow - TopBar: hide Quicksave/Quickload/Save/Load/Help on mobile (max-md:hidden) - MobileDrawer: add action chips row at top with auto-close on tap - GameLayout: wire new MobileDrawer action emits - 3 new e2e tests covering mobile toolbar overflow fix Formatting constraints verified --- web/e2e/game.spec.ts | 56 +++++++++++++++++++++++++++++ web/src/components/GameLayout.vue | 5 +++ web/src/components/MobileDrawer.vue | 54 ++++++++++++++++++++++++++++ web/src/components/TopBar.vue | 10 +++--- 4 files changed, 120 insertions(+), 5 deletions(-) diff --git a/web/e2e/game.spec.ts b/web/e2e/game.spec.ts index c1ae2a3..dce4a90 100644 --- a/web/e2e/game.spec.ts +++ b/web/e2e/game.spec.ts @@ -375,3 +375,59 @@ test.describe('Mobile viewport', () => { await expect(page.getByRole('button', { name: 'Close menu' })).toBeHidden() }) }) + +/* ------------------------------------------------------------------ */ +/* Mobile toolbar overflow fix */ +/* ------------------------------------------------------------------ */ + +test.describe('Mobile toolbar overflow fix', () => { + test.use({ viewport: { width: 390, height: 844 } }) + + test.beforeEach(async ({ page }) => { + await page.goto(APP_URL) + await expect(page.locator('.loading-spinner')).toBeVisible({ + timeout: 10_000, + }) + await injectMockGameState(page) + }) + + test('Quicksave, Quickload, Save, Load, Help are hidden in toolbar on mobile', async ({ + page, + }) => { + await expect( + page.getByRole('button', { name: /Quicksave/i }).first(), + ).toBeHidden() + await expect( + page.getByRole('button', { name: /Quickload/i }).first(), + ).toBeHidden() + await expect( + page.getByRole('button', { name: /^💾 Save$/i }), + ).toBeHidden() + await expect( + page.getByRole('button', { name: /^📂 Load$/i }), + ).toBeHidden() + await expect( + page.getByRole('button', { name: /^❓ Help$/i }), + ).toBeHidden() + }) + + test('drawer shows Quicksave, Quickload, Save, Load, Help action chips', async ({ + page, + }) => { + await page.getByRole('button', { name: 'Open sidebar' }).click() + const drawer = page.getByTestId('mobile-drawer-panel') + await expect(drawer.getByRole('button', { name: /Quicksave/i })).toBeVisible() + await expect(drawer.getByRole('button', { name: /Quickload/i })).toBeVisible() + await expect(drawer.getByRole('button', { name: /Save/i }).first()).toBeVisible() + await expect(drawer.getByRole('button', { name: /Load/i }).first()).toBeVisible() + await expect(drawer.getByRole('button', { name: /Help/i }).first()).toBeVisible() + }) + + test('tapping a drawer action chip closes the drawer', async ({ page }) => { + await page.getByRole('button', { name: 'Open sidebar' }).click() + const drawer = page.getByTestId('mobile-drawer-panel') + await expect(drawer).toBeVisible() + await drawer.getByRole('button', { name: /Quicksave/i }).click() + await expect(drawer).toBeHidden() + }) +}) diff --git a/web/src/components/GameLayout.vue b/web/src/components/GameLayout.vue index 9462ca6..e8cd3cd 100644 --- a/web/src/components/GameLayout.vue +++ b/web/src/components/GameLayout.vue @@ -256,6 +256,11 @@ function closeMenus() { @close="showDrawer = false" @inventory-click="onInventoryClick" @spell-click="onSpellClick" + @quick-save="store.saveGame()" + @quick-load="store.loadGame()" + @save="onOpenSaveDialog" + @load="onOpenLoadDialog" + @help="onHelp" /> () @@ -50,6 +55,55 @@ defineEmits<{ + +
+ + + + + +
+
diff --git a/web/src/components/TopBar.vue b/web/src/components/TopBar.vue index 4642a5d..a74fc31 100644 --- a/web/src/components/TopBar.vue +++ b/web/src/components/TopBar.vue @@ -27,25 +27,25 @@ defineEmits<{