Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
afterEach, describe, expect, it, jest,
} from '@jest/globals';
import { themeReadyCallback } from '@ts/ui/m_themes_callback';

import Pagination from '../wrappers/pagination';

jest.mock('@ts/ui/themes', () => ({
...jest.requireActual<object>('@ts/ui/themes'),
isPendingThemeLoaded: (): boolean => false,
}));

const instances: Pagination[] = [];

function createAdaptivePagination(width: string): HTMLElement {
const root = document.createElement('div');
document.body.appendChild(root);
const container = document.createElement('div');
container.style.width = width;
root.appendChild(container);
instances.push(new Pagination(container, {
pageCount: 10,
pageIndex: 1,
pageSize: 5,
itemCount: 46,
showPageSizeSelector: true,
allowedPageSizes: [5, 10],
}));
return container;
}

function applyThemeLayout(container: HTMLElement): void {
const pageIndexes = container.querySelector('.dx-page-indexes') as HTMLElement;
const pageSizes = container.querySelector('.dx-page-sizes') as HTMLElement;
pageIndexes.style.display = 'inline-block';
pageIndexes.style.width = '300px';
pageSizes.style.cssFloat = 'left';
pageSizes.style.width = '100px';
}

afterEach(() => {
instances.splice(0).forEach((pagination) => pagination.dispose());
document.body.innerHTML = '';
});

