diff --git a/docs/sw/integrated-improvement-qa/final-overview/after-index.json b/docs/sw/integrated-improvement-qa/final-overview/after-index.json index 1359483..a204206 100644 --- a/docs/sw/integrated-improvement-qa/final-overview/after-index.json +++ b/docs/sw/integrated-improvement-qa/final-overview/after-index.json @@ -13,6 +13,34 @@ ], "exportName": "ComposedControls" }, + { + "type": "story", + "subtype": "story", + "id": "sw-header--document-open", + "name": "Document Open", + "title": "SW/Header", + "importPath": "./src/sw/sw-header-open.stories.tsx", + "tags": [ + "dev", + "test", + "manifest" + ], + "exportName": "DocumentOpen" + }, + { + "type": "story", + "subtype": "story", + "id": "sw-header--hidden-sub-controls", + "name": "Hidden Sub Controls", + "title": "SW/Header", + "importPath": "./src/sw/sw-header-open.stories.tsx", + "tags": [ + "dev", + "test", + "manifest" + ], + "exportName": "HiddenSubControls" + }, { "type": "story", "subtype": "story", diff --git a/docs/sw/integrated-improvement-qa/final-overview/chrome-result.json b/docs/sw/integrated-improvement-qa/final-overview/chrome-result.json index f372ba8..5da3aa6 100644 --- a/docs/sw/integrated-improvement-qa/final-overview/chrome-result.json +++ b/docs/sw/integrated-improvement-qa/final-overview/chrome-result.json @@ -644,6 +644,8 @@ "browser": "155.0.8059.80", "managerOrder": [ "sw-header--composed-controls", + "sw-header--document-open", + "sw-header--hidden-sub-controls", "sw-header--overview", "sw-header--interactive-states", "sw-header--window-move-motion", diff --git a/docs/sw/integrated-improvement-qa/final-overview/manager-story-order.json b/docs/sw/integrated-improvement-qa/final-overview/manager-story-order.json index 756b232..d63978f 100644 --- a/docs/sw/integrated-improvement-qa/final-overview/manager-story-order.json +++ b/docs/sw/integrated-improvement-qa/final-overview/manager-story-order.json @@ -1,5 +1,7 @@ [ "sw-header--composed-controls", + "sw-header--document-open", + "sw-header--hidden-sub-controls", "sw-header--overview", "sw-header--interactive-states", "sw-header--window-move-motion", diff --git a/docs/sw/integrated-improvement-qa/final-storybook-unused-removal/manager-sidebar.png b/docs/sw/integrated-improvement-qa/final-storybook-unused-removal/manager-sidebar.png index aff595b..28cedf6 100644 Binary files a/docs/sw/integrated-improvement-qa/final-storybook-unused-removal/manager-sidebar.png and b/docs/sw/integrated-improvement-qa/final-storybook-unused-removal/manager-sidebar.png differ diff --git a/docs/sw/integrated-improvement-qa/final-storybook-unused-removal/manager-story-order.json b/docs/sw/integrated-improvement-qa/final-storybook-unused-removal/manager-story-order.json index 11e2001..cc84dcd 100644 --- a/docs/sw/integrated-improvement-qa/final-storybook-unused-removal/manager-story-order.json +++ b/docs/sw/integrated-improvement-qa/final-storybook-unused-removal/manager-story-order.json @@ -3,6 +3,14 @@ "sw-header--composed-controls", "Dropdown" ], + [ + "sw-header--document-open", + "Document Open" + ], + [ + "sw-header--hidden-sub-controls", + "Hidden Sub Controls" + ], [ "sw-header--overview", "Overview" diff --git a/docs/sw/integrated-improvement-qa/final-storybook-unused-removal/result.json b/docs/sw/integrated-improvement-qa/final-storybook-unused-removal/result.json index e624a1f..93a3f53 100644 --- a/docs/sw/integrated-improvement-qa/final-storybook-unused-removal/result.json +++ b/docs/sw/integrated-improvement-qa/final-storybook-unused-removal/result.json @@ -2,7 +2,7 @@ "baseCommit": "c8e5fbeebc26fba92998fc5ddcd28c6c110e452d", "checks": [ { - "name": "inventory: 19 -> 10 SW/Header Stories, 338 -> 329 total plus only the 7 integrated-improvement Stories", + "name": "inventory: 19 -> 10 retained SW/Header Stories, 338 -> 329 total plus only the integrated-improvement Stories", "status": "PASS", "data": { "before": [ @@ -10,14 +10,22 @@ 19 ], "after": [ - 336, - 10 + 334, + 12 ], "sw": [ [ "sw-header--composed-controls", "Dropdown" ], + [ + "sw-header--document-open", + "Document Open" + ], + [ + "sw-header--hidden-sub-controls", + "Hidden Sub Controls" + ], [ "sw-header--detail-buttons", "Detail Buttons (Shared States)" @@ -56,10 +64,8 @@ ] ], "added": [ - "sw-header-options--open-placement", - "sw-header-options--hidden-sub-controls", - "sw-header-options--composed-open", - "sw-header-options--composed-responsive", + "sw-header--document-open", + "sw-header--hidden-sub-controls", "sw-popup--custom-content", "sw-popup--multiple-popups", "sw-tool--tool-buttons" @@ -112,7 +118,7 @@ "status": "PASS", "data": { "unexpected": [], - "integrated": 19 + "integrated": 23 } }, { @@ -123,6 +129,14 @@ "sw-header--composed-controls", "Dropdown" ], + [ + "sw-header--document-open", + "Document Open" + ], + [ + "sw-header--hidden-sub-controls", + "Hidden Sub Controls" + ], [ "sw-header--overview", "Overview" @@ -180,6 +194,14 @@ "name": "render sw-header--composed-controls", "status": "PASS" }, + { + "name": "render sw-header--document-open", + "status": "PASS" + }, + { + "name": "render sw-header--hidden-sub-controls", + "status": "PASS" + }, { "name": "render sw-header--detail-buttons", "status": "PASS" @@ -894,14 +916,22 @@ 19 ], "after": [ - 336, - 10 + 334, + 12 ], "sw": [ [ "sw-header--composed-controls", "Dropdown" ], + [ + "sw-header--document-open", + "Document Open" + ], + [ + "sw-header--hidden-sub-controls", + "Hidden Sub Controls" + ], [ "sw-header--detail-buttons", "Detail Buttons (Shared States)" @@ -945,6 +975,14 @@ "sw-header--composed-controls", "Dropdown" ], + [ + "sw-header--document-open", + "Document Open" + ], + [ + "sw-header--hidden-sub-controls", + "Hidden Sub Controls" + ], [ "sw-header--overview", "Overview" diff --git a/docs/sw/integrated-improvement-qa/final-storybook-unused-removal/story-inventory-after.json b/docs/sw/integrated-improvement-qa/final-storybook-unused-removal/story-inventory-after.json index 70b67ce..847d527 100644 --- a/docs/sw/integrated-improvement-qa/final-storybook-unused-removal/story-inventory-after.json +++ b/docs/sw/integrated-improvement-qa/final-storybook-unused-removal/story-inventory-after.json @@ -1,5 +1,5 @@ { - "total": 336, + "total": 334, "sw": [ { "id": "sw-header--composed-controls", @@ -11,6 +11,26 @@ "manifest" ] }, + { + "id": "sw-header--document-open", + "name": "Document Open", + "title": "SW/Header", + "tags": [ + "dev", + "test", + "manifest" + ] + }, + { + "id": "sw-header--hidden-sub-controls", + "name": "Hidden Sub Controls", + "title": "SW/Header", + "tags": [ + "dev", + "test", + "manifest" + ] + }, { "id": "sw-header--detail-buttons", "name": "Detail Buttons (Shared States)", diff --git a/docs/sw/integrated-improvement-qa/verify/composed-open.png b/docs/sw/integrated-improvement-qa/verify/composed-open.png index f85f874..30aed34 100644 Binary files a/docs/sw/integrated-improvement-qa/verify/composed-open.png and b/docs/sw/integrated-improvement-qa/verify/composed-open.png differ diff --git a/docs/sw/integrated-improvement-qa/verify/composed-responsive.png b/docs/sw/integrated-improvement-qa/verify/composed-responsive.png index c85a301..2866a98 100644 Binary files a/docs/sw/integrated-improvement-qa/verify/composed-responsive.png and b/docs/sw/integrated-improvement-qa/verify/composed-responsive.png differ diff --git a/docs/sw/integrated-improvement-qa/verify/integrated-improvement-result.json b/docs/sw/integrated-improvement-qa/verify/integrated-improvement-result.json index 1d61e84..a4c6cad 100644 --- a/docs/sw/integrated-improvement-qa/verify/integrated-improvement-result.json +++ b/docs/sw/integrated-improvement-qa/verify/integrated-improvement-result.json @@ -510,14 +510,14 @@ "data": { "cHome": { "x": 360, - "y": 285.5, + "y": 6118.5, "w": 80, "h": 48, "right": 440 }, "cOpen": { "x": 425, - "y": 285.5, + "y": 6118.5, "w": 58.265625, "h": 48, "right": 483.265625 diff --git a/docs/sw/integrated-improvement-qa/verify/open-placement-responsive.png b/docs/sw/integrated-improvement-qa/verify/open-placement-responsive.png index 6cb2bb0..51d8cbf 100644 Binary files a/docs/sw/integrated-improvement-qa/verify/open-placement-responsive.png and b/docs/sw/integrated-improvement-qa/verify/open-placement-responsive.png differ diff --git a/docs/sw/sw-header-usage.md b/docs/sw/sw-header-usage.md index 56f9f48..915884b 100644 --- a/docs/sw/sw-header-usage.md +++ b/docs/sw/sw-header-usage.md @@ -1,6 +1,6 @@ # SW Header 사용 가이드 -> **0.2.0 통합 개선(2026-10-19):** 로고 오른쪽 파일 열기(`documentOpenPlacement="header"`, `documentOpenLabel` 재사용, 실측 기반 More 이동), `leading` 슬롯, 하위 항목 숨김(`hiddenControls`의 `documentNew`·`documentOpen`·`layerFreeze`·`layerLock`), 도구 막대 `SWToolButton` / `SWToolGroup`, 속성창 프레임 `SWPopup`(자유 `children`)과 `SWHeaderPropertyPanel`의 `children`·`title`을 추가했습니다. API·예제는 패키지 [AI 카탈로그](../../packages/sw/ai/COMPONENT_CATALOG.md)와 [조합 패턴](../../packages/sw/ai/COMPOSITION_PATTERNS.md), Story는 `SW / Header / Options`·`SW / Tool`·`SW / Popup`, 검증은 [통합 개선 기록](./sw-integrated-improvement-2026-10-19.md)을 따릅니다. +> **0.2.0 통합 개선(2026-10-19):** 로고 오른쪽 파일 열기(`documentOpenPlacement="header"`, `documentOpenLabel` 재사용, 실측 기반 More 이동), `leading` 슬롯, 하위 항목 숨김(`hiddenControls`의 `documentNew`·`documentOpen`·`layerFreeze`·`layerLock`), 도구 막대 `SWToolButton` / `SWToolGroup`, 속성창 프레임 `SWPopup`(자유 `children`)과 `SWHeaderPropertyPanel`의 `children`·`title`을 추가했습니다. API·예제는 패키지 [AI 카탈로그](../../packages/sw/ai/COMPONENT_CATALOG.md)와 [조합 패턴](../../packages/sw/ai/COMPOSITION_PATTERNS.md), Story는 `SW / Header`의 Document Open·Hidden Sub Controls, `SW / Tool`, `SW / Popup`, 검증은 [통합 개선 기록](./sw-integrated-improvement-2026-10-19.md)을 따릅니다. > **현재 기준 색인(2026-10-19 1차 정리):** 아래 날짜별 안내 중 최신 항목이 같은 주제의 이전 설명보다 우선합니다. 속성 필드 13개·필드 Dropdown(`picker`·`menus`·`decimals`)은 [속성창·K 가이드 추가 연결](#속성창k-가이드-추가-연결-2026-10-16)의 2026-10-19 항목, 표시·숨김(`hiddenControls`)·메뉴 순서(`menuOrder`)·속성창 4상태·형상 Slot은 [3차 절](#구성요소-표시메뉴-순서속성창-4상태형상-slot-2026-10-16-3차), 속성Bar·`controls`·Colorize는 [후속 절](#후속-5개-요구사항-연결-2026-10-16)을 봅니다. 현재 Storybook 33개 목록은 [§12](#12-storybook)와 [1차 정리 보고](./sw-first-pass-cleanup-review-2026-10-19.md)에 있습니다. diff --git a/docs/sw/sw-integrated-improvement-2026-10-19.md b/docs/sw/sw-integrated-improvement-2026-10-19.md index e773387..095cc5d 100644 --- a/docs/sw/sw-integrated-improvement-2026-10-19.md +++ b/docs/sw/sw-integrated-improvement-2026-10-19.md @@ -15,7 +15,7 @@ | 5 SWPopup | 속성창 프레임을 `SWPopupFrame`(내부)으로 분리해 `SWPopup`과 `SWHeaderPropertyPanel`이 같은 DOM 사용. `children`·`title`·`autoFocus`, `selection`/`properties`/`groups` 선택형. Escape 스택(안 = 그 Popup, 밖 = 최근 사용 Popup 하나, `defaultPrevented` 존중), autoFocus 시 안에서 닫히면 Focus 복귀 | PASS | | 6 Popup 안 Dropdown | 실측: 짧은 Popup에서 레이어 목록이 본문 경계에서 잘림 확인 → 속성창 Picker의 고정 위치 계산을 `useSWFixedDropdownPosition`으로 추출해 Picker와 공개 Dropdown 5종이 공유(중복 구현 없음). `SWPopupSection` / `SWPopupRow` 추가 | PASS | | 7 조합형 반응형 | 새 More 로직 없음. `SWHeader`의 `controls`·`documentOpenPlacement`·`leading`·`tools`로 조합형 구성이 기존 More·실측 판단을 그대로 사용. `SWTheme` 직접 배치에는 More 없음(문서화) | PARTIAL(도구 막대 자체는 More로 이동하지 않음, §6) | -| 8 문서·Storybook·소비 검증 | AI 가이드 4종·예제 E/F·README·사용 가이드, Story 3개 파일(7 Story), 독립 소비 프로젝트(SW + HM UI) | PASS | +| 8 문서·Storybook·소비 검증 | AI 가이드 4종·예제 E/F·README·사용 가이드, Story 3개 파일(5 Story: `SW/Header`의 Document Open·Hidden Sub Controls, `SW/Tool` 1, `SW/Popup` 2. 별도 Options 메뉴 없음), 독립 소비 프로젝트(SW + HM UI) | PASS | ## 2. 추가·변경 공개 API (모두 선택, 기본 동작 동일) @@ -74,5 +74,6 @@ | [ai-docs.check.mjs](../../packages/sw/tests/ai-docs.check.mjs) | 21/21 PASS(예제 E·F 포함) | | [integrated-visual.browser.mjs](../../packages/sw/tests/integrated-visual.browser.mjs) | 28/28 픽셀 동일 | | [build-reproducibility.mjs](../../packages/sw/tests/build-reproducibility.mjs) | PASS: LF 2경로 × 2회 dist·Tarball 동일(`e98cc5ed…`), CRLF 복사본 dist 동일, 비-LF 파일 0 | -| storybook-unused-removal / overview(기존 회귀) | 처음 실행 78/81 · 51/54: 6건 중 4건은 이 작업이 의도적으로 바꾼 범위 단언(Story 수 329 → 336, SW 소스·AI 문서), 2건은 변경 전 기준선에서도 실패하던 overview 과거 단언. 4건은 검사 의미를 유지해 범위를 갱신(SW/Header 10개·다른 항목 동일, 추가는 승인된 7개 Story만, 다른 패키지·설정·자산 무변경, SW 변경은 통합 개선 파일 목록 안에서만) → **81/81 · 52/54**. 남은 2건은 기준선과 동일 | +| storybook-unused-removal / overview(기존 회귀) | 처음 실행 78/81 · 51/54: 6건 중 4건은 이 작업이 의도적으로 바꾼 범위 단언(Story 수 증가, SW 소스·AI 문서), 2건은 변경 전 기준선에서도 실패하던 overview 과거 단언. 4건은 검사 의미를 유지해 범위를 갱신(SW/Header 10개·다른 항목 동일, 추가는 승인된 5개 Story만, 다른 패키지·설정·자산 무변경, SW 변경은 통합 개선 파일 목록 안에서만) → **81/81 · 52/54**. Options 메뉴를 `SW/Header`로 통합한 뒤(Story 2개 렌더 검사 추가) **83/83 · 52/54** | +| overview 남은 실패 2건 — **기존 실패, 신규 결함 아님** | "24 retained Story IDs…", "retained non-Overview declarations byte-identical…": 이미 은퇴한 Dropdown Story 시점(24개, 기준 Commit `50bf5bd`)을 고정 비교하는 과거 단언입니다. 이 작업을 시작하기 전 기준선(HEAD `5faa1f1`)에서도 같은 2건이 실패했습니다([baseline-overview.log](./integrated-improvement-qa/baseline-overview.log)). 현재 인벤토리는 storybook-unused-removal이 검증하며, overview 기능 검사 52건은 모두 PASS | | font-weight 정책 테스트 | 3/3 PASS | diff --git a/packages/sw/tests/integrated-improvement.browser.mjs b/packages/sw/tests/integrated-improvement.browser.mjs index 87d080d..ceb052f 100644 --- a/packages/sw/tests/integrated-improvement.browser.mjs +++ b/packages/sw/tests/integrated-improvement.browser.mjs @@ -33,7 +33,7 @@ const log = () => page.locator('[data-callback-log]').first().textContent() const rect = locator => locator.evaluate(e => { const r = e.getBoundingClientRect(); return { x: r.x, y: r.y, w: r.width, h: r.height, right: r.right } }) if (sections.includes('options')) { - await goto('sw-header-options--open-placement') + await goto('sw-header--document-open') const frames = page.locator('.sw-story-frame') // Default placement unchanged: no header Open, dropdown footer keeps + New / Open. const plain = frames.nth(0).locator('header.hm-sw-header') @@ -63,7 +63,7 @@ if (sections.includes('options')) { await page.keyboard.press('Escape') await open.click() check('header: Open calls onDocumentOpen', (await log()).includes('onDocumentOpen')) - check('documentOpenLabel is the Open text', await frames.nth(2).locator('[data-document-open]').textContent() === '파일 열기') + check('documentOpenLabel is the Open text', await page.locator('[data-open-label] [data-document-open]').textContent() === '파일 열기') // Responsive: measured room. No overflow anywhere; Open is either shown or reachable in More. const widths = [] @@ -106,7 +106,7 @@ if (sections.includes('options')) { const flips = trace.slice(1).filter((step, index) => step[1] !== trace[index][1]).length check('resize: collapsed Open returns once when the header grows (measured, with hysteresis)', trace[0][1] === 0 && trace.at(-1)[1] === 1 && flips === 1, trace) - await goto('sw-header-options--hidden-sub-controls') + await goto('sw-header--hidden-sub-controls') const onlyOpen = page.locator('[data-hidden="new"] .hm-sw-header__document-footer') const footer = await rect(onlyOpen), action = await rect(onlyOpen.locator('.hm-sw-header__document-action')) check('hidden documentNew: footer keeps Open on its right side', JSON.stringify(await onlyOpen.locator('.hm-sw-header__document-action').allTextContents()) === '["Open"]' && Math.abs(footer.right - 8 - action.right) < 1, { footer, action }) @@ -121,7 +121,7 @@ if (sections.includes('options')) { await page.locator('[data-hidden="layer-states"]').screenshot({ path: path.join(out, 'hidden-layer-states.png') }) await page.locator('[data-hidden="new"]').screenshot({ path: path.join(out, 'hidden-document-new.png') }) - await goto('sw-header-options--composed-open') + await goto('sw-header--document-open') const bar = page.locator('[data-composed-open]') const cHome = await rect(bar.locator('.hm-sw-header__home')), cOpen = await rect(bar.locator('.hm-sw-header__document[data-tool] button')), cDoc = bar.locator('.hm-sw-header__document:not([data-tool]) .hm-sw-header__document-button') check('composed: Open right of the logo at the same junction offset as SWHeader', Math.abs(cOpen.x - cHome.right - report.data.junction) < 0.6 && cOpen.h === 48, { cHome, cOpen }) @@ -135,7 +135,7 @@ if (sections.includes('options')) { } if (sections.includes('options') || sections.includes('composed')) { - await goto('sw-header-options--composed-responsive') + await goto('sw-header--document-open') const rows = [] for (const item of await page.locator('[data-composed-responsive] > [data-width]').all()) { const width = Number(await item.getAttribute('data-width')) diff --git a/packages/sw/tests/storybook-inventory.mjs b/packages/sw/tests/storybook-inventory.mjs index d26c57d..a5b65ba 100644 --- a/packages/sw/tests/storybook-inventory.mjs +++ b/packages/sw/tests/storybook-inventory.mjs @@ -10,13 +10,16 @@ export const swStoryEntries = index => Object.values(index.entries).filter(entry export const retainedInventory = entries => entries.filter(entry => ![...retiredStage1Ids, ...retiredDropdownIds].includes(entry.id)) .map(entry => entry.id === 'sw-header--composed-controls' ? { ...entry, name: 'Dropdown', title: 'SW/Header' } : entry) export const inventorySignature = entries => entries.map(entry => [entry.id, entry.name, entry.tags]).sort((a, b) => a[0].localeCompare(b[0])) -// 2026-10-19 SW integrated improvement (0.2.0): the only Stories added after the unused-demo removal, under their -// own titles (SW/Header keeps its 10 Stories), and the only files of the package it changed. -export const integratedStoryIds = ['sw-header-options--open-placement', 'sw-header-options--hidden-sub-controls', 'sw-header-options--composed-open', 'sw-header-options--composed-responsive', 'sw-tool--tool-buttons', 'sw-popup--custom-content', 'sw-popup--multiple-popups'] +// 2026-10-19 SW integrated improvement (0.2.0): the only Stories added after the unused-demo removal (two under +// SW/Header next to its 10 retained Stories, the rest under SW/Tool and SW/Popup), and the only package files it changed. +// The two Header Stories sit directly under SW/Header (no separate Options menu), in src/sw/sw-header-open.stories.tsx. +export const integratedStoryIds = ['sw-header--document-open', 'sw-header--hidden-sub-controls', 'sw-tool--tool-buttons', 'sw-popup--custom-content', 'sw-popup--multiple-popups'] export const integratedChangedFiles = [ 'packages/sw/README.md', 'packages/sw/package.json', 'packages/sw/build.mjs', 'packages/sw/ai/AI_USAGE.md', 'packages/sw/ai/COMPONENT_CATALOG.md', 'packages/sw/ai/COMPOSITION_PATTERNS.md', 'packages/sw/ai/DESIGN_RULES.md', + 'packages/sw/ai/examples/ExampleE-CustomPopup.tsx', 'packages/sw/ai/examples/ExampleF-ViewerToolbar.tsx', 'packages/sw/src/index.ts', 'packages/sw/src/theme/sw.css', + 'packages/sw/src/components/sw-popup/SWPopup.tsx', 'packages/sw/src/components/sw-tool/SWTool.tsx', 'packages/sw/src/components/sw-header/SWHeader.tsx', 'packages/sw/src/components/sw-header/index.ts', 'packages/sw/src/components/sw-header/types.ts', ...['SWHeaderDocumentTitle', 'SWHeaderDropdown', 'SWHeaderFields', 'SWHeaderLayerMenu', 'SWHeaderLineStyleMenu', 'SWHeaderMore', 'SWHeaderPropertyFields', 'SWHeaderPropertyPanel'].map(name => `packages/sw/src/components/sw-header/components/${name}.tsx`), ] diff --git a/packages/sw/tests/storybook-unused-removal.browser.mjs b/packages/sw/tests/storybook-unused-removal.browser.mjs index 22a58e3..97309db 100644 --- a/packages/sw/tests/storybook-unused-removal.browser.mjs +++ b/packages/sw/tests/storybook-unused-removal.browser.mjs @@ -26,12 +26,13 @@ const beforeSW = swStoryEntries(beforeIndex), afterSW = swStoryEntries(afterInde const expected = beforeSW.filter(e => !retiredUnusedDemoIds.includes(e.id)) report.data.inventory = { before: [stories(beforeIndex).length, beforeSW.length], after: [stories(afterIndex).length, afterSW.length], sw: afterSW.map(e => [e.id, e.name]) } await writeFile(path.join(out, 'story-inventory-after.json'), JSON.stringify({ total: stories(afterIndex).length, sw: afterSW.map(e => ({ id: e.id, name: e.name, title: e.title, tags: e.tags })) }, null, 2) + '\n') -// 2026-10-19 integrated improvement: 329 + its 7 Stories under SW/Header/Options, SW/Tool and SW/Popup (SW/Header stays 10). +// 2026-10-19 integrated improvement: 329 + its 5 Stories (2 under SW/Header, SW/Tool, SW/Popup); the 10 retained SW/Header Stories stay. const added = stories(afterIndex).filter(e => !beforeIndex.entries[e.id]) -check('inventory: 19 -> 10 SW/Header Stories, 338 -> 329 total plus only the 7 integrated-improvement Stories', beforeSW.length === 19 && afterSW.length === 10 && stories(beforeIndex).length === 338 && stories(afterIndex).length === 329 + integratedStoryIds.length && JSON.stringify(added.map(e => e.id).sort()) === JSON.stringify([...integratedStoryIds].sort()), { ...report.data.inventory, added: added.map(e => e.id) }) +const retainedSW = afterSW.filter(e => !integratedStoryIds.includes(e.id)) +check('inventory: 19 -> 10 retained SW/Header Stories, 338 -> 329 total plus only the integrated-improvement Stories', beforeSW.length === 19 && retainedSW.length === 10 && stories(beforeIndex).length === 338 && stories(afterIndex).length === 329 + integratedStoryIds.length && JSON.stringify(added.map(e => e.id).sort()) === JSON.stringify([...integratedStoryIds].sort()), { ...report.data.inventory, added: added.map(e => e.id) }) check('inventory: the nine requested IDs existed before and are absent now', retiredUnusedDemoIds.length === 9 && retiredUnusedDemoIds.every(id => beforeIndex.entries[id] && !afterIndex.entries[id])) -check('inventory: retained SW IDs, names, titles, tags and order unchanged', sig(expected) === sig(afterSW), afterSW.map(e => e.id)) -const others = index => Object.values(index.entries).filter(e => !retiredUnusedDemoIds.includes(e.id) && !integratedStoryIds.includes(e.id) && !/^sw-(header-options|tool|popup)--docs$/.test(e.id)) +check('inventory: retained SW IDs, names, titles, tags and order unchanged', sig(expected) === sig(retainedSW), retainedSW.map(e => e.id)) +const others = index => Object.values(index.entries).filter(e => !retiredUnusedDemoIds.includes(e.id) && !integratedStoryIds.includes(e.id) && !/^sw-(tool|popup)--docs$/.test(e.id)) check('inventory: every non-removed index entry identical (docs and other packages included)', JSON.stringify(others(beforeIndex)) === JSON.stringify(others(afterIndex))) check('inventory: Dropdown keeps sw-header--composed-controls / Dropdown / SW/Header', afterIndex.entries['sw-header--composed-controls']?.name === 'Dropdown' && afterIndex.entries['sw-header--composed-controls']?.title === 'SW/Header') check('inventory: comparison keeps its ID and name', afterIndex.entries['sw-header--property-panel-compare']?.name === 'Property Panel · 4개 비교') diff --git a/src/sw/sw-header-options.stories.tsx b/src/sw/sw-header-open.stories.tsx similarity index 79% rename from src/sw/sw-header-options.stories.tsx rename to src/sw/sw-header-open.stories.tsx index e76eb3d..1e1291f 100644 --- a/src/sw/sw-header-options.stories.tsx +++ b/src/sw/sw-header-open.stories.tsx @@ -49,21 +49,36 @@ function useLog() { return [view, (line: string) => setLog(lines => [...lines.slice(-7), line])] as const } +// Two Stories directly under SW/Header (no separate Options menu): Document Open and Hidden Sub Controls. const meta = { - title: "SW/Header/Options", + title: "SW/Header", + id: "sw-header", parameters: { layout: "fullscreen", controls: { disable: true } }, } satisfies Meta export default meta type Story = StoryObj -function OpenPlacementPage() { +function DocumentOpenPage() { const [log, record] = useLog() + const [layerId, setLayerId] = useState("wall") const header = (props: Partial) => ( record("onDocumentOpen")} onDocumentNew={() => record("onDocumentNew")} {...props} /> ) + // Composed layouts that need responsive behavior use SWHeader itself (controls + documentOpenPlacement): + // the one existing More logic serves them, no second More is created. + const viewer = (width: number) => ( +
+ + record("onDocumentOpen")} onColorSelect={option => record(`onColorSelect(${option.id})`)} onPopupGuide={() => record("onPopupGuide")} /> + +
+ ) return ( - {log} @@ -73,18 +88,35 @@ function OpenPlacementPage() {
{header({ documentOpenPlacement: "header" })}
- {header({ documentOpenPlacement: "header", documentOpenLabel: "파일 열기" })} +
{header({ documentOpenPlacement: "header", documentOpenLabel: "파일 열기" })}
{OPTION_WIDTHS.map(width =>
{header({ documentOpenPlacement: "header" })}
)}
+ + + + record("onHomeClick")} /> + record("onDocumentOpen")}>Open + record(`onDocumentSelect(${document.id})`)} onNew={() => record("onNew")} /> +
+ layer.id === layerId)!.name }} layers={LAYERS} activeLayerId={layerId} + onLayerSelect={layer => { setLayerId(layer.id); record(`onLayerSelect(${layer.id})`) }} /> +
+
+ +
+ +
{OPTION_WIDTHS.map(viewer)}
+
) } -export const OpenPlacement: Story = { name: "Open Placement", render: () => } +export const DocumentOpen: Story = { name: "Document Open", render: () => } function HiddenSubControlsPage() { const [log, record] = useLog() @@ -106,57 +138,3 @@ function HiddenSubControlsPage() { } export const HiddenSubControls: Story = { name: "Hidden Sub Controls", render: () => } - -function ComposedOpenPage() { - const [log, record] = useLog() - const [layerId, setLayerId] = useState("wall") - return ( - - - {log} - - - - record("onHomeClick")} /> - record("onDocumentOpen")}>Open - record(`onDocumentSelect(${document.id})`)} onNew={() => record("onNew")} /> -
- layer.id === layerId)!.name }} layers={LAYERS} activeLayerId={layerId} - onLayerSelect={layer => { setLayerId(layer.id); record(`onLayerSelect(${layer.id})`) }} /> -
-
- -
-
- ) -} - -export const ComposedOpen: Story = { name: "Composed Open", render: () => } - -// Composed layouts that need responsive behavior use SWHeader itself (controls + leading + documentOpenPlacement): -// the one existing More logic serves them, no second More is created. -function ComposedResponsivePage() { - const [log, record] = useLog() - const viewer = (width: number) => ( -
- - record("onDocumentOpen")} onColorSelect={option => record(`onColorSelect(${option.id})`)} onPopupGuide={() => record("onPopupGuide")} /> - -
- ) - return ( - - - {log} -
{OPTION_WIDTHS.map(viewer)}
-
- ) -} - -export const ComposedResponsive: Story = { name: "Composed Responsive", render: () => }