Merge pull request #1897 from jbargu/jg_sci_3646

[SCI-3646] Add paging functionality to external protocols
This commit is contained in:
Jure Grabnar 2019-07-08 13:06:19 +02:00 committed by GitHub
commit ae7da78bff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 101 additions and 35 deletions

View file

@ -53,6 +53,14 @@ function setDefaultViewState() {
$('.list-wrapper').hide(); $('.list-wrapper').hide();
} }
// Handle clicks on Load more protocols button
function applyClickCallbackOnShowMoreProtocols() {
$('.show-more-protocols-btn button').off('click').on('click', function() {
$('form.protocols-search-bar #page-id').val($(this).data('next-page-id'));
$('form.protocols-search-bar').submit();
});
}
// Apply AJAX callbacks onto the search box // Apply AJAX callbacks onto the search box
function applySearchCallback() { function applySearchCallback() {
var timeout; var timeout;
@ -75,20 +83,31 @@ function applySearchCallback() {
// Bind ajax calls on the form // Bind ajax calls on the form
$('form.protocols-search-bar').off('ajax:success').off('ajax:error') $('form.protocols-search-bar').off('ajax:success').off('ajax:error')
.bind('ajax:success', function(evt, data, status, xhr) { .bind('ajax:success', function(evt, data) {
if (data.html) { if (data.page_id > 1) {
// Remove old load more button since we will append a new one
$('.show-more-protocols-btn').remove();
$('.list-wrapper').append(data.html);
} else if (data.html) {
resetPreviewPanel(); resetPreviewPanel();
$('.empty-text').hide(); $('.empty-text').hide();
$('.list-wrapper').show(); $('.list-wrapper').show();
$('.list-wrapper').html(data.html); $('.list-wrapper').html(data.html);
applyClickCallbackOnProtocolCards(); $('.list-wrapper').scrollTo(0);
initLoadProtocolModalPreview();
} else { } else {
setDefaultViewState(); setDefaultViewState();
} }
// Reset page id after every request
$('form.protocols-search-bar #page-id').val(1);
// Apply all callbacks on new elements
applyClickCallbackOnProtocolCards();
applyClickCallbackOnShowMoreProtocols();
initLoadProtocolModalPreview();
}) })
.bind("ajax:error", function(evt, xhr, status, error) { .bind('ajax:error', function(evt, xhr) {
setDefaultViewState(); setDefaultViewState();
console.log(xhr.responseText); console.log(xhr.responseText);

View file

@ -196,6 +196,11 @@
padding-right: 0; padding-right: 0;
} }
.show-more-protocols-btn {
margin-bottom: 5px;
margin-top: 5px;
}
} }
.protocol-preview-panel { .protocol-preview-panel {

View file

@ -15,7 +15,8 @@ class ExternalProtocolsController < ApplicationController
html: render_to_string( html: render_to_string(
partial: 'protocol_importers/list_of_protocol_cards.html.erb', partial: 'protocol_importers/list_of_protocol_cards.html.erb',
locals: { protocols: service_call.protocols_list, show_import_button: show_import_button } locals: { protocols: service_call.protocols_list, show_import_button: show_import_button }
) ),
page_id: service_call.protocols_list[:pagination][:current_page]
} }
else else
render json: { errors: service_call.errors }, status: 400 render json: { errors: service_call.errors }, status: 400

View file

@ -41,9 +41,7 @@ module ProtocolImporters
@errors[:invalid_params][:key] = 'Key cannot be empty' if @query_params[:key].blank? @errors[:invalid_params][:key] = 'Key cannot be empty' if @query_params[:key].blank?
# try if page id is ok # try if page id is ok
if @query_params[:page_id] && !@query_params[:page_id].to_i.positive? @errors[:invalid_params][:page_id] = 'Page needs to be positive' if @query_params[:page_id]&.to_i&.negative?
@errors[:invalid_params][:page_id] = 'Page needs to be positive'
end
# try if endpints exists # try if endpints exists
@errors[:invalid_params][:source_endpoint] = 'Wrong source endpoint' unless endpoint_name&.is_a?(String) @errors[:invalid_params][:source_endpoint] = 'Wrong source endpoint' unless endpoint_name&.is_a?(String)

View file

@ -46,10 +46,11 @@ module ProtocolImporters
response = with_handle_network_errors do response = with_handle_network_errors do
sort_mappings = CONSTANTS[:sort_mappings] sort_mappings = CONSTANTS[:sort_mappings]
query = CONSTANTS.dig(:endpoints, :protocols, :default_query_params) query = CONSTANTS.dig(:endpoints, :protocols, :default_query_params)
.stringify_keys
.merge(query_params.except(:sort_by)) .merge(query_params.except(:sort_by))
if sort_mappings[query_params[:sort_by]&.to_sym] if sort_mappings[query_params[:sort_by]&.to_sym]
query = query.merge(sort_mappings[query_params[:sort_by].to_sym]) query = query.merge(sort_mappings[query_params[:sort_by].to_sym].stringify_keys)
end end
self.class.get('/protocols', query: query) self.class.get('/protocols', query: query)
@ -95,7 +96,7 @@ module ProtocolImporters
when 1219 when 1219
raise ProtocolImporters::ProtocolsIO::V3::UnauthorizedError.new(:token_expires), error_message raise ProtocolImporters::ProtocolsIO::V3::UnauthorizedError.new(:token_expires), error_message
else else
raise ProtocolImporters::ProtocolsIO::V3::Error.new(e.class), error_message raise ProtocolImporters::ProtocolsIO::V3::Error.new(:api_response_error), response.parsed_response
end end
end end
end end

View file

@ -73,6 +73,7 @@ module ProtocolImporters
def normalize_list(client_data) def normalize_list(client_data)
# client_data is HttpParty ApiReponse object # client_data is HttpParty ApiReponse object
protocols_hash = client_data.parsed_response.with_indifferent_access[:items] protocols_hash = client_data.parsed_response.with_indifferent_access[:items]
pagination = client_data.parsed_response.with_indifferent_access[:pagination]
normalized_data = {} normalized_data = {}
normalized_data[:protocols] = protocols_hash.map do |e| normalized_data[:protocols] = protocols_hash.map do |e|
@ -87,6 +88,14 @@ module ProtocolImporters
uri: e[:uri] uri: e[:uri]
} }
end end
# Parse pagination
normalized_data[:pagination] = {
current_page: pagination[:current_page],
total_pages: pagination[:total_pages],
page_size: pagination[:page_size]
}
normalized_data normalized_data
rescue StandardError => e rescue StandardError => e
raise ProtocolImporters::ProtocolsIO::V3::NormalizerError.new(e.class.to_s.downcase.to_sym), e.message raise ProtocolImporters::ProtocolsIO::V3::NormalizerError.new(e.class.to_s.downcase.to_sym), e.message

View file

@ -2,3 +2,12 @@
<%= render partial: 'protocol_importers/protocol_card', <%= render partial: 'protocol_importers/protocol_card',
locals: { protocol: protocol, show_import_button: show_import_button } %> locals: { protocol: protocol, show_import_button: show_import_button } %>
<% end %> <% end %>
<% if protocols[:pagination][:current_page] + 1 <= protocols[:pagination][:total_pages] %>
<div class='show-more-protocols-btn text-center'>
<button class='btn btn-default'
data-next-page-id='<%= protocols[:pagination][:current_page] + 1 %>'>
<%= t('protocol_importers.show_more_button.title') %>
</button>
</div>
<% end %>

View file

@ -1,25 +1,25 @@
<div class='protocol-card' <div class='protocol-card'
data-id='<%= protocol[:id] %>' data-id='<%= protocol[:id] %>'
data-protocol-source='<%= protocol[:source] %>' data-protocol-source='<%= protocol[:source] %>'
data-url='<%= team_build_external_protocol_path(current_team.id,) %>' data-url='<%= team_build_external_protocol_path(current_team.id,) %>'
data-show-url='<%= team_show_external_protocol_path(current_team.id) %>' data-show-url='<%= team_show_external_protocol_path(current_team.id) %>'
data-show-protocol-id='<%= protocol[:uri] %>'> data-show-protocol-id='<%= protocol[:uri] %>'>
<p class='protocol-title'><%= protocol[:title] %></p> <p class='protocol-title'><%= protocol[:title] %></p>
<div class='row'> <div class='row'>
<div class='row col-md-12 info-line'> <div class='row col-md-12 info-line'>
<%= l(Time.at(protocol[:created_on]).to_datetime, format: :full_date) %> • <%= protocol[:authors] %> <%= l(Time.at(protocol[:created_on]).to_datetime, format: :full_date) %> • <%= protocol[:authors] %>
</div> </div>
<div class='row'> <div class='row'>
<div class='col-md-6 info-line'> <div class='col-md-6 info-line'>
<%= t('protocol_importers.card.views_and_steps', nr_of_views: protocol[:nr_of_views], nr_of_steps: protocol[:nr_of_steps]) %> <%= t('protocol_importers.card.views_and_steps', nr_of_views: protocol[:nr_of_views], nr_of_steps: protocol[:nr_of_steps]) %>
</div> </div>
<div class='col-md-6 info-line'> <div class='col-md-6 info-line'>
<% if show_import_button %> <% if show_import_button %>
<button type="button" class='external-import-btn btn btn-primary pull-right'><%= t('protocol_importers.card.import_button_text') %></button> <button type="button" class='external-import-btn btn btn-primary pull-right'><%= t('protocol_importers.card.import_button_text') %></button>
<% end %> <% end %>
</div> </div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -10,6 +10,7 @@
class: 'protocolsio-logo' %> class: 'protocolsio-logo' %>
<span class='protocolsio-title'><%= t('protocols.index.external_protocols.protocolsio_title') %></span> <span class='protocolsio-title'><%= t('protocols.index.external_protocols.protocolsio_title') %></span>
<%= hidden_field_tag 'protocol_source', 'protocolsio/v3' %> <%= hidden_field_tag 'protocol_source', 'protocolsio/v3' %>
<%= hidden_field_tag 'page_id', 1, id: 'page-id' %>
</div> </div>
<div class='input-group'> <div class='input-group'>

View file

@ -231,7 +231,7 @@ class Constants
key: '', key: '',
order_field: :activity, order_field: :activity,
order_dir: :desc, order_dir: :desc,
page_size: 10, page_size: 50,
page_id: 1, page_id: 1,
fields: 'id,title,authors,created_on,uri,stats' fields: 'id,title,authors,created_on,uri,stats'
} }

