feat: refine archived habit management
This commit is contained in:
@@ -267,16 +267,56 @@ describe('mobile list row language', () => {
|
||||
expect(mvpPanel).toContain("habitDetailSheet.value?.focus()")
|
||||
})
|
||||
|
||||
it('provides a minimal archived-habit viewing path', () => {
|
||||
it('provides an archived-habit viewing path', () => {
|
||||
expect(mvpPanel).toContain("request('/habits?archived=true')")
|
||||
expect(mvpPanel).toContain("{{ archivedHabitsLoaded ? `已归档(${archivedHabits.length})` : '已归档' }}")
|
||||
expect(mvpPanel).toContain("archiveState === 'success' ? `已归档(${archivedHabits.length})` : '已归档'")
|
||||
expect(mvpPanel).toContain('class="archived-toggle habit-archive-toggle"')
|
||||
expect(mvpPanel).toContain('v-for="h in archivedHabits"')
|
||||
expect(css).toMatch(/@media\(max-width:930px\)\{\.habit-archive-toggle\{[^}]*width:100%;[^}]*min-height:44px;[^}]*appearance:none;[^}]*background:var\(--surface-raised\);[^}]*border:1px solid var\(--border-cream\)/)
|
||||
expect(mvpPanel).toContain('archiveFlags.list ? archivedHabits : []')
|
||||
expect(css).toMatch(/@media\(max-width:930px\)\{\.habit-archive-section\{[^}]*width:100%/)
|
||||
expect(css).toMatch(/\.archived-habits\{[^}]*width:100%;[^}]*display:grid/)
|
||||
expect(css).toMatch(/\.archived-habits>button\{[^}]*width:100%;[^}]*min-height:44px;[^}]*appearance:none;[^}]*grid-template-columns:minmax\(0,1fr\) auto;[^}]*background:var\(--surface-raised\);[^}]*border-bottom:1px solid var\(--border-cream\)/)
|
||||
expect(css).toMatch(/\.archived-habits>button span\{[^}]*min-width:0;[^}]*overflow:hidden;[^}]*text-overflow:ellipsis;[^}]*white-space:nowrap/)
|
||||
expect(css).toMatch(/\.archived-habits>button small\{[^}]*white-space:nowrap/)
|
||||
expect(css).toMatch(/\.archived-habit-row\{[^}]*width:100%;[^}]*min-height:44px;[^}]*grid-template-columns:minmax\(0,1fr\) auto/)
|
||||
expect(css).toMatch(/\.archived-habit-row b,\.archived-habit-row small\{[^}]*min-width:0;[^}]*overflow:hidden;[^}]*text-overflow:ellipsis;[^}]*white-space:nowrap/)
|
||||
})
|
||||
|
||||
it('renders a separate accessible archive section with explicit states and lifecycle actions', () => {
|
||||
const activeList = mvpPanel.slice(mvpPanel.indexOf('<div v-if="view === \'habits\'" class="habit-list">'), mvpPanel.indexOf('<section v-if="view === \'habits\'" class="habit-archive-section"'))
|
||||
const todayList = mvpPanel.slice(mvpPanel.indexOf('class="habit-list today-habit-list"'), mvpPanel.indexOf('<!-- 完整习惯列表 -->'))
|
||||
expect(activeList).not.toContain('habit-archive-toggle')
|
||||
expect(todayList).not.toContain('habit-archive-section')
|
||||
expect(mvpPanel).toContain('class="habit-archive-section"')
|
||||
expect(mvpPanel).toContain('aria-controls="archived-habits-panel"')
|
||||
expect(mvpPanel).toContain(':aria-expanded="showArchivedHabits"')
|
||||
expect(mvpPanel).toContain('id="archived-habits-panel"')
|
||||
expect(mvpPanel).toContain('archiveState === \'loading\'')
|
||||
expect(mvpPanel).toContain('archiveState === \'error\'')
|
||||
expect(mvpPanel).toContain('@click="loadArchivedHabits"')
|
||||
expect(mvpPanel).toContain('formatArchivedAt(h.archived_at)')
|
||||
expect(mvpPanel).toContain('<ChevronRight aria-hidden="true"/>')
|
||||
expect(mvpPanel).toContain('@click="restoreHabit(selectedHabit)"')
|
||||
expect(mvpPanel).toContain("method: 'POST'")
|
||||
expect(mvpPanel).toContain('archivedHabits.value = archivedHabits.value.filter((item) => item.id !== h.id)')
|
||||
expect(mvpPanel).toContain('恢复习惯')
|
||||
})
|
||||
|
||||
it('preserves archived detail after restore or purge failures and falls focus back to disclosure after removal', () => {
|
||||
const restoreBlock = mvpPanel.slice(mvpPanel.indexOf('async function restoreHabit'), mvpPanel.indexOf('async function deleteHabit'))
|
||||
const deleteBlock = mvpPanel.slice(mvpPanel.indexOf('async function deleteHabit'), mvpPanel.indexOf('async function loadArchivedHabits'))
|
||||
expect(restoreBlock).toContain('catch (reason)')
|
||||
expect(deleteBlock).toContain('catch (reason)')
|
||||
expect(restoreBlock).toContain('performHabitRestore({')
|
||||
expect(restoreBlock).toContain('invalidateHabitGridCache()')
|
||||
expect(restoreBlock).toContain("result.refreshed ? '习惯已恢复' : '习惯已恢复,但列表刷新失败,请重试'")
|
||||
expect(restoreBlock.indexOf('selectedHabit.value = null')).toBeGreaterThan(restoreBlock.indexOf("method: 'POST'"))
|
||||
expect(deleteBlock.indexOf('selectedHabit.value = null')).toBeGreaterThan(deleteBlock.indexOf("method: 'DELETE'"))
|
||||
expect(mvpPanel).toContain('habitArchiveToggle.value?.focus()')
|
||||
expect(mvpPanel).toContain('ref="habitArchiveToggle"')
|
||||
})
|
||||
|
||||
it('styles archive rows as 44px full-width desktop rows and a mobile bottom sheet detail', () => {
|
||||
expect(css).toMatch(/\.habit-archive-toggle\{[^}]*min-height:44px/)
|
||||
expect(css).toMatch(/\.archived-habit-row\{[^}]*width:100%;[^}]*min-height:44px;[^}]*grid-template-columns:minmax\(0,1fr\) auto/)
|
||||
expect(css).toContain('@media(max-width:930px){.habit-archive-section')
|
||||
expect(css).toContain('.habit-detail-sheet.app-sheet--detail{')
|
||||
})
|
||||
|
||||
it('reuses the habit composer for edits and patches only changed fields', () => {
|
||||
|
||||
Reference in New Issue
Block a user