From 004f3a0bc2e3c85518a2a95db68945b47acc14c7 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Sat, 18 Mar 2017 08:01:00 -0400 Subject: [PATCH] Activate group name toggle based on horizontal space --- app/assets/javascripts/group_name.js | 42 +++++++++++--- app/assets/stylesheets/framework/header.scss | 56 +++++++++---------- app/assets/stylesheets/pages/commits.scss | 4 +- app/assets/stylesheets/pages/groups.scss | 7 +++ app/views/layouts/header/_default.html.haml | 13 +++-- app/views/shared/_group_form.html.haml | 4 +- .../features/groups/group_name_toggle_spec.rb | 55 ++++++++++-------- .../merge_requests/toggler_behavior_spec.rb | 2 +- 8 files changed, 110 insertions(+), 73 deletions(-) diff --git a/app/assets/javascripts/group_name.js b/app/assets/javascripts/group_name.js index 6a028f299b1..62675d7e67e 100644 --- a/app/assets/javascripts/group_name.js +++ b/app/assets/javascripts/group_name.js @@ -1,40 +1,64 @@ -const GROUP_LIMIT = 2; + +import _ from 'underscore'; export default class GroupName { constructor() { - this.titleContainer = document.querySelector('.title'); - this.groups = document.querySelectorAll('.group-path'); + this.titleContainer = document.querySelector('.title-container'); + this.title = document.querySelector('.title'); + this.titleWidth = this.title.offsetWidth; this.groupTitle = document.querySelector('.group-title'); + this.groups = document.querySelectorAll('.group-path'); this.toggle = null; this.isHidden = false; this.init(); } init() { - if (this.groups.length > GROUP_LIMIT) { + if (this.groups.length > 0) { this.groups[this.groups.length - 1].classList.remove('hidable'); - this.addToggle(); + this.toggleHandler(); + window.addEventListener('resize', _.debounce(this.toggleHandler.bind(this), 100)); } this.render(); } - addToggle() { - const header = document.querySelector('.header-content'); + toggleHandler() { + if (this.titleWidth > this.titleContainer.offsetWidth) { + if (!this.toggle) this.createToggle(); + this.showToggle(); + } else if (this.toggle) { + this.hideToggle(); + } + } + + createToggle() { this.toggle = document.createElement('button'); this.toggle.className = 'text-expander group-name-toggle'; this.toggle.setAttribute('aria-label', 'Toggle full path'); this.toggle.innerHTML = '...'; this.toggle.addEventListener('click', this.toggleGroups.bind(this)); - header.insertBefore(this.toggle, this.titleContainer); + this.titleContainer.insertBefore(this.toggle, this.title); this.toggleGroups(); } + showToggle() { + this.title.classList.add('wrap'); + this.toggle.classList.remove('hidden'); + if (this.isHidden) this.groupTitle.classList.add('is-hidden'); + } + + hideToggle() { + this.title.classList.remove('wrap'); + this.toggle.classList.add('hidden'); + if (this.isHidden) this.groupTitle.classList.remove('is-hidden'); + } + toggleGroups() { this.isHidden = !this.isHidden; this.groupTitle.classList.toggle('is-hidden'); } render() { - this.titleContainer.classList.remove('initializing'); + this.title.classList.remove('initializing'); } } diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 6660a022260..b6b52325088 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -26,7 +26,7 @@ header { padding: 0 16px; z-index: 100; margin-bottom: 0; - height: $header-height; + min-height: $header-height; background-color: $gray-light; border: none; border-bottom: 1px solid $border-color; @@ -85,7 +85,7 @@ header { .navbar-toggle { color: $nav-toggle-gray; - margin: 6px 0; + margin: 7px 0; border-radius: 0; position: absolute; right: -10px; @@ -135,12 +135,14 @@ header { } .header-content { + display: flex; + justify-content: space-between; position: relative; - height: $header-height; + min-height: $header-height; padding-left: 30px; - @media (min-width: $screen-sm-min) { - padding-right: 0; + @media (max-width: $screen-sm-max) { + padding-right: 20px; } .dropdown-menu { @@ -165,8 +167,7 @@ header { } .group-name-toggle { - margin: 0 5px; - vertical-align: sub; + margin: 3px 5px; } .group-title { @@ -177,39 +178,32 @@ header { } } + .title-container { + display: flex; + align-items: flex-start; + flex: 1 1 auto; + padding-top: (($header-height - 19) / 2); + overflow: hidden; + } + .title { position: relative; padding-right: 20px; margin: 0; font-size: 18px; - max-width: 385px; + line-height: 22px; display: inline-block; - line-height: $header-height; font-weight: normal; color: $gl-text-color; - overflow: hidden; - text-overflow: ellipsis; vertical-align: top; white-space: nowrap; + &.wrap { + white-space: normal; + } + &.initializing { - display: none; - } - - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - max-width: 300px; - } - - @media (max-width: $screen-xs-max) { - max-width: 190px; - } - - @media (min-width: $screen-sm-min) and (max-width: $screen-md-max) { - max-width: 428px; - } - - @media (min-width: $screen-lg-min) { - max-width: 685px; + opacity: 0; } a { @@ -226,10 +220,10 @@ header { border: transparent; background: transparent; position: absolute; + top: 2px; right: 3px; width: 12px; line-height: 19px; - margin-top: (($header-height - 19) / 2); padding: 0; font-size: 10px; text-align: center; @@ -247,7 +241,7 @@ header { } .navbar-collapse { - float: right; + flex: 0 0 auto; border-top: none; @media (min-width: $screen-md-min) { @@ -255,7 +249,7 @@ header { } @media (max-width: $screen-xs-max) { - float: none; + flex: 1 1 auto; } } } diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index da8410eca66..0dad91ba128 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -142,7 +142,9 @@ border: 1px solid $border-gray-dark; border-radius: $border-radius-default; margin-left: 5px; - line-height: 1; + font-size: $gl-font-size; + line-height: $gl-font-size; + outline: none; &:hover { background-color: darken($gray-light, 10%); diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index 84d21e48463..cf45f0af2aa 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -9,6 +9,13 @@ } } +.group-root-path { + max-width: 40vw; + overflow: hidden; + text-overflow: ellipsis; + word-wrap: nowrap; +} + .group-row { .stats { float: right; diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 5fde5c2613e..c2396a214df 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -15,6 +15,13 @@ %span.sr-only Toggle navigation = icon('ellipsis-v') + .header-logo + = link_to root_path, class: 'home', title: 'Dashboard', id: 'logo' do + = brand_header_logo + + .title-container + %h1.title{ class: ('initializing' if @has_group_title) }= title + .navbar-collapse.collapse %ul.nav.navbar-nav %li.hidden-sm.hidden-xs @@ -63,12 +70,6 @@ %div = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success' - .header-logo - = link_to root_path, class: 'home', title: 'Dashboard', id: 'logo' do - = brand_header_logo - - %h1.title{ class: ('initializing' if @has_group_title) }= title - = yield :header_content = render 'shared/outdated_browser' diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml index c2d9ac87b20..7974eb67f0f 100644 --- a/app/views/shared/_group_form.html.haml +++ b/app/views/shared/_group_form.html.haml @@ -1,4 +1,6 @@ - parent = Group.find_by(id: params[:parent_id] || @group.parent_id) +- group_path = root_url +- group_path << parent.full_path + '/' if parent - if @group.persisted? .form-group = f.label :name, class: 'control-label' do @@ -11,7 +13,7 @@ Group path .col-sm-10 .input-group.gl-field-error-anchor - .input-group-addon + .group-root-path.input-group-addon.has-tooltip{ title: group_path, :'data-placement' => 'bottom' } %span>= root_url - if parent %strong= parent.full_path + '/' diff --git a/spec/features/groups/group_name_toggle_spec.rb b/spec/features/groups/group_name_toggle_spec.rb index 8528718a2f7..8a1d415c4f1 100644 --- a/spec/features/groups/group_name_toggle_spec.rb +++ b/spec/features/groups/group_name_toggle_spec.rb @@ -6,39 +6,46 @@ feature 'Group name toggle', feature: true, js: true do let(:nested_group_2) { create(:group, parent: nested_group_1) } let(:nested_group_3) { create(:group, parent: nested_group_2) } + SMALL_SCREEN = 300 + before do login_as :user end - it 'is not present for less than 3 groups' do - visit group_path(group) - expect(page).not_to have_css('.group-name-toggle') - - visit group_path(nested_group_1) - expect(page).not_to have_css('.group-name-toggle') - end - - it 'is present for nested group of 3 or more in the namespace' do - visit group_path(nested_group_2) - expect(page).to have_css('.group-name-toggle') - + it 'is not present if enough horizontal space' do visit group_path(nested_group_3) - expect(page).to have_css('.group-name-toggle') + + container_width = page.evaluate_script("$('.title-container')[0].offsetWidth") + title_width = page.evaluate_script("$('.title')[0].offsetWidth") + + expect(container_width).to be > title_width + expect(page).not_to have_css('.group-name-toggle') end - context 'for group with at least 3 groups' do - before do - visit group_path(nested_group_2) - end + it 'is present if the title is longer than the container' do + visit group_path(nested_group_3) + title_width = page.evaluate_script("$('.title')[0].offsetWidth") - it 'should show the full group namespace when toggled' do - expect(page).not_to have_content(group.name) - expect(page).to have_css('.group-path.hidable', visible: false) + page_height = page.current_window.size[1] + page.current_window.resize_to(SMALL_SCREEN, page_height) - click_button '...' + find('.group-name-toggle') + container_width = page.evaluate_script("$('.title-container')[0].offsetWidth") - expect(page).to have_content(group.name) - expect(page).to have_css('.group-path.hidable', visible: true) - end + expect(title_width).to be > container_width + end + + it 'should show the full group namespace when toggled' do + page_height = page.current_window.size[1] + page.current_window.resize_to(SMALL_SCREEN, page_height) + visit group_path(nested_group_3) + + expect(page).not_to have_content(group.name) + expect(page).to have_css('.group-path.hidable', visible: false) + + click_button '...' + + expect(page).to have_content(group.name) + expect(page).to have_css('.group-path.hidable', visible: true) end end diff --git a/spec/features/merge_requests/toggler_behavior_spec.rb b/spec/features/merge_requests/toggler_behavior_spec.rb index a2cf9b18bf2..3acd3f6a8b3 100644 --- a/spec/features/merge_requests/toggler_behavior_spec.rb +++ b/spec/features/merge_requests/toggler_behavior_spec.rb @@ -18,7 +18,7 @@ feature 'toggler_behavior', js: true, feature: true do it 'should be scrolled down to fragment' do page_height = page.current_window.size[1] page_scroll_y = page.evaluate_script("window.scrollY") - fragment_position_top = page.evaluate_script("$('#{fragment_id}').offset().top") + fragment_position_top = page.evaluate_script("Math.round($('#{fragment_id}').offset().top)") expect(find('.js-toggle-content').visible?).to eq true expect(find(fragment_id).visible?).to eq true expect(fragment_position_top).to be >= page_scroll_y