Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pagination #13

Merged
merged 10 commits into from
May 20, 2022
112 changes: 77 additions & 35 deletions src/lib/components/pagination.svelte
Original file line number Diff line number Diff line change
@@ -1,54 +1,96 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';

export let sum: number;
export let limit: number;
export let offset: number;
export let sum: number;

const dispatch = createEventDispatcher();
const totalPages = Math.ceil(sum / limit);
let currentPage = Math.floor(offset / limit + 1);

function handleOptionClick(page: number) {
if (currentPage !== page) {
offset = limit * (page - 1);
currentPage = page;
dispatch('change');
}
}

const next = () => {
if (offset + limit > sum) {
offset = sum;
} else {
offset += limit;
function handleButtonPage(direction: string) {
if (direction === 'next' && currentPage < totalPages) {
currentPage += 1;
offset = limit * (currentPage - 1);
dispatch('change');
} else if (direction === 'prev' && currentPage > 1) {
currentPage -= 1;
offset = limit * (currentPage - 1);
dispatch('change');
}
dispatch('change');
};

const prev = () => {
if (offset - limit < 0) {
offset = 0;
} else {
offset -= limit;
}

let pages = pagination(currentPage, totalPages);

function pagination(current: number, total: number) {
let delta = 2,
left = current - delta,
right = current + delta + 1,
range = [],
rangeWithDots = [];

for (let i = 1; i <= total; i++) {
if (i == 1 || i == total || (i >= left && i < right)) {
range.push(i);
}
}
dispatch('change');
};

$: noPrev = offset === 0;
$: noNext = sum - limit < offset;
$: currentPage = offset / limit + 1;
$: totalPages = Math.ceil(sum / limit);
rangeWithDots = range.reduce((prev, current, index) => {
if (current - prev[index - 1] > delta) {
prev.push('...');
}
prev.push(current);
return prev;
}, []);
return rangeWithDots;
}
</script>

{#if sum >= limit}
<nav class="pagination is-center">
{#if totalPages > 1}
<nav class="pagination">
<button
on:click={prev}
disabled={noPrev}
class:is-disabled={noPrev}
class="button is-only-icon"
aria-label="previous page">
<span class="icon-left-open" aria-hidden="true" />
on:click={() => handleButtonPage('prev')}
class:is-disabled={currentPage <= 1}
class="button is-text"
aria-label="prev page">
<span class="icon-cheveron-left" aria-hidden="true" />
<span class="text">Prev</span>
</button>
<span class="pagination-info">{currentPage} / {totalPages}</span>
<ol class="pagination-list is-only-desktop">
{#each pages as page}
{#if typeof page === 'number'}
<li class="pagination-item">
<button
class="button"
on:click={() => handleOptionClick(page)}
class:is-disabled={currentPage === page}
class:is-text={currentPage !== page}
aria-label="page">
<span class="text">{page}</span>
</button>
</li>
{:else}
<li class="li is-text">
<span class="icon">...</span>
</li>
{/if}
{/each}
</ol>
<button
on:click={next}
disabled={noNext}
class:is-disabled={noNext}
class="button is-only-icon"
on:click={() => handleButtonPage('next')}
class:is-disabled={currentPage === totalPages}
class="button is-text"
aria-label="next page">
<span class="icon-right-open" aria-hidden="true" />
<span class="text">Next</span>
<span class="icon-cheveron-right" aria-hidden="true" />
</button>
</nav>
{/if}
4 changes: 2 additions & 2 deletions src/routes/console/[project]/users/index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
let search = '';
let showCreate = false;
let offset = 0;
const limit = 25;
const limit = 5;

const project = $page.params.project;
const getAvatar = (name: string) => sdkForProject.avatars.getInitials(name, 30, 30).toString();
Expand Down Expand Up @@ -90,7 +90,7 @@
</Table>
<div class="u-flex common-section u-main-space-between">
<p class="text">Total results: {response.total}</p>
<Pagination {limit} bind:offset sum={response.total} />
<Pagination bind:offset {limit} sum={response.total} />
</div>
{:else if search}
<Empty>
Expand Down
65 changes: 48 additions & 17 deletions tests/unit/components/pagination.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { Pagination } from '../../../src/lib/components';

test('shows controls', () => {
const { getByLabelText } = render(Pagination, {
limit: 0,
limit: 100,
offset: 0,
sum: 100
sum: 150
});
expect(getByLabelText('previous page')).toBeInTheDocument();
expect(getByLabelText('prev page')).toBeInTheDocument();
expect(getByLabelText('next page')).toBeInTheDocument();
});

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

expect(getByLabelText('previous page')).toBeDisabled();
expect(getByLabelText('next page')).not.toBeDisabled();
expect(getByLabelText('prev page')).toHaveClass('is-disabled');
expect(getByLabelText('next page')).not.toHaveClass('is-disabled');
});

test('pagination - last page', () => {
Expand All @@ -30,8 +30,8 @@ test('pagination - last page', () => {
sum: 30
});

expect(getByLabelText('previous page')).not.toBeDisabled();
expect(getByLabelText('next page')).toBeDisabled();
expect(getByLabelText('prev page')).not.toHaveClass('is-disabled');
expect(getByLabelText('next page')).toHaveClass('is-disabled');
});

test('pagination - forward', async () => {
Expand All @@ -41,9 +41,9 @@ test('pagination - forward', async () => {
sum: 60
});

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

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

await fireEvent.click(forth);
expect(component.offset).toEqual(60);
expect(forth).toBeDisabled();
expect(component.offset).toEqual(50);
expect(forth).toHaveClass('is-disabled');
});

test('pagination - backwards', async () => {
Expand All @@ -63,19 +63,50 @@ test('pagination - backwards', async () => {
sum: 60
});

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

await fireEvent.click(back);
expect(component.offset).toEqual(30);
expect(component.offset).toEqual(25);

await fireEvent.click(back);
expect(component.offset).toEqual(5);
expect(component.offset).toEqual(0);

await fireEvent.click(back);
expect(component.offset).toEqual(0);
expect(back).toBeDisabled();
expect(back).toHaveClass('is-disabled');
});

test('pagination - number button click', async () => {
const { getByText, getAllByLabelText, component } = render(Pagination, {
limit: 25,
offset: 0,
sum: 60
});

const buttons = getAllByLabelText('page');
const [button1, button2, button3] = buttons;

const one = getByText('1');
const two = getByText('2');
const three = getByText('3');
expect(button1).toHaveClass('is-disabled');

await fireEvent.click(two);
expect(component.offset).toEqual(25);
expect(button1).not.toHaveClass('is-disabled');
expect(button2).toHaveClass('is-disabled');

await fireEvent.click(three);
expect(component.offset).toEqual(50);
expect(button2).not.toHaveClass('is-disabled');
expect(button3).toHaveClass('is-disabled');

await fireEvent.click(one);
expect(component.offset).toEqual(0);
expect(button1).toHaveClass('is-disabled');
expect(button3).not.toHaveClass('is-disabled');
});

test('shows no controls', () => {
Expand All @@ -85,6 +116,6 @@ test('shows no controls', () => {
sum: 10
});

expect(queryByLabelText('previous page')).not.toBeInTheDocument();
expect(queryByLabelText('prev page')).not.toBeInTheDocument();
expect(queryByLabelText('next page')).not.toBeInTheDocument();
});