diff options
31 files changed, 646 insertions, 138 deletions
diff --git a/CHANGELOG b/CHANGELOG index f26570965e6..8ebd4addcbb 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,6 +10,7 @@ v 7.3.0 - Support Unix domain sockets for Redis - Store session Redis keys in 'session:gitlab:' namespace - Deprecate LDAP account takeover based on partial LDAP email / GitLab username match + - Keyboard shortcuts for productivity (Robert Schilling) v 7.2.0 - Explore page @@ -156,6 +156,9 @@ gem "rack-attack" # Ace editor gem 'ace-rails-ap' +# Keyboard shortcuts +gem 'mousetrap-rails' + # Semantic UI Sass for Sidebar gem 'semantic-ui-sass', '~> 0.16.1.0' diff --git a/Gemfile.lock b/Gemfile.lock index edd30fda37a..cee22969215 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -287,6 +287,7 @@ GEM mime-types (1.25.1) mini_portile (0.6.0) minitest (5.3.5) + mousetrap-rails (1.4.6) multi_json (1.10.1) multi_xml (0.5.5) multipart-post (1.2.0) @@ -636,6 +637,7 @@ DEPENDENCIES launchy letter_opener minitest (~> 5.3.0) + mousetrap-rails mysql2 nprogress-rails omniauth (~> 1.1.3) diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 6bc24cf7590..86ccd8c21ed 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -33,6 +33,12 @@ #= require nprogress-turbolinks #= require dropzone #= require semantic-ui/sidebar +#= require mousetrap +#= require shortcuts +#= require shortcuts_navigation +#= require shortcuts_dashboard_navigation +#= require shortcuts_issueable +#= require shortcuts_network #= require_tree . window.slugify = (text) -> @@ -119,6 +125,13 @@ $ -> # Initialize select2 selects $('select.select2').select2(width: 'resolve', dropdownAutoWidth: true) + # Close select2 on escape + $('.js-select2').bind 'select2-close', -> + setTimeout ( -> + $('.select2-container-active').removeClass('select2-container-active') + $(':focus').blur() + ), 1 + # Initialize tooltips $('.has_tooltip').tooltip() @@ -151,20 +164,6 @@ $ -> # Show/Hide the profile menu when hovering the account box $('.account-box').hover -> $(@).toggleClass('hover') - # Focus search field by pressing 's' key - $(document).keypress (e) -> - # Don't do anything if typing in an input - return if $(e.target).is(":input") - - switch e.which - when 115 - $("#search").focus() - e.preventDefault() - when 63 - new Shortcuts() - e.preventDefault() - - # Commit show suppressed diff $(".diff-content").on "click", ".supp_diff_link", -> $(@).next('table').show() diff --git a/app/assets/javascripts/branch-graph.js.coffee b/app/assets/javascripts/branch-graph.js.coffee index f6d57bd55bb..b8af07579f2 100644 --- a/app/assets/javascripts/branch-graph.js.coffee +++ b/app/assets/javascripts/branch-graph.js.coffee @@ -1,4 +1,4 @@ -class BranchGraph +class @BranchGraph constructor: (@element, @options) -> @preparedCommits = {} @mtime = 0 @@ -120,23 +120,32 @@ class BranchGraph @top.toFront() bindEvents: -> - drag = {} element = @element $(element).scroll (event) => @renderPartialGraph() - $(window).on - keydown: (event) => - # left - element.scrollLeft element.scrollLeft() - 50 if event.keyCode is 37 - # top - element.scrollTop element.scrollTop() - 50 if event.keyCode is 38 - # right - element.scrollLeft element.scrollLeft() + 50 if event.keyCode is 39 - # bottom - element.scrollTop element.scrollTop() + 50 if event.keyCode is 40 - @renderPartialGraph() + scrollDown: => + @element.scrollTop @element.scrollTop() + 50 + @renderPartialGraph() + + scrollUp: => + @element.scrollTop @element.scrollTop() - 50 + @renderPartialGraph() + + scrollLeft: => + @element.scrollLeft @element.scrollLeft() - 50 + @renderPartialGraph() + + scrollRight: => + @element.scrollLeft @element.scrollLeft() + 50 + @renderPartialGraph() + + scrollBottom: => + @element.scrollTop @element.find('svg').height() + + scrollTop: => + @element.scrollTop 0 appendLabel: (x, y, commit) -> return unless commit.refs @@ -325,5 +334,3 @@ Raphael::textWrap = (t, width) -> b = t.getBBox() h = Math.abs(b.y2) - Math.abs(b.y) + 1 t.attr y: b.y + h - -@BranchGraph = BranchGraph diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index e5e62c87e40..ae4cf577179 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -15,49 +15,83 @@ class Dispatcher return false path = page.split(':') + shortcut_handler = null switch page when 'projects:issues:index' Issues.init() + shortcut_handler = new ShortcutsNavigation() when 'projects:issues:show' new Issue() + shortcut_handler = new ShortcutsIssueable() when 'projects:milestones:show' new Milestone() when 'projects:issues:new' GitLab.GfmAutoComplete.setup() + shortcut_handler = new ShortcutsNavigation() when 'projects:merge_requests:new' GitLab.GfmAutoComplete.setup() new Diff() + shortcut_handler = new ShortcutsNavigation() when 'projects:merge_requests:show' new Diff() + shortcut_handler = new ShortcutsIssueable() when "projects:merge_requests:diffs" new Diff() + when 'projects:merge_requests:index' + shortcut_handler = new ShortcutsNavigation() when 'dashboard:show' new Dashboard() new Activities() when 'projects:commit:show' new Commit() new Diff() + shortcut_handler = new ShortcutsNavigation() + when 'projects:commits:show' + shortcut_handler = new ShortcutsNavigation() when 'groups:show', 'projects:show' new Activities() - when 'projects:new', 'projects:edit' + shortcut_handler = new ShortcutsNavigation() + when 'projects:new' new Project() + when 'projects:edit' + new Project() + shortcut_handler = new ShortcutsNavigation() when 'projects:teams:members:index' new TeamMembers() when 'groups:members' new GroupMembers() when 'projects:tree:show' new TreeView() + shortcut_handler = new ShortcutsNavigation() when 'projects:blob:show' new BlobView() + shortcut_handler = new ShortcutsNavigation() when 'projects:labels:new', 'projects:labels:edit' new Labels() + when 'projects:network:show' + # Ensure we don't create a particular shortcut handler here. This is + # already created, where the network graph is created. + shortcut_handler = true switch path.first() when 'admin' then new Admin() + when 'dashboard' + shortcut_handler = new ShortcutsDashboardNavigation() when 'projects' - new Wikis() if path[1] == 'wikis' + switch path[1] + when 'wikis' + new Wikis() + shortcut_handler = new ShortcutsNavigation() + when 'snippets', 'labels', 'graphs' + shortcut_handler = new ShortcutsNavigation() + when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches' + shortcut_handler = new ShortcutsNavigation() + + # If we haven't installed a custom shortcut handler, install the default one + if not shortcut_handler + new Shortcuts() initSearch: -> opts = $('.search-autocomplete-opts') diff --git a/app/assets/javascripts/network.js.coffee b/app/assets/javascripts/network.js.coffee index cea5986f45a..f4ef07a50a7 100644 --- a/app/assets/javascripts/network.js.coffee +++ b/app/assets/javascripts/network.js.coffee @@ -1,11 +1,9 @@ -class Network +class @Network constructor: (opts) -> $("#filter_ref").click -> $(this).closest('form').submit() - branch_graph = new BranchGraph($(".network-graph"), opts) + @branch_graph = new BranchGraph($(".network-graph"), opts) vph = $(window).height() - 250 $('.network-graph').css 'height': (vph + 'px') - -@Network = Network diff --git a/app/assets/javascripts/shortcuts.js.coffee b/app/assets/javascripts/shortcuts.js.coffee index e7e40a066ec..e9aeb1e9525 100644 --- a/app/assets/javascripts/shortcuts.js.coffee +++ b/app/assets/javascripts/shortcuts.js.coffee @@ -1,11 +1,30 @@ -class Shortcuts +class @Shortcuts constructor: -> + @enabledHelp = [] + Mousetrap.reset() + Mousetrap.bind('?', @selectiveHelp) + Mousetrap.bind('s', Shortcuts.focusSearch) + + selectiveHelp: (e) => + Shortcuts.showHelp(e, @enabledHelp) + + @showHelp: (e, location) -> if $('#modal-shortcuts').length > 0 $('#modal-shortcuts').modal('show') else $.ajax( url: '/help/shortcuts', - dataType: "script" + dataType: 'script', + success: (e) -> + if location and location.length > 0 + for l in location + $(l).show() + else + $('.hidden-shortcut').show() + $('.js-more-help-button').remove() ) + e.preventDefault() -@Shortcuts = Shortcuts + @focusSearch: (e) -> + $('#search').focus() + e.preventDefault() diff --git a/app/assets/javascripts/shortcuts_dashboard_navigation.js.coffee b/app/assets/javascripts/shortcuts_dashboard_navigation.js.coffee new file mode 100644 index 00000000000..d522d9f3b90 --- /dev/null +++ b/app/assets/javascripts/shortcuts_dashboard_navigation.js.coffee @@ -0,0 +1,14 @@ +#= require shortcuts + +class @ShortcutsDashboardNavigation extends Shortcuts + constructor: -> + super() + Mousetrap.bind('g a', -> ShortcutsDashboardNavigation.findAndollowLink('.shortcuts-activity')) + Mousetrap.bind('g p', -> ShortcutsDashboardNavigation.findAndollowLink('.shortcuts-projects')) + Mousetrap.bind('g i', -> ShortcutsDashboardNavigation.findAndollowLink('.shortcuts-issues')) + Mousetrap.bind('g m', -> ShortcutsDashboardNavigation.findAndollowLink('.shortcuts-merge_requests')) + + @findAndollowLink: (selector) -> + link = $(selector).attr('href') + if link + window.location = link diff --git a/app/assets/javascripts/shortcuts_issueable.coffee b/app/assets/javascripts/shortcuts_issueable.coffee new file mode 100644 index 00000000000..b8dae71e037 --- /dev/null +++ b/app/assets/javascripts/shortcuts_issueable.coffee @@ -0,0 +1,19 @@ +#= require shortcuts_navigation + +class @ShortcutsIssueable extends ShortcutsNavigation + constructor: (isMergeRequest) -> + super() + Mousetrap.bind('a', -> + $('.js-assignee').select2('open') + return false + ) + Mousetrap.bind('m', -> + $('.js-milestone').select2('open') + return false + ) + + if isMergeRequest + @enabledHelp.push('.hidden-shortcut.merge_reuests') + else + @enabledHelp.push('.hidden-shortcut.issues') + diff --git a/app/assets/javascripts/shortcuts_navigation.coffee b/app/assets/javascripts/shortcuts_navigation.coffee new file mode 100644 index 00000000000..e24a74ea9b6 --- /dev/null +++ b/app/assets/javascripts/shortcuts_navigation.coffee @@ -0,0 +1,20 @@ +#= require shortcuts + +class @ShortcutsNavigation extends Shortcuts + constructor: -> + super() + Mousetrap.bind('g a', -> ShortcutsNavigation.findAndollowLink('.shortcuts-activity')) + Mousetrap.bind('g f', -> ShortcutsNavigation.findAndollowLink('.shortcuts-tree')) + Mousetrap.bind('g c', -> ShortcutsNavigation.findAndollowLink('.shortcuts-commits')) + Mousetrap.bind('g n', -> ShortcutsNavigation.findAndollowLink('.shortcuts-network')) + Mousetrap.bind('g g', -> ShortcutsNavigation.findAndollowLink('.shortcuts-graphs')) + Mousetrap.bind('g i', -> ShortcutsNavigation.findAndollowLink('.shortcuts-issues')) + Mousetrap.bind('g m', -> ShortcutsNavigation.findAndollowLink('.shortcuts-merge_requests')) + Mousetrap.bind('g w', -> ShortcutsNavigation.findAndollowLink('.shortcuts-wiki')) + Mousetrap.bind('g s', -> ShortcutsNavigation.findAndollowLink('.shortcuts-snippets')) + @enabledHelp.push('.hidden-shortcut.project') + + @findAndollowLink: (selector) -> + link = $(selector).attr('href') + if link + window.location = link diff --git a/app/assets/javascripts/shortcuts_network.js.coffee b/app/assets/javascripts/shortcuts_network.js.coffee new file mode 100644 index 00000000000..cc95ad7ebfe --- /dev/null +++ b/app/assets/javascripts/shortcuts_network.js.coffee @@ -0,0 +1,12 @@ +#= require shortcuts_navigation + +class @ShortcutsNetwork extends ShortcutsNavigation + constructor: (@graph) -> + super() + Mousetrap.bind(['left', 'h'], @graph.scrollLeft) + Mousetrap.bind(['right', 'l'], @graph.scrollRight) + Mousetrap.bind(['up', 'k'], @graph.scrollUp) + Mousetrap.bind(['down', 'j'], @graph.scrollDown) + Mousetrap.bind(['shift+up', 'shift+k'], @graph.scrollTop) + Mousetrap.bind(['shift+down', 'shift+j'], @graph.scrollBottom) + @enabledHelp.push('.hidden-shortcut.network') diff --git a/app/assets/stylesheets/main/layout.scss b/app/assets/stylesheets/main/layout.scss index e28da65c01f..2800feb81f2 100644 --- a/app/assets/stylesheets/main/layout.scss +++ b/app/assets/stylesheets/main/layout.scss @@ -16,3 +16,4 @@ body { .container .content { margin: 0 0; } + diff --git a/app/assets/stylesheets/sections/help.scss b/app/assets/stylesheets/sections/help.scss index 90ed98ba25f..07c62f98c36 100644 --- a/app/assets/stylesheets/sections/help.scss +++ b/app/assets/stylesheets/sections/help.scss @@ -17,3 +17,56 @@ } } } + + +.shortcut-mappings { + font-size: 12px; + color: #555; + + tbody:first-child tr:first-child { + padding-top: 0 + } + + th { + padding-top: 15px; + font-size: 14px; + line-height: 1.5; + color: #333; + text-align: left + } + + td { + padding-top: 3px; + padding-bottom: 3px; + vertical-align: top; + line-height: 20px + } + + .shortcut { + padding-right: 10px; + color: #999; + text-align: right; + white-space: nowrap + } + + .key { + @extend .label; + @extend .label-inverse; + font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace; + padding: 3px 5px; + } +} + +.modal-body { + position: relative; + overflow-y: auto; + padding: 15px; +} + +body.modal-open { + overflow: hidden; +} + +.modal .modal-dialog { + width: 860px; +} diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml index 500e5dc65e1..4301a6eafc1 100644 --- a/app/views/help/_shortcuts.html.haml +++ b/app/views/help/_shortcuts.html.haml @@ -3,30 +3,207 @@ .modal-content .modal-header %a.close{href: "#", "data-dismiss" => "modal"} × - %h3 Keyboard Shortcuts - .modal-body - %h5 Global Shortcuts - %p - %span.label.label-inverse s - – - Focus Search - %p - %span.label.label-inverse ? - – - Show this dialog + %h4 + Keyboard Shortcuts + %small + = link_to '(Show all)', '#', class: 'js-more-help-button' + .modal-body.shortcuts-cheatsheet + .col-lg-4 + %table.shortcut-mappings + %tbody + %tr + %th + %th Global Shortcuts + %tr + %td.shortcut + .key s + %td Focus Search + %tr + %td.shortcut + .key ? + %td Show this dialog + %tbody + %tr + %th + %th Project Files browsing + %tr + %td.shortcut + .key + %i.icon-arrow-up + %td Move selection up + %tr + %td.shortcut + .key + %i.icon-arrow-down + %td Move selection down + %tr + %td.shortcut + .key enter + %td Open Selection - %h5 Project Files browsing - %p - %span.label.label-inverse - %i.icon-arrow-up - – - Move selection up - %p - %span.label.label-inverse - %i.icon-arrow-down - – - Move selection down - %p - %span.label.label-inverse Enter - – - Open selection + .col-lg-4 + %table.shortcut-mappings + %tbody{ class: 'hidden-shortcut project', style: 'display:none' } + %tr + %th + %th Global Dashboard + %tr + %td.shortcut + .key g + .key a + %td + Go to the activity feed + %tr + %td.shortcut + .key g + .key p + %td + Go to projects + %tr + %td.shortcut + .key g + .key i + %td + Go to issues + %tr + %td.shortcut + .key g + .key m + %td + Go to merge requests + %tbody + %tr + %th + %th Project + %tr + %td.shortcut + .key g + .key a + %td + Go to the activity feed + %tr + %td.shortcut + .key g + .key f + %td + Go to files + %tr + %td.shortcut + .key g + .key c + %td + Go to commits + %tr + %td.shortcut + .key g + .key n + %td + Go to network graph + %tr + %td.shortcut + .key g + .key g + %td + Go to graphs + %tr + %td.shortcut + .key g + .key i + %td + Go to issues + %tr + %td.shortcut + .key g + .key m + %td + Go to merge requests + %tr + %td.shortcut + .key g + .key s + %td + Go to snippets + .col-lg-4 + %table.shortcut-mappings + %tbody{ class: 'hidden-shortcut network', style: 'display:none' } + %tr + %th + %th Network Graph + %tr + %td.shortcut + .key + %i.icon-arrow-left + \/ + .key h + %td Scroll left + %tr + %td.shortcut + .key + %i.icon-arrow-right + \/ + .key l + %td Scroll right + %tr + %td.shortcut + .key + %i.icon-arrow-up + \/ + .key k + %td Scroll up + %tr + %td.shortcut + .key + %i.icon-arrow-down + \/ + .key j + %td Scroll down + %tr + %td.shortcut + .key + shift + %i.icon-arrow-up + \/ + .key + shift k + %td Scroll to top + %tr + %td.shortcut + .key + shift + %i.icon-arrow-down + \/ + .key + shift j + %td Scroll to bottom + %tbody{ class: 'hidden-shortcut issues', style: 'display:none' } + %tr + %th + %th Issues + %tr + %td.shortcut + .key a + %td Change assignee + %tr + %td.shortcut + .key m + %td Change milestone + %tbody{ class: 'hidden-shortcut merge_reuests', style: 'display:none' } + %tr + %th + %th Merge Requests + %tr + %td.shortcut + .key a + %td Change assignee + %tr + %td.shortcut + .key m + %td Change milestone + + +:javascript + $('.js-more-help-button').click(function(e){ + $(this).remove() + $('.hidden-shortcut').show() + e.preventDefault() + }); diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index 219693af09f..903e093e5fc 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -37,8 +37,8 @@ = link_to "getting help", "https://www.gitlab.com/getting-help/" %li Use the - = link_to "search bar", '#', onclick: "$('#search').focus();" + = link_to 'search bar', '#', onclick: 'Shortcuts.focusSearch(event)' on the top of this page %li Use - = link_to "shortcuts", '#', onclick: "new Shortcuts()" + = link_to 'shortcuts', '#', onclick: 'Shortcuts.showHelp(event)' diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index caf0e39234a..f485aee1e1a 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -8,3 +8,10 @@ = hidden_field_tag :repository_ref, @ref = submit_tag 'Go' if ENV['RAILS_ENV'] == 'test' .search-autocomplete-opts.hide{:'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref } + +:javascript + $('.search-input').on('keyup', function(e) { + if (e.keyCode == 27) { + $('.search-input').blur() + } + }) diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index a300bbc1904..a6e9772d93f 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -1,16 +1,16 @@ %ul = nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do - = link_to root_path, title: "Home" do + = link_to root_path, title: 'Home', class: 'shortcuts-activity' do Activity = nav_link(path: 'dashboard#projects') do - = link_to projects_dashboard_path do + = link_to projects_dashboard_path, class: 'shortcuts-projects' do Projects = nav_link(path: 'dashboard#issues') do - = link_to issues_dashboard_path do + = link_to issues_dashboard_path, class: 'shortcuts-issues' do Issues %span.count= current_user.assigned_issues.opened.count = nav_link(path: 'dashboard#merge_requests') do - = link_to merge_requests_dashboard_path do + = link_to merge_requests_dashboard_path, class: 'shortcuts-merge_requests' do Merge Requests %span.count= current_user.assigned_merge_requests.opened.count = nav_link(controller: :help) do diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 92ef7923714..b26bc797e63 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -1,44 +1,43 @@ -%ul +%ul.project-navigation = nav_link(path: 'projects#show', html_options: {class: "home"}) do - = link_to project_path(@project), title: "Project" do - Project - + = link_to project_path(@project), title: 'Project', class: 'shortcuts-activity' do + Activity - if project_nav_tab? :files = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do - = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref) + = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref), class: 'shortcuts-tree' - if project_nav_tab? :commits = nav_link(controller: %w(commit commits compare repositories tags branches)) do - = link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref) + = link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref), class: 'shortcuts-commits' - if project_nav_tab? :network = nav_link(controller: %w(network)) do - = link_to "Network", project_network_path(@project, @ref || @repository.root_ref) + = link_to "Network", project_network_path(@project, @ref || @repository.root_ref), class: 'shortcuts-network' - if project_nav_tab? :graphs = nav_link(controller: %w(graphs)) do - = link_to "Graphs", project_graph_path(@project, @ref || @repository.root_ref) + = link_to "Graphs", project_graph_path(@project, @ref || @repository.root_ref), class: 'shortcuts-graphs' - if project_nav_tab? :issues = nav_link(controller: %w(issues milestones labels)) do - = link_to url_for_project_issues do + = link_to url_for_project_issues, class: 'shortcuts-issues' do Issues - if @project.used_default_issues_tracker? %span.count.issue_counter= @project.issues.opened.count - if project_nav_tab? :merge_requests = nav_link(controller: :merge_requests) do - = link_to project_merge_requests_path(@project) do + = link_to project_merge_requests_path(@project), class: 'shortcuts-merge_requests' do Merge Requests %span.count.merge_counter= @project.merge_requests.opened.count - if project_nav_tab? :wiki = nav_link(controller: :wikis) do - = link_to 'Wiki', project_wiki_path(@project, :home) + = link_to 'Wiki', project_wiki_path(@project, :home), class: 'shortcuts-wiki' - if project_nav_tab? :snippets = nav_link(controller: :snippets) do - = link_to 'Snippets', project_snippets_path(@project) + = link_to 'Snippets', project_snippets_path(@project), class: 'shortcuts-snippets' - if project_nav_tab? :settings = nav_link(html_options: {class: "#{project_tab_class}"}) do diff --git a/app/views/projects/issues/_issue_context.html.haml b/app/views/projects/issues/_issue_context.html.haml index d7987f43fbb..8c3f0823386 100644 --- a/app/views/projects/issues/_issue_context.html.haml +++ b/app/views/projects/issues/_issue_context.html.haml @@ -5,7 +5,7 @@ Assignee: - if can?(current_user, :modify_issue, @issue) - = project_users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control', selected: @issue.assignee_id) + = project_users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id) - elsif issue.assignee = link_to_member(@project, @issue.assignee) - else @@ -15,7 +15,7 @@ %strong.append-right-10 Milestone: - if can?(current_user, :modify_issue, @issue) - = f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'select2 select2-compact'}) + = f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'}) = hidden_field_tag :issue_context = f.submit class: 'btn' - elsif issue.milestone diff --git a/app/views/projects/merge_requests/show/_context.html.haml b/app/views/projects/merge_requests/show/_context.html.haml index ab00b34242a..089302e3588 100644 --- a/app/views/projects/merge_requests/show/_context.html.haml +++ b/app/views/projects/merge_requests/show/_context.html.haml @@ -5,7 +5,7 @@ Assignee: - if can?(current_user, :modify_merge_request, @merge_request) - = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control', selected: @merge_request.assignee_id) + = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id) - elsif merge_request.assignee = link_to_member(@project, @merge_request.assignee) - else @@ -15,7 +15,7 @@ %strong.append-right-10 Milestone: - if can?(current_user, :modify_merge_request, @merge_request) - = f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact'}) + = f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'}) = hidden_field_tag :merge_request_context = f.submit class: 'btn' - elsif merge_request.milestone diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index 5310822823d..8356bef28b0 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -15,9 +15,10 @@ = spinner nil, true :javascript - new Network({ + network_graph = new Network({ url: '#{project_network_path(@project, @ref, @options.merge(format: :json))}', commit_url: '#{project_commit_path(@project, 'ae45ca32').gsub("ae45ca32", "%s")}', ref: '#{@ref}', commit_id: '#{@commit.id}' }) + new ShortcutsNetwork(network_graph.branch_graph) diff --git a/features/dashboard/shortcuts.feature b/features/dashboard/shortcuts.feature new file mode 100644 index 00000000000..7c25b3926c9 --- /dev/null +++ b/features/dashboard/shortcuts.feature @@ -0,0 +1,21 @@ +@dashboard +Feature: Dashboard shortcuts + Background: + Given I sign in as a user + And I visit dashboard page + + @javascript + Scenario: Navigate to projects tab + Given I press "g" and "p" + Then the active main tab should be Projects + + @javascript + Scenario: Navigate to issue tab + Given I press "g" and "i" + Then the active main tab should be Issues + + @javascript + Scenario: Navigate to merge requests tab + Given I press "g" and "m" + Then the active main tab should be Merge Requests + diff --git a/features/project/shortcuts.feature b/features/project/shortcuts.feature new file mode 100644 index 00000000000..16882fded8e --- /dev/null +++ b/features/project/shortcuts.feature @@ -0,0 +1,46 @@ +@dashboard +Feature: Project shortcuts + Background: + Given I sign in as a user + And I own a project + And I visit my project's home page + + @javascript + Scenario: Navigate to files tab + Given I press "g" and "f" + Then the active main tab should be Files + + @javascript + Scenario: Navigate to commits tab + Given I press "g" and "c" + Then the active main tab should be Commits + + @javascript + Scenario: Navigate to network tab + Given I press "g" and "n" + Then the active main tab should be Network + + @javascript + Scenario: Navigate to graphs tab + Given I press "g" and "g" + Then the active main tab should be Graphs + + @javascript + Scenario: Navigate to issues tab + Given I press "g" and "i" + Then the active main tab should be Issues + + @javascript + Scenario: Navigate to merge requests tab + Given I press "g" and "m" + Then the active main tab should be Merge Requests + + @javascript + Scenario: Navigate to snippets tab + Given I press "g" and "s" + Then the active main tab should be Snippets + + @javascript + Scenario: Navigate to wiki tab + Given I press "g" and "w" + Then the active main tab should be Wiki diff --git a/features/steps/dashboard/active_tab.rb b/features/steps/dashboard/active_tab.rb index 68d32ed971a..d5db3339df2 100644 --- a/features/steps/dashboard/active_tab.rb +++ b/features/steps/dashboard/active_tab.rb @@ -3,19 +3,7 @@ class DashboardActiveTab < Spinach::FeatureSteps include SharedPaths include SharedActiveTab - Then 'the active main tab should be Home' do - ensure_active_main_tab('Activity') - end - - Then 'the active main tab should be Issues' do - ensure_active_main_tab('Issues') - end - - Then 'the active main tab should be Merge Requests' do - ensure_active_main_tab('Merge Requests') - end - - Then 'the active main tab should be Help' do + step 'the active main tab should be Help' do ensure_active_main_tab('Help') end end diff --git a/features/steps/dashboard/shortcuts.rb b/features/steps/dashboard/shortcuts.rb new file mode 100644 index 00000000000..d4484e7a20f --- /dev/null +++ b/features/steps/dashboard/shortcuts.rb @@ -0,0 +1,6 @@ +class DashboardShortcuts < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedProject + include SharedActiveTab +end diff --git a/features/steps/project/active_tab.rb b/features/steps/project/active_tab.rb index e39c0b65b91..2862256e03b 100644 --- a/features/steps/project/active_tab.rb +++ b/features/steps/project/active_tab.rb @@ -3,44 +3,7 @@ class ProjectActiveTab < Spinach::FeatureSteps include SharedPaths include SharedProject include SharedActiveTab - - # Main Tabs - - Then 'the active main tab should be Home' do - ensure_active_main_tab('Project') - end - - Then 'the active main tab should be Settings' do - ensure_active_main_tab('Settings') - end - - Then 'the active main tab should be Files' do - ensure_active_main_tab('Files') - end - - Then 'the active main tab should be Commits' do - ensure_active_main_tab('Commits') - end - - Then 'the active main tab should be Network' do - ensure_active_main_tab('Network') - end - - Then 'the active main tab should be Issues' do - ensure_active_main_tab('Issues') - end - - Then 'the active main tab should be Merge Requests' do - ensure_active_main_tab('Merge Requests') - end - - Then 'the active main tab should be Wall' do - ensure_active_main_tab('Wall') - end - - Then 'the active main tab should be Wiki' do - ensure_active_main_tab('Wiki') - end + include SharedProjectTab # Sub Tabs: Home diff --git a/features/steps/project/project_shortcuts.rb b/features/steps/project/project_shortcuts.rb new file mode 100644 index 00000000000..ce6e21a4258 --- /dev/null +++ b/features/steps/project/project_shortcuts.rb @@ -0,0 +1,36 @@ +class ProjectShortcuts < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedProject + include SharedProjectTab + + step 'I press "g" and "f"' do + find('body').native.send_key('g') + find('body').native.send_key('f') + end + + step 'I press "g" and "c"' do + find('body').native.send_key('g') + find('body').native.send_key('c') + end + + step 'I press "g" and "n"' do + find('body').native.send_key('g') + find('body').native.send_key('n') + end + + step 'I press "g" and "g"' do + find('body').native.send_key('g') + find('body').native.send_key('g') + end + + step 'I press "g" and "s"' do + find('body').native.send_key('g') + find('body').native.send_key('s') + end + + step 'I press "g" and "w"' do + find('body').native.send_key('g') + find('body').native.send_key('w') + end +end diff --git a/features/steps/shared/active_tab.rb b/features/steps/shared/active_tab.rb index e3cd5fcfe85..c776af14e04 100644 --- a/features/steps/shared/active_tab.rb +++ b/features/steps/shared/active_tab.rb @@ -24,4 +24,24 @@ module SharedActiveTab And 'no other sub navs should be active' do page.should have_selector('div.content ul.nav-stacked-menu li.active', count: 1) end + + step 'the active main tab should be Home' do + ensure_active_main_tab('Activity') + end + + step 'the active main tab should be Projects' do + ensure_active_main_tab('Projects') + end + + step 'the active main tab should be Issues' do + ensure_active_main_tab('Issues') + end + + step 'the active main tab should be Merge Requests' do + ensure_active_main_tab('Merge Requests') + end + + step 'the active main tab should be Help' do + ensure_active_main_tab('Help') + end end diff --git a/features/steps/shared/project_tab.rb b/features/steps/shared/project_tab.rb new file mode 100644 index 00000000000..00630da83a3 --- /dev/null +++ b/features/steps/shared/project_tab.rb @@ -0,0 +1,44 @@ +module SharedProjectTab + include Spinach::DSL + include SharedActiveTab + + step 'the active main tab should be Home' do + ensure_active_main_tab('Activity') + end + + step 'the active main tab should be Files' do + ensure_active_main_tab('Files') + end + + step 'the active main tab should be Commits' do + ensure_active_main_tab('Commits') + end + + step 'the active main tab should be Network' do + ensure_active_main_tab('Network') + end + + step 'the active main tab should be Graphs' do + ensure_active_main_tab('Graphs') + end + + step 'the active main tab should be Issues' do + ensure_active_main_tab('Issues') + end + + step 'the active main tab should be Merge Requests' do + ensure_active_main_tab('Merge Requests') + end + + step 'the active main tab should be Snippets' do + ensure_active_main_tab('Snippets') + end + + step 'the active main tab should be Wiki' do + ensure_active_main_tab('Wiki') + end + + step 'the active main tab should be Settings' do + ensure_active_main_tab('Settings') + end +end diff --git a/features/steps/shared/shortcuts.rb b/features/steps/shared/shortcuts.rb new file mode 100644 index 00000000000..bbb7afec0ad --- /dev/null +++ b/features/steps/shared/shortcuts.rb @@ -0,0 +1,18 @@ +module SharedActiveTab + include Spinach::DSL + + step 'I press "g" and "p"' do + find('body').native.send_key('g') + find('body').native.send_key('p') + end + + step 'I press "g" and "i"' do + find('body').native.send_key('g') + find('body').native.send_key('i') + end + + step 'I press "g" and "m"' do + find('body').native.send_key('g') + find('body').native.send_key('m') + end +end |