Commit c7a4a3f9 authored by Takuya Noguchi's avatar Takuya Noguchi

Reduce page_bundle for TODO list

Improve user experience in terms of performance

Changelog: fixed
Co-authored-by: default avatarPhil Hughes <me@iamphill.com>
Signed-off-by: default avatarTakuya Noguchi <takninnovationresearch@gmail.com>
parent 7368e2a6
...@@ -8,8 +8,6 @@ ...@@ -8,8 +8,6 @@
.todos-list > .todo { .todos-list > .todo {
// workaround because we cannot use border-collapse // workaround because we cannot use border-collapse
border-top: 1px solid transparent; border-top: 1px solid transparent;
display: flex;
flex-direction: row;
&:hover { &:hover {
background-color: var(--blue-50, $blue-50); background-color: var(--blue-50, $blue-50);
...@@ -26,25 +24,6 @@ ...@@ -26,25 +24,6 @@
} }
} }
.todo-avatar,
.todo-actions {
@include transition(opacity);
flex: 0 0 auto;
}
.todo-actions {
display: flex;
justify-content: center;
flex-direction: column;
margin-left: 10px;
min-width: 55px;
}
.todo-item {
flex: 0 1 100%;
min-width: 0;
}
&.todo-pending.done-reversible { &.todo-pending.done-reversible {
&:hover { &:hover {
border-color: var(--border-color, $border-color); border-color: var(--border-color, $border-color);
...@@ -71,58 +50,22 @@ ...@@ -71,58 +50,22 @@
.todo-item { .todo-item {
@include transition(opacity); @include transition(opacity);
.todo-title {
> .title-item {
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
}
.todo-label {
flex: 0 1 auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.status-box { .status-box {
margin: 0;
float: none;
display: inline-block;
font-weight: $gl-font-weight-normal;
padding: 0 5px;
line-height: inherit; line-height: inherit;
font-size: 14px;
} }
.todo-label, .todo-label,
.todo-project { .todo-project {
a { a {
font-weight: $gl-font-weight-normal;
color: var(--blue-600, $blue-600); color: var(--blue-600, $blue-600);
} }
} }
.todo-body { .todo-body {
.badge.badge-pill,
p { p {
color: var(--gl-text-color, $gl-text-color); color: var(--gl-text-color, $gl-text-color);
} }
.md {
color: $gl-grayish-blue;
font-size: $gl-font-size;
}
code {
white-space: pre-wrap;
}
pre { pre {
border: 0; border: 0;
background: var(--gray-50, $gray-50); background: var(--gray-50, $gray-50);
...@@ -139,120 +82,13 @@ ...@@ -139,120 +82,13 @@
float: none; float: none;
} }
p:last-child { .gl-label-scoped {
margin-bottom: 0; --label-inset-border: inset 0 0 0 1px currentColor;
}
}
.gl-label-scoped {
--label-inset-border: inset 0 0 0 1px currentColor;
}
}
@include media-breakpoint-down(lg) {
.todos-filters {
.filter-categories {
width: 75%;
.filter-item {
margin-bottom: 10px;
}
} }
}
}
@include media-breakpoint-down(sm) { @include media-breakpoint-down(sm) {
.container-fluid .todos-list-container {
margin: 0 (-$gl-padding);
}
.todo {
.avatar {
display: none;
}
}
.todo-item {
.todo-title {
margin-bottom: 10px;
.todo-label {
white-space: normal;
}
}
.todo-body {
margin: 0;
border-left: 2px solid var(--border-color, $border-color); border-left: 2px solid var(--border-color, $border-color);
padding-left: 10px; padding-left: 10px;
} }
} }
.todos-filters {
.filter-categories {
width: auto;
}
.dropdown-menu-toggle {
width: 100%;
}
.dropdown-menu-toggle-sort {
width: auto;
}
}
}
.todos-empty {
display: flex;
flex-direction: column;
max-width: 900px;
margin-left: auto;
margin-right: auto;
@include media-breakpoint-up(sm) {
flex-direction: row;
padding-top: 80px;
}
}
.todos-empty-content {
align-self: center;
max-width: 480px;
}
.todos-empty-hero {
width: 200px;
margin-left: auto;
margin-right: auto;
@include media-breakpoint-up(sm) {
width: 300px;
margin-right: 0;
order: 2;
}
}
.todos-all-done {
padding-top: 20px;
@include media-breakpoint-up(sm) {
padding-top: 50px;
}
> svg {
display: block;
max-width: 300px;
margin: 0 auto 20px;
}
p {
max-width: 470px;
margin-left: auto;
margin-right: auto;
}
a {
font-weight: $gl-font-weight-bold;
}
} }
...@@ -110,10 +110,8 @@ module TodosHelper ...@@ -110,10 +110,8 @@ module TodosHelper
'alert' 'alert'
end end
content_tag(:span, nil, class: 'target-status') do tag.span class: "gl-my-0 gl-px-2 status-box status-box-#{type}-#{todo.target.state.to_s.dasherize}" do
content_tag(:span, nil, class: "status-box status-box-#{type}-#{todo.target.state.to_s.dasherize}") do todo.target.state.to_s.capitalize
todo.target.state.to_s.capitalize
end
end end
end end
......
%li{ class: "todo todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo), data: { url: todo_target_path(todo) } } %li.todo{ class: "todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo), data: { url: todo_target_path(todo) } }
.todo-avatar .gl-display-flex.gl-flex-direction-row
= author_avatar(todo, size: 40) .todo-avatar.gl-display-none.gl-sm-display-inline-block
= author_avatar(todo, size: 40)
.todo-item.todo-block.align-self-center{ data: { qa_selector: "todo_item_container" } }
.todo-title .todo-item.gl-w-full.gl-align-self-center{ data: { qa_selector: "todo_item_container" } }
- if todo_author_display?(todo) .todo-title.gl-mb-3.gl-md-mb-0
= todo_target_state_pill(todo) - if todo_author_display?(todo)
= todo_target_state_pill(todo)
%span.title-item.author-name.bold
- if todo.author %span.title-item.author-name.bold
= link_to_author(todo, self_added: todo.self_added?) - if todo.author
= link_to_author(todo, self_added: todo.self_added?)
- else
(removed)
%span.title-item.action-name{ data: { qa_selector: "todo_action_name_content" } }
= todo_action_name(todo)
%span.title-item.todo-label.todo-target-link
- if todo.target
= todo_target_link(todo)
- else - else
(removed) = _("(removed)")
%span.title-item.action-name{ data: { qa_selector: "todo_action_name_content" } } %span.title-item.todo-target-title{ data: { qa_selector: "todo_target_title_content" } }
= todo_action_name(todo) = todo_target_title(todo)
%span.title-item.todo-label.todo-target-link %span.title-item.todo-project.todo-label
- if todo.target at
= todo_target_link(todo) = todo_parent_path(todo)
- else
= _("(removed)") - if todo.self_assigned?
%span.title-item.action-name
%span.title-item.todo-target-title{ data: { qa_selector: "todo_target_title_content" } } = todo_self_addressing(todo)
= todo_target_title(todo)
%span.title-item
%span.title-item.todo-project.todo-label &middot;
at
= todo_parent_path(todo) %span.title-item.todo-timestamp
#{time_ago_with_tooltip(todo.created_at)}
- if todo.self_assigned? = todo_due_date(todo)
%span.title-item.action-name
= todo_self_addressing(todo) - if todo.note.present?
.todo-body
%span.title-item .todo-note.break-word
&middot; .md
= first_line_in_markdown(todo, :body, 150, project: todo.project)
%span.title-item.todo-timestamp
#{time_ago_with_tooltip(todo.created_at)} .todo-actions.gl-ml-3
= todo_due_date(todo) - if todo.pending?
= link_to dashboard_todo_path(todo), method: :delete, class: 'gl-button btn btn-default btn-loading d-flex align-items-center js-done-todo', data: { href: dashboard_todo_path(todo) } do
- if todo.note.present? Done
.todo-body %span.gl-spinner.ml-1
.todo-note.break-word = link_to restore_dashboard_todo_path(todo), method: :patch, class: 'gl-button btn btn-default btn-loading d-flex align-items-center js-undo-todo hidden', data: { href: restore_dashboard_todo_path(todo) } do
.md Undo
= first_line_in_markdown(todo, :body, 150, project: todo.project) %span.gl-spinner.ml-1
- else
- if todo.pending? = link_to restore_dashboard_todo_path(todo), method: :patch, class: 'gl-button btn btn-default btn-loading d-flex align-items-center js-add-todo', data: { href: restore_dashboard_todo_path(todo) } do
.todo-actions Add a to do
= link_to dashboard_todo_path(todo), method: :delete, class: 'gl-button btn btn-default btn-loading d-flex align-items-center js-done-todo', data: { href: dashboard_todo_path(todo) } do %span.gl-spinner.ml-1
Done
%span.gl-spinner.ml-1
= link_to restore_dashboard_todo_path(todo), method: :patch, class: 'gl-button btn btn-default btn-loading d-flex align-items-center js-undo-todo hidden', data: { href: restore_dashboard_todo_path(todo) } do
Undo
%span.gl-spinner.ml-1
- else
.todo-actions
= link_to restore_dashboard_todo_path(todo), method: :patch, class: 'gl-button btn btn-default btn-loading d-flex align-items-center js-add-todo', data: { href: restore_dashboard_todo_path(todo) } do
Add a to do
%span.gl-spinner.ml-1
...@@ -36,34 +36,34 @@ ...@@ -36,34 +36,34 @@
.todos-filters .todos-filters
.issues-details-filters.row-content-block.second-block .issues-details-filters.row-content-block.second-block
= form_tag todos_filter_path(without: [:project_id, :author_id, :type, :action_id]), method: :get, class: 'filter-form d-sm-flex' do = form_tag todos_filter_path(without: [:project_id, :author_id, :type, :action_id]), method: :get, class: 'filter-form gl-display-flex gl-flex-direction-column gl-sm-flex-direction-row' do
.filter-categories.flex-fill .filter-categories.gl-display-flex.gl-flex-direction-column.gl-md-flex-direction-row.gl-flex-fill-1.gl-flex-wrap.gl-mx-n2
.filter-item.inline .filter-item.gl-m-2
- if params[:group_id].present? - if params[:group_id].present?
= hidden_field_tag(:group_id, params[:group_id]) = hidden_field_tag(:group_id, params[:group_id])
= dropdown_tag(group_dropdown_label(params[:group_id], 'Group'), options: { toggle_class: 'js-group-search js-filter-submit', title: 'Filter by group', filter: true, filterInput: 'input#group-search', dropdown_class: 'dropdown-menu-selectable dropdown-menu-group js-filter-submit', = dropdown_tag(group_dropdown_label(params[:group_id], 'Group'), options: { toggle_class: 'js-group-search js-filter-submit gl-xs-w-full!', title: 'Filter by group', filter: true, filterInput: 'input#group-search', dropdown_class: 'dropdown-menu-selectable dropdown-menu-group js-filter-submit',
placeholder: 'Search groups', data: { default_label: 'Group', display: 'static' } }) placeholder: 'Search groups', data: { default_label: 'Group', display: 'static' } })
.filter-item.inline .filter-item.gl-m-2
- if params[:project_id].present? - if params[:project_id].present?
= hidden_field_tag(:project_id, params[:project_id]) = hidden_field_tag(:project_id, params[:project_id])
= dropdown_tag(project_dropdown_label(params[:project_id], 'Project'), options: { toggle_class: 'js-project-search js-filter-submit', title: 'Filter by project', filter: true, filterInput: 'input#project-search', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit', = dropdown_tag(project_dropdown_label(params[:project_id], 'Project'), options: { toggle_class: 'js-project-search js-filter-submit gl-xs-w-full!', title: 'Filter by project', filter: true, filterInput: 'input#project-search', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit',
placeholder: 'Search projects', data: { default_label: 'Project', display: 'static' } }) placeholder: 'Search projects', data: { default_label: 'Project', display: 'static' } })
.filter-item.inline .filter-item.gl-m-2
- if params[:author_id].present? - if params[:author_id].present?
= hidden_field_tag(:author_id, params[:author_id]) = hidden_field_tag(:author_id, params[:author_id])
= dropdown_tag(user_dropdown_label(params[:author_id], 'Author'), options: { toggle_class: 'js-user-search js-filter-submit js-author-search', title: 'Filter by author', filter: true, filterInput: 'input#author-search', dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit', = dropdown_tag(user_dropdown_label(params[:author_id], 'Author'), options: { toggle_class: 'js-user-search js-filter-submit js-author-search gl-xs-w-full!', title: 'Filter by author', filter: true, filterInput: 'input#author-search', dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit',
placeholder: 'Search authors', data: { any_user: 'Any Author', first_user: (current_user.username if current_user), project_id: (@project.id if @project), selected: params[:author_id], field_name: 'author_id', default_label: 'Author', todo_filter: true, todo_state_filter: params[:state] || 'pending' } }) placeholder: 'Search authors', data: { any_user: 'Any Author', first_user: (current_user.username if current_user), project_id: (@project.id if @project), selected: params[:author_id], field_name: 'author_id', default_label: 'Author', todo_filter: true, todo_state_filter: params[:state] || 'pending' } })
.filter-item.inline .filter-item.gl-m-2
- if params[:type].present? - if params[:type].present?
= hidden_field_tag(:type, params[:type]) = hidden_field_tag(:type, params[:type])
= dropdown_tag(todo_types_dropdown_label(params[:type], 'Type'), options: { toggle_class: 'js-type-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-type js-filter-submit', = dropdown_tag(todo_types_dropdown_label(params[:type], 'Type'), options: { toggle_class: 'js-type-search js-filter-submit gl-xs-w-full!', dropdown_class: 'dropdown-menu-selectable dropdown-menu-type js-filter-submit',
data: { data: todo_types_options, default_label: 'Type' } }) data: { data: todo_types_options, default_label: 'Type' } })
.filter-item.inline.actions-filter .filter-item.actions-filter.gl-m-2
- if params[:action_id].present? - if params[:action_id].present?
= hidden_field_tag(:action_id, params[:action_id]) = hidden_field_tag(:action_id, params[:action_id])
= dropdown_tag(todo_actions_dropdown_label(params[:action_id], 'Action'), options: { toggle_class: 'js-action-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-action js-filter-submit', = dropdown_tag(todo_actions_dropdown_label(params[:action_id], 'Action'), options: { toggle_class: 'js-action-search js-filter-submit gl-xs-w-full!', dropdown_class: 'dropdown-menu-selectable dropdown-menu-action js-filter-submit',
data: { data: todo_actions_options, default_label: 'Action' } }) data: { data: todo_actions_options, default_label: 'Action' } })
.filter-item.sort-filter .filter-item.sort-filter.gl-mt-3.gl-sm-mt-0.gl-mb-0.gl-sm-mb-0
.dropdown .dropdown
%button.dropdown-menu-toggle.dropdown-menu-toggle-sort{ type: 'button', class: 'gl-xs-w-full!', 'data-toggle' => 'dropdown' } %button.dropdown-menu-toggle.dropdown-menu-toggle-sort{ type: 'button', class: 'gl-xs-w-full!', 'data-toggle' => 'dropdown' }
%span.light %span.light
...@@ -81,40 +81,45 @@ ...@@ -81,40 +81,45 @@
= link_to todos_filter_path(sort: sort_value_oldest_created) do = link_to todos_filter_path(sort: sort_value_oldest_created) do
= sort_title_oldest_created = sort_title_oldest_created
.todos-list-container.js-todos-all .row.js-todos-all
- if @todos.any? - if @todos.any?
.js-todos-list-container{ data: { qa_selector: "todos_list_container" } } .col.js-todos-list-container{ data: { qa_selector: "todos_list_container" } }
.js-todos-options{ data: { per_page: @todos.limit_value, current_page: @todos.current_page, total_pages: @todos.total_pages } } .js-todos-options{ data: { per_page: @todos.limit_value, current_page: @todos.current_page, total_pages: @todos.total_pages } }
%ul.content-list.todos-list %ul.content-list.todos-list
= render @todos = render @todos
= paginate @todos, theme: "gitlab" = paginate @todos, theme: "gitlab"
.js-nothing-here-container.todos-all-done.hidden.svg-content .js-nothing-here-container.empty-state.hidden
= image_tag 'illustrations/todos_all_done.svg' .svg-content
%h4.text-center = image_tag 'illustrations/todos_all_done.svg'
You're all done! .text-content
%h4.text-center
You're all done!
- elsif current_user.todos.any? - elsif current_user.todos.any?
.todos-all-done .col.todos-all-done.empty-state
.svg-content.svg-250 .svg-content.svg-250
= image_tag 'illustrations/todos_all_done.svg' = image_tag 'illustrations/todos_all_done.svg'
- if todos_filter_empty? .text-content
%h4.text-center - if todos_filter_empty?
= Gitlab.config.gitlab.no_todos_messages.sample %h4.text-center
%p = Gitlab.config.gitlab.no_todos_messages.sample
Are you looking for things to do? Take a look at %p
= succeed "," do Are you looking for things to do? Take a look at
= link_to "open issues", issues_dashboard_path = succeed "," do
contribute to %strong
= link_to "a merge request\,", merge_requests_dashboard_path = link_to "open issues", issues_dashboard_path
or mention someone in a comment to automatically assign them a new to-do item. contribute to
- else %strong
%h4.text-center = link_to "a merge request\,", merge_requests_dashboard_path
Nothing is on your to-do list. Nice work! or mention someone in a comment to automatically assign them a new to-do item.
- else
%h4.text-center
Nothing is on your to-do list. Nice work!
- else - else
.todos-empty .col.empty-state
.todos-empty-hero.svg-content .svg-content
= image_tag 'illustrations/todos_empty.svg' = image_tag 'illustrations/todos_empty.svg'
.todos-empty-content.gl-mx-5 .text-content
%h4 %h4.text-center
Your To-Do List shows what to work on next Your To-Do List shows what to work on next
%p %p
When an issue or merge request is assigned to you, or when you receive a When an issue or merge request is assigned to you, or when you receive a
......
...@@ -79,7 +79,7 @@ RSpec.describe 'Dashboard Todos' do ...@@ -79,7 +79,7 @@ RSpec.describe 'Dashboard Todos' do
end end
it 'has not "All done" message' do it 'has not "All done" message' do
expect(page).not_to have_selector('.todos-all-done') expect(page).not_to have_selector('.empty-state')
end end
end end
......
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