describe('adaptive pagination and the theme loading', () => {
it('switches a narrow pagination to the compact mode when the theme is loaded', () => {
const container = createAdaptivePagination('200px');

expect(container.classList.contains('dx-light-mode')).toBe(false);

applyThemeLayout(container);
themeReadyCallback.fire();

expect(container.classList.contains('dx-light-mode')).toBe(true);
});

it('keeps a wide pagination in the full mode when the theme is loaded', () => {
const container = createAdaptivePagination('1000px');

applyThemeLayout(container);
themeReadyCallback.fire();

expect(container.classList.contains('dx-light-mode')).toBe(false);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
describe, expect, it, jest,
} from '@jest/globals';
import { themeReadyCallback } from '@ts/ui/m_themes_callback';

import { onThemeReady } from '../utils/on_theme_ready';

const mockThemeState = { loaded: false };

jest.mock('@ts/ui/themes', () => ({
...jest.requireActual<object>('@ts/ui/themes'),
isPendingThemeLoaded: (): boolean => mockThemeState.loaded,
}));

describe('onThemeReady', () => {
it('runs the callback once the pending theme is loaded and unsubscribes on dispose', () => {
mockThemeState.loaded = false;
const callback = jest.fn();

const dispose = onThemeReady(callback);

expect(callback).not.toHaveBeenCalled();

themeReadyCallback.fire();

expect(callback).toHaveBeenCalledTimes(1);

dispose?.();
themeReadyCallback.fire();

expect(callback).toHaveBeenCalledTimes(1);
});

it('does not subscribe when the theme is already loaded', () => {
mockThemeState.loaded = true;
const callback = jest.fn();

expect(onThemeReady(callback)).toBeUndefined();

themeReadyCallback.fire();

expect(callback).not.toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
afterEach, describe, expect, it, jest,
} from '@jest/globals';
import NumberBox from '@js/ui/number_box';
import SelectBox from '@js/ui/select_box';
import { themeReadyCallback } from '@ts/ui/m_themes_callback';

import Pagination from '../wrappers/pagination';

jest.mock('@ts/ui/themes', () => ({
...jest.requireActual<object>('@ts/ui/themes'),
isPendingThemeLoaded: (): boolean => false,
}));

const FALLBACK_MIN_WIDTH = 10;
const ONE_DIGIT_WIDTH = 10;

const instances: Pagination[] = [];

function createCompactPagination(config: object = {}): {
container: HTMLElement;
pagination: Pagination;
} {
const root = document.createElement('div');
document.body.appendChild(root);
const container = document.createElement('div');
root.appendChild(container);
const pagination = new Pagination(container, {
displayMode: 'compact',
pageCount: 10,
pageIndex: 1,
pageSize: 5,
itemCount: 46,
pagesCountText: 'of',
...config,
});
instances.push(pagination);
return { container, pagination };
}

afterEach(() => {
instances.splice(0).forEach((pagination) => pagination.dispose());
document.body.innerHTML = '';
});

describe('compact pagination editors', () => {
it('re-reads the css min-width of the page index after the next update', async () => {
const { container, pagination } = createCompactPagination();
const pageIndex = container.querySelector('.dx-page-index') as HTMLElement;
const numberBox = NumberBox.getInstance(pageIndex) as NumberBox;

expect(numberBox.option('width')).toBe(FALLBACK_MIN_WIDTH + 2 * ONE_DIGIT_WIDTH);

pageIndex.style.minWidth = '32px';
pagination.option('pageIndex', 2);
await new Promise((resolve) => { setTimeout(resolve); });

expect(numberBox.option('width')).toBe(32 + 2 * ONE_DIGIT_WIDTH);
});

it('re-reads the css min-width of the page index when the theme is loaded', () => {
const { container } = createCompactPagination();
const pageIndex = container.querySelector('.dx-page-index') as HTMLElement;
const numberBox = NumberBox.getInstance(pageIndex) as NumberBox;

pageIndex.style.minWidth = '32px';
themeReadyCallback.fire();

expect(numberBox.option('width')).toBe(32 + 2 * ONE_DIGIT_WIDTH);
});

it('re-reads the css min-width of the page sizes when the theme is loaded', () => {
const { container } = createCompactPagination({
showPageSizeSelector: true,
allowedPageSizes: [5, 10, 20],
});
const pageSizes = container.querySelector('.dx-page-sizes') as HTMLElement;
const selectBox = SelectBox.getInstance(pageSizes.querySelector('.dx-selectbox') as HTMLElement) as SelectBox;

expect(selectBox.option('width')).toBe(FALLBACK_MIN_WIDTH + 2 * ONE_DIGIT_WIDTH);

pageSizes.style.minWidth = '62px';
themeReadyCallback.fire();

expect(selectBox.option('width')).toBe(62 + 2 * ONE_DIGIT_WIDTH);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import {
afterEach, describe, expect, it, jest,
} from '@jest/globals';

import { isLayoutApplied, ResizableContainer } from '../resizable_container';

function createElement(style: Partial<CSSStyleDeclaration>): HTMLDivElement {
const element = document.createElement('div');
Object.assign(element.style, style);
document.body.appendChild(element);
return element;
}

interface Elements {
parent: HTMLDivElement;
pages?: HTMLDivElement;
allowedPageSizes?: HTMLDivElement;
info?: HTMLDivElement;
}

function createContainer({
parent, pages, allowedPageSizes, info,
}: Elements): ResizableContainer {
const container = new ResizableContainer({
paginationProps: {},
contentTemplate: (): null => null,
} as never);

jest.spyOn(container, 'setState').mockImplementation((updater): void => {
const update = typeof updater === 'function' ? updater(container.state, container.props) : updater;
container.state = { ...container.state, ...update };
});

Object.assign(container, {
parentRef: { current: parent },
pagesRef: { current: pages ?? null },
allowedPageSizesRef: { current: allowedPageSizes ?? null },
infoTextRef: { current: info ?? null },
});

return container;
}

afterEach(() => {
document.body.innerHTML = '';
});

describe('isLayoutApplied', () => {
it('is false while the pager containers are plain block-level elements', () => {
const pages = createElement({ width: '1000px' });
const allowedPageSizes = createElement({ width: '1000px' });

expect(isLayoutApplied({ pages, allowedPageSizes, info: null })).toBe(false);

pages.style.display = 'inline-block';

expect(isLayoutApplied({ pages, allowedPageSizes, info: null })).toBe(false);

allowedPageSizes.style.cssFloat = 'left';

expect(isLayoutApplied({ pages, allowedPageSizes, info: null })).toBe(true);
});

it('is false while the info text is a plain block-level element', () => {
const info = createElement({ width: '1000px' });

expect(isLayoutApplied({ pages: null, allowedPageSizes: null, info })).toBe(false);

info.style.display = 'inline-block';

expect(isLayoutApplied({ pages: null, allowedPageSizes: null, info })).toBe(true);
});

it('ignores containers that are not rendered', () => {
expect(isLayoutApplied({ pages: null, allowedPageSizes: undefined, info: null })).toBe(true);
});
});

describe('ResizableContainer adaptivity', () => {
it('does not switch to compact mode from a measurement taken before the theme css is applied', () => {
const parent = createElement({ width: '1000px' });
const pages = createElement({ width: '1000px' });
const container = createContainer({ parent, pages });

container.updateAdaptivityProps();

expect(container.state.isLargeDisplayMode).toBe(true);

pages.style.display = 'inline-block';
pages.style.width = '300px';
container.updateAdaptivityProps();

expect(container.state.isLargeDisplayMode).toBe(true);
});

it('keeps switching between the modes once the theme css is applied', () => {
const parent = createElement({ width: '1000px' });
const pages = createElement({ width: '1000px' });
const container = createContainer({ parent, pages });

container.updateAdaptivityProps();
pages.style.display = 'inline-block';
pages.style.width = '300px';
container.updateAdaptivityProps();

parent.style.width = '200px';
container.updateAdaptivityProps();

expect(container.state.isLargeDisplayMode).toBe(false);

parent.style.width = '1000px';
container.updateAdaptivityProps();

expect(container.state.isLargeDisplayMode).toBe(true);
});

it('keeps the current mode while the theme css is unavailable', () => {
const parent = createElement({ width: '200px' });
const pages = createElement({ width: '300px', display: 'inline-block' });
const container = createContainer({ parent, pages });

container.updateAdaptivityProps();

expect(container.state.isLargeDisplayMode).toBe(false);

pages.style.display = 'block';
pages.style.width = '200px';
parent.style.width = '1000px';
container.updateAdaptivityProps();

expect(container.state.isLargeDisplayMode).toBe(false);
});

it('does not hide the info text from a measurement taken before the theme css is applied', () => {
const parent = createElement({ width: '1000px' });
const info = createElement({ width: '1000px' });
const container = createContainer({ parent, info });

container.updateAdaptivityProps();

expect(container.state.infoTextVisible).toBe(true);

info.style.display = 'inline-block';
info.style.width = '150px';
container.updateAdaptivityProps();

expect(container.state.infoTextVisible).toBe(true);

parent.style.width = '100px';
container.updateAdaptivityProps();

expect(container.state.infoTextVisible).toBe(false);
});
});
Loading
Loading