diff --git a/dist/client/client_api.js b/dist/client/client_api.js index 0eb2d67568e5..e228cb52db9e 100644 --- a/dist/client/client_api.js +++ b/dist/client/client_api.js @@ -18,6 +18,8 @@ var _createClass3 = _interopRequireDefault(_createClass2); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +var actionIds = 0; + var ClientApi = function () { function ClientApi(_ref) { var syncedStore = _ref.syncedStore; @@ -69,7 +71,8 @@ var ClientApi = function () { args[0] = '[SyntheticEvent]'; } - actions = [{ name: name, args: args }].concat(actions.slice(0, 4)); + var id = actionIds++; + actions = [{ id: id, name: name, args: args }].concat(actions.slice(0, 4)); syncedStore.setData({ actions: actions }); }; } diff --git a/dist/client/ui/action_logger.js b/dist/client/ui/action_logger.js index 45040063d42f..e950cebf4daa 100644 --- a/dist/client/ui/action_logger.js +++ b/dist/client/ui/action_logger.js @@ -28,6 +28,10 @@ var _react = require('react'); var _react2 = _interopRequireDefault(_react); +var _foldable = require('./foldable'); + +var _foldable2 = _interopRequireDefault(_foldable); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var preStyle = { @@ -63,11 +67,6 @@ var btnStyle = { marginLeft: 5 }; -var latestActionLogStyle = { - backgroundColor: '#FFFCE0', - transition: 'all .2s ease-in' -}; - var ActionLogger = function (_Component) { (0, _inherits3.default)(ActionLogger, _Component); @@ -77,31 +76,10 @@ var ActionLogger = function (_Component) { } (0, _createClass3.default)(ActionLogger, [{ - key: 'componentDidUpdate', - value: function componentDidUpdate() { - var _this2 = this; - - if (this.refs.actionLogger && window.setTimeout) { - this.refs.actionLogger.style.backgroundColor = latestActionLogStyle.backgroundColor; - setTimeout(function () { - _this2.refs.actionLogger.style.backgroundColor = 'white'; - }, 500); - } - } - }, { key: 'getActionData', value: function getActionData() { - return this.props.actionLogs.map(function (action, i) { - // assuming that the first object in the array is the latest addition. - return i === 0 ? _react2.default.createElement( - 'div', - { style: latestActionLogStyle, ref: 'actionLogger', key: i }, - action - ) : _react2.default.createElement( - 'div', - { key: i }, - action - ); + return this.props.actions.map(function (action) { + return _react2.default.createElement(_foldable2.default, { key: action.id, action: action }); }); } }, { @@ -135,7 +113,7 @@ var ActionLogger = function (_Component) { ActionLogger.propTypes = { onClear: _react2.default.PropTypes.func, - actionLogs: _react2.default.PropTypes.array + actions: _react2.default.PropTypes.array }; exports.default = ActionLogger; \ No newline at end of file diff --git a/dist/client/ui/admin.js b/dist/client/ui/admin.js index ad9a9a170c37..524c2aa77b15 100644 --- a/dist/client/ui/admin.js +++ b/dist/client/ui/admin.js @@ -22,10 +22,6 @@ var _reactDom = require('react-dom'); var _reactDom2 = _interopRequireDefault(_reactDom); -var _jsonStringifySafe = require('json-stringify-safe'); - -var _jsonStringifySafe2 = _interopRequireDefault(_jsonStringifySafe); - var _controls = require('./controls'); var _controls2 = _interopRequireDefault(_controls); @@ -102,10 +98,7 @@ function getActionLogger(data) { var _data$actions = data.actions; var actions = _data$actions === undefined ? [] : _data$actions; - var logs = actions.map(function (action) { - return (0, _jsonStringifySafe2.default)(action, null, 2); - }); - return _react2.default.createElement(_action_logger2.default, { actionLogs: logs, onClear: clearLogs }); + return _react2.default.createElement(_action_logger2.default, { actions: actions, onClear: clearLogs }); } function renderMain(data) { diff --git a/dist/client/ui/foldable.js b/dist/client/ui/foldable.js new file mode 100644 index 000000000000..6e1bf9379c67 --- /dev/null +++ b/dist/client/ui/foldable.js @@ -0,0 +1,142 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends2 = require('babel-runtime/helpers/extends'); + +var _extends3 = _interopRequireDefault(_extends2); + +var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _inherits2 = require('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _jsonStringifySafe = require('json-stringify-safe'); + +var _jsonStringifySafe2 = _interopRequireDefault(_jsonStringifySafe); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var folderStyle = { + display: 'block', + width: '100%', + marginBottom: '10px', + backgroundColor: 'white', + transition: 'background-color .2s ease-in' +}; + +var folderSidebarStyle = { + display: 'block', + width: '10px', + float: 'left', + height: '100%', + color: '#ccc', + userSelect: 'none', + WebkitUserSelect: 'none', + msUserSelect: 'none', + MozUserSelect: 'none', + cursor: 'pointer' +}; + +var folderContentStyle = { + display: 'inline-block', + clear: 'right', + marginLeft: '5px', + padding: '0px', + paddingLeft: '5px', + width: 'auto' +}; + +var Foldable = function (_React$Component) { + (0, _inherits3.default)(Foldable, _React$Component); + + function Foldable(props) { + (0, _classCallCheck3.default)(this, Foldable); + + var _this = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(Foldable).call(this, props)); + + _this.state = { + collapsed: true + }; + _this.onToggleCallback = _this.onToggle.bind(_this); + return _this; + } + + (0, _createClass3.default)(Foldable, [{ + key: 'componentDidMount', + value: function componentDidMount() { + var _this2 = this; + + this.refs.folder.style.backgroundColor = '#FFFCE0'; + setTimeout(function () { + _this2.refs.folder.style.backgroundColor = folderStyle.backgroundColor; + }, 500); + } + }, { + key: 'onToggle', + value: function onToggle() { + this.setState({ collapsed: !this.state.collapsed }); + } + }, { + key: 'render', + value: function render() { + var action = (0, _extends3.default)({}, this.props.action); + delete action.id; + var content = void 0; + + if (this.state.collapsed) { + // return the shortest string representation possible + content = (0, _jsonStringifySafe2.default)(action); + } else { + content = (0, _jsonStringifySafe2.default)(action, null, 2); + } + + return _react2.default.createElement( + 'div', + { ref: 'folder', style: folderStyle }, + _react2.default.createElement( + 'div', + { style: folderSidebarStyle }, + _react2.default.createElement( + 'span', + { className: 'foldable-toggle', onClick: this.onToggleCallback }, + this.state.collapsed ? '►' : '▼' + ) + ), + _react2.default.createElement( + 'div', + { className: 'foldable-content', style: folderContentStyle }, + content + ) + ); + } + }]); + return Foldable; +}(_react2.default.Component); + +Foldable.propTypes = { + action: _react2.default.PropTypes.object +}; + +exports.default = Foldable; \ No newline at end of file diff --git a/src/client/__tests__/client_api.js b/src/client/__tests__/client_api.js index 38713d9407b6..1b4a0913bfff 100644 --- a/src/client/__tests__/client_api.js +++ b/src/client/__tests__/client_api.js @@ -88,6 +88,7 @@ describe('client.ClientApi', () => { { name: 'hello', args: [10, 20], + id: 0, }, ]); }); @@ -107,6 +108,7 @@ describe('client.ClientApi', () => { { name: 'hello', args: [10, 20], + id: 1, }, 50, 40, @@ -132,6 +134,7 @@ describe('client.ClientApi', () => { { name: 'hello', args: ['[SyntheticEvent]'], + id: 2, }, ]); }); diff --git a/src/client/client_api.js b/src/client/client_api.js index 2cf80fbc2ea6..2de412ae4719 100644 --- a/src/client/client_api.js +++ b/src/client/client_api.js @@ -1,3 +1,5 @@ +let actionIds = 0; + export default class ClientApi { constructor({ syncedStore, storyStore }) { this._syncedStore = syncedStore; @@ -34,7 +36,8 @@ export default class ClientApi { args[0] = '[SyntheticEvent]'; } - actions = [{ name, args }].concat(actions.slice(0, 4)); + const id = actionIds++; + actions = [{ id, name, args }].concat(actions.slice(0, 4)); syncedStore.setData({ actions }); }; } diff --git a/src/client/ui/__tests__/action_logger.js b/src/client/ui/__tests__/action_logger.js index bfadde9f4e95..1ba781eda8d8 100644 --- a/src/client/ui/__tests__/action_logger.js +++ b/src/client/ui/__tests__/action_logger.js @@ -8,23 +8,29 @@ import ActionLogger from '../action_logger'; describe('', function () { describe('render', function () { it('should render logs - empty', function () { - const wrap = shallow(); + const wrap = shallow(); const logs = wrap.find('pre').first(); expect(logs.text()).to.equal(''); }); it('should render logs', function () { - const data = ['a1', 'a2', 'a3']; - const wrap = shallow(); + const action1 = { name: 'a1' }; + const action2 = { name: 'a2' }; + const action3 = { name: 'a3' }; + + const data = [action1, action2, action3]; + + const wrap = shallow(); const logs = wrap.find('pre').first(); - expect(logs.text()).to.equal('a1a2a3'); + + expect(logs.children().length).to.equal(3); }); }); describe('functions', function () { it('should call the onClear prop when the button is clicked', function () { const onClear = sinon.spy(); - const wrap = shallow(); + const wrap = shallow(); const clear = wrap.find('button').first(); clear.simulate('click'); expect(onClear.calledOnce).to.equal(true); diff --git a/src/client/ui/__tests__/foldable.js b/src/client/ui/__tests__/foldable.js new file mode 100644 index 000000000000..9b7a8b2ae85c --- /dev/null +++ b/src/client/ui/__tests__/foldable.js @@ -0,0 +1,43 @@ +const { describe, it } = global; +import { expect } from 'chai'; +import { shallow } from 'enzyme'; +import React from 'react'; +import Foldable from '../foldable'; + +describe('', function () { + describe('render', function () { + it('should render action compact by default', function () { + const data = { + name: 'test action', + args: 'things', + }; + + const compactString = '{"name":"test action","args":"things"}'; + + const wrap = shallow(); + const content = wrap.find('.foldable-content').first(); + + expect(content.text()).to.equal(compactString); + }); + + it('should render action in full when unfolded', function () { + const data = { + name: 'test action', + args: 'things', + }; + + const fullString = '{ "name": "test action",\n "args": "things"\n}'; + + const wrap = shallow(); + const toggle = wrap.find('.foldable-toggle').first(); + + toggle.simulate('click'); + + expect(wrap.state()).to.deep.equal({ collapsed: false }); + + const content = wrap.find('.foldable-content').first(); + + expect(content.text()).to.equal(fullString); + }); + }); +}); diff --git a/src/client/ui/action_logger.js b/src/client/ui/action_logger.js index 62ecb61fdf99..e787a3b99884 100644 --- a/src/client/ui/action_logger.js +++ b/src/client/ui/action_logger.js @@ -1,4 +1,5 @@ import React, { Component } from 'react'; +import Foldable from './foldable'; const preStyle = { color: '#666', @@ -36,31 +37,13 @@ const btnStyle = { marginLeft: 5, }; -const latestActionLogStyle = { - backgroundColor: '#FFFCE0', - transition: 'all .2s ease-in', -}; - class ActionLogger extends Component { - componentDidUpdate() { - if (this.refs.actionLogger && window.setTimeout) { - this.refs.actionLogger.style.backgroundColor = latestActionLogStyle.backgroundColor; - setTimeout(() => { - this.refs.actionLogger.style.backgroundColor = 'white'; - }, 500); - } - } getActionData() { - return this.props.actionLogs - .map((action, i) => { - // assuming that the first object in the array is the latest addition. - return i === 0 ? ( -
{action}
- ) : ( -
{action}
- ); - }); + return this.props.actions + .map((action) => { + return (); + }); } render() { @@ -79,7 +62,7 @@ class ActionLogger extends Component { ActionLogger.propTypes = { onClear: React.PropTypes.func, - actionLogs: React.PropTypes.array, + actions: React.PropTypes.array, }; export default ActionLogger; diff --git a/src/client/ui/admin.js b/src/client/ui/admin.js index 9e324e46da25..c2f9fc831666 100644 --- a/src/client/ui/admin.js +++ b/src/client/ui/admin.js @@ -1,6 +1,5 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import stringify from 'json-stringify-safe'; import StorybookControls from './controls'; import ActionLogger from './action_logger'; import Layout from './layout'; @@ -67,9 +66,7 @@ export function getIframe(data) { export function getActionLogger(data) { const { actions = [] } = data; - const logs = actions - .map((action) => stringify(action, null, 2)); - return (); + return (); } export function renderMain(data) { diff --git a/src/client/ui/foldable.js b/src/client/ui/foldable.js new file mode 100644 index 000000000000..cc227f7edbf6 --- /dev/null +++ b/src/client/ui/foldable.js @@ -0,0 +1,86 @@ +import React from 'react'; +import stringify from 'json-stringify-safe'; + +const folderStyle = { + display: 'block', + width: '100%', + marginBottom: '10px', + backgroundColor: 'white', + transition: 'background-color .2s ease-in', +}; + +const folderSidebarStyle = { + display: 'block', + width: '10px', + float: 'left', + height: '100%', + color: '#ccc', + userSelect: 'none', + WebkitUserSelect: 'none', + msUserSelect: 'none', + MozUserSelect: 'none', + cursor: 'pointer', +}; + +const folderContentStyle = { + display: 'inline-block', + clear: 'right', + marginLeft: '5px', + padding: '0px', + paddingLeft: '5px', + width: 'auto', +}; + +class Foldable extends React.Component { + constructor(props) { + super(props); + this.state = { + collapsed: true, + }; + this.onToggleCallback = this.onToggle.bind(this); + } + + componentDidMount() { + this.refs.folder.style.backgroundColor = '#FFFCE0'; + setTimeout(() => { + this.refs.folder.style.backgroundColor = folderStyle.backgroundColor; + }, 500); + } + + onToggle() { + this.setState({ collapsed: !this.state.collapsed }); + } + + render() { + const action = { ...this.props.action }; + delete action.id; + let content; + + if (this.state.collapsed) { + // return the shortest string representation possible + content = stringify(action); + } else { + content = stringify(action, null, 2); + } + + return ( +
+
+ + { this.state.collapsed ? '►' : '▼' } + +
+ +
+ { content } +
+
+ ); + } +} + +Foldable.propTypes = { + action: React.PropTypes.object, +}; + +export default Foldable;