@dbs-portal/tool-mock
Version:
API mocking toolkit using MSW for DBS Portal development workflows
230 lines • 6.89 kB
JavaScript
/**
* Pagination utilities for mock responses
*/
/**
* Create a paginated response from items
*/
export function createPaginatedResponse(options) {
const { items, page, pageSize, total } = options;
const totalItems = total ?? items.length;
const totalPages = Math.ceil(totalItems / pageSize);
const startIndex = (page - 1) * pageSize;
const endIndex = startIndex + pageSize;
const paginatedItems = items.slice(startIndex, endIndex);
const paginatedResponse = {
data: paginatedItems,
meta: {
page,
pageSize,
total: totalItems,
totalPages,
hasNextPage: page < totalPages,
hasPreviousPage: page > 1,
},
};
return {
data: paginatedResponse,
items: paginatedItems,
};
}
/**
* Create pagination metadata only
*/
export function createPaginationMeta(page, pageSize, total) {
const totalPages = Math.ceil(total / pageSize);
return {
page,
pageSize,
total,
totalPages,
hasNextPage: page < totalPages,
hasPreviousPage: page > 1,
};
}
/**
* Parse pagination parameters from URL search params
*/
export function parsePaginationParams(searchParams, defaults = {
page: 1,
pageSize: 10,
maxPageSize: 100,
}) {
const page = Math.max(1, parseInt(searchParams.get('page') || '1', 10));
const pageSize = Math.min(defaults.maxPageSize, Math.max(1, parseInt(searchParams.get('pageSize') || String(defaults.pageSize), 10)));
return { page, pageSize };
}
/**
* Create cursor-based pagination response
*/
export function createCursorPaginatedResponse(items, cursor, limit, getCursor) {
let startIndex = 0;
// Find start index based on cursor
if (cursor) {
const cursorIndex = items.findIndex(item => getCursor(item) === cursor);
if (cursorIndex !== -1) {
startIndex = cursorIndex + 1;
}
}
const endIndex = startIndex + limit;
const paginatedItems = items.slice(startIndex, endIndex);
const hasNextPage = endIndex < items.length;
const hasPreviousPage = startIndex > 0;
const lastItem = paginatedItems[paginatedItems.length - 1];
const nextCursor = hasNextPage && paginatedItems.length > 0 && lastItem
? getCursor(lastItem)
: null;
const prevItem = items[startIndex - 1];
const previousCursor = hasPreviousPage && startIndex > 0 && prevItem
? getCursor(prevItem)
: null;
return {
data: paginatedItems,
meta: {
hasNextPage,
hasPreviousPage,
nextCursor,
previousCursor,
limit,
},
};
}
/**
* Create offset-based pagination response
*/
export function createOffsetPaginatedResponse(items, offset, limit) {
const startIndex = Math.max(0, offset);
const endIndex = startIndex + limit;
const paginatedItems = items.slice(startIndex, endIndex);
const hasMore = endIndex < items.length;
return {
data: paginatedItems,
meta: {
offset: startIndex,
limit,
total: items.length,
hasMore,
},
};
}
/**
* Create infinite scroll pagination response
*/
export function createInfiniteScrollResponse(items, page, pageSize) {
const startIndex = (page - 1) * pageSize;
const endIndex = startIndex + pageSize;
const paginatedItems = items.slice(startIndex, endIndex);
const hasMore = endIndex < items.length;
return {
data: paginatedItems,
meta: {
page,
pageSize,
hasMore,
nextPage: hasMore ? page + 1 : null,
},
};
}
/**
* Validate pagination parameters
*/
export function validatePaginationParams(page, pageSize, maxPageSize = 100) {
const errors = [];
if (page < 1) {
errors.push('Page must be greater than 0');
}
if (pageSize < 1) {
errors.push('Page size must be greater than 0');
}
if (pageSize > maxPageSize) {
errors.push(`Page size cannot exceed ${maxPageSize}`);
}
return {
valid: errors.length === 0,
errors,
};
}
/**
* Calculate pagination ranges for UI
*/
export function calculatePaginationRanges(currentPage, totalPages, maxVisible = 5) {
const pages = [];
const half = Math.floor(maxVisible / 2);
let start = Math.max(1, currentPage - half);
let end = Math.min(totalPages, currentPage + half);
// Adjust if we're near the beginning or end
if (end - start + 1 < maxVisible) {
if (start === 1) {
end = Math.min(totalPages, start + maxVisible - 1);
}
else if (end === totalPages) {
start = Math.max(1, end - maxVisible + 1);
}
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return {
pages,
showFirst: start > 1,
showLast: end < totalPages,
showPrevious: currentPage > 1,
showNext: currentPage < totalPages,
};
}
/**
* Create pagination links for API responses
*/
export function createPaginationLinks(baseUrl, page, pageSize, totalPages) {
const links = {};
const createUrl = (p) => {
const url = new URL(baseUrl);
url.searchParams.set('page', p.toString());
url.searchParams.set('pageSize', pageSize.toString());
return url.toString();
};
if (page > 1) {
links['first'] = createUrl(1);
links['previous'] = createUrl(page - 1);
}
if (page < totalPages) {
links['next'] = createUrl(page + 1);
links['last'] = createUrl(totalPages);
}
return links;
}
/**
* Create search and filter pagination
*/
export function createSearchPaginatedResponse(items, searchQuery, searchFields, filters, page, pageSize) {
let filteredItems = items;
// Apply search
if (searchQuery) {
const query = searchQuery.toLowerCase();
filteredItems = filteredItems.filter(item => searchFields.some(field => {
const value = item[field];
return value && String(value).toLowerCase().includes(query);
}));
}
// Apply filters
if (Object.keys(filters).length > 0) {
filteredItems = filteredItems.filter(item => Object.entries(filters).every(([key, value]) => {
const itemValue = item[key];
if (Array.isArray(value)) {
return value.includes(itemValue);
}
return itemValue === value;
}));
}
const filteredCount = filteredItems.length;
const paginatedResponse = createPaginatedResponse({
items: filteredItems,
page,
pageSize,
});
return {
data: paginatedResponse.data,
filteredCount,
totalCount: items.length,
};
}
//# sourceMappingURL=pagination.js.map