@furystack/shades-common-components
Version:
Common UI components for FuryStack Shades
327 lines • 17.5 kB
JavaScript
import { createInjector } from '@furystack/inject';
import { createComponent, flushUpdates, initializeShadeRoot } from '@furystack/shades';
import { usingAsync } from '@furystack/utils';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { CollectionService } from '../../services/collection-service.js';
import { DataGridFooter, dataGridItemsPerPage } from './footer.js';
describe('DataGridFooter', () => {
beforeEach(() => {
document.body.innerHTML = '<div id="root"></div>';
});
afterEach(() => {
document.body.innerHTML = '';
});
const createService = (entries = [], count) => {
const service = new CollectionService();
service.data.setValue({ entries, count: count ?? entries.length });
return service;
};
const createFindOptions = (top = 10, skip = 0) => {
return { top, skip };
};
it('should render with custom element', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const service = createService();
const findOptions = createFindOptions();
const onFindOptionsChange = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(DataGridFooter, { service: service, findOptions: findOptions, onFindOptionsChange: onFindOptionsChange })),
});
await flushUpdates();
const footer = document.querySelector('shade-data-grid-footer');
expect(footer).not.toBeNull();
});
});
it('should render items per page select', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const service = createService();
const findOptions = createFindOptions();
const onFindOptionsChange = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(DataGridFooter, { service: service, findOptions: findOptions, onFindOptionsChange: onFindOptionsChange })),
});
await flushUpdates();
const footer = document.querySelector('shade-data-grid-footer');
const rowsPerPageSelect = footer?.querySelector('.pager-section select');
expect(rowsPerPageSelect).not.toBeNull();
});
});
it('should render all items per page options', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const service = createService();
const findOptions = createFindOptions();
const onFindOptionsChange = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(DataGridFooter, { service: service, findOptions: findOptions, onFindOptionsChange: onFindOptionsChange })),
});
await flushUpdates();
const footer = document.querySelector('shade-data-grid-footer');
const selects = Array.from(footer?.querySelectorAll('select') ?? []);
const itemsPerPageSelect = selects.find((s) => {
const parent = s.closest('.pager-section');
return parent?.textContent?.includes('Rows per page');
});
expect(itemsPerPageSelect).toBeDefined();
const options = itemsPerPageSelect?.querySelectorAll('option');
expect(options?.length).toBe(dataGridItemsPerPage.length);
});
});
it('should show Pagination component when pagination is enabled', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const service = createService([], 100);
const findOptions = createFindOptions(10, 0);
const onFindOptionsChange = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(DataGridFooter, { service: service, findOptions: findOptions, onFindOptionsChange: onFindOptionsChange })),
});
await flushUpdates();
const footer = document.querySelector('shade-data-grid-footer');
const pagination = footer?.querySelector('shade-pagination');
expect(pagination).not.toBeNull();
});
});
it('should hide Pagination when showing all items (Infinity)', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const service = createService([], 50);
const findOptions = createFindOptions(Infinity, 0);
const onFindOptionsChange = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(DataGridFooter, { service: service, findOptions: findOptions, onFindOptionsChange: onFindOptionsChange })),
});
await flushUpdates();
const footer = document.querySelector('shade-data-grid-footer');
const pagination = footer?.querySelector('shade-pagination');
expect(pagination).toBeNull();
});
});
it('should render page buttons in Pagination based on data count and items per page', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const service = createService([], 100);
const findOptions = createFindOptions(25, 0);
const onFindOptionsChange = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(DataGridFooter, { service: service, findOptions: findOptions, onFindOptionsChange: onFindOptionsChange })),
});
await flushUpdates();
const footer = document.querySelector('shade-data-grid-footer');
const pagination = footer?.querySelector('shade-pagination');
expect(pagination).not.toBeNull();
const pageButtons = Array.from(pagination?.querySelectorAll('.pagination-item') ?? []).filter((btn) => btn.getAttribute('aria-label')?.startsWith('Go to page'));
expect(pageButtons.length).toBe(4);
});
});
it('should call onFindOptionsChange when page is changed via Pagination', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const service = createService([], 100);
const findOptions = createFindOptions(10, 0);
const onFindOptionsChange = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(DataGridFooter, { service: service, findOptions: findOptions, onFindOptionsChange: onFindOptionsChange })),
});
await flushUpdates();
const footer = document.querySelector('shade-data-grid-footer');
const pagination = footer?.querySelector('shade-pagination');
const nextButton = pagination?.querySelector('[aria-label="Go to next page"]');
expect(nextButton).not.toBeNull();
nextButton.click();
await flushUpdates();
expect(onFindOptionsChange).toHaveBeenCalledWith(expect.objectContaining({ skip: 10 }));
});
});
it('should call onFindOptionsChange when items per page is changed', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const service = createService([], 100);
const findOptions = createFindOptions(10, 0);
const onFindOptionsChange = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(DataGridFooter, { service: service, findOptions: findOptions, onFindOptionsChange: onFindOptionsChange })),
});
await flushUpdates();
const footer = document.querySelector('shade-data-grid-footer');
const selects = Array.from(footer?.querySelectorAll('select') ?? []);
const itemsPerPageSelect = selects.find((s) => {
const parent = s.closest('.pager-section');
return parent?.textContent?.includes('Rows per page');
});
expect(itemsPerPageSelect).toBeDefined();
itemsPerPageSelect.value = '25';
itemsPerPageSelect.dispatchEvent(new Event('change', { bubbles: true }));
await flushUpdates();
expect(onFindOptionsChange).toHaveBeenCalledWith(expect.objectContaining({ top: 25 }));
});
});
it('should preserve current page position when changing items per page', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const service = createService([], 100);
const findOptions = createFindOptions(10, 20);
const onFindOptionsChange = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(DataGridFooter, { service: service, findOptions: findOptions, onFindOptionsChange: onFindOptionsChange })),
});
await flushUpdates();
const footer = document.querySelector('shade-data-grid-footer');
const selects = Array.from(footer?.querySelectorAll('select') ?? []);
const itemsPerPageSelect = selects.find((s) => {
const parent = s.closest('.pager-section');
return parent?.textContent?.includes('Rows per page');
});
expect(itemsPerPageSelect).toBeDefined();
itemsPerPageSelect.value = '25';
itemsPerPageSelect.dispatchEvent(new Event('change', { bubbles: true }));
await flushUpdates();
expect(onFindOptionsChange).toHaveBeenCalledWith(expect.objectContaining({ top: 25, skip: 50 }));
});
});
it('should highlight the correct current page in Pagination', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const service = createService([], 100);
const findOptions = createFindOptions(10, 30);
const onFindOptionsChange = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(DataGridFooter, { service: service, findOptions: findOptions, onFindOptionsChange: onFindOptionsChange })),
});
await flushUpdates();
const footer = document.querySelector('shade-data-grid-footer');
const pagination = footer?.querySelector('shade-pagination');
const selectedButton = pagination?.querySelector('.pagination-item[data-selected]');
expect(selectedButton).not.toBeNull();
expect(selectedButton?.textContent?.trim()).toBe('4');
});
});
it('should select the correct items per page option', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const service = createService([], 100);
const findOptions = createFindOptions(25, 0);
const onFindOptionsChange = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(DataGridFooter, { service: service, findOptions: findOptions, onFindOptionsChange: onFindOptionsChange })),
});
await flushUpdates();
const footer = document.querySelector('shade-data-grid-footer');
const selects = Array.from(footer?.querySelectorAll('select') ?? []);
const itemsPerPageSelect = selects.find((s) => {
const parent = s.closest('.pager-section');
return parent?.textContent?.includes('Rows per page');
});
expect(itemsPerPageSelect).toBeDefined();
expect(itemsPerPageSelect?.value).toBe('25');
});
});
it('should react to data count changes', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const service = createService([], 30);
const findOptions = createFindOptions(10, 0);
const onFindOptionsChange = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(DataGridFooter, { service: service, findOptions: findOptions, onFindOptionsChange: onFindOptionsChange })),
});
await flushUpdates();
let footer = document.querySelector('shade-data-grid-footer');
let pagination = footer?.querySelector('shade-pagination');
let pageButtons = Array.from(pagination?.querySelectorAll('.pagination-item') ?? []).filter((btn) => btn.getAttribute('aria-label')?.startsWith('Go to page'));
expect(pageButtons.length).toBe(3);
service.data.setValue({ entries: [], count: 50 });
await flushUpdates();
footer = document.querySelector('shade-data-grid-footer');
pagination = footer?.querySelector('shade-pagination');
pageButtons = Array.from(pagination?.querySelectorAll('.pagination-item') ?? []).filter((btn) => btn.getAttribute('aria-label')?.startsWith('Go to page'));
expect(pageButtons.length).toBe(5);
});
});
it('should export dataGridItemsPerPage constant', () => {
expect(dataGridItemsPerPage).toEqual([10, 20, 25, 50, 100, Infinity]);
});
it('should render custom paginationOptions', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const service = createService();
const findOptions = createFindOptions();
const onFindOptionsChange = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(DataGridFooter, { service: service, findOptions: findOptions, onFindOptionsChange: onFindOptionsChange, paginationOptions: [5, 15, 30] })),
});
await flushUpdates();
const footer = document.querySelector('shade-data-grid-footer');
const itemsPerPageSelect = footer?.querySelector('.pager-section select');
expect(itemsPerPageSelect).not.toBeNull();
const options = itemsPerPageSelect?.querySelectorAll('option');
expect(options?.length).toBe(3);
expect(options?.[0]?.textContent).toBe('5');
expect(options?.[1]?.textContent).toBe('15');
expect(options?.[2]?.textContent).toBe('30');
});
});
it('should hide the rows-per-page selector when only one paginationOption is provided', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const service = createService([], 50);
const findOptions = createFindOptions(10, 0);
const onFindOptionsChange = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(DataGridFooter, { service: service, findOptions: findOptions, onFindOptionsChange: onFindOptionsChange, paginationOptions: [10] })),
});
await flushUpdates();
const footer = document.querySelector('shade-data-grid-footer');
const pagerSection = footer?.querySelector('.pager-section');
expect(pagerSection).toBeNull();
});
});
it('should use default dataGridItemsPerPage when paginationOptions is not provided', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const service = createService();
const findOptions = createFindOptions();
const onFindOptionsChange = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(DataGridFooter, { service: service, findOptions: findOptions, onFindOptionsChange: onFindOptionsChange })),
});
await flushUpdates();
const footer = document.querySelector('shade-data-grid-footer');
const options = footer?.querySelectorAll('.pager-section select option');
expect(options?.length).toBe(dataGridItemsPerPage.length);
});
});
});
//# sourceMappingURL=footer.spec.js.map