From cbac828984809c9b359cdf829aaaca550b8dfebb Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Mon, 20 Apr 2020 16:26:23 +0200 Subject: [PATCH 1/8] REMOVE addon-context --- addons/contexts/README.md | 254 ------------------ addons/contexts/package.json | 74 ----- addons/contexts/preact.js | 4 - addons/contexts/rax.js | 4 - addons/contexts/react.js | 4 - addons/contexts/register.js | 1 - addons/contexts/src/index.ts | 37 --- .../contexts/src/manager/ContextsManager.tsx | 34 --- .../src/manager/components/ToolBar.test.tsx | 103 ------- .../src/manager/components/ToolBar.tsx | 26 -- .../components/ToolBarControl.test.tsx | 102 ------- .../src/manager/components/ToolBarControl.tsx | 53 ---- .../manager/components/ToolBarMenu.test.tsx | 109 -------- .../src/manager/components/ToolBarMenu.tsx | 39 --- .../components/ToolBarMenuOptions.test.tsx | 51 ---- .../manager/components/ToolBarMenuOptions.tsx | 22 -- .../src/preview/ContextsPreviewAPI.ts | 81 ------ .../contexts/src/preview/frameworks/preact.ts | 14 - addons/contexts/src/preview/frameworks/rax.ts | 14 - .../contexts/src/preview/frameworks/react.ts | 14 - addons/contexts/src/preview/frameworks/vue.ts | 28 -- .../src/preview/libs/decorators.test.ts | 59 ---- .../contexts/src/preview/libs/decorators.ts | 26 -- .../src/preview/libs/getContextNodes.test.ts | 139 ---------- .../src/preview/libs/getContextNodes.ts | 53 ---- .../src/preview/libs/getPropsMap.test.ts | 88 ------ .../contexts/src/preview/libs/getPropsMap.ts | 41 --- .../src/preview/libs/getRendererFrom.test.ts | 110 -------- .../src/preview/libs/getRendererFrom.ts | 66 ----- addons/contexts/src/preview/libs/index.ts | 4 - addons/contexts/src/register.ts | 13 - .../src/shared/@mock-types/_global.d.ts | 1 - .../src/shared/@mock-types/_preact.d.ts | 7 - addons/contexts/src/shared/constants.ts | 17 -- .../contexts/src/shared/serializers.test.ts | 21 -- addons/contexts/src/shared/serializers.ts | 29 -- addons/contexts/src/shared/types.d.ts | 49 ---- addons/contexts/tsconfig.json | 15 -- addons/contexts/vue.js | 4 - 39 files changed, 1810 deletions(-) delete mode 100644 addons/contexts/README.md delete mode 100644 addons/contexts/package.json delete mode 100644 addons/contexts/preact.js delete mode 100644 addons/contexts/rax.js delete mode 100644 addons/contexts/react.js delete mode 100644 addons/contexts/register.js delete mode 100644 addons/contexts/src/index.ts delete mode 100644 addons/contexts/src/manager/ContextsManager.tsx delete mode 100644 addons/contexts/src/manager/components/ToolBar.test.tsx delete mode 100644 addons/contexts/src/manager/components/ToolBar.tsx delete mode 100644 addons/contexts/src/manager/components/ToolBarControl.test.tsx delete mode 100644 addons/contexts/src/manager/components/ToolBarControl.tsx delete mode 100644 addons/contexts/src/manager/components/ToolBarMenu.test.tsx delete mode 100644 addons/contexts/src/manager/components/ToolBarMenu.tsx delete mode 100644 addons/contexts/src/manager/components/ToolBarMenuOptions.test.tsx delete mode 100644 addons/contexts/src/manager/components/ToolBarMenuOptions.tsx delete mode 100644 addons/contexts/src/preview/ContextsPreviewAPI.ts delete mode 100644 addons/contexts/src/preview/frameworks/preact.ts delete mode 100644 addons/contexts/src/preview/frameworks/rax.ts delete mode 100644 addons/contexts/src/preview/frameworks/react.ts delete mode 100644 addons/contexts/src/preview/frameworks/vue.ts delete mode 100644 addons/contexts/src/preview/libs/decorators.test.ts delete mode 100644 addons/contexts/src/preview/libs/decorators.ts delete mode 100644 addons/contexts/src/preview/libs/getContextNodes.test.ts delete mode 100644 addons/contexts/src/preview/libs/getContextNodes.ts delete mode 100644 addons/contexts/src/preview/libs/getPropsMap.test.ts delete mode 100644 addons/contexts/src/preview/libs/getPropsMap.ts delete mode 100644 addons/contexts/src/preview/libs/getRendererFrom.test.ts delete mode 100644 addons/contexts/src/preview/libs/getRendererFrom.ts delete mode 100644 addons/contexts/src/preview/libs/index.ts delete mode 100644 addons/contexts/src/register.ts delete mode 100644 addons/contexts/src/shared/@mock-types/_global.d.ts delete mode 100644 addons/contexts/src/shared/@mock-types/_preact.d.ts delete mode 100644 addons/contexts/src/shared/constants.ts delete mode 100644 addons/contexts/src/shared/serializers.test.ts delete mode 100644 addons/contexts/src/shared/serializers.ts delete mode 100644 addons/contexts/src/shared/types.d.ts delete mode 100644 addons/contexts/tsconfig.json delete mode 100644 addons/contexts/vue.js diff --git a/addons/contexts/README.md b/addons/contexts/README.md deleted file mode 100644 index b8984367959c..000000000000 --- a/addons/contexts/README.md +++ /dev/null @@ -1,254 +0,0 @@ -# Storybook Addon Contexts - -**Storybook Addon Contexts** is an addon for driving your components under dynamic contexts in -[Storybook](https://storybook.js.org/). - -## 💡 Why you need this? - -Real world users expects your application being customizable, that is why often your components are **polymorphic**: -they need to adapt themselves under different contextual environments. Imagine your components can speak -Chinese, English, or even French, and they change their skin tone under dark or light theme. Yeah, you want to make -sure a component looks great in all scenarios. - -A good practice to write maintainable components is separate the presentation and its business logic. Storybook is -a great place for exercising the visualization and interaction of your components, which may depend on some contexts. -Often enough, you will find it become very tedious to wrap each component deeply with its contextual environments -before you can really write the main story. You even start to write extra components or factory functions just to -make your life easier. How about changing the context of your story dynamically?! There was simply no good way so -you ended up writing stories like an accountant. - -That is why you need this. An elegant way to wrap your component stories and change their contextual environment -directly and dynamically in Storybook UI! Kind of like a dependency injection, eh! The best bit is **you define it -once then apply it everywhere**. - -## ✅ Features - -1. Define a single global file for managing contextual environments (a.k.a. containers) for all of your stories - declaratively. No more repetitive setups or noisy wrapping, making your stories more focused and readable. -2. Support dynamic contextual props switching from Storybook toolbar at runtime. You can slice into - different environments (e.g. languages or themes ) to understand how your component is going to respond. -3. Library agnostic: no presumption on what kind of components you want to wrap around your stories. You can even - use it to bridge with your favorite routing, state-management solutions, or even your own - [React Context](https://reactjs.org/docs/context.html) provider. -4. Offer chainable and granular configurations. It is even possible to fine-tune at per story level. -5. Visual regression friendly. You can use this addon to drive the same story under different contexts to smoke - test important visual states. - -## 🧰 Requirements - -Make sure the version of your Storybook is above v5. For the full list of the current supported frameworks, see -[Addon / Framework Support Table](../../ADDONS_SUPPORT.md). - -## 🎬 Getting started - -To get it started, add this package into your project: - -```bash -yarn add -D @storybook/addon-contexts -``` - -within `.storybook/main.js`: - -```js -module.exports = { - addons: ['@storybook/addon-contexts'] -} -``` - -To load your contextual setups for your stories globally, add the following lines into `preview.js` file (you should -see it near your `addon.js` file): - -```js -import { addDecorator } from '@storybook/[framework]'; -import { withContexts } from '@storybook/addon-contexts/[framework]'; -import { contexts } from './configs/contexts'; // we will define the contextual setups later in API section - -addDecorator(withContexts(contexts)); -``` - -Alternatively, like other addons, you can use this addon only for a given set of stories: - -```js -import { withContexts } from '@storybook/addon-contexts/[framework]'; -import { contexts } from './configs/contexts'; - -export default { - title: 'Component With Contexts', - decorators: [withContexts(contexts)], -}; -``` - -Finally, you may want to create new contextual environments or disable default setups at the story level. To create a new contextual environment at the story level: - -```js -export const defaultView = () =>
; // sample story in CSF format -defaultView.story = { - parameters: { - contexts: [{ /* contextual environment defined using the API below */ }] - } -}; -``` - -To disable a default setup at the story level: - -```js -export const defaultView = () =>
; -defaultView.story = { - parameters: { - contexts: [ - { - title: '[title of contextual environment defined in contexts.js]' - options: { disable: true } - } - ] - } -}; -``` - -To override the default option for a default setup at the story level, see [this suggestion](https://discordapp.com/channels/486522875931656193/501692020226654208/687359410577604732). - - -## ⚙️ Setups - -### Overview - -It is recommended to have a separate file for managing your contextual environment setups. Let's add a file named -`contexts.js` first. Before diving into API details, here is an overview on the landscape. For example (in React), -to inject component theming contexts to both `styled-components` and `material-ui` theme providers in stories: - -```js -export const contexts = [ - { - icon: 'box', // a icon displayed in the Storybook toolbar to control contextual props - title: 'Themes', // an unique name of a contextual environment - components: [ - // an array of components that is going to be injected to wrap stories - /* Styled-components ThemeProvider, */ - /* Material-ui ThemeProvider, */ - ], - params: [ - // an array of params contains a set of predefined `props` for `components` - { name: 'Light Theme', props: { theme /* : your light theme */ } }, - { name: 'Dark Theme', props: { theme /* : your dark theme */ }, default: true }, - ], - options: { - deep: true, // pass the `props` deeply into all wrapping components - disable: false, // disable this contextual environment completely - cancelable: false, // allow this contextual environment to be opt-out optionally in toolbar - }, - }, - /* ... */ // multiple contexts setups are supported -]; -``` - ---- - -### APIs - -#### `withContexts(contexts) : function` - -A decorating function for wrapping your stories under your predefined `contexts`. This means multiple contextual -environments are supported. They are going to be loaded layer by layer and wrapped in a descending oder (top -> down --> story). The `contexts` is an array of objects that should have the following properties: - ---- - -#### `icon : string?` - -(default `undefined`) - -An icon displayed in the Storybook toolbar to control contextual props. This addon allows you to define an icon for -each contextual environment individually. Take a look at the currently supported -[icon lists](https://storybooks-official.netlify.com/?path=/story/basics-icon--labels) from the official Storybook -story. You must define an icon first if you want to take advantage of switching props dynamically in your Storybook -toolbar. - ---- - -#### `title : string` - -(required) - -A unique name of a contextual environment; if duplicate names are provided, the latter is going to be ignored. - ---- - -#### `components : (Component|string)[]` - -(required) - -An array of components that is going to be injected to wrap stories. This means this addon allows multiple wrapping -components to coexist. The wrapping sequence is from the left to right (parent -> children -> story). This nested -wrapping behaviour can be useful in some cases; for instance, in the above example, we are wrapping stories under -`styled-components` and `material-ui` theme providers. Also, you can use this addon to wrap any valid HTML tags. - ---- - -#### `params : object[] | undefined` - -(default: `undefined`) - -An array of params contains a set of predefined `props` for `components`. This object has the following properties: - -#### `params.name : string` - -(required) - -A unique name for representing the props. - -#### `params.props : object | null:` - -(required) - -The `props` that are accepted by the wrapping component(s). - -#### `params.default : true?` - -(default: `undefined`) - -Set to `true` if you want to use this param initially. Only the first one marked as default is identified. - ---- - -#### `options` - -A set of options offers more granular control over the defined contextual environment. These properties can be -overridden at the story level: - -#### `options.deep : boolean?` - -(default: `false`) - -Pass the `props` deeply into all wrapping components. Useful when you want them all to be passed with the same props. - -#### `options.disable : boolean?` - -(default: `false`) - -Disable this contextual environment completely. Useful when you want to opt-out this context from a given story. - -#### `options.cancelable : boolean?` - -(default: `false`) - -Allow this contextual environment to be opt-out optionally in toolbar. When set to `true`, an **Off** option will -be shown at first in the toolbar menu in your Storybook. - -## 📔 Notes - -1. You can use this addon to inject any valid components, that is why `icon` and `params` can be optional. -2. As mentioned, extra contextual environment setups can be added at the story level. Please make sure they are - passed via the second argument as `{ contexts: [{ /* extra contexts */ }}`. -3. Additional `params` can be "appended" into an existing setup at the story level too (make sure it goes with the - correct `title`); however, they are never be able to overridden the default setups. So it is important to have - non-colliding names. -4. The addon will persist the selected params (the addon state) between stories at run-time (similar to other - addons). If the active params were gone after story switching, it falls back to the default then the first. As a - rule of thumb, whenever collisions are possible, the first always wins. -5. Query parameters are supported for pre-selecting contexts param, which comes in handy for visual regression testing. - You can do this by appending `&contexts=[name of contexts]=[name of param]` in the URL under iframe mode. Use `,` - to separate multiple contexts (e.g. `&contexts=Theme=Forests,Language=Fr`). - -## 📖 License - -MIT diff --git a/addons/contexts/package.json b/addons/contexts/package.json deleted file mode 100644 index 02272e034424..000000000000 --- a/addons/contexts/package.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "name": "@storybook/addon-contexts", - "version": "6.0.0-alpha.40", - "description": "Storybook Addon Contexts", - "keywords": [ - "preact", - "react", - "storybook", - "vue" - ], - "repository": { - "type": "git", - "url": "https://github.com/storybookjs/storybook.git", - "directory": "addons/contexts" - }, - "license": "MIT", - "author": "Leo Y. Li", - "main": "dist/register.js", - "files": [ - "dist/**/*", - "README.md", - "*.js", - "*.d.ts", - "ts3.5/**/*" - ], - "scripts": { - "dev:check-types": "tsc --noEmit", - "prepare": "node ../../scripts/prepare.js" - }, - "dependencies": { - "@storybook/addons": "6.0.0-alpha.40", - "@storybook/api": "6.0.0-alpha.40", - "@storybook/components": "6.0.0-alpha.40", - "@storybook/core-events": "6.0.0-alpha.40", - "core-js": "^3.0.1", - "global": "^4.3.2", - "qs": "^6.6.0", - "regenerator-runtime": "^0.13.3" - }, - "devDependencies": { - "@types/enzyme": "^3.10.5", - "enzyme": "^3.11.0" - }, - "peerDependencies": { - "preact": "*", - "qs": "*", - "rax": "*", - "react": "*", - "react-dom": "*", - "vue": "*" - }, - "peerDependenciesMeta": { - "preact": { - "optional": true - }, - "rax": { - "optional": true - }, - "vue": { - "optional": true - } - }, - "publishConfig": { - "access": "public" - }, - "gitHead": "4b9d901add9452525135caae98ae5f78dd8da9ff", - "typesVersions": { - "<=3.5": { - "*": [ - "ts3.5/*" - ] - } - } -} diff --git a/addons/contexts/preact.js b/addons/contexts/preact.js deleted file mode 100644 index 6faa6b4ca5e5..000000000000 --- a/addons/contexts/preact.js +++ /dev/null @@ -1,4 +0,0 @@ -import { withContexts } from './dist/preview/frameworks/preact'; - -export { withContexts }; -export default withContexts; diff --git a/addons/contexts/rax.js b/addons/contexts/rax.js deleted file mode 100644 index 06abd7467f83..000000000000 --- a/addons/contexts/rax.js +++ /dev/null @@ -1,4 +0,0 @@ -import { withContexts } from './dist/preview/frameworks/rax'; - -export { withContexts }; -export default withContexts; diff --git a/addons/contexts/react.js b/addons/contexts/react.js deleted file mode 100644 index 896dd41d75b0..000000000000 --- a/addons/contexts/react.js +++ /dev/null @@ -1,4 +0,0 @@ -import { withContexts } from './dist/preview/frameworks/react'; - -export { withContexts }; -export default withContexts; diff --git a/addons/contexts/register.js b/addons/contexts/register.js deleted file mode 100644 index 06b5a5887266..000000000000 --- a/addons/contexts/register.js +++ /dev/null @@ -1 +0,0 @@ -export * from './dist/register'; diff --git a/addons/contexts/src/index.ts b/addons/contexts/src/index.ts deleted file mode 100644 index 6577078bbb28..000000000000 --- a/addons/contexts/src/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { makeDecorator, StoryWrapper } from '@storybook/addons'; -import { ContextsPreviewAPI } from './preview/ContextsPreviewAPI'; -import { ID, PARAM } from './shared/constants'; -import { AddonSetting, AnyFunctionReturns, ContextNode, PropsMap } from './shared/types.d'; - -/** - * This file serves a idiomatic facade of a Storybook decorator. - * - * Wrapper function get called whenever the Storybook rerender the view. This reflow logic is - * framework agnostic; on the other hand, the framework specific bindings are the implementation - * details hidden behind the passed `render` function. - * - * Here, we need a dedicated singleton as a state manager for preview (the addon API, in vanilla) - * who is also knowing how to communicate with the Storybook manager (in React) via the Storybook - * event system. - * - * @param {Render} render - framework specific bindings - */ -export type Render = (...args: [ContextNode[], PropsMap, AnyFunctionReturns]) => T; -type CreateAddonDecorator = (render: Render) => (contexts: AddonSetting[]) => unknown; - -export const createAddonDecorator: CreateAddonDecorator = (render) => { - const wrapper: StoryWrapper = (getStory, context, settings: any) => { - const { getContextNodes, getSelectionState, getPropsMap } = ContextsPreviewAPI(); - const nodes = getContextNodes(settings); - const state = getSelectionState(); - const props = getPropsMap(nodes, state); - return render(nodes, props, () => getStory(context)); - }; - - return makeDecorator({ - name: ID, - parameterName: PARAM, - skipIfNoParametersOrOptions: true, - wrapper, - }); -}; diff --git a/addons/contexts/src/manager/ContextsManager.tsx b/addons/contexts/src/manager/ContextsManager.tsx deleted file mode 100644 index 67bd38f041c2..000000000000 --- a/addons/contexts/src/manager/ContextsManager.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { useEffect, useState, useCallback } from 'react'; -import { useChannel } from '@storybook/api'; -import { ToolBar } from './components/ToolBar'; -import { deserialize, serialize } from '../shared/serializers'; -import { PARAM, REBOOT_MANAGER, UPDATE_MANAGER, UPDATE_PREVIEW } from '../shared/constants'; -import { FCNoChildren, ManagerAPI } from '../shared/types.d'; - -/** - * A smart component for handling manager-preview interactions. - */ -type ContextsManager = FCNoChildren<{ - api: ManagerAPI; -}>; - -export const ContextsManager: ContextsManager = ({ api }) => { - const [nodes, setNodes] = useState([]); - const [state, setState] = useState(deserialize(api.getQueryParam(PARAM))); - const setSelected = useCallback( - (nodeId, name) => setState((obj) => ({ ...obj, [nodeId]: name })), - [] - ); - - // from preview - const emit = useChannel({ - [UPDATE_MANAGER]: (newNodes) => setNodes(newNodes || []), - }); - - // to preview - useEffect(() => emit(REBOOT_MANAGER), []); - useEffect(() => emit(UPDATE_PREVIEW, state), [state]); - useEffect(() => api.setQueryParams({ [PARAM]: serialize(state) }), [state]); - - return ; -}; diff --git a/addons/contexts/src/manager/components/ToolBar.test.tsx b/addons/contexts/src/manager/components/ToolBar.test.tsx deleted file mode 100644 index 35caba83f2c6..000000000000 --- a/addons/contexts/src/manager/components/ToolBar.test.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import React from 'react'; -import { shallow } from 'enzyme'; -import { ToolBar } from './ToolBar'; - -describe('Tests on addon-contexts component: ToolBar', () => { - it('should render nothing if receive an empty contextNodes', () => { - // when - const result = shallow(); - - // then - expect(result).toMatchInlineSnapshot(`""`); - }); - - it('should spawn ToolBarControl based on the given contextNodes', () => { - // given - const someContextNodes = [ - { - components: ['span'], - icon: 'box' as const, - nodeId: 'Some Context A', - options: { cancelable: false, deep: false, disable: false }, - params: [{ name: '', props: {} }], - title: 'Some Context A', - }, - { - components: ['div'], - icon: 'box' as const, - nodeId: 'Some Context B', - options: { cancelable: true, deep: false, disable: false }, - params: [ - { name: 'Some Param X', props: {} }, - { name: 'Some Param Y', props: {} }, - ], - title: 'Some Context B', - }, - ]; - const someSelectionState = { - 'Some Context B': 'Some Param Y', - }; - - // when - const result = shallow( - - ); - - // then - expect(result).toMatchInlineSnapshot(` - - - - - - `); - }); -}); diff --git a/addons/contexts/src/manager/components/ToolBar.tsx b/addons/contexts/src/manager/components/ToolBar.tsx deleted file mode 100644 index 863d36aa9903..000000000000 --- a/addons/contexts/src/manager/components/ToolBar.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React, { ComponentProps, memo } from 'react'; -import { Separator } from '@storybook/components'; -import { ToolBarControl } from './ToolBarControl'; -import { ContextNode, FCNoChildren, SelectionState } from '../../shared/types.d'; - -type ToolBar = FCNoChildren<{ - nodes: ContextNode[]; - state: SelectionState; - setSelected: ComponentProps['setSelected']; -}>; - -export const ToolBar: ToolBar = memo(({ nodes, state, setSelected }) => - nodes.length ? ( - <> - - {nodes.map(({ components, ...forwardProps }) => ( - - ))} - - ) : null -); diff --git a/addons/contexts/src/manager/components/ToolBarControl.test.tsx b/addons/contexts/src/manager/components/ToolBarControl.test.tsx deleted file mode 100644 index 975f420dc27f..000000000000 --- a/addons/contexts/src/manager/components/ToolBarControl.test.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import React from 'react'; -import { shallow } from 'enzyme'; -import { ToolBarControl } from './ToolBarControl'; -import { OPT_OUT } from '../../shared/constants'; - -describe('Tests on addon-contexts component: ToolBarControl', () => { - // given - const someBasicProps = { - icon: 'box' as const, - nodeId: 'Some Context', - options: { cancelable: true, deep: false, disable: false }, - params: [ - { name: 'A', props: {} }, - { name: 'B', props: {} }, - ], - title: 'Some Context', - selected: '', - setSelected: jest.fn, - }; - - it('should control menu: set as inactive if being out-out (if cancelable)', () => { - // when - const result = shallow(); - - // then - expect(result.props().active).toBe(false); - }); - - it('should control menu: valid "selected" to give "activeName"', () => { - // given - const selected = 'C'; - const anotherSelected = 'B'; - - // when - const result = shallow(); - const anotherResult = shallow( - - ); - - // then - expect(result.props().optionsProps.activeName).not.toBe(selected); - expect(anotherResult.props().optionsProps.activeName).toBe(anotherSelected); - }); - - it('should control menu: fallback "activeName" to the default param', () => { - // given - const name = 'C'; - const params = [...someBasicProps.params, { name, props: {}, default: true }]; - - // when - const result = shallow(); - - // then - expect(result.props().optionsProps.activeName).toBe(name); - }); - - it('should control menu: fallback "activeName" to the first (if default not found)', () => { - // when - const result = shallow(); - - // then - expect(result.props().optionsProps.activeName).toBe(someBasicProps.params[0].name); - }); - - it('should render nothing if being disabled', () => { - // given - const options = { ...someBasicProps.options, disable: true }; - - // when - const result = shallow(); - - // then - expect(result).toMatchInlineSnapshot(`""`); - }); - - it('should document the shallowly rendered result', () => { - // when - const result = shallow(); - - // then - expect(result).toMatchInlineSnapshot(` - - `); - }); -}); diff --git a/addons/contexts/src/manager/components/ToolBarControl.tsx b/addons/contexts/src/manager/components/ToolBarControl.tsx deleted file mode 100644 index 468501b64c47..000000000000 --- a/addons/contexts/src/manager/components/ToolBarControl.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { useState } from 'react'; -import { ToolBarMenu } from './ToolBarMenu'; -import { OPT_OUT } from '../../shared/constants'; -import { ContextNode, FCNoChildren } from '../../shared/types.d'; - -type ToolBarControl = FCNoChildren< - Omit< - ContextNode & { - selected: string; - setSelected: (nodeId: string, name: string) => void; - }, - 'components' - > ->; - -export const ToolBarControl: ToolBarControl = ({ - nodeId, - icon, - title, - params, - options, - selected, - setSelected, -}) => { - const [expanded, setExpanded] = useState(false); - const paramNames = params.map(({ name }) => name); - const activeName = - // validate the integrity of the selected name - ([...paramNames, options.cancelable && OPT_OUT].includes(selected) && selected) || - // fallback to default - (params.find((param) => !!param.default) || { name: null }).name || - // fallback to the first - params[0].name; - const list = options.cancelable ? [OPT_OUT, ...paramNames] : paramNames; - const props = { - title, - active: activeName !== OPT_OUT, - expanded, - setExpanded, - optionsProps: { - activeName, - list, - onSelectOption: (name: string) => () => { - setExpanded(false); - setSelected(nodeId, name); - }, - }, - }; - - return Array.isArray(list) && list.length && !options.disable ? ( - - ) : null; -}; diff --git a/addons/contexts/src/manager/components/ToolBarMenu.test.tsx b/addons/contexts/src/manager/components/ToolBarMenu.test.tsx deleted file mode 100644 index 8eb15db3f65d..000000000000 --- a/addons/contexts/src/manager/components/ToolBarMenu.test.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import React from 'react'; -import { shallow } from 'enzyme'; -import { ToolBarMenu } from './ToolBarMenu'; - -describe('Tests on addon-contexts component: ToolBarMenu', () => { - it('should glue `@storybook/ui` components to produce a context menu', () => { - // given - const someProps = { - icon: 'globe' as const, - title: 'Some Context', - active: true, - expanded: false, - setExpanded: jest.fn, - optionsProps: { - activeName: 'A', - list: ['A', 'B'], - onSelectOption: jest.fn, - }, - }; - - // when - const result = shallow(); - - // then - expect(result).toMatchInlineSnapshot(` - - } - tooltipShown={false} - trigger="click" - > - - - - - `); - }); - - it('should render TabButton with title if the icon is given', () => { - // given - const someProps = { - title: 'Some Context', - active: true, - expanded: false, - setExpanded: jest.fn, - optionsProps: { - activeName: 'A', - list: ['A', 'B'], - onSelectOption: jest.fn, - }, - }; - - // when - const result = shallow(); - - // then - expect(result).toMatchInlineSnapshot(` - - } - tooltipShown={false} - trigger="click" - > - - Some Context - - - `); - }); -}); diff --git a/addons/contexts/src/manager/components/ToolBarMenu.tsx b/addons/contexts/src/manager/components/ToolBarMenu.tsx deleted file mode 100644 index dc19a6fccf72..000000000000 --- a/addons/contexts/src/manager/components/ToolBarMenu.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { ComponentProps } from 'react'; -import { Icons, IconButton, WithTooltipPure, TabButton } from '@storybook/components'; -import { ToolBarMenuOptions } from './ToolBarMenuOptions'; -import { ContextNode, FCNoChildren } from '../../shared/types.d'; - -type ToolBarMenu = FCNoChildren<{ - icon?: ComponentProps['icon'] | '' | void; - title: ContextNode['title']; - active: boolean; - expanded: boolean; - setExpanded: (state: boolean) => void; - optionsProps: ComponentProps; -}>; - -export const ToolBarMenu: ToolBarMenu = ({ - icon, - title, - active, - expanded, - setExpanded, - optionsProps, -}) => ( - } - > - {icon ? ( - - - - ) : ( - {title} - )} - -); diff --git a/addons/contexts/src/manager/components/ToolBarMenuOptions.test.tsx b/addons/contexts/src/manager/components/ToolBarMenuOptions.test.tsx deleted file mode 100644 index d22a88153572..000000000000 --- a/addons/contexts/src/manager/components/ToolBarMenuOptions.test.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React from 'react'; -import { shallow } from 'enzyme'; -import { ToolBarMenuOptions } from './ToolBarMenuOptions'; -import { OPT_OUT } from '../../shared/constants'; - -describe('Tests on addon-contexts component: ToolBarMenuOptions', () => { - it('should glue TooltipLinkList and set the active item correspondingly', () => { - // given - const list = [OPT_OUT, 'A', 'B']; - const activeName = 'B'; - - // when - const result = shallow( - - ); - - // then - expect(result.props().links.length).toBe(list.length); - expect(result.props().links.find((link: any) => link.title === activeName).active).toBe(true); - expect(result).toMatchInlineSnapshot(` - - `); - }); -}); diff --git a/addons/contexts/src/manager/components/ToolBarMenuOptions.tsx b/addons/contexts/src/manager/components/ToolBarMenuOptions.tsx deleted file mode 100644 index 38bf28db6eb8..000000000000 --- a/addons/contexts/src/manager/components/ToolBarMenuOptions.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import * as React from 'react'; -import { TooltipLinkList } from '@storybook/components'; -import { OPT_OUT } from '../../shared/constants'; -import { FCNoChildren } from '../../shared/types.d'; - -type ToolBarMenuOptions = FCNoChildren<{ - activeName: string; - list: string[]; - onSelectOption: (name: string) => () => void; -}>; - -export const ToolBarMenuOptions: ToolBarMenuOptions = ({ activeName, list, onSelectOption }) => ( - ({ - key: name, - id: name, - title: name !== OPT_OUT ? name : 'Off', - active: name === activeName, - onClick: onSelectOption(name), - }))} - /> -); diff --git a/addons/contexts/src/preview/ContextsPreviewAPI.ts b/addons/contexts/src/preview/ContextsPreviewAPI.ts deleted file mode 100644 index f9afc084a2d1..000000000000 --- a/addons/contexts/src/preview/ContextsPreviewAPI.ts +++ /dev/null @@ -1,81 +0,0 @@ -import addons from '@storybook/addons'; -import { window } from 'global'; -import { parse } from 'qs'; -import { getContextNodes, getPropsMap, getRendererFrom, singleton } from './libs'; -import { deserialize } from '../shared/serializers'; -import { - PARAM, - REBOOT_MANAGER, - UPDATE_PREVIEW, - UPDATE_MANAGER, - FORCE_RE_RENDER, - SET_CURRENT_STORY, -} from '../shared/constants'; -import { ContextNode, PropsMap, SelectionState } from '../shared/types.d'; - -/** - * A singleton for handling preview-manager and one-time-only side-effects. - */ -export const ContextsPreviewAPI = singleton(() => { - const channel = addons.getChannel(); - let contextsNodesMemo: ContextNode[] | null = null; - let selectionState: SelectionState = {}; - - /** - * URL query param can be used to predetermine the contexts a story should render, - * which is useful for performing image snapshot testing or URL sharing. - */ - if (window && window.location) { - selectionState = deserialize(parse(window.location.search)[PARAM]) || {}; - } - - /** - * (Vue specific) - * Vue will inject getter/setter watchers on the first rendering of the addon, - * which is why we have to keep an internal reference and use `Object.assign` to notify the watcher. - */ - const reactivePropsMap = {}; - const updateReactiveSystem = (propsMap: PropsMap) => Object.assign(reactivePropsMap, propsMap); - - /** - * Preview-manager communications. - */ - // from manager - channel.on(UPDATE_PREVIEW, (state) => { - if (state) { - selectionState = state; - channel.emit(FORCE_RE_RENDER); - } - }); - channel.on(REBOOT_MANAGER, () => { - channel.emit(UPDATE_MANAGER, contextsNodesMemo); - }); - channel.on(SET_CURRENT_STORY, () => { - // trash the memorization since the story-level setting may change (diffing it is much expensive) - contextsNodesMemo = null; - }); - - // to manager - const getContextNodesWithSideEffects: typeof getContextNodes = (...arg) => { - if (contextsNodesMemo === null) { - contextsNodesMemo = getContextNodes(...arg); - channel.emit(UPDATE_MANAGER, contextsNodesMemo); - } - return contextsNodesMemo; - }; - - /** - * @Public - * Exposed interfaces - */ - return { - // methods get called on Storybook event lifecycle - getContextNodes: getContextNodesWithSideEffects, - getSelectionState: () => selectionState, - getPropsMap, - - // methods for processing framework specific bindings - getRendererFrom, - updateReactiveSystem, - }; -}); diff --git a/addons/contexts/src/preview/frameworks/preact.ts b/addons/contexts/src/preview/frameworks/preact.ts deleted file mode 100644 index ece1de938e6b..000000000000 --- a/addons/contexts/src/preview/frameworks/preact.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { h, VNode } from 'preact'; -import { createAddonDecorator, Render } from '../../index'; -import { ContextsPreviewAPI } from '../ContextsPreviewAPI'; - -/** - * This is the framework specific bindings for Preact. - * '@storybook/preact' expects the returning object from a decorator to be a 'Preact vNode'. - */ -export const renderPreact: Render = (contextNodes, propsMap, getStoryVNode) => { - const { getRendererFrom } = ContextsPreviewAPI(); - return getRendererFrom(h)(contextNodes, propsMap, getStoryVNode); -}; - -export const withContexts = createAddonDecorator(renderPreact); diff --git a/addons/contexts/src/preview/frameworks/rax.ts b/addons/contexts/src/preview/frameworks/rax.ts deleted file mode 100644 index 0748a7d7f7cf..000000000000 --- a/addons/contexts/src/preview/frameworks/rax.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { createElement, RaxElement } from 'rax'; -import { createAddonDecorator, Render } from '../../index'; -import { ContextsPreviewAPI } from '../ContextsPreviewAPI'; - -/** - * This is the framework specific bindings for Rax. - * '@storybook/rax' expects the returning object from a decorator to be a 'Rax Element' (vNode). - */ -export const renderRax: Render> = (contextNodes, propsMap, getStoryVNode) => { - const { getRendererFrom } = ContextsPreviewAPI(); - return getRendererFrom(createElement)(contextNodes, propsMap, getStoryVNode); -}; - -export const withContexts = createAddonDecorator(renderRax); diff --git a/addons/contexts/src/preview/frameworks/react.ts b/addons/contexts/src/preview/frameworks/react.ts deleted file mode 100644 index d2a0458bbccb..000000000000 --- a/addons/contexts/src/preview/frameworks/react.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { createElement, ReactElement } from 'react'; -import { createAddonDecorator, Render } from '../../index'; -import { ContextsPreviewAPI } from '../ContextsPreviewAPI'; - -/** - * This is the framework specific bindings for React. - * '@storybook/react' expects the returning object from a decorator to be a 'React Element' (vNode). - */ -export const renderReact: Render = (contextNodes, propsMap, getStoryVNode) => { - const { getRendererFrom } = ContextsPreviewAPI(); - return getRendererFrom(createElement)(contextNodes, propsMap, getStoryVNode); -}; - -export const withContexts = createAddonDecorator(renderReact); diff --git a/addons/contexts/src/preview/frameworks/vue.ts b/addons/contexts/src/preview/frameworks/vue.ts deleted file mode 100644 index 650f29de4fb6..000000000000 --- a/addons/contexts/src/preview/frameworks/vue.ts +++ /dev/null @@ -1,28 +0,0 @@ -import Vue from 'vue'; -import { createAddonDecorator, Render } from '../../index'; -import { ContextsPreviewAPI } from '../ContextsPreviewAPI'; -import { ID } from '../../shared/constants'; - -/** - * This is the framework specific bindings for Vue. - * '@storybook/vue' expects the returning object from a decorator to be a 'VueComponent'. - */ -export const renderVue: Render = (contextNodes, propsMap, getStoryComponent) => { - const { getRendererFrom, updateReactiveSystem } = ContextsPreviewAPI(); - const reactiveProps = updateReactiveSystem(propsMap); - return Vue.extend({ - name: ID, - data: () => reactiveProps, // deepscan-disable-line - render: (createElement) => - getRendererFrom((Component, props, children) => { - const { key, ref, style, classNames, ...rest } = props || Object(); - const contextData = - Component instanceof Object - ? { key, ref, style, class: classNames, props: rest } // component as a Vue object - : { key, ref, style, class: classNames, attrs: rest }; // component as a HTML tag string - return createElement(Component, contextData, [children]); - })(contextNodes, reactiveProps, () => createElement(getStoryComponent())), - }); -}; - -export const withContexts = createAddonDecorator(renderVue); diff --git a/addons/contexts/src/preview/libs/decorators.test.ts b/addons/contexts/src/preview/libs/decorators.test.ts deleted file mode 100644 index f9c93ff33d3f..000000000000 --- a/addons/contexts/src/preview/libs/decorators.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { memorize, singleton } from './decorators'; - -describe('Test on functional helpers: memorize', () => { - it('should memorize the calculated result', () => { - // given - const someFn = jest.fn((x) => [x]); - const someFnMemo = memorize(someFn); - - // when - const resultA = someFnMemo(1); - const resultB = someFnMemo(2); - const resultC = someFnMemo(1); - - // then - expect(someFn).toHaveBeenCalledTimes(2); - expect(resultA).toEqual(someFn(1)); - expect(resultA).not.toEqual(resultB); - expect(resultA).toBe(resultC); - expect(resultB).not.toEqual(resultC); - }); - - it('should memorize based on the second argument', () => { - // given - const someFn = jest.fn((x, y) => [x, y]); - const someFnMemo = memorize(someFn, (x, y) => y); - - // when - const resultA = someFnMemo(1, 2); - const resultB = someFnMemo(2, 2); - const resultC = someFnMemo(1, 3); - - // then - expect(someFn).toHaveBeenCalledTimes(2); - expect(resultA).toEqual(someFn(1, 2)); - expect(resultA).toBe(resultB); - expect(resultA).not.toEqual(resultC); - expect(resultB).not.toEqual(resultC); - }); -}); - -describe('Test on functional helpers: singleton', () => { - it('should make a function singleton', () => { - // given - const someFn = jest.fn((x, y, z) => [x, y, z]); - const someFnSingleton = singleton(someFn); - - // when - const resultA = someFnSingleton(1, 2, 3); - const resultB = someFnSingleton(4, 5, 6); - const resultC = someFnSingleton(7, 8, 9); - - // then - expect(someFn).toHaveBeenCalledTimes(1); - expect(resultA).toEqual(someFn(1, 2, 3)); - expect(resultA).toBe(resultB); - expect(resultA).toBe(resultC); - expect(resultB).toBe(resultC); - }); -}); diff --git a/addons/contexts/src/preview/libs/decorators.ts b/addons/contexts/src/preview/libs/decorators.ts deleted file mode 100644 index c446f0a48fe5..000000000000 --- a/addons/contexts/src/preview/libs/decorators.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Memorize the calculated result of a function by an ES6 Map; - * the default is to memorize its the first argument; - * @return the memorized version of a function. - */ -type memorize = ( - fn: (...args: U) => T, - resolver?: (...args: U) => unknown -) => (...args: U) => T; - -export const memorize: memorize = (fn, resolver) => { - const memo = new Map(); - return (...arg) => { - const key = resolver ? resolver(...arg) : arg[0]; - return memo.get(key) || memo.set(key, fn(...arg)).get(key); - }; -}; - -/** - * Enforce a given function can only be executed once; - * the returned value is cached for resolving the subsequent calls. - * @return the singleton version of a function. - */ -type singleton = (fn: (...args: U) => T) => (...args: U) => T; - -export const singleton: singleton = (fn) => memorize(fn, () => 'singleton'); diff --git a/addons/contexts/src/preview/libs/getContextNodes.test.ts b/addons/contexts/src/preview/libs/getContextNodes.test.ts deleted file mode 100644 index d6bbb23c24e6..000000000000 --- a/addons/contexts/src/preview/libs/getContextNodes.test.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { _getMergedSettings, getContextNodes } from './getContextNodes'; - -describe('Test on the merging result of a pair of settings', () => { - it('should retain the basic structure even receiving empty objects', () => { - // when - const result = _getMergedSettings({}, {}); - - // then - expect(result).toEqual({ - components: [], - icon: '', - nodeId: '', - options: { cancelable: false, deep: false, disable: false }, - params: [{ name: '', props: {} }], - title: '', - }); - }); - - it('should correctly merge two settings', () => { - // given - const someTopLevelSettings = { - icon: 'box' as const, - title: 'Some Context', - components: ['div'], - params: [ - { name: 'T1', props: {} }, - { name: 'T2', props: {} }, - ], - options: { - cancelable: true, - disable: true, - }, - }; - const someStoryLevelSettings = { - icon: 'box' as const, - title: 'Some Context', - components: ['span'], - params: [ - { name: 'S1', props: {} }, - { name: 'S2', props: {} }, - ], - options: { - deep: true, - disable: false, - }, - }; - - // when - const result = _getMergedSettings(someTopLevelSettings, someStoryLevelSettings); - - // then - expect(result).toEqual({ - // topLevel over storyLevel - nodeId: someTopLevelSettings.title, - icon: someTopLevelSettings.icon, - title: someTopLevelSettings.title, - components: someTopLevelSettings.components, - - // storyLevel appends to topLevel - params: [...someTopLevelSettings.params, ...someStoryLevelSettings.params], - - // storyLevel over topLevel - options: { - cancelable: someTopLevelSettings.options.cancelable, - deep: someStoryLevelSettings.options.deep, - disable: someStoryLevelSettings.options.disable, - }, - }); - }); -}); - -describe('Test on reconciliation of settings', () => { - it('should have a stable array ordering after normalization', () => { - // when - const result = getContextNodes({ - // from the topLevel - options: [ - { - icon: 'box', - title: 'Some Context', - components: ['div'], - params: [{ name: 'T1', props: {} }], - }, - { - icon: 'box', - title: 'Another Context', - components: ['div'], - params: [{ name: 'T2', props: {} }], - }, - ], - // from the storyLevel - parameters: [ - { - icon: 'box', - title: 'Other Contexts', - components: ['span'], - params: [{ name: 'S1', props: {} }], - }, - { - icon: 'box', - title: 'Some Context', - components: ['p'], - params: [{ name: 'S2', props: {}, default: true }], - }, - ], - }); - - // then - expect(result).toEqual([ - { - components: ['div'], - icon: 'box', - nodeId: 'Some Context', - options: { cancelable: false, deep: false, disable: false }, - params: [ - { name: 'T1', props: {} }, - { name: 'S2', props: {}, default: true }, - ], - title: 'Some Context', - }, - { - components: ['div'], - icon: 'box', - nodeId: 'Another Context', - options: { cancelable: false, deep: false, disable: false }, - params: [{ name: 'T2', props: {} }], - title: 'Another Context', - }, - { - components: ['span'], - icon: 'box', - nodeId: 'Other Contexts', - options: { cancelable: false, deep: false, disable: false }, - params: [{ name: 'S1', props: {} }], - title: 'Other Contexts', - }, - ]); - }); -}); diff --git a/addons/contexts/src/preview/libs/getContextNodes.ts b/addons/contexts/src/preview/libs/getContextNodes.ts deleted file mode 100644 index b0ff813c74da..000000000000 --- a/addons/contexts/src/preview/libs/getContextNodes.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* eslint-disable no-underscore-dangle */ -import { AddonSetting, ContextNode, WrapperSettings } from '../../shared/types.d'; - -/** - * @private - * Merge the top-level (global options) and the story-level (parameters) from a pair of setting; - * @return the normalized definition for a contextual environment (i.e. a contextNode). - */ -type _getMergedSettings = ( - topLevel: Partial, - storyLevel: Partial -) => ContextNode; - -export const _getMergedSettings: _getMergedSettings = (topLevel, storyLevel) => ({ - // strip out special characters reserved for serializing - nodeId: (topLevel.title || storyLevel.title || '').replace(/[,+]/g, ''), - icon: topLevel.icon || storyLevel.icon || '', - title: topLevel.title || storyLevel.title || '', - components: topLevel.components || storyLevel.components || [], - params: - topLevel.params || storyLevel.params - ? [...(topLevel.params || []), ...(storyLevel.params || [])].filter(Boolean) - : [{ name: '', props: {} }], - options: { - deep: false, - disable: false, - cancelable: false, - ...topLevel.options, - ...storyLevel.options, - }, -}); - -/** - * @nosideeffects - * Pair up settings for merging normalizations to produce the contextual definitions (i.e. contextNodes); - * it guarantee the adding order can be respected but not duplicated. - */ -type getContextNodes = (settings: WrapperSettings) => ContextNode[]; - -export const getContextNodes: getContextNodes = ({ options, parameters }) => { - const titles = [...(options || []), ...(parameters || [])] - .filter(Boolean) - .map(({ title }) => title); - - return Array.from(new Set(titles)) - .filter(Boolean) - .map((title) => - _getMergedSettings( - (options && options.find((option) => option.title === title)) || {}, - (parameters && parameters.find((param) => param.title === title)) || {} - ) - ); -}; diff --git a/addons/contexts/src/preview/libs/getPropsMap.test.ts b/addons/contexts/src/preview/libs/getPropsMap.test.ts deleted file mode 100644 index 321d926e38b8..000000000000 --- a/addons/contexts/src/preview/libs/getPropsMap.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { _getPropsByParamName, getPropsMap } from './getPropsMap'; -import { OPT_OUT } from '../../shared/constants'; - -describe('Test on behaviors from collecting the propsMap', () => { - const someParams = [ - { name: 'A', props: {} }, - { name: 'B', props: {} }, - ]; - - it('should return "null" when params in 0 length', () => { - const result = _getPropsByParamName([]); - expect(result).toBe(null); - }); - - it('should return "OPT_OUT" token when the context being opted out', () => { - const result = _getPropsByParamName(someParams, OPT_OUT, { cancelable: true }); - expect(result).toBe(OPT_OUT); - }); - - it('should return the props from params when the name existed', () => { - const target = {}; - const result = _getPropsByParamName([...someParams, { name: 'C', props: target }], 'C'); - expect(result).toBe(target); - }); - - it('should otherwise fallback to default props in params for a bad name', () => { - const target = {}; - const result = _getPropsByParamName( - [...someParams, { name: 'C', props: target, default: true }], - 'X' - ); - expect(result).toBe(target); - }); - - it('should otherwise fallback to the first props in params for a bad name, if no marked default props', () => { - const result = _getPropsByParamName(someParams, 'A'); - expect(result).toBe(someParams[0].props); - }); -}); - -describe('Test on the integrity of the method to get the propMaps', () => { - it('should return the correct propsMap from the specified selectionState', () => { - // given - const someContextNodes = [ - { - components: ['div'], - icon: 'box' as const, - nodeId: 'Some Context', - options: { cancelable: false, deep: false, disable: false }, - params: [ - { name: 'A1', props: { a: 1 } }, - { name: 'A2', props: { a: 2 }, default: true }, - ], - title: 'Some Context', - }, - { - components: ['div'], - icon: 'box' as const, - nodeId: 'Another Context', - options: { cancelable: false, deep: false, disable: false }, - params: [{ name: 'B', props: { b: 1 } }], - title: 'Another Context', - }, - { - components: ['span'], - icon: 'box' as const, - nodeId: 'Other Contexts', - options: { cancelable: false, deep: false, disable: false }, - params: [{ name: 'C', props: { c: 1 } }], - title: 'Other Contexts', - }, - ]; - const someSelectionState = { - 'Some Context': 'A1', - 'Another Context': OPT_OUT, // an inconsistent but possible state being introduced via query param - }; - - // when - const result = getPropsMap(someContextNodes, someSelectionState); - - // then - expect(result).toEqual({ - 'Some Context': { a: 1 }, - 'Another Context': { b: 1 }, // not equal to `OPT_OUT` due to the context is not cancelable - 'Other Contexts': { c: 1 }, - }); - }); -}); diff --git a/addons/contexts/src/preview/libs/getPropsMap.ts b/addons/contexts/src/preview/libs/getPropsMap.ts deleted file mode 100644 index 2915fbe502ef..000000000000 --- a/addons/contexts/src/preview/libs/getPropsMap.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* eslint-disable no-underscore-dangle */ -import { OPT_OUT } from '../../shared/constants'; -import { ContextNode, GenericProp, PropsMap, SelectionState } from '../../shared/types.d'; - -/** - * @private - * Extract the activated props by name from a given contextual params. - */ -type _getPropsByParamName = ( - params: ContextNode['params'], - name?: string, - options?: Partial -) => GenericProp | typeof OPT_OUT; - -export const _getPropsByParamName: _getPropsByParamName = (params, name = '', options = {}) => { - const { props = null } = - // when opt-out context - (options.cancelable && name === OPT_OUT && { props: OPT_OUT }) || - // when menu option get selected - (name && params.find((param) => param.name === name)) || - // when being initialized - params.find((param) => !!param.default) || - // fallback to the first - params[0] || - // fallback for destructuring - {}; - return props; -}; - -/** - * @nosideeffects - * Collect the propsMap from Nodes based on a controlled state tracker. - */ -type getPropsMap = (contextNodes: ContextNode[], selectionState: SelectionState) => PropsMap; - -export const getPropsMap: getPropsMap = (contextNodes, selectionState) => - contextNodes.reduce((agg, { nodeId, params, options }) => { - // eslint-disable-next-line no-param-reassign - agg[nodeId] = _getPropsByParamName(params, selectionState[nodeId], options); - return agg; - }, Object()); diff --git a/addons/contexts/src/preview/libs/getRendererFrom.test.ts b/addons/contexts/src/preview/libs/getRendererFrom.test.ts deleted file mode 100644 index 210b54c7733d..000000000000 --- a/addons/contexts/src/preview/libs/getRendererFrom.test.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { _getAggregatedWrap, getRendererFrom } from './getRendererFrom'; -import { OPT_OUT } from '../../shared/constants'; - -// mocks -const h = jest.fn(); -const spiedAggregator = _getAggregatedWrap(h); - -beforeEach(() => { - h.mockReset(); -}); - -// tests -describe('Test on aggregation of a single context', () => { - const fakeTag = 'fakeTag'; - const fakeComponent = () => ''; - - it('should skip wrapping when being set to disable', () => { - // given - const testedProps = {}; - const testedOption = { disable: true }; - - // when - spiedAggregator([fakeTag, fakeComponent], testedProps, testedOption)(); - - // then - expect(h).toHaveBeenCalledTimes(0); - }); - - it('should skip wrapping when props is marked as "OPT_OUT"', () => { - // given - const testedProps = OPT_OUT; - const testedOption = { cancelable: true }; - - // when - spiedAggregator([fakeTag, fakeComponent], testedProps, testedOption)(); - - // then - expect(h).toHaveBeenCalledTimes(0); - }); - - it('should wrap components in the stacking order', () => { - // given - const testedProps = {}; - const testedOption = {}; - - // when - spiedAggregator([fakeTag, fakeComponent], testedProps, testedOption)(); - - // then - expect(h).toHaveBeenCalledTimes(2); - expect(h.mock.calls[0][0]).toBe(fakeComponent); - expect(h.mock.calls[1][0]).toBe(fakeTag); - }); - - it('should NOT pass props deeply by default', () => { - // given - const testedProps = {}; - const testedOption = {}; - - // when - spiedAggregator([fakeTag, fakeComponent], testedProps, testedOption)(); - - // then - expect(h.mock.calls[0][1]).toBe(null); - expect(h.mock.calls[1][1]).toBe(testedProps); - }); - - it('should pass props deeply', () => { - const testedProps = {}; - const testedOption = { deep: true }; - spiedAggregator([fakeTag, fakeComponent], testedProps, testedOption)(); - expect(h.mock.calls[0][1]).toBe(testedProps); - expect(h.mock.calls[1][1]).toBe(testedProps); - }); -}); - -describe('Test on aggregation of contexts', () => { - it('should aggregate contexts in the stacking order', () => { - // given - const someContextNodes = [ - { - components: ['div'], - icon: 'box' as const, - nodeId: 'Some Context', - options: { cancelable: false, deep: false, disable: false }, - params: [{ name: 'A', props: {} }], - title: 'Some Context', - }, - { - components: ['span'], - icon: 'box' as const, - nodeId: 'Another Context', - options: { cancelable: false, deep: false, disable: false }, - params: [{ name: 'B', props: {} }], - title: 'Another Context', - }, - ]; - const propsMap = { - 'Some Context': {}, - 'Another Context': {}, - }; - - // when - getRendererFrom(h)(someContextNodes, propsMap, () => {}); - - // then - expect(h.mock.calls[0][0]).toBe(someContextNodes[1].components[0]); - expect(h.mock.calls[1][0]).toBe(someContextNodes[0].components[0]); - }); -}); diff --git a/addons/contexts/src/preview/libs/getRendererFrom.ts b/addons/contexts/src/preview/libs/getRendererFrom.ts deleted file mode 100644 index 823e892dbd0e..000000000000 --- a/addons/contexts/src/preview/libs/getRendererFrom.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* eslint-disable no-underscore-dangle */ -import { OPT_OUT } from '../../shared/constants'; -import { - AddonOptions, - AnyFunctionReturns, - ContextNode, - GenericProp, - PropsMap, -} from '../../shared/types.d'; - -/** - * @private - * Aggregate component vNodes with activated props in a descending order, - * based on the given options in the contextual environment setup. - * - * @param {function} h - the associated `createElement` vNode creator from the framework - */ -type _getAggregatedWrap = ( - h: AnyFunctionReturns -) => ( - components: ContextNode['components'], - props: GenericProp | typeof OPT_OUT, - options: AddonOptions -) => AnyFunctionReturns; - -export const _getAggregatedWrap: _getAggregatedWrap = (h) => (components, props, options) => ( - vNode -) => { - const last = components.length - 1; - const isSkipped = - // when set to disable - options.disable || - // when opt-out context - (options.cancelable && props === OPT_OUT); - - return isSkipped - ? vNode - : components - // shallow clone the array since .reverse() is not pure - .concat() - // reverse the array to get the correct wrapping sequence (i.e. left(right)) - .reverse() - .reduce((acc, C, index) => h(C, options.deep || index === last ? props : null, acc), vNode); -}; - -/** - * @nosideeffects - * Aggregate aggregated-components among all contextual nodes in a descending order; - * this is the core of this addon, which is based on the general virtual DOM implementation. - * - * @param {function} h - the associated `createElement` vNode creator from the framework - */ -type getRendererFrom = ( - h: AnyFunctionReturns -) => (contextNodes: ContextNode[], propsMap: PropsMap, getStoryVNode: AnyFunctionReturns) => T; - -export const getRendererFrom: getRendererFrom = (h) => (contextNodes, propsMap, getStoryVNode) => - contextNodes - // map over contextual nodes to get the wrapping function - .map(({ nodeId, components, options }) => - _getAggregatedWrap(h)(components, propsMap[nodeId], options) - ) - // reverse the array to get the correct wrapping sequence (i.e. top(down)) - .reverse() - // stitch everything to get the final vNode - .reduce((vNode, wrap) => wrap(vNode), getStoryVNode()); diff --git a/addons/contexts/src/preview/libs/index.ts b/addons/contexts/src/preview/libs/index.ts deleted file mode 100644 index 95230084eeb7..000000000000 --- a/addons/contexts/src/preview/libs/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { memorize, singleton } from './decorators'; -export { getContextNodes } from './getContextNodes'; -export { getPropsMap } from './getPropsMap'; -export { getRendererFrom } from './getRendererFrom'; diff --git a/addons/contexts/src/register.ts b/addons/contexts/src/register.ts deleted file mode 100644 index af3f7d308337..000000000000 --- a/addons/contexts/src/register.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { createElement } from 'react'; -import addons, { types } from '@storybook/addons'; -import { ContextsManager } from './manager/ContextsManager'; -import { ID } from './shared/constants'; - -addons.register(ID, (api) => - addons.add(ID, { - title: ID, - type: types.TOOL, - match: ({ viewMode }) => viewMode === 'story', - render: () => createElement(ContextsManager, { api }), - }) -); diff --git a/addons/contexts/src/shared/@mock-types/_global.d.ts b/addons/contexts/src/shared/@mock-types/_global.d.ts deleted file mode 100644 index 2f4eb9cf4fd9..000000000000 --- a/addons/contexts/src/shared/@mock-types/_global.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module 'global'; diff --git a/addons/contexts/src/shared/@mock-types/_preact.d.ts b/addons/contexts/src/shared/@mock-types/_preact.d.ts deleted file mode 100644 index 2b90ddf377ac..000000000000 --- a/addons/contexts/src/shared/@mock-types/_preact.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Preact v8.4.2 shipped with global polluted JSX typing, which breaks the React components typing under Manager - */ -declare module 'preact' { - declare type VNode = any; - declare const h: any = () => {}; -} diff --git a/addons/contexts/src/shared/constants.ts b/addons/contexts/src/shared/constants.ts deleted file mode 100644 index 0e90045a06f6..000000000000 --- a/addons/contexts/src/shared/constants.ts +++ /dev/null @@ -1,17 +0,0 @@ -export { FORCE_RE_RENDER, SET_CURRENT_STORY } from '@storybook/core-events'; - -// configs -export const ID = 'addon-contexts' as const; -export const PARAM = 'contexts' as const; - -// tokens -/** - * OPT_OUT is a token for skipping a context, dundering the string to avoid name collisions; - * ES6 Symbol is not available due to stringify used in Storybook event system via the channel. - */ -export const OPT_OUT = '__OPT_OUT__' as const; - -// events -export const REBOOT_MANAGER = `${ID}/REBOOT_MANAGER`; -export const UPDATE_MANAGER = `${ID}/UPDATE_MANAGER`; -export const UPDATE_PREVIEW = `${ID}/UPDATE_PREVIEW`; diff --git a/addons/contexts/src/shared/serializers.test.ts b/addons/contexts/src/shared/serializers.test.ts deleted file mode 100644 index b4cc47a2648f..000000000000 --- a/addons/contexts/src/shared/serializers.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { deserialize, serialize } from './serializers'; - -describe('Test on serializers', () => { - // given - const someContextsQueryParam = 'CSS Themes=Forests,Languages=Fr'; - const someSelectionState = { - 'CSS Themes': 'Forests', - Languages: 'Fr', - }; - - it('Should deserialize a string representation into the represented selection state', () => { - expect(deserialize('')).toEqual(null); - expect(deserialize('An invalid string=')).toEqual(null); - expect(deserialize(someContextsQueryParam)).toEqual(someSelectionState); - }); - - it('Should serialize selection state into its string representation', () => { - expect(serialize(null)).toEqual(null); - expect(serialize(someSelectionState)).toEqual(someContextsQueryParam); - }); -}); diff --git a/addons/contexts/src/shared/serializers.ts b/addons/contexts/src/shared/serializers.ts deleted file mode 100644 index f7f3d5cdf1d6..000000000000 --- a/addons/contexts/src/shared/serializers.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { SelectionState } from './types.d'; - -/** - * Deserialize URL query param into the specified selection state. - */ -type deserialize = (param?: string) => SelectionState | null; - -export const deserialize: deserialize = (param) => - !param - ? null - : param - .split(/,+/g) - .map((str) => str.split(/=+/g)) - .reduce( - (acc, [nodeId, name]) => (nodeId && name ? { ...acc, [nodeId]: name } : acc), - null - ); - -/** - * Serialize the selection state in its string representation. - */ -type serialize = (state: ReturnType) => string | null; - -export const serialize: serialize = (state) => - !state - ? null - : Object.entries(state) - .map((tuple) => tuple.join('=')) - .join(','); diff --git a/addons/contexts/src/shared/types.d.ts b/addons/contexts/src/shared/types.d.ts deleted file mode 100644 index a60159fcb1a8..000000000000 --- a/addons/contexts/src/shared/types.d.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { ComponentProps, FunctionComponent } from 'react'; -import { Icons } from '@storybook/components'; - -export { API as ManagerAPI } from '@storybook/api'; - -// helpers -export declare type AnyFunctionReturns = (...arg: any[]) => T; -export declare type FCNoChildren

= FunctionComponent<{ children?: never } & P>; -export declare type GenericProp = null | { - readonly [key: string]: unknown; -}; - -// interfaces -export declare interface AddonOptions { - deep?: boolean; - disable?: boolean; - cancelable?: boolean; -} - -export declare interface AddonSetting { - icon?: ComponentProps['icon'] | ''; - title: string; - components?: unknown[]; - params?: { - name: string; - props: GenericProp; - default?: boolean; - }[]; - options?: AddonOptions; -} - -export declare interface ContextNode extends Required { - nodeId: string; - options: Required; -} - -export declare interface SelectionState { - readonly [key: string]: string | undefined; -} - -export declare interface PropsMap { - readonly [key: string]: GenericProp; -} - -export declare interface WrapperSettings { - options?: AddonSetting[]; - // `parameters` can be set to `false` to disable the addon - parameters?: AddonSetting[] | false; -} diff --git a/addons/contexts/tsconfig.json b/addons/contexts/tsconfig.json deleted file mode 100644 index 9e57fa03aa0e..000000000000 --- a/addons/contexts/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "baseUrl": ".", - "paths": { - "preact": ["src/shared/@mock-types/_preact.d.ts"] - }, - "strictNullChecks": true, - "removeComments": true, - "rootDir": "./src", - "types": ["webpack-env", "jest"] - }, - "include": ["src/**/*"], - "exclude": ["src/register.ts", "src/**/*.test.ts"] -} diff --git a/addons/contexts/vue.js b/addons/contexts/vue.js deleted file mode 100644 index 474e9fb610c5..000000000000 --- a/addons/contexts/vue.js +++ /dev/null @@ -1,4 +0,0 @@ -import { withContexts } from './dist/preview/frameworks/vue'; - -export { withContexts }; -export default withContexts; From 98df22656162f7338bfa7c8628767ae8c8472a8c Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Mon, 20 Apr 2020 16:28:48 +0200 Subject: [PATCH 2/8] CLEANUP after removing addon-contexts --- examples/official-storybook/main.js | 1 - examples/official-storybook/package.json | 1 - .../stories/addon-contexts.stories.js | 101 ------------ .../preact-kitchen-sink/.storybook/main.js | 1 - examples/preact-kitchen-sink/package.json | 1 - .../src/stories/addon-contexts.stories.js | 58 ------- .../src/stories/addon-contexts.stories.js | 57 ------- examples/vue-kitchen-sink/.storybook/main.js | 1 - examples/vue-kitchen-sink/package.json | 1 - .../src/stories/addon-contexts.stories.js | 146 ------------------ 10 files changed, 368 deletions(-) delete mode 100644 examples/official-storybook/stories/addon-contexts.stories.js delete mode 100644 examples/preact-kitchen-sink/src/stories/addon-contexts.stories.js delete mode 100644 examples/rax-kitchen-sink/src/stories/addon-contexts.stories.js delete mode 100644 examples/vue-kitchen-sink/src/stories/addon-contexts.stories.js diff --git a/examples/official-storybook/main.js b/examples/official-storybook/main.js index b1a91108b4b1..71770c99b4cc 100644 --- a/examples/official-storybook/main.js +++ b/examples/official-storybook/main.js @@ -20,7 +20,6 @@ module.exports = { '@storybook/addon-jest', '@storybook/addon-viewport', '@storybook/addon-graphql', - '@storybook/addon-contexts', '@storybook/addon-toolbars', '@storybook/addon-queryparams', ], diff --git a/examples/official-storybook/package.json b/examples/official-storybook/package.json index 8f685b473974..ff9b5da74a65 100644 --- a/examples/official-storybook/package.json +++ b/examples/official-storybook/package.json @@ -17,7 +17,6 @@ "@storybook/addon-a11y": "6.0.0-alpha.40", "@storybook/addon-actions": "6.0.0-alpha.40", "@storybook/addon-backgrounds": "6.0.0-alpha.40", - "@storybook/addon-contexts": "6.0.0-alpha.40", "@storybook/addon-cssresources": "6.0.0-alpha.40", "@storybook/addon-design-assets": "6.0.0-alpha.40", "@storybook/addon-docs": "6.0.0-alpha.40", diff --git a/examples/official-storybook/stories/addon-contexts.stories.js b/examples/official-storybook/stories/addon-contexts.stories.js deleted file mode 100644 index d85a97ad6c79..000000000000 --- a/examples/official-storybook/stories/addon-contexts.stories.js +++ /dev/null @@ -1,101 +0,0 @@ -import React from 'react'; -import { withContexts } from '@storybook/addon-contexts/react'; - -// Example A: Simple CSS Theming -const topLevelContexts = [ - { - title: 'CSS Themes', - components: ['div'], - params: [ - { - name: 'Desert', - props: { - style: { color: 'brown', background: '#F4A261', height: '100vh', padding: '10px' }, - }, - }, - { - name: 'Ocean', - props: { - style: { color: 'white', background: '#173F5F', height: '100vh', padding: '10px' }, - }, - default: true, - }, - ], - }, -]; - -const storyLevelContexts = [ - { - title: 'CSS Themes', - params: [ - { - name: 'Forest', - props: { - style: { color: 'teal', background: '#00b894', height: '100vh', padding: '10px' }, - }, - }, - ], - }, -]; - -export default { - title: 'Addons/Contexts', - decorators: [withContexts(topLevelContexts)], -}; - -export const SimpleCssTheming = () => ( - <>I'm a children of the injected 'div' (where provides a theming context). -); -SimpleCssTheming.story = { - name: 'Simple CSS Theming', - parameters: { - contexts: storyLevelContexts, - }, -}; - -// Example B: Language (React Contexts API) -const NaiveIntlContext = React.createContext({ - locale: 'unknown', - greeting: 'NULL', -}); - -export const Languages = () => ( - - {({ locale, greeting }) => `Your locale is "${locale}", so I say "${greeting}"!`} - -); - -Languages.story = { - parameters: { - contexts: [ - { - icon: 'globe', - title: 'Languages', - components: [NaiveIntlContext.Provider], - params: [ - { - name: 'English', - props: { - value: { locale: 'en', greeting: 'Hello' }, - }, - }, - { - name: 'French', - props: { - value: { locale: 'fr', greeting: 'Bonjour' }, - }, - }, - { - name: 'Chinese', - props: { - value: { locale: 'cn', greeting: '你好' }, - }, - }, - ], - options: { - cancelable: true, - }, - }, - ], - }, -}; diff --git a/examples/preact-kitchen-sink/.storybook/main.js b/examples/preact-kitchen-sink/.storybook/main.js index 2d8104bb0224..c20a8b0496a0 100644 --- a/examples/preact-kitchen-sink/.storybook/main.js +++ b/examples/preact-kitchen-sink/.storybook/main.js @@ -9,7 +9,6 @@ module.exports = { '@storybook/addon-knobs', '@storybook/addon-viewport', '@storybook/addon-backgrounds', - '@storybook/addon-contexts', '@storybook/addon-a11y', ], webpackFinal: (config) => { diff --git a/examples/preact-kitchen-sink/package.json b/examples/preact-kitchen-sink/package.json index 1e8be50c8ba5..eea2d6fbe6fd 100644 --- a/examples/preact-kitchen-sink/package.json +++ b/examples/preact-kitchen-sink/package.json @@ -18,7 +18,6 @@ "@storybook/addon-a11y": "6.0.0-alpha.40", "@storybook/addon-actions": "6.0.0-alpha.40", "@storybook/addon-backgrounds": "6.0.0-alpha.40", - "@storybook/addon-contexts": "6.0.0-alpha.40", "@storybook/addon-knobs": "6.0.0-alpha.40", "@storybook/addon-links": "6.0.0-alpha.40", "@storybook/addon-storyshots": "6.0.0-alpha.40", diff --git a/examples/preact-kitchen-sink/src/stories/addon-contexts.stories.js b/examples/preact-kitchen-sink/src/stories/addon-contexts.stories.js deleted file mode 100644 index f310300239ce..000000000000 --- a/examples/preact-kitchen-sink/src/stories/addon-contexts.stories.js +++ /dev/null @@ -1,58 +0,0 @@ -/** @jsx h */ -import { h } from 'preact'; -import { withContexts } from '@storybook/addon-contexts/preact'; - -// Example A: Simple CSS Theming -const topLevelContexts = [ - { - icon: 'sidebaralt', - title: 'CSS Themes', - components: ['div'], - params: [ - { - name: 'Desert', - props: { - style: { color: 'brown', background: '#F4A261', height: '100vh', padding: '10px' }, - }, - }, - { - name: 'Ocean', - props: { - style: { color: 'white', background: '#173F5F', height: '100vh', padding: '10px' }, - }, - default: true, - }, - ], - }, -]; - -const storyLevelContexts = [ - { - title: 'CSS Themes', - params: [ - { - name: 'Forest', - props: { - style: { color: 'teal', background: '#00b894', height: '100vh', padding: '10px' }, - }, - }, - ], - }, -]; - -export default { - title: 'Addons/Contexts', - decorators: [withContexts(topLevelContexts)], -}; - -export const SimpleCssTheming = () => ( -

I'm a children of the injected 'div' (where provides a theming context).
-); - -SimpleCssTheming.story = { - name: 'Simple CSS Theming', - - parameters: { - contexts: storyLevelContexts, - }, -}; diff --git a/examples/rax-kitchen-sink/src/stories/addon-contexts.stories.js b/examples/rax-kitchen-sink/src/stories/addon-contexts.stories.js deleted file mode 100644 index 32513fbf72e5..000000000000 --- a/examples/rax-kitchen-sink/src/stories/addon-contexts.stories.js +++ /dev/null @@ -1,57 +0,0 @@ -import { createElement } from 'rax'; -import { withContexts } from '@storybook/addon-contexts/rax'; - -// Example A: Simple CSS Theming -const topLevelContexts = [ - { - icon: 'sidebaralt', - title: 'CSS Themes', - components: ['div'], - params: [ - { - name: 'Desert', - props: { - style: { color: 'brown', background: '#F4A261', height: '100vh', padding: '10px' }, - }, - }, - { - name: 'Ocean', - props: { - style: { color: 'white', background: '#173F5F', height: '100vh', padding: '10px' }, - }, - default: true, - }, - ], - }, -]; - -const storyLevelContexts = [ - { - title: 'CSS Themes', - params: [ - { - name: 'Forest', - props: { - style: { color: 'teal', background: '#00b894', height: '100vh', padding: '10px' }, - }, - }, - ], - }, -]; - -export default { - title: 'Addons/Contexts', - decorators: [withContexts(topLevelContexts)], -}; - -export const SimpleCssTheming = () => ( -
I'm a children of the injected 'div' (where provides a theming context).
-); - -SimpleCssTheming.story = { - name: 'Simple CSS Theming', - - parameters: { - contexts: storyLevelContexts, - }, -}; diff --git a/examples/vue-kitchen-sink/.storybook/main.js b/examples/vue-kitchen-sink/.storybook/main.js index 639f9c07317d..fe184fca9fd1 100644 --- a/examples/vue-kitchen-sink/.storybook/main.js +++ b/examples/vue-kitchen-sink/.storybook/main.js @@ -9,6 +9,5 @@ module.exports = { '@storybook/addon-viewport', '@storybook/addon-backgrounds', '@storybook/addon-a11y', - '@storybook/addon-contexts', ], }; diff --git a/examples/vue-kitchen-sink/package.json b/examples/vue-kitchen-sink/package.json index ad7561fd20a7..42d4055c5e21 100644 --- a/examples/vue-kitchen-sink/package.json +++ b/examples/vue-kitchen-sink/package.json @@ -17,7 +17,6 @@ "@storybook/addon-a11y": "6.0.0-alpha.40", "@storybook/addon-actions": "6.0.0-alpha.40", "@storybook/addon-backgrounds": "6.0.0-alpha.40", - "@storybook/addon-contexts": "6.0.0-alpha.40", "@storybook/addon-docs": "6.0.0-alpha.40", "@storybook/addon-knobs": "6.0.0-alpha.40", "@storybook/addon-links": "6.0.0-alpha.40", diff --git a/examples/vue-kitchen-sink/src/stories/addon-contexts.stories.js b/examples/vue-kitchen-sink/src/stories/addon-contexts.stories.js deleted file mode 100644 index 983a20144b6e..000000000000 --- a/examples/vue-kitchen-sink/src/stories/addon-contexts.stories.js +++ /dev/null @@ -1,146 +0,0 @@ -import { withContexts } from '@storybook/addon-contexts/vue'; - -// Example A: Simple CSS Theming -const topLevelContexts = [ - { - icon: 'sidebaralt', - title: 'CSS Themes', - components: ['div'], - params: [ - { - name: 'Desert', - props: { - style: { color: 'brown', background: '#F4A261', height: '100vh', padding: '10px' }, - }, - }, - { - name: 'Ocean', - props: { - style: { color: 'white', background: '#173F5F', height: '100vh', padding: '10px' }, - }, - default: true, - }, - ], - }, -]; - -const storyLevelContexts = [ - { - title: 'CSS Themes', - params: [ - { - name: 'Forest', - props: { - style: { color: 'teal', background: '#00b894', height: '100vh', padding: '10px' }, - }, - }, - ], - }, -]; - -export default { - title: 'Addon/Contexts', - decorators: [withContexts(topLevelContexts)], -}; - -export const SimpleCssTheming = () => ({ - template: "I'm a children of the injected 'div' (where provides a theming context).", -}); - -SimpleCssTheming.story = { - name: 'Simple CSS Theming', - parameters: { - contexts: storyLevelContexts, - }, -}; - -// Example B: Language (Vue provide/inject API) -const createContext = (initialValue) => { - const uid = `_${Date.now()}${Math.random()}`; - return { - Provider: { - name: `Context.Provider`, - props: ['value'], - provide() { - return this.value === undefined - ? undefined - : { - [uid]: () => this.value, - }; - }, - template: ` -
- -
- `, - }, - Consumer: { - name: `Context.Consumer`, - inject: { - [uid]: { - default: () => () => - initialValue instanceof Object ? { ...initialValue } : { value: initialValue }, - }, - }, - computed: { - value() { - return this[uid](); - }, - }, - template: ` -
- -
- `, - }, - }; -}; - -const NaiveIntlContext = createContext({ - locale: 'unknown', - greeting: 'NULL', -}); - -export const Languages = () => ({ - components: { 'NaiveIntlContext.Consumer': NaiveIntlContext.Consumer }, - template: ` - - Your locale is {{ locale }}, so I say {{ greeting }}! - - `, -}); - -Languages.story = { - parameters: { - contexts: [ - { - icon: 'globe', - title: 'Languages', - components: [NaiveIntlContext.Provider], - params: [ - { - name: 'English', - props: { - value: { locale: 'en', greeting: 'Hello' }, - }, - }, - { - name: 'French', - props: { - value: { locale: 'fr', greeting: 'Bonjour' }, - }, - }, - { - name: 'Chinese', - props: { - value: { locale: 'cn', greeting: '你好' }, - }, - }, - ], - options: { - cancelable: true, - }, - }, - ], - }, -}; From 11c301c10697ac56a998ccd5a39ace4d6b0a0519 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Mon, 20 Apr 2020 23:51:08 +0800 Subject: [PATCH 3/8] Add deprecated addons warning to MIGRATION.md --- MIGRATION.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 8adcd4880b59..9e91dd0dfe6d 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,6 +1,7 @@

Migration

- [From version 5.3.x to 6.0.x](#from-version-53x-to-60x) + - [6.0 Deprecated Addons](#60-deprecated-addons) - [DocsPage slots removed](#docspage-slots-removed) - [React prop tables with Typescript](#react-prop-tables-with-typescript) - [React.FC interfaces](#reactfc-interfaces) @@ -101,6 +102,18 @@ ## From version 5.3.x to 6.0.x +### 6.0 Deprecated Addons + +We have moved a few addons into the [deprecated addons repo](https://github.com/storybookjs/deprecated-addons). This means that these addons will no longer be maintained as part of Storybook's core and will be released on an irregular schedule, based on community contributions. + +| Deprecated | Replacement | +| --------------------------- | --------------------------- | +| `@storybook/addon-info` | `@storybook/addon-docs` | +| `@storybook/addon-notes` | `@storybook/addon-docs` | +| `@storybook/addon-contexts` | `@storybook/addon-toolbars` | + +If you are able to upgrade to the recommended replacement, we recommend it! Not only are these new addons an improvement on the packages they replace, but they are actively maintained. + ### DocsPage slots removed In SB5.2, we introduced the concept of [DocsPage slots](https://github.com/storybookjs/storybook/blob/0de8575eab73bfd5c5c7ba5fe33e53a49b92db3a/addons/docs/docs/docspage.md#docspage-slots) for customizing the DocsPage. @@ -746,7 +759,7 @@ var sortedModules = modules.slice().sort((a, b) => { }); // execute them -sortedModules.forEach(key => { +sortedModules.forEach((key) => { context(key); }); ``` @@ -1310,7 +1323,7 @@ Here's an example of using Notes and Info in 3.2 with the new API. storiesOf('composition', module).add( 'new addons api', withInfo('see Notes panel for composition info')( - withNotes({ text: 'Composition: Info(Notes())' })(context => ( + withNotes({ text: 'Composition: Info(Notes())' })((context) => ( )) ) From 57ec4d664b3bd06a50b55f059aaf6800d54a6f34 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Thu, 23 Apr 2020 09:56:03 +0200 Subject: [PATCH 4/8] FIX tests --- .../addon-contexts.stories.storyshot | 18 --------------- .../addon-contexts.stories.storyshot | 23 ------------------- 2 files changed, 41 deletions(-) delete mode 100644 examples/preact-kitchen-sink/src/stories/__snapshots__/addon-contexts.stories.storyshot delete mode 100644 examples/vue-kitchen-sink/src/stories/__snapshots__/addon-contexts.stories.storyshot diff --git a/examples/preact-kitchen-sink/src/stories/__snapshots__/addon-contexts.stories.storyshot b/examples/preact-kitchen-sink/src/stories/__snapshots__/addon-contexts.stories.storyshot deleted file mode 100644 index a6b5d181139f..000000000000 --- a/examples/preact-kitchen-sink/src/stories/__snapshots__/addon-contexts.stories.storyshot +++ /dev/null @@ -1,18 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Storyshots Addons/Contexts Simple CSS Theming 1`] = ` -
-
- I'm a children of the injected 'div' (where provides a theming context). -
-
-`; diff --git a/examples/vue-kitchen-sink/src/stories/__snapshots__/addon-contexts.stories.storyshot b/examples/vue-kitchen-sink/src/stories/__snapshots__/addon-contexts.stories.storyshot deleted file mode 100644 index 5272bd046f37..000000000000 --- a/examples/vue-kitchen-sink/src/stories/__snapshots__/addon-contexts.stories.storyshot +++ /dev/null @@ -1,23 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Storyshots Addon/Contexts Languages 1`] = ` -
-
- - Your locale is unknown, so I say NULL! - -
-
-`; - -exports[`Storyshots Addon/Contexts Simple CSS Theming 1`] = ` -
- - I'm a children of the injected 'div' (where provides a theming context). - -
-`; From d8317ae5efeab1aad767c3740bb48288f13dca5b Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Thu, 23 Apr 2020 13:25:18 +0200 Subject: [PATCH 5/8] CLEANUP --- ADDONS_SUPPORT.md | 1 - README.md | 1 - addons/contexts/package.json | 74 ------------------- .../src/blocks/DocsPageExampleCaption.md | 1 - .../src/blocks/DocsPageExampleCaption.mdx | 1 - 5 files changed, 78 deletions(-) delete mode 100644 addons/contexts/package.json diff --git a/ADDONS_SUPPORT.md b/ADDONS_SUPPORT.md index cc66f2dcebd6..cdad86428e7a 100644 --- a/ADDONS_SUPPORT.md +++ b/ADDONS_SUPPORT.md @@ -5,7 +5,6 @@ | [a11y](addons/a11y) | + | | + | + | + | + | + | + | + | + | + | + | | [actions](addons/actions) | + | +\* | + | + | + | + | + | + | + | + | + | + | | [backgrounds](addons/backgrounds) | + | \* | + | + | + | + | + | + | + | + | + | + | -| [contexts](addons/contexts) | + | | + | | | | | | | | + | + | | [cssresources](addons/cssresources) | + | | + | + | + | + | + | + | + | + | + | + | | [design assets](addons/design-assets) | + | | + | + | + | + | + | + | + | + | + | + | | [docs](addons/docs) | + | | + | + | + | + | + | + | + | + | + | + | diff --git a/README.md b/README.md index c08cc4b3a9c2..3497808f0c07 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,6 @@ For additional help, join us [in our Discord](https://discord.gg/sMFvFsG) or [Sl | [a11y](addons/a11y/) | Test components for user accessibility in Storybook | | [actions](addons/actions/) | Log actions as users interact with components in the Storybook UI | | [backgrounds](addons/backgrounds/) | Let users choose backgrounds in the Storybook UI | -| [contexts](addons/contexts/) | Interactively inject component contexts for stories in the Storybook UI | | [cssresources](addons/cssresources/) | Dynamically add/remove css resources to the component iframe | | [design assets](addons/design-assets/) | View images, videos, weblinks alongside your story | | [docs](addons/docs/) | Add high quality documentation to your components | diff --git a/addons/contexts/package.json b/addons/contexts/package.json deleted file mode 100644 index 4697342e42d7..000000000000 --- a/addons/contexts/package.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "name": "@storybook/addon-contexts", - "version": "6.0.0-alpha.42", - "description": "Storybook Addon Contexts", - "keywords": [ - "preact", - "react", - "storybook", - "vue" - ], - "repository": { - "type": "git", - "url": "https://github.com/storybookjs/storybook.git", - "directory": "addons/contexts" - }, - "license": "MIT", - "author": "Leo Y. Li", - "main": "dist/register.js", - "files": [ - "dist/**/*", - "README.md", - "*.js", - "*.d.ts", - "ts3.5/**/*" - ], - "scripts": { - "dev:check-types": "tsc --noEmit", - "prepare": "node ../../scripts/prepare.js" - }, - "dependencies": { - "@storybook/addons": "6.0.0-alpha.42", - "@storybook/api": "6.0.0-alpha.42", - "@storybook/components": "6.0.0-alpha.42", - "@storybook/core-events": "6.0.0-alpha.42", - "core-js": "^3.0.1", - "global": "^4.3.2", - "qs": "^6.6.0", - "regenerator-runtime": "^0.13.3" - }, - "devDependencies": { - "@types/enzyme": "^3.10.5", - "enzyme": "^3.11.0" - }, - "peerDependencies": { - "preact": "*", - "qs": "*", - "rax": "*", - "react": "*", - "react-dom": "*", - "vue": "*" - }, - "peerDependenciesMeta": { - "preact": { - "optional": true - }, - "rax": { - "optional": true - }, - "vue": { - "optional": true - } - }, - "publishConfig": { - "access": "public" - }, - "gitHead": "4b9d901add9452525135caae98ae5f78dd8da9ff", - "typesVersions": { - "<=3.5": { - "*": [ - "ts3.5/*" - ] - } - } -} diff --git a/lib/components/src/blocks/DocsPageExampleCaption.md b/lib/components/src/blocks/DocsPageExampleCaption.md index 450b0115c4a2..beb4dee3ca25 100644 --- a/lib/components/src/blocks/DocsPageExampleCaption.md +++ b/lib/components/src/blocks/DocsPageExampleCaption.md @@ -82,7 +82,6 @@ Let's throw in a crazy table, because why not? | [actions](addons/actions) | + | + | + | + | + | + | + | + | + | + | + | | [backgrounds](addons/backgrounds) | + | \* | + | + | + | + | + | + | + | + | + | | [centered](addons/centered) | + | | + | + | + | + | | + | | + | + | -| [contexts](addons/contexts) | + | | + | | | | | | | | + | ## Code diff --git a/lib/components/src/blocks/DocsPageExampleCaption.mdx b/lib/components/src/blocks/DocsPageExampleCaption.mdx index 450b0115c4a2..beb4dee3ca25 100644 --- a/lib/components/src/blocks/DocsPageExampleCaption.mdx +++ b/lib/components/src/blocks/DocsPageExampleCaption.mdx @@ -82,7 +82,6 @@ Let's throw in a crazy table, because why not? | [actions](addons/actions) | + | + | + | + | + | + | + | + | + | + | + | | [backgrounds](addons/backgrounds) | + | \* | + | + | + | + | + | + | + | + | + | | [centered](addons/centered) | + | | + | + | + | + | | + | | + | + | -| [contexts](addons/contexts) | + | | + | | | | | | | | + | ## Code From 4eeaccd92954864af19ffd94c81d49cab437807c Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Thu, 23 Apr 2020 16:07:46 +0200 Subject: [PATCH 6/8] FIX --- cypress/integration/navigation.spec.ts | 5 +---- cypress/support/commands.js | 9 +++------ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/cypress/integration/navigation.spec.ts b/cypress/integration/navigation.spec.ts index f1a31b79e2ce..0981fce32388 100644 --- a/cypress/integration/navigation.spec.ts +++ b/cypress/integration/navigation.spec.ts @@ -22,13 +22,10 @@ describe('Navigation', () => { }); describe('Routing', () => { - before(() => { + it('should navigate to story addons-a11y-basebutton--default', () => { visitExample('official-storybook'); - }); - it('should navigate to story addons-a11y-basebutton--default', () => { cy.get('#addons-a11y-basebutton--label').click(); - cy.url().should('include', 'path=/story/addons-a11y-basebutton--label'); }); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 676486b34ff9..1d52c113fdf1 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -38,18 +38,15 @@ Cypress.Commands.add( ); Cypress.Commands.add('preview', {}, () => { - return cy.get(`#storybook-preview-iframe`).then({ timeout: 10000 }, (iframe) => { + return cy.get(`#storybook-preview-iframe`).then({ timeout: 20000 }, (iframe) => { const content = iframe[0].contentDocument; const element = content !== null ? content.documentElement : null; return cy - .wrap(iframe) + .get(iframe, { timeout: 20000 }) .should(() => { expect(element).not.null; - - if (element !== null) { - expect(element.querySelector('#root > *')).not.null; - } + expect(element.querySelector('#root')).not.null; }) .then(() => { return element.querySelector('#root'); From 3d03f3a74467b0e768d434f23710a80f0e6c64a8 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Thu, 23 Apr 2020 17:44:33 +0200 Subject: [PATCH 7/8] rerun CI --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3497808f0c07..bdf05a87d6ee 100644 --- a/README.md +++ b/README.md @@ -301,3 +301,4 @@ Support us with a monthly donation and help us continue our activities. \[[Becom [MIT](https://github.com/storybookjs/storybook/blob/master/LICENSE) -the end- + From c133b33621bdfec1732eaec01e00d37f4ffbc387 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Thu, 23 Apr 2020 17:44:53 +0200 Subject: [PATCH 8/8] - --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index bdf05a87d6ee..3497808f0c07 100644 --- a/README.md +++ b/README.md @@ -301,4 +301,3 @@ Support us with a monthly donation and help us continue our activities. \[[Becom [MIT](https://github.com/storybookjs/storybook/blob/master/LICENSE) -the end- -