Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
08fcc1e9
Commit
08fcc1e9
authored
May 15, 2017
by
Bryce Johnson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Move issuable bulk edit form into a new sidebar.
parent
a89ebdb1
Changes
24
Show whitespace changes
Inline
Side-by-side
Showing
24 changed files
with
553 additions
and
394 deletions
+553
-394
app/assets/javascripts/dispatcher.js
app/assets/javascripts/dispatcher.js
+4
-5
app/assets/javascripts/issuable_bulk_update_actions.js
app/assets/javascripts/issuable_bulk_update_actions.js
+159
-0
app/assets/javascripts/issuable_bulk_update_sidebar.js
app/assets/javascripts/issuable_bulk_update_sidebar.js
+165
-0
app/assets/javascripts/issuable_index.js
app/assets/javascripts/issuable_index.js
+32
-49
app/assets/javascripts/issues_bulk_assignment.js
app/assets/javascripts/issues_bulk_assignment.js
+0
-166
app/assets/javascripts/labels_select.js
app/assets/javascripts/labels_select.js
+6
-9
app/assets/javascripts/main.js
app/assets/javascripts/main.js
+1
-2
app/assets/stylesheets/framework/common.scss
app/assets/stylesheets/framework/common.scss
+6
-0
app/assets/stylesheets/framework/filters.scss
app/assets/stylesheets/framework/filters.scss
+0
-12
app/assets/stylesheets/framework/mobile.scss
app/assets/stylesheets/framework/mobile.scss
+0
-4
app/assets/stylesheets/framework/sidebar.scss
app/assets/stylesheets/framework/sidebar.scss
+34
-2
app/views/projects/issues/_issue.html.haml
app/views/projects/issues/_issue.html.haml
+2
-2
app/views/projects/issues/index.html.haml
app/views/projects/issues/index.html.haml
+6
-1
app/views/projects/merge_requests/_merge_request.html.haml
app/views/projects/merge_requests/_merge_request.html.haml
+2
-2
app/views/projects/merge_requests/index.html.haml
app/views/projects/merge_requests/index.html.haml
+6
-1
app/views/shared/issuable/_bulk_update_sidebar.html.haml
app/views/shared/issuable/_bulk_update_sidebar.html.haml
+53
-0
app/views/shared/issuable/_filter.html.haml
app/views/shared/issuable/_filter.html.haml
+0
-33
app/views/shared/issuable/_label_dropdown.html.haml
app/views/shared/issuable/_label_dropdown.html.haml
+5
-2
app/views/shared/issuable/_search_bar.html.haml
app/views/shared/issuable/_search_bar.html.haml
+3
-48
spec/features/issues/bulk_assignment_labels_spec.rb
spec/features/issues/bulk_assignment_labels_spec.rb
+37
-33
spec/features/issues/update_issues_spec.rb
spec/features/issues/update_issues_spec.rb
+15
-9
spec/features/merge_requests/update_merge_requests_spec.rb
spec/features/merge_requests/update_merge_requests_spec.rb
+8
-5
spec/javascripts/fixtures/issuable_filter.html.haml
spec/javascripts/fixtures/issuable_filter.html.haml
+1
-1
spec/javascripts/issuable_spec.js
spec/javascripts/issuable_spec.js
+8
-8
No files found.
app/assets/javascripts/dispatcher.js
View file @
08fcc1e9
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
/* global ActiveTabMemoizer */
/* global ActiveTabMemoizer */
/* global ShortcutsNavigation */
/* global ShortcutsNavigation */
/* global Build */
/* global Build */
/* global Issuable */
/* global Issuable
Index
*/
/* global ShortcutsIssuable */
/* global ShortcutsIssuable */
/* global ZenMode */
/* global ZenMode */
/* global Milestone */
/* global Milestone */
...
@@ -135,10 +135,9 @@ import ApproversSelect from './approvers_select';
...
@@ -135,10 +135,9 @@ import ApproversSelect from './approvers_select';
);
);
filteredSearchManager
.
setup
();
filteredSearchManager
.
setup
();
}
}
Issuable
.
init
();
const
pagePrefix
=
page
===
'
projects:merge_requests:index
'
?
'
merge_request_
'
:
'
issue_
'
;
new
gl
.
IssuableBulkActions
({
IssuableIndex
.
init
(
pagePrefix
);
prefixId
:
page
===
'
projects:merge_requests:index
'
?
'
merge_request_
'
:
'
issue_
'
,
});
shortcut_handler
=
new
ShortcutsNavigation
();
shortcut_handler
=
new
ShortcutsNavigation
();
new
UsersSelect
();
new
UsersSelect
();
break
;
break
;
...
...
app/assets/javascripts/issuable_bulk_update_actions.js
0 → 100644
View file @
08fcc1e9
/* eslint-disable comma-dangle, quotes, consistent-return, func-names, array-callback-return, space-before-function-paren, prefer-arrow-callback, max-len, no-unused-expressions, no-sequences, no-underscore-dangle, no-unused-vars, no-param-reassign */
/* global IssuableIndex */
/* global Flash */
export
default
{
init
({
container
,
form
,
issues
,
prefixId
}
=
{})
{
this
.
prefixId
=
prefixId
||
'
issue_
'
;
this
.
form
=
form
||
this
.
getElement
(
'
.bulk-update
'
);
this
.
$labelDropdown
=
this
.
form
.
find
(
'
.js-label-select
'
);
this
.
issues
=
issues
||
this
.
getElement
(
'
.issues-list .issue
'
);
this
.
willUpdateLabels
=
false
;
this
.
bindEvents
();
},
bindEvents
()
{
return
this
.
form
.
off
(
'
submit
'
).
on
(
'
submit
'
,
this
.
onFormSubmit
.
bind
(
this
));
},
onFormSubmit
(
e
)
{
e
.
preventDefault
();
return
this
.
submit
();
},
submit
()
{
const
_this
=
this
;
const
xhr
=
$
.
ajax
({
url
:
this
.
form
.
attr
(
'
action
'
),
method
:
this
.
form
.
attr
(
'
method
'
),
dataType
:
'
JSON
'
,
data
:
this
.
getFormDataAsObject
()
});
xhr
.
done
(()
=>
window
.
location
.
reload
());
xhr
.
fail
(()
=>
this
.
onFormSubmitFailure
());
},
onFormSubmitFailure
()
{
this
.
form
.
find
(
'
[type="submit"]
'
).
enable
();
return
new
Flash
(
"
Issue update failed
"
);
},
getSelectedIssues
()
{
return
this
.
issues
.
has
(
'
.selected_issue:checked
'
);
},
getLabelsFromSelection
()
{
const
labels
=
[];
this
.
getSelectedIssues
().
map
(
function
()
{
const
labelsData
=
$
(
this
).
data
(
'
labels
'
);
if
(
labelsData
)
{
return
labelsData
.
map
(
function
(
labelId
)
{
if
(
labels
.
indexOf
(
labelId
)
===
-
1
)
{
return
labels
.
push
(
labelId
);
}
});
}
});
return
labels
;
},
/**
* Will return only labels that were marked previously and the user has unmarked
* @return {Array} Label IDs
*/
getUnmarkedIndeterminedLabels
()
{
const
result
=
[];
const
labelsToKeep
=
this
.
$labelDropdown
.
data
(
'
indeterminate
'
);
this
.
getLabelsFromSelection
().
forEach
((
id
)
=>
{
if
(
labelsToKeep
.
indexOf
(
id
)
===
-
1
)
{
result
.
push
(
id
);
}
});
return
result
;
},
/**
* Simple form serialization, it will return just what we need
* Returns key/value pairs from form data
*/
getFormDataAsObject
()
{
const
formData
=
{
update
:
{
state_event
:
this
.
form
.
find
(
'
input[name="update[state_event]"]
'
).
val
(),
// For Merge Requests
assignee_id
:
this
.
form
.
find
(
'
input[name="update[assignee_id]"]
'
).
val
(),
// For Issues
assignee_ids
:
[
this
.
form
.
find
(
'
input[name="update[assignee_ids][]"]
'
).
val
()],
milestone_id
:
this
.
form
.
find
(
'
input[name="update[milestone_id]"]
'
).
val
(),
issuable_ids
:
this
.
form
.
find
(
'
input[name="update[issuable_ids]"]
'
).
val
(),
subscription_event
:
this
.
form
.
find
(
'
input[name="update[subscription_event]"]
'
).
val
(),
add_label_ids
:
[],
remove_label_ids
:
[]
}
};
if
(
this
.
willUpdateLabels
)
{
formData
.
update
.
add_label_ids
=
this
.
$labelDropdown
.
data
(
'
marked
'
);
formData
.
update
.
remove_label_ids
=
this
.
$labelDropdown
.
data
(
'
unmarked
'
);
}
return
formData
;
},
setOriginalDropdownData
()
{
const
$labelSelect
=
$
(
'
.bulk-update .js-label-select
'
);
$labelSelect
.
data
(
'
common
'
,
this
.
getOriginalCommonIds
());
$labelSelect
.
data
(
'
marked
'
,
this
.
getOriginalMarkedIds
());
$labelSelect
.
data
(
'
indeterminate
'
,
this
.
getOriginalIndeterminateIds
());
},
// From issuable's initial bulk selection
getOriginalCommonIds
()
{
const
labelIds
=
[];
this
.
getElement
(
'
.selected_issue:checked
'
).
each
((
i
,
el
)
=>
{
labelIds
.
push
(
this
.
getElement
(
`#
${
this
.
prefixId
}${
el
.
dataset
.
id
}
`
).
data
(
'
labels
'
));
});
return
_
.
intersection
.
apply
(
this
,
labelIds
);
},
// From issuable's initial bulk selection
getOriginalMarkedIds
()
{
const
labelIds
=
[];
this
.
getElement
(
'
.selected_issue:checked
'
).
each
((
i
,
el
)
=>
{
labelIds
.
push
(
this
.
getElement
(
`#
${
this
.
prefixId
}${
el
.
dataset
.
id
}
`
).
data
(
'
labels
'
));
});
return
_
.
intersection
.
apply
(
this
,
labelIds
);
},
// From issuable's initial bulk selection
getOriginalIndeterminateIds
()
{
const
uniqueIds
=
[];
const
labelIds
=
[];
let
issuableLabels
=
[];
// Collect unique label IDs for all checked issues
this
.
getElement
(
'
.selected_issue:checked
'
).
each
((
i
,
el
)
=>
{
issuableLabels
=
this
.
getElement
(
`#
${
this
.
prefixId
}${
el
.
dataset
.
id
}
`
).
data
(
'
labels
'
);
issuableLabels
.
forEach
((
labelId
)
=>
{
// Store unique IDs
if
(
uniqueIds
.
indexOf
(
labelId
)
===
-
1
)
{
uniqueIds
.
push
(
labelId
);
}
});
// Store array of IDs per issuable
labelIds
.
push
(
issuableLabels
);
});
// Add uniqueIds to add it as argument for _.intersection
labelIds
.
unshift
(
uniqueIds
);
// Return IDs that are present but not in all selected issueables
return
_
.
difference
(
uniqueIds
,
_
.
intersection
.
apply
(
this
,
labelIds
));
},
getElement
(
selector
)
{
this
.
scopeEl
=
this
.
scopeEl
||
$
(
'
.content
'
);
return
this
.
scopeEl
.
find
(
selector
);
},
};
app/assets/javascripts/issuable_bulk_update_sidebar.js
0 → 100644
View file @
08fcc1e9
/* eslint-disable class-methods-use-this, no-new */
/* global LabelsSelect */
/* global MilestoneSelect */
/* global IssueStatusSelect */
/* global SubscriptionSelect */
import
IssuableBulkUpdateActions
from
'
./issuable_bulk_update_actions
'
;
const
HIDDEN_CLASS
=
'
hidden
'
;
const
DISABLED_CONTENT_CLASS
=
'
disabled-content
'
;
const
SIDEBAR_EXPANDED_CLASS
=
'
right-sidebar-expanded issuable-bulk-update-sidebar
'
;
const
SIDEBAR_COLLAPSED_CLASS
=
'
right-sidebar-collapsed issuable-bulk-update-sidebar
'
;
export
default
class
IssuableBulkUpdateSidebar
{
constructor
()
{
this
.
initDomElements
();
this
.
bindEvents
();
this
.
initDropdowns
();
this
.
setupBulkUpdateActions
();
}
initDomElements
()
{
this
.
$page
=
$
(
'
.page-with-sidebar
'
);
this
.
$sidebar
=
$
(
'
.right-sidebar
'
);
this
.
$bulkEditCancelBtn
=
$
(
'
.js-bulk-update-menu-hide
'
);
this
.
$bulkEditSubmitBtn
=
$
(
'
.update-selected-issues
'
);
this
.
$bulkUpdateEnableBtn
=
$
(
'
.js-bulk-update-toggle
'
);
this
.
$otherFilters
=
$
(
'
.issues-other-filters
'
);
this
.
$checkAllContainer
=
$
(
'
.check-all-holder
'
);
this
.
$issueChecks
=
$
(
'
.issue-check
'
);
this
.
$issuesList
=
$
(
'
.selected_issue
'
);
this
.
$issuableIdsInput
=
$
(
'
#update_issuable_ids
'
);
}
bindEvents
()
{
this
.
$bulkUpdateEnableBtn
.
on
(
'
click
'
,
e
=>
this
.
toggleBulkEdit
(
e
,
true
));
this
.
$bulkEditCancelBtn
.
on
(
'
click
'
,
e
=>
this
.
toggleBulkEdit
(
e
,
false
));
this
.
$checkAllContainer
.
on
(
'
click
'
,
e
=>
this
.
selectAll
(
e
));
this
.
$issuesList
.
on
(
'
change
'
,
()
=>
this
.
updateFormState
());
this
.
$bulkEditSubmitBtn
.
on
(
'
click
'
,
()
=>
this
.
prepForSubmit
());
this
.
$checkAllContainer
.
on
(
'
click
'
,
()
=>
this
.
updateFormState
());
}
initDropdowns
()
{
new
LabelsSelect
();
new
MilestoneSelect
();
new
IssueStatusSelect
();
new
SubscriptionSelect
();
}
getNavHeight
()
{
const
navbarHeight
=
$
(
'
.navbar-gitlab
'
).
outerHeight
();
const
layoutNavHeight
=
$
(
'
.layout-nav
'
).
outerHeight
();
const
subNavScroll
=
$
(
'
.sub-nav-scroll
'
).
outerHeight
();
return
navbarHeight
+
layoutNavHeight
+
subNavScroll
;
}
initSidebar
()
{
if
(
!
this
.
navHeight
)
{
this
.
navHeight
=
this
.
getNavHeight
();
}
if
(
!
this
.
sidebarInitialized
)
{
$
(
document
).
off
(
'
scroll
'
).
on
(
'
scroll
'
,
_
.
throttle
(
this
.
setSidebarHeight
,
10
).
bind
(
this
));
$
(
window
).
off
(
'
resize
'
).
on
(
'
resize
'
,
_
.
throttle
(
this
.
setSidebarHeight
,
10
).
bind
(
this
));
this
.
sidebarInitialized
=
true
;
}
}
setupBulkUpdateActions
()
{
IssuableBulkUpdateActions
.
setOriginalDropdownData
();
}
updateFormState
()
{
const
noCheckedIssues
=
!
$
(
'
.selected_issue:checked
'
).
length
;
this
.
toggleSubmitButtonDisabled
(
noCheckedIssues
);
this
.
updateSelectedIssuableIds
();
IssuableBulkUpdateActions
.
setOriginalDropdownData
();
}
prepForSubmit
()
{
// if submit button is disabled, submission is blocked. This ensures we disable after
// form submission is carried out
setTimeout
(()
=>
this
.
$bulkEditSubmitBtn
.
disable
());
this
.
updateSelectedIssuableIds
();
}
toggleBulkEdit
(
e
,
enable
)
{
e
.
preventDefault
();
this
.
toggleSidebarDisplay
(
enable
);
this
.
toggleBulkEditButtonDisabled
(
enable
);
this
.
toggleOtherFiltersDisabled
(
enable
);
this
.
toggleCheckboxDisplay
(
enable
);
if
(
enable
)
{
this
.
initSidebar
();
}
}
updateSelectedIssuableIds
()
{
this
.
$issuableIdsInput
.
val
(
IssuableBulkUpdateSidebar
.
getCheckedIssueIds
());
}
selectAll
()
{
const
checkAllButtonState
=
this
.
$checkAllContainer
.
find
(
'
input
'
).
prop
(
'
checked
'
);
this
.
$issuesList
.
prop
(
'
checked
'
,
checkAllButtonState
);
}
toggleSidebarDisplay
(
show
)
{
this
.
$page
.
toggleClass
(
SIDEBAR_EXPANDED_CLASS
,
show
);
this
.
$page
.
toggleClass
(
SIDEBAR_COLLAPSED_CLASS
,
!
show
);
this
.
$sidebar
.
toggleClass
(
SIDEBAR_EXPANDED_CLASS
,
show
);
this
.
$sidebar
.
toggleClass
(
SIDEBAR_COLLAPSED_CLASS
,
!
show
);
}
toggleBulkEditButtonDisabled
(
disable
)
{
if
(
disable
)
{
this
.
$bulkUpdateEnableBtn
.
disable
();
}
else
{
this
.
$bulkUpdateEnableBtn
.
enable
();
}
}
toggleCheckboxDisplay
(
show
)
{
this
.
$checkAllContainer
.
toggleClass
(
HIDDEN_CLASS
,
!
show
);
this
.
$issueChecks
.
toggleClass
(
HIDDEN_CLASS
,
!
show
);
}
toggleOtherFiltersDisabled
(
disable
)
{
this
.
$otherFilters
.
toggleClass
(
DISABLED_CONTENT_CLASS
,
disable
);
}
toggleSubmitButtonDisabled
(
disable
)
{
if
(
disable
)
{
this
.
$bulkEditSubmitBtn
.
disable
();
}
else
{
this
.
$bulkEditSubmitBtn
.
enable
();
}
}
// loosely based on method of the same name in right_sidebar.js
setSidebarHeight
()
{
const
currentScrollDepth
=
window
.
pageYOffset
||
0
;
const
diff
=
this
.
navHeight
-
currentScrollDepth
;
if
(
diff
>
0
)
{
this
.
$sidebar
.
outerHeight
(
window
.
innerHeight
-
diff
);
}
else
{
this
.
$sidebar
.
outerHeight
(
'
100%
'
);
}
}
static
getCheckedIssueIds
()
{
const
$checkedIssues
=
$
(
'
.selected_issue:checked
'
);
if
(
$checkedIssues
.
length
>
0
)
{
return
$
.
map
(
$checkedIssues
,
value
=>
$
(
value
).
data
(
'
id
'
));
}
return
[];
}
}
app/assets/javascripts/issuable.js
→
app/assets/javascripts/issuable
_index
.js
View file @
08fcc1e9
/* eslint-disable no-param-reassign, func-names, no-var, camelcase, no-unused-vars, object-shorthand, space-before-function-paren, no-return-assign, comma-dangle, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, prefer-arrow-callback, wrap-iife, max-len */
/* eslint-disable no-param-reassign, func-names, no-var, camelcase, no-unused-vars, object-shorthand, space-before-function-paren, no-return-assign, comma-dangle, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, prefer-arrow-callback, wrap-iife, max-len */
/* global Issuable */
/* global IssuableIndex */
import
IssuableBulkUpdateSidebar
from
'
./issuable_bulk_update_sidebar
'
;
import
IssuableBulkUpdateActions
from
'
./issuable_bulk_update_actions
'
;
((
global
)
=>
{
((
global
)
=>
{
var
issuable_created
;
var
issuable_created
;
issuable_created
=
false
;
issuable_created
=
false
;
global
.
Issuable
=
{
global
.
Issuable
Index
=
{
init
:
function
()
{
init
:
function
(
pagePrefix
)
{
Issuable
.
initTemplates
();
Issuable
Index
.
initTemplates
();
Issuable
.
initSearch
();
Issuable
Index
.
initSearch
();
Issuable
.
initChecks
(
);
Issuable
Index
.
initBulkUpdate
(
pagePrefix
);
Issuable
.
initResetFilters
();
Issuable
Index
.
initResetFilters
();
Issuable
.
resetIncomingEmailToken
();
Issuable
Index
.
resetIncomingEmailToken
();
return
Issuable
.
initLabelFilterRemove
();
IssuableIndex
.
initLabelFilterRemove
();
},
},
initTemplates
:
function
()
{
initTemplates
:
function
()
{
return
Issuable
.
labelRow
=
_
.
template
(
'
<% _.each(labels, function(label){ %> <span class="label-row btn-group" role="group" aria-label="<%- label.title %>" style="color: <%- label.text_color %>;"> <a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%- label.color %>;" title="<%- label.description %>" data-container="body"> <%- label.title %> </a> <button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%- label.color %>;" data-label="<%- label.title %>"> <i class="fa fa-times"></i> </button> </span> <% }); %>
'
);
return
Issuable
Index
.
labelRow
=
_
.
template
(
'
<% _.each(labels, function(label){ %> <span class="label-row btn-group" role="group" aria-label="<%- label.title %>" style="color: <%- label.text_color %>;"> <a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%- label.color %>;" title="<%- label.description %>" data-container="body"> <%- label.title %> </a> <button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%- label.color %>;" data-label="<%- label.title %>"> <i class="fa fa-times"></i> </button> </span> <% }); %>
'
);
},
},
initSearch
:
function
()
{
initSearch
:
function
()
{
const
$searchInput
=
$
(
'
#issuable_search
'
);
const
$searchInput
=
$
(
'
#issuable_search
'
);
Issuable
.
initSearchState
(
$searchInput
);
Issuable
Index
.
initSearchState
(
$searchInput
);
// `immediate` param set to false debounces on the `trailing` edge, lets user finish typing
// `immediate` param set to false debounces on the `trailing` edge, lets user finish typing
const
debouncedExecSearch
=
_
.
debounce
(
Issuable
.
executeSearch
,
1000
,
false
);
const
debouncedExecSearch
=
_
.
debounce
(
Issuable
Index
.
executeSearch
,
1000
,
false
);
$searchInput
.
off
(
'
keyup
'
).
on
(
'
keyup
'
,
debouncedExecSearch
);
$searchInput
.
off
(
'
keyup
'
).
on
(
'
keyup
'
,
debouncedExecSearch
);
...
@@ -37,16 +40,16 @@
...
@@ -37,16 +40,16 @@
initSearchState
:
function
(
$searchInput
)
{
initSearchState
:
function
(
$searchInput
)
{
const
currentSearchVal
=
$searchInput
.
val
();
const
currentSearchVal
=
$searchInput
.
val
();
Issuable
.
searchState
=
{
Issuable
Index
.
searchState
=
{
elem
:
$searchInput
,
elem
:
$searchInput
,
current
:
currentSearchVal
current
:
currentSearchVal
};
};
Issuable
.
maybeFocusOnSearch
();
Issuable
Index
.
maybeFocusOnSearch
();
},
},
accessSearchPristine
:
function
(
set
)
{
accessSearchPristine
:
function
(
set
)
{
// store reference to previous value to prevent search on non-mutating keyup
// store reference to previous value to prevent search on non-mutating keyup
const
state
=
Issuable
.
searchState
;
const
state
=
Issuable
Index
.
searchState
;
const
currentSearchVal
=
state
.
elem
.
val
();
const
currentSearchVal
=
state
.
elem
.
val
();
if
(
set
)
{
if
(
set
)
{
...
@@ -56,10 +59,10 @@
...
@@ -56,10 +59,10 @@
}
}
},
},
maybeFocusOnSearch
:
function
()
{
maybeFocusOnSearch
:
function
()
{
const
currentSearchVal
=
Issuable
.
searchState
.
current
;
const
currentSearchVal
=
Issuable
Index
.
searchState
.
current
;
if
(
currentSearchVal
&&
currentSearchVal
!==
''
)
{
if
(
currentSearchVal
&&
currentSearchVal
!==
''
)
{
const
queryLength
=
currentSearchVal
.
length
;
const
queryLength
=
currentSearchVal
.
length
;
const
$searchInput
=
Issuable
.
searchState
.
elem
;
const
$searchInput
=
Issuable
Index
.
searchState
.
elem
;
/* The following ensures that the cursor is initially placed at
/* The following ensures that the cursor is initially placed at
* the end of search input when focus is applied. It accounts
* the end of search input when focus is applied. It accounts
...
@@ -80,7 +83,7 @@
...
@@ -80,7 +83,7 @@
const
$searchValue
=
$search
.
val
();
const
$searchValue
=
$search
.
val
();
const
$filtersForm
=
$
(
'
.js-filter-form
'
);
const
$filtersForm
=
$
(
'
.js-filter-form
'
);
const
$input
=
$
(
`input[name='
${
$searchName
}
']`
,
$filtersForm
);
const
$input
=
$
(
`input[name='
${
$searchName
}
']`
,
$filtersForm
);
const
isPristine
=
Issuable
.
accessSearchPristine
();
const
isPristine
=
Issuable
Index
.
accessSearchPristine
();
if
(
isPristine
)
{
if
(
isPristine
)
{
return
;
return
;
...
@@ -92,7 +95,7 @@
...
@@ -92,7 +95,7 @@
$input
.
val
(
$searchValue
);
$input
.
val
(
$searchValue
);
}
}
Issuable
.
filterResults
(
$filtersForm
);
Issuable
Index
.
filterResults
(
$filtersForm
);
},
},
initLabelFilterRemove
:
function
()
{
initLabelFilterRemove
:
function
()
{
return
$
(
document
).
off
(
'
click
'
,
'
.js-label-filter-remove
'
).
on
(
'
click
'
,
'
.js-label-filter-remove
'
,
function
(
e
)
{
return
$
(
document
).
off
(
'
click
'
,
'
.js-label-filter-remove
'
).
on
(
'
click
'
,
'
.js-label-filter-remove
'
,
function
(
e
)
{
...
@@ -103,7 +106,7 @@
...
@@ -103,7 +106,7 @@
return
this
.
value
===
$button
.
data
(
'
label
'
);
return
this
.
value
===
$button
.
data
(
'
label
'
);
}).
remove
();
}).
remove
();
// Submit the form to get new data
// Submit the form to get new data
Issuable
.
filterResults
(
$
(
'
.filter-form
'
));
Issuable
Index
.
filterResults
(
$
(
'
.filter-form
'
));
});
});
},
},
filterResults
:
(
function
(
_this
)
{
filterResults
:
(
function
(
_this
)
{
...
@@ -132,38 +135,18 @@
...
@@ -132,38 +135,18 @@
gl
.
utils
.
visitUrl
(
baseIssuesUrl
);
gl
.
utils
.
visitUrl
(
baseIssuesUrl
);
});
});
},
},
initChecks
:
function
()
{
initBulkUpdate
:
function
(
pagePrefix
)
{
this
.
issuableBulkActions
=
$
(
'
.bulk-update
'
).
data
(
'
bulkActions
'
);
const
userCanBulkUpdate
=
$
(
'
.issues-bulk-update
'
).
length
>
0
;
$
(
'
.check_all_issues
'
).
off
(
'
click
'
).
on
(
'
click
'
,
function
()
{
const
alreadyInitialized
=
!!
this
.
bulkUpdateSidebar
;
$
(
'
.selected_issue
'
).
prop
(
'
checked
'
,
this
.
checked
);
return
Issuable
.
checkChanged
();
if
(
userCanBulkUpdate
&&
!
alreadyInitialized
)
{
});
IssuableBulkUpdateActions
.
init
({
return
$
(
'
.selected_issue
'
).
off
(
'
change
'
).
on
(
'
change
'
,
Issuable
.
checkChanged
.
bind
(
this
));
prefixId
:
pagePrefix
,
},
checkChanged
:
function
()
{
const
$checkedIssues
=
$
(
'
.selected_issue:checked
'
);
const
$updateIssuesIds
=
$
(
'
#update_issuable_ids
'
);
const
$issuesOtherFilters
=
$
(
'
.issues-other-filters
'
);
const
$issuesBulkUpdate
=
$
(
'
.issues_bulk_update
'
);
this
.
issuableBulkActions
.
willUpdateLabels
=
false
;
this
.
issuableBulkActions
.
setOriginalDropdownData
();
if
(
$checkedIssues
.
length
>
0
)
{
const
ids
=
$
.
map
(
$checkedIssues
,
function
(
value
)
{
return
$
(
value
).
data
(
'
id
'
);
});
});
$updateIssuesIds
.
val
(
ids
);
$issuesOtherFilters
.
hide
();
this
.
bulkUpdateSidebar
=
new
IssuableBulkUpdateSidebar
();
$issuesBulkUpdate
.
show
();
}
else
{
$updateIssuesIds
.
val
([]);
$issuesBulkUpdate
.
hide
();
$issuesOtherFilters
.
show
();
}
}
return
true
;
},
},
resetIncomingEmailToken
:
function
()
{
resetIncomingEmailToken
:
function
()
{
$
(
'
.incoming-email-token-reset
'
).
on
(
'
click
'
,
function
(
e
)
{
$
(
'
.incoming-email-token-reset
'
).
on
(
'
click
'
,
function
(
e
)
{
e
.
preventDefault
();
e
.
preventDefault
();
...
...
app/assets/javascripts/issues_bulk_assignment.js
deleted
100644 → 0
View file @
a89ebdb1
/* eslint-disable comma-dangle, quotes, consistent-return, func-names, array-callback-return, space-before-function-paren, prefer-arrow-callback, max-len, no-unused-expressions, no-sequences, no-underscore-dangle, no-unused-vars, no-param-reassign */
/* global Issuable */
/* global Flash */
((
global
)
=>
{
class
IssuableBulkActions
{
constructor
({
container
,
form
,
issues
,
prefixId
}
=
{})
{
this
.
prefixId
=
prefixId
||
'
issue_
'
;
this
.
form
=
form
||
this
.
getElement
(
'
.bulk-update
'
);
this
.
$labelDropdown
=
this
.
form
.
find
(
'
.js-label-select
'
);
this
.
issues
=
issues
||
this
.
getElement
(
'
.issues-list .issue
'
);
this
.
form
.
data
(
'
bulkActions
'
,
this
);
this
.
willUpdateLabels
=
false
;
this
.
bindEvents
();
// Fixes bulk-assign not working when navigating through pages
Issuable
.
initChecks
();
}
bindEvents
()
{
return
this
.
form
.
off
(
'
submit
'
).
on
(
'
submit
'
,
this
.
onFormSubmit
.
bind
(
this
));
}
onFormSubmit
(
e
)
{
e
.
preventDefault
();
return
this
.
submit
();
}
submit
()
{
const
_this
=
this
;
const
xhr
=
$
.
ajax
({
url
:
this
.
form
.
attr
(
'
action
'
),
method
:
this
.
form
.
attr
(
'
method
'
),
dataType
:
'
JSON
'
,
data
:
this
.
getFormDataAsObject
()
});
xhr
.
done
(()
=>
window
.
location
.
reload
());
xhr
.
fail
(()
=>
new
Flash
(
"
Issue update failed
"
));
return
xhr
.
always
(
this
.
onFormSubmitAlways
.
bind
(
this
));
}
onFormSubmitAlways
()
{
return
this
.
form
.
find
(
'
[type="submit"]
'
).
enable
();
}
getSelectedIssues
()
{
return
this
.
issues
.
has
(
'
.selected_issue:checked
'
);
}
getLabelsFromSelection
()
{
const
labels
=
[];
this
.
getSelectedIssues
().
map
(
function
()
{
const
labelsData
=
$
(
this
).
data
(
'
labels
'
);
if
(
labelsData
)
{
return
labelsData
.
map
(
function
(
labelId
)
{
if
(
labels
.
indexOf
(
labelId
)
===
-
1
)
{
return
labels
.
push
(
labelId
);
}
});
}
});
return
labels
;
}
/**
* Will return only labels that were marked previously and the user has unmarked
* @return {Array} Label IDs
*/
getUnmarkedIndeterminedLabels
()
{
const
result
=
[];
const
labelsToKeep
=
this
.
$labelDropdown
.
data
(
'
indeterminate
'
);
this
.
getLabelsFromSelection
().
forEach
((
id
)
=>
{
if
(
labelsToKeep
.
indexOf
(
id
)
===
-
1
)
{
result
.
push
(
id
);
}
});
return
result
;
}
/**
* Simple form serialization, it will return just what we need
* Returns key/value pairs from form data
*/
getFormDataAsObject
()
{
const
formData
=
{
update
:
{
state_event
:
this
.
form
.
find
(
'
input[name="update[state_event]"]
'
).
val
(),
// For Merge Requests
assignee_id
:
this
.
form
.
find
(
'
input[name="update[assignee_id]"]
'
).
val
(),
// For Issues
assignee_ids
:
[
this
.
form
.
find
(
'
input[name="update[assignee_ids][]"]
'
).
val
()],
milestone_id
:
this
.
form
.
find
(
'
input[name="update[milestone_id]"]
'
).
val
(),
issuable_ids
:
this
.
form
.
find
(
'
input[name="update[issuable_ids]"]
'
).
val
(),
subscription_event
:
this
.
form
.
find
(
'
input[name="update[subscription_event]"]
'
).
val
(),
add_label_ids
:
[],
remove_label_ids
:
[]
}
};
if
(
this
.
willUpdateLabels
)
{
formData
.
update
.
add_label_ids
=
this
.
$labelDropdown
.
data
(
'
marked
'
);
formData
.
update
.
remove_label_ids
=
this
.
$labelDropdown
.
data
(
'
unmarked
'
);
}
return
formData
;
}
setOriginalDropdownData
()
{
const
$labelSelect
=
$
(
'
.bulk-update .js-label-select
'
);
$labelSelect
.
data
(
'
common
'
,
this
.
getOriginalCommonIds
());
$labelSelect
.
data
(
'
marked
'
,
this
.
getOriginalMarkedIds
());
$labelSelect
.
data
(
'
indeterminate
'
,
this
.
getOriginalIndeterminateIds
());
}
// From issuable's initial bulk selection
getOriginalCommonIds
()
{
const
labelIds
=
[];
this
.
getElement
(
'
.selected_issue:checked
'
).
each
((
i
,
el
)
=>
{
labelIds
.
push
(
this
.
getElement
(
`#
${
this
.
prefixId
}${
el
.
dataset
.
id
}
`
).
data
(
'
labels
'
));
});
return
_
.
intersection
.
apply
(
this
,
labelIds
);
}
// From issuable's initial bulk selection
getOriginalMarkedIds
()
{
const
labelIds
=
[];
this
.
getElement
(
'
.selected_issue:checked
'
).
each
((
i
,
el
)
=>
{
labelIds
.
push
(
this
.
getElement
(
`#
${
this
.
prefixId
}${
el
.
dataset
.
id
}
`
).
data
(
'
labels
'
));
});
return
_
.
intersection
.
apply
(
this
,
labelIds
);
}
// From issuable's initial bulk selection
getOriginalIndeterminateIds
()
{
const
uniqueIds
=
[];
const
labelIds
=
[];
let
issuableLabels
=
[];
// Collect unique label IDs for all checked issues
this
.
getElement
(
'
.selected_issue:checked
'
).
each
((
i
,
el
)
=>
{
issuableLabels
=
this
.
getElement
(
`#
${
this
.
prefixId
}${
el
.
dataset
.
id
}
`
).
data
(
'
labels
'
);
issuableLabels
.
forEach
((
labelId
)
=>
{
// Store unique IDs
if
(
uniqueIds
.
indexOf
(
labelId
)
===
-
1
)
{
uniqueIds
.
push
(
labelId
);
}
});
// Store array of IDs per issuable
labelIds
.
push
(
issuableLabels
);
});
// Add uniqueIds to add it as argument for _.intersection
labelIds
.
unshift
(
uniqueIds
);
// Return IDs that are present but not in all selected issueables
return
_
.
difference
(
uniqueIds
,
_
.
intersection
.
apply
(
this
,
labelIds
));
}
getElement
(
selector
)
{
this
.
scopeEl
=
this
.
scopeEl
||
$
(
'
.content
'
);
return
this
.
scopeEl
.
find
(
selector
);
}
}
global
.
IssuableBulkActions
=
IssuableBulkActions
;
})(
window
.
gl
||
(
window
.
gl
=
{}));
app/assets/javascripts/labels_select.js
View file @
08fcc1e9
...
@@ -2,6 +2,8 @@
...
@@ -2,6 +2,8 @@
/* global Issuable */
/* global Issuable */
/* global ListLabel */
/* global ListLabel */
import
IssuableBulkUpdateActions
from
'
./issuable_bulk_update_actions
'
;
(
function
()
{
(
function
()
{
this
.
LabelsSelect
=
(
function
()
{
this
.
LabelsSelect
=
(
function
()
{
function
LabelsSelect
(
els
)
{
function
LabelsSelect
(
els
)
{
...
@@ -430,20 +432,15 @@
...
@@ -430,20 +432,15 @@
if
(
$
(
'
.selected_issue:checked
'
).
length
)
{
if
(
$
(
'
.selected_issue:checked
'
).
length
)
{
return
;
return
;
}
}
return
$
(
'
.issues
_bulk_
update .labels-filter .dropdown-toggle-text
'
).
text
(
'
Label
'
);
return
$
(
'
.issues
-bulk-
update .labels-filter .dropdown-toggle-text
'
).
text
(
'
Label
'
);
};
};
LabelsSelect
.
prototype
.
enableBulkLabelDropdown
=
function
()
{
LabelsSelect
.
prototype
.
enableBulkLabelDropdown
=
function
()
{
var
issuableBulkActions
;
IssuableBulkUpdateActions
.
willUpdateLabels
=
true
;
if
(
$
(
'
.selected_issue:checked
'
).
length
)
{
issuableBulkActions
=
$
(
'
.bulk-update
'
).
data
(
'
bulkActions
'
);
return
issuableBulkActions
.
willUpdateLabels
=
true
;
}
};
};
LabelsSelect
.
prototype
.
setDropdownData
=
function
(
$dropdown
,
isMarking
,
value
)
{
LabelsSelect
.
prototype
.
setDropdownData
=
function
(
$dropdown
,
isMarking
,
value
)
{
var
i
,
markedIds
,
unmarkedIds
,
indeterminateIds
;
var
i
,
markedIds
,
unmarkedIds
,
indeterminateIds
;
var
issuableBulkActions
=
$
(
'
.bulk-update
'
).
data
(
'
bulkActions
'
);
markedIds
=
$dropdown
.
data
(
'
marked
'
)
||
[];
markedIds
=
$dropdown
.
data
(
'
marked
'
)
||
[];
unmarkedIds
=
$dropdown
.
data
(
'
unmarked
'
)
||
[];
unmarkedIds
=
$dropdown
.
data
(
'
unmarked
'
)
||
[];
...
@@ -469,13 +466,13 @@
...
@@ -469,13 +466,13 @@
}
}
// If an indeterminate item is being unmarked
// If an indeterminate item is being unmarked
if
(
issuableBulk
Actions
.
getOriginalIndeterminateIds
().
indexOf
(
value
)
>
-
1
)
{
if
(
IssuableBulkUpdate
Actions
.
getOriginalIndeterminateIds
().
indexOf
(
value
)
>
-
1
)
{
unmarkedIds
.
push
(
value
);
unmarkedIds
.
push
(
value
);
}
}
// If a marked item is being unmarked
// If a marked item is being unmarked
// (a marked item could also be a label that is present in all selection)
// (a marked item could also be a label that is present in all selection)
if
(
issuableBulk
Actions
.
getOriginalCommonIds
().
indexOf
(
value
)
>
-
1
)
{
if
(
IssuableBulkUpdate
Actions
.
getOriginalCommonIds
().
indexOf
(
value
)
>
-
1
)
{
unmarkedIds
.
push
(
value
);
unmarkedIds
.
push
(
value
);
}
}
}
}
...
...
app/assets/javascripts/main.js
View file @
08fcc1e9
...
@@ -105,12 +105,11 @@ import './group_label_subscription';
...
@@ -105,12 +105,11 @@ import './group_label_subscription';
import
'
./groups_select
'
;
import
'
./groups_select
'
;
import
'
./header
'
;
import
'
./header
'
;
import
'
./importer_status
'
;
import
'
./importer_status
'
;
import
'
./issuable
'
;
import
'
./issuable
_index
'
;
import
'
./issuable_context
'
;
import
'
./issuable_context
'
;
import
'
./issuable_form
'
;
import
'
./issuable_form
'
;
import
'
./issue
'
;
import
'
./issue
'
;
import
'
./issue_status_select
'
;
import
'
./issue_status_select
'
;
import
'
./issues_bulk_assignment
'
;
import
'
./label_manager
'
;
import
'
./label_manager
'
;
import
'
./labels
'
;
import
'
./labels
'
;
import
'
./labels_select
'
;
import
'
./labels_select
'
;
...
...
app/assets/stylesheets/framework/common.scss
View file @
08fcc1e9
...
@@ -449,3 +449,9 @@ table {
...
@@ -449,3 +449,9 @@ table {
word-wrap
:
break-word
;
word-wrap
:
break-word
;
}
}
}
}
.disabled-content
{
pointer-events
:
none
;
opacity
:
.5
;
}
app/assets/stylesheets/framework/filters.scss
View file @
08fcc1e9
...
@@ -22,12 +22,6 @@
...
@@ -22,12 +22,6 @@
}
}
@media
(
min-width
:
$screen-sm-min
)
{
@media
(
min-width
:
$screen-sm-min
)
{
.issues_bulk_update
{
.dropdown-menu-toggle
:not
(
.wide
)
{
width
:
132px
;
}
}
.filter-item
:not
(
:last-child
)
{
.filter-item
:not
(
:last-child
)
{
margin-right
:
6px
;
margin-right
:
6px
;
}
}
...
@@ -375,12 +369,6 @@
...
@@ -375,12 +369,6 @@
padding
:
0
;
padding
:
0
;
}
}
@media
(
min-width
:
$screen-sm-min
)
and
(
max-width
:
$screen-sm-max
)
{
.issue-bulk-update-dropdown-toggle
{
width
:
100px
;
}
}
@media
(
max-width
:
$screen-xs-max
)
{
@media
(
max-width
:
$screen-xs-max
)
{
.issues-details-filters
{
.issues-details-filters
{
padding
:
0
0
10px
;
padding
:
0
0
10px
;
...
...
app/assets/stylesheets/framework/mobile.scss
View file @
08fcc1e9
...
@@ -29,10 +29,6 @@
...
@@ -29,10 +29,6 @@
display
:
none
;
display
:
none
;
}
}
.issues-holder
.issue-check
{
display
:
none
;
}
.rss-btn
{
.rss-btn
{
display
:
none
;
display
:
none
;
}
}
...
...
app/assets/stylesheets/framework/sidebar.scss
View file @
08fcc1e9
...
@@ -33,7 +33,7 @@
...
@@ -33,7 +33,7 @@
padding-right
:
0
;
padding-right
:
0
;
@media
(
min-width
:
$screen-sm-min
)
{
@media
(
min-width
:
$screen-sm-min
)
{
&
:not
(
.wiki-sidebar
)
:not
(
.build-sidebar
)
.content-wrapper
{
&
:not
(
.wiki-sidebar
)
:not
(
.build-sidebar
)
:not
(
.issuable-bulk-update-sidebar
)
.content-wrapper
{
padding-right
:
$gutter_collapsed_width
;
padding-right
:
$gutter_collapsed_width
;
}
}
...
@@ -56,7 +56,7 @@
...
@@ -56,7 +56,7 @@
z-index
:
300
;
z-index
:
300
;
@media
(
min-width
:
$screen-sm-min
)
and
(
max-width
:
$screen-sm-max
)
{
@media
(
min-width
:
$screen-sm-min
)
and
(
max-width
:
$screen-sm-max
)
{
&
:not
(
.wiki-sidebar
)
:not
(
.build-sidebar
)
.content-wrapper
{
&
:not
(
.wiki-sidebar
)
:not
(
.build-sidebar
)
:not
(
.issuable-bulk-update-sidebar
)
.content-wrapper
{
padding-right
:
$gutter_collapsed_width
;
padding-right
:
$gutter_collapsed_width
;
}
}
}
}
...
@@ -88,3 +88,35 @@
...
@@ -88,3 +88,35 @@
min-height
:
100%
;
min-height
:
100%
;
}
}
}
}
@mixin
maintain-sidebar-dimensions
{
display
:
block
;
width
:
$gutter-width
;
padding
:
10px
20px
;
}
.issues-bulk-update.right-sidebar
{
@include
maintain-sidebar-dimensions
;
transition
:
right
$sidebar-transition-duration
;
right
:
-
$gutter-width
;
&
.right-sidebar-expanded
{
@include
maintain-sidebar-dimensions
;
right
:
0
;
}
&
.right-sidebar-collapsed
{
@include
maintain-sidebar-dimensions
;
right
:
-
$gutter-width
;
.block
{
padding
:
16px
0
;
width
:
250px
;
border-bottom
:
1px
solid
$border-color
;
}
}
.issuable-sidebar
{
padding
:
0
3px
;
}
}
app/views/projects/issues/_issue.html.haml
View file @
08fcc1e9
%li
{
id:
dom_id
(
issue
),
class:
issue_css_classes
(
issue
),
url:
issue_path
(
issue
),
data:
{
labels:
issue
.
label_ids
,
id:
issue
.
id
}
}
%li
{
id:
dom_id
(
issue
),
class:
issue_css_classes
(
issue
),
url:
issue_path
(
issue
),
data:
{
labels:
issue
.
label_ids
,
id:
issue
.
id
}
}
.issue-box
.issue-box
-
if
@
bulk_edit
-
if
@
can_bulk_update
.issue-check
.issue-check
.hidden
=
check_box_tag
dom_id
(
issue
,
"selected"
),
nil
,
false
,
'data-id'
=>
issue
.
id
,
class:
"selected_issue"
=
check_box_tag
dom_id
(
issue
,
"selected"
),
nil
,
false
,
'data-id'
=>
issue
.
id
,
class:
"selected_issue"
.issue-info-container
.issue-info-container
.issue-title.title
.issue-title.title
...
...
app/views/projects/issues/index.html.haml
View file @
08fcc1e9
-
@no_container
=
true
-
@no_container
=
true
-
@
bulk_edit
=
can?
(
current_user
,
:admin_issue
,
@project
)
-
@
can_bulk_update
=
can?
(
current_user
,
:admin_issue
,
@project
)
-
page_title
"Issues"
-
page_title
"Issues"
-
new_issue_email
=
@project
.
new_issue_address
(
current_user
)
-
new_issue_email
=
@project
.
new_issue_address
(
current_user
)
...
@@ -26,6 +26,8 @@
...
@@ -26,6 +26,8 @@
-
if
current_user
-
if
current_user
%button
.csv_download_link.btn.append-right-10.has-tooltip
{
title:
'Export as CSV'
}
%button
.csv_download_link.btn.append-right-10.has-tooltip
{
title:
'Export as CSV'
}
=
icon
(
'download'
)
=
icon
(
'download'
)
-
if
@can_bulk_update
=
button_tag
"Edit Issues"
,
class:
"btn btn-default js-bulk-update-toggle"
=
link_to
new_namespace_project_issue_path
(
@project
.
namespace
,
=
link_to
new_namespace_project_issue_path
(
@project
.
namespace
,
@project
,
@project
,
issue:
{
assignee_id:
issues_finder
.
assignee
.
try
(
:id
),
issue:
{
assignee_id:
issues_finder
.
assignee
.
try
(
:id
),
...
@@ -36,6 +38,9 @@
...
@@ -36,6 +38,9 @@
New issue
New issue
=
render
'shared/issuable/search_bar'
,
type: :issues
=
render
'shared/issuable/search_bar'
,
type: :issues
-
if
@can_bulk_update
=
render
'shared/issuable/bulk_update_sidebar'
,
type: :issues
.issues-holder
.issues-holder
=
render
'issues'
=
render
'issues'
-
if
new_issue_email
-
if
new_issue_email
...
...
app/views/projects/merge_requests/_merge_request.html.haml
View file @
08fcc1e9
%li
{
id:
dom_id
(
merge_request
),
class:
mr_css_classes
(
merge_request
),
data:
{
labels:
merge_request
.
label_ids
,
id:
merge_request
.
id
}
}
%li
{
id:
dom_id
(
merge_request
),
class:
mr_css_classes
(
merge_request
),
data:
{
labels:
merge_request
.
label_ids
,
id:
merge_request
.
id
}
}
-
if
@
bulk_edit
-
if
@
can_bulk_update
.issue-check
.issue-check
.hidden
=
check_box_tag
dom_id
(
merge_request
,
"selected"
),
nil
,
false
,
'data-id'
=>
merge_request
.
id
,
class:
"selected_issue"
=
check_box_tag
dom_id
(
merge_request
,
"selected"
),
nil
,
false
,
'data-id'
=>
merge_request
.
id
,
class:
"selected_issue"
.issue-info-container
.issue-info-container
...
...
app/views/projects/merge_requests/index.html.haml
View file @
08fcc1e9
-
@no_container
=
true
-
@no_container
=
true
-
@
bulk_edit
=
can?
(
current_user
,
:admin_merge_request
,
@project
)
-
@
can_bulk_update
=
can?
(
current_user
,
:admin_merge_request
,
@project
)
-
page_title
"Merge Requests"
-
page_title
"Merge Requests"
-
unless
@project
.
default_issues_tracker?
-
unless
@project
.
default_issues_tracker?
...
@@ -18,6 +18,8 @@
...
@@ -18,6 +18,8 @@
.top-area
.top-area
=
render
'shared/issuable/nav'
,
type: :merge_requests
=
render
'shared/issuable/nav'
,
type: :merge_requests
.nav-controls
.nav-controls
-
if
@can_bulk_update
=
button_tag
"Edit Merge Requests"
,
class:
"btn js-bulk-update-toggle"
-
merge_project
=
can?
(
current_user
,
:create_merge_request
,
@project
)
?
@project
:
(
current_user
&&
current_user
.
fork_of
(
@project
))
-
merge_project
=
can?
(
current_user
,
:create_merge_request
,
@project
)
?
@project
:
(
current_user
&&
current_user
.
fork_of
(
@project
))
-
if
merge_project
-
if
merge_project
=
link_to
new_namespace_project_merge_request_path
(
merge_project
.
namespace
,
merge_project
),
class:
"btn btn-new"
,
title:
"New merge request"
do
=
link_to
new_namespace_project_merge_request_path
(
merge_project
.
namespace
,
merge_project
),
class:
"btn btn-new"
,
title:
"New merge request"
do
...
@@ -25,6 +27,9 @@
...
@@ -25,6 +27,9 @@
=
render
'shared/issuable/search_bar'
,
type: :merge_requests
=
render
'shared/issuable/search_bar'
,
type: :merge_requests
-
if
@can_bulk_update
=
render
'shared/issuable/bulk_update_sidebar'
,
type: :merge_requests
.merge-requests-holder
.merge-requests-holder
=
render
'merge_requests'
=
render
'merge_requests'
-
else
-
else
...
...
app/views/shared/issuable/_bulk_update_sidebar.html.haml
0 → 100644
View file @
08fcc1e9
-
type
=
local_assigns
.
fetch
(
:type
)
%aside
.issues-bulk-update.js-right-sidebar.right-sidebar.affix-top
{
data:
{
"offset-top"
=>
"50"
,
"spy"
=>
"affix"
},
"aria-live"
=>
"polite"
}
.issuable-sidebar
=
form_tag
[
:bulk_update
,
@project
.
namespace
.
becomes
(
Namespace
),
@project
,
type
],
method: :post
,
class:
"bulk-update"
do
.block
.filter-item.inline.update-issues-btn.pull-left
=
button_tag
"Update all"
,
class:
"btn update-selected-issues btn-info"
,
disabled:
true
=
button_tag
"Cancel"
,
class:
"btn btn-default js-bulk-update-menu-hide pull-right"
.block
.title
Status
.filter-item
=
dropdown_tag
(
"Select status"
,
options:
{
toggle_class:
"js-issue-status"
,
title:
"Change status"
,
dropdown_class:
"dropdown-menu-status dropdown-menu-selectable"
,
data:
{
field_name:
"update[state_event]"
,
default_label:
"Status"
}
}
)
do
%ul
%li
%a
{
href:
"#"
,
data:
{
id:
"reopen"
}
}
Open
%li
%a
{
href:
"#"
,
data:
{
id:
"close"
}
}
Closed
.block
.title
Assignee
.filter-item
-
if
type
==
:issues
-
field_name
=
"update[assignee_ids][]"
-
else
-
field_name
=
"update[assignee_id]"
=
dropdown_tag
(
"Select assignee"
,
options:
{
toggle_class:
"js-user-search js-update-assignee js-filter-submit js-filter-bulk-update"
,
title:
"Assign to"
,
filter:
true
,
dropdown_class:
"dropdown-menu-user dropdown-menu-selectable"
,
placeholder:
"Search authors"
,
data:
{
first_user:
(
current_user
.
username
if
current_user
),
null_user:
true
,
current_user:
true
,
project_id:
@project
.
id
,
field_name:
field_name
}
})
.block
.title
Milestone
.filter-item
=
dropdown_tag
(
"Select milestone"
,
options:
{
title:
"Assign milestone"
,
toggle_class:
"js-milestone-select js-extra-options js-filter-submit js-filter-bulk-update"
,
filter:
true
,
dropdown_class:
"dropdown-menu-selectable dropdown-menu-milestone"
,
placeholder:
"Search milestones"
,
data:
{
show_no:
true
,
field_name:
"update[milestone_id]"
,
project_id:
@project
.
id
,
milestones:
namespace_project_milestones_path
(
@project
.
namespace
,
@project
,
:json
),
use_id:
true
,
default_label:
"Milestone"
}
})
.block
.title
Labels
.filter-item.labels-filter
=
render
"shared/issuable/label_dropdown"
,
classes:
[
"js-filter-bulk-update"
,
"js-multiselect"
],
dropdown_title:
"Apply a label"
,
show_create:
false
,
show_footer:
false
,
extra_options:
false
,
filter_submit:
false
,
data_options:
{
persist_when_hide:
"true"
,
field_name:
"update[label_ids][]"
,
show_no:
false
,
show_any:
false
,
use_id:
true
,
default_label:
"Labels"
},
label_name:
"Select labels"
,
no_default_styles:
true
.block
.title
Subscriptions
.filter-item
=
dropdown_tag
(
"Select subscription"
,
options:
{
toggle_class:
"js-subscription-event"
,
title:
"Change subscription"
,
dropdown_class:
"dropdown-menu-selectable"
,
data:
{
field_name:
"update[subscription_event]"
,
default_label:
"Subscription"
}
}
)
do
%ul
%li
%a
{
href:
"#"
,
data:
{
id:
"subscribe"
}
}
Subscribe
%li
%a
{
href:
"#"
,
data:
{
id:
"unsubscribe"
}
}
Unsubscribe
=
hidden_field_tag
"update[issuable_ids]"
,
[]
=
hidden_field_tag
:state_event
,
params
[
:state_event
]
app/views/shared/issuable/_filter.html.haml
View file @
08fcc1e9
...
@@ -7,10 +7,6 @@
...
@@ -7,10 +7,6 @@
=
form_tag
page_filter_path
(
without:
[
:assignee_id
,
:author_id
,
:milestone_title
,
:label_name
,
:search
]),
method: :get
,
class:
'filter-form js-filter-form'
do
=
form_tag
page_filter_path
(
without:
[
:assignee_id
,
:author_id
,
:milestone_title
,
:label_name
,
:search
]),
method: :get
,
class:
'filter-form js-filter-form'
do
-
if
params
[
:search
].
present?
-
if
params
[
:search
].
present?
=
hidden_field_tag
:search
,
params
[
:search
]
=
hidden_field_tag
:search
,
params
[
:search
]
-
if
@bulk_edit
.check-all-holder
=
check_box_tag
"check_all_issues"
,
nil
,
false
,
class:
"check_all_issues left"
.issues-other-filters
.issues-other-filters
.filter-item.inline
.filter-item.inline
-
if
params
[
:author_id
].
present?
-
if
params
[
:author_id
].
present?
...
@@ -37,35 +33,6 @@
...
@@ -37,35 +33,6 @@
.pull-right
.pull-right
=
render
'shared/sort_dropdown'
=
render
'shared/sort_dropdown'
-
if
@bulk_edit
.issues_bulk_update.hide
=
form_tag
[
:bulk_update
,
@project
.
namespace
.
becomes
(
Namespace
),
@project
,
type
],
method: :post
,
class:
'bulk-update'
do
.filter-item.inline
=
dropdown_tag
(
"Status"
,
options:
{
toggle_class:
"issue-bulk-update-dropdown-toggle js-issue-status"
,
title:
"Change status"
,
dropdown_class:
"dropdown-menu-status dropdown-menu-selectable"
,
data:
{
field_name:
"update[state_event]"
,
default_label:
"Status"
}
}
)
do
%ul
%li
%a
{
href:
"#"
,
data:
{
id:
"reopen"
}
}
Open
%li
%a
{
href:
"#"
,
data:
{
id:
"close"
}
}
Closed
.filter-item.inline
=
dropdown_tag
(
"Assignee"
,
options:
{
toggle_class:
"issue-bulk-update-dropdown-toggle js-user-search js-update-assignee js-filter-submit js-filter-bulk-update"
,
title:
"Assign to"
,
filter:
true
,
dropdown_class:
"dropdown-menu-user dropdown-menu-selectable"
,
placeholder:
"Search authors"
,
data:
{
first_user:
(
current_user
.
username
if
current_user
),
null_user:
true
,
current_user:
true
,
project_id:
@project
.
id
,
field_name:
"update[assignee_id]"
,
default_label:
"Assignee"
}
})
.filter-item.inline
=
dropdown_tag
(
"Milestone"
,
options:
{
title:
"Assign milestone"
,
toggle_class:
'issue-bulk-update-dropdown-toggle js-milestone-select js-extra-options js-filter-submit js-filter-bulk-update'
,
filter:
true
,
dropdown_class:
"dropdown-menu-selectable dropdown-menu-milestone"
,
placeholder:
"Search milestones"
,
data:
{
show_no:
true
,
field_name:
"update[milestone_id]"
,
default_label:
"Milestone"
,
project_id:
@project
.
id
,
milestones:
namespace_project_milestones_path
(
@project
.
namespace
,
@project
,
:json
),
use_id:
true
}
})
.filter-item.inline.labels-filter
=
render
"shared/issuable/label_dropdown"
,
classes:
[
'js-filter-bulk-update'
,
'js-multiselect'
],
dropdown_title:
'Apply a label'
,
show_create:
false
,
show_footer:
false
,
extra_options:
false
,
filter_submit:
false
,
data_options:
{
persist_when_hide:
"true"
,
field_name:
"update[label_ids][]"
,
show_no:
false
,
show_any:
false
,
use_id:
true
}
.filter-item.inline
=
dropdown_tag
(
"Subscription"
,
options:
{
toggle_class:
"issue-bulk-update-dropdown-toggle js-subscription-event"
,
title:
"Change subscription"
,
dropdown_class:
"dropdown-menu-selectable"
,
data:
{
field_name:
"update[subscription_event]"
,
default_label:
"Subscription"
}
}
)
do
%ul
%li
%a
{
href:
"#"
,
data:
{
id:
"subscribe"
}
}
Subscribe
%li
%a
{
href:
"#"
,
data:
{
id:
"unsubscribe"
}
}
Unsubscribe
=
hidden_field_tag
'update[issuable_ids]'
,
[]
=
hidden_field_tag
:state_event
,
params
[
:state_event
]
.filter-item.inline
=
button_tag
"Update
#{
type
.
to_s
.
humanize
(
capitalize:
false
)
}
"
,
class:
"btn update_selected_issues btn-save"
-
has_labels
=
@labels
&&
@labels
.
any?
-
has_labels
=
@labels
&&
@labels
.
any?
.row-content-block.second-block.filtered-labels
{
class:
(
"hidden"
unless
has_labels
)
}
.row-content-block.second-block.filtered-labels
{
class:
(
"hidden"
unless
has_labels
)
}
-
if
has_labels
-
if
has_labels
...
...
app/views/shared/issuable/_label_dropdown.html.haml
View file @
08fcc1e9
...
@@ -11,6 +11,8 @@
...
@@ -11,6 +11,8 @@
-
dropdown_title
=
local_assigns
.
fetch
(
:dropdown_title
,
"Filter by label"
)
-
dropdown_title
=
local_assigns
.
fetch
(
:dropdown_title
,
"Filter by label"
)
-
dropdown_data
=
{
toggle:
'dropdown'
,
field_name:
"label_name[]"
,
show_no:
"true"
,
show_any:
"true"
,
namespace_path:
@project
.
try
(
:namespace
).
try
(
:full_path
),
project_path:
@project
.
try
(
:path
),
labels:
labels_filter_path
,
default_label:
"Labels"
}
-
dropdown_data
=
{
toggle:
'dropdown'
,
field_name:
"label_name[]"
,
show_no:
"true"
,
show_any:
"true"
,
namespace_path:
@project
.
try
(
:namespace
).
try
(
:full_path
),
project_path:
@project
.
try
(
:path
),
labels:
labels_filter_path
,
default_label:
"Labels"
}
-
dropdown_data
.
merge!
(
data_options
)
-
dropdown_data
.
merge!
(
data_options
)
-
label_name
=
local_assigns
.
fetch
(
:label_name
,
"Labels"
)
-
no_default_styles
=
local_assigns
.
fetch
(
:no_default_styles
,
false
)
-
classes
<<
'js-extra-options'
if
extra_options
-
classes
<<
'js-extra-options'
if
extra_options
-
classes
<<
'js-filter-submit'
if
filter_submit
-
classes
<<
'js-filter-submit'
if
filter_submit
...
@@ -20,8 +22,9 @@
...
@@ -20,8 +22,9 @@
.dropdown
.dropdown
%button
.dropdown-menu-toggle.js-label-select.js-multiselect
{
class:
classes
.
join
(
' '
),
type:
"button"
,
data:
dropdown_data
}
%button
.dropdown-menu-toggle.js-label-select.js-multiselect
{
class:
classes
.
join
(
' '
),
type:
"button"
,
data:
dropdown_data
}
%span
.dropdown-toggle-text
{
class:
(
"is-default"
if
selected
.
nil?
||
selected
.
empty?
)
}
-
apply_is_default_styles
=
(
selected
.
nil?
||
selected
.
empty?
)
&&
!
no_default_styles
=
multi_label_name
(
selected
,
"Labels"
)
%span
.dropdown-toggle-text
{
class:
(
"is-default"
if
apply_is_default_styles
)
}
=
multi_label_name
(
selected
,
label_name
)
=
icon
(
'chevron-down'
)
=
icon
(
'chevron-down'
)
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
=
render
partial:
"shared/issuable/label_page_default"
,
locals:
{
title:
dropdown_title
,
show_footer:
show_footer
,
show_create:
show_create
}
=
render
partial:
"shared/issuable/label_page_default"
,
locals:
{
title:
dropdown_title
,
show_footer:
show_footer
,
show_create:
show_create
}
...
...
app/views/shared/issuable/_search_bar.html.haml
View file @
08fcc1e9
...
@@ -10,10 +10,9 @@
...
@@ -10,10 +10,9 @@
=
form_tag
page_filter_path
(
without:
[
:assignee_id
,
:author_id
,
:milestone_title
,
:label_name
,
:search
]),
method: :get
,
class:
'filter-form js-filter-form'
do
=
form_tag
page_filter_path
(
without:
[
:assignee_id
,
:author_id
,
:milestone_title
,
:label_name
,
:search
]),
method: :get
,
class:
'filter-form js-filter-form'
do
-
if
params
[
:search
].
present?
-
if
params
[
:search
].
present?
=
hidden_field_tag
:search
,
params
[
:search
]
=
hidden_field_tag
:search
,
params
[
:search
]
-
if
@bulk_edit
-
if
@can_bulk_update
.check-all-holder
.check-all-holder.hidden
=
check_box_tag
"check_all_issues"
,
nil
,
false
,
=
check_box_tag
"check-all-issues"
,
nil
,
false
,
class:
"check-all-issues left"
class:
"check_all_issues left"
.issues-other-filters.filtered-search-wrapper
.issues-other-filters.filtered-search-wrapper
.filtered-search-box
.filtered-search-box
-
if
type
!=
:boards_modal
&&
type
!=
:boards
-
if
type
!=
:boards_modal
&&
type
!=
:boards
...
@@ -133,55 +132,11 @@
...
@@ -133,55 +132,11 @@
-
elsif
type
!=
:boards_modal
-
elsif
type
!=
:boards_modal
=
render
'shared/sort_dropdown'
,
type:
local_assigns
[
:type
]
=
render
'shared/sort_dropdown'
,
type:
local_assigns
[
:type
]
-
if
@bulk_edit
.issues_bulk_update.hide
=
form_tag
[
:bulk_update
,
@project
.
namespace
.
becomes
(
Namespace
),
@project
,
type
],
method: :post
,
class:
'bulk-update'
do
.filter-item.inline
=
dropdown_tag
(
"Status"
,
options:
{
toggle_class:
"js-issue-status"
,
title:
"Change status"
,
dropdown_class:
"dropdown-menu-status dropdown-menu-selectable"
,
data:
{
field_name:
"update[state_event]"
,
default_label:
"Status"
}
}
)
do
%ul
%li
%a
{
href:
"#"
,
data:
{
id:
"reopen"
}
}
Open
%li
%a
{
href:
"#"
,
data:
{
id:
"close"
}
}
Closed
.filter-item.inline
-
if
type
==
:issues
-
field_name
=
"update[assignee_ids][]"
-
else
-
field_name
=
"update[assignee_id]"
=
dropdown_tag
(
"Assignee"
,
options:
{
toggle_class:
"js-user-search js-update-assignee js-filter-submit js-filter-bulk-update"
,
title:
"Assign to"
,
filter:
true
,
dropdown_class:
"dropdown-menu-user dropdown-menu-selectable"
,
placeholder:
"Search authors"
,
data:
{
first_user:
(
current_user
.
username
if
current_user
),
null_user:
true
,
current_user:
true
,
project_id:
@project
.
id
,
field_name:
field_name
}
})
.filter-item.inline
=
dropdown_tag
(
"Milestone"
,
options:
{
title:
"Assign milestone"
,
toggle_class:
'js-milestone-select js-extra-options js-filter-submit js-filter-bulk-update'
,
filter:
true
,
dropdown_class:
"dropdown-menu-selectable dropdown-menu-milestone"
,
placeholder:
"Search milestones"
,
data:
{
show_no:
true
,
field_name:
"update[milestone_id]"
,
project_id:
@project
.
id
,
milestones:
namespace_project_milestones_path
(
@project
.
namespace
,
@project
,
:json
),
use_id:
true
,
default_label:
"Milestone"
}
})
.filter-item.inline.labels-filter
=
render
"shared/issuable/label_dropdown"
,
classes:
[
'js-filter-bulk-update'
,
'js-multiselect'
],
dropdown_title:
'Apply a label'
,
show_create:
false
,
show_footer:
false
,
extra_options:
false
,
filter_submit:
false
,
data_options:
{
persist_when_hide:
"true"
,
field_name:
"update[label_ids][]"
,
show_no:
false
,
show_any:
false
,
use_id:
true
,
default_label:
"Labels"
}
.filter-item.inline
=
dropdown_tag
(
"Subscription"
,
options:
{
toggle_class:
"js-subscription-event"
,
title:
"Change subscription"
,
dropdown_class:
"dropdown-menu-selectable"
,
data:
{
field_name:
"update[subscription_event]"
,
default_label:
"Subscription"
}
}
)
do
%ul
%li
%a
{
href:
"#"
,
data:
{
id:
"subscribe"
}
}
Subscribe
%li
%a
{
href:
"#"
,
data:
{
id:
"unsubscribe"
}
}
Unsubscribe
=
hidden_field_tag
'update[issuable_ids]'
,
[]
=
hidden_field_tag
:state_event
,
params
[
:state_event
]
.filter-item.inline.update-issues-btn
=
button_tag
"Update
#{
type
.
to_s
.
humanize
(
capitalize:
false
)
}
"
,
class:
"btn update_selected_issues btn-save"
-
unless
type
===
:boards_modal
-
unless
type
===
:boards_modal
:javascript
:javascript
new
LabelsSelect
();
new
MilestoneSelect
();
new
IssueStatusSelect
();
new
SubscriptionSelect
();
$
(
document
).
off
(
'
page:restore
'
).
on
(
'
page:restore
'
,
function
(
event
)
{
$
(
document
).
off
(
'
page:restore
'
).
on
(
'
page:restore
'
,
function
(
event
)
{
if
(
gl
.
FilteredSearchManager
)
{
if
(
gl
.
FilteredSearchManager
)
{
const
filteredSearchManager
=
new
gl
.
FilteredSearchManager
();
const
filteredSearchManager
=
new
gl
.
FilteredSearchManager
();
filteredSearchManager
.
setup
();
filteredSearchManager
.
setup
();
}
}
Issuable
.
init
();
new
gl
.
IssuableBulkActions
({
prefixId
:
'
issue_
'
,
});
});
});
spec/features/issues/bulk_assignment_labels_spec.rb
View file @
08fcc1e9
...
@@ -18,13 +18,13 @@ feature 'Issues > Labels bulk assignment', feature: true do
...
@@ -18,13 +18,13 @@ feature 'Issues > Labels bulk assignment', feature: true do
context
'can bulk assign'
do
context
'can bulk assign'
do
before
do
before
do
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
enable_bulk_update
end
end
context
'a label'
do
context
'a label'
do
context
'to all issues'
do
context
'to all issues'
do
before
do
before
do
check
'check
_all_
issues'
check
'check
-all-
issues'
open_labels_dropdown
[
'bug'
]
open_labels_dropdown
[
'bug'
]
update_issues
update_issues
end
end
...
@@ -52,7 +52,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
...
@@ -52,7 +52,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
context
'multiple labels'
do
context
'multiple labels'
do
context
'to all issues'
do
context
'to all issues'
do
before
do
before
do
check
'check
_all_
issues'
check
'check
-all-
issues'
open_labels_dropdown
%w(bug feature)
open_labels_dropdown
%w(bug feature)
update_issues
update_issues
end
end
...
@@ -86,9 +86,10 @@ feature 'Issues > Labels bulk assignment', feature: true do
...
@@ -86,9 +86,10 @@ feature 'Issues > Labels bulk assignment', feature: true do
before
do
before
do
issue2
.
labels
<<
bug
issue2
.
labels
<<
bug
issue2
.
labels
<<
feature
issue2
.
labels
<<
feature
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
check
'check_all_issues'
enable_bulk_update
check
'check-all-issues'
open_labels_dropdown
[
'bug'
]
open_labels_dropdown
[
'bug'
]
update_issues
update_issues
end
end
...
@@ -107,9 +108,8 @@ feature 'Issues > Labels bulk assignment', feature: true do
...
@@ -107,9 +108,8 @@ feature 'Issues > Labels bulk assignment', feature: true do
issue2
.
labels
<<
bug
issue2
.
labels
<<
bug
issue2
.
labels
<<
feature
issue2
.
labels
<<
feature
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
enable_bulk_update
check
'check-all-issues'
check
'check_all_issues'
unmark_labels_in_dropdown
%w(bug feature)
unmark_labels_in_dropdown
%w(bug feature)
update_issues
update_issues
end
end
...
@@ -127,8 +127,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
...
@@ -127,8 +127,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
issue1
.
labels
<<
bug
issue1
.
labels
<<
bug
issue2
.
labels
<<
feature
issue2
.
labels
<<
feature
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
enable_bulk_update
check_issue
issue1
check_issue
issue1
unmark_labels_in_dropdown
[
'bug'
]
unmark_labels_in_dropdown
[
'bug'
]
update_issues
update_issues
...
@@ -147,8 +146,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
...
@@ -147,8 +146,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
issue2
.
labels
<<
bug
issue2
.
labels
<<
bug
issue2
.
labels
<<
feature
issue2
.
labels
<<
feature
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
enable_bulk_update
check_issue
issue1
check_issue
issue1
check_issue
issue2
check_issue
issue2
unmark_labels_in_dropdown
[
'bug'
]
unmark_labels_in_dropdown
[
'bug'
]
...
@@ -171,14 +169,15 @@ feature 'Issues > Labels bulk assignment', feature: true do
...
@@ -171,14 +169,15 @@ feature 'Issues > Labels bulk assignment', feature: true do
before
do
before
do
issue1
.
labels
<<
bug
issue1
.
labels
<<
bug
issue2
.
labels
<<
feature
issue2
.
labels
<<
feature
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
enable_bulk_update
end
end
it
'keeps labels'
do
it
'keeps labels'
do
expect
(
find
(
"#issue_
#{
issue1
.
id
}
"
)).
to
have_content
'bug'
expect
(
find
(
"#issue_
#{
issue1
.
id
}
"
)).
to
have_content
'bug'
expect
(
find
(
"#issue_
#{
issue2
.
id
}
"
)).
to
have_content
'feature'
expect
(
find
(
"#issue_
#{
issue2
.
id
}
"
)).
to
have_content
'feature'
check
'check_all_issues'
check
'check-all-issues'
open_milestone_dropdown
([
'First Release'
])
open_milestone_dropdown
([
'First Release'
])
update_issues
update_issues
...
@@ -192,14 +191,13 @@ feature 'Issues > Labels bulk assignment', feature: true do
...
@@ -192,14 +191,13 @@ feature 'Issues > Labels bulk assignment', feature: true do
context
'setting a milestone and adding another label'
do
context
'setting a milestone and adding another label'
do
before
do
before
do
issue1
.
labels
<<
bug
issue1
.
labels
<<
bug
enable_bulk_update
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
end
end
it
'keeps existing label and new label is present'
do
it
'keeps existing label and new label is present'
do
expect
(
find
(
"#issue_
#{
issue1
.
id
}
"
)).
to
have_content
'bug'
expect
(
find
(
"#issue_
#{
issue1
.
id
}
"
)).
to
have_content
'bug'
check
'check
_all_
issues'
check
'check
-all-
issues'
open_milestone_dropdown
[
'First Release'
]
open_milestone_dropdown
[
'First Release'
]
open_labels_dropdown
[
'feature'
]
open_labels_dropdown
[
'feature'
]
update_issues
update_issues
...
@@ -218,7 +216,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
...
@@ -218,7 +216,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
issue1
.
labels
<<
feature
issue1
.
labels
<<
feature
issue2
.
labels
<<
feature
issue2
.
labels
<<
feature
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
enable_bulk_update
end
end
it
'keeps existing label and new label is present'
do
it
'keeps existing label and new label is present'
do
...
@@ -226,7 +224,8 @@ feature 'Issues > Labels bulk assignment', feature: true do
...
@@ -226,7 +224,8 @@ feature 'Issues > Labels bulk assignment', feature: true do
expect
(
find
(
"#issue_
#{
issue1
.
id
}
"
)).
to
have_content
'bug'
expect
(
find
(
"#issue_
#{
issue1
.
id
}
"
)).
to
have_content
'bug'
expect
(
find
(
"#issue_
#{
issue2
.
id
}
"
)).
to
have_content
'feature'
expect
(
find
(
"#issue_
#{
issue2
.
id
}
"
)).
to
have_content
'feature'
check
'check_all_issues'
check
'check-all-issues'
open_milestone_dropdown
[
'First Release'
]
open_milestone_dropdown
[
'First Release'
]
unmark_labels_in_dropdown
[
'feature'
]
unmark_labels_in_dropdown
[
'feature'
]
update_issues
update_issues
...
@@ -248,7 +247,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
...
@@ -248,7 +247,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
issue1
.
labels
<<
bug
issue1
.
labels
<<
bug
issue2
.
labels
<<
feature
issue2
.
labels
<<
feature
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
enable_bulk_update
end
end
it
'keeps labels'
do
it
'keeps labels'
do
...
@@ -257,7 +256,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
...
@@ -257,7 +256,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
expect
(
find
(
"#issue_
#{
issue2
.
id
}
"
)).
to
have_content
'feature'
expect
(
find
(
"#issue_
#{
issue2
.
id
}
"
)).
to
have_content
'feature'
expect
(
find
(
"#issue_
#{
issue2
.
id
}
"
)).
to
have_content
'First Release'
expect
(
find
(
"#issue_
#{
issue2
.
id
}
"
)).
to
have_content
'First Release'
check
'check
_all_
issues'
check
'check
-all-
issues'
open_milestone_dropdown
([
'No Milestone'
])
open_milestone_dropdown
([
'No Milestone'
])
update_issues
update_issues
...
@@ -272,8 +271,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
...
@@ -272,8 +271,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
context
'toggling checked issues'
do
context
'toggling checked issues'
do
before
do
before
do
issue1
.
labels
<<
bug
issue1
.
labels
<<
bug
enable_bulk_update
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
end
end
it
do
it
do
...
@@ -298,14 +296,14 @@ feature 'Issues > Labels bulk assignment', feature: true do
...
@@ -298,14 +296,14 @@ feature 'Issues > Labels bulk assignment', feature: true do
issue1
.
labels
<<
feature
issue1
.
labels
<<
feature
issue2
.
labels
<<
bug
issue2
.
labels
<<
bug
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
enable_bulk_update
end
end
it
'applies label from filtered results'
do
it
'applies label from filtered results'
do
check
'check
_all_
issues'
check
'check
-all-
issues'
page
.
within
(
'.issues
_bulk_
update'
)
do
page
.
within
(
'.issues
-bulk-
update'
)
do
click_button
'
L
abels'
click_button
'
Select l
abels'
wait_for_requests
wait_for_requests
expect
(
find
(
'.dropdown-menu-labels li'
,
text:
'bug'
)).
to
have_css
(
'.is-active'
)
expect
(
find
(
'.dropdown-menu-labels li'
,
text:
'bug'
)).
to
have_css
(
'.is-active'
)
...
@@ -340,15 +338,16 @@ feature 'Issues > Labels bulk assignment', feature: true do
...
@@ -340,15 +338,16 @@ feature 'Issues > Labels bulk assignment', feature: true do
context
'cannot bulk assign labels'
do
context
'cannot bulk assign labels'
do
it
do
it
do
expect
(
page
).
not_to
have_css
'.check_all_issues'
expect
(
page
).
not_to
have_button
'Edit Issues'
expect
(
page
).
not_to
have_css
'.check-all-issues'
expect
(
page
).
not_to
have_css
'.issue-check'
expect
(
page
).
not_to
have_css
'.issue-check'
end
end
end
end
end
end
def
open_milestone_dropdown
(
items
=
[])
def
open_milestone_dropdown
(
items
=
[])
page
.
within
(
'.issues
_bulk_
update'
)
do
page
.
within
(
'.issues
-bulk-
update'
)
do
click_button
'
M
ilestone'
click_button
'
Select m
ilestone'
wait_for_requests
wait_for_requests
items
.
map
do
|
item
|
items
.
map
do
|
item
|
click_link
item
click_link
item
...
@@ -357,8 +356,8 @@ feature 'Issues > Labels bulk assignment', feature: true do
...
@@ -357,8 +356,8 @@ feature 'Issues > Labels bulk assignment', feature: true do
end
end
def
open_labels_dropdown
(
items
=
[],
unmark
=
false
)
def
open_labels_dropdown
(
items
=
[],
unmark
=
false
)
page
.
within
(
'.issues
_bulk_
update'
)
do
page
.
within
(
'.issues
-bulk-
update'
)
do
click_button
'
L
abels'
click_button
'
Select l
abels'
wait_for_requests
wait_for_requests
items
.
map
do
|
item
|
items
.
map
do
|
item
|
click_link
item
click_link
item
...
@@ -391,7 +390,12 @@ feature 'Issues > Labels bulk assignment', feature: true do
...
@@ -391,7 +390,12 @@ feature 'Issues > Labels bulk assignment', feature: true do
end
end
def
update_issues
def
update_issues
click_button
'Update
issues
'
click_button
'Update
all
'
wait_for_requests
wait_for_requests
end
end
def
enable_bulk_update
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
click_button
'Edit Issues'
end
end
end
spec/features/issues/update_issues_spec.rb
View file @
08fcc1e9
...
@@ -14,7 +14,8 @@ feature 'Multiple issue updating from issues#index', feature: true do
...
@@ -14,7 +14,8 @@ feature 'Multiple issue updating from issues#index', feature: true do
it
'sets to closed'
do
it
'sets to closed'
do
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
find
(
'#check_all_issues'
).
click
click_button
'Edit Issues'
find
(
'#check-all-issues'
).
click
find
(
'.js-issue-status'
).
click
find
(
'.js-issue-status'
).
click
find
(
'.dropdown-menu-status a'
,
text:
'Closed'
).
click
find
(
'.dropdown-menu-status a'
,
text:
'Closed'
).
click
...
@@ -26,7 +27,8 @@ feature 'Multiple issue updating from issues#index', feature: true do
...
@@ -26,7 +27,8 @@ feature 'Multiple issue updating from issues#index', feature: true do
create_closed
create_closed
visit
namespace_project_issues_path
(
project
.
namespace
,
project
,
state:
'closed'
)
visit
namespace_project_issues_path
(
project
.
namespace
,
project
,
state:
'closed'
)
find
(
'#check_all_issues'
).
click
click_button
'Edit Issues'
find
(
'#check-all-issues'
).
click
find
(
'.js-issue-status'
).
click
find
(
'.js-issue-status'
).
click
find
(
'.dropdown-menu-status a'
,
text:
'Open'
).
click
find
(
'.dropdown-menu-status a'
,
text:
'Open'
).
click
...
@@ -39,7 +41,8 @@ feature 'Multiple issue updating from issues#index', feature: true do
...
@@ -39,7 +41,8 @@ feature 'Multiple issue updating from issues#index', feature: true do
it
'updates to current user'
do
it
'updates to current user'
do
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
find
(
'#check_all_issues'
).
click
click_button
'Edit Issues'
find
(
'#check-all-issues'
).
click
click_update_assignee_button
click_update_assignee_button
find
(
'.dropdown-menu-user-link'
,
text:
user
.
username
).
click
find
(
'.dropdown-menu-user-link'
,
text:
user
.
username
).
click
...
@@ -54,7 +57,8 @@ feature 'Multiple issue updating from issues#index', feature: true do
...
@@ -54,7 +57,8 @@ feature 'Multiple issue updating from issues#index', feature: true do
create_assigned
create_assigned
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
find
(
'#check_all_issues'
).
click
click_button
'Edit Issues'
find
(
'#check-all-issues'
).
click
click_update_assignee_button
click_update_assignee_button
click_link
'Unassigned'
click_link
'Unassigned'
...
@@ -69,8 +73,9 @@ feature 'Multiple issue updating from issues#index', feature: true do
...
@@ -69,8 +73,9 @@ feature 'Multiple issue updating from issues#index', feature: true do
it
'updates milestone'
do
it
'updates milestone'
do
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
find
(
'#check_all_issues'
).
click
click_button
'Edit Issues'
find
(
'.issues_bulk_update .js-milestone-select'
).
click
find
(
'#check-all-issues'
).
click
find
(
'.issues-bulk-update .js-milestone-select'
).
click
find
(
'.dropdown-menu-milestone a'
,
text:
milestone
.
title
).
click
find
(
'.dropdown-menu-milestone a'
,
text:
milestone
.
title
).
click
click_update_issues_button
click_update_issues_button
...
@@ -84,8 +89,9 @@ feature 'Multiple issue updating from issues#index', feature: true do
...
@@ -84,8 +89,9 @@ feature 'Multiple issue updating from issues#index', feature: true do
expect
(
first
(
'.issue'
)).
to
have_content
milestone
.
title
expect
(
first
(
'.issue'
)).
to
have_content
milestone
.
title
find
(
'#check_all_issues'
).
click
click_button
'Edit Issues'
find
(
'.issues_bulk_update .js-milestone-select'
).
click
find
(
'#check-all-issues'
).
click
find
(
'.issues-bulk-update .js-milestone-select'
).
click
find
(
'.dropdown-menu-milestone a'
,
text:
"No Milestone"
).
click
find
(
'.dropdown-menu-milestone a'
,
text:
"No Milestone"
).
click
click_update_issues_button
click_update_issues_button
...
@@ -112,7 +118,7 @@ feature 'Multiple issue updating from issues#index', feature: true do
...
@@ -112,7 +118,7 @@ feature 'Multiple issue updating from issues#index', feature: true do
end
end
def
click_update_issues_button
def
click_update_issues_button
find
(
'.update
_selected_
issues'
).
click
find
(
'.update
-selected-
issues'
).
click
wait_for_requests
wait_for_requests
end
end
end
end
spec/features/merge_requests/update_merge_requests_spec.rb
View file @
08fcc1e9
...
@@ -98,14 +98,16 @@ feature 'Multiple merge requests updating from merge_requests#index', feature: t
...
@@ -98,14 +98,16 @@ feature 'Multiple merge requests updating from merge_requests#index', feature: t
end
end
def
change_status
(
text
)
def
change_status
(
text
)
find
(
'#check_all_issues'
).
click
click_button
'Edit Merge Requests'
find
(
'#check-all-issues'
).
click
find
(
'.js-issue-status'
).
click
find
(
'.js-issue-status'
).
click
find
(
'.dropdown-menu-status a'
,
text:
text
).
click
find
(
'.dropdown-menu-status a'
,
text:
text
).
click
click_update_merge_requests_button
click_update_merge_requests_button
end
end
def
change_assignee
(
text
)
def
change_assignee
(
text
)
find
(
'#check_all_issues'
).
click
click_button
'Edit Merge Requests'
find
(
'#check-all-issues'
).
click
find
(
'.js-update-assignee'
).
click
find
(
'.js-update-assignee'
).
click
wait_for_requests
wait_for_requests
...
@@ -117,14 +119,15 @@ feature 'Multiple merge requests updating from merge_requests#index', feature: t
...
@@ -117,14 +119,15 @@ feature 'Multiple merge requests updating from merge_requests#index', feature: t
end
end
def
change_milestone
(
text
)
def
change_milestone
(
text
)
find
(
'#check_all_issues'
).
click
click_button
'Edit Merge Requests'
find
(
'.issues_bulk_update .js-milestone-select'
).
click
find
(
'#check-all-issues'
).
click
find
(
'.issues-bulk-update .js-milestone-select'
).
click
find
(
'.dropdown-menu-milestone a'
,
text:
text
).
click
find
(
'.dropdown-menu-milestone a'
,
text:
text
).
click
click_update_merge_requests_button
click_update_merge_requests_button
end
end
def
click_update_merge_requests_button
def
click_update_merge_requests_button
find
(
'.update
_selected_
issues'
).
click
find
(
'.update
-selected-
issues'
).
click
wait_for_requests
wait_for_requests
end
end
end
end
spec/javascripts/fixtures/issuable_filter.html.haml
View file @
08fcc1e9
%form
.js-filter-form
{
action:
'/user/project/issues?scope=all&state=closed'
}
%form
.js-filter-form
{
action:
'/user/project/issues?scope=all&state=closed'
}
%input
{
id:
'utf8'
,
name:
'utf8'
,
value:
'✓'
}
%input
{
id:
'utf8'
,
name:
'utf8'
,
value:
'✓'
}
%input
{
id:
'check
_all_issues'
,
name:
'check_all_
issues'
}
%input
{
id:
'check
-all-issues'
,
name:
'check-all-
issues'
}
%input
{
id:
'search'
,
name:
'search'
}
%input
{
id:
'search'
,
name:
'search'
}
%input
{
id:
'author_id'
,
name:
'author_id'
}
%input
{
id:
'author_id'
,
name:
'author_id'
}
%input
{
id:
'assignee_id'
,
name:
'assignee_id'
}
%input
{
id:
'assignee_id'
,
name:
'assignee_id'
}
...
...
spec/javascripts/issuable_spec.js
View file @
08fcc1e9
/* global Issuable */
/* global Issuable
Index
*/
import
'
~/lib/utils/url_utility
'
;
import
'
~/lib/utils/url_utility
'
;
import
'
~/issuable
'
;
import
'
~/issuable
_index
'
;
(()
=>
{
(()
=>
{
const
BASE_URL
=
'
/user/project/issues?scope=all&state=closed
'
;
const
BASE_URL
=
'
/user/project/issues?scope=all&state=closed
'
;
...
@@ -24,11 +24,11 @@ import '~/issuable';
...
@@ -24,11 +24,11 @@ import '~/issuable';
beforeEach
(()
=>
{
beforeEach
(()
=>
{
loadFixtures
(
'
static/issuable_filter.html.raw
'
);
loadFixtures
(
'
static/issuable_filter.html.raw
'
);
Issuable
.
init
();
Issuable
Index
.
init
();
});
});
it
(
'
should be defined
'
,
()
=>
{
it
(
'
should be defined
'
,
()
=>
{
expect
(
window
.
Issuable
).
toBeDefined
();
expect
(
window
.
Issuable
Index
).
toBeDefined
();
});
});
describe
(
'
filtering
'
,
()
=>
{
describe
(
'
filtering
'
,
()
=>
{
...
@@ -43,7 +43,7 @@ import '~/issuable';
...
@@ -43,7 +43,7 @@ import '~/issuable';
it
(
'
should contain only the default parameters
'
,
()
=>
{
it
(
'
should contain only the default parameters
'
,
()
=>
{
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
Issuable
.
filterResults
(
$filtersForm
);
Issuable
Index
.
filterResults
(
$filtersForm
);
expect
(
gl
.
utils
.
visitUrl
).
toHaveBeenCalledWith
(
BASE_URL
+
DEFAULT_PARAMS
);
expect
(
gl
.
utils
.
visitUrl
).
toHaveBeenCalledWith
(
BASE_URL
+
DEFAULT_PARAMS
);
});
});
...
@@ -52,7 +52,7 @@ import '~/issuable';
...
@@ -52,7 +52,7 @@ import '~/issuable';
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
updateForm
({
search
:
'
broken
'
},
$filtersForm
);
updateForm
({
search
:
'
broken
'
},
$filtersForm
);
Issuable
.
filterResults
(
$filtersForm
);
Issuable
Index
.
filterResults
(
$filtersForm
);
const
params
=
`
${
DEFAULT_PARAMS
}
&search=broken`
;
const
params
=
`
${
DEFAULT_PARAMS
}
&search=broken`
;
expect
(
gl
.
utils
.
visitUrl
).
toHaveBeenCalledWith
(
BASE_URL
+
params
);
expect
(
gl
.
utils
.
visitUrl
).
toHaveBeenCalledWith
(
BASE_URL
+
params
);
...
@@ -64,14 +64,14 @@ import '~/issuable';
...
@@ -64,14 +64,14 @@ import '~/issuable';
// initial filter
// initial filter
updateForm
({
milestone_title
:
'
v1.0
'
},
$filtersForm
);
updateForm
({
milestone_title
:
'
v1.0
'
},
$filtersForm
);
Issuable
.
filterResults
(
$filtersForm
);
Issuable
Index
.
filterResults
(
$filtersForm
);
let
params
=
`
${
DEFAULT_PARAMS
}
&milestone_title=v1.0`
;
let
params
=
`
${
DEFAULT_PARAMS
}
&milestone_title=v1.0`
;
expect
(
gl
.
utils
.
visitUrl
).
toHaveBeenCalledWith
(
BASE_URL
+
params
);
expect
(
gl
.
utils
.
visitUrl
).
toHaveBeenCalledWith
(
BASE_URL
+
params
);
// update filter
// update filter
updateForm
({
label_name
:
'
Frontend
'
},
$filtersForm
);
updateForm
({
label_name
:
'
Frontend
'
},
$filtersForm
);
Issuable
.
filterResults
(
$filtersForm
);
Issuable
Index
.
filterResults
(
$filtersForm
);
params
=
`
${
DEFAULT_PARAMS
}
&milestone_title=v1.0&label_name=Frontend`
;
params
=
`
${
DEFAULT_PARAMS
}
&milestone_title=v1.0&label_name=Frontend`
;
expect
(
gl
.
utils
.
visitUrl
).
toHaveBeenCalledWith
(
BASE_URL
+
params
);
expect
(
gl
.
utils
.
visitUrl
).
toHaveBeenCalledWith
(
BASE_URL
+
params
);
});
});
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment