From 6198900014c8f756c02cf1c7ad67209719ae0c50 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Tue, 9 Sep 2025 13:18:33 +0200 Subject: [PATCH] fix: limit reset when no pagination bar (#10634) --- .../common/Table/PaginationBar/PaginationBar.tsx | 7 ------- .../component/events/EventLog/useEventLogSearch.ts | 12 ++++-------- .../FeatureToggleList/useGlobalFeatureSearch.ts | 12 ++++-------- .../useProjectFeatureSearch.ts | 6 +++--- frontend/src/hooks/usePersistentTableState.ts | 13 +++++++++++++ frontend/src/utils/safeNumberParam.ts | 12 ++++++++++++ 6 files changed, 36 insertions(+), 26 deletions(-) create mode 100644 frontend/src/utils/safeNumberParam.ts diff --git a/frontend/src/component/common/Table/PaginationBar/PaginationBar.tsx b/frontend/src/component/common/Table/PaginationBar/PaginationBar.tsx index 7e265ea05e..9660ddde2e 100644 --- a/frontend/src/component/common/Table/PaginationBar/PaginationBar.tsx +++ b/frontend/src/component/common/Table/PaginationBar/PaginationBar.tsx @@ -1,5 +1,4 @@ import type React from 'react'; -import { useEffect } from 'react'; import { Box, Typography, Button, styled } from '@mui/material'; import { ConditionallyRender } from '../../ConditionallyRender/ConditionallyRender.tsx'; import { ReactComponent as ArrowRight } from 'assets/icons/arrowRight.svg'; @@ -61,12 +60,6 @@ export const PaginationBar: React.FC = ({ fetchNextPage, setPageLimit, }) => { - useEffect(() => { - if (![25, 50, 75, 100].includes(pageSize)) { - setPageLimit(25); - } - }, [pageSize]); - const itemRange = totalItems !== undefined && pageSize && totalItems > 1 ? `${pageIndex * pageSize + 1}-${Math.min( diff --git a/frontend/src/component/events/EventLog/useEventLogSearch.ts b/frontend/src/component/events/EventLog/useEventLogSearch.ts index ce51ba35d9..efbebc2fd7 100644 --- a/frontend/src/component/events/EventLog/useEventLogSearch.ts +++ b/frontend/src/component/events/EventLog/useEventLogSearch.ts @@ -1,9 +1,4 @@ -import { - encodeQueryParams, - NumberParam, - StringParam, - withDefault, -} from 'use-query-params'; +import { encodeQueryParams, StringParam, withDefault } from 'use-query-params'; import { FilterItemParam } from 'utils/serializeQueryParams'; import { usePersistentTableState } from 'hooks/usePersistentTableState'; import mapValues from 'lodash.mapvalues'; @@ -11,6 +6,7 @@ import { useEventSearch } from 'hooks/api/getters/useEventSearch/useEventSearch' import type { SearchEventsParams } from 'openapi'; import type { FilterItemParamHolder } from 'component/filter/Filters/Filters'; import { format, subYears } from 'date-fns'; +import { SafeNumberParam } from 'utils/safeNumberParam'; type Log = | { type: 'global' } @@ -58,8 +54,8 @@ export const useEventLogSearch = ( refreshInterval = 15 * 1000, ) => { const stateConfig = { - offset: withDefault(NumberParam, 0), - limit: withDefault(NumberParam, DEFAULT_PAGE_SIZE), + offset: withDefault(SafeNumberParam, 0), + limit: withDefault(SafeNumberParam, DEFAULT_PAGE_SIZE), query: StringParam, from: withDefault(FilterItemParam, { values: [format(subYears(new Date(), 1), 'yyyy-MM-dd')], diff --git a/frontend/src/component/feature/FeatureToggleList/useGlobalFeatureSearch.ts b/frontend/src/component/feature/FeatureToggleList/useGlobalFeatureSearch.ts index 32b5a7cab7..4dbf56fbdf 100644 --- a/frontend/src/component/feature/FeatureToggleList/useGlobalFeatureSearch.ts +++ b/frontend/src/component/feature/FeatureToggleList/useGlobalFeatureSearch.ts @@ -1,10 +1,5 @@ import { useCallback } from 'react'; -import { - encodeQueryParams, - NumberParam, - StringParam, - withDefault, -} from 'use-query-params'; +import { encodeQueryParams, StringParam, withDefault } from 'use-query-params'; import { DEFAULT_PAGE_LIMIT, useFeatureSearch, @@ -16,12 +11,13 @@ import { import { usePersistentTableState } from 'hooks/usePersistentTableState'; import mapValues from 'lodash.mapvalues'; import type { SearchFeaturesParams } from 'openapi'; +import { SafeNumberParam } from 'utils/safeNumberParam'; export const useGlobalFeatureSearch = (pageLimit = DEFAULT_PAGE_LIMIT) => { const storageKey = 'features-list-table'; const stateConfig = { - offset: withDefault(NumberParam, 0), - limit: withDefault(NumberParam, pageLimit), + offset: withDefault(SafeNumberParam, 0), + limit: withDefault(SafeNumberParam, pageLimit), query: StringParam, favoritesFirst: withDefault(BooleansStringParam, true), sortBy: withDefault(StringParam, 'createdAt'), diff --git a/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/useProjectFeatureSearch.ts b/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/useProjectFeatureSearch.ts index 78c43401e0..20ebc83e82 100644 --- a/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/useProjectFeatureSearch.ts +++ b/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/useProjectFeatureSearch.ts @@ -1,7 +1,6 @@ import { ArrayParam, encodeQueryParams, - NumberParam, StringParam, withDefault, } from 'use-query-params'; @@ -16,6 +15,7 @@ import { import { usePersistentTableState } from 'hooks/usePersistentTableState'; import mapValues from 'lodash.mapvalues'; import type { SearchFeaturesParams } from 'openapi'; +import { SafeNumberParam } from 'utils/safeNumberParam'; type Attribute = | { key: 'tag'; operator: 'INCLUDE' } @@ -28,8 +28,8 @@ export const useProjectFeatureSearch = ( refreshInterval = 15 * 1000, ) => { const stateConfig = { - offset: withDefault(NumberParam, 0), - limit: withDefault(NumberParam, DEFAULT_PAGE_LIMIT), + offset: withDefault(SafeNumberParam, 0), + limit: withDefault(SafeNumberParam, DEFAULT_PAGE_LIMIT), query: StringParam, favoritesFirst: withDefault(BooleansStringParam, true), sortBy: withDefault(StringParam, 'createdAt'), diff --git a/frontend/src/hooks/usePersistentTableState.ts b/frontend/src/hooks/usePersistentTableState.ts index 6087033bc8..5d13618372 100644 --- a/frontend/src/hooks/usePersistentTableState.ts +++ b/frontend/src/hooks/usePersistentTableState.ts @@ -51,6 +51,19 @@ export const usePersistentTableState = ( return reorderObject(tableState, [...searchParams.keys()]); }, [searchParams, tableState, reorderObject]); + useEffect(() => { + if ( + tableState.limit && + ![25, 50, 75, 100].includes(tableState.limit as number) + ) { + setTableStateInternal((prevState) => ({ + ...prevState, + limit: 25, + offset: 0, // Reset offset when changing limit + })); + } + }, [tableState.limit, setTableStateInternal]); + type SetTableStateInternalParam = Parameters< typeof setTableStateInternal >[0]; diff --git a/frontend/src/utils/safeNumberParam.ts b/frontend/src/utils/safeNumberParam.ts new file mode 100644 index 0000000000..da06d417a8 --- /dev/null +++ b/frontend/src/utils/safeNumberParam.ts @@ -0,0 +1,12 @@ +import { encodeNumber, decodeNumber } from 'serialize-query-params'; + +/** + * @see: https://github.com/pbeshai/use-query-params/issues/175#issuecomment-982791559 + */ +export const SafeNumberParam = { + encode: encodeNumber, + decode: (input: any) => { + const result = decodeNumber(input); + return result == null ? result : Number.isNaN(result) ? null : result; + }, +};