mirror of
https://github.com/ansible/awx.git
synced 2026-01-16 12:20:45 -03:30
Merge pull request #5912 from keithjgrant/4239-pagination-on-delete
Adjust pagination when deleting final page of items Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
commit
1ae86ae752
@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useLocation, useHistory } from 'react-router-dom';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { CredentialsAPI } from '@api';
|
||||
@ -11,7 +11,12 @@ import PaginatedDataList, {
|
||||
ToolbarAddButton,
|
||||
ToolbarDeleteButton,
|
||||
} from '@components/PaginatedDataList';
|
||||
import { getQSConfig, parseQueryString } from '@util/qs';
|
||||
import {
|
||||
getQSConfig,
|
||||
parseQueryString,
|
||||
replaceParams,
|
||||
encodeNonDefaultQueryString,
|
||||
} from '@util/qs';
|
||||
import { CredentialListItem } from '.';
|
||||
|
||||
const QS_CONFIG = getQSConfig('credential', {
|
||||
@ -30,6 +35,7 @@ function CredentialList({ i18n }) {
|
||||
const [selected, setSelected] = useState([]);
|
||||
|
||||
const location = useLocation();
|
||||
const history = useHistory();
|
||||
|
||||
const loadCredentials = async ({ search }) => {
|
||||
const params = parseQueryString(QS_CONFIG, search);
|
||||
@ -92,20 +98,21 @@ function CredentialList({ i18n }) {
|
||||
setDeletionError(error);
|
||||
}
|
||||
|
||||
adjustPagination();
|
||||
setSelected([]);
|
||||
};
|
||||
|
||||
const adjustPagination = () => {
|
||||
const params = parseQueryString(QS_CONFIG, location.search);
|
||||
try {
|
||||
const {
|
||||
data: { count, results },
|
||||
} = await CredentialsAPI.read(params);
|
||||
|
||||
setCredentials(results);
|
||||
setCredentialCount(count);
|
||||
setSelected([]);
|
||||
} catch (error) {
|
||||
setContentError(error);
|
||||
if (params.page > 1 && selected.length === credentials.length) {
|
||||
const newParams = encodeNonDefaultQueryString(
|
||||
QS_CONFIG,
|
||||
replaceParams(params, { page: params.page - 1 })
|
||||
);
|
||||
history.push(`${location.pathname}?${newParams}`);
|
||||
} else {
|
||||
loadCredentials(location);
|
||||
}
|
||||
|
||||
setHasContentLoading(false);
|
||||
};
|
||||
|
||||
const canAdd =
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { useLocation, useRouteMatch } from 'react-router-dom';
|
||||
import { useLocation, useHistory, useRouteMatch } from 'react-router-dom';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { Card, PageSection } from '@patternfly/react-core';
|
||||
@ -13,7 +13,12 @@ import PaginatedDataList, {
|
||||
ToolbarAddButton,
|
||||
ToolbarDeleteButton,
|
||||
} from '@components/PaginatedDataList';
|
||||
import { getQSConfig, parseQueryString } from '@util/qs';
|
||||
import {
|
||||
getQSConfig,
|
||||
parseQueryString,
|
||||
replaceParams,
|
||||
encodeNonDefaultQueryString,
|
||||
} from '@util/qs';
|
||||
|
||||
import OrganizationListItem from './OrganizationListItem';
|
||||
|
||||
@ -25,6 +30,7 @@ const QS_CONFIG = getQSConfig('organization', {
|
||||
|
||||
function OrganizationsList({ i18n }) {
|
||||
const location = useLocation();
|
||||
const history = useHistory();
|
||||
const match = useRouteMatch();
|
||||
|
||||
const [selected, setSelected] = useState([]);
|
||||
@ -81,7 +87,21 @@ function OrganizationsList({ i18n }) {
|
||||
|
||||
const handleOrgDelete = async () => {
|
||||
await deleteOrganizations();
|
||||
await fetchOrganizations();
|
||||
await adjustPagination();
|
||||
setSelected([]);
|
||||
};
|
||||
|
||||
const adjustPagination = () => {
|
||||
const params = parseQueryString(QS_CONFIG, location.search);
|
||||
if (params.page > 1 && selected.length === organizations.length) {
|
||||
const newParams = encodeNonDefaultQueryString(
|
||||
QS_CONFIG,
|
||||
replaceParams(params, { page: params.page - 1 })
|
||||
);
|
||||
history.push(`${location.pathname}?${newParams}`);
|
||||
} else {
|
||||
fetchOrganizations();
|
||||
}
|
||||
};
|
||||
|
||||
const hasContentLoading = isDeleteLoading || isOrgsLoading;
|
||||
|
||||
@ -29,7 +29,7 @@ const QS_CONFIG = getQSConfig('template', {
|
||||
type: 'job_template,workflow_job_template',
|
||||
});
|
||||
|
||||
function TemplatesList({ i18n }) {
|
||||
function TemplateList({ i18n }) {
|
||||
const { id: projectId } = useParams();
|
||||
const { pathname, search } = useLocation();
|
||||
|
||||
@ -268,5 +268,4 @@ function TemplatesList({ i18n }) {
|
||||
);
|
||||
}
|
||||
|
||||
export { TemplatesList as _TemplatesList };
|
||||
export default withI18n()(TemplatesList);
|
||||
export default withI18n()(TemplateList);
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
} from '@api';
|
||||
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
||||
|
||||
import TemplatesList from './TemplateList';
|
||||
import TemplateList from './TemplateList';
|
||||
|
||||
jest.mock('@api');
|
||||
|
||||
@ -71,7 +71,7 @@ const mockTemplates = [
|
||||
},
|
||||
];
|
||||
|
||||
describe('<TemplatesList />', () => {
|
||||
describe('<TemplateList />', () => {
|
||||
beforeEach(() => {
|
||||
UnifiedJobTemplatesAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
@ -94,7 +94,7 @@ describe('<TemplatesList />', () => {
|
||||
test('initially renders successfully', async () => {
|
||||
await act(async () => {
|
||||
mountWithContexts(
|
||||
<TemplatesList
|
||||
<TemplateList
|
||||
match={{ path: '/templates', url: '/templates' }}
|
||||
location={{ search: '', pathname: '/templates' }}
|
||||
/>
|
||||
@ -105,7 +105,7 @@ describe('<TemplatesList />', () => {
|
||||
test('Templates are retrieved from the api and the components finishes loading', async () => {
|
||||
let wrapper;
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<TemplatesList />);
|
||||
wrapper = mountWithContexts(<TemplateList />);
|
||||
});
|
||||
expect(UnifiedJobTemplatesAPI.read).toBeCalled();
|
||||
await act(async () => {
|
||||
@ -115,7 +115,7 @@ describe('<TemplatesList />', () => {
|
||||
});
|
||||
|
||||
test('handleSelect is called when a template list item is selected', async () => {
|
||||
const wrapper = mountWithContexts(<TemplatesList />);
|
||||
const wrapper = mountWithContexts(<TemplateList />);
|
||||
await act(async () => {
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
});
|
||||
@ -143,7 +143,7 @@ describe('<TemplatesList />', () => {
|
||||
});
|
||||
|
||||
test('handleSelectAll is called when a template list item is selected', async () => {
|
||||
const wrapper = mountWithContexts(<TemplatesList />);
|
||||
const wrapper = mountWithContexts(<TemplateList />);
|
||||
await act(async () => {
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
});
|
||||
@ -158,7 +158,7 @@ describe('<TemplatesList />', () => {
|
||||
});
|
||||
|
||||
test('delete button is disabled if user does not have delete capabilities on a selected template', async () => {
|
||||
const wrapper = mountWithContexts(<TemplatesList />);
|
||||
const wrapper = mountWithContexts(<TemplateList />);
|
||||
await act(async () => {
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
});
|
||||
@ -217,7 +217,7 @@ describe('<TemplatesList />', () => {
|
||||
});
|
||||
|
||||
test('api is called to delete templates for each selected template.', async () => {
|
||||
const wrapper = mountWithContexts(<TemplatesList />);
|
||||
const wrapper = mountWithContexts(<TemplateList />);
|
||||
await act(async () => {
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
});
|
||||
@ -277,7 +277,7 @@ describe('<TemplatesList />', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
const wrapper = mountWithContexts(<TemplatesList />);
|
||||
const wrapper = mountWithContexts(<TemplateList />);
|
||||
await act(async () => {
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
});
|
||||
@ -315,7 +315,7 @@ describe('<TemplatesList />', () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<Route
|
||||
path="/projects/:id/job_templates"
|
||||
component={() => <TemplatesList />}
|
||||
component={() => <TemplateList />}
|
||||
/>,
|
||||
{
|
||||
context: {
|
||||
@ -2,12 +2,12 @@ import React from 'react';
|
||||
|
||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||
|
||||
import TemplatesListItem from './TemplateListItem';
|
||||
import TemplateListItem from './TemplateListItem';
|
||||
|
||||
describe('<TemplatesListItem />', () => {
|
||||
describe('<TemplateListItem />', () => {
|
||||
test('launch button shown to users with start capabilities', () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<TemplatesListItem
|
||||
<TemplateListItem
|
||||
isSelected={false}
|
||||
template={{
|
||||
id: 1,
|
||||
@ -26,7 +26,7 @@ describe('<TemplatesListItem />', () => {
|
||||
});
|
||||
test('launch button hidden from users without start capabilities', () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<TemplatesListItem
|
||||
<TemplateListItem
|
||||
isSelected={false}
|
||||
template={{
|
||||
id: 1,
|
||||
@ -45,7 +45,7 @@ describe('<TemplatesListItem />', () => {
|
||||
});
|
||||
test('edit button shown to users with edit capabilities', () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<TemplatesListItem
|
||||
<TemplateListItem
|
||||
isSelected={false}
|
||||
template={{
|
||||
id: 1,
|
||||
@ -64,7 +64,7 @@ describe('<TemplatesListItem />', () => {
|
||||
});
|
||||
test('edit button hidden from users without edit capabilities', () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<TemplatesListItem
|
||||
<TemplateListItem
|
||||
isSelected={false}
|
||||
template={{
|
||||
id: 1,
|
||||
@ -83,7 +83,7 @@ describe('<TemplatesListItem />', () => {
|
||||
});
|
||||
test('missing resource icon is shown.', () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<TemplatesListItem
|
||||
<TemplateListItem
|
||||
isSelected={false}
|
||||
template={{
|
||||
id: 1,
|
||||
@ -102,7 +102,7 @@ describe('<TemplatesListItem />', () => {
|
||||
});
|
||||
test('missing resource icon is not shown when there is a project and an inventory.', () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<TemplatesListItem
|
||||
<TemplateListItem
|
||||
isSelected={false}
|
||||
template={{
|
||||
id: 1,
|
||||
@ -123,7 +123,7 @@ describe('<TemplatesListItem />', () => {
|
||||
});
|
||||
test('missing resource icon is not shown when inventory is prompt_on_launch, and a project', () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<TemplatesListItem
|
||||
<TemplateListItem
|
||||
isSelected={false}
|
||||
template={{
|
||||
id: 1,
|
||||
@ -144,7 +144,7 @@ describe('<TemplatesListItem />', () => {
|
||||
});
|
||||
test('missing resource icon is not shown type is workflow_job_template', () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<TemplatesListItem
|
||||
<TemplateListItem
|
||||
isSelected={false}
|
||||
template={{
|
||||
id: 1,
|
||||
@ -90,7 +90,6 @@ export { addDefaultsToObject as _addDefaultsToObject };
|
||||
/**
|
||||
* Convert query param object to url query string
|
||||
* Used to encode params for interacting with the api
|
||||
* @param {object} qs config object for namespacing params, filtering defaults
|
||||
* @param {object} query param object
|
||||
* @return {string} url query string
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user