Skip to content

Commit f573024

Browse files
Merge pull request #13 from appwrite/pagination
Pagination
2 parents bfc43f8 + f712bff commit f573024

File tree

3 files changed

+127
-54
lines changed

3 files changed

+127
-54
lines changed
Lines changed: 77 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,96 @@
11
<script lang="ts">
22
import { createEventDispatcher } from 'svelte';
3-
3+
export let sum: number;
44
export let limit: number;
55
export let offset: number;
6-
export let sum: number;
76
87
const dispatch = createEventDispatcher();
8+
const totalPages = Math.ceil(sum / limit);
9+
let currentPage = Math.floor(offset / limit + 1);
10+
11+
function handleOptionClick(page: number) {
12+
if (currentPage !== page) {
13+
offset = limit * (page - 1);
14+
currentPage = page;
15+
dispatch('change');
16+
}
17+
}
918
10-
const next = () => {
11-
if (offset + limit > sum) {
12-
offset = sum;
13-
} else {
14-
offset += limit;
19+
function handleButtonPage(direction: string) {
20+
if (direction === 'next' && currentPage < totalPages) {
21+
currentPage += 1;
22+
offset = limit * (currentPage - 1);
23+
dispatch('change');
24+
} else if (direction === 'prev' && currentPage > 1) {
25+
currentPage -= 1;
26+
offset = limit * (currentPage - 1);
27+
dispatch('change');
1528
}
16-
dispatch('change');
17-
};
18-
19-
const prev = () => {
20-
if (offset - limit < 0) {
21-
offset = 0;
22-
} else {
23-
offset -= limit;
29+
}
30+
31+
let pages = pagination(currentPage, totalPages);
32+
33+
function pagination(current: number, total: number) {
34+
let delta = 2,
35+
left = current - delta,
36+
right = current + delta + 1,
37+
range = [],
38+
rangeWithDots = [];
39+
40+
for (let i = 1; i <= total; i++) {
41+
if (i == 1 || i == total || (i >= left && i < right)) {
42+
range.push(i);
43+
}
2444
}
25-
dispatch('change');
26-
};
2745
28-
$: noPrev = offset === 0;
29-
$: noNext = sum - limit < offset;
30-
$: currentPage = offset / limit + 1;
31-
$: totalPages = Math.ceil(sum / limit);
46+
rangeWithDots = range.reduce((prev, current, index) => {
47+
if (current - prev[index - 1] > delta) {
48+
prev.push('...');
49+
}
50+
prev.push(current);
51+
return prev;
52+
}, []);
53+
return rangeWithDots;
54+
}
3255
</script>
3356

34-
{#if sum >= limit}
35-
<nav class="pagination is-center">
57+
{#if totalPages > 1}
58+
<nav class="pagination">
3659
<button
37-
on:click={prev}
38-
disabled={noPrev}
39-
class:is-disabled={noPrev}
40-
class="button is-only-icon"
41-
aria-label="previous page">
42-
<span class="icon-left-open" aria-hidden="true" />
60+
on:click={() => handleButtonPage('prev')}
61+
class:is-disabled={currentPage <= 1}
62+
class="button is-text"
63+
aria-label="prev page">
64+
<span class="icon-cheveron-left" aria-hidden="true" />
65+
<span class="text">Prev</span>
4366
</button>
44-
<span class="pagination-info">{currentPage} / {totalPages}</span>
67+
<ol class="pagination-list is-only-desktop">
68+
{#each pages as page}
69+
{#if typeof page === 'number'}
70+
<li class="pagination-item">
71+
<button
72+
class="button"
73+
on:click={() => handleOptionClick(page)}
74+
class:is-disabled={currentPage === page}
75+
class:is-text={currentPage !== page}
76+
aria-label="page">
77+
<span class="text">{page}</span>
78+
</button>
79+
</li>
80+
{:else}
81+
<li class="li is-text">
82+
<span class="icon">...</span>
83+
</li>
84+
{/if}
85+
{/each}
86+
</ol>
4587
<button
46-
on:click={next}
47-
disabled={noNext}
48-
class:is-disabled={noNext}
49-
class="button is-only-icon"
88+
on:click={() => handleButtonPage('next')}
89+
class:is-disabled={currentPage === totalPages}
90+
class="button is-text"
5091
aria-label="next page">
51-
<span class="icon-right-open" aria-hidden="true" />
92+
<span class="text">Next</span>
93+
<span class="icon-cheveron-right" aria-hidden="true" />
5294
</button>
5395
</nav>
5496
{/if}

src/routes/console/[project]/users/index.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
let search = '';
2525
let showCreate = false;
2626
let offset = 0;
27-
const limit = 25;
27+
const limit = 5;
2828
2929
const project = $page.params.project;
3030
const getAvatar = (name: string) => sdkForProject.avatars.getInitials(name, 30, 30).toString();
@@ -90,7 +90,7 @@
9090
</Table>
9191
<div class="u-flex common-section u-main-space-between">
9292
<p class="text">Total results: {response.total}</p>
93-
<Pagination {limit} bind:offset sum={response.total} />
93+
<Pagination bind:offset {limit} sum={response.total} />
9494
</div>
9595
{:else if search}
9696
<Empty>

tests/unit/components/pagination.test.ts

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import { Pagination } from '../../../src/lib/components';
44

55
test('shows controls', () => {
66
const { getByLabelText } = render(Pagination, {
7-
limit: 0,
7+
limit: 100,
88
offset: 0,
9-
sum: 100
9+
sum: 150
1010
});
11-
expect(getByLabelText('previous page')).toBeInTheDocument();
11+
expect(getByLabelText('prev page')).toBeInTheDocument();
1212
expect(getByLabelText('next page')).toBeInTheDocument();
1313
});
1414

@@ -19,8 +19,8 @@ test('pagination - first page', () => {
1919
sum: 100
2020
});
2121

22-
expect(getByLabelText('previous page')).toBeDisabled();
23-
expect(getByLabelText('next page')).not.toBeDisabled();
22+
expect(getByLabelText('prev page')).toHaveClass('is-disabled');
23+
expect(getByLabelText('next page')).not.toHaveClass('is-disabled');
2424
});
2525

2626
test('pagination - last page', () => {
@@ -30,8 +30,8 @@ test('pagination - last page', () => {
3030
sum: 30
3131
});
3232

33-
expect(getByLabelText('previous page')).not.toBeDisabled();
34-
expect(getByLabelText('next page')).toBeDisabled();
33+
expect(getByLabelText('prev page')).not.toHaveClass('is-disabled');
34+
expect(getByLabelText('next page')).toHaveClass('is-disabled');
3535
});
3636

3737
test('pagination - forward', async () => {
@@ -41,9 +41,9 @@ test('pagination - forward', async () => {
4141
sum: 60
4242
});
4343

44-
const back = getByLabelText('previous page');
44+
const back = getByLabelText('prev page');
4545
const forth = getByLabelText('next page');
46-
expect(back).toBeDisabled();
46+
expect(back).toHaveClass('is-disabled');
4747

4848
await fireEvent.click(forth);
4949
expect(component.offset).toEqual(25);
@@ -52,8 +52,8 @@ test('pagination - forward', async () => {
5252
expect(component.offset).toEqual(50);
5353

5454
await fireEvent.click(forth);
55-
expect(component.offset).toEqual(60);
56-
expect(forth).toBeDisabled();
55+
expect(component.offset).toEqual(50);
56+
expect(forth).toHaveClass('is-disabled');
5757
});
5858

5959
test('pagination - backwards', async () => {
@@ -63,19 +63,50 @@ test('pagination - backwards', async () => {
6363
sum: 60
6464
});
6565

66-
const back = getByLabelText('previous page');
66+
const back = getByLabelText('prev page');
6767
const forth = getByLabelText('next page');
68-
expect(forth).toBeDisabled();
68+
expect(forth).toHaveClass('is-disabled');
6969

7070
await fireEvent.click(back);
71-
expect(component.offset).toEqual(30);
71+
expect(component.offset).toEqual(25);
7272

7373
await fireEvent.click(back);
74-
expect(component.offset).toEqual(5);
74+
expect(component.offset).toEqual(0);
7575

7676
await fireEvent.click(back);
7777
expect(component.offset).toEqual(0);
78-
expect(back).toBeDisabled();
78+
expect(back).toHaveClass('is-disabled');
79+
});
80+
81+
test('pagination - number button click', async () => {
82+
const { getByText, getAllByLabelText, component } = render(Pagination, {
83+
limit: 25,
84+
offset: 0,
85+
sum: 60
86+
});
87+
88+
const buttons = getAllByLabelText('page');
89+
const [button1, button2, button3] = buttons;
90+
91+
const one = getByText('1');
92+
const two = getByText('2');
93+
const three = getByText('3');
94+
expect(button1).toHaveClass('is-disabled');
95+
96+
await fireEvent.click(two);
97+
expect(component.offset).toEqual(25);
98+
expect(button1).not.toHaveClass('is-disabled');
99+
expect(button2).toHaveClass('is-disabled');
100+
101+
await fireEvent.click(three);
102+
expect(component.offset).toEqual(50);
103+
expect(button2).not.toHaveClass('is-disabled');
104+
expect(button3).toHaveClass('is-disabled');
105+
106+
await fireEvent.click(one);
107+
expect(component.offset).toEqual(0);
108+
expect(button1).toHaveClass('is-disabled');
109+
expect(button3).not.toHaveClass('is-disabled');
79110
});
80111

81112
test('shows no controls', () => {
@@ -85,6 +116,6 @@ test('shows no controls', () => {
85116
sum: 10
86117
});
87118

88-
expect(queryByLabelText('previous page')).not.toBeInTheDocument();
119+
expect(queryByLabelText('prev page')).not.toBeInTheDocument();
89120
expect(queryByLabelText('next page')).not.toBeInTheDocument();
90121
});

0 commit comments

Comments
 (0)