Commit 772e482b authored by Phil Hughes's avatar Phil Hughes

Merge branch '44704-improve-project-overview-ui' into 'master'

Resolve "Improve project overview UI"

Closes #44704

See merge request gitlab-org/gitlab-ce!20536
parents d32cec18 ec4ad656
...@@ -13,40 +13,52 @@ export default class Project { ...@@ -13,40 +13,52 @@ export default class Project {
constructor() { constructor() {
const $cloneOptions = $('ul.clone-options-dropdown'); const $cloneOptions = $('ul.clone-options-dropdown');
const $projectCloneField = $('#project_clone'); const $projectCloneField = $('#project_clone');
const $cloneBtnText = $('a.clone-dropdown-btn span'); const $cloneBtnLabel = $('.js-git-clone-holder .js-clone-dropdown-label');
const selectedCloneOption = $cloneBtnText.text().trim(); const selectedCloneOption = $cloneBtnLabel.text().trim();
if (selectedCloneOption.length > 0) { if (selectedCloneOption.length > 0) {
$(`a:contains('${selectedCloneOption}')`, $cloneOptions).addClass('is-active'); $(`a:contains('${selectedCloneOption}')`, $cloneOptions).addClass('is-active');
} }
$('a', $cloneOptions).on('click', (e) => { $('a', $cloneOptions).on('click', e => {
e.preventDefault();
const $this = $(e.currentTarget); const $this = $(e.currentTarget);
const url = $this.attr('href'); const url = $this.attr('href');
const activeText = $this.find('.dropdown-menu-inner-title').text(); const cloneType = $this.data('cloneType');
e.preventDefault(); $('.is-active', $cloneOptions).removeClass('is-active');
$(`a[data-clone-type="${cloneType}"]`).each(function() {
const $el = $(this);
const activeText = $el.find('.dropdown-menu-inner-title').text();
const $container = $el.closest('.project-clone-holder');
const $label = $container.find('.js-clone-dropdown-label');
$('.is-active', $cloneOptions).not($this).removeClass('is-active'); $el.toggleClass('is-active');
$this.toggleClass('is-active'); $label.text(activeText);
$projectCloneField.val(url); });
$cloneBtnText.text(activeText);
return $('.clone').text(url); $projectCloneField.val(url);
$('.js-git-empty .js-clone').text(url);
}); });
// Ref switcher // Ref switcher
Project.initRefSwitcher(); Project.initRefSwitcher();
$('.project-refs-select').on('change', function() { $('.project-refs-select').on('change', function() {
return $(this).parents('form').submit(); return $(this)
.parents('form')
.submit();
}); });
$('.hide-no-ssh-message').on('click', function(e) { $('.hide-no-ssh-message').on('click', function(e) {
Cookies.set('hide_no_ssh_message', 'false'); Cookies.set('hide_no_ssh_message', 'false');
$(this).parents('.no-ssh-key-message').remove(); $(this)
.parents('.no-ssh-key-message')
.remove();
return e.preventDefault(); return e.preventDefault();
}); });
$('.hide-no-password-message').on('click', function(e) { $('.hide-no-password-message').on('click', function(e) {
Cookies.set('hide_no_password_message', 'false'); Cookies.set('hide_no_password_message', 'false');
$(this).parents('.no-password-message').remove(); $(this)
.parents('.no-password-message')
.remove();
return e.preventDefault(); return e.preventDefault();
}); });
Project.projectSelectDropdown(); Project.projectSelectDropdown();
...@@ -58,7 +70,7 @@ export default class Project { ...@@ -58,7 +70,7 @@ export default class Project {
} }
static changeProject(url) { static changeProject(url) {
return window.location = url; return (window.location = url);
} }
static initRefSwitcher() { static initRefSwitcher() {
...@@ -73,14 +85,15 @@ export default class Project { ...@@ -73,14 +85,15 @@ export default class Project {
selected = $dropdown.data('selected'); selected = $dropdown.data('selected');
return $dropdown.glDropdown({ return $dropdown.glDropdown({
data(term, callback) { data(term, callback) {
axios.get($dropdown.data('refsUrl'), { axios
params: { .get($dropdown.data('refsUrl'), {
ref: $dropdown.data('ref'), params: {
search: term, ref: $dropdown.data('ref'),
}, search: term,
}) },
.then(({ data }) => callback(data)) })
.catch(() => flash(__('An error occurred while getting projects'))); .then(({ data }) => callback(data))
.catch(() => flash(__('An error occurred while getting projects')));
}, },
selectable: true, selectable: true,
filterable: true, filterable: true,
......
...@@ -8,15 +8,18 @@ import BlobViewer from '~/blob/viewer/index'; ...@@ -8,15 +8,18 @@ import BlobViewer from '~/blob/viewer/index';
import Activities from '~/activities'; import Activities from '~/activities';
import { ajaxGet } from '~/lib/utils/common_utils'; import { ajaxGet } from '~/lib/utils/common_utils';
import GpgBadges from '~/gpg_badges'; import GpgBadges from '~/gpg_badges';
import initReadMore from '~/read_more';
import Star from '../../../star'; import Star from '../../../star';
import notificationsDropdown from '../../../notifications_dropdown'; import notificationsDropdown from '../../../notifications_dropdown';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
initReadMore();
new Star(); // eslint-disable-line no-new new Star(); // eslint-disable-line no-new
notificationsDropdown(); notificationsDropdown();
new ShortcutsNavigation(); // eslint-disable-line no-new new ShortcutsNavigation(); // eslint-disable-line no-new
new NotificationsForm(); // eslint-disable-line no-new new NotificationsForm(); // eslint-disable-line no-new
new UserCallout({ // eslint-disable-line no-new // eslint-disable-next-line no-new
new UserCallout({
setCalloutPerProject: false, setCalloutPerProject: false,
className: 'js-autodevops-banner', className: 'js-autodevops-banner',
}); });
......
/**
* ReadMore
*
* Adds "read more" functionality to elements.
*
* Specifically, it looks for a trigger, by default ".js-read-more-trigger", and adds the class
* "is-expanded" to the previous element in order to provide a click to expand functionality.
*
* This is useful for long text elements that you would like to truncate, especially for mobile.
*
* Example Markup
* <div class="read-more-container">
* <p>Some text that should be long enough to have to truncate within a specified container.</p>
* <p>This text will not appear in the container, as only the first line can be truncated.</p>
* <p>This should also not appear, if everything is working correctly!</p>
* </div>
* <button class="js-read-more-trigger">Read more</button>
*
*/
export default function initReadMore(triggerSelector = '.js-read-more-trigger') {
const triggerEls = document.querySelectorAll(triggerSelector);
if (!triggerEls) return;
triggerEls.forEach(triggerEl => {
const targetEl = triggerEl.previousElementSibling;
if (!targetEl) {
return;
}
triggerEl.addEventListener(
'click',
e => {
targetEl.classList.add('is-expanded');
e.target.remove();
},
{ once: true },
);
});
}
...@@ -64,3 +64,4 @@ ...@@ -64,3 +64,4 @@
@import 'framework/ci_variable_list'; @import 'framework/ci_variable_list';
@import 'framework/feature_highlight'; @import 'framework/feature_highlight';
@import 'framework/terms'; @import 'framework/terms';
@import 'framework/read_more';
...@@ -44,12 +44,8 @@ ...@@ -44,12 +44,8 @@
.project-repo-buttons { .project-repo-buttons {
display: block; display: block;
.count-buttons .btn { .count-buttons .count-badge {
margin: 0 10px; margin-top: $gl-padding-8;
}
.count-buttons .count-with-arrow {
display: none;
} }
} }
} }
......
.read-more-container {
@include media-breakpoint-down(md) {
&:not(.is-expanded) {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
> * {
display: inline;
}
}
}
}
...@@ -271,6 +271,7 @@ $performance-bar-height: 35px; ...@@ -271,6 +271,7 @@ $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;
/* /*
* Common component specific colors * Common component specific colors
......
...@@ -115,7 +115,7 @@ ...@@ -115,7 +115,7 @@
.project-feature-controls { .project-feature-controls {
display: flex; display: flex;
align-items: center; align-items: center;
margin: 8px 0; margin: $gl-padding-8 0;
max-width: 432px; max-width: 432px;
.toggle-wrapper { .toggle-wrapper {
...@@ -144,12 +144,8 @@ ...@@ -144,12 +144,8 @@
.group-home-panel { .group-home-panel {
padding-top: 24px; padding-top: 24px;
padding-bottom: 24px; padding-bottom: 24px;
border-bottom: 1px solid $border-color;
@include media-breakpoint-up(sm) {
border-bottom: 1px solid $border-color;
}
.project-avatar,
.group-avatar { .group-avatar {
float: none; float: none;
margin: 0 auto; margin: 0 auto;
...@@ -175,7 +171,6 @@ ...@@ -175,7 +171,6 @@
} }
} }
.project-home-desc,
.group-home-desc { .group-home-desc {
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
...@@ -199,6 +194,62 @@ ...@@ -199,6 +194,62 @@
} }
} }
.project-home-panel {
padding-top: $gl-padding-8;
padding-bottom: $gl-padding-24;
.project-title-row {
margin-right: $gl-padding-8;
}
.project-avatar {
width: $project-title-row-height;
height: $project-title-row-height;
flex-shrink: 0;
flex-basis: $project-title-row-height;
margin: 0 $gl-padding-8 0 0;
}
.project-title {
font-size: 20px;
line-height: $project-title-row-height;
font-weight: bold;
}
.project-metadata {
font-weight: normal;
font-size: 14px;
line-height: $gl-btn-line-height;
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 {
.btn {
line-height: 0;
border-width: 0;
}
}
.project-tag-list,
.project-license {
.icon {
position: relative;
top: 2px;
}
}
}
}
.nav > .project-repo-buttons { .nav > .project-repo-buttons {
margin-top: 0; margin-top: 0;
} }
...@@ -206,8 +257,6 @@ ...@@ -206,8 +257,6 @@
.project-repo-buttons, .project-repo-buttons,
.group-buttons { .group-buttons {
.btn { .btn {
padding: 3px 10px;
&:last-child { &:last-child {
margin-left: 0; margin-left: 0;
} }
...@@ -222,11 +271,15 @@ ...@@ -222,11 +271,15 @@
.fa-caret-down { .fa-caret-down {
margin-left: 3px; margin-left: 3px;
&.dropdown-btn-icon {
margin-left: 0;
}
} }
} }
.project-action-button { .project-action-button {
margin: 15px 5px 0; margin: $gl-padding $gl-padding-8 0 0;
vertical-align: top; vertical-align: top;
} }
...@@ -243,82 +296,45 @@ ...@@ -243,82 +296,45 @@
.count-buttons { .count-buttons {
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
margin-top: 15px; margin-top: $gl-padding;
}
.project-clone-holder { .count-badge {
display: inline-block; height: $input-height;
margin: 15px 5px 0 0;
input { .icon {
height: 28px; top: -1px;
}
} }
}
.count-with-arrow { .count-badge-count,
display: inline-block; .count-badge-button {
position: relative; border: 1px solid $border-color;
margin-left: 4px; line-height: 1;
}
.arrow { .count,
&::before { .count-badge-button {
content: ''; color: $gl-text-color;
display: inline-block; }
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
top: 50%;
left: 0;
margin-top: -6px;
border-width: 7px 5px 7px 0;
border-right-color: $count-arrow-border;
pointer-events: none;
}
&::after { .count-badge-count {
content: ''; padding: 0 12px;
position: absolute; border-right: 0;
width: 0; border-radius: $border-radius-base 0 0 $border-radius-base;
height: 0; background: $gray-light;
border-color: transparent;
border-style: solid;
top: 50%;
left: 1px;
margin-top: -9px;
border-width: 10px 7px 10px 0;
border-right-color: $white-light;
pointer-events: none;
}
} }
.count { .count-badge-button {
@include btn-white; border-radius: 0 $border-radius-base $border-radius-base 0;
display: inline-block; }
background: $white-light; }
border-radius: 2px;
border-width: 1px;
border-style: solid;
font-size: 13px;
font-weight: $gl-font-weight-bold;
line-height: 13px;
letter-spacing: 0.4px;
padding: 6px 14px;
text-align: center;
vertical-align: middle;
touch-action: manipulation;
background-image: none;
white-space: nowrap;
margin: 0 10px 0 4px;
a { .project-clone-holder {
color: inherit; display: inline-block;
} margin: $gl-padding $gl-padding-8 0 0;
&:hover { input {
background: $white-light; height: $input-height;
}
} }
} }
...@@ -333,6 +349,14 @@ ...@@ -333,6 +349,14 @@
min-width: 320px; min-width: 320px;
} }
} }
.mobile-git-clone {
margin-top: $gl-padding-8;
.dropdown-menu-inner-content {
@extend .monospace;
}
}
} }
.split-one { .split-one {
...@@ -511,7 +535,6 @@ ...@@ -511,7 +535,6 @@
.controls { .controls {
margin-left: auto; margin-left: auto;
} }
} }
.choose-template { .choose-template {
...@@ -574,7 +597,7 @@ ...@@ -574,7 +597,7 @@
flex-wrap: wrap; flex-wrap: wrap;
.btn { .btn {
padding: 8px; padding: $gl-padding-8;
margin-right: 10px; margin-right: 10px;
} }
...@@ -651,7 +674,7 @@ ...@@ -651,7 +674,7 @@
left: -10px; left: -10px;
top: 50%; top: 50%;
z-index: 10; z-index: 10;
padding: 8px 0; padding: $gl-padding-8 0;
text-align: center; text-align: center;
background-color: $white-light; background-color: $white-light;
color: $gl-text-color-tertiary; color: $gl-text-color-tertiary;
...@@ -665,7 +688,7 @@ ...@@ -665,7 +688,7 @@
left: 50%; left: 50%;
top: 0; top: 0;
transform: translateX(-50%); transform: translateX(-50%);
padding: 0 8px; padding: 0 $gl-padding-8;
} }
} }
...@@ -699,17 +722,51 @@ ...@@ -699,17 +722,51 @@
.project-stats { .project-stats {
font-size: 0; font-size: 0;
text-align: center; text-align: center;
max-width: 100%;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
.nav { .scrolling-tabs-container {
margin-top: $gl-padding-8; .scrolling-tabs {
margin-bottom: $gl-padding-8; margin-top: $gl-padding-8;
margin-bottom: $gl-padding-8;
flex-wrap: wrap;
border-bottom: 0;
}
.fade-left,
.fade-right {
top: 0;
height: 100%;
.fa {
top: 50%;
margin-top: -$gl-padding-8;
}
}
.nav {
flex-basis: 100%;
+ .nav {
margin: $gl-padding-8 0;
}
}
@include media-breakpoint-down(md) {
flex-direction: column;
.nav {
flex-wrap: nowrap;
}
.nav:first-child {
margin-right: $gl-padding-8;
}
}
}
.nav {
> li { > li {
display: inline-block; display: inline-block;
margin-top: $gl-padding-4;
margin-bottom: $gl-padding-4;
&:not(:last-child) { &:not(:last-child) {
margin-right: $gl-padding; margin-right: $gl-padding;
...@@ -732,13 +789,17 @@ ...@@ -732,13 +789,17 @@
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;
} }
.stat-link { .stat-link {
border-bottom: 0;
&:hover, &:hover,
&:focus { &:focus {
color: $gl-text-color; color: $gl-text-color;
text-decoration: underline; text-decoration: underline;
border-bottom: 0;
} }
} }
...@@ -868,7 +929,7 @@ pre.light-well { ...@@ -868,7 +929,7 @@ pre.light-well {
} }
.git-clone-holder { .git-clone-holder {
width: 380px; width: 320px;
.btn-clipboard { .btn-clipboard {
border: 1px solid $border-color; border: 1px solid $border-color;
......
...@@ -61,7 +61,7 @@ module ButtonHelper ...@@ -61,7 +61,7 @@ module ButtonHelper
dropdown_description = http_dropdown_description(protocol) dropdown_description = http_dropdown_description(protocol)
append_url = project.http_url_to_repo if append_link append_url = project.http_url_to_repo if append_link
dropdown_item_with_description(protocol, dropdown_description, href: append_url) dropdown_item_with_description(protocol, dropdown_description, href: append_url, data: { clone_type: 'http' })
end end
def http_dropdown_description(protocol) def http_dropdown_description(protocol)
...@@ -80,16 +80,17 @@ module ButtonHelper ...@@ -80,16 +80,17 @@ module ButtonHelper
append_url = project.ssh_url_to_repo if append_link append_url = project.ssh_url_to_repo if append_link
dropdown_item_with_description('SSH', dropdown_description, href: append_url) 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) def dropdown_item_with_description(title, description, href: nil, data: nil)
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",
href: (href if href) href: (href if href),
data: (data if data)
end end
end end
...@@ -86,7 +86,7 @@ module IconsHelper ...@@ -86,7 +86,7 @@ module IconsHelper
end end
end end
def visibility_level_icon(level, fw: true) def visibility_level_icon(level, fw: true, options: {})
name = name =
case level case level
when Gitlab::VisibilityLevel::PRIVATE when Gitlab::VisibilityLevel::PRIVATE
...@@ -99,7 +99,7 @@ module IconsHelper ...@@ -99,7 +99,7 @@ module IconsHelper
name << " fw" if fw name << " fw" if fw
icon(name) icon(name, options)
end end
def file_type_icon_class(type, mode, name) def file_type_icon_class(type, mode, name)
......
...@@ -351,6 +351,10 @@ module ProjectsHelper ...@@ -351,6 +351,10 @@ module ProjectsHelper
end end
end end
def default_clone_label
_("Copy %{protocol} clone URL") % { protocol: default_clone_protocol.upcase }
end
def default_clone_protocol def default_clone_protocol
if allowed_protocols_present? if allowed_protocols_present?
enabled_protocol enabled_protocol
......
...@@ -138,7 +138,7 @@ module VisibilityLevelHelper ...@@ -138,7 +138,7 @@ module VisibilityLevelHelper
end end
def project_visibility_icon_description(level) def project_visibility_icon_description(level)
"#{visibility_level_label(level)} - #{project_visibility_level_description(level)}" "#{project_visibility_level_description(level)}"
end end
def visibility_level_label(level) def visibility_level_label(level)
......
...@@ -11,16 +11,18 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated ...@@ -11,16 +11,18 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
presents :project presents :project
AnchorData = Struct.new(:enabled, :label, :link, :class_modifier)
MAX_TAGS_TO_SHOW = 3
def statistics_anchors(show_auto_devops_callout:) def statistics_anchors(show_auto_devops_callout:)
[ [
readme_anchor_data,
changelog_anchor_data,
contribution_guide_anchor_data,
files_anchor_data, files_anchor_data,
commits_anchor_data, commits_anchor_data,
branches_anchor_data, branches_anchor_data,
tags_anchor_data, tags_anchor_data,
readme_anchor_data,
changelog_anchor_data,
license_anchor_data,
contribution_guide_anchor_data,
gitlab_ci_anchor_data, gitlab_ci_anchor_data,
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
...@@ -31,7 +33,6 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated ...@@ -31,7 +33,6 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
[ [
readme_anchor_data, readme_anchor_data,
changelog_anchor_data, changelog_anchor_data,
license_anchor_data,
contribution_guide_anchor_data, contribution_guide_anchor_data,
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,
...@@ -42,6 +43,10 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated ...@@ -42,6 +43,10 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
def empty_repo_statistics_anchors def empty_repo_statistics_anchors
[ [
files_anchor_data,
commits_anchor_data,
branches_anchor_data,
tags_anchor_data,
autodevops_anchor_data, autodevops_anchor_data,
kubernetes_cluster_anchor_data kubernetes_cluster_anchor_data
].compact.select { |item| item.enabled } ].compact.select { |item| item.enabled }
...@@ -51,7 +56,6 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated ...@@ -51,7 +56,6 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
[ [
new_file_anchor_data, new_file_anchor_data,
readme_anchor_data, readme_anchor_data,
license_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.enabled }
...@@ -182,95 +186,101 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated ...@@ -182,95 +186,101 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end end
def files_anchor_data def files_anchor_data
OpenStruct.new(enabled: true, AnchorData.new(true,
label: _('Files (%{human_size})') % { human_size: storage_counter(statistics.total_repository_size) }, _('Files (%{human_size})') % { human_size: storage_counter(statistics.total_repository_size) },
link: project_tree_path(project)) empty_repo? ? nil : project_tree_path(project))
end end
def commits_anchor_data def commits_anchor_data
OpenStruct.new(enabled: true, AnchorData.new(true,
label: n_('Commit (%{commit_count})', 'Commits (%{commit_count})', statistics.commit_count) % { commit_count: number_with_delimiter(statistics.commit_count) }, n_('Commit (%{commit_count})', 'Commits (%{commit_count})', statistics.commit_count) % { commit_count: number_with_delimiter(statistics.commit_count) },
link: 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
OpenStruct.new(enabled: true, AnchorData.new(true,
label: n_('Branch (%{branch_count})', 'Branches (%{branch_count})', repository.branch_count) % { branch_count: number_with_delimiter(repository.branch_count) }, n_('Branch (%{branch_count})', 'Branches (%{branch_count})', repository.branch_count) % { branch_count: number_with_delimiter(repository.branch_count) },
link: project_branches_path(project)) empty_repo? ? nil : project_branches_path(project))
end end
def tags_anchor_data def tags_anchor_data
OpenStruct.new(enabled: true, AnchorData.new(true,
label: n_('Tag (%{tag_count})', 'Tags (%{tag_count})', repository.tag_count) % { tag_count: number_with_delimiter(repository.tag_count) }, n_('Tag (%{tag_count})', 'Tags (%{tag_count})', repository.tag_count) % { tag_count: number_with_delimiter(repository.tag_count) },
link: 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?
OpenStruct.new(enabled: false, AnchorData.new(false,
label: _('New file'), _('New file'),
link: project_new_blob_path(project, default_branch || 'master'), project_new_blob_path(project, default_branch || 'master'),
class_modifier: 'new') 'new')
end end
end end
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?
OpenStruct.new(enabled: false, AnchorData.new(false,
label: _('Add Readme'), _('Add Readme'),
link: add_readme_path) add_readme_path)
elsif repository.readme elsif repository.readme
OpenStruct.new(enabled: true, AnchorData.new(true,
label: _('Readme'), _('Readme'),
link: default_view != 'readme' ? readme_path : '#readme') default_view != 'readme' ? readme_path : '#readme')
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?
OpenStruct.new(enabled: false, AnchorData.new(false,
label: _('Add Changelog'), _('Add Changelog'),
link: add_changelog_path) add_changelog_path)
elsif repository.changelog.present? elsif repository.changelog.present?
OpenStruct.new(enabled: true, AnchorData.new(true,
label: _('Changelog'), _('Changelog'),
link: changelog_path) changelog_path)
end end
end end
def license_anchor_data def license_anchor_data
if current_user && can_current_user_push_to_default_branch? && repository.license_blob.blank? if repository.license_blob.present?
OpenStruct.new(enabled: false, AnchorData.new(true,
label: _('Add License'), license_short_name,
link: add_license_path) license_path)
elsif repository.license_blob.present? else
OpenStruct.new(enabled: true, if current_user && can_current_user_push_to_default_branch?
label: license_short_name, AnchorData.new(false,
link: license_path) _('Add license'),
add_license_path)
else
AnchorData.new(false,
_('No license. All rights reserved'),
nil)
end
end end
end end
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?
OpenStruct.new(enabled: false, AnchorData.new(false,
label: _('Add Contribution guide'), _('Add Contribution guide'),
link: add_contribution_guide_path) add_contribution_guide_path)
elsif repository.contribution_guide.present? elsif repository.contribution_guide.present?
OpenStruct.new(enabled: true, AnchorData.new(true,
label: _('Contribution guide'), _('Contribution guide'),
link: 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
OpenStruct.new(enabled: auto_devops_enabled?, AnchorData.new(auto_devops_enabled?,
label: auto_devops_enabled? ? _('Auto DevOps enabled') : _('Enable Auto DevOps'), auto_devops_enabled? ? _('Auto DevOps enabled') : _('Enable Auto DevOps'),
link: project_settings_ci_cd_path(project, anchor: 'autodevops-settings')) project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
elsif auto_devops_enabled? elsif auto_devops_enabled?
OpenStruct.new(enabled: true, AnchorData.new(true,
label: _('Auto DevOps enabled'), _('Auto DevOps enabled'),
link: nil) nil)
end end
end end
...@@ -282,32 +292,48 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated ...@@ -282,32 +292,48 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
cluster_link = new_project_cluster_path(project) cluster_link = new_project_cluster_path(project)
end end
OpenStruct.new(enabled: !clusters.empty?, AnchorData.new(!clusters.empty?,
label: clusters.empty? ? _('Add Kubernetes cluster') : _('Kubernetes configured'), clusters.empty? ? _('Add Kubernetes cluster') : _('Kubernetes configured'),
link: cluster_link) cluster_link)
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?
OpenStruct.new(enabled: false, AnchorData.new(false,
label: _('Set up CI/CD'), _('Set up CI/CD'),
link: add_ci_yml_path) add_ci_yml_path)
elsif repository.gitlab_ci_yml.present? elsif repository.gitlab_ci_yml.present?
OpenStruct.new(enabled: true, AnchorData.new(true,
label: _('CI/CD configuration'), _('CI/CD configuration'),
link: ci_configuration_path) ci_configuration_path)
end end
end end
def koding_anchor_data def koding_anchor_data
if current_user && can_current_user_push_code? && koding_enabled? && repository.koding_yml.blank? if current_user && can_current_user_push_code? && koding_enabled? && repository.koding_yml.blank?
OpenStruct.new(enabled: false, AnchorData.new(false,
label: _('Set up Koding'), _('Set up Koding'),
link: add_koding_stack_path) add_koding_stack_path)
end end
end end
def tags_to_show
project.tag_list.take(MAX_TAGS_TO_SHOW)
end
def count_of_extra_tags_not_shown
if project.tag_list.count > MAX_TAGS_TO_SHOW
project.tag_list.count - MAX_TAGS_TO_SHOW
else
0
end
end
def has_extra_tags?
count_of_extra_tags_not_shown > 0
end
private private
def filename_path(filename) def filename_path(filename)
......
- empty_repo = @project.empty_repo? - empty_repo = @project.empty_repo?
.project-home-panel.text-center{ class: ("empty-project" if empty_repo) } - license = @project.license_anchor_data
.project-home-panel{ class: ("empty-project" if empty_repo) }
.limit-container-width{ class: container_class } .limit-container-width{ class: container_class }
.avatar-container.s70.project-avatar .project-header.d-flex.flex-row.flex-wrap.align-items-center.append-bottom-8
= project_icon(@project, alt: @project.name, class: 'avatar s70 avatar-tile', width: 70, height: 70) .project-title-row.d-flex.align-items-center
%h1.project-title.qa-project-name .avatar-container.project-avatar.float-none
= @project.name = project_icon(@project, alt: @project.name, class: 'avatar avatar-tile')
%span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@project) } %h1.project-title.d-flex.align-items-baseline.qa-project-name
= visibility_level_icon(@project.visibility_level, fw: false) = @project.name
.project-metadata.d-flex.flex-row.flex-wrap.align-items-baseline
.project-visibility.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_label(@project.visibility_level)
- if license.present?
.project-license.d-inline-flex.align-items-baseline
= 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'
- if @project.tag_list.present?
.project-tag-list.d-inline-flex.align-items-baseline.has-tooltip{ data: { container: 'body' }, title: @project.has_extra_tags? ? @project.tag_list.join(', ') : nil }
= sprite_icon('tag', size: 16, css_class: 'icon')
= @project.tags_to_show
- if @project.has_extra_tags?
= _("+ %{count} more") % { count: @project.count_of_extra_tags_not_shown }
.project-home-desc .project-home-desc
- if @project.description.present? - if @project.description.present?
= markdown_field(@project, :description) .project-description
.project-description-markdown.read-more-container
= 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) - if can?(current_user, :read_project, @project)
.text-secondary.prepend-top-8 .text-secondary.prepend-top-8
= s_('ProjectPage|Project ID: %{project_id}') % { project_id: @project.id } = s_('ProjectPage|Project ID: %{project_id}') % { project_id: @project.id }
...@@ -25,34 +44,42 @@ ...@@ -25,34 +44,42 @@
- deleted_message = s_('ForkedFromProjectPath|Forked from %{project_name} (deleted)') - deleted_message = s_('ForkedFromProjectPath|Forked from %{project_name} (deleted)')
= deleted_message % { project_name: fork_source_name(@project) } = deleted_message % { project_name: fork_source_name(@project) }
.project-badges.prepend-top-default.append-bottom-default - if @project.badges.present?
- @project.badges.each do |badge| .project-badges.prepend-top-default.append-bottom-default
%a.append-right-8{ href: badge.rendered_link_url(@project), - @project.badges.each do |badge|
target: '_blank', %a.append-right-8{ href: badge.rendered_link_url(@project),
rel: 'noopener noreferrer' }> target: '_blank',
%img.project-badge{ src: badge.rendered_image_url(@project), rel: 'noopener noreferrer' }>
'aria-hidden': true, %img.project-badge{ src: badge.rendered_image_url(@project),
alt: '' }> 'aria-hidden': true,
alt: 'Project badge' }>
.project-repo-buttons
.count-buttons .project-repo-buttons.d-inline-flex.flex-wrap
.count-buttons.d-inline-flex
= render 'projects/buttons/star' = render 'projects/buttons/star'
= render 'projects/buttons/fork' = render 'projects/buttons/fork'
%span.d-none.d-sm-inline - 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 = render "shared/mobile_clone_panel"
= render "shared/clone_panel"
- if show_xcode_link?(@project) .project-clone-holder.d-none.d-sm-inline-flex
.project-action-button.project-xcode.inline = render "shared/clone_panel"
= render "projects/buttons/xcode_link"
- if current_user - if show_xcode_link?(@project)
- if can?(current_user, :download_code, @project) .project-action-button.project-xcode.inline
= render "projects/buttons/xcode_link"
- if current_user
- if can?(current_user, :download_code, @project)
.d-none.d-sm-inline-flex
= render 'projects/buttons/download', project: @project, ref: @ref = render 'projects/buttons/download', project: @project, ref: @ref
.d-none.d-sm-inline-flex
= render 'projects/buttons/dropdown' = render 'projects/buttons/dropdown'
.d-none.d-sm-inline-flex
= render 'projects/buttons/koding' = render 'projects/buttons/koding'
.d-none.d-sm-inline-flex
= render 'shared/notifications/button', notification_setting: @notification_setting = render 'shared/notifications/button', notification_setting: @notification_setting
.d-none.d-sm-inline-flex
= render 'shared/members/access_request_buttons', source: @project = render 'shared/members/access_request_buttons', source: @project
- anchors = local_assigns.fetch(:anchors, []) - anchors = local_assigns.fetch(:anchors, [])
- return unless anchors.any? - return unless anchors.any?
%ul.nav.justify-content-center %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.enabled ? 'nav-link stat-link' : "nav-link btn btn-#{anchor.class_modifier || 'missing'}" do
......
- 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)
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 .count-badge.d-inline-flex.align-item-stretch.append-right-8
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: _('Go to your fork'), class: 'btn has-tooltip' do %span.fork-count.count-badge-count.d-flex.align-items-center
= custom_icon('icon_fork') = link_to project_forks_path(@project), title: n_(s_('ProjectOverview|Fork'), s_('ProjectOverview|Forks'), @project.forks_count), class: 'count' do
%span= s_('GoToYourFork|Fork') = @project.forks_count
- else - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
- can_create_fork = current_user.can?(:create_fork) = 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 new_project_fork_path(@project), = sprite_icon('fork', { css_class: 'icon' })
class: "btn btn-default #{'has-tooltip disabled' unless can_create_fork}", %span= s_('ProjectOverview|Fork')
title: (_('You have reached your project limit') unless can_create_fork) do - else
= custom_icon('icon_fork') - can_create_fork = current_user.can?(:create_fork)
%span= s_('CreateNewFork|Fork') = link_to new_project_fork_path(@project),
.count-with-arrow class: "btn btn-default has-tooltip count-badge-button d-flex align-items-center fork-btn #{'has-tooltip disabled' unless can_create_fork}",
%span.arrow title: (s_('ProjectOverview|You have reached your project limit') unless can_create_fork) do
= link_to project_forks_path(@project), title: n_('Fork', 'Forks', @project.forks_count), class: 'count' do = sprite_icon('fork', { css_class: 'icon' })
= @project.forks_count %span= s_('ProjectOverview|Fork')
- if current_user - if current_user
%button.btn.btn-default.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }> .count-badge.d-inline-flex.align-item-stretch.append-right-8
- if current_user.starred?(@project) %span.star-count.count-badge-count.d-flex.align-items-center
= sprite_icon('star')
%span.starred= _('Unstar')
- else
= sprite_icon('star-o')
%span= s_('StarProject|Star')
.count-with-arrow
%span.arrow
%span.count.star-count
= @project.star_count = @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)
= sprite_icon('star', { css_class: 'icon' })
%span.starred= s_('ProjectOverview|Unstar')
- else
= sprite_icon('star-o', { css_class: 'icon' })
%span= s_('ProjectOverview|Star')
- else - else
= link_to new_user_session_path, class: 'btn has-tooltip star-btn', title: _('You must sign in to star a project') do .count-badge.d-inline-flex.align-item-stretch.append-right-8
= sprite_icon('star') %span.star-count.count-badge-count.d-flex.align-items-center
#{ s_('StarProject|Star') }
.count-with-arrow
%span.arrow
%span.count
= @project.star_count = @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' })
%span= s_('ProjectOverview|Star')
...@@ -32,9 +32,13 @@ ...@@ -32,9 +32,13 @@
= _('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 } %nav.project-stats{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
= render 'stat_anchor_list', anchors: @project.empty_repo_statistics_anchors .scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
= render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons .fade-left= icon('angle-left')
.fade-right= icon('angle-right')
.nav-links.scrolling-tabs
= render 'stat_anchor_list', anchors: @project.empty_repo_statistics_anchors
= 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{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
...@@ -42,7 +46,7 @@ ...@@ -42,7 +46,7 @@
.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 .git-empty.js-git-empty
%fieldset %fieldset
%h5 Git global setup %h5 Git global setup
%pre.bg-light %pre.bg-light
...@@ -54,7 +58,7 @@ ...@@ -54,7 +58,7 @@
%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: '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
...@@ -69,7 +73,7 @@ ...@@ -69,7 +73,7 @@
:preserve :preserve
cd existing_folder cd existing_folder
git init git init
git remote add origin #{ content_tag(:span, default_url_to_repo, class: '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?
...@@ -82,7 +86,7 @@ ...@@ -82,7 +86,7 @@
: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: '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
......
...@@ -19,8 +19,13 @@ ...@@ -19,8 +19,13 @@
- if can?(current_user, :download_code, @project) - if can?(current_user, :download_code, @project)
%nav.project-stats{ class: [container_class, ("limit-container-width" unless fluid_layout)] } %nav.project-stats{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
= render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout) .scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
= render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout) .fade-left= icon('angle-left')
.fade-right= icon('angle-right')
.nav-links.scrolling-tabs
= 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)
= repository_languages_bar(@project.repository_languages) = repository_languages_bar(@project.repository_languages)
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] } %div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
......
- project = project || @project - project = project || @project
.git-clone-holder.input-group .git-clone-holder.js-git-clone-holder.input-group
.input-group-prepend .input-group-prepend
- if allowed_protocols_present? - if allowed_protocols_present?
.input-group-text.clone-dropdown-btn.btn .input-group-text.clone-dropdown-btn.btn
%span %span.js-clone-dropdown-label
= enabled_project_button(project, enabled_protocol) = enabled_project_button(project, enabled_protocol)
- else - else
%a#clone-dropdown.input-group-text.btn.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } } %a#clone-dropdown.input-group-text.btn.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } }
%span %span.js-clone-dropdown-label
= default_clone_protocol.upcase = default_clone_protocol.upcase
= icon('caret-down') = icon('caret-down')
%ul.dropdown-menu.dropdown-menu-selectable.clone-options-dropdown %ul.dropdown-menu.dropdown-menu-selectable.clone-options-dropdown
......
- project = project || @project
- ssh_copy_label = _("Copy SSH clone URL")
- http_copy_label = _("Copy HTTPS clone URL")
.btn-group.mobile-git-clone.js-mobile-git-clone
= 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")
%button.btn.btn-default.dropdown-toggle.js-dropdown-toggle{ type: "button", data: { toggle: "dropdown" } }
= icon("caret-down", class: "dropdown-btn-icon")
%ul.dropdown-menu.dropdown-menu-selectable.dropdown-menu-right.clone-options-dropdown{ data: { dropdown: true } }
%li
= dropdown_item_with_description(ssh_copy_label, project.ssh_url_to_repo, href: project.ssh_url_to_repo, data: { clone_type: 'ssh' })
%li
= dropdown_item_with_description(http_copy_label, project.http_url_to_repo, href: project.http_url_to_repo, data: { clone_type: 'http' })
---
title: Update design of project overview page
merge_request: 20536
author:
type: changed
...@@ -150,6 +150,9 @@ msgstr "" ...@@ -150,6 +150,9 @@ msgstr ""
msgid "%{unstaged} unstaged and %{staged} staged changes" msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr "" msgstr ""
msgid "+ %{count} more"
msgstr ""
msgid "+ %{moreCount} more" msgid "+ %{moreCount} more"
msgstr "" msgstr ""
...@@ -319,10 +322,10 @@ msgstr "" ...@@ -319,10 +322,10 @@ msgstr ""
msgid "Add Kubernetes cluster" msgid "Add Kubernetes cluster"
msgstr "" msgstr ""
msgid "Add License" msgid "Add Readme"
msgstr "" msgstr ""
msgid "Add Readme" msgid "Add license"
msgstr "" msgstr ""
msgid "Add new application" msgid "Add new application"
...@@ -1897,6 +1900,15 @@ msgstr "" ...@@ -1897,6 +1900,15 @@ msgstr ""
msgid "ConvDev Index" msgid "ConvDev Index"
msgstr "" msgstr ""
msgid "Copy %{protocol} clone URL"
msgstr ""
msgid "Copy HTTPS clone URL"
msgstr ""
msgid "Copy SSH clone URL"
msgstr ""
msgid "Copy URL to clipboard" msgid "Copy URL to clipboard"
msgstr "" msgstr ""
...@@ -1990,9 +2002,6 @@ msgstr "" ...@@ -1990,9 +2002,6 @@ msgstr ""
msgid "Create project label" msgid "Create project label"
msgstr "" msgstr ""
msgid "CreateNewFork|Fork"
msgstr ""
msgid "CreateTag|Tag" msgid "CreateTag|Tag"
msgstr "" msgstr ""
...@@ -2730,11 +2739,6 @@ msgstr "" ...@@ -2730,11 +2739,6 @@ msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)" msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr "" msgstr ""
msgid "Fork"
msgid_plural "Forks"
msgstr[0] ""
msgstr[1] ""
msgid "ForkedFromProjectPath|Forked from" msgid "ForkedFromProjectPath|Forked from"
msgstr "" msgstr ""
...@@ -2858,12 +2862,6 @@ msgstr "" ...@@ -2858,12 +2862,6 @@ msgstr ""
msgid "Go to %{link_to_google_takeout}." msgid "Go to %{link_to_google_takeout}."
msgstr "" msgstr ""
msgid "Go to your fork"
msgstr ""
msgid "GoToYourFork|Fork"
msgstr ""
msgid "Google Code import" msgid "Google Code import"
msgstr "" msgstr ""
...@@ -3895,6 +3893,9 @@ msgstr "" ...@@ -3895,6 +3893,9 @@ msgstr ""
msgid "No labels with such name or description" msgid "No labels with such name or description"
msgstr "" msgstr ""
msgid "No license. All rights reserved"
msgstr ""
msgid "No merge requests found" msgid "No merge requests found"
msgstr "" msgstr ""
...@@ -4554,6 +4555,27 @@ msgstr "" ...@@ -4554,6 +4555,27 @@ msgstr ""
msgid "ProjectLifecycle|Stage" msgid "ProjectLifecycle|Stage"
msgstr "" msgstr ""
msgid "ProjectOverview|Fork"
msgstr ""
msgid "ProjectOverview|Forks"
msgstr ""
msgid "ProjectOverview|Go to your fork"
msgstr ""
msgid "ProjectOverview|Star"
msgstr ""
msgid "ProjectOverview|Unstar"
msgstr ""
msgid "ProjectOverview|You have reached your project limit"
msgstr ""
msgid "ProjectOverview|You must sign in to star a project"
msgstr ""
msgid "ProjectPage|Project ID: %{project_id}" msgid "ProjectPage|Project ID: %{project_id}"
msgstr "" msgstr ""
...@@ -6516,9 +6538,6 @@ msgstr "" ...@@ -6516,9 +6538,6 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock" msgid "You must have maintainer access to force delete a lock"
msgstr "" msgstr ""
msgid "You must sign in to star a project"
msgstr ""
msgid "You need permission." msgid "You need permission."
msgstr "" msgstr ""
......
...@@ -23,7 +23,7 @@ module QA ...@@ -23,7 +23,7 @@ module QA
end end
view 'app/views/projects/buttons/_fork.html.haml' do view 'app/views/projects/buttons/_fork.html.haml' do
element :fork_label, "%span= s_('GoToYourFork|Fork')" element :fork_label, "%span= s_('ProjectOverview|Fork')"
element :fork_link, "link_to new_project_fork_path(@project)" element :fork_link, "link_to new_project_fork_path(@project)"
end end
...@@ -32,7 +32,7 @@ module QA ...@@ -32,7 +32,7 @@ module QA
end end
view 'app/presenters/project_presenter.rb' do view 'app/presenters/project_presenter.rb' do
element :new_file_button, "label: _('New file')," element :new_file_button, "_('New file'),"
end end
def project_name def project_name
......
...@@ -36,7 +36,7 @@ describe 'Projects > Files > Project owner creates a license file', :js do ...@@ -36,7 +36,7 @@ describe 'Projects > Files > Project owner creates a license file', :js do
end end
it 'project maintainer creates a license file from the "Add license" link' do it 'project maintainer creates a license file from the "Add license" link' do
click_link 'Add License' click_link 'Add license'
expect(page).to have_content('New file') expect(page).to have_content('New file')
expect(current_path).to eq( expect(current_path).to eq(
......
...@@ -10,7 +10,7 @@ describe 'Projects > Files > Project owner sees a link to create a license file ...@@ -10,7 +10,7 @@ describe 'Projects > Files > Project owner sees a link to create a license file
it 'project maintainer creates a license file from a template' do it 'project maintainer creates a license file from a template' do
visit project_path(project) visit project_path(project)
click_on 'Add License' click_on 'Add license'
expect(page).to have_content('New file') expect(page).to have_content('New file')
expect(current_path).to eq( expect(current_path).to eq(
......
require 'spec_helper' require 'spec_helper'
describe 'Projects > Show > User sees setup shortcut buttons' do describe 'Projects > Show > User sees setup shortcut buttons' do
# For "New file", "Add License" functionality, # For "New file", "Add license" functionality,
# see spec/features/projects/files/project_owner_creates_license_file_spec.rb # see spec/features/projects/files/project_owner_creates_license_file_spec.rb
# see spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb # see spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
...@@ -58,9 +58,9 @@ describe 'Projects > Show > User sees setup shortcut buttons' do ...@@ -58,9 +58,9 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
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-stats') do page.within('.project-metadata') 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
...@@ -201,13 +201,13 @@ describe 'Projects > Show > User sees setup shortcut buttons' do ...@@ -201,13 +201,13 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
end end
end end
it 'no "Add License" button if the project already has a license' do it 'no "Add license" button if the project already has a license' do
visit project_path(project) visit project_path(project)
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-stats') do
expect(page).not_to have_link('Add License') expect(page).not_to have_link('Add license')
end end
end end
......
...@@ -2,6 +2,7 @@ require 'spec_helper' ...@@ -2,6 +2,7 @@ require 'spec_helper'
describe 'Project' do describe 'Project' do
include ProjectForksHelper include ProjectForksHelper
include MobileHelpers
describe 'creating from template' do describe 'creating from template' do
let(:user) { create(:user) } let(:user) { create(:user) }
...@@ -54,25 +55,72 @@ describe 'Project' do ...@@ -54,25 +55,72 @@ describe 'Project' do
it 'parses Markdown' do it 'parses Markdown' do
project.update_attribute(:description, 'This is **my** project') project.update_attribute(:description, 'This is **my** project')
visit path visit path
expect(page).to have_css('.project-home-desc > p > strong') expect(page).to have_css('.project-description > .project-description-markdown > p > strong')
end end
it 'passes through html-pipeline' do it 'passes through html-pipeline' do
project.update_attribute(:description, 'This project is the :poop:') project.update_attribute(:description, 'This project is the :poop:')
visit path visit path
expect(page).to have_css('.project-home-desc > p > gl-emoji') expect(page).to have_css('.project-description > .project-description-markdown > p > gl-emoji')
end end
it 'sanitizes unwanted tags' do it 'sanitizes unwanted tags' do
project.update_attribute(:description, "```\ncode\n```") project.update_attribute(:description, "```\ncode\n```")
visit path visit path
expect(page).not_to have_css('.project-home-desc code') expect(page).not_to have_css('.project-description code')
end end
it 'permits `rel` attribute on links' do it 'permits `rel` attribute on links' do
project.update_attribute(:description, 'https://google.com/') project.update_attribute(:description, 'https://google.com/')
visit path visit path
expect(page).to have_css('.project-home-desc a[rel]') expect(page).to have_css('.project-description a[rel]')
end
context 'read more', :js do
let(:read_more_selector) { '.read-more-container' }
let(:read_more_trigger_selector) { '.project-home-desc .js-read-more-trigger' }
it 'does not display "read more" link on desktop breakpoint' do
project.update_attribute(:description, 'This is **my** project')
visit path
expect(find(read_more_trigger_selector, visible: false)).not_to be_visible
end
it 'displays "read more" link on mobile breakpoint' do
project.update_attribute(:description, 'This is **my** project')
visit path
resize_screen_xs
find(read_more_trigger_selector).click
expect(page).to have_css('.project-description .is-expanded')
end
end
end
describe 'copy clone URL to clipboard', :js do
let(:project) { create(:project, :repository) }
let(:path) { project_path(project) }
before do
sign_in(create(:admin))
visit path
end
context 'desktop component' do
it 'shows on md and larger breakpoints' do
expect(find('.git-clone-holder')).to be_visible
expect(find('.mobile-git-clone', visible: false)).not_to be_visible
end
end
context 'mobile component' do
it 'shows mobile component on sm and smaller breakpoints' do
resize_screen_xs
expect(find('.mobile-git-clone')).to be_visible
expect(find('.git-clone-holder', visible: false)).not_to be_visible
end
end end
end end
......
...@@ -6,6 +6,7 @@ describe 'Projects (JavaScript fixtures)', type: :controller do ...@@ -6,6 +6,7 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project, namespace: namespace, path: 'builds-project') } let(:project) { create(:project, namespace: namespace, path: 'builds-project') }
let(:project_with_repo) { create(:project, :repository, description: 'Code and stuff') }
let(:project_variable_populated) { create(:project, namespace: namespace, path: 'builds-project2') } let(:project_variable_populated) { create(:project, namespace: namespace, path: 'builds-project2') }
let!(:variable1) { create(:ci_variable, project: project_variable_populated) } let!(:variable1) { create(:ci_variable, project: project_variable_populated) }
let!(:variable2) { create(:ci_variable, project: project_variable_populated) } let!(:variable2) { create(:ci_variable, project: project_variable_populated) }
...@@ -35,6 +36,15 @@ describe 'Projects (JavaScript fixtures)', type: :controller do ...@@ -35,6 +36,15 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
store_frontend_fixture(response, example.description) store_frontend_fixture(response, example.description)
end end
it 'projects/overview.html.raw' do |example|
get :show,
namespace_id: project_with_repo.namespace.to_param,
id: project_with_repo
expect(response).to be_success
store_frontend_fixture(response, example.description)
end
it 'projects/edit.html.raw' do |example| it 'projects/edit.html.raw' do |example|
get :edit, get :edit,
namespace_id: project.namespace.to_param, namespace_id: project.namespace.to_param,
......
import initReadMore from '~/read_more';
describe('Read more click-to-expand functionality', () => {
const fixtureName = 'projects/overview.html.raw';
preloadFixtures(fixtureName);
beforeEach(() => {
loadFixtures(fixtureName);
});
describe('expands target element', () => {
it('adds "is-expanded" class to target element', () => {
const target = document.querySelector('.read-more-container');
const trigger = document.querySelector('.js-read-more-trigger');
initReadMore();
trigger.click();
expect(target.classList.contains('is-expanded')).toEqual(true);
});
});
});
...@@ -159,39 +159,76 @@ describe ProjectPresenter do ...@@ -159,39 +159,76 @@ describe ProjectPresenter do
end end
end end
context 'statistics anchors (empty repo)' do
let(:project) { create(:project, :empty_repo) }
let(:presenter) { described_class.new(project, current_user: user) }
describe '#files_anchor_data' do
it 'returns files data' do
expect(presenter.files_anchor_data).to have_attributes(enabled: true,
label: 'Files (0 Bytes)',
link: nil)
end
end
describe '#commits_anchor_data' do
it 'returns commits data' do
expect(presenter.commits_anchor_data).to have_attributes(enabled: true,
label: 'Commits (0)',
link: nil)
end
end
describe '#branches_anchor_data' do
it 'returns branches data' do
expect(presenter.branches_anchor_data).to have_attributes(enabled: true,
label: "Branches (0)",
link: nil)
end
end
describe '#tags_anchor_data' do
it 'returns tags data' do
expect(presenter.tags_anchor_data).to have_attributes(enabled: true,
label: "Tags (0)",
link: nil)
end
end
end
context 'statistics anchors' do context 'statistics anchors' do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:presenter) { described_class.new(project, current_user: user) } let(:presenter) { described_class.new(project, current_user: user) }
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 eq(OpenStruct.new(enabled: true, expect(presenter.files_anchor_data).to have_attributes(enabled: true,
label: 'Files (0 Bytes)', label: 'Files (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 eq(OpenStruct.new(enabled: true, expect(presenter.commits_anchor_data).to have_attributes(enabled: true,
label: 'Commits (0)', label: 'Commits (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 eq(OpenStruct.new(enabled: true, expect(presenter.branches_anchor_data).to have_attributes(enabled: true,
label: "Branches (#{project.repository.branches.size})", label: "Branches (#{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 eq(OpenStruct.new(enabled: true, expect(presenter.tags_anchor_data).to have_attributes(enabled: true,
label: "Tags (#{project.repository.tags.size})", label: "Tags (#{project.repository.tags.size})",
link: presenter.project_tags_path(project))) link: presenter.project_tags_path(project))
end end
end end
...@@ -199,10 +236,10 @@ describe ProjectPresenter do ...@@ -199,10 +236,10 @@ 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 eq(OpenStruct.new(enabled: false, expect(presenter.new_file_anchor_data).to have_attributes(enabled: false,
label: "New file", label: "New file",
link: presenter.project_new_blob_path(project, 'master'), link: presenter.project_new_blob_path(project, 'master'),
class_modifier: 'new')) class_modifier: 'new')
end end
it 'returns nil if user cannot push' do it 'returns nil if user cannot push' do
...@@ -227,9 +264,9 @@ describe ProjectPresenter do ...@@ -227,9 +264,9 @@ 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 eq(OpenStruct.new(enabled: false, expect(presenter.readme_anchor_data).to have_attributes(enabled: false,
label: 'Add Readme', label: 'Add Readme',
link: presenter.add_readme_path)) link: presenter.add_readme_path)
end end
end end
...@@ -237,9 +274,9 @@ describe ProjectPresenter do ...@@ -237,9 +274,9 @@ 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 eq(OpenStruct.new(enabled: true, expect(presenter.readme_anchor_data).to have_attributes(enabled: true,
label: 'Readme', label: 'Readme',
link: presenter.readme_path)) link: presenter.readme_path)
end end
end end
end end
...@@ -250,9 +287,9 @@ describe ProjectPresenter do ...@@ -250,9 +287,9 @@ describe ProjectPresenter 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 eq(OpenStruct.new(enabled: false, expect(presenter.changelog_anchor_data).to have_attributes(enabled: false,
label: 'Add Changelog', label: 'Add Changelog',
link: presenter.add_changelog_path)) link: presenter.add_changelog_path)
end end
end end
...@@ -260,9 +297,9 @@ describe ProjectPresenter do ...@@ -260,9 +297,9 @@ 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 eq(OpenStruct.new(enabled: true, expect(presenter.changelog_anchor_data).to have_attributes(enabled: true,
label: 'Changelog', label: 'Changelog',
link: presenter.changelog_path)) link: presenter.changelog_path)
end end
end end
end end
...@@ -273,9 +310,9 @@ describe ProjectPresenter do ...@@ -273,9 +310,9 @@ describe ProjectPresenter 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 eq(OpenStruct.new(enabled: false, expect(presenter.license_anchor_data).to have_attributes(enabled: false,
label: 'Add License', label: 'Add license',
link: presenter.add_license_path)) link: presenter.add_license_path)
end end
end end
...@@ -283,9 +320,9 @@ describe ProjectPresenter do ...@@ -283,9 +320,9 @@ 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 eq(OpenStruct.new(enabled: true, expect(presenter.license_anchor_data).to have_attributes(enabled: true,
label: presenter.license_short_name, label: presenter.license_short_name,
link: presenter.license_path)) link: presenter.license_path)
end end
end end
end end
...@@ -296,9 +333,9 @@ describe ProjectPresenter do ...@@ -296,9 +333,9 @@ describe ProjectPresenter 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 eq(OpenStruct.new(enabled: false, expect(presenter.contribution_guide_anchor_data).to have_attributes(enabled: false,
label: 'Add Contribution guide', label: 'Add Contribution guide',
link: presenter.add_contribution_guide_path)) link: presenter.add_contribution_guide_path)
end end
end end
...@@ -306,9 +343,9 @@ describe ProjectPresenter do ...@@ -306,9 +343,9 @@ 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 eq(OpenStruct.new(enabled: true, expect(presenter.contribution_guide_anchor_data).to have_attributes(enabled: true,
label: 'Contribution guide', label: 'Contribution guide',
link: presenter.contribution_guide_path)) link: presenter.contribution_guide_path)
end end
end end
end end
...@@ -318,9 +355,9 @@ describe ProjectPresenter do ...@@ -318,9 +355,9 @@ 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 eq(OpenStruct.new(enabled: true, expect(presenter.autodevops_anchor_data).to have_attributes(enabled: true,
label: 'Auto DevOps enabled', label: 'Auto DevOps enabled',
link: nil)) link: nil)
end end
end end
...@@ -330,9 +367,9 @@ describe ProjectPresenter do ...@@ -330,9 +367,9 @@ describe ProjectPresenter do
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 eq(OpenStruct.new(enabled: false, expect(presenter.autodevops_anchor_data).to have_attributes(enabled: false,
label: 'Enable Auto DevOps', label: '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
end end
...@@ -343,9 +380,9 @@ describe ProjectPresenter do ...@@ -343,9 +380,9 @@ 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 eq(OpenStruct.new(enabled: true, expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(enabled: true,
label: 'Kubernetes configured', label: 'Kubernetes configured',
link: presenter.project_cluster_path(project, cluster))) link: presenter.project_cluster_path(project, cluster))
end end
it 'returns link to clusters page if more than one exists' do it 'returns link to clusters page if more than one exists' do
...@@ -353,17 +390,17 @@ describe ProjectPresenter do ...@@ -353,17 +390,17 @@ 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 eq(OpenStruct.new(enabled: true, expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(enabled: true,
label: 'Kubernetes configured', label: '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 eq(OpenStruct.new(enabled: false, expect(presenter.kubernetes_cluster_anchor_data).to have_attributes(enabled: false,
label: 'Add Kubernetes cluster', label: 'Add Kubernetes cluster',
link: presenter.new_project_cluster_path(project))) link: presenter.new_project_cluster_path(project))
end end
end end
...@@ -380,9 +417,9 @@ describe ProjectPresenter do ...@@ -380,9 +417,9 @@ describe ProjectPresenter do
allow(project.repository).to receive(:koding_yml).and_return(nil) allow(project.repository).to receive(:koding_yml).and_return(nil)
allow(Gitlab::CurrentSettings).to receive(:koding_enabled?).and_return(true) allow(Gitlab::CurrentSettings).to receive(:koding_enabled?).and_return(true)
expect(presenter.koding_anchor_data).to eq(OpenStruct.new(enabled: false, expect(presenter.koding_anchor_data).to have_attributes(enabled: false,
label: 'Set up Koding', label: 'Set up Koding',
link: presenter.add_koding_stack_path)) link: presenter.add_koding_stack_path)
end end
it 'returns nil if user cannot push' do it 'returns nil if user cannot push' do
......
...@@ -9,6 +9,7 @@ describe 'projects/_home_panel' do ...@@ -9,6 +9,7 @@ describe 'projects/_home_panel' do
allow(view).to receive(:current_user).and_return(user) allow(view).to receive(:current_user).and_return(user)
allow(view).to receive(:can?).with(user, :read_project, project).and_return(false) allow(view).to receive(:can?).with(user, :read_project, project).and_return(false)
allow(project).to receive(:license_anchor_data).and_return(false)
end end
context 'when user is signed in' do context 'when user is signed in' do
...@@ -63,6 +64,7 @@ describe 'projects/_home_panel' do ...@@ -63,6 +64,7 @@ describe 'projects/_home_panel' do
allow(view).to receive(:current_user).and_return(user) allow(view).to receive(:current_user).and_return(user)
allow(view).to receive(:can?).with(user, :read_project, project).and_return(false) allow(view).to receive(:can?).with(user, :read_project, project).and_return(false)
allow(project).to receive(:license_anchor_data).and_return(false)
end end
context 'has no badges' do context 'has no badges' do
...@@ -71,8 +73,7 @@ describe 'projects/_home_panel' do ...@@ -71,8 +73,7 @@ describe 'projects/_home_panel' do
it 'should not render any badge' do it 'should not render any badge' do
render render
expect(rendered).to have_selector('.project-badges') expect(rendered).not_to have_selector('.project-badges')
expect(rendered).not_to have_selector('.project-badges > a')
end end
end end
...@@ -118,6 +119,7 @@ describe 'projects/_home_panel' do ...@@ -118,6 +119,7 @@ describe 'projects/_home_panel' do
assign(:project, project) assign(:project, project)
allow(view).to receive(:current_user).and_return(user) allow(view).to receive(:current_user).and_return(user)
allow(project).to receive(:license_anchor_data).and_return(false)
end end
context 'user can read project' do context 'user can read project' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment