Merge pull request #10215 from keithjgrant/9218-sort-in-modal

fix namespaced url params

SUMMARY
Fixes issues when multiple lists are on the page (generally only occurs now when on a list page with an open modal that contains a second list) — Navigating within the modal list currently wipes out URL parameters for the main list, causing the page to reload, which closes the modal. The fix prevents changes to one set of namespaced URL parameters from wiping out URL parameters from another namespace

Refactors query string utils to consolidate a lot of repeated logic from components into a new util, updateQueryString. Use this function to modify query string parameters while maintaining params for multiple namespaces at the same time.
QS utils that are no longer needed have been deleted: replaceParams and encodeNonDefaultQueryString

Addresses #10181 and #9218
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME

UI

Reviewed-by: Keith Grant <keithjgrant@gmail.com>
Reviewed-by: John Mitchell <None>
Reviewed-by: Alex Corey <Alex.swansboro@gmail.com>
This commit is contained in:
softwarefactory-project-zuul[bot] 2021-06-02 21:01:49 +00:00 committed by GitHub
commit d70dfec6b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 277 additions and 301 deletions

View File

@ -6,11 +6,10 @@ import { Toolbar, ToolbarContent } from '@patternfly/react-core';
import DataListToolbar from '../DataListToolbar';
import {
encodeNonDefaultQueryString,
parseQueryString,
mergeParams,
replaceParams,
removeParams,
updateQueryString,
} from '../../util/qs';
import { QSConfig, SearchColumns, SortColumns } from '../../types';
@ -38,60 +37,57 @@ class ListHeader extends React.Component {
handleSearch(key, value) {
const { location, qsConfig } = this.props;
let params = parseQueryString(qsConfig, location.search);
params = mergeParams(params, { [key]: value });
params = replaceParams(params, { page: 1 });
this.pushHistoryState(params);
const params = parseQueryString(qsConfig, location.search);
const qs = updateQueryString(qsConfig, location.search, {
...mergeParams(params, { [key]: value }),
page: 1,
});
this.pushHistoryState(qs);
}
handleReplaceSearch(key, value) {
const { location, qsConfig } = this.props;
const oldParams = parseQueryString(qsConfig, location.search);
this.pushHistoryState(replaceParams(oldParams, { [key]: value }));
const qs = updateQueryString(qsConfig, location.search, {
[key]: value,
});
this.pushHistoryState(qs);
}
handleRemove(key, value) {
const { location, qsConfig } = this.props;
let oldParams = parseQueryString(qsConfig, location.search);
if (parseInt(value, 10)) {
oldParams = removeParams(qsConfig, oldParams, {
[key]: parseInt(value, 10),
});
}
this.pushHistoryState(removeParams(qsConfig, oldParams, { [key]: value }));
const oldParams = parseQueryString(qsConfig, location.search);
const updatedParams = removeParams(qsConfig, oldParams, {
[key]: value,
});
const qs = updateQueryString(qsConfig, location.search, updatedParams);
this.pushHistoryState(qs);
}
handleRemoveAll() {
// remove everything in oldParams except for page_size and order_by
const { location, qsConfig } = this.props;
const oldParams = parseQueryString(qsConfig, location.search);
const oldParamsClone = { ...oldParams };
delete oldParamsClone.page_size;
delete oldParamsClone.order_by;
this.pushHistoryState(removeParams(qsConfig, oldParams, oldParamsClone));
Object.keys(oldParams).forEach(key => {
oldParams[key] = null;
});
delete oldParams.page_size;
delete oldParams.order_by;
const qs = updateQueryString(qsConfig, location.search, oldParams);
this.pushHistoryState(qs);
}
handleSort(key, order) {
const { location, qsConfig } = this.props;
const oldParams = parseQueryString(qsConfig, location.search);
this.pushHistoryState(
replaceParams(oldParams, {
order_by: order === 'ascending' ? key : `-${key}`,
page: null,
})
);
const qs = updateQueryString(qsConfig, location.search, {
order_by: order === 'ascending' ? key : `-${key}`,
page: null,
});
this.pushHistoryState(qs);
}
pushHistoryState(params) {
const { history, qsConfig } = this.props;
pushHistoryState(queryString) {
const { history } = this.props;
const { pathname } = history.location;
const nonNamespacedParams = parseQueryString({}, history.location.search);
const encodedParams = encodeNonDefaultQueryString(
qsConfig,
params,
nonNamespacedParams
);
history.push(encodedParams ? `${pathname}?${encodedParams}` : pathname);
history.push(queryString ? `${pathname}?${queryString}` : pathname);
}
render() {

View File

@ -51,7 +51,7 @@ describe('ListHeader', () => {
expect(history.location.search).toEqual('');
});
test('should test clear all', () => {
test('should clear all', () => {
const query = '?item.page_size=5&item.name=foo';
const history = createMemoryHistory({
initialEntries: [`/organizations/1/teams${query}`],

View File

@ -13,11 +13,7 @@ import ContentLoading from '../ContentLoading';
import Pagination from '../Pagination';
import DataListToolbar from '../DataListToolbar';
import {
encodeNonDefaultQueryString,
parseQueryString,
replaceParams,
} from '../../util/qs';
import { parseQueryString, updateQueryString } from '../../util/qs';
import { QSConfig, SearchColumns, SortColumns } from '../../types';
@ -40,7 +36,6 @@ function PaginatedDataList({
pluralizedItemName,
showPageSizeOptions,
location,
renderToolbar,
}) {
const { search, pathname } = useLocation();
@ -51,23 +46,22 @@ function PaginatedDataList({
};
const handleSetPage = (event, pageNumber) => {
const oldParams = parseQueryString(qsConfig, search);
pushHistoryState(replaceParams(oldParams, { page: pageNumber }));
const qs = updateQueryString(qsConfig, search, {
page: pageNumber,
});
pushHistoryState(qs);
};
const handleSetPageSize = (event, pageSize, page) => {
const oldParams = parseQueryString(qsConfig, search);
pushHistoryState(replaceParams(oldParams, { page_size: pageSize, page }));
const qs = updateQueryString(qsConfig, search, {
page_size: pageSize,
page,
});
pushHistoryState(qs);
};
const pushHistoryState = params => {
const nonNamespacedParams = parseQueryString({}, history.location.search);
const encodedParams = encodeNonDefaultQueryString(
qsConfig,
params,
nonNamespacedParams
);
history.push(encodedParams ? `${pathname}?${encodedParams}` : pathname);
const pushHistoryState = qs => {
history.push(qs ? `${pathname}?${qs}` : pathname);
};
const searchColumns = toolbarSearchColumns.length

View File

@ -3,11 +3,7 @@ import React from 'react';
import { useLocation, useHistory } from 'react-router-dom';
import { Thead, Tr, Th as PFTh } from '@patternfly/react-table';
import styled from 'styled-components';
import {
encodeNonDefaultQueryString,
parseQueryString,
replaceParams,
} from '../../util/qs';
import { parseQueryString, updateQueryString } from '../../util/qs';
const Th = styled(PFTh)`
--pf-c-table--cell--Overflow: initial;
@ -25,21 +21,11 @@ export default function HeaderRow({
const params = parseQueryString(qsConfig, location.search);
const onSort = (key, order) => {
const newParams = replaceParams(params, {
const qs = updateQueryString(qsConfig, location.search, {
order_by: order === 'asc' ? key : `-${key}`,
page: null,
});
const nonNamespacedParams = parseQueryString({}, history.location.search);
const encodedParams = encodeNonDefaultQueryString(
qsConfig,
newParams,
nonNamespacedParams
);
history.push(
encodedParams
? `${location.pathname}?${encodedParams}`
: location.pathname
);
history.push(qs ? `${location.pathname}?${qs}` : location.pathname);
};
const sortKey = params.order_by?.replace('-', '');

View File

@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import { TableComposable, Tbody } from '@patternfly/react-table';
import { t } from '@lingui/macro';
import { useHistory } from 'react-router-dom';
import { useLocation, useHistory } from 'react-router-dom';
import ListHeader from '../ListHeader';
import ContentEmpty from '../ContentEmpty';
@ -14,11 +14,7 @@ import Pagination from '../Pagination';
import DataListToolbar from '../DataListToolbar';
import LoadingSpinner from '../LoadingSpinner';
import {
encodeNonDefaultQueryString,
parseQueryString,
replaceParams,
} from '../../util/qs';
import { parseQueryString, updateQueryString } from '../../util/qs';
import { QSConfig, SearchColumns } from '../../types';
function PaginatedTable({
@ -39,27 +35,26 @@ function PaginatedTable({
emptyContentMessage,
ouiaId,
}) {
const { search, pathname } = useLocation();
const history = useHistory();
const pushHistoryState = params => {
const { pathname, search } = history.location;
const nonNamespacedParams = parseQueryString({}, search);
const encodedParams = encodeNonDefaultQueryString(
qsConfig,
params,
nonNamespacedParams
);
history.push(encodedParams ? `${pathname}?${encodedParams}` : pathname);
const pushHistoryState = qs => {
history.push(qs ? `${pathname}?${qs}` : pathname);
};
const handleSetPage = (event, pageNumber) => {
const oldParams = parseQueryString(qsConfig, history.location.search);
pushHistoryState(replaceParams(oldParams, { page: pageNumber }));
const qs = updateQueryString(qsConfig, search, {
page: pageNumber,
});
pushHistoryState(qs);
};
const handleSetPageSize = (event, pageSize, page) => {
const oldParams = parseQueryString(qsConfig, history.location.search);
pushHistoryState(replaceParams(oldParams, { page_size: pageSize, page }));
const qs = updateQueryString(qsConfig, search, {
page_size: pageSize,
page,
});
pushHistoryState(qs);
};
const searchColumns = toolbarSearchColumns.length

View File

@ -22,8 +22,7 @@ import useRequest from '../../util/useRequest';
import {
getQSConfig,
parseQueryString,
replaceParams,
encodeNonDefaultQueryString,
updateQueryString,
} from '../../util/qs';
import { ActivityStreamAPI } from '../../api';
@ -96,16 +95,14 @@ function ActivityStream() {
}, [fetchActivityStream]);
const pushHistoryState = urlParamsToAdd => {
let searchParams = parseQueryString(QS_CONFIG, location.search);
searchParams = replaceParams(searchParams, { page: 1 });
const encodedParams = encodeNonDefaultQueryString(QS_CONFIG, searchParams, {
const pageOneQs = updateQueryString(QS_CONFIG, location.search, {
page: 1,
});
const qs = updateQueryString(null, pageOneQs, {
type: urlParamsToAdd.get('type'),
});
history.push(
encodedParams
? `${location.pathname}?${encodedParams}`
: location.pathname
);
history.push(qs ? `${location.pathname}?${qs}` : location.pathname);
};
return (

View File

@ -35,7 +35,7 @@ function Host({ setBreadcrumb }) {
useEffect(() => {
fetchHost();
}, [fetchHost, location]);
}, [fetchHost, location.pathname]);
const tabsArray = [
{

View File

@ -1,10 +1,6 @@
import { useState, useEffect } from 'react';
import { useLocation, useHistory } from 'react-router-dom';
import {
parseQueryString,
replaceParams,
encodeNonDefaultQueryString,
} from '../../../util/qs';
import { parseQueryString, updateQueryString } from '../../../util/qs';
import useWebsocket from '../../../util/useWebsocket';
import useThrottle from '../../../util/useThrottle';
@ -90,13 +86,10 @@ export default function useWsInventories(
) {
// We've deleted the last inventory on this page so we'll
// try to navigate back to the previous page
const newParams = encodeNonDefaultQueryString(
qsConfig,
replaceParams(params, {
page: params.page - 1,
})
);
history.push(`${location.pathname}?${newParams}`);
const qs = updateQueryString(qsConfig, location.search, {
page: params.page - 1,
});
history.push(`${location.pathname}?${qs}`);
return;
}

View File

@ -39,12 +39,11 @@ import getRowRangePageSize from './shared/jobOutputUtils';
import { getJobModel, isJobRunning } from '../../../util/jobs';
import useRequest, { useDismissableError } from '../../../util/useRequest';
import {
encodeNonDefaultQueryString,
parseQueryString,
mergeParams,
replaceParams,
removeParams,
getQSConfig,
updateQueryString,
} from '../../../util/qs';
import useIsMounted from '../../../util/useIsMounted';
@ -589,35 +588,43 @@ function JobOutput({ job, eventRelatedSearchableKeys, eventSearchableKeys }) {
};
const handleSearch = (key, value) => {
let params = parseQueryString(QS_CONFIG, location.search);
params = mergeParams(params, { [key]: value });
pushHistoryState(params);
const params = parseQueryString(QS_CONFIG, location.search);
const qs = updateQueryString(
QS_CONFIG,
location.search,
mergeParams(params, { [key]: value })
);
pushHistoryState(qs);
};
const handleReplaceSearch = (key, value) => {
const oldParams = parseQueryString(QS_CONFIG, location.search);
pushHistoryState(replaceParams(oldParams, { [key]: value }));
const qs = updateQueryString(QS_CONFIG, location.search, {
[key]: value,
});
pushHistoryState(qs);
};
const handleRemoveSearchTerm = (key, value) => {
let oldParams = parseQueryString(QS_CONFIG, location.search);
if (parseInt(value, 10)) {
oldParams = removeParams(QS_CONFIG, oldParams, {
[key]: parseInt(value, 10),
});
}
pushHistoryState(removeParams(QS_CONFIG, oldParams, { [key]: value }));
const oldParams = parseQueryString(QS_CONFIG, location.search);
const updatedParams = removeParams(QS_CONFIG, oldParams, {
[key]: value,
});
const qs = updateQueryString(QS_CONFIG, location.search, updatedParams);
pushHistoryState(qs);
};
const handleRemoveAllSearchTerms = () => {
const oldParams = parseQueryString(QS_CONFIG, location.search);
pushHistoryState(removeParams(QS_CONFIG, oldParams, { ...oldParams }));
Object.keys(oldParams).forEach(key => {
oldParams[key] = null;
});
const qs = updateQueryString(QS_CONFIG, location.search, oldParams);
pushHistoryState(qs);
};
const pushHistoryState = params => {
const pushHistoryState = qs => {
const { pathname } = history.location;
const encodedParams = encodeNonDefaultQueryString(QS_CONFIG, params);
history.push(encodedParams ? `${pathname}?${encodedParams}` : pathname);
history.push(qs ? `${pathname}?${qs}` : pathname);
};
const renderSearchComponent = () => (

View File

@ -113,44 +113,6 @@ function encodeValue(key, value) {
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
}
/**
* Convert query param object to url query string, adding namespace and
* removing defaults. Used to put into url bar after ui route
* @param {object} qs config object for namespacing params, filtering defaults
* @param {object} query param object
* @param {object} any non-namespaced params to append
* @return {string} url query string
*/
export const encodeNonDefaultQueryString = (
config,
params,
nonNamespacedParams = {}
) => {
if (!params) return '';
const paramsWithoutDefaults = removeParams({}, params, config.defaultParams);
return encodeQueryString({
...namespaceParams(config.namespace, paramsWithoutDefaults),
...nonNamespacedParams,
});
};
/**
* helper function to namespace params object
* @param {string} namespace to append to params
* @param {object} params object to append namespace to
* @return {object} params object with namespaced keys
*/
const namespaceParams = (namespace, params) => {
if (!namespace) return params;
const namespaced = {};
Object.keys(params).forEach(key => {
namespaced[`${namespace}.${key}`] = params[key];
});
return namespaced;
};
/**
* Removes params from the search string and returns the updated list of params
* @param {object} qs config object (used for getting defaults, current query params etc.)
@ -163,10 +125,19 @@ export function removeParams(config, oldParams, paramsToRemove) {
...config.defaultParams,
};
Object.keys(oldParams).forEach(key => {
const value = removeParam(oldParams[key], paramsToRemove[key]);
if (value !== null) {
updated[key] = value;
const valToRemove = paramsToRemove[key];
const isInt = config.integerFields?.includes(key);
const updatedValue = removeParam(
oldParams[key],
isInt ? parseInt(valToRemove, 10) : valToRemove
);
if (
updatedValue == null &&
Object.prototype.hasOwnProperty.call(updated, key)
) {
return;
}
updated[key] = updatedValue;
});
return updated;
}
@ -234,15 +205,42 @@ function dedupeArray(arr) {
}
/**
* Join old and new params together, replacing old values with new ones where
* necessary
* @param {object} namespaced params object of old params
* @param {object} namespaced params object of new params
* @return {object} joined namespaced params object
* Update namespaced param(s), returning a new query string. Leaves params
* from other namespaces unaltered
* @param {object} qs config object for namespacing params, filtering defaults
* @param {string} the url query string to update
* @param {object} namespaced params to add or update. use null to indicate
* a param that should be deleted from the query string
* @return {string} url query string
*/
export function replaceParams(oldParams, newParams) {
return {
...oldParams,
...newParams,
};
export function updateQueryString(config, queryString, newParams) {
const allParams = parseFullQueryString(queryString);
const { namespace = null, defaultParams = {} } = config || {};
Object.keys(newParams).forEach(key => {
const val = newParams[key];
const fullKey = namespace ? `${namespace}.${key}` : key;
if (val === null || val === defaultParams[key]) {
delete allParams[fullKey];
} else {
allParams[fullKey] = newParams[key];
}
});
return encodeQueryString(allParams);
}
function parseFullQueryString(queryString) {
const allParams = {};
queryString
.replace(/^\?/, '')
.split('&')
.map(s => s.split('='))
.forEach(([rawKey, rawValue]) => {
if (!rawKey) {
return;
}
const key = decodeURIComponent(rawKey);
const value = decodeURIComponent(rawValue);
allParams[key] = mergeParam(allParams[key], value);
});
return allParams;
}

View File

@ -1,13 +1,12 @@
import {
encodeQueryString,
encodeNonDefaultQueryString,
parseQueryString,
getQSConfig,
removeParams,
_stringToObject,
_addDefaultsToObject,
mergeParams,
replaceParams,
updateQueryString,
} from './qs';
describe('qs (qs.js)', () => {
@ -47,70 +46,6 @@ describe('qs (qs.js)', () => {
});
});
describe('encodeNonDefaultQueryString', () => {
const config = {
namespace: null,
defaultParams: { page: 1, page_size: 5, order_by: 'name' },
integerFields: ['page'],
};
test('should return the expected queryString', () => {
[
[null, ''],
[{}, ''],
[{ order_by: 'name', page: 1, page_size: 5 }, ''],
[{ order_by: '-name', page: 1, page_size: 5 }, 'order_by=-name'],
[
{ order_by: '-name', page: 3, page_size: 10 },
'order_by=-name&page=3&page_size=10',
],
[
{ order_by: '-name', page: 3, page_size: 10, foo: 'bar' },
'foo=bar&order_by=-name&page=3&page_size=10',
],
].forEach(([params, expectedQueryString]) => {
const actualQueryString = encodeNonDefaultQueryString(config, params);
expect(actualQueryString).toEqual(expectedQueryString);
});
});
test('should omit null values', () => {
const vals = {
order_by: 'foo',
page: null,
};
expect(encodeNonDefaultQueryString(config, vals)).toEqual('order_by=foo');
});
test('should namespace encoded params', () => {
const conf = {
namespace: 'item',
defaultParams: { page: 1 },
};
const params = {
page: 1,
foo: 'bar',
};
expect(encodeNonDefaultQueryString(conf, params)).toEqual('item.foo=bar');
});
test('should handle array values', () => {
const vals = {
foo: ['one', 'two'],
bar: ['alpha', 'beta'],
};
const conf = {
defaultParams: {
foo: ['one', 'two'],
},
};
expect(encodeNonDefaultQueryString(conf, vals)).toEqual(
'bar=alpha&bar=beta'
);
});
});
describe('getQSConfig', () => {
test('should get default QS config object', () => {
expect(getQSConfig('organization')).toEqual({
@ -340,6 +275,7 @@ describe('qs (qs.js)', () => {
baz: 'bar',
page: 3,
page_size: 15,
bag: null,
});
});
@ -428,6 +364,7 @@ describe('qs (qs.js)', () => {
baz: ['bar', 'bang'],
page: 3,
page_size: 15,
pat: null,
});
});
@ -442,6 +379,7 @@ describe('qs (qs.js)', () => {
expect(removeParams(config, oldParams, toRemove)).toEqual({
page: 3,
page_size: 15,
baz: null,
});
});
@ -456,6 +394,7 @@ describe('qs (qs.js)', () => {
expect(removeParams(config, oldParams, toRemove)).toEqual({
page: 1,
page_size: 15,
baz: null,
});
});
@ -525,6 +464,7 @@ describe('qs (qs.js)', () => {
baz: ['one', 'two', 'three'],
page: 3,
page_size: 15,
bag: null,
});
});
@ -545,6 +485,7 @@ describe('qs (qs.js)', () => {
baz: ['bar', 'bang'],
page: 3,
page_size: 15,
pat: null,
});
});
@ -558,10 +499,43 @@ describe('qs (qs.js)', () => {
const toRemove = { bag: 'boom' };
expect(removeParams(config, oldParams, toRemove)).toEqual({
baz: '',
bag: null,
page: 3,
page_size: 15,
});
});
test('should remove integer fields when given string value', () => {
const config = {
namespace: null,
defaultParams: { page: 1, page_size: 15 },
integerFields: ['id', 'page', 'page_size'],
};
const oldParams = { id: 199, foo: 'bar', page: 1, page_size: 15 };
const toRemove = { id: '199' };
expect(removeParams(config, oldParams, toRemove)).toEqual({
foo: 'bar',
id: null,
page: 1,
page_size: 15,
});
});
test('should remove integer fields from array when given string value', () => {
const config = {
namespace: null,
defaultParams: { page: 1, page_size: 15 },
integerFields: ['id', 'page', 'page_size'],
};
const oldParams = { id: [199, 200], foo: 'bar', page: 1, page_size: 15 };
const toRemove = { id: '199' };
expect(removeParams(config, oldParams, toRemove)).toEqual({
foo: 'bar',
id: 200,
page: 1,
page_size: 15,
});
});
});
describe('_stringToObject', () => {
@ -763,51 +737,94 @@ describe('qs (qs.js)', () => {
});
});
describe('replaceParams', () => {
it('should collect params into one object', () => {
const oldParams = { foo: 'one' };
const newParams = { bar: 'two' };
expect(replaceParams(oldParams, newParams)).toEqual({
foo: 'one',
bar: 'two',
});
});
describe('updateQueryString', () => {
const config = {
namespace: 'template',
defaultParams: { page: 1, page_size: 5, order_by: 'name' },
integerFields: ['page'],
};
it('should retain unaltered params', () => {
const oldParams = {
foo: 'one',
bar: 'baz',
};
const newParams = { foo: 'two' };
expect(replaceParams(oldParams, newParams)).toEqual({
foo: 'two',
bar: 'baz',
});
});
it('should override old values with new ones', () => {
const oldParams = {
foo: 'one',
bar: 'three',
};
test('should add param to empty query string', () => {
const newParams = {
foo: 'two',
baz: 'four',
page: 3,
};
expect(replaceParams(oldParams, newParams)).toEqual({
foo: 'two',
bar: 'three',
baz: 'four',
});
expect(updateQueryString(config, '', newParams)).toEqual(
'template.page=3'
);
});
it('should handle exact duplicates', () => {
const oldParams = { foo: 'one' };
const newParams = { foo: 'one', bar: 'two' };
expect(replaceParams(oldParams, newParams)).toEqual({
foo: 'one',
bar: 'two',
});
test('should update namespaced param', () => {
const query = 'template.name__icontains=workflow&template.page=2';
const newParams = {
page: 3,
};
expect(updateQueryString(config, query, newParams)).toEqual(
'template.name__icontains=workflow&template.page=3'
);
});
test('should add new namespaced param', () => {
const query = 'template.name__icontains=workflow&template.page=2';
const newParams = {
or__type: 'job_template',
};
expect(updateQueryString(config, query, newParams)).toEqual(
'template.name__icontains=workflow&template.or__type=job_template&template.page=2'
);
});
test('should maintain non-namespaced param', () => {
const query = 'foo=bar&template.page=2&template.name__icontains=workflow';
const newParams = {
page: 3,
};
expect(updateQueryString(config, query, newParams)).toEqual(
'foo=bar&template.name__icontains=workflow&template.page=3'
);
});
test('should omit null values', () => {
const query = 'template.name__icontains=workflow&template.page=2';
const newParams = {
page: 3,
name__icontains: null,
};
expect(updateQueryString(config, query, newParams)).toEqual(
'template.page=3'
);
});
test('should omit default values', () => {
const query = 'template.page=2';
const newParams = {
page: 3,
page_size: 5,
};
expect(updateQueryString(config, query, newParams)).toEqual(
'template.page=3'
);
});
test('should update non-namespaced param', () => {
const query =
'activity_stream.name__icontains=workflow&activity_stream.page=2';
const newParams = {
type: 'job',
};
expect(updateQueryString(null, query, newParams)).toEqual(
'activity_stream.name__icontains=workflow&activity_stream.page=2&type=job'
);
});
test('should not alter params of other namespaces', () => {
const query =
'template.name__icontains=workflow&template.page=2&credential.page=3';
const newParams = {
page: 3,
};
expect(updateQueryString(config, query, newParams)).toEqual(
'credential.page=3&template.name__icontains=workflow&template.page=3'
);
});
});
});

View File

@ -1,10 +1,6 @@
import { useEffect, useState, useCallback } from 'react';
import { useLocation, useHistory } from 'react-router-dom';
import {
parseQueryString,
replaceParams,
encodeNonDefaultQueryString,
} from './qs';
import { parseQueryString, updateQueryString } from './qs';
import useIsMounted from './useIsMounted';
/*
@ -111,13 +107,10 @@ export function useDeleteItems(
}
const params = parseQueryString(qsConfig, location.search);
if (params.page > 1 && allItemsSelected) {
const newParams = encodeNonDefaultQueryString(
qsConfig,
replaceParams(params, {
page: params.page - 1,
})
);
history.push(`${location.pathname}?${newParams}`);
const qs = updateQueryString(qsConfig, location.search, {
page: params.page - 1,
});
history.push(`${location.pathname}?${qs}`);
} else {
fetchItems();
}