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

Feature/1487 poc paginator component #1497

Draft
wants to merge 46 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
8f6c161
Initial paginator component files structure
anna-lach Aug 29, 2024
fda7740
Merge remote-tracking branch 'origin/main' into feature/1487-POC-pagi…
anna-lach Aug 29, 2024
1f7e02e
First elements of paginator
anna-lach Aug 29, 2024
514fd18
Paginator improvements, buttons, items per page, working setting acti…
anna-lach Aug 30, 2024
f39f005
paginator improvements, pageSizes, onResize
anna-lach Sep 2, 2024
f56441e
update items per page on select option
anna-lach Sep 2, 2024
3d4f86a
Paginator improvements - start, end, currently visisble items and oth…
anna-lach Sep 3, 2024
e00faf7
Merge remote-tracking branch 'origin/main' into feature/1487-POC-pagi…
anna-lach Sep 4, 2024
fc25793
some paginator styling changes
anna-lach Sep 4, 2024
e0110c2
some paginator styling changes
anna-lach Sep 5, 2024
1fa9cca
paginator structure changes, first version of links
anna-lach Sep 5, 2024
a7d3727
paginator with link, stories improvements, styling changes
anna-lach Sep 6, 2024
b4d179e
Merge remote-tracking branch 'origin/main' into feature/1487-POC-pagi…
anna-lach Sep 11, 2024
be9a6b5
previous and next buttons without labels (only aria-labels) - arrows …
anna-lach Sep 11, 2024
095f01e
overflow version with menu button
anna-lach Sep 12, 2024
d91843d
first part of truncation, removed for now links stories
anna-lach Sep 13, 2024
2c2bd46
links variant removed - with navigation property, overflow changes, p…
anna-lach Sep 17, 2024
dc9df74
Merge remote-tracking branch 'origin/main' into feature/1487-POC-pagi…
anna-lach Sep 17, 2024
02461fe
Merge remote-tracking branch 'origin/main' into feature/1487-POC-pagi…
anna-lach Sep 18, 2024
a232e59
new files for separated parts of the component, truncation working on…
anna-lach Sep 18, 2024
5de91d3
ellipsis for the last page on the overflow variant, partially added a…
anna-lach Sep 19, 2024
04a00ea
working truncation, partially added mobile variant with select only -…
anna-lach Sep 20, 2024
011f38d
Merge remote-tracking branch 'origin/main' into feature/1487-POC-pagi…
anna-lach Sep 23, 2024
fdacdc3
improved responsiveness - mobile version, some styling changes
anna-lach Sep 23, 2024
ebd9189
removed unnecessary dependency
anna-lach Sep 23, 2024
deab17f
Merge remote-tracking branch 'origin/main' into feature/1487-POC-pagi…
anna-lach Sep 24, 2024
d9f00e5
improved responsiveness for the first and last part, still buggy in t…
anna-lach Sep 24, 2024
0b25730
Merge remote-tracking branch 'origin/main' into feature/1487-POC-pagi…
anna-lach Sep 25, 2024
b7f0863
improved responsiveness, first version of pageSize component, missing…
anna-lach Sep 25, 2024
5637bda
visible items component, stories changes and new stories partially ad…
anna-lach Sep 26, 2024
af44c6a
story with cards improvements
anna-lach Sep 26, 2024
8f57d01
visible items component, page size component and paginator changes
anna-lach Sep 27, 2024
4b862c5
mobile story, new icons used, mobile and desktop variant improvements…
anna-lach Sep 30, 2024
5586ea3
Merge remote-tracking branch 'origin/main' into feature/1487-POC-pagi…
anna-lach Oct 1, 2024
45e4f39
changes in the mobile and desktop versions
anna-lach Oct 1, 2024
69102de
improved mobile variant
anna-lach Oct 2, 2024
63dcec5
added missing dependencies, example story of grid with paginator and …
anna-lach Oct 3, 2024
5efd486
Merge remote-tracking branch 'origin/main' into feature/1487-POC-pagi…
anna-lach Oct 3, 2024
8d1d81d
data source changes, paginator with data grid and filtering, sorting …
anna-lach Oct 4, 2024
9fac037
Merge remote-tracking branch 'origin/main' into feature/1487-POC-pagi…
anna-lach Oct 7, 2024
8496800
New Min-Width token for the paginator button page
arecuenco-dsgn Oct 7, 2024
65b75fb
new token for paginator page button min width, new ellipsis down icon
anna-lach Oct 7, 2024
95940ac
new ellipsis-down icon
anna-lach Oct 7, 2024
2c96591
new page component with custom button, partially styled with tokens, …
anna-lach Oct 7, 2024
f6400a9
Merge remote-tracking branch 'origin/main' into feature/1487-POC-pagi…
anna-lach Oct 8, 2024
f6f9fde
paginator and items counter refactor, fixing bugs, currently visible …
anna-lach Oct 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5,220 changes: 5,219 additions & 1 deletion packages/checklist/src/sanoma-learning-theme.scss.ts

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions packages/components/data-source/src/array-data-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
export class ArrayDataSource<T = any> extends DataSource<T> {
#filteredItems: T[] = [];
#items: T[];
#paginatedItems: T[] = [];

get items(): T[] {
return this.#filteredItems;
Expand All @@ -20,6 +21,16 @@ export class ArrayDataSource<T = any> extends DataSource<T> {
this.update();
}

get paginatedItems(): T[] {
return this.#paginatedItems;
}

set paginatedItems(items: T[]) {
this.#paginatedItems = items;
console.log('test', this.#paginatedItems, items, this.items);
this.update();
}

get size(): number {
return this.#items.length;
}
Expand All @@ -28,6 +39,7 @@ export class ArrayDataSource<T = any> extends DataSource<T> {
super();
this.#filteredItems = [...items];
this.#items = [...items];
this.#paginatedItems = [...items];
}

update(): void {
Expand Down Expand Up @@ -125,6 +137,37 @@ export class ArrayDataSource<T = any> extends DataSource<T> {
});
}

console.log('items before paginated, but should be filtered if filtered', items, this.filters, this.filters.size > 0, this.filters.values());

this.#paginatedItems = items;

// paginate items
if (this.paginateItems) {
console.log('this.paginateItems', this.paginateItems);
// const pagination = {pageNumber: pageNumber, pageSize: pageSize};
// console.log('pagination in paginate', pagination);

// const pageNumber = /*filtersChanged /!*this.filters.size > 0*!/ ? 1 :*/ this.paginateItems.pageNumber;
// this.paginateItems.pageNumber = pageNumber;
// this.paginate(10, pageNumber);
// this.paginateItems.pageNumber = pageNumber;

const startIndex = (this.paginateItems.pageNumber /*pageNumber*/ - 1) * this.paginateItems.pageSize;
const endIndex = startIndex + this.paginateItems.pageSize;
// console.log('pageNumber in array data source', pageNumber, filtersChanged, startIndex);
// Get the items for the current page
/*const paginatedItems*/items = /*this.*/items.slice(startIndex, endIndex);
// console.log('paginated data', paginatedItems);
// Update this.items with the paginated items
// this.paginatedItems/*items*/ = paginatedItems;
// console.log('updated items', this.items, this.paginatedItems, items);
}

// TODO: I need to have amount of all filtered data for total to show in the paginator...

console.log('paginateditems?', this.paginatedItems);
console.log('items in array data', items);

this.#filteredItems = items;
this.dispatchEvent(new CustomEvent('sl-update', { detail: { dataSource: this } }));
}
Expand Down
29 changes: 27 additions & 2 deletions packages/components/data-source/src/data-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export type DataSourceSortByFunction<T = unknown> = {

export type DataSourceSort<T> = DataSourceSortByFunction<T> | DataSourceSortByPath;

export type DataSourcePagination = { pageNumber: number, pageSize: number };

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type DataSourceUpdateEvent<T = any> = CustomEvent<{ dataSource: DataSource<T> }>;

Expand All @@ -46,12 +48,16 @@ export abstract class DataSource<T = any> extends EventTarget {
/** Order the items by grouping them on the given attributes. */
#groupBy?: DataSourceGroupBy<T>;

#paginateItems?: DataSourcePagination;

/**
* The value and path/function to use for sorting. When setting this property,
* it will cause the data to be automatically sorted.
*/
#sort?: DataSourceSort<T>;

// #pagination

get filters(): Map<string, DataSourceFilter<T>> {
return this.#filters;
}
Expand All @@ -60,30 +66,38 @@ export abstract class DataSource<T = any> extends EventTarget {
return this.#groupBy;
}

get paginateItems(): DataSourcePagination| undefined {
return this.#paginateItems;
}

get sort(): DataSourceSort<T> | undefined {
return this.#sort;
}

/** The filtered & sorted array of items. */
abstract items: T[];

/** The array of all items, used for pagination. */
// abstract paginatedItems?: T[];

/** Total number of items in this data source. */
abstract readonly size: number;

/** Updates the list of items using filter and sorting if available. */
/** Updates the list of items using filter, sorting and pagination if available. */
abstract update(): void;

addFilter<U extends string | DataSourceFilterFunction<T>>(
id: string,
pathOrFilter: U,
value?: string | string[]
): void {
console.log('value in addFilter', value);
if (typeof pathOrFilter === 'string') {
this.#filters.set(id, { path: pathOrFilter, value: value ?? '' });
} else {
this.#filters.set(id, { filter: pathOrFilter, value });
}
}
} // TODO: maybe here emit an event?

removeFilter(id: string): void {
this.#filters.delete(id);
Expand Down Expand Up @@ -147,4 +161,15 @@ export abstract class DataSource<T = any> extends EventTarget {

this.update();
}

/**
* Use to get the paginated data for usage with the sl-paginator component.
* */
paginate(pageNumber: number, pageSize: number): void {
this.#paginateItems = { pageNumber: pageNumber, pageSize: pageSize};

this.update();
}

// TODO: paginatedData
}
2 changes: 2 additions & 0 deletions packages/components/grid/src/grid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ export class Grid<T = any> extends ScopedElementsMixin(LitElement) {
}

override willUpdate(changes: PropertyValues<this>): void {
console.log('changes in willChange', changes);
if (changes.has('dataSource')) {
this.#updateDataSource(this.dataSource);
}
Expand All @@ -286,6 +287,7 @@ export class Grid<T = any> extends ScopedElementsMixin(LitElement) {
}

override render(): TemplateResult {
console.log('in grid render', this.items, this.dataSource);
return html`
<slot @sl-column-update=${this.#onColumnUpdate} @slotchange=${this.#onSlotChange} style="display:none"></slot>
<style>
Expand Down
227 changes: 227 additions & 0 deletions packages/components/grid/src/stories/pagination.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import '@sl-design-system/button/register.js';
import '@sl-design-system/button-bar/register.js';
import { type Person, getPeople } from '@sl-design-system/example-data';
import { ArrayDataSource } from '@sl-design-system/data-source';
import {PageSize, Paginator, ItemsCounter} from "@sl-design-system/paginator";
import '@sl-design-system/paginator/register.js';
import '@sl-design-system/text-field/register.js';
import { type Meta, type StoryObj } from '@storybook/web-components';
import { html } from 'lit';
import '../../register.js';
import { Grid } from '../grid.js';

type Story = StoryObj;

export default {
title: 'Grid/Pagination',
tags: ['draft'],
loaders: [async () => ({ people: (await getPeople()).people })],
parameters: {
// Disables Chromatic's snapshotting on a story level
chromatic: { disableSnapshot: true }
}
} satisfies Meta;

export const Basic: Story = {
render: (_, { loaded: { people } }) => {
const pageSizes = [5, 10, 15];
let activePage = 1,
itemsPerPage = pageSizes[1],
startIndex = (activePage - 1) * itemsPerPage,
endIndex = startIndex + itemsPerPage;

setTimeout(() => {
const paginator = document.querySelector('sl-paginator') as Paginator,
pageSize = document.querySelector('sl-page-size') as PageSize,
visibleItems = document.querySelector('sl-items-counter') as ItemsCounter,
grid = document.querySelector('sl-grid') as Grid;

paginator?.addEventListener('sl-page-change', event => {
visibleItems.activePage = event.detail;
activePage = event.detail;
startIndex = (event.detail - 1) * itemsPerPage;
endIndex = startIndex + itemsPerPage;
grid.items = people.slice(startIndex, endIndex);
});

pageSize?.addEventListener('sl-page-size-change', event => {
paginator.itemsPerPage = event.detail;
visibleItems.itemsPerPage = event.detail;
itemsPerPage = event.detail;
});
});

return html`
<style>
.pagination {
display: flex;
gap: 1rem;
align-items: center;
margin-block: 1rem;
justify-content: space-between;
}

sl-paginator {
flex: 1;
}
</style>
<sl-grid .items=${people.slice(startIndex, endIndex)}>
<sl-grid-column path="firstName"></sl-grid-column>
<sl-grid-column path="lastName"></sl-grid-column>
<sl-grid-column path="profession"></sl-grid-column>
<sl-grid-column path="status"></sl-grid-column>
<sl-grid-column path="membership"></sl-grid-column>
</sl-grid>
<div class="pagination">
<sl-items-counter .total=${people.length} .activePage=${activePage} .itemsPerPage=${itemsPerPage}></sl-items-counter>
<sl-paginator .total=${people.length} .pageSizes=${pageSizes} .activePage=${activePage}
.itemsPerPage=${itemsPerPage}></sl-paginator>
<sl-page-size .pageSizes=${pageSizes} .itemsPerPage=${itemsPerPage}></sl-page-size>
</div>
`
}
};

export const PaginatedDataSourceWithFilter: Story = {
render: (_, { loaded: { people } }) => {
const pageSizes = [5, 10, 15, 20],
dataSource = new ArrayDataSource(people as Person[]);

let total = dataSource.paginatedItems.length;
dataSource.paginate(1, 10);

setTimeout(() => {
const paginator = document.querySelector('sl-paginator') as Paginator,
pageSize = document.querySelector('sl-page-size') as PageSize,
visibleItems = document.querySelector('sl-items-counter') as ItemsCounter,
grid = document.querySelector('sl-grid') as Grid;

paginator?.addEventListener('sl-page-change', event => {
dataSource.paginate(event.detail, paginator.itemsPerPage ?? pageSizes[0]);
visibleItems.activePage = event.detail;
});

pageSize?.addEventListener('sl-page-size-change', event => {
paginator.itemsPerPage = event.detail;
visibleItems.itemsPerPage = event.detail;
dataSource.paginate(paginator.activePage, event.detail);
});

dataSource?.addEventListener('sl-update', () => {
paginator.total = dataSource.paginatedItems.length;
visibleItems.total = dataSource.paginatedItems.length;
});

grid?.addEventListener('sl-filter-value-change', () => {
// go back to the first page on filter change
paginator.activePage = 1;
});
});

return html`
<style>
.pagination {
display: flex;
gap: 1rem;
align-items: center;
margin-block: 1rem;
justify-content: space-between;
}

sl-paginator {
flex: 1;
}
</style>
<sl-grid .dataSource=${dataSource}>
<sl-grid-column path="firstName"></sl-grid-column>
<sl-grid-column path="lastName"></sl-grid-column>
<sl-grid-filter-column id="filter-profession" mode="text" path="profession"></sl-grid-filter-column>
<sl-grid-filter-column id="filter-status" path="status"></sl-grid-filter-column>
<sl-grid-filter-column id="filter-membership" path="membership"></sl-grid-filter-column>
</sl-grid>
<div class="pagination">
<sl-items-counter .total=${total} .activePage=${1} .itemsPerPage=${10}></sl-items-counter>
<sl-paginator .total=${total} .pageSizes=${pageSizes} .activePage=${1} .itemsPerPage=${10}></sl-paginator>
<sl-page-size .pageSizes=${pageSizes} .itemsPerPage=${10}></sl-page-size>
</div>
`;
}
};

export const PaginatedDataSourceWithSorter: Story = {
render: (_, { loaded: { people } }) => {
const sorter = (a: Person, b: Person): number => {
const lastNameCmp = a.lastName.localeCompare(b.lastName);

if (lastNameCmp === 0) {
return a.firstName.localeCompare(b.firstName);
} else {
return lastNameCmp;
}
};

const dataSource = new ArrayDataSource(people as Person[]);
dataSource.setSort('custom', sorter, 'asc');

const pageSizes = [10, 15, 20];
let total = dataSource.paginatedItems.length;
dataSource.paginate(1, 10);

setTimeout(() => {
const paginator = document.querySelector('sl-paginator') as Paginator,
pageSize = document.querySelector('sl-page-size') as PageSize,
visibleItems = document.querySelector('sl-items-counter') as ItemsCounter,
grid = document.querySelector('sl-grid') as Grid;

paginator?.addEventListener('sl-page-change', event => {
dataSource.paginate(event.detail, paginator.itemsPerPage ?? pageSizes[0]);
visibleItems.activePage = event.detail;
});

pageSize?.addEventListener('sl-page-size-change', event => {
paginator.itemsPerPage = event.detail;
visibleItems.itemsPerPage = event.detail;
dataSource.paginate(paginator.activePage, event.detail);
});

dataSource?.addEventListener('sl-update', () => {
paginator.total = dataSource.paginatedItems.length;
visibleItems.total = dataSource.paginatedItems.length;
});

grid?.addEventListener('sl-sort-direction-change', () => {
// go back to the first page on filter change
paginator.activePage = 1;
});
});

return html`
<style>
.pagination {
display: flex;
gap: 1rem;
align-items: center;
margin-block: 1rem;
justify-content: space-between;
}

sl-paginator {
flex: 1;
}
</style>
<p>This grid sorts people by last name, then first name, via a custom sorter on the data directly.</p>
<sl-grid .dataSource=${dataSource}>
<sl-grid-sort-column path="firstName"></sl-grid-sort-column>
<sl-grid-sort-column path="lastName"></sl-grid-sort-column>
<sl-grid-column path="email"></sl-grid-column>
</sl-grid>
<div class="pagination">
<sl-items-counter .total=${total} .activePage=${1} .itemsPerPage=${10}></sl-items-counter>
<sl-paginator .total=${total} .pageSizes=${pageSizes} .activePage=${1} .itemsPerPage=${10}></sl-paginator>
<sl-page-size .pageSizes=${pageSizes} .itemsPerPage=${10}></sl-page-size>
</div>
`;
}
};


Loading
Loading