├── .gitignore ├── .npmignore ├── README.md ├── demo ├── demo.js ├── index.html └── vendor │ └── prism.js ├── package.json ├── screenshot.png ├── scss ├── main.scss └── vendor │ └── _prism.scss └── src ├── Styleguide.js ├── StyleguideDemo.js ├── examples ├── Blockquote.js ├── Button.js └── Footer.js └── test └── StyleguideTest.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 27 | node_modules 28 | 29 | dist/ 30 | 31 | scss/main.css 32 | scss/main.css.map 33 | scss/.sass-cache/ 34 | 35 | run.sh 36 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /src 2 | *~ 3 | 4 | run.sh -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | react-styleguide 2 | ---------------- 3 | A React component that takes in other components and organizes them into an easy to use styleguide. 4 | 5 | Installation 6 | ------------ 7 | ``` 8 | $ npm install --save react-styleguide 9 | ``` 10 | 11 | To view a working demo version, 12 | 13 | ``` 14 | $ npm install 15 | $ npm run build 16 | ``` 17 | 18 | And then open the `demo/index.html` file in your browser. 19 | 20 | Or visit the live demo at [http://jmfurlott.github.io/react-styleguide](http://jmfurlott.github.io/react-styleguide) 21 | 22 | Example Usage 23 | ------------- 24 | Include the generated css file from `dist` if you want to use the styling that I've created in your project. 25 | 26 | Just create a new component containing the Styleguide component. Include all the components that you want in the styleguide and provide the information that you want in include in the props. The props that can be included for each are: 27 | 28 | - title 29 | - description 30 | - example (this is the code that will be displayed) 31 | 32 | Inside the `div`, include the code that should be rendered and displayed in the styleguide. This will probably be the same as the `example` prop. 33 | 34 | 35 | ```javascript 36 | var React = require('react'); 37 | var Styleguide = require('react-styleguide'); 38 | 39 | // If you would like to include Prism.js as the syntax highlighter 40 | var Prism from '../demo/vendor/prism.js'; 41 | 42 | // Require the components that you want to include 43 | import Button from './examples/Button.js'; 44 | import Blockquote from './examples/Blockquote.js'; 45 | 46 | var Component = React.createClass({ 47 | render: function() { 48 | 49 |
56 |
57 |
58 | 59 |
64 |
66 | 67 | ... 68 | 69 |
70 | } 71 | }); 72 | ``` 73 | 74 | Styleguide Options 75 | ------------------ 76 | For the styleguide, the following options are available: 77 | 78 | - `codeClassName` - changes the `` class if you do not want to use Prism.js for highlighting otherwise defaults to `language-javascript` 79 | - `highlight` - a function that can be passed if you want highlight the code using a different function from Prism.js's function. Defaults to just the code example itself. 80 | 81 | Optional Includes 82 | ----------------- 83 | You can have the styleguide come styling free if you desire, but it is also possible to have the included styles be used (they can be seen in the screenshots). Please look at the `demo/index.html` to see what else could be included. 84 | 85 | - Google web fonts 86 | - `main.css` located in the `dist` folder 87 | - `prism.js` located in the `demo` folder (can be included via Node.js or browser side) 88 | 89 | Any and all of these are optional but are recommended. 90 | 91 | Extending the Styles 92 | -------------------- 93 | The styles of the styleguide follow a simple version of [SuitCSS](https://suitcss.github.io/), and follow a simple hierarchy. Please view the `scss/main.scss` for a look at how to style the page. 94 | 95 | Screenshots 96 | ----------- 97 | ![Screenshot](./screenshot.png) 98 | 99 | TODO 100 | ---- 101 | - Better way of converting from example and example code to the styleguide (instead of including it twice, essentially) 102 | - Write tests for the component (framework is already set in place) 103 | - Make styled version responsive 104 | 105 | License 106 | ------- 107 | MIT 108 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /demo/vendor/prism.js: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism-okaidia&languages=markup+css+clike+javascript */ 2 | self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{};var Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content),e.alias):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(d instanceof a)){g.lastIndex=0;var m=g.exec(d);if(m){c&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,k=y+v,b=d.slice(0,y+1),w=d.slice(k+1),O=[p,1];b&&O.push(b);var N=new a(l,u?t.tokenize(m,u):m,h);O.push(N),w&&O.push(w),Array.prototype.splice.apply(r,O)}}}}}return r},hooks:{all:{},add:function(e,n){var a=t.hooks.all;a[e]=a[e]||[],a[e].push(n)},run:function(e,n){var a=t.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(n)}}},n=t.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(n.stringify=function(e,a,r){if("string"==typeof e)return e;if("[object Array]"==Object.prototype.toString.call(e))return e.map(function(t){return n.stringify(t,a,e)}).join("");var i={type:e.type,content:n.stringify(e.content,a,r),tag:"span",classes:["token",e.type],attributes:{},language:a,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===t.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}t.hooks.run("wrap",i);var s="";for(var o in i.attributes)s+=o+'="'+(i.attributes[o]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+s+">"+i.content+""},!self.document)return self.addEventListener?(self.addEventListener("message",function(e){var n=JSON.parse(e.data),a=n.language,r=n.code;self.postMessage(JSON.stringify(t.util.encode(t.tokenize(r,t.languages[a])))),self.close()},!1),self.Prism):self.Prism;var a=document.getElementsByTagName("script");return a=a[a.length-1],a&&(t.filename=a.src,document.addEventListener&&!a.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism);; 3 | Prism.languages.markup={comment://g,prolog:/<\?.+?\?>/,doctype://,cdata://i,tag:{pattern:/<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|[^\s'">=]+))?\s*)*\/?>/gi,inside:{tag:{pattern:/^<\/?[\w:-]+/i,inside:{punctuation:/^<\/?/,namespace:/^[\w-]+?:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi,inside:{punctuation:/=|>|"/g}},punctuation:/\/?>/g,"attr-name":{pattern:/[\w:-]+/g,inside:{namespace:/^[\w-]+?:/}}}},entity:/\&#?[\da-z]{1,8};/gi},Prism.hooks.add("wrap",function(t){"entity"===t.type&&(t.attributes.title=t.content.replace(/&/,"&"))});; 4 | Prism.languages.css={comment:/\/\*[\w\W]*?\*\//g,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*{))/gi,inside:{punctuation:/[;:]/g}},url:/url\((["']?).*?\1\)/gi,selector:/[^\{\}\s][^\{\};]*(?=\s*\{)/g,property:/(\b|\B)[\w-]+(?=\s*:)/gi,string:/("|')(\\?.)*?\1/g,important:/\B!important\b/gi,punctuation:/[\{\};:]/g,"function":/[-a-z0-9]+(?=\()/gi},Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/[\w\W]*?<\/style>/gi,inside:{tag:{pattern:/|<\/style>/gi,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.css},alias:"language-css"}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').+?\1/gi,inside:{"attr-name":{pattern:/^\s*style/gi,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/gi,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag));; 5 | Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//g,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*?(\r?\n|$)/g,lookbehind:!0}],string:/("|')(\\?.)*?\1/g,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/gi,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g,"boolean":/\b(true|false)\b/g,"function":{pattern:/[a-z0-9_]+\(/gi,inside:{punctuation:/\(/}},number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,operator:/[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};; 6 | Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|-?Infinity)\b/g,"function":/(?!\d)[a-z0-9_$]+(?=\()/gi}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,lookbehind:!0}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/[\w\W]*?<\/script>/gi,inside:{tag:{pattern:/|<\/script>/gi,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript},alias:"language-javascript"}});; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-styleguide", 3 | "version": "0.0.4", 4 | "description": "A component that generates a style guide from components", 5 | "main": "dist/Styleguide.js", 6 | "scripts": { 7 | "prebuild": "mkdir -p dist && npm run styles", 8 | "build": "6to5 src -d dist --modules commonInterop", 9 | "postbuild": "browserify dist/StyleguideDemo.js -o demo/demo.js", 10 | "test": "npm run build && mocha ./dist/test/**/*.js", 11 | "styles": "node-sass scss/main.scss dist/main.css", 12 | "prepublish": "npm run test", 13 | "watch": "6to5 -w src -d dist --modules commonInterop && watchify dist/StyleguideDemo.js -o demo/demo.js" 14 | }, 15 | "keywords": [ 16 | "react", 17 | "styleguide", 18 | "components", 19 | "generate" 20 | ], 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/jmfurlott/react-styleguide.git" 24 | }, 25 | "author": "Joseph Furlott ", 26 | "license": "MIT", 27 | "devDependencies": { 28 | "6to5": "~1.15.0", 29 | "chai": "~1.10.0", 30 | "jquery": "~2.1.3", 31 | "jsdom": "~1.5.0", 32 | "mocha": "~2.0.1", 33 | "node-sass": "1.2.2", 34 | "react": "0.12.x", 35 | "reactify": "~0.17.1", 36 | "react-to-jsx": "^1.2.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmfurlott/react-styleguide/b40416a1aee674e597e2c46e498164cf82648eb6/screenshot.png -------------------------------------------------------------------------------- /scss/main.scss: -------------------------------------------------------------------------------- 1 | @import 'vendor/prism'; 2 | .Styleguide { 3 | font-family: 'Open Sans', sans-serif; 4 | 5 | &-header { 6 | height: 5rem; 7 | width: 100%; 8 | position: fixed; 9 | z-index: 1; 10 | background-color: #272822; 11 | color: white; 12 | > h1 { 13 | font-family: 'Open Sans', sans-serif; 14 | padding-left: 2rem; 15 | font-weight: 100; 16 | margin: 0; 17 | line-height: 5rem; 18 | } 19 | } 20 | 21 | &-sidebar { 22 | position: fixed; 23 | top: 5rem; 24 | left: 0; 25 | height: 100%; 26 | width: 12.5rem; 27 | border-right: solid 1px #eee; 28 | 29 | > ul { 30 | list-style-type: none; 31 | padding-left: 0; 32 | 33 | li { 34 | position: relative; 35 | text-align: center; 36 | transition: background-color 150ms linear; 37 | a { 38 | text-decoration: none; 39 | color: black; 40 | width: 100%; 41 | padding: 1rem 0; 42 | display: block; 43 | } 44 | &:hover { 45 | background-color: #eee; 46 | transition: background-color 150ms linear; 47 | } 48 | } 49 | } 50 | } 51 | 52 | &-components { 53 | position: relative; 54 | margin-left: 12.5rem; 55 | padding-top: 1rem; 56 | padding-left: 1rem; 57 | padding-right: 1rem; 58 | 59 | &-component { 60 | padding-top: 4rem; 61 | 62 | &-example { 63 | border: solid 1px #eeeeee; 64 | padding: 1rem; 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /scss/vendor/_prism.scss: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism-okaidia&languages=markup+css+clike+javascript */ 2 | /** 3 | * okaidia theme for JavaScript, CSS and HTML 4 | * Loosely based on Monokai textmate theme by http://www.monokai.nl/ 5 | * @author ocodia 6 | */ 7 | 8 | code[class*="language-"], 9 | pre[class*="language-"] { 10 | color: #f8f8f2; 11 | text-shadow: 0 1px rgba(0, 0, 0, 0.3); 12 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 13 | direction: ltr; 14 | text-align: left; 15 | white-space: pre; 16 | word-spacing: normal; 17 | word-break: normal; 18 | line-height: 1.5; 19 | 20 | -moz-tab-size: 4; 21 | -o-tab-size: 4; 22 | tab-size: 4; 23 | 24 | -webkit-hyphens: none; 25 | -moz-hyphens: none; 26 | -ms-hyphens: none; 27 | hyphens: none; 28 | } 29 | 30 | /* Code blocks */ 31 | pre[class*="language-"] { 32 | padding: 1em; 33 | margin: .5em 0; 34 | overflow: auto; 35 | border-radius: 0.3em; 36 | } 37 | 38 | :not(pre) > code[class*="language-"], 39 | pre[class*="language-"] { 40 | background: #272822; 41 | } 42 | 43 | /* Inline code */ 44 | :not(pre) > code[class*="language-"] { 45 | padding: .1em; 46 | border-radius: .3em; 47 | } 48 | 49 | .token.comment, 50 | .token.prolog, 51 | .token.doctype, 52 | .token.cdata { 53 | color: slategray; 54 | } 55 | 56 | .token.punctuation { 57 | color: #f8f8f2; 58 | } 59 | 60 | .namespace { 61 | opacity: .7; 62 | } 63 | 64 | .token.property, 65 | .token.tag, 66 | .token.constant, 67 | .token.symbol, 68 | .token.deleted { 69 | color: #f92672; 70 | } 71 | 72 | .token.boolean, 73 | .token.number { 74 | color: #ae81ff; 75 | } 76 | 77 | .token.selector, 78 | .token.attr-name, 79 | .token.string, 80 | .token.char, 81 | .token.builtin, 82 | .token.inserted { 83 | color: #a6e22e; 84 | } 85 | 86 | .token.operator, 87 | .token.entity, 88 | .token.url, 89 | .language-css .token.string, 90 | .style .token.string, 91 | .token.variable { 92 | color: #f8f8f2; 93 | } 94 | 95 | .token.atrule, 96 | .token.attr-value { 97 | color: #e6db74; 98 | } 99 | 100 | .token.keyword { 101 | color: #66d9ef; 102 | } 103 | 104 | .token.regex, 105 | .token.important { 106 | color: #fd971f; 107 | } 108 | 109 | .token.important { 110 | font-weight: bold; 111 | } 112 | 113 | .token.entity { 114 | cursor: help; 115 | } 116 | 117 | -------------------------------------------------------------------------------- /src/Styleguide.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react/addons'; 4 | import reactToJsx from 'react-to-jsx'; 5 | 6 | let Styleguide = React.createClass({ 7 | 8 | listComponentTitles() { 9 | let children = this.props.children; 10 | 11 | children = (React.Children.count(children) == 1) ? [ children ] : children; 12 | 13 | return children.map(function(child, index) { 14 | let title = child.props.title.replace(' ','-'); 15 | return
  • {child.props.title}
  • ; 16 | }); 17 | }, 18 | 19 | listComponents() { 20 | let children = this.props.children; 21 | let self = this; 22 | 23 | children = (React.Children.count(children) == 1) ? [ children ] : children; 24 | return children.map(function(child, index) { 25 | console.log(child); 26 | 27 | let title = child.props.title.replace(' ','-'); 28 | let code = reactToJsx(child.props.children); 29 | return( 30 |
    31 |

    {child.props.title}

    32 |

    {child.props.description}

    33 |
    {child.props.children}
    34 |
    35 |
    36 |                
    37 |                 {self.props.highlight ? self.props.highlight(code) : code}
    38 |               
    39 |             
    40 |
    41 |
    42 | ); 43 | }); 44 | }, 45 | 46 | componentDidMount() { 47 | //console.log(this.props.children); 48 | }, 49 | 50 | render() { 51 | 52 | return( 53 |
    54 |
    55 |

    {this.props.title}

    56 |
    57 |
    58 |
      59 | {this.listComponentTitles()} 60 |
    61 |
    62 |
    63 | {this.listComponents()} 64 |
    65 |
    66 | ); 67 | } 68 | 69 | }); 70 | 71 | export default Styleguide; 72 | -------------------------------------------------------------------------------- /src/StyleguideDemo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react/addons'; 4 | import Styleguide from './Styleguide.js'; 5 | 6 | import Prism from '../demo/vendor/prism.js'; 7 | 8 | // Components to be included 9 | import Button from './examples/Button.js'; 10 | import Blockquote from './examples/Blockquote.js'; 11 | import Footer from './examples/Footer.js'; 12 | 13 | React.render( 14 |
    18 |
    19 |
    20 | 21 |
    25 |
    27 |
    31 |
    32 |
    33 | 34 |
    35 | , document.getElementById('demo')); 36 | 37 | -------------------------------------------------------------------------------- /src/examples/Blockquote.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | 5 | var Blockquote = React.createClass({ 6 | getInitialState: function() { 7 | return { 8 | text : false, 9 | speaker : false 10 | }; 11 | }, 12 | 13 | render: function() { 14 | return ( 15 |
    16 |
    {this.props.text}
    17 | — {this.props.speaker} 18 |
    19 | ); 20 | } 21 | }); 22 | 23 | module.exports = Blockquote; 24 | -------------------------------------------------------------------------------- /src/examples/Button.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react/addons'; 4 | 5 | let Button = React.createClass({ 6 | 7 | render() { 8 | return ; 9 | } 10 | 11 | }); 12 | 13 | export default Button; 14 | -------------------------------------------------------------------------------- /src/examples/Footer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | 5 | var Footer = React.createClass({ 6 | getInitialState: function() { 7 | return { 8 | text : false 9 | }; 10 | }, 11 | 12 | render: function() { 13 | return ( 14 |
    15 |
    {this.props.text}
    16 |
    17 | ); 18 | } 19 | }); 20 | 21 | module.exports = Footer; 22 | -------------------------------------------------------------------------------- /src/test/StyleguideTest.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { expect } from 'chai'; 4 | import { jsdom } from 'jsdom'; 5 | global.document = jsdom(''); 6 | global.window = document.parentWindow; 7 | global.navigator = {}; 8 | navigator.userAgent = 'node'; 9 | 10 | describe('Styleguide', function() { 11 | 12 | import React from 'react/addons'; 13 | let { TestUtils } = React.addons; 14 | 15 | // Import component here 16 | 17 | // Tests here 18 | it('says hello world', function() { 19 | expect(true).to.be.true; 20 | }); 21 | 22 | }); 23 | --------------------------------------------------------------------------------