mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
feat: reset persistent table offset on change (#5650)
This commit is contained in:
parent
53b32db278
commit
0726887bb8
@ -92,9 +92,6 @@ export const FeatureToggleListTable: VFC = () => {
|
||||
'features-list-table',
|
||||
stateConfig,
|
||||
);
|
||||
// offset needs to be first so we can override it
|
||||
const setTableStateWithOffsetReset: typeof setTableState = (data) =>
|
||||
setTableState({ offset: 0, ...data });
|
||||
|
||||
const filterState = {
|
||||
project: tableState.project,
|
||||
@ -141,7 +138,7 @@ export const FeatureToggleListTable: VFC = () => {
|
||||
<FavoriteIconHeader
|
||||
isActive={tableState.favoritesFirst}
|
||||
onClick={() =>
|
||||
setTableStateWithOffsetReset({
|
||||
setTableState({
|
||||
favoritesFirst: !tableState.favoritesFirst,
|
||||
})
|
||||
}
|
||||
@ -230,7 +227,7 @@ export const FeatureToggleListTable: VFC = () => {
|
||||
);
|
||||
|
||||
const table = useReactTable(
|
||||
withTableState(tableState, setTableStateWithOffsetReset, {
|
||||
withTableState(tableState, setTableState, {
|
||||
columns,
|
||||
data,
|
||||
}),
|
||||
@ -255,8 +252,7 @@ export const FeatureToggleListTable: VFC = () => {
|
||||
}
|
||||
}, [isSmallScreen, isMediumScreen]);
|
||||
|
||||
const setSearchValue = (query = '') =>
|
||||
setTableStateWithOffsetReset({ query });
|
||||
const setSearchValue = (query = '') => setTableState({ query });
|
||||
|
||||
const rows = table.getRowModel().rows;
|
||||
|
||||
@ -340,7 +336,7 @@ export const FeatureToggleListTable: VFC = () => {
|
||||
}
|
||||
>
|
||||
<FeatureToggleFilters
|
||||
onChange={setTableStateWithOffsetReset}
|
||||
onChange={setTableState}
|
||||
state={filterState}
|
||||
/>
|
||||
<SearchHighlightProvider value={tableState.query || ''}>
|
||||
|
@ -33,7 +33,7 @@ interface IAddFilterButtonProps {
|
||||
availableFilters: IFilterItem[];
|
||||
}
|
||||
|
||||
const AddFilterButton = ({
|
||||
export const AddFilterButton = ({
|
||||
visibleOptions,
|
||||
setVisibleOptions,
|
||||
hiddenOptions,
|
||||
@ -87,5 +87,3 @@ const AddFilterButton = ({
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddFilterButton;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useEffect, useState, VFC } from 'react';
|
||||
import { Box, styled } from '@mui/material';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import AddFilterButton from '../AddFilterButton';
|
||||
import { AddFilterButton } from '../AddFilterButton';
|
||||
import { FilterDateItem } from 'component/common/FilterDateItem/FilterDateItem';
|
||||
import { FilterItem, FilterItemParams } from '../FilterItem/FilterItem';
|
||||
|
||||
|
@ -1,68 +1,26 @@
|
||||
import React, {
|
||||
type CSSProperties,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import {
|
||||
Checkbox,
|
||||
IconButton,
|
||||
styled,
|
||||
Tooltip,
|
||||
useMediaQuery,
|
||||
Box,
|
||||
useTheme,
|
||||
} from '@mui/material';
|
||||
import { Add } from '@mui/icons-material';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
useFlexLayout,
|
||||
usePagination,
|
||||
useRowSelect,
|
||||
useSortBy,
|
||||
useTable,
|
||||
} from 'react-table';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||
import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton';
|
||||
import { getCreateTogglePath } from 'utils/routePathHelpers';
|
||||
import { CREATE_FEATURE } from 'component/providers/AccessProvider/permissions';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import { DateCell } from 'component/common/Table/cells/DateCell/DateCell';
|
||||
import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
|
||||
import { FeatureSeenCell } from 'component/common/Table/cells/FeatureSeenCell/FeatureSeenCell';
|
||||
import { FeatureTypeCell } from 'component/common/Table/cells/FeatureTypeCell/FeatureTypeCell';
|
||||
import { IProject } from 'interfaces/project';
|
||||
import { PaginatedTable, VirtualizedTable } from 'component/common/Table';
|
||||
import { PaginatedTable } from 'component/common/Table';
|
||||
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
||||
import { FeatureStaleDialog } from 'component/common/FeatureStaleDialog/FeatureStaleDialog';
|
||||
import { FeatureArchiveDialog } from 'component/common/FeatureArchiveDialog/FeatureArchiveDialog';
|
||||
import { getColumnValues, includesFilter, useSearch } from 'hooks/useSearch';
|
||||
import { Search } from 'component/common/Search/Search';
|
||||
import { IFeatureToggleListItem } from 'interfaces/featureToggle';
|
||||
import { FavoriteIconHeader } from 'component/common/Table/FavoriteIconHeader/FavoriteIconHeader';
|
||||
import { FavoriteIconCell } from 'component/common/Table/cells/FavoriteIconCell/FavoriteIconCell';
|
||||
import { ProjectEnvironmentType } from '../../ProjectFeatureToggles/hooks/useEnvironmentsRef';
|
||||
import { ActionsCell } from '../../ProjectFeatureToggles/ActionsCell/ActionsCell';
|
||||
import { ExperimentalColumnsMenu as ColumnsMenu } from './ExperimentalColumnsMenu/ExperimentalColumnsMenu';
|
||||
import { useStyles } from '../../ProjectFeatureToggles/ProjectFeatureToggles.styles';
|
||||
import { useFavoriteFeaturesApi } from 'hooks/api/actions/useFavoriteFeaturesApi/useFavoriteFeaturesApi';
|
||||
import { FeatureTagCell } from 'component/common/Table/cells/FeatureTagCell/FeatureTagCell';
|
||||
import FileDownload from '@mui/icons-material/FileDownload';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { ExportDialog } from 'component/feature/FeatureToggleList/ExportDialog';
|
||||
import { MemoizedRowSelectCell } from '../../ProjectFeatureToggles/RowSelectCell/RowSelectCell';
|
||||
import { BatchSelectionActionsBar } from 'component/common/BatchSelectionActionsBar/BatchSelectionActionsBar';
|
||||
import { ProjectFeaturesBatchActions } from '../../ProjectFeatureToggles/ProjectFeaturesBatchActions/ProjectFeaturesBatchActions';
|
||||
import { MemoizedFeatureEnvironmentSeenCell } from 'component/common/Table/cells/FeatureSeenCell/FeatureEnvironmentSeenCell';
|
||||
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
|
||||
import { ListItemType } from '../../ProjectFeatureToggles/ProjectFeatureToggles.types';
|
||||
import { createFeatureToggleCell } from '../../ProjectFeatureToggles/FeatureToggleSwitch/createFeatureToggleCell';
|
||||
import { useFeatureToggleSwitch } from '../../ProjectFeatureToggles/FeatureToggleSwitch/useFeatureToggleSwitch';
|
||||
import useLoading from 'hooks/useLoading';
|
||||
import { StickyPaginationBar } from '../../../../common/Table/StickyPaginationBar/StickyPaginationBar';
|
||||
import {
|
||||
DEFAULT_PAGE_LIMIT,
|
||||
useFeatureSearch,
|
||||
@ -74,11 +32,11 @@ import {
|
||||
FilterItemParam,
|
||||
} from 'utils/serializeQueryParams';
|
||||
import {
|
||||
ArrayParam,
|
||||
encodeQueryParams,
|
||||
NumberParam,
|
||||
StringParam,
|
||||
ArrayParam,
|
||||
withDefault,
|
||||
encodeQueryParams,
|
||||
} from 'use-query-params';
|
||||
import { ProjectFeatureTogglesHeader } from './ProjectFeatureTogglesHeader/ProjectFeatureTogglesHeader';
|
||||
import { createColumnHelper, useReactTable } from '@tanstack/react-table';
|
||||
|
@ -172,11 +172,61 @@ describe('usePersistentTableState', () => {
|
||||
screen.getByText('Update Offset').click();
|
||||
screen.getByText('Update State').click();
|
||||
|
||||
expect(window.location.href).toContain('my-url?query=after&offset=20');
|
||||
expect(window.location.href).toContain('my-url?query=after&offset=0');
|
||||
|
||||
await waitFor(() => {
|
||||
const { value } = createLocalStorage('testKey', {});
|
||||
expect(value).toStrictEqual({ query: 'after' });
|
||||
});
|
||||
});
|
||||
|
||||
it('resets offset to 0 on state update', async () => {
|
||||
createLocalStorage('testKey', {}).setValue({ query: 'before' });
|
||||
|
||||
render(
|
||||
<TestComponent
|
||||
keyName='testKey'
|
||||
queryParamsDefinition={{
|
||||
query: StringParam,
|
||||
offset: NumberParam,
|
||||
}}
|
||||
/>,
|
||||
{ route: '/my-url?query=before&offset=10' },
|
||||
);
|
||||
|
||||
expect(window.location.href).toContain('my-url?query=before&offset=10');
|
||||
|
||||
screen.getByText('Update State').click();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.location.href).toContain(
|
||||
'my-url?query=after&offset=0',
|
||||
);
|
||||
expect(window.location.href).not.toContain('offset=10');
|
||||
});
|
||||
});
|
||||
|
||||
it('does not reset offset to 0 without offset decoder', async () => {
|
||||
createLocalStorage('testKey', {}).setValue({ query: 'before' });
|
||||
|
||||
render(
|
||||
<TestComponent
|
||||
keyName='testKey'
|
||||
queryParamsDefinition={{
|
||||
query: StringParam,
|
||||
}}
|
||||
/>,
|
||||
{ route: '/my-url?query=before&offset=10' },
|
||||
);
|
||||
|
||||
expect(window.location.href).toContain('my-url?query=before&offset=10');
|
||||
|
||||
screen.getByText('Update State').click();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.location.href).toContain(
|
||||
'my-url?query=after&offset=10',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useCallback } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { createLocalStorage } from 'utils/createLocalStorage';
|
||||
import { useQueryParams, encodeQueryParams } from 'use-query-params';
|
||||
import { encodeQueryParams, useQueryParams } from 'use-query-params';
|
||||
import { QueryParamConfigMap } from 'serialize-query-params/src/types';
|
||||
|
||||
const usePersistentSearchParams = <T extends QueryParamConfigMap>(
|
||||
@ -39,7 +39,41 @@ export const usePersistentTableState = <T extends QueryParamConfigMap>(
|
||||
queryParamsDefinition,
|
||||
);
|
||||
|
||||
const [tableState, setTableState] = useQueryParams(queryParamsDefinition);
|
||||
const [tableState, setTableStateInternal] = useQueryParams(
|
||||
queryParamsDefinition,
|
||||
);
|
||||
|
||||
type SetTableStateInternalParam = Parameters<
|
||||
typeof setTableStateInternal
|
||||
>[0];
|
||||
|
||||
const setTableState = useCallback(
|
||||
(newState: SetTableStateInternalParam) => {
|
||||
if (!queryParamsDefinition.offset) {
|
||||
return setTableStateInternal(newState);
|
||||
}
|
||||
if (typeof newState === 'function') {
|
||||
setTableStateInternal((prevState) => {
|
||||
const updatedState = (newState as Function)(prevState);
|
||||
return queryParamsDefinition.offset
|
||||
? {
|
||||
offset: queryParamsDefinition.offset.decode('0'),
|
||||
...updatedState,
|
||||
}
|
||||
: updatedState;
|
||||
});
|
||||
} else {
|
||||
const updatedState = queryParamsDefinition.offset
|
||||
? {
|
||||
offset: queryParamsDefinition.offset.decode('0'),
|
||||
...newState,
|
||||
}
|
||||
: newState;
|
||||
setTableStateInternal(updatedState);
|
||||
}
|
||||
},
|
||||
[setTableStateInternal, queryParamsDefinition.offset],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const { offset, ...rest } = tableState;
|
||||
|
Loading…
Reference in New Issue
Block a user