launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #16100
[Merge] lp:~stevenk/launchpad/inline-diff-comments-ui into lp:launchpad
Steve Kowalik has proposed merging lp:~stevenk/launchpad/inline-diff-comments-ui into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~stevenk/launchpad/inline-diff-comments-ui/+merge/184006
Add the fully JavaScript UI for inline comments for diffs. This is protected by a feature flag, so should be safe to land but will change a little, since the server side bits to populate existing comments, save draft comments, and publish them require DB changes and modeling.
I have more then enough LoC to eat the net-positive cost of this branch.
--
https://code.launchpad.net/~stevenk/launchpad/inline-diff-comments-ui/+merge/184006
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stevenk/launchpad/inline-diff-comments-ui into lp:launchpad.
=== modified file 'lib/canonical/launchpad/icing/style.css'
--- lib/canonical/launchpad/icing/style.css 2012-08-09 04:56:41 +0000
+++ lib/canonical/launchpad/icing/style.css 2013-09-17 06:16:26 +0000
@@ -628,6 +628,9 @@
margin: 0 0 0 0;
}
+tr.ict-header {
+ background-color: #EEEEEE;
+}
/* === Bugs === */
=== modified file 'lib/lp/app/browser/stringformatter.py'
--- lib/lp/app/browser/stringformatter.py 2012-12-11 05:41:38 +0000
+++ lib/lp/app/browser/stringformatter.py 2013-09-17 06:16:26 +0000
@@ -878,7 +878,7 @@
max_format_lines = config.diff.max_format_lines
header_next = False
for row, line in enumerate(text.splitlines()[:max_format_lines]):
- result.append('<tr>')
+ result.append('<tr id="diff-line-%s">' % (row + 1))
result.append('<td class="line-no">%s</td>' % (row + 1))
if line.startswith('==='):
css_class = 'diff-file text'
=== modified file 'lib/lp/code/browser/branchmergeproposal.py'
--- lib/lp/code/browser/branchmergeproposal.py 2013-04-10 08:09:05 +0000
+++ lib/lp/code/browser/branchmergeproposal.py 2013-09-17 06:16:26 +0000
@@ -623,8 +623,10 @@
self.context.source_branch.unique_name),
})
if getFeatureFlag("longpoll.merge_proposals.enabled"):
- cache.objects['merge_proposal_event_key'] = (
- subscribe(self.context).event_key)
+ cache.objects['merge_proposal_event_key'] = subscribe(
+ self.context).event_key
+ if getFeatureFlag("code.inline_diff_comments.enabled"):
+ cache.objects['inline_diff_comments'] = True
@action('Claim', name='claim')
def claim_action(self, action, data):
=== added file 'lib/lp/code/javascript/branchmergeproposal.inlinecomments.js'
--- lib/lp/code/javascript/branchmergeproposal.inlinecomments.js 1970-01-01 00:00:00 +0000
+++ lib/lp/code/javascript/branchmergeproposal.inlinecomments.js 2013-09-17 06:16:26 +0000
@@ -0,0 +1,132 @@
+/* Copyright 2013 Canonical Ltd. This software is licensed under the
+ * GNU Affero General Public License version 3 (see the file LICENSE).
+ *
+ * Code for handling inline comments in diffs.
+ *
+ * @module lp.code.branchmergeproposal.inlinecomments
+ * @requires node
+ */
+
+YUI.add('lp.code.branchmergeproposal.inlinecomments', function(Y) {
+
+// Grab the namespace in order to be able to expose the connect methods.
+var namespace = Y.namespace('lp.code.branchmergeproposal.inlinecomments');
+
+namespace.add_doubleclick_handler = function() {
+ var inlinecomments = {};
+ var rows = Y.one('.diff').all('tr');
+ var handling_request = false;
+ handler = function(e) {
+ if (handling_request === true) {
+ return;
+ }
+ handling_request = true;
+ var linenumberdata = e.currentTarget.one('.line-no');
+ var rownumber = linenumberdata.get('text');
+ var rows = namespace.create_or_return_row(rownumber, null, null, null);
+ var headerrow = rows[0];
+ var newrow = rows[1];
+ var widget = new Y.EditableText({
+ contentBox: newrow.one('#inlinecomment-' + rownumber + '-draft'),
+ initial_value_override: inlinecomments[rownumber],
+ accept_empty: true,
+ multiline: true,
+ buttons: 'top'
+ });
+ widget.render();
+ handle_widget_button = function(saved, comment) {
+ if (saved === true) {
+ inlinecomments[rownumber] = comment;
+ }
+ if (comment === '') {
+ headerrow.remove(true);
+ newrow.remove(true);
+ }
+ handling_request = false;
+ };
+ widget.editor.on('save', function() {
+ handle_widget_button(true, this.get('value'));
+ });
+ widget.editor.on('cancel', function(e) {
+ handle_widget_button(false, this.get('value'));
+ });
+ widget._triggerEdit(e);
+ };
+ rows.on('dblclick', handler);
+};
+
+namespace.create_or_return_row = function(rownumber, person, comment, date) {
+ var suffix = '-draft';
+ var middle = rownumber + suffix;
+ var draft = true;
+ if (person !== null) {
+ draft = false;
+ }
+ var headerrow = null;
+ if (draft === true) {
+ headerrow = Y.one('#ict-' + middle + '-header');
+ if (headerrow !== null) {
+ return [headerrow, headerrow.next()];
+ }
+ }
+ headerrow = Y.Node.create(
+ '<tr><td colspan="2"></td></tr>').addClass('ict-header');
+ var headerspan;
+ if (person !== null && date !== null) {
+ headerspan = Y.Node.create(
+ '<span>Comment by <a></a> on <span></span></span>');
+ headerspan.one('a').set('href', person.web_link).set(
+ 'text', person.display_name + ' (' + person.name + ')');
+ headerspan.one('span').set('text', date);
+ } else {
+ headerspan = Y.Node.create('<span></span>').set(
+ 'text', 'Draft comment.');
+ }
+ headerrow.one('td').appendChild(headerspan);
+ if (draft === true) {
+ headerrow.set('id', 'ict-' + middle + '-header');
+ newrow = Y.Node.create('<tr><td></td><td><div>' +
+ '<span class="yui3-editable_text-text"></span>' +
+ '<div class="yui3-editable_text-trigger"></div>' +
+ '</div></td></tr>').set('id', 'ict-' + middle);
+ newrow.one('td>div').set('id', 'inlinecomment-' + middle);
+ } else {
+ newrow = Y.Node.create('<tr><td></td><td><span></span></td></tr>');
+ newrow.one('span').set('text', comment);
+ }
+ // We want to have the comments in order after the line, so grab the
+ // next row.
+ var tr = Y.one('#diff-line-' + (parseInt(rownumber, 10) + 1));
+ if (tr !== null) {
+ tr.insert(headerrow, 'before');
+ } else {
+ // If this is the last row, grab the last child.
+ tr = Y.one('.diff>tbody>tr:last-child');
+ tr.insert(headerrow, 'after');
+ }
+ // The header row is the tricky one to place, the comment just goes
+ // after it.
+ headerrow.insert(newrow, 'after');
+ return [headerrow, newrow];
+};
+
+namespace.populate_existing_comments = function() {
+ var i;
+ for (i = 0; i < LP.cache.published_inline_comments.length; i++) {
+ var row = LP.cache.published_inline_comments[i];
+ namespace.create_or_return_row(
+ row.line, row.person, row.comment, row.date);
+ }
+};
+
+namespace.setup_inline_comments = function() {
+ if (LP.cache.inline_diff_comments === true) {
+ // Add the double-click handler for each row in the diff. This needs
+ // to be done first since populating existing published and draft
+ // comments may add more rows.
+ namespace.add_doubleclick_handler();
+ namespace.populate_existing_comments();
+ }
+};
+
+ }, '0.1', {requires: ['event', 'io', 'node', 'widget', 'lp.ui.editor']});
=== added file 'lib/lp/code/javascript/tests/test_branchmergeproposal.inlinecomments.html'
--- lib/lp/code/javascript/tests/test_branchmergeproposal.inlinecomments.html 1970-01-01 00:00:00 +0000
+++ lib/lp/code/javascript/tests/test_branchmergeproposal.inlinecomments.html 2013-09-17 06:16:26 +0000
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<!--
+Copyright 2013 Canonical Ltd. This software is licensed under the
+GNU Affero General Public License version 3 (see the file LICENSE).
+-->
+
+<html>
+ <head>
+ <title>code.branchmergeproposal.inlinecomments Tests</title>
+
+ <!-- YUI and test setup -->
+ <script type="text/javascript"
+ src="../../../../../build/js/yui/yui/yui.js">
+ </script>
+ <link rel="stylesheet"
+ href="../../../../../build/js/yui/console/assets/console-core.css" />
+ <link rel="stylesheet"
+ href="../../../../../build/js/yui/test-console/assets/skins/sam/test-console.css" />
+ <link rel="stylesheet"
+ href="../../../../../build/js/yui/test/assets/skins/sam/test.css" />
+
+ <script type="text/javascript"
+ src="../../../../../build/js/lp/app/testing/testrunner.js"></script>
+ <script type="text/javascript"
+ src="../../../../../build/js/lp/app/testing/helpers.js"></script>
+
+ <link rel="stylesheet" href="../../../app/javascript/testing/test.css" />
+
+ <!-- Dependencies -->
+ <script type="text/javascript"
+ src="../../../../../build/js/lp/app/ui/ui.js"></script>
+ <script type="text/javascript"
+ src="../../../../../build/js/lp/app/extras/extras.js"></script>
+ <script type="text/javascript"
+ src="../../../../../build/js/lp/app/anim/anim.js"></script>
+ <script type="text/javascript"
+ src="../../../../../build/js/lp/app/errors.js"></script>
+ <script type="text/javascript"
+ src="../../../../../build/js/lp/app/overlay/overlay.js"></script>
+ <script type="text/javascript"
+ src="../../../../../build/js/lp/app/formoverlay/formoverlay.js"></script>
+ <script type="text/javascript"
+ src="../../../../../build/js/lp/app/formwidgets/resizing_textarea.js"></script>
+ <script type="text/javascript"
+ src="../../../../../build/js/lp/app/ellipsis.js"></script>
+ <script type="text/javascript"
+ src="../../../../../build/js/lp/app/inlineedit/editor.js"></script>
+
+ <!-- The module under test. -->
+ <script type="text/javascript" src="../branchmergeproposal.inlinecomments.js"></script>
+
+ <!-- The test suite -->
+ <script type="text/javascript" src="test_branchmergeproposal.inlinecomments.js"></script>
+
+ <!-- expected variable -->
+ <script type="text/javascript">
+ var LP = {
+ cache: {}
+ };
+ </script>
+
+ </head>
+ <body class="yui3-skin-sam">
+ <ul id="suites">
+ <li>lp.code.branchmergeproposal.inlinecomments.test</li>
+ </ul>
+ <table class="diff">
+ <tbody>
+ <tr id="diff-line-1">
+ <td class="line-no">1</td>
+ <td class="diff-header text">foo bar</td>
+ </tr>
+ <tr id="diff-line-2">
+ <td class="line-no">2</td>
+ <td class="text">baz</td>
+ </tr>
+ <tr id="diff-line-3">
+ <td class="line-no">3</td>
+ <td class="text">quux</td>
+ </tr>
+ </tbody>
+ </table>
+ </body>
+</html>
=== added file 'lib/lp/code/javascript/tests/test_branchmergeproposal.inlinecomments.js'
--- lib/lp/code/javascript/tests/test_branchmergeproposal.inlinecomments.js 1970-01-01 00:00:00 +0000
+++ lib/lp/code/javascript/tests/test_branchmergeproposal.inlinecomments.js 2013-09-17 06:16:26 +0000
@@ -0,0 +1,91 @@
+/* Copyright 2013 Canonical Ltd. This software is licensed under the
+ * GNU Affero General Public License version 3 (see the file LICENSE). */
+
+YUI.add('lp.code.branchmergeproposal.inlinecomments.test', function (Y) {
+
+ var module = Y.lp.code.branchmergeproposal.inlinecomments;
+ var tests = Y.namespace('lp.code.branchmergeproposal.inlinecomments.test');
+ tests.suite = new Y.Test.Suite('branchmergeproposal.inlinecomments Tests');
+
+ tests.suite.add(new Y.Test.Case({
+ name: 'code.branchmergeproposal.inlinecomments_tests',
+
+ setUp: function () {},
+ tearDown: function () {},
+
+ test_library_exists: function () {
+ Y.Assert.isObject(
+ module, "Could not locate the " +
+ "lp.code.branchmergeproposal.inlinecomments module");
+ },
+
+ test_populatation: function () {
+ var published_inline_comments = [
+ {'line': 2, 'person': {'display_name': 'Sample Person',
+ 'name': 'name.12', 'web_link': 'http://launchpad.dev/~name12'},
+ 'comment': 'This is preloaded.', 'date': '2012-08-12 17:45'}];
+ LP.cache.published_inline_comments = published_inline_comments;
+ module.populate_existing_comments();
+ var row = Y.one('#diff-line-2').next().next();
+ Y.Assert.areEqual('This is preloaded.', row.get('text'));
+ },
+
+ test_handler_normal: function() {
+ module.add_doubleclick_handler();
+ var line_2 = Y.one('#diff-line-2');
+ line_2.simulate('dblclick');
+ var comment = 'This is a comment.';
+ Y.one('.yui3-ieditor-input>textarea').set('value', comment);
+ Y.one('.lazr-pos').simulate('click');
+ Y.Assert.areEqual(
+ comment, Y.one('.yui3-editable_text-text').get('text'));
+ },
+
+ test_handler_cancel: function() {
+ var line_2 = Y.one('#diff-line-2');
+ line_2.simulate('dblclick');
+ var comment = 'Cancelling test.';
+ Y.one('.yui3-ieditor-input>textarea').set('value', comment);
+ Y.one('.lazr-pos').simulate('click');
+ line_2.simulate('dblclick');
+ Y.one('.yui3-ieditor-input>textarea').set('value', 'Foobar!');
+ Y.one('.lazr-neg').simulate('click');
+ Y.Assert.areEqual(
+ comment, Y.one('.yui3-editable_text-text').get('text'));
+ },
+
+ test_handler_cancel_immediately: function() {
+ var line_1 = Y.one('#diff-line-1');
+ line_1.simulate('dblclick');
+ Y.one('.lazr-neg').simulate('click');
+ Y.Assert.isNull(Y.one('#ict-1-draft-header'));
+ },
+
+ test_feature_flag_off: function() {
+ var called = false;
+ add_doubleclick_handler = function() {
+ called = true;
+ };
+ module.add_doubleclick_handler = add_doubleclick_handler;
+ module.setup_inline_comments();
+ Y.Assert.isFalse(called);
+ },
+
+ test_feature_flag: function() {
+ LP.cache.published_inline_comments = [];
+ var called = false;
+ add_doubleclick_handler = function() {
+ called = true;
+ };
+ module.add_doubleclick_handler = add_doubleclick_handler;
+ window.LP.cache.inline_diff_comments = true;
+ module.setup_inline_comments();
+ Y.Assert.isTrue(called);
+ }
+
+ }));
+
+}, '0.1', {
+ requires: ['node-event-simulate', 'test', 'lp.testing.helpers', 'console',
+ 'lp.code.branchmergeproposal.inlinecomments']
+});
=== modified file 'lib/lp/code/templates/branchmergeproposal-index.pt'
--- lib/lp/code/templates/branchmergeproposal-index.pt 2012-06-28 14:19:28 +0000
+++ lib/lp/code/templates/branchmergeproposal-index.pt 2013-09-17 06:16:26 +0000
@@ -197,7 +197,8 @@
LPJS.use('io-base', 'lp.code.branchmergeproposal.reviewcomment',
'lp.code.branchmergeproposal.status', 'lp.app.comment',
'lp.code.branchmergeproposal.updater', 'lp.app.widgets.expander',
- 'lp.code.branch.revisionexpander', function(Y) {
+ 'lp.code.branch.revisionexpander',
+ 'lp.code.branchmergeproposal.inlinecomments', function(Y) {
Y.on('load', function() {
var logged_in = LP.links['me'] !== undefined;
@@ -237,6 +238,7 @@
'.expander-content',
false,
Y.lp.code.branch.revisionexpander.bmp_diff_loader);
+ Y.lp.code.branchmergeproposal.inlinecomments.setup_inline_comments();
});
});
-->
=== modified file 'lib/lp/services/features/flags.py'
--- lib/lp/services/features/flags.py 2013-05-22 09:48:07 +0000
+++ lib/lp/services/features/flags.py 2013-09-17 06:16:26 +0000
@@ -209,6 +209,12 @@
'',
'',
''),
+ ('code.inline_diff_comments.enabled',
+ 'space delimited',
+ 'Names of projects have inline diff comments enabled.',
+ '',
+ '',
+ ''),
])
# The set of all flag names that are documented.
Follow ups