From 1ad7e663a1f54e7184d62451b52b7e4f25475e1e Mon Sep 17 00:00:00 2001 From: Keith Grant Date: Tue, 11 Feb 2020 10:11:17 -0800 Subject: [PATCH 1/5] fix org list page on delete --- .../OrganizationList/OrganizationList.jsx | 20 +++++++++++++++++-- awx/ui_next/src/util/qs.js | 1 - 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx index 3051861a05..2b278d2585 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, + removeParams, + 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,6 +87,16 @@ function OrganizationsList({ i18n }) { const handleOrgDelete = async () => { await deleteOrganizations(); + const params = parseQueryString(QS_CONFIG, location.search); + if (params.page > 1 && selected.length === organizations.length) { + const newParams = removeParams(QS_CONFIG, params, { page: params.page }); + history.push( + `${location.pathname}?${encodeNonDefaultQueryString( + QS_CONFIG, + newParams + )}` + ); + } await fetchOrganizations(); }; diff --git a/awx/ui_next/src/util/qs.js b/awx/ui_next/src/util/qs.js index c31e0061e9..8cdc9cc56d 100644 --- a/awx/ui_next/src/util/qs.js +++ b/awx/ui_next/src/util/qs.js @@ -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 */ From f61af39f0851cf93395d7b249d77ad94bf36c5cf Mon Sep 17 00:00:00 2001 From: Keith Grant Date: Tue, 11 Feb 2020 10:24:08 -0800 Subject: [PATCH 2/5] fix Template(s)List naming discrepancies --- .../src/screens/Template/TemplateList/TemplateList.jsx | 6 +++--- .../{TemplatesList.test.jsx => TemplateList.test.jsx} | 2 +- ...TemplatesListItem.test.jsx => TemplateListItem.test.jsx} | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename awx/ui_next/src/screens/Template/TemplateList/{TemplatesList.test.jsx => TemplateList.test.jsx} (99%) rename awx/ui_next/src/screens/Template/TemplateList/{TemplatesListItem.test.jsx => TemplateListItem.test.jsx} (99%) diff --git a/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx b/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx index 312c8e0fd4..ec46a1e779 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(); @@ -252,5 +252,5 @@ function TemplatesList({ i18n }) { ); } -export { TemplatesList as _TemplatesList }; -export default withI18n()(TemplatesList); +export { TemplateList as _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 99% 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..ab57868282 100644 --- a/awx/ui_next/src/screens/Template/TemplateList/TemplatesList.test.jsx +++ b/awx/ui_next/src/screens/Template/TemplateList/TemplateList.test.jsx @@ -71,7 +71,7 @@ const mockTemplates = [ }, ]; -describe('', () => { +describe('', () => { beforeEach(() => { UnifiedJobTemplatesAPI.read.mockResolvedValue({ data: { 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 99% 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..9392039508 100644 --- a/awx/ui_next/src/screens/Template/TemplateList/TemplatesListItem.test.jsx +++ b/awx/ui_next/src/screens/Template/TemplateList/TemplateListItem.test.jsx @@ -4,7 +4,7 @@ import { mountWithContexts } from '@testUtils/enzymeHelpers'; import TemplatesListItem from './TemplateListItem'; -describe('', () => { +describe('', () => { test('launch button shown to users with start capabilities', () => { const wrapper = mountWithContexts( Date: Tue, 11 Feb 2020 12:09:13 -0800 Subject: [PATCH 3/5] fix credential list page number after deleting --- .../CredentialList/CredentialList.jsx | 37 ++++++++++++------- .../OrganizationList/OrganizationList.jsx | 3 +- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx b/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx index 3374fd90bb..67ef629e31 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, + removeParams, + 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,23 @@ 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 = removeParams(QS_CONFIG, params, { page: params.page }); + history.push( + `${location.pathname}?${encodeNonDefaultQueryString( + QS_CONFIG, + 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 2b278d2585..e97d0b439e 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx @@ -96,8 +96,9 @@ function OrganizationsList({ i18n }) { newParams )}` ); + } else { + await fetchOrganizations(); } - await fetchOrganizations(); }; const hasContentLoading = isDeleteLoading || isOrgsLoading; From c33cc82d53c4eeb77a138cd111b1c37e0153e694 Mon Sep 17 00:00:00 2001 From: Keith Grant Date: Wed, 12 Feb 2020 08:53:34 -0800 Subject: [PATCH 4/5] go back one page when deleting all items off last page (orgs/creds) --- .../CredentialList/CredentialList.jsx | 12 +++++------- .../OrganizationList/OrganizationList.jsx | 18 ++++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx b/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx index 67ef629e31..c3ef0ebff5 100644 --- a/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx +++ b/awx/ui_next/src/screens/Credential/CredentialList/CredentialList.jsx @@ -14,7 +14,7 @@ import PaginatedDataList, { import { getQSConfig, parseQueryString, - removeParams, + replaceParams, encodeNonDefaultQueryString, } from '@util/qs'; import { CredentialListItem } from '.'; @@ -105,13 +105,11 @@ function CredentialList({ i18n }) { const adjustPagination = () => { const params = parseQueryString(QS_CONFIG, location.search); if (params.page > 1 && selected.length === credentials.length) { - const newParams = removeParams(QS_CONFIG, params, { page: params.page }); - history.push( - `${location.pathname}?${encodeNonDefaultQueryString( - QS_CONFIG, - newParams - )}` + const newParams = encodeNonDefaultQueryString( + QS_CONFIG, + replaceParams(params, { page: params.page - 1 }) ); + history.push(`${location.pathname}?${newParams}`); } else { loadCredentials(location); } diff --git a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx index e97d0b439e..f30d0cfd8c 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx @@ -16,7 +16,7 @@ import PaginatedDataList, { import { getQSConfig, parseQueryString, - removeParams, + replaceParams, encodeNonDefaultQueryString, } from '@util/qs'; @@ -87,17 +87,19 @@ function OrganizationsList({ i18n }) { const handleOrgDelete = async () => { await deleteOrganizations(); + await adjustPagination(); + }; + + const adjustPagination = () => { const params = parseQueryString(QS_CONFIG, location.search); if (params.page > 1 && selected.length === organizations.length) { - const newParams = removeParams(QS_CONFIG, params, { page: params.page }); - history.push( - `${location.pathname}?${encodeNonDefaultQueryString( - QS_CONFIG, - newParams - )}` + const newParams = encodeNonDefaultQueryString( + QS_CONFIG, + replaceParams(params, { page: params.page - 1 }) ); + history.push(`${location.pathname}?${newParams}`); } else { - await fetchOrganizations(); + fetchOrganizations(); } }; From 490b76b141a9573cb0de0e92e51ccd2cd5f9060e Mon Sep 17 00:00:00 2001 From: Keith Grant Date: Wed, 12 Feb 2020 13:10:14 -0800 Subject: [PATCH 5/5] fix TemplateList name in tests --- .../OrganizationList/OrganizationList.jsx | 1 + .../Template/TemplateList/TemplateList.jsx | 1 - .../TemplateList/TemplateList.test.jsx | 18 +++++++++--------- .../TemplateList/TemplateListItem.test.jsx | 18 +++++++++--------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx index f30d0cfd8c..02b176ffda 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx @@ -88,6 +88,7 @@ function OrganizationsList({ i18n }) { const handleOrgDelete = async () => { await deleteOrganizations(); await adjustPagination(); + setSelected([]); }; const adjustPagination = () => { diff --git a/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx b/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx index ec46a1e779..416df5d8be 100644 --- a/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx +++ b/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx @@ -252,5 +252,4 @@ function TemplateList({ i18n }) { ); } -export { TemplateList as _TemplatesList }; export default withI18n()(TemplateList); diff --git a/awx/ui_next/src/screens/Template/TemplateList/TemplateList.test.jsx b/awx/ui_next/src/screens/Template/TemplateList/TemplateList.test.jsx index ab57868282..d80a244b27 100644 --- a/awx/ui_next/src/screens/Template/TemplateList/TemplateList.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'); @@ -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/TemplateListItem.test.jsx b/awx/ui_next/src/screens/Template/TemplateList/TemplateListItem.test.jsx index 9392039508..8fa48e08d6 100644 --- a/awx/ui_next/src/screens/Template/TemplateList/TemplateListItem.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('', () => { 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( -