Further design iteration on project overview

Continues the iteration on the project overview UI:

- moved star, fork and new clone button
(copy SSH/HTTPS URLs) to top right,
  made them smaller
- avatar is now larger (64px)
- 'Request access' is now a link instead
  of a button
- overview comes before the description +
  changed styling and added icons
- description font-size is now 16px (large-paragraph)
- quick links to files are moved downwards
  below the commit/pipeline info
- margins changed to group content into
  4 groups to clean up the interface
- visibility info reduced to icon-only and
  moved to the right of the title
This commit is contained in:
Dennis Tang 2018-12-07 14:11:42 +00:00
parent 61d91f640b
commit 75496059a1
36 changed files with 673 additions and 488 deletions

View File

@ -12,6 +12,10 @@ export default function notificationsDropdown() {
const form = $(this).parents('.notification-form:first'); const form = $(this).parents('.notification-form:first');
form.find('.js-notification-loading').toggleClass('fa-bell fa-spin fa-spinner'); form.find('.js-notification-loading').toggleClass('fa-bell fa-spin fa-spinner');
if (form.hasClass('no-label')) {
form.find('.js-notification-loading').toggleClass('hidden');
form.find('.js-notifications-icon').toggleClass('hidden');
}
form.find('#notification_setting_level').val(notificationLevel); form.find('#notification_setting_level').val(notificationLevel);
form.submit(); form.submit();
}); });

View File

@ -13,6 +13,9 @@ export default class Project {
const $cloneOptions = $('ul.clone-options-dropdown'); const $cloneOptions = $('ul.clone-options-dropdown');
const $projectCloneField = $('#project_clone'); const $projectCloneField = $('#project_clone');
const $cloneBtnLabel = $('.js-git-clone-holder .js-clone-dropdown-label'); const $cloneBtnLabel = $('.js-git-clone-holder .js-clone-dropdown-label');
const mobileCloneField = document.querySelector(
'.js-mobile-git-clone .js-clone-dropdown-label',
);
const selectedCloneOption = $cloneBtnLabel.text().trim(); const selectedCloneOption = $cloneBtnLabel.text().trim();
if (selectedCloneOption.length > 0) { if (selectedCloneOption.length > 0) {
@ -36,7 +39,11 @@ export default class Project {
$label.text(activeText); $label.text(activeText);
}); });
$projectCloneField.val(url); if (mobileCloneField) {
mobileCloneField.dataset.clipboardText = url;
} else {
$projectCloneField.val(url);
}
$('.js-git-empty .js-clone').text(url); $('.js-git-empty .js-clone').text(url);
}); });
// Ref switcher // Ref switcher

View File

@ -21,6 +21,7 @@
&.s46 { @include avatar-size(46px, 15px); } &.s46 { @include avatar-size(46px, 15px); }
&.s48 { @include avatar-size(48px, 10px); } &.s48 { @include avatar-size(48px, 10px); }
&.s60 { @include avatar-size(60px, 12px); } &.s60 { @include avatar-size(60px, 12px); }
&.s64 { @include avatar-size(64px, 14px); }
&.s70 { @include avatar-size(70px, 14px); } &.s70 { @include avatar-size(70px, 14px); }
&.s90 { @include avatar-size(90px, 15px); } &.s90 { @include avatar-size(90px, 15px); }
&.s100 { @include avatar-size(100px, 15px); } &.s100 { @include avatar-size(100px, 15px); }
@ -80,6 +81,7 @@
&.s40 { font-size: 16px; line-height: 38px; } &.s40 { font-size: 16px; line-height: 38px; }
&.s48 { font-size: 20px; line-height: 46px; } &.s48 { font-size: 20px; line-height: 46px; }
&.s60 { font-size: 32px; line-height: 58px; } &.s60 { font-size: 32px; line-height: 58px; }
&.s64 { font-size: 32px; line-height: 64px; }
&.s70 { font-size: 34px; line-height: 70px; } &.s70 { font-size: 34px; line-height: 70px; }
&.s90 { font-size: 36px; line-height: 88px; } &.s90 { font-size: 36px; line-height: 88px; }
&.s100 { font-size: 36px; line-height: 98px; } &.s100 { font-size: 36px; line-height: 98px; }

View File

@ -142,8 +142,14 @@
&.btn-sm { &.btn-sm {
padding: 4px 10px; padding: 4px 10px;
font-size: 13px; font-size: $gl-btn-small-font-size;
line-height: 18px; line-height: $gl-btn-small-line-height;
}
&.btn-xs {
padding: 2px $gl-btn-padding;
font-size: $gl-btn-small-font-size;
line-height: $gl-btn-small-line-height;
} }
&.btn-success, &.btn-success,

View File

