From 142a2dd2eb071b268c2c114b5f29820602f18486 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Fri, 5 Feb 2016 15:45:30 +0100 Subject: [PATCH] Limit comment size to 1000 in UI Whenever the limit is almost reached (90% of the length), a tooltip will appear. Once the limit is exceeded, the "Post" button will be disabled and the field will become red. --- apps/comments/css/comments.css | 10 ++- apps/comments/js/commentstabview.js | 30 ++++++- apps/comments/tests/js/commentstabviewSpec.js | 78 +++++++++++++++++++ 3 files changed, 116 insertions(+), 2 deletions(-) diff --git a/apps/comments/css/comments.css b/apps/comments/css/comments.css index b86ed38efe..85569102e1 100644 --- a/apps/comments/css/comments.css +++ b/apps/comments/css/comments.css @@ -14,7 +14,7 @@ #commentsTabView .newCommentForm .message { width: 90%; - resize: none; + resize: vertical; } #commentsTabView .newCommentForm .submitLoading { @@ -77,6 +77,14 @@ visibility: hidden; } +#commentsTabView .message.error { + color: #e9322d; + border-color: #e9322d; + -webkit-box-shadow: 0 0 6px #f8b9b7; + -moz-box-shadow: 0 0 6px #f8b9b7; + box-shadow: 0 0 6px #f8b9b7; +} + .app-files .action-comment>img { margin-right: 5px; } diff --git a/apps/comments/js/commentstabview.js b/apps/comments/js/commentstabview.js index 8faf98b35a..d75cf39538 100644 --- a/apps/comments/js/commentstabview.js +++ b/apps/comments/js/commentstabview.js @@ -71,6 +71,8 @@ 'click .cancel': '_onClickCloseComment' }, + _commentMaxLength: 1000, + initialize: function() { OCA.Files.DetailTabView.prototype.initialize.apply(this, arguments); this.collection = new OCA.Comments.CommentCollection(); @@ -80,7 +82,10 @@ this._avatarsEnabled = !!OC.config.enable_avatars; + this._commentMaxThreshold = this._commentMaxLength * 0.9; + // TODO: error handling + _.bindAll(this, '_onTypeComment'); }, template: function(params) { @@ -162,6 +167,7 @@ this.$el.find('.avatar').avatar(OC.getCurrentUser().uid, 28); } this.delegateEvents(); + this.$el.find('textarea').on('keyup input change', this._onTypeComment); }, _formatItem: function(commentModel) { @@ -262,6 +268,7 @@ // spawn form $comment.after($formRow); $formRow.data('commentEl', $comment); + $formRow.find('textarea').on('keyup input change', this._onTypeComment); // copy avatar element from original to avoid flickering $formRow.find('.avatar').replaceWith($comment.find('.avatar').clone()); @@ -270,6 +277,27 @@ return false; }, + _onTypeComment: function(ev) { + var $field = $(ev.target); + var len = $field.val().length; + var $submitButton = $field.data('submitButtonEl'); + if (!$submitButton) { + $submitButton = $field.closest('form').find('.submit'); + $field.data('submitButtonEl', $submitButton); + } + $field.tooltip('hide'); + if (len > this._commentMaxThreshold) { + $field.attr('data-original-title', t('comments', 'Allowed characters {count} of {max}', {count: len, max: this._commentMaxLength})); + $field.tooltip({trigger: 'manual'}); + $field.tooltip('show'); + $field.addClass('error'); + } + + var limitExceeded = (len > this._commentMaxLength); + $field.toggleClass('error', limitExceeded); + $submitButton.prop('disabled', limitExceeded); + }, + _onClickCloseComment: function(ev) { ev.preventDefault(); var $row = $(ev.target).closest('.comment'); @@ -318,7 +346,7 @@ var message = $textArea.val().trim(); e.preventDefault(); - if (!message.length) { + if (!message.length || message.length > this._commentMaxLength) { return; } diff --git a/apps/comments/tests/js/commentstabviewSpec.js b/apps/comments/tests/js/commentstabviewSpec.js index 9e986899f7..70930da752 100644 --- a/apps/comments/tests/js/commentstabviewSpec.js +++ b/apps/comments/tests/js/commentstabviewSpec.js @@ -25,6 +25,20 @@ describe('OCA.Comments.CommentsTabView tests', function() { var testComments; var clock; + /** + * Creates a dummy message with the given length + * + * @param {int} len length + * @return {string} message + */ + function createMessageWithLength(len) { + var bigMessage = ''; + for (var i = 0; i < len; i++) { + bigMessage += 'a'; + } + return bigMessage; + } + beforeEach(function() { clock = sinon.useFakeTimers(Date.UTC(2016, 1, 3, 10, 5, 9)); fetchStub = sinon.stub(OCA.Comments.CommentCollection.prototype, 'fetchNext'); @@ -201,7 +215,55 @@ describe('OCA.Comments.CommentsTabView tests', function() { expect(createStub.notCalled).toEqual(true); }); + it('does not create a comment if the field length is too large', function() { + var bigMessage = ''; + for (var i = 0; i < view._commentMaxLength * 2; i++) { + bigMessage += 'a'; + } + view.$el.find('.message').val(bigMessage); + view.$el.find('form').submit(); + expect(createStub.notCalled).toEqual(true); + }); + describe('limit indicator', function() { + var tooltipStub; + var $message; + var $submitButton; + + beforeEach(function() { + tooltipStub = sinon.stub($.fn, 'tooltip'); + $message = view.$el.find('.message'); + $submitButton = view.$el.find('.submit'); + }); + afterEach(function() { + tooltipStub.restore(); + }); + + it('does not displays tooltip when limit is far away', function() { + $message.val(createMessageWithLength(3)); + $message.trigger('change'); + + expect(tooltipStub.calledWith('show')).toEqual(false); + expect($submitButton.prop('disabled')).toEqual(false); + expect($message.hasClass('error')).toEqual(false); + }); + it('displays tooltip when limit is almost reached', function() { + $message.val(createMessageWithLength(view._commentMaxLength - 2)); + $message.trigger('change'); + + expect(tooltipStub.calledWith('show')).toEqual(true); + expect($submitButton.prop('disabled')).toEqual(false); + expect($message.hasClass('error')).toEqual(false); + }); + it('displays tooltip and disabled button when limit is exceeded', function() { + $message.val(createMessageWithLength(view._commentMaxLength + 2)); + $message.trigger('change'); + + expect(tooltipStub.calledWith('show')).toEqual(true); + expect($submitButton.prop('disabled')).toEqual(true); + expect($message.hasClass('error')).toEqual(true); + }); + }); }); describe('editing comments', function() { var saveStub; @@ -336,6 +398,22 @@ describe('OCA.Comments.CommentsTabView tests', function() { destroyStub.restore(); }); + it('does not submit comment if the field is empty', function() { + var $comment = view.$el.find('.comment[data-id=1]'); + $comment.find('.action.edit').click(); + $comment.find('.message').val(' '); + $comment.find('form').submit(); + + expect(saveStub.notCalled).toEqual(true); + }); + it('does not submit comment if the field length is too large', function() { + var $comment = view.$el.find('.comment[data-id=1]'); + $comment.find('.action.edit').click(); + $comment.find('.message').val(createMessageWithLength(view._commentMaxLength * 2)); + $comment.find('form').submit(); + + expect(saveStub.notCalled).toEqual(true); + }); }); describe('read marker', function() { var updateMarkerStub;