From f825f53aa78a7e531eaca007806145eccfc9b557 Mon Sep 17 00:00:00 2001 From: atanasster Date: Tue, 21 Apr 2020 17:42:25 -0400 Subject: [PATCH] feat: replace broadcast channel with storybook channel --- docs/iframe.html | 2 +- docs/index.html | 2 +- .../.storybook/main.js | 55 ------------- .../.storybook/page-component-blocks.tsx | 8 +- .../.storybook/page-docs-blocks.tsx | 2 +- .../.storybook/page-mixed-blocks.tsx | 8 +- .../.storybook/page-simple.tsx | 8 +- .../storybook/src/context/BlockContext.tsx | 2 +- .../storybook/src/docs-page/DocsContainer.tsx | 13 +--- .../storybook/src/docs-page/full-page.tsx | 4 +- misc/storybook-custom-docs/package.json | 1 - .../src/components/ManagerContainer.tsx | 78 ++++++------------- misc/storybook-custom-docs/src/index.tsx | 22 +++++- .../src/manager-loader.ts | 8 +- .../src/preview-loader.ts | 39 +++++++--- misc/storybook-custom-docs/src/types.ts | 9 +-- 16 files changed, 102 insertions(+), 159 deletions(-) diff --git a/docs/iframe.html b/docs/iframe.html index 0a567401f..f70fdfa64 100644 --- a/docs/iframe.html +++ b/docs/iframe.html @@ -71,4 +71,4 @@ }

No Preview

Sorry, but you either have no stories or none are selected somehow.

  • Please check the Storybook config.
  • Try reloading the page.

If the problem persists, check the browser console, or the terminal you've run Storybook from.

\ No newline at end of file + }

No Preview

Sorry, but you either have no stories or none are selected somehow.

  • Please check the Storybook config.
  • Try reloading the page.

If the problem persists, check the browser console, or the terminal you've run Storybook from.

\ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 345b6bc48..d707b2a5d 100644 --- a/docs/index.html +++ b/docs/index.html @@ -16,4 +16,4 @@ }
\ No newline at end of file + }
\ No newline at end of file diff --git a/examples/storybook-custom-docs-pages/.storybook/main.js b/examples/storybook-custom-docs-pages/.storybook/main.js index 0041de367..6d390c64a 100644 --- a/examples/storybook-custom-docs-pages/.storybook/main.js +++ b/examples/storybook-custom-docs-pages/.storybook/main.js @@ -23,68 +23,13 @@ module.exports = { configureJSX: true, }, }, - { - name: '@component-controls/storybook', - options: { - addonPanel: false, - docsPage: false, - }, - }, { name: '@component-controls/storybook-custom-docs', options: { pages: [ require.resolve('./page-simple.tsx'), - require.resolve('./page-docs-blocks.tsx'), - require.resolve('./page-component-blocks.tsx'), - require.resolve('./page-mixed-blocks.tsx') ] }, } ], - webpackFinal: async (config, { configType }) => { - return { - ...config, - module: { - ...config.module, - rules: [ - ...config.module.rules, - { - test: /\.(story|stories).(js|jsx|ts|tsx|mdx)$/, - loader: "@component-controls/loader/loader", - exclude: [/node_modules/], - enforce: 'pre', - options: { - propsLoaders: [ - { name: '@component-controls/react-docgen-info', test: /\.(js|jsx)$/}, - { name: '@component-controls/react-docgen-typescript-info', test: /\.(ts|tsx)$/} - ], - prettier: { - tabWidth: 2, - }, - components: { - storeSourceFile: true, //false - resolveFile: (componentName, filePath) => { - if (filePath.includes('/theme-ui/dist')) { - return `${ - filePath.split('/theme-ui/dist')[0] - }/@theme-ui/components/src/${componentName}.js`; - } else if (filePath.includes('@component-controls/storybook/dist')) { - return path.resolve(path.dirname(filePath), `../src/blocks/${componentName}.tsx`) - } - return filePath; - }, - }, - stories: { - storeSourceFile: true, //false - }, - }, - }, - ], - }, - resolve: { - ...config.resolve, - extensions: [...(config.resolve.extensions || []), '.ts', '.tsx'], - }, - }}, }; diff --git a/examples/storybook-custom-docs-pages/.storybook/page-component-blocks.tsx b/examples/storybook-custom-docs-pages/.storybook/page-component-blocks.tsx index 1a211704e..9d37a2079 100644 --- a/examples/storybook-custom-docs-pages/.storybook/page-component-blocks.tsx +++ b/examples/storybook-custom-docs-pages/.storybook/page-component-blocks.tsx @@ -4,9 +4,9 @@ import { Story, Title, ControlsTable, Playground } from '@component-controls/bl import { CustomPageDef } from '@component-controls/storybook-custom-docs'; -const Page = ({ active, storyId }) => { +const Page = ({ active }) => { return ( - + Component controls blocks @@ -18,8 +18,8 @@ const Page = ({ active, storyId }) => { const page: CustomPageDef = { key: 'component-page', title: 'Controls blocks', - render: ({ active, storyId }) => { - return ; + render: ({ active }) => { + return ; } } diff --git a/examples/storybook-custom-docs-pages/.storybook/page-docs-blocks.tsx b/examples/storybook-custom-docs-pages/.storybook/page-docs-blocks.tsx index 14c6b0e95..74f8629de 100644 --- a/examples/storybook-custom-docs-pages/.storybook/page-docs-blocks.tsx +++ b/examples/storybook-custom-docs-pages/.storybook/page-docs-blocks.tsx @@ -14,7 +14,7 @@ const Page = () => { const page: CustomPageDef = { key: 'docs-page', title: 'Docs blocks', - render: ({ active, storyId }) => { + render: ({ active }) => { return active ? : null; } } diff --git a/examples/storybook-custom-docs-pages/.storybook/page-mixed-blocks.tsx b/examples/storybook-custom-docs-pages/.storybook/page-mixed-blocks.tsx index 63fb227c8..71a5c64b9 100644 --- a/examples/storybook-custom-docs-pages/.storybook/page-mixed-blocks.tsx +++ b/examples/storybook-custom-docs-pages/.storybook/page-mixed-blocks.tsx @@ -6,8 +6,8 @@ import { Story, Title, Playground, PropsTable } from '@component-controls/block import { CustomPageDef } from '@component-controls/storybook-custom-docs'; -const Page = ({ storyId: id }) => { - const storyId = useStoryId(id); +const Page = () => { + const storyId = useStoryId(); return ( <> @@ -30,8 +30,8 @@ const Page = ({ storyId: id }) => { const page: CustomPageDef = { key: 'mixed-page', title: 'Mixed blocks', - render: ({ active, storyId }) => { - return active ? : null; + render: ({ active }) => { + return active ? : null; } } diff --git a/examples/storybook-custom-docs-pages/.storybook/page-simple.tsx b/examples/storybook-custom-docs-pages/.storybook/page-simple.tsx index 6418e810c..95306a272 100644 --- a/examples/storybook-custom-docs-pages/.storybook/page-simple.tsx +++ b/examples/storybook-custom-docs-pages/.storybook/page-simple.tsx @@ -1,10 +1,14 @@ import React from 'react'; -import { CustomPageDef } from '@component-controls/storybook-custom-docs'; +import { CustomPageDef, useStoryId } from '@component-controls/storybook-custom-docs'; +const CustomPage: React.FC = () => { + const storyId = useStoryId(); + return

Simple docs page

{storyId}

+} const page: CustomPageDef = { key: 'custom', title: 'Simple Page', - render: ({ active, storyId }) => active ?

Simple docs page

{storyId}

: null, + render: ({ active }) => active ? : null, } export default page; diff --git a/integrations/storybook/src/context/BlockContext.tsx b/integrations/storybook/src/context/BlockContext.tsx index 0c9f12c42..d2c96629b 100644 --- a/integrations/storybook/src/context/BlockContext.tsx +++ b/integrations/storybook/src/context/BlockContext.tsx @@ -9,7 +9,7 @@ export const BlockContextProvider: React.FC = ({ children, id, }) => { - const defaultStoyId = useStoryId(id); + const defaultStoyId = useStoryId(); const storyId = id ? id : defaultStoyId; return ( {children} diff --git a/integrations/storybook/src/docs-page/DocsContainer.tsx b/integrations/storybook/src/docs-page/DocsContainer.tsx index 9c3f07109..82d149aa8 100644 --- a/integrations/storybook/src/docs-page/DocsContainer.tsx +++ b/integrations/storybook/src/docs-page/DocsContainer.tsx @@ -6,11 +6,8 @@ import { import { useStoryId } from '@component-controls/storybook-custom-docs'; import { useIsDark } from '../context/useIsDark'; -export const PageContextContainer: FC = ({ - children, - storyId: defaultStoryId, -}) => { - const storyId = useStoryId(defaultStoryId); +export const PageContextContainer: FC = ({ children }) => { + const storyId = useStoryId(); const isDark = useIsDark(); return ( @@ -22,8 +19,4 @@ export const PageContextContainer: FC = ({ export const DocsContainer: FC = ({ children, active = true, - storyId, -}) => - active ? ( - {children} - ) : null; +}) => (active ? {children} : null); diff --git a/integrations/storybook/src/docs-page/full-page.tsx b/integrations/storybook/src/docs-page/full-page.tsx index 47cebb48e..b890694ac 100644 --- a/integrations/storybook/src/docs-page/full-page.tsx +++ b/integrations/storybook/src/docs-page/full-page.tsx @@ -6,9 +6,9 @@ import { ControlsPage } from './ControlsPage'; export default { key: 'page', title: 'Page', - render: ({ active, storyId }: { active: boolean; storyId: string }) => { + render: ({ active }: { active: boolean }) => { return ( - + ); diff --git a/misc/storybook-custom-docs/package.json b/misc/storybook-custom-docs/package.json index 94cb4df69..a411083b4 100644 --- a/misc/storybook-custom-docs/package.json +++ b/misc/storybook-custom-docs/package.json @@ -34,7 +34,6 @@ }, "license": "MIT", "dependencies": { - "broadcast-channel": "^3.1.0", "react": "^16.12.0", "react-dom": "^16.12.0" }, diff --git a/misc/storybook-custom-docs/src/components/ManagerContainer.tsx b/misc/storybook-custom-docs/src/components/ManagerContainer.tsx index 3d6513733..e09f76d22 100644 --- a/misc/storybook-custom-docs/src/components/ManagerContainer.tsx +++ b/misc/storybook-custom-docs/src/components/ManagerContainer.tsx @@ -1,67 +1,39 @@ import React from 'react'; -import { BroadcastChannel } from 'broadcast-channel'; import { API } from '@storybook/api'; interface ManagerContainerProps { active?: boolean; - id: string; api: API; + route: string; title: string; } -const activePages: string[] = []; -export const ManagerContainer: React.FC = ({ - active, - id, - api, - title, -}) => { - const channel = React.useMemo( - () => - new BroadcastChannel(`attach_docs_page_${title}`, { - type: 'localstorage', - }), - [], - ); + +export const ManagerContainer: React.FC = props => { + const { active, api, route } = props; + const ATTACH_DOCS_PAGE = `attach_docs_page_${route}`; + const REQUEST_DOCS_PAGE = `request_docs_page_${route}`; + const channel = React.useMemo(() => api.getChannel(), []); + const sendMessage = () => { + const story = api.getCurrentStoryData(); + channel.emit(ATTACH_DOCS_PAGE, { active, storyId: story?.id }); + }; + React.useEffect(() => { - const iframe = document.getElementById( - 'storybook-preview-iframe', - ) as HTMLIFrameElement; const wrapper = document.getElementById('storybook-preview-wrapper'); - if (iframe && iframe.contentDocument) { - const updateDOM = () => { - const story = api.getCurrentStoryData(); - channel.postMessage({ id: id, active, storyId: story?.id }); - const root = iframe?.contentDocument?.getElementById('root'); - if (wrapper && root) { - const pageIndex = activePages.indexOf(id); - if (active) { - if (pageIndex < 0) { - activePages.push(id); - } - root.style.setProperty('display', 'none'); - wrapper.removeAttribute('hidden'); - } else if (pageIndex >= 0) { - activePages.splice(pageIndex, 1); - if (activePages.length === 0) { - root.style.removeProperty('display'); - } - } - } - }; - - if (!iframe.contentDocument.getElementById('root')) { - const saveOnLoad = iframe.onload; - iframe.onload = e => { - updateDOM(); - if (saveOnLoad) { - //@ts-ignore - saveOnLoad(e); - } - }; - } else { - updateDOM(); + const onRequestPage = () => { + sendMessage(); + }; + channel.on(REQUEST_DOCS_PAGE, onRequestPage); + const updateDOM = () => { + sendMessage(); + if (wrapper) { + wrapper.removeAttribute('hidden'); } - } + }; + updateDOM(); + return () => { + channel.off(REQUEST_DOCS_PAGE, onRequestPage); + }; }, [active]); return null; }; diff --git a/misc/storybook-custom-docs/src/index.tsx b/misc/storybook-custom-docs/src/index.tsx index d67b35f6b..8faccb354 100644 --- a/misc/storybook-custom-docs/src/index.tsx +++ b/misc/storybook-custom-docs/src/index.tsx @@ -35,20 +35,36 @@ export const getContext = () => { parameters, }; }; + +/** + * function returning the current story id + */ +export const getCurrentStoryId = (): string | undefined => { + const selection = + window && + //@ts-ignore + window.__STORYBOOK_CLIENT_API__ && + //@ts-ignore + window.__STORYBOOK_CLIENT_API__.store().getSelection(); + return selection ? selection.storyId : undefined; +}; + /** * React hook hook that tracks the changes to the current story and returns it's id * @param defaultId initial story value, if not provided will return the current story * @returns a story id as a React hook, when the the current story changes, will call back */ -export const useStoryId = (defaultId: string = '.') => { - const [storyId, setStoryId] = React.useState(defaultId); +export const useStoryId = () => { + const [storyId, setStoryId] = React.useState( + getCurrentStoryId() || '.', + ); const channel = React.useMemo(() => addons.getChannel(), []); React.useEffect(() => { const onStoryChange = (id: string) => { setStoryId(id); }; - channel.on(STORY_CHANGED, onStoryChange); + return () => { channel.off(STORY_CHANGED, onStoryChange); }; diff --git a/misc/storybook-custom-docs/src/manager-loader.ts b/misc/storybook-custom-docs/src/manager-loader.ts index ff7987977..8ddede32e 100644 --- a/misc/storybook-custom-docs/src/manager-loader.ts +++ b/misc/storybook-custom-docs/src/manager-loader.ts @@ -30,12 +30,12 @@ module.exports.default = async function() { route: ({ storyId }) => \`/\${key}/\${storyId}\`, match: ({ viewMode }) => viewMode === key, render: ({ active }) => { - return React.createElement(ManagerContainer, { + const props = { active, - title, - id:\`controls-docs-page-\${key}\`, + route: key, api, - }); + }; + return React.createElement(ManagerContainer, props); }, }); }); diff --git a/misc/storybook-custom-docs/src/preview-loader.ts b/misc/storybook-custom-docs/src/preview-loader.ts index 57190f3d5..be3202fb5 100644 --- a/misc/storybook-custom-docs/src/preview-loader.ts +++ b/misc/storybook-custom-docs/src/preview-loader.ts @@ -3,6 +3,7 @@ import { getOptions } from 'loader-utils'; interface DocsOptions { pages: string[]; } + module.exports.default = async function() { const options: DocsOptions = (getOptions(this) as DocsOptions) || {}; const { pages } = options; @@ -10,35 +11,49 @@ module.exports.default = async function() { const code = ` const React = require('react'); const ReactDOM = require('react-dom'); +import addons from '@storybook/addons'; ${pages .map((file, index) => `const pageConfig_${index} = require("${file}");`) .join('\n')} -const { BroadcastChannel } = require('broadcast-channel'); + +const channel = addons.getChannel(); + +const activePages: string[] = []; const attachPage = (pageConfig) => { - const ATTACH_DOCS_PAGE = \`attach_docs_page_\${pageConfig.title}\`; - const channel = new BroadcastChannel(ATTACH_DOCS_PAGE, { - type: 'localstorage', - }); - - channel.onmessage = ({ id, active, storyId }) => { + const ATTACH_DOCS_PAGE = \`attach_docs_page_\${pageConfig.key}\`; + + channel.on(ATTACH_DOCS_PAGE, ({ active, storyId }) => { + const id = \`controls-docs-page-\${pageConfig.key}\`; var node = document.getElementById(id); if (!node) { node = document.createElement('div'); node.setAttribute('id', id); document.body.appendChild(node); } + const root = document.getElementById('root'); + const pageIndex = activePages.indexOf(id); if (active) { + if (pageIndex < 0) { + activePages.push(id); + } + root.style.setProperty('display', 'none'); node.removeAttribute('hidden'); - ReactDOM.render(pageConfig.render({ active, storyId }), + ReactDOM.render(pageConfig.render({ active }), document.getElementById(id), ); } else { node.setAttribute('hidden', 'true'); ReactDOM.unmountComponentAtNode(node); + if (pageIndex >= 0) { + activePages.splice(pageIndex, 1); + if (activePages.length === 0) { + root.style.removeProperty('display'); + } + } } - }; + }); }; const pageConfigs = []; ${pages @@ -48,7 +63,11 @@ ${pages ) .join('\n')} -pageConfigs.forEach(p => attachPage(p)); +pageConfigs.forEach(p => { + attachPage(p); + const REQUEST_DOCS_PAGE = \`request_docs_page_\${p.key}\`; + channel.emit(REQUEST_DOCS_PAGE); +}); `; return callback(null, code); diff --git a/misc/storybook-custom-docs/src/types.ts b/misc/storybook-custom-docs/src/types.ts index 997d61eac..c613877bc 100644 --- a/misc/storybook-custom-docs/src/types.ts +++ b/misc/storybook-custom-docs/src/types.ts @@ -11,19 +11,15 @@ import * as React from 'react'; export default { key: 'custom', title: 'Simple Page', - render: ({ active, storyId }) => active ?

Simple docs page

{storyId}

: null, + render: ({ active }) => active ?

Simple docs page

: null, }``` */ export interface CustomPageRenderFnParams { /** - * is gthe page active (visible) or not (hidden) + * is the page active (visible) or not (hidden) */ active: boolean; - /** - * initial sgtory id. - */ - storyId: string; } export type CustomPageRenderFn = ( @@ -48,7 +44,6 @@ export interface CustomPageDef { */ // // active boolean - if the tab custom page is active - // storyId as a string // Return an object that can be rendered from ReactDOM.render render: CustomPageRenderFn; }