BigW Consortium Gitlab
Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
gitlab-ce
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Forest Godfrey
gitlab-ce
Commits
cc64eda9
Commit
cc64eda9
authored
Mar 13, 2017
by
Sean McGivern
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch '27114-add-undo-to-todos-in-the-done-tab' into 'master'
Add 'Undo' to Todos in the Done tab Closes #27114 See merge request !8782
parents
0240caa0
e20ffc6e
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
169 additions
and
127 deletions
+169
-127
todos.js
app/assets/javascripts/todos.js
+122
-122
_todo.html.haml
app/views/dashboard/todos/_todo.html.haml
+5
-0
27114-add-undo-to-todos-in-the-done-tab.yml
...gs/unreleased/27114-add-undo-to-todos-in-the-done-tab.yml
+4
-0
todos_spec.rb
spec/features/todos/todos_spec.rb
+38
-5
No files found.
app/assets/javascripts/todos.js
View file @
cc64eda9
/* eslint-disable class-methods-use-this, no-
new, func-names, no-unneeded-ternary, object-shorthand, quote-props, no-param-reassign, max-len
*/
/* eslint-disable class-methods-use-this, no-
unneeded-ternary, quote-props
*/
/* global UsersSelect */
((
global
)
=>
{
class
Todos
{
constructor
()
{
this
.
initFilters
();
this
.
bindEvents
();
class
Todos
{
constructor
()
{
this
.
initFilters
();
this
.
bindEvents
();
this
.
cleanupWrapper
=
this
.
cleanup
.
bind
(
this
);
document
.
addEventListener
(
'beforeunload'
,
this
.
cleanupWrapper
);
}
this
.
cleanupWrapper
=
this
.
cleanup
.
bind
(
this
);
document
.
addEventListener
(
'beforeunload'
,
this
.
cleanupWrapper
);
}
cleanup
()
{
this
.
unbindEvents
();
document
.
removeEventListener
(
'beforeunload'
,
this
.
cleanupWrapper
);
}
cleanup
()
{
this
.
unbindEvents
();
document
.
removeEventListener
(
'beforeunload'
,
this
.
cleanupWrapper
);
}
unbindEvents
()
{
$
(
'.js-done-todo, .js-undo-todo'
).
off
(
'click'
,
this
.
update
StateClickedWrapper
);
$
(
'.js-todos-mark-all'
).
off
(
'click'
,
this
.
allDoneClickedWrapper
);
$
(
'.todo'
).
off
(
'click'
,
this
.
goToTodoUrl
);
}
unbindEvents
()
{
$
(
'.js-done-todo, .js-undo-todo, .js-add-todo'
).
off
(
'click'
,
this
.
updateRow
StateClickedWrapper
);
$
(
'.js-todos-mark-all'
).
off
(
'click'
,
this
.
allDoneClickedWrapper
);
$
(
'.todo'
).
off
(
'click'
,
this
.
goToTodoUrl
);
}
bindEvents
()
{
this
.
updateStateClickedWrapper
=
this
.
update
StateClicked
.
bind
(
this
);
this
.
allDoneClickedWrapper
=
this
.
allDoneClicked
.
bind
(
this
);
bindEvents
()
{
this
.
updateRowStateClickedWrapper
=
this
.
updateRow
StateClicked
.
bind
(
this
);
this
.
allDoneClickedWrapper
=
this
.
allDoneClicked
.
bind
(
this
);
$
(
'.js-done-todo, .js-undo-todo'
).
on
(
'click'
,
this
.
update
StateClickedWrapper
);
$
(
'.js-todos-mark-all'
).
on
(
'click'
,
this
.
allDoneClickedWrapper
);
$
(
'.todo'
).
on
(
'click'
,
this
.
goToTodoUrl
);
}
$
(
'.js-done-todo, .js-undo-todo, .js-add-todo'
).
on
(
'click'
,
this
.
updateRow
StateClickedWrapper
);
$
(
'.js-todos-mark-all'
).
on
(
'click'
,
this
.
allDoneClickedWrapper
);
$
(
'.todo'
).
on
(
'click'
,
this
.
goToTodoUrl
);
}
initFilters
()
{
new
UsersSelect
();
this
.
initFilterDropdown
(
$
(
'.js-project-search'
),
'project_id'
,
[
'text'
]);
this
.
initFilterDropdown
(
$
(
'.js-type-search'
),
'type'
);
this
.
initFilterDropdown
(
$
(
'.js-action-search'
),
'action_id'
);
initFilters
()
{
this
.
initFilterDropdown
(
$
(
'.js-project-search'
),
'project_id'
,
[
'text'
]);
this
.
initFilterDropdown
(
$
(
'.js-type-search'
),
'type'
);
this
.
initFilterDropdown
(
$
(
'.js-action-search'
),
'action_id'
);
$
(
'form.filter-form'
).
on
(
'submit'
,
function
(
event
)
{
event
.
preventDefault
();
gl
.
utils
.
visitUrl
(
`
${
this
.
action
}
&
${
$
(
this
).
serialize
()}
`
);
});
}
$
(
'form.filter-form'
).
on
(
'submit'
,
function
applyFilters
(
event
)
{
event
.
preventDefault
();
gl
.
utils
.
visitUrl
(
`
${
this
.
action
}
&
${
$
(
this
).
serialize
()}
`
);
});
return
new
UsersSelect
();
}
initFilterDropdown
(
$dropdown
,
fieldName
,
searchFields
)
{
$dropdown
.
glDropdown
({
fieldName
,
selectable
:
true
,
filterable
:
searchFields
?
true
:
false
,
search
:
{
fields
:
searchFields
},
data
:
$dropdown
.
data
(
'data'
),
clicked
:
function
()
{
return
$dropdown
.
closest
(
'form.filter-form'
).
submit
();
},
});
}
initFilterDropdown
(
$dropdown
,
fieldName
,
searchFields
)
{
$dropdown
.
glDropdown
({
fieldName
,
selectable
:
true
,
filterable
:
searchFields
?
true
:
false
,
search
:
{
fields
:
searchFields
},
data
:
$dropdown
.
data
(
'data'
),
clicked
:
()
=>
$dropdown
.
closest
(
'form.filter-form'
).
submit
(),
});
}
updateStateClicked
(
e
)
{
e
.
preventDefault
();
const
target
=
e
.
target
;
target
.
setAttribute
(
'disabled'
,
''
);
target
.
classList
.
add
(
'disabled'
);
$
.
ajax
({
type
:
'POST'
,
url
:
target
.
getAttribute
(
'href'
),
dataType
:
'json'
,
data
:
{
'_method'
:
target
.
getAttribute
(
'data-method'
),
},
success
:
(
data
)
=>
{
this
.
updateState
(
target
);
this
.
updateBadges
(
data
);
},
});
}
updateRowStateClicked
(
e
)
{
e
.
preventDefault
();
const
target
=
e
.
target
;
target
.
setAttribute
(
'disabled'
,
''
);
target
.
classList
.
add
(
'disabled'
);
$
.
ajax
({
type
:
'POST'
,
url
:
target
.
getAttribute
(
'href'
),
dataType
:
'json'
,
data
:
{
'_method'
:
target
.
getAttribute
(
'data-method'
),
},
success
:
(
data
)
=>
{
this
.
updateRowState
(
target
);
return
this
.
updateBadges
(
data
);
},
});
}
allDoneClicked
(
e
)
{
e
.
preventDefault
();
const
$target
=
$
(
e
.
currentTarget
);
$target
.
disable
();
$
.
ajax
({
type
:
'POST'
,
url
:
$target
.
attr
(
'href'
),
dataType
:
'json'
,
data
:
{
'_method'
:
'delete'
,
},
success
:
(
data
)
=>
{
$target
.
remove
();
$
(
'.js-todos-all'
).
html
(
'<div class="nothing-here-block">You
\'
re all done!</div>'
);
this
.
updateBadges
(
data
);
},
});
allDoneClicked
(
e
)
{
e
.
preventDefault
();
const
$target
=
$
(
e
.
currentTarget
);
$target
.
disable
();
$
.
ajax
({
type
:
'POST'
,
url
:
$target
.
attr
(
'href'
),
dataType
:
'json'
,
data
:
{
'_method'
:
'delete'
,
},
success
:
(
data
)
=>
{
$target
.
remove
();
$
(
'.js-todos-all'
).
html
(
'<div class="nothing-here-block">You
\'
re all done!</div>'
);
this
.
updateBadges
(
data
);
},
});
}
updateRowState
(
target
)
{
const
row
=
target
.
closest
(
'li'
);
const
restoreBtn
=
row
.
querySelector
(
'.js-undo-todo'
);
const
doneBtn
=
row
.
querySelector
(
'.js-done-todo'
);
target
.
classList
.
add
(
'hidden'
);
target
.
removeAttribute
(
'disabled'
);
target
.
classList
.
remove
(
'disabled'
);
if
(
target
===
doneBtn
)
{
row
.
classList
.
add
(
'done-reversible'
);
restoreBtn
.
classList
.
remove
(
'hidden'
);
}
else
if
(
target
===
restoreBtn
)
{
row
.
classList
.
remove
(
'done-reversible'
);
doneBtn
.
classList
.
remove
(
'hidden'
);
}
else
{
row
.
parentNode
.
removeChild
(
row
);
}
}
updateState
(
target
)
{
const
row
=
target
.
closest
(
'li'
);
const
restoreBtn
=
row
.
querySelector
(
'.js-undo-todo'
);
const
doneBtn
=
row
.
querySelector
(
'.js-done-todo'
);
updateBadges
(
data
)
{
$
(
document
).
trigger
(
'todo:toggle'
,
data
.
count
);
document
.
querySelector
(
'.todos-pending .badge'
).
innerHTML
=
data
.
count
;
document
.
querySelector
(
'.todos-done .badge'
).
innerHTML
=
data
.
done_count
;
}
target
.
removeAttribute
(
'disabled'
);
target
.
classList
.
remove
(
'disabled'
);
target
.
classList
.
add
(
'hidden'
);
goToTodoUrl
(
e
)
{
const
todoLink
=
this
.
dataset
.
url
;
if
(
target
===
doneBtn
)
{
row
.
classList
.
add
(
'done-reversible'
);
restoreBtn
.
classList
.
remove
(
'hidden'
);
}
else
{
row
.
classList
.
remove
(
'done-reversible'
);
doneBtn
.
classList
.
remove
(
'hidden'
);
}
}
updateBadges
(
data
)
{
$
(
document
).
trigger
(
'todo:toggle'
,
data
.
count
);
$
(
'.todos-pending .badge'
).
text
(
data
.
count
);
$
(
'.todos-done .badge'
).
text
(
data
.
done_count
);
if
(
!
todoLink
)
{
return
;
}
goToTodoUrl
(
e
)
{
const
todoLink
=
this
.
dataset
.
url
;
if
(
!
todoLink
)
{
return
;
}
if
(
gl
.
utils
.
isMetaClick
(
e
))
{
const
windowTarget
=
'_blank'
;
const
selected
=
e
.
target
;
e
.
preventDefault
();
if
(
gl
.
utils
.
isMetaClick
(
e
))
{
const
windowTarget
=
'_blank'
;
const
selected
=
e
.
target
;
e
.
preventDefault
();
if
(
selected
.
tagName
===
'IMG'
)
{
const
avatarUrl
=
selected
.
parentElement
.
getAttribute
(
'href'
);
window
.
open
(
avatarUrl
,
windowTarget
);
}
else
{
window
.
open
(
todoLink
,
windowTarget
);
}
if
(
selected
.
tagName
===
'IMG'
)
{
const
avatarUrl
=
selected
.
parentElement
.
getAttribute
(
'href'
);
window
.
open
(
avatarUrl
,
windowTarget
);
}
else
{
gl
.
utils
.
visitUrl
(
todoLink
);
window
.
open
(
todoLink
,
windowTarget
);
}
}
else
{
gl
.
utils
.
visitUrl
(
todoLink
);
}
}
}
global
.
Todos
=
Todos
;
})(
window
.
gl
||
(
window
.
gl
=
{}))
;
window
.
gl
=
window
.
gl
||
{}
;
gl
.
Todos
=
Todos
;
app/views/dashboard/todos/_todo.html.haml
View file @
cc64eda9
...
...
@@ -42,3 +42,8 @@
=
link_to
restore_dashboard_todo_path
(
todo
),
method: :patch
,
class:
'btn btn-loading js-undo-todo hidden'
do
Undo
=
icon
(
'spinner spin'
)
-
else
.todo-actions
=
link_to
restore_dashboard_todo_path
(
todo
),
method: :patch
,
class:
'btn btn-loading js-add-todo'
do
Add todo
=
icon
(
'spinner spin'
)
changelogs/unreleased/27114-add-undo-to-todos-in-the-done-tab.yml
0 → 100644
View file @
cc64eda9
---
title
:
Add Undo to Todos in the Done tab
merge_request
:
8782
author
:
Jacopo Beschi @jacopo-beschi
spec/features/todos/todos_spec.rb
View file @
cc64eda9
...
...
@@ -38,7 +38,9 @@ describe 'Dashboard Todos', feature: true do
shared_examples
'deleting the todo'
do
before
do
first
(
'.js-done-todo'
).
click
within
first
(
'.todo'
)
do
click_link
'Done'
end
end
it
'is marked as done-reversible in the list'
do
...
...
@@ -62,9 +64,11 @@ describe 'Dashboard Todos', feature: true do
shared_examples
'deleting and restoring the todo'
do
before
do
first
(
'.js-done-todo'
).
click
wait_for_ajax
first
(
'.js-undo-todo'
).
click
within
first
(
'.todo'
)
do
click_link
'Done'
wait_for_ajax
click_link
'Undo'
end
end
it
'is marked back as pending in the list'
do
...
...
@@ -97,6 +101,35 @@ describe 'Dashboard Todos', feature: true do
end
end
context
'User has done todos'
,
js:
true
do
before
do
create
(
:todo
,
:mentioned
,
:done
,
user:
user
,
project:
project
,
target:
issue
,
author:
author
)
login_as
(
user
)
visit
dashboard_todos_path
(
state: :done
)
end
it
'has the done todo present'
do
expect
(
page
).
to
have_selector
(
'.todos-list .todo.todo-done'
,
count:
1
)
end
describe
'restoring the todo'
do
before
do
within
first
(
'.todo'
)
do
click_link
'Add todo'
end
end
it
'is removed from the list'
do
expect
(
page
).
not_to
have_selector
(
'.todos-list .todo.todo-done'
)
end
it
'updates todo count'
do
expect
(
page
).
to
have_content
'To do 1'
expect
(
page
).
to
have_content
'Done 0'
end
end
end
context
'User has Todos with labels spanning multiple projects'
do
before
do
label1
=
create
(
:label
,
project:
project
)
...
...
@@ -143,7 +176,7 @@ describe 'Dashboard Todos', feature: true do
describe
'mark all as done'
,
js:
true
do
before
do
visit
dashboard_todos_path
click_link
(
'Mark all as done'
)
click_link
'Mark all as done'
end
it
'shows "All done" message!'
do
...
...
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