View file

@ -5,6 +5,8 @@ en:
card: card:
views_and_steps: '%{nr_of_views} views • %{nr_of_steps} steps' views_and_steps: '%{nr_of_views} views • %{nr_of_steps} steps'
import_button_text: 'Import...' import_button_text: 'Import...'
show_more_button:
title: 'Show more protocols'
templates: templates:
amount: amount:
title: 'Amount' title: 'Amount'

View file

@ -15,8 +15,8 @@ describe ExternalProtocolsController, type: :controller do
team_id: team.id, team_id: team.id,
key: 'search_string', key: 'search_string',
protocol_source: 'protocolsio/v3', protocol_source: 'protocolsio/v3',
page_id: 1, page_id: 2,
page_size: 10, page_size: 50,
order_field: 'activity', order_field: 'activity',
order_dir: 'desc' order_dir: 'desc'
} }
@ -24,10 +24,21 @@ describe ExternalProtocolsController, type: :controller do
let(:action) { get :index, params: params } let(:action) { get :index, params: params }
let(:valid_search_response) do
{
protocols: [],
pagination: {
current_page: 2,
total_pages: 6,
page_size: 10
}
}
end
before do before do
service = double('success_service') service = double('success_service')
allow(service).to(receive(:succeed?)).and_return(true) allow(service).to(receive(:succeed?)).and_return(true)
allow(service).to(receive(:protocols_list)).and_return({}) allow(service).to(receive(:protocols_list)).and_return(valid_search_response)
allow_any_instance_of(ProtocolImporters::SearchProtocolsService).to(receive(:call)).and_return(service) allow_any_instance_of(ProtocolImporters::SearchProtocolsService).to(receive(:call)).and_return(service)
end end
@ -42,6 +53,11 @@ describe ExternalProtocolsController, type: :controller do
action action
expect(JSON.parse(response.body)).to have_key('html') expect(JSON.parse(response.body)).to have_key('html')
end end
it 'contains page_id in the response' do
action
expect(JSON.parse(response.body)['page_id']).to eq 2
end
end end
describe 'GET show' do describe 'GET show' do

View file

@ -100,5 +100,10 @@
"nr_of_views": 33, "nr_of_views": 33,
"uri": "SN-Maintenance-Medium-for-Synechococcus-c9hz35" "uri": "SN-Maintenance-Medium-for-Synechococcus-c9hz35"
} }
] ],
"pagination": {
"current_page": 1,
"total_pages": 6,
"page_size": 10
}
} }

View file

@ -72,7 +72,7 @@ describe ProtocolImporters::SearchProtocolsService do
.and_return(normalized_list)) .and_return(normalized_list))
end end
it 'returns an error when params are invalid' do it 'returns normalized list' do
expect(service_call.protocols_list).to be == normalized_list expect(service_call.protocols_list).to be == normalized_list
end end
end end