Commit 3b325e92 authored by Jonas Wälter's avatar Jonas Wälter Committed by Peter Hegman

Add 'show' page for topic

Changelog: added
parent 77ff4521
...@@ -68,6 +68,15 @@ class Explore::ProjectsController < Explore::ApplicationController ...@@ -68,6 +68,15 @@ class Explore::ProjectsController < Explore::ApplicationController
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def topic
load_topic
return render_404 unless @topic
params[:topic] = @topic.name
@projects = load_projects
end
private private
def load_project_counts def load_project_counts
...@@ -86,6 +95,10 @@ class Explore::ProjectsController < Explore::ApplicationController ...@@ -86,6 +95,10 @@ class Explore::ProjectsController < Explore::ApplicationController
prepare_projects_for_rendering(projects) prepare_projects_for_rendering(projects)
end end
def load_topic
@topic = Projects::Topic.find_by_name(params[:topic_name])
end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def preload_associations(projects) def preload_associations(projects)
projects.includes(:route, :creator, :group, :project_feature, :topics, namespace: [:route, :owner]) projects.includes(:route, :creator, :group, :project_feature, :topics, namespace: [:route, :owner])
......
- @hide_top_links = false
- @no_container = true
- page_title @topic.name, _("Topics")
- max_topic_name_length = 50
= render_dashboard_ultimate_trial(current_user)
.gl-text-center.gl-bg-gray-10.gl-pb-2.gl-pt-6
.gl-pb-5.gl-align-items-center.gl-justify-content-center.gl-display-flex
.avatar-container.s60.gl-flex-shrink-0
= topic_icon(@topic, alt: _('Topic avatar'), class: 'avatar topic-avatar s60')
- if @topic.name.length > max_topic_name_length
%h1.gl-mt-3.str-truncated.has-tooltip{ title: @topic.name }
= truncate(@topic.name, length: max_topic_name_length)
- else
%h1.gl-mt-3
= @topic.name
- if @topic.description.present?
.topic-description.gl-ml-4.gl-mr-4
= markdown(@topic.description)
%div{ class: container_class }
.gl-py-5.gl-border-gray-100.gl-border-b-solid.gl-border-b-1
%h3.gl-m-0= _('Projects with this topic')
.top-area.gl-pt-2.gl-pb-2
.nav-controls
= render 'shared/projects/search_form'
= render 'shared/projects/dropdown'
= render 'filter'
= render 'projects', projects: @projects
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
= sprite_icon('tag', css_class: 'icon gl-relative gl-mr-2') = sprite_icon('tag', css_class: 'icon gl-relative gl-mr-2')
- project.topics_to_show.each do |topic| - project.topics_to_show.each do |topic|
- explore_project_topic_path = explore_projects_path(topic: topic) - explore_project_topic_path = topic_explore_projects_path(topic_name: topic)
- if topic.length > max_project_topic_length - if topic.length > max_project_topic_length
%a{ class: "#{ project_topics_classes } str-truncated-30 has-tooltip", data: { container: "body" }, title: topic, href: explore_project_topic_path, itemprop: 'keywords' } %a{ class: "#{ project_topics_classes } str-truncated-30 has-tooltip", data: { container: "body" }, title: topic, href: explore_project_topic_path, itemprop: 'keywords' }
= truncate(topic, length: max_project_topic_length) = truncate(topic, length: max_project_topic_length)
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
- content = capture do - content = capture do
%span.gl-display-inline-flex.gl-flex-wrap %span.gl-display-inline-flex.gl-flex-wrap
- project.topics_not_shown.each do |topic| - project.topics_not_shown.each do |topic|
- explore_project_topic_path = explore_projects_path(topic: topic) - explore_project_topic_path = topic_explore_projects_path(topic_name: topic)
- if topic.length > max_project_topic_length - if topic.length > max_project_topic_length
%a{ class: "#{ project_topics_classes } gl-mb-3 str-truncated has-tooltip", data: { container: "body" }, title: topic, href: explore_project_topic_path, itemprop: 'keywords' } %a{ class: "#{ project_topics_classes } gl-mb-3 str-truncated has-tooltip", data: { container: "body" }, title: topic, href: explore_project_topic_path, itemprop: 'keywords' }
= truncate(topic, length: max_project_topic_length) = truncate(topic, length: max_project_topic_length)
......
...@@ -5,6 +5,7 @@ namespace :explore do ...@@ -5,6 +5,7 @@ namespace :explore do
collection do collection do
get :trending get :trending
get :starred get :starred
get 'topics/:topic_name', action: :topic, as: :topic, constraints: { topic_name: /.+/ }
end end
end end
......
...@@ -27391,6 +27391,9 @@ msgstr "" ...@@ -27391,6 +27391,9 @@ msgstr ""
msgid "Projects with no vulnerabilities and security scanning enabled" msgid "Projects with no vulnerabilities and security scanning enabled"
msgstr "" msgstr ""
msgid "Projects with this topic"
msgstr ""
msgid "Projects with write access" msgid "Projects with write access"
msgstr "" msgstr ""
......
...@@ -74,6 +74,28 @@ RSpec.describe Explore::ProjectsController do ...@@ -74,6 +74,28 @@ RSpec.describe Explore::ProjectsController do
end end
end end
end end
describe 'GET #topic' do
context 'when topic does not exist' do
it 'renders a 404 error' do
get :topic, params: { topic_name: 'topic1' }
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when topic exists' do
before do
create(:topic, name: 'topic1')
end
it 'renders the template' do
get :topic, params: { topic_name: 'topic1' }
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template('topic')
end
end
end
end end
shared_examples "blocks high page numbers" do shared_examples "blocks high page numbers" do
......
...@@ -204,7 +204,7 @@ RSpec.describe 'Dashboard Projects' do ...@@ -204,7 +204,7 @@ RSpec.describe 'Dashboard Projects' do
visit dashboard_projects_path visit dashboard_projects_path
expect(page).to have_selector('[data-testid="project_topic_list"]') expect(page).to have_selector('[data-testid="project_topic_list"]')
expect(page).to have_link('topic1', href: explore_projects_path(topic: 'topic1')) expect(page).to have_link('topic1', href: topic_explore_projects_path(topic_name: 'topic1'))
end end
end end
......
...@@ -133,7 +133,7 @@ RSpec.describe 'Project' do ...@@ -133,7 +133,7 @@ RSpec.describe 'Project' do
visit path visit path
expect(page).to have_selector('[data-testid="project_topic_list"]') expect(page).to have_selector('[data-testid="project_topic_list"]')
expect(page).to have_link('topic1', href: explore_projects_path(topic: 'topic1')) expect(page).to have_link('topic1', href: topic_explore_projects_path(topic_name: 'topic1'))
end end
it 'shows up to 3 project topics' do it 'shows up to 3 project topics' do
...@@ -142,9 +142,9 @@ RSpec.describe 'Project' do ...@@ -142,9 +142,9 @@ RSpec.describe 'Project' do
visit path visit path
expect(page).to have_selector('[data-testid="project_topic_list"]') expect(page).to have_selector('[data-testid="project_topic_list"]')
expect(page).to have_link('topic1', href: explore_projects_path(topic: 'topic1')) expect(page).to have_link('topic1', href: topic_explore_projects_path(topic_name: 'topic1'))
expect(page).to have_link('topic2', href: explore_projects_path(topic: 'topic2')) expect(page).to have_link('topic2', href: topic_explore_projects_path(topic_name: 'topic2'))
expect(page).to have_link('topic3', href: explore_projects_path(topic: 'topic3')) expect(page).to have_link('topic3', href: topic_explore_projects_path(topic_name: 'topic3'))
expect(page).to have_content('+ 1 more') expect(page).to have_content('+ 1 more')
end end
end end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Topic show page' do
let_it_be(:topic) { create(:topic, name: 'my-topic', description: 'This is **my** topic https://google.com/ :poop: ```\ncode\n```', avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) }
context 'when topic does not exist' do
let(:path) { topic_explore_projects_path(topic_name: 'non-existing') }
it 'renders 404' do
visit path
expect(status_code).to eq(404)
end
end
context 'when topic exists' do
before do
visit topic_explore_projects_path(topic_name: topic.name)
end
it 'shows name, avatar and description as markdown' do
expect(page).to have_content(topic.name)
expect(page).to have_selector('.avatar-container > img.topic-avatar')
expect(find('.topic-description')).to have_selector('p > strong')
expect(find('.topic-description')).to have_selector('p > a[rel]')
expect(find('.topic-description')).to have_selector('p > gl-emoji')
expect(find('.topic-description')).to have_selector('p > code')
end
context 'with associated projects' do
let!(:project) { create(:project, :public, topic_list: topic.name) }
it 'shows project list' do
visit topic_explore_projects_path(topic_name: topic.name)
expect(find('.projects-list .project-name')).to have_content(project.name)
end
end
context 'without associated projects' do
it 'shows correct empty state message' do
expect(page).to have_content('Explore public groups to find projects to contribute to.')
end
end
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