Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | 16x 11x 11x 10x 7x 11x 9x 11x 7x 11x 14x 11x 11x 3x | /**
* Story → component-key resolution.
*
* The CLI writes one report per component, named with the camelCase
* component key (e.g. `avatarGroup.json`, `progressIndicator.json`).
* Storybook stories title themselves on a different convention —
* typically "SLDS2 Components/<Display Name>" — so we walk the second
* title segment back to its camelCase form to locate the artifact.
*
* Lives in the compliance package (not in any host) so every consumer
* resolving a story to a per-component file (Storybook addons, MDX doc
* blocks, sandbox routes …) reads from one source of truth.
*/
/**
* Story-like input the resolver accepts. Loose by design: covers prepared
* payloads (`{ title, ... }`), Storybook `meta` objects, and direct calls
* with arbitrary objects that share the same fields.
*/
export interface StoryLike {
title?: string;
componentName?: string;
}
/**
* Storybook manager-api shape used by addon panels — passed in as `api`
* so the resolver can read the active story when no payload is in hand.
*/
export interface StoryApi {
getCurrentStoryData(): unknown;
}
/**
* Lower-camelCase a multi-word display name (e.g. `"Avatar Group"` →
* `"avatarGroup"`). Trims and collapses whitespace; mid-word characters
* are normalized to lowercase so screaming-snake or all-caps display
* names collapse into the canonical key the CLI writes.
*
* Returns `null` when the input has no word characters, so callers can
* key empty/whitespace inputs off the same `null` sentinel as a missing
* `title` segment.
*/
export function toComponentKey(name: string | null | undefined): string | null {
if (!name) return null;
const parts = name.trim().split(/\s+/).filter(Boolean);
if (!parts.length) return null;
return (
parts[0].toLowerCase() +
parts
.slice(1)
.map((p) => p[0].toUpperCase() + p.slice(1).toLowerCase())
.join('')
);
}
/**
* Resolve the component key from either:
* - a manager-api instance with `getCurrentStoryData()` (addons), or
* - a story-like payload (`STORY_PREPARED`, `meta`, etc.)
*
* Strategy: title's second segment first ("SLDS2 Components/<Name>"),
* falling back to `componentName` when titles don't carry a slash.
* Returns `null` when no key can be derived so consumers can render an
* empty-state without throwing.
*/
export function deriveComponentKey(input: StoryApi | StoryLike | null | undefined): string | null {
if (input === null || typeof input !== 'object') return null;
const story =
'getCurrentStoryData' in input && typeof input.getCurrentStoryData === 'function'
? (input.getCurrentStoryData() as StoryLike | null | undefined)
: (input as StoryLike);
if (!story) return null;
const title = typeof story.title === 'string' ? story.title : '';
const segments = title
.split('/')
.map((s) => s.trim())
.filter(Boolean);
const second = segments[1];
if (second) return toComponentKey(second);
return toComponentKey(story.componentName ?? null);
}
|