Files
HMDesign/packages/sw/tests/storybook-simplification.browser.mjs
T
shkim4 454472d344 docs(sw): AI rules, example E, tests and evidence for the popup / footer / header changes
AI documents (packages/sw/ai)
- SWPopupPropertyRow, SWSelect, SWSlider in the catalog, usage table,
  composition patterns and design rules; SWViewerSlider noted as SWSlider panel.
- Rule: the SWHeader layer / color dropdowns are header-only; popup layer /
  color / line style use SWPopupPropertyRow (property window pickers).
- Footer rules: gra-border-orange surfaces, Model / Layout text unchanged on
  hover, side button hover, vacant surface limited to the slot row.
- Header rules: measured More growing behavior (hysteresis fix A).
- Example E (custom popup) uses SWPopupPropertyRow, SWSelect and SWSlider
  instead of SWHeaderLayerMenu in the popup.

Tests (packages/sw/tests)
- New footer-popup-viewer-refinement.browser.mjs (47 checks: footer stories,
  hover / gradient / tab text / pocket, popup pickers vs property window rows,
  SWSlider, unit SWSelect parity, Popup B, header trace, viewer title art).
- integrated-popup / integrated-consumer: popup pickers and SWSelect instead of
  the header dropdown and the HM UI Select (same overlay / callback / Escape
  requirements).
- integrated-improvement: default sections skip the retired SW/Tool story
  ('tool' still runs by name); storybook-inventory / simplification record the
  retired footer IDs; theme-inheritance widths use sw-footer--full-footer.

Docs (docs/sw)
- sw-footer-popup-viewer-refinement-2026-10-20.md: causes, decisions (the
  first-pass "header dropdown in popup" change is marked withdrawn),
  verification and baseline failures; evidence footer-popup-viewer-refinement-qa
  (47/47). Footer mapping document updated for the removed sections.

Baseline failures (unchanged by this work): home-menu-integration 1,
storybook-unused-removal 5 (10-19 snapshot comparisons), range-cross-browser and
property-panel-comparison open stories retired on 2026-10-19.
2026-10-20 15:20:27 +09:00

