diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index d1a662459e1..80490052389 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -33,6 +33,8 @@ /* global ProjectShow */ /* global Labels */ /* global Shortcuts */ +/* global Sidebar */ + import Issue from './issue'; import BindInOut from './behaviors/bind_in_out'; @@ -118,6 +120,7 @@ const ShortcutsBlob = require('./shortcuts_blob'); case 'groups:milestones:show': case 'dashboard:milestones:show': new Milestone(); + new Sidebar(); break; case 'dashboard:todos:index': new gl.Todos(); diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index efbd9365fd9..335e587b8f4 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -52,67 +52,63 @@ } } -.milestone-summary { +.milestone-sidebar { + .gutter-toggle { + margin-bottom: 10px; + } + + .milestone-progress { + .title { + padding-top: 5px; + } + + .progress { + height: 6px; + margin: 0; + } + } + + .collapsed-milestone-date { + font-size: 12px; + } + + .milestone-date { + display: block; + } + + .date-separator { + line-height: 5px; + } + + .remaining-days strong { + font-weight: normal; + } + .milestone-stat { - white-space: nowrap; - margin-right: 10px; - - &.with-drilldown { - margin-right: 2px; - } + float: left; + margin-right: 14px; } - .remaining-days { - color: $orange-600; + .milestone-stat:last-child { + margin-right: 0; } - .milestone-stats-and-buttons { - display: flex; - justify-content: flex-start; - flex-wrap: wrap; + .milestone-progress { + .sidebar-collapsed-icon { + clear: both; + padding: 15px 5px 5px; - @media (min-width: $screen-xs-min) { - justify-content: space-between; - flex-wrap: nowrap; - } - } - - .milestone-progress-buttons { - order: 1; - margin-top: 10px; - - @media (min-width: $screen-xs-min) { - order: 2; - margin-top: 0; - flex-shrink: 0; - } - - .btn { - float: left; - margin-right: $btn-side-margin; - - &:last-child { - margin-right: 0; + .progress { + margin: 5px 0; } } } - .milestone-stats { - order: 2; - width: 100%; - padding: 7px 0; - flex-shrink: 1; - - @media (min-width: $screen-xs-min) { - // when displayed on one line stats go first, buttons second - order: 1; + .right-sidebar-collapsed & { + .reference { + border-top: 1px solid $border-gray-normal; } } - - .progress { - width: 100%; - margin: 15px 0; - } } .issues-sortable-list, diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb index bd3f51fc658..c9e70faa52e 100644 --- a/app/helpers/milestones_helper.rb +++ b/app/helpers/milestones_helper.rb @@ -19,8 +19,8 @@ module MilestonesHelper end end - def milestones_browse_issuables_path(milestone, type:) - opts = { milestone_title: milestone.title } + def milestones_browse_issuables_path(milestone, state: nil, type:) + opts = { milestone_title: milestone.title, state: state } if @project polymorphic_path([@project.namespace.becomes(Namespace), @project, type], opts) diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index 991fd949b94..17bfd07e00f 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -6,7 +6,8 @@ module NavHelper current_path?('merge_requests#builds') || current_path?('merge_requests#conflicts') || current_path?('merge_requests#pipelines') || - current_path?('issues#show') + current_path?('issues#show') || + current_path?('milestones#show') if cookies[:collapsed_gutter] == 'true' "page-gutter right-sidebar-collapsed" else diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml index 60c84a26420..2129920afd2 100644 --- a/app/views/dashboard/milestones/show.html.haml +++ b/app/views/dashboard/milestones/show.html.haml @@ -1,5 +1,5 @@ - header_title "Milestones", dashboard_milestones_path = render 'shared/milestones/top', milestone: @milestone -= render 'shared/milestones/summary', milestone: @milestone = render 'shared/milestones/tabs', milestone: @milestone, show_full_project_name: true += render 'shared/milestones/sidebar', milestone: @milestone, affix_offset: 51 diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index e66a8e0a3b3..8e83b2002b2 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -4,5 +4,5 @@ = page_specific_javascript_bundle_tag('simulate_drag') if Rails.env.test? = render 'shared/milestones/top', milestone: @milestone, group: @group -= render 'shared/milestones/summary', milestone: @milestone = render 'shared/milestones/tabs', milestone: @milestone, show_project_name: true += render 'shared/milestones/sidebar', milestone: @milestone, affix_offset: 102 diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index d16f49bd33a..f612b5c7d6b 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -36,6 +36,9 @@ = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-danger" do Delete + %a.btn.btn-default.btn-grouped.pull-right.visible-xs-block.js-sidebar-toggle{ href: "#" } + = icon('angle-double-left') + .detail-page-description.milestone-detail{ class: ('hide-bottom-border' unless @milestone.description.present? ) } %h2.title = markdown_field(@milestone, :title) @@ -53,5 +56,5 @@ .alert.alert-success.prepend-top-default %span All issues for this milestone are closed. You may close this milestone now. - = render 'shared/milestones/summary', milestone: @milestone, project: @project = render 'shared/milestones/tabs', milestone: @milestone + = render 'shared/milestones/sidebar', milestone: @milestone, project: @project, affix_offset: 153 diff --git a/app/views/shared/milestones/_sidebar.html.haml b/app/views/shared/milestones/_sidebar.html.haml new file mode 100644 index 00000000000..2810f1377b2 --- /dev/null +++ b/app/views/shared/milestones/_sidebar.html.haml @@ -0,0 +1,131 @@ +- affix_offset = local_assigns.fetch(:affix_offset, "102") +- project = local_assigns[:project] + +%aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => affix_offset, "spy" => "affix" }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' } + .issuable-sidebar.milestone-sidebar + .block.milestone-progress.issuable-sidebar-header + %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", "aria-label" => "Toggle sidebar" } + = sidebar_gutter_toggle_icon + + .sidebar-collapsed-icon + %span== #{milestone.percent_complete(current_user)}% + = milestone_progress_bar(milestone) + .title.hide-collapsed + %strong.bold== #{milestone.percent_complete(current_user)}% + %span.hide-collapsed + complete + .value.hide-collapsed + = milestone_progress_bar(milestone) + + .block.start_date.hide-collapsed + .title + Start date + - if @project && can?(current_user, :admin_milestone, @project) + = link_to 'Edit', edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: 'edit-link pull-right' + .value + %span.value-content + - if milestone.start_date + %span.bold= milestone.start_date.to_s(:medium) + - else + %span.no-value No start date + + .block.due_date + .sidebar-collapsed-icon + = icon('calendar', 'aria-hidden': 'true') + %span.collapsed-milestone-date + - if milestone.start_date && milestone.due_date + - if milestone.start_date.year == milestone.due_date.year + .milestone-date= milestone.start_date.strftime('%b %-d') + - else + .milestone-date= milestone.start_date.strftime('%b %-d %Y') + .date-separator - + .due_date= milestone.due_date.strftime('%b %-d %Y') + - elsif milestone.start_date + From + .milestone-date= milestone.start_date.strftime('%b %-d %Y') + - elsif milestone.due_date + Until + .milestone-date= milestone.due_date.strftime('%b %-d %Y') + - else + None + .title.hide-collapsed + Due date + - if @project && can?(current_user, :admin_milestone, @project) + = link_to 'Edit', edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: 'edit-link pull-right' + .value.hide-collapsed + %span.value-content + - if milestone.due_date + %span.bold= milestone.due_date.to_s(:medium) + - else + %span.no-value No due date + - remaining_days = milestone_remaining_days(milestone) + - if remaining_days.present? + = surround '(', ')' do + %span.remaining-days= remaining_days + + - if !project || can?(current_user, :read_issue, project) + .block + .sidebar-collapsed-icon + %strong + = icon('hashtag', 'aria-hidden': 'true') + %span= milestone.issues_visible_to_user(current_user).count + .title.hide-collapsed + Issues + %span.badge= milestone.issues_visible_to_user(current_user).count + - if project && can?(current_user, :create_issue, project) + = link_to new_namespace_project_issue_path(project.namespace, project, issue: { milestone_id: milestone.id }), class: "pull-right", title: "New Issue" do + New issue + .value.hide-collapsed.bold + %span.milestone-stat + = link_to milestones_browse_issuables_path(milestone, type: :issues) do + Open: + = milestone.issues_visible_to_user(current_user).opened.count + %span.milestone-stat + = link_to milestones_browse_issuables_path(milestone, type: :issues, state: 'closed') do + Closed: + = milestone.issues_visible_to_user(current_user).closed.count + + .block + .sidebar-collapsed-icon + %strong + = icon('exclamation', 'aria-hidden': 'true') + %span= milestone.issues_visible_to_user(current_user).count + .title.hide-collapsed + Merge requests + %span.badge= milestone.merge_requests.count + .value.hide-collapsed.bold + - if !project || can?(current_user, :read_merge_request, project) + %span.milestone-stat + = link_to milestones_browse_issuables_path(milestone, type: :merge_requests) do + Open: + = milestone.merge_requests.opened.count + %span.milestone-stat + = link_to milestones_browse_issuables_path(milestone, type: :merge_requests, state: 'closed') do + Closed: + = milestone.merge_requests.closed.count + %span.milestone-stat + = link_to milestones_browse_issuables_path(milestone, type: :merge_requests, state: 'merged') do + Merged: + = milestone.merge_requests.merged.count + - else + %span.milestone-stat + Open: + = milestone.merge_requests.opened.count + %span.milestone-stat + Closed: + = milestone.merge_requests.closed.count + %span.milestone-stat + Merged: + = milestone.merge_requests.merged.count + + - milestone_ref = milestone.try(:to_reference, full: true) + - if milestone_ref.present? + .block.reference + .sidebar-collapsed-icon.dont-change-state + = clipboard_button(clipboard_text: milestone_ref, title: "Copy reference to clipboard", placement: "left") + .cross-project-reference.hide-collapsed + %span + Reference: + %cite{ title: milestone_ref } + = milestone_ref + = clipboard_button(clipboard_text: milestone_ref, title: "Copy reference to clipboard", placement: "left") diff --git a/app/views/shared/milestones/_summary.html.haml b/app/views/shared/milestones/_summary.html.haml deleted file mode 100644 index 78079f633d5..00000000000 --- a/app/views/shared/milestones/_summary.html.haml +++ /dev/null @@ -1,45 +0,0 @@ -- project = local_assigns[:project] - -.context.prepend-top-default - .milestone-summary - %h4 Progress - - .milestone-stats-and-buttons - .milestone-stats - - if !project || can?(current_user, :read_issue, project) - %span.milestone-stat.with-drilldown - %strong= milestone.issues_visible_to_user(current_user).size - issues: - %span.milestone-stat - %strong= milestone.issues_visible_to_user(current_user).opened.size - open and - %strong= milestone.issues_visible_to_user(current_user).closed.size - closed - %span.milestone-stat.with-drilldown - %strong= milestone.merge_requests.size - merge requests: - %span.milestone-stat - %strong= milestone.merge_requests.opened.size - open and - %strong= milestone.merge_requests.merged.size - merged - %span.milestone-stat - %strong== #{milestone.percent_complete(current_user)}% - complete - - remaining_days = milestone_remaining_days(milestone) - - if remaining_days.present? - %span.milestone-stat - %span.remaining-days= remaining_days - - .milestone-progress-buttons - %span.tab-issues-buttons - - if project - - if can?(current_user, :create_issue, project) - = link_to new_namespace_project_issue_path(project.namespace, project, issue: { milestone_id: milestone.id }), class: "btn", title: "New Issue" do - New Issue - - if can?(current_user, :read_issue, project) - = link_to 'Browse Issues', milestones_browse_issuables_path(milestone, type: :issues), class: "btn" - %span.tab-merge-requests-buttons.hidden - = link_to 'Browse Merge Requests', milestones_browse_issuables_path(milestone, type: :merge_requests), class: "btn" - - = milestone_progress_bar(milestone) diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index 497446c1ef3..2562f085338 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -3,6 +3,9 @@ - group = local_assigns[:group] .detail-page-header + %a.btn.btn-default.btn-grouped.pull-right.visible-xs-block.js-sidebar-toggle{ href: "#" } + = icon('angle-double-left') + .status-box{ class: "status-box-#{milestone.closed? ? 'closed' : 'open'}" } - if milestone.closed? Closed diff --git a/changelogs/unreleased/23674-simplify-milestone-summary.yml b/changelogs/unreleased/23674-simplify-milestone-summary.yml new file mode 100644 index 00000000000..7a315c25151 --- /dev/null +++ b/changelogs/unreleased/23674-simplify-milestone-summary.yml @@ -0,0 +1,4 @@ +--- +title: Move milestone summary content into the sidebar +merge_request: 10096 +author: diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb index 20204ad8654..9996f3baf0d 100644 --- a/features/steps/group/milestones.rb +++ b/features/steps/group/milestones.rb @@ -36,7 +36,7 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps step 'I should see group milestone with all issues and MRs assigned to that milestone' do expect(page).to have_content('Milestone GL-113') - expect(page).to have_content('3 issues: 3 open and 0 closed') + expect(page).to have_content('Issues 3 Open: 3 Closed: 0') issue = Milestone.find_by(name: 'GL-113').issues.first expect(page).to have_link(issue.title, href: namespace_project_issue_path(issue.project.namespace, issue.project, issue)) end diff --git a/spec/features/projects/milestones/milestone_spec.rb b/spec/features/projects/milestones/milestone_spec.rb index df229d0aa78..dab78fd3571 100644 --- a/spec/features/projects/milestones/milestone_spec.rb +++ b/spec/features/projects/milestones/milestone_spec.rb @@ -23,12 +23,14 @@ feature 'Project milestone', :feature do end it 'shows issues stats' do - expect(page).to have_content 'issues:' + expect(find('.milestone-sidebar')).to have_content 'Issues 0' end - it 'shows Browse Issues button' do - within('#content-body') do - expect(page).to have_link 'Browse Issues' + it 'shows link to browse and add issues' do + within('.milestone-sidebar') do + expect(page).to have_link 'New issue' + expect(page).to have_link 'Open: 0' + expect(page).to have_link 'Closed: 0' end end end @@ -48,12 +50,12 @@ feature 'Project milestone', :feature do end it 'hides issues stats' do - expect(page).to have_no_content 'issues:' + expect(find('.milestone-sidebar')).not_to have_content 'Issues 0' end - it 'hides Browse Issues button' do - within('#content-body') do - expect(page).not_to have_link 'Browse Issues' + it 'hides new issue button' do + within('.milestone-sidebar') do + expect(page).not_to have_link 'New issue' end end