Repository: couchdb-fauxton Updated Branches: refs/heads/master 37cfb74bc -> b8c4554cf
Truncate large docs in JSON view Currently there's no restriction on the size of documents that we attempt to display in JSON view. If the user uses include_docs=true it can actually crash people's browsers if the documents are too large. This PR adds in a truncation option to the Document component, defaulting to enabled, and 500 lines. It displays a simple "truncated" note at the bottom of the result. Project: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/commit/b8c4554c Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/tree/b8c4554c Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/diff/b8c4554c Branch: refs/heads/master Commit: b8c4554cfb40aab253328afd31b77591d5246fcc Parents: 37cfb74 Author: Ben Keen <ben.k...@gmail.com> Authored: Tue Feb 16 16:25:22 2016 -0800 Committer: Ben Keen <ben.k...@gmail.com> Committed: Tue Feb 23 10:19:34 2016 -0800 ---------------------------------------------------------------------- app/addons/components/assets/less/docs.less | 8 ++++ .../components/react-components.react.jsx | 42 +++++++++++++++----- app/addons/components/tests/docSpec.react.jsx | 39 ++++++++++++++++++ app/addons/documents/helpers.js | 18 ++++++++- app/addons/documents/tests/helpersSpec.js | 21 ++++++++++ 5 files changed, 116 insertions(+), 12 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/b8c4554c/app/addons/components/assets/less/docs.less ---------------------------------------------------------------------- diff --git a/app/addons/components/assets/less/docs.less b/app/addons/components/assets/less/docs.less index ed93f34..04b79f8 100644 --- a/app/addons/components/assets/less/docs.less +++ b/app/addons/components/assets/less/docs.less @@ -105,3 +105,11 @@ background-color: @docCheckBoxCheckedBG; } } + +.doc-content-truncated { + background-color: #4d4d4d; + color: #bbbbbb; + padding: 10px; + font-size: 13px; + margin: 0; +} http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/b8c4554c/app/addons/components/react-components.react.jsx ---------------------------------------------------------------------- diff --git a/app/addons/components/react-components.react.jsx b/app/addons/components/react-components.react.jsx index 356197f..499eb97 100644 --- a/app/addons/components/react-components.react.jsx +++ b/app/addons/components/react-components.react.jsx @@ -17,7 +17,6 @@ define([ 'react-dom', 'addons/components/actions', 'addons/components/stores', - 'addons/fauxton/components.react', 'addons/documents/helpers', 'ace/ace', @@ -25,8 +24,8 @@ define([ 'libs/react-bootstrap' ], -function (app, FauxtonAPI, React, ReactDOM, Actions, Stores, - FauxtonComponents, Helpers, ace, beautifyHelper, ReactBootstrap) { +function (app, FauxtonAPI, React, ReactDOM, Actions, Stores, FauxtonComponents, Helpers, ace, beautifyHelper, + ReactBootstrap) { var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; var componentStore = Stores.componentStore; @@ -36,7 +35,7 @@ function (app, FauxtonAPI, React, ReactDOM, Actions, Stores, propTypes: { elements: React.PropTypes.array.isRequired, - removeBadge: React.PropTypes.func.isRequired, + removeBadge: React.PropTypes.func.isRequired }, getDefaultProps: function () { @@ -994,7 +993,16 @@ function (app, FauxtonAPI, React, ReactDOM, Actions, Stores, var Document = React.createClass({ propTypes: { docIdentifier: React.PropTypes.string.isRequired, - docChecked: React.PropTypes.func.isRequired + docChecked: React.PropTypes.func.isRequired, + truncate: React.PropTypes.bool, + maxRows: React.PropTypes.number + }, + + getDefaultProps: function () { + return { + truncate: true, + maxRows: 500 + }; }, onChange: function (e) { @@ -1046,13 +1054,25 @@ function (app, FauxtonAPI, React, ReactDOM, Actions, Stores, }, getDocContent: function () { - if (!_.isEmpty(this.props.docContent)) { - return ( - <div className="doc-data"> - <pre className="prettyprint">{this.props.docContent}</pre> - </div> - ); + if (_.isEmpty(this.props.docContent)) { + return null; + } + + // if need be, truncate the document + var content = this.props.docContent; + var isTruncated = false; + if (this.props.truncate) { + var result = Helpers.truncateDoc(this.props.docContent, this.props.maxRows); + isTruncated = result.isTruncated; + content = result.content; } + + return ( + <div className="doc-data"> + <pre className="prettyprint">{content}</pre> + {isTruncated ? <div className="doc-content-truncated">(truncated)</div> : null} + </div> + ); }, render: function () { http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/b8c4554c/app/addons/components/tests/docSpec.react.jsx ---------------------------------------------------------------------- diff --git a/app/addons/components/tests/docSpec.react.jsx b/app/addons/components/tests/docSpec.react.jsx index a013cb0..23e16da 100644 --- a/app/addons/components/tests/docSpec.react.jsx +++ b/app/addons/components/tests/docSpec.react.jsx @@ -23,6 +23,12 @@ define([ describe('Document', function () { var container, el; + var doc = {}; + _.times(1000, function (n) { + doc['prop' + n] = n; + }); + var docContent = JSON.stringify(doc, null, ' '); + beforeEach(function () { container = document.createElement('div'); }); @@ -130,6 +136,39 @@ define([ ); assert.equal('"foo"', $(ReactDOM.findDOMNode(el)).find('.header-doc-id').text()); }); + + it('small docs should not be truncated', function () { + el = TestUtils.renderIntoDocument( + <ReactComponents.Document header="foo" isDeletable={true} checked={true} docIdentifier="foo" docContent='{ "content": true }' />, + container + ); + assert.equal($(ReactDOM.findDOMNode(el)).find('.doc-content-truncated').length, 0); + }); + + it('large docs should get truncated', function () { + el = TestUtils.renderIntoDocument( + <ReactComponents.Document header="foo" isDeletable={true} checked={true} docIdentifier="foo" docContent={docContent} />, + container + ); + assert.equal($(ReactDOM.findDOMNode(el)).find('.doc-content-truncated').length, 1); + }); + + it('custom truncate value', function () { + el = TestUtils.renderIntoDocument( + <ReactComponents.Document header="foo" isDeletable={true} checked={true} docIdentifier="foo" docContent={docContent} maxRows={2000} />, + container + ); + assert.equal($(ReactDOM.findDOMNode(el)).find('.doc-content-truncated').length, 0); + }); + + it('disabling truncation', function () { + el = TestUtils.renderIntoDocument( + <ReactComponents.Document header="foo" isDeletable={true} checked={true} docIdentifier="foo" docContent={docContent} truncate={false} />, + container + ); + assert.equal($(ReactDOM.findDOMNode(el)).find('.doc-content-truncated').length, 0); + }); + }); }); http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/b8c4554c/app/addons/documents/helpers.js ---------------------------------------------------------------------- diff --git a/app/addons/documents/helpers.js b/app/addons/documents/helpers.js index 32fe4f2..c6a1da0 100644 --- a/app/addons/documents/helpers.js +++ b/app/addons/documents/helpers.js @@ -96,12 +96,28 @@ define([ }]; } + function truncateDoc (docString, maxRows) { + var lines = docString.split('\n'); + var isTruncated = false; + if (lines.length > maxRows) { + isTruncated = true; + lines = lines.slice(0, maxRows); + docString = lines.join('\n'); + } + return { + isTruncated: isTruncated, + content: docString + }; + } + + return { getPreviousPageForDoc: getPreviousPageForDoc, getPreviousPage: getPreviousPage, getSeqNum: getSeqNum, getNewButtonLinks: getNewButtonLinks, getModifyDatabaseLinks: getModifyDatabaseLinks, - parseJSON: parseJSON + parseJSON: parseJSON, + truncateDoc: truncateDoc }; }); http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/b8c4554c/app/addons/documents/tests/helpersSpec.js ---------------------------------------------------------------------- diff --git a/app/addons/documents/tests/helpersSpec.js b/app/addons/documents/tests/helpersSpec.js index 0f2ba29..ccbdfad 100644 --- a/app/addons/documents/tests/helpersSpec.js +++ b/app/addons/documents/tests/helpersSpec.js @@ -26,6 +26,27 @@ define([ }); }); + describe('truncateDoc', function () { + var sevenLineDoc = '{\n"line2": 2,\n"line3": 3,\n"line4": 4,\n"line5": 5,\n"line6": 6\n}'; + + it('does no truncation if maxRows set higher than doc', function () { + var result = Helpers.truncateDoc(sevenLineDoc, 10); + assert.equal(result.isTruncated, false); + assert.equal(result.content, result.content); + }); + + it('truncates by specified line count', function () { + var result = Helpers.truncateDoc(sevenLineDoc, 5); + assert.equal(result.isTruncated, true); + assert.equal(result.content, '{\n"line2": 2,\n"line3": 3,\n"line4": 4,\n"line5": 5,'); + + var result2 = Helpers.truncateDoc(sevenLineDoc, 2); + assert.equal(result2.isTruncated, true); + assert.equal(result2.content, '{\n"line2": 2,'); + }); + + }); + }); });