Commit c8114c57 authored by Dylan Griffith's avatar Dylan Griffith Committed by Kerri Miller

Extract shared view search/results/_issuable.html.haml

At present the merge request result page has an old design and we want
it to match the issue design which was recently updated.

It seems the best way to do this was to share code so we've extracted a
shared HAML template for both of these.
parent 32cd8ac9
...@@ -9,6 +9,7 @@ class SearchController < ApplicationController ...@@ -9,6 +9,7 @@ class SearchController < ApplicationController
SCOPE_PRELOAD_METHOD = { SCOPE_PRELOAD_METHOD = {
projects: :with_web_entity_associations, projects: :with_web_entity_associations,
issues: :with_web_entity_associations, issues: :with_web_entity_associations,
merge_requests: :with_web_entity_associations,
epics: :with_web_entity_associations epics: :with_web_entity_associations
}.freeze }.freeze
......
...@@ -367,10 +367,10 @@ module SearchHelper ...@@ -367,10 +367,10 @@ module SearchHelper
end end
# _search_highlight is used in EE override # _search_highlight is used in EE override
def highlight_and_truncate_issue(issue, search_term, _search_highlight) def highlight_and_truncate_issuable(issuable, search_term, _search_highlight)
return unless issue.description.present? return unless issuable.description.present?
simple_search_highlight_and_truncate(issue.description, search_term, highlighter: '<span class="gl-text-black-normal gl-font-weight-bold">\1</span>') simple_search_highlight_and_truncate(issuable.description, search_term, highlighter: '<span class="gl-text-black-normal gl-font-weight-bold">\1</span>')
end end
def show_user_search_tab? def show_user_search_tab?
...@@ -380,6 +380,36 @@ module SearchHelper ...@@ -380,6 +380,36 @@ module SearchHelper
can?(current_user, :read_users_list) can?(current_user, :read_users_list)
end end
end end
def issuable_state_to_badge_class(issuable)
# Closed is considered "danger" for MR so we need to handle separately
if issuable.is_a?(::MergeRequest)
if issuable.merged?
:primary
elsif issuable.closed?
:danger
else
:success
end
else
if issuable.closed?
:info
else
:success
end
end
end
def issuable_state_text(issuable)
case issuable.state
when 'merged'
_("Merged")
when 'closed'
_("Closed")
else
_("Open")
end
end
end end
SearchHelper.prepend_if_ee('EE::SearchHelper') SearchHelper.prepend_if_ee('EE::SearchHelper')
...@@ -294,6 +294,7 @@ class MergeRequest < ApplicationRecord ...@@ -294,6 +294,7 @@ class MergeRequest < ApplicationRecord
scope :preload_author, -> { preload(:author) } scope :preload_author, -> { preload(:author) }
scope :preload_approved_by_users, -> { preload(:approved_by_users) } scope :preload_approved_by_users, -> { preload(:approved_by_users) }
scope :preload_metrics, -> (relation) { preload(metrics: relation) } scope :preload_metrics, -> (relation) { preload(metrics: relation) }
scope :with_web_entity_associations, -> { preload(:author, :target_project) }
scope :with_auto_merge_enabled, -> do scope :with_auto_merge_enabled, -> do
with_state(:opened).where(auto_merge_enabled: true) with_state(:opened).where(auto_merge_enabled: true)
......
%div{ class: 'search-result-row gl-pb-3! gl-mt-5 gl-mb-0!' }
%span.gl-display-flex.gl-align-items-center
%span.badge.badge-pill.gl-badge.sm{ class: "badge-#{issuable_state_to_badge_class(issuable)}" }= issuable_state_text(issuable)
= sprite_icon('eye-slash', css_class: 'gl-text-gray-500 gl-ml-2') if issuable.respond_to?(:confidential?) && issuable.confidential?
= link_to issuable_path(issuable), data: { track_event: 'click_text', track_label: "#{issuable.class.name.downcase}_title", track_property: 'search_result' }, class: 'gl-w-full' do
%span.term.str-truncated.gl-font-weight-bold.gl-ml-2= issuable.title
.gl-text-gray-500.gl-my-3
= sprintf(s_(' %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}'), { project_name: issuable.project.full_name, issuable_iid: issuable.iid, issuable_created: time_ago_with_tooltip(issuable.created_at, placement: 'bottom'), author: link_to_member(@project, issuable.author, avatar: false) }).html_safe
.description.term.col-sm-10.gl-px-0
= highlight_and_truncate_issuable(issuable, @search_term, @search_highlight)
%div{ class: 'search-result-row gl-pb-3! gl-mt-5 gl-mb-0!' } = render partial: 'search/results/issuable', object: issue
%span.gl-display-flex.gl-align-items-center
- if issue.closed?
%span.badge.badge-info.badge-pill.gl-badge.sm= _("Closed")
- else
%span.badge.badge-success.badge-pill.gl-badge.sm= _("Open")
= sprite_icon('eye-slash', css_class: 'gl-text-gray-500 gl-ml-2') if issue.confidential?
= link_to project_issue_path(issue.project, issue), data: { track_event: 'click_text', track_label: 'issue_title', track_property: 'search_result' }, class: 'gl-w-full' do
%span.term.str-truncated.gl-font-weight-bold.gl-ml-2= issue.title
.gl-text-gray-500.gl-my-3
= sprintf(s_(' %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}'), { project_name: issue.project.full_name, issue_iid: issue.iid, issue_created: time_ago_with_tooltip(issue.created_at, placement: 'bottom'), author: link_to_member(@project, issue.author, avatar: false) }).html_safe
.description.term.col-sm-10.gl-px-0
= highlight_and_truncate_issue(issue, @search_term, @search_highlight)
.search-result-row = render partial: 'search/results/issuable', object: merge_request
%h4
= link_to project_merge_request_path(merge_request.target_project, merge_request), data: {track_event: 'click_text', track_label: 'merge_request_title', track_property: 'search_result'} do
%span.term.str-truncated= merge_request.title
- if merge_request.merged?
%span.badge.badge-primary.gl-ml-2= _("Merged")
- elsif merge_request.closed?
%span.badge.badge-danger.gl-ml-2= _("Closed")
.float-right= merge_request.to_reference
- if merge_request.description.present?
.description.term
= search_md_sanitize(merge_request.description)
%span.light
#{merge_request.project.full_name}
---
title: Update merge request search results design
merge_request: 46944
author:
type: changed
...@@ -61,14 +61,14 @@ module EE ...@@ -61,14 +61,14 @@ module EE
end end
end end
override :highlight_and_truncate_issue override :highlight_and_truncate_issuable
def highlight_and_truncate_issue(issue, search_term, search_highlight) def highlight_and_truncate_issuable(issuable, search_term, search_highlight)
return super unless search_service.use_elasticsearch? && search_highlight[issue.id]&.description.present? return super unless search_service.use_elasticsearch? && search_highlight[issuable.id]&.description.present?
# We use Elasticsearch highlighting for results from Elasticsearch. Sanitize the description, replace the # We use Elasticsearch highlighting for results from Elasticsearch. Sanitize the description, replace the
# pre/post tags from Elasticsearch with highlighting, truncate, and mark as html_safe. HTML tags are not # pre/post tags from Elasticsearch with highlighting, truncate, and mark as html_safe. HTML tags are not
# counted towards the character limit. # counted towards the character limit.
text = sanitize(search_highlight[issue.id].description.first) text = sanitize(search_highlight[issuable.id].description.first)
text.gsub!(::Elastic::Latest::GitClassProxy::HIGHLIGHT_START_TAG, '<span class="gl-text-black-normal gl-font-weight-bold">') text.gsub!(::Elastic::Latest::GitClassProxy::HIGHLIGHT_START_TAG, '<span class="gl-text-black-normal gl-font-weight-bold">')
text.gsub!(::Elastic::Latest::GitClassProxy::HIGHLIGHT_END_TAG, '</span>') text.gsub!(::Elastic::Latest::GitClassProxy::HIGHLIGHT_END_TAG, '</span>')
Truncato.truncate(text, count_tags: false, count_tail: false, max_length: 200).html_safe Truncato.truncate(text, count_tags: false, count_tail: false, max_length: 200).html_safe
......
...@@ -148,7 +148,7 @@ RSpec.describe SearchHelper do ...@@ -148,7 +148,7 @@ RSpec.describe SearchHelper do
end end
end end
describe '#highlight_and_truncate_issue' do describe '#highlight_and_truncate_issuable' do
let(:description) { 'hello world' } let(:description) { 'hello world' }
let(:issue) { create(:issue, description: description) } let(:issue) { create(:issue, description: description) }
let(:user) { create(:user) } let(:user) { create(:user) }
...@@ -160,7 +160,7 @@ RSpec.describe SearchHelper do ...@@ -160,7 +160,7 @@ RSpec.describe SearchHelper do
end end
# Elasticsearch returns Elasticsearch::Model::HashWrapper class for the highlighting # Elasticsearch returns Elasticsearch::Model::HashWrapper class for the highlighting
subject { highlight_and_truncate_issue(issue, 'test', Elasticsearch::Model::HashWrapper.new(search_highlight)) } subject { highlight_and_truncate_issuable(issue, 'test', Elasticsearch::Model::HashWrapper.new(search_highlight)) }
context 'when description is not present' do context 'when description is not present' do
let(:description) { nil } let(:description) { nil }
......
...@@ -16,7 +16,7 @@ msgstr "" ...@@ -16,7 +16,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}" msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr "" msgstr ""
msgid " %{start} to %{end}" msgid " %{start} to %{end}"
......
...@@ -477,7 +477,7 @@ RSpec.describe SearchHelper do ...@@ -477,7 +477,7 @@ RSpec.describe SearchHelper do
end end
end end
describe '#highlight_and_truncate_issue' do describe '#highlight_and_truncate_issuable' do
let(:description) { 'hello world' } let(:description) { 'hello world' }
let(:issue) { create(:issue, description: description) } let(:issue) { create(:issue, description: description) }
let(:user) { create(:user) } let(:user) { create(:user) }
...@@ -486,7 +486,7 @@ RSpec.describe SearchHelper do ...@@ -486,7 +486,7 @@ RSpec.describe SearchHelper do
allow(self).to receive(:current_user).and_return(user) allow(self).to receive(:current_user).and_return(user)
end end
subject { highlight_and_truncate_issue(issue, 'test', {}) } subject { highlight_and_truncate_issuable(issue, 'test', {}) }
context 'when description is not present' do context 'when description is not present' do
let(:description) { nil } let(:description) { nil }
...@@ -545,4 +545,38 @@ RSpec.describe SearchHelper do ...@@ -545,4 +545,38 @@ RSpec.describe SearchHelper do
end end
end end
end end
describe '#issuable_state_to_badge_class' do
context 'with merge request' do
it 'returns correct badge based on status' do
expect(issuable_state_to_badge_class(build(:merge_request, :merged))).to eq(:primary)
expect(issuable_state_to_badge_class(build(:merge_request, :closed))).to eq(:danger)
expect(issuable_state_to_badge_class(build(:merge_request, :opened))).to eq(:success)
end
end
context 'with an issue' do
it 'returns correct badge based on status' do
expect(issuable_state_to_badge_class(build(:issue, :closed))).to eq(:info)
expect(issuable_state_to_badge_class(build(:issue, :opened))).to eq(:success)
end
end
end
describe '#issuable_state_text' do
context 'with merge request' do
it 'returns correct badge based on status' do
expect(issuable_state_text(build(:merge_request, :merged))).to eq(_('Merged'))
expect(issuable_state_text(build(:merge_request, :closed))).to eq(_('Closed'))
expect(issuable_state_text(build(:merge_request, :opened))).to eq(_('Open'))
end
end
context 'with an issue' do
it 'returns correct badge based on status' do
expect(issuable_state_text(build(:issue, :closed))).to eq(_('Closed'))
expect(issuable_state_text(build(:issue, :opened))).to eq(_('Open'))
end
end
end
end end
...@@ -45,14 +45,9 @@ RSpec.describe SearchController, type: :request do ...@@ -45,14 +45,9 @@ RSpec.describe SearchController, type: :request do
let(:object) { :merge_request } let(:object) { :merge_request }
let(:creation_args) { { source_project: project, title: 'bar' } } let(:creation_args) { { source_project: project, title: 'bar' } }
let(:params) { { search: 'bar', scope: 'merge_requests' } } let(:params) { { search: 'bar', scope: 'merge_requests' } }
# some N+1 queries exist # there are 4 additional queries run for the logged in user:
# each merge request require 4 extra queries for:
# - one for projects
# - one for namespaces
# - two for routes
# plus 4 additional queries run for the logged in user:
# - (1) geo_nodes, (1) users, (2) broadcast_messages # - (1) geo_nodes, (1) users, (2) broadcast_messages
let(:threshold) { 16 } let(:threshold) { 4 }
it_behaves_like 'an efficient database result' it_behaves_like 'an efficient database result'
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment