diff --git a/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx b/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx index 3374fd90bb..c3ef0ebff5 100644 --- a/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx +++ b/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx @@ -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 = diff --git a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx index 3051861a05..02b176ffda 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx @@ -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; diff --git a/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx b/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx index ce24889924..8937a2e364 100644 --- a/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx +++ b/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx @@ -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); diff --git a/awx/ui_next/src/screens/Template/TemplateList/TemplatesList.test.jsx b/awx/ui_next/src/screens/Template/TemplateList/TemplateList.test.jsx similarity index 94% rename from awx/ui_next/src/screens/Template/TemplateList/TemplatesList.test.jsx rename to awx/ui_next/src/screens/Template/TemplateList/TemplateList.test.jsx index 58eefbbe5b..d80a244b27 100644 --- a/awx/ui_next/src/screens/Template/TemplateList/TemplatesList.test.jsx +++ b/awx/ui_next/src/screens/Template/TemplateList/TemplateList.test.jsx @@ -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('', () => { +describe('', () => { beforeEach(() => { UnifiedJobTemplatesAPI.read.mockResolvedValue({ data: { @@ -94,7 +94,7 @@ describe('', () => { test('initially renders successfully', async () => { await act(async () => { mountWithContexts( - @@ -105,7 +105,7 @@ describe('', () => { test('Templates are retrieved from the api and the components finishes loading', async () => { let wrapper; await act(async () => { - wrapper = mountWithContexts(); + wrapper = mountWithContexts(); }); expect(UnifiedJobTemplatesAPI.read).toBeCalled(); await act(async () => { @@ -115,7 +115,7 @@ describe('', () => { }); test('handleSelect is called when a template list item is selected', async () => { - const wrapper = mountWithContexts(); + const wrapper = mountWithContexts(); await act(async () => { await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); }); @@ -143,7 +143,7 @@ describe('', () => { }); test('handleSelectAll is called when a template list item is selected', async () => { - const wrapper = mountWithContexts(); + const wrapper = mountWithContexts(); await act(async () => { await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); }); @@ -158,7 +158,7 @@ describe('', () => { }); test('delete button is disabled if user does not have delete capabilities on a selected template', async () => { - const wrapper = mountWithContexts(); + const wrapper = mountWithContexts(); await act(async () => { await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); }); @@ -217,7 +217,7 @@ describe('', () => { }); test('api is called to delete templates for each selected template.', async () => { - const wrapper = mountWithContexts(); + const wrapper = mountWithContexts(); await act(async () => { await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); }); @@ -277,7 +277,7 @@ describe('', () => { }, }) ); - const wrapper = mountWithContexts(); + const wrapper = mountWithContexts(); await act(async () => { await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); }); @@ -315,7 +315,7 @@ describe('', () => { const wrapper = mountWithContexts( } + component={() => } />, { context: { diff --git a/awx/ui_next/src/screens/Template/TemplateList/TemplatesListItem.test.jsx b/awx/ui_next/src/screens/Template/TemplateList/TemplateListItem.test.jsx similarity index 93% rename from awx/ui_next/src/screens/Template/TemplateList/TemplatesListItem.test.jsx rename to awx/ui_next/src/screens/Template/TemplateList/TemplateListItem.test.jsx index 8a340f1e62..8fa48e08d6 100644 --- a/awx/ui_next/src/screens/Template/TemplateList/TemplatesListItem.test.jsx +++ b/awx/ui_next/src/screens/Template/TemplateList/TemplateListItem.test.jsx @@ -2,12 +2,12 @@ import React from 'react'; import { mountWithContexts } from '@testUtils/enzymeHelpers'; -import TemplatesListItem from './TemplateListItem'; +import TemplateListItem from './TemplateListItem'; -describe('', () => { +describe('', () => { test('launch button shown to users with start capabilities', () => { const wrapper = mountWithContexts( - ', () => { }); test('launch button hidden from users without start capabilities', () => { const wrapper = mountWithContexts( - ', () => { }); test('edit button shown to users with edit capabilities', () => { const wrapper = mountWithContexts( - ', () => { }); test('edit button hidden from users without edit capabilities', () => { const wrapper = mountWithContexts( - ', () => { }); test('missing resource icon is shown.', () => { const wrapper = mountWithContexts( - ', () => { }); test('missing resource icon is not shown when there is a project and an inventory.', () => { const wrapper = mountWithContexts( - ', () => { }); test('missing resource icon is not shown when inventory is prompt_on_launch, and a project', () => { const wrapper = mountWithContexts( - ', () => { }); test('missing resource icon is not shown type is workflow_job_template', () => { const wrapper = mountWithContexts( -