Skip to content

Commit

Permalink
Merge pull request #806 from edx/alangsto/add_loading_spinner
Browse files Browse the repository at this point in the history
added loading spinner to search bar
  • Loading branch information
alangsto committed Mar 8, 2021
2 parents 09a111e + 925e751 commit 02f315b
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 13 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ Change Log
Unreleased
~~~~~~~~~~

[3.7.7] - 2021-03-08
~~~~~~~~~~~~~~~~~~~~
* Add loading spinner for searching to onboarding attempt and special attempts sections on the
instructor dashboard

[3.7.6] - 2021-03-05
~~~~~~~~~~~~~~~~~~~~
* Fix bug with StudentProctoredExamAttempt put handler where course_id was being incorrectly determined,
Expand Down
2 changes: 1 addition & 1 deletion edx_proctoring/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
"""

# Be sure to update the version number in edx_proctoring/package.json
__version__ = '3.7.6'
__version__ = '3.7.7'

default_app_config = 'edx_proctoring.apps.EdxProctoringConfig' # pylint: disable=invalid-name
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,16 @@ edx = edx || {};
edx.dashboard.dropdown.toggleExamAttemptActionDropdownMenu(event);
},
searchAttempts: function(event) {
var $searchIcon, $spinner;
var searchText = $('#search_attempt_id').val();
if (searchText !== '') {
this.inSearchMode = true;
this.searchText = searchText;
this.collection.url = this.initial_url + this.course_id + '/search/' + searchText;
$searchIcon = $(document.getElementById('attempt-search-indicator'));
$searchIcon.addClass('hidden');
$spinner = $(document.getElementById('attempt-loading-indicator'));
$spinner.removeClass('hidden');
this.hydrate();
event.stopPropagation();
event.preventDefault();
Expand Down Expand Up @@ -147,7 +152,12 @@ edx = edx || {};
var self = this;
self.collection.fetch({
success: function() {
var $searchIcon, $spinner;
self.render();
$spinner = $(document.getElementById('attempt-loading-indicator'));
$spinner.addClass('hidden');
$searchIcon = $(document.getElementById('attempt-search-indicator'));
$searchIcon.removeClass('hidden');
}
});
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,17 @@ edx = edx || {};
'click li > a.target-link': 'getPaginatedItems'
},
searchItems: function(event) {
var $searchIcon, $spinner;
var searchText = $('#search_onboarding_id').val();
if (searchText !== '') {
this.inSearchMode = true;
this.searchText = searchText;
this.currentPage = 1;
this.collection.url = this.constructUrl();
$searchIcon = $(document.getElementById('onboarding-search-indicator'));
$searchIcon.addClass('hidden');
$spinner = $(document.getElementById('onboarding-loading-indicator'));
$spinner.removeClass('hidden');
this.hydrate();
event.stopPropagation();
event.preventDefault();
Expand Down Expand Up @@ -164,12 +169,22 @@ edx = edx || {};
var self = this;
self.collection.fetch({
success: function() {
var $searchIcon, $spinner;
self.render();
$spinner = $(document.getElementById('onboarding-loading-indicator'));
$spinner.addClass('hidden');
$searchIcon = $(document.getElementById('onboarding-search-indicator'));
$searchIcon.removeClass('hidden');
},
error: function() {
// in the case that there is no onboarding data, we
// still want the view to render
var $searchIcon, $spinner;
self.render();
$spinner = $(document.getElementById('onboarding-loading-indicator'));
$spinner.addClass('hidden');
$searchIcon = $(document.getElementById('onboarding-search-indicator'));
$searchIcon.removeClass('hidden');
}
});
},
Expand Down
100 changes: 96 additions & 4 deletions edx_proctoring/static/proctoring/spec/proctored_exam_attempt_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,16 @@ describe('ProctoredExamAttemptView', function() {
'<div class="top-header">' +
'<div class="search-attempts">' +
'<input type="text" id="search_attempt_id" placeholder="e.g johndoe or [email protected]"' +
'<% if (inSearchMode) { %> value="<%= searchText %>" <%} %>' +
'/> <span class="search"><span class="icon fa fa-search" aria-hidden="true"></span></span> ' +
'<% if (inSearchMode) { %> value="<%= searchText %>" <%} %> /> ' +
'<span class="search">' +
'<span class="icon fa fa-search" id="attempt-search-indicator" aria-hidden="true"></span>' +
'<div aria-live="polite" aria-relevant="all">' +
'<div id="attempt-loading-indicator" class="hidden">' +
'<span class="icon fa fa-spinner fa-pulse" aria-hidden="true"></span>' +
'<span class="sr"><%- gettext("Loading") %></span>' +
'</div>' +
'</div>' +
'</span>' +
'<span class="clear-search"><span class="icon fa fa-remove" aria-hidden="true"></span></span>' +
'</div>' +
'<ul class="pagination">' +
Expand Down Expand Up @@ -268,8 +276,16 @@ describe('ProctoredExamAttemptView', function() {
'<div class="top-header">' +
'<div class="search-attempts">' +
'<input type="text" id="search_attempt_id" placeholder="e.g johndoe or [email protected]"' +
'<% if (inSearchMode) { %> value="<%= searchText %>" <%} %>' +
'/> <span class="search"><span class="icon fa fa-search" aria-hidden="true"></span></span> ' +
'<% if (inSearchMode) { %> value="<%= searchText %>" <%} %> /> ' +
'<span class="search">' +
'<span class="icon fa fa-search" id="attempt-search-indicator" aria-hidden="true"></span>' +
'<div aria-live="polite" aria-relevant="all">' +
'<div id="attempt-loading-indicator" class="hidden">' +
'<span class="icon fa fa-spinner fa-pulse" aria-hidden="true"></span>' +
'<span class="sr"><%- gettext("Loading") %></span>' +
'</div>' +
'</div>' +
'</span>' +
'<span class="clear-search"><span class="icon fa fa-remove" aria-hidden="true"></span></span>' +
'</div>' +
'<ul class="pagination">' +
Expand Down Expand Up @@ -518,6 +534,10 @@ describe('ProctoredExamAttemptView', function() {

expect(this.proctored_exam_attempt_view.$el.find('tr.allowance-items')).toContainHtml('<td> testuser1 </td>');
expect(this.proctored_exam_attempt_view.$el.find('tr.allowance-items').html()).toContain('Normal Exam');
expect(this.proctored_exam_attempt_view.$el.find('#attempt-search-indicator').hasClass('hidden'))
.toEqual(false);
expect(this.proctored_exam_attempt_view.$el.find('#attempt-loading-indicator').hasClass('hidden'))
.toEqual(true);

$('#search_attempt_id').val(searchText);

Expand All @@ -538,13 +558,25 @@ describe('ProctoredExamAttemptView', function() {
spyOnEvent('.search-attempts > span.search', 'click');
$('.search-attempts > span.search').trigger('click');

// check that spinner is visible
expect(this.proctored_exam_attempt_view.$el.find('#attempt-search-indicator').hasClass('hidden'))
.toEqual(true);
expect(this.proctored_exam_attempt_view.$el.find('#attempt-loading-indicator').hasClass('hidden'))
.toEqual(false);

// process the search attempt requests.
this.server.respond();

// search matches the existing attempts
expect(this.proctored_exam_attempt_view.$el.find('tr.allowance-items').html()).toContain('testuser1');
expect(this.proctored_exam_attempt_view.$el.find('tr.allowance-items').html()).toContain('Normal Exam');
// check that spinner is hidden again
expect(this.proctored_exam_attempt_view.$el.find('#attempt-search-indicator').hasClass('hidden'))
.toEqual(false);
expect(this.proctored_exam_attempt_view.$el.find('#attempt-loading-indicator').hasClass('hidden'))
.toEqual(true);
});

it('should clear the search for the proctored exam attempt', function() {
var searchText = 'invalid_search_text';
this.server.respondWith('GET', '/api/edx_proctoring/v1/proctored_exam/attempt/course_id/test_course_id',
Expand Down Expand Up @@ -860,4 +892,64 @@ describe('ProctoredExamAttemptView', function() {
// check that accordion is no longer hidden
expect(this.proctored_exam_attempt_view.$el.find('.accordion-panel').hasClass('is-hidden')).toEqual(false);
});

it('searches and shows spinner for grouped attempts', function() {
var searchText = 'testuser1';
setFixtures('<div class="student-proctored-exam-container" data-course-id="test_course_id" ' +
'data-enable-exam-resume-proctoring-improvements="True"></div>');

this.server.respondWith('GET', '/api/edx_proctoring/v1/proctored_exam/attempt/grouped/course_id/test_course_id',
[
200,
{
'Content-Type': 'application/json'
},
JSON.stringify(getExpectedGroupedProctoredExamAttemptWithAttemptStatusJson('submitted', false))
]
);
this.proctored_exam_attempt_view = new edx.instructor_dashboard.proctoring.ProctoredExamAttemptView();

// Process all requests so far
this.server.respond();
this.server.respond();

expect(this.proctored_exam_attempt_view.$el.find('#attempt-search-indicator').hasClass('hidden'))
.toEqual(false);
expect(this.proctored_exam_attempt_view.$el.find('#attempt-loading-indicator').hasClass('hidden'))
.toEqual(true);

$('#search_attempt_id').val(searchText);

// search for the proctored exam attempt
this.server.respondWith(
'GET',
'/api/edx_proctoring/v1/proctored_exam/attempt/grouped/course_id/test_course_id/search/' + searchText,
[
200,
{
'Content-Type': 'application/json'
},
JSON.stringify(getExpectedGroupedProctoredExamAttemptWithAttemptStatusJson('started'))
]
);

// trigger the search attempt event.
spyOnEvent('.search-attempts > span.search', 'click');
$('.search-attempts > span.search').trigger('click');

// check that spinner is visible
expect(this.proctored_exam_attempt_view.$el.find('#attempt-search-indicator').hasClass('hidden'))
.toEqual(true);
expect(this.proctored_exam_attempt_view.$el.find('#attempt-loading-indicator').hasClass('hidden'))
.toEqual(false);

// process the search attempt requests.
this.server.respond();

// check that spinner is hidden again
expect(this.proctored_exam_attempt_view.$el.find('#attempt-search-indicator').hasClass('hidden'))
.toEqual(false);
expect(this.proctored_exam_attempt_view.$el.find('#attempt-loading-indicator').hasClass('hidden'))
.toEqual(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,15 @@ describe('ProctoredExamOnboardingView', function() {
'value="<%= searchText %>"' +
'<%} %>' +
'/>' +
'<span class="search"><span class="icon fa fa-search" aria-hidden="true"></span></span>' +
'<span class="search">' +
'<span class="icon fa fa-search" id="onboarding-search-indicator" aria-hidden="true"></span>' +
'<div aria-live="polite" aria-relevant="all">' +
'<div id="onboarding-loading-indicator" class="hidden">' +
'<span class="icon fa fa-spinner fa-pulse" aria-hidden="true"></span>' +
'<span class="sr"><%- gettext("Loading") %></span>' +
'</div>' +
'</div>' +
'</span>' +
'<span class="clear-search"><span class="icon fa fa-remove" aria-hidden="true"></span></span>' +
'</div>' +
'<ul class="pagination">' +
Expand Down Expand Up @@ -266,4 +274,67 @@ describe('ProctoredExamOnboardingView', function() {
expect(this.proctored_exam_onboarding_view.$el.find('.onboarding-items').last().html())
.toContain('Approved in Another Course');
});

it('should search for onboarding attempts', function() {
var searchText = 'badSearch';
this.server.respondWith('GET', '/api/edx_proctoring/v1/user_onboarding/status/course_id/test_course_id',
[
200,
{
'Content-Type': 'application/json'
},
JSON.stringify(expectedOnboardingDataJson)
]
);
this.proctored_exam_onboarding_view = new edx.instructor_dashboard.proctoring.ProctoredExamOnboardingView();

// Process all requests so far
this.server.respond();
this.server.respond();

expect(this.proctored_exam_onboarding_view.$el.find('.onboarding-items').html())
.toContain('testuser1');
expect(this.proctored_exam_onboarding_view.$el.find('.onboarding-items').html())
.toContain('Not Started');
expect(this.proctored_exam_onboarding_view.$el.find('.onboarding-items').html())
.toContain('---');
expect(this.proctored_exam_onboarding_view.$el.find('#onboarding-search-indicator').hasClass('hidden'))
.toEqual(false);
expect(this.proctored_exam_onboarding_view.$el.find('#onboarding-loading-indicator').hasClass('hidden'))
.toEqual(true);

$('#search_onboarding_id').val(searchText);

// search for the proctored exam attempt
this.server.respondWith(
'GET',
'/api/edx_proctoring/v1/user_onboarding/status/course_id/test_course_id?text_search=' + searchText,
[
200,
{
'Content-Type': 'application/json'
},
JSON.stringify(noDataJson)
]
);

// trigger the search attempt event.
spyOnEvent('.search-onboarding > span.search', 'click');
$('.search-onboarding > span.search').trigger('click');

// check that spinner is visible
expect(this.proctored_exam_onboarding_view.$el.find('#onboarding-search-indicator').hasClass('hidden'))
.toEqual(true);
expect(this.proctored_exam_onboarding_view.$el.find('#onboarding-loading-indicator').hasClass('hidden'))
.toEqual(false);

// process the search attempt requests.
this.server.respond();

// check that spinner is hidden again
expect(this.proctored_exam_onboarding_view.$el.find('#onboarding-search-indicator').hasClass('hidden'))
.toEqual(false);
expect(this.proctored_exam_onboarding_view.$el.find('#onboarding-loading-indicator').hasClass('hidden'))
.toEqual(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,15 @@
value="<%= searchText %>"
<%} %>
/>
<span class="search"><span class="icon fa fa-search" aria-hidden="true"></span></span>
<span class="search">
<span class="icon fa fa-search" id="onboarding-search-indicator" aria-hidden="true"></span>
<div aria-live="polite" aria-relevant="all">
<div id="onboarding-loading-indicator" class="hidden">
<span class="icon fa fa-spinner fa-pulse" aria-hidden="true"></span>
<span class="sr"><%- gettext("Loading") %></span>
</div>
</div>
</span>
<span class="clear-search"><span class="icon fa fa-remove" aria-hidden="true"></span></span>
</div>
<ul class="pagination">
Expand Down Expand Up @@ -87,10 +95,10 @@
<ul class="status-checkboxes">
<% _.each(onboardingStatuses, function(status){ %>
<li>
<input type="checkbox" id="<%= status %>" name="status-filters" value="<%= status %>"
<input type="checkbox" id="<%= status %>" name="status-filters" value="<%= status %>"
<% if (filters.includes(status)) { %>checked="true"<% } %>>
<label for="<%= status %>">
<%- interpolate(gettext(" %(onboardingStatus)s "),
<%- interpolate(gettext(" %(onboardingStatus)s "),
{ onboardingStatus: getOnboardingStatus(status) }, true) %>
</label>
</li>
Expand All @@ -117,7 +125,7 @@
<%- interpolate(gettext(" %(username)s "), { username: item.username }, true) %>
</td>
<td>
<%- interpolate(gettext(" %(onboardingStatus)s "),
<%- interpolate(gettext(" %(onboardingStatus)s "),
{ onboardingStatus: getOnboardingStatus(item.status) }, true) %>
</td>
<td><%= getDateFormat(item.modified) %></td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,15 @@
value="<%= searchText %>"
<%} %>
/>
<span class="search"><span class="icon fa fa-search" aria-hidden="true"></span></span>
<span class="search">
<span class="icon fa fa-search" id="attempt-search-indicator" aria-hidden="true"></span>
<div aria-live="polite" aria-relevant="all">
<div id="attempt-loading-indicator" class="hidden">
<span class="icon fa fa-spinner fa-pulse" aria-hidden="true"></span>
<span class="sr"><%- gettext("Loading") %></span>
</div>
</div>
</span>
<span class="clear-search"><span class="icon fa fa-remove" aria-hidden="true"></span></span>
</div>
<ul class="pagination">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,15 @@
value="<%= searchText %>"
<%} %>
/>
<span class="search"><span class="icon fa fa-search" aria-hidden="true"></span></span>
<span class="search">
<span class="icon fa fa-search" id="attempt-search-indicator" aria-hidden="true"></span>
<div aria-live="polite" aria-relevant="all">
<div id="attempt-loading-indicator" class="hidden">
<span class="icon fa fa-spinner fa-pulse" aria-hidden="true"></span>
<span class="sr"><%- gettext("Loading") %></span>
</div>
</div>
</span>
<span class="clear-search"><span class="icon fa fa-remove" aria-hidden="true"></span></span>
</div>
<ul class="pagination">
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@edx/edx-proctoring",
"//": "Be sure to update the version number in edx_proctoring/__init__.py",
"//": "Note that the version format is slightly different than that of the Python version when using prereleases.",
"version": "3.7.6",
"version": "3.7.7",
"main": "edx_proctoring/static/index.js",
"repository": {
"type": "git",
Expand Down

0 comments on commit 02f315b

Please sign in to comment.