fix(frontend): align ref menu structure

This commit is contained in:
2026-07-30 19:58:38 +00:00
parent b17843b609
commit 2455c4efb3
4 changed files with 83 additions and 22 deletions
@@ -288,6 +288,17 @@ test('desktop menus preserve ref columns, prefix-safe routes, and controlled dro
await expect(page.locator('.main-mobile-bottom')).toBeHidden();
await expect(page.locator('.layout-desktop')).toBeVisible();
await expect(page.locator('.layout-mobile')).toHaveCount(0);
const contentOrder = await page
.locator('.record-zone, [data-menu-position="middle"], .desktop-message-panel, [data-menu-position="bottom"]')
.evaluateAll((elements) =>
elements.map((element) => {
if (element.classList.contains('record-zone')) return 'records';
if (element.getAttribute('data-menu-position') === 'middle') return 'middle-menu';
if (element.classList.contains('desktop-message-panel')) return 'messages';
return 'bottom-menu';
})
);
expect(contentOrder).toEqual(['records', 'middle-menu', 'messages', 'bottom-menu']);
const global = page.locator('.main-global-menu').first();
await expect(global.locator('[data-navigation-id="nation-betting"]')).toHaveAttribute(
@@ -383,8 +394,30 @@ test('the 939/940 boundary and 500px bottom bar match the ref responsive contrac
expect(popupStyle.overflowY).toBe('auto');
expect(popupStyle.maxHeight).toBe('850px');
expect(popupStyle.bottom).toBe('47px');
const globalPopup = page.locator('#mobile-global-menu');
await expect(globalPopup.getByText('게시판', { exact: true })).toHaveCount(1);
await expect(globalPopup.getByText('공식 오픈 톡', { exact: true })).toHaveCount(1);
await expect(globalPopup.getByText('게임정보', { exact: true })).toHaveCount(1);
await expect(globalPopup.getByText('기타 정보', { exact: true })).toHaveCount(1);
await persistArtifact(page, `${basePath.slice(1)}-mobile-global-open-500`);
await page.keyboard.press('Escape');
await expect(externalTrigger).toBeFocused();
await page.locator('[data-bottom-menu="nation"]').click();
const nationPopup = page.locator('#mobile-nation-menu');
await expect(nationPopup.getByText('금/쌀 경매장', { exact: true })).toHaveCount(1);
await expect(nationPopup.getByText('유니크 경매장', { exact: true })).toHaveCount(1);
await page.keyboard.press('Escape');
await page.locator('[data-bottom-menu="quick"]').click();
const quickPopup = page.locator('#mobile-quick-menu');
for (const heading of ['국가 정보', '동향 정보', '메시지']) {
await expect(quickPopup.locator('.bottom-heading').getByText(heading, { exact: true })).toHaveCount(1);
}
await expect(quickPopup.locator('.bottom-heading')).toHaveCount(3);
await expect(quickPopup.locator('.bottom-divider')).toHaveCount(3);
await persistArtifact(page, `${basePath.slice(1)}-mobile-quick-open-500`);
await page.keyboard.press('Escape');
await persistArtifact(page, `${basePath.slice(1)}-mobile-500`);
});
@@ -66,21 +66,33 @@ const onQuick = (item: QuickNavigationItem) => {
@navigate="close()"
/>
</li>
<template v-else>
<template v-else-if="entry.kind === 'group'">
<li class="bottom-heading" role="presentation">
{{ entry.kind === 'group' ? entry.label : entry.main.label }}
{{ entry.label }}
</li>
<template v-if="entry.kind === 'split'">
<li role="none">
<template v-for="item in entry.items" :key="item.id">
<li v-if="item.kind === 'divider'" class="bottom-divider" role="separator"></li>
<li v-else role="none">
<MainNavigationLink
:link="entry.main"
:enabled="isNavigationConfigured(entry.main)"
:link="item"
:enabled="isNavigationConfigured(item)"
compact
role="menuitem"
@navigate="close()"
/>
</li>
</template>
</template>
<template v-else-if="entry.kind === 'split'">
<li role="none">
<MainNavigationLink
:link="entry.main"
:enabled="isNavigationConfigured(entry.main)"
compact
role="menuitem"
@navigate="close()"
/>
</li>
<template v-for="item in entry.items" :key="item.id">
<li v-if="item.kind === 'divider'" class="bottom-divider" role="separator"></li>
<li v-else role="none">
@@ -123,15 +135,6 @@ const onQuick = (item: QuickNavigationItem) => {
/>
</li>
<template v-else-if="entry.kind === 'split'">
<li role="none">
<MainNavigationLink
:link="entry.main"
:enabled="isNationNavigationEnabled(entry.main, access)"
compact
role="menuitem"
@navigate="close()"
/>
</li>
<li v-for="item in entry.items" :key="item.id" role="none">
<template v-if="item.kind === 'link'">
<MainNavigationLink
@@ -162,7 +165,10 @@ const onQuick = (item: QuickNavigationItem) => {
</button>
<ul v-show="openId === 'quick'" id="mobile-quick-menu" class="bottom-popup" role="menu">
<template v-for="item in quickNavigation" :key="item.id">
<li v-if="'kind' in item" class="bottom-divider" role="separator"></li>
<template v-if="'kind' in item">
<li class="bottom-heading" role="presentation">{{ item.label }}</li>
<li class="bottom-divider" role="separator"></li>
</template>
<li v-else role="none">
<button
class="quick-link"
@@ -17,6 +17,7 @@ export type MainNavigationLink = {
export interface MainNavigationDivider {
kind: 'divider';
id: string;
label?: string;
}
export interface MainNavigationGroup {
@@ -317,18 +318,18 @@ export const nationNavigation: MainNavigationEntry[] = [
];
export const quickNavigation: Array<QuickNavigationItem | MainNavigationDivider> = [
{ kind: 'divider', id: 'quick-nation-heading' },
{ kind: 'divider', id: 'quick-nation-heading', label: '국가 정보' },
{ id: 'policy', label: '방침', tab: 'map', selector: '[data-main-target="policy"]' },
{ id: 'commands', label: '명령', tab: 'commands', selector: '[data-main-target="commands"]' },
{ id: 'nation', label: '국가', tab: 'status', selector: '[data-main-target="nation"]' },
{ id: 'general', label: '장수', tab: 'status', selector: '[data-main-target="general"]' },
{ id: 'city', label: '도시', tab: 'status', selector: '[data-main-target="city"]' },
{ kind: 'divider', id: 'quick-record-heading' },
{ kind: 'divider', id: 'quick-record-heading', label: '동향 정보' },
{ id: 'map', label: '지도', tab: 'map', selector: '[data-main-target="map"]' },
{ id: 'global-records', label: '동향', tab: 'world', selector: '[data-main-target="global-records"]' },
{ id: 'general-records', label: '개인', tab: 'world', selector: '[data-main-target="general-records"]' },
{ id: 'world-history', label: '정세', tab: 'world', selector: '[data-main-target="world-history"]' },
{ kind: 'divider', id: 'quick-message-heading' },
{ kind: 'divider', id: 'quick-message-heading', label: '메시지' },
{ id: 'public-message', label: '전체', tab: 'messages', selector: '[data-message-type="public"]' },
{ id: 'national-message', label: '국가', tab: 'messages', selector: '[data-message-type="national"]' },
{ id: 'private-message', label: '개인', tab: 'messages', selector: '[data-message-type="private"]' },
+24 -3
View File
@@ -143,7 +143,7 @@ watch(
<template>
<main class="game-shell main-page">
<MainGlobalMenu :npc-mode="npcMode" :vote-active="voteActive" />
<MainGlobalMenu data-menu-position="top" :npc-mode="npcMode" :vote-active="voteActive" />
<header class="game-shell__header">
<div>
@@ -280,6 +280,13 @@ watch(
</RecordPanel>
</div>
<MainGlobalMenu
class="common-menu-middle"
data-menu-position="middle"
:npc-mode="npcMode"
:vote-active="voteActive"
/>
<div v-if="mobileTab === 'messages'" class="mobile-panel">
<MessagePanel
class="mobile-message-panel"
@@ -383,6 +390,12 @@ watch(
</div>
</RecordPanel>
</section>
<MainGlobalMenu
class="common-menu-middle"
data-menu-position="middle"
:npc-mode="npcMode"
:vote-active="voteActive"
/>
<MessagePanel
class="desktop-message-panel"
:messages="messages"
@@ -405,8 +418,12 @@ watch(
/>
</section>
<MainGlobalMenu class="common-menu-repeat" :npc-mode="npcMode" :vote-active="voteActive" />
<MainGlobalMenu class="common-menu-repeat" :npc-mode="npcMode" :vote-active="voteActive" />
<MainGlobalMenu
class="common-menu-repeat"
data-menu-position="bottom"
:npc-mode="npcMode"
:vote-active="voteActive"
/>
<MainMobileBottomBar
:access="nationAccess"
@@ -515,6 +532,10 @@ button {
grid-column: 1 / -1;
}
.common-menu-middle {
grid-column: 1 / -1;
}
.record-zone {
grid-column: 1 / -1;
display: grid;