1
0
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:
Mateusz Kwasniewski 2023-12-15 10:20:55 +01:00 committed by GitHub
parent 53b32db278
commit 0726887bb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 98 additions and 62 deletions

View File

@ -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 || ''}>

View File

@ -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;

View File

@ -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';

View File

@ -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';

View File

@ -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',
);
});
});
});

View File

@ -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;