Add long range comment chip

https://imgur.com/a/6pK4RnS

Change-Id: Ib524ee0bd837d8a02c0d4276e7b9ce5593d38874
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-utils.ts b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-utils.ts
index 038153a..1e5a8d3 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-utils.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-utils.ts
@@ -39,6 +39,10 @@
   );
 }
 
+export function isLongCommentRange(range: CommentRange): boolean {
+  return range.end_line - range.start_line > 5;
+}
+
 export function getLineNumber(lineEl?: Element | null): LineNumber | null {
   if (!lineEl) return null;
   const lineNumberStr = lineEl.getAttribute('data-value');
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.ts b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.ts
index 39d645e..661d5d9 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.ts
@@ -22,6 +22,7 @@
 import '../gr-diff-selection/gr-diff-selection';
 import '../gr-syntax-themes/gr-syntax-theme';
 import '../gr-ranged-comment-themes/gr-ranged-comment-theme';
+import '../gr-ranged-comment-chip/gr-ranged-comment-chip';
 import {PolymerElement} from '@polymer/polymer/polymer-element';
 import {dom, EventApi} from '@polymer/polymer/lib/legacy/polymer.dom';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners';
@@ -34,6 +35,7 @@
   getRange,
   getSide,
   GrDiffThreadElement,
+  isLongCommentRange,
   isThreadEl,
   rangesEqual,
 } from './gr-diff-utils';
@@ -873,6 +875,7 @@
       for (const threadEl of addedThreadEls) {
         const lineNum = getLine(threadEl);
         const commentSide = getSide(threadEl);
+        const range = getRange(threadEl);
         if (!commentSide) continue;
         const lineEl = this.$.diffBuilder.getLineElByNumber(
           lineNum,
@@ -896,6 +899,18 @@
           contentEl,
           commentSide
         );
+
+        const slotAtt = threadEl.getAttribute('slot');
+        if (range && isLongCommentRange(range) && slotAtt) {
+          const longRangeCommentChip = document.createElement(
+            'gr-ranged-comment-chip'
+          );
+          longRangeCommentChip.range = range;
+          longRangeCommentChip.setAttribute('threadElRootId', threadEl.rootId);
+          longRangeCommentChip.setAttribute('slot', slotAtt);
+          this.insertBefore(longRangeCommentChip, threadEl);
+        }
+
         // Create a slot for the thread and attach it to the thread group.
         // The Polyfill has some bugs and this only works if the slot is
         // attached to the group after the group is attached to the DOM.
@@ -903,7 +918,6 @@
         // that is okay because the first matching slot is used and the rest
         // are ignored.
         const slot = document.createElement('slot') as HTMLSlotElement;
-        const slotAtt = threadEl.getAttribute('slot');
         if (slotAtt) slot.name = slotAtt;
         threadGroupEl.appendChild(slot);
         lastEl = threadEl;
@@ -915,6 +929,13 @@
       if (lastEl && lastEl.replaceWith) {
         lastEl.replaceWith(lastEl);
       }
+
+      const removedThreadEls = info.removedNodes.filter(isThreadEl);
+      for (const threadEl of removedThreadEls) {
+        this.querySelector(
+          `gr-ranged-comment-chip[threadElRootId="${threadEl.rootId}"]`
+        )?.remove();
+      }
     });
   }
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
index c5a8db4..da893c9 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
@@ -19,7 +19,6 @@
 import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
 import {getMockDiffResponse} from '../../../test/mocks/diff-response.js';
 import './gr-diff.js';
-import {flush} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 import {GrDiffBuilderImage} from '../gr-diff-builder/gr-diff-builder-image.js';
 import {getComputedStyleValue} from '../../../utils/dom-util.js';
 import {_setHiddenScroll} from '../../../scripts/hiddenscroll.js';
@@ -678,6 +677,64 @@
       assert.isFalse(element._createComment.called);
     });
 
+    test('adds long range comment chip', async () => {
+      const range = {
+        start_line: 1,
+        end_line: 7,
+        start_character: 0,
+        end_character: 0,
+      };
+      const threadEl = document.createElement('div');
+      threadEl.className = 'comment-thread';
+      threadEl.setAttribute('diff-side', 'right');
+      threadEl.setAttribute('line-num', 1);
+      threadEl.setAttribute('range', JSON.stringify(range));
+      threadEl.setAttribute('slot', 'right-1');
+      const content = [{
+        a: [],
+        b: [],
+      }, {
+        ab: Array(8).fill('text'),
+      }];
+      setupSampleDiff({content});
+
+      element.appendChild(threadEl);
+      await flush();
+
+      assert.deepEqual(
+          element.querySelector('gr-ranged-comment-chip').range, range);
+    });
+
+    test('removes long range comment chip when comment is discarded',
+        async () => {
+          const range = {
+            start_line: 1,
+            end_line: 7,
+            start_character: 0,
+            end_character: 0,
+          };
+          const threadEl = document.createElement('div');
+          threadEl.className = 'comment-thread';
+          threadEl.setAttribute('diff-side', 'right');
+          threadEl.setAttribute('line-num', 1);
+          threadEl.setAttribute('range', JSON.stringify(range));
+          threadEl.setAttribute('slot', 'right-1');
+          const content = [{
+            a: [],
+            b: [],
+          }, {
+            ab: Array(8).fill('text'),
+          }];
+          setupSampleDiff({content});
+          element.appendChild(threadEl);
+          await flush();
+
+          threadEl.remove();
+          await flush();
+
+          assert.isEmpty(element.querySelectorAll('gr-ranged-comment-chip'));
+        });
+
     suite('change in preferences', () => {
       setup(() => {
         element.diff = {