diff options
author | Jarka Kadlecová <jarka@gitlab.com> | 2018-07-11 19:33:24 +0300 |
---|---|---|
committer | Jarka Kadlecová <jarka@gitlab.com> | 2018-07-11 19:33:24 +0300 |
commit | 8717c7dad9b5a8fa21ec9a652c54718a6b4c2175 (patch) | |
tree | abedd3ea3e4de67953b992df1a67d7baef8aebe2 /app | |
parent | c7c630f142a23505b4adce449c0a176dec142384 (diff) |
Revert "Merge branch 'ee-5481-epic-todos' into 'master'"
This reverts commit 4d9a3f42f1fd3be21555e19872b7121cca65015e, reversing
changes made to ecf9c145f6e4d170cd059df88743393d9e63c489.
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/pages/dashboard/todos/index/todos.js | 12 | ||||
-rw-r--r-- | app/assets/javascripts/sidebar/components/todo_toggle/todo.vue | 98 | ||||
-rw-r--r-- | app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue | 7 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/issuable.scss | 1 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/todos.scss | 16 | ||||
-rw-r--r-- | app/controllers/concerns/todos_actions.rb | 12 | ||||
-rw-r--r-- | app/controllers/dashboard/todos_controller.rb | 2 | ||||
-rw-r--r-- | app/controllers/projects/todos_controller.rb | 14 | ||||
-rw-r--r-- | app/finders/todos_finder.rb | 52 | ||||
-rw-r--r-- | app/helpers/issuables_helper.rb | 13 | ||||
-rw-r--r-- | app/helpers/todos_helper.rb | 10 | ||||
-rw-r--r-- | app/models/concerns/issuable.rb | 6 | ||||
-rw-r--r-- | app/models/group.rb | 8 | ||||
-rw-r--r-- | app/models/issue.rb | 4 | ||||
-rw-r--r-- | app/models/note.rb | 4 | ||||
-rw-r--r-- | app/models/todo.rb | 11 | ||||
-rw-r--r-- | app/services/todo_service.rb | 30 | ||||
-rw-r--r-- | app/views/dashboard/todos/index.html.haml | 48 |
18 files changed, 67 insertions, 281 deletions
diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js index 9aa83ce6269..ff19b9a9c30 100644 --- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js +++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js @@ -39,7 +39,6 @@ export default class Todos { } initFilters() { - this.initFilterDropdown($('.js-group-search'), 'group_id', ['text']); this.initFilterDropdown($('.js-project-search'), 'project_id', ['text']); this.initFilterDropdown($('.js-type-search'), 'type'); this.initFilterDropdown($('.js-action-search'), 'action_id'); @@ -54,16 +53,7 @@ export default class Todos { filterable: searchFields ? true : false, search: { fields: searchFields }, data: $dropdown.data('data'), - clicked: () => { - const $formEl = $dropdown.closest('form.filter-form'); - const mutexDropdowns = { - group_id: 'project_id', - project_id: 'group_id', - }; - - $formEl.find(`input[name="${mutexDropdowns[fieldName]}"]`).remove(); - $formEl.submit(); - }, + clicked: () => $dropdown.closest('form.filter-form').submit(), }); } diff --git a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue deleted file mode 100644 index ffaed9c7193..00000000000 --- a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue +++ /dev/null @@ -1,98 +0,0 @@ -<script> -import { __ } from '~/locale'; -import tooltip from '~/vue_shared/directives/tooltip'; - -import Icon from '~/vue_shared/components/icon.vue'; -import LoadingIcon from '~/vue_shared/components/loading_icon.vue'; - -const MARK_TEXT = __('Mark todo as done'); -const TODO_TEXT = __('Add todo'); - -export default { - directives: { - tooltip, - }, - components: { - Icon, - LoadingIcon, - }, - props: { - issuableId: { - type: Number, - required: true, - }, - issuableType: { - type: String, - required: true, - }, - isTodo: { - type: Boolean, - required: false, - default: true, - }, - isActionActive: { - type: Boolean, - required: false, - default: false, - }, - collapsed: { - type: Boolean, - required: false, - default: false, - }, - }, - computed: { - buttonClasses() { - return this.collapsed ? - 'btn-blank btn-todo sidebar-collapsed-icon dont-change-state' : - 'btn btn-default btn-todo issuable-header-btn float-right'; - }, - buttonLabel() { - return this.isTodo ? MARK_TEXT : TODO_TEXT; - }, - collapsedButtonIconClasses() { - return this.isTodo ? 'todo-undone' : ''; - }, - collapsedButtonIcon() { - return this.isTodo ? 'todo-done' : 'todo-add'; - }, - }, - methods: { - handleButtonClick() { - this.$emit('toggleTodo'); - }, - }, -}; -</script> - -<template> - <button - v-tooltip - :class="buttonClasses" - :title="buttonLabel" - :aria-label="buttonLabel" - :data-issuable-id="issuableId" - :data-issuable-type="issuableType" - type="button" - data-container="body" - data-placement="left" - data-boundary="viewport" - @click="handleButtonClick" - > - <icon - v-show="collapsed" - :css-classes="collapsedButtonIconClasses" - :name="collapsedButtonIcon" - /> - <span - v-show="!collapsed" - class="issuable-todo-inner" - > - {{ buttonLabel }} - </span> - <loading-icon - v-show="isActionActive" - :inline="true" - /> - </button> -</template> diff --git a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue b/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue index 80dc7d3557c..ac2e99abe77 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue @@ -12,11 +12,6 @@ export default { type: Boolean, required: true, }, - cssClasses: { - type: String, - required: false, - default: '', - }, }, computed: { tooltipLabel() { @@ -35,12 +30,10 @@ export default { <button v-tooltip :title="tooltipLabel" - :class="cssClasses" type="button" class="btn btn-blank gutter-toggle btn-sidebar-action" data-container="body" data-placement="left" - data-boundary="viewport" @click="toggle" > <i diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index f6617380cc0..f9fd9f1ab8b 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -449,7 +449,6 @@ .todo-undone { color: $gl-link-color; - fill: $gl-link-color; } .author { diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss index 010a2c05a1c..e5d7dd13915 100644 --- a/app/assets/stylesheets/pages/todos.scss +++ b/app/assets/stylesheets/pages/todos.scss @@ -174,18 +174,6 @@ } } -@include media-breakpoint-down(lg) { - .todos-filters { - .filter-categories { - width: 75%; - - .filter-item { - margin-bottom: 10px; - } - } - } -} - @include media-breakpoint-down(xs) { .todo { .avatar { @@ -211,10 +199,6 @@ } .todos-filters { - .filter-categories { - width: auto; - } - .dropdown-menu-toggle { width: 100%; } diff --git a/app/controllers/concerns/todos_actions.rb b/app/controllers/concerns/todos_actions.rb deleted file mode 100644 index c0acdb3498d..00000000000 --- a/app/controllers/concerns/todos_actions.rb +++ /dev/null @@ -1,12 +0,0 @@ -module TodosActions - extend ActiveSupport::Concern - - def create - todo = TodoService.new.mark_todo(issuable, current_user) - - render json: { - count: TodosFinder.new(current_user, state: :pending).execute.count, - delete_path: dashboard_todo_path(todo) - } - end -end diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb index bd7111e28bc..f9e8fe624e8 100644 --- a/app/controllers/dashboard/todos_controller.rb +++ b/app/controllers/dashboard/todos_controller.rb @@ -70,7 +70,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController end def todo_params - params.permit(:action_id, :author_id, :project_id, :type, :sort, :state, :group_id) + params.permit(:action_id, :author_id, :project_id, :type, :sort, :state) end def redirect_out_of_range(todos) diff --git a/app/controllers/projects/todos_controller.rb b/app/controllers/projects/todos_controller.rb index 93fb9da6510..a41fcb85c40 100644 --- a/app/controllers/projects/todos_controller.rb +++ b/app/controllers/projects/todos_controller.rb @@ -1,13 +1,19 @@ class Projects::TodosController < Projects::ApplicationController - include Gitlab::Utils::StrongMemoize - include TodosActions - before_action :authenticate_user!, only: [:create] + def create + todo = TodoService.new.mark_todo(issuable, current_user) + + render json: { + count: TodosFinder.new(current_user, state: :pending).execute.count, + delete_path: dashboard_todo_path(todo) + } + end + private def issuable - strong_memoize(:issuable) do + @issuable ||= begin case params[:issuable_type] when "issue" IssuesFinder.new(current_user, project_id: @project.id).find(params[:issuable_id]) diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb index 2156413fb26..09e2c586f2a 100644 --- a/app/finders/todos_finder.rb +++ b/app/finders/todos_finder.rb @@ -15,7 +15,6 @@ class TodosFinder prepend FinderWithCrossProjectAccess include FinderMethods - include Gitlab::Utils::StrongMemoize requires_cross_project_access unless: -> { project? } @@ -35,11 +34,9 @@ class TodosFinder items = by_author(items) items = by_state(items) items = by_type(items) - items = by_group(items) # Filtering by project HAS TO be the last because we use # the project IDs yielded by the todos query thus far items = by_project(items) - items = visible_to_user(items) sort(items) end @@ -85,10 +82,6 @@ class TodosFinder params[:project_id].present? end - def group? - params[:group_id].present? - end - def project return @project if defined?(@project) @@ -107,14 +100,18 @@ class TodosFinder @project end - def group - strong_memoize(:group) do - Group.find(params[:group_id]) + def project_ids(items) + ids = items.except(:order).select(:project_id) + if Gitlab::Database.mysql? + # To make UPDATE work on MySQL, wrap it in a SELECT with an alias + ids = Todo.except(:order).select('*').from("(#{ids.to_sql}) AS t") end + + ids end def type? - type.present? && %w(Issue MergeRequest Epic).include?(type) + type.present? && %w(Issue MergeRequest).include?(type) end def type @@ -151,37 +148,12 @@ class TodosFinder def by_project(items) if project? - items = items.where(project: project) - end - - items - end + items.where(project: project) + else + projects = Project.public_or_visible_to_user(current_user) - def by_group(items) - if group? - groups = group.self_and_descendants - items = items.where( - 'project_id IN (?) OR group_id IN (?)', - Project.where(group: groups).select(:id), - groups.select(:id) - ) + items.joins(:project).merge(projects) end - - items - end - - def visible_to_user(items) - projects = Project.public_or_visible_to_user(current_user) - groups = Group.public_or_visible_to_user(current_user) - - items - .joins('LEFT JOIN namespaces ON namespaces.id = todos.group_id') - .joins('LEFT JOIN projects ON projects.id = todos.project_id') - .where( - 'project_id IN (?) OR group_id IN (?)', - projects.select(:id), - groups.select(:id) - ) end def by_state(items) diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 7bbdc798ddd..8766bb43cac 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -131,19 +131,6 @@ module IssuablesHelper end end - def group_dropdown_label(group_id, default_label) - return default_label if group_id.nil? - return "Any group" if group_id == "0" - - group = ::Group.find_by(id: group_id) - - if group - group.full_name - else - default_label - end - end - def milestone_dropdown_label(milestone_title, default_label = "Milestone") title = case milestone_title diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index 7cd74358168..f7620e0b6b8 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -43,7 +43,7 @@ module TodosHelper project_commit_path(todo.project, todo.target, anchor: anchor) else - path = [todo.parent, todo.target] + path = [todo.project.namespace.becomes(Namespace), todo.project, todo.target] path.unshift(:pipelines) if todo.build_failed? @@ -167,12 +167,4 @@ module TodosHelper def show_todo_state?(todo) (todo.target.is_a?(MergeRequest) || todo.target.is_a?(Issue)) && %w(closed merged).include?(todo.target.state) end - - def todo_group_options - groups = current_user.authorized_groups.map do |group| - { id: group.id, text: group.full_name } - end - - groups.unshift({ id: '', text: 'Any Group' }).to_json - end end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 7a459078151..b93c1145f82 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -243,12 +243,6 @@ module Issuable opened? end - def overdue? - return false unless respond_to?(:due_date) - - due_date.try(:past?) || false - end - def user_notes_count if notes.loaded? # Use the in-memory association to select and count to avoid hitting the db diff --git a/app/models/group.rb b/app/models/group.rb index 28677320e28..ddebaff50b0 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -39,8 +39,6 @@ class Group < Namespace has_many :boards has_many :badges, class_name: 'GroupBadge' - has_many :todos - accepts_nested_attributes_for :variables, allow_destroy: true validate :visibility_level_allowed_by_projects @@ -84,12 +82,6 @@ class Group < Namespace where(id: user.authorized_groups.select(:id).reorder(nil)) end - def public_or_visible_to_user(user) - where('id IN (?) OR namespaces.visibility_level IN (?)', - user.authorized_groups.select(:id), - Gitlab::VisibilityLevel.levels_for_user(user)) - end - def select_for_project_authorization if current_scope.joins_values.include?(:shared_projects) joins('INNER JOIN namespaces project_namespace ON project_namespace.id = projects.namespace_id') diff --git a/app/models/issue.rb b/app/models/issue.rb index 983684a5e05..4715d942c8d 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -275,6 +275,10 @@ class Issue < ActiveRecord::Base user ? readable_by?(user) : publicly_visible? end + def overdue? + due_date.try(:past?) || false + end + def check_for_spam? project.public? && (title_changed? || description_changed?) end diff --git a/app/models/note.rb b/app/models/note.rb index 3918bbee194..abc40d9016e 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -229,10 +229,6 @@ class Note < ActiveRecord::Base !for_personal_snippet? end - def for_issuable? - for_issue? || for_merge_request? - end - def skip_project_check? !for_project_noteable? end diff --git a/app/models/todo.rb b/app/models/todo.rb index 942cbb754e3..a2ab405fdbe 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -22,18 +22,15 @@ class Todo < ActiveRecord::Base belongs_to :author, class_name: "User" belongs_to :note belongs_to :project - belongs_to :group belongs_to :target, polymorphic: true, touch: true # rubocop:disable Cop/PolymorphicAssociations belongs_to :user delegate :name, :email, to: :author, prefix: true, allow_nil: true - validates :action, :target_type, :user, presence: true + validates :action, :project, :target_type, :user, presence: true validates :author, presence: true validates :target_id, presence: true, unless: :for_commit? validates :commit_id, presence: true, if: :for_commit? - validates :project, presence: true, unless: :group_id - validates :group, presence: true, unless: :project_id scope :pending, -> { with_state(:pending) } scope :done, -> { with_state(:done) } @@ -47,7 +44,7 @@ class Todo < ActiveRecord::Base state :done end - after_save :keep_around_commit, if: :commit_id + after_save :keep_around_commit class << self # Priority sorting isn't displayed in the dropdown, because we don't show @@ -82,10 +79,6 @@ class Todo < ActiveRecord::Base end end - def parent - project - end - def unmergeable? action == UNMERGEABLE end diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb index 46f12086555..f91cd03bf5c 100644 --- a/app/services/todo_service.rb +++ b/app/services/todo_service.rb @@ -260,15 +260,15 @@ class TodoService end end - def create_mention_todos(parent, target, author, note = nil, skip_users = []) + def create_mention_todos(project, target, author, note = nil, skip_users = []) # Create Todos for directly addressed users - directly_addressed_users = filter_directly_addressed_users(parent, note || target, author, skip_users) - attributes = attributes_for_todo(parent, target, author, Todo::DIRECTLY_ADDRESSED, note) + directly_addressed_users = filter_directly_addressed_users(project, note || target, author, skip_users) + attributes = attributes_for_todo(project, target, author, Todo::DIRECTLY_ADDRESSED, note) create_todos(directly_addressed_users, attributes) # Create Todos for mentioned users - mentioned_users = filter_mentioned_users(parent, note || target, author, skip_users) - attributes = attributes_for_todo(parent, target, author, Todo::MENTIONED, note) + mentioned_users = filter_mentioned_users(project, note || target, author, skip_users) + attributes = attributes_for_todo(project, target, author, Todo::MENTIONED, note) create_todos(mentioned_users, attributes) end @@ -299,36 +299,36 @@ class TodoService def attributes_for_todo(project, target, author, action, note = nil) attributes_for_target(target).merge!( - project_id: project&.id, + project_id: project.id, author_id: author.id, action: action, note: note ) end - def filter_todo_users(users, parent, target) - reject_users_without_access(users, parent, target).uniq + def filter_todo_users(users, project, target) + reject_users_without_access(users, project, target).uniq end - def filter_mentioned_users(parent, target, author, skip_users = []) + def filter_mentioned_users(project, target, author, skip_users = []) mentioned_users = target.mentioned_users(author) - skip_users - filter_todo_users(mentioned_users, parent, target) + filter_todo_users(mentioned_users, project, target) end - def filter_directly_addressed_users(parent, target, author, skip_users = []) + def filter_directly_addressed_users(project, target, author, skip_users = []) directly_addressed_users = target.directly_addressed_users(author) - skip_users - filter_todo_users(directly_addressed_users, parent, target) + filter_todo_users(directly_addressed_users, project, target) end - def reject_users_without_access(users, parent, target) - if target.is_a?(Note) && target.for_issuable? + def reject_users_without_access(users, project, target) + if target.is_a?(Note) && (target.for_issue? || target.for_merge_request?) target = target.noteable end if target.is_a?(Issuable) select_users(users, :"read_#{target.to_ability_name}", target) else - select_users(users, :read_project, parent) + select_users(users, :read_project, project) end end diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index 8b3974d97f8..d5a9cc646a6 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -30,33 +30,27 @@ .todos-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 - .filter-categories.flex-fill - .filter-item.inline - - if params[:group_id].present? - = 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', - placeholder: 'Search groups', data: { data: todo_group_options, default_label: 'Group', display: 'static' } }) - .filter-item.inline - - if params[:project_id].present? - = 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', - placeholder: 'Search projects', data: { data: todo_projects_options, default_label: 'Project', display: 'static' } }) - .filter-item.inline - - if params[:author_id].present? - = 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', - 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 - - if params[:type].present? - = 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', - data: { data: todo_types_options, default_label: 'Type' } }) - .filter-item.inline.actions-filter - - if params[:action_id].present? - = 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', - data: { data: todo_actions_options, default_label: 'Action' } }) + = form_tag todos_filter_path(without: [:project_id, :author_id, :type, :action_id]), method: :get, class: 'filter-form' do + .filter-item.inline + - if params[:project_id].present? + = 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', + placeholder: 'Search projects', data: { data: todo_projects_options, default_label: 'Project', display: 'static' } }) + .filter-item.inline + - if params[:author_id].present? + = 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', + 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 + - if params[:type].present? + = 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', + data: { data: todo_types_options, default_label: 'Type' } }) + .filter-item.inline.actions-filter + - if params[:action_id].present? + = 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', + data: { data: todo_actions_options, default_label: 'Action' } }) .filter-item.sort-filter .dropdown %button.dropdown-menu-toggle.dropdown-menu-toggle-sort{ type: 'button', 'data-toggle' => 'dropdown' } |