docs(plan-2.5): closure — RFC supersession, BACKLOG landing, applyDomAttributes dedupe
Plan 2.5 final phase (P8). Closes the PrimeVue shell-migration workstream. - RFC-WS-PRIMEVUE-PLAN-2-5: added Supersessions section recording the governing-RFC divergences (§4 dark mode `<body>`→`<html>` per AD-2.5-D1; §7.4 workspace sub option A reversed to placeholder after visual review). Added closure summary (phases, ADs, brand-square recipe, suite delta, lessons). Status → COMPLETE. - BACKLOG: landed 8 items surfaced during Plan 2.5 (MOBILE-SHELL-PARITY, WORKSPACE-DROPDOWN-SUB-CONTENT, DENSITY-AWARE-SPACING, TOPBAR-H-VAR- DECLARE, CSP-FONT-SRC-LOCKDOWN, AUTO-IMPORTS-V2-SCAN, PNPM-RESOLUTIONS- ROOT, SHELLUI-STALE-DATA-THEME-CLEANUP). Marked GRADIENT-BRAND- ALIGNMENT as resolved. - useShellUiStore.toggleDensity: removed redundant applyDomAttributes() call (the AppShellV2 watch already covers density changes). Moved the DOM-write assertion to AppShellV2 watcher-coverage specs. Plan 2.5 status: COMPLETE. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -59,20 +59,19 @@ describe('useShellUiStore', () => {
|
||||
})
|
||||
|
||||
// Plan 2.5 P6 Fix 10 — toggleDensity is the binary UI flip exposed by
|
||||
// the topbar density button. Calls applyDomAttributes() so the
|
||||
// <html data-density> attribute mutates synchronously (consumers
|
||||
// like the AppShellV2 watcher would also fire, but the synchronous
|
||||
// contract is what topbar callers rely on).
|
||||
it('toggleDensity flips comfortable ⇔ compact and writes data-density', () => {
|
||||
// the topbar density button. Plan 2.5 P8 dedupe: it is now a pure state
|
||||
// mutation — the <html data-density> write is owned solely by AppShellV2's
|
||||
// watch([theme, density]) (the single applyDomAttributes() authority).
|
||||
// The watcher-coverage assertion lives in
|
||||
// tests/component/layouts/AppShellV2.spec.ts; here we assert the flip only.
|
||||
it('toggleDensity flips comfortable ⇔ compact', () => {
|
||||
const s = useShellUiStore()
|
||||
|
||||
expect(s.density).toBe('comfortable')
|
||||
s.toggleDensity()
|
||||
expect(s.density).toBe('compact')
|
||||
expect(document.documentElement.getAttribute('data-density')).toBe('compact')
|
||||
s.toggleDensity()
|
||||
expect(s.density).toBe('comfortable')
|
||||
expect(document.documentElement.getAttribute('data-density')).toBe('comfortable')
|
||||
})
|
||||
|
||||
it('toggleDensity from compact returns to comfortable on the second call', () => {
|
||||
|
||||
@@ -46,14 +46,18 @@ export const useShellUiStore = defineStore('shellUi', () => {
|
||||
|
||||
/**
|
||||
* Plan 2.5 P6 Fix 10: binary UI toggle between the two density extremes
|
||||
* the topbar exposes. Calls applyDomAttributes() so consumers (PrimeVue
|
||||
* Aura preset + Tailwind component styles) react to the new
|
||||
* <html data-density> immediately, mirroring how AppShellV2's watcher
|
||||
* would update on a setDensity() call but synchronously.
|
||||
* the topbar exposes. Pure state mutation — the <html data-density> write
|
||||
* is owned by AppShellV2's `watch([theme, density])`, the single
|
||||
* applyDomAttributes() authority. Mirrors setTheme(), which has always
|
||||
* relied on that same watcher.
|
||||
*
|
||||
* Plan 2.5 P8 dedupe: this previously also called applyDomAttributes()
|
||||
* directly, so a density toggle fired it twice (idempotent but redundant —
|
||||
* once here, once via the watch). The direct call was removed; the watch
|
||||
* still covers the change.
|
||||
*/
|
||||
function toggleDensity(): void {
|
||||
density.value = density.value === 'compact' ? 'comfortable' : 'compact'
|
||||
applyDomAttributes()
|
||||
}
|
||||
|
||||
function applyDomAttributes(): void {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { createPinia } from 'pinia'
|
||||
import AppShellV2 from '@/layouts/components/AppShellV2.vue'
|
||||
@@ -31,4 +31,40 @@ describe('AppShellV2 (skeleton)', () => {
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('[data-testid="appshell-v2"]').classes()).toContain('is-collapsed')
|
||||
})
|
||||
|
||||
// Plan 2.5 P8 dedupe — AppShellV2's watch([theme, density]) is the single
|
||||
// applyDomAttributes() authority. toggleDensity() (useShellUiStore) no longer
|
||||
// writes the DOM itself; the watch covers it. These specs lock that contract:
|
||||
// the density change reaches <html> via the watch, and exactly one
|
||||
// applyDomAttributes() call fires per toggle (no redundant double-call).
|
||||
it('watch writes the toggled density to <html data-density>', async () => {
|
||||
document.documentElement.removeAttribute('data-density')
|
||||
|
||||
const pinia = createPinia()
|
||||
const wrapper = mount(AppShellV2, { global: { plugins: [pinia] } })
|
||||
const { useShellUiStore } = await import('@/stores/useShellUiStore')
|
||||
const shell = useShellUiStore()
|
||||
|
||||
// onMounted seeded the comfortable default.
|
||||
expect(document.documentElement.getAttribute('data-density')).toBe('comfortable')
|
||||
|
||||
shell.toggleDensity()
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(document.documentElement.getAttribute('data-density')).toBe('compact')
|
||||
})
|
||||
|
||||
it('density toggle fires applyDomAttributes exactly once (via the watch, not toggleDensity)', async () => {
|
||||
const pinia = createPinia()
|
||||
const wrapper = mount(AppShellV2, { global: { plugins: [pinia] } })
|
||||
const { useShellUiStore } = await import('@/stores/useShellUiStore')
|
||||
const shell = useShellUiStore()
|
||||
|
||||
// Spy AFTER mount so the onMounted seed call is excluded from the count.
|
||||
const spy = vi.spyOn(shell, 'applyDomAttributes')
|
||||
|
||||
shell.toggleDensity()
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
expect(spy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user