test(site): replace api-docs-builder design doc with E2E spec tests (#1225)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Darius Cepulis
2026-04-06 17:36:37 -05:00
committed by GitHub
co-authored by Claude
parent 09bb93c723
commit 64f5b5d5da
43 changed files with 1602 additions and 3340 deletions
@@ -0,0 +1,32 @@
/**
* Multi-part component fixture (core).
*
* Exercises: multi-part Props/State extraction, defaultProps merging,
* non-boolean data-attr type inference (string union, number).
*/
export type FillLevel = 'empty' | 'partial' | 'full';
export interface GaugeProps {
/** Minimum value. */
min: number;
/** Maximum value. */
max: number;
/** Custom label for accessibility. */
label: string | ((state: GaugeState) => string);
}
export interface GaugeState {
/** Current value as a percentage (01). */
percentage: number;
/** The fill level. */
fillLevel: FillLevel;
}
export class GaugeCore {
static readonly defaultProps = {
min: 0,
max: 100,
label: '',
};
}
@@ -0,0 +1,10 @@
/**
* CSS vars fixture for multi-part component.
*
* Exercises: CSS custom properties on a multi-part component (assigned to primary part).
*/
export const GaugeCSSVars = {
/** The fill color of the gauge. */
fill: '--media-gauge-fill',
} as const;
@@ -0,0 +1,23 @@
/**
* Data attributes fixture for multi-part component.
*
* Exercises: non-boolean type inference through satisfies StateAttrMap<State>.
* - percentage → number type (shown in output)
* - fillLevel → string literal union (shown in output, expanded from FillLevel alias)
*/
type StateAttrMap<State> = { [Key in keyof State]?: string };
type FillLevel = 'empty' | 'partial' | 'full';
interface GaugeState {
percentage: number;
fillLevel: FillLevel;
}
export const GaugeDataAttrs = {
/** Current percentage as a string. */
percentage: 'data-percentage',
/** The fill level. */
fillLevel: 'data-fill-level',
} as const satisfies StateAttrMap<GaugeState>;
@@ -0,0 +1,22 @@
/**
* NAME_OVERRIDES fixture.
*
* Exercises: The NAME_OVERRIDES map in pipeline.ts. The directory name is
* "pip-button", which kebabToPascal would convert to "PipButton". But the
* override maps it to "PiPButton" (capital P at position 2).
*
* This covers cases where standard kebab-to-PascalCase conversion produces
* the wrong name. The builder uses NAME_OVERRIDES[dirName] ?? kebabToPascal(dirName).
*/
export interface PiPButtonProps {
/** Whether the button is disabled. */
disabled: boolean;
}
export interface PiPButtonState {
/** Whether picture-in-picture is active. */
active: boolean;
}
export class PiPButtonCore {}
@@ -0,0 +1,27 @@
/**
* Base slider component (core).
*
* Exercises: base component whose parts get re-exported by domain variants
* (volume-slider). This component is also discovered on its own.
*/
export interface SliderProps {
/** Minimum slider value. */
min: number;
/** Maximum slider value. */
max: number;
}
export interface SliderState {
/** Current slider value (01). */
value: number;
/** Whether the user is dragging. */
dragging: boolean;
}
export class SliderCore {
static readonly defaultProps = {
min: 0,
max: 100,
};
}
@@ -0,0 +1,19 @@
/**
* Data attributes for slider base (used by re-exported sub-parts).
*
* Exercises: boolean type (omitted) + non-boolean type for re-exported parts.
*/
type StateAttrMap<State> = { [Key in keyof State]?: string };
interface SliderState {
value: number;
dragging: boolean;
}
export const SliderDataAttrs = {
/** The current slider value. */
value: 'data-value',
/** Present when the user is dragging the slider. */
dragging: 'data-dragging',
} as const satisfies StateAttrMap<SliderState>;
@@ -0,0 +1,34 @@
/**
* Single-part component fixture.
*
* Exercises: Props interface, State interface, defaultProps, function-typed prop
* (triggers type abbreviation), @ignore JSDoc (skipped prop), ref prop (auto-skipped),
* required prop (no default, not optional).
*/
export interface ToggleButtonProps {
/** Whether the button is disabled. */
disabled: boolean;
/** Custom label for the button. */
label: string | ((state: ToggleButtonState) => string);
/** @ignore Internal ref — should be excluded from output. */
_internalFlag: boolean;
/** React ref — auto-skipped by the builder. */
ref: unknown;
/** Callback when pressed state changes. */
onPressedChange: (pressed: boolean) => void;
}
export interface ToggleButtonState {
/** Whether the toggle is pressed. */
pressed: boolean;
/** Whether the button is disabled. */
disabled: boolean;
}
export class ToggleButtonCore {
static readonly defaultProps = {
disabled: false,
label: '',
};
}
@@ -0,0 +1,12 @@
/**
* CSS vars fixture for single-part component.
*
* Exercises: CSS custom property extraction with JSDoc descriptions.
*/
export const ToggleButtonCSSVars = {
/** Background color when pressed. */
pressed: '--media-toggle-pressed-bg',
/** Transition duration for the toggle animation. */
transition: '--media-toggle-transition',
} as const;
@@ -0,0 +1,19 @@
/**
* Data attributes fixture for single-part component.
*
* Exercises: boolean type inference (omitted), satisfies StateAttrMap<State> pattern.
*/
type StateAttrMap<State> = { [Key in keyof State]?: string };
interface ToggleButtonState {
pressed: boolean;
disabled: boolean;
}
export const ToggleButtonDataAttrs = {
/** Present when the toggle is pressed. */
pressed: 'data-pressed',
/** Present when the button is disabled. */
disabled: 'data-disabled',
} as const satisfies StateAttrMap<ToggleButtonState>;
@@ -0,0 +1,23 @@
/**
* Domain variant component (core).
*
* Exercises: domain variant components that share base logic (slider/)
* but have their own directory under core/ui/. The builder discovers
* components by directory — this file must exist for volume-slider to be found.
*/
export interface VolumeSliderProps {
/** The orientation of the slider. */
orientation: 'horizontal' | 'vertical';
}
export interface VolumeSliderState {
/** Current volume (01). */
volume: number;
}
export class VolumeSliderCore {
static readonly defaultProps = {
orientation: 'horizontal',
};
}
@@ -0,0 +1,9 @@
/**
* HTML element fixture for multi-part primary part.
*
* Exercises: primary part gets the root element's tagName.
*/
export class GaugeElement {
static readonly tagName = 'media-gauge';
}
@@ -0,0 +1,7 @@
/**
* HTML element fixture for multi-part sub-part.
*/
export class GaugeFillElement {
static readonly tagName = 'media-gauge-fill';
}
@@ -0,0 +1,9 @@
/**
* HTML element fixture for multi-part sub-part.
*
* Exercises: sub-part element file naming convention ({component}-{part}-element.ts).
*/
export class GaugeTrackElement {
static readonly tagName = 'media-gauge-track';
}
@@ -0,0 +1,3 @@
export class SliderElement {
static readonly tagName = 'media-slider';
}
@@ -0,0 +1,3 @@
export class SliderThumbElement {
static readonly tagName = 'media-slider-thumb';
}
@@ -0,0 +1,3 @@
export class SliderTrackElement {
static readonly tagName = 'media-slider-track';
}
@@ -0,0 +1,9 @@
/**
* HTML element fixture for single-part component.
*
* Exercises: static tagName extraction for platforms.html.
*/
export class ToggleButtonElement {
static readonly tagName = 'media-toggle-button';
}
@@ -0,0 +1,3 @@
export class VolumeSliderElement {
static readonly tagName = 'media-volume-slider';
}
@@ -0,0 +1,28 @@
/**
* Sub-part React component that references stateAttrMap.
*
* Exercises:
* 1. Sub-part inheriting shared data-attrs from the component's data-attrs
* file. The builder uses a string search heuristic if the React source
* contains "stateAttrMap", the sub-part gets shared data attributes.
* 2. Sub-part custom React props. The builder extracts own members from the
* `{LocalName}Props` interface (must be `interface`, not `type`).
* `children` and React DOM attributes are excluded.
*/
import type { GaugeDataAttrs } from '../../../../core/src/core/ui/gauge/gauge-data-attrs';
const stateAttrMap = {} as typeof GaugeDataAttrs;
/** The filled portion of the gauge. Renders a `<div>` element. */
export function Fill() {
return null;
}
// Must be `interface` (not `type`) for extractSubPartProps to detect it.
// `children` is auto-excluded by the builder.
export interface FillProps {
/** The color of the fill bar. */
color: string;
children: unknown;
}
@@ -0,0 +1,16 @@
/**
* Primary part React component.
*
* Exercises: primary part detection via `new GaugeCore` instantiation.
* The builder checks React source files for `new {ComponentName}Core\b`.
*/
class GaugeCore {}
/** A visual indicator for the current value. Renders a `<span>` element. */
export function Indicator() {
const core = new GaugeCore();
return null;
}
export type IndicatorProps = {};
@@ -0,0 +1,14 @@
/**
* React-only sub-part (no HTML element counterpart).
*
* Exercises: framework-divergent parts. Parts discovered from index.parts.ts
* always get platforms.react. Parts WITHOUT a matching HTML element file do NOT
* get platforms.html. This part has no gauge-label-element.ts in the HTML dir.
*/
/** An accessible label for the gauge value. Renders a `<span>` element. */
export function Label() {
return null;
}
export type LabelProps = {};
@@ -0,0 +1,13 @@
/**
* Sub-part React component with no special behavior.
*
* Exercises: sub-part that has an HTML element counterpart but no data-attrs reference.
* Gets empty props, state, dataAttributes, cssCustomProperties.
*/
/** The track area of the gauge. Renders a `<div>` element. */
export function Track() {
return null;
}
export type TrackProps = {};
@@ -0,0 +1,14 @@
/**
* React parts index for multi-part component.
*
* Exercises: multi-part detection, local exports for part discovery.
* - Indicator: primary part (instantiates GaugeCore)
* - Track: sub-part with HTML element
* - Fill: sub-part with HTML element and stateAttrMap reference (gets shared data-attrs)
* - Label: React-only part (no HTML element file)
*/
export { Fill, type FillProps } from './gauge-fill';
export { Indicator, type IndicatorProps } from './gauge-indicator';
export { Label, type LabelProps } from './gauge-label';
export { Track, type TrackProps } from './gauge-track';
@@ -0,0 +1,8 @@
/**
* Slider base parts index.
*
* All local exports. volume-slider re-exports Thumb and Track from here.
*/
export { Root, type RootProps } from './slider-root';
export { Thumb, type ThumbProps } from './slider-thumb';
export { Track, type TrackProps } from './slider-track';
@@ -0,0 +1,13 @@
/**
* Primary part of slider instantiates SliderCore.
*/
class SliderCore {}
/** The root slider container. Renders a `<div>` element. */
export function Root() {
const core = new SliderCore();
return null;
}
export type RootProps = {};
@@ -0,0 +1,15 @@
/**
* Slider sub-part that references stateAttrMap.
*
* When volume-slider re-exports this part, data-attrs come from
* the ORIGIN component (slider), not the consuming component (volume-slider).
*/
const stateAttrMap = {};
/** The draggable thumb of the slider. Renders a `<div>` element. */
export function Thumb() {
return null;
}
export type ThumbProps = {};
@@ -0,0 +1,6 @@
/** The track area of the slider. Renders a `<div>` element. */
export function Track() {
return null;
}
export type TrackProps = {};
@@ -0,0 +1,17 @@
/**
* Volume slider parts index re-exports from slider base.
*
* Exercises: re-exported parts from another component.
* - Root: local export (primary part, instantiates VolumeSliderCore)
* - Thumb: re-exported from slider (gets slider's HTML elements + data-attrs)
* - Track: re-exported from slider (gets slider's HTML elements)
*
* Re-exported parts are NEVER primary. Their element files and data-attrs
* are resolved from the ORIGIN component (slider), not the consumer (volume-slider).
*
* Because there are re-exported parts, this always produces multi-part output
* (no single-part fallback).
*/
export { Thumb, type ThumbProps, Track, type TrackProps } from '../slider/index.parts';
export { Root, type RootProps } from './volume-slider-root';
@@ -0,0 +1,13 @@
/**
* Primary part of volume-slider instantiates VolumeSliderCore.
*/
class VolumeSliderCore {}
/** The root volume slider container. Renders a `<div>` element. */
export function Root() {
const core = new VolumeSliderCore();
return null;
}
export type RootProps = {};