style(storybook): tidy CENTER, chart, org chart and SW story pages

- CENTER plain page layout and shorter reference demo notes (spec values
  moved to the CENTER README); chart specimen descriptions trimmed
- Org chart page names and previews; SW width rows follow the 30px frame
  margin (1980 / 1065 / 727px) and the SW tests match the current layout
- Story inventory regenerated and WORKING_CONTEXT updated
This commit is contained in:
shkim4 committed 2026-11-13 17:58:01 +09:00
1 parent bad2acfc49
commit ed7e8790ec
40 files changed
+1525 -333

No files matched your search

@@ -6,7 +6,7 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises'
import { execFileSync } from 'node:child_process'
import path from 'node:path'
import http from 'node:http'
import { swStoryEntries, retiredPropertyPanelIds, retiredUnusedDemoIds, approvedAddedStoryIds, headerInventoryIssues } from './storybook-inventory.mjs'
import { swStoryEntries, retiredPropertyPanelIds, headerInventoryIssues, swScopeIssues } from './storybook-inventory.mjs'
import { loadPlaywright } from './playwright-runtime.mjs'
// Property panel comparison consolidation (2026-10-19).
@@ -39,7 +39,7 @@ const geometry = () => page.evaluate(() => {
const r = e => { const b = e.getBoundingClientRect(); return { x: b.x, y: b.y, w: b.width, h: b.height, right: b.right, bottom: b.bottom } }
const scroller = document.querySelector('[data-compare-scroll]')
return {
cells: [...document.querySelectorAll('[data-compare]')].map(c => ({ id: c.dataset.compare, cell: r(c), panel: r(c.querySelector('.hm-sw-property')), caption: c.querySelector('figcaption strong')?.textContent })),
cells: [...document.querySelectorAll('[data-compare]')].map(c => ({ id: c.dataset.compare, cell: r(c), panel: r(c.querySelector('.hm-sw-property')), caption: c.querySelector('figcaption strong')?.textContent ?? c.getAttribute('aria-label') })),
scroller: scroller && { ...r(scroller), scrollW: scroller.scrollWidth, clientW: scroller.clientWidth, scrollH: scroller.scrollHeight, clientH: scroller.clientHeight },
doc: { scrollW: document.documentElement.scrollWidth, clientW: document.documentElement.clientWidth, scrollH: document.documentElement.scrollHeight, clientH: document.documentElement.clientHeight },
}
@@ -70,12 +70,15 @@ try {
// approved additions, the verification tags and the Popup move).
const recorded = JSON.parse(execFileSync('git', ['show', 'c8e5fbeebc26fba92998fc5ddcd28c6c110e452d:docs/sw/property-panel-comparison-qa/verify/result.json'], { encoding: 'utf8', maxBuffer: 1 << 26 })).data.inventory
const now = headerInventoryIssues(before.sw, index)
// 2026-11-13 (user request): the former whole-Storybook count (338 - retired + approved SW additions) failed whenever a
// Story outside SW was added; the SW Story set is checked instead (swScopeIssues: no unapproved SW addition, deletion or ID change).
const swScope = swScopeIssues(before.sw, index)
report.data.inventory.recorded = { total: recorded.total, sw: recorded.sw }
check('inventory: 24 -> 19 SW Stories, 343 -> 338 total, only 5 approved retirements (recorded at c8e5fbe); now only approved later changes', before.sw.length === 24 && recorded.sw === 19 && before.total === 343 && recorded.total === 338 &&
JSON.stringify(recorded.ids.map(([id]) => id).sort()) === JSON.stringify(expected.map(e => e.id).sort()) && retiredPropertyPanelIds.every(id => !index.entries[id]) &&
stories.length === 338 - retiredUnusedDemoIds.length + approvedAddedStoryIds.length && now.unexpected.length === 0 && now.missing.length === 0 && now.retiredPresent.length === 0, { before: [before.total, before.sw.length], recorded: [recorded.total, recorded.sw], after: [stories.length, sw.length], ...now })
swScope.ok && now.unexpected.length === 0 && now.missing.length === 0 && now.retiredPresent.length === 0, { before: [before.total, before.sw.length], recorded: [recorded.total, recorded.sw], after: [stories.length, sw.length], ...now, swScope })
check('inventory: retained SW Story IDs, names, titles, tags unchanged (recorded at c8e5fbe); now only the approved tags and Popup move', JSON.stringify(recorded.ids) === JSON.stringify(expected.map(e => [e.id, e.name])) && now.changed.length === 0, { retained: sw.length, changed: now.changed })
check('inventory: compare Story keeps its ID and name', index.entries['sw-header--property-panel-compare']?.name === 'Property Panel')
check('inventory: compare Story keeps its ID and name', index.entries['sw-header--property-panel-compare']?.name === '속성창')
check('inventory: Dropdown Story keeps ID / name / title', index.entries['sw-header--composed-controls']?.name === 'Dropdown' && index.entries['sw-header--composed-controls']?.title === 'SW/Header')
// ---- Layout at four widths
@@ -93,7 +96,8 @@ try {
// 2026-10-26 (user request): from 1080px, 3 columns — none/all stacked in column 1, circle and expanded beside them.
// 2026-11-05: the Story page Inner keeps 60px on each side (story-page.css), so the steps moved to 787px / 1125px
// (sw-stories.css .sw-story-compare--grid). It keeps 16px up to 480px (growing to 60px at 768px), so 390px has 358px.
const oneColumn = width < 787, threeColumns = width >= 1125
// 2026-11-13: the side margin maximum is 30px (story-page.css), so sw-stories.css moved the steps to 727px / 1065px.
const oneColumn = width < 727, threeColumns = width >= 1065
if (threeColumns) {
const gaps = { stack: b.cell.y - a.cell.bottom, col1: c.cell.x - Math.max(a.cell.right, b.cell.right), col2: d.cell.x - c.cell.right }
report.data[`gaps${width}`] = gaps
@@ -141,21 +145,31 @@ try {
report.data[`drop-${id}`] = info
check(`Dropdown ${id}: opens, has items, fully inside the viewport`, info.items > 0 && info.x >= 0 && info.y >= 0 && info.right <= info.vw + 0.5 && info.bottom <= info.vh + 0.5, info)
await page.screenshot({ path: path.join(out, `dropdown-${id}.png`) })
if (id === 'expanded') {
// Scroll the page while open (2026-10-22: no Inner scroller): the fixed overlay must stay attached to its field.
const moved = await page.evaluate(() => { const t = scrollY; scrollBy(0, 120); return scrollY - t })
await page.waitForTimeout(150)
const after = await page.evaluate(() => {
const d = document.querySelector('.hm-sw-property__drop')?.getBoundingClientRect()
const f = document.querySelector('[data-compare="expanded"] [data-property-id="layer"] [aria-expanded="true"]')?.getBoundingClientRect()
return d && f ? { dTop: d.top, dBottom: d.bottom, fTop: f.top, fBottom: f.bottom } : null
})
report.data.dropScroll = { moved, before: info, after }
check('Dropdown follows the page scroll (stays attached to its field)', moved > 0 && after && (Math.abs(after.dTop - after.fBottom) < 1.5 || Math.abs(after.dBottom - after.fTop) < 1.5), report.data.dropScroll)
}
await page.keyboard.press('Escape')
check(`Dropdown ${id}: Escape closes and returns focus to its field`, await page.locator('.hm-sw-property__drop').count() === 0 && await trigger.evaluate(e => document.activeElement === e || e.contains(document.activeElement)))
}
// Scroll the page while open (2026-10-22: no Inner scroller): the fixed overlay must stay attached to its field.
// 2026-11-13: at 1440 × 900 the page (3-column layout since 2026-10-26) fits the viewport and cannot scroll, and the Story
// preview sets `html { scroll-behavior: smooth }` (src/hm-theme.css), so scrollY read right after scrollBy stayed 0. The
// check runs at 1440 × 600, where the page scrolls, with an instant scroll; the viewport and scroll position are restored.
{
const size = page.viewportSize()
await page.setViewportSize({ width: size.width, height: 600 })
await page.evaluate(() => scrollTo({ top: 0, behavior: 'instant' }))
const { trigger, info } = await openPicker('expanded')
const moved = await page.evaluate(() => { const t = scrollY; scrollBy({ top: 120, behavior: 'instant' }); return scrollY - t })
await page.waitForTimeout(150)
const after = await page.evaluate(() => {
const d = document.querySelector('.hm-sw-property__drop')?.getBoundingClientRect()
const f = document.querySelector('[data-compare="expanded"] [data-property-id="layer"] [aria-expanded="true"]')?.getBoundingClientRect()
return d && f ? { dTop: d.top, dBottom: d.bottom, fTop: f.top, fBottom: f.bottom } : null
})
report.data.dropScroll = { viewport: { width: size.width, height: 600 }, moved, before: info, after }
check('Dropdown follows the page scroll (stays attached to its field)', moved > 0 && after && (Math.abs(after.dTop - after.fBottom) < 1.5 || Math.abs(after.dBottom - after.fTop) < 1.5), report.data.dropScroll)
await page.keyboard.press('Escape')
await trigger.evaluate(() => scrollTo({ top: 0, behavior: 'instant' }))
await page.setViewportSize(size)
}
// Select a layer value: callback updates only that panel.
{
const { drop } = await openPicker('all')