Merge pull request #2377 from edx/gprice/forum-thread-pagination
Add pagination of responses to forum threads
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
describe "DiscussionThreadView", ->
|
||||
beforeEach ->
|
||||
setFixtures(
|
||||
"""
|
||||
<script type="text/template" id="thread-template">
|
||||
<article class="discussion-article">
|
||||
<div class="response-count"/>
|
||||
<ol class="responses"/>
|
||||
<div class="response-pagination"/>
|
||||
</article>
|
||||
</script>
|
||||
<div class="thread-fixture"/>
|
||||
"""
|
||||
)
|
||||
|
||||
jasmine.Clock.useMock()
|
||||
@threadData = {
|
||||
id: "dummy"
|
||||
}
|
||||
@thread = new Thread(@threadData)
|
||||
@view = new DiscussionThreadView({ model: @thread })
|
||||
@view.setElement($(".thread-fixture"))
|
||||
spyOn($, "ajax")
|
||||
# Avoid unnecessary boilerplate
|
||||
spyOn(@view.showView, "render")
|
||||
spyOn(@view, "makeWmdEditor")
|
||||
spyOn(DiscussionThreadView.prototype, "renderResponse")
|
||||
|
||||
describe "response count and pagination", ->
|
||||
|
||||
setNextResponseContent = (content) ->
|
||||
$.ajax.andCallFake(
|
||||
(params) =>
|
||||
params.success({"content": content})
|
||||
{always: ->}
|
||||
)
|
||||
|
||||
renderWithContent = (view, content) ->
|
||||
setNextResponseContent(content)
|
||||
view.render()
|
||||
jasmine.Clock.tick(100)
|
||||
|
||||
assertRenderedCorrectly = (view, countText, displayCountText, buttonText) ->
|
||||
expect(view.$el.find(".response-count").text()).toEqual(countText)
|
||||
if displayCountText
|
||||
expect(view.$el.find(".response-display-count").text()).toEqual(displayCountText)
|
||||
else
|
||||
expect(view.$el.find(".response-display-count").length).toEqual(0)
|
||||
if buttonText
|
||||
expect(view.$el.find(".load-response-button").text()).toEqual(buttonText)
|
||||
else
|
||||
expect(view.$el.find(".load-response-button").length).toEqual(0)
|
||||
|
||||
it "correctly render for a thread with no responses", ->
|
||||
renderWithContent(@view, {resp_total: 0, children: []})
|
||||
assertRenderedCorrectly(@view, "0 responses", null, null)
|
||||
|
||||
it "correctly render for a thread with one response", ->
|
||||
renderWithContent(@view, {resp_total: 1, children: [{}]})
|
||||
assertRenderedCorrectly(@view, "1 response", "Showing all responses", null)
|
||||
|
||||
it "correctly render for a thread with one additional page", ->
|
||||
renderWithContent(@view, {resp_total: 2, children: [{}]})
|
||||
assertRenderedCorrectly(@view, "2 responses", "Showing first response", "Load all responses")
|
||||
|
||||
it "correctly render for a thread with multiple additional pages", ->
|
||||
renderWithContent(@view, {resp_total: 111, children: [{}, {}]})
|
||||
assertRenderedCorrectly(@view, "111 responses", "Showing first 2 responses", "Load next 100 responses")
|
||||
|
||||
describe "on clicking the load more button", ->
|
||||
beforeEach ->
|
||||
renderWithContent(@view, {resp_total: 5, children: [{}]})
|
||||
assertRenderedCorrectly(@view, "5 responses", "Showing first response", "Load all responses")
|
||||
|
||||
it "correctly re-render when all threads have loaded", ->
|
||||
setNextResponseContent({resp_total: 5, children: [{}, {}, {}, {}]})
|
||||
@view.$el.find(".load-response-button").click()
|
||||
assertRenderedCorrectly(@view, "5 responses", "Showing all responses", null)
|
||||
|
||||
it "correctly re-render when one page remains", ->
|
||||
setNextResponseContent({resp_total: 42, children: [{}, {}]})
|
||||
@view.$el.find(".load-response-button").click()
|
||||
assertRenderedCorrectly(@view, "42 responses", "Showing first 3 responses", "Load all responses")
|
||||
|
||||
it "correctly re-render when multiple pages remain", ->
|
||||
setNextResponseContent({resp_total: 111, children: [{}, {}]})
|
||||
@view.$el.find(".load-response-button").click()
|
||||
assertRenderedCorrectly(@view, "111 responses", "Showing first 3 responses", "Load next 100 responses")
|
||||
@@ -1,6 +1,9 @@
|
||||
if Backbone?
|
||||
class @DiscussionThreadView extends DiscussionContentView
|
||||
|
||||
INITIAL_RESPONSE_PAGE_SIZE = 25
|
||||
SUBSEQUENT_RESPONSE_PAGE_SIZE = 100
|
||||
|
||||
events:
|
||||
"click .discussion-submit-post": "submitComment"
|
||||
"click .add-response-btn": "scrollToAddResponse"
|
||||
@@ -11,6 +14,7 @@ if Backbone?
|
||||
initialize: ->
|
||||
super()
|
||||
@createShowView()
|
||||
@responses = new Comments()
|
||||
|
||||
renderTemplate: ->
|
||||
@template = _.template($("#thread-template").html())
|
||||
@@ -18,7 +22,6 @@ if Backbone?
|
||||
|
||||
render: ->
|
||||
@$el.html(@renderTemplate())
|
||||
@$el.find(".loading").hide()
|
||||
@delegateEvents()
|
||||
|
||||
@renderShowView()
|
||||
@@ -27,26 +30,95 @@ if Backbone?
|
||||
@$("span.timeago").timeago()
|
||||
@makeWmdEditor "reply-body"
|
||||
@renderAddResponseButton()
|
||||
@renderResponses()
|
||||
@responses.on("add", @renderResponse)
|
||||
# Without a delay, jQuery doesn't add the loading extension defined in
|
||||
# utils.coffee before safeAjax is invoked, which results in an error
|
||||
setTimeout(
|
||||
=> @loadResponses(INITIAL_RESPONSE_PAGE_SIZE, @$el.find(".responses"), true),
|
||||
100
|
||||
)
|
||||
@
|
||||
|
||||
cleanup: ->
|
||||
if @responsesRequest?
|
||||
@responsesRequest.abort()
|
||||
|
||||
renderResponses: ->
|
||||
setTimeout(=>
|
||||
@$el.find(".loading").show()
|
||||
, 200)
|
||||
loadResponses: (responseLimit, elem, firstLoad) ->
|
||||
@responsesRequest = DiscussionUtil.safeAjax
|
||||
url: DiscussionUtil.urlFor('retrieve_single_thread', @model.get('commentable_id'), @model.id)
|
||||
data:
|
||||
resp_skip: @responses.size()
|
||||
resp_limit: responseLimit if responseLimit
|
||||
$elem: elem
|
||||
$loading: elem
|
||||
takeFocus: true
|
||||
complete: =>
|
||||
@responseRequest = null
|
||||
success: (data, textStatus, xhr) =>
|
||||
@responsesRequest = null
|
||||
@$el.find(".loading").remove()
|
||||
Content.loadContentInfos(data['annotated_content_info'])
|
||||
comments = new Comments(data['content']['children'])
|
||||
comments.each @renderResponse
|
||||
@responses.add(data['content']['children'])
|
||||
@renderResponseCountAndPagination(data['content']['resp_total'])
|
||||
@trigger "thread:responses:rendered"
|
||||
error: =>
|
||||
if firstLoad
|
||||
DiscussionUtil.discussionAlert(
|
||||
gettext("Sorry"),
|
||||
gettext("We had some trouble loading responses. Please reload the page.")
|
||||
)
|
||||
else
|
||||
DiscussionUtil.discussionAlert(
|
||||
gettext("Sorry"),
|
||||
gettext("We had some trouble loading more responses. Please try again.")
|
||||
)
|
||||
|
||||
renderResponseCountAndPagination: (responseTotal) =>
|
||||
@$el.find(".response-count").html(
|
||||
interpolate(
|
||||
ngettext(
|
||||
"%(numResponses)s response",
|
||||
"%(numResponses)s responses",
|
||||
responseTotal
|
||||
),
|
||||
{numResponses: responseTotal},
|
||||
true
|
||||
)
|
||||
)
|
||||
responsePagination = @$el.find(".response-pagination")
|
||||
responsePagination.empty()
|
||||
if responseTotal > 0
|
||||
responsesRemaining = responseTotal - @responses.size()
|
||||
showingResponsesText =
|
||||
if responsesRemaining == 0
|
||||
gettext("Showing all responses")
|
||||
else
|
||||
interpolate(
|
||||
ngettext(
|
||||
"Showing first response",
|
||||
"Showing first %(numResponses)s responses",
|
||||
@responses.size()
|
||||
),
|
||||
{numResponses: @responses.size()},
|
||||
true
|
||||
)
|
||||
responsePagination.append($("<span>").addClass("response-display-count").html(
|
||||
_.escape(showingResponsesText)
|
||||
))
|
||||
if responsesRemaining > 0
|
||||
if responsesRemaining < SUBSEQUENT_RESPONSE_PAGE_SIZE
|
||||
responseLimit = null
|
||||
buttonText = gettext("Load all responses")
|
||||
else
|
||||
responseLimit = SUBSEQUENT_RESPONSE_PAGE_SIZE
|
||||
buttonText = interpolate(
|
||||
gettext("Load next %(numResponses)s responses"),
|
||||
{numResponses: responseLimit},
|
||||
true
|
||||
)
|
||||
loadMoreButton = $("<button>").addClass("load-response-button").html(
|
||||
_.escape(buttonText)
|
||||
)
|
||||
loadMoreButton.click((event) => @loadResponses(responseLimit, loadMoreButton))
|
||||
responsePagination.append(loadMoreButton)
|
||||
|
||||
renderResponse: (response) =>
|
||||
response.set('thread', @model)
|
||||
|
||||
Reference in New Issue
Block a user