├── .babelrc ├── .eslintrc ├── .gitignore ├── .npmignore ├── .scripts ├── get_gh_pages_url.js ├── mocha_runner.js ├── prepublish.sh ├── publish_storybook.sh └── user │ ├── prepublish.sh │ └── pretest.js ├── .storybook ├── addons.js ├── config.js ├── user │ └── modify_webpack_config.js └── webpack.config.js ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── dist ├── KnobManager.js ├── KnobStore.js ├── components │ ├── Panel.js │ ├── PropField.js │ ├── PropForm.js │ ├── WrapStory.js │ └── types │ │ ├── Array.js │ │ ├── Boolean.js │ │ ├── Color.js │ │ ├── Date │ │ ├── index.js │ │ └── styles.js │ │ ├── Number.js │ │ ├── Object.js │ │ ├── Select.js │ │ ├── Text.js │ │ └── index.js ├── index.js └── register.js ├── docs ├── demo.gif ├── demo.png └── storybook-knobs-example.png ├── example ├── stories │ └── index.js └── typescript │ └── index.tsx ├── package.json ├── register.js ├── src ├── KnobManager.js ├── KnobStore.js ├── components │ ├── Panel.js │ ├── PropField.js │ ├── PropForm.js │ ├── WrapStory.js │ ├── tests │ │ ├── Array.js │ │ └── Panel.js │ └── types │ │ ├── Array.js │ │ ├── Boolean.js │ │ ├── Color.js │ │ ├── Date │ │ ├── index.js │ │ └── styles.js │ │ ├── Number.js │ │ ├── Object.js │ │ ├── Select.js │ │ ├── Text.js │ │ └── index.js ├── index.js ├── register.js └── tests │ ├── KnobManager.js │ └── typescript.js ├── storybook-addon-knobs.d.ts ├── tsconfig.json └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "rules": { 4 | "arrow-body-style": 0, 5 | "prefer-arrow-callback": 0, 6 | "func-names": 0, 7 | "react/jsx-no-bind": 0, 8 | "react/jsx-uses-react": 1, 9 | "react/prefer-stateless-function": 0 10 | }, 11 | "parserOptions": { 12 | "ecmaVersion": 6, 13 | "ecmaFeatures": { 14 | "experimentalObjectRestSpread": true 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .idea 4 | .tern-port -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | .babelrc 3 | -------------------------------------------------------------------------------- /.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-core/register'); 8 | require('babel-polyfill'); 9 | 10 | // Add jsdom support, which is required for enzyme. 11 | var jsdom = require('jsdom').jsdom; 12 | 13 | var exposedProperties = ['window', 'navigator', 'document']; 14 | 15 | global.document = jsdom(''); 16 | global.window = document.defaultView; 17 | Object.keys(document.defaultView).forEach((property) => { 18 | if (typeof global[property] === 'undefined') { 19 | exposedProperties.push(property); 20 | global[property] = document.defaultView[property]; 21 | } 22 | }); 23 | 24 | global.navigator = { 25 | userAgent: 'node.js' 26 | }; 27 | 28 | process.on('unhandledRejection', function (error) { 29 | console.error('Unhandled Promise Rejection:'); 30 | console.error(error && error.stack || error); 31 | }); 32 | 33 | require('./user/pretest.js'); 34 | -------------------------------------------------------------------------------- /.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 "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/addons.js: -------------------------------------------------------------------------------- 1 | import '../src/register'; 2 | import '@kadira/storybook/addons' 3 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | // IMPORTANT 2 | // --------- 3 | // This is an auto generated file with React CDK. 4 | // Do not modify this file. 5 | 6 | import { configure } from '@kadira/storybook'; 7 | 8 | function loadStories() { 9 | require('../example/stories'); 10 | } 11 | 12 | configure(loadStories, module); 13 | -------------------------------------------------------------------------------- /.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 | }; 5 | -------------------------------------------------------------------------------- /.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 | loaders: [ 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 | # Changelog 2 | 3 | ### v1.7.1 4 | 01-December-2016 5 | 6 | Update style changes in the panel. [PR79](https://github.com/storybooks/storybook-addon-knobs/pull/79) 7 | 8 | ### v1.7.0 9 | 25-November-2016 10 | 11 | Modify number() to support a range slider as an input type for a knob.[PR77](https://github.com/storybooks/storybook-addon-knobs/pull/77) 12 | 13 | ### v1.6.0 14 | 19-November-2016 15 | 16 | Add Color as a type. [PR75](https://github.com/storybooks/storybook-addon-knobs/pull/75) 17 | 18 | ### v1.5.0 19 | 8-November-2016 20 | 21 | Add Array as a type. [PR70](https://github.com/storybooks/storybook-addon-knobs/pull/70). 22 | 23 | ### v1.4.1 24 | 27-October-2016 25 | 26 | Fix some issues related to typescript declaration. [PR69](Typescript declaration updates) 27 | 28 | ### v1.4.0 29 | 27-October-2016 30 | 31 | Add TypeScript support [PR65](https://github.com/kadirahq/storybook-addon-knobs/pull/65). 32 | 33 | ### v1.3.3 34 | 13-October-2016 35 | 36 | Fix issue where hot reloaded updates were not rendered [PR62](https://github.com/kadirahq/storybook-addon-knobs/pull/62). 37 | 38 | ### v1.3.2 39 | 13-October-2016 40 | 41 | Make the render function of WrapStory pure [PR61](https://github.com/kadirahq/storybook-addon-knobs/pull/61). 42 | 43 | ### v1.3.1 44 | 13-October-2016 45 | 46 | Show or hide knob from panel depending on whether it was called in the story or not [PR59](https://github.com/kadirahq/storybook-addon-knobs/pull/59). 47 | 48 | ### v1.3.0 49 | 05-October-2016 50 | 51 | Accept null values for the date type. [PR56](https://github.com/kadirahq/storybook-addon-knobs/pull/56). 52 | 53 | ### v1.2.3 54 | 28-September-2016 55 | 56 | Remove setting selected prop in options in Select knobs [PR47](https://github.com/kadirahq/storybook-addon-knobs/pull/47) 57 | 58 | ### v1.2.2 59 | 25-September-2016 60 | 61 | Fix copy-paste error [PR37](https://github.com/kadirahq/storybook-addon-knobs/pull/37) 62 | Remove
wrapper from WrapStory [PR42](https://github.com/kadirahq/storybook-addon-knobs/pull/42) 63 | 64 | ### v1.2.1 65 | 20-September-2016 66 | 67 | Support to use date type without a default value. [PR32](https://github.com/kadirahq/storybook-addon-knobs/pull/32) 68 | 69 | ### v1.2.0 70 | 19-September-2016 71 | 72 | Add a knob to get date from the user. [PR26](https://github.com/kadirahq/storybook-addon-knobs/pull/26) 73 | 74 | ### v1.1.0 75 | 11-September-2016 76 | 77 | Implement the select knob. [PR21](https://github.com/kadirahq/storybook-addon-knobs/pull/21) 78 | 79 | ### v1.0.1 80 | 09-September-2016 81 | 82 | Allow user to write JSON in the Object knob freely. 83 | Earlier, it's hard to add new fields without creating a JSON error. 84 | 85 | ### v1.0.0 86 | 09-September-2016 87 | 88 | Initial Release. 89 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Storybook Knobs 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 | 9 | ## Setting Up 10 | 11 | You can start the built-in storybook and play with this addon. Do this for that: 12 | 13 | ```sh 14 | npm i 15 | npm run storybook 16 | ``` 17 | 18 | Storybook lives inside the `example` directory and source code lives in `src` directory. 19 | 20 | ## Adding a new knob 21 | 22 | Adding a new knob is pretty easy. First you need to add the story side API to the `src/index.js`. 23 | 24 | Then you need to write the UI component to show/edit this knob inside `src/types` directory. Have a look at existing types for more information. 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Kadira Inc. 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 | # Storybook Addon Knobs 2 | 3 | This contents of this repo was moved to the [Storybook monorepo](https://github.com/storybooks/storybook/) and the NPM package name has been changed. 4 | 5 | - The old name of the package was: **@kadira/storybook-addon-knobs** 6 | - The new name of the package is: **@storybook/addon-knobs** 7 | - The location of the code is: https://github.com/storybooks/storybook/tree/master/addons/knobs 8 | 9 | The repo you're looking at now is out of date and no longer maintained. 10 | -------------------------------------------------------------------------------- /dist/KnobManager.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _extends2 = require('babel-runtime/helpers/extends'); 8 | 9 | var _extends3 = _interopRequireDefault(_extends2); 10 | 11 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 12 | 13 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 14 | 15 | var _createClass2 = require('babel-runtime/helpers/createClass'); 16 | 17 | var _createClass3 = _interopRequireDefault(_createClass2); 18 | 19 | var _react = require('react'); 20 | 21 | var _react2 = _interopRequireDefault(_react); 22 | 23 | var _WrapStory = require('./components/WrapStory'); 24 | 25 | var _WrapStory2 = _interopRequireDefault(_WrapStory); 26 | 27 | var _KnobStore = require('./KnobStore'); 28 | 29 | var _KnobStore2 = _interopRequireDefault(_KnobStore); 30 | 31 | var _deepEqual = require('deep-equal'); 32 | 33 | var _deepEqual2 = _interopRequireDefault(_deepEqual); 34 | 35 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 36 | 37 | // This is used by _mayCallChannel to determine how long to wait to before triggering a panel update 38 | var PANEL_UPDATE_INTERVAL = 400; 39 | 40 | var KnobManager = function () { 41 | function KnobManager() { 42 | (0, _classCallCheck3.default)(this, KnobManager); 43 | 44 | this.knobStore = null; 45 | this.knobStoreMap = {}; 46 | } 47 | 48 | (0, _createClass3.default)(KnobManager, [{ 49 | key: 'knob', 50 | value: function knob(name, options) { 51 | this._mayCallChannel(); 52 | 53 | var knobStore = this.knobStore; 54 | var existingKnob = knobStore.get(name); 55 | // We need to return the value set by the knob editor via this. 56 | // But, if the user changes the code for the defaultValue we should set 57 | // that value instead. 58 | if (existingKnob && (0, _deepEqual2.default)(options.value, existingKnob.defaultValue)) { 59 | return existingKnob.value; 60 | } 61 | 62 | var defaultValue = options.value; 63 | var knobInfo = (0, _extends3.default)({}, options, { 64 | name: name, 65 | defaultValue: defaultValue 66 | }); 67 | 68 | knobStore.set(name, knobInfo); 69 | return knobStore.get(name).value; 70 | } 71 | }, { 72 | key: 'wrapStory', 73 | value: function wrapStory(channel, storyFn, context) { 74 | this.channel = channel; 75 | var key = context.kind + ':::' + context.story; 76 | var knobStore = this.knobStoreMap[key]; 77 | 78 | if (!knobStore) { 79 | knobStore = this.knobStoreMap[key] = new _KnobStore2.default(); 80 | } 81 | 82 | this.knobStore = knobStore; 83 | knobStore.markAllUnused(); 84 | var initialContent = storyFn(context); 85 | var props = { context: context, storyFn: storyFn, channel: channel, knobStore: knobStore, initialContent: initialContent }; 86 | return _react2.default.createElement(_WrapStory2.default, props); 87 | } 88 | }, { 89 | key: '_mayCallChannel', 90 | value: function _mayCallChannel() { 91 | var _this = this; 92 | 93 | // Re rendering of the story may cause changes to the knobStore. Some new knobs maybe added and 94 | // Some knobs may go unused. So we need to update the panel accordingly. For example remove the 95 | // unused knobs from the panel. This function sends the `setKnobs` message to the channel 96 | // triggering a panel re-render. 97 | 98 | if (this.calling) { 99 | // If a call to channel has already registered ignore this call. 100 | // Once the previous call is completed all the changes to knobStore including the one that 101 | // triggered this, will be added to the panel. 102 | // This avoids emitting to the channel within very short periods of time. 103 | return; 104 | } 105 | this.calling = true; 106 | 107 | setTimeout(function () { 108 | _this.calling = false; 109 | // emit to the channel and trigger a panel re-render 110 | _this.channel.emit('addon:knobs:setKnobs', _this.knobStore.getAll()); 111 | }, PANEL_UPDATE_INTERVAL); 112 | } 113 | }]); 114 | return KnobManager; 115 | }(); 116 | 117 | exports.default = KnobManager; -------------------------------------------------------------------------------- /dist/KnobStore.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _keys = require("babel-runtime/core-js/object/keys"); 8 | 9 | var _keys2 = _interopRequireDefault(_keys); 10 | 11 | var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck"); 12 | 13 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 14 | 15 | var _createClass2 = require("babel-runtime/helpers/createClass"); 16 | 17 | var _createClass3 = _interopRequireDefault(_createClass2); 18 | 19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 20 | 21 | var KnobStore = function () { 22 | function KnobStore() { 23 | (0, _classCallCheck3.default)(this, KnobStore); 24 | 25 | this.store = {}; 26 | this.callbacks = []; 27 | } 28 | 29 | (0, _createClass3.default)(KnobStore, [{ 30 | key: "has", 31 | value: function has(key) { 32 | return this.store[key] !== undefined; 33 | } 34 | }, { 35 | key: "set", 36 | value: function set(key, value) { 37 | this.store[key] = value; 38 | this.store[key].used = true; 39 | this.callbacks.forEach(function (cb) { 40 | return cb(); 41 | }); 42 | } 43 | }, { 44 | key: "get", 45 | value: function get(key) { 46 | var knob = this.store[key]; 47 | if (knob) { 48 | knob.used = true; 49 | } 50 | return knob; 51 | } 52 | }, { 53 | key: "getAll", 54 | value: function getAll() { 55 | return this.store; 56 | } 57 | }, { 58 | key: "reset", 59 | value: function reset() { 60 | this.store = {}; 61 | } 62 | }, { 63 | key: "markAllUnused", 64 | value: function markAllUnused() { 65 | var _this = this; 66 | 67 | (0, _keys2.default)(this.store).forEach(function (knobName) { 68 | _this.store[knobName].used = false; 69 | }); 70 | } 71 | }, { 72 | key: "subscribe", 73 | value: function subscribe(cb) { 74 | this.callbacks.push(cb); 75 | } 76 | }, { 77 | key: "unsubscribe", 78 | value: function unsubscribe(cb) { 79 | var index = this.callbacks.indexOf(cb); 80 | this.callbacks.splice(index, 1); 81 | } 82 | }]); 83 | return KnobStore; 84 | }(); 85 | 86 | exports.default = KnobStore; -------------------------------------------------------------------------------- /dist/components/Panel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _extends2 = require('babel-runtime/helpers/extends'); 8 | 9 | var _extends3 = _interopRequireDefault(_extends2); 10 | 11 | var _keys = require('babel-runtime/core-js/object/keys'); 12 | 13 | var _keys2 = _interopRequireDefault(_keys); 14 | 15 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); 16 | 17 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 18 | 19 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 20 | 21 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 22 | 23 | var _createClass2 = require('babel-runtime/helpers/createClass'); 24 | 25 | var _createClass3 = _interopRequireDefault(_createClass2); 26 | 27 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); 28 | 29 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 30 | 31 | var _inherits2 = require('babel-runtime/helpers/inherits'); 32 | 33 | var _inherits3 = _interopRequireDefault(_inherits2); 34 | 35 | var _react = require('react'); 36 | 37 | var _react2 = _interopRequireDefault(_react); 38 | 39 | var _PropForm = require('./PropForm'); 40 | 41 | var _PropForm2 = _interopRequireDefault(_PropForm); 42 | 43 | var _types = require('./types'); 44 | 45 | var _types2 = _interopRequireDefault(_types); 46 | 47 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 48 | 49 | var styles = { 50 | panelWrapper: { 51 | width: '100%' 52 | }, 53 | panel: { 54 | padding: '5px', 55 | width: 'auto', 56 | position: 'relative' 57 | }, 58 | noKnobs: { 59 | fontFamily: '\n -apple-system, ".SFNSText-Regular", "San Francisco", "Roboto",\n "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif\n ', 60 | display: 'inline', 61 | width: '100%', 62 | textAlign: 'center', 63 | color: 'rgb(190, 190, 190)', 64 | padding: '10px' 65 | }, 66 | resetButton: { 67 | position: 'absolute', 68 | bottom: 11, 69 | right: 10, 70 | border: 'none', 71 | borderTop: 'solid 1px rgba(0, 0, 0, 0.2)', 72 | borderLeft: 'solid 1px rgba(0, 0, 0, 0.2)', 73 | background: 'rgba(255, 255, 255, 0.5)', 74 | padding: '5px 10px', 75 | borderRadius: '4px 0 0 0', 76 | color: 'rgba(0, 0, 0, 0.5)', 77 | outline: 'none' 78 | } 79 | }; 80 | 81 | var Panel = function (_React$Component) { 82 | (0, _inherits3.default)(Panel, _React$Component); 83 | 84 | function Panel(props) { 85 | (0, _classCallCheck3.default)(this, Panel); 86 | 87 | var _this = (0, _possibleConstructorReturn3.default)(this, (Panel.__proto__ || (0, _getPrototypeOf2.default)(Panel)).call(this, props)); 88 | 89 | _this.handleChange = _this.handleChange.bind(_this); 90 | _this.setKnobs = _this.setKnobs.bind(_this); 91 | _this.reset = _this.reset.bind(_this); 92 | 93 | _this.state = { knobs: {} }; 94 | _this.loadedFromUrl = false; 95 | _this.props.channel.on('addon:knobs:setKnobs', _this.setKnobs); 96 | return _this; 97 | } 98 | 99 | (0, _createClass3.default)(Panel, [{ 100 | key: 'componentWillUnmount', 101 | value: function componentWillUnmount() { 102 | this.props.channel.removeListener('addon:knobs:setKnobs', this.setKnobs); 103 | } 104 | }, { 105 | key: 'setKnobs', 106 | value: function setKnobs(knobs) { 107 | var _this2 = this; 108 | 109 | var queryParams = {}; 110 | var _props = this.props, 111 | api = _props.api, 112 | channel = _props.channel; 113 | 114 | 115 | (0, _keys2.default)(knobs).forEach(function (name) { 116 | var knob = knobs[name]; 117 | // For the first time, get values from the URL and set them. 118 | if (!_this2.loadedFromUrl) { 119 | var urlValue = api.getQueryParam('knob-' + name); 120 | 121 | if (urlValue !== undefined) { 122 | // If the knob value present in url 123 | knob.value = _types2.default[knob.type].deserialize(urlValue); 124 | channel.emit('addon:knobs:knobChange', knob); 125 | } 126 | } 127 | 128 | queryParams['knob-' + name] = _types2.default[knob.type].serialize(knob.value); 129 | }); 130 | 131 | this.loadedFromUrl = true; 132 | api.setQueryParams(queryParams); 133 | this.setState({ knobs: knobs }); 134 | } 135 | }, { 136 | key: 'reset', 137 | value: function reset() { 138 | this.props.channel.emit('addon:knobs:reset'); 139 | } 140 | }, { 141 | key: 'handleChange', 142 | value: function handleChange(changedKnob) { 143 | var _props2 = this.props, 144 | api = _props2.api, 145 | channel = _props2.channel; 146 | var knobs = this.state.knobs; 147 | var name = changedKnob.name, 148 | type = changedKnob.type, 149 | value = changedKnob.value; 150 | 151 | var newKnobs = (0, _extends3.default)({}, knobs); 152 | newKnobs[name] = (0, _extends3.default)({}, newKnobs[name], changedKnob); 153 | 154 | this.setState({ knobs: newKnobs }); 155 | 156 | var queryParams = {}; 157 | queryParams['knob-' + name] = _types2.default[type].serialize(value); 158 | 159 | api.setQueryParams(queryParams); 160 | channel.emit('addon:knobs:knobChange', changedKnob); 161 | } 162 | }, { 163 | key: 'render', 164 | value: function render() { 165 | var knobs = this.state.knobs; 166 | 167 | var knobsArray = (0, _keys2.default)(knobs).filter(function (key) { 168 | return knobs[key].used; 169 | }).map(function (key) { 170 | return knobs[key]; 171 | }); 172 | 173 | if (knobsArray.length === 0) { 174 | return _react2.default.createElement( 175 | 'div', 176 | { style: styles.noKnobs }, 177 | 'NO KNOBS' 178 | ); 179 | } 180 | 181 | return _react2.default.createElement( 182 | 'div', 183 | { style: styles.panelWrapper }, 184 | _react2.default.createElement( 185 | 'div', 186 | { style: styles.panel }, 187 | _react2.default.createElement(_PropForm2.default, { knobs: knobsArray, onFieldChange: this.handleChange }) 188 | ), 189 | _react2.default.createElement( 190 | 'button', 191 | { style: styles.resetButton, onClick: this.reset }, 192 | 'RESET' 193 | ) 194 | ); 195 | } 196 | }]); 197 | return Panel; 198 | }(_react2.default.Component); 199 | 200 | exports.default = Panel; 201 | 202 | 203 | Panel.propTypes = { 204 | channel: _react2.default.PropTypes.object, 205 | onReset: _react2.default.PropTypes.object, 206 | api: _react2.default.PropTypes.object 207 | }; -------------------------------------------------------------------------------- /dist/components/PropField.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); 8 | 9 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 10 | 11 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 12 | 13 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 14 | 15 | var _createClass2 = require('babel-runtime/helpers/createClass'); 16 | 17 | var _createClass3 = _interopRequireDefault(_createClass2); 18 | 19 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); 20 | 21 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 22 | 23 | var _inherits2 = require('babel-runtime/helpers/inherits'); 24 | 25 | var _inherits3 = _interopRequireDefault(_inherits2); 26 | 27 | var _extends2 = require('babel-runtime/helpers/extends'); 28 | 29 | var _extends3 = _interopRequireDefault(_extends2); 30 | 31 | var _react = require('react'); 32 | 33 | var _react2 = _interopRequireDefault(_react); 34 | 35 | var _types = require('./types'); 36 | 37 | var _types2 = _interopRequireDefault(_types); 38 | 39 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 40 | 41 | var InvalidType = function InvalidType() { 42 | return _react2.default.createElement( 43 | 'span', 44 | null, 45 | 'Invalid Type' 46 | ); 47 | }; 48 | 49 | var stylesheet = { 50 | field: { 51 | display: 'table-row', 52 | padding: '5px' 53 | }, 54 | label: { 55 | display: 'table-cell', 56 | boxSizing: 'border-box', 57 | verticalAlign: 'top', 58 | paddingRight: 5, 59 | paddingTop: 7, 60 | textAlign: 'right', 61 | width: 80, 62 | fontSize: 10, 63 | color: 'rgb(68, 68, 68)', 64 | textTransform: 'uppercase', 65 | fontWeight: 600 66 | } 67 | }; 68 | 69 | stylesheet.textarea = (0, _extends3.default)({}, stylesheet.input, { 70 | height: '100px' 71 | }); 72 | 73 | stylesheet.checkbox = (0, _extends3.default)({}, stylesheet.input, { 74 | width: 'auto' 75 | }); 76 | 77 | var PropField = function (_React$Component) { 78 | (0, _inherits3.default)(PropField, _React$Component); 79 | 80 | function PropField(props) { 81 | (0, _classCallCheck3.default)(this, PropField); 82 | 83 | var _this = (0, _possibleConstructorReturn3.default)(this, (PropField.__proto__ || (0, _getPrototypeOf2.default)(PropField)).call(this, props)); 84 | 85 | _this._onChange = _this.onChange.bind(_this); 86 | return _this; 87 | } 88 | 89 | (0, _createClass3.default)(PropField, [{ 90 | key: 'onChange', 91 | value: function onChange(e) { 92 | this.props.onChange(e.target.value); 93 | } 94 | }, { 95 | key: 'render', 96 | value: function render() { 97 | var _props = this.props, 98 | onChange = _props.onChange, 99 | knob = _props.knob; 100 | 101 | 102 | var InputType = _types2.default[knob.type] || InvalidType; 103 | 104 | return _react2.default.createElement( 105 | 'div', 106 | { style: stylesheet.field }, 107 | _react2.default.createElement( 108 | 'label', 109 | { htmlFor: knob.name, style: stylesheet.label }, 110 | '' + knob.name 111 | ), 112 | _react2.default.createElement(InputType, { 113 | knob: knob, 114 | onChange: onChange 115 | }) 116 | ); 117 | } 118 | }]); 119 | return PropField; 120 | }(_react2.default.Component); 121 | 122 | exports.default = PropField; 123 | 124 | 125 | PropField.propTypes = { 126 | onChange: _react2.default.PropTypes.func.isRequired, 127 | knob: _react2.default.PropTypes.object 128 | }; -------------------------------------------------------------------------------- /dist/components/PropForm.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); 8 | 9 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 10 | 11 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 12 | 13 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 14 | 15 | var _createClass2 = require('babel-runtime/helpers/createClass'); 16 | 17 | var _createClass3 = _interopRequireDefault(_createClass2); 18 | 19 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); 20 | 21 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 22 | 23 | var _inherits2 = require('babel-runtime/helpers/inherits'); 24 | 25 | var _inherits3 = _interopRequireDefault(_inherits2); 26 | 27 | var _react = require('react'); 28 | 29 | var _react2 = _interopRequireDefault(_react); 30 | 31 | var _PropField = require('./PropField'); 32 | 33 | var _PropField2 = _interopRequireDefault(_PropField); 34 | 35 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 36 | 37 | var stylesheet = { 38 | propForm: { 39 | fontFamily: '\n -apple-system, ".SFNSText-Regular", "San Francisco", "Roboto",\n "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif\n ', 40 | display: 'table', 41 | boxSizing: 'border-box', 42 | width: '100%', 43 | borderCollapse: 'separate', 44 | borderSpacing: '5px' 45 | } 46 | }; 47 | 48 | var propForm = function (_React$Component) { 49 | (0, _inherits3.default)(propForm, _React$Component); 50 | 51 | function propForm() { 52 | (0, _classCallCheck3.default)(this, propForm); 53 | 54 | var _this = (0, _possibleConstructorReturn3.default)(this, (propForm.__proto__ || (0, _getPrototypeOf2.default)(propForm)).call(this)); 55 | 56 | _this._onFieldChange = _this.onFieldChange.bind(_this); 57 | return _this; 58 | } 59 | 60 | (0, _createClass3.default)(propForm, [{ 61 | key: 'onFieldChange', 62 | value: function onFieldChange(name, type, value) { 63 | var change = { name: name, type: type, value: value }; 64 | this.props.onFieldChange(change); 65 | } 66 | }, { 67 | key: 'render', 68 | value: function render() { 69 | var _this2 = this; 70 | 71 | var knobs = this.props.knobs; 72 | 73 | return _react2.default.createElement( 74 | 'form', 75 | { style: stylesheet.propForm }, 76 | knobs.map(function (knob) { 77 | return _react2.default.createElement(_PropField2.default, { 78 | key: knob.name, 79 | name: knob.name, 80 | type: knob.type, 81 | value: knob.value, 82 | knob: knob, 83 | onChange: _this2._onFieldChange.bind(null, knob.name, knob.type) 84 | }); 85 | }) 86 | ); 87 | } 88 | }]); 89 | return propForm; 90 | }(_react2.default.Component); 91 | 92 | exports.default = propForm; 93 | 94 | 95 | propForm.displayName = 'propForm'; 96 | 97 | propForm.propTypes = { 98 | knobs: _react2.default.PropTypes.array.isRequired, 99 | onFieldChange: _react2.default.PropTypes.func.isRequired 100 | }; -------------------------------------------------------------------------------- /dist/components/WrapStory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); 8 | 9 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 10 | 11 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 12 | 13 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 14 | 15 | var _createClass2 = require('babel-runtime/helpers/createClass'); 16 | 17 | var _createClass3 = _interopRequireDefault(_createClass2); 18 | 19 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); 20 | 21 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 22 | 23 | var _inherits2 = require('babel-runtime/helpers/inherits'); 24 | 25 | var _inherits3 = _interopRequireDefault(_inherits2); 26 | 27 | var _react = require('react'); 28 | 29 | var _react2 = _interopRequireDefault(_react); 30 | 31 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 32 | 33 | var WrapStory = function (_React$Component) { 34 | (0, _inherits3.default)(WrapStory, _React$Component); 35 | 36 | function WrapStory(props) { 37 | (0, _classCallCheck3.default)(this, WrapStory); 38 | 39 | var _this = (0, _possibleConstructorReturn3.default)(this, (WrapStory.__proto__ || (0, _getPrototypeOf2.default)(WrapStory)).call(this, props)); 40 | 41 | _this.knobChanged = _this.knobChanged.bind(_this); 42 | _this.resetKnobs = _this.resetKnobs.bind(_this); 43 | _this.setPaneKnobs = _this.setPaneKnobs.bind(_this); 44 | _this._knobsAreReset = false; 45 | _this.state = { storyContent: _this.props.initialContent }; 46 | return _this; 47 | } 48 | 49 | (0, _createClass3.default)(WrapStory, [{ 50 | key: 'componentDidMount', 51 | value: function componentDidMount() { 52 | // Watch for changes in knob editor. 53 | this.props.channel.on('addon:knobs:knobChange', this.knobChanged); 54 | // Watch for the reset event and reset knobs. 55 | this.props.channel.on('addon:knobs:reset', this.resetKnobs); 56 | // Watch for any change in the knobStore and set the panel again for those 57 | // changes. 58 | this.props.knobStore.subscribe(this.setPaneKnobs); 59 | // Set knobs in the panel for the first time. 60 | this.setPaneKnobs(); 61 | } 62 | }, { 63 | key: 'componentWillReceiveProps', 64 | value: function componentWillReceiveProps(props) { 65 | this.setState({ storyContent: props.initialContent }); 66 | } 67 | }, { 68 | key: 'componentWillUnmount', 69 | value: function componentWillUnmount() { 70 | this.props.channel.removeListener('addon:knobs:knobChange', this.knobChanged); 71 | this.props.channel.removeListener('addon:knobs:reset', this.resetKnobs); 72 | this.props.knobStore.unsubscribe(this.setPaneKnobs); 73 | } 74 | }, { 75 | key: 'setPaneKnobs', 76 | value: function setPaneKnobs() { 77 | var _props = this.props, 78 | channel = _props.channel, 79 | knobStore = _props.knobStore; 80 | 81 | channel.emit('addon:knobs:setKnobs', knobStore.getAll()); 82 | } 83 | }, { 84 | key: 'knobChanged', 85 | value: function knobChanged(change) { 86 | var name = change.name, 87 | value = change.value; 88 | var _props2 = this.props, 89 | knobStore = _props2.knobStore, 90 | storyFn = _props2.storyFn, 91 | context = _props2.context; 92 | // Update the related knob and it's value. 93 | 94 | var knobOptions = knobStore.get(name); 95 | knobOptions.value = value; 96 | knobStore.markAllUnused(); 97 | this.setState({ storyContent: storyFn(context) }); 98 | } 99 | }, { 100 | key: 'resetKnobs', 101 | value: function resetKnobs() { 102 | var _props3 = this.props, 103 | knobStore = _props3.knobStore, 104 | storyFn = _props3.storyFn, 105 | context = _props3.context; 106 | 107 | knobStore.reset(); 108 | this.setState({ storyContent: storyFn(context) }); 109 | this.setPaneKnobs(); 110 | } 111 | }, { 112 | key: 'render', 113 | value: function render() { 114 | return this.state.storyContent; 115 | } 116 | }]); 117 | return WrapStory; 118 | }(_react2.default.Component); 119 | 120 | exports.default = WrapStory; 121 | 122 | 123 | WrapStory.propTypes = { 124 | context: _react2.default.PropTypes.object, 125 | storyFn: _react2.default.PropTypes.func, 126 | channel: _react2.default.PropTypes.object, 127 | knobStore: _react2.default.PropTypes.object, 128 | initialContent: _react2.default.PropTypes.object 129 | }; -------------------------------------------------------------------------------- /dist/components/types/Array.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); 8 | 9 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 10 | 11 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 12 | 13 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 14 | 15 | var _createClass2 = require('babel-runtime/helpers/createClass'); 16 | 17 | var _createClass3 = _interopRequireDefault(_createClass2); 18 | 19 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); 20 | 21 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 22 | 23 | var _inherits2 = require('babel-runtime/helpers/inherits'); 24 | 25 | var _inherits3 = _interopRequireDefault(_inherits2); 26 | 27 | var _react = require('react'); 28 | 29 | var _react2 = _interopRequireDefault(_react); 30 | 31 | var _reactTextareaAutosize = require('react-textarea-autosize'); 32 | 33 | var _reactTextareaAutosize2 = _interopRequireDefault(_reactTextareaAutosize); 34 | 35 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 36 | 37 | var styles = { 38 | display: 'table-cell', 39 | boxSizing: 'border-box', 40 | verticalAlign: 'middle', 41 | height: '26px', 42 | width: '100%', 43 | outline: 'none', 44 | border: '1px solid #f7f4f4', 45 | borderRadius: 2, 46 | fontSize: 11, 47 | padding: '5px', 48 | color: '#555' 49 | }; 50 | 51 | var ArrayType = function (_React$Component) { 52 | (0, _inherits3.default)(ArrayType, _React$Component); 53 | 54 | function ArrayType() { 55 | (0, _classCallCheck3.default)(this, ArrayType); 56 | return (0, _possibleConstructorReturn3.default)(this, (ArrayType.__proto__ || (0, _getPrototypeOf2.default)(ArrayType)).apply(this, arguments)); 57 | } 58 | 59 | (0, _createClass3.default)(ArrayType, [{ 60 | key: 'render', 61 | value: function render() { 62 | var _props = this.props, 63 | knob = _props.knob, 64 | _onChange = _props.onChange; 65 | 66 | return _react2.default.createElement(_reactTextareaAutosize2.default, { 67 | id: knob.name, 68 | ref: 'input', 69 | style: styles, 70 | value: knob.value.join(knob.separator), 71 | onChange: function onChange(e) { 72 | return _onChange(e.target.value.split(knob.separator)); 73 | } 74 | }); 75 | } 76 | }]); 77 | return ArrayType; 78 | }(_react2.default.Component); 79 | 80 | ArrayType.propTypes = { 81 | knob: _react2.default.PropTypes.object, 82 | onChange: _react2.default.PropTypes.func 83 | }; 84 | 85 | ArrayType.serialize = function (value) { 86 | return value; 87 | }; 88 | 89 | ArrayType.deserialize = function (value) { 90 | return value; 91 | }; 92 | 93 | exports.default = ArrayType; -------------------------------------------------------------------------------- /dist/components/types/Boolean.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); 8 | 9 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 10 | 11 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 12 | 13 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 14 | 15 | var _createClass2 = require('babel-runtime/helpers/createClass'); 16 | 17 | var _createClass3 = _interopRequireDefault(_createClass2); 18 | 19 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); 20 | 21 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 22 | 23 | var _inherits2 = require('babel-runtime/helpers/inherits'); 24 | 25 | var _inherits3 = _interopRequireDefault(_inherits2); 26 | 27 | var _react = require('react'); 28 | 29 | var _react2 = _interopRequireDefault(_react); 30 | 31 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 32 | 33 | var styles = { 34 | display: 'table-cell', 35 | boxSizing: 'border-box', 36 | verticalAlign: 'top', 37 | height: 21, 38 | outline: 'none', 39 | border: '1px solid #ececec', 40 | fontSize: '12px', 41 | color: '#555' 42 | }; 43 | 44 | var BooleanType = function (_React$Component) { 45 | (0, _inherits3.default)(BooleanType, _React$Component); 46 | 47 | function BooleanType() { 48 | (0, _classCallCheck3.default)(this, BooleanType); 49 | return (0, _possibleConstructorReturn3.default)(this, (BooleanType.__proto__ || (0, _getPrototypeOf2.default)(BooleanType)).apply(this, arguments)); 50 | } 51 | 52 | (0, _createClass3.default)(BooleanType, [{ 53 | key: 'render', 54 | value: function render() { 55 | var _this2 = this; 56 | 57 | var _props = this.props, 58 | knob = _props.knob, 59 | _onChange = _props.onChange; 60 | 61 | 62 | return _react2.default.createElement('input', { 63 | id: knob.name, 64 | ref: 'input', 65 | style: styles, 66 | type: 'checkbox', 67 | onChange: function onChange() { 68 | return _onChange(_this2.refs.input.checked); 69 | }, 70 | checked: knob.value 71 | }); 72 | } 73 | }]); 74 | return BooleanType; 75 | }(_react2.default.Component); 76 | 77 | BooleanType.propTypes = { 78 | knob: _react2.default.PropTypes.object, 79 | onChange: _react2.default.PropTypes.func 80 | }; 81 | 82 | BooleanType.serialize = function (value) { 83 | return String(value); 84 | }; 85 | 86 | BooleanType.deserialize = function (value) { 87 | if (!value) return false; 88 | return value.trim() === 'true'; 89 | }; 90 | 91 | exports.default = BooleanType; -------------------------------------------------------------------------------- /dist/components/types/Color.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); 8 | 9 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 10 | 11 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 12 | 13 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 14 | 15 | var _createClass2 = require('babel-runtime/helpers/createClass'); 16 | 17 | var _createClass3 = _interopRequireDefault(_createClass2); 18 | 19 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); 20 | 21 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 22 | 23 | var _inherits2 = require('babel-runtime/helpers/inherits'); 24 | 25 | var _inherits3 = _interopRequireDefault(_inherits2); 26 | 27 | var _react = require('react'); 28 | 29 | var _react2 = _interopRequireDefault(_react); 30 | 31 | var _reactColor = require('react-color'); 32 | 33 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 34 | 35 | var styles = { 36 | swatch: { 37 | background: '#fff', 38 | borderRadius: '1px', 39 | border: '1px solid rgb(247, 244, 244)', 40 | display: 'inline-block', 41 | cursor: 'pointer', 42 | width: '100%' 43 | }, 44 | popover: { 45 | position: 'absolute', 46 | zIndex: '2' 47 | }, 48 | cover: { 49 | position: 'fixed', 50 | top: '0px', 51 | right: '0px', 52 | bottom: '0px', 53 | left: '0px' 54 | } 55 | }; 56 | 57 | var ColorType = function (_React$Component) { 58 | (0, _inherits3.default)(ColorType, _React$Component); 59 | 60 | function ColorType(props) { 61 | (0, _classCallCheck3.default)(this, ColorType); 62 | 63 | var _this = (0, _possibleConstructorReturn3.default)(this, (ColorType.__proto__ || (0, _getPrototypeOf2.default)(ColorType)).call(this, props)); 64 | 65 | _this.handleClick = _this.handleClick.bind(_this); 66 | _this.onWindowMouseDown = _this.onWindowMouseDown.bind(_this); 67 | _this.state = { 68 | displayColorPicker: false 69 | }; 70 | return _this; 71 | } 72 | 73 | (0, _createClass3.default)(ColorType, [{ 74 | key: 'componentDidMount', 75 | value: function componentDidMount() { 76 | document.addEventListener('mousedown', this.onWindowMouseDown); 77 | } 78 | }, { 79 | key: 'componentWillUnmount', 80 | value: function componentWillUnmount() { 81 | document.removeEventListener('mousedown', this.onWindowMouseDown); 82 | } 83 | }, { 84 | key: 'onWindowMouseDown', 85 | value: function onWindowMouseDown(e) { 86 | if (!this.state.displayColorPicker) return; 87 | if (this.popover.contains(e.target)) return; 88 | 89 | this.setState({ 90 | displayColorPicker: false 91 | }); 92 | } 93 | }, { 94 | key: 'handleClick', 95 | value: function handleClick() { 96 | this.setState({ 97 | displayColorPicker: !this.state.displayColorPicker 98 | }); 99 | } 100 | }, { 101 | key: 'render', 102 | value: function render() { 103 | var _this2 = this; 104 | 105 | var _props = this.props, 106 | knob = _props.knob, 107 | _onChange = _props.onChange; 108 | 109 | var colorStyle = { 110 | width: 'auto', 111 | height: '20px', 112 | borderRadius: '2px', 113 | margin: 5, 114 | background: knob.value 115 | }; 116 | return _react2.default.createElement( 117 | 'div', 118 | { id: knob.name }, 119 | _react2.default.createElement( 120 | 'div', 121 | { style: styles.swatch, onClick: this.handleClick }, 122 | _react2.default.createElement('div', { style: colorStyle }) 123 | ), 124 | this.state.displayColorPicker ? _react2.default.createElement( 125 | 'div', 126 | { style: styles.popover, ref: function ref(e) { 127 | _this2.popover = e; 128 | } }, 129 | _react2.default.createElement(_reactColor.SketchPicker, { color: knob.value, onChange: function onChange(color) { 130 | return _onChange(color.hex); 131 | } }) 132 | ) : null 133 | ); 134 | } 135 | }]); 136 | return ColorType; 137 | }(_react2.default.Component); 138 | 139 | ColorType.propTypes = { 140 | knob: _react2.default.PropTypes.object, 141 | onChange: _react2.default.PropTypes.func 142 | }; 143 | 144 | ColorType.serialize = function (value) { 145 | return value; 146 | }; 147 | 148 | ColorType.deserialize = function (value) { 149 | return value; 150 | }; 151 | 152 | exports.default = ColorType; -------------------------------------------------------------------------------- /dist/components/types/Date/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); 8 | 9 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 10 | 11 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 12 | 13 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 14 | 15 | var _createClass2 = require('babel-runtime/helpers/createClass'); 16 | 17 | var _createClass3 = _interopRequireDefault(_createClass2); 18 | 19 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); 20 | 21 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 22 | 23 | var _inherits2 = require('babel-runtime/helpers/inherits'); 24 | 25 | var _inherits3 = _interopRequireDefault(_inherits2); 26 | 27 | var _react = require('react'); 28 | 29 | var _react2 = _interopRequireDefault(_react); 30 | 31 | var _reactDatetime = require('react-datetime'); 32 | 33 | var _reactDatetime2 = _interopRequireDefault(_reactDatetime); 34 | 35 | var _insertCss = require('insert-css'); 36 | 37 | var _insertCss2 = _interopRequireDefault(_insertCss); 38 | 39 | var _styles = require('./styles'); 40 | 41 | var _styles2 = _interopRequireDefault(_styles); 42 | 43 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 44 | 45 | var customStyle = '\n .rdt input {\n outline: 0;\n width: 100%;\n border: 1px solid #f7f4f4;\n border-radius: 2px;\n font-size: 11px;\n padding: 5px;\n color: #555;\n display: table-cell;\n box-sizing: border-box;\n }\n'; 46 | 47 | (0, _insertCss2.default)(_styles2.default); 48 | (0, _insertCss2.default)(customStyle); 49 | 50 | var DateType = function (_React$Component) { 51 | (0, _inherits3.default)(DateType, _React$Component); 52 | 53 | function DateType() { 54 | (0, _classCallCheck3.default)(this, DateType); 55 | return (0, _possibleConstructorReturn3.default)(this, (DateType.__proto__ || (0, _getPrototypeOf2.default)(DateType)).apply(this, arguments)); 56 | } 57 | 58 | (0, _createClass3.default)(DateType, [{ 59 | key: 'render', 60 | value: function render() { 61 | var _props = this.props, 62 | knob = _props.knob, 63 | _onChange = _props.onChange; 64 | 65 | return _react2.default.createElement( 66 | 'div', 67 | null, 68 | _react2.default.createElement(_reactDatetime2.default, { 69 | id: knob.name, 70 | value: knob.value ? new Date(knob.value) : null, 71 | type: 'date', 72 | onChange: function onChange(date) { 73 | return _onChange(date.valueOf()); 74 | } 75 | }) 76 | ); 77 | } 78 | }]); 79 | return DateType; 80 | }(_react2.default.Component); 81 | 82 | DateType.propTypes = { 83 | knob: _react2.default.PropTypes.object, 84 | onChange: _react2.default.PropTypes.func 85 | }; 86 | 87 | DateType.serialize = function (value) { 88 | return String(value); 89 | }; 90 | 91 | DateType.deserialize = function (value) { 92 | return parseFloat(value); 93 | }; 94 | 95 | exports.default = DateType; -------------------------------------------------------------------------------- /dist/components/types/Date/styles.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = "\n .rdt {\n position: relative;\n }\n .rdtPicker {\n display: none;\n position: absolute;\n width: 200px;\n padding: 4px;\n margin-top: 1px;\n z-index: 99999 !important;\n background: #fff;\n box-shadow: 0 1px 3px rgba(0,0,0,.1);\n border: 1px solid #f9f9f9;\n }\n .rdtOpen .rdtPicker {\n display: block;\n }\n .rdtStatic .rdtPicker {\n box-shadow: none;\n position: static;\n }\n\n .rdtPicker .rdtTimeToggle {\n text-align: center;\n font-size:11px;\n }\n\n .rdtPicker table {\n width: 100%;\n margin: 0;\n }\n .rdtPicker td,\n .rdtPicker th {\n text-align: center;\n height: 28px;\n }\n .rdtPicker td {\n cursor: pointer;\n }\n .rdtPicker td.rdtDay:hover,\n .rdtPicker td.rdtHour:hover,\n .rdtPicker td.rdtMinute:hover,\n .rdtPicker td.rdtSecond:hover,\n .rdtPicker .rdtTimeToggle:hover {\n background: #eeeeee;\n cursor: pointer;\n }\n .rdtPicker td.rdtOld,\n .rdtPicker td.rdtNew {\n color: #999999;\n }\n .rdtPicker td.rdtToday {\n position: relative;\n }\n .rdtPicker td.rdtToday:before {\n content: '';\n display: inline-block;\n border-left: 7px solid transparent;\n border-bottom: 7px solid #428bca;\n border-top-color: rgba(0, 0, 0, 0.2);\n position: absolute;\n bottom: 4px;\n right: 4px;\n }\n .rdtPicker td.rdtActive,\n .rdtPicker td.rdtActive:hover {\n background-color: #428bca;\n color: #fff;\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n }\n .rdtPicker td.rdtActive.rdtToday:before {\n border-bottom-color: #fff;\n }\n .rdtPicker td.rdtDisabled,\n .rdtPicker td.rdtDisabled:hover {\n background: none;\n color: #999999;\n cursor: not-allowed;\n }\n\n .rdtPicker td span.rdtOld {\n color: #999999;\n }\n .rdtPicker td span.rdtDisabled,\n .rdtPicker td span.rdtDisabled:hover {\n background: none;\n color: #999999;\n cursor: not-allowed;\n }\n .rdtPicker th {\n border-bottom: 1px solid #f9f9f9;\n }\n .rdtPicker .dow {\n width: 14.2857%;\n font-size: 11px;\n border-bottom: none;\n }\n .rdtPicker th.rdtSwitch {\n width: 100px;\n font-size: 11px;\n }\n .rdtPicker th.rdtNext,\n .rdtPicker th.rdtPrev {\n font-size: 11px;\n vertical-align: top;\n }\n\n .rdtPrev span,\n .rdtNext span {\n display: block;\n -webkit-touch-callout: none; /* iOS Safari */\n -webkit-user-select: none; /* Chrome/Safari/Opera */\n -khtml-user-select: none; /* Konqueror */\n -moz-user-select: none; /* Firefox */\n -ms-user-select: none; /* Internet Explorer/Edge */\n user-select: none;\n }\n\n .rdtPicker th.rdtDisabled,\n .rdtPicker th.rdtDisabled:hover {\n background: none;\n color: #999999;\n cursor: not-allowed;\n }\n .rdtPicker thead tr:first-child th {\n cursor: pointer;\n }\n .rdtPicker thead tr:first-child th:hover {\n background: #eeeeee;\n }\n\n .rdtPicker tfoot {\n border-top: 1px solid #f9f9f9;\n }\n\n .rdtPicker button {\n border: none;\n background: none;\n cursor: pointer;\n }\n .rdtPicker button:hover {\n background-color: #eee;\n }\n\n .rdtPicker thead button {\n width: 100%;\n height: 100%;\n }\n\n td.rdtMonth,\n td.rdtYear {\n height: 50px;\n width: 25%;\n cursor: pointer;\n }\n td.rdtMonth:hover,\n td.rdtYear:hover {\n background: #eee;\n }\n\n td.rdtDay {\n font-size: 11px\n }\n\n .rdtCounters {\n display: inline-block;\n }\n\n .rdtCounters > div {\n float: left;\n }\n\n .rdtCounter {\n height: 100px;\n }\n\n .rdtCounter {\n width: 40px;\n }\n\n .rdtCounterSeparator {\n line-height: 100px;\n }\n\n .rdtCounter .rdtBtn {\n height: 40%;\n line-height: 40px;\n cursor: pointer;\n display: block;\n font-size: 11px;\n\n -webkit-touch-callout: none; /* iOS Safari */\n -webkit-user-select: none; /* Chrome/Safari/Opera */\n -khtml-user-select: none; /* Konqueror */\n -moz-user-select: none; /* Firefox */\n -ms-user-select: none; /* Internet Explorer/Edge */\n user-select: none;\n }\n .rdtCounter .rdtBtn:hover {\n background: #eee;\n }\n .rdtCounter .rdtCount {\n height: 20%;\n font-size: 11px;\n }\n\n .rdtMilli {\n vertical-align: middle;\n padding-left: 8px;\n width: 48px;\n }\n\n .rdtMilli input {\n width: 100%;\n font-size: 11px;\n margin-top: 37px;\n }\n"; -------------------------------------------------------------------------------- /dist/components/types/Number.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); 8 | 9 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 10 | 11 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 12 | 13 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 14 | 15 | var _createClass2 = require('babel-runtime/helpers/createClass'); 16 | 17 | var _createClass3 = _interopRequireDefault(_createClass2); 18 | 19 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); 20 | 21 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 22 | 23 | var _inherits2 = require('babel-runtime/helpers/inherits'); 24 | 25 | var _inherits3 = _interopRequireDefault(_inherits2); 26 | 27 | var _react = require('react'); 28 | 29 | var _react2 = _interopRequireDefault(_react); 30 | 31 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 32 | 33 | var styles = { 34 | display: 'table-cell', 35 | boxSizing: 'border-box', 36 | verticalAlign: 'middle', 37 | height: '26px', 38 | width: '100%', 39 | outline: 'none', 40 | border: '1px solid #f7f4f4', 41 | borderRadius: 2, 42 | fontSize: 11, 43 | padding: '5px', 44 | color: '#444' 45 | }; 46 | 47 | var NumberType = function (_React$Component) { 48 | (0, _inherits3.default)(NumberType, _React$Component); 49 | 50 | function NumberType(props) { 51 | (0, _classCallCheck3.default)(this, NumberType); 52 | 53 | var _this = (0, _possibleConstructorReturn3.default)(this, (NumberType.__proto__ || (0, _getPrototypeOf2.default)(NumberType)).call(this, props)); 54 | 55 | _this.renderNormal = _this.renderNormal.bind(_this); 56 | _this.renderRange = _this.renderRange.bind(_this); 57 | return _this; 58 | } 59 | 60 | (0, _createClass3.default)(NumberType, [{ 61 | key: 'renderNormal', 62 | value: function renderNormal() { 63 | var _this2 = this; 64 | 65 | var _props = this.props, 66 | knob = _props.knob, 67 | _onChange = _props.onChange; 68 | 69 | 70 | return _react2.default.createElement('input', { 71 | id: knob.name, 72 | ref: 'input', 73 | style: styles, 74 | value: knob.value, 75 | type: 'number', 76 | onChange: function onChange() { 77 | return _onChange(parseFloat(_this2.refs.input.value)); 78 | } 79 | }); 80 | } 81 | }, { 82 | key: 'renderRange', 83 | value: function renderRange() { 84 | var _this3 = this; 85 | 86 | var _props2 = this.props, 87 | knob = _props2.knob, 88 | _onChange2 = _props2.onChange; 89 | 90 | 91 | return _react2.default.createElement('input', { 92 | id: knob.name, 93 | ref: 'input', 94 | style: styles, 95 | value: knob.value, 96 | type: 'range', 97 | min: knob.min, 98 | max: knob.max, 99 | step: knob.step, 100 | onChange: function onChange() { 101 | return _onChange2(parseFloat(_this3.refs.input.value)); 102 | } 103 | }); 104 | } 105 | }, { 106 | key: 'render', 107 | value: function render() { 108 | var knob = this.props.knob; 109 | 110 | 111 | return knob.range ? this.renderRange() : this.renderNormal(); 112 | } 113 | }]); 114 | return NumberType; 115 | }(_react2.default.Component); 116 | 117 | NumberType.propTypes = { 118 | knob: _react2.default.PropTypes.object, 119 | onChange: _react2.default.PropTypes.func 120 | }; 121 | 122 | NumberType.serialize = function (value) { 123 | return String(value); 124 | }; 125 | 126 | NumberType.deserialize = function (value) { 127 | return parseFloat(value); 128 | }; 129 | 130 | exports.default = NumberType; -------------------------------------------------------------------------------- /dist/components/types/Object.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _extends2 = require('babel-runtime/helpers/extends'); 8 | 9 | var _extends3 = _interopRequireDefault(_extends2); 10 | 11 | var _stringify = require('babel-runtime/core-js/json/stringify'); 12 | 13 | var _stringify2 = _interopRequireDefault(_stringify); 14 | 15 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); 16 | 17 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 18 | 19 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 20 | 21 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 22 | 23 | var _createClass2 = require('babel-runtime/helpers/createClass'); 24 | 25 | var _createClass3 = _interopRequireDefault(_createClass2); 26 | 27 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); 28 | 29 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 30 | 31 | var _inherits2 = require('babel-runtime/helpers/inherits'); 32 | 33 | var _inherits3 = _interopRequireDefault(_inherits2); 34 | 35 | var _react = require('react'); 36 | 37 | var _react2 = _interopRequireDefault(_react); 38 | 39 | var _reactTextareaAutosize = require('react-textarea-autosize'); 40 | 41 | var _reactTextareaAutosize2 = _interopRequireDefault(_reactTextareaAutosize); 42 | 43 | var _deepEqual = require('deep-equal'); 44 | 45 | var _deepEqual2 = _interopRequireDefault(_deepEqual); 46 | 47 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 48 | 49 | var styles = { 50 | display: 'table-cell', 51 | boxSizing: 'border-box', 52 | verticalAlign: 'middle', 53 | width: '100%', 54 | outline: 'none', 55 | border: '1px solid #f7f4f4', 56 | borderRadius: 2, 57 | fontSize: 11, 58 | padding: '5px', 59 | color: '#555', 60 | fontFamily: 'monospace' 61 | }; 62 | 63 | var ObjectType = function (_React$Component) { 64 | (0, _inherits3.default)(ObjectType, _React$Component); 65 | 66 | function ObjectType() { 67 | var _ref; 68 | 69 | (0, _classCallCheck3.default)(this, ObjectType); 70 | 71 | for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 72 | args[_key] = arguments[_key]; 73 | } 74 | 75 | var _this = (0, _possibleConstructorReturn3.default)(this, (_ref = ObjectType.__proto__ || (0, _getPrototypeOf2.default)(ObjectType)).call.apply(_ref, [this].concat(args))); 76 | 77 | _this.state = {}; 78 | return _this; 79 | } 80 | 81 | (0, _createClass3.default)(ObjectType, [{ 82 | key: 'getJSONString', 83 | value: function getJSONString() { 84 | var _state = this.state, 85 | json = _state.json, 86 | jsonString = _state.jsonString; 87 | var knob = this.props.knob; 88 | 89 | // If there is an error in the JSON, we need to give that errored JSON. 90 | 91 | if (this.failed) return jsonString; 92 | 93 | // If the editor value and the knob value is the same, we need to return the 94 | // editor value as it allow user to add new fields to the JSON. 95 | if ((0, _deepEqual2.default)(json, knob.value)) return jsonString; 96 | 97 | // If the knob's value is different from the editor, it seems like 98 | // there's a outside change and we need to get that. 99 | return (0, _stringify2.default)(knob.value, null, 2); 100 | } 101 | }, { 102 | key: 'handleChange', 103 | value: function handleChange(e) { 104 | var onChange = this.props.onChange; 105 | 106 | var newState = { 107 | jsonString: e.target.value 108 | }; 109 | 110 | try { 111 | newState.json = JSON.parse(e.target.value.trim()); 112 | onChange(newState.json); 113 | this.failed = false; 114 | } catch (err) { 115 | this.failed = true; 116 | } 117 | 118 | this.setState(newState); 119 | } 120 | }, { 121 | key: 'render', 122 | value: function render() { 123 | var _this2 = this; 124 | 125 | var knob = this.props.knob; 126 | 127 | var jsonString = this.getJSONString(); 128 | var extraStyle = {}; 129 | 130 | if (this.failed) { 131 | extraStyle.border = '1px solid #fadddd'; 132 | extraStyle.backgroundColor = '#fff5f5'; 133 | } 134 | 135 | return _react2.default.createElement(_reactTextareaAutosize2.default, { 136 | id: knob.name, 137 | ref: 'input', 138 | style: (0, _extends3.default)({}, styles, extraStyle), 139 | value: jsonString, 140 | onChange: function onChange(e) { 141 | return _this2.handleChange(e); 142 | } 143 | }); 144 | } 145 | }]); 146 | return ObjectType; 147 | }(_react2.default.Component); 148 | 149 | ObjectType.propTypes = { 150 | knob: _react2.default.PropTypes.object, 151 | onChange: _react2.default.PropTypes.func 152 | }; 153 | 154 | ObjectType.serialize = function (object) { 155 | return (0, _stringify2.default)(object); 156 | }; 157 | 158 | ObjectType.deserialize = function (value) { 159 | if (!value) return {}; 160 | return JSON.parse(value); 161 | }; 162 | 163 | exports.default = ObjectType; -------------------------------------------------------------------------------- /dist/components/types/Select.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _keys = require('babel-runtime/core-js/object/keys'); 8 | 9 | var _keys2 = _interopRequireDefault(_keys); 10 | 11 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); 12 | 13 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 14 | 15 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 16 | 17 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 18 | 19 | var _createClass2 = require('babel-runtime/helpers/createClass'); 20 | 21 | var _createClass3 = _interopRequireDefault(_createClass2); 22 | 23 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); 24 | 25 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 26 | 27 | var _inherits2 = require('babel-runtime/helpers/inherits'); 28 | 29 | var _inherits3 = _interopRequireDefault(_inherits2); 30 | 31 | var _react = require('react'); 32 | 33 | var _react2 = _interopRequireDefault(_react); 34 | 35 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 36 | 37 | var styles = { 38 | display: 'table-cell', 39 | boxSizing: 'border-box', 40 | verticalAlign: 'middle', 41 | height: '26px', 42 | width: '100%', 43 | outline: 'none', 44 | border: '1px solid #f7f4f4', 45 | borderRadius: 2, 46 | fontSize: 11, 47 | padding: '5px', 48 | color: '#555' 49 | }; 50 | 51 | var SelectType = function (_React$Component) { 52 | (0, _inherits3.default)(SelectType, _React$Component); 53 | 54 | function SelectType() { 55 | (0, _classCallCheck3.default)(this, SelectType); 56 | return (0, _possibleConstructorReturn3.default)(this, (SelectType.__proto__ || (0, _getPrototypeOf2.default)(SelectType)).apply(this, arguments)); 57 | } 58 | 59 | (0, _createClass3.default)(SelectType, [{ 60 | key: '_makeOpt', 61 | value: function _makeOpt(key, val) { 62 | var opts = { 63 | key: key, 64 | value: key 65 | }; 66 | 67 | return _react2.default.createElement( 68 | 'option', 69 | opts, 70 | val 71 | ); 72 | } 73 | }, { 74 | key: '_options', 75 | value: function _options(values) { 76 | var _this2 = this; 77 | 78 | var data = []; 79 | if (Array.isArray(values)) { 80 | data = values.map(function (val) { 81 | return _this2._makeOpt(val, val); 82 | }); 83 | } else { 84 | data = (0, _keys2.default)(values).map(function (key) { 85 | return _this2._makeOpt(key, values[key]); 86 | }); 87 | } 88 | 89 | return data; 90 | } 91 | }, { 92 | key: 'render', 93 | value: function render() { 94 | var _props = this.props, 95 | knob = _props.knob, 96 | _onChange = _props.onChange; 97 | 98 | 99 | return _react2.default.createElement( 100 | 'select', 101 | { 102 | id: knob.name, 103 | ref: 'input', 104 | style: styles, 105 | value: knob.value, 106 | onChange: function onChange(e) { 107 | return _onChange(e.target.value); 108 | } 109 | }, 110 | this._options(knob.options) 111 | ); 112 | } 113 | }]); 114 | return SelectType; 115 | }(_react2.default.Component); 116 | 117 | SelectType.propTypes = { 118 | knob: _react2.default.PropTypes.object, 119 | onChange: _react2.default.PropTypes.func 120 | }; 121 | 122 | SelectType.serialize = function (value) { 123 | return value; 124 | }; 125 | 126 | SelectType.deserialize = function (value) { 127 | return value; 128 | }; 129 | 130 | exports.default = SelectType; -------------------------------------------------------------------------------- /dist/components/types/Text.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); 8 | 9 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 10 | 11 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 12 | 13 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 14 | 15 | var _createClass2 = require('babel-runtime/helpers/createClass'); 16 | 17 | var _createClass3 = _interopRequireDefault(_createClass2); 18 | 19 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); 20 | 21 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 22 | 23 | var _inherits2 = require('babel-runtime/helpers/inherits'); 24 | 25 | var _inherits3 = _interopRequireDefault(_inherits2); 26 | 27 | var _react = require('react'); 28 | 29 | var _react2 = _interopRequireDefault(_react); 30 | 31 | var _reactTextareaAutosize = require('react-textarea-autosize'); 32 | 33 | var _reactTextareaAutosize2 = _interopRequireDefault(_reactTextareaAutosize); 34 | 35 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 36 | 37 | var styles = { 38 | display: 'table-cell', 39 | boxSizing: 'border-box', 40 | verticalAlign: 'middle', 41 | height: '26px', 42 | width: '100%', 43 | outline: 'none', 44 | border: '1px solid #f7f4f4', 45 | borderRadius: 2, 46 | fontSize: 11, 47 | padding: '5px', 48 | color: '#555' 49 | }; 50 | 51 | var TextType = function (_React$Component) { 52 | (0, _inherits3.default)(TextType, _React$Component); 53 | 54 | function TextType() { 55 | (0, _classCallCheck3.default)(this, TextType); 56 | return (0, _possibleConstructorReturn3.default)(this, (TextType.__proto__ || (0, _getPrototypeOf2.default)(TextType)).apply(this, arguments)); 57 | } 58 | 59 | (0, _createClass3.default)(TextType, [{ 60 | key: 'render', 61 | value: function render() { 62 | var _props = this.props, 63 | knob = _props.knob, 64 | _onChange = _props.onChange; 65 | 66 | 67 | return _react2.default.createElement(_reactTextareaAutosize2.default, { 68 | id: knob.name, 69 | ref: 'input', 70 | style: styles, 71 | value: knob.value, 72 | onChange: function onChange(e) { 73 | return _onChange(e.target.value); 74 | } 75 | }); 76 | } 77 | }]); 78 | return TextType; 79 | }(_react2.default.Component); 80 | 81 | TextType.propTypes = { 82 | knob: _react2.default.PropTypes.object, 83 | onChange: _react2.default.PropTypes.func 84 | }; 85 | 86 | TextType.serialize = function (value) { 87 | return value; 88 | }; 89 | 90 | TextType.deserialize = function (value) { 91 | return value; 92 | }; 93 | 94 | exports.default = TextType; -------------------------------------------------------------------------------- /dist/components/types/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _Text = require('./Text'); 8 | 9 | var _Text2 = _interopRequireDefault(_Text); 10 | 11 | var _Number = require('./Number'); 12 | 13 | var _Number2 = _interopRequireDefault(_Number); 14 | 15 | var _Color = require('./Color'); 16 | 17 | var _Color2 = _interopRequireDefault(_Color); 18 | 19 | var _Boolean = require('./Boolean'); 20 | 21 | var _Boolean2 = _interopRequireDefault(_Boolean); 22 | 23 | var _Object = require('./Object'); 24 | 25 | var _Object2 = _interopRequireDefault(_Object); 26 | 27 | var _Select = require('./Select'); 28 | 29 | var _Select2 = _interopRequireDefault(_Select); 30 | 31 | var _Array = require('./Array'); 32 | 33 | var _Array2 = _interopRequireDefault(_Array); 34 | 35 | var _Date = require('./Date'); 36 | 37 | var _Date2 = _interopRequireDefault(_Date); 38 | 39 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 40 | 41 | exports.default = { 42 | text: _Text2.default, 43 | number: _Number2.default, 44 | color: _Color2.default, 45 | boolean: _Boolean2.default, 46 | object: _Object2.default, 47 | select: _Select2.default, 48 | array: _Array2.default, 49 | date: _Date2.default 50 | }; -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _extends2 = require('babel-runtime/helpers/extends'); 8 | 9 | var _extends3 = _interopRequireDefault(_extends2); 10 | 11 | exports.knob = knob; 12 | exports.text = text; 13 | exports.boolean = boolean; 14 | exports.number = number; 15 | exports.color = color; 16 | exports.object = object; 17 | exports.select = select; 18 | exports.array = array; 19 | exports.date = date; 20 | exports.withKnobs = withKnobs; 21 | 22 | var _storybookAddons = require('@kadira/storybook-addons'); 23 | 24 | var _storybookAddons2 = _interopRequireDefault(_storybookAddons); 25 | 26 | var _KnobManager = require('./KnobManager'); 27 | 28 | var _KnobManager2 = _interopRequireDefault(_KnobManager); 29 | 30 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 31 | 32 | var manager = new _KnobManager2.default(); 33 | 34 | function knob(name, options) { 35 | return manager.knob(name, options); 36 | } 37 | 38 | function text(name, value) { 39 | return manager.knob(name, { type: 'text', value: value }); 40 | } 41 | 42 | function boolean(name, value) { 43 | return manager.knob(name, { type: 'boolean', value: value }); 44 | } 45 | 46 | function number(name, value) { 47 | var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 48 | 49 | var defaults = { 50 | range: false, 51 | min: 0, 52 | max: 10, 53 | step: 1 54 | }; 55 | 56 | var mergedOptions = (0, _extends3.default)({}, defaults, options); 57 | 58 | var finalOptions = (0, _extends3.default)({}, mergedOptions, { 59 | type: 'number', 60 | value: value 61 | }); 62 | 63 | return manager.knob(name, finalOptions); 64 | } 65 | 66 | function color(name, value) { 67 | return manager.knob(name, { type: 'color', value: value }); 68 | } 69 | 70 | function object(name, value) { 71 | return manager.knob(name, { type: 'object', value: value }); 72 | } 73 | 74 | function select(name, options, value) { 75 | return manager.knob(name, { type: 'select', options: options, value: value }); 76 | } 77 | 78 | function array(name, value) { 79 | var separator = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ','; 80 | 81 | return manager.knob(name, { type: 'array', value: value, separator: separator }); 82 | } 83 | 84 | function date(name) { 85 | var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Date(); 86 | 87 | var proxyValue = value ? value.getTime() : null; 88 | return manager.knob(name, { type: 'date', value: proxyValue }); 89 | } 90 | 91 | function withKnobs(storyFn, context) { 92 | var channel = _storybookAddons2.default.getChannel(); 93 | return manager.wrapStory(channel, storyFn, context); 94 | } -------------------------------------------------------------------------------- /dist/register.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _react = require('react'); 4 | 5 | var _react2 = _interopRequireDefault(_react); 6 | 7 | var _storybookAddons = require('@kadira/storybook-addons'); 8 | 9 | var _storybookAddons2 = _interopRequireDefault(_storybookAddons); 10 | 11 | var _Panel = require('./components/Panel'); 12 | 13 | var _Panel2 = _interopRequireDefault(_Panel); 14 | 15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 16 | 17 | _storybookAddons2.default.register('kadirahq/storybook-addon-knobs', function (api) { 18 | var channel = _storybookAddons2.default.getChannel(); 19 | 20 | _storybookAddons2.default.addPanel('kadirahq/storybook-addon-knobs', { 21 | title: 'Knobs', 22 | render: function render() { 23 | return _react2.default.createElement(_Panel2.default, { channel: channel, api: api, key: 'knobs-panel' }); 24 | } 25 | }); 26 | }); -------------------------------------------------------------------------------- /docs/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybook-eol/storybook-addon-knobs/ab9d5913679fa59e9009f0f53bd21bed2cf999b2/docs/demo.gif -------------------------------------------------------------------------------- /docs/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybook-eol/storybook-addon-knobs/ab9d5913679fa59e9009f0f53bd21bed2cf999b2/docs/demo.png -------------------------------------------------------------------------------- /docs/storybook-knobs-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybook-eol/storybook-addon-knobs/ab9d5913679fa59e9009f0f53bd21bed2cf999b2/docs/storybook-knobs-example.png -------------------------------------------------------------------------------- /example/stories/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@kadira/storybook'; 3 | import moment from 'moment'; 4 | import { 5 | withKnobs, 6 | number, 7 | object, 8 | boolean, 9 | text, 10 | select, 11 | date, 12 | array, 13 | color, 14 | } from '../../src'; 15 | 16 | const stories = storiesOf('Example of Knobs', module); 17 | 18 | stories.addDecorator(withKnobs); 19 | 20 | stories.add('simple example', () => ( 21 | 22 | )); 23 | 24 | stories.add('with all knobs', () => { 25 | const name = text('Name', 'Tom Cary'); 26 | const dob = date('DOB', new Date('January 20 1887')); 27 | 28 | const bold = boolean('Bold', false); 29 | const selectedColor = color('Color', 'black'); 30 | const favoriteNumber = number('Favorite Number', 42); 31 | const comfortTemp = number('Comfort Temp', 72, {range:true, min: 60, max: 90, step: 1}); 32 | 33 | const passions = array('Passions', ['Fishing', 'Skiing']); 34 | 35 | const customStyle = object('Style', { 36 | fontFamily: 'Arial', 37 | padding: 20, 38 | }); 39 | 40 | const style = { 41 | ...customStyle, 42 | fontWeight: bold ? 800: 400, 43 | favoriteNumber: favoriteNumber, 44 | color: selectedColor, 45 | }; 46 | 47 | return ( 48 |
49 | I'm {name} and I was born on "{moment(dob).format("DD MMM YYYY")}" 50 | I like:
    {passions.map((p, i) =>
  • {p}
  • )}
51 |

My favorite number is {favoriteNumber}.

52 |

My most comfortable room temperature is {comfortTemp} degrees Fahrenheit.

53 |
54 | ); 55 | }) 56 | 57 | stories.add('dates Knob', () => { 58 | const today = date('today'); 59 | const dob = date('DOB', null); 60 | const myDob = date('My DOB', new Date('July 07 1993')); 61 | 62 | return ( 63 | 86 | ) 87 | }) 88 | 89 | stories.add('dynamic knobs', () => { 90 | const showOptional = select('Show optional', ['yes', 'no'], 'yes') 91 | return ( 92 |
93 |
94 | {text('compulsary', 'I must be here')} 95 |
96 | { showOptional==='yes' ?
{text('optional', 'I can disapear')}
: null } 97 |
98 | ) 99 | }) 100 | 101 | stories.add('without any knob', () => ( 102 | 103 | )); 104 | -------------------------------------------------------------------------------- /example/typescript/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { storiesOf } from '@kadira/storybook'; 3 | import * as moment from 'moment'; 4 | import { 5 | withKnobs, 6 | number, 7 | color, 8 | object, 9 | boolean, 10 | text, 11 | select, 12 | date 13 | } from '../../storybook-addon-knobs'; 14 | 15 | const stories = storiesOf('Example of Knobs', module); 16 | 17 | stories.addDecorator(withKnobs); 18 | 19 | stories.add('simple example', () => ( 20 | 21 | )); 22 | 23 | stories.add('with all knobs', () => { 24 | const name = text('Name', 'Tom Cary'); 25 | const dob = date('DOB', new Date('January 20 1887')); 26 | 27 | const bold = boolean('Bold', false); 28 | const color = color('Color', 'black'); 29 | const textDecoration = select('Decoration', { 30 | none: 'None', 31 | underline: 'Underline', 32 | "line-through": 'Line-Through' 33 | }, 'none'); 34 | 35 | const customStyle = object('Style', { 36 | fontFamily: 'Arial', 37 | padding: 20, 38 | }); 39 | 40 | const style = Object.assign({}, customStyle, { 41 | fontWeight: bold ? 800: 400, 42 | color, 43 | textDecoration 44 | }); 45 | 46 | return ( 47 |
48 | I'm {name} and I was born on "{moment(dob).format("DD MMM YYYY")}" 49 |
50 | ); 51 | }); 52 | 53 | stories.add('dates Knob', () => { 54 | const today = date('today'); 55 | const dob = date('DOB', null); 56 | const myDob = date('My DOB', new Date('July 07 1993')); 57 | 58 | return ( 59 | 82 | ) 83 | }); 84 | 85 | stories.add('dynamic knobs', () => { 86 | const showOptional = select('Show optional', ['yes', 'no'], 'yes') 87 | return ( 88 |
89 |
90 | {text('compulsary', 'I must be here')} 91 |
92 | { showOptional==='yes' ?
{text('optional', 'I can disapear')}
: null } 93 |
94 | ) 95 | }); 96 | 97 | stories.add('without any knob', () => ( 98 | 99 | )); 100 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@kadira/storybook-addon-knobs", 3 | "version": "1.7.1", 4 | "description": "React Storybook Addon Prop Editor Component", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/kadirahq/react-storybook-addon-knobs.git" 8 | }, 9 | "license": "MIT", 10 | "scripts": { 11 | "start": "./example/prepublish.sh", 12 | "prepublish": ". ./.scripts/prepublish.sh", 13 | "lint": "eslint src", 14 | "lintfix": "eslint src --fix", 15 | "testonly": "mocha --require .scripts/mocha_runner src/**/tests/**/*.js", 16 | "test": "npm run lint && npm run testonly", 17 | "test-watch": "npm run testonly -- --watch --watch-extensions js", 18 | "storybook": "start-storybook -p 9010", 19 | "publish-storybook": "bash .scripts/publish_storybook.sh" 20 | }, 21 | "devDependencies": { 22 | "@kadira/storybook": "^2.1.0", 23 | "@types/mocha": "^2.2.32", 24 | "@types/node": "^6.0.46", 25 | "@types/react": "^0.14.42", 26 | "babel-cli": "^6.5.0", 27 | "babel-core": "^6.5.0", 28 | "babel-eslint": "^7.0.0", 29 | "babel-loader": "^6.2.4", 30 | "babel-plugin-transform-runtime": "^6.5.0", 31 | "babel-polyfill": "^6.5.0", 32 | "babel-preset-es2015": "^6.5.0", 33 | "babel-preset-react": "^6.5.0", 34 | "babel-preset-stage-2": "^6.5.0", 35 | "chai": "^3.5.0", 36 | "enzyme": "^2.2.0", 37 | "eslint": "^2.7.0", 38 | "eslint-config-airbnb": "^7.0.0", 39 | "eslint-plugin-babel": "^3.2.0", 40 | "eslint-plugin-jsx-a11y": "^0.6.2", 41 | "eslint-plugin-react": "^4.3.0", 42 | "git-url-parse": "^6.0.1", 43 | "jsdom": "^8.3.1", 44 | "mocha": "^3.1.0", 45 | "raw-loader": "^0.5.1", 46 | "react": "^15.3.2", 47 | "react-addons-test-utils": "^15.0.0", 48 | "react-dom": "^15.3.2", 49 | "sinon": "^1.17.3", 50 | "style-loader": "^0.13.1", 51 | "typescript": "^2.0.6", 52 | "typescript-definition-tester": "^0.0.5" 53 | }, 54 | "peerDependencies": { 55 | "@kadira/storybook-addons": "^1.3.0", 56 | "react": "^0.14.7 || ^15.0.0", 57 | "react-dom": "^0.14.7 || ^15.0.0" 58 | }, 59 | "dependencies": { 60 | "babel-runtime": "^6.5.0", 61 | "deep-equal": "^1.0.1", 62 | "insert-css": "^1.0.0", 63 | "moment": "^2.15.0", 64 | "react-color": "^2.4.3", 65 | "react-datetime": "^2.6.0", 66 | "react-textarea-autosize": "^4.0.5" 67 | }, 68 | "main": "dist/index.js", 69 | "engines": { 70 | "npm": "^3.0.0" 71 | }, 72 | "typings": "./storybook-addon-knobs.d.ts" 73 | } 74 | -------------------------------------------------------------------------------- /register.js: -------------------------------------------------------------------------------- 1 | require('./dist/register'); 2 | -------------------------------------------------------------------------------- /src/KnobManager.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import WrapStory from './components/WrapStory'; 3 | import KnobStore from './KnobStore'; 4 | import deepEqual from 'deep-equal'; 5 | 6 | // This is used by _mayCallChannel to determine how long to wait to before triggering a panel update 7 | const PANEL_UPDATE_INTERVAL = 400; 8 | 9 | export default class KnobManager { 10 | constructor() { 11 | this.knobStore = null; 12 | this.knobStoreMap = {}; 13 | } 14 | 15 | knob(name, options) { 16 | this._mayCallChannel(); 17 | 18 | const knobStore = this.knobStore; 19 | const existingKnob = knobStore.get(name); 20 | // We need to return the value set by the knob editor via this. 21 | // But, if the user changes the code for the defaultValue we should set 22 | // that value instead. 23 | if (existingKnob && deepEqual(options.value, existingKnob.defaultValue)) { 24 | return existingKnob.value; 25 | } 26 | 27 | const defaultValue = options.value; 28 | const knobInfo = { 29 | ...options, 30 | name, 31 | defaultValue, 32 | }; 33 | 34 | knobStore.set(name, knobInfo); 35 | return knobStore.get(name).value; 36 | } 37 | 38 | wrapStory(channel, storyFn, context) { 39 | this.channel = channel; 40 | const key = `${context.kind}:::${context.story}`; 41 | let knobStore = this.knobStoreMap[key]; 42 | 43 | if (!knobStore) { 44 | knobStore = this.knobStoreMap[key] = new KnobStore(); 45 | } 46 | 47 | this.knobStore = knobStore; 48 | knobStore.markAllUnused(); 49 | const initialContent = storyFn(context); 50 | const props = { context, storyFn, channel, knobStore, initialContent }; 51 | return (); 52 | } 53 | 54 | _mayCallChannel() { 55 | // Re rendering of the story may cause changes to the knobStore. Some new knobs maybe added and 56 | // Some knobs may go unused. So we need to update the panel accordingly. For example remove the 57 | // unused knobs from the panel. This function sends the `setKnobs` message to the channel 58 | // triggering a panel re-render. 59 | 60 | if (this.calling) { 61 | // If a call to channel has already registered ignore this call. 62 | // Once the previous call is completed all the changes to knobStore including the one that 63 | // triggered this, will be added to the panel. 64 | // This avoids emitting to the channel within very short periods of time. 65 | return; 66 | } 67 | this.calling = true; 68 | 69 | setTimeout(() => { 70 | this.calling = false; 71 | // emit to the channel and trigger a panel re-render 72 | this.channel.emit('addon:knobs:setKnobs', this.knobStore.getAll()); 73 | }, PANEL_UPDATE_INTERVAL); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/KnobStore.js: -------------------------------------------------------------------------------- 1 | export default class KnobStore { 2 | constructor() { 3 | this.store = {}; 4 | this.callbacks = []; 5 | } 6 | 7 | has(key) { 8 | return this.store[key] !== undefined; 9 | } 10 | 11 | set(key, value) { 12 | this.store[key] = value; 13 | this.store[key].used = true; 14 | this.callbacks.forEach(cb => cb()); 15 | } 16 | 17 | get(key) { 18 | const knob = this.store[key]; 19 | if (knob) { 20 | knob.used = true; 21 | } 22 | return knob; 23 | } 24 | 25 | getAll() { 26 | return this.store; 27 | } 28 | 29 | reset() { 30 | this.store = {}; 31 | } 32 | 33 | markAllUnused() { 34 | Object.keys(this.store).forEach(knobName => { 35 | this.store[knobName].used = false; 36 | }); 37 | } 38 | 39 | subscribe(cb) { 40 | this.callbacks.push(cb); 41 | } 42 | 43 | unsubscribe(cb) { 44 | const index = this.callbacks.indexOf(cb); 45 | this.callbacks.splice(index, 1); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/components/Panel.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropForm from './PropForm'; 3 | import Types from './types'; 4 | 5 | const styles = { 6 | panelWrapper: { 7 | width: '100%', 8 | }, 9 | panel: { 10 | padding: '5px', 11 | width: 'auto', 12 | position: 'relative', 13 | }, 14 | noKnobs: { 15 | fontFamily: ` 16 | -apple-system, ".SFNSText-Regular", "San Francisco", "Roboto", 17 | "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif 18 | `, 19 | display: 'inline', 20 | width: '100%', 21 | textAlign: 'center', 22 | color: 'rgb(190, 190, 190)', 23 | padding: '10px', 24 | }, 25 | resetButton: { 26 | position: 'absolute', 27 | bottom: 11, 28 | right: 10, 29 | border: 'none', 30 | borderTop: 'solid 1px rgba(0, 0, 0, 0.2)', 31 | borderLeft: 'solid 1px rgba(0, 0, 0, 0.2)', 32 | background: 'rgba(255, 255, 255, 0.5)', 33 | padding: '5px 10px', 34 | borderRadius: '4px 0 0 0', 35 | color: 'rgba(0, 0, 0, 0.5)', 36 | outline: 'none', 37 | }, 38 | }; 39 | 40 | export default class Panel extends React.Component { 41 | constructor(props) { 42 | super(props); 43 | this.handleChange = this.handleChange.bind(this); 44 | this.setKnobs = this.setKnobs.bind(this); 45 | this.reset = this.reset.bind(this); 46 | 47 | this.state = { knobs: {} }; 48 | this.loadedFromUrl = false; 49 | this.props.channel.on('addon:knobs:setKnobs', this.setKnobs); 50 | } 51 | 52 | componentWillUnmount() { 53 | this.props.channel.removeListener('addon:knobs:setKnobs', this.setKnobs); 54 | } 55 | 56 | setKnobs(knobs) { 57 | const queryParams = {}; 58 | const { api, channel } = this.props; 59 | 60 | Object.keys(knobs).forEach((name) => { 61 | const knob = knobs[name]; 62 | // For the first time, get values from the URL and set them. 63 | if (!this.loadedFromUrl) { 64 | const urlValue = api.getQueryParam(`knob-${name}`); 65 | 66 | if (urlValue !== undefined) { // If the knob value present in url 67 | knob.value = Types[knob.type].deserialize(urlValue); 68 | channel.emit('addon:knobs:knobChange', knob); 69 | } 70 | } 71 | 72 | queryParams[`knob-${name}`] = Types[knob.type].serialize(knob.value); 73 | }); 74 | 75 | this.loadedFromUrl = true; 76 | api.setQueryParams(queryParams); 77 | this.setState({ knobs }); 78 | } 79 | 80 | reset() { 81 | this.props.channel.emit('addon:knobs:reset'); 82 | } 83 | 84 | handleChange(changedKnob) { 85 | const { api, channel } = this.props; 86 | const { knobs } = this.state; 87 | const { name, type, value } = changedKnob; 88 | const newKnobs = { ...knobs }; 89 | newKnobs[name] = { 90 | ...newKnobs[name], 91 | ...changedKnob, 92 | }; 93 | 94 | this.setState({ knobs: newKnobs }); 95 | 96 | const queryParams = {}; 97 | queryParams[`knob-${name}`] = Types[type].serialize(value); 98 | 99 | api.setQueryParams(queryParams); 100 | channel.emit('addon:knobs:knobChange', changedKnob); 101 | } 102 | 103 | render() { 104 | const { knobs } = this.state; 105 | const knobsArray = Object.keys(knobs) 106 | .filter(key => (knobs[key].used)) 107 | .map(key => (knobs[key])); 108 | 109 | if (knobsArray.length === 0) { 110 | return ( 111 |
NO KNOBS
112 | ); 113 | } 114 | 115 | return ( 116 |
117 |
118 | 119 |
120 | 121 |
122 | ); 123 | } 124 | } 125 | 126 | Panel.propTypes = { 127 | channel: React.PropTypes.object, 128 | onReset: React.PropTypes.object, 129 | api: React.PropTypes.object, 130 | }; 131 | -------------------------------------------------------------------------------- /src/components/PropField.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TypeMap from './types'; 3 | 4 | const InvalidType = () => (Invalid Type); 5 | 6 | const stylesheet = { 7 | field: { 8 | display: 'table-row', 9 | padding: '5px', 10 | }, 11 | label: { 12 | display: 'table-cell', 13 | boxSizing: 'border-box', 14 | verticalAlign: 'top', 15 | paddingRight: 5, 16 | paddingTop: 7, 17 | textAlign: 'right', 18 | width: 80, 19 | fontSize: 10, 20 | color: 'rgb(68, 68, 68)', 21 | fontWeight: 600, 22 | }, 23 | }; 24 | 25 | stylesheet.textarea = { 26 | ...stylesheet.input, 27 | height: '100px', 28 | }; 29 | 30 | stylesheet.checkbox = { 31 | ...stylesheet.input, 32 | width: 'auto', 33 | }; 34 | 35 | export default class PropField extends React.Component { 36 | constructor(props) { 37 | super(props); 38 | this._onChange = this.onChange.bind(this); 39 | } 40 | 41 | onChange(e) { 42 | this.props.onChange(e.target.value); 43 | } 44 | 45 | render() { 46 | const { onChange, knob } = this.props; 47 | 48 | let InputType = TypeMap[knob.type] || InvalidType; 49 | 50 | return ( 51 |
52 | 55 | 59 |
60 | ); 61 | } 62 | } 63 | 64 | PropField.propTypes = { 65 | onChange: React.PropTypes.func.isRequired, 66 | knob: React.PropTypes.object, 67 | }; 68 | -------------------------------------------------------------------------------- /src/components/PropForm.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import PropField from './PropField'; 4 | 5 | const stylesheet = { 6 | propForm: { 7 | fontFamily: ` 8 | -apple-system, ".SFNSText-Regular", "San Francisco", "Roboto", 9 | "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif 10 | `, 11 | display: 'table', 12 | boxSizing: 'border-box', 13 | width: '100%', 14 | borderCollapse: 'separate', 15 | borderSpacing: '5px', 16 | }, 17 | }; 18 | 19 | export default class propForm extends React.Component { 20 | constructor() { 21 | super(); 22 | this._onFieldChange = this.onFieldChange.bind(this); 23 | } 24 | 25 | onFieldChange(name, type, value) { 26 | const change = { name, type, value }; 27 | this.props.onFieldChange(change); 28 | } 29 | 30 | render() { 31 | const knobs = this.props.knobs; 32 | 33 | return ( 34 |
35 | {knobs.map(knob => ( 36 | 44 | ))} 45 | 46 | ); 47 | } 48 | } 49 | 50 | propForm.displayName = 'propForm'; 51 | 52 | propForm.propTypes = { 53 | knobs: React.PropTypes.array.isRequired, 54 | onFieldChange: React.PropTypes.func.isRequired, 55 | }; 56 | -------------------------------------------------------------------------------- /src/components/WrapStory.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class WrapStory extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | this.knobChanged = this.knobChanged.bind(this); 7 | this.resetKnobs = this.resetKnobs.bind(this); 8 | this.setPaneKnobs = this.setPaneKnobs.bind(this); 9 | this._knobsAreReset = false; 10 | this.state = { storyContent: this.props.initialContent }; 11 | } 12 | 13 | componentDidMount() { 14 | // Watch for changes in knob editor. 15 | this.props.channel.on('addon:knobs:knobChange', this.knobChanged); 16 | // Watch for the reset event and reset knobs. 17 | this.props.channel.on('addon:knobs:reset', this.resetKnobs); 18 | // Watch for any change in the knobStore and set the panel again for those 19 | // changes. 20 | this.props.knobStore.subscribe(this.setPaneKnobs); 21 | // Set knobs in the panel for the first time. 22 | this.setPaneKnobs(); 23 | } 24 | 25 | componentWillReceiveProps(props) { 26 | this.setState({ storyContent: props.initialContent }); 27 | } 28 | 29 | componentWillUnmount() { 30 | this.props.channel.removeListener('addon:knobs:knobChange', this.knobChanged); 31 | this.props.channel.removeListener('addon:knobs:reset', this.resetKnobs); 32 | this.props.knobStore.unsubscribe(this.setPaneKnobs); 33 | } 34 | 35 | setPaneKnobs() { 36 | const { channel, knobStore } = this.props; 37 | channel.emit('addon:knobs:setKnobs', knobStore.getAll()); 38 | } 39 | 40 | knobChanged(change) { 41 | const { name, value } = change; 42 | const { knobStore, storyFn, context } = this.props; 43 | // Update the related knob and it's value. 44 | const knobOptions = knobStore.get(name); 45 | knobOptions.value = value; 46 | knobStore.markAllUnused(); 47 | this.setState({ storyContent: storyFn(context) }); 48 | } 49 | 50 | resetKnobs() { 51 | const { knobStore, storyFn, context } = this.props; 52 | knobStore.reset(); 53 | this.setState({ storyContent: storyFn(context) }); 54 | this.setPaneKnobs(); 55 | } 56 | 57 | render() { 58 | return this.state.storyContent; 59 | } 60 | } 61 | 62 | WrapStory.propTypes = { 63 | context: React.PropTypes.object, 64 | storyFn: React.PropTypes.func, 65 | channel: React.PropTypes.object, 66 | knobStore: React.PropTypes.object, 67 | initialContent: React.PropTypes.object, 68 | }; 69 | -------------------------------------------------------------------------------- /src/components/tests/Array.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import Array from '../types/Array'; 4 | import { expect } from 'chai'; 5 | import sinon from 'sinon'; 6 | const { describe, it } = global; 7 | 8 | describe('Array', () => { 9 | it('should subscribe to setKnobs event of channel', () => { 10 | const onChange = sinon.spy(); 11 | const wrapper = shallow(); 14 | 15 | wrapper.simulate('change', { target: { value: 'Fhishing,Skiing,Dancing' } }); 16 | expect(onChange.calledWith(['Fhishing', 'Skiing', 'Dancing'])).to.equal(true); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/components/tests/Panel.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import Panel from '../Panel'; 4 | import { expect } from 'chai'; 5 | import sinon from 'sinon'; 6 | const { describe, it } = global; 7 | 8 | describe('Panel', () => { 9 | it('should subscribe to setKnobs event of channel', () => { 10 | const testChannel = { on: sinon.spy() }; 11 | shallow(); 12 | expect(testChannel.on.calledWith('addon:knobs:setKnobs')).to.equal(true); 13 | }); 14 | 15 | describe('setKnobs handler', () => { 16 | it('should read url params and set values for existing knobs', () => { 17 | const handlers = {}; 18 | 19 | const testChannel = { 20 | on: (e, handler) => { 21 | handlers[e] = handler; 22 | }, 23 | emit: sinon.spy(), 24 | }; 25 | 26 | const testQueryParams = { 27 | 'knob-foo': 'test string', 28 | bar: 'some other string', 29 | }; 30 | 31 | const testApi = { 32 | getQueryParam: key => { 33 | return testQueryParams[key]; 34 | }, 35 | setQueryParams: sinon.spy(), 36 | }; 37 | 38 | shallow(); 39 | const setKnobsHandler = handlers['addon:knobs:setKnobs']; 40 | 41 | const knobs = { 42 | foo: { 43 | name: 'foo', 44 | value: 'default string', 45 | type: 'text', 46 | }, 47 | baz: { 48 | name: 'baz', 49 | value: 'another knob value', 50 | type: 'text', 51 | }, 52 | }; 53 | 54 | setKnobsHandler(knobs); 55 | const knobFromUrl = { 56 | name: 'foo', 57 | value: testQueryParams['knob-foo'], 58 | type: 'text', 59 | }; 60 | const e = 'addon:knobs:knobChange'; 61 | expect(testChannel.emit.calledWith(e, knobFromUrl)).to.equal(true); 62 | }); 63 | 64 | it('should set query params when url params are already read', () => { 65 | const handlers = {}; 66 | 67 | const testChannel = { 68 | on: (e, handler) => { 69 | handlers[e] = handler; 70 | }, 71 | emit: sinon.spy(), 72 | }; 73 | 74 | const testQueryParams = { 75 | 'knob-foo': 'test string', 76 | bar: 'some other string', 77 | }; 78 | 79 | const testApi = { 80 | getQueryParam: key => { 81 | return testQueryParams[key]; 82 | }, 83 | setQueryParams: sinon.spy(), 84 | }; 85 | 86 | const wrapper = shallow(); 87 | const setKnobsHandler = handlers['addon:knobs:setKnobs']; 88 | 89 | const knobs = { 90 | foo: { 91 | name: 'foo', 92 | value: 'default string', 93 | type: 'text', 94 | }, 95 | baz: { 96 | name: 'baz', 97 | value: 'another knob value', 98 | type: 'text', 99 | }, 100 | }; 101 | 102 | // Make it act like that url params are already checked 103 | wrapper.instance().loadedFromUrl = true; 104 | 105 | setKnobsHandler(knobs); 106 | const knobFromStory = { 107 | 'knob-foo': knobs.foo.value, 108 | 'knob-baz': knobs.baz.value, 109 | }; 110 | 111 | expect(testApi.setQueryParams.calledWith(knobFromStory)).to.equal(true); 112 | }); 113 | }); 114 | 115 | describe('handleChange()', () => { 116 | it('should set queryParams and emit knobChange event', () => { 117 | const testChannel = { 118 | on: sinon.spy(), 119 | emit: sinon.spy(), 120 | }; 121 | 122 | const testApi = { 123 | getQueryParam: sinon.spy(), 124 | setQueryParams: sinon.spy(), 125 | }; 126 | 127 | const wrapper = shallow(); 128 | 129 | const testChangedKnob = { 130 | name: 'foo', 131 | value: 'changed text', 132 | type: 'text', 133 | }; 134 | wrapper.instance().handleChange(testChangedKnob); 135 | expect(testChannel.emit.calledWith( 136 | 'addon:knobs:knobChange', testChangedKnob)).to.equal(true); 137 | 138 | const paramsChange = { 'knob-foo': 'changed text' }; 139 | expect(testApi.setQueryParams.calledWith(paramsChange)).to.equal(true); 140 | }); 141 | }); 142 | }); 143 | -------------------------------------------------------------------------------- /src/components/types/Array.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Textarea from 'react-textarea-autosize'; 3 | 4 | const styles = { 5 | display: 'table-cell', 6 | boxSizing: 'border-box', 7 | verticalAlign: 'middle', 8 | height: '26px', 9 | width: '100%', 10 | outline: 'none', 11 | border: '1px solid #f7f4f4', 12 | borderRadius: 2, 13 | fontSize: 11, 14 | padding: '5px', 15 | color: '#555', 16 | }; 17 | 18 | class ArrayType extends React.Component { 19 | render() { 20 | const { knob, onChange } = this.props; 21 | return ( 22 |