Commit eca1fdc3 authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'ee-fly-out-top-level-item' into 'master'

EE port of fly-out-top-level-item

See merge request !2856
parents b7d5ce50 3db45574
...@@ -20,8 +20,10 @@ let headerHeight = 50; ...@@ -20,8 +20,10 @@ let headerHeight = 50;
export const getHeaderHeight = () => headerHeight; export const getHeaderHeight = () => headerHeight;
export const isSidebarCollapsed = () => sidebar && sidebar.classList.contains('sidebar-icons-only');
export const canShowActiveSubItems = (el) => { export const canShowActiveSubItems = (el) => {
if (el.classList.contains('active') && (sidebar && !sidebar.classList.contains('sidebar-icons-only'))) { if (el.classList.contains('active') && !isSidebarCollapsed()) {
return false; return false;
} }
...@@ -99,12 +101,13 @@ export const moveSubItemsToPosition = (el, subItems) => { ...@@ -99,12 +101,13 @@ export const moveSubItemsToPosition = (el, subItems) => {
export const showSubLevelItems = (el) => { export const showSubLevelItems = (el) => {
const subItems = el.querySelector('.sidebar-sub-level-items'); const subItems = el.querySelector('.sidebar-sub-level-items');
const isIconOnly = subItems && subItems.classList.contains('is-fly-out-only');
if (!canShowSubItems() || !canShowActiveSubItems(el)) return; if (!canShowSubItems() || !canShowActiveSubItems(el)) return;
el.classList.add(IS_OVER_CLASS); el.classList.add(IS_OVER_CLASS);
if (!subItems) return; if (!subItems || (!isSidebarCollapsed() && isIconOnly)) return;
subItems.style.display = 'block'; subItems.style.display = 'block';
el.classList.add(IS_SHOWING_FLY_OUT_CLASS); el.classList.add(IS_SHOWING_FLY_OUT_CLASS);
......
...@@ -74,7 +74,7 @@ class Issue { ...@@ -74,7 +74,7 @@ class Issue {
this.toggleCloseReopenButton(isClosed); this.toggleCloseReopenButton(isClosed);
let numProjectIssues = Number(projectIssuesCounter.text().replace(/[^\d]/, '')); let numProjectIssues = Number(projectIssuesCounter.first().text().trim().replace(/[^\d]/, ''));
numProjectIssues = isClosed ? numProjectIssues - 1 : numProjectIssues + 1; numProjectIssues = isClosed ? numProjectIssues - 1 : numProjectIssues + 1;
projectIssuesCounter.text(gl.text.addDelimiter(numProjectIssues)); projectIssuesCounter.text(gl.text.addDelimiter(numProjectIssues));
......
...@@ -106,11 +106,8 @@ $new-sidebar-collapsed-width: 50px; ...@@ -106,11 +106,8 @@ $new-sidebar-collapsed-width: 50px;
overflow-x: hidden; overflow-x: hidden;
} }
.badge, .badge:not(.fly-out-badge),
.sidebar-context-title { .sidebar-context-title,
display: none;
}
.nav-item-name { .nav-item-name {
display: none; display: none;
} }
...@@ -118,6 +115,10 @@ $new-sidebar-collapsed-width: 50px; ...@@ -118,6 +115,10 @@ $new-sidebar-collapsed-width: 50px;
.sidebar-top-level-items > li > a { .sidebar-top-level-items > li > a {
min-height: 44px; min-height: 44px;
} }
.fly-out-top-item {
display: block;
}
} }
&.nav-sidebar-expanded { &.nav-sidebar-expanded {
...@@ -179,6 +180,10 @@ $new-sidebar-collapsed-width: 50px; ...@@ -179,6 +180,10 @@ $new-sidebar-collapsed-width: 50px;
width: 16px; width: 16px;
} }
} }
.fly-out-top-item {
display: none;
}
} }
.nav-sidebar-inner-scroll { .nav-sidebar-inner-scroll {
...@@ -249,7 +254,7 @@ $new-sidebar-collapsed-width: 50px; ...@@ -249,7 +254,7 @@ $new-sidebar-collapsed-width: 50px;
left: $new-sidebar-width; left: $new-sidebar-width;
min-width: 150px; min-width: 150px;
margin-top: -1px; margin-top: -1px;
padding: 8px 1px; padding: 4px 1px;
background-color: $white-light; background-color: $white-light;
box-shadow: 2px 1px 3px $dropdown-shadow-color; box-shadow: 2px 1px 3px $dropdown-shadow-color;
border: 1px solid $gray-darker; border: 1px solid $gray-darker;
...@@ -270,6 +275,13 @@ $new-sidebar-collapsed-width: 50px; ...@@ -270,6 +275,13 @@ $new-sidebar-collapsed-width: 50px;
margin-top: 1px; margin-top: 1px;
} }
.divider {
height: 1px;
margin: 4px -1px;
padding: 0;
background-color: $dropdown-divider-color;
}
> .active { > .active {
box-shadow: none; box-shadow: none;
...@@ -309,7 +321,7 @@ $new-sidebar-collapsed-width: 50px; ...@@ -309,7 +321,7 @@ $new-sidebar-collapsed-width: 50px;
font-weight: $gl-font-weight-bold; font-weight: $gl-font-weight-bold;
} }
.sidebar-sub-level-items { .sidebar-sub-level-items:not(.is-fly-out-only) {
display: block; display: block;
} }
} }
...@@ -403,6 +415,19 @@ $new-sidebar-collapsed-width: 50px; ...@@ -403,6 +415,19 @@ $new-sidebar-collapsed-width: 50px;
} }
} }
.fly-out-top-item {
> a {
display: flex;
}
.fly-out-badge {
margin-left: 8px;
}
}
.fly-out-top-item-name {
flex: 1;
}
// Mobile nav // Mobile nav
......
...@@ -14,6 +14,11 @@ ...@@ -14,6 +14,11 @@
Overview Overview
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts), html_options: { class: "fly-out-top-item" } ) do
= link_to admin_root_path do
%strong.fly-out-top-item-name
#{ _('Overview') }
%li.divider.fly-out-top-item
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do = nav_link(controller: :dashboard, html_options: {class: 'home'}) do
= link_to admin_root_path, title: 'Overview' do = link_to admin_root_path, title: 'Overview' do
%span %span
...@@ -55,6 +60,11 @@ ...@@ -55,6 +60,11 @@
Monitoring Monitoring
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(controller: %w(conversational_development_index system_info background_jobs logs health_check requests_profiles), html_options: { class: "fly-out-top-item" } ) do
= link_to admin_conversational_development_index_path do
%strong.fly-out-top-item-name
#{ _('Monitoring') }
%li.divider.fly-out-top-item
= nav_link(controller: :system_info) do = nav_link(controller: :system_info) do
= link_to admin_system_info_path, title: 'System Info' do = link_to admin_system_info_path, title: 'System Info' do
%span %span
...@@ -83,6 +93,11 @@ ...@@ -83,6 +93,11 @@
= custom_icon('messages') = custom_icon('messages')
%span.nav-item-name %span.nav-item-name
Messages Messages
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :broadcast_messages, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_broadcast_messages_path do
%strong.fly-out-top-item-name
#{ _('Messages') }
= nav_link(controller: [:hooks, :hook_logs]) do = nav_link(controller: [:hooks, :hook_logs]) do
= sidebar_link admin_hooks_path, title: _('Hooks') do = sidebar_link admin_hooks_path, title: _('Hooks') do
...@@ -90,6 +105,11 @@ ...@@ -90,6 +105,11 @@
= custom_icon('system_hooks') = custom_icon('system_hooks')
%span.nav-item-name %span.nav-item-name
System Hooks System Hooks
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: [:hooks, :hook_logs], html_options: { class: "fly-out-top-item" } ) do
= link_to admin_hooks_path do
%strong.fly-out-top-item-name
#{ _('System Hooks') }
= nav_link(controller: :applications) do = nav_link(controller: :applications) do
= sidebar_link admin_applications_path, title: _('Applications') do = sidebar_link admin_applications_path, title: _('Applications') do
...@@ -97,6 +117,11 @@ ...@@ -97,6 +117,11 @@
= custom_icon('applications') = custom_icon('applications')
%span.nav-item-name %span.nav-item-name
Applications Applications
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :applications, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_applications_path do
%strong.fly-out-top-item-name
#{ _('Applications') }
= nav_link(controller: :abuse_reports) do = nav_link(controller: :abuse_reports) do
= sidebar_link admin_abuse_reports_path, title: _("Abuse Reports") do = sidebar_link admin_abuse_reports_path, title: _("Abuse Reports") do
...@@ -105,6 +130,12 @@ ...@@ -105,6 +130,12 @@
%span.nav-item-name %span.nav-item-name
Abuse Reports Abuse Reports
%span.badge.count= number_with_delimiter(AbuseReport.count(:all)) %span.badge.count= number_with_delimiter(AbuseReport.count(:all))
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :abuse_reports, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_broadcast_messages_path do
%strong.fly-out-top-item-name
#{ _('Abuse Reports') }
%span.badge.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(AbuseReport.count(:all))
= nav_link(controller: :licenses) do = nav_link(controller: :licenses) do
= sidebar_link admin_license_path, title: _('License') do = sidebar_link admin_license_path, title: _('License') do
...@@ -112,6 +143,11 @@ ...@@ -112,6 +143,11 @@
= custom_icon('license') = custom_icon('license')
%span.nav-item-name %span.nav-item-name
License License
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :licenses, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_license_path do
%strong.fly-out-top-item-name
#{ _('License') }
- if akismet_enabled? - if akismet_enabled?
= nav_link(controller: :spam_logs) do = nav_link(controller: :spam_logs) do
...@@ -120,6 +156,11 @@ ...@@ -120,6 +156,11 @@
= custom_icon('spam_logs') = custom_icon('spam_logs')
%span.nav-item-name %span.nav-item-name
Spam Logs Spam Logs
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :spam_logs, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_spam_logs_path do
%strong.fly-out-top-item-name
#{ _('Spam Logs') }
= nav_link(controller: :push_rules) do = nav_link(controller: :push_rules) do
= sidebar_link admin_push_rule_path, title: _('Push Rules') do = sidebar_link admin_push_rule_path, title: _('Push Rules') do
...@@ -127,6 +168,11 @@ ...@@ -127,6 +168,11 @@
= custom_icon('push_rules') = custom_icon('push_rules')
%span.nav-item-name %span.nav-item-name
Push Rules Push Rules
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :push_rules, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_push_rule_path do
%strong.fly-out-top-item-name
#{ _('Push Rules') }
= nav_link(controller: :geo_nodes) do = nav_link(controller: :geo_nodes) do
= sidebar_link admin_geo_nodes_path, title: _('Geo Nodes') do = sidebar_link admin_geo_nodes_path, title: _('Geo Nodes') do
...@@ -134,6 +180,11 @@ ...@@ -134,6 +180,11 @@
= custom_icon('geo_nodes') = custom_icon('geo_nodes')
%span.nav-item-name %span.nav-item-name
Geo Nodes Geo Nodes
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :geo_nodes, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_geo_nodes_path do
%strong.fly-out-top-item-name
#{ _('Geo Nodes') }
= nav_link(controller: :deploy_keys) do = nav_link(controller: :deploy_keys) do
= sidebar_link admin_deploy_keys_path, title: _('Deploy Keys') do = sidebar_link admin_deploy_keys_path, title: _('Deploy Keys') do
...@@ -141,6 +192,11 @@ ...@@ -141,6 +192,11 @@
= custom_icon('key') = custom_icon('key')
%span.nav-item-name %span.nav-item-name
Deploy Keys Deploy Keys
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :deploy_keys, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_deploy_keys_path do
%strong.fly-out-top-item-name
#{ _('Deploy Keys') }
= nav_link(controller: :services) do = nav_link(controller: :services) do
= sidebar_link admin_application_settings_services_path, title: _('Service Templates') do = sidebar_link admin_application_settings_services_path, title: _('Service Templates') do
...@@ -148,6 +204,11 @@ ...@@ -148,6 +204,11 @@
= custom_icon('service_templates') = custom_icon('service_templates')
%span.nav-item-name %span.nav-item-name
Service Templates Service Templates
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :services, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_application_settings_services_path do
%strong.fly-out-top-item-name
#{ _('Service Templates') }
= nav_link(controller: :labels) do = nav_link(controller: :labels) do
= sidebar_link admin_labels_path, title: _('Labels') do = sidebar_link admin_labels_path, title: _('Labels') do
...@@ -155,6 +216,11 @@ ...@@ -155,6 +216,11 @@
= custom_icon('labels') = custom_icon('labels')
%span.nav-item-name %span.nav-item-name
Labels Labels
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :labels, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_labels_path do
%strong.fly-out-top-item-name
#{ _('Labels') }
= nav_link(controller: :appearances) do = nav_link(controller: :appearances) do
= sidebar_link admin_appearances_path, title: _('Appearances') do = sidebar_link admin_appearances_path, title: _('Appearances') do
...@@ -162,6 +228,11 @@ ...@@ -162,6 +228,11 @@
= custom_icon('appearance') = custom_icon('appearance')
%span.nav-item-name %span.nav-item-name
Appearance Appearance
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :appearances, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_appearances_path do
%strong.fly-out-top-item-name
#{ _('Appearance') }
= nav_link(controller: :application_settings) do = nav_link(controller: :application_settings) do
= sidebar_link admin_application_settings_path, title: _('Settings') do = sidebar_link admin_application_settings_path, title: _('Settings') do
...@@ -169,5 +240,10 @@ ...@@ -169,5 +240,10 @@
= custom_icon('settings') = custom_icon('settings')
%span.nav-item-name %span.nav-item-name
Settings Settings
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :application_settings, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_application_settings_path do
%strong.fly-out-top-item-name
#{ _('Settings') }
= render 'shared/sidebar_toggle_button' = render 'shared/sidebar_toggle_button'
- issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute
.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) } .nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll .nav-sidebar-inner-scroll
.context-header .context-header
...@@ -15,6 +18,11 @@ ...@@ -15,6 +18,11 @@
Overview Overview
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: "fly-out-top-item" } ) do
= link_to group_path(@group) do
%strong.fly-out-top-item-name
#{ _('Overview') }
%li.divider.fly-out-top-item
= nav_link(path: ['groups#show', 'groups#subgroups'], html_options: { class: 'home' }) do = nav_link(path: ['groups#show', 'groups#subgroups'], html_options: { class: 'home' }) do
= link_to group_path(@group), title: 'Group details' do = link_to group_path(@group), title: 'Group details' do
%span %span
...@@ -36,11 +44,16 @@ ...@@ -36,11 +44,16 @@
.nav-icon-container .nav-icon-container
= custom_icon('issues') = custom_icon('issues')
%span.nav-item-name %span.nav-item-name
- issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
Issues Issues
%span.badge.count= number_with_delimiter(issues.count) %span.badge.count= number_with_delimiter(issues.count)
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(path: ['groups#issues', 'labels#index', 'milestones#index'], html_options: { class: "fly-out-top-item" } ) do
= link_to issues_group_path(@group) do
%strong.fly-out-top-item-name
#{ _('Issues') }
%span.badge.count.issue_counter.fly-out-badge= number_with_delimiter(issues.count)
%li.divider.fly-out-top-item
= nav_link(path: 'groups#issues', html_options: { class: 'home' }) do = nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
= link_to issues_group_path(@group), title: 'List' do = link_to issues_group_path(@group), title: 'List' do
%span %span
...@@ -61,15 +74,25 @@ ...@@ -61,15 +74,25 @@
.nav-icon-container .nav-icon-container
= custom_icon('mr_bold') = custom_icon('mr_bold')
%span.nav-item-name %span.nav-item-name
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute
Merge Requests Merge Requests
%span.badge.count= number_with_delimiter(merge_requests.count) %span.badge.count= number_with_delimiter(merge_requests.count)
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(path: 'groups#merge_requests', html_options: { class: "fly-out-top-item" } ) do
= link_to merge_requests_group_path(@group) do
%strong.fly-out-top-item-name
#{ _('Merge Requests') }
%span.badge.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(merge_requests.count)
= nav_link(path: 'group_members#index') do = nav_link(path: 'group_members#index') do
= sidebar_link group_group_members_path(@group), title: _('Members') do = sidebar_link group_group_members_path(@group), title: _('Members') do
.nav-icon-container .nav-icon-container
= custom_icon('members') = custom_icon('members')
%span.nav-item-name %span.nav-item-name
Members Members
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(path: 'group_members#index', html_options: { class: "fly-out-top-item" } ) do
= link_to merge_requests_group_path(@group) do
%strong.fly-out-top-item-name
#{ _('Members') }
- if current_user && can?(current_user, :admin_group, @group) - if current_user && can?(current_user, :admin_group, @group)
= nav_link(path: %w[groups#projects groups#edit ci_cd#show ldap_group_links#index hooks#index audit_events#index pipeline_quota#index]) do = nav_link(path: %w[groups#projects groups#edit ci_cd#show ldap_group_links#index hooks#index audit_events#index pipeline_quota#index]) do
= sidebar_link edit_group_path(@group), title: _('Settings') do = sidebar_link edit_group_path(@group), title: _('Settings') do
...@@ -78,6 +101,11 @@ ...@@ -78,6 +101,11 @@
%span.nav-item-name %span.nav-item-name
Settings Settings
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(path: %w[groups#projects groups#edit ci_cd#show], html_options: { class: "fly-out-top-item" } ) do
= link_to edit_group_path(@group) do
%strong.fly-out-top-item-name
#{ _('Settings') }
%li.divider.fly-out-top-item
= nav_link(path: 'groups#edit') do = nav_link(path: 'groups#edit') do
= link_to edit_group_path(@group), title: 'General' do = link_to edit_group_path(@group), title: 'General' do
%span %span
......
...@@ -12,12 +12,22 @@ ...@@ -12,12 +12,22 @@
= custom_icon('profile') = custom_icon('profile')
%span.nav-item-name %span.nav-item-name
Profile Profile
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(path: 'profiles#show', html_options: { class: "fly-out-top-item" } ) do
= link_to profile_path do
%strong.fly-out-top-item-name
#{ _('Profile') }
= nav_link(controller: [:accounts, :two_factor_auths]) do = nav_link(controller: [:accounts, :two_factor_auths]) do
= sidebar_link profile_account_path, title: _('Account') do = sidebar_link profile_account_path, title: _('Account') do
.nav-icon-container .nav-icon-container
= custom_icon('account') = custom_icon('account')
%span.nav-item-name %span.nav-item-name
Account Account
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: [:accounts, :two_factor_auths], html_options: { class: "fly-out-top-item" } ) do
= link_to profile_account_path do
%strong.fly-out-top-item-name
#{ _('Account') }
- if current_application_settings.should_check_namespace_plan? - if current_application_settings.should_check_namespace_plan?
= nav_link(controller: :billings) do = nav_link(controller: :billings) do
= sidebar_link profile_billings_path, title: _('Billing') do = sidebar_link profile_billings_path, title: _('Billing') do
...@@ -25,6 +35,11 @@ ...@@ -25,6 +35,11 @@
= custom_icon('credit_card') = custom_icon('credit_card')
%span.nav-item-name %span.nav-item-name
Billing Billing
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :billings, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_billings_path do
%strong.fly-out-top-item-name
#{ _('Billing') }
- if current_application_settings.user_oauth_applications? - if current_application_settings.user_oauth_applications?
= nav_link(controller: 'oauth/applications') do = nav_link(controller: 'oauth/applications') do
= sidebar_link applications_profile_path, title: _('Applications') do = sidebar_link applications_profile_path, title: _('Applications') do
...@@ -32,24 +47,44 @@ ...@@ -32,24 +47,44 @@
= custom_icon('applications') = custom_icon('applications')
%span.nav-item-name %span.nav-item-name
Applications Applications
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: 'oauth/applications', html_options: { class: "fly-out-top-item" } ) do
= link_to applications_profile_path do
%strong.fly-out-top-item-name
#{ _('Applications') }
= nav_link(controller: :chat_names) do = nav_link(controller: :chat_names) do
= sidebar_link profile_chat_names_path, title: _('Chat') do = sidebar_link profile_chat_names_path, title: _('Chat') do
.nav-icon-container .nav-icon-container
= custom_icon('chat') = custom_icon('chat')
%span.nav-item-name %span.nav-item-name
Chat Chat
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :chat_names, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_chat_names_path do
%strong.fly-out-top-item-name
#{ _('Chat') }
= nav_link(controller: :personal_access_tokens) do = nav_link(controller: :personal_access_tokens) do
= sidebar_link profile_personal_access_tokens_path, title: _('Access Tokens') do = sidebar_link profile_personal_access_tokens_path, title: _('Access Tokens') do
.nav-icon-container .nav-icon-container
= custom_icon('access_tokens') = custom_icon('access_tokens')
%span.nav-item-name %span.nav-item-name
Access Tokens Access Tokens
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :personal_access_tokens, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_personal_access_tokens_path do
%strong.fly-out-top-item-name
#{ _('Access Tokens') }
= nav_link(controller: :emails) do = nav_link(controller: :emails) do
= sidebar_link profile_emails_path, title: _('Emails') do = sidebar_link profile_emails_path, title: _('Emails') do
.nav-icon-container .nav-icon-container
= custom_icon('emails') = custom_icon('emails')
%span.nav-item-name %span.nav-item-name
Emails Emails
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :emails, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_emails_path do
%strong.fly-out-top-item-name
#{ _('Emails') }
- unless current_user.ldap_user? - unless current_user.ldap_user?
= nav_link(controller: :passwords) do = nav_link(controller: :passwords) do
= sidebar_link edit_profile_password_path, title: _('Password') do = sidebar_link edit_profile_password_path, title: _('Password') do
...@@ -57,41 +92,76 @@ ...@@ -57,41 +92,76 @@
= custom_icon('lock') = custom_icon('lock')
%span.nav-item-name %span.nav-item-name
Password Password
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :passwords, html_options: { class: "fly-out-top-item" } ) do
= link_to edit_profile_password_path do
%strong.fly-out-top-item-name
#{ _('Password') }
= nav_link(controller: :notifications) do = nav_link(controller: :notifications) do
= sidebar_link profile_notifications_path, title: _('Notifications') do = sidebar_link profile_notifications_path, title: _('Notifications') do
.nav-icon-container .nav-icon-container
= custom_icon('notifications') = custom_icon('notifications')
%span.nav-item-name %span.nav-item-name
Notifications Notifications
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :notifications, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_notifications_path do
%strong.fly-out-top-item-name
#{ _('Notifications') }
= nav_link(controller: :keys) do = nav_link(controller: :keys) do
= sidebar_link profile_keys_path, title: _('SSH Keys') do = sidebar_link profile_keys_path, title: _('SSH Keys') do
.nav-icon-container .nav-icon-container
= custom_icon('key') = custom_icon('key')
%span.nav-item-name %span.nav-item-name
SSH Keys SSH Keys
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :keys, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_keys_path do
%strong.fly-out-top-item-name
#{ _('SSH Keys') }
= nav_link(controller: :gpg_keys) do = nav_link(controller: :gpg_keys) do
= sidebar_link profile_gpg_keys_path, title: _('GPG Keys') do = sidebar_link profile_gpg_keys_path, title: _('GPG Keys') do
.nav-icon-container .nav-icon-container
= custom_icon('key_2') = custom_icon('key_2')
%span.nav-item-name %span.nav-item-name
GPG Keys GPG Keys
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :gpg_keys, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_gpg_keys_path do
%strong.fly-out-top-item-name
#{ _('GPG Keys') }
= nav_link(controller: :preferences) do = nav_link(controller: :preferences) do
= sidebar_link profile_preferences_path, title: _('Preferences') do = sidebar_link profile_preferences_path, title: _('Preferences') do
.nav-icon-container .nav-icon-container
= custom_icon('preferences') = custom_icon('preferences')
%span.nav-item-name %span.nav-item-name
Preferences Preferences
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :preferences, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_preferences_path do
%strong.fly-out-top-item-name
#{ _('Preferences') }
= nav_link(path: 'profiles#audit_log') do = nav_link(path: 'profiles#audit_log') do
= sidebar_link audit_log_profile_path, title: _('Authentication log') do = sidebar_link audit_log_profile_path, title: _('Authentication log') do
.nav-icon-container .nav-icon-container
= custom_icon('authentication_log') = custom_icon('authentication_log')
%span.nav-item-name %span.nav-item-name
Authentication log Authentication log
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(path: 'profiles#audit_log', html_options: { class: "fly-out-top-item" } ) do
= link_to audit_log_profile_path do
%strong.fly-out-top-item-name
#{ _('Authentication Log') }
= nav_link(path: 'profiles#pipeline_quota') do = nav_link(path: 'profiles#pipeline_quota') do
= sidebar_link profile_pipeline_quota_path, title: _('Pipeline quota') do = sidebar_link profile_pipeline_quota_path, title: _('Pipeline quota') do
.nav-icon-container .nav-icon-container
= custom_icon('pipeline') = custom_icon('pipeline')
%span.nav-item-name %span.nav-item-name
Pipeline quota Pipeline quota
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(path: 'profiles#pipeline_quota', html_options: { class: "fly-out-top-item" } ) do
= link_to profile_pipeline_quota_path do
%strong.fly-out-top-item-name
#{ _('Pipeline quota') }
= render 'shared/sidebar_toggle_button' = render 'shared/sidebar_toggle_button'
...@@ -16,6 +16,11 @@ ...@@ -16,6 +16,11 @@
Overview Overview
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(path: 'projects#show', html_options: { class: "fly-out-top-item" } ) do
= link_to project_path(@project) do
%strong.fly-out-top-item-name
#{ _('Overview') }
%li.divider.fly-out-top-item
= nav_link(path: 'projects#show') do = nav_link(path: 'projects#show') do
= link_to project_path(@project), title: _('Project details'), class: 'shortcuts-project' do = link_to project_path(@project), title: _('Project details'), class: 'shortcuts-project' do
%span= _('Details') %span= _('Details')
...@@ -38,6 +43,11 @@ ...@@ -38,6 +43,11 @@
Repository Repository
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare projects/repositories tags branches releases graphs network), html_options: { class: "fly-out-top-item" } ) do
= link_to project_tree_path(@project) do
%strong.fly-out-top-item-name
#{ _('Repository') }
%li.divider.fly-out-top-item
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
= link_to project_tree_path(@project) do = link_to project_tree_path(@project) do
#{ _('Files') } #{ _('Files') }
...@@ -82,6 +92,11 @@ ...@@ -82,6 +92,11 @@
= custom_icon('container_registry') = custom_icon('container_registry')
%span.nav-item-name %span.nav-item-name
Registry Registry
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: %w[projects/registry/repositories], html_options: { class: "fly-out-top-item" } ) do
= link_to project_container_registry_index_path(@project) do
%strong.fly-out-top-item-name
#{ _('Registry') }
- if project_nav_tab? :issues - if project_nav_tab? :issues
= nav_link(controller: @project.issues_enabled? ? [:issues, :labels, :milestones, :boards] : :issues) do = nav_link(controller: @project.issues_enabled? ? [:issues, :labels, :milestones, :boards] : :issues) do
...@@ -95,6 +110,14 @@ ...@@ -95,6 +110,14 @@
= number_with_delimiter(@project.open_issues_count) = number_with_delimiter(@project.open_issues_count)
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(controller: :issues, html_options: { class: "fly-out-top-item" } ) do
= link_to project_issues_path(@project) do
%strong.fly-out-top-item-name
#{ _('Issues') }
- if @project.issues_enabled?
%span.badge.count.issue_counter.fly-out-badge
= number_with_delimiter(@project.open_issues_count)
%li.divider.fly-out-top-item
= nav_link(controller: :issues) do = nav_link(controller: :issues) do
= link_to project_issues_path(@project), title: 'Issues' do = link_to project_issues_path(@project), title: 'Issues' do
%span %span
...@@ -124,6 +147,13 @@ ...@@ -124,6 +147,13 @@
Merge Requests Merge Requests
%span.badge.count.merge_counter.js-merge-counter %span.badge.count.merge_counter.js-merge-counter
= number_with_delimiter(@project.open_merge_requests_count) = number_with_delimiter(@project.open_merge_requests_count)
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :merge_requests, html_options: { class: "fly-out-top-item" } ) do
= link_to project_merge_requests_path(@project) do
%strong.fly-out-top-item-name
#{ _('Merge Requests') }
%span.badge.count.merge_counter.js-merge-counter.fly-out-badge
= number_with_delimiter(@project.open_merge_requests_count)
- if project_nav_tab? :pipelines - if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do = nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do
...@@ -134,6 +164,11 @@ ...@@ -134,6 +164,11 @@
CI / CD CI / CD
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts], html_options: { class: "fly-out-top-item" } ) do
= link_to project_pipelines_path(@project) do
%strong.fly-out-top-item-name
#{ _('CI / CD') }
%li.divider.fly-out-top-item
- if project_nav_tab? :pipelines - if project_nav_tab? :pipelines
= nav_link(path: ['pipelines#index', 'pipelines#show']) do = nav_link(path: ['pipelines#index', 'pipelines#show']) do
= link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
...@@ -171,6 +206,11 @@ ...@@ -171,6 +206,11 @@
= custom_icon('wiki') = custom_icon('wiki')
%span.nav-item-name %span.nav-item-name
Wiki Wiki
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :wikis, html_options: { class: "fly-out-top-item" } ) do
= link_to get_project_wiki_path(@project) do
%strong.fly-out-top-item-name
#{ _('Wiki') }
- if project_nav_tab? :snippets - if project_nav_tab? :snippets
= nav_link(controller: :snippets) do = nav_link(controller: :snippets) do
...@@ -179,6 +219,11 @@ ...@@ -179,6 +219,11 @@
= custom_icon('snippets') = custom_icon('snippets')
%span.nav-item-name %span.nav-item-name
Snippets Snippets
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :snippets, html_options: { class: "fly-out-top-item" } ) do
= link_to project_snippets_path(@project) do
%strong.fly-out-top-item-name
#{ _('Snippets') }
- if project_nav_tab? :settings - if project_nav_tab? :settings
= nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show]) do = nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show]) do
...@@ -191,6 +236,11 @@ ...@@ -191,6 +236,11 @@
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
- can_edit = can?(current_user, :admin_project, @project) - can_edit = can?(current_user, :admin_project, @project)
- if can_edit - if can_edit
= nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show], html_options: { class: "fly-out-top-item" } ) do
= link_to edit_project_path(@project) do
%strong.fly-out-top-item-name
#{ _('Settings') }
%li.divider.fly-out-top-item
= nav_link(path: %w[projects#edit]) do = nav_link(path: %w[projects#edit]) do
= link_to edit_project_path(@project), title: 'General' do = link_to edit_project_path(@project), title: 'General' do
%span %span
......
...@@ -24,7 +24,7 @@ class Spinach::Features::GroupsManagement < Spinach::FeatureSteps ...@@ -24,7 +24,7 @@ class Spinach::Features::GroupsManagement < Spinach::FeatureSteps
step 'I go to "Open" project members page' do step 'I go to "Open" project members page' do
click_link 'Sourcing / Open' click_link 'Sourcing / Open'
page.within('.nav-sidebar') do page.within('.nav-sidebar') do
click_link 'Settings' first(:link, text: 'Settings').click
end end
click_link 'Members' click_link 'Members'
end end
...@@ -38,7 +38,7 @@ class Spinach::Features::GroupsManagement < Spinach::FeatureSteps ...@@ -38,7 +38,7 @@ class Spinach::Features::GroupsManagement < Spinach::FeatureSteps
visit root_path visit root_path
click_link 'Sourcing / Open' click_link 'Sourcing / Open'
page.within('.nav-sidebar') do page.within('.nav-sidebar') do
click_link 'Settings' first(:link, text: 'Settings').click
end end
click_link 'Members' click_link 'Members'
end end
...@@ -47,10 +47,10 @@ class Spinach::Features::GroupsManagement < Spinach::FeatureSteps ...@@ -47,10 +47,10 @@ class Spinach::Features::GroupsManagement < Spinach::FeatureSteps
visit dashboard_groups_path visit dashboard_groups_path
click_link 'Sourcing' click_link 'Sourcing'
page.within '.nav-sidebar' do page.within '.nav-sidebar' do
find('a', text: 'Settings').trigger('click') first(:link, text: 'Settings').click
end end
page.within '.sidebar-top-level-items > .active' do page.within '.sidebar-top-level-items > .active' do
find('a', text: 'General').trigger('click') click_link 'General'
end end
end end
......
...@@ -37,7 +37,7 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps ...@@ -37,7 +37,7 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps
step 'I goto the Merge Requests page' do step 'I goto the Merge Requests page' do
page.within '.nav-sidebar' do page.within '.nav-sidebar' do
click_link "Merge Requests" first(:link, "Merge Requests").click
end end
end end
......
...@@ -218,7 +218,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps ...@@ -218,7 +218,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
# Wiki # Wiki
step 'I go to wiki page' do step 'I go to wiki page' do
click_link "Wiki" first(:link, "Wiki").click
expect(current_path).to eq project_wiki_path(@project, "home") expect(current_path).to eq project_wiki_path(@project, "home")
end end
......
...@@ -11,7 +11,7 @@ module SharedActiveTab ...@@ -11,7 +11,7 @@ module SharedActiveTab
end end
def ensure_active_sub_tab(content) def ensure_active_sub_tab(content)
expect(find('.sidebar-sub-level-items > li.active')).to have_content(content) expect(find('.sidebar-sub-level-items > li.active:not(.fly-out-top-item)')).to have_content(content)
end end
def ensure_active_sub_nav(content) def ensure_active_sub_nav(content)
...@@ -23,7 +23,7 @@ module SharedActiveTab ...@@ -23,7 +23,7 @@ module SharedActiveTab
end end
step 'no other sub tabs should be active' do step 'no other sub tabs should be active' do
expect(page).to have_selector('.sidebar-sub-level-items > li.active', count: 1) expect(page).to have_selector('.sidebar-sub-level-items > li.active:not(.fly-out-top-item)', count: 1)
end end
step 'no other sub navs should be active' do step 'no other sub navs should be active' do
......
...@@ -14,8 +14,8 @@ RSpec.describe 'admin active tab' do ...@@ -14,8 +14,8 @@ RSpec.describe 'admin active tab' do
shared_examples 'page has active sub tab' do |title| shared_examples 'page has active sub tab' do |title|
it "activates #{title} sub tab" do it "activates #{title} sub tab" do
expect(page).to have_selector('.sidebar-sub-level-items li.active', count: 1) expect(page).to have_selector('.sidebar-sub-level-items > li.active', count: 2)
expect(page.find('.sidebar-sub-level-items li.active')).to have_content(title) expect(page.all('.sidebar-sub-level-items > li.active')[1]).to have_content(title)
end end
end end
......
...@@ -71,7 +71,7 @@ feature 'Admin updates settings' do ...@@ -71,7 +71,7 @@ feature 'Admin updates settings' do
end end
scenario 'Change Slack Notifications Service template settings' do scenario 'Change Slack Notifications Service template settings' do
click_link 'Service Templates' first(:link, 'Service Templates').click
click_link 'Slack notifications' click_link 'Slack notifications'
fill_in 'Webhook', with: 'http://localhost' fill_in 'Webhook', with: 'http://localhost'
fill_in 'Username', with: 'test_user' fill_in 'Username', with: 'test_user'
......
...@@ -51,7 +51,7 @@ feature 'Groups > Members > Request access' do ...@@ -51,7 +51,7 @@ feature 'Groups > Members > Request access' do
expect(group.requesters.exists?(user_id: user)).to be_truthy expect(group.requesters.exists?(user_id: user)).to be_truthy
click_link 'Members' first(:link, 'Members').click
page.within('.content') do page.within('.content') do
expect(page).not_to have_content(user.name) expect(page).not_to have_content(user.name)
......
...@@ -32,6 +32,8 @@ describe('Fly out sidebar navigation', () => { ...@@ -32,6 +32,8 @@ describe('Fly out sidebar navigation', () => {
document.body.innerHTML = ''; document.body.innerHTML = '';
breakpointSize = 'lg'; breakpointSize = 'lg';
mousePos.length = 0; mousePos.length = 0;
setSidebar(null);
}); });
describe('calculateTop', () => { describe('calculateTop', () => {
...@@ -240,6 +242,32 @@ describe('Fly out sidebar navigation', () => { ...@@ -240,6 +242,32 @@ describe('Fly out sidebar navigation', () => {
).toBe('block'); ).toBe('block');
}); });
it('shows collapsed only sub-items if icon only sidebar', () => {
const subItems = el.querySelector('.sidebar-sub-level-items');
const sidebar = document.createElement('div');
sidebar.classList.add('sidebar-icons-only');
subItems.classList.add('is-fly-out-only');
setSidebar(sidebar);
showSubLevelItems(el);
expect(
el.querySelector('.sidebar-sub-level-items').style.display,
).toBe('block');
});
it('does not show collapsed only sub-items if icon only sidebar', () => {
const subItems = el.querySelector('.sidebar-sub-level-items');
subItems.classList.add('is-fly-out-only');
showSubLevelItems(el);
expect(
subItems.style.display,
).not.toBe('block');
});
it('sets transform of sub-items', () => { it('sets transform of sub-items', () => {
const subItems = el.querySelector('.sidebar-sub-level-items'); const subItems = el.querySelector('.sidebar-sub-level-items');
showSubLevelItems(el); showSubLevelItems(el);
...@@ -281,10 +309,6 @@ describe('Fly out sidebar navigation', () => { ...@@ -281,10 +309,6 @@ describe('Fly out sidebar navigation', () => {
}); });
describe('canShowActiveSubItems', () => { describe('canShowActiveSubItems', () => {
afterEach(() => {
setSidebar(null);
});
it('returns true by default', () => { it('returns true by default', () => {
expect( expect(
canShowActiveSubItems(el), canShowActiveSubItems(el),
......
...@@ -118,7 +118,7 @@ describe('Issue', function() { ...@@ -118,7 +118,7 @@ describe('Issue', function() {
this.$triggeredButton = $btn; this.$triggeredButton = $btn;
this.$projectIssuesCounter = $('.issue_counter'); this.$projectIssuesCounter = $('.issue_counter').first();
this.$projectIssuesCounter.text('1,001'); this.$projectIssuesCounter.text('1,001');
this.issueStateDeferred = new jQuery.Deferred(); this.issueStateDeferred = new jQuery.Deferred();
......
...@@ -27,7 +27,7 @@ describe 'layouts/nav/sidebar/_project' do ...@@ -27,7 +27,7 @@ describe 'layouts/nav/sidebar/_project' do
it 'highlights only one tab' do it 'highlights only one tab' do
render render
expect(rendered).to have_css('.active', count: 1) expect(rendered).to have_css('.active', count: 2)
end end
it 'highlights container registry tab only' do it 'highlights container registry tab only' 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