mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
docs(site): complete menu radio group references with demos and options hooks (#1807)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
82b9e43bb5
commit
a9a09a7e52
@@ -23,7 +23,9 @@
|
||||
* Core instantiation, sub-parts with/without HTML elements,
|
||||
* React-only parts (no platforms.html), sub-part data-attr
|
||||
* inheritance (stateAttrMap heuristic), non-boolean type
|
||||
* inference (number, string literal union via type alias).
|
||||
* inference (number, string literal union via type alias),
|
||||
* extra @parts-tagged data-attrs files attaching to the
|
||||
* listed parts (gauge-label-data-attrs.ts).
|
||||
* slider/ — Base multi-part component. Exercises: base component whose
|
||||
* parts are re-exported by domain variants.
|
||||
* volume-slider/ — Domain variant. Exercises: re-exported parts from slider,
|
||||
@@ -35,6 +37,13 @@
|
||||
* create* factory, mixin display name stripping, selector discovery,
|
||||
* @label overloads, slug collision (react vs html create-player),
|
||||
* framework assignment.
|
||||
* ui/rate-options/ — Hook re-exported through a directory index
|
||||
* (entry index → ./ui/rate-options → ./use-rate-options), with a
|
||||
* namespace merged onto the function (Props/Result pattern).
|
||||
* Exercises: recursive re-export resolution in util discovery,
|
||||
* entry-visibility filtering (useRateInternals is scanned but never
|
||||
* re-exported to the entry), and skipping re-exports that resolve to
|
||||
* a directory with no index.ts (./legacy holds only compiled JS).
|
||||
*
|
||||
* Features (packages/core/src/dom/store/features/):
|
||||
* playback.ts — Simple feature. Exercises: boolean state properties,
|
||||
@@ -343,6 +352,28 @@ describe('Component pipeline (end-to-end)', () => {
|
||||
expect(label.platforms.react).toEqual({});
|
||||
expect(label.platforms.html).toBeUndefined();
|
||||
});
|
||||
|
||||
// Extra data-attrs files ({component}-{x}-data-attrs.ts, next to the
|
||||
// main {component}-data-attrs.ts) declare their target parts with a
|
||||
// @parts JSDoc tag. This covers attrs that a DOM layer applies to
|
||||
// parts directly, invisible to the per-part stateAttrMap heuristic
|
||||
// (e.g. menu-item-data-attrs.ts applied by create-menu.ts).
|
||||
it('extra @parts-tagged data-attrs file attaches to listed parts', () => {
|
||||
const parts = findComponent('Gauge')!.reference.parts!;
|
||||
|
||||
// label: no other attrs — gets the extra file's attrs
|
||||
expect(parts.label!.dataAttributes['data-emphasized']).toMatchObject({
|
||||
description: 'Present when the value is emphasized.',
|
||||
});
|
||||
|
||||
// fill: extra attrs merge with attrs inherited via stateAttrMap
|
||||
expect(parts.fill!.dataAttributes['data-emphasized']).toBeDefined();
|
||||
expect(parts.fill!.dataAttributes['data-percentage']).toBeDefined();
|
||||
|
||||
// parts not listed in @parts are untouched
|
||||
expect(parts.track!.dataAttributes).toEqual({});
|
||||
expect(parts.indicator!.dataAttributes['data-emphasized']).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
@@ -539,6 +570,39 @@ describe('Util pipeline (end-to-end)', () => {
|
||||
expect(findByName('useFormat', 'react')).toBeDefined();
|
||||
});
|
||||
|
||||
// Entry indexes often re-export hooks through a directory index
|
||||
// (entry index → ./ui/rate-options → ./use-rate-options). Discovery
|
||||
// must follow re-export hops to the declaring module so JSDoc and
|
||||
// overload extraction read the real source file. The fixture also
|
||||
// merges a namespace onto the hook (the repo's Props/Result pattern),
|
||||
// which must not break FunctionNode detection.
|
||||
it('discovers hooks re-exported through a directory index', () => {
|
||||
const entry = findByName('useRateOptions', 'react');
|
||||
expect(entry).toBeDefined();
|
||||
expect(entry!.slug).toBe('use-rate-options');
|
||||
expect(entry!.data.description).toContain('Create rate menu options');
|
||||
|
||||
const overload = entry!.data.overloads[0]!;
|
||||
expect(overload.parameters.props).toBeDefined();
|
||||
expect(overload.parameters.props!.description).toContain('formatRate');
|
||||
});
|
||||
|
||||
// Whole modules are scanned, but only names visible from the entry
|
||||
// point (through named re-exports and local `export *` chains) are
|
||||
// public API. useRateInternals matches the use* convention and lives
|
||||
// in a scanned file, but is never re-exported up to the entry.
|
||||
it('excludes exports that are not visible from the entry point', () => {
|
||||
expect(findByName('useRateInternals', 'react')).toBeUndefined();
|
||||
});
|
||||
|
||||
// rate-options/index.ts re-exports './legacy', which resolves to a
|
||||
// directory with no index.ts (only compiled index.js). Discovery must
|
||||
// skip it — not crash reading a directory — and the unreachable export
|
||||
// stays undocumented.
|
||||
it('skips re-exports that resolve to a directory without index.ts', () => {
|
||||
expect(findByName('useLegacyRate', 'react')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('discovers controllers from HTML entry points', () => {
|
||||
expect(findByName('PlayerController', 'html')).toBeDefined();
|
||||
expect(findByName('SnapshotController', 'html')).toBeDefined();
|
||||
@@ -562,6 +626,13 @@ describe('Util pipeline (end-to-end)', () => {
|
||||
expect(findByName('createPlayer', 'html')).toBeDefined();
|
||||
expect(findByName('createSelector', null)).toBeDefined();
|
||||
});
|
||||
|
||||
it('leaves external re-exports with their canonical entry point', () => {
|
||||
const matches = entries.filter((entry) => entry.data.name === 'createSelector');
|
||||
|
||||
expect(matches).toHaveLength(1);
|
||||
expect(matches[0]).toMatchObject({ slug: 'create-selector', framework: null });
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Data attributes fixture for part-scoped attrs files.
|
||||
*
|
||||
* Exercises: extra data-attrs files ({component}-{x}-data-attrs.ts) declare
|
||||
* their target parts with a @parts JSDoc tag on the exported const. Listed
|
||||
* parts get the attrs merged into whatever they already have — plain attach
|
||||
* (label) and merge with attrs inherited via the stateAttrMap heuristic
|
||||
* (fill). This header's own raw "@parts" mention is a deliberate hazard:
|
||||
* the builder must bind to the tag in the JSDoc block closest to the export,
|
||||
* not the first match anywhere in the file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Data attributes set on caption-like parts.
|
||||
*
|
||||
* @parts label, fill
|
||||
*/
|
||||
export const GaugeLabelDataAttrs = {
|
||||
/** Present when the value is emphasized. */
|
||||
emphasized: 'data-emphasized',
|
||||
} as const;
|
||||
+2
@@ -1,4 +1,6 @@
|
||||
export { usePlayer } from './player/context';
|
||||
export { createPlayer } from './player/create-player';
|
||||
export { useRateOptions } from './ui/rate-options';
|
||||
export { createSelector } from './utils/external';
|
||||
export { mergeProps } from './utils/merge-props';
|
||||
export { useFormat } from './utils/use-format';
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// Resolves to a directory with no index.ts — discovery must skip it, not crash.
|
||||
export { useLegacyRate } from './legacy';
|
||||
export {
|
||||
type RateOption,
|
||||
type RateOptionsProps,
|
||||
type RateOptionsResult,
|
||||
useRateOptions,
|
||||
} from './use-rate-options';
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// Compiled-only module (no TypeScript source): exercises discovery skipping
|
||||
// a re-export specifier that resolves to a directory without index.ts/.tsx.
|
||||
export function useLegacyRate() {
|
||||
return 1;
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
export interface RateOptionsProps {
|
||||
/** Custom formatter for visible rate labels. */
|
||||
formatRate?: ((rate: number) => string) | undefined;
|
||||
/** Whether rate selection is disabled. */
|
||||
disabled?: boolean | undefined;
|
||||
}
|
||||
|
||||
export interface RateOption {
|
||||
rate: number;
|
||||
label: string;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
export interface RateOptionsResult {
|
||||
rate: number;
|
||||
options: RateOption[];
|
||||
setRate: (rate: number) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create rate menu options from the player rate state. Returns `null` when
|
||||
* the rate feature is not configured.
|
||||
*
|
||||
* @param props - Optional `formatRate` and `disabled` overrides.
|
||||
*/
|
||||
export function useRateOptions(props?: RateOptionsProps): RateOptionsResult | null {
|
||||
return props?.disabled ? null : { rate: 1, options: [], setRate: () => {} };
|
||||
}
|
||||
|
||||
export namespace useRateOptions {
|
||||
export type Props = RateOptionsProps;
|
||||
export type Result = RateOptionsResult;
|
||||
export type Option = RateOption;
|
||||
}
|
||||
|
||||
/** Internal helper — matches the use* convention but is never re-exported to the entry point. */
|
||||
export function useRateInternals(): number {
|
||||
return 1;
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
export { createContext as createSelector } from 'react';
|
||||
Reference in New Issue
Block a user