From f0512489881d09477e16437b79a51074ad41bfc8 Mon Sep 17 00:00:00 2001 From: atanasster Date: Thu, 23 Apr 2020 01:25:06 -0400 Subject: [PATCH] feat: added props table panel --- README.md | 7 +- core/store/README.md | 37 ++++----- core/store/src/index.ts | 67 +++++++--------- core/store/src/serialization/StoreStorage.ts | 78 +++++++++++++++++++ core/store/src/types.ts | 3 +- core/webpack-rules/README.md | 22 +++++- examples/storybook-6/.storybook/main.js | 8 +- integrations/storybook/README.md | 52 ++++++++----- integrations/storybook/rollup.config.js | 3 +- .../storybook/src/panel/AddonPanel.tsx | 34 ++++++++ .../storybook/src/panel/ControlsPanel.tsx | 35 ++------- .../storybook/src/panel/PropsTablePanel.tsx | 9 +++ integrations/storybook/src/panel/constants.ts | 4 +- integrations/storybook/src/preset.ts | 16 ++-- ...-panel.tsx => register-controls-panel.tsx} | 6 +- .../storybook/src/register-props-panel.tsx | 14 ++++ integrations/storybook/src/types.ts | 11 ++- misc/storybook-custom-docs/src/index.tsx | 8 +- .../src/context/block/BlockDataContext.tsx | 2 +- .../context/components/ComponentsContext.tsx | 3 +- 20 files changed, 286 insertions(+), 133 deletions(-) create mode 100644 core/store/src/serialization/StoreStorage.ts create mode 100644 integrations/storybook/src/panel/AddonPanel.tsx create mode 100644 integrations/storybook/src/panel/PropsTablePanel.tsx rename integrations/storybook/src/{register-panel.tsx => register-controls-panel.tsx} (67%) create mode 100644 integrations/storybook/src/register-props-panel.tsx diff --git a/README.md b/README.md index 72f5e1996..72b64eb53 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ - [Roadmap](#roadmap) - [Integrations](#integrations) - [@component-controls/storybook](#component-controlsstorybook) + - [storybook integration of component-controls.](#storybook-integration-of-component-controls) - [Motivation](#motivation-1) - [Limitations](#limitations) - [Core packages](#core-packages) @@ -74,7 +75,11 @@ There are many developments that have contributed to the creation of `component- Storybook Addon For live editing of component controls -The Storybook]() integration of component-controls. +### [storybook](https://storybook.js.org) integration of component-controls. + +

+ introduction to using component-controls +

### Motivation diff --git a/core/store/README.md b/core/store/README.md index 1e8729b04..b81d3a137 100644 --- a/core/store/README.md +++ b/core/store/README.md @@ -31,25 +31,25 @@ $ npm install @component-controls/store --save-dev Store class used to query the stories and exchange information between processes -_defined in [@component-controls/store/src/index.ts](https://github.com/ccontrols/component-controls/tree/master/core/store/src/index.ts#L29)_ +_defined in [@component-controls/store/src/index.ts](https://github.com/ccontrols/component-controls/tree/master/core/store/src/index.ts#L33)_ ### properties -| Name | Type | Description | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | -| `constructor*` | **function** constructor | create a store with options | -| `addObserver*` | **function** addObserver(`observer`\*: [StoreObserver](#storeobserver)): number; | add observer callback function | -| `getStore*` | **function** getStore(): [StoriesStore](#storiesstore); | returns an instance of the store | -| `getStory*` | **function** getStory(`storyId`\*: string): [Story](#story); | given a story id return a story from the store | -| `removeObserver*` | **function** removeObserver(`observer`\*: [StoreObserver](#storeobserver)): **function** (`storyId`: string): void;\[]; | remove installed observer callback function | -| `setStore*` | **function** setStore(`store`: [StoriesStore](#storiesstore)): void; | internal set store, use for testing with mockup store. | -| `updateStoryProp*` | **function** updateStoryProp(`storyId`\*: string, `propName`\*: string, `newVal`\*: any): [StoriesStore](#storiesstore) \| undefined; | modify story properties, for example controls values. will notify all installed store observers of the changed story. | +| Name | Type | Description | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | +| `constructor*` | **function** constructor | create a store with options | +| `addObserver*` | **function** addObserver(`observer`\*: [StoreObserver](#storeobserver)): number; | add observer callback function | +| `getStore*` | **function** getStore(): [StoriesStore](#storiesstore); | returns an instance of the store | +| `getStory*` | **function** getStory(`storyId`\*: string): [Story](#story); | given a story id return a story from the store | +| `removeObserver*` | **function** removeObserver(`observer`\*: [StoreObserver](#storeobserver)): **function** (`storyId`: string, `propName`: string): void;\[]; | remove installed observer callback function | +| `setStore*` | **function** setStore(`store`: [StoriesStore](#storiesstore)): void; | internal set store, use for testing with mockup store. | +| `updateStoryProp*` | **function** updateStoryProp(`storyId`\*: string, `propName`\*: string, `newValue`\*: any): [StoriesStore](#storiesstore) \| undefined; | modify story properties, for example controls values. will notify all installed store observers of the changed story. | ## StoreOptions -_defined in [@component-controls/store/src/index.ts](https://github.com/ccontrols/component-controls/tree/master/core/store/src/index.ts#L16)_ +_defined in [@component-controls/store/src/index.ts](https://github.com/ccontrols/component-controls/tree/master/core/store/src/index.ts#L20)_ @@ -64,13 +64,13 @@ _defined in [@component-controls/store/src/index.ts](https://github.com/ccontrol store variable, automatically filled with stories. -_defined in [@component-controls/store/src/index.ts](https://github.com/ccontrols/component-controls/tree/master/core/store/src/index.ts#L148)_ +_defined in [@component-controls/store/src/index.ts](https://github.com/ccontrols/component-controls/tree/master/core/store/src/index.ts#L142)_ ## stores -_defined in [@component-controls/store/src/index.ts](https://github.com/ccontrols/component-controls/tree/master/core/store/src/index.ts#L150)_ +_defined in [@component-controls/store/src/index.ts](https://github.com/ccontrols/component-controls/tree/master/core/store/src/index.ts#L144)_ @@ -82,13 +82,14 @@ so they can re-load the stories _defined in [@component-controls/store/src/types.ts](https://github.com/ccontrols/component-controls/tree/master/core/store/src/types.ts#L8)_ -**function** (`storyId`: string): void; +**function** (`storyId`: string, `propName`: string): void; ### parameters -| Name | Type | Description | -| --------- | ------ | ----------- | -| `storyId` | string | | -| `returns` | void | | +| Name | Type | Description | +| ---------- | ------ | ----------- | +| `storyId` | string | | +| `propName` | string | | +| `returns` | void | | diff --git a/core/store/src/index.ts b/core/store/src/index.ts index ad637e12c..1a3c9aaec 100644 --- a/core/store/src/index.ts +++ b/core/store/src/index.ts @@ -8,8 +8,12 @@ import { StoryStore, MessageType, UPDATE_STORY_MSG, - COMPONENT_CONTROLS_STORAGE, } from './types'; +import { + saveStore, + readStore, + updateStory, +} from './serialization/StoreStorage'; export { StoreObserver, StoryStore }; @@ -45,11 +49,11 @@ export class Store implements StoryStore { type: 'localstorage', }); this.observers = []; - this.channel.onmessage = ({ storyId, moduleId }: MessageType) => { + this.channel.onmessage = ({ storyId, moduleId, propName }: MessageType) => { if (storyId && moduleId) { if (this.moduleId !== moduleId) { - this.readData(storyId); - this.notifyObservers(storyId); + this.readData(storyId, propName); + this.notifyObservers(storyId, propName); } } }; @@ -65,9 +69,9 @@ export class Store implements StoryStore { removeObserver = (observer: StoreObserver) => (this.observers = this.observers.filter(o => o !== observer)); - private notifyObservers = (storyId?: string) => { + private notifyObservers = (storyId?: string, propName?: string) => { if (this.observers.length > 0) { - this.observers.forEach(observer => observer(storyId)); + this.observers.forEach(observer => observer(storyId, propName)); } }; @@ -78,23 +82,10 @@ export class Store implements StoryStore { this.loadedStore = store; this.notifyObservers(); }; - private readData = (storyId?: string) => { - const data = localStorage.getItem(COMPONENT_CONTROLS_STORAGE); - if (data) { - try { - const newStore = JSON.parse(data) as StoriesStore; - - if (this.loadedStore && storyId) { - this.loadedStore.stories[storyId] = { - ...this.loadedStore.stories[storyId], - controls: { ...newStore.stories[storyId].controls }, - }; - } else { - this.loadedStore = newStore; - } - } catch (e) {} - } + private readData = (storyId?: string, propName?: string) => { + this.loadedStore = readStore(this.loadedStore, storyId, propName); }; + /** * returns an instance of the store */ @@ -122,22 +113,25 @@ export class Store implements StoryStore { updateStoryProp = ( storyId: string, propName: string, - newVal: any, + newValue: any, ): StoriesStore | undefined => { + this.loadedStore = updateStory( + this.loadedStore, + storyId, + propName, + newValue, + this.updateLocalStorage, + ); if (this.loadedStore) { - this.loadedStore.stories[storyId] = { - ...this.loadedStore.stories[storyId], - [propName]: newVal, - }; if (this.updateLocalStorage) { - localStorage.setItem( - COMPONENT_CONTROLS_STORAGE, - JSON.stringify(this.loadedStore), - ); - const message: MessageType = { storyId, moduleId: this.moduleId }; + const message: MessageType = { + storyId, + moduleId: this.moduleId, + propName, + }; this.channel.postMessage(message); } - this.notifyObservers(storyId); + this.notifyObservers(storyId, propName); } return this.loadedStore; }; @@ -150,10 +144,5 @@ export const store = new Store(); const stores = loadStoryStore(); if (stores) { store.setStore(stores); - for (var key in localStorage) { - if (key.indexOf(COMPONENT_CONTROLS_STORAGE) == 0) { - localStorage.removeItem(key); - } - } - localStorage.setItem(COMPONENT_CONTROLS_STORAGE, JSON.stringify(stores)); + saveStore(stores); } diff --git a/core/store/src/serialization/StoreStorage.ts b/core/store/src/serialization/StoreStorage.ts new file mode 100644 index 000000000..a2b85d894 --- /dev/null +++ b/core/store/src/serialization/StoreStorage.ts @@ -0,0 +1,78 @@ +import { + StoriesStore, + getComponentName, +} from '@component-controls/specification'; + +import { COMPONENT_CONTROLS_STORAGE } from '../types'; + +const encodeFn = (name: string, val: any) => { + // convert RegExp to string + if (val && val.constructor === RegExp) { + return val.toString(); + } else if (name === 'component') { + //serialize components as string of their name + const component = getComponentName(val); + return component ? component : val; + } + return val; +}; +export const saveStore = (store: StoriesStore) => { + for (var key in localStorage) { + if (key.indexOf(COMPONENT_CONTROLS_STORAGE) == 0) { + localStorage.removeItem(key); + } + } + localStorage.setItem( + COMPONENT_CONTROLS_STORAGE, + JSON.stringify(store, encodeFn), + ); +}; + +export const readStore = ( + store?: StoriesStore, + storyId?: string, + propName?: string, +): StoriesStore | undefined => { + const data = localStorage.getItem(COMPONENT_CONTROLS_STORAGE); + if (data) { + const newStore = JSON.parse(data) as StoriesStore; + if (store && storyId && propName) { + const newValue = (newStore.stories[storyId] as any)[propName]; + store.stories = { + ...store.stories, + [storyId]: { + ...store.stories[storyId], + [propName]: newValue, + }, + }; + return store; + } + return newStore; + } + return store; +}; + +export const updateStory = ( + store: StoriesStore | undefined, + storyId: string, + propName: string, + newValue: any, + updateLocalStorage?: boolean, +): StoriesStore | undefined => { + if (store) { + store.stories = { + ...store.stories, + [storyId]: { + ...store.stories[storyId], + [propName]: newValue, + }, + }; + if (updateLocalStorage) { + localStorage.setItem( + COMPONENT_CONTROLS_STORAGE, + JSON.stringify(store, encodeFn), + ); + } + } + return store; +}; diff --git a/core/store/src/types.ts b/core/store/src/types.ts index e8a4ca18d..b2c635b9f 100644 --- a/core/store/src/types.ts +++ b/core/store/src/types.ts @@ -5,7 +5,7 @@ import { StoriesStore, Story } from '@component-controls/specification'; * when updateStoryProp is called on the store, the store observers will be notified * so they can re-load the stories */ -export type StoreObserver = (storyId?: string) => void; +export type StoreObserver = (storyId?: string, propName?: string) => void; export interface StoryStore { getStore: () => StoriesStore | undefined; @@ -25,4 +25,5 @@ export const COMPONENT_CONTROLS_STORAGE = 'component-controls-store-data'; export interface MessageType { storyId: string; moduleId: number; + propName: string; } diff --git a/core/webpack-rules/README.md b/core/webpack-rules/README.md index 22efed325..a0b01ce9b 100644 --- a/core/webpack-rules/README.md +++ b/core/webpack-rules/README.md @@ -8,6 +8,7 @@ - [getRules](#getrules) - [ruleMerge](#rulemerge) - [rulesFactory](#rulesfactory) + - [RuleOptions](#ruleoptions) - [RuleType](#ruletype) - [RuleTypes](#ruletypes) - [WebpackRule](#webpackrule) @@ -55,7 +56,7 @@ _defined in [@component-controls/webpack-rules/src/index.ts](https://github.com/ expands the rules into webpack rules -_defined in [@component-controls/webpack-rules/src/index.ts](https://github.com/ccontrols/component-controls/tree/master/core/webpack-rules/src/index.ts#L31)_ +_defined in [@component-controls/webpack-rules/src/index.ts](https://github.com/ccontrols/component-controls/tree/master/core/webpack-rules/src/index.ts#L33)_ **function** getRules(`rules`\*: [RuleType](#ruletype)\[]): [WebpackRules](#webpackrules); @@ -94,15 +95,28 @@ _defined in [@component-controls/webpack-rules/src/index.ts](https://github.com/ | `react-docgen*` | [RuleSetRule](#rulesetrule)\[] | | | `react-docgen-typescript*` | [RuleSetRule](#rulesetrule)\[] | | -## RuleType +## RuleOptions _defined in [@component-controls/webpack-rules/src/types.ts](https://github.com/ccontrols/component-controls/tree/master/core/webpack-rules/src/types.ts#L6)_ -[WebpackRule](#webpackrule) | string | **options**: [WebpackRules](#webpackrules)**rules**: [RuleTypes](#ruletypes) + + +### properties + +| Name | Type | Description | +| -------- | ----------------------------- | ----------- | +| `name*` | string | | +| `rules*` | [WebpackRules](#webpackrules) | | + +## RuleType + +_defined in [@component-controls/webpack-rules/src/types.ts](https://github.com/ccontrols/component-controls/tree/master/core/webpack-rules/src/types.ts#L10)_ + +[WebpackRule](#webpackrule) | string | [RuleOptions](#ruleoptions) ## RuleTypes -_defined in [@component-controls/webpack-rules/src/types.ts](https://github.com/ccontrols/component-controls/tree/master/core/webpack-rules/src/types.ts#L14)_ +_defined in [@component-controls/webpack-rules/src/types.ts](https://github.com/ccontrols/component-controls/tree/master/core/webpack-rules/src/types.ts#L12)_ [RuleType](#ruletype)\[] diff --git a/examples/storybook-6/.storybook/main.js b/examples/storybook-6/.storybook/main.js index 9c965a526..2611f81f7 100644 --- a/examples/storybook-6/.storybook/main.js +++ b/examples/storybook-6/.storybook/main.js @@ -24,6 +24,12 @@ module.exports = { configureJSX: true, }, }, - '@component-controls/storybook', + { + name: '@component-controls/storybook', + options: { + controlsPanel: true, + propsPanel: true, + } + } ], }; diff --git a/integrations/storybook/README.md b/integrations/storybook/README.md index a98a6f998..4d4d5f50d 100644 --- a/integrations/storybook/README.md +++ b/integrations/storybook/README.md @@ -1,10 +1,9 @@ # Table of contents - [Overview](#overview) - - - [Motivation](#motivation) + - - [storybook integration of component-controls.](#storybook-integration-of-component-controls) + - [Motivation](#motivation) - [Limitations](#limitations) -- [Storybook](#storybook) -- [Introduction](#introduction) - [Getting Started](#getting-started) - [Install](#install) - [Configure](#configure) @@ -19,10 +18,11 @@ - [Smart Controls Options](#smart-controls-options) - [Testing with random data generators](#testing-with-random-data-generators) - [Categories](#categories) -- [Storybook Docs Block](#storybook-docs-block) -- [Configuration options](#configuration-options) +- [Advanced configuration options](#advanced-configuration-options) + - [Custom loader options](#custom-loader-options) - [PresetOptions](#presetoptions) - [defaultRules](#defaultrules) + - [Storybook addon panels](#storybook-addon-panels) - [List of components](#list-of-components) - [ComponentSource](#inscomponentsourceins) - [ControlsTable](#inscontrolstableins) @@ -41,14 +41,12 @@ # Overview -### [storybook]() integration of component-controls. +### [storybook](https://storybook.js.org) integration of component-controls.

introduction to using component-controls

- - ### Motivation - Allow adding component-controls in storybook DocsPage. @@ -65,7 +63,6 @@ - Only handles the CSF and MDX stories format. The storeisOf API is not supported and there are currently no plans to support it. - The Storybook MDX is a proprietary format that will be replaced in due time with a portable [frontmatter](https://www.gatsbyjs.org/docs/mdx/markdown-syntax/#frontmatter--mdx-example) stories format, similar to the CSF format. - # Getting Started ## Install @@ -273,7 +270,6 @@ You can see Controls in separate tabs as shown below. control groups

- # Advanced configuration options The storybook addon controls comes with pre-configured options that you can use for quick start, but you can also customise the options. @@ -281,6 +277,7 @@ The storybook addon controls comes with pre-configured options that you can use ## Custom loader options `.storybook/main.js`: + ```js addons: [ ... @@ -288,7 +285,7 @@ The storybook addon controls comes with pre-configured options that you can use name: '@component-controls/storybook', options: { - addonPanel: false, + controlsPanel: false, webpackRules: [{ name: 'react-docgen-typescript', rules: [{ @@ -319,6 +316,7 @@ The storybook addon controls comes with pre-configured options that you can use }, }], ``` + For more information on [InstrumentOptions](../../core/instrument/README.md#instrumentoptions) @@ -333,21 +331,39 @@ _defined in [@component-controls/storybook/src/types.ts](https://github.com/ccon ### properties -| Name | Type | Description | -| -------------- | ----------------------- | ------------------------------------------------------------------------------- | -| `addonPanel` | boolean | whether to display the addon panel in storybook | -| `docsPage` | boolean | whether to add a Page documentation page with a classic componnet-controls page | -| `pages` | string\[] | additional custom documentation pages | -| `webpackRules` | [RuleTypes](#ruletypes) | options that will be passed to the instrumenter. | +| Name | Type | Description | +| --------------- | ----------------------- | -------------------------------------------------------------------- | +| `controlsPanel` | boolean | whether to display the controls table as an addon panel in storybook | +| `pages` | string\[] | additional custom documentation pages | +| `propsPanel` | boolean | whether to display the props table as an addon panel in storybook | +| `webpackRules` | [RuleTypes](#ruletypes) | options that will be passed to the instrumenter. | ## defaultRules -_defined in [@component-controls/storybook/src/types.ts](https://github.com/ccontrols/component-controls/tree/master/integrations/storybook/src/types.ts#L22)_ +_defined in [@component-controls/storybook/src/types.ts](https://github.com/ccontrols/component-controls/tree/master/integrations/storybook/src/types.ts#L25)_ +## Storybook addon panels + +The `component-controls` block components ahev been designed from the ground up to be able to be placed either on documentation pages or in addon tabs. + +You can turn on and off various panels: + + { + name: '@component-controls/storybook', + options: { + controlsPanel: true, + propsPanel: true, + } + } + +

+ control groups +

+ # List of components diff --git a/integrations/storybook/rollup.config.js b/integrations/storybook/rollup.config.js index e00778928..fe12c0d9a 100644 --- a/integrations/storybook/rollup.config.js +++ b/integrations/storybook/rollup.config.js @@ -5,7 +5,8 @@ export default config({ './src/index.ts', './src/preset.ts', './src/config.tsx', - './src/register-panel.tsx', + './src/register-controls-panel.tsx', + './src/register-props-panel.tsx', './src//docs-page/full-page.tsx', ], }); diff --git a/integrations/storybook/src/panel/AddonPanel.tsx b/integrations/storybook/src/panel/AddonPanel.tsx new file mode 100644 index 000000000..da7884fb0 --- /dev/null +++ b/integrations/storybook/src/panel/AddonPanel.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { BlockContextProvider } from '@component-controls/blocks'; +import { API } from '@storybook/api'; +import { SET_CURRENT_STORY } from '@storybook/core-events'; +import { ThemeProvider } from '../context/ThemeProvider'; + +export interface AddonPanelProps { + active?: boolean; + api: API; +} +export const AddonPanel: React.FC = ({ + active, + api, + children, +}) => { + const [storyId, setStoryId] = React.useState(); + + const channel = React.useMemo(() => api.getChannel(), []); + React.useEffect(() => { + const onChangeStory = (props: any) => { + setStoryId(props.storyId); + }; + const { id } = api.getCurrentStoryData() || {}; + setStoryId(id); + channel.on(SET_CURRENT_STORY, onChangeStory); + return () => channel.off(SET_CURRENT_STORY, onChangeStory); + }); + + return active && storyId ? ( + + {children} + + ) : null; +}; diff --git a/integrations/storybook/src/panel/ControlsPanel.tsx b/integrations/storybook/src/panel/ControlsPanel.tsx index e03c65c42..06204a69c 100644 --- a/integrations/storybook/src/panel/ControlsPanel.tsx +++ b/integrations/storybook/src/panel/ControlsPanel.tsx @@ -1,30 +1,9 @@ import React from 'react'; -import { BlockContextProvider } from '@component-controls/blocks'; -import { API } from '@storybook/api'; -import { SET_CURRENT_STORY } from '@storybook/core-events'; -import { ControlsTable } from '../blocks/ControlsTable'; +import { ControlsTable } from '@component-controls/blocks'; +import { AddonPanel, AddonPanelProps } from './AddonPanel'; -export interface ControlsPanelProps { - active?: boolean; - api: API; -} -export const ControlsPanel: React.FC = ({ - active, - api, -}) => { - const [storyId, setStoryId] = React.useState(); - const channel = React.useMemo(() => api.getChannel(), []); - React.useEffect(() => { - const onChangeStory = (props: any) => { - setStoryId(props.storyId); - }; - channel.on(SET_CURRENT_STORY, onChangeStory); - return () => channel.off(SET_CURRENT_STORY, onChangeStory); - }); - - return active && storyId ? ( - - - - ) : null; -}; +export const ControlsPanel: React.FC = props => ( + + + +); diff --git a/integrations/storybook/src/panel/PropsTablePanel.tsx b/integrations/storybook/src/panel/PropsTablePanel.tsx new file mode 100644 index 000000000..0b2218efe --- /dev/null +++ b/integrations/storybook/src/panel/PropsTablePanel.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { PropsTable } from '@component-controls/blocks'; +import { AddonPanel, AddonPanelProps } from './AddonPanel'; + +export const PropsTablePanel: React.FC = props => ( + + + +); diff --git a/integrations/storybook/src/panel/constants.ts b/integrations/storybook/src/panel/constants.ts index 0598aaf70..578462eab 100644 --- a/integrations/storybook/src/panel/constants.ts +++ b/integrations/storybook/src/panel/constants.ts @@ -1,2 +1,2 @@ -export const ADDON_ID = 'controls/panel'; -export const PANEL_ID = 'controls/panel/panel'; +export const CONTROLS_PANEL_ID = 'controls/panels/controls'; +export const PROPS_PANEL_ID = 'controls/panels/props'; diff --git a/integrations/storybook/src/preset.ts b/integrations/storybook/src/preset.ts index 28eb8e69d..aeddf17e8 100644 --- a/integrations/storybook/src/preset.ts +++ b/integrations/storybook/src/preset.ts @@ -10,10 +10,8 @@ module.exports = { return result; }, addons: (entry: any = {}) => { - const { pages: customPages = [], docsPage = true } = entry; - const pages = docsPage - ? [require.resolve('./full-page'), ...customPages] - : customPages; + const { pages: customPages } = entry; + const pages = customPages || [require.resolve('./full-page')]; if (pages.length) { return [ { @@ -28,10 +26,14 @@ module.exports = { }, managerEntries: (entry: any[] = [], options: PresetOptions = {}) => { const result = [...entry]; - const { addonPanel = true } = options; - if (addonPanel) { - result.push(require.resolve('./register-panel')); + const { controlsPanel = true, propsPanel = false } = options; + if (controlsPanel) { + result.push(require.resolve('./register-controls-panel')); } + if (propsPanel) { + result.push(require.resolve('./register-props-panel')); + } + return result; }, webpackFinal: (config: any = {}, options: PresetOptions = {}) => { diff --git a/integrations/storybook/src/register-panel.tsx b/integrations/storybook/src/register-controls-panel.tsx similarity index 67% rename from integrations/storybook/src/register-panel.tsx rename to integrations/storybook/src/register-controls-panel.tsx index 7c85b73dc..22df42af9 100644 --- a/integrations/storybook/src/register-panel.tsx +++ b/integrations/storybook/src/register-controls-panel.tsx @@ -2,10 +2,10 @@ import * as React from 'react'; import addons from '@storybook/addons'; import { ControlsPanel } from './panel/ControlsPanel'; -import { ADDON_ID, PANEL_ID } from './panel/constants'; +import { CONTROLS_PANEL_ID } from './panel/constants'; -addons.register(ADDON_ID, api => { - addons.addPanel(PANEL_ID, { +addons.register(CONTROLS_PANEL_ID, api => { + addons.addPanel(`${CONTROLS_PANEL_ID}_panel`, { title: 'Controls', render: ({ active, key }) => ( diff --git a/integrations/storybook/src/register-props-panel.tsx b/integrations/storybook/src/register-props-panel.tsx new file mode 100644 index 000000000..c34fbe13d --- /dev/null +++ b/integrations/storybook/src/register-props-panel.tsx @@ -0,0 +1,14 @@ +/* eslint-disable react/display-name */ +import * as React from 'react'; +import addons from '@storybook/addons'; +import { PropsTablePanel } from './panel/PropsTablePanel'; +import { PROPS_PANEL_ID } from './panel/constants'; + +addons.register(PROPS_PANEL_ID, api => { + addons.addPanel(`${PROPS_PANEL_ID}_panel`, { + title: 'Props', + render: ({ active, key }) => ( + + ), + }); +}); diff --git a/integrations/storybook/src/types.ts b/integrations/storybook/src/types.ts index a525eaa15..c947e1b57 100644 --- a/integrations/storybook/src/types.ts +++ b/integrations/storybook/src/types.ts @@ -2,13 +2,16 @@ import { RuleTypes } from '@component-controls/webpack-rules'; export interface PresetOptions { /** - * whether to display the addon panel in storybook + * whether to display the controls table as an addon panel in storybook + * @defaultValue true */ - addonPanel?: boolean; + controlsPanel?: boolean; + /** - * whether to add a Page documentation page with a classic componnet-controls page + * whether to display the props table as an addon panel in storybook */ - docsPage?: boolean; + propsPanel?: boolean; + /** * additional custom documentation pages */ diff --git a/misc/storybook-custom-docs/src/index.tsx b/misc/storybook-custom-docs/src/index.tsx index 8faccb354..2cef749d8 100644 --- a/misc/storybook-custom-docs/src/index.tsx +++ b/misc/storybook-custom-docs/src/index.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import addons from '@storybook/addons'; -import { STORY_CHANGED } from '@storybook/core-events'; +import { SET_CURRENT_STORY } from '@storybook/core-events'; import { ConfigApi } from '@storybook/client-api'; export * from './types'; @@ -60,13 +60,13 @@ export const useStoryId = () => { ); const channel = React.useMemo(() => addons.getChannel(), []); React.useEffect(() => { - const onStoryChange = (id: string) => { + const onStoryChange = ({ storyId: id }: { storyId: string }) => { setStoryId(id); }; - channel.on(STORY_CHANGED, onStoryChange); + channel.on(SET_CURRENT_STORY, onStoryChange); return () => { - channel.off(STORY_CHANGED, onStoryChange); + channel.off(SET_CURRENT_STORY, onStoryChange); }; }, []); return storyId; diff --git a/ui/blocks/src/context/block/BlockDataContext.tsx b/ui/blocks/src/context/block/BlockDataContext.tsx index 6d8ace814..f8f1407db 100644 --- a/ui/blocks/src/context/block/BlockDataContext.tsx +++ b/ui/blocks/src/context/block/BlockDataContext.tsx @@ -54,8 +54,8 @@ export const BlockDataContextProvider: React.FC = ({ store && story && story.kind ? store.kinds[story.kind] : undefined; const storyComponent: any = story && kind ? story.component || kind.component : undefined; - const componentName = getComponentName(storyComponent); + const componentName = getComponentName(storyComponent); const component = store && componentName && kind && kind.components[componentName] ? store.components[kind.components[componentName]] diff --git a/ui/blocks/src/context/components/ComponentsContext.tsx b/ui/blocks/src/context/components/ComponentsContext.tsx index 595ab31f5..0a6fe0a6d 100644 --- a/ui/blocks/src/context/components/ComponentsContext.tsx +++ b/ui/blocks/src/context/components/ComponentsContext.tsx @@ -15,7 +15,7 @@ export interface ComponentInputProps { * The default, a value of `"."` will indicate to display information for the current component (associated with the current Story). * If an array of components is specified, each component will be displayed in a separate tab. */ - of?: '.' | any; + of?: typeof CURRENT_STORY | any; } export interface ComponentContextProps { @@ -29,6 +29,7 @@ export const useComponentsContext = ({ }: ComponentInputProps): ComponentContextProps => { const { getStoryData, getComponents } = React.useContext(BlockDataContext); const { story, kind, component } = getStoryData(); + if (!story) { return { components: {},