11import { type CSSProperties , type ReactNode } from "react" ;
2- import { Header1 , Header2 } from "~/components/primitives/Headers" ;
2+ import { CopyableText } from "~/components/primitives/CopyableText" ;
3+ import { Header1 , Header2 , Header3 } from "~/components/primitives/Headers" ;
34import { Paragraph } from "~/components/primitives/Paragraph" ;
45import { cn } from "~/utils/cn" ;
56
67/* Shared scaffolding for storybook pages: one long scrolling page per component
78 (or component group), split into titled sections whose samples sit in a
89 responsive grid of labelled cells. */
910
11+ /**
12+ * The component's own file name, copyable on hover, so a story can be traced
13+ * back to source without hunting. Pass every file a page covers.
14+ */
15+ export function ComponentNames ( { names } : { names : string [ ] } ) {
16+ return (
17+ < div className = "flex flex-wrap items-center gap-x-4 gap-y-1" >
18+ { names . map ( ( name ) => (
19+ < CopyableText key = { name } value = { name } className = "font-mono text-xs text-text-dimmed" />
20+ ) ) }
21+ </ div >
22+ ) ;
23+ }
24+
1025export function StoryPage ( {
1126 title,
27+ componentNames,
1228 description,
1329 children,
1430 className,
1531} : {
1632 title : string ;
33+ /** File names of the components shown, e.g. ["Buttons.tsx"]. */
34+ componentNames ?: string [ ] ;
1735 description ?: string ;
1836 children : ReactNode ;
1937 className ?: string ;
2038} ) {
2139 return (
2240 < div className = { cn ( "flex flex-col gap-10 p-8 pb-24" , className ) } >
23- < div className = "space-y-1" >
41+ < div className = "space-y-1.5 " >
2442 < Header1 > { title } </ Header1 >
43+ { componentNames && componentNames . length > 0 && < ComponentNames names = { componentNames } /> }
2544 { description && < Paragraph variant = "small" > { description } </ Paragraph > }
2645 </ div >
2746 { children }
@@ -31,26 +50,52 @@ export function StoryPage({
3150
3251export function StorySection ( {
3352 title,
53+ componentName,
3454 description,
3555 children,
3656 className,
3757} : {
3858 title : string ;
59+ /** Shown beside the heading when a page covers several component files. */
60+ componentName ?: string ;
3961 description ?: string ;
4062 children : ReactNode ;
4163 className ?: string ;
4264} ) {
4365 return (
4466 < section className = { cn ( "space-y-3" , className ) } >
4567 < div className = "space-y-0.5 border-b border-grid-dimmed pb-2" >
46- < Header2 > { title } </ Header2 >
68+ < div className = "flex flex-wrap items-baseline gap-x-3" >
69+ < Header2 > { title } </ Header2 >
70+ { componentName && (
71+ < CopyableText value = { componentName } className = "font-mono text-xs text-text-dimmed" />
72+ ) }
73+ </ div >
4774 { description && < Paragraph variant = "extra-small" > { description } </ Paragraph > }
4875 </ div >
4976 { children }
5077 </ section >
5178 ) ;
5279}
5380
81+ /** Sub-heading inside a section, for grouping variants of one component. */
82+ export function StorySubSection ( {
83+ title,
84+ children,
85+ className,
86+ } : {
87+ title : string ;
88+ children : ReactNode ;
89+ className ?: string ;
90+ } ) {
91+ return (
92+ < div className = { cn ( "space-y-2" , className ) } >
93+ < Header3 className = "text-text-dimmed" > { title } </ Header3 >
94+ { children }
95+ </ div >
96+ ) ;
97+ }
98+
5499/** Responsive auto-fill grid; tune the cell floor with `min`. */
55100export function StoryGrid ( {
56101 children,
0 commit comments