From fb50aaf5d88a98eefed847a28831d08a91e99b2d Mon Sep 17 00:00:00 2001 From: Bill Collins Date: Wed, 3 Sep 2025 11:28:44 +0100 Subject: [PATCH 1/3] Add parameter typings for addon-pseudo-state --- code/addons/pseudo-states/src/constants.ts | 2 -- code/addons/pseudo-states/src/index.ts | 5 +++- .../src/preview/withPseudoState.ts | 10 +------- code/addons/pseudo-states/src/types.ts | 24 +++++++++++++++++++ 4 files changed, 29 insertions(+), 12 deletions(-) create mode 100644 code/addons/pseudo-states/src/types.ts diff --git a/code/addons/pseudo-states/src/constants.ts b/code/addons/pseudo-states/src/constants.ts index a01ae9a77340..6e169a007102 100644 --- a/code/addons/pseudo-states/src/constants.ts +++ b/code/addons/pseudo-states/src/constants.ts @@ -41,5 +41,3 @@ export const PSEUDO_STATES = { link: 'link', target: 'target', } as const; - -export type PseudoState = keyof typeof PSEUDO_STATES; diff --git a/code/addons/pseudo-states/src/index.ts b/code/addons/pseudo-states/src/index.ts index 40fcb0b5511a..b91e16697407 100644 --- a/code/addons/pseudo-states/src/index.ts +++ b/code/addons/pseudo-states/src/index.ts @@ -1,7 +1,10 @@ import { definePreviewAddon } from 'storybook/internal/csf'; import * as addonAnnotations from './preview'; +import type { PseudoTypes } from './types'; export { PARAM_KEY } from './constants'; -export default () => definePreviewAddon(addonAnnotations); +export type { PseudoTypes } from './types'; + +export default () => definePreviewAddon(addonAnnotations); diff --git a/code/addons/pseudo-states/src/preview/withPseudoState.ts b/code/addons/pseudo-states/src/preview/withPseudoState.ts index 75492b5684d3..25d8c8a92d97 100644 --- a/code/addons/pseudo-states/src/preview/withPseudoState.ts +++ b/code/addons/pseudo-states/src/preview/withPseudoState.ts @@ -12,18 +12,10 @@ import type { DecoratorFunction } from 'storybook/internal/types'; import { addons, useEffect, useMemo, useRef } from 'storybook/preview-api'; -import type { PseudoState } from '../constants'; import { PSEUDO_STATES } from '../constants'; +import type { PseudoParameter, PseudoState, PseudoStateConfig } from '../types'; import { rewriteStyleSheet } from './rewriteStyleSheet'; -type PseudoStateConfig = { - [P in PseudoState]?: boolean | string | string[]; -}; - -export interface PseudoParameter extends PseudoStateConfig { - rootSelector?: string; -} - const channel = addons.getChannel(); const shadowHosts = new Set(); diff --git a/code/addons/pseudo-states/src/types.ts b/code/addons/pseudo-states/src/types.ts new file mode 100644 index 000000000000..3215f44105a2 --- /dev/null +++ b/code/addons/pseudo-states/src/types.ts @@ -0,0 +1,24 @@ +import type { PSEUDO_STATES } from './constants'; + +export type PseudoState = keyof typeof PSEUDO_STATES; + +export type PseudoStateConfig = { + [P in PseudoState]?: boolean | string | string[]; +}; + +export interface PseudoParameter extends PseudoStateConfig { + rootSelector?: string; +} + +export interface PseudoParameters { + /** + * Pseudo state configuration + * + * @see https://storybook.js.org/addons/storybook-addon-pseudo-states + */ + pseudo?: PseudoParameter; +} + +export interface PseudoTypes { + parameters: PseudoParameters; +} From 3b70a9bbd2f6f935f5797e56caa86ca454e27b12 Mon Sep 17 00:00:00 2001 From: Bill Collins Date: Mon, 8 Sep 2025 15:03:22 +0100 Subject: [PATCH 2/3] Add rudimentary type tests --- code/addons/pseudo-states/src/types.test.ts | 40 +++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 code/addons/pseudo-states/src/types.test.ts diff --git a/code/addons/pseudo-states/src/types.test.ts b/code/addons/pseudo-states/src/types.test.ts new file mode 100644 index 000000000000..f01ef709ceed --- /dev/null +++ b/code/addons/pseudo-states/src/types.test.ts @@ -0,0 +1,40 @@ +import { describe, it } from 'vitest'; + +import { definePreview } from 'storybook/internal/csf'; + +describe('addon parameters', () => { + // Skip this tests - it's for the type checker only, and the preview import doesn't work in a non-DOM environment + it.skip('are injected to csf factory', async () => { + // Late import to prevent error referencing `Element` + const pseudoAddon = await import('.'); + + // Define preview with psuedo addon + const preview = definePreview({ addons: [pseudoAddon.default()] }); + + preview.meta({ + parameters: { + pseudo: { + // @ts-expect-error focus should be bool/string + focus: 2, + }, + }, + }); + preview.meta({ + parameters: { + pseudo: { + // @ts-expect-error this pseudo state doesn't exist + madeUpKey: true, + }, + }, + }); + // And now for something completely different - valid config + preview.meta({ + parameters: { + pseudo: { + rootSelector: 'body', + focus: true, + }, + }, + }); + }); +}); From 7977df32397e39a223ec9010314fa12665ea0df5 Mon Sep 17 00:00:00 2001 From: Bill Collins Date: Mon, 15 Sep 2025 11:12:15 +0100 Subject: [PATCH 3/3] Set up type testing correctly --- code/addons/pseudo-states/src/types.test-d.ts | 46 +++++++++++++++++++ code/addons/pseudo-states/src/types.test.ts | 40 ---------------- code/addons/pseudo-states/vitest.config.ts | 6 ++- 3 files changed, 51 insertions(+), 41 deletions(-) create mode 100644 code/addons/pseudo-states/src/types.test-d.ts delete mode 100644 code/addons/pseudo-states/src/types.test.ts diff --git a/code/addons/pseudo-states/src/types.test-d.ts b/code/addons/pseudo-states/src/types.test-d.ts new file mode 100644 index 000000000000..dcf7d5d0edcd --- /dev/null +++ b/code/addons/pseudo-states/src/types.test-d.ts @@ -0,0 +1,46 @@ +import { describe, expectTypeOf, it } from 'vitest'; + +import { definePreview } from 'storybook/internal/csf'; + +import pseudoAddon from '.'; + +describe('addon parameters are injected to csf factory', () => { + // Define preview with psuedo addon + const preview = definePreview({ addons: [pseudoAddon()] }); + + it('with invalid value', () => { + const meta = preview.meta({ + parameters: { + pseudo: { + // @ts-expect-error focus should be bool/string + focus: 2, + }, + }, + }); + expectTypeOf(meta.input.parameters!.pseudo).not.toExtend<{ focus: number }>(); + }); + + it('with invalid key', () => { + const meta = preview.meta({ + parameters: { + pseudo: { + // @ts-expect-error this pseudo state doesn't exist + madeUpKey: true, + }, + }, + }); + expectTypeOf(meta.input.parameters!.pseudo).not.toExtend<{ madeUpKey: boolean }>(); + }); + + it('with valid config', () => { + const meta = preview.meta({ + parameters: { + pseudo: { + rootSelector: 'body', + focus: true, + }, + }, + }); + expectTypeOf(meta.input.parameters!.pseudo!).toExtend<{ focus: boolean }>(); + }); +}); diff --git a/code/addons/pseudo-states/src/types.test.ts b/code/addons/pseudo-states/src/types.test.ts deleted file mode 100644 index f01ef709ceed..000000000000 --- a/code/addons/pseudo-states/src/types.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { describe, it } from 'vitest'; - -import { definePreview } from 'storybook/internal/csf'; - -describe('addon parameters', () => { - // Skip this tests - it's for the type checker only, and the preview import doesn't work in a non-DOM environment - it.skip('are injected to csf factory', async () => { - // Late import to prevent error referencing `Element` - const pseudoAddon = await import('.'); - - // Define preview with psuedo addon - const preview = definePreview({ addons: [pseudoAddon.default()] }); - - preview.meta({ - parameters: { - pseudo: { - // @ts-expect-error focus should be bool/string - focus: 2, - }, - }, - }); - preview.meta({ - parameters: { - pseudo: { - // @ts-expect-error this pseudo state doesn't exist - madeUpKey: true, - }, - }, - }); - // And now for something completely different - valid config - preview.meta({ - parameters: { - pseudo: { - rootSelector: 'body', - focus: true, - }, - }, - }); - }); -}); diff --git a/code/addons/pseudo-states/vitest.config.ts b/code/addons/pseudo-states/vitest.config.ts index 7420176b2e46..5347797a6664 100644 --- a/code/addons/pseudo-states/vitest.config.ts +++ b/code/addons/pseudo-states/vitest.config.ts @@ -5,6 +5,10 @@ import { vitestCommonConfig } from '../../vitest.workspace'; export default mergeConfig( vitestCommonConfig, defineConfig({ - // Add custom config here + test: { + typecheck: { + enabled: true, + }, + }, }) );