@ -39,15 +39,6 @@
.git-clone-holder { .git-clone-holder {
display: none; display: none;
} }
// Display Star and Fork buttons without counters on mobile.
.project-repo-buttons {
display: block;
.count-buttons .count-badge {
margin-top: $gl-padding-8;
}
}
} }
.group-buttons { .group-buttons {

View File

@ -197,6 +197,7 @@ $well-light-text-color: #5b6169;
$gl-font-size: 14px; $gl-font-size: 14px;
$gl-font-size-xs: 11px; $gl-font-size-xs: 11px;
$gl-font-size-small: 12px; $gl-font-size-small: 12px;
$gl-font-size-large: 16px;
$gl-font-weight-normal: 400; $gl-font-weight-normal: 400;
$gl-font-weight-bold: 600; $gl-font-weight-bold: 600;
$gl-text-color: #2e2e2e; $gl-text-color: #2e2e2e;
@ -270,7 +271,8 @@ $performance-bar-height: 35px;
$flash-height: 52px; $flash-height: 52px;
$context-header-height: 60px; $context-header-height: 60px;
$breadcrumb-min-height: 48px; $breadcrumb-min-height: 48px;
$project-title-row-height: 24px; $project-title-row-height: 64px;
$project-avatar-mobile-size: 24px;
$gl-line-height: 16px; $gl-line-height: 16px;
$gl-line-height-24: 24px; $gl-line-height-24: 24px;
@ -365,6 +367,8 @@ $gl-btn-padding: 10px;
$gl-btn-line-height: 16px; $gl-btn-line-height: 16px;
$gl-btn-vert-padding: 8px; $gl-btn-vert-padding: 8px;
$gl-btn-horz-padding: 12px; $gl-btn-horz-padding: 12px;
$gl-btn-small-font-size: 13px;
$gl-btn-small-line-height: 13px;
/* /*
* Badges * Badges

View File

@ -144,7 +144,6 @@
.group-home-panel { .group-home-panel {
padding-top: 24px; padding-top: 24px;
padding-bottom: 24px; padding-bottom: 24px;
border-bottom: 1px solid $border-color;
.group-avatar { .group-avatar {
float: none; float: none;
@ -155,7 +154,6 @@
} }
} }
.project-title,
.group-title { .group-title {
margin-top: 10px; margin-top: 10px;
margin-bottom: 10px; margin-bottom: 10px;
@ -195,25 +193,69 @@
} }
.project-home-panel { .project-home-panel {
padding-top: $gl-padding-8; padding-top: $gl-padding;
padding-bottom: $gl-padding-24; padding-bottom: $gl-padding;
.project-title-row {
margin-right: $gl-padding-8;
}
.project-avatar { .project-avatar {
width: $project-title-row-height; width: $project-title-row-height;
height: $project-title-row-height; height: $project-title-row-height;
flex-shrink: 0; flex-shrink: 0;
flex-basis: $project-title-row-height; flex-basis: $project-title-row-height;
margin: 0 $gl-padding-8 0 0; margin: 0 $gl-padding 0 0;
} }
.project-title { .project-title {
margin-top: 8px;
margin-bottom: 5px;
font-size: 20px; font-size: 20px;
line-height: $project-title-row-height; line-height: $gl-line-height-24;
font-weight: bold; font-weight: bold;
.icon {
font-size: $gl-font-size-large;
}
.project-visibility {
color: $gl-text-color-secondary;
}
.project-tag-list {
font-size: $gl-font-size;
font-weight: $gl-font-weight-normal;
.icon {
position: relative;
top: 3px;
margin-right: $gl-padding-4;
}
}
}
.project-title-row {
@include media-breakpoint-down(sm) {
.project-avatar {
width: $project-avatar-mobile-size;
height: $project-avatar-mobile-size;
flex-basis: $project-avatar-mobile-size;
.avatar {
font-size: 20px;
line-height: 46px;
}
}
.project-title {
margin-top: 4px;
margin-bottom: 2px;
font-size: $gl-font-size;
line-height: $gl-font-size-large;
}
.project-tag-list,
.project-metadata {
font-size: $gl-font-size-small;
}
}
} }
.project-metadata { .project-metadata {
@ -222,16 +264,6 @@
line-height: $gl-btn-line-height; line-height: $gl-btn-line-height;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
.icon {
margin-right: $gl-padding-4;
font-size: 16px;
}
.project-visibility,
.project-license,
.project-tag-list {
margin-right: $gl-padding-8;
}
.project-license { .project-license {
.btn { .btn {
@ -240,12 +272,22 @@
} }
} }
.project-tag-list, .access-request-link,
.project-license { .project-tag-list {
.icon { padding-left: $gl-padding-8;
position: relative; border-left: 1px solid $gl-text-color-secondary;
top: 2px; }
} }
.project-description {
@include media-breakpoint-up(md) {
font-size: $gl-font-size-large;
}
}
.notifications-btn {
.fa-bell {
margin-right: 0;
} }
} }
} }
@ -298,14 +340,6 @@
vertical-align: top; vertical-align: top;
margin-top: $gl-padding; margin-top: $gl-padding;
.count-badge {
height: $input-height;
.icon {
top: -1px;
}
}
.count-badge-count, .count-badge-count,
.count-badge-button { .count-badge-button {
border: 1px solid $border-color; border: 1px solid $border-color;
@ -319,29 +353,25 @@
.count-badge-count { .count-badge-count {
padding: 0 12px; padding: 0 12px;
border-right: 0;
border-radius: $border-radius-base 0 0 $border-radius-base;
background: $gray-light; background: $gray-light;
border-radius: 0 $border-radius-base $border-radius-base 0;
} }
.count-badge-button { .count-badge-button {
border-radius: 0 $border-radius-base $border-radius-base 0; border-right: 0;
border-radius: $border-radius-base 0 0 $border-radius-base;
} }
} }
.project-clone-holder { .project-clone-holder {
display: inline-block; display: inline-block;
margin: $gl-padding $gl-padding-8 0 0; margin: $gl-padding 0 0;
input { input {
height: $input-height; height: $input-height;
} }
} }
.clone-dropdown-btn {
background-color: $white-light;
}
.clone-options-dropdown { .clone-options-dropdown {
min-width: 240px; min-width: 240px;
@ -355,6 +385,31 @@
} }
} }
.project-repo-buttons {
.icon {
top: 0;
}
.count-badge,
.btn-xs {
height: 24px;
}
.dropdown-toggle,
.clone-dropdown-btn {
.fa {
color: unset;
}
}
.btn {
.notifications-icon {
top: 1px;
margin-right: 0;
}
}
}
.split-one { .split-one {
display: inline-table; display: inline-table;
margin-right: 12px; margin-right: 12px;
@ -715,10 +770,10 @@
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
} }
.project-stats { .project-stats,
.project-buttons {
font-size: 0; font-size: 0;
text-align: center; text-align: center;
border-bottom: 1px solid $border-color;
.scrolling-tabs-container { .scrolling-tabs-container {
.scrolling-tabs { .scrolling-tabs {
@ -786,23 +841,43 @@
font-size: $gl-font-size; font-size: $gl-font-size;
line-height: $gl-btn-line-height; line-height: $gl-btn-line-height;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
white-space: nowrap; white-space: pre-wrap;
} }
.stat-link { .stat-link {
border-bottom: 0; border-bottom: 0;
color: $black;
&:hover, &:hover,
&:focus { &:focus {
color: $gl-text-color;
text-decoration: underline; text-decoration: underline;
border-bottom: 0; border-bottom: 0;
} }
.project-stat-value {
color: $gl-text-color;
}
.icon {
color: $gl-text-color-secondary;
}
.add-license-link {
&,
.icon {
color: $blue-600;
}
}
} }
.btn { .btn {
padding: $gl-btn-vert-padding $gl-btn-horz-padding; margin-top: $gl-padding;
padding: $gl-btn-vert-padding $gl-btn-padding;
line-height: $gl-btn-line-height; line-height: $gl-btn-line-height;
.icon {
top: 0;
}
} }
.btn-missing { .btn-missing {
@ -811,6 +886,13 @@
} }
} }
.project-buttons {
.stat-text {
@extend .btn;
@extend .btn-default;
}
}
.repository-languages-bar { .repository-languages-bar {
height: 8px; height: 8px;
margin-bottom: $gl-padding-8; margin-bottom: $gl-padding-8;
@ -934,8 +1016,6 @@ pre.light-well {
} }
.git-clone-holder { .git-clone-holder {
width: 320px;
.btn-clipboard { .btn-clipboard {
border: 1px solid $border-color; border: 1px solid $border-color;
} }
@ -958,6 +1038,15 @@ pre.light-well {
} }
} }
.git-clone-holder,
.mobile-git-clone {
.btn {
.icon {
fill: $white;
}
}
}
.cannot-be-merged, .cannot-be-merged,
.cannot-be-merged:hover { .cannot-be-merged:hover {
color: $red-500; color: $red-500;

View File

@ -16,7 +16,11 @@ class NotificationSettingsController < ApplicationController
@notification_setting = current_user.notification_settings.find(params[:id]) @notification_setting = current_user.notification_settings.find(params[:id])
@saved = @notification_setting.update(notification_setting_params_for(@notification_setting.source)) @saved = @notification_setting.update(notification_setting_params_for(@notification_setting.source))
render_response if params[:hide_label].present?
render_response("projects/buttons/_notifications")
else
render_response
end
end end
private private
@ -37,9 +41,9 @@ class NotificationSettingsController < ApplicationController
can?(current_user, ability_name, resource) can?(current_user, ability_name, resource)
end end
def render_response def render_response(response_template = "shared/notifications/_button")
render json: { render json: {
html: view_to_html_string("shared/notifications/_button", notification_setting: @notification_setting), html: view_to_html_string(response_template, notification_setting: @notification_setting),
saved: @saved saved: @saved
} }
end end

View File

@ -85,13 +85,14 @@ module ButtonHelper
dropdown_item_with_description('SSH', dropdown_description, href: append_url, data: { clone_type: 'ssh' }) dropdown_item_with_description('SSH', dropdown_description, href: append_url, data: { clone_type: 'ssh' })
end end
def dropdown_item_with_description(title, description, href: nil, data: nil) def dropdown_item_with_description(title, description, href: nil, data: nil, default: false)
active_class = "is-active" if default
button_content = content_tag(:strong, title, class: 'dropdown-menu-inner-title') button_content = content_tag(:strong, title, class: 'dropdown-menu-inner-title')
button_content << content_tag(:span, description, class: 'dropdown-menu-inner-content') if description button_content << content_tag(:span, description, class: 'dropdown-menu-inner-content') if description
content_tag (href ? :a : :span), content_tag (href ? :a : :span),
(href ? button_content : title), (href ? button_content : title),
class: "#{title.downcase}-selector", class: "#{title.downcase}-selector #{active_class}",
href: (href if href), href: (href if href),
data: (data if data) data: (data if data)
end end

View File

@ -140,7 +140,7 @@ module VisibilityLevelHelper
end end
def project_visibility_icon_description(level) def project_visibility_icon_description(level)
"#{project_visibility_level_description(level)}" "#{visibility_level_label(level)} - #{project_visibility_level_description(level)}"
end end
def visibility_level_label(level) def visibility_level_label(level)

View File

@ -6,27 +6,27 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
include GitlabRoutingHelper include GitlabRoutingHelper
include StorageHelper include StorageHelper
include TreeHelper include TreeHelper
include IconsHelper
include ChecksCollaboration include ChecksCollaboration
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
presents :project presents :project
AnchorData = Struct.new(:enabled, :label, :link, :class_modifier) AnchorData = Struct.new(:is_link, :label, :link, :class_modifier, :icon)
MAX_TAGS_TO_SHOW = 3 MAX_TAGS_TO_SHOW = 3
def statistic_icon(icon_name = 'plus-square-o')
sprite_icon(icon_name, size: 16, css_class: 'icon append-right-4')
end
def statistics_anchors(show_auto_devops_callout:) def statistics_anchors(show_auto_devops_callout:)
[ [
readme_anchor_data, license_anchor_data,
changelog_anchor_data,
contribution_guide_anchor_data,
files_anchor_data,
commits_anchor_data, commits_anchor_data,
branches_anchor_data, branches_anchor_data,
tags_anchor_data, tags_anchor_data,
gitlab_ci_anchor_data, files_anchor_data
autodevops_anchor_data(show_auto_devops_callout: show_auto_devops_callout), ].compact.select(&:is_link)
kubernetes_cluster_anchor_data
].compact.select { |item| item.enabled }
end end
def statistics_buttons(show_auto_devops_callout:) def statistics_buttons(show_auto_devops_callout:)
@ -37,27 +37,28 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
autodevops_anchor_data(show_auto_devops_callout: show_auto_devops_callout), autodevops_anchor_data(show_auto_devops_callout: show_auto_devops_callout),
kubernetes_cluster_anchor_data, kubernetes_cluster_anchor_data,
gitlab_ci_anchor_data gitlab_ci_anchor_data
].compact.reject { |item| item.enabled } ].compact.reject(&:is_link)
end end
def empty_repo_statistics_anchors def empty_repo_statistics_anchors
[ [
files_anchor_data, license_anchor_data,
commits_anchor_data, commits_anchor_data,
branches_anchor_data, branches_anchor_data,
tags_anchor_data, tags_anchor_data,
autodevops_anchor_data, files_anchor_data
kubernetes_cluster_anchor_data ].compact.select { |item| item.is_link }
].compact.select { |item| item.enabled }
end end
def empty_repo_statistics_buttons def empty_repo_statistics_buttons
[ [
new_file_anchor_data, new_file_anchor_data,
readme_anchor_data, readme_anchor_data,
changelog_anchor_data,
contribution_guide_anchor_data,
autodevops_anchor_data, autodevops_anchor_data,
kubernetes_cluster_anchor_data kubernetes_cluster_anchor_data
].compact.reject { |item| item.enabled } ].compact.reject { |item| item.is_link }
end end
def default_view def default_view
@ -113,7 +114,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end end
def add_contribution_guide_path def add_contribution_guide_path
add_special_file_path(file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') add_special_file_path(file_name: 'CONTRIBUTING.md', commit_message: 'Add CONTRIBUTING')
end end
def add_ci_yml_path def add_ci_yml_path
@ -149,32 +150,52 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
def files_anchor_data def files_anchor_data
AnchorData.new(true, AnchorData.new(true,
_('Files (%{human_size})') % { human_size: storage_counter(statistics.total_repository_size) }, statistic_icon('doc-code') +
_('%{strong_start}%{human_size}%{strong_end} Files').html_safe % {
human_size: storage_counter(statistics.total_repository_size),
strong_start: '<strong class="project-stat-value">'.html_safe,
strong_end: '</strong>'.html_safe
},
empty_repo? ? nil : project_tree_path(project)) empty_repo? ? nil : project_tree_path(project))
end end
def commits_anchor_data def commits_anchor_data
AnchorData.new(true, AnchorData.new(true,
n_('Commit (%{commit_count})', 'Commits (%{commit_count})', statistics.commit_count) % { commit_count: number_with_delimiter(statistics.commit_count) }, statistic_icon('commit') +
n_('%{strong_start}%{commit_count}%{strong_end} Commit', '%{strong_start}%{commit_count}%{strong_end} Commits', statistics.commit_count).html_safe % {
commit_count: number_with_delimiter(statistics.commit_count),
strong_start: '<strong class="project-stat-value">'.html_safe,
strong_end: '</strong>'.html_safe
},
empty_repo? ? nil : project_commits_path(project, repository.root_ref)) empty_repo? ? nil : project_commits_path(project, repository.root_ref))
end end
def branches_anchor_data def branches_anchor_data
AnchorData.new(true, AnchorData.new(true,
n_('Branch (%{branch_count})', 'Branches (%{branch_count})', repository.branch_count) % { branch_count: number_with_delimiter(repository.branch_count) }, statistic_icon('branch') +
n_('%{strong_start}%{branch_count}%{strong_end} Branch', '%{strong_start}%{branch_count}%{strong_end} Branches', repository.branch_count).html_safe % {
branch_count: number_with_delimiter(repository.branch_count),
strong_start: '<strong class="project-stat-value">'.html_safe,
strong_end: '</strong>'.html_safe
},
empty_repo? ? nil : project_branches_path(project)) empty_repo? ? nil : project_branches_path(project))
end end
def tags_anchor_data def tags_anchor_data
AnchorData.new(true, AnchorData.new(true,
n_('Tag (%{tag_count})', 'Tags (%{tag_count})', repository.tag_count) % { tag_count: number_with_delimiter(repository.tag_count) }, statistic_icon('label') +
n_('%{strong_start}%{tag_count}%{strong_end} Tag', '%{strong_start}%{tag_count}%{strong_end} Tags', repository.tag_count).html_safe % {
tag_count: number_with_delimiter(repository.tag_count),
strong_start: '<strong class="project-stat-value">'.html_safe,
strong_end: '</strong>'.html_safe
},
empty_repo? ? nil : project_tags_path(project)) empty_repo? ? nil : project_tags_path(project))
end end
def new_file_anchor_data def new_file_anchor_data
if current_user && can_current_user_push_to_default_branch? if current_user && can_current_user_push_to_default_branch?
AnchorData.new(false, AnchorData.new(false,
_('New file'), statistic_icon + _('New file'),
project_new_blob_path(project, default_branch || 'master'), project_new_blob_path(project, default_branch || 'master'),
'success') 'success')
end end
@ -183,40 +204,45 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
def readme_anchor_data def readme_anchor_data
if current_user && can_current_user_push_to_default_branch? && repository.readme.nil? if current_user && can_current_user_push_to_default_branch? && repository.readme.nil?
AnchorData.new(false, AnchorData.new(false,
_('Add Readme'), statistic_icon + _('Add README'),
add_readme_path) add_readme_path)
elsif repository.readme elsif repository.readme
AnchorData.new(true, AnchorData.new(false,
_('Readme'), statistic_icon('doc-text') + _('README'),
default_view != 'readme' ? readme_path : '#readme') default_view != 'readme' ? readme_path : '#readme',
'default',
'doc-text')
end end
end end
def changelog_anchor_data def changelog_anchor_data
if current_user && can_current_user_push_to_default_branch? && repository.changelog.blank? if current_user && can_current_user_push_to_default_branch? && repository.changelog.blank?
AnchorData.new(false, AnchorData.new(false,
_('Add Changelog'), statistic_icon + _('Add CHANGELOG'),
add_changelog_path) add_changelog_path)
elsif repository.changelog.present? elsif repository.changelog.present?
AnchorData.new(true, AnchorData.new(false,
_('Changelog'), statistic_icon('doc-text') + _('CHANGELOG'),
changelog_path) changelog_path,
'default')
end end
end end
def license_anchor_data def license_anchor_data
icon = statistic_icon('scale')
if repository.license_blob.present? if repository.license_blob.present?
AnchorData.new(true, AnchorData.new(true,
license_short_name, icon + content_tag(:strong, license_short_name, class: 'project-stat-value'),
license_path) license_path)
else else
if current_user && can_current_user_push_to_default_branch? if current_user && can_current_user_push_to_default_branch?
AnchorData.new(false, AnchorData.new(true,
_('Add license'), content_tag(:span, icon + _('Add license'), class: 'add-license-link d-flex'),
add_license_path) add_license_path)
else else
AnchorData.new(false, AnchorData.new(true,
_('No license. All rights reserved'), icon + content_tag(:strong, _('No license. All rights reserved'), class: 'project-stat-value'),
nil) nil)
end end
end end
@ -225,22 +251,29 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
def contribution_guide_anchor_data def contribution_guide_anchor_data
if current_user && can_current_user_push_to_default_branch? && repository.contribution_guide.blank? if current_user && can_current_user_push_to_default_branch? && repository.contribution_guide.blank?
AnchorData.new(false, AnchorData.new(false,
_('Add Contribution guide'), statistic_icon + _('Add CONTRIBUTING'),
add_contribution_guide_path) add_contribution_guide_path)
elsif repository.contribution_guide.present? elsif repository.contribution_guide.present?
AnchorData.new(true, AnchorData.new(false,
_('Contribution guide'), statistic_icon('doc-text') + _('CONTRIBUTING'),
contribution_guide_path) contribution_guide_path)
end end
end end
def autodevops_anchor_data(show_auto_devops_callout: false) def autodevops_anchor_data(show_auto_devops_callout: false)
if current_user && can?(current_user, :admin_pipeline, project) && repository.gitlab_ci_yml.blank? && !show_auto_devops_callout if current_user && can?(current_user, :admin_pipeline, project) && repository.gitlab_ci_yml.blank? && !show_auto_devops_callout
AnchorData.new(auto_devops_enabled?, if auto_devops_enabled?
auto_devops_enabled? ? _('Auto DevOps enabled') : _('Enable Auto DevOps'), AnchorData.new(false,
project_settings_ci_cd_path(project, anchor: 'autodevops-settings')) statistic_icon('doc-text') + _('Auto DevOps enabled'),
project_settings_ci_cd_path(project, anchor: 'autodevops-settings'),
'default')
else
AnchorData.new(false,
statistic_icon + _('Enable Auto DevOps'),
project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
end
elsif auto_devops_enabled? elsif auto_devops_enabled?
AnchorData.new(true, AnchorData.new(false,
_('Auto DevOps enabled'), _('Auto DevOps enabled'),
nil) nil)
end end
@ -248,27 +281,32 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
def kubernetes_cluster_anchor_data def kubernetes_cluster_anchor_data
if current_user && can?(current_user, :create_cluster, project) if current_user && can?(current_user, :create_cluster, project)
cluster_link = clusters.count == 1 ? project_cluster_path(project, clusters.first) : project_clusters_path(project)
if clusters.empty? if clusters.empty?
cluster_link = new_project_cluster_path(project) AnchorData.new(false,
end statistic_icon + _('Add Kubernetes cluster'),
new_project_cluster_path(project))
else
cluster_link = clusters.count == 1 ? project_cluster_path(project, clusters.first) : project_clusters_path(project)
AnchorData.new(!clusters.empty?, AnchorData.new(false,
clusters.empty? ? _('Add Kubernetes cluster') : _('Kubernetes configured'), _('Kubernetes configured'),
cluster_link) cluster_link,
'default')
end
end end
end end
def gitlab_ci_anchor_data def gitlab_ci_anchor_data
if current_user && can_current_user_push_code? && repository.gitlab_ci_yml.blank? && !auto_devops_enabled? if current_user && can_current_user_push_code? && repository.gitlab_ci_yml.blank? && !auto_devops_enabled?
AnchorData.new(false, AnchorData.new(false,
_('Set up CI/CD'), statistic_icon + _('Set up CI/CD'),
add_ci_yml_path) add_ci_yml_path)
elsif repository.gitlab_ci_yml.present? elsif repository.gitlab_ci_yml.present?
AnchorData.new(true, AnchorData.new(false,
_('CI/CD configuration'), statistic_icon('doc-text') + _('CI/CD configuration'),
ci_configuration_path) ci_configuration_path,
'default')
end end
end end

View File

@ -1,4 +1,4 @@
.group-home-panel.text-center .group-home-panel.text-center.border-bottom
%div{ class: container_class } %div{ class: container_class }
.avatar-container.s70.group-avatar .avatar-container.s70.group-avatar
= group_icon(@group, class: "avatar s70 avatar-tile") = group_icon(@group, class: "avatar s70 avatar-tile")

View File

@ -1,7 +1,9 @@
- is_project_overview = local_assigns.fetch(:is_project_overview, false)
- commit = local_assigns.fetch(:commit) { @repository.commit } - commit = local_assigns.fetch(:commit) { @repository.commit }
- ref = local_assigns.fetch(:ref) { current_ref } - ref = local_assigns.fetch(:ref) { current_ref }
- project = local_assigns.fetch(:project) { @project } - project = local_assigns.fetch(:project) { @project }
- content_url = local_assigns.fetch(:content_url) { @tree.readme ? project_blob_path(@project, tree_join(@ref, @tree.readme.path)) : project_tree_path(@project, @ref) } - content_url = local_assigns.fetch(:content_url) { @tree.readme ? project_blob_path(@project, tree_join(@ref, @tree.readme.path)) : project_tree_path(@project, @ref) }
- show_auto_devops_callout = show_auto_devops_callout?(@project)
#tree-holder.tree-holder.clearfix #tree-holder.tree-holder.clearfix
.nav-block .nav-block
@ -10,4 +12,8 @@
- if commit - if commit
= render 'shared/commit_well', commit: commit, ref: ref, project: project = render 'shared/commit_well', commit: commit, ref: ref, project: project
- if is_project_overview
.project-buttons.append-bottom-default
= render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout)
= render 'projects/tree/tree_content', tree: @tree, content_url: content_url = render 'projects/tree/tree_content', tree: @tree, content_url: content_url

View File

@ -1,83 +1,75 @@
- empty_repo = @project.empty_repo? - empty_repo = @project.empty_repo?
- license = @project.license_anchor_data - show_auto_devops_callout = show_auto_devops_callout?(@project)
.project-home-panel{ class: ("empty-project" if empty_repo) } .project-home-panel{ class: ("empty-project" if empty_repo) }
.limit-container-width{ class: container_class } .project-header.row.append-bottom-8
.project-header.d-flex.flex-row.flex-wrap.align-items-center.append-bottom-8 .project-title-row.col-md-12.col-lg-7.d-flex
.project-title-row.d-flex.align-items-center .avatar-container.project-avatar.float-none
.avatar-container.project-avatar.float-none = project_icon(@project, alt: @project.name, class: 'avatar avatar-tile s64', width: 64, height: 64)
= project_icon(@project, alt: @project.name, class: 'avatar avatar-tile s24', width: 24, height: 24) .d-flex.flex-column.flex-wrap.align-items-baseline
%h1.project-title.d-flex.align-items-baseline.qa-project-name .d-inline-flex.align-items-baseline
= @project.name %h1.project-title.qa-project-name
.project-metadata.d-flex.flex-row.flex-wrap.align-items-baseline = @project.name
.project-visibility.d-inline-flex.align-items-baseline.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@project) } %span.project-visibility.prepend-left-8.d-inline-flex.align-items-baseline.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@project) }
= visibility_level_icon(@project.visibility_level, fw: false, options: {class: 'icon'}) = visibility_level_icon(@project.visibility_level, fw: false, options: {class: 'icon'})
= visibility_level_label(@project.visibility_level) .project-metadata.d-flex.align-items-center
- if license.present? - if can?(current_user, :read_project, @project)
.project-license.d-inline-flex.align-items-baseline %span.text-secondary
= link_to_if license.link, sprite_icon('scale', size: 16, css_class: 'icon') + license.label, license.link, class: license.enabled ? 'btn btn-link btn-secondary-hover-link' : 'btn btn-link' = s_('ProjectPage|Project ID: %{project_id}') % { project_id: @project.id }
- if @project.tag_list.present? - if current_user
.project-tag-list.d-inline-flex.align-items-baseline.has-tooltip{ data: { container: 'body' }, title: @project.has_extra_tags? ? @project.tag_list.join(', ') : nil } %span.access-request-links.prepend-left-8
= sprite_icon('tag', size: 16, css_class: 'icon') = render 'shared/members/access_request_links', source: @project
= @project.tags_to_show - if @project.tag_list.present?
- if @project.has_extra_tags? %span.project-tag-list.d-inline-flex.prepend-left-8.has-tooltip{ data: { container: 'body' }, title: @project.has_extra_tags? ? @project.tag_list.join(', ') : nil }
= _("+ %{count} more") % { count: @project.count_of_extra_tags_not_shown } = sprite_icon('tag', size: 16, css_class: 'icon append-right-4')
= @project.tags_to_show
- if @project.has_extra_tags?
= _("+ %{count} more") % { count: @project.count_of_extra_tags_not_shown }
.project-home-desc .project-repo-buttons.col-md-12.col-lg-5.d-inline-flex.flex-wrap.justify-content-lg-end
- if @project.description.present? - if current_user
.project-description .d-inline-flex
.project-description-markdown.read-more-container = render 'projects/buttons/notifications', notification_setting: @notification_setting, btn_class: 'btn-xs'
= markdown_field(@project, :description)
%button.btn.btn-blank.btn-link.text-secondary.js-read-more-trigger.text-secondary.d-lg-none{ type: "button" }
= _("Read more")
- if can?(current_user, :read_project, @project)
.text-secondary.prepend-top-8
= s_('ProjectPage|Project ID: %{project_id}') % { project_id: @project.id }
- if @project.forked?
%p
- if @project.fork_source
#{ s_('ForkedFromProjectPath|Forked from') }
= link_to project_path(@project.fork_source) do
= fork_source_name(@project)
- else
- deleted_message = s_('ForkedFromProjectPath|Forked from %{project_name} (deleted)')
= deleted_message % { project_name: fork_source_name(@project) }
- if @project.badges.present?
.project-badges.prepend-top-default.append-bottom-default
- @project.badges.each do |badge|
%a.append-right-8{ href: badge.rendered_link_url(@project),
target: '_blank',
rel: 'noopener noreferrer' }>
%img.project-badge{ src: badge.rendered_image_url(@project),
'aria-hidden': true,
alt: 'Project badge' }>
.project-repo-buttons.d-inline-flex.flex-wrap
.count-buttons.d-inline-flex .count-buttons.d-inline-flex
= render 'projects/buttons/star' = render 'projects/buttons/star'
= render 'projects/buttons/fork' = render 'projects/buttons/fork'
- if can?(current_user, :download_code, @project) - if can?(current_user, :download_code, @project)
.project-clone-holder.d-inline-flex.d-sm-none .project-clone-holder.d-inline-flex.d-md-none.btn-block
= render "shared/mobile_clone_panel" = render "shared/mobile_clone_panel"
.project-clone-holder.d-none.d-sm-inline-flex .project-clone-holder.d-none.d-md-inline-flex
= render "shared/clone_panel" = render "projects/buttons/clone"
- if show_xcode_link?(@project) - if can?(current_user, :download_code, @project)
.project-action-button.project-xcode.inline %nav.project-stats
= render "projects/buttons/xcode_link" .nav-links.quick-links.mt-3
= render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout)
- if current_user .project-home-desc.mt-1
- if can?(current_user, :download_code, @project) - if @project.description.present?
.d-none.d-sm-inline-flex .project-description
= render 'projects/buttons/download', project: @project, ref: @ref .project-description-markdown.read-more-container
.d-none.d-sm-inline-flex = markdown_field(@project, :description)
= render 'projects/buttons/dropdown' %button.btn.btn-blank.btn-link.js-read-more-trigger.d-lg-none{ type: "button" }
= _("Read more")
.d-none.d-sm-inline-flex - if @project.forked?
= render 'shared/notifications/button', notification_setting: @notification_setting %p
.d-none.d-sm-inline-flex - if @project.fork_source
= render 'shared/members/access_request_buttons', source: @project #{ s_('ForkedFromProjectPath|Forked from') }
= link_to project_path(@project.fork_source) do
= fork_source_name(@project)
- else
- deleted_message = s_('ForkedFromProjectPath|Forked from %{project_name} (deleted)')
= deleted_message % { project_name: fork_source_name(@project) }
- if @project.badges.present?
.project-badges.mb-2
- @project.badges.each do |badge|
%a.append-right-8{ href: badge.rendered_link_url(@project),
target: '_blank',
rel: 'noopener noreferrer' }>
%img.project-badge{ src: badge.rendered_image_url(@project),
'aria-hidden': true,
alt: 'Project badge' }>

View File

@ -4,5 +4,5 @@
%ul.nav %ul.nav
- anchors.each do |anchor| - anchors.each do |anchor|
%li.nav-item %li.nav-item
= link_to_if anchor.link, anchor.label, anchor.link, class: anchor.enabled ? 'nav-link stat-link' : "nav-link btn btn-#{anchor.class_modifier || 'missing'}" do = link_to_if anchor.link, anchor.label, anchor.link, class: anchor.is_link ? 'nav-link stat-link d-flex align-items-center' : "nav-link btn btn-#{anchor.class_modifier || 'missing'} d-flex align-items-center" do
.stat-text= anchor.label .stat-text.d-flex.align-items-center= anchor.label

View File

@ -0,0 +1,31 @@
- project = project || @project
.git-clone-holder.js-git-clone-holder.input-group
- if allowed_protocols_present?
.input-group-text.clone-dropdown-btn.btn
%span.js-clone-dropdown-label
= enabled_project_button(project, enabled_protocol)
- else
%a#clone-dropdown.input-group-text.btn.btn-primary.btn-xs.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } }
%span.append-right-4.js-clone-dropdown-label
= _('Clone')
= sprite_icon("arrow-down", css_class: "icon")
%form.p-3.dropdown-menu.dropdown-menu-right.dropdown-menu-large.dropdown-menu-selectable.clone-options-dropdown
%li.pb-2
%label.label-bold
= _('Clone with SSH')
.input-group
= text_field_tag :ssh_project_clone, project.ssh_url_to_repo, class: "js-select-on-focus form-control", readonly: true, aria: { label: 'Project clone URL' }
.input-group-append
= clipboard_button(target: '#ssh_project_clone', title: _("Copy URL to clipboard"), class: "input-group-text btn-default btn-clipboard")
= render_if_exists 'projects/buttons/geo'
%li
%label.label-bold
= _('Clone with %{http_label}') % { http_label: gitlab_config.protocol.upcase }
.input-group
= text_field_tag :http_project_clone, project.http_url_to_repo, class: "js-select-on-focus form-control", readonly: true, aria: { label: 'Project clone URL' }
.input-group-append
= clipboard_button(target: '#http_project_clone', title: _("Copy URL to clipboard"), class: "input-group-text btn-default btn-clipboard")
= render_if_exists 'projects/buttons/geo'
= render_if_exists 'shared/geo_info_modal', project: project

View File

@ -5,8 +5,8 @@
.project-action-button.dropdown.inline> .project-action-button.dropdown.inline>
%button.btn.has-tooltip{ title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download'), 'data-display' => 'static' } %button.btn.has-tooltip{ title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download'), 'data-display' => 'static' }
= sprite_icon('download') = sprite_icon('download')
= icon("caret-down")
%span.sr-only= _('Select Archive Format') %span.sr-only= _('Select Archive Format')
= sprite_icon("arrow-down")
%ul.dropdown-menu.dropdown-menu-right{ role: 'menu' } %ul.dropdown-menu.dropdown-menu-right{ role: 'menu' }
%li.dropdown-header %li.dropdown-header
#{ _('Source code') } #{ _('Source code') }

View File

@ -1,9 +1,6 @@
- unless @project.empty_repo? - unless @project.empty_repo?
- if current_user && can?(current_user, :fork_project, @project) - if current_user && can?(current_user, :fork_project, @project)
.count-badge.d-inline-flex.align-item-stretch.append-right-8 .count-badge.d-inline-flex.align-item-stretch.append-right-8
%span.fork-count.count-badge-count.d-flex.align-items-center
= link_to project_forks_path(@project), title: n_(s_('ProjectOverview|Fork'), s_('ProjectOverview|Forks'), @project.forks_count), class: 'count' do
= @project.forks_count
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: s_('ProjectOverview|Go to your fork'), class: 'btn btn-default has-tooltip count-badge-button d-flex align-items-center fork-btn' do = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: s_('ProjectOverview|Go to your fork'), class: 'btn btn-default has-tooltip count-badge-button d-flex align-items-center fork-btn' do
= sprite_icon('fork', { css_class: 'icon' }) = sprite_icon('fork', { css_class: 'icon' })
@ -15,3 +12,6 @@
title: (s_('ProjectOverview|You have reached your project limit') unless can_create_fork) do title: (s_('ProjectOverview|You have reached your project limit') unless can_create_fork) do
= sprite_icon('fork', { css_class: 'icon' }) = sprite_icon('fork', { css_class: 'icon' })
%span= s_('ProjectOverview|Fork') %span= s_('ProjectOverview|Fork')
%span.fork-count.count-badge-count.d-flex.align-items-center
= link_to project_forks_path(@project), title: n_(s_('ProjectOverview|Fork'), s_('ProjectOverview|Forks'), @project.forks_count), class: 'count' do
= @project.forks_count

View File

@ -0,0 +1,27 @@
- btn_class = local_assigns.fetch(:btn_class, "btn-xs")
- if notification_setting
.js-notification-dropdown.notification-dropdown.project-action-button.dropdown.inline
= form_for notification_setting, remote: true, html: { class: "inline notification-form no-label" } do |f|
= hidden_setting_source_input(notification_setting)
= hidden_field_tag "hide_label", true
= f.hidden_field :level, class: "notification_setting_level"
.js-notification-toggle-btns
%div{ class: ("btn-group" if notification_setting.custom?) }
- if notification_setting.custom?
%button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) }, class: "#{btn_class}", "aria-label" => _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) }, data: { container: "body", toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } }
= sprite_icon("notifications", css_class: "icon notifications-icon js-notifications-icon")
%span.js-notification-loading.fa.hidden
%button.btn.dropdown-toggle{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
= sprite_icon("arrow-down", css_class: "icon")
.sr-only Toggle dropdown
- else
%button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting - #{notification_title(notification_setting.level)}", class: "#{btn_class}", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
= sprite_icon("notifications", css_class: "icon notifications-icon js-notifications-icon")
%span.js-notification-loading.fa.hidden
= sprite_icon("arrow-down", css_class: "icon")
= render "shared/notifications/notification_dropdown", notification_setting: notification_setting
= content_for :scripts_body do
= render "shared/notifications/custom_notifications", notification_setting: notification_setting

View File

@ -1,19 +1,19 @@
- if current_user - if current_user
.count-badge.d-inline-flex.align-item-stretch.append-right-8 .count-badge.d-inline-flex.align-item-stretch.append-right-8
%span.star-count.count-badge-count.d-flex.align-items-center %button.count-badge-button.btn.btn-default.btn-xs.d-flex.align-items-center.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }
= @project.star_count
%button.count-badge-button.btn.btn-default.d-flex.align-items-center.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }
- if current_user.starred?(@project) - if current_user.starred?(@project)
= sprite_icon('star', { css_class: 'icon' }) = sprite_icon('star', { css_class: 'icon' })
%span.starred= s_('ProjectOverview|Unstar') %span.starred= s_('ProjectOverview|Unstar')
- else - else
= sprite_icon('star-o', { css_class: 'icon' }) = sprite_icon('star-o', { css_class: 'icon' })
%span= s_('ProjectOverview|Star') %span= s_('ProjectOverview|Star')
%span.star-count.count-badge-count.d-flex.align-items-center
= @project.star_count
- else - else
.count-badge.d-inline-flex.align-item-stretch.append-right-8 .count-badge.d-inline-flex.align-item-stretch.append-right-8
%span.star-count.count-badge-count.d-flex.align-items-center = link_to new_user_session_path, class: 'btn btn-default btn-xs has-tooltip count-badge-button d-flex align-items-center star-btn', title: s_('ProjectOverview|You must sign in to star a project') do
= @project.star_count
= link_to new_user_session_path, class: 'btn btn-default has-tooltip count-badge-button d-flex align-items-center star-btn', title: s_('ProjectOverview|You must sign in to star a project') do
= sprite_icon('star-o', { css_class: 'icon' }) = sprite_icon('star-o', { css_class: 'icon' })
%span= s_('ProjectOverview|Star') %span= s_('ProjectOverview|Star')
%span.star-count.count-badge-count.d-flex.align-items-center
= @project.star_count

View File

@ -4,11 +4,10 @@
= render partial: 'flash_messages', locals: { project: @project } = render partial: 'flash_messages', locals: { project: @project }
= render "home_panel" %div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
= render "home_panel"
.project-empty-note-panel .project-empty-note-panel
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
.prepend-top-20
%h4.append-bottom-20 %h4.append-bottom-20
= _('The repository for this project is empty') = _('The repository for this project is empty')
@ -32,66 +31,65 @@
= _('Otherwise it is recommended you start with one of the options below.') = _('Otherwise it is recommended you start with one of the options below.')
.prepend-top-20 .prepend-top-20
%nav.project-stats{ class: [container_class, ("limit-container-width" unless fluid_layout)] } %nav.project-buttons
.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller .scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
.fade-left= icon('angle-left') .fade-left= icon('angle-left')
.fade-right= icon('angle-right') .fade-right= icon('angle-right')
.nav-links.scrolling-tabs.quick-links .nav-links.scrolling-tabs.quick-links
= render 'stat_anchor_list', anchors: @project.empty_repo_statistics_anchors = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons
= render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] } %div
.prepend-top-20 .prepend-top-20
.empty_wrapper .empty_wrapper
%h3#repo-command-line-instructions.page-title-empty %h3#repo-command-line-instructions.page-title-empty
Command line instructions = _('Command line instructions')
.git-empty.js-git-empty .git-empty.js-git-empty
%fieldset %fieldset
%h5 Git global setup %h5= _('Git global setup')
%pre.bg-light %pre.bg-light
:preserve :preserve
git config --global user.name "#{h git_user_name}" git config --global user.name "#{h git_user_name}"
git config --global user.email "#{h git_user_email}" git config --global user.email "#{h git_user_email}"
%fieldset %fieldset
%h5 Create a new repository %h5= _('Create a new repository')
%pre.bg-light %pre.bg-light
:preserve :preserve
git clone #{ content_tag(:span, default_url_to_repo, class: 'js-clone')} git clone #{ content_tag(:span, default_url_to_repo, class: 'js-clone')}
cd #{h @project.path} cd #{h @project.path}
touch README.md touch README.md
git add README.md git add README.md
git commit -m "add README" git commit -m "add README"
- if @project.can_current_user_push_to_default_branch? - if @project.can_current_user_push_to_default_branch?
%span>< %span><
git push -u origin master git push -u origin master
%fieldset %fieldset
%h5 Existing folder %h5= _('Existing folder')
%pre.bg-light %pre.bg-light
:preserve :preserve
cd existing_folder cd existing_folder
git init git init
git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')} git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')}
git add . git add .
git commit -m "Initial commit" git commit -m "Initial commit"
- if @project.can_current_user_push_to_default_branch? - if @project.can_current_user_push_to_default_branch?
%span>< %span><
git push -u origin master git push -u origin master
%fieldset %fieldset
%h5 Existing Git repository %h5= _('Existing Git repository')
%pre.bg-light %pre.bg-light
:preserve :preserve
cd existing_repo cd existing_repo
git remote rename origin old-origin git remote rename origin old-origin
git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')} git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')}
- if @project.can_current_user_push_to_default_branch? - if @project.can_current_user_push_to_default_branch?
%span>< %span><
git push -u origin --all git push -u origin --all
git push -u origin --tags git push -u origin --tags
- if can? current_user, :remove_project, @project - if can? current_user, :remove_project, @project
.prepend-top-20 .prepend-top-20
= link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-inverted btn-remove float-right" = link_to _('Remove project'), [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-inverted btn-remove float-right"

View File

@ -1,7 +1,6 @@
- @no_container = true - @no_container = true
- breadcrumb_title _("Details") - breadcrumb_title _("Details")
- @content_class = "limit-container-width" unless fluid_layout - @content_class = "limit-container-width" unless fluid_layout
- show_auto_devops_callout = show_auto_devops_callout?(@project)
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, project_path(@project, rss_url_options), title: "#{@project.name} activity") = auto_discovery_link_tag(:atom, project_path(@project, rss_url_options), title: "#{@project.name} activity")
@ -15,20 +14,11 @@
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] } %div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
= render "projects/last_push" = render "projects/last_push"
= render "home_panel" = render "home_panel"
- if can?(current_user, :download_code, @project)
%nav.project-stats{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
.nav-links.scrolling-tabs.quick-links
= render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout)
= render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout)
- if can?(current_user, :download_code, @project) && @project.repository_languages.present?
= repository_languages_bar(@project.repository_languages) = repository_languages_bar(@project.repository_languages)
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
- if @project.archived? - if @project.archived?
.text-warning.center.prepend-top-20 .text-warning.center.prepend-top-20
%p %p
@ -41,4 +31,4 @@
= render 'shared/auto_devops_callout' = render 'shared/auto_devops_callout'
%div{ class: project_child_container_class(view_path) } %div{ class: project_child_container_class(view_path) }
= render view_path = render view_path, is_project_overview: true

View File

@ -85,4 +85,8 @@
= link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default qa-web-ide-button' do = link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default qa-web-ide-button' do
= _('Web IDE') = _('Web IDE')
- if show_xcode_link?(@project)
.project-action-button.project-xcode.inline
= render "projects/buttons/xcode_link"
= render 'projects/buttons/download', project: @project, ref: @ref = render 'projects/buttons/download', project: @project, ref: @ref

View File

@ -1,13 +1,13 @@
- project = project || @project - project = project || @project
- ssh_copy_label = _("Copy SSH clone URL") - ssh_copy_label = _("Copy SSH clone URL")
- http_copy_label = _("Copy HTTPS clone URL") - http_copy_label = _('Copy %{http_label} clone URL') % { http_label: gitlab_config.protocol.upcase }
.btn-group.mobile-git-clone.js-mobile-git-clone .btn-group.mobile-git-clone.js-mobile-git-clone.btn-block
= clipboard_button(button_text: default_clone_label, target: '#project_clone', hide_button_icon: true, class: "input-group-text clone-dropdown-btn js-clone-dropdown-label btn btn-default") = clipboard_button(button_text: default_clone_label, text: default_url_to_repo(project), hide_button_icon: true, class: "btn-primary flex-fill bold justify-content-center input-group-text clone-dropdown-btn js-clone-dropdown-label")
%button.btn.btn-default.dropdown-toggle.js-dropdown-toggle{ type: "button", data: { toggle: "dropdown" } } %button.btn.btn-primary.dropdown-toggle.js-dropdown-toggle{ type: "button", data: { toggle: "dropdown" } }
= icon("caret-down", class: "dropdown-btn-icon") = sprite_icon("arrow-down", css_class: "dropdown-btn-icon icon")
%ul.dropdown-menu.dropdown-menu-selectable.dropdown-menu-right.clone-options-dropdown{ data: { dropdown: true } } %ul.dropdown-menu.dropdown-menu-selectable.dropdown-menu-right.clone-options-dropdown{ data: { dropdown: true } }
%li %li
= dropdown_item_with_description(ssh_copy_label, project.ssh_url_to_repo, href: project.ssh_url_to_repo, data: { clone_type: 'ssh' }) = dropdown_item_with_description(ssh_copy_label, project.ssh_url_to_repo, href: project.ssh_url_to_repo, data: { clone_type: 'ssh' }, default: true)
%li %li
= dropdown_item_with_description(http_copy_label, project.http_url_to_repo, href: project.http_url_to_repo, data: { clone_type: 'http' }) = dropdown_item_with_description(http_copy_label, project.http_url_to_repo, href: project.http_url_to_repo, data: { clone_type: 'http' })

View File

@ -0,0 +1,17 @@
- model_name = source.model_name.to_s.downcase
- if can?(current_user, :"destroy_#{model_name}_member", source.members.find_by(user_id: current_user.id)) # rubocop: disable CodeReuse/ActiveRecord
- link_text = source.is_a?(Group) ? _('Leave group') : _('Leave project')
= link_to link_text, polymorphic_path([:leave, source, :members]),
method: :delete,
data: { confirm: leave_confirmation_message(source) },
class: 'access-request-link'
- elsif requester = source.requesters.find_by(user_id: current_user.id) # rubocop: disable CodeReuse/ActiveRecord
= link_to _('Withdraw Access Request'), polymorphic_path([:leave, source, :members]),
method: :delete,
data: { confirm: remove_member_message(requester) },
class: 'access-request-link'
- elsif source.request_access_enabled && can?(current_user, :request_access, source)
= link_to _('Request Access'), polymorphic_path([:request_access, source, :members]),
method: :post,
class: 'access-request-link'

View File

@ -1,3 +1,5 @@
- btn_class = local_assigns.fetch(:btn_class, nil)
- if notification_setting - if notification_setting
.js-notification-dropdown.notification-dropdown.project-action-button.dropdown.inline .js-notification-dropdown.notification-dropdown.project-action-button.dropdown.inline
= form_for notification_setting, remote: true, html: { class: "inline notification-form" } do |f| = form_for notification_setting, remote: true, html: { class: "inline notification-form" } do |f|
@ -6,14 +8,14 @@
.js-notification-toggle-btns .js-notification-toggle-btns
%div{ class: ("btn-group" if notification_setting.custom?) } %div{ class: ("btn-group" if notification_setting.custom?) }
- if notification_setting.custom? - if notification_setting.custom?
%button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } } %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: _("Notification setting"), class: "#{btn_class}", "aria-label" => _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) }, data: { container: "body", toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } }
= icon("bell", class: "js-notification-loading") = icon("bell", class: "js-notification-loading")
= notification_title(notification_setting.level) = notification_title(notification_setting.level)
%button.btn.dropdown-toggle{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } } %button.btn.dropdown-toggle{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
= icon('caret-down') = icon('caret-down')
.sr-only Toggle dropdown .sr-only Toggle dropdown
- else - else
%button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } } %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting", class: "#{btn_class}", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
= icon("bell", class: "js-notification-loading") = icon("bell", class: "js-notification-loading")
= notification_title(notification_setting.level) = notification_title(notification_setting.level)
= icon("caret-down") = icon("caret-down")

View File

@ -0,0 +1,5 @@
---
title: Design improvements to project overview page
merge_request: 22196
author:
type: changed

View File

@ -141,6 +141,24 @@ msgstr ""
msgid "%{percent}%% complete" msgid "%{percent}%% complete"
msgstr "" msgstr ""
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
msgstr[1] ""
msgid "%{strong_start}%{commit_count}%{strong_end} Commit"
msgid_plural "%{strong_start}%{commit_count}%{strong_end} Commits"
msgstr[0] ""
msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Files"
msgstr ""
msgid "%{strong_start}%{tag_count}%{strong_end} Tag"
msgid_plural "%{strong_start}%{tag_count}%{strong_end} Tags"
msgstr[0] ""
msgstr[1] ""
msgid "%{text} %{files}" msgid "%{text} %{files}"
msgid_plural "%{text} %{files} files" msgid_plural "%{text} %{files} files"
msgstr[0] "" msgstr[0] ""
@ -333,16 +351,16 @@ msgstr ""
msgid "Activity" msgid "Activity"
msgstr "" msgstr ""
msgid "Add Changelog" msgid "Add CHANGELOG"
msgstr "" msgstr ""
msgid "Add Contribution guide" msgid "Add CONTRIBUTING"
msgstr "" msgstr ""
msgid "Add Kubernetes cluster" msgid "Add Kubernetes cluster"
msgstr "" msgstr ""
msgid "Add Readme" msgid "Add README"
msgstr "" msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message." msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
@ -945,11 +963,6 @@ msgstr ""
msgid "Branch %{branchName} was not found in this project's repository." msgid "Branch %{branchName} was not found in this project's repository."
msgstr "" msgstr ""
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
msgstr[0] ""
msgstr[1] ""
msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}" msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}"
msgstr "" msgstr ""
@ -1103,6 +1116,9 @@ msgstr ""
msgid "ByAuthor|by" msgid "ByAuthor|by"
msgstr "" msgstr ""
msgid "CHANGELOG"
msgstr ""
msgid "CI / CD" msgid "CI / CD"
msgstr "" msgstr ""
@ -1160,6 +1176,9 @@ msgstr ""
msgid "CICD|instance enabled" msgid "CICD|instance enabled"
msgstr "" msgstr ""
msgid "CONTRIBUTING"
msgstr ""
msgid "Callback URL" msgid "Callback URL"
msgstr "" msgstr ""
@ -1202,9 +1221,6 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes." msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr "" msgstr ""
msgid "Changelog"
msgstr ""
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision." msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr "" msgstr ""
@ -1388,9 +1404,18 @@ msgstr ""
msgid "Clients" msgid "Clients"
msgstr "" msgstr ""
msgid "Clone"
msgstr ""
msgid "Clone repository" msgid "Clone repository"
msgstr "" msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
msgid "Clone with SSH"
msgstr ""
msgid "Close" msgid "Close"
msgstr "" msgstr ""
@ -1811,6 +1836,9 @@ msgstr ""
msgid "Collapse sidebar" msgid "Collapse sidebar"
msgstr "" msgstr ""
msgid "Command line instructions"
msgstr ""
msgid "Comment" msgid "Comment"
msgstr "" msgstr ""
@ -1831,11 +1859,6 @@ msgid_plural "Commits"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
msgid "Commit (%{commit_count})"
msgid_plural "Commits (%{commit_count})"
msgstr[0] ""
msgstr[1] ""
msgid "Commit Message" msgid "Commit Message"
msgstr "" msgstr ""
@ -2019,9 +2042,6 @@ msgstr ""
msgid "Contribution Charts" msgid "Contribution Charts"
msgstr "" msgstr ""
msgid "Contribution guide"
msgstr ""
msgid "Contributions for <strong>%{calendar_date}</strong>" msgid "Contributions for <strong>%{calendar_date}</strong>"
msgstr "" msgstr ""
@ -2046,10 +2066,10 @@ msgstr ""
msgid "ConvDev Index" msgid "ConvDev Index"
msgstr "" msgstr ""
msgid "Copy %{protocol} clone URL" msgid "Copy %{http_label} clone URL"
msgstr "" msgstr ""
msgid "Copy HTTPS clone URL" msgid "Copy %{protocol} clone URL"
msgstr "" msgstr ""
msgid "Copy ID to clipboard" msgid "Copy ID to clipboard"
@ -2112,6 +2132,9 @@ msgstr ""
msgid "Create a new issue" msgid "Create a new issue"
msgstr "" msgstr ""
msgid "Create a new repository"
msgstr ""
msgid "Create a personal access token on your account to pull or push via %{protocol}." msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "" msgstr ""
@ -2870,6 +2893,12 @@ msgstr ""
msgid "Everyone can contribute" msgid "Everyone can contribute"
msgstr "" msgstr ""
msgid "Existing Git repository"
msgstr ""
msgid "Existing folder"
msgstr ""
msgid "Expand" msgid "Expand"
msgstr "" msgstr ""
@ -2978,9 +3007,6 @@ msgstr ""
msgid "Files" msgid "Files"
msgstr "" msgstr ""
msgid "Files (%{human_size})"
msgstr ""
msgid "Filter" msgid "Filter"
msgstr "" msgstr ""
@ -3107,6 +3133,9 @@ msgstr ""
msgid "Git" msgid "Git"
msgstr "" msgstr ""
msgid "Git global setup"
msgstr ""
msgid "Git repository URL" msgid "Git repository URL"
msgstr "" msgstr ""
@ -4438,6 +4467,12 @@ msgstr ""
msgid "Notification events" msgid "Notification events"
msgstr "" msgstr ""
msgid "Notification setting"
msgstr ""
msgid "Notification setting - %{notification_title}"
msgstr ""
msgid "NotificationEvent|Close issue" msgid "NotificationEvent|Close issue"
msgstr "" msgstr ""
@ -5349,6 +5384,9 @@ msgstr ""
msgid "Quick actions can be used in the issues description and comment boxes." msgid "Quick actions can be used in the issues description and comment boxes."
msgstr "" msgstr ""
msgid "README"
msgstr ""
msgid "Read more" msgid "Read more"
msgstr "" msgstr ""
@ -5358,9 +5396,6 @@ msgstr ""
msgid "Read more about project permissions <strong>%{link_to_help}</strong>" msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
msgstr "" msgstr ""
msgid "Readme"
msgstr ""
msgid "Real-time features" msgid "Real-time features"
msgstr "" msgstr ""
@ -6294,11 +6329,6 @@ msgstr ""
msgid "System metrics (Kubernetes)" msgid "System metrics (Kubernetes)"
msgstr "" msgstr ""
msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})"
msgstr[0] ""
msgstr[1] ""
msgid "Tags" msgid "Tags"
msgstr "" msgstr ""

View File

@ -10,54 +10,9 @@ describe 'Projects > Show > Developer views empty project instructions' do
sign_in(developer) sign_in(developer)
end end
context 'without an SSH key' do it 'displays "git clone" instructions' do
it 'defaults to HTTP' do
visit_project
expect_instructions_for('http')
end
it 'switches to SSH', :js do
visit_project
select_protocol('SSH')
expect_instructions_for('ssh')
end
end
context 'with an SSH key' do
before do
create(:personal_key, user: developer)
end
it 'defaults to SSH' do
visit_project
expect_instructions_for('ssh')
end
it 'switches to HTTP', :js do
visit_project
select_protocol('HTTP')
expect_instructions_for('http')
end
end
def visit_project
visit project_path(project) visit project_path(project)
end
def select_protocol(protocol) expect(page).to have_content("git clone")
find('#clone-dropdown').click
find(".#{protocol.downcase}-selector").click
end
def expect_instructions_for(protocol)
msg = :"#{protocol.downcase}_url_to_repo"
expect(page).to have_content("git clone #{project.send(msg)}")
end end
end end

View File

@ -8,13 +8,18 @@ describe 'Projects > Show > User manages notifications', :js do
visit project_path(project) visit project_path(project)
end end
it 'changes the notification setting' do def click_notifications_button
first('.notifications-btn').click first('.notifications-btn').click
end
it 'changes the notification setting' do
click_notifications_button
click_link 'On mention' click_link 'On mention'
page.within '#notifications-button' do wait_for_requests
expect(page).to have_content 'On mention'
end click_notifications_button
expect(find('.update-notification.is-active')).to have_content('On mention')
end end
context 'custom notification settings' do context 'custom notification settings' do
@ -38,7 +43,7 @@ describe 'Projects > Show > User manages notifications', :js do
end end
it 'shows notification settings checkbox' do it 'shows notification settings checkbox' do
first('.notifications-btn').click click_notifications_button
page.find('a[data-notification-level="custom"]').click page.find('a[data-notification-level="custom"]').click
page.within('.custom-notifications-form') do page.within('.custom-notifications-form') do

View File

@ -21,18 +21,6 @@ describe 'Projects > Show > Collaboration links' do
end end
end end
# The project header
page.within('.project-home-panel') do
aggregate_failures 'dropdown links in the project home panel' do
expect(page).to have_link('New issue')
expect(page).to have_link('New merge request')
expect(page).to have_link('New snippet')
expect(page).to have_link('New file')
expect(page).to have_link('New branch')
expect(page).to have_link('New tag')
end
end
# The dropdown above the tree # The dropdown above the tree
page.within('.repo-breadcrumb') do page.within('.repo-breadcrumb') do
aggregate_failures 'dropdown links above the repo tree' do aggregate_failures 'dropdown links above the repo tree' do
@ -61,17 +49,6 @@ describe 'Projects > Show > Collaboration links' do
end end
end end
page.within('.project-home-panel') do
aggregate_failures 'dropdown links' do
expect(page).not_to have_link('New issue')
expect(page).not_to have_link('New merge request')
expect(page).not_to have_link('New snippet')
expect(page).not_to have_link('New file')
expect(page).not_to have_link('New branch')
expect(page).not_to have_link('New tag')
end
end
page.within('.repo-breadcrumb') do page.within('.repo-breadcrumb') do
aggregate_failures 'dropdown links' do aggregate_failures 'dropdown links' do
expect(page).not_to have_link('New file') expect(page).not_to have_link('New file')

View File

@ -29,7 +29,7 @@ describe 'Projects > Show > User sees Git instructions' do
expect(element.text).to include(project.http_url_to_repo) expect(element.text).to include(project.http_url_to_repo)
end end
expect(page).to have_field('project_clone', with: project.http_url_to_repo) unless user_has_ssh_key expect(page).to have_field('http_project_clone', with: project.http_url_to_repo) unless user_has_ssh_key
end end
end end
@ -41,7 +41,7 @@ describe 'Projects > Show > User sees Git instructions' do
expect(page).to have_content(project.title) expect(page).to have_content(project.title)
end end
expect(page).to have_field('project_clone', with: project.http_url_to_repo) unless user_has_ssh_key expect(page).to have_field('http_project_clone', with: project.http_url_to_repo) unless user_has_ssh_key
end end
end end

View File

@ -21,7 +21,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
end end
it 'no Auto DevOps button if can not manage pipelines' do it 'no Auto DevOps button if can not manage pipelines' do
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).not_to have_link('Enable Auto DevOps') expect(page).not_to have_link('Enable Auto DevOps')
expect(page).not_to have_link('Auto DevOps enabled') expect(page).not_to have_link('Auto DevOps enabled')
end end
@ -30,7 +30,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
it '"Auto DevOps enabled" button not linked' do it '"Auto DevOps enabled" button not linked' do
visit project_path(project) visit project_path(project)
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).to have_text('Auto DevOps enabled') expect(page).to have_text('Auto DevOps enabled')
end end
end end
@ -45,19 +45,19 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
end end
it '"New file" button linked to new file page' do it '"New file" button linked to new file page' do
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).to have_link('New file', href: project_new_blob_path(project, project.default_branch || 'master')) expect(page).to have_link('New file', href: project_new_blob_path(project, project.default_branch || 'master'))
end end
end end
it '"Add Readme" button linked to new file populated for a readme' do it '"Add README" button linked to new file populated for a README' do
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).to have_link('Add Readme', href: presenter.add_readme_path) expect(page).to have_link('Add README', href: presenter.add_readme_path)
end end
end end
it '"Add license" button linked to new file populated for a license' do it '"Add license" button linked to new file populated for a license' do
page.within('.project-metadata') do page.within('.project-stats') do
expect(page).to have_link('Add license', href: presenter.add_license_path) expect(page).to have_link('Add license', href: presenter.add_license_path)
end end
end end
@ -67,7 +67,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
it '"Auto DevOps enabled" anchor linked to settings page' do it '"Auto DevOps enabled" anchor linked to settings page' do
visit project_path(project) visit project_path(project)
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings')) expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
end end
end end
@ -77,7 +77,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
let(:project) { create(:project, :public, :empty_repo, auto_devops_attributes: { enabled: false }) } let(:project) { create(:project, :public, :empty_repo, auto_devops_attributes: { enabled: false }) }
it '"Enable Auto DevOps" button linked to settings page' do it '"Enable Auto DevOps" button linked to settings page' do
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings')) expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
end end
end end
@ -86,7 +86,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
describe 'Kubernetes cluster button' do describe 'Kubernetes cluster button' do
it '"Add Kubernetes cluster" button linked to clusters page' do it '"Add Kubernetes cluster" button linked to clusters page' do
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).to have_link('Add Kubernetes cluster', href: new_project_cluster_path(project)) expect(page).to have_link('Add Kubernetes cluster', href: new_project_cluster_path(project))
end end
end end
@ -96,7 +96,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
visit project_path(project) visit project_path(project)
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).to have_link('Kubernetes configured', href: project_cluster_path(project, cluster)) expect(page).to have_link('Kubernetes configured', href: project_cluster_path(project, cluster))
end end
end end
@ -119,7 +119,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
it '"Auto DevOps enabled" button not linked' do it '"Auto DevOps enabled" button not linked' do
visit project_path(project) visit project_path(project)
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).to have_text('Auto DevOps enabled') expect(page).to have_text('Auto DevOps enabled')
end end
end end
@ -129,14 +129,14 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
let(:project) { create(:project, :public, :repository, auto_devops_attributes: { enabled: false }) } let(:project) { create(:project, :public, :repository, auto_devops_attributes: { enabled: false }) }
it 'no Auto DevOps button if can not manage pipelines' do it 'no Auto DevOps button if can not manage pipelines' do
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).not_to have_link('Enable Auto DevOps') expect(page).not_to have_link('Enable Auto DevOps')
expect(page).not_to have_link('Auto DevOps enabled') expect(page).not_to have_link('Auto DevOps enabled')
end end
end end
it 'no Kubernetes cluster button if can not manage clusters' do it 'no Kubernetes cluster button if can not manage clusters' do
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).not_to have_link('Add Kubernetes cluster') expect(page).not_to have_link('Add Kubernetes cluster')
expect(page).not_to have_link('Kubernetes configured') expect(page).not_to have_link('Kubernetes configured')
end end
@ -151,59 +151,59 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
sign_in(user) sign_in(user)
end end
context 'Readme button' do context 'README button' do
before do before do
allow(Project).to receive(:find_by_full_path) allow(Project).to receive(:find_by_full_path)
.with(project.full_path, follow_redirects: true) .with(project.full_path, follow_redirects: true)
.and_return(project) .and_return(project)
end end
context 'when the project has a populated Readme' do context 'when the project has a populated README' do
it 'show the "Readme" anchor' do it 'show the "README" anchor' do
visit project_path(project) visit project_path(project)
expect(project.repository.readme).not_to be_nil expect(project.repository.readme).not_to be_nil
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).not_to have_link('Add Readme', href: presenter.add_readme_path) expect(page).not_to have_link('Add README', href: presenter.add_readme_path)
expect(page).to have_link('Readme', href: presenter.readme_path) expect(page).to have_link('README', href: presenter.readme_path)
end end
end end
context 'when the project has an empty Readme' do context 'when the project has an empty README' do
it 'show the "Readme" anchor' do it 'show the "README" anchor' do
allow(project.repository).to receive(:readme).and_return(fake_blob(path: 'README.md', data: '', size: 0)) allow(project.repository).to receive(:readme).and_return(fake_blob(path: 'README.md', data: '', size: 0))
visit project_path(project) visit project_path(project)
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).not_to have_link('Add Readme', href: presenter.add_readme_path) expect(page).not_to have_link('Add README', href: presenter.add_readme_path)
expect(page).to have_link('Readme', href: presenter.readme_path) expect(page).to have_link('README', href: presenter.readme_path)
end end
end end
end end
end end
context 'when the project does not have a Readme' do context 'when the project does not have a README' do
it 'shows the "Add Readme" button' do it 'shows the "Add README" button' do
allow(project.repository).to receive(:readme).and_return(nil) allow(project.repository).to receive(:readme).and_return(nil)
visit project_path(project) visit project_path(project)
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).to have_link('Add Readme', href: presenter.add_readme_path) expect(page).to have_link('Add README', href: presenter.add_readme_path)
end end
end end
end end
end end
it 'no "Add Changelog" button if the project already has a changelog' do it 'no "Add CHANGELOG" button if the project already has a changelog' do
visit project_path(project) visit project_path(project)
expect(project.repository.changelog).not_to be_nil expect(project.repository.changelog).not_to be_nil
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).not_to have_link('Add Changelog') expect(page).not_to have_link('Add CHANGELOG')
end end
end end
@ -212,18 +212,18 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
expect(project.repository.license_blob).not_to be_nil expect(project.repository.license_blob).not_to be_nil
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).not_to have_link('Add license') expect(page).not_to have_link('Add license')
end end
end end
it 'no "Add Contribution guide" button if the project already has a contribution guide' do it 'no "Add CONTRIBUTING" button if the project already has a contribution guide' do
visit project_path(project) visit project_path(project)
expect(project.repository.contribution_guide).not_to be_nil expect(project.repository.contribution_guide).not_to be_nil
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).not_to have_link('Add Contribution guide') expect(page).not_to have_link('Add CONTRIBUTING')
end end
end end
@ -232,7 +232,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
it 'no "Set up CI/CD" button if the project has Auto DevOps enabled' do it 'no "Set up CI/CD" button if the project has Auto DevOps enabled' do
visit project_path(project) visit project_path(project)
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).not_to have_link('Set up CI/CD') expect(page).not_to have_link('Set up CI/CD')
end end
end end
@ -246,7 +246,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
expect(project.repository.gitlab_ci_yml).to be_nil expect(project.repository.gitlab_ci_yml).to be_nil
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).to have_link('Set up CI/CD', href: presenter.add_ci_yml_path) expect(page).to have_link('Set up CI/CD', href: presenter.add_ci_yml_path)
end end
end end
@ -266,7 +266,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
visit project_path(project) visit project_path(project)
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).not_to have_link('Set up CI/CD') expect(page).not_to have_link('Set up CI/CD')
end end
end end
@ -278,7 +278,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
it '"Auto DevOps enabled" anchor linked to settings page' do it '"Auto DevOps enabled" anchor linked to settings page' do
visit project_path(project) visit project_path(project)
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings')) expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
end end
end end
@ -290,7 +290,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
it '"Enable Auto DevOps" button linked to settings page' do it '"Enable Auto DevOps" button linked to settings page' do
visit project_path(project) visit project_path(project)
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings')) expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
end end
end end
@ -302,7 +302,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
expect(page).to have_selector('.js-autodevops-banner') expect(page).to have_selector('.js-autodevops-banner')
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).not_to have_link('Enable Auto DevOps') expect(page).not_to have_link('Enable Auto DevOps')
expect(page).not_to have_link('Auto DevOps enabled') expect(page).not_to have_link('Auto DevOps enabled')
end end
@ -323,7 +323,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
visit project_path(project) visit project_path(project)
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).not_to have_link('Enable Auto DevOps') expect(page).not_to have_link('Enable Auto DevOps')
expect(page).not_to have_link('Auto DevOps enabled') expect(page).not_to have_link('Auto DevOps enabled')
end end
@ -335,7 +335,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
it '"Add Kubernetes cluster" button linked to clusters page' do it '"Add Kubernetes cluster" button linked to clusters page' do
visit project_path(project) visit project_path(project)
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).to have_link('Add Kubernetes cluster', href: new_project_cluster_path(project)) expect(page).to have_link('Add Kubernetes cluster', href: new_project_cluster_path(project))
end end
end end
@ -345,7 +345,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
visit project_path(project) visit project_path(project)
page.within('.project-stats') do page.within('.project-buttons') do
expect(page).to have_link('Kubernetes configured', href: project_cluster_path(project, cluster)) expect(page).to have_link('Kubernetes configured', href: project_cluster_path(project, cluster))
end end
end end

View File

@ -13,7 +13,7 @@ describe 'Maintainer views tags' do
before do before do
visit project_path(project) visit project_path(project)
click_on 'Add Readme' click_on 'Add README'
fill_in :commit_message, with: 'Add a README file', visible: true fill_in :commit_message, with: 'Add a README file', visible: true
click_button 'Commit changes' click_button 'Commit changes'
visit project_tags_path(project) visit project_tags_path(project)

View File

@ -165,32 +165,32 @@ describe ProjectPresenter do
describe '#files_anchor_data' do describe '#files_anchor_data' do
it 'returns files data' do it 'returns files data' do
expect(presenter.files_anchor_data).to have_attributes(enabled: true, expect(presenter.files_anchor_data).to have_attributes(is_link: true,
label: 'Files (0 Bytes)', label: a_string_including('0 Bytes'),
link: nil) link: nil)
end end
end end
describe '#commits_anchor_data' do describe '#commits_anchor_data' do
it 'returns commits data' do it 'returns commits data' do
expect(presenter.commits_anchor_data).to have_attributes(enabled: true, expect(presenter.commits_anchor_data).to have_attributes(is_link: true,
label: 'Commits (0)', label: a_string_including('0'),
link: nil) link: nil)
end end
end end
describe '#branches_anchor_data' do describe '#branches_anchor_data' do
it 'returns branches data' do it 'returns branches data' do
expect(presenter.branches_anchor_data).to have_attributes(enabled: true, expect(presenter.branches_anchor_data).to have_attributes(is_link: true,
label: "Branches (0)", label: a_string_including('0'),
link: nil) link: nil)
end end
end end
describe '#tags_anchor_data' do describe '#tags_anchor_data' do
it 'returns tags data' do it 'returns tags data' do
expect(presenter.tags_anchor_data).to have_attributes(enabled: true, expect(presenter.tags_anchor_data).to have_attributes(is_link: true,
label: "Tags (0)", label: a_string_including('0'),
link: nil) link: nil)
end end
end end
@ -202,32 +202,32 @@ describe ProjectPresenter do
describe '#files_anchor_data' do describe '#files_anchor_data' do
it 'returns files data' do it 'returns files data' do
expect(presenter.files_anchor_data).to have_attributes(enabled: true, expect(presenter.files_anchor_data).to have_attributes(is_link: true,
label: 'Files (0 Bytes)', label: a_string_including('0 Bytes'),
link: presenter.project_tree_path(project)) link: presenter.project_tree_path(project))
end end
end end
describe '#commits_anchor_data' do describe '#commits_anchor_data' do
it 'returns commits data' do it 'returns commits data' do
expect(presenter.commits_anchor_data).to have_attributes(enabled: true, expect(presenter.commits_anchor_data).to have_attributes(is_link: true,
label: 'Commits (0)', label: a_string_including('0'),
link: presenter.project_commits_path(project, project.repository.root_ref)) link: presenter.project_commits_path(project, project.repository.root_ref))
end end
end end
describe '#branches_anchor_data' do describe '#branches_anchor_data' do
it 'returns branches data' do it 'returns branches data' do
expect(presenter.branches_anchor_data).to have_attributes(enabled: true, expect(presenter.branches_anchor_data).to have_attributes(is_link: true,
label: "Branches (#{project.repository.branches.size})", label: a_string_including("#{project.repository.branches.size}"),
link: presenter.project_branches_path(project)) link: presenter.project_branches_path(project))
end end
end end
describe '#tags_anchor_data' do describe '#tags_anchor_data' do
it 'returns tags data' do it 'returns tags data' do
expect(presenter.tags_anchor_data).to have_attributes(enabled: true, expect(presenter.tags_anchor_data).to have_attributes(is_link: true,
label: "Tags (#{project.repository.tags.size})", label: a_string_including("#{project.repository.tags.size}"),
link: presenter.project_tags_path(project)) link: presenter.project_tags_path(project))
end end
end end
@ -236,8 +236,8 @@ describe ProjectPresenter do
it 'returns new file data if user can push' do it 'returns new file data if user can push' do
project.add_developer(user) project.add_developer(user)
expect(presenter.new_file_anchor_data).to have_attributes(enabled: false, expect(presenter.new_file_anchor_data).to have_attributes(is_link: false,
label: "New file", label: a_string_including("New file"),
link: presenter.project_new_blob_path(project, 'master'), link: presenter.project_new_blob_path(project, 'master'),
class_modifier: 'success') class_modifier: 'success')
end end
@ -264,8 +264,8 @@ describe ProjectPresenter do
project.add_developer(user) project.add_developer(user)
allow(project.repository).to receive(:readme).and_return(nil) allow(project.repository).to receive(:readme).and_return(nil)
expect(presenter.readme_anchor_data).to have_attributes(enabled: false, expect(presenter.readme_anchor_data).to have_attributes(is_link: false,
label: 'Add Readme', label: a_string_including('Add README'),
link: presenter.add_readme_path) link: presenter.add_readme_path)
end end
end end
@ -274,21 +274,21 @@ describe ProjectPresenter do
it 'returns anchor data' do it 'returns anchor data' do
allow(project.repository).to receive(:readme).and_return(double(name: 'readme')) allow(project.repository).to receive(:readme).and_return(double(name: 'readme'))
expect(presenter.readme_anchor_data).to have_attributes(enabled: true, expect(presenter.readme_anchor_data).to have_attributes(is_link: false,
label: 'Readme', label: a_string_including('README'),
link: presenter.readme_path) link: presenter.readme_path)
end end
end end
end end
describe '#changelog_anchor_data' do describe '#changelog_anchor_data' do
context 'when user can push and CHANGELOG does not exists' do context 'when user can push and CHANGELOG does not exist' do
it 'returns anchor data' do it 'returns anchor data' do
project.add_developer(user) project.add_developer(user)
allow(project.repository).to receive(:changelog).and_return(nil) allow(project.repository).to receive(:changelog).and_return(nil)
expect(presenter.changelog_anchor_data).to have_attributes(enabled: false, expect(presenter.changelog_anchor_data).to have_attributes(is_link: false,
label: 'Add Changelog', label: a_string_including('Add CHANGELOG'),
link: presenter.add_changelog_path) link: presenter.add_changelog_path)
end end
end end
@ -297,21 +297,21 @@ describe ProjectPresenter do
it 'returns anchor data' do it 'returns anchor data' do
allow(project.repository).to receive(:changelog).and_return(double(name: 'foo')) allow(project.repository).to receive(:changelog).and_return(double(name: 'foo'))
expect(presenter.changelog_anchor_data).to have_attributes(enabled: true, expect(presenter.changelog_anchor_data).to have_attributes(is_link: false,
label: 'Changelog', label: a_string_including('CHANGELOG'),
link: presenter.changelog_path) link: presenter.changelog_path)
end end
end end
end end
describe '#license_anchor_data' do describe '#license_anchor_data' do
context 'when user can push and LICENSE does not exists' do context 'when user can push and LICENSE does not exist' do
it 'returns anchor data' do it 'returns anchor data' do
project.add_developer(user) project.add_developer(user)
allow(project.repository).to receive(:license_blob).and_return(nil) allow(project.repository).to receive(:license_blob).and_return(nil)
expect(presenter.license_anchor_data).to have_attributes(enabled: false, expect(presenter.license_anchor_data).to have_attributes(is_link: true,
label: 'Add license', label: a_string_including('Add license'),
link: presenter.add_license_path) link: presenter.add_license_path)
end end
end end
@ -320,21 +320,21 @@ describe ProjectPresenter do
it 'returns anchor data' do it 'returns anchor data' do
allow(project.repository).to receive(:license_blob).and_return(double(name: 'foo')) allow(project.repository).to receive(:license_blob).and_return(double(name: 'foo'))
expect(presenter.license_anchor_data).to have_attributes(enabled: true, expect(presenter.license_anchor_data).to have_attributes(is_link: true,
label: presenter.license_short_name, label: a_string_including(presenter.license_short_name),
link: presenter.license_path) link: presenter.license_path)
end end
end end
end end
describe '#contribution_guide_anchor_data' do describe '#contribution_guide_anchor_data' do
context 'when user can push and CONTRIBUTING does not exists' do context 'when user can push and CONTRIBUTING does not exist' do
it 'returns anchor data' do it 'returns anchor data' do
project.add_developer(user) project.add_developer(user)
allow(project.repository).to receive(:contribution_guide).and_return(nil) allow(project.repository).to receive(:contribution_guide).and_return(nil)
expect(presenter.contribution_guide_anchor_data).to have_attributes(enabled: false, expect(presenter.contribution_guide_anchor_data).to have_attributes(is_link: false,
label: 'Add Contribution guide', label: a_string_including('Add CONTRIBUTING'),
link: presenter.add_contribution_guide_path) link: presenter.add_contribution_guide_path)
end end
end end
@ -343,8 +343,8 @@ describe ProjectPresenter do
it 'returns anchor data' do it 'returns anchor data' do
allow(project.repository).to receive(:contribution_guide).and_return(double(name: 'foo')) allow(project.repository).to receive(:contribution_guide).and_return(double(name: 'foo'))
expect(presenter.contribution_guide_anchor_data).to have_attributes(enabled: true, expect(presenter.contribution_guide_anchor_data).to have_attributes(is_link: false,
label: 'Contribution guide', label: a_string_including('CONTRIBUTING'),
link: presenter.contribution_guide_path) link: presenter.contribution_guide_path)
end end
end end
@ -355,20 +355,20 @@ describe ProjectPresenter do
it 'returns anchor data' do it 'returns anchor data' do
allow(project).to receive(:auto_devops_enabled?).and_return(true) allow(project).to receive(:auto_devops_enabled?).and_return(true)
expect(presenter.autodevops_anchor_data).to have_attributes(enabled: true, expect(presenter.autodevops_anchor_data).to have_attributes(is_link: false,
label: 'Auto DevOps enabled', label: a_string_including('Auto DevOps enabled'),
link: nil) link: nil)
end end
end end
context 'when user can admin pipeline and CI yml does not exists' do context 'when user can admin pipeline and CI yml does not exist' do
it 'returns anchor data' do it 'returns anchor data' do
project.add_maintainer(user) project.add_maintainer(user)
allow(project).to receive(:auto_devops_enabled?).and_return(false) allow(project).to receive(:auto_devops_enabled?).and_return(false)
allow(project.repository).to receive(:gitlab_ci_yml).and_return(nil) allow(project.repository).to receive(:gitlab_ci_yml).and_return(nil)
expect(presenter.autodevops_anchor_data).to have_attributes(enabled: false, expect(presenter.autodevops_anchor_data).to have_attributes(is_link: false,
label: 'Enable Auto DevOps', label: a_string_including('Enable Auto DevOps'),
link: presenter.project_settings_ci_cd_path(project, anchor: 'autodevops-settings')) link: presenter.project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
end end
end end
@ -380,8 +380,8 @@ describe ProjectPresenter do
project.add_maintainer(user) project.add_maintainer(user)
cluster = create(:cluster, projects: [project]) cluster = create(:cluster, projects: [project])
expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(enabled: true, expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(is_link: false,
label: 'Kubernetes configured', label: a_string_including('Kubernetes configured'),
link: presenter.project_cluster_path(project, cluster)) link: presenter.project_cluster_path(project, cluster))
end end
@ -390,16 +390,16 @@ describe ProjectPresenter do
create(:cluster, :production_environment, projects: [project]) create(:cluster, :production_environment, projects: [project])
create(:cluster, projects: [project]) create(:cluster, projects: [project])
expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(enabled: true, expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(is_link: false,
label: 'Kubernetes configured', label: a_string_including('Kubernetes configured'),
link: presenter.project_clusters_path(project)) link: presenter.project_clusters_path(project))
end end
it 'returns link to create a cluster if no cluster exists' do it 'returns link to create a cluster if no cluster exists' do
project.add_maintainer(user) project.add_maintainer(user)
expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(enabled: false, expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(is_link: false,
label: 'Add Kubernetes cluster', label: a_string_including('Add Kubernetes cluster'),
link: presenter.new_project_cluster_path(project)) link: presenter.new_project_cluster_path(project))
end end
end end

View File

@ -23,7 +23,7 @@ describe 'projects/_home_panel' do
it 'makes it possible to set notification level' do it 'makes it possible to set notification level' do
render render
expect(view).to render_template('shared/notifications/_button') expect(view).to render_template('projects/buttons/_notifications')
expect(rendered).to have_selector('.notification-dropdown') expect(rendered).to have_selector('.notification-dropdown')
end end
end end