diff --git a/code/lib/preview-api/src/modules/addons/hooks.ts b/code/lib/preview-api/src/modules/addons/hooks.ts index ce1643315824..e7a747d571f1 100644 --- a/code/lib/preview-api/src/modules/addons/hooks.ts +++ b/code/lib/preview-api/src/modules/addons/hooks.ts @@ -299,11 +299,36 @@ function useMemoLike(name: string, nextCreate: () => T, deps: any[] | undefin return memoizedState; } -/* Returns a memoized value, see https://reactjs.org/docs/hooks-reference.html#usememo */ +/** + * Returns a memoized value. + * @template T The type of the memoized value. + * @param {() => T} nextCreate A function that returns the memoized value. + * @param {any[]} [deps] An optional array of dependencies. If any of the dependencies change, the memoized value will be recomputed. + * @returns {T} The memoized value. + * @example + * const memoizedValue = useMemo(() => { + * return doExpensiveCalculation(a, b); + * }, [a, b]); + */ export function useMemo(nextCreate: () => T, deps?: any[]): T { return useMemoLike('useMemo', nextCreate, deps); } +/** Returns a memoized callback. + * + * @template T The type of the callback function. + * @param {T} callback The callback function to memoize. + * @param {any[]} [deps] An optional array of dependencies. If any of the dependencies change, the memoized callback will be recomputed. + * @returns {T} The memoized callback. + * + * @example + * const memoizedCallback = useCallback( + * () => { + * doSomething(a, b); + * }, + * [a, b], + * ); + */ /* Returns a memoized callback, see https://reactjs.org/docs/hooks-reference.html#usecallback */ export function useCallback(callback: T, deps?: any[]): T { return useMemoLike('useCallback', () => callback, deps); @@ -313,6 +338,17 @@ function useRefLike(name: string, initialValue: T): { current: T } { return useMemoLike(name, () => ({ current: initialValue }), []); } +/** + * Returns a mutable ref object. + * + * @template T The type of the ref object. + * @param {T} initialValue The initial value of the ref object. + * @returns {{ current: T }} The mutable ref object. + * + * @example + * const ref = useRef(0); + * ref.current = 1; + */ /* Returns a mutable ref object, see https://reactjs.org/docs/hooks-reference.html#useref */ export function useRef(initialValue: T): { current: T } { return useRefLike('useRef', initialValue); @@ -349,14 +385,58 @@ function useStateLike( return [stateRef.current, setState]; } -/* Returns a stateful value, and a function to update it, see https://reactjs.org/docs/hooks-reference.html#usestate */ +/** + * Returns a stateful value and a function to update it. + * + * @template S The type of the state. + * @param {(() => S) | S} initialState The initial state value or a function that returns the initial state value. + * @returns {[S, (update: ((prevState: S) => S) | S) => void]} An array containing the current state value and a function to update it. + * + * @example + * const [count, setCount] = useState(0); + * setCount(count + 1); + */ export function useState( initialState: (() => S) | S ): [S, (update: ((prevState: S) => S) | S) => void] { return useStateLike('useState', initialState); } -/* A redux-like alternative to useState, see https://reactjs.org/docs/hooks-reference.html#usereducer */ +/** + * A redux-like alternative to useState. + * + * @template S The type of the state. + * @template A The type of the action. + * @param {(state: S, action: A) => S} reducer The reducer function that returns the new state. + * @param {S | I} initialArg The initial state value or the initial argument for the init function. + * @param {(initialArg: I) => S} [init] An optional function that returns the initial state value. + * @returns {[S, (action: A) => void]} An array containing the current state value and a function to dispatch actions. + * + * @example + * const initialState = { count: 0 }; + * + * function reducer(state, action) { + * switch (action.type) { + * case 'increment': + * return { count: state.count + 1 }; + * case 'decrement': + * return { count: state.count - 1 }; + * default: + * throw new Error(); + * } + * } + * + * function Counter() { + * const [state, dispatch] = useReducer(reducer, initialState); + * return ( + * <> + * Count: {state.count} + * + * + * + * ); + * } + */ export function useReducer( reducer: (state: S, action: A) => S, initialState: S @@ -377,10 +457,22 @@ export function useReducer( return [state, dispatch]; } -/* - Triggers a side effect, see https://reactjs.org/docs/hooks-reference.html#usestate - Effects are triggered synchronously after rendering the story -*/ +/** + * Triggers a side effect, see https://reactjs.org/docs/hooks-reference.html#usestate + * Effects are triggered synchronously after rendering the story + * + * @param {() => (() => void) | void} create A function that creates the effect. It should return a cleanup function, or nothing. + * @param {any[]} [deps] An optional array of dependencies. If any of the dependencies change, the effect will be re-run. + * @returns {void} + * + * @example + * useEffect(() => { + * // Do something after rendering the story + * return () => { + * // Do something when the component unmounts or the effect is re-run + * }; + * }, [dependency1, dependency2]); + */ export function useEffect(create: () => (() => void) | void, deps?: any[]): void { const hooks = getHooksContextOrThrow(); const effect = useMemoLike('useEffect', () => ({ create }), deps); @@ -397,7 +489,18 @@ export interface EventMap { [eventId: string]: Listener; } -/* Accepts a map of Storybook channel event listeners, returns an emit function */ +/** + * Subscribes to events emitted by the Storybook channel and returns a function to emit events. + * + * @param {EventMap} eventMap A map of event listeners to subscribe to. + * @param {any[]} [deps=[]] An optional array of dependencies. If any of the dependencies change, the event listeners will be re-subscribed. + * @returns {(...args: any[]) => void} A function to emit events to the Storybook channel. + * + * @example + * // Subscribe to an event and emit it + * const emit = useChannel({ 'my-event': (arg1, arg2) => console.log(arg1, arg2) }); + * emit('my-event', 'Hello', 'world!'); + */ export function useChannel(eventMap: EventMap, deps: any[] = []) { const channel = addons.getChannel(); @@ -413,7 +516,18 @@ export function useChannel(eventMap: EventMap, deps: any[] = []) { return useCallback(channel.emit.bind(channel), [channel]); } -/* Returns current story context */ +/** + * Returns the current story context, including the story's ID, parameters, and other metadata. + * + * @template TRenderer The type of the story's renderer. + * @template TArgs The type of the story's args. + * @returns {StoryContext} The current story context. + * + * @example + * const { id, parameters } = useStoryContext(); + * console.log(`Current story ID: ${id}`); + * console.log(`Current story parameters: ${JSON.stringify(parameters)}`); + */ export function useStoryContext< TRenderer extends Renderer, TArgs extends Args = Args @@ -426,7 +540,19 @@ export function useStoryContext< return currentContext; } -/* Returns current value of a story parameter */ +/** + * Returns the value of a specific parameter for the current story, or a default value if the parameter is not set. + * + * @template S The type of the parameter value. + * @param {string} parameterKey The key of the parameter to retrieve. + * @param {S} [defaultValue] An optional default value to return if the parameter is not set. + * @returns {S | undefined} The value of the parameter, or the default value if the parameter is not set. + * + * @example + * // Retrieve the value of a parameter named "myParam" + * const myParamValue = useParameter('myParam', 'default value'); + * console.log(`The value of myParam is: ${myParamValue}`); + */ export function useParameter(parameterKey: string, defaultValue?: S): S | undefined { const { parameters } = useStoryContext(); if (parameterKey) { @@ -435,7 +561,18 @@ export function useParameter(parameterKey: string, defaultValue?: S): S | und return undefined; } -/* Returns current value of story args */ +/** + * Returns the current args for the story, and functions to update and reset them. + * + * @template TArgs The type of the story's args. + * @returns {[TArgs, (newArgs: Partial) => void, (argNames?: (keyof TArgs)[]) => void]} An array containing the current args, a function to update them, and a function to reset them. + * + * @example + * const [args, updateArgs, resetArgs] = useArgs<{ name: string, age: number }>(); + * console.log(`Current args: ${JSON.stringify(args)}`); + * updateArgs({ name: 'John' }); + * resetArgs(['name']); + */ export function useArgs(): [ TArgs, (newArgs: Partial) => void, @@ -457,7 +594,16 @@ export function useArgs(): [ return [args as TArgs, updateArgs, resetArgs]; } -/* Returns current value of global args */ +/** + * Returns the current global args for the story, and a function to update them. + * + * @returns {[Args, (newGlobals: Args) => void]} An array containing the current global args, and a function to update them. + * + * @example + * const [globals, updateGlobals] = useGlobals(); + * console.log(`Current globals: ${JSON.stringify(globals)}`); + * updateGlobals({ theme: 'dark' }); + */ export function useGlobals(): [Args, (newGlobals: Args) => void] { const channel = addons.getChannel(); const { globals } = useStoryContext(); diff --git a/code/lib/preview-api/src/modules/addons/make-decorator.ts b/code/lib/preview-api/src/modules/addons/make-decorator.ts index 8735eeda006f..7dc23881fcae 100644 --- a/code/lib/preview-api/src/modules/addons/make-decorator.ts +++ b/code/lib/preview-api/src/modules/addons/make-decorator.ts @@ -9,6 +9,28 @@ export interface MakeDecoratorOptions { wrapper: Addon_StoryWrapper; } +/** + * Creates a Storybook decorator function that can be used to wrap stories with additional functionality. + * + * @param {MakeDecoratorOptions} options - The options for the decorator. + * @param {string} options.name - The name of the decorator. + * @param {string} options.parameterName - The name of the parameter that will be used to pass options to the decorator. + * @param {Addon_StoryWrapper} options.wrapper - The function that will be used to wrap the story. + * @param {boolean} [options.skipIfNoParametersOrOptions=false] - Whether to skip the decorator if no options or parameters are provided. + * @returns {MakeDecoratorResult} A function that can be used as a Storybook decorator. + * + * @example + * const myDecorator = makeDecorator({ + * name: 'My Decorator', + * parameterName: 'myDecorator', + * wrapper: (storyFn, context, { options }) => { + * const { myOption } = options; + * return
{storyFn()}
; + * }, + * }); + * + * export const decorators = [myDecorator]; + */ export const makeDecorator = ({ name, parameterName, diff --git a/code/lib/preview-api/src/modules/core-client/start.ts b/code/lib/preview-api/src/modules/core-client/start.ts index 8e6f1529fa77..b27f64e48799 100644 --- a/code/lib/preview-api/src/modules/core-client/start.ts +++ b/code/lib/preview-api/src/modules/core-client/start.ts @@ -17,26 +17,63 @@ const removedApi = (name: string) => () => { }; interface CoreClient_RendererImplementation { + /** + * A function that applies decorators to a story. + * @template TRenderer The type of renderer used by the Storybook client API. + * @type {ProjectAnnotations['applyDecorators']} + */ decorateStory?: ProjectAnnotations['applyDecorators']; + /** + * A function that renders a story with args. + * @template TRenderer The type of renderer used by the Storybook client API. + * @type {ArgsStoryFn} + */ render?: ArgsStoryFn; } interface CoreClient_ClientAPIFacade { - /* deprecated */ + /** + * The old way of adding stories at runtime. + * @deprecated This method is deprecated and will be removed in a future version. + */ storiesOf: (...args: any[]) => never; - /* deprecated */ + /** + * The old way of retrieving the list of stories at runtime. + * @deprecated This method is deprecated and will be removed in a future version. + */ raw: (...args: any[]) => never; } interface CoreClient_StartReturnValue { - /* deprecated */ + /** + * Forces a re-render of all stories in the Storybook preview. + * This function emits the `FORCE_RE_RENDER` event to the Storybook channel. + * @deprecated This method is deprecated and will be removed in a future version. + * @returns {void} + */ forceReRender: () => void; - /* deprecated */ + /** + * The old way of setting up storybook with runtime configuration. + * @deprecated This method is deprecated and will be removed in a future version. + * @returns {void} + */ configure: any; - /* deprecated */ + /** + * @deprecated This property is deprecated and will be removed in a future version. + * @type {ClientApi | CoreClient_ClientAPIFacade} + */ clientApi: ClientApi | CoreClient_ClientAPIFacade; } +/** + * Initializes the Storybook preview API. + * @template TRenderer The type of renderer used by the Storybook client API. + * @param {ProjectAnnotations['renderToCanvas']} renderToCanvas A function that renders a story to a canvas. + * @param {CoreClient_RendererImplementation} [options] Optional configuration options for the renderer implementation. + * @param {ProjectAnnotations['applyDecorators']} [options.decorateStory] A function that applies decorators to a story. + * @param {ArgsStoryFn} [options.render] A function that renders a story with arguments. + * @returns {CoreClient_StartReturnValue} An object containing functions and objects related to the Storybook preview API. + */ export function start( renderToCanvas: ProjectAnnotations['renderToCanvas'], { decorateStory, render }: CoreClient_RendererImplementation = {} diff --git a/code/lib/preview-api/src/modules/store/hooks.ts b/code/lib/preview-api/src/modules/store/hooks.ts index 0fee97ba8893..29762cec575a 100644 --- a/code/lib/preview-api/src/modules/store/hooks.ts +++ b/code/lib/preview-api/src/modules/store/hooks.ts @@ -33,6 +33,17 @@ export { useGlobals, }; +/** + * @param {string} sharedId - The ID of the shared state. + * @param {S} [defaultState] - The default state of the shared state. + * @deprecated This API might get dropped, if you are using this, please file an issue. + * @returns {[S, (s: S) => void]} - A tuple containing the current state and a function to update the state. + * @example + * const [state, setState] = useSharedState('my-addon', { count: 0 }); + * console.log(state); // { count: 0 } + * setState({ count: 1 }); + * console.log(state); // { count: 1 } + */ export function useSharedState(sharedId: string, defaultState?: S): [S, (s: S) => void] { const channel = addons.getChannel(); @@ -69,6 +80,17 @@ export function useSharedState(sharedId: string, defaultState?: S): [S, (s: S ]; } +/** + * @param {string} sharedId - The ID of the shared state. + * @param {S} [defaultState] - The default state of the shared state. + * @deprecated This API might get dropped, if you are using this, please file an issue. + * @returns {[S, (s: S) => void]} - A tuple containing the current state and a function to update the state. + * @example + * const [state, setState] = useSharedState('my-addon', { count: 0 }); + * console.log(state); // { count: 0 } + * setState({ count: 1 }); + * console.log(state); // { count: 1 } + */ export function useAddonState(addonId: string, defaultState?: S): [S, (s: S) => void] { return useSharedState(addonId, defaultState); }