67 lines
6.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 2026-10-20 SW Storybook simplification: sidebar order, hidden verification Stories, removed IDs, direct URL render of
// every SW Story and the moved Property Panel (SW/Popup, ID sw-header--property-panel-compare).
// Usage: build Storybook first, then node packages/sw/tests/storybook-simplification.browser.mjs [evidence-dir]
import { createRequire } from 'node:module'
import { readFile, writeFile, mkdir } from 'node:fs/promises'
import path from 'node:path'
import http from 'node:http'
const runtime = process.env.SW_RUNTIME_MODULES ?? 'C:/Users/user/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules'
const require = createRequire(path.join(runtime, 'index.js'))
const { chromium } = require('playwright')
const out = path.resolve(process.argv[2] ?? 'docs/sw/storybook-simplification-qa'); await mkdir(out, { recursive: true })
const root = path.resolve('storybook-static'), mime = { '.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css', '.json': 'application/json', '.svg': 'image/svg+xml', '.png': 'image/png', '.ttf': 'font/ttf', '.woff2': 'font/woff2' }
const server = http.createServer(async (req, res) => { try { const p = new URL(req.url, 'http://localhost').pathname, file = path.resolve(root, '.' + decodeURIComponent(p === '/' ? '/index.html' : p)); res.setHeader('Content-Type', mime[path.extname(file)] ?? 'application/octet-stream'); res.end(await readFile(file)) } catch { res.writeHead(404).end() } })
await new Promise(r => server.listen(0, '127.0.0.1', r)); const base = `http://127.0.0.1:${server.address().port}`
const browser = await chromium.launch({ headless: true, executablePath: process.env.SW_CHROME_PATH ?? 'C:/Program Files/Google/Chrome/Application/chrome.exe' })
const checks = []; const check = (n, ok, d) => { checks.push({ n, ok, d }); console.log(ok ? 'PASS' : 'FAIL', n, ok ? '' : JSON.stringify(d)) }
const index = JSON.parse(await readFile(path.join(root, 'index.json'), 'utf8'))
const sw = Object.values(index.entries).filter(e => e.type === 'story' && e.title.startsWith('SW/'))
// 1. every SW story renders by direct URL without page errors
const page = await browser.newPage({ viewport: { width: 1920, height: 1000 } })
let errors = []; page.on('pageerror', e => errors.push(e.message)); page.on('console', m => { if (m.type() === 'error') errors.push(m.text()) })
const bad = []
for (const e of sw) {
errors = []
await page.goto(`${base}/iframe.html?id=${e.id}&viewMode=story`)
try { await page.locator('#storybook-root > *').first().waitFor({ timeout: 15000 }) } catch { bad.push([e.id, 'no root']) ; continue }
await page.waitForTimeout(150)
const err = await page.locator('.sb-show-errordisplay').count()
if (err || errors.length) bad.push([e.id, errors.slice(0, 2)])
}
check(`direct URL render: ${sw.length} SW Stories, no error display / page error`, bad.length === 0, bad)
// 2. removed IDs are gone
const removed = ['figma', 'capture-active', 'capture-outline', 'capture-recent', 'variants', 'recent-files'].map(id => `sw-header--home-menu-${id}`)
check('removed Home Menu IDs absent from index', removed.every(id => !index.entries[id]))
// 2026-10-20 Footer cleanup: retired Footer IDs are gone, the two main Footer Stories stay.
check('removed Footer IDs absent, full-footer / figma-variants kept', ['sw-footer--default', 'sw-footer--states'].every(id => !index.entries[id]) && ['sw-footer--full-footer', 'sw-footer--figma-variants'].every(id => index.entries[id]))
// 3. Property panel in Popup: four states
await page.goto(`${base}/iframe.html?id=sw-header--property-panel-compare&viewMode=story`)
await page.locator('[data-compare]').first().waitFor()
await page.waitForTimeout(300)
const cells = await page.locator('[data-compare]').evaluateAll(list => list.map(c => { const r = c.querySelector('.hm-sw-property').getBoundingClientRect(); return [c.dataset.compare, Math.round(r.width), Math.round(r.height)] }))
check('Popup › Property Panel: four states none/all/circle/expanded at 314 × 256/361/658/754', JSON.stringify(cells) === JSON.stringify([['none', 314, 256], ['all', 314, 361], ['circle', 314, 658], ['expanded', 314, 754]]), cells)
await page.screenshot({ path: path.join(out, 'popup-property-panel-compare.png'), fullPage: true })
// 4. manager sidebar
const m = await browser.newPage({ viewport: { width: 1600, height: 2400 } })
await m.goto(`${base}/?path=/story/sw-header--overview`)
await m.locator('[data-item-id="sw-header--overview"]').waitFor()
for (const c of ['sw-footer', 'sw-popup', 'sw-viewer', 'sw-theme']) { const b = m.locator(`[data-item-id="${c}"] > button[aria-expanded="false"]`); if (await b.count()) await b.click() }
await m.waitForTimeout(500)
const rows = await m.locator('#storybook-explorer-tree [data-item-id^="sw-"]').evaluateAll(list => list.map(e => [e.dataset.itemId, e.dataset.nodetype, e.textContent.replace('Skip to content', '').trim()]))
await writeFile(path.join(out, 'sidebar.json'), JSON.stringify(rows, null, 1))
const comps = rows.filter(r => r[1] === 'component').map(r => r[2])
check('sidebar SW order Header → Footer → Popup → Viewer → Theme', JSON.stringify(comps) === JSON.stringify(['Header', 'Footer', 'Popup', 'Viewer', 'Theme']), comps)
const hidden = ['sw-header--window-move-motion', 'sw-header--k-guide', 'sw-footer--figma-variants', 'sw-header--detail-buttons', 'sw-header--home-menu-capture-default']
check('verification Stories not in sidebar', rows.every(r => !hidden.includes(r[0])), rows.filter(r => hidden.includes(r[0])))
const popupRows = await m.locator('#storybook-explorer-tree [data-item-id]').evaluateAll(list => list.map(e => e.dataset.itemId))
const pi = popupRows.indexOf('sw-popup')
check('Popup children order: Custom Content, Multiple Popups, Property Panel', JSON.stringify(popupRows.slice(pi + 1, pi + 4)) === JSON.stringify(['sw-popup--custom-content', 'sw-popup--multiple-popups', 'sw-header--property-panel-compare']), popupRows.slice(pi + 1, pi + 4))
const headerIds = rows.filter(r => r[0].startsWith('sw-header--')).map(r => r[0])
check('Property Panel listed once (under Popup only)', headerIds.filter(id => id === 'sw-header--property-panel-compare').length === 1)
await m.locator('[data-item-id="sw-header--property-panel-compare"]').click(); await m.waitForTimeout(800)
check('sidebar link opens moved Story with same ID', m.url().includes('sw-header--property-panel-compare'), m.url())
await m.screenshot({ path: path.join(out, 'sidebar.png'), clip: { x: 0, y: 0, width: 320, height: 2400 } })
await writeFile(path.join(out, 'result.json'), JSON.stringify(checks, null, 1))
console.log(checks.filter(c => c.ok).length + '/' + checks.length + ' PASS')
await browser.close(); server.close()