From 8c90e5ac46d633e91fa3c7db8a8d82f5f84ce9ea Mon Sep 17 00:00:00 2001
From: Augier <contact@c-henry.fr>
Date: Sat, 1 Oct 2016 09:18:18 +0200
Subject: [PATCH] Search suggestion can be opened in a new tab + displays
 contact's hovercard

closes #7134
---
 Changelog.md                                  |  1 +
 .../app/views/publisher/mention_view.js       |  2 +-
 .../javascripts/app/views/search_base_view.js |  8 ++--
 .../javascripts/app/views/search_view.js      |  3 +-
 app/assets/stylesheets/typeahead.scss         | 11 +++++
 .../templates/search_suggestion_tpl.jst.hbs   | 17 +++++--
 .../app/views/search_base_view_spec.js        | 47 +++++++++++++++++--
 7 files changed, 76 insertions(+), 13 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index 7f41e3b27e..3fed23c44f 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -46,6 +46,7 @@ Note: Although this is a minor release, the configuration file changed because t
 * Directly link to a comment in commented notification mails [#7124](https://github.com/diaspora/diaspora/pull/7124)
 * Add optional `Content-Security-Policy` header [#7128](https://github.com/diaspora/diaspora/pull/7128)
 * Add links to main stream and public stream to the mobile drawer [#7144](https://github.com/diaspora/diaspora/pull/7144)
+* Allow opening search results from the dropdown in a new tab [#7021](https://github.com/diaspora/diaspora/issues/7021)
 
 # 0.6.0.1
 
diff --git a/app/assets/javascripts/app/views/publisher/mention_view.js b/app/assets/javascripts/app/views/publisher/mention_view.js
index b8f6143f55..75c0015ea4 100644
--- a/app/assets/javascripts/app/views/publisher/mention_view.js
+++ b/app/assets/javascripts/app/views/publisher/mention_view.js
@@ -14,7 +14,7 @@ app.views.PublisherMention = app.views.SearchBase.extend({
     "keydown #status_message_fake_text": "onInputBoxKeyDown",
     "input #status_message_fake_text": "onInputBoxInput",
     "click #status_message_fake_text": "onInputBoxClick",
-    "blur #status_message_fake_text": "onInputBoxBlur",
+    "blur #status_message_fake_text": "onInputBoxBlur"
   },
 
   initialize: function() {
diff --git a/app/assets/javascripts/app/views/search_base_view.js b/app/assets/javascripts/app/views/search_base_view.js
index 5769c7fb89..44277b8510 100644
--- a/app/assets/javascripts/app/views/search_base_view.js
+++ b/app/assets/javascripts/app/views/search_base_view.js
@@ -2,6 +2,7 @@ app.views.SearchBase = app.views.Base.extend({
   initialize: function(options) {
     this.ignoreDiasporaIds = [];
     this.typeaheadInput = options.typeaheadInput;
+    this.suggestionLink = options.suggestionLink || false;
     this.setupBloodhound(options);
     if(options.customSearch) { this.setupCustomSearch(); }
     this.setupTypeahead();
@@ -31,7 +32,7 @@ app.views.SearchBase = app.views.Base.extend({
       bloodhoundOptions.remote = {
         url: options.remoteRoute + ".json?q=%QUERY",
         wildcard: "%QUERY",
-        transform: this.transformBloodhoundResponse
+        transform: this.transformBloodhoundResponse.bind(this)
       };
     }
 
@@ -75,6 +76,7 @@ app.views.SearchBase = app.views.Base.extend({
       // person
       if(data.handle) {
         data.person = true;
+        data.link = this.suggestionLink;
         return data;
       }
 
@@ -84,7 +86,7 @@ app.views.SearchBase = app.views.Base.extend({
         name: data.name,
         url: Routes.tag(data.name.substring(1))
       };
-    });
+    }.bind(this));
   },
 
   _deselectAllSuggestions: function() {
@@ -106,5 +108,5 @@ app.views.SearchBase = app.views.Base.extend({
 
   ignorePersonForSuggestions: function(person) {
     if(person.handle) { this.ignoreDiasporaIds.push(person.handle); }
-  },
+  }
 });
diff --git a/app/assets/javascripts/app/views/search_view.js b/app/assets/javascripts/app/views/search_view.js
index 254bcb071a..b2486659b9 100644
--- a/app/assets/javascripts/app/views/search_view.js
+++ b/app/assets/javascripts/app/views/search_view.js
@@ -10,7 +10,8 @@ app.views.Search = app.views.SearchBase.extend({
     this.searchInput = this.$("#q");
     app.views.SearchBase.prototype.initialize.call(this, {
       typeaheadInput: this.searchInput,
-      remoteRoute: this.$el.attr("action")
+      remoteRoute: this.$el.attr("action"),
+      suggestionLink: true
     });
     this.searchInput.on("typeahead:select", this.suggestionSelected);
   },
diff --git a/app/assets/stylesheets/typeahead.scss b/app/assets/stylesheets/typeahead.scss
index 7f427c0972..af635352b3 100644
--- a/app/assets/stylesheets/typeahead.scss
+++ b/app/assets/stylesheets/typeahead.scss
@@ -31,4 +31,15 @@
     padding: 8px 20px;
     .name { line-height: 25px; }
   }
+
+  &.search-suggestion-person,
+  &.search-suggestion-hashtag {
+    display: block;
+
+    &:hover,
+    *:hover {
+      color: $white;
+      text-decoration: none;
+    }
+  }
 }
diff --git a/app/assets/templates/search_suggestion_tpl.jst.hbs b/app/assets/templates/search_suggestion_tpl.jst.hbs
index a2c734cab5..1eaedc9d79 100644
--- a/app/assets/templates/search_suggestion_tpl.jst.hbs
+++ b/app/assets/templates/search_suggestion_tpl.jst.hbs
@@ -1,13 +1,22 @@
 {{#if person}}
-  <div class="search-suggestion-person">
+  {{#if link}}
+    <a class="search-suggestion-person" href="{{ url }}">
+  {{else}}
+    <div class="search-suggestion-person">
+  {{/if}}
     {{#if avatar}}
       <img src="{{ avatar }}" class="avatar pull-left">
     {{/if}}
     <div class="name">{{ name }}</div>
     <div class="diaspora-id">{{ handle }}</div>
-  </div>
+  {{#if link}}
+    </a>
+  {{else}}
+    </div>
+  {{/if}}
+
 {{else}}{{#if hashtag}}
-  <div class="search-suggestion-hashtag">
+  <a class="search-suggestion-hashtag" href="{{ url }}">
     <div class="name">{{ name }}</div>
-  </div>
+  </a>
 {{/if}}{{/if}}
diff --git a/spec/javascripts/app/views/search_base_view_spec.js b/spec/javascripts/app/views/search_base_view_spec.js
index f53b52a1f4..905c17da0d 100644
--- a/spec/javascripts/app/views/search_base_view_spec.js
+++ b/spec/javascripts/app/views/search_base_view_spec.js
@@ -11,8 +11,8 @@ describe("app.views.SearchBase", function() {
       view.$("#q").trigger("focus");
     };
     this.bloodhoundData = [
-      {"person": true, "name": "user1", "handle": "user1@pod.tld"},
-      {"person": true, "name": "user2", "handle": "user2@pod.tld"}
+      {"person": true, "name": "user1", "handle": "user1@pod.tld", url: "/people/1"},
+      {"person": true, "name": "user2", "handle": "user2@pod.tld", url: "/people/2"}
     ];
   });
 
@@ -139,8 +139,8 @@ describe("app.views.SearchBase", function() {
 
       it("sets data.person to true", function() {
         expect(this.view.transformBloodhoundResponse(this.response)).toEqual([
-         {name: "Person", handle: "person@pod.tld", person: true},
-         {name: "User", handle: "user@pod.tld", person: true}
+         {name: "Person", handle: "person@pod.tld", person: true, link: false},
+         {name: "User", handle: "user@pod.tld", person: true, link: false}
         ]);
       });
     });
@@ -157,6 +157,25 @@ describe("app.views.SearchBase", function() {
         ]);
       });
     });
+
+    context("with suggestionLink option set to true", function() {
+      beforeEach(function() {
+        this.view = new app.views.SearchBase({
+          el: "#search_people_form",
+          typeaheadInput: $("#q"),
+          suggestionLink: true
+        });
+
+        this.response = [{name: "Person", handle: "person@pod.tld"}, {name: "User", handle: "user@pod.tld"}];
+      });
+
+      it("sets data.link to true", function() {
+        expect(this.view.transformBloodhoundResponse(this.response)).toEqual([
+          {name: "Person", handle: "person@pod.tld", person: true, link: true},
+          {name: "User", handle: "user@pod.tld", person: true, link: true}
+        ]);
+      });
+    });
   });
 
   describe("typeahead mouse events", function() {
@@ -262,4 +281,24 @@ describe("app.views.SearchBase", function() {
       expect(this.view.ignoreDiasporaIds.length).toBe(0);
     });
   });
+
+  describe("render results", function() {
+    beforeEach(function() {
+      this.view = new app.views.SearchBase({
+        el: "#search_people_form",
+        typeaheadInput: $("#q"),
+        autoselect: true,
+        suggestionLink: true
+      });
+
+      this.view.bloodhound.add(this.view.transformBloodhoundResponse(this.bloodhoundData));
+    });
+
+    it("produces a link when initialized with suggestionLink option set to true", function() {
+      this.view.typeaheadInput.typeahead("val", "user");
+      this.view.typeaheadInput.typeahead("open");
+      expect(this.view.suggestionLink).toBe(true);
+      expect($(".search-suggestion-person").first().is("a")).toBe(true);
+    });
+  });
 });
-- 
GitLab