├── .babelrc ├── .eslintrc ├── .gitignore ├── .npmignore ├── .scripts ├── get_gh_pages_url.js ├── mocha_runner.js ├── prepublish.sh ├── publish_storybook.sh └── user │ ├── prepublish.sh │ └── pretest.js ├── .storybook ├── config.js ├── user │ └── modify_webpack_config.js └── webpack.config.js ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs └── home-screenshot.png ├── example ├── Button.js └── story.js ├── package-lock.json ├── package.json └── src ├── components ├── Chapter.jsx ├── Node.jsx ├── PropTable.jsx ├── Section.jsx └── Story.jsx ├── index.js ├── tests └── index.js ├── theme.js └── utils └── info-content.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react" 5 | ] 6 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "rules": { 4 | "arrow-body-style": 0, 5 | "prefer-arrow-callback": 0, 6 | "func-names": 0, 7 | "react/jsx-filename-extension": 0, 8 | "react/jsx-no-bind": 0, 9 | "react/jsx-uses-react": 1, 10 | "react/prefer-stateless-function": 0, 11 | "import/no-named-as-default": 0, 12 | "import/no-named-as-default-member": 0, 13 | "import/named": 0 14 | }, 15 | "parserOptions": { 16 | "ecmaVersion": 9 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | dist 4 | .vscode 5 | .idea 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .babelrc 2 | -------------------------------------------------------------------------------- /.scripts/get_gh_pages_url.js: -------------------------------------------------------------------------------- 1 | // IMPORTANT 2 | // --------- 3 | // This is an auto generated file with React CDK. 4 | // Do not modify this file. 5 | 6 | const parse = require('git-url-parse'); 7 | var ghUrl = process.argv[2]; 8 | const parsedUrl = parse(ghUrl); 9 | 10 | const ghPagesUrl = 'https://' + parsedUrl.owner + '.github.io/' + parsedUrl.name; 11 | console.log(ghPagesUrl); 12 | -------------------------------------------------------------------------------- /.scripts/mocha_runner.js: -------------------------------------------------------------------------------- 1 | // IMPORTANT 2 | // --------- 3 | // This is an auto generated file with React CDK. 4 | // Do not modify this file. 5 | // Use `.scripts/user/pretest.js instead`. 6 | 7 | require('@babel/register'); 8 | require('core-js/stable'); 9 | require('regenerator-runtime/runtime'); 10 | 11 | // Add jsdom support, which is required for enzyme. 12 | const { JSDOM } = require('jsdom'); 13 | 14 | const { document } = (new JSDOM('', { url: 'http://localhost:9011'})).window; 15 | 16 | var exposedProperties = ['window', 'navigator', 'document']; 17 | 18 | global.document = document; 19 | global.window = document.defaultView; 20 | Object.keys(document.defaultView).forEach((property) => { 21 | if (typeof global[property] === 'undefined') { 22 | exposedProperties.push(property); 23 | global[property] = document.defaultView[property]; 24 | } 25 | }); 26 | 27 | global.navigator = { 28 | userAgent: 'node.js' 29 | }; 30 | 31 | process.on('unhandledRejection', function (error) { 32 | console.error('Unhandled Promise Rejection:'); 33 | console.error(error && error.stack || error); 34 | }); 35 | 36 | require('./user/pretest.js'); 37 | -------------------------------------------------------------------------------- /.scripts/prepublish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # IMPORTANT 4 | # --------- 5 | # This is an auto generated file with React CDK. 6 | # Do not modify this file. 7 | # Use `.scripts/user/prepublish.sh instead`. 8 | 9 | echo "=> Transpiling 'src' into ES5 ..." 10 | echo "" 11 | rm -rf ./dist 12 | ./node_modules/.bin/babel --ignore tests,stories --plugins "@babel/transform-runtime" ./src --out-dir ./dist 13 | echo "" 14 | echo "=> Transpiling completed." 15 | 16 | . .scripts/user/prepublish.sh 17 | -------------------------------------------------------------------------------- /.scripts/publish_storybook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # IMPORTANT 4 | # --------- 5 | # This is an auto generated file with React CDK. 6 | # Do not modify this file. 7 | 8 | set -e # exit with nonzero exit code if anything fails 9 | 10 | # get GIT url 11 | 12 | GIT_URL=`git config --get remote.origin.url` 13 | if [[ $GIT_URL == "" ]]; then 14 | echo "This project is not configured with a remote git repo". 15 | exit 1 16 | fi 17 | 18 | # clear and re-create the out directory 19 | rm -rf .out || exit 0; 20 | mkdir .out; 21 | 22 | # run our compile script, discussed above 23 | build-storybook -o .out 24 | 25 | # go to the out directory and create a *new* Git repo 26 | cd .out 27 | git init 28 | 29 | # inside this git repo we'll pretend to be a new user 30 | git config user.name "GH Pages Bot" 31 | git config user.email "hello@ghbot.com" 32 | 33 | # The first and only commit to this new Git repo contains all the 34 | # files present with the commit message "Deploy to GitHub Pages". 35 | git add . 36 | git commit -m "Deploy Storybook to GitHub Pages" 37 | 38 | # Force push from the current repo's master branch to the remote 39 | # repo's gh-pages branch. (All previous history on the gh-pages branch 40 | # will be lost, since we are overwriting it.) We redirect any output to 41 | # /dev/null to hide any sensitive credential data that might otherwise be exposed. 42 | git push --force --quiet $GIT_URL master:gh-pages > /dev/null 2>&1 43 | cd .. 44 | rm -rf .out 45 | 46 | echo "" 47 | echo "=> Storybook deployed to: `node .scripts/get_gh_pages_url.js $GIT_URL`" 48 | -------------------------------------------------------------------------------- /.scripts/user/prepublish.sh: -------------------------------------------------------------------------------- 1 | # Use this file to your own code to run at NPM `prepublish` event. 2 | -------------------------------------------------------------------------------- /.scripts/user/pretest.js: -------------------------------------------------------------------------------- 1 | // Use this file to setup any test utilities. 2 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { configure, setAddon, addDecorator } from '@storybook/react'; 3 | import chapterAddon from '../src/'; 4 | 5 | addDecorator((story) => ( 6 |
7 | {story()} 8 |
9 | )); 10 | 11 | setAddon(chapterAddon); 12 | 13 | configure(function () { 14 | require('../example/story'); 15 | }, module); 16 | -------------------------------------------------------------------------------- /.storybook/user/modify_webpack_config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (config) { 2 | // This is the default webpack config defined in the `../webpack.config.js` 3 | // modify as you need. 4 | }; -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | // IMPORTANT 2 | // --------- 3 | // This is an auto generated file with React CDK. 4 | // Do not modify this file. 5 | // Use `.storybook/user/modify_webpack_config.js instead`. 6 | 7 | const path = require('path'); 8 | const updateConfig = require('./user/modify_webpack_config'); 9 | 10 | const config = { 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.css?$/, 15 | loaders: ['style', 'raw'], 16 | include: path.resolve(__dirname, '../'), 17 | }, 18 | ], 19 | }, 20 | }; 21 | 22 | updateConfig(config); 23 | module.exports = config; 24 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ### v3.3.0 4 | 5 | * Add setDefaults function [PR114](https://github.com/kadirahq/react-storybook-addon-info/pull/114) 6 | 7 | ### v3.2.4 8 | 9 | * Add missing dist files [PR113](https://github.com/kadirahq/react-storybook-addon-info/pull/113) 10 | 11 | ### v3.2.3 12 | 13 | * Handle number type nodes [PR110](https://github.com/kadirahq/react-storybook-addon-info/pull/110) 14 | 15 | ### v3.2.2 16 | 17 | * Use markdown-to-react-components npm package instead of our fork. Our PR to them is merged and published. [PR109](https://github.com/kadirahq/react-storybook-addon-info/pull/109) 18 | 19 | ### v3.2.1 20 | 21 | * Handle false values for types [PR54](https://github.com/kadirahq/react-storybook-addon-info/pull/54) 22 | 23 | ### v3.2.0 24 | 25 | * Support custom MTRC config [PR54](https://github.com/kadirahq/react-storybook-addon-info/pull/54) 26 | * Fix propTables prop validation with a default value [PR55](https://github.com/kadirahq/react-storybook-addon-info/pull/55) 27 | 28 | ### v3.1.4 29 | 30 | * Remove propTables prop validation warning [PR53](https://github.com/kadirahq/react-storybook-addon-info/pull/53) 31 | * Update example storybook [PR52](https://github.com/kadirahq/react-storybook-addon-info/pull/52) 32 | 33 | ### v3.1.3 34 | 35 | * Fix wrong detection of propType when isRequired is set [PR49](https://github.com/kadirahq/react-storybook-addon-info/pull/49) 36 | * Add displayName for Button [PR51](https://github.com/kadirahq/react-storybook-addon-info/pull/51) 37 | 38 | ### v3.1.2 39 | 40 | * Fixed a bug which made the `info` to not display and the `options` parameter to be ignored when `info` is not given.[PR45](https://github.com/kadirahq/react-storybook-addon-info/pull/45) 41 | 42 | ### v3.1.1 43 | 44 | * Add a z-index for rendered items to make the overlay always display on top [PR38](https://github.com/kadirahq/react-storybook-addon-info/pull/38) 45 | 46 | ### v3.1.0 47 | 48 | * Make the `info` argument optional [PR37](https://github.com/kadirahq/react-storybook-addon-info/pull/37) 49 | 50 | ### v3.0.10 51 | 52 | * Render the component inside a div element when on inline mode [PR34](https://github.com/kadirahq/react-storybook-addon-info/pull/34) 53 | 54 | ### v3.0.9 55 | 56 | * Add missing `@kadira/storybook` devDependencies [PR25](https://github.com/kadirahq/react-storybook-addon-info/pull/25) 57 | * Improve prop rendering in jsx source view [PR24](https://github.com/kadirahq/react-storybook-addon-info/pull/24) 58 | * Avoid warning message with "webkitFontSmoothing" [PR30](https://github.com/kadirahq/react-storybook-addon-info/pull/30) 59 | * Remove max-width style rule for wrapper [PR31](https://github.com/kadirahq/react-storybook-addon-info/pull/31) and [PR36](https://github.com/kadirahq/react-storybook-addon-info/pull/36) 60 | * Improve prop table rendering (handle css resets) [PR32](https://github.com/kadirahq/react-storybook-addon-info/pull/32) 61 | 62 | ### v3.0.8 63 | 64 | * Fixed unkeyed array iteration warning in React with: [PR23](https://github.com/kadirahq/react-storybook-addon-info/pull/23) 65 | 66 | ### v3.0.7 67 | 68 | * Improve default display in prop table. See [#16](https://github.com/kadirahq/react-storybook-addon-info/pull/16) 69 | 70 | ### v3.0.6 71 | 72 | * Improve function type and react element type props display. See [#14](https://github.com/kadirahq/react-storybook-addon-info/pull/14) 73 | 74 | ### v3.0.5 75 | 76 | * Over-indentation of ending tag in source code is fixed. See [#13](https://github.com/kadirahq/react-storybook-addon-info/pull/13) 77 | 78 | ### v3.0.4 79 | 80 | * Remove the need to use json-loader with webpack when using this package. 81 | See: [#12](https://github.com/kadirahq/react-storybook-addon-info/issues/12) 82 | 83 | ### v3.0.0 84 | 85 | * Add the version which works as an React Storybook addon. 86 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to React Storybook Story Component 2 | 3 | We welcome your help to make this component better. This document will help to streamline the contributing process and save everyone's precious time. 4 | 5 | ## Development Setup 6 | 7 | This component has been setup with [React CDK](https://github.com/kadirahq/react-cdk). Refer [React CDK documentation](https://github.com/kadirahq/react-cdk)) to get started with the development. 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Your Name. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ⛔ [DEPRECATED] This addon is no longer maintained and is now considered deprecated in favour of Storybook's native [nesting and component hierarchy](https://storybook.js.org/docs/react/writing-stories/naming-components-and-hierarchy). We recommend Storybook version 6 be used instead. 2 | 3 |
4 | 5 | # React Storybook Chapters Addon 6 | 7 | React Storybook Chapters addon allows showcasing of multiple components within a story by breaking it down into smaller categories (chapters) and subcategories (sections) for more organizational goodness. 8 | 9 | Using the addon, a story can consist of multiple chapters and a chapter consists of multiple sections. Each section can render a block of code, 10 | which typically used to showcase one component or a particular state of a component. 11 | 12 | Chapters can be used to group related components together, or show varying states of a component. 13 | Each chapter comes with a **Chapter Title**, **Chapter Subtitle**, **Chapter Info** and a list of **Sections**. 14 | Simply omit any of them to hide them from rendering. 15 | 16 | Each section comes with a **Section Title**, **Section Subtitle**, **Section Info**. 17 | 18 | This addon was modified from [react-storybook-addon-info](https://github.com/storybooks/react-storybook-addon-info) and uses some of the component code from there. 19 | 20 | ![React Storybook Screenshot](docs/home-screenshot.png) 21 | 22 | ## Usage 23 | 24 | Install the following npm module: 25 | 26 | ```sh 27 | npm install --save-dev react-storybook-addon-chapters 28 | ``` 29 | 30 | Then set the addon in the place you configure storybook like this: 31 | 32 | ```js 33 | import React from 'react'; 34 | import { configure, setAddon } from '@storybook/react'; 35 | import chaptersAddon from 'react-storybook-addon-chapters'; 36 | 37 | setAddon(chaptersAddon); 38 | 39 | configure(function () { 40 | ... 41 | }, module); 42 | ``` 43 | 44 | To turn off the default styles add: 45 | 46 | ```js 47 | setDefaults({ sectionOptions: { useTheme: false } }); 48 | ``` 49 | 50 | All rendered components have a specified class. With the 'useTheme' set to false you should have no problem styling your chapters. 51 | 52 | Then create your stories with the `.addWithChapters` API. 53 | 54 | ```js 55 | import React from 'react'; 56 | import Button from './Button'; 57 | import { storiesOf } from '@storybook/react'; 58 | 59 | storiesOf('Addon Chapters') 60 | .addWithChapters( 61 | 'Story With Chapters', 62 | { 63 | subtitle: , 64 | info: , 65 | chapters: [ 66 | // List of chapters. Refer to Configuration Format section. 67 | { 68 | title: , 69 | subtitle: , 70 | info: , 71 | sections: [ 72 | // List of sections. 73 | { 74 | title: , 75 | subtitle: , 76 | info: , 77 | sectionFn: () => ( 35 | ); 36 | } 37 | 38 | Button.propTypes = propTypes; 39 | Button.defaultProps = defaultProps; 40 | export default Button; 41 | -------------------------------------------------------------------------------- /example/story.js: -------------------------------------------------------------------------------- 1 | import { storiesOf } from '@storybook/react'; 2 | import React from 'react'; 3 | import Button from './Button'; 4 | 5 | storiesOf('Addon Chapters', module) 6 | .addWithChapters( 7 | 'Story With Chapters', 8 | { 9 | useTheme: false, 10 | subtitle: 'Display multiple components within one story!', 11 | info: ` 12 | React Storybook Chapters addon allows showcasing of multiple components within a story by breaking it down into smaller categories (**Chapters**) and subcategories (**Sections**) for more organizational goodness. 13 | 14 | This section is called **Story Info** and you can provide an abstract of your story here. 15 | 16 | A story consists of multiple chapters and a chapter consists of multiple sections. Each section can render a block of code, 17 | which typically used to showcase one component or a particular state of a component. 18 | 19 | Yes, all info sections support markdown formatting! 20 | `, 21 | chapters: [ 22 | // List of chapters. 23 | { 24 | title: 'This is a Chapter\'s Title', 25 | subtitle: 'And this is a chapter\'s subtitle', 26 | info: ` 27 | Chapters can be used to group related components together, or show varying states of a component. 28 | Each chapter comes with a **Chapter Title**, **Chapter Subtitle**, **Chapter Info** and a list of **Sections**. 29 | Simply omit any of them to hide them from rendering. 30 | `, 31 | sections: [ 32 | // List of sections. 33 | { 34 | title: 'This is a Section\'s Title', 35 | subtitle: 'Each section can be used to render a component', 36 | info: ` 37 | Provide additional information about your section here. 38 | Each section comes with a **Section Title**, **Section Subtitle**, **Section Info**. 39 | Simply omit any of them to hide them from rendering. The section below does not have a subtitle nor info. 40 | 41 | There's also the option of showing the source code and propTypes of the component. 42 | `, 43 | sectionFn: () => (), 106 | options: { 107 | showSource: true, 108 | allowSourceToggling: true, 109 | showPropTables: true, 110 | allowPropTablesToggling: true, 111 | }, 112 | }, 113 | ... 114 | ], 115 | }, 116 | ... 117 | ] 118 | } 119 | ); 120 | ~~~ 121 | `, 122 | }, 123 | ], 124 | } 125 | ) 126 | .addWithChapters( 127 | 'Story Without Chapters', 128 | { 129 | info: ` 130 | If you don't require displaying of the chapter information, simply use only one chapter with your list of sections and omit the chapter-related parameters. 131 | You'll end up with just a list of rendered sections. Refer to the example in **example/story.js**. 132 | `, 133 | chapters: [ 134 | { 135 | sections: [ 136 | { 137 | title: 'Section Title', 138 | subtitle: 'Section Subtitle', 139 | info: ` 140 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 141 | `, 142 | sectionFn: () => ( 313 | ), 314 | 315 | allowSourceToggling && ( 316 | 333 | ) 334 | ]; 335 | 336 | const additional = ( 337 |
338 | {info && SectionDecorator.info(renderInfoContent(info), useTheme)} 339 | {showButtonsRow && SectionDecorator.buttons(buttons, useTheme)} 340 | {isSourceShown && this.renderSourceCode(useTheme)} 341 | {isPropsTableShown && this.renderPropTables(useTheme)} 342 |
343 | ); 344 | 345 | return SectionDecorator.main( 346 | SectionDecorator.header(header), 347 | SectionDecorator.component(children, useTheme, decorator), 348 | SectionDecorator.additional(additional), 349 | useTheme 350 | ); 351 | } 352 | } 353 | 354 | Section.displayName = 'Section'; 355 | Section.propTypes = propTypes; 356 | Section.defaultProps = defaultProps; 357 | -------------------------------------------------------------------------------- /src/components/Story.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { baseFonts } from '@storybook/components'; 4 | import Chapter from './Chapter'; 5 | import renderInfoContent from '../utils/info-content'; 6 | import theme from '../theme'; 7 | 8 | const propTypes = { 9 | context: PropTypes.object, 10 | title: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), 11 | subtitle: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), 12 | info: PropTypes.string, 13 | chapters: PropTypes.arrayOf(PropTypes.object), 14 | addonInfo: PropTypes.object, 15 | sectionOptions: PropTypes.object, 16 | }; 17 | 18 | const defaultProps = { 19 | context: {}, 20 | title: '', 21 | subtitle: '', 22 | info: '', 23 | chapters: [], 24 | }; 25 | 26 | export const storyStyles = { 27 | story: { 28 | ...baseFonts, 29 | }, 30 | header: { 31 | marginBottom: 60, 32 | }, 33 | title: { 34 | color: theme.grayDarkest, 35 | fontSize: 36, 36 | marginBottom: 10, 37 | }, 38 | subtitle: { 39 | color: theme.grayDark, 40 | fontSize: 18, 41 | marginBottom: 20, 42 | marginTop: 0, 43 | }, 44 | info: theme.infoStyle, 45 | }; 46 | 47 | export class StoryDecorator { 48 | static title(title, useTheme) { 49 | return ( 50 |

{title}

51 | ); 52 | } 53 | 54 | static subtitle(subtitle, useTheme) { 55 | return ( 56 | {subtitle} 57 | ); 58 | } 59 | 60 | static info(info, useTheme) { 61 | return ( 62 |
{info}
63 | ); 64 | } 65 | 66 | static main(header, chapters, useTheme) { 67 | return ( 68 |
69 |
{header}
70 |
71 | {chapters} 72 |
73 |
74 | ); 75 | } 76 | } 77 | 78 | export default class Story extends Component { 79 | render() { 80 | const { 81 | context, subtitle, title, info, chapters, addonInfo, sectionOptions, 82 | } = this.props; 83 | const { useTheme } = sectionOptions; 84 | 85 | const header = ( 86 |
87 | {title && StoryDecorator.title(title, useTheme)} 88 | {subtitle && StoryDecorator.subtitle(subtitle, useTheme)} 89 | {info && StoryDecorator.subtitle(renderInfoContent(info), useTheme)} 90 |
91 | ); 92 | 93 | const renderedChapters = chapters.map((chapter, i) => ( 94 | 95 | )); 96 | 97 | return StoryDecorator.main(header, renderedChapters, useTheme); 98 | } 99 | } 100 | 101 | Story.displayName = 'Story'; 102 | Story.propTypes = propTypes; 103 | Story.defaultProps = defaultProps; 104 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Theme from './theme'; 3 | import Story, { StoryDecorator, storyStyles } from './components/Story'; 4 | import { ChapterDecorator, chapterStyles } from './components/Chapter'; 5 | import { SectionDecorator, sectionStyles } from './components/Section'; 6 | import { propTableStyles } from './components/PropTable'; 7 | 8 | export { StoryDecorator, ChapterDecorator, SectionDecorator }; 9 | export { 10 | storyStyles, chapterStyles, sectionStyles, propTableStyles, 11 | }; 12 | export { Theme }; 13 | 14 | const defaultProps = { 15 | addonInfo: { 16 | inline: false, 17 | header: true, 18 | source: true, 19 | propTables: [], 20 | maxPropsIntoLine: 3, 21 | maxPropObjectKeys: 3, 22 | maxPropArrayLength: 3, 23 | maxPropStringLength: 50, 24 | }, 25 | sectionOptions: { 26 | showSource: true, 27 | allowSourceToggling: true, 28 | showPropTables: false, 29 | allowPropTablesToggling: true, 30 | useTheme: true, 31 | decorator: false, 32 | }, 33 | }; 34 | 35 | export default { 36 | addWithChapters(storyName, storyContentOrFn = {}) { 37 | return this.add(storyName, (context) => { 38 | const storyContent = typeof storyContentOrFn === 'function' 39 | ? storyContentOrFn() 40 | : storyContentOrFn; 41 | 42 | (storyContent.chapters || []).forEach((chapter) => { 43 | (chapter.sections || []).forEach((section) => { 44 | Object.assign(section, { 45 | options: Object.assign({}, defaultProps.sectionOptions, section.options), 46 | }); 47 | }); 48 | }); 49 | 50 | return ( 51 | 59 | ); 60 | }); 61 | }, 62 | }; 63 | 64 | export function setDefaults(newDefaults) { 65 | Object.assign(defaultProps.addonInfo, newDefaults.addonInfo); 66 | Object.assign(defaultProps.sectionOptions, newDefaults.sectionOptions); 67 | return defaultProps; 68 | } 69 | -------------------------------------------------------------------------------- /src/tests/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-expressions, import/no-extraneous-dependencies */ 2 | import { expect } from 'chai'; 3 | import addon, { setDefaults } from '../index'; 4 | 5 | const { describe, it } = global; 6 | 7 | const DEFAULTS = { 8 | addonInfo: { 9 | inline: false, 10 | header: true, 11 | source: true, 12 | propTables: [], 13 | maxPropsIntoLine: 3, 14 | maxPropObjectKeys: 3, 15 | maxPropArrayLength: 3, 16 | maxPropStringLength: 50, 17 | }, 18 | sectionOptions: { 19 | showSource: true, 20 | allowSourceToggling: true, 21 | showPropTables: false, 22 | allowPropTablesToggling: true, 23 | useTheme: true, 24 | decorator: false, 25 | }, 26 | }; 27 | 28 | describe('Chapters Addon', () => { 29 | describe('addWithChapters method', () => { 30 | it('does not break if no arguments are passed', () => { 31 | const scope = { 32 | add(name, callback) { 33 | callback(); 34 | }, 35 | }; 36 | const fn = addon.addWithChapters.bind(scope); 37 | expect(fn).to.not.throw(); 38 | }); 39 | 40 | it('passes all expected params to the underlying Story component', () => { 41 | const context = {}; 42 | const content = { 43 | subtitle: 'Story subtitle', 44 | info: 'Story info', 45 | chapters: [], 46 | }; 47 | const scope = { 48 | add(name, callback) { 49 | const story = callback(context); 50 | expect(story.props.title).to.equal('Story title'); 51 | expect(story.props.subtitle).to.equal(content.subtitle); 52 | expect(story.props.info).to.equal(content.info); 53 | expect(story.props.chapters).to.equal(content.chapters); 54 | expect(story.props.context).to.equal(context); 55 | }, 56 | }; 57 | addon.addWithChapters.call(scope, 'Story title', content); 58 | }); 59 | it('can be passed a function as a configuration argument', () => { 60 | const expected = { 61 | subtitle: 'Story subtitle', 62 | info: 'Story info', 63 | chapters: [], 64 | }; 65 | const content = () => expected; 66 | const scope = { 67 | add(name, callback) { 68 | const story = callback({}); 69 | const actual = story.props; 70 | expect(actual).to.include.all.keys(expected); 71 | }, 72 | }; 73 | addon.addWithChapters.call(scope, 'Story title', content); 74 | }); 75 | }); 76 | 77 | describe('default options', () => { 78 | it('are what we expect', () => { 79 | const scope = { 80 | add(name, callback) { 81 | const story = callback({}); 82 | expect(story.props.addonInfo).to.deep.equal(DEFAULTS.addonInfo); 83 | expect(story.props.sectionOptions).to.deep.equal(DEFAULTS.sectionOptions); 84 | }, 85 | }; 86 | addon.addWithChapters.call(scope); 87 | }); 88 | 89 | it('can be overridden by setDefaults', () => { 90 | const addonInfo = { header: false, source: false }; 91 | const sectionOptions = { showSource: false, showPropTables: true }; 92 | 93 | setDefaults({ addonInfo, sectionOptions }); 94 | const scope = { 95 | add(name, callback) { 96 | const story = callback({}); 97 | expect(story.props.addonInfo.header).to.be.false; 98 | expect(story.props.addonInfo.header).to.be.false; 99 | expect(story.props.sectionOptions.showSource).to.be.false; 100 | expect(story.props.sectionOptions.showPropTables).to.be.true; 101 | // reset to default values 102 | setDefaults(DEFAULTS); 103 | }, 104 | }; 105 | addon.addWithChapters.call(scope); 106 | }); 107 | }); 108 | }); 109 | -------------------------------------------------------------------------------- /src/theme.js: -------------------------------------------------------------------------------- 1 | const grayDarkest = '#363a45'; 2 | const grayDarker = '#565d6b'; 3 | const grayDark = '#898d97'; 4 | const gray = '#ccd6dd'; 5 | const grayLight = '#eaeff2'; 6 | const grayLighter = '#f7f9fb'; 7 | 8 | export default { 9 | border: gray, 10 | grayDarkest, 11 | grayDarker, 12 | grayDark, 13 | gray, 14 | grayLight, 15 | grayLighter, 16 | infoStyle: { 17 | color: grayDarker, 18 | fontSize: 14, 19 | lineHeight: 1.5, 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /src/utils/info-content.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import marksy from 'marksy'; 3 | import { 4 | H1, H2, H3, H4, H5, H6, Code, UL, A, LI, P, 5 | } from '@storybook/addon-info/dist/components/markdown'; 6 | 7 | 8 | const defaultMarksyConf = { 9 | h1: H1, 10 | h2: H2, 11 | h3: H3, 12 | h4: H4, 13 | h5: H5, 14 | h6: H6, 15 | code: Code, 16 | a: A, 17 | p: P, 18 | li: LI, 19 | ul: UL, 20 | }; 21 | 22 | export default function renderInfoContent(content) { 23 | const markdownInfo = marksy(defaultMarksyConf, content); 24 | 25 | if (!content || content === '' || typeof content !== 'string') { 26 | return null; 27 | } 28 | 29 | const lines = content.split('\n'); 30 | while (lines[0].trim() === '') { 31 | lines.shift(); 32 | } 33 | let padding = 0; 34 | const matches = lines[0].match(/^ */); 35 | if (matches) { 36 | padding = matches[0].length; 37 | } 38 | const source = lines.map(s => s.slice(padding)).join('\n'); 39 | 40 | return ( 41 |
42 | {markdownInfo(source).tree} 43 |
44 | ); 45 | } 46 | --------------------------------------------------------------------------------