Commit 3e7d64b6 authored by Peter Hegman's avatar Peter Hegman

Merge branch 'topics/topic-detail-page' into 'master'

Project topics: Add detail page with associated projects

See merge request gitlab-org/gitlab!71830
parents 56063e97 3b325e92
......@@ -68,6 +68,15 @@ class Explore::ProjectsController < Explore::ApplicationController
end
# rubocop: enable CodeReuse/ActiveRecord
def topic
load_topic
return render_404 unless @topic
params[:topic] = @topic.name
@projects = load_projects
end
private
def load_project_counts
......@@ -86,6 +95,10 @@ class Explore::ProjectsController < Explore::ApplicationController
prepare_projects_for_rendering(projects)
end
def load_topic
@topic = Projects::Topic.find_by_name(params[:topic_name])
end
# rubocop: disable CodeReuse/ActiveRecord
def preload_associations(projects)
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 @@
= sprite_icon('tag', css_class: 'icon gl-relative gl-mr-2')
- 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
%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)
......@@ -21,7 +21,7 @@
- content = capture do
%span.gl-display-inline-flex.gl-flex-wrap
- 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
%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)
......
......@@ -5,6 +5,7 @@ namespace :explore do
collection do
get :trending
get :starred
get 'topics/:topic_name', action: :topic, as: :topic, constraints: { topic_name: /.+/ }
end
end
......
......@@ -27385,6 +27385,9 @@ msgstr ""
msgid "Projects with no vulnerabilities and security scanning enabled"
msgstr ""
msgid "Projects with this topic"
msgstr ""
msgid "Projects with write access"
msgstr ""
......
......@@ -74,6 +74,28 @@ RSpec.describe Explore::ProjectsController do
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
shared_examples "blocks high page numbers" do
......
......@@ -204,7 +204,7 @@ RSpec.describe 'Dashboard Projects' do
visit dashboard_projects_path
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
......
......@@ -133,7 +133,7 @@ RSpec.describe 'Project' do
visit path
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
it 'shows up to 3 project topics' do
......@@ -142,9 +142,9 @@ RSpec.describe 'Project' do
visit path
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('topic2', href: explore_projects_path(topic: 'topic2'))
expect(page).to have_link('topic3', href: explore_projects_path(topic: 'topic3'))
expect(page).to have_link('topic1', href: topic_explore_projects_path(topic_name: 'topic1'))
expect(page).to have_link('topic2', href: topic_explore_projects_path(topic_name: 'topic2'))
expect(page).to have_link('topic3', href: topic_explore_projects_path(topic_name: 'topic3'))
expect(page).to have_content('+ 1 more')
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