Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
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
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
e6871af2
Commit
e6871af2
authored
Jun 16, 2014
by
jsa
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
forums: add UX for username search results.
JIRA: FOR-627
parent
7cc85b43
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
193 additions
and
64 deletions
+193
-64
common/static/coffee/spec/discussion/view/discussion_thread_list_view_spec.coffee
+126
-57
common/static/coffee/src/discussion/utils.coffee
+1
-0
common/static/coffee/src/discussion/views/discussion_thread_list_view.coffee
+27
-0
common/test/acceptance/pages/lms/discussion.py
+5
-2
common/test/acceptance/tests/test_discussion.py
+31
-3
lms/static/sass/discussion/_discussion-developer.scss
+3
-2
No files found.
common/static/coffee/spec/discussion/view/discussion_thread_list_view_spec.coffee
View file @
e6871af2
...
...
@@ -84,63 +84,6 @@ describe "DiscussionThreadListView", ->
@
view
=
new
DiscussionThreadListView
({
collection
:
@
discussion
,
el
:
$
(
".sidebar"
)})
@
view
.
render
()
testAlertMessages
=
(
expectedMessages
)
->
expect
(
$
(
".search-alert .message"
).
map
(
->
$
(
@
).
html
()
).
get
()).
toEqual
(
expectedMessages
)
it
"renders and removes search alerts"
,
->
testAlertMessages
[]
foo
=
@
view
.
addSearchAlert
(
"foo"
)
testAlertMessages
[
"foo"
]
bar
=
@
view
.
addSearchAlert
(
"bar"
)
testAlertMessages
[
"foo"
,
"bar"
]
@
view
.
removeSearchAlert
(
foo
.
cid
)
testAlertMessages
[
"bar"
]
@
view
.
removeSearchAlert
(
bar
.
cid
)
testAlertMessages
[]
it
"clears all search alerts"
,
->
@
view
.
addSearchAlert
(
"foo"
)
@
view
.
addSearchAlert
(
"bar"
)
@
view
.
addSearchAlert
(
"baz"
)
testAlertMessages
[
"foo"
,
"bar"
,
"baz"
]
@
view
.
clearSearchAlerts
()
testAlertMessages
[]
testCorrection
=
(
view
,
correctedText
)
->
spyOn
(
view
,
"addSearchAlert"
)
$
.
ajax
.
andCallFake
(
(
params
)
=>
params
.
success
(
{
discussion_data
:
[],
page
:
42
,
num_pages
:
99
,
corrected_text
:
correctedText
},
'success'
)
{
always
:
->
}
)
view
.
searchFor
(
"dummy"
)
expect
(
$
.
ajax
).
toHaveBeenCalled
()
it
"adds a search alert when an alternate term was searched"
,
->
testCorrection
(
@
view
,
"foo"
)
expect
(
@
view
.
addSearchAlert
).
toHaveBeenCalled
()
expect
(
@
view
.
addSearchAlert
.
mostRecentCall
.
args
[
0
]).
toMatch
(
/foo/
)
it
"does not add a search alert when no alternate term was searched"
,
->
testCorrection
(
@
view
,
null
)
expect
(
@
view
.
addSearchAlert
).
not
.
toHaveBeenCalled
()
it
"clears search alerts when a new search is performed"
,
->
spyOn
(
@
view
,
"clearSearchAlerts"
)
spyOn
(
DiscussionUtil
,
"safeAjax"
)
@
view
.
searchFor
(
"dummy"
)
expect
(
@
view
.
clearSearchAlerts
).
toHaveBeenCalled
()
it
"clears search alerts when the underlying collection changes"
,
->
spyOn
(
@
view
,
"clearSearchAlerts"
)
spyOn
(
@
view
,
"renderThread"
)
@
view
.
collection
.
trigger
(
"change"
,
new
Thread
({
id
:
1
}))
expect
(
@
view
.
clearSearchAlerts
).
toHaveBeenCalled
()
makeView
=
(
discussion
)
->
return
new
DiscussionThreadListView
(
el
:
$
(
".sidebar"
),
...
...
@@ -202,3 +145,129 @@ describe "DiscussionThreadListView", ->
it
"with sort preference comments"
,
->
changeSorting
(
@
threads
,
"votes"
,
"comments"
,
[
"Thread3"
,
"Thread2"
,
"Thread1"
])
describe
"search alerts"
,
->
testAlertMessages
=
(
expectedMessages
)
->
expect
(
$
(
".search-alert .message"
).
map
(
->
$
(
@
).
html
()
).
get
()).
toEqual
(
expectedMessages
)
it
"renders and removes search alerts"
,
->
testAlertMessages
[]
foo
=
@
view
.
addSearchAlert
(
"foo"
)
testAlertMessages
[
"foo"
]
bar
=
@
view
.
addSearchAlert
(
"bar"
)
testAlertMessages
[
"foo"
,
"bar"
]
@
view
.
removeSearchAlert
(
foo
.
cid
)
testAlertMessages
[
"bar"
]
@
view
.
removeSearchAlert
(
bar
.
cid
)
testAlertMessages
[]
it
"clears all search alerts"
,
->
@
view
.
addSearchAlert
(
"foo"
)
@
view
.
addSearchAlert
(
"bar"
)
@
view
.
addSearchAlert
(
"baz"
)
testAlertMessages
[
"foo"
,
"bar"
,
"baz"
]
@
view
.
clearSearchAlerts
()
testAlertMessages
[]
describe
"search spell correction"
,
->
beforeEach
->
spyOn
(
@
view
,
"searchForUser"
)
testCorrection
=
(
view
,
correctedText
)
->
spyOn
(
view
,
"addSearchAlert"
)
$
.
ajax
.
andCallFake
(
(
params
)
=>
params
.
success
(
{
discussion_data
:
[],
page
:
42
,
num_pages
:
99
,
corrected_text
:
correctedText
},
'success'
)
{
always
:
->
}
)
view
.
searchFor
(
"dummy"
)
expect
(
$
.
ajax
).
toHaveBeenCalled
()
it
"adds a search alert when an alternate term was searched"
,
->
testCorrection
(
@
view
,
"foo"
)
expect
(
@
view
.
addSearchAlert
.
callCount
).
toEqual
(
1
)
expect
(
@
view
.
addSearchAlert
.
mostRecentCall
.
args
[
0
]).
toMatch
(
/foo/
)
it
"does not add a search alert when no alternate term was searched"
,
->
testCorrection
(
@
view
,
null
)
expect
(
@
view
.
addSearchAlert
.
callCount
).
toEqual
(
1
)
expect
(
@
view
.
addSearchAlert
.
mostRecentCall
.
args
[
0
]).
toMatch
(
/no threads matched/i
)
it
"clears search alerts when a new search is performed"
,
->
spyOn
(
@
view
,
"clearSearchAlerts"
)
spyOn
(
DiscussionUtil
,
"safeAjax"
)
@
view
.
searchFor
(
"dummy"
)
expect
(
@
view
.
clearSearchAlerts
).
toHaveBeenCalled
()
it
"clears search alerts when the underlying collection changes"
,
->
spyOn
(
@
view
,
"clearSearchAlerts"
)
spyOn
(
@
view
,
"renderThread"
)
@
view
.
collection
.
trigger
(
"change"
,
new
Thread
({
id
:
1
}))
expect
(
@
view
.
clearSearchAlerts
).
toHaveBeenCalled
()
describe
"username search"
,
->
it
"makes correct ajax calls"
,
->
$
.
ajax
.
andCallFake
(
(
params
)
=>
expect
(
params
.
data
.
username
).
toEqual
(
"testing-username"
)
expect
(
params
.
url
.
path
()).
toEqual
(
DiscussionUtil
.
urlFor
(
"users"
))
params
.
success
(
{
users
:
[]},
'success'
)
{
always
:
->
}
)
@
view
.
searchForUser
(
"testing-username"
)
expect
(
$
.
ajax
).
toHaveBeenCalled
()
setAjaxResults
=
(
threadSuccess
,
userResult
)
->
# threadSuccess is a boolean indicating whether the thread search ajax call should succeed
# userResult is the value that should be returned as data from the username search ajax call
$
.
ajax
.
andCallFake
(
(
params
)
=>
if
params
.
data
.
text
and
threadSuccess
params
.
success
(
{
discussion_data
:
[],
page
:
42
,
num_pages
:
99
,
corrected_text
:
"dummy"
},
"success"
)
else
if
params
.
data
.
username
params
.
success
(
{
users
:
userResult
},
"success"
)
{
always
:
->
}
)
it
"gets called after a thread search succeeds"
,
->
spyOn
(
@
view
,
"searchForUser"
).
andCallThrough
()
setAjaxResults
(
true
,
[])
@
view
.
searchFor
(
"gizmo"
)
expect
(
@
view
.
searchForUser
).
toHaveBeenCalled
()
expect
(
$
.
ajax
.
mostRecentCall
.
args
[
0
].
data
.
username
).
toEqual
(
"gizmo"
)
it
"does not get called after a thread search fails"
,
->
spyOn
(
@
view
,
"searchForUser"
).
andCallThrough
()
setAjaxResults
(
false
,
[])
@
view
.
searchFor
(
"gizmo"
)
expect
(
@
view
.
searchForUser
).
not
.
toHaveBeenCalled
()
it
"adds a search alert when an username was matched"
,
->
spyOn
(
@
view
,
"addSearchAlert"
)
setAjaxResults
(
true
,
[{
username
:
"gizmo"
,
id
:
"1"
}])
@
view
.
searchForUser
(
"dummy"
)
expect
(
$
.
ajax
).
toHaveBeenCalled
()
expect
(
@
view
.
addSearchAlert
).
toHaveBeenCalled
()
expect
(
@
view
.
addSearchAlert
.
mostRecentCall
.
args
[
0
]).
toMatch
(
/gizmo/
)
it
"does not add a search alert when no username was matched"
,
->
spyOn
(
@
view
,
"addSearchAlert"
)
setAjaxResults
(
true
,
[])
@
view
.
searchForUser
(
"dummy"
)
expect
(
$
.
ajax
).
toHaveBeenCalled
()
expect
(
@
view
.
addSearchAlert
).
not
.
toHaveBeenCalled
()
common/static/coffee/src/discussion/utils.coffee
View file @
e6871af2
...
...
@@ -73,6 +73,7 @@ class @DiscussionUtil
downvote_comment
:
"/courses/
#{
$$course_id
}
/discussion/comments/
#{
param
}
/downvote"
undo_vote_for_comment
:
"/courses/
#{
$$course_id
}
/discussion/comments/
#{
param
}
/unvote"
upload
:
"/courses/
#{
$$course_id
}
/discussion/upload"
users
:
"/courses/
#{
$$course_id
}
/discussion/users"
search
:
"/courses/
#{
$$course_id
}
/discussion/forum/search"
retrieve_discussion
:
"/courses/
#{
$$course_id
}
/discussion/forum/
#{
param
}
/inline"
retrieve_single_thread
:
"/courses/
#{
$$course_id
}
/discussion/forum/
#{
param
}
/threads/
#{
param1
}
"
...
...
common/static/coffee/src/discussion/views/discussion_thread_list_view.coffee
View file @
e6871af2
...
...
@@ -445,6 +445,7 @@ if Backbone?
data
:
{
text
:
text
}
url
:
url
type
:
"GET"
dataType
:
'json'
$loading
:
$
loadingCallback
:
=>
@
$
(
".post-list"
).
html
(
'<li class="loading"><div class="loading-animation"><span class="sr">'
+
gettext
(
'Loading thread list'
)
+
'</span></div></li>'
)
...
...
@@ -465,10 +466,36 @@ if Backbone?
true
)
@
addSearchAlert
(
message
)
else
if
response
.
discussion_data
.
length
==
0
@
addSearchAlert
(
gettext
(
'No threads matched your query.'
))
# TODO: Perhaps reload user info so that votes can be updated.
# In the future we might not load all of a user's votes at once
# so this would probably be necessary anyway
@
displayedCollection
.
reset
(
@
collection
.
models
)
# Don't think this is necessary
@
searchForUser
(
text
)
if
text
searchForUser
:
(
text
)
->
DiscussionUtil
.
safeAjax
data
:
{
username
:
text
}
url
:
DiscussionUtil
.
urlFor
(
"users"
)
type
:
"GET"
dataType
:
'json'
error
:
=>
return
success
:
(
response
)
=>
if
response
.
users
.
length
>
0
message
=
interpolate
(
_
.
escape
(
gettext
(
'Show posts by %(username)s.'
)),
{
"username"
:
_
.
template
(
'<a class="link-jump" href="<%= url %>"><%- username %></a>'
,
{
url
:
DiscussionUtil
.
urlFor
(
"user_profile"
,
response
.
users
[
0
].
id
),
username
:
response
.
users
[
0
].
username
})
},
true
)
@
addSearchAlert
(
message
)
clearSearch
:
(
callback
,
value
)
->
@
$
(
".post-search-field"
).
val
(
""
)
...
...
common/test/acceptance/pages/lms/discussion.py
View file @
e6871af2
...
...
@@ -351,13 +351,13 @@ class DiscussionTabHomePage(CoursePage, DiscussionPageMixin):
def
is_browser_on_page
(
self
):
return
self
.
q
(
css
=
".discussion-body section.home-header"
)
.
present
def
perform_search
(
self
):
def
perform_search
(
self
,
text
=
"dummy"
):
self
.
q
(
css
=
".discussion-body .sidebar .search"
)
.
first
.
click
()
EmptyPromise
(
lambda
:
self
.
q
(
css
=
".discussion-body .sidebar .search.is-open"
)
.
present
,
"waiting for search input to be available"
)
.
fulfill
()
self
.
q
(
css
=
"#search-discussions"
)
.
fill
(
"dummy"
+
chr
(
10
))
self
.
q
(
css
=
"#search-discussions"
)
.
fill
(
text
+
chr
(
10
))
EmptyPromise
(
self
.
is_ajax_finished
,
"waiting for server to return result"
...
...
@@ -366,6 +366,9 @@ class DiscussionTabHomePage(CoursePage, DiscussionPageMixin):
def
get_search_alert_messages
(
self
):
return
self
.
q
(
css
=
self
.
ALERT_SELECTOR
+
" .message"
)
.
text
def
get_search_alert_links
(
self
):
return
self
.
q
(
css
=
self
.
ALERT_SELECTOR
+
" .link-jump"
)
def
dismiss_alert_message
(
self
,
text
):
"""
dismiss any search alert message containing the specified text.
...
...
common/test/acceptance/tests/test_discussion.py
View file @
e6871af2
...
...
@@ -421,9 +421,18 @@ class DiscussionSearchAlertTest(UniqueCourseTest):
Tests for spawning and dismissing alerts related to user search actions and their results.
"""
SEARCHED_USERNAME
=
"gizmo"
def
setUp
(
self
):
super
(
DiscussionSearchAlertTest
,
self
)
.
setUp
()
CourseFixture
(
**
self
.
course_info
)
.
install
()
# first auto auth call sets up a user that we will search for in some tests
self
.
searched_user_id
=
AutoAuthPage
(
self
.
browser
,
username
=
self
.
SEARCHED_USERNAME
,
course_id
=
self
.
course_id
)
.
visit
()
.
get_user_id
()
# this auto auth call creates the actual session user
AutoAuthPage
(
self
.
browser
,
course_id
=
self
.
course_id
)
.
visit
()
self
.
page
=
DiscussionTabHomePage
(
self
.
browser
,
self
.
course_id
)
self
.
page
.
visit
()
...
...
@@ -433,12 +442,12 @@ class DiscussionSearchAlertTest(UniqueCourseTest):
def
check_search_alert_messages
(
self
,
expected
):
actual
=
self
.
page
.
get_search_alert_messages
()
self
.
assertTrue
(
all
(
map
(
lambda
msg
,
sub
:
msg
.
find
(
sub
)
>=
0
,
actual
,
expected
)))
self
.
assertTrue
(
all
(
map
(
lambda
msg
,
sub
:
msg
.
lower
()
.
find
(
sub
.
lower
()
)
>=
0
,
actual
,
expected
)))
def
test_no_rewrite
(
self
):
self
.
setup_corrected_text
(
None
)
self
.
page
.
perform_search
()
self
.
check_search_alert_messages
([])
self
.
check_search_alert_messages
([
"no threads"
])
def
test_rewrite_dismiss
(
self
):
self
.
setup_corrected_text
(
"foo"
)
...
...
@@ -458,7 +467,26 @@ class DiscussionSearchAlertTest(UniqueCourseTest):
self
.
setup_corrected_text
(
None
)
self
.
page
.
perform_search
()
self
.
check_search_alert_messages
([])
self
.
check_search_alert_messages
([
"no threads"
])
def
test_rewrite_and_user
(
self
):
self
.
setup_corrected_text
(
"foo"
)
self
.
page
.
perform_search
(
self
.
SEARCHED_USERNAME
)
self
.
check_search_alert_messages
([
"foo"
,
self
.
SEARCHED_USERNAME
])
def
test_user_only
(
self
):
self
.
setup_corrected_text
(
None
)
self
.
page
.
perform_search
(
self
.
SEARCHED_USERNAME
)
self
.
check_search_alert_messages
([
"no threads"
,
self
.
SEARCHED_USERNAME
])
# make sure clicking the link leads to the user profile page
UserProfileViewFixture
([])
.
push
()
self
.
page
.
get_search_alert_links
()
.
first
.
click
()
DiscussionUserProfilePage
(
self
.
browser
,
self
.
course_id
,
self
.
searched_user_id
,
self
.
SEARCHED_USERNAME
)
.
wait_for_page
()
class
DiscussionSortPreferenceTest
(
UniqueCourseTest
):
...
...
lms/static/sass/discussion/_discussion-developer.scss
View file @
e6871af2
...
...
@@ -70,11 +70,12 @@ body.discussion {
@include
transition
(
none
);
@extend
%t-weight5
;
padding
:
(
$baseline
/
4
)
(
$baseline
/
2
);
color
:
$white
;
// reseting poorly globally scoped hover/focus state for this control
&
:hover
,
&
:focus
{
color
:
$
link-color
;
text-decoration
:
underli
ne
;
color
:
$
white
;
text-decoration
:
no
ne
;
}
}
}
...
...
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