diff --git a/frontend/e2e/mobile-ui.spec.ts b/frontend/e2e/mobile-ui.spec.ts index bf2134b..5ea87cb 100644 --- a/frontend/e2e/mobile-ui.spec.ts +++ b/frontend/e2e/mobile-ui.spec.ts @@ -200,16 +200,21 @@ test('all bottom destinations expose one active page and desktop layout stays un expect(desktopBottomGap).toBe(84) }) -test('calendar week focus keeps seven usable day controls without page overflow', async ({ page }) => { +test('calendar week focus and sheets keep the approved responsive geometry', async ({ page }) => { + const now = new Date() + const startsAt = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 9).toISOString() + const endsAt = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 10).toISOString() await page.route('**/api/v1/calendar-subscriptions', route => route.fulfill({ json: [{ id:'calendar-source', name:'工作', url:'https://example.com/work.ics', color:'#f15a29', enabled:true, refreshed_at:null, last_error:null, stale:false }] })) - await page.route('**/api/v1/calendar-events?*', route => route.fulfill({ json: { events: [], sources: [] } })) + await page.route('**/api/v1/calendar-events?*', route => route.fulfill({ json: { events: [{ id:'calendar-event', title:'晨会', starts_at:startsAt, ends_at:endsAt, all_day:false, source_id:'calendar-source', source_name:'工作', color:'#f15a29', description:'同步本周事项', location:'会议室' }], sources: [] } })) await page.goto('/') - const mobile = (await page.viewportSize())!.width <= 930 + const viewport = page.viewportSize()! + const mobile = viewport.width <= 930 if (mobile) await bottomTab(page, '日历订阅').click() else await page.locator('.primary-nav').getByRole('button', { name: '日历订阅', exact: true }).click() const strip = page.getByRole('group', { name: '选择日期' }) await expect(strip).toBeVisible() await expect(strip.locator('.calendar-week-day')).toHaveCount(7) + await expect(page.locator('.calendar-heading h1')).toHaveCount(0) const metrics = await strip.evaluate(element => ({ documentOverflow: document.documentElement.scrollWidth - document.documentElement.clientWidth, stripOverflow: element.scrollWidth > element.clientWidth, @@ -222,7 +227,38 @@ test('calendar week focus keeps seven usable day controls without page overflow' return { background: style.backgroundColor, color: style.color } }) expect(selectedColors).toEqual({ background: 'rgb(241, 90, 41)', color: 'rgb(255, 255, 255)' }) - if ((await page.viewportSize())!.width <= 390) expect(metrics.stripOverflow).toBeTruthy() + if (viewport.width <= 375) expect(metrics.stripOverflow).toBeTruthy() + + const shell = page.locator('.shell') + const assertSheetGeometry = async (sheet: ReturnType) => { + await expect(sheet).toBeVisible() + const box = await sheet.boundingBox() + expect(box).not.toBeNull() + expect(await page.evaluate(() => document.documentElement.scrollWidth - innerWidth)).toBe(0) + if (mobile) { + expect(box!.x).toBeCloseTo(0, 0) + expect(box!.width).toBeCloseTo(viewport.width, 0) + const innerOverflow = await sheet.evaluate(element => ({ clientWidth: element.clientWidth, scrollWidth: element.scrollWidth, childrenInside: [...element.querySelectorAll('.app-sheet__header,.app-sheet__body,.app-sheet__footer')].every(child => child.getBoundingClientRect().right <= element.getBoundingClientRect().right + 1) })) + expect(innerOverflow.scrollWidth).toBeLessThanOrEqual(innerOverflow.clientWidth) + expect(innerOverflow.childrenInside).toBeTruthy() + } else { + await expect(shell).toHaveClass(/detail-open/) + expect(box!.width).toBeCloseTo(350, 0) + expect(box!.x + box!.width).toBeCloseTo(viewport.width, 0) + expect(box!.height).toBeCloseTo(viewport.height, 0) + } + } + + await page.getByRole('button', { name: '管理日历源' }).click() + await page.getByRole('button', { name: '添加日历订阅' }).click() + const form = page.getByRole('dialog', { name: '添加日历源' }) + await assertSheetGeometry(form) + await form.getByRole('button', { name: '关闭订阅表单' }).click() + await page.getByRole('button', { name: '关闭日历源' }).click() + + await page.locator('[data-event-id="calendar-event"]').click() + const detail = page.getByRole('dialog', { name: '晨会' }) + await assertSheetGeometry(detail) }) test('settings match the approved paper-ledger geometry and action hierarchy', async ({ page }) => { diff --git a/frontend/src/App.vue b/frontend/src/App.vue index b8babdb..dcf1bbf 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -183,6 +183,7 @@ const memoPanel = ref | null>(null) const memoTrash = ref(false) const memoDetailOpen = ref(false) const habitDetailOpen = ref(false) +const calendarDetailOpen = ref(false) const compactLayout = ref(desktopViewportWidth.value <= 930) const memoShellState = computed(() => deriveMemoShellState({ view: activeView.value, detailOpen: memoDetailOpen.value, compact: compactLayout.value, trash: memoTrash.value })) const memoBackgroundInert = computed(() => memoShellState.value.backgroundInert) @@ -1604,7 +1605,7 @@ onUnmounted(() => {

登录后继续你的清单

-
+