Commit 01b767bd authored by Fatih Acet's avatar Fatih Acet

Merge branch '19990-update-snippets-page-design' into 'master'

Resolve "Updated UI for Snippets pages"

## What does this MR do?

## Are there points in the code the reviewer needs to double check?

## Why was this MR needed?

## Screenshots (if relevant)

## Does this MR meet the acceptance criteria?

- [ ] [Changelog entry](https://docs.gitlab.com/ce/development/changelog.html) added
- [ ] [Documentation created/updated](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/doc_styleguide.md)
- [ ] API support added
- Tests
  - [ ] Added for this feature/bug
  - [ ] All builds are passing
- [ ] Conform by the [merge request performance guides](http://docs.gitlab.com/ce/development/merge_request_performance_guidelines.html)
- [ ] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#style-guides)
- [ ] Branch has no merge conflicts with `master` (if it does - rebase it please)
- [ ] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)

## What are the relevant issue numbers?


Closes #19990

See merge request !7861
parents 927a7556 730ff2e5
.snippet-row {
.title {
margin-bottom: 2px;
}
.snippet-filename {
padding: 0 2px;
}
}
.snippet-form-holder .file-holder .file-title { .snippet-form-holder .file-holder .file-title {
padding: 2px; padding: 2px;
} }
...@@ -24,11 +34,17 @@ ...@@ -24,11 +34,17 @@
padding-bottom: $gl-padding; padding-bottom: $gl-padding;
} }
.snippet-header {
padding: $gl-padding 0;
}
.snippet-title { .snippet-title {
font-size: 24px; font-size: 24px;
font-weight: 600; font-weight: 600;
padding: $gl-padding; }
padding-left: 0;
.snippet-edited-ago {
color: $gray-darkest;
} }
.snippet-actions { .snippet-actions {
......
...@@ -19,10 +19,12 @@ class Projects::SnippetsController < Projects::ApplicationController ...@@ -19,10 +19,12 @@ class Projects::SnippetsController < Projects::ApplicationController
respond_to :html respond_to :html
def index def index
@snippets = SnippetsFinder.new.execute(current_user, { @snippets = SnippetsFinder.new.execute(
current_user,
filter: :by_project, filter: :by_project,
project: @project project: @project,
}) scope: params[:scope]
)
@snippets = @snippets.page(params[:page]) @snippets = @snippets.page(params[:page])
end end
......
...@@ -11,7 +11,7 @@ class SnippetsFinder ...@@ -11,7 +11,7 @@ class SnippetsFinder
when :by_user then when :by_user then
by_user(current_user, user, params[:scope]) by_user(current_user, user, params[:scope])
when :by_project when :by_project
by_project(current_user, params[:project]) by_project(current_user, params[:project], params[:scope])
end end
end end
...@@ -32,35 +32,35 @@ class SnippetsFinder ...@@ -32,35 +32,35 @@ class SnippetsFinder
def by_user(current_user, user, scope) def by_user(current_user, user, scope)
snippets = user.snippets.fresh snippets = user.snippets.fresh
return snippets.are_public unless current_user if current_user
include_private = user == current_user
if user == current_user by_scope(snippets, scope, include_private)
case scope
when 'are_internal' then
snippets.are_internal
when 'are_private' then
snippets.are_private
when 'are_public' then
snippets.are_public
else
snippets
end
else else
snippets.public_and_internal snippets.are_public
end end
end end
def by_project(current_user, project) def by_project(current_user, project, scope)
snippets = project.snippets.fresh snippets = project.snippets.fresh
if current_user if current_user
if project.team.member?(current_user) || current_user.admin? include_private = project.team.member?(current_user) || current_user.admin?
snippets by_scope(snippets, scope, include_private)
else
snippets.public_and_internal
end
else else
snippets.are_public snippets.are_public
end end
end end
def by_scope(snippets, scope = nil, include_private = false)
case scope.to_s
when 'are_private'
include_private ? snippets.are_private : Snippet.none
when 'are_internal'
snippets.are_internal
when 'are_public'
snippets.are_public
else
include_private ? snippets : snippets.public_and_internal
end
end
end end
...@@ -8,6 +8,17 @@ module SnippetsHelper ...@@ -8,6 +8,17 @@ module SnippetsHelper
end end
end end
# Return the path of a snippets index for a user or for a project
#
# @returns String, path to snippet index
def subject_snippets_path(subject = nil, opts = nil)
if subject.is_a?(Project)
namespace_project_snippets_path(subject.namespace, subject, opts)
else # assume subject === User
dashboard_snippets_path(opts)
end
end
# Get an array of line numbers surrounding a matching # Get an array of line numbers surrounding a matching
# line, bounded by min/max. # line, bounded by min/max.
# #
......
%ul.nav-links .top-area
= nav_link(page: dashboard_snippets_path, html_options: {class: 'home'}) do %ul.nav-links
= link_to dashboard_snippets_path, title: 'Your snippets', data: {placement: 'right'} do = nav_link(page: dashboard_snippets_path, html_options: {class: 'home'}) do
Your Snippets = link_to dashboard_snippets_path, title: 'Your snippets', data: {placement: 'right'} do
= nav_link(page: explore_snippets_path) do Your Snippets
= link_to explore_snippets_path, title: 'Explore snippets', data: {placement: 'right'} do = nav_link(page: explore_snippets_path) do
Explore Snippets = link_to explore_snippets_path, title: 'Explore snippets', data: {placement: 'right'} do
Explore Snippets
- if current_user
.nav-controls.hidden-xs
= link_to new_snippet_path, class: "btn btn-new", title: "New snippet" do
New snippet
...@@ -2,41 +2,11 @@ ...@@ -2,41 +2,11 @@
- header_title "Snippets", dashboard_snippets_path - header_title "Snippets", dashboard_snippets_path
= render 'dashboard/snippets_head' = render 'dashboard/snippets_head'
= render partial: 'snippets/snippets_scope_menu', locals: { include_private: true }
.nav-block .visible-xs
.controls.hidden-xs &nbsp;
= link_to new_snippet_path, class: "btn btn-new", title: "New snippet" do = link_to new_snippet_path, class: "btn btn-new btn-block", title: "New snippet" do
= icon('plus') New snippet
New snippet
.nav-links.snippet-scope-menu = render partial: 'snippets/snippets', locals: { link_project: true }
%li{ class: ("active" unless params[:scope]) }
= link_to dashboard_snippets_path do
All
%span.badge
= current_user.snippets.count
%li{ class: ("active" if params[:scope] == "are_private") }
= link_to dashboard_snippets_path(scope: 'are_private') do
Private
%span.badge
= current_user.snippets.are_private.count
%li{ class: ("active" if params[:scope] == "are_internal") }
= link_to dashboard_snippets_path(scope: 'are_internal') do
Internal
%span.badge
= current_user.snippets.are_internal.count
%li{ class: ("active" if params[:scope] == "are_public") }
= link_to dashboard_snippets_path(scope: 'are_public') do
Public
%span.badge
= current_user.snippets.are_public.count
.visible-xs
= link_to new_snippet_path, class: "btn btn-new btn-block", title: "New snippet" do
= icon('plus')
New snippet
= render 'snippets/snippets'
...@@ -6,12 +6,4 @@ ...@@ -6,12 +6,4 @@
- else - else
= render 'explore/head' = render 'explore/head'
.row-content-block = render partial: 'snippets/snippets', locals: { link_project: true }
- if current_user
= link_to new_snippet_path, class: "btn btn-new btn-wide-on-sm pull-right", title: "New snippet" do
New snippet
.oneline
Public snippets created by you and other users are listed here
= render 'snippets/snippets'
.hidden-xs .hidden-xs
- if can?(current_user, :create_project_snippet, @project)
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-create new-snippet-link', title: "New snippet" do
New snippet
- if can?(current_user, :update_project_snippet, @snippet)
= link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-danger", title: 'Delete Snippet' do
Delete
- if can?(current_user, :update_project_snippet, @snippet) - if can?(current_user, :update_project_snippet, @snippet)
= link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped" do
Edit Edit
- if can?(current_user, :update_project_snippet, @snippet)
= link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-inverted btn-remove", title: 'Delete Snippet' do
Delete
- if can?(current_user, :create_project_snippet, @project)
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-inverted btn-create', title: "New snippet" do
New snippet
- if can?(current_user, :create_project_snippet, @project) || can?(current_user, :update_project_snippet, @snippet) - if can?(current_user, :create_project_snippet, @project) || can?(current_user, :update_project_snippet, @snippet)
.visible-xs-block.dropdown .visible-xs-block.dropdown
%button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } } %button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
......
- page_title "Snippets" - page_title "Snippets"
.sub-header-block - if current_user
- if can?(current_user, :create_project_snippet, @project) .top-area
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new btn-wide-on-sm pull-right", title: "New snippet" do - include_private = @project.team.member?(current_user) || current_user.admin?
New snippet = render partial: 'snippets/snippets_scope_menu', locals: { subject: @project, include_private: include_private }
.nav-controls.hidden-xs
- if can?(current_user, :create_project_snippet, @project)
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New snippet" do
New snippet
.oneline - if can?(current_user, :create_project_snippet, @project)
Share code pastes with others out of git repository .visible-xs
&nbsp;
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new btn-block", title: "New snippet" do
New snippet
= render 'snippets/snippets' = render 'snippets/snippets'
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
= link_to snippet_title.project.name_with_namespace, namespace_project_path(snippet_title.project.namespace, snippet_title.project) = link_to snippet_title.project.name_with_namespace, namespace_project_path(snippet_title.project.namespace, snippet_title.project)
.snippet-info .snippet-info
= "##{snippet_title.id}" = snippet_title.to_reference
%span %span
by by
= link_to user_snippets_path(snippet_title.author) do = link_to user_snippets_path(snippet_title.author) do
......
...@@ -8,10 +8,6 @@ ...@@ -8,10 +8,6 @@
%span.creator %span.creator
authored authored
= time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago') = time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago')
- if @snippet.updated_at != @snippet.created_at
%span
= icon('edit', title: 'edited')
= time_ago_with_tooltip(@snippet.updated_at, placement: 'bottom', html_class: 'snippet_edited_ago')
by #{link_to_member(@project, @snippet.author, size: 24, author_class: "author item-title", avatar_class: "hidden-xs")} by #{link_to_member(@project, @snippet.author, size: 24, author_class: "author item-title", avatar_class: "hidden-xs")}
.snippet-actions .snippet-actions
...@@ -20,5 +16,9 @@ ...@@ -20,5 +16,9 @@
- else - else
= render "snippets/actions" = render "snippets/actions"
%h2.snippet-title.prepend-top-0.append-bottom-0 .snippet-header
= markdown_field(@snippet, :title) %h2.snippet-title.prepend-top-0.append-bottom-0
= markdown_field(@snippet, :title)
- if @snippet.updated_at != @snippet.created_at
= edited_time_ago_with_tooltip(@snippet, placement: 'bottom', html_class: 'snippet-edited-ago')
- link_project = local_assigns.fetch(:link_project, false)
%li.snippet-row %li.snippet-row
= image_tag avatar_icon(snippet.author_email), class: "avatar s40 hidden-xs", alt: '' = image_tag avatar_icon(snippet.author_email), class: "avatar s40 hidden-xs", alt: ''
.title .title
= link_to reliable_snippet_path(snippet) do = link_to reliable_snippet_path(snippet) do
= snippet.title = snippet.title
- if snippet.private? - if snippet.file_name
%span.label.label-gray.hidden-xs %span.snippet-filename.monospace.hidden-xs
= icon('lock') = snippet.file_name
private
%span.monospace.pull-right.hidden-xs
= snippet.file_name
%ul.controls.visible-xs %ul.controls
%li %li
- note_count = snippet.notes.user.count - note_count = snippet.notes.user.count
= link_to reliable_snippet_path(snippet, anchor: 'notes'), class: ('no-comments' if note_count.zero?) do = link_to reliable_snippet_path(snippet, anchor: 'notes'), class: ('no-comments' if note_count.zero?) do
...@@ -22,11 +21,17 @@ ...@@ -22,11 +21,17 @@
= visibility_level_label(snippet.visibility_level) = visibility_level_label(snippet.visibility_level)
= visibility_level_icon(snippet.visibility_level, fw: false) = visibility_level_icon(snippet.visibility_level, fw: false)
%small.pull-right.cgray.hidden-xs .snippet-info
- if snippet.project_id? #{snippet.to_reference} &middot;
= link_to snippet.project.name_with_namespace, namespace_project_path(snippet.project.namespace, snippet.project) authored #{time_ago_with_tooltip(snippet.created_at, placement: 'bottom', html_class: 'snippet-created-ago')}
by
.snippet-info.hidden-xs
= link_to user_snippets_path(snippet.author) do = link_to user_snippets_path(snippet.author) do
= snippet.author_name = snippet.author_name
authored #{time_ago_with_tooltip(snippet.created_at)} - if link_project && snippet.project_id?
%span.hidden-xs
in
= link_to namespace_project_path(snippet.project.namespace, snippet.project) do
= snippet.project.name_with_namespace
.pull-right.snippet-updated-at
%span updated #{time_ago_with_tooltip(snippet.updated_at, placement: 'bottom')}
.hidden-xs .hidden-xs
- if current_user
= link_to new_snippet_path, class: "btn btn-grouped btn-create new-snippet-link", title: "New snippet" do
New snippet
- if can?(current_user, :admin_personal_snippet, @snippet)
= link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-danger", title: 'Delete Snippet' do
Delete
- if can?(current_user, :update_personal_snippet, @snippet) - if can?(current_user, :update_personal_snippet, @snippet)
= link_to edit_snippet_path(@snippet), class: "btn btn-grouped snippable-edit" do = link_to edit_snippet_path(@snippet), class: "btn btn-grouped" do
Edit Edit
- if can?(current_user, :admin_personal_snippet, @snippet)
= link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-inverted btn-remove", title: 'Delete Snippet' do
Delete
- if current_user
= link_to new_snippet_path, class: "btn btn-grouped btn-inverted btn-create", title: "New snippet" do
New snippet
- if current_user - if current_user
.visible-xs-block.dropdown .visible-xs-block.dropdown
%button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } } %button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
......
- remote = local_assigns.fetch(:remote, false) - remote = local_assigns.fetch(:remote, false)
- link_project = local_assigns.fetch(:link_project, false)
.snippets-list-holder .snippets-list-holder
%ul.content-list %ul.content-list
= render partial: 'shared/snippets/snippet', collection: @snippets = render partial: 'shared/snippets/snippet', collection: @snippets, locals: { link_project: link_project }
- if @snippets.empty? - if @snippets.empty?
%li %li
.nothing-here-block Nothing here. .nothing-here-block Nothing here.
......
- subject = local_assigns.fetch(:subject, current_user)
- include_private = local_assigns.fetch(:include_private, false)
.nav-links.snippet-scope-menu
%li{ class: ("active" unless params[:scope]) }
= link_to subject_snippets_path(subject) do
All
%span.badge
- if include_private
= subject.snippets.count
- else
= subject.snippets.public_and_internal.count
- if include_private
%li{ class: ("active" if params[:scope] == "are_private") }
= link_to subject_snippets_path(subject, scope: 'are_private') do
Private
%span.badge
= subject.snippets.are_private.count
%li{ class: ("active" if params[:scope] == "are_internal") }
= link_to subject_snippets_path(subject, scope: 'are_internal') do
Internal
%span.badge
= subject.snippets.are_internal.count
%li{ class: ("active" if params[:scope] == "are_public") }
= link_to subject_snippets_path(subject, scope: 'are_public') do
Public
%span.badge
= subject.snippets.are_public.count
...@@ -22,7 +22,7 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps ...@@ -22,7 +22,7 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps
end end
step 'I click link "New snippet"' do step 'I click link "New snippet"' do
click_link "New snippet" first(:link, "New snippet").click
end end
step 'I click link "Snippet one"' do step 'I click link "Snippet one"' do
......
...@@ -36,7 +36,7 @@ feature 'Tooltips on .timeago dates', feature: true, js: true do ...@@ -36,7 +36,7 @@ feature 'Tooltips on .timeago dates', feature: true, js: true do
visit user_snippets_path(user) visit user_snippets_path(user)
wait_for_ajax() wait_for_ajax()
page.find('.js-timeago').hover page.find('.js-timeago.snippet-created-ago').hover
end end
it 'has the datetime formated correctly' do it 'has the datetime formated correctly' do
......
...@@ -93,16 +93,39 @@ describe SnippetsFinder do ...@@ -93,16 +93,39 @@ describe SnippetsFinder do
expect(snippets).not_to include(@snippet1, @snippet2) expect(snippets).not_to include(@snippet1, @snippet2)
end end
it "returns public and internal snippets for none project members" do it "returns public and internal snippets for non project members" do
snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1) snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1)
expect(snippets).to include(@snippet2, @snippet3) expect(snippets).to include(@snippet2, @snippet3)
expect(snippets).not_to include(@snippet1) expect(snippets).not_to include(@snippet1)
end end
it "returns public snippets for non project members" do
snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1, scope: "are_public")
expect(snippets).to include(@snippet3)
expect(snippets).not_to include(@snippet1, @snippet2)
end
it "returns internal snippets for non project members" do
snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1, scope: "are_internal")
expect(snippets).to include(@snippet2)
expect(snippets).not_to include(@snippet1, @snippet3)
end
it "does not return private snippets for non project members" do
snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1, scope: "are_private")
expect(snippets).not_to include(@snippet1, @snippet2, @snippet3)
end
it "returns all snippets for project members" do it "returns all snippets for project members" do
project1.team << [user, :developer] project1.team << [user, :developer]
snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1) snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1)
expect(snippets).to include(@snippet1, @snippet2, @snippet3) expect(snippets).to include(@snippet1, @snippet2, @snippet3)
end end
it "returns private snippets for project members" do
project1.team << [user, :developer]
snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1, scope: "are_private")
expect(snippets).to include(@snippet1)
end
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