docs(storybook): preview-first Expanded OrgChart and leaner Tabs / Button pages
Deploy Storybook / deploy (push) Successful in 54s

- Expanded OrgChart: the chart moves right under the page title; data, layout
  and interaction settings stay below it. Setting notes, section descriptions,
  hidden legends and the Drag & Drop ON/OFF text are removed; the example
  counts stay only as a hidden data hook for the OrgChart browser checks, and
  the pending note shows only while the employee count differs.
- Tabs: drop the text-only Do / Don't section (rules live in tabs.md §16).
- Button: the representative page hides the Brand / Standard state notes
  (story-only showNote); the hidden States Stories keep them.
- Report: the follow-up simplification and its verification.
This commit is contained in:
shkim4 committed 2026-10-22 17:25:00 +09:00
1 parent c5c6fed38e
commit 4a54cfbeb2
5 files changed
+48 -53

No files matched your search

@@ -35,3 +35,10 @@
- `packages/sw/tests/storybook-simplification.browser.mjs` 9/9 PASS
- `examples/org-chart/verify-p9.mjs`: 변경 전 빌드로 만든 로컬 기준에 대해 변경 후 67 PASS / 17 FAIL입니다. 변경 전 빌드도 같은 17건이 FAIL이므로 기존 이슈입니다.
- `packages/sw/tests/integrated-visual.browser.mjs` capture/compare: 숨김 검증 Story 6종(detail-buttons 등)은 동일합니다. 표시 Story(overview, logo-slot, responsive-more, property-panel-compare)의 차이는 모두 분류 라벨 제거에 따른 세로 이동(24px, Overview는 12px)이며 그 외 픽셀 차이는 없습니다. 시각 기준 이미지는 갱신하지 않았습니다.
## 추가 간소화 (da0e647 이후)
- **Expanded OrgChart (Preview first)**: 조직도 미리보기를 제목 바로 아래로 옮겼습니다. 1440 × 900에서 미리보기 시작 위치가 1103 → 177px입니다. 데이터·레이아웃·인터랙션 설정은 미리보기 아래에 그대로 둡니다. 설정 안내 문구(회사 숨김 동작, Hybrid/TB 조건, Compact 미지원, Pan·클릭 안내)와 섹션 설명은 제거했습니다. Pan·Alt+M 안내는 조직도 툴바가 이미 표시합니다. `OrgChartExampleCounts` 집계 문구는 화면에서 제거하고, OrgChart 브라우저 검증(P8, P9-2/3/4)이 읽는 `hidden` 데이터 hook으로만 남겼습니다. 숨김 legend 3개(데이터·레이아웃·기능 설정)와 Drag & Drop ON/OFF 글자도 제거했습니다. 적용 예정 상태는 직원 수를 바꾼 동안에만 표시합니다. 초기 보기는 기존 100% center를 유지하며 Fit to View로 전체를 볼 수 있습니다.
- **Tabs → Do / Don't**: UI 예시 없이 문단만 있던 섹션을 제거했습니다. 규칙은 [Tabs §16](../components/tabs.md)에 있습니다.
- **Button → States**: 대표 화면에서만 Brand / Standard 상태 설명 문단을 숨깁니다(Story 전용 `showNote`). 숨김 Story `States`·`StandardStates`는 문단을 유지하고, 규칙은 [Button](../components/button.md)과 UI AI 가이드에 있습니다. 모든 상태 버튼과 `Activated` 출력은 유지합니다.
- 검증: tsc PASS, `test:guard` 94/94, `check:ai-docs` PASS, Story 359개 렌더·ID 동일, SW simplification 9/9. Expanded 설정 조작(직원 수·데이터 적용·LR 전환·Drag & Drop·초기화) 9/9. OrgChart P9(67/17)와 P9-2(2/4)는 da0e647 빌드와 같은 FAIL 목록이므로 기존 이슈입니다.
+15 -10
View File
@@ -118,7 +118,8 @@ function SizesBoard() {
)
}
function BrandStates() {
// `showNote={false}` keeps the representative page UI-only; the verification Stories keep the state notes.
function BrandStates({ showNote = true }: { showNote?: boolean }) {
const [count, setCount] = useState(0)
return (
<div className={stack}>
@@ -132,9 +133,11 @@ function BrandStates() {
</Button>
</div>
<output aria-live="polite">Activated: {count}</output>
<p className={note}>
Hover와 Pressed는 Storybook이 고정한 상태입니다. Interact 위에서 실제 Hover·Pressed·포인터 이탈을 확인합니다. Disabled는 실행되지 않습니다.
</p>
{showNote ? (
<p className={note}>
Hover와 Pressed는 Storybook이 고정한 상태입니다. Interact 위에서 실제 Hover·Pressed·포인터 이탈을 확인합니다. Disabled는 실행되지 않습니다.
</p>
) : null}
</div>
)
}
@@ -182,14 +185,16 @@ function StandardStateTable({ appearance, title }: { appearance: "solid" | "outl
)
}
function StandardStatesBoard() {
function StandardStatesBoard({ showNote = true }: { showNote?: boolean }) {
return (
<div className={stack}>
<StandardStateTable appearance="solid" title="Standard Solid" />
<StandardStateTable appearance="outline" title="Standard Outline" />
<p className={note}>
Standard Solid의 Hover·Pressed는 역할 면 위에 Black 20%를 합성합니다(Pressed = Hover). Green·Brown Outline은 Hover에서 역할색 면 + White 라벨, Pressed에서 White 면 + Black 20%입니다. Red Outline은 Hover·Pressed 모두 Red 8% 면이며 Red 라벨·보더를 유지합니다. Disabled는 Default 모양 그대로 opacity 60%이고 상태 면을 적용하지 않습니다. Default 열의 버튼으로 실제 포인터 상태도 확인할 수 있습니다. Brand 상태는 Brand States에서 확인합니다.
</p>
{showNote ? (
<p className={note}>
Standard Solid의 Hover·Pressed는 역할 면 위에 Black 20%를 합성합니다(Pressed = Hover). Green·Brown Outline은 Hover에서 역할색 면 + White 라벨, Pressed에서 White 면 + Black 20%입니다. Red Outline은 Hover·Pressed 모두 Red 8% 면이며 Red 라벨·보더를 유지합니다. Disabled는 Default 모양 그대로 opacity 60%이고 상태 면을 적용하지 않습니다. Default 열의 버튼으로 실제 포인터 상태도 확인할 수 있습니다. Brand 상태는 Brand States에서 확인합니다.
</p>
) : null}
</div>
)
}
@@ -236,8 +241,8 @@ export const All: Story = {
<StateBoard
min={1200}
states={[
{ id: "brand-states", label: "Brand", children: <BrandStates /> },
{ id: "standard-states", label: "Standard Solid · Standard Outline", children: <StandardStatesBoard /> },
{ id: "brand-states", label: "Brand", children: <BrandStates showNote={false} /> },
{ id: "standard-states", label: "Standard Solid · Standard Outline", children: <StandardStatesBoard showNote={false} /> },
]}
/>
</StorySection>
-3
View File
@@ -185,9 +185,6 @@ export const All: Story = {
{ id: "usage-page", label: "페이지 직접 배치 · Primary", noteAfter: true, note: "Layout content 배경 사용 · 추가 흰색 박스 없음", children: <SurfaceUsage /> },
]} />
</StorySection>
<StorySection title="Do / Don't" description="Card Token을 화면 전체 간격에 쓰지 않음">
<p className="text-sm text-muted-foreground">Do: Card 또는 Layout이 외곽 여백을 소유하고 내부 Input·Table·Alert의 고유 스타일을 유지합니다. Don't: 패널 박스 제거를 모든 border 제거로 해석하거나, 그라데이션 alpha 조정을 탭 전체 opacity 변경으로 구현하지 않습니다. 실제 패널 계산값·키보드 동작·모바일 넘침을 확인합니다.</p>
</StorySection>
</StoryPage>
),
}
+6 -12
View File
@@ -7,6 +7,12 @@
--hm-orgchart-expanded-preview-radius: var(--sb-center-card-radius);
padding: 0;
}
/* Preview first: the chart follows the page header with the header's own 24px rhythm. */
.hm-orgchart-story-expanded .hm-orgchart-story-lead {
display: grid;
gap: 0.75rem;
margin-top: 24px;
}
.hm-orgchart-story-expanded .hm-orgchart-story-settings {
display: grid;
@@ -16,18 +22,6 @@
border: 0;
min-width: 0;
}
/* StorySection supplies the visible heading; keep each fieldset named for assistive technology. */
.hm-orgchart-story-expanded .hm-orgchart-story-section-legend {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.hm-orgchart-story-expanded .hm-orgchart-story-controls {
align-items: flex-start;
}
+20 -28
View File
@@ -116,9 +116,15 @@ function ExpandedPreview() {
title="Expanded OrgChart"
description="모든 직원은 가상 데이터"
/>
<StorySection title="데이터 설정" description="직원 수 변경은 데이터 적용으로 반영">
{/* Preview first: the chart sits right under the page title; its own header already names it, so no section header. */}
<div className="hm-orgchart-story hm-orgchart-story-lead">
{error ? <p role="alert" className="hm-orgchart-story-note">{error}</p> : null}
<div className="hm-orgchart-story-preview sb-center">
<OrgChart dataSource={source} stylesheetUrls={compositionStylesheets} displayMode="readOnly" layoutMode="auto" autoLayout={layout} interactions={interactions} dragDropEnabled={dragDropEnabled} selectedCompanyIds={applied.companyIds} companyTopNodes={topNodes} employeePopup={popup} className="hm-orgchart-story-chart" onMove={result => { if (result.moved) setSession({ baseline: dataset, current: result.data }); }} onLifecycleError={cause => setError(cause instanceof Error ? cause.message : '조직도 설정을 적용할 수 없습니다.')} onStateChange={state => { if (state.status === 'error') setError(state.issues.map(issue => issue.message).join(' / ')); }} />
</div>
</div>
<StorySection title="데이터 설정">
<fieldset className="hm-orgchart-story-settings">
<legend className="hm-orgchart-story-section-legend">데이터 설정</legend>
<FilterBar layout="list-extended">
<div className="hm-orgchart-story-instance">
<Label id={`${id}-companies-label`} htmlFor={`${id}-companies`}>회사 선택</Label>
@@ -160,15 +166,19 @@ function ExpandedPreview() {
<Button variant="cancel" appearance="outline" onClick={() => { setError(null); setDraft(initialSettings); setApplied(initialSettings); setDataRevision(current => current + 1); }}>초기화</Button>
</FilterBarActions>
</FilterBar>
{/* Not shown: kept only as the data hook that the OrgChart browser checks (P8, P9-2/3/4) read. */}
<p hidden data-component="OrgChartExampleCounts">
표시 중: {applied.companyIds.length}개 회사 · 현재 소속 기준 직원 합계 {displayedTotal.toLocaleString('ko-KR')}명 / 전체 {sessionTotal.toLocaleString('ko-KR')}명
{companyCountSummary ? ` · ${companyCountSummary}` : ''}
</p>
{/* Live feedback only while the employee count differs from the applied data. */}
<p className="hm-orgchart-story-note" role="status" aria-live="polite">
{pending ? '적용 예정' : '예시 생성 기준'}: {draft.companyIds.length}개 회사 · 회사당 {draft.employeeCount}명 · 총 {predicted.toLocaleString('ko-KR')}명{pending ? ' · 데이터 적용을 눌러 반영하세요.' : ''}
{pending ? `적용 예정: ${draft.companyIds.length}개 회사 · 회사당 ${draft.employeeCount}명 · 총 ${predicted.toLocaleString('ko-KR')}명 · 데이터 적용을 눌러 반영하세요.` : ''}
</p>
<p className="hm-orgchart-story-note">회사 숨김·다시 표시는 현재 소속 변경을 유지합니다. 데이터 적용은 선택한 조건의 예시를 새로 만들고, 초기화는 최초 데이터 설정과 소속으로 돌아갑니다.</p>
</fieldset>
</StorySection>
<StorySection title="레이아웃 설정" description="표시 유형 · 초기 Depth">
<StorySection title="레이아웃 설정">
<fieldset className="hm-orgchart-story-settings">
<legend className="hm-orgchart-story-section-legend">레이아웃 설정</legend>
<div className="hm-orgchart-story-controls">
<div className="hm-orgchart-story-instance">
<Label id={`${id}-layout-label`} htmlFor={`${id}-layout`}>조직도 표시 유형</Label>
@@ -180,18 +190,16 @@ function ExpandedPreview() {
<div className="hm-orgchart-story-instance">
<Label id={`${id}-hybrid-label`} htmlFor={`${id}-hybrid`}>혼합형 시작 Depth</Label>
<Select value={String(layout.hybridStartDepth)} disabled={layout.type !== 'hybrid'} onValueChange={value => setLayout(current => ({ ...current, hybridStartDepth: Number(value) as 2 | 3 | 4 }))}>
<SelectTrigger id={`${id}-hybrid`} aria-labelledby={`${id}-hybrid-label`} aria-describedby={`${id}-hybrid-help`} size="medium"><SelectValue /></SelectTrigger>
<SelectTrigger id={`${id}-hybrid`} aria-labelledby={`${id}-hybrid-label`} size="medium"><SelectValue /></SelectTrigger>
<SelectContent>{[2, 3, 4].map(depth => <SelectItem key={depth} value={String(depth)}>Depth {depth}부터 세로 배치</SelectItem>)}</SelectContent>
</Select>
<span id={`${id}-hybrid-help`} className="hm-orgchart-story-note">Hybrid에만 적용합니다.</span>
</div>
<div className="hm-orgchart-story-instance">
<Label id={`${id}-group-hybrid-label`} htmlFor={`${id}-group-hybrid`}>특정 그룹 세로 배치</Label>
<Select value={layout.hybridOrganizationIds?.[0] ?? 'none'} disabled={layout.type !== 'tb' || !applied.companyIds.length} onValueChange={value => setLayout(current => ({ ...current, hybridOrganizationIds: value === 'none' ? [] : [value] }))}>
<SelectTrigger id={`${id}-group-hybrid`} aria-labelledby={`${id}-group-hybrid-label`} aria-describedby={`${id}-group-hybrid-help`} size="medium"><SelectValue /></SelectTrigger>
<SelectTrigger id={`${id}-group-hybrid`} aria-labelledby={`${id}-group-hybrid-label`} size="medium"><SelectValue /></SelectTrigger>
<SelectContent><SelectItem value="none">사용 안 함</SelectItem>{groupChoices.map(organization => <SelectItem key={organization.id} value={organization.id}>{organization.name}</SelectItem>)}</SelectContent>
</Select>
<span id={`${id}-group-hybrid-help`} className="hm-orgchart-story-note">TB에서 선택한 그룹 아래만 적용합니다. LR·Hybrid 전환 시 해제됩니다.</span>
</div>
<div className="hm-orgchart-story-instance">
<Label id={`${id}-depth-label`} htmlFor={`${id}-depth`}>초기 표시 Depth</Label>
@@ -201,17 +209,14 @@ function ExpandedPreview() {
</Select>
</div>
</div>
<p className="hm-orgchart-story-note">Compact: 미지원. 기존 직원 카드 디자인을 보존하며, 압축 배치 선택은 제공하지 않습니다.</p>
</fieldset>
</StorySection>
<StorySection title="인터랙션·직원 정보" description="Drag & Drop · 탐색 · 팝업 방식">
<StorySection title="인터랙션·직원 정보">
<div className="hm-orgchart-story-settings">
<fieldset className="hm-orgchart-story-companies">
<legend>독립 기능 설정 · 모든 표시 유형에서 개별 선택</legend>
<div className="hm-orgchart-story-company">
<Switch id={`${id}-drag-drop`} checked={dragDropEnabled} onCheckedChange={setDragDropEnabled} aria-describedby={`${id}-drag-drop-state`} />
<Switch id={`${id}-drag-drop`} checked={dragDropEnabled} onCheckedChange={setDragDropEnabled} />
<Label htmlFor={`${id}-drag-drop`}>Drag &amp; Drop</Label>
<span id={`${id}-drag-drop-state`}>{dragDropEnabled ? 'ON' : 'OFF'}</span>
</div>
{([
['collapse', '접기·펼치기 사용'], ['zoom', 'Zoom 사용'], ['pan', 'Pan 사용'],
@@ -231,19 +236,6 @@ function ExpandedPreview() {
</div>
</div>
</StorySection>
<StorySection title="조직도 미리보기" description="직원 카드를 눌러 정보 확인">
<div className="hm-orgchart-story">
<p className="hm-orgchart-story-note">빈 공간 드래그는 Pan입니다. {dragDropEnabled ? '직원 카드·그룹 타이틀 드래그로 회사 안팎의 소속을 변경합니다. 키보드: Alt+M → 방향키 → Enter / ESC 취소.' : '소속 변경은 Drag & Drop을 ON으로 설정하면 사용할 수 있습니다.'} 클릭은 직원 정보·그룹 접기입니다.</p>
<p className="hm-orgchart-story-note" data-component="OrgChartExampleCounts" aria-live="polite">
표시 중: {applied.companyIds.length}개 회사 · 현재 소속 기준 직원 합계 {displayedTotal.toLocaleString('ko-KR')}명 / 전체 {sessionTotal.toLocaleString('ko-KR')}명
{companyCountSummary ? ` · ${companyCountSummary}` : ''}
</p>
{error ? <p role="alert" className="hm-orgchart-story-note">{error}</p> : null}
<div className="hm-orgchart-story-preview sb-center">
<OrgChart dataSource={source} stylesheetUrls={compositionStylesheets} displayMode="readOnly" layoutMode="auto" autoLayout={layout} interactions={interactions} dragDropEnabled={dragDropEnabled} selectedCompanyIds={applied.companyIds} companyTopNodes={topNodes} employeePopup={popup} className="hm-orgchart-story-chart" onMove={result => { if (result.moved) setSession({ baseline: dataset, current: result.data }); }} onLifecycleError={cause => setError(cause instanceof Error ? cause.message : '조직도 설정을 적용할 수 없습니다.')} onStateChange={state => { if (state.status === 'error') setError(state.issues.map(issue => issue.message).join(' / ')); }} />
</div>
</div>
</StorySection>
</StoryPage>;
}