Skip to content

Commit 35c74ce

Browse files
GeorgeGorbanevandrew
authored andcommitted
dashboard/paginator (#518)
1 parent ac43e6c commit 35c74ce

File tree

7 files changed

+354
-1
lines changed

7 files changed

+354
-1
lines changed

lib/split/dashboard.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require 'split'
44
require 'bigdecimal'
55
require 'split/dashboard/helpers'
6+
require 'split/dashboard/pagination_helpers'
67

78
module Split
89
class Dashboard < Sinatra::Base
@@ -14,6 +15,7 @@ class Dashboard < Sinatra::Base
1415
set :method_override, true
1516

1617
helpers Split::DashboardHelpers
18+
helpers Split::DashboardPaginationHelpers
1719

1820
get '/' do
1921
# Display experiments without a winner at the top of the dashboard
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# frozen_string_literal: true
2+
require 'split/dashboard/paginator'
3+
4+
module Split
5+
module DashboardPaginationHelpers
6+
DEFAULT_PER = 10
7+
8+
def pagination_per
9+
@pagination_per ||= (params[:per] || DEFAULT_PER).to_i
10+
end
11+
12+
def page_number
13+
@page_number ||= (params[:page] || 1).to_i
14+
end
15+
16+
def paginated(collection)
17+
Split::DashboardPaginator.new(collection, page_number, pagination_per).paginate
18+
end
19+
20+
def pagination(collection)
21+
html = []
22+
html << first_page_tag if show_first_page_tag?
23+
html << ellipsis_tag if show_first_ellipsis_tag?
24+
html << prev_page_tag if show_prev_page_tag?
25+
html << current_page_tag
26+
html << next_page_tag if show_next_page_tag?(collection)
27+
html << ellipsis_tag if show_last_ellipsis_tag?(collection)
28+
html << last_page_tagcollection if show_last_page_tag?(collection)
29+
html.join
30+
end
31+
32+
private
33+
34+
def show_first_page_tag?
35+
page_number > 2
36+
end
37+
38+
def first_page_tag
39+
%Q(<a href="#{url.chop}?page=1&per=#{pagination_per}">1</a>)
40+
end
41+
42+
def show_first_ellipsis_tag?
43+
page_number >= 4
44+
end
45+
46+
def ellipsis_tag
47+
'<span>...</span>'
48+
end
49+
50+
def show_prev_page_tag?
51+
page_number > 1
52+
end
53+
54+
def prev_page_tag
55+
%Q(<a href="#{url.chop}?page=#{page_number - 1}&per=#{pagination_per}">#{page_number - 1}</a>)
56+
end
57+
58+
def current_page_tag
59+
"<span><b>#{page_number}</b></span>"
60+
end
61+
62+
def show_next_page_tag?(collection)
63+
(page_number * pagination_per) < collection.count
64+
end
65+
66+
def next_page_tag
67+
%Q(<a href="#{url.chop}?page=#{page_number + 1}&per=#{pagination_per}">#{page_number + 1}</a>)
68+
end
69+
70+
def show_last_ellipsis_tag?(collection)
71+
(total_pages(collection) - page_number) >= 3
72+
end
73+
74+
def total_pages(collection)
75+
collection.count / pagination_per + ((collection.count % pagination_per).zero? ? 0 : 1)
76+
end
77+
78+
def show_last_page_tag?(collection)
79+
page_number < (total_pages(collection) - 1)
80+
end
81+
82+
def last_page_tag(collection)
83+
total = total_pages(collection)
84+
%Q(<a href="#{url.chop}?page=#{total}&per=#{pagination_per}">#{total}</a>)
85+
end
86+
end
87+
end

lib/split/dashboard/paginator.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# frozen_string_literal: true
2+
module Split
3+
class DashboardPaginator
4+
def initialize(collection, page_number, per)
5+
@collection = collection
6+
@page_number = page_number
7+
@per = per
8+
end
9+
10+
def paginate
11+
to = @page_number * @per
12+
from = to - @per
13+
@collection[from...to]
14+
end
15+
end
16+
end

lib/split/dashboard/public/style.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,3 +317,12 @@ a.button.green:focus, button.green:focus, input[type="submit"].green:focus {
317317
}
318318

319319

320+
.pagination {
321+
text-align: center;
322+
font-size: 15px;
323+
}
324+
325+
.pagination a, .paginaton span {
326+
display: inline-block;
327+
padding: 5px;
328+
}

lib/split/dashboard/views/index.erb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<input type="button" id="toggle-active" value="Hide active" />
77
<input type="button" id="clear-filter" value="Clear filters" />
88

9-
<% @experiments.each do |experiment| %>
9+
<% paginated(@experiments).each do |experiment| %>
1010
<% if experiment.goals.empty? %>
1111
<%= erb :_experiment, :locals => {:goal => nil, :experiment => experiment} %>
1212
<% else %>
@@ -16,6 +16,10 @@
1616
<% end %>
1717
<% end %>
1818
<% end %>
19+
20+
<div class="pagination">
21+
<%= pagination(@experiments) %>
22+
</div>
1923
<% else %>
2024
<p class="intro">No experiments have started yet, you need to define them in your code and introduce them to your users.</p>
2125
<p class="intro">Check out the <a href='https://github.com/splitrb/split#readme'>Readme</a> for more help getting started.</p>
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
require 'spec_helper'
2+
require 'split/dashboard/pagination_helpers'
3+
4+
describe Split::DashboardPaginationHelpers do
5+
include Split::DashboardPaginationHelpers
6+
7+
let(:url) { '/split/' }
8+
9+
describe '#pagination_per' do
10+
context 'when params empty' do
11+
let(:params) { Hash[] }
12+
13+
it 'returns 10' do
14+
expect(pagination_per).to eql 10
15+
end
16+
end
17+
18+
context 'when params[:per] is 5' do
19+
let(:params) { Hash[per: 5] }
20+
21+
it 'returns 5' do
22+
expect(pagination_per).to eql 5
23+
end
24+
end
25+
end
26+
27+
describe '#page_number' do
28+
context 'when params empty' do
29+
let(:params) { Hash[] }
30+
31+
it 'returns 1' do
32+
expect(page_number).to eql 1
33+
end
34+
end
35+
36+
context 'when params[:page] is "2"' do
37+
let(:params) { Hash[page: '2'] }
38+
39+
it 'returns 2' do
40+
expect(page_number).to eql 2
41+
end
42+
end
43+
end
44+
45+
describe '#paginated' do
46+
let(:collection) { (1..20).to_a }
47+
let(:params) { Hash[per: '5', page: '3'] }
48+
49+
it { expect(paginated(collection)).to eql [11, 12, 13, 14, 15] }
50+
end
51+
52+
describe '#show_first_page_tag?' do
53+
context 'when page is 1' do
54+
it { expect(show_first_page_tag?).to be false }
55+
end
56+
57+
context 'when page is 3' do
58+
let(:params) { Hash[page: '3'] }
59+
it { expect(show_first_page_tag?).to be true }
60+
end
61+
end
62+
63+
describe '#first_page_tag' do
64+
it { expect(first_page_tag).to eql '<a href="/split?page=1&per=10">1</a>' }
65+
end
66+
67+
describe '#show_first_ellipsis_tag?' do
68+
context 'when page is 1' do
69+
it { expect(show_first_ellipsis_tag?).to be false }
70+
end
71+
72+
context 'when page is 4' do
73+
let(:params) { Hash[page: '4'] }
74+
it { expect(show_first_ellipsis_tag?).to be true }
75+
end
76+
end
77+
78+
describe '#ellipsis_tag' do
79+
it { expect(ellipsis_tag).to eql '<span>...</span>' }
80+
end
81+
82+
describe '#show_prev_page_tag?' do
83+
context 'when page is 1' do
84+
it { expect(show_prev_page_tag?).to be false }
85+
end
86+
87+
context 'when page is 2' do
88+
let(:params) { Hash[page: '2'] }
89+
it { expect(show_prev_page_tag?).to be true }
90+
end
91+
end
92+
93+
describe '#prev_page_tag' do
94+
context 'when page is 2' do
95+
let(:params) { Hash[page: '2'] }
96+
97+
it do
98+
expect(prev_page_tag).to eql '<a href="/split?page=1&per=10">1</a>'
99+
end
100+
end
101+
102+
context 'when page is 3' do
103+
let(:params) { Hash[page: '3'] }
104+
105+
it do
106+
expect(prev_page_tag).to eql '<a href="/split?page=2&per=10">2</a>'
107+
end
108+
end
109+
end
110+
111+
describe '#show_prev_page_tag?' do
112+
context 'when page is 1' do
113+
it { expect(show_prev_page_tag?).to be false }
114+
end
115+
116+
context 'when page is 2' do
117+
let(:params) { Hash[page: '2'] }
118+
it { expect(show_prev_page_tag?).to be true }
119+
end
120+
end
121+
122+
describe '#current_page_tag' do
123+
context 'when page is 1' do
124+
let(:params) { Hash[page: '1'] }
125+
it { expect(current_page_tag).to eql '<span><b>1</b></span>' }
126+
end
127+
128+
context 'when page is 2' do
129+
let(:params) { Hash[page: '2'] }
130+
it { expect(current_page_tag).to eql '<span><b>2</b></span>' }
131+
end
132+
end
133+
134+
describe '#show_next_page_tag?' do
135+
context 'when page is 2' do
136+
let(:params) { Hash[page: '2'] }
137+
138+
context 'when collection length is 20' do
139+
let(:collection) { (1..20).to_a }
140+
it { expect(show_next_page_tag?(collection)).to be false }
141+
end
142+
143+
context 'when collection length is 25' do
144+
let(:collection) { (1..25).to_a }
145+
it { expect(show_next_page_tag?(collection)).to be true }
146+
end
147+
end
148+
end
149+
150+
describe '#next_page_tag' do
151+
context 'when page is 1' do
152+
let(:params) { Hash[page: '1'] }
153+
it { expect(next_page_tag).to eql '<a href="/split?page=2&per=10">2</a>' }
154+
end
155+
156+
context 'when page is 2' do
157+
let(:params) { Hash[page: '2'] }
158+
it { expect(next_page_tag).to eql '<a href="/split?page=3&per=10">3</a>' }
159+
end
160+
end
161+
162+
describe '#total_pages' do
163+
context 'when collection length is 30' do
164+
let(:collection) { (1..30).to_a }
165+
it { expect(total_pages(collection)).to eql 3 }
166+
end
167+
168+
context 'when collection length is 35' do
169+
let(:collection) { (1..35).to_a }
170+
it { expect(total_pages(collection)).to eql 4 }
171+
end
172+
end
173+
174+
describe '#show_last_ellipsis_tag?' do
175+
let(:collection) { (1..30).to_a }
176+
let(:params) { Hash[per: '5', page: '2'] }
177+
it { expect(show_last_ellipsis_tag?(collection)).to be true }
178+
end
179+
180+
describe '#show_last_page_tag?' do
181+
let(:collection) { (1..30).to_a }
182+
183+
context 'when page is 5/6' do
184+
let(:params) { Hash[per: '5', page: '5'] }
185+
it { expect(show_last_page_tag?(collection)).to be false }
186+
end
187+
188+
context 'when page is 4/6' do
189+
let(:params) { Hash[per: '5', page: '4'] }
190+
it { expect(show_last_page_tag?(collection)).to be true }
191+
end
192+
end
193+
194+
describe '#last_page_tag' do
195+
let(:collection) { (1..30).to_a }
196+
it { expect(last_page_tag(collection)).to eql '<a href="/split?page=3&per=10">3</a>' }
197+
end
198+
end

spec/dashboard/paginator_spec.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# frozen_string_literal: true
2+
require 'spec_helper'
3+
require 'split/dashboard/paginator'
4+
5+
describe Split::DashboardPaginator do
6+
context 'when collection is 1..20' do
7+
let(:collection) { (1..20).to_a }
8+
9+
context 'when per 5 for page' do
10+
let(:per) { 5 }
11+
12+
it 'when page number is 1 result is [1, 2, 3, 4, 5]' do
13+
result = Split::DashboardPaginator.new(collection, 1, per).paginate
14+
expect(result).to eql [1, 2, 3, 4, 5]
15+
end
16+
17+
it 'when page number is 2 result is [6, 7, 8, 9, 10]' do
18+
result = Split::DashboardPaginator.new(collection, 2, per).paginate
19+
expect(result).to eql [6, 7, 8, 9, 10]
20+
end
21+
end
22+
23+
context 'when per 10 for page' do
24+
let(:per) { 10 }
25+
26+
it 'when page number is 1 result is [1..10]' do
27+
result = Split::DashboardPaginator.new(collection, 1, per).paginate
28+
expect(result).to eql [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
29+
end
30+
31+
it 'when page number is 2 result is [10..20]' do
32+
result = Split::DashboardPaginator.new(collection, 2, per).paginate
33+
expect(result).to eql [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
34+
end
35+
end
36+
end
37+
end

0 commit comments

Comments
 (0)