mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 10:31:15 +00:00
fix(editor): Reset current page if out of bounds after page size change (#17124)
This commit is contained in:
committed by
GitHub
parent
591aa2d20c
commit
b9e7b719c0
@@ -20,7 +20,7 @@ const itemFactory = () => ({
|
|||||||
});
|
});
|
||||||
type Item = ReturnType<typeof itemFactory>;
|
type Item = ReturnType<typeof itemFactory>;
|
||||||
|
|
||||||
const items: Item[] = [...Array(20).keys()].map(itemFactory);
|
const items: Item[] = [...Array(106).keys()].map(itemFactory);
|
||||||
const headers: Array<TableHeader<Item>> = [
|
const headers: Array<TableHeader<Item>> = [
|
||||||
{
|
{
|
||||||
title: 'Id',
|
title: 'Id',
|
||||||
@@ -102,26 +102,30 @@ describe('N8nDataTableServer', () => {
|
|||||||
await userEvent.click(container.querySelector('thead tr th')!);
|
await userEvent.click(container.querySelector('thead tr th')!);
|
||||||
await userEvent.click(within(getByTestId('pagination')).getByLabelText('page 2'));
|
await userEvent.click(within(getByTestId('pagination')).getByLabelText('page 2'));
|
||||||
|
|
||||||
// change the page size select option
|
expect(emitted('update:options').length).toBe(3);
|
||||||
const selectInput = await findAllByRole('combobox'); // Find the select input
|
|
||||||
await userEvent.click(selectInput[0]);
|
|
||||||
|
|
||||||
const options = await getRenderedOptions();
|
|
||||||
expect(options.length).toBe(4);
|
|
||||||
|
|
||||||
const option50 = Array.from(options).find((option) => option.textContent === '50');
|
|
||||||
await userEvent.click(option50!);
|
|
||||||
|
|
||||||
expect(emitted('update:options').length).toBeGreaterThanOrEqual(4);
|
|
||||||
expect(emitted('update:options')[0]).toStrictEqual([
|
expect(emitted('update:options')[0]).toStrictEqual([
|
||||||
expect.objectContaining({ sortBy: [{ id: 'id', desc: false }] }),
|
expect.objectContaining({ sortBy: [{ id: 'id', desc: false }] }),
|
||||||
]);
|
]);
|
||||||
expect(emitted('update:options')[1]).toStrictEqual([
|
expect(emitted('update:options')[1]).toStrictEqual([
|
||||||
expect.objectContaining({ sortBy: [{ id: 'id', desc: true }] }),
|
expect.objectContaining({ sortBy: [{ id: 'id', desc: true }] }),
|
||||||
]);
|
]);
|
||||||
expect(emitted('update:options')[2]).toStrictEqual([expect.objectContaining({ page: 1 })]);
|
|
||||||
expect(emitted('update:options')[3]).toStrictEqual([
|
// // change the page size select option
|
||||||
expect.objectContaining({ itemsPerPage: 50 }),
|
const selectInput = await findAllByRole('combobox'); // Find the select input
|
||||||
|
await userEvent.click(selectInput[0]);
|
||||||
|
|
||||||
|
const options = await getRenderedOptions();
|
||||||
|
expect(options.length).toBe(4);
|
||||||
|
|
||||||
|
// account for the debounce
|
||||||
|
await new Promise((r) => setTimeout(r, 100));
|
||||||
|
|
||||||
|
const option50 = Array.from(options).find((option) => option.textContent === '50');
|
||||||
|
await userEvent.click(option50!);
|
||||||
|
|
||||||
|
expect(emitted('update:options').length).toBe(4);
|
||||||
|
expect(emitted('update:options').at(-1)).toStrictEqual([
|
||||||
|
expect.objectContaining({ page: 1, itemsPerPage: 50 }),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -157,4 +161,28 @@ describe('N8nDataTableServer', () => {
|
|||||||
|
|
||||||
expect(queryByTestId('pagination')).not.toBeInTheDocument();
|
expect(queryByTestId('pagination')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should adjust page to highest available when page size changes and current page exceeds maximum', async () => {
|
||||||
|
const { emitted, findAllByRole } = renderComponent({
|
||||||
|
props: { items, headers, itemsLength: 106, itemsPerPage: 50, page: 2 },
|
||||||
|
});
|
||||||
|
|
||||||
|
// change the page size select option
|
||||||
|
const selectInput = await findAllByRole('combobox'); // Find the select input
|
||||||
|
await userEvent.click(selectInput[0]);
|
||||||
|
|
||||||
|
const options = await getRenderedOptions();
|
||||||
|
expect(options.length).toBe(4);
|
||||||
|
|
||||||
|
const option100 = Array.from(options).find((option) => option.textContent === '100');
|
||||||
|
await userEvent.click(option100!);
|
||||||
|
|
||||||
|
// With 106 items and 50 per page, max page should be 2 (0-based index 1)
|
||||||
|
// Since we were on page 2, we should be adjusted to page 1
|
||||||
|
expect(emitted('update:options')).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.arrayContaining([expect.objectContaining({ page: 1, itemsPerPage: 100 })]),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import type {
|
|||||||
Updater,
|
Updater,
|
||||||
} from '@tanstack/vue-table';
|
} from '@tanstack/vue-table';
|
||||||
import { createColumnHelper, FlexRender, getCoreRowModel, useVueTable } from '@tanstack/vue-table';
|
import { createColumnHelper, FlexRender, getCoreRowModel, useVueTable } from '@tanstack/vue-table';
|
||||||
|
import { useThrottleFn } from '@vueuse/core';
|
||||||
import { ElCheckbox, ElOption, ElSelect, ElSkeletonItem } from 'element-plus';
|
import { ElCheckbox, ElOption, ElSelect, ElSkeletonItem } from 'element-plus';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import { computed, h, ref, shallowRef, useSlots, watch } from 'vue';
|
import { computed, h, ref, shallowRef, useSlots, watch } from 'vue';
|
||||||
@@ -204,10 +205,7 @@ const page = defineModel<number>('page', { default: 0 });
|
|||||||
watch(page, () => table.setPageIndex(page.value));
|
watch(page, () => table.setPageIndex(page.value));
|
||||||
|
|
||||||
const itemsPerPage = defineModel<number>('items-per-page', { default: 10 });
|
const itemsPerPage = defineModel<number>('items-per-page', { default: 10 });
|
||||||
watch(itemsPerPage, () => {
|
watch(itemsPerPage, () => table.setPageSize(itemsPerPage.value));
|
||||||
page.value = 0;
|
|
||||||
table.setPageSize(itemsPerPage.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
const pagination = computed<PaginationState>({
|
const pagination = computed<PaginationState>({
|
||||||
get() {
|
get() {
|
||||||
@@ -313,6 +311,20 @@ const rowSelection = ref(
|
|||||||
}, {}),
|
}, {}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const emitUpdateOptions = useThrottleFn(
|
||||||
|
(payload: TableOptions) => emit('update:options', payload),
|
||||||
|
100,
|
||||||
|
);
|
||||||
|
|
||||||
|
function handlePageSizeChange(newPageSize: number) {
|
||||||
|
// Calculate the maximum available page (0-based indexing)
|
||||||
|
const maxPage = Math.max(0, Math.ceil(props.itemsLength / newPageSize) - 1);
|
||||||
|
const newPage = Math.min(page.value, maxPage);
|
||||||
|
|
||||||
|
page.value = newPage;
|
||||||
|
itemsPerPage.value = newPageSize;
|
||||||
|
}
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<T>();
|
const columnHelper = createColumnHelper<T>();
|
||||||
const table = useVueTable({
|
const table = useVueTable({
|
||||||
data,
|
data,
|
||||||
@@ -334,12 +346,13 @@ const table = useVueTable({
|
|||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
onSortingChange: handleSortingChange,
|
onSortingChange: handleSortingChange,
|
||||||
onPaginationChange(updaterOrValue) {
|
onPaginationChange(updaterOrValue) {
|
||||||
pagination.value =
|
const newValue =
|
||||||
typeof updaterOrValue === 'function' ? updaterOrValue(pagination.value) : updaterOrValue;
|
typeof updaterOrValue === 'function' ? updaterOrValue(pagination.value) : updaterOrValue;
|
||||||
|
|
||||||
emit('update:options', {
|
// prevent duplicate events from being fired
|
||||||
page: page.value,
|
void emitUpdateOptions({
|
||||||
itemsPerPage: itemsPerPage.value,
|
page: newValue.pageIndex,
|
||||||
|
itemsPerPage: newValue.pageSize,
|
||||||
sortBy: sortBy.value,
|
sortBy: sortBy.value,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -474,6 +487,7 @@ const table = useVueTable({
|
|||||||
class="table-pagination__sizes__select"
|
class="table-pagination__sizes__select"
|
||||||
size="small"
|
size="small"
|
||||||
:teleported="false"
|
:teleported="false"
|
||||||
|
@update:model-value="handlePageSizeChange"
|
||||||
>
|
>
|
||||||
<ElOption v-for="item in pageSizes" :key="item" :label="item" :value="item" />
|
<ElOption v-for="item in pageSizes" :key="item" :label="item" :value="item" />
|
||||||
</ElSelect>
|
</ElSelect>
|
||||||
|
|||||||
@@ -508,4 +508,8 @@ async function onUpdateMfaEnforced(value: boolean) {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user