docs(storybook): tidy SW Storybook gauges, responsive rows and page titles
Deploy Storybook / deploy (push) Successful in 1m11s

- SWPopup Custom Content and the SW Viewer options panel use SWSlider style B
  (appearance="property") only; the A/B comparison, the style select and the
  green panel gauge are gone (SWViewerSlider itself is unchanged).
- SW Header / Viewer / Footer Responsive More: the 1920px row shows only when the
  content Inner is 1920px or wider (container query); Footer rows fit the page
  width without scrollers so ▲ More handles narrow widths.
- Logo pages fit without scrollers; SW Header Logo Slot is "Logo" in the sidebar
  with the title "SW Header — 로고" (ID sw-header--logo-slot kept).
- Property Panel (was "Property Panel · 4개 비교"): no Inner scroller, the page
  scrolls, 2 × 2 grid with one column below 720px.
- SW Footer drops the "실제 Footer 조합 (동작)" heading; SW Viewer Header page is
  titled "SW Viewer Header" with 기본형 and 최소화 · 닫기 버튼 only.
- Browser tests, inventory and README follow these changes; visual baselines are
  not updated.
This commit is contained in:
shkim4 committed 2026-10-22 19:42:20 +09:00
1 parent ccafc6e332
commit 258e0a4b68
14 files changed
+123 -109

No files matched your search

@@ -75,7 +75,7 @@ try {
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 })
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 · 4개 비교')
check('inventory: compare Story keeps its ID and name', index.entries['sw-header--property-panel-compare']?.name === 'Property Panel')
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
@@ -89,23 +89,16 @@ try {
check(`${width}: original panel sizes 314 × 256 / 361 / 658 / 754`, STATES.every(([id, h], i) => sizes[i][0] === id && sizes[i][1] === 314 && sizes[i][2] === h), sizes)
const colGap = b.cell.x - a.cell.right, colGap2 = d.cell.x - c.cell.right, rowGap = c.cell.y - Math.max(a.cell.bottom, b.cell.bottom)
report.data[`gaps${width}`] = { colGap, colGap2, rowGap }
check(`${width}: 2 columns × 2 rows by coordinates`, Math.abs(a.cell.y - b.cell.y) < 0.5 && Math.abs(c.cell.y - d.cell.y) < 0.5 && Math.abs(a.cell.x - c.cell.x) < 0.5 && Math.abs(b.cell.x - d.cell.x) < 0.5 && b.cell.x >= a.cell.right && c.cell.y >= Math.max(a.cell.bottom, b.cell.bottom), { a: a.cell, b: b.cell, c: c.cell, d: d.cell })
check(`${width}: consistent 24px column and row gaps`, [colGap, colGap2, rowGap].every(v => Math.abs(v - 24) < 0.5), { colGap, colGap2, rowGap })
check(`${width}: page itself does not scroll (no document overflow)`, g.doc.scrollW <= g.doc.clientW && g.doc.scrollH <= g.doc.clientH, g.doc)
check(`${width}: Inner scroller scrolls vertically to the 754px panel`, g.scroller.scrollH > g.scroller.clientH, g.scroller)
await page.screenshot({ path: path.join(out, `compare-${width}-top.png`) })
// Scroll to the very end: every panel must be reachable with its bottom inside the scroller.
const end = await page.evaluate(() => {
const s = document.querySelector('[data-compare-scroll]'); s.scrollTop = s.scrollHeight; s.scrollLeft = s.scrollWidth
const sr = s.getBoundingClientRect()
return { scrollTop: s.scrollTop, scrollLeft: s.scrollLeft, panels: [...s.querySelectorAll('.hm-sw-property')].map(p => { const r = p.getBoundingClientRect(); return { bottom: r.bottom, right: r.right } }), sBottom: sr.bottom, sRight: sr.right }
})
report.data[`end${width}`] = end
const last = end.panels[3], right = end.panels[1]
check(`${width}: bottom of the 754px panel reachable, not clipped`, last.bottom <= end.sBottom + 0.5 && last.right <= end.sRight + 0.5, end)
if (g.scroller.scrollW > g.scroller.clientW) check(`${width}: narrow Inner scrolls horizontally, keeping 2 columns`, end.scrollLeft > 0 && right.right <= end.sRight + 0.5, { scrollW: g.scroller.scrollW, clientW: g.scroller.clientW, end })
else check(`${width}: both columns fit without horizontal scroll`, g.scroller.scrollW <= g.scroller.clientW && b.cell.right <= g.scroller.right, g.scroller)
await page.screenshot({ path: path.join(out, `compare-${width}-end.png`) })
// 2026-10-22 (user request): no Inner scroller; the page scrolls. 2 columns × 2 rows, one column below 720px.
const oneColumn = width < 720
if (oneColumn) check(`${width}: one column in order`, [a, b, c, d].every((cell, i, all) => i === 0 || (Math.abs(cell.cell.x - a.cell.x) < 0.5 && cell.cell.y >= all[i - 1].cell.bottom)), { a: a.cell, b: b.cell, c: c.cell, d: d.cell })
else {
check(`${width}: 2 columns × 2 rows by coordinates`, Math.abs(a.cell.y - b.cell.y) < 0.5 && Math.abs(c.cell.y - d.cell.y) < 0.5 && Math.abs(a.cell.x - c.cell.x) < 0.5 && Math.abs(b.cell.x - d.cell.x) < 0.5 && b.cell.x >= a.cell.right && c.cell.y >= Math.max(a.cell.bottom, b.cell.bottom), { a: a.cell, b: b.cell, c: c.cell, d: d.cell })
check(`${width}: consistent 24px column and row gaps`, [colGap, colGap2, rowGap].every(v => Math.abs(v - 24) < 0.5), { colGap, colGap2, rowGap })
}
check(`${width}: no Inner scroll (wrapper does not scroll)`, g.scroller.scrollW <= g.scroller.clientW && g.scroller.scrollH <= g.scroller.clientH, g.scroller)
check(`${width}: no horizontal page scroll; panels inside the page width`, g.doc.scrollW <= g.doc.clientW && g.cells.every(cell => cell.panel.right <= g.doc.clientW + 0.5), g.doc)
await page.screenshot({ path: path.join(out, `compare-${width}-top.png`), fullPage: true })
}
// ---- Behaviour (1440 × 900)
@@ -141,8 +134,8 @@ try {
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 Inner while open: the fixed overlay must stay attached to its field (below or flipped above).
const moved = await page.locator('[data-compare-scroll]').evaluate(s => { const t = s.scrollTop; s.scrollTop += 120; return s.scrollTop - t })
// 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()
@@ -150,7 +143,7 @@ try {
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 Inner scroller (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)
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)))