From 4a153d60911f15fb50ed232a90db1c3faaed238d Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 18 Mar 2025 10:27:04 +0100 Subject: [PATCH 001/104] Remove viewportStoryGlobals feature and legacy viewport tool. Update viewport addon to use modern globals. Clean up related documentation and types. --- code/.storybook/main.ts | 1 - .../addons/viewport/src/legacy/ToolLegacy.tsx | 244 ------------------ .../src/legacy/ViewportAddonParameter.ts | 9 - code/addons/viewport/src/manager.tsx | 4 +- code/addons/viewport/src/preview.ts | 7 +- .../template/stories/parameters.stories.ts | 83 ------ code/core/src/types/modules/core-common.ts | 2 - docs/api/main-config/main-config-features.mdx | 12 - 8 files changed, 2 insertions(+), 360 deletions(-) delete mode 100644 code/addons/viewport/src/legacy/ToolLegacy.tsx delete mode 100644 code/addons/viewport/src/legacy/ViewportAddonParameter.ts delete mode 100644 code/addons/viewport/template/stories/parameters.stories.ts diff --git a/code/.storybook/main.ts b/code/.storybook/main.ts index f32112ae798..986121db460 100644 --- a/code/.storybook/main.ts +++ b/code/.storybook/main.ts @@ -127,7 +127,6 @@ const config = defineMain({ disableTelemetry: true, }, features: { - viewportStoryGlobals: true, backgroundsStoryGlobals: true, developmentModeForBuild: true, }, diff --git a/code/addons/viewport/src/legacy/ToolLegacy.tsx b/code/addons/viewport/src/legacy/ToolLegacy.tsx deleted file mode 100644 index 857943a5b7c..00000000000 --- a/code/addons/viewport/src/legacy/ToolLegacy.tsx +++ /dev/null @@ -1,244 +0,0 @@ -import type { FC, ReactNode } from 'react'; -import React, { Fragment, memo, useEffect, useRef, useState } from 'react'; - -import { IconButton, TooltipLinkList, WithTooltip } from 'storybook/internal/components'; - -import { GrowIcon, TransferIcon } from '@storybook/icons'; - -import memoize from 'memoizerific'; -import { useGlobals, useParameter, useStorybookApi } from 'storybook/manager-api'; -import { Global, styled } from 'storybook/theming'; - -import { PARAM_KEY } from '../constants'; -import { MINIMAL_VIEWPORTS } from '../defaults'; -import { registerShortcuts } from '../shortcuts'; -import type { Styles, ViewportMap, ViewportStyles } from '../types'; -import type { ViewportAddonParameter } from './ViewportAddonParameter'; - -interface ViewportItem { - id: string; - title: string; - styles: Styles; - type: 'desktop' | 'mobile' | 'tablet' | 'other'; - default?: boolean; -} - -const toList = memoize(50)((items: ViewportMap): ViewportItem[] => [ - ...baseViewports, - ...Object.entries(items).map(([id, { name, ...rest }]) => ({ ...rest, id, title: name })), -]); - -const responsiveViewport: ViewportItem = { - id: 'reset', - title: 'Reset viewport', - styles: null, - type: 'other', -}; - -const baseViewports: ViewportItem[] = [responsiveViewport]; - -const toLinks = memoize(50)(( - list: ViewportItem[], - active: LinkBase, - updateGlobals, - close -): Link[] => { - return list - .filter((i) => i.id !== responsiveViewport.id || active.id !== i.id) - .map((i) => { - return { - ...i, - onClick: () => { - updateGlobals({ viewport: i.id }); - close(); - }, - }; - }); -}); - -interface LinkBase { - id: string; - title: string; - right?: ReactNode; - type: 'desktop' | 'mobile' | 'tablet' | 'other'; - styles: ViewportStyles | ((s: ViewportStyles) => ViewportStyles) | null; -} - -interface Link extends LinkBase { - onClick: () => void; -} - -const flip = ({ width, height, ...styles }: ViewportStyles) => ({ - ...styles, - height: width, - width: height, -}); - -const ActiveViewportSize = styled.div({ - display: 'inline-flex', - alignItems: 'center', -}); - -const ActiveViewportLabel = styled.div(({ theme }) => ({ - display: 'inline-block', - textDecoration: 'none', - padding: 10, - fontWeight: theme.typography.weight.bold, - fontSize: theme.typography.size.s2 - 1, - lineHeight: '1', - height: 40, - border: 'none', - borderTop: '3px solid transparent', - borderBottom: '3px solid transparent', - background: 'transparent', -})); - -const IconButtonWithLabel = styled(IconButton)(() => ({ - display: 'inline-flex', - alignItems: 'center', -})); - -const IconButtonLabel = styled.div(({ theme }) => ({ - fontSize: theme.typography.size.s2 - 1, - marginLeft: 10, -})); - -const getStyles = ( - prevStyles: ViewportStyles | undefined, - styles: Styles, - isRotated: boolean -): ViewportStyles | undefined => { - if (styles === null) { - return undefined; - } - - const result = typeof styles === 'function' ? styles(prevStyles) : styles; - return isRotated ? flip(result) : result; -}; - -export const ViewportToolLegacy: FC = memo(function Tool() { - const [globals, updateGlobals] = useGlobals(); - - const { - viewports = MINIMAL_VIEWPORTS, - defaultOrientation, - defaultViewport, - disable, - } = useParameter(PARAM_KEY, {}); - - const list = toList(viewports); - const api = useStorybookApi(); - const [isTooltipVisible, setIsTooltipVisible] = useState(false); - - if (defaultViewport && !list.find((i) => i.id === defaultViewport)) { - console.warn( - `Cannot find "defaultViewport" of "${defaultViewport}" in addon-viewport configs, please check the "viewports" setting in the configuration.` - ); - } - - useEffect(() => { - registerShortcuts(api, globals, updateGlobals, Object.keys(viewports)); - }, [viewports, globals, globals.viewport, updateGlobals, api]); - - useEffect(() => { - const defaultRotated = defaultOrientation === 'landscape'; - - if ( - (defaultViewport && globals.viewport !== defaultViewport) || - (defaultOrientation && globals.viewportRotated !== defaultRotated) - ) { - updateGlobals({ - viewport: defaultViewport, - viewportRotated: defaultRotated, - }); - } - // NOTE: we don't want to re-run this effect when `globals` changes - // due to https://github.com/storybookjs/storybook/issues/26334 - // - // Also, this *will* rerun every time you change story as the parameter is briefly `undefined`. - // This behavior is intentional, if a bit of a happy accident in implementation. - // - // Ultimately this process of "locking in" a parameter value should be - // replaced by https://github.com/storybookjs/storybook/discussions/23347 - // or something similar. - // - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [defaultOrientation, defaultViewport, updateGlobals]); - - const item = - list.find((i) => i.id === globals.viewport) || - list.find((i) => i.id === defaultViewport) || - list.find((i) => i.default) || - responsiveViewport; - - const ref = useRef(); - - const styles = getStyles(ref.current, item.styles, globals.viewportRotated); - - useEffect(() => { - ref.current = styles; - }, [item]); - - if (disable || Object.entries(viewports).length === 0) { - return null; - } - - return ( - - ( - - )} - closeOnOutsideClick - onVisibleChange={setIsTooltipVisible} - > - { - updateGlobals({ viewport: responsiveViewport.id }); - }} - > - - {styles ? ( - - {globals.viewportRotated ? `${item.title} (L)` : `${item.title} (P)`} - - ) : null} - - - - {styles ? ( - - - - {styles.width.replace('px', '')} - - { - updateGlobals({ viewportRotated: !globals.viewportRotated }); - }} - > - - - - {styles.height.replace('px', '')} - - - ) : null} - - ); -}); diff --git a/code/addons/viewport/src/legacy/ViewportAddonParameter.ts b/code/addons/viewport/src/legacy/ViewportAddonParameter.ts deleted file mode 100644 index 1d68f691f90..00000000000 --- a/code/addons/viewport/src/legacy/ViewportAddonParameter.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { ViewportMap } from '../types'; - -// TODO: remove at 9.0 -export interface ViewportAddonParameter { - disable?: boolean; - defaultOrientation?: 'portrait' | 'landscape'; - defaultViewport?: string; - viewports?: ViewportMap; -} diff --git a/code/addons/viewport/src/manager.tsx b/code/addons/viewport/src/manager.tsx index 18b1ff2eeed..1e3f9c3e7ee 100644 --- a/code/addons/viewport/src/manager.tsx +++ b/code/addons/viewport/src/manager.tsx @@ -4,14 +4,12 @@ import { addons, types } from 'storybook/manager-api'; import { ViewportTool } from './components/Tool'; import { ADDON_ID } from './constants'; -import { ViewportToolLegacy } from './legacy/ToolLegacy'; addons.register(ADDON_ID, (api) => { addons.add(ADDON_ID, { title: 'viewport / media-queries', type: types.TOOL, match: ({ viewMode, tabId }) => viewMode === 'story' && !tabId, - render: () => - FEATURES?.viewportStoryGlobals ? : , + render: () => , }); }); diff --git a/code/addons/viewport/src/preview.ts b/code/addons/viewport/src/preview.ts index 448909996c7..fe17da2a7d4 100644 --- a/code/addons/viewport/src/preview.ts +++ b/code/addons/viewport/src/preview.ts @@ -1,11 +1,6 @@ import { PARAM_KEY as KEY } from './constants'; import type { GlobalState } from './types'; -const modern: Record = { +export const initialGlobals: Record = { [KEY]: { value: undefined, isRotated: false }, }; - -// TODO: remove in 9.0 -const legacy = { viewport: 'reset', viewportRotated: false }; - -export const initialGlobals = globalThis.FEATURES?.viewportStoryGlobals ? modern : legacy; diff --git a/code/addons/viewport/template/stories/parameters.stories.ts b/code/addons/viewport/template/stories/parameters.stories.ts deleted file mode 100644 index 4ee419895ae..00000000000 --- a/code/addons/viewport/template/stories/parameters.stories.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { global as globalThis } from '@storybook/global'; - -import { MINIMAL_VIEWPORTS } from '@storybook/addon-viewport'; - -import { expect } from 'storybook/test'; - -// these stories only work with `viewportStoryGlobals` set to false -// because the `default` prop is dropped and because, `values` changed to `options` and is now an object - -const first = Object.keys(MINIMAL_VIEWPORTS)[0]; - -export default { - component: globalThis.Components.Button, - args: { - label: 'Click Me!', - }, - parameters: { - viewport: { - viewports: MINIMAL_VIEWPORTS, - }, - chromatic: { disable: true }, - }, -}; - -export const Basic = { - parameters: {}, -}; - -export const Selected = { - parameters: { - viewport: { - defaultViewport: first, - }, - }, - play: async () => { - const viewportStyles = MINIMAL_VIEWPORTS[first].styles; - const viewportDimensions = { - width: typeof viewportStyles === 'object' && Number.parseInt(viewportStyles!.width, 10), - height: typeof viewportStyles === 'object' && Number.parseInt(viewportStyles!.height, 10), - }; - - const windowDimensions = { - width: window.innerWidth, - height: window.innerHeight, - }; - - await expect(viewportDimensions).toEqual(windowDimensions); - }, - tags: ['!test'], -}; - -export const Orientation = { - parameters: { - viewport: { - defaultViewport: first, - defaultOrientation: 'landscape', - }, - }, -}; - -export const Custom = { - parameters: { - viewport: { - defaultViewport: 'phone', - viewports: { - phone: { - name: 'Phone Width', - styles: { - height: '600px', - width: '100vh', - }, - type: 'mobile', - }, - }, - }, - }, -}; - -export const Disabled = { - parameters: { - viewport: { disable: true }, - }, -}; diff --git a/code/core/src/types/modules/core-common.ts b/code/core/src/types/modules/core-common.ts index b13f868e760..92475f081e0 100644 --- a/code/core/src/types/modules/core-common.ts +++ b/code/core/src/types/modules/core-common.ts @@ -379,8 +379,6 @@ export interface StorybookConfigRaw { /** Enable asynchronous component rendering in React renderer */ experimentalRSC?: boolean; - /** Use globals & globalTypes for configuring the viewport addon */ - viewportStoryGlobals?: boolean; /** Use globals & globalTypes for configuring the backgrounds addon */ backgroundsStoryGlobals?: boolean; /** Set NODE_ENV to development in built Storybooks for better testability and debuggability */ diff --git a/docs/api/main-config/main-config-features.mdx b/docs/api/main-config/main-config-features.mdx index 30ad5fda908..78353082313 100644 --- a/docs/api/main-config/main-config-features.mdx +++ b/docs/api/main-config/main-config-features.mdx @@ -59,18 +59,6 @@ Apply decorators from preview.js before decorators from addons or frameworks. [M {/* prettier-ignore-end */} -## `viewportStoryGlobals` - -Type: `boolean` - -Configures the [Viewports addon](../../essentials/viewport.mdx) to opt-in to the new story globals API for configuring viewports. - -{/* prettier-ignore-start */} - - - -{/* prettier-ignore-end */} - ## `developmentModeForBuild` Type: `boolean` From a920973d5a96e7627741ed85e88a02ddb544b758 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 18 Mar 2025 10:36:39 +0100 Subject: [PATCH 002/104] Refactor backgrounds addon to remove legacy globals and components. Simplify configuration by eliminating backgroundsStoryGlobals feature and related legacy tools. Update documentation accordingly. --- code/.storybook/main.ts | 1 - .../src/legacy/BackgroundSelectorLegacy.tsx | 148 ------------------ .../backgrounds/src/legacy/ColorIcon.tsx | 14 -- .../src/legacy/GridSelectorLegacy.tsx | 39 ----- .../src/legacy/getBackgroundColorByName.ts | 41 ----- .../src/legacy/withBackgroundLegacy.ts | 63 -------- .../backgrounds/src/legacy/withGridLegacy.ts | 61 -------- code/addons/backgrounds/src/manager.tsx | 14 +- code/addons/backgrounds/src/preview.ts | 17 +- .../template/stories/parameters.stories.ts | 86 ---------- code/core/src/types/modules/core-common.ts | 2 - docs/api/main-config/main-config-features.mdx | 12 -- 12 files changed, 4 insertions(+), 494 deletions(-) delete mode 100644 code/addons/backgrounds/src/legacy/BackgroundSelectorLegacy.tsx delete mode 100644 code/addons/backgrounds/src/legacy/ColorIcon.tsx delete mode 100644 code/addons/backgrounds/src/legacy/GridSelectorLegacy.tsx delete mode 100644 code/addons/backgrounds/src/legacy/getBackgroundColorByName.ts delete mode 100644 code/addons/backgrounds/src/legacy/withBackgroundLegacy.ts delete mode 100644 code/addons/backgrounds/src/legacy/withGridLegacy.ts delete mode 100644 code/addons/backgrounds/template/stories/parameters.stories.ts diff --git a/code/.storybook/main.ts b/code/.storybook/main.ts index 986121db460..62dadd0dc51 100644 --- a/code/.storybook/main.ts +++ b/code/.storybook/main.ts @@ -127,7 +127,6 @@ const config = defineMain({ disableTelemetry: true, }, features: { - backgroundsStoryGlobals: true, developmentModeForBuild: true, }, viteFinal: async (viteConfig, { configType }) => { diff --git a/code/addons/backgrounds/src/legacy/BackgroundSelectorLegacy.tsx b/code/addons/backgrounds/src/legacy/BackgroundSelectorLegacy.tsx deleted file mode 100644 index 20aee5b1988..00000000000 --- a/code/addons/backgrounds/src/legacy/BackgroundSelectorLegacy.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import type { FC, ReactElement } from 'react'; -import React, { memo, useCallback, useMemo, useState } from 'react'; - -import { logger } from 'storybook/internal/client-logger'; -import { IconButton, TooltipLinkList, WithTooltip } from 'storybook/internal/components'; - -import { PhotoIcon } from '@storybook/icons'; - -import memoize from 'memoizerific'; -import { useGlobals, useParameter } from 'storybook/manager-api'; - -import { PARAM_KEY as BACKGROUNDS_PARAM_KEY } from '../constants'; -import type { Background } from '../types'; -import { ColorIcon } from './ColorIcon'; -import { getBackgroundColorByName } from './getBackgroundColorByName'; - -export interface DeprecatedGlobalState { - name: string | undefined; - selected: string | undefined; -} - -export interface BackgroundsParameter { - default?: string | null; - disable?: boolean; - values: Background[]; -} - -export interface BackgroundSelectorItem { - id: string; - title: string; - onClick: () => void; - value: string; - active: boolean; - right?: ReactElement; -} - -const createBackgroundSelectorItem = memoize(1000)( - ( - id: string | null, - name: string, - value: string, - hasSwatch: boolean | null, - change: (arg: { selected: string; name: string }) => void, - active: boolean - ): BackgroundSelectorItem => ({ - id: id || name, - title: name, - onClick: () => { - change({ selected: value, name }); - }, - value, - right: hasSwatch ? : undefined, - active, - }) -); - -const getDisplayedItems = memoize(10)(( - backgrounds: Background[], - selectedBackgroundColor: string | null, - change: (arg: { selected: string; name: string }) => void -) => { - const backgroundSelectorItems = backgrounds.map(({ name, value }) => - createBackgroundSelectorItem(null, name, value, true, change, value === selectedBackgroundColor) - ); - - if (selectedBackgroundColor !== 'transparent') { - return [ - createBackgroundSelectorItem('reset', 'Clear background', 'transparent', null, change, false), - ...backgroundSelectorItems, - ]; - } - - return backgroundSelectorItems; -}); - -const DEFAULT_BACKGROUNDS_CONFIG: BackgroundsParameter = { - default: null, - disable: true, - values: [], -}; - -export const BackgroundToolLegacy: FC = memo(function BackgroundSelector() { - const backgroundsConfig = useParameter( - BACKGROUNDS_PARAM_KEY, - DEFAULT_BACKGROUNDS_CONFIG - ); - const [isTooltipVisible, setIsTooltipVisible] = useState(false); - const [globals, updateGlobals] = useGlobals(); - - const globalsBackgroundColor = globals[BACKGROUNDS_PARAM_KEY]?.value; - - const selectedBackgroundColor = useMemo(() => { - return getBackgroundColorByName( - globalsBackgroundColor, - backgroundsConfig.values, - backgroundsConfig.default - ); - }, [backgroundsConfig, globalsBackgroundColor]); - - if (Array.isArray(backgroundsConfig)) { - logger.warn( - 'Addon Backgrounds api has changed in Storybook 6.0. Please refer to the migration guide: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md' - ); - } - - const onBackgroundChange = useCallback( - (value: string | undefined) => { - updateGlobals({ [BACKGROUNDS_PARAM_KEY]: { ...globals[BACKGROUNDS_PARAM_KEY], value } }); - }, - [backgroundsConfig, globals, updateGlobals] - ); - - if (backgroundsConfig.disable) { - return null; - } - - return ( - { - return ( - { - if (selectedBackgroundColor !== selected) { - onBackgroundChange(selected); - } - onHide(); - } - )} - /> - ); - }} - onVisibleChange={setIsTooltipVisible} - > - - - - - ); -}); diff --git a/code/addons/backgrounds/src/legacy/ColorIcon.tsx b/code/addons/backgrounds/src/legacy/ColorIcon.tsx deleted file mode 100644 index 42264caf0ba..00000000000 --- a/code/addons/backgrounds/src/legacy/ColorIcon.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { styled } from 'storybook/theming'; - -export const ColorIcon = styled.span( - ({ background }: { background: string }) => ({ - borderRadius: '1rem', - display: 'block', - height: '1rem', - width: '1rem', - background, - }), - ({ theme }) => ({ - boxShadow: `${theme.appBorderColor} 0 0 0 1px inset`, - }) -); diff --git a/code/addons/backgrounds/src/legacy/GridSelectorLegacy.tsx b/code/addons/backgrounds/src/legacy/GridSelectorLegacy.tsx deleted file mode 100644 index d85490cb004..00000000000 --- a/code/addons/backgrounds/src/legacy/GridSelectorLegacy.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import type { FC } from 'react'; -import React, { memo } from 'react'; - -import { IconButton } from 'storybook/internal/components'; - -import { GridIcon } from '@storybook/icons'; - -import { useGlobals, useParameter } from 'storybook/manager-api'; - -import { PARAM_KEY as BACKGROUNDS_PARAM_KEY } from '../constants'; - -export const GridToolLegacy: FC = memo(function GridSelector() { - const [globals, updateGlobals] = useGlobals(); - - const { grid } = useParameter(BACKGROUNDS_PARAM_KEY, { - grid: { disable: false }, - }); - - if (grid?.disable) { - return null; - } - - const isActive = globals[BACKGROUNDS_PARAM_KEY]?.grid || false; - - return ( - - updateGlobals({ - [BACKGROUNDS_PARAM_KEY]: { ...globals[BACKGROUNDS_PARAM_KEY], grid: !isActive }, - }) - } - > - - - ); -}); diff --git a/code/addons/backgrounds/src/legacy/getBackgroundColorByName.ts b/code/addons/backgrounds/src/legacy/getBackgroundColorByName.ts deleted file mode 100644 index e5fea7ee637..00000000000 --- a/code/addons/backgrounds/src/legacy/getBackgroundColorByName.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { logger } from 'storybook/internal/client-logger'; - -import { dedent } from 'ts-dedent'; - -import type { Background } from '../types'; - -export const getBackgroundColorByName = ( - currentSelectedValue: string, - backgrounds: Background[] = [], - defaultName: string | null | undefined -): string => { - if (currentSelectedValue === 'transparent') { - return 'transparent'; - } - - if (backgrounds.find((background) => background.value === currentSelectedValue)) { - return currentSelectedValue; - } - - if (currentSelectedValue) { - return currentSelectedValue; - } - - const defaultBackground = backgrounds.find((background) => background.name === defaultName); - if (defaultBackground) { - return defaultBackground.value; - } - - if (defaultName) { - const availableColors = backgrounds.map((background) => background.name).join(', '); - logger.warn( - dedent` - Backgrounds Addon: could not find the default color "${defaultName}". - These are the available colors for your story based on your configuration: - ${availableColors}. - ` - ); - } - - return 'transparent'; -}; diff --git a/code/addons/backgrounds/src/legacy/withBackgroundLegacy.ts b/code/addons/backgrounds/src/legacy/withBackgroundLegacy.ts deleted file mode 100644 index 706a4370b22..00000000000 --- a/code/addons/backgrounds/src/legacy/withBackgroundLegacy.ts +++ /dev/null @@ -1,63 +0,0 @@ -import type { DecoratorFunction } from 'storybook/internal/types'; - -import { useEffect, useMemo } from 'storybook/preview-api'; - -import { PARAM_KEY as BACKGROUNDS_PARAM_KEY } from '../constants'; -import { addBackgroundStyle, clearStyles, isReduceMotionEnabled } from '../utils'; -import { getBackgroundColorByName } from './getBackgroundColorByName'; - -export const withBackground: DecoratorFunction = (StoryFn, context) => { - const { globals, parameters } = context; - const globalsBackgroundColor = globals[BACKGROUNDS_PARAM_KEY]?.value; - const backgroundsConfig = parameters[BACKGROUNDS_PARAM_KEY]; - - const selectedBackgroundColor = useMemo(() => { - if (backgroundsConfig.disable) { - return 'transparent'; - } - - return getBackgroundColorByName( - globalsBackgroundColor, - backgroundsConfig.values, - backgroundsConfig.default - ); - }, [backgroundsConfig, globalsBackgroundColor]); - - const isActive = useMemo( - () => selectedBackgroundColor && selectedBackgroundColor !== 'transparent', - [selectedBackgroundColor] - ); - - const selector = - context.viewMode === 'docs' ? `#anchor--${context.id} .docs-story` : '.sb-show-main'; - - const backgroundStyles = useMemo(() => { - const transitionStyle = 'transition: background-color 0.3s;'; - return ` - ${selector} { - background: ${selectedBackgroundColor} !important; - ${isReduceMotionEnabled() ? '' : transitionStyle} - } - `; - }, [selectedBackgroundColor, selector]); - - useEffect(() => { - const selectorId = - context.viewMode === 'docs' - ? `addon-backgrounds-docs-${context.id}` - : `addon-backgrounds-color`; - - if (!isActive) { - clearStyles(selectorId); - return; - } - - addBackgroundStyle( - selectorId, - backgroundStyles, - context.viewMode === 'docs' ? context.id : null - ); - }, [isActive, backgroundStyles, context]); - - return StoryFn(); -}; diff --git a/code/addons/backgrounds/src/legacy/withGridLegacy.ts b/code/addons/backgrounds/src/legacy/withGridLegacy.ts deleted file mode 100644 index cca60097428..00000000000 --- a/code/addons/backgrounds/src/legacy/withGridLegacy.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { DecoratorFunction } from 'storybook/internal/types'; - -import { useEffect, useMemo } from 'storybook/preview-api'; - -import { PARAM_KEY as BACKGROUNDS_PARAM_KEY } from '../constants'; -import { addGridStyle, clearStyles } from '../utils'; - -export const withGrid: DecoratorFunction = (StoryFn, context) => { - const { globals, parameters } = context; - const gridParameters = parameters[BACKGROUNDS_PARAM_KEY].grid; - const isActive = globals[BACKGROUNDS_PARAM_KEY]?.grid === true && gridParameters.disable !== true; - const { cellAmount, cellSize, opacity } = gridParameters; - const isInDocs = context.viewMode === 'docs'; - - const isLayoutPadded = parameters.layout === undefined || parameters.layout === 'padded'; - // 16px offset in the grid to account for padded layout - const defaultOffset = isLayoutPadded ? 16 : 0; - const offsetX = gridParameters.offsetX ?? (isInDocs ? 20 : defaultOffset); - const offsetY = gridParameters.offsetY ?? (isInDocs ? 20 : defaultOffset); - - const gridStyles = useMemo(() => { - const selector = - context.viewMode === 'docs' ? `#anchor--${context.id} .docs-story` : '.sb-show-main'; - - const backgroundSize = [ - `${cellSize * cellAmount}px ${cellSize * cellAmount}px`, - `${cellSize * cellAmount}px ${cellSize * cellAmount}px`, - `${cellSize}px ${cellSize}px`, - `${cellSize}px ${cellSize}px`, - ].join(', '); - - return ` - ${selector} { - background-size: ${backgroundSize} !important; - background-position: ${offsetX}px ${offsetY}px, ${offsetX}px ${offsetY}px, ${offsetX}px ${offsetY}px, ${offsetX}px ${offsetY}px !important; - background-blend-mode: difference !important; - background-image: linear-gradient(rgba(130, 130, 130, ${opacity}) 1px, transparent 1px), - linear-gradient(90deg, rgba(130, 130, 130, ${opacity}) 1px, transparent 1px), - linear-gradient(rgba(130, 130, 130, ${opacity / 2}) 1px, transparent 1px), - linear-gradient(90deg, rgba(130, 130, 130, ${ - opacity / 2 - }) 1px, transparent 1px) !important; - } - `; - }, [cellSize]); - - useEffect(() => { - const selectorId = - context.viewMode === 'docs' - ? `addon-backgrounds-grid-docs-${context.id}` - : `addon-backgrounds-grid`; - if (!isActive) { - clearStyles(selectorId); - return; - } - - addGridStyle(selectorId, gridStyles); - }, [isActive, gridStyles, context]); - - return StoryFn(); -}; diff --git a/code/addons/backgrounds/src/manager.tsx b/code/addons/backgrounds/src/manager.tsx index 058dccbae34..d8b9b9187f2 100644 --- a/code/addons/backgrounds/src/manager.tsx +++ b/code/addons/backgrounds/src/manager.tsx @@ -1,25 +1,15 @@ -import React, { Fragment } from 'react'; +import React from 'react'; import { addons, types } from 'storybook/manager-api'; import { BackgroundTool } from './components/Tool'; import { ADDON_ID } from './constants'; -import { BackgroundToolLegacy } from './legacy/BackgroundSelectorLegacy'; -import { GridToolLegacy } from './legacy/GridSelectorLegacy'; addons.register(ADDON_ID, () => { addons.add(ADDON_ID, { title: 'Backgrounds', type: types.TOOL, match: ({ viewMode, tabId }) => !!(viewMode && viewMode.match(/^(story|docs)$/)) && !tabId, - render: () => - FEATURES?.backgroundsStoryGlobals ? ( - - ) : ( - - - - - ), + render: () => , }); }); diff --git a/code/addons/backgrounds/src/preview.ts b/code/addons/backgrounds/src/preview.ts index 27f9e258a7d..384858eb377 100644 --- a/code/addons/backgrounds/src/preview.ts +++ b/code/addons/backgrounds/src/preview.ts @@ -1,13 +1,8 @@ import { PARAM_KEY as KEY } from './constants'; import { withBackgroundAndGrid } from './decorator'; -import { DEFAULT_BACKGROUNDS } from './defaults'; -import { withBackground } from './legacy/withBackgroundLegacy'; -import { withGrid } from './legacy/withGridLegacy'; import type { Config, GlobalState } from './types'; -export const decorators = globalThis.FEATURES?.backgroundsStoryGlobals - ? [withBackgroundAndGrid] - : [withGrid, withBackground]; +export const decorators = [withBackgroundAndGrid]; export const parameters = { [KEY]: { @@ -17,17 +12,9 @@ export const parameters = { cellAmount: 5, }, disable: false, - // TODO: remove in 9.0 - ...(!globalThis.FEATURES?.backgroundsStoryGlobals && { - values: Object.values(DEFAULT_BACKGROUNDS), - }), } satisfies Partial, }; -const modern: Record = { +export const initialGlobals: Record = { [KEY]: { value: undefined, grid: false }, }; - -export const initialGlobals = globalThis.FEATURES?.backgroundsStoryGlobals - ? modern - : { [KEY]: null }; diff --git a/code/addons/backgrounds/template/stories/parameters.stories.ts b/code/addons/backgrounds/template/stories/parameters.stories.ts deleted file mode 100644 index 16f8b13d66c..00000000000 --- a/code/addons/backgrounds/template/stories/parameters.stories.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { global as globalThis } from '@storybook/global'; - -// these stories only work with `backgroundsStoryGlobals` set to false -// because the `default` prop is dropped and because, `values` changed to `options` and is now an object - -const img = - 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTkwIDQ3NSIgZmlsbD0ibm9uZSI+CiAgPGcgaWQ9ImNvbXBvbmVudC1jb21wb3NpdGlvbiI+CiAgICA8bWFzayBpZD0iY29tcG9uZW50cyIgd2lkdGg9IjcxMSIgaGVpZ2h0PSI2NjciIHg9Ii01MiIgeT0iLTg2IiBtYXNrVW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KICAgICAgPHBhdGggZmlsbD0iI2ZmZiIgZD0iTTY1OC4wMiAxODEuNDAzbC00NC4zMTItLjAwMS0uMDAxIDEzMi4xODVoNDQuMzEzdjQ1LjA2NmgtNDQuMzEybC0uMDAxIDEzMi45MzktODguNjI1LS4wMDEtLjAwMSA4OC42MjZoLTQ1LjA2NnYtNDQuMzEybC00NC4zMTItLjAwMXYtNDQuMzEzaC04Ny44NzJsLS4wMDEgNDQuMzEzLTQ0LjMxMi4wMDF2NDQuMzEyaC00NS4wNjZ2LTQ0LjMxM2gtNDQuMzEzdi00NC4zMTJsLTQzLjU1OC0uMDAxLS4wMDIgNDQuMzEzaC00NS4wNjZsLjAwMS00NC4zMTMtNDMuNTYuMDAxdjQ0LjMxMkgzNi44ODh2LTQ0LjMxM2wtNDMuNTYuMDAxdjg4LjYyNWgtNDUuMDY2VjM1Ny45aDQ0LjMxNHYtNDMuNTU5aC00NC4zMTN2LTQ1LjA2Nmg0NC4zMTNWMTM3LjA5aC00NC4zMTNWOTIuMDI0bDQ0LjMxMy0uMDAxVjQuMTUyaC00NC4zMTN2LTQ1LjA2NkgzNy42NFYzLjM5OEgyMTQuMTRsLS4wMDEtNDQuMzEyaDQ1LjA2NlYzLjM5OGgyMjAuODExbC0uMDAxLTg4LjYyNWg0NS4wNjZsLjAwMSA0NC4zMTNoNDQuMzEybC4wMDEgNDQuMzEyaDQ0LjMxMmwuMDAxIDg4LjYyNWg0NC4zMTJ2ODkuMzh6TTM2Ljg4OCAzNTcuOVYyMjUuNzE2aC00My41NnY0NC4zMTJoLTQ0LjMxMnY0My41Nmw0NC4zMTMtLjAwMVYzNTcuOWg0My41NnptMCA4OC42MjV2LTg3Ljg3MmgtODcuODcydjg3Ljg3Mmg4Ny44NzJ6bTAtMjIxLjU2M1Y5Mi43NzdoLTg3Ljg3MnY0My41Nmw0NC4zMTMtLjAwMXY4OC42MjZoNDMuNTU5ek04MS4yIDQ3LjcxMVY0LjE1SDM2Ljg4OFYtNDAuMTZoLTg3Ljg3MnY0My41Nmg0NC4zMTNWNDcuNzFIODEuMnpNMzYuODg4IDQ5MC44Mzl2LTQzLjU2aC04Ny44NzJ2MTMyLjE4NWg0My41NnYtODguNjI2bDQ0LjMxMi4wMDF6bTg4LjYyNi0zOTguMDYySDgxLjIwMVY0OC40NjRILTYuNjcxdjQzLjU2SDM3LjY0djQ0LjMxMmg4Ny44NzN2LTQzLjU2em0tLjAwMSAxMzIuMTg1di00My41NmwtNDQuMzEyLjAwMVYxMzcuMDlIMzcuNjR2MTMyLjE4NGg0My41NnYtNDQuMzEyaDQ0LjMxMnptMCAyNjUuODc2di00My41NTlIODEuMjAxdi00NC4zMTNIMzcuNjR2MTMyLjE4NWg0My41NnYtNDQuMzEzaDQ0LjMxMnptLjAwMS0xNzcuMjUxdi00My41NTlIMzcuNjQydjEzMi4xODVIODEuMnYtODguNjI2aDQ0LjMxM3ptODguNjI1LTEzMi45MzhsLjAwMS00My41NTlIODEuOTU1bC0uMDAxIDQzLjU1OWg0NC4zMTN2NDQuMzEzaDQzLjU1OXYtNDQuMzEzaDQ0LjMxM3ptMC04OC42MjVWNC4xNTJIODEuOTU0djQzLjU2bDg4LjYyNS0uMDAxLjAwMSA0NC4zMTJoNDMuNTU5em0wIDE3Ny4yNXYtNDMuNTU5SDgxLjk1NHY0My41NTlIMjE0LjE0em0wLTEzMi45Mzh2LTQzLjU2bC00NC4zMTMuMDAxVjQ4LjQ2NEg4MS45NTR2NDMuNTZoNDQuMzEzdjQ0LjMxMmg4Ny44NzJ6bS00NC4zMTMgMjY1Ljg3N3YtNDMuNTZoLTQ0LjMxM2wuMDAxLTQ0LjMxMmgtNDMuNTZ2MTMyLjE4NGw0My41NTkuMDAxdi00NC4zMTNoNDQuMzEzem00NC4zMTMtNDQuMzEzdi04Ny44NzJoLTg3Ljg3MlYzNTcuOWg4Ny44NzJ6bTAgODguNjI2di00My41NmgtODcuODcydjEzMi4xODVoNDMuNTU5di04OC42MjVoNDQuMzEzem04OC42MjYtMjY1Ljg3N2wtLjAwMS00My41NTloLTg3Ljg3MnY0NC4zMTNIMTcwLjU4djQzLjU1OWg4Ny44NzJ2LTQ0LjMxM2g0NC4zMTN6bTAgMzEwLjE4OXYtNDMuNTU5SDE3MC41OHY0My41NTloNDQuMzEybC4wMDEgNDQuMzEzaDQzLjU1OXYtNDQuMzEzaDQ0LjMxM3ptMC04Ny44NzJoLTQ0LjMxM3YtNDQuMzEzSDE3MC41OHY0My41Nmg0NC4zMTJsLjAwMSA0NC4zMTJoODcuODcydi00My41NTl6bTQ0LjMxMi0yMjIuMzE3bC4wMDEtODcuODcySDIxNC44OTJ2NDMuNTZsODguNjI2LS4wMDEuMDAxIDQ0LjMxM2g0My41NTh6TTMwMi43NjQgNDcuNzExVjQuMTVoLTQ0LjMxMmwtLjAwMS00NC4zMTJoLTQzLjU1OVY5Mi4wMjRsNDMuNTYtLjAwMVY0Ny43MWg0NC4zMTJ6bS00NC4zMTMgMjIxLjU2NGwuMDAxLTQzLjU2aC00My41NTl2NDMuNTU5bDQzLjU1OC4wMDF6bTg4LjYyNyA4OC42MjVsLS4wMDEtNDMuNTU5SDIxNC44OTNWMzU3LjloMTMyLjE4NXptLTg3Ljg3My04Ny44NzJoLTQ0LjMxM3Y0My41NTloODcuODcyVjE4MS40MDJsLTQzLjU1OS4wMDF2ODguNjI1ek0zOTEuMzkgNDcuNzExVjQuMTUxaC04Ny44NzJ2NDQuMzEzaC00NC4zMTN2NDMuNTZoODcuODcyVjQ3LjcxaDQ0LjMxM3ptLTg4LjYyNSAzNTQuNTAydi00My41NmgtNDMuNTZ2NDMuNTZoNDMuNTZ6bTQ0LjMxMiAxMzIuOTM4di04Ny44NzJoLTQzLjU1OXY0NC4zMTNoLTQ0LjMxM3Y4Ny44NzJoNDMuNTZsLS4wMDEtNDQuMzEzaDQ0LjMxM3ptNDQuMzEyLTEzMi45MzhsLjAwMS00My41NmgtODcuODcydjg3Ljg3Mmg0My41NTl2LTQ0LjMxMmg0NC4zMTJ6bTAtNDQuMzEzdi04Ny44NzJoLTQ0LjMxMnYtNDQuMzEzaC00My41NTh2ODcuODcyaDQ0LjMxMVYzNTcuOWg0My41NTl6bTQ0LjMxNC0xMzIuOTM4di00My41NkgzMDMuNTE4djQzLjU2aDQ0LjMxMmwuMDAxIDQ0LjMxMmg0My41NTl2LTQ0LjMxMmg0NC4zMTN6bTAgMjIxLjU2M1YzMTQuMzQxaC00My41NTlsLS4wMDEgODguNjI1aC00NC4zMTJsLS4wMDEgNDMuNTU5aDg3Ljg3M3ptNDQuMzEyIDQ0LjMxM3YtODcuODcyaC00My41NTl2NDQuMzEzSDM0Ny44M3Y0My41NTloMTMyLjE4NXptMC0zMTAuMTg5VjEzNy4wOUgzOTEuMzlWOTIuNzc3aC00My41NTl2ODcuODcyaDEzMi4xODR6bTAtODguNjI2VjQ4LjQ2NEgzNDcuODMxdjQzLjU2aDQ0LjMxMnY0NC4zMTJoNDMuNTZWOTIuMDI0aDQ0LjMxMnptLjAwMSAxNzcuMjUxbC0uMDAxLTg3Ljg3MWgtNDMuNTU5djQ0LjMxMmgtNDQuMzEydjg3Ljg3M2w0My41NTktLjAwMXYtNDQuMzEybDQ0LjMxMy0uMDAxem00NC4zMTItMTc3LjI1VjQuMTUySDM5Mi4xNDN2NDMuNTZsODguNjI2LS4wMDF2NDQuMzEzaDQzLjU1OXptNDQuMzEzIDg4LjYyNWwuMDAxLTQzLjU1OWgtNDQuMzE0VjkyLjc3N2gtODcuODcydjQzLjU1OWg0NC4zMTN2NDQuMzEzaDg3Ljg3MnpNNTI0LjMyOCAzNTcuOWwuMDAxLTQzLjU1OWgtNDQuMzE0bC4wMDEtNDQuMzEzaC00My41NmwuMDAxIDEzMi4xODQgNDMuNTU4LjAwMVYzNTcuOWg0NC4zMTN6bTAgMjIxLjU2NFY0NDcuMjc5aC00My41NTl2NDQuMzEzaC00NC4zMTN2NDMuNTU5aDQ0LjMxM3Y0NC4zMTNoNDMuNTU5em00NC4zMTMtODguNjI2di04Ny44NzJoLTQ0LjMxM3YtNDQuMzEybC00My41Ni0uMDAxLjAwMiA4Ny44NzIgNDQuMzExLjAwMXY0NC4zMTJoNDMuNTZ6bTAtNDQzLjEyN3YtODcuODcyaC00NC4zMTN2LTQ0LjMxM2gtNDMuNTU5VjMuMzk4aDQ0LjMxMmwuMDAxIDQ0LjMxM2g0My41NTl6bTAgMTc3LjI1MXYtNDMuNTU5aC04Ny44NzJ2MTMyLjE4NGg0My41NTl2LTg4LjYyNWg0NC4zMTN6bTQ0LjMxMy0xMzIuOTM4VjQuMTUyaC00My41NnY0NC4zMTJoLTQ0LjMxM3Y4Ny44NzJoNDMuNTZWOTIuMDI0aDQ0LjMxM3ptLS4wMDEgMzk4LjgxNFYzNTguNjU0aC04Ny44NzJ2NDMuNTU5aDQ0LjMxM3Y4OC42MjVoNDMuNTU5em0wLTIyMS41NjN2LTg3Ljg3MmgtNDMuNTU5djQ0LjMxMmwtNDQuMzEyLjAwMXY4Ny44NzFoNDMuNTU5di00NC4zMTJoNDQuMzEyem00NC4zMTMgODguNjI1di00My41NTloLTQ0LjMxM3YtNDQuMzEzaC00My41NTl2NDQuMzEzaC00NC4zMTJWMzU3LjloMTMyLjE4NHptMC0xNzcuMjUxVjkyLjc3N2gtODcuODcydjg3Ljg3Mmg4Ny44NzJ6Ij4KICAgICAgPC9wYXRoPgogICAgPC9tYXNrPgogICAgPGcgbWFzaz0idXJsKCNjb21wb25lbnRzKSI+CiAgICAgIDxyZWN0IHdpZHRoPSI1OTAiIGhlaWdodD0iNDc1IiBmaWxsPSJ1cmwoI2dyYWRpZW50LWZpbGwpIiBvcGFjaXR5PSIwLjc1Ij4KICAgICAgPC9yZWN0PgogICAgPC9nPgogIDwvZz4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0id2luZG93LWNsaXAiPgogICAgICA8cGF0aCBmaWxsPSIjZmZmIiBkPSJNMCAwSDU4OFY0NTNIMHoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEgMjEpIj4KICAgICAgPC9wYXRoPgogICAgPC9jbGlwUGF0aD4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iZ3JhZGllbnQtZmlsbCIgeDE9Ijc2IiB4Mj0iNTUzLjUiIHkxPSIyNjkiIHkyPSI0MTUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI0EyNEZCRCI+CiAgICAgIDwvc3RvcD4KICAgICAgPHN0b3Agb2Zmc2V0PSIwLjQ3NyIgc3RvcC1jb2xvcj0iIzM2N0VENiI+CiAgICAgIDwvc3RvcD4KICAgICAgPHN0b3Agb2Zmc2V0PSIwLjk4MiIgc3RvcC1jb2xvcj0iI0UxMjY0RCI+CiAgICAgIDwvc3RvcD4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgPC9kZWZzPgo8L3N2Zz4K'; - -const COLORS = [ - { name: 'red', value: '#FB001D' }, - { name: 'orange', value: '#FB8118' }, - { name: 'yellow', value: '#FCFF0C' }, - { name: 'green', value: '#1AB408' }, - { name: 'blue', value: '#0F0084' }, - { name: 'purple', value: '#620073' }, -]; - -export default { - component: globalThis.Components.Pre, - args: { - text: 'Testing the background', - }, - parameters: { - backgrounds: { - values: COLORS, - }, - chromatic: { disable: true }, - }, -}; - -export const Default = { - parameters: { - backgrounds: { - default: COLORS[0].name, - }, - }, -}; - -export const StorySpecific = { - parameters: { - backgrounds: { - default: 'pink', - values: [ - { name: 'white', value: '#F9F5F1' }, - { name: 'pink', value: '#F99CB4' }, - { name: 'teal', value: '#67CDE8' }, - { name: 'brown', value: '#4D2C10' }, - { name: 'black', value: '#000400' }, - ], - }, - }, -}; - -export const Gradient = { - parameters: { - backgrounds: { - default: 'gradient', - values: [ - { - name: 'gradient', - value: 'linear-gradient(90deg, #CA005E 0%, #863783 50%, #112396)', - }, - ], - }, - }, -}; - -export const Image = { - parameters: { - backgrounds: { - default: 'component-driven', - values: [ - { - name: 'component-driven', - value: `#000 center / cover no-repeat url(data:image/svg+xml;base64,${img})`, - }, - ], - }, - }, -}; - -export const Disabled = { - parameters: { - backgrounds: { disable: true }, - }, -}; diff --git a/code/core/src/types/modules/core-common.ts b/code/core/src/types/modules/core-common.ts index 92475f081e0..3627539992a 100644 --- a/code/core/src/types/modules/core-common.ts +++ b/code/core/src/types/modules/core-common.ts @@ -379,8 +379,6 @@ export interface StorybookConfigRaw { /** Enable asynchronous component rendering in React renderer */ experimentalRSC?: boolean; - /** Use globals & globalTypes for configuring the backgrounds addon */ - backgroundsStoryGlobals?: boolean; /** Set NODE_ENV to development in built Storybooks for better testability and debuggability */ developmentModeForBuild?: boolean; }; diff --git a/docs/api/main-config/main-config-features.mdx b/docs/api/main-config/main-config-features.mdx index 78353082313..3c05e993b10 100644 --- a/docs/api/main-config/main-config-features.mdx +++ b/docs/api/main-config/main-config-features.mdx @@ -35,18 +35,6 @@ Filter args with a "target" on the type from the render function. {/* prettier-ignore-end */} -## `backgroundsStoryGlobals` - -Type: `boolean` - -Configures the [Backgrounds addon](../../essentials/backgrounds.mdx) to opt-in to the new story globals API for configuring backgrounds. - -{/* prettier-ignore-start */} - - - -{/* prettier-ignore-end */} - ## `legacyDecoratorFileOrder` Type: `boolean` From c49c67636eacb19a77320c09f0d4e8c981d0928f Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 18 Mar 2025 11:58:14 +0100 Subject: [PATCH 003/104] Update addon-viewport test to navigate to the correct story path for globals instead of parameters --- code/e2e-tests/addon-viewport.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/e2e-tests/addon-viewport.spec.ts b/code/e2e-tests/addon-viewport.spec.ts index 6f2ba1c4c88..a9fbba86149 100644 --- a/code/e2e-tests/addon-viewport.spec.ts +++ b/code/e2e-tests/addon-viewport.spec.ts @@ -46,7 +46,7 @@ test.describe('addon-viewport', () => { const sbPage = new SbPage(page, expect); // Story parameters/selected is set to small mobile - await sbPage.navigateToStory('addons/viewport/parameters', 'selected'); + await sbPage.navigateToStory('addons/viewport/globals', 'selected'); // Measure the original dimensions of previewRoot const originalDimensions = await sbPage.getCanvasBodyElement().boundingBox(); From 772cfaa8f8ade03e53151fce03d59f8791553df3 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 18 Mar 2025 12:37:07 +0100 Subject: [PATCH 004/104] Update addon-viewport test to reflect that the viewport is uneditable when set via globals, and ensure the toolbar is disabled accordingly. --- code/e2e-tests/addon-viewport.spec.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/code/e2e-tests/addon-viewport.spec.ts b/code/e2e-tests/addon-viewport.spec.ts index a9fbba86149..8ed6a355785 100644 --- a/code/e2e-tests/addon-viewport.spec.ts +++ b/code/e2e-tests/addon-viewport.spec.ts @@ -42,7 +42,7 @@ test.describe('addon-viewport', () => { await expect(adjustedDimensions?.width).not.toBe(originalDimensions?.width); }); - test('viewport should be editable when a default viewport is set', async ({ page }) => { + test('viewport should be uneditable when a viewport is set via globals', async ({ page }) => { const sbPage = new SbPage(page, expect); // Story parameters/selected is set to small mobile @@ -52,15 +52,8 @@ test.describe('addon-viewport', () => { const originalDimensions = await sbPage.getCanvasBodyElement().boundingBox(); await expect(originalDimensions?.width).toBeDefined(); - // Manually select "large mobile" and give it time to adjust - await sbPage.selectToolbar('[title="Change the size of the preview"]', '#list-item-mobile2'); - await new Promise((r) => setTimeout(r, 200)); + const toolbar = page.getByTitle('Change the size of the preview'); - // Measure the adjusted dimensions of previewRoot after clicking the mobile item. - const adjustedDimensions = await sbPage.getCanvasBodyElement().boundingBox(); - await expect(adjustedDimensions?.width).toBeDefined(); - - // Compare the two widths - await expect(adjustedDimensions?.width).not.toBe(originalDimensions?.width); + await expect(toolbar).toBeDisabled(); }); }); From f3622c418460b8742ffde3ee6883e4d36c492140 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 18 Mar 2025 14:13:27 +0100 Subject: [PATCH 005/104] Refactor viewport types and update related components. Removed deprecated function type from styles, adjusted viewport configuration in Tool component, and added shorthand example in globals stories. --- code/addons/viewport/src/components/Tool.tsx | 4 +-- code/addons/viewport/src/types.ts | 29 ++----------------- .../template/stories/globals.stories.ts | 6 ++++ 3 files changed, 10 insertions(+), 29 deletions(-) diff --git a/code/addons/viewport/src/components/Tool.tsx b/code/addons/viewport/src/components/Tool.tsx index 212b7c660b8..07d6cc5252a 100644 --- a/code/addons/viewport/src/components/Tool.tsx +++ b/code/addons/viewport/src/components/Tool.tsx @@ -11,7 +11,7 @@ import { PARAM_KEY as KEY } from '../constants'; import { MINIMAL_VIEWPORTS } from '../defaults'; import { responsiveViewport } from '../responsiveViewport'; import { registerShortcuts } from '../shortcuts'; -import type { Config, GlobalStateUpdate, Viewport, ViewportMap } from '../types'; +import type { GlobalStateUpdate, Viewport, ViewportMap, ViewportParameters } from '../types'; import { ActiveViewportLabel, ActiveViewportSize, @@ -36,7 +36,7 @@ interface PureProps { type Link = Parameters['0']['links'][0]; export const ViewportTool: FC<{ api: API }> = ({ api }) => { - const config = useParameter(KEY); + const config = useParameter(KEY); const [globals, updateGlobals, storyGlobals] = useGlobals(); const [isTooltipVisible, setIsTooltipVisible] = useState(false); diff --git a/code/addons/viewport/src/types.ts b/code/addons/viewport/src/types.ts index 0a99d209f64..896c764f2f4 100644 --- a/code/addons/viewport/src/types.ts +++ b/code/addons/viewport/src/types.ts @@ -1,12 +1,4 @@ -// TODO: remove the function type from styles in 9.0 -export type Styles = ViewportStyles | ((s: ViewportStyles | undefined) => ViewportStyles) | null; - export interface Viewport { - name: string; - styles: Styles; - type: 'desktop' | 'mobile' | 'tablet' | 'other'; -} -export interface ModernViewport { name: string; styles: ViewportStyles; type: 'desktop' | 'mobile' | 'tablet' | 'other'; @@ -19,11 +11,6 @@ export interface ViewportStyles { export type ViewportMap = Record; -export interface Config { - options: Record; - disable: boolean; -} - export type GlobalState = { /** * When set, the viewport is applied and cannot be changed using the toolbar. Must match the key @@ -46,31 +33,19 @@ export interface ViewportParameters { * @see https://storybook.js.org/docs/essentials/viewport#parameters */ viewport: { - /** - * Specifies the default orientation used when viewing a story. Only available if you haven't - * enabled the globals API. - */ - defaultOrientation?: 'landscape' | 'portrait'; - - /** - * Specifies the default viewport used when viewing a story. Must match a key in the viewports - * (or options) object. - */ - defaultViewport?: string; - /** * Remove the addon panel and disable the addon's behavior . If you wish to turn off this addon * for the entire Storybook, you should do so when registering addon-essentials * * @see https://storybook.js.org/docs/essentials/index#disabling-addons */ - disabled?: boolean; + disable?: boolean; /** * Specify the available viewports. The width and height values must include the unit, e.g. * '320px'. */ - viewports?: Viewport; // TODO: use ModernViewport in 9.0 + options: Record; }; } diff --git a/code/addons/viewport/template/stories/globals.stories.ts b/code/addons/viewport/template/stories/globals.stories.ts index ebfcc11f9b6..6b7a03e7210 100644 --- a/code/addons/viewport/template/stories/globals.stories.ts +++ b/code/addons/viewport/template/stories/globals.stories.ts @@ -45,6 +45,12 @@ export const Invalid = { }, }; +export const Shorthand = { + globals: { + viewport: 'phone', + }, +}; + export const NoRationDefined = { globals: { viewport: { From 26a257e01e8d5c16697dc0ae3b20454027ef343f Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 18 Mar 2025 14:15:06 +0100 Subject: [PATCH 006/104] Update viewport types to allow for string values in globals and adjust Tool component to handle new data structure. --- code/addons/viewport/src/components/Tool.tsx | 2 +- code/addons/viewport/src/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/addons/viewport/src/components/Tool.tsx b/code/addons/viewport/src/components/Tool.tsx index 07d6cc5252a..1146f2b91fa 100644 --- a/code/addons/viewport/src/components/Tool.tsx +++ b/code/addons/viewport/src/components/Tool.tsx @@ -42,7 +42,7 @@ export const ViewportTool: FC<{ api: API }> = ({ api }) => { const { options = MINIMAL_VIEWPORTS, disable } = config || {}; const data = globals?.[KEY] || {}; - const viewportName: string = data.value; + const viewportName: string = typeof data === 'string' ? data : data.value; const isRotated: boolean = data.isRotated; const item = options[viewportName] || responsiveViewport; diff --git a/code/addons/viewport/src/types.ts b/code/addons/viewport/src/types.ts index 896c764f2f4..92494dc3475 100644 --- a/code/addons/viewport/src/types.ts +++ b/code/addons/viewport/src/types.ts @@ -56,6 +56,6 @@ export interface ViewportGlobals { * @see https://storybook.js.org/docs/essentials/viewport#globals */ viewport: { - [key: string]: GlobalState; + [key: string]: GlobalState | GlobalState['value']; }; } From aab94d17820a114f317a85967baa68ebd095c7c6 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 18 Mar 2025 14:47:55 +0100 Subject: [PATCH 007/104] Fix Tool component to handle string values for viewport data and update globals stories to use a variable for viewport. --- code/addons/viewport/src/components/Tool.tsx | 4 +++- code/addons/viewport/template/stories/globals.stories.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/code/addons/viewport/src/components/Tool.tsx b/code/addons/viewport/src/components/Tool.tsx index 1146f2b91fa..1272e76955f 100644 --- a/code/addons/viewport/src/components/Tool.tsx +++ b/code/addons/viewport/src/components/Tool.tsx @@ -43,12 +43,14 @@ export const ViewportTool: FC<{ api: API }> = ({ api }) => { const { options = MINIMAL_VIEWPORTS, disable } = config || {}; const data = globals?.[KEY] || {}; const viewportName: string = typeof data === 'string' ? data : data.value; - const isRotated: boolean = data.isRotated; + const isRotated: boolean = typeof data === 'string' ? false : data.isRotated; const item = options[viewportName] || responsiveViewport; const isActive = isTooltipVisible || item !== responsiveViewport; const isLocked = KEY in storyGlobals; + console.log({ viewportName, data, item, isActive, isLocked, options }); + const length = Object.keys(options).length; useEffect(() => { diff --git a/code/addons/viewport/template/stories/globals.stories.ts b/code/addons/viewport/template/stories/globals.stories.ts index 6b7a03e7210..26689ccacb3 100644 --- a/code/addons/viewport/template/stories/globals.stories.ts +++ b/code/addons/viewport/template/stories/globals.stories.ts @@ -47,7 +47,7 @@ export const Invalid = { export const Shorthand = { globals: { - viewport: 'phone', + viewport: first, }, }; From 63a700ccc5eeb5450306442f9a5e1d1760f61268 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 18 Mar 2025 14:49:23 +0100 Subject: [PATCH 008/104] Remove console log from Tool component to clean up code and improve performance. --- code/addons/viewport/src/components/Tool.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/addons/viewport/src/components/Tool.tsx b/code/addons/viewport/src/components/Tool.tsx index 1272e76955f..3b67f5af007 100644 --- a/code/addons/viewport/src/components/Tool.tsx +++ b/code/addons/viewport/src/components/Tool.tsx @@ -49,8 +49,6 @@ export const ViewportTool: FC<{ api: API }> = ({ api }) => { const isActive = isTooltipVisible || item !== responsiveViewport; const isLocked = KEY in storyGlobals; - console.log({ viewportName, data, item, isActive, isLocked, options }); - const length = Object.keys(options).length; useEffect(() => { From 4ef1e15600796eeec5f13480ca0250ccd7da1a58 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 18 Mar 2025 15:20:45 +0100 Subject: [PATCH 009/104] Refactor backgrounds addon to use BackgroundsParameters type, updating related components and stories for improved type safety and consistency. --- .../backgrounds/src/components/Tool.tsx | 4 ++-- code/addons/backgrounds/src/decorator.ts | 10 ++++---- code/addons/backgrounds/src/preview.ts | 6 ++--- code/addons/backgrounds/src/types.ts | 24 +++++-------------- .../template/stories/globals.stories.ts | 6 +++++ 5 files changed, 22 insertions(+), 28 deletions(-) diff --git a/code/addons/backgrounds/src/components/Tool.tsx b/code/addons/backgrounds/src/components/Tool.tsx index ed7d792d8b3..b40808d2b03 100644 --- a/code/addons/backgrounds/src/components/Tool.tsx +++ b/code/addons/backgrounds/src/components/Tool.tsx @@ -8,12 +8,12 @@ import { useGlobals, useParameter } from 'storybook/manager-api'; import { PARAM_KEY as KEY } from '../constants'; import { DEFAULT_BACKGROUNDS } from '../defaults'; -import type { Background, BackgroundMap, Config, GlobalStateUpdate } from '../types'; +import type { Background, BackgroundMap, BackgroundsParameters, GlobalStateUpdate } from '../types'; type Link = Parameters['0']['links'][0]; export const BackgroundTool = memo(function BackgroundSelector() { - const config = useParameter(KEY); + const config = useParameter(KEY); const [globals, updateGlobals, storyGlobals] = useGlobals(); const [isTooltipVisible, setIsTooltipVisible] = useState(false); diff --git a/code/addons/backgrounds/src/decorator.ts b/code/addons/backgrounds/src/decorator.ts index c9eedd22015..23d1fcef69a 100644 --- a/code/addons/backgrounds/src/decorator.ts +++ b/code/addons/backgrounds/src/decorator.ts @@ -4,7 +4,7 @@ import { useEffect } from 'storybook/preview-api'; import { PARAM_KEY as KEY } from './constants'; import { DEFAULT_BACKGROUNDS } from './defaults'; -import type { Config, GridConfig } from './types'; +import type { BackgroundsParameters, GridConfig } from './types'; import { addBackgroundStyle, addGridStyle, clearStyles, isReduceMotionEnabled } from './utils'; const defaultGrid: GridConfig = { @@ -24,14 +24,14 @@ export const withBackgroundAndGrid: DecoratorFunction = (StoryFn, context) => { options = DEFAULT_BACKGROUNDS, disable, grid = defaultGrid, - } = (parameters[KEY] || {}) as Config; + } = (parameters[KEY] || {}) as BackgroundsParameters['backgrounds']; const data = globals[KEY] || {}; - const backgroundName: string | undefined = data.value; + const backgroundName: string | undefined = typeof data === 'string' ? data : data.value; const item = backgroundName ? options[backgroundName] : undefined; - const value = item?.value || 'transparent'; + const value = typeof item === 'string' ? item : item?.value || 'transparent'; - const showGrid = data.grid || false; + const showGrid = typeof data === 'string' ? false : data.grid || false; const shownBackground = !!item && !disable; const backgroundSelector = viewMode === 'docs' ? `#anchor--${id} .docs-story` : '.sb-show-main'; diff --git a/code/addons/backgrounds/src/preview.ts b/code/addons/backgrounds/src/preview.ts index 384858eb377..3242f4f559a 100644 --- a/code/addons/backgrounds/src/preview.ts +++ b/code/addons/backgrounds/src/preview.ts @@ -1,6 +1,6 @@ import { PARAM_KEY as KEY } from './constants'; import { withBackgroundAndGrid } from './decorator'; -import type { Config, GlobalState } from './types'; +import type { BackgroundsParameters, GlobalState } from './types'; export const decorators = [withBackgroundAndGrid]; @@ -12,8 +12,8 @@ export const parameters = { cellAmount: 5, }, disable: false, - } satisfies Partial, -}; + }, +} satisfies Partial; export const initialGlobals: Record = { [KEY]: { value: undefined, grid: false }, diff --git a/code/addons/backgrounds/src/types.ts b/code/addons/backgrounds/src/types.ts index e9d4fd84608..e82bd400204 100644 --- a/code/addons/backgrounds/src/types.ts +++ b/code/addons/backgrounds/src/types.ts @@ -1,3 +1,5 @@ +import type { PARAM_KEY } from './constants'; + export interface Background { name: string; value: string; @@ -13,33 +15,19 @@ export interface GridConfig { offsetY?: number; } -export interface Config { - options: BackgroundMap; - disable: boolean; - grid: GridConfig; -} - export type GlobalState = { value: string | undefined; grid: boolean }; export type GlobalStateUpdate = Partial; export interface BackgroundsParameters { - /** - * Backgrounds configuration - * - * @see https://storybook.js.org/docs/essentials/backgrounds#parameters - */ - backgrounds: { - /** Default background color */ - default?: string; - + [PARAM_KEY]: { /** Remove the addon panel and disable the addon's behavior */ disable?: boolean; /** Configuration for the background grid */ - grid?: Partial; + grid?: GridConfig; /** Available background colors */ - values?: Array; + options?: BackgroundMap; }; } @@ -49,5 +37,5 @@ export interface BackgroundsGlobals { * * @see https://storybook.js.org/docs/essentials/backgrounds#globals */ - backgrounds: GlobalState; + [PARAM_KEY]: GlobalState | GlobalState['value']; } diff --git a/code/addons/backgrounds/template/stories/globals.stories.ts b/code/addons/backgrounds/template/stories/globals.stories.ts index ff6d407a6be..d78ab2b10f9 100644 --- a/code/addons/backgrounds/template/stories/globals.stories.ts +++ b/code/addons/backgrounds/template/stories/globals.stories.ts @@ -22,6 +22,12 @@ export const Set = { }, }; +export const Shorthand = { + globals: { + backgrounds: 'red', + }, +}; + export const SetAndCustom = { parameters: { backgrounds: { From ffd9d5c11407e18e9bf60910384b4e91c2341da8 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 19 Mar 2025 11:12:57 +0100 Subject: [PATCH 010/104] Update migration documentation to include new synchronized configuration for addon viewport and backgrounds, and adjust formatting for consistency. --- MIGRATION.md | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 103ac432c3e..63243f7bc92 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,6 +1,7 @@

Migration

- [From version 8.x to 9.0.0](#from-version-8x-to-900) + - [Addon viewport and addon backgrounds synchronized configuration and use globals](#addon-viewport-and-addon-backgrounds-synchronized-configuration-and-use-globals) - [Manager builder removed alias for `util`, `assert` and `process`](#manager-builder-removed-alias-for-util-assert-and-process) - [Actions addon moved to core](#actions-addon-moved-to-core) - [Dropped support for legacy packages](#dropped-support-for-legacy-packages) @@ -124,17 +125,17 @@ - [Tab addons cannot manually route, Tool addons can filter their visibility via tabId](#tab-addons-cannot-manually-route-tool-addons-can-filter-their-visibility-via-tabid) - [Removed `config` preset](#removed-config-preset-1) - [From version 7.5.0 to 7.6.0](#from-version-750-to-760) - - [CommonJS with Vite is deprecated](#commonjs-with-vite-is-deprecated) - - [Using implicit actions during rendering is deprecated](#using-implicit-actions-during-rendering-is-deprecated) - - [typescript.skipBabel deprecated](#typescriptskipbabel-deprecated) - - [Primary doc block accepts of prop](#primary-doc-block-accepts-of-prop) - - [Addons no longer need a peer dependency on React](#addons-no-longer-need-a-peer-dependency-on-react) + - [CommonJS with Vite is deprecated](#commonjs-with-vite-is-deprecated) + - [Using implicit actions during rendering is deprecated](#using-implicit-actions-during-rendering-is-deprecated) + - [typescript.skipBabel deprecated](#typescriptskipbabel-deprecated) + - [Primary doc block accepts of prop](#primary-doc-block-accepts-of-prop) + - [Addons no longer need a peer dependency on React](#addons-no-longer-need-a-peer-dependency-on-react) - [From version 7.4.0 to 7.5.0](#from-version-740-to-750) - - [`storyStoreV6` and `storiesOf` is deprecated](#storystorev6-and-storiesof-is-deprecated) - - [`storyIndexers` is replaced with `experimental_indexers`](#storyindexers-is-replaced-with-experimental_indexers) + - [`storyStoreV6` and `storiesOf` is deprecated](#storystorev6-and-storiesof-is-deprecated) + - [`storyIndexers` is replaced with `experimental_indexers`](#storyindexers-is-replaced-with-experimental_indexers) - [From version 7.0.0 to 7.2.0](#from-version-700-to-720) - - [Addon API is more type-strict](#addon-api-is-more-type-strict) - - [Addon-controls hideNoControlsWarning parameter is deprecated](#addon-controls-hidenocontrolswarning-parameter-is-deprecated) + - [Addon API is more type-strict](#addon-api-is-more-type-strict) + - [Addon-controls hideNoControlsWarning parameter is deprecated](#addon-controls-hidenocontrolswarning-parameter-is-deprecated) - [From version 6.5.x to 7.0.0](#from-version-65x-to-700) - [7.0 breaking changes](#70-breaking-changes) - [Dropped support for Node 15 and below](#dropped-support-for-node-15-and-below) @@ -160,7 +161,7 @@ - [Deploying build artifacts](#deploying-build-artifacts) - [Dropped support for file URLs](#dropped-support-for-file-urls) - [Serving with nginx](#serving-with-nginx) - - [Ignore story files from node_modules](#ignore-story-files-from-node_modules) + - [Ignore story files from node\_modules](#ignore-story-files-from-node_modules) - [7.0 Core changes](#70-core-changes) - [7.0 feature flags removed](#70-feature-flags-removed) - [Story context is prepared before for supporting fine grained updates](#story-context-is-prepared-before-for-supporting-fine-grained-updates) @@ -174,7 +175,7 @@ - [Addon-interactions: Interactions debugger is now default](#addon-interactions-interactions-debugger-is-now-default) - [7.0 Vite changes](#70-vite-changes) - [Vite builder uses Vite config automatically](#vite-builder-uses-vite-config-automatically) - - [Vite cache moved to node_modules/.cache/.vite-storybook](#vite-cache-moved-to-node_modulescachevite-storybook) + - [Vite cache moved to node\_modules/.cache/.vite-storybook](#vite-cache-moved-to-node_modulescachevite-storybook) - [7.0 Webpack changes](#70-webpack-changes) - [Webpack4 support discontinued](#webpack4-support-discontinued) - [Babel mode v7 exclusively](#babel-mode-v7-exclusively) @@ -225,7 +226,7 @@ - [Dropped addon-docs manual babel configuration](#dropped-addon-docs-manual-babel-configuration) - [Dropped addon-docs manual configuration](#dropped-addon-docs-manual-configuration) - [Autoplay in docs](#autoplay-in-docs) - - [Removed STORYBOOK_REACT_CLASSES global](#removed-storybook_react_classes-global) + - [Removed STORYBOOK\_REACT\_CLASSES global](#removed-storybook_react_classes-global) - [7.0 Deprecations and default changes](#70-deprecations-and-default-changes) - [storyStoreV7 enabled by default](#storystorev7-enabled-by-default) - [`Story` type deprecated](#story-type-deprecated) @@ -440,6 +441,15 @@ ## From version 8.x to 9.0.0 +### Addon viewport and addon backgrounds synchronized configuration and use globals + +The feature flags: `viewportStoryGlobals` and `backgroundsStoryGlobals` have been removed, please remove these from your `.storybook/main.ts` file. + +See here for the ways you have to configure addon viewports & backgrounds: + +- [New parameters format for addon backgrounds](#new-parameters-format-for-addon-backgrounds) +- [New parameters format for addon viewport](#new-parameters-format-for-addon-viewport) + ### Manager builder removed alias for `util`, `assert` and `process` These dependencies (often used accidentally) were polyfilled to mocks or browser equivalents by storybook's manager builder. From 21dc99a2887cb58b1862b67b6cc87bdf81668c50 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 19 Mar 2025 11:51:01 +0100 Subject: [PATCH 011/104] Remove @storybook/addon-docs from essentials package, including its related files and references in configuration. Update README and types to reflect the changes. --- code/addons/essentials/README.md | 3 +-- code/addons/essentials/package.json | 19 ++----------------- code/addons/essentials/src/docs/manager.ts | 1 - .../essentials/src/docs/mdx-react-shim.ts | 1 - code/addons/essentials/src/docs/preset.ts | 11 ----------- code/addons/essentials/src/docs/preview.ts | 1 - code/addons/essentials/src/preset.ts | 18 +----------------- code/addons/essentials/src/types.ts | 2 -- scripts/tasks/sandbox-parts.ts | 1 - 9 files changed, 4 insertions(+), 53 deletions(-) delete mode 100644 code/addons/essentials/src/docs/manager.ts delete mode 100644 code/addons/essentials/src/docs/mdx-react-shim.ts delete mode 100644 code/addons/essentials/src/docs/preset.ts delete mode 100644 code/addons/essentials/src/docs/preview.ts diff --git a/code/addons/essentials/README.md b/code/addons/essentials/README.md index a30868ca891..1f182cec1f8 100644 --- a/code/addons/essentials/README.md +++ b/code/addons/essentials/README.md @@ -10,7 +10,6 @@ Storybook essentials includes the following addons. Addons can be disabled and r - [Backgrounds](https://github.com/storybookjs/storybook/tree/next/code/addons/backgrounds) - [Controls](https://github.com/storybookjs/storybook/tree/next/code/addons/controls) -- [Docs](https://github.com/storybookjs/storybook/tree/next/code/addons/docs) - [Viewport](https://github.com/storybookjs/storybook/tree/next/code/addons/viewport) - [Toolbars](https://github.com/storybookjs/storybook/tree/next/code/addons/toolbars) - [Measure](https://github.com/storybookjs/storybook/tree/next/code/addons/measure) @@ -53,4 +52,4 @@ export default { }; ``` -Valid addon keys include: `backgrounds`, `controls`, `docs`, `viewport`, `toolbars`. +Valid addon keys include: `backgrounds`, `controls`, `viewport`, `toolbars`. diff --git a/code/addons/essentials/package.json b/code/addons/essentials/package.json index 2ef9db749eb..3ebf172e816 100644 --- a/code/addons/essentials/package.json +++ b/code/addons/essentials/package.json @@ -39,14 +39,6 @@ }, "./backgrounds/manager": "./dist/backgrounds/manager.js", "./controls/manager": "./dist/controls/manager.js", - "./docs/manager": "./dist/docs/manager.js", - "./docs/preview": { - "types": "./dist/docs/preview.d.ts", - "import": "./dist/docs/preview.mjs", - "require": "./dist/docs/preview.js" - }, - "./docs/preset": "./dist/docs/preset.js", - "./docs/mdx-react-shim": "./dist/docs/mdx-react-shim.js", "./highlight/preview": { "types": "./dist/highlight/preview.d.ts", "import": "./dist/highlight/preview.mjs", @@ -101,7 +93,6 @@ "dependencies": { "@storybook/addon-backgrounds": "workspace:*", "@storybook/addon-controls": "workspace:*", - "@storybook/addon-docs": "workspace:*", "@storybook/addon-highlight": "workspace:*", "@storybook/addon-measure": "workspace:*", "@storybook/addon-outline": "workspace:*", @@ -120,20 +111,15 @@ }, "bundler": { "nodeEntries": [ - "./src/preset.ts", - "./src/docs/preset.ts", - "./src/docs/mdx-react-shim.ts" + "./src/preset.ts" ], "exportEntries": [ "./src/index.ts" ], - "entries": [ - "./src/docs/manager.ts" - ], + "entries": [], "managerEntries": [ "./src/backgrounds/manager.ts", "./src/controls/manager.ts", - "./src/docs/manager.ts", "./src/measure/manager.ts", "./src/outline/manager.ts", "./src/toolbars/manager.ts", @@ -142,7 +128,6 @@ "previewEntries": [ "./src/preview.ts", "./src/backgrounds/preview.ts", - "./src/docs/preview.ts", "./src/highlight/preview.ts", "./src/measure/preview.ts", "./src/outline/preview.ts", diff --git a/code/addons/essentials/src/docs/manager.ts b/code/addons/essentials/src/docs/manager.ts deleted file mode 100644 index 9f14a38904c..00000000000 --- a/code/addons/essentials/src/docs/manager.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@storybook/addon-docs/manager'; diff --git a/code/addons/essentials/src/docs/mdx-react-shim.ts b/code/addons/essentials/src/docs/mdx-react-shim.ts deleted file mode 100644 index cd32a767005..00000000000 --- a/code/addons/essentials/src/docs/mdx-react-shim.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@storybook/addon-docs/dist/shims/mdx-react-shim'; diff --git a/code/addons/essentials/src/docs/preset.ts b/code/addons/essentials/src/docs/preset.ts deleted file mode 100644 index 1d6ed6fbbc5..00000000000 --- a/code/addons/essentials/src/docs/preset.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { dirname, join } from 'node:path'; - -export * from '@storybook/addon-docs/dist/preset'; - -export const mdxLoaderOptions = async (config: any) => { - config.mdxCompileOptions.providerImportSource = join( - dirname(require.resolve('@storybook/addon-docs/package.json')), - '/dist/shims/mdx-react-shim.mjs' - ); - return config; -}; diff --git a/code/addons/essentials/src/docs/preview.ts b/code/addons/essentials/src/docs/preview.ts deleted file mode 100644 index 176a03c1f83..00000000000 --- a/code/addons/essentials/src/docs/preview.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@storybook/addon-docs/dist/preview'; diff --git a/code/addons/essentials/src/preset.ts b/code/addons/essentials/src/preset.ts index 70207422661..173a6963316 100644 --- a/code/addons/essentials/src/preset.ts +++ b/code/addons/essentials/src/preset.ts @@ -19,13 +19,6 @@ interface PresetOptions { * @see https://storybook.js.org/addons/@storybook/addon-controls */ controls?: boolean; - /** - * Allow to use @storybook/addon-docs - * - * @default true - * @see https://storybook.js.org/addons/@storybook/addon-docs - */ - docs?: boolean; /** * Allow to use @storybook/addon-measure * @@ -80,16 +73,7 @@ export function addons(options: PresetOptions) { const main = requireMain(options.configDir); // NOTE: The order of these addons is important. - return [ - 'controls', - 'docs', - 'backgrounds', - 'viewport', - 'toolbars', - 'measure', - 'outline', - 'highlight', - ] + return ['controls', 'backgrounds', 'viewport', 'toolbars', 'measure', 'outline', 'highlight'] .filter((key) => (options as any)[key] !== false) .filter((addon) => !checkInstalled(addon, main)) .map((addon) => { diff --git a/code/addons/essentials/src/types.ts b/code/addons/essentials/src/types.ts index 648adc607fe..2edf2c1433c 100644 --- a/code/addons/essentials/src/types.ts +++ b/code/addons/essentials/src/types.ts @@ -1,5 +1,4 @@ import type { BackgroundsParameters } from '@storybook/addon-backgrounds'; -import type { DocsParameters } from '@storybook/addon-docs'; import type { HighlightParameters } from '@storybook/addon-highlight'; import type { MeasureParameters } from '@storybook/addon-measure'; import type { OutlineParameters } from '@storybook/addon-outline'; @@ -7,7 +6,6 @@ import type { ViewportParameters } from '@storybook/addon-viewport'; export interface EssentialsParameters extends BackgroundsParameters, - DocsParameters, HighlightParameters, MeasureParameters, OutlineParameters, diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index 3dd496181e5..e464597741b 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -49,7 +49,6 @@ export const essentialsAddons = [ 'actions', 'backgrounds', 'controls', - 'docs', 'highlight', 'measure', 'outline', From 82395d36d0edb8a5f8472d1e11e7c8d61cfe6f7c Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 19 Mar 2025 11:52:18 +0100 Subject: [PATCH 012/104] Remove unused docsAddon import from essentials preview configuration to streamline the code. --- code/addons/essentials/src/preview.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/code/addons/essentials/src/preview.ts b/code/addons/essentials/src/preview.ts index 6a21b5abcb3..212e4ddd827 100644 --- a/code/addons/essentials/src/preview.ts +++ b/code/addons/essentials/src/preview.ts @@ -1,7 +1,4 @@ import backgroundsAddon from '@storybook/addon-backgrounds'; -// We can't use docs as function yet because of the --test flag. Once we figure out disabling docs properly in CSF4, we can change this -// eslint-disable-next-line import/namespace -import * as docsAddon from '@storybook/addon-docs/preview'; import highlightAddon from '@storybook/addon-highlight'; import measureAddon from '@storybook/addon-measure'; import outlineAddon from '@storybook/addon-outline'; @@ -10,8 +7,6 @@ import viewportAddon from '@storybook/addon-viewport'; import { composeConfigs } from 'storybook/preview-api'; export default composeConfigs([ - // TODO: we can't use this as function because of the --test flag - docsAddon, backgroundsAddon(), viewportAddon(), measureAddon(), From c7895260287b8ad4a3d38b14339f99d5708e0102 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 19 Mar 2025 11:57:15 +0100 Subject: [PATCH 013/104] Refactor baseGenerator to conditionally add @storybook/addon-docs based on selected features, streamlining the addon installation process. --- .../create-storybook/src/generators/baseGenerator.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/code/lib/create-storybook/src/generators/baseGenerator.ts b/code/lib/create-storybook/src/generators/baseGenerator.ts index 274e3171985..a69b6f99e2f 100644 --- a/code/lib/create-storybook/src/generators/baseGenerator.ts +++ b/code/lib/create-storybook/src/generators/baseGenerator.ts @@ -271,10 +271,6 @@ export async function baseGenerator( const compiler = webpackCompiler ? webpackCompiler({ builder }) : undefined; - const essentials = features.includes('docs') - ? '@storybook/addon-essentials' - : { name: '@storybook/addon-essentials', options: { docs: false } }; - const extraAddonsToInstall = typeof extraAddonPackages === 'function' ? await extraAddonPackages({ @@ -286,10 +282,15 @@ export async function baseGenerator( // TODO: change the semver range to '^4' when VTA 4 and SB 9 is released extraAddonsToInstall.push('@chromatic-com/storybook@^4.0.0-0'); + // Add @storybook/addon-docs when docs feature is selected + if (features.includes('docs')) { + extraAddonsToInstall.push('@storybook/addon-docs'); + } + // added to main.js const addons = [ ...(compiler ? [`@storybook/addon-webpack5-compiler-${compiler}`] : []), - essentials, + '@storybook/addon-essentials', ...stripVersions(extraAddonsToInstall), ].filter(Boolean); From ddf72a73a7423ac40aab45bb3deba30296b72b80 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 19 Mar 2025 12:07:38 +0100 Subject: [PATCH 014/104] Update sandbox-parts to filter out @storybook/addon-docs from addons when documentation is disabled, enhancing configuration clarity. --- scripts/tasks/sandbox-parts.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index e464597741b..4a7acf9ef68 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -864,9 +864,7 @@ export const extendMain: Task['run'] = async ({ template, sandboxDir, key }, { d // Simulate Storybook Lite if (disableDocs) { const addons = mainConfig.getFieldValue(['addons']); - const addonsNoDocs = addons.map((addon: any) => - addon !== '@storybook/addon-essentials' ? addon : { name: addon, options: { docs: false } } - ); + const addonsNoDocs = addons.filter((addon: any) => addon !== '@storybook/addon-docs'); mainConfig.setFieldValue(['addons'], addonsNoDocs); // remove the docs options so that docs tags are ignored From 3e5ec2e4874e3e26c959770f0104c1f2a6509cc7 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 19 Mar 2025 13:45:17 +0100 Subject: [PATCH 015/104] Add migration for @storybook/addon-docs removal from essentials Implement a migration that checks for the presence of @storybook/addon-essentials in the configuration. If found, it either removes the docs configuration if disabled or installs @storybook/addon-docs if enabled. Update the main configuration accordingly. Add tests to ensure correct functionality. --- .../addon-essentials-remove-docs.test.ts | 273 ++++++++++++++++++ .../fixes/addon-essentials-remove-docs.ts | 124 ++++++++ .../src/automigrate/fixes/index.ts | 2 + code/yarn.lock | 1 - 4 files changed, 399 insertions(+), 1 deletion(-) create mode 100644 code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.test.ts create mode 100644 code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.ts diff --git a/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.test.ts b/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.test.ts new file mode 100644 index 00000000000..e5b15e331a6 --- /dev/null +++ b/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.test.ts @@ -0,0 +1,273 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { getStorybookVersionSpecifier } from 'storybook/internal/cli'; +import type { JsPackageManager, PackageJson } from 'storybook/internal/common'; +import type { StorybookConfigRaw } from 'storybook/internal/types'; + +import type { CheckOptions, RunOptions } from '../types'; +import { addonEssentialsRemoveDocs } from './addon-essentials-remove-docs'; + +// Mock modules before any other imports or declarations +vi.mock('node:fs/promises', async () => { + return { + readFile: vi.fn(), + lstat: vi.fn(), + }; +}); + +vi.mock('../helpers/mainConfigFile', () => { + const updateMainConfig = vi.fn().mockImplementation(({ mainConfigPath, dryRun }, callback) => { + return callback(mockConfigs.get(mainConfigPath)); + }); + return { updateMainConfig }; +}); + +vi.mock('storybook/internal/cli', () => ({ + getStorybookVersionSpecifier: vi.fn(), +})); + +// Mock ConfigFile type +interface MockConfigFile { + getFieldValue: (path: string[]) => any; + setFieldValue: (path: string[], value: any) => void; + appendValueToArray: (path: string[], value: any) => void; + _ast: Record; + _code: string; + _exports: Record; + _exportDecls: unknown[]; +} + +// Store mock configs by path +const mockConfigs = new Map(); + +// Get reference to mocked readFile +const readFileMock = vi.mocked(await import('node:fs/promises')).readFile; + +const mockPackageManager = { + retrievePackageJson: vi.fn(), + addDependencies: vi.fn(), +} as unknown as JsPackageManager; + +const mockPackageJson = { + dependencies: {}, + devDependencies: {}, +} as PackageJson; + +const baseCheckOptions: CheckOptions = { + packageManager: mockPackageManager, + mainConfig: { + stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'], + } as StorybookConfigRaw, + storybookVersion: '7.0.0', + configDir: '.storybook', +}; + +interface AddonDocsOptions { + mainConfigPath: string; + hasEssentials: boolean; + hasDocsDisabled: boolean; +} + +// Add type for migration object +interface Migration { + check: (options: CheckOptions) => Promise; + run: (options: RunOptions) => Promise; +} + +const typedAddonDocsEssentials = addonEssentialsRemoveDocs as Migration; + +describe('addon-essentials-remove-docs migration', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockConfigs.clear(); + }); + + describe('check phase', () => { + it('returns null if no mainConfigPath provided', async () => { + const result = await typedAddonDocsEssentials.check(baseCheckOptions); + expect(result).toBeNull(); + }); + + it('returns null if essentials not found in config', async () => { + const mainConfig = ` + export default { + stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'], + addons: ['@storybook/addon-links'], + }; + `; + readFileMock.mockResolvedValueOnce(mainConfig); + + const result = await typedAddonDocsEssentials.check({ + ...baseCheckOptions, + mainConfigPath: 'main.ts', + }); + expect(result).toBeNull(); + }); + + it('detects essentials with docs disabled', async () => { + const mainConfig = ` + export default { + stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'], + addons: [ + { + name: '@storybook/addon-essentials', + options: { + "docs": false + } + } + ] + }; + `; + readFileMock.mockResolvedValueOnce(mainConfig); + + const result = await typedAddonDocsEssentials.check({ + ...baseCheckOptions, + mainConfigPath: 'main.ts', + }); + expect(result).toEqual({ + mainConfigPath: 'main.ts', + hasEssentials: true, + hasDocsDisabled: true, + }); + }); + + it('detects essentials with docs enabled', async () => { + const mainConfig = ` + export default { + stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'], + addons: ['@storybook/addon-essentials'], + }; + `; + readFileMock.mockResolvedValueOnce(mainConfig); + + const result = await typedAddonDocsEssentials.check({ + ...baseCheckOptions, + mainConfigPath: 'main.ts', + }); + expect(result).toEqual({ + mainConfigPath: 'main.ts', + hasEssentials: true, + hasDocsDisabled: false, + }); + }); + }); + + describe('run phase', () => { + it('removes docs config when docs is disabled', async () => { + const mockMain: MockConfigFile = { + getFieldValue: vi.fn().mockReturnValue([ + { + name: '@storybook/addon-essentials', + options: { docs: false, actions: true }, + }, + ]), + setFieldValue: vi.fn(), + appendValueToArray: vi.fn(), + _ast: {}, + _code: '', + _exports: {}, + _exportDecls: [], + }; + + mockConfigs.set('main.ts', mockMain); + + await typedAddonDocsEssentials.run({ + result: { + mainConfigPath: 'main.ts', + hasEssentials: true, + hasDocsDisabled: true, + }, + dryRun: false, + packageManager: mockPackageManager, + packageJson: mockPackageJson, + mainConfigPath: 'main.ts', + mainConfig: { + stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'], + } as StorybookConfigRaw, + }); + + expect(mockMain.setFieldValue).toHaveBeenCalledWith( + ['addons'], + [ + { + name: '@storybook/addon-essentials', + options: { actions: true }, + }, + ] + ); + }); + + it('installs and adds addon-docs when docs is enabled', async () => { + const mockMain: MockConfigFile = { + getFieldValue: vi.fn().mockReturnValue(['@storybook/addon-essentials']), + setFieldValue: vi.fn(), + appendValueToArray: vi.fn(), + _ast: {}, + _code: '', + _exports: {}, + _exportDecls: [], + }; + + mockConfigs.set('main.ts', mockMain); + + vi.mocked(getStorybookVersionSpecifier).mockReturnValue('^7.0.0'); + vi.mocked(mockPackageManager.retrievePackageJson).mockResolvedValue({ + dependencies: {}, + devDependencies: {}, + }); + + await typedAddonDocsEssentials.run({ + result: { + mainConfigPath: 'main.ts', + hasEssentials: true, + hasDocsDisabled: false, + }, + dryRun: false, + packageManager: mockPackageManager, + packageJson: mockPackageJson, + mainConfigPath: 'main.ts', + mainConfig: { + stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'], + } as StorybookConfigRaw, + }); + + expect(mockPackageManager.addDependencies).toHaveBeenCalledWith( + { installAsDevDependencies: true, skipInstall: false }, + ['@storybook/addon-docs@^7.0.0'] + ); + + expect(mockMain.appendValueToArray).toHaveBeenCalledWith(['addons'], '@storybook/addon-docs'); + }); + + it('does nothing in dry run mode', async () => { + const mockMain: MockConfigFile = { + getFieldValue: vi.fn().mockReturnValue(['@storybook/addon-essentials']), + setFieldValue: vi.fn(), + appendValueToArray: vi.fn(), + _ast: {}, + _code: '', + _exports: {}, + _exportDecls: [], + }; + + mockConfigs.set('main.ts', mockMain); + + await typedAddonDocsEssentials.run({ + result: { + mainConfigPath: 'main.ts', + hasEssentials: true, + hasDocsDisabled: false, + }, + dryRun: true, + packageManager: mockPackageManager, + packageJson: mockPackageJson, + mainConfigPath: 'main.ts', + mainConfig: { + stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'], + } as StorybookConfigRaw, + }); + + expect(mockPackageManager.addDependencies).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.ts b/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.ts new file mode 100644 index 00000000000..005619af5c6 --- /dev/null +++ b/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.ts @@ -0,0 +1,124 @@ +import { readFile } from 'node:fs/promises'; + +import { getStorybookVersionSpecifier } from 'storybook/internal/cli'; + +import { dedent } from 'ts-dedent'; + +import { updateMainConfig } from '../helpers/mainConfigFile'; +import type { Fix } from '../types'; + +interface AddonDocsOptions { + mainConfigPath: string; + hasEssentials: boolean; + hasDocsDisabled: boolean; +} + +/** + * Migration to handle @storybook/addon-docs being removed from @storybook/addon-essentials + * + * - If user has essentials with docs disabled: Remove the docs disabling config + * - If user has essentials without docs disabled: Install @storybook/addon-docs and add to main.ts + * - If user doesn't have essentials: Skip migration + */ +export const addonEssentialsRemoveDocs: Fix = { + id: 'addon-essentials-remove-docs', + versionRange: ['<9.0.0', '^9.0.0-0 || ^9.0.0'], + + async check({ mainConfigPath }) { + if (!mainConfigPath) { + return null; + } + + try { + const mainConfig = await readFile(mainConfigPath, 'utf-8'); + + // Check if addon-essentials is present + const hasEssentials = mainConfig.includes('@storybook/addon-essentials'); + if (!hasEssentials) { + return null; + } + + // Check if docs is disabled in essentials config + const hasDocsDisabled = + mainConfig.includes('"docs": false') || + mainConfig.includes("'docs': false") || + mainConfig.includes('docs:false'); + + return { + mainConfigPath, + hasEssentials, + hasDocsDisabled, + }; + } catch (err) { + return null; + } + }, + + prompt({ hasDocsDisabled }) { + if (hasDocsDisabled) { + return dedent` + We've detected that you have @storybook/addon-essentials with docs disabled. + + @storybook/addon-docs has been removed from @storybook/addon-essentials. + We'll remove the docs disabling configuration since it's no longer needed. + `; + } + + return dedent` + We've detected that you have @storybook/addon-essentials with docs enabled. + + @storybook/addon-docs has been removed from @storybook/addon-essentials. + We'll install @storybook/addon-docs separately and add it to your configuration. + `; + }, + + async run({ result, dryRun, packageManager, skipInstall = false }) { + const { mainConfigPath, hasDocsDisabled } = result; + + await updateMainConfig({ mainConfigPath, dryRun: !!dryRun }, async (main) => { + // Get the current addons array + const addons = main.getFieldValue(['addons']) || []; + + // Find the essentials entry + const essentialsIndex = addons.findIndex((addon: any) => { + if (typeof addon === 'string') { + return addon === '@storybook/addon-essentials'; + } + return addon?.name === '@storybook/addon-essentials'; + }); + + if (hasDocsDisabled) { + // If docs was disabled, simply remove the docs config from essentials + if (typeof addons[essentialsIndex] === 'object') { + const options = addons[essentialsIndex].options || {}; + delete options.docs; + + // If options is now empty, convert back to string format + if (Object.keys(options).length === 0) { + addons[essentialsIndex] = '@storybook/addon-essentials'; + } else { + addons[essentialsIndex].options = options; + } + } + } else { + // If docs was enabled, add @storybook/addon-docs as a separate addon + if (!dryRun) { + const versionToInstall = getStorybookVersionSpecifier( + await packageManager.retrievePackageJson() + ); + + // Install @storybook/addon-docs + await packageManager.addDependencies({ installAsDevDependencies: true, skipInstall }, [ + `@storybook/addon-docs@${versionToInstall}`, + ]); + + // Add @storybook/addon-docs to the addons array + main.appendValueToArray(['addons'], '@storybook/addon-docs'); + } + } + + // Update the addons array + main.setFieldValue(['addons'], addons); + }); + }, +}; diff --git a/code/lib/cli-storybook/src/automigrate/fixes/index.ts b/code/lib/cli-storybook/src/automigrate/fixes/index.ts index ef1348a13b2..fa5d3ca39ec 100644 --- a/code/lib/cli-storybook/src/automigrate/fixes/index.ts +++ b/code/lib/cli-storybook/src/automigrate/fixes/index.ts @@ -1,6 +1,7 @@ import { csfFactories } from '../../codemod/csf-factories'; import type { CommandFix, Fix } from '../types'; import { addonA11yAddonTest } from './addon-a11y-addon-test'; +import { addonEssentialsRemoveDocs } from './addon-essentials-remove-docs'; import { addonExperimentalTest } from './addon-experimental-test'; import { addonPostCSS } from './addon-postcss'; import { addonsAPI } from './addons-api'; @@ -70,6 +71,7 @@ export const allFixes: Fix[] = [ consolidatedImports, addonExperimentalTest, rendererToFramework, + addonEssentialsRemoveDocs, ]; export const initFixes: Fix[] = [eslintPlugin]; diff --git a/code/yarn.lock b/code/yarn.lock index f9109e39014..c9563e108aa 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6148,7 +6148,6 @@ __metadata: dependencies: "@storybook/addon-backgrounds": "workspace:*" "@storybook/addon-controls": "workspace:*" - "@storybook/addon-docs": "workspace:*" "@storybook/addon-highlight": "workspace:*" "@storybook/addon-measure": "workspace:*" "@storybook/addon-outline": "workspace:*" From 2585fc92324f242da9947982677feb9b3258f8f7 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 19 Mar 2025 14:21:32 +0100 Subject: [PATCH 016/104] Update README to include new valid addon keys and clarify docs addon separation --- code/addons/essentials/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/addons/essentials/README.md b/code/addons/essentials/README.md index 1f182cec1f8..0cf28f897f2 100644 --- a/code/addons/essentials/README.md +++ b/code/addons/essentials/README.md @@ -52,4 +52,6 @@ export default { }; ``` -Valid addon keys include: `backgrounds`, `controls`, `viewport`, `toolbars`. +Valid addon keys include: `backgrounds`, `controls`, `viewport`, `toolbars`, `measure`, `outline`. + +Note: The `docs` addon was previously part of essentials but is now a separate package. If you need documentation features, please install `@storybook/addon-docs` separately. From e306469ec6cfea232cbc1790ece5bc41893d94d2 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 19 Mar 2025 14:26:56 +0100 Subject: [PATCH 017/104] Add @storybook/addon-docs to the Storybook configuration for enhanced documentation support --- code/.storybook/main.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/code/.storybook/main.ts b/code/.storybook/main.ts index f32112ae798..05d5f23e00a 100644 --- a/code/.storybook/main.ts +++ b/code/.storybook/main.ts @@ -92,6 +92,7 @@ const config = defineMain({ ], addons: [ '@storybook/addon-themes', + '@storybook/addon-docs', '@storybook/addon-essentials', '@storybook/addon-storysource', '@storybook/addon-designs', From 23acfb59d2491fe8b74f7293a8acf37e9fd35a19 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 19 Mar 2025 15:00:50 +0100 Subject: [PATCH 018/104] Refactor addon-essentials-remove-docs migration to use mock configuration for testing Removed the mainConfigPath from the AddonDocsOptions interface and updated tests to utilize a mock configuration object instead of reading from a file. This enhances the clarity and maintainability of the migration logic. --- .../addon-essentials-remove-docs.test.ts | 54 +++++++++---------- .../fixes/addon-essentials-remove-docs.ts | 40 ++++++++------ 2 files changed, 52 insertions(+), 42 deletions(-) diff --git a/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.test.ts b/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.test.ts index e5b15e331a6..1fdb25349c8 100644 --- a/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.test.ts +++ b/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.test.ts @@ -63,7 +63,6 @@ const baseCheckOptions: CheckOptions = { }; interface AddonDocsOptions { - mainConfigPath: string; hasEssentials: boolean; hasDocsDisabled: boolean; } @@ -105,47 +104,51 @@ describe('addon-essentials-remove-docs migration', () => { }); it('detects essentials with docs disabled', async () => { - const mainConfig = ` - export default { - stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'], - addons: [ - { - name: '@storybook/addon-essentials', - options: { - "docs": false - } - } - ] - }; - `; - readFileMock.mockResolvedValueOnce(mainConfig); + const mockMain: MockConfigFile = { + getFieldValue: vi.fn().mockReturnValue([ + { + name: '@storybook/addon-essentials', + options: { docs: false }, + }, + ]), + setFieldValue: vi.fn(), + appendValueToArray: vi.fn(), + _ast: {}, + _code: '', + _exports: {}, + _exportDecls: [], + }; + + mockConfigs.set('main.ts', mockMain); const result = await typedAddonDocsEssentials.check({ ...baseCheckOptions, mainConfigPath: 'main.ts', }); expect(result).toEqual({ - mainConfigPath: 'main.ts', hasEssentials: true, hasDocsDisabled: true, }); }); it('detects essentials with docs enabled', async () => { - const mainConfig = ` - export default { - stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'], - addons: ['@storybook/addon-essentials'], - }; - `; - readFileMock.mockResolvedValueOnce(mainConfig); + const mockMain: MockConfigFile = { + getFieldValue: vi.fn().mockReturnValue(['@storybook/addon-essentials']), + setFieldValue: vi.fn(), + appendValueToArray: vi.fn(), + _ast: {}, + _code: '', + _exports: {}, + _exportDecls: [], + }; + + mockConfigs.set('main.ts', mockMain); const result = await typedAddonDocsEssentials.check({ ...baseCheckOptions, mainConfigPath: 'main.ts', }); expect(result).toEqual({ - mainConfigPath: 'main.ts', hasEssentials: true, hasDocsDisabled: false, }); @@ -173,7 +176,6 @@ describe('addon-essentials-remove-docs migration', () => { await typedAddonDocsEssentials.run({ result: { - mainConfigPath: 'main.ts', hasEssentials: true, hasDocsDisabled: true, }, @@ -218,7 +220,6 @@ describe('addon-essentials-remove-docs migration', () => { await typedAddonDocsEssentials.run({ result: { - mainConfigPath: 'main.ts', hasEssentials: true, hasDocsDisabled: false, }, @@ -254,7 +255,6 @@ describe('addon-essentials-remove-docs migration', () => { await typedAddonDocsEssentials.run({ result: { - mainConfigPath: 'main.ts', hasEssentials: true, hasDocsDisabled: false, }, diff --git a/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.ts b/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.ts index 005619af5c6..3b066038ed2 100644 --- a/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.ts +++ b/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.ts @@ -1,5 +1,3 @@ -import { readFile } from 'node:fs/promises'; - import { getStorybookVersionSpecifier } from 'storybook/internal/cli'; import { dedent } from 'ts-dedent'; @@ -8,7 +6,6 @@ import { updateMainConfig } from '../helpers/mainConfigFile'; import type { Fix } from '../types'; interface AddonDocsOptions { - mainConfigPath: string; hasEssentials: boolean; hasDocsDisabled: boolean; } @@ -30,22 +27,35 @@ export const addonEssentialsRemoveDocs: Fix = { } try { - const mainConfig = await readFile(mainConfigPath, 'utf-8'); + let hasEssentials = false; + let hasDocsDisabled = false; + + await updateMainConfig({ mainConfigPath, dryRun: true }, (main) => { + const addons = main.getFieldValue(['addons']) || []; + + // Find the essentials entry and check its configuration + const essentialsEntry = addons.find((addon: any) => { + if (typeof addon === 'string') { + return addon === '@storybook/addon-essentials'; + } + return addon?.name === '@storybook/addon-essentials'; + }); + + if (essentialsEntry) { + hasEssentials = true; + // Check if docs is explicitly disabled in the options + if (typeof essentialsEntry === 'object') { + const options = essentialsEntry.options || {}; + hasDocsDisabled = options.docs === false; + } + } + }); - // Check if addon-essentials is present - const hasEssentials = mainConfig.includes('@storybook/addon-essentials'); if (!hasEssentials) { return null; } - // Check if docs is disabled in essentials config - const hasDocsDisabled = - mainConfig.includes('"docs": false') || - mainConfig.includes("'docs': false") || - mainConfig.includes('docs:false'); - return { - mainConfigPath, hasEssentials, hasDocsDisabled, }; @@ -72,8 +82,8 @@ export const addonEssentialsRemoveDocs: Fix = { `; }, - async run({ result, dryRun, packageManager, skipInstall = false }) { - const { mainConfigPath, hasDocsDisabled } = result; + async run({ result, dryRun, packageManager, skipInstall = false, mainConfigPath }) { + const { hasDocsDisabled } = result; await updateMainConfig({ mainConfigPath, dryRun: !!dryRun }, async (main) => { // Get the current addons array From 983baa01ab966a383ffb1446026d0bec42af3163 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 19 Mar 2025 15:04:03 +0100 Subject: [PATCH 019/104] Update Migration interface to specify return type for check method in addon-essentials-remove-docs test --- .../src/automigrate/fixes/addon-essentials-remove-docs.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.test.ts b/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.test.ts index 1fdb25349c8..c985c7f5e43 100644 --- a/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.test.ts +++ b/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.test.ts @@ -69,7 +69,7 @@ interface AddonDocsOptions { // Add type for migration object interface Migration { - check: (options: CheckOptions) => Promise; + check: (options: CheckOptions) => Promise; run: (options: RunOptions) => Promise; } From da61b3250cfb4db319a5efdaef38f733fa878cf0 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 19 Mar 2025 15:05:14 +0100 Subject: [PATCH 020/104] Add safety check for essentials presence in addon-essentials-remove-docs migration Implemented a safety check to ensure that the @storybook/addon-essentials is present before attempting to modify its configuration. This prevents potential errors when the essentials are not found. --- .../src/automigrate/fixes/addon-essentials-remove-docs.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.ts b/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.ts index 3b066038ed2..cabd9e295c1 100644 --- a/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.ts +++ b/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.ts @@ -97,6 +97,11 @@ export const addonEssentialsRemoveDocs: Fix = { return addon?.name === '@storybook/addon-essentials'; }); + // Safety check: if essentials isn't found, we can't modify it + if (essentialsIndex === -1) { + return; + } + if (hasDocsDisabled) { // If docs was disabled, simply remove the docs config from essentials if (typeof addons[essentialsIndex] === 'object') { From f31f39416cad7780bebbc5950fc67d3db8edad5b Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 19 Mar 2025 15:06:20 +0100 Subject: [PATCH 021/104] Add test for handling missing essentials addon in addon-essentials-remove-docs migration Implemented a new test case to verify that the migration logic correctly handles scenarios where the @storybook/addon-essentials is not present in the configuration. This ensures that no modifications are made when essentials are missing, enhancing the robustness of the migration process. --- .../addon-essentials-remove-docs.test.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.test.ts b/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.test.ts index c985c7f5e43..5755c922c9c 100644 --- a/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.test.ts +++ b/code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.test.ts @@ -269,5 +269,38 @@ describe('addon-essentials-remove-docs migration', () => { expect(mockPackageManager.addDependencies).not.toHaveBeenCalled(); }); + + it('handles missing essentials addon gracefully', async () => { + const mockMain: MockConfigFile = { + getFieldValue: vi.fn().mockReturnValue(['@storybook/addon-links']), // No essentials here + setFieldValue: vi.fn(), + appendValueToArray: vi.fn(), + _ast: {}, + _code: '', + _exports: {}, + _exportDecls: [], + }; + + mockConfigs.set('main.ts', mockMain); + + await typedAddonDocsEssentials.run({ + result: { + hasEssentials: true, + hasDocsDisabled: false, + }, + dryRun: false, + packageManager: mockPackageManager, + packageJson: mockPackageJson, + mainConfigPath: 'main.ts', + mainConfig: { + stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'], + } as StorybookConfigRaw, + }); + + // Should not modify anything if essentials isn't found + expect(mockMain.setFieldValue).not.toHaveBeenCalled(); + expect(mockPackageManager.addDependencies).not.toHaveBeenCalled(); + expect(mockMain.appendValueToArray).not.toHaveBeenCalled(); + }); }); }); From 4113cd8491c41ed36c3407961ef4aaa5128faf1b Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 19 Mar 2025 16:01:20 +0100 Subject: [PATCH 022/104] Remove unused '@storybook/addon-essentials/docs/preview' from INCLUDE_CANDIDATES in optimizeDeps.ts --- code/builders/builder-vite/src/optimizeDeps.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/code/builders/builder-vite/src/optimizeDeps.ts b/code/builders/builder-vite/src/optimizeDeps.ts index 428dda72b22..aba8435bd00 100644 --- a/code/builders/builder-vite/src/optimizeDeps.ts +++ b/code/builders/builder-vite/src/optimizeDeps.ts @@ -20,7 +20,6 @@ const INCLUDE_CANDIDATES = [ '@storybook/addon-designs/blocks', '@storybook/addon-docs/preview', '@storybook/addon-essentials/backgrounds/preview', - '@storybook/addon-essentials/docs/preview', '@storybook/addon-essentials/highlight/preview', '@storybook/addon-essentials/measure/preview', '@storybook/addon-essentials/outline/preview', From d1e0712b9ea9f80b5667c725a7f4e3bf6aae6934 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 19 Mar 2025 16:28:01 +0100 Subject: [PATCH 023/104] Remove unused '@storybook/addon-essentials/docs/mdx-react-shim' from optimizeViteDeps in preset.ts --- code/addons/docs/src/preset.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/code/addons/docs/src/preset.ts b/code/addons/docs/src/preset.ts index 484782c4e2f..9f5e685582f 100644 --- a/code/addons/docs/src/preset.ts +++ b/code/addons/docs/src/preset.ts @@ -211,7 +211,6 @@ const optimizeViteDeps = [ '@mdx-js/react', '@storybook/addon-docs > acorn-jsx', '@storybook/addon-docs', - '@storybook/addon-essentials/docs/mdx-react-shim', 'markdown-to-jsx', ]; From 816107a48a75b7f02d2b5a15bb36586a17e05982 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Thu, 20 Mar 2025 10:16:38 +0100 Subject: [PATCH 024/104] Remove '@storybook/addon-controls' from package.json and related files, migrating its functionality into core. Update dependencies and documentation accordingly. --- code/addons/controls/README.md | 179 ------------------ code/addons/controls/manager.js | 1 - code/addons/controls/package.json | 89 --------- code/addons/controls/project.json | 8 - code/addons/controls/src/index.ts | 7 - code/addons/controls/tsconfig.json | 5 - code/addons/controls/vitest.config.ts | 10 - code/addons/essentials/README.md | 3 +- code/addons/essentials/package.json | 3 - .../addons/essentials/src/controls/manager.ts | 2 - code/addons/essentials/src/preset.ts | 9 +- code/addons/onboarding/src/Onboarding.tsx | 3 +- code/addons/onboarding/src/manager.tsx | 3 +- .../docs}/addon-controls-args-annotated.png | Bin .../addon-controls-args-background-color.png | Bin .../addon-controls-args-background-string.png | Bin .../assets/docs}/addon-controls-args-docs.png | Bin .../docs}/addon-controls-args-logging.png | Bin .../addon-controls-args-no-annotation.png | Bin .../addon-controls-args-reflow-slider.png | Bin .../docs}/addon-controls-args-reflow.png | Bin .../docs}/addon-controls-args-template.png | Bin .../assets/docs}/addon-controls-expanded.png | Bin .../assets/docs}/addon-controls-hero.gif | Bin .../assets/docs}/addon-controls-install.png | Bin code/core/package.json | 52 +++++ code/core/scripts/entries.ts | 5 + code/core/src/common/versions.ts | 1 - code/core/src/controls/README.md | 22 +++ .../controls/components}/ControlsPanel.tsx | 8 +- .../components}/SaveStory.stories.tsx | 0 .../src/controls/components}/SaveStory.tsx | 0 .../src => core/src/controls}/constants.ts | 0 code/core/src/controls/decorator.ts | 7 + code/core/src/controls/index.ts | 2 + .../src => core/src/controls}/manager.tsx | 2 +- .../src/controls}/preset/checkDocsLoaded.ts | 0 code/core/src/controls/preview.ts | 6 + .../src => core/src/controls}/types.ts | 0 .../src => core/src/controls}/typings.d.ts | 0 .../stories/controls}/basics.stories.ts | 16 -- .../stories/controls}/conditional.stories.ts | 0 .../stories/controls}/disable.stories.ts | 0 .../stories/controls}/filters.stories.ts | 0 .../stories/controls}/issues.stories.ts | 0 .../stories/controls}/matchers.stories.ts | 0 .../stories/controls}/sorting.stories.ts | 0 code/core/tsconfig.json | 2 +- code/package.json | 1 - code/yarn.lock | 18 -- test-storybooks/ember-cli/.storybook/main.js | 1 - test-storybooks/ember-cli/package.json | 4 +- test-storybooks/external-docs/package.json | 3 +- .../nextjs/package.json | 3 +- .../react/.storybook/main.ts | 1 - .../react/package.json | 2 - .../svelte/package.json | 3 +- .../vue3/package.json | 3 +- .../server-kitchen-sink/.storybook/main.ts | 1 - .../server-kitchen-sink/package.json | 4 +- .../standalone-preview/package.json | 3 +- 61 files changed, 115 insertions(+), 377 deletions(-) delete mode 100644 code/addons/controls/README.md delete mode 100644 code/addons/controls/manager.js delete mode 100644 code/addons/controls/package.json delete mode 100644 code/addons/controls/project.json delete mode 100644 code/addons/controls/src/index.ts delete mode 100644 code/addons/controls/tsconfig.json delete mode 100644 code/addons/controls/vitest.config.ts delete mode 100644 code/addons/essentials/src/controls/manager.ts rename code/{addons/controls/docs/media => core/assets/docs}/addon-controls-args-annotated.png (100%) rename code/{addons/controls/docs/media => core/assets/docs}/addon-controls-args-background-color.png (100%) rename code/{addons/controls/docs/media => core/assets/docs}/addon-controls-args-background-string.png (100%) rename code/{addons/controls/docs/media => core/assets/docs}/addon-controls-args-docs.png (100%) rename code/{addons/controls/docs/media => core/assets/docs}/addon-controls-args-logging.png (100%) rename code/{addons/controls/docs/media => core/assets/docs}/addon-controls-args-no-annotation.png (100%) rename code/{addons/controls/docs/media => core/assets/docs}/addon-controls-args-reflow-slider.png (100%) rename code/{addons/controls/docs/media => core/assets/docs}/addon-controls-args-reflow.png (100%) rename code/{addons/controls/docs/media => core/assets/docs}/addon-controls-args-template.png (100%) rename code/{addons/controls/docs/media => core/assets/docs}/addon-controls-expanded.png (100%) rename code/{addons/controls/docs/media => core/assets/docs}/addon-controls-hero.gif (100%) rename code/{addons/controls/docs/media => core/assets/docs}/addon-controls-install.png (100%) create mode 100644 code/core/src/controls/README.md rename code/{addons/controls/src => core/src/controls/components}/ControlsPanel.tsx (94%) rename code/{addons/controls/src => core/src/controls/components}/SaveStory.stories.tsx (100%) rename code/{addons/controls/src => core/src/controls/components}/SaveStory.tsx (100%) rename code/{addons/controls/src => core/src/controls}/constants.ts (100%) create mode 100644 code/core/src/controls/decorator.ts create mode 100644 code/core/src/controls/index.ts rename code/{addons/controls/src => core/src/controls}/manager.tsx (98%) rename code/{addons/controls/src => core/src/controls}/preset/checkDocsLoaded.ts (100%) create mode 100644 code/core/src/controls/preview.ts rename code/{addons/controls/src => core/src/controls}/types.ts (100%) rename code/{addons/controls/src => core/src/controls}/typings.d.ts (100%) rename code/{addons/controls/template/stories => core/template/stories/controls}/basics.stories.ts (81%) rename code/{addons/controls/template/stories => core/template/stories/controls}/conditional.stories.ts (100%) rename code/{addons/controls/template/stories => core/template/stories/controls}/disable.stories.ts (100%) rename code/{addons/controls/template/stories => core/template/stories/controls}/filters.stories.ts (100%) rename code/{addons/controls/template/stories => core/template/stories/controls}/issues.stories.ts (100%) rename code/{addons/controls/template/stories => core/template/stories/controls}/matchers.stories.ts (100%) rename code/{addons/controls/template/stories => core/template/stories/controls}/sorting.stories.ts (100%) diff --git a/code/addons/controls/README.md b/code/addons/controls/README.md deleted file mode 100644 index e368a598e82..00000000000 --- a/code/addons/controls/README.md +++ /dev/null @@ -1,179 +0,0 @@ -# Storybook Controls Addon - -[Storybook](https://storybook.js.org) Controls gives you a graphical UI to interact with a component's arguments dynamically, without needing to code. It creates an addon panel next to your component examples ("stories"), so you can edit them live. - -[Framework Support](https://storybook.js.org/docs/configure/integration/frameworks-feature-support) - -![Screenshot](https://raw.githubusercontent.com/storybookjs/storybook/next/code/addons/controls/docs/media/addon-controls-hero.gif) - -## Installation - -Controls is part of [essentials](https://storybook.js.org/docs/essentials) and so is installed in all new Storybooks by default. If you need to add it to your Storybook, you can run: - -```sh -npm i -D @storybook/addon-controls -``` - -Then, add following content to [`.storybook/main.js`](https://storybook.js.org/docs/configure#configure-your-storybook-project): - -```js -export default { - addons: ['@storybook/addon-controls'], -}; -``` - -## Usage - -The usage is documented in the [documentation](https://storybook.js.org/docs/essentials/controls). - -## FAQs - -- [Storybook Controls Addon](#storybook-controls-addon) - - [Installation](#installation) - - [Usage](#usage) - - [FAQs](#faqs) - - [How will this replace addon-knobs?](#how-will-this-replace-addon-knobs) - - [How do I migrate from addon-knobs?](#how-do-i-migrate-from-addon-knobs) - - [My controls aren't being auto-generated. What should I do?](#my-controls-arent-being-auto-generated-what-should-i-do) - - [How can I disable controls for certain fields on a particular story?](#how-can-i-disable-controls-for-certain-fields-on-a-particular-story) - - [How do controls work with MDX?](#how-do-controls-work-with-mdx) - -### How will this replace addon-knobs? - -Addon-knobs is one of Storybook's most popular addons with over 1M weekly downloads, so we know lots of users will be affected by this change. Knobs is also a mature addon, with various options that are not available in addon-controls. - -Therefore, rather than deprecating addon-knobs immediately, we will continue to release knobs with the Storybook core distribution until 7.0. This will give us time to improve Controls based on user feedback, and also give knobs users ample time to migrate. - -If you are somehow tied to knobs or prefer the knobs interface, we are happy to take on maintainers for the knobs project. If this interests you, please get in touch with us in the [`#contributing`](https://discord.com/channels/486522875931656193/839297503446695956) Discord channel. - -### How do I migrate from addon-knobs? - -If you're already using [Storybook Knobs](https://github.com/storybookjs/addon-knobs) you should consider migrating to Controls. - -You're probably using it for something that can be satisfied by one of the cases [described above](#writing-stories). - -Let's walk through two examples: migrating [knobs to auto-generated args](#knobs-to-custom-args) and [knobs to custom args](#knobs-to-custom-args). - -

Knobs to auto-generated args

- -First, let's consider a knobs version of a basic story that fills in the props for a component: - -```jsx -import { text } from '@storybook/addon-knobs'; -import { Button } from './Button'; - -export const Basic = () =>