From fdd560747d17fcc4963b99eba80bd2ef7a4ec6c6 Mon Sep 17 00:00:00 2001 From: Keith Grant Date: Mon, 6 Jun 2022 13:56:45 -0700 Subject: [PATCH] Persistent list filters (#12229) * add PersistentFilters component * add PersistentFilters test * add persistent filters to all list pages * update tests * clear sessionStorage on logout * fix persistent filter on wfjt detail; cleanup --- awx/ui/.eslintrc.json | 17 ++- .../PersistentFilters/PersistentFilters.js | 33 ++++++ .../PersistentFilters.test.js | 111 ++++++++++++++++++ .../src/components/PersistentFilters/index.js | 1 + .../src/components/RoutedTabs/RoutedTabs.js | 8 +- .../components/RoutedTabs/RoutedTabs.test.js | 7 +- .../src/components/Schedule/Schedule.test.js | 9 +- awx/ui/src/constants.js | 1 + awx/ui/src/contexts/Session.js | 1 + .../Application/Application/Application.js | 1 + .../src/screens/Application/Applications.js | 5 +- awx/ui/src/screens/Credential/Credential.js | 1 + awx/ui/src/screens/Credential/Credentials.js | 5 +- .../screens/CredentialType/CredentialType.js | 1 + .../screens/CredentialType/CredentialTypes.js | 6 +- .../ExecutionEnvironment.js | 1 + .../ExecutionEnvironments.js | 6 +- awx/ui/src/screens/Host/Host.js | 1 + awx/ui/src/screens/Host/Hosts.js | 7 +- .../screens/InstanceGroup/InstanceGroup.js | 1 + .../screens/InstanceGroup/InstanceGroups.js | 13 +- awx/ui/src/screens/Instances/Instance.js | 1 + awx/ui/src/screens/Instances/Instances.js | 5 +- awx/ui/src/screens/Inventory/Inventories.js | 5 +- awx/ui/src/screens/Inventory/Inventory.js | 1 + .../InventoryGroup/InventoryGroup.test.js | 14 ++- awx/ui/src/screens/Job/Job.js | 1 + awx/ui/src/screens/Job/Jobs.js | 5 +- .../screens/ManagementJob/ManagementJob.js | 1 + .../screens/ManagementJob/ManagementJobs.js | 7 +- .../NotificationTemplate.js | 1 + .../NotificationTemplates.js | 5 +- .../src/screens/Organization/Organization.js | 1 + .../src/screens/Organization/Organizations.js | 6 +- awx/ui/src/screens/Project/Project.js | 1 + awx/ui/src/screens/Project/Projects.js | 8 +- awx/ui/src/screens/Team/Team.js | 1 + awx/ui/src/screens/Team/Teams.js | 9 +- awx/ui/src/screens/Template/Template.js | 1 + awx/ui/src/screens/Template/Templates.js | 5 +- .../screens/Template/WorkflowJobTemplate.js | 1 + awx/ui/src/screens/User/User.js | 1 + awx/ui/src/screens/User/Users.js | 6 +- .../WorkflowApproval/WorkflowApproval.js | 1 + .../WorkflowApproval/WorkflowApprovals.js | 5 +- 45 files changed, 277 insertions(+), 51 deletions(-) create mode 100644 awx/ui/src/components/PersistentFilters/PersistentFilters.js create mode 100644 awx/ui/src/components/PersistentFilters/PersistentFilters.test.js create mode 100644 awx/ui/src/components/PersistentFilters/index.js diff --git a/awx/ui/.eslintrc.json b/awx/ui/.eslintrc.json index ca8352549e63..7cf4965cbd80 100644 --- a/awx/ui/.eslintrc.json +++ b/awx/ui/.eslintrc.json @@ -11,7 +11,7 @@ }, "babelOptions": { "presets": ["@babel/preset-react"] - } + } }, "plugins": ["react-hooks", "jsx-a11y", "i18next", "@babel"], "extends": [ @@ -96,9 +96,18 @@ "modifier", "data-cy", "fieldName", - "splitButtonVariant" + "splitButtonVariant", + "pageKey" + ], + "ignore": [ + "Ansible", + "Tower", + "JSON", + "YAML", + "lg", + "hh:mm AM/PM", + "Twilio" ], - "ignore": ["Ansible", "Tower", "JSON", "YAML", "lg", "hh:mm AM/PM", "Twilio"], "ignoreComponent": [ "AboutModal", "code", @@ -139,7 +148,7 @@ "object-curly-newline": "off", "no-trailing-spaces": ["error"], "no-unused-expressions": ["error", { "allowShortCircuit": true }], - "react/jsx-props-no-spreading":["off"], + "react/jsx-props-no-spreading": ["off"], "react/prefer-stateless-function": "off", "react/prop-types": "off", "react/sort-comp": ["error", {}], diff --git a/awx/ui/src/components/PersistentFilters/PersistentFilters.js b/awx/ui/src/components/PersistentFilters/PersistentFilters.js new file mode 100644 index 000000000000..2fb754ac5a54 --- /dev/null +++ b/awx/ui/src/components/PersistentFilters/PersistentFilters.js @@ -0,0 +1,33 @@ +import { useEffect } from 'react'; +import { useLocation, useHistory } from 'react-router'; +import { PERSISTENT_FILTER_KEY } from '../../constants'; + +export default function PersistentFilters({ pageKey, children }) { + const location = useLocation(); + const history = useHistory(); + + useEffect(() => { + if (!location.search.includes('restoreFilters=true')) { + return; + } + + const filterString = sessionStorage.getItem(PERSISTENT_FILTER_KEY); + const filter = filterString ? JSON.parse(filterString) : { qs: '' }; + + if (filter.pageKey === pageKey) { + history.replace(`${location.pathname}${filter.qs}`); + } else { + history.replace(location.pathname); + } + }, [history, location, pageKey]); + + useEffect(() => { + const filter = { + pageKey, + qs: location.search, + }; + sessionStorage.setItem(PERSISTENT_FILTER_KEY, JSON.stringify(filter)); + }, [location.search, pageKey]); + + return children; +} diff --git a/awx/ui/src/components/PersistentFilters/PersistentFilters.test.js b/awx/ui/src/components/PersistentFilters/PersistentFilters.test.js new file mode 100644 index 000000000000..bb13f56c0f7e --- /dev/null +++ b/awx/ui/src/components/PersistentFilters/PersistentFilters.test.js @@ -0,0 +1,111 @@ +import React from 'react'; +import { render, screen, waitFor } from '@testing-library/react'; +import { Router, Route } from 'react-router-dom'; +import { createMemoryHistory } from 'history'; +import PersistentFilters from './PersistentFilters'; + +const KEY = 'awx-persistent-filter'; + +describe('PersistentFilters', () => { + test('should initialize filter in sessionStorage', () => { + expect(sessionStorage.getItem(KEY)).toEqual(null); + const history = createMemoryHistory({ + initialEntries: ['/templates'], + }); + render( + + test + + ); + + expect(JSON.parse(sessionStorage.getItem(KEY))).toEqual({ + pageKey: 'templates', + qs: '', + }); + }); + + test('should restore filters from sessionStorage', () => { + expect( + sessionStorage.setItem( + KEY, + JSON.stringify({ + pageKey: 'templates', + qs: '?page=2&name=foo', + }) + ) + ); + const history = createMemoryHistory({ + initialEntries: ['/templates?restoreFilters=true'], + }); + render( + + test + + ); + + expect(history.location.search).toEqual('?page=2&name=foo'); + }); + + test('should not restore filters without restoreFilters query param', () => { + expect( + sessionStorage.setItem( + KEY, + JSON.stringify({ + pageKey: 'templates', + qs: '?page=2&name=foo', + }) + ) + ); + const history = createMemoryHistory({ + initialEntries: ['/templates'], + }); + render( + + test + + ); + + expect(history.location.search).toEqual(''); + }); + + test("should not restore filters if page key doesn't match", () => { + expect( + sessionStorage.setItem( + KEY, + JSON.stringify({ + pageKey: 'projects', + qs: '?page=2&name=foo', + }) + ) + ); + const history = createMemoryHistory({ + initialEntries: ['/templates?restoreFilters=true'], + }); + render( + + test + + ); + + expect(history.location.search).toEqual(''); + }); + + test('should update stored filters when qs changes', async () => { + const history = createMemoryHistory({ + initialEntries: ['/templates'], + }); + render( + + test + + ); + + history.push('/templates?page=3'); + await waitFor(() => true); + + expect(JSON.parse(sessionStorage.getItem(KEY))).toEqual({ + pageKey: 'templates', + qs: '?page=3', + }); + }); +}); diff --git a/awx/ui/src/components/PersistentFilters/index.js b/awx/ui/src/components/PersistentFilters/index.js new file mode 100644 index 000000000000..ee9b84f9db29 --- /dev/null +++ b/awx/ui/src/components/PersistentFilters/index.js @@ -0,0 +1 @@ +export { default } from './PersistentFilters'; diff --git a/awx/ui/src/components/RoutedTabs/RoutedTabs.js b/awx/ui/src/components/RoutedTabs/RoutedTabs.js index 9cc6ab920dbe..f68d6d733b80 100644 --- a/awx/ui/src/components/RoutedTabs/RoutedTabs.js +++ b/awx/ui/src/components/RoutedTabs/RoutedTabs.js @@ -24,7 +24,11 @@ function RoutedTabs({ tabsArray }) { const handleTabSelect = (event, eventKey) => { const match = tabsArray.find((tab) => tab.id === eventKey); if (match) { - history.push(match.link); + event.preventDefault(); + const link = match.isBackButton + ? `${match.link}?restoreFilters=true` + : match.link; + history.push(link); } }; @@ -39,7 +43,7 @@ function RoutedTabs({ tabsArray }) { aria-label={typeof tab.name === 'string' ? tab.name : null} eventKey={tab.id} key={tab.id} - link={tab.link} + href={`#${tab.link}`} title={{tab.name}} aria-controls="" ouiaId={`${tab.name}-tab`} diff --git a/awx/ui/src/components/RoutedTabs/RoutedTabs.test.js b/awx/ui/src/components/RoutedTabs/RoutedTabs.test.js index 4e3156060360..475723b00080 100644 --- a/awx/ui/src/components/RoutedTabs/RoutedTabs.test.js +++ b/awx/ui/src/components/RoutedTabs/RoutedTabs.test.js @@ -37,7 +37,12 @@ describe('', () => { }); test('should update history when new tab selected', async () => { - wrapper.find('Tabs').invoke('onSelect')({}, 2); + wrapper.find('Tabs').invoke('onSelect')( + { + preventDefault: () => {}, + }, + 2 + ); wrapper.update(); expect(history.location.pathname).toEqual('/organizations/19/access'); diff --git a/awx/ui/src/components/Schedule/Schedule.test.js b/awx/ui/src/components/Schedule/Schedule.test.js index 21c1636a8daa..e9432c87257b 100644 --- a/awx/ui/src/components/Schedule/Schedule.test.js +++ b/awx/ui/src/components/Schedule/Schedule.test.js @@ -119,9 +119,10 @@ describe('', () => { }); test('expect all tabs to exist, including Back to Schedules', async () => { - expect( - wrapper.find('button[link="/templates/job_template/1/schedules"]').length - ).toBe(1); - expect(wrapper.find('button[aria-label="Details"]').length).toBe(1); + const routedTabs = wrapper.find('RoutedTabs'); + const tabs = routedTabs.prop('tabsArray'); + + expect(tabs[0].link).toEqual('/templates/job_template/1/schedules'); + expect(tabs[1].name).toEqual('Details'); }); }); diff --git a/awx/ui/src/constants.js b/awx/ui/src/constants.js index 092bdcd5b0aa..303061e3ea40 100644 --- a/awx/ui/src/constants.js +++ b/awx/ui/src/constants.js @@ -10,3 +10,4 @@ export const JOB_TYPE_URL_SEGMENTS = { export const SESSION_TIMEOUT_KEY = 'awx-session-timeout'; export const SESSION_REDIRECT_URL = 'awx-redirect-url'; +export const PERSISTENT_FILTER_KEY = 'awx-persistent-filter'; diff --git a/awx/ui/src/contexts/Session.js b/awx/ui/src/contexts/Session.js index df01ae6f07e5..3a33fb3f6294 100644 --- a/awx/ui/src/contexts/Session.js +++ b/awx/ui/src/contexts/Session.js @@ -102,6 +102,7 @@ function SessionProvider({ children }) { if (!isSessionExpired.current) { setAuthRedirectTo('/logout'); } + sessionStorage.clear(); await RootAPI.logout(); setSessionTimeout(0); setSessionCountdown(0); diff --git a/awx/ui/src/screens/Application/Application/Application.js b/awx/ui/src/screens/Application/Application/Application.js index bd7da2d72cda..e6d3a04ed056 100644 --- a/awx/ui/src/screens/Application/Application/Application.js +++ b/awx/ui/src/screens/Application/Application/Application.js @@ -74,6 +74,7 @@ function Application({ setBreadcrumb }) { ), link: '/applications', id: 0, + isBackButton: true, }, { name: t`Details`, link: `/applications/${id}/details`, id: 1 }, { name: t`Tokens`, link: `/applications/${id}/tokens`, id: 2 }, diff --git a/awx/ui/src/screens/Application/Applications.js b/awx/ui/src/screens/Application/Applications.js index 5ef267f9e71c..37b60fa7ff75 100644 --- a/awx/ui/src/screens/Application/Applications.js +++ b/awx/ui/src/screens/Application/Applications.js @@ -11,6 +11,7 @@ import { } from '@patternfly/react-core'; import ScreenHeader from 'components/ScreenHeader'; import { Detail, DetailList } from 'components/DetailList'; +import PersistentFilters from 'components/PersistentFilters'; import ApplicationsList from './ApplicationsList'; import ApplicationAdd from './ApplicationAdd'; import Application from './Application'; @@ -56,7 +57,9 @@ function Applications() { - + + + {applicationModalSource && ( diff --git a/awx/ui/src/screens/Credential/Credential.js b/awx/ui/src/screens/Credential/Credential.js index ba580fd76e3e..45c507e23e7f 100644 --- a/awx/ui/src/screens/Credential/Credential.js +++ b/awx/ui/src/screens/Credential/Credential.js @@ -67,6 +67,7 @@ function Credential({ setBreadcrumb }) { ), link: `/credentials`, id: 99, + isBackButton: true, }, { name: t`Details`, link: `/credentials/${id}/details`, id: 0 }, { diff --git a/awx/ui/src/screens/Credential/Credentials.js b/awx/ui/src/screens/Credential/Credentials.js index cb213b553495..f7f38e817912 100644 --- a/awx/ui/src/screens/Credential/Credentials.js +++ b/awx/ui/src/screens/Credential/Credentials.js @@ -4,6 +4,7 @@ import { Route, Switch } from 'react-router-dom'; import { t } from '@lingui/macro'; import { Config } from 'contexts/Config'; import ScreenHeader from 'components/ScreenHeader'; +import PersistentFilters from 'components/PersistentFilters'; import Credential from './Credential'; import CredentialAdd from './CredentialAdd'; import { CredentialList } from './CredentialList'; @@ -44,7 +45,9 @@ function Credentials() { - + + + diff --git a/awx/ui/src/screens/CredentialType/CredentialType.js b/awx/ui/src/screens/CredentialType/CredentialType.js index 07942674f959..452662909768 100644 --- a/awx/ui/src/screens/CredentialType/CredentialType.js +++ b/awx/ui/src/screens/CredentialType/CredentialType.js @@ -57,6 +57,7 @@ function CredentialType({ setBreadcrumb }) { ), link: '/credential_types', id: 99, + isBackButton: true, }, { name: t`Details`, diff --git a/awx/ui/src/screens/CredentialType/CredentialTypes.js b/awx/ui/src/screens/CredentialType/CredentialTypes.js index f99ff1ae35f3..6178091e833a 100644 --- a/awx/ui/src/screens/CredentialType/CredentialTypes.js +++ b/awx/ui/src/screens/CredentialType/CredentialTypes.js @@ -2,7 +2,7 @@ import React, { useState, useCallback } from 'react'; import { t } from '@lingui/macro'; import { Route, Switch } from 'react-router-dom'; - +import PersistentFilters from 'components/PersistentFilters'; import ScreenHeader from 'components/ScreenHeader'; import CredentialTypeAdd from './CredentialTypeAdd'; import CredentialTypeList from './CredentialTypeList'; @@ -40,7 +40,9 @@ function CredentialTypes() { - + + + diff --git a/awx/ui/src/screens/ExecutionEnvironment/ExecutionEnvironment.js b/awx/ui/src/screens/ExecutionEnvironment/ExecutionEnvironment.js index 846bd3143369..8896025bb65c 100644 --- a/awx/ui/src/screens/ExecutionEnvironment/ExecutionEnvironment.js +++ b/awx/ui/src/screens/ExecutionEnvironment/ExecutionEnvironment.js @@ -59,6 +59,7 @@ function ExecutionEnvironment({ setBreadcrumb }) { ), link: '/execution_environments', id: 99, + isBackButton: true, }, { name: t`Details`, diff --git a/awx/ui/src/screens/ExecutionEnvironment/ExecutionEnvironments.js b/awx/ui/src/screens/ExecutionEnvironment/ExecutionEnvironments.js index 952eef00fd15..e2a9a26cf60b 100644 --- a/awx/ui/src/screens/ExecutionEnvironment/ExecutionEnvironments.js +++ b/awx/ui/src/screens/ExecutionEnvironment/ExecutionEnvironments.js @@ -2,7 +2,7 @@ import React, { useState, useCallback } from 'react'; import { t } from '@lingui/macro'; import { Route, Switch } from 'react-router-dom'; - +import PersistentFilters from 'components/PersistentFilters'; import ScreenHeader from 'components/ScreenHeader/ScreenHeader'; import ExecutionEnvironment from './ExecutionEnvironment'; import ExecutionEnvironmentAdd from './ExecutionEnvironmentAdd'; @@ -40,7 +40,9 @@ function ExecutionEnvironments() { - + + + diff --git a/awx/ui/src/screens/Host/Host.js b/awx/ui/src/screens/Host/Host.js index 728eac8c102b..d62e34c69395 100644 --- a/awx/ui/src/screens/Host/Host.js +++ b/awx/ui/src/screens/Host/Host.js @@ -52,6 +52,7 @@ function Host({ setBreadcrumb }) { ), link: `/hosts`, id: 99, + isBackButton: true, }, { name: t`Details`, diff --git a/awx/ui/src/screens/Host/Hosts.js b/awx/ui/src/screens/Host/Hosts.js index d104b3ec689c..522acd124e02 100644 --- a/awx/ui/src/screens/Host/Hosts.js +++ b/awx/ui/src/screens/Host/Hosts.js @@ -1,11 +1,10 @@ import React, { useState, useCallback } from 'react'; import { Route, Switch } from 'react-router-dom'; - import { t } from '@lingui/macro'; import { Config } from 'contexts/Config'; import ScreenHeader from 'components/ScreenHeader/ScreenHeader'; - +import PersistentFilters from 'components/PersistentFilters'; import HostList from './HostList'; import HostAdd from './HostAdd'; import Host from './Host'; @@ -47,7 +46,9 @@ function Hosts() { - + + + diff --git a/awx/ui/src/screens/InstanceGroup/InstanceGroup.js b/awx/ui/src/screens/InstanceGroup/InstanceGroup.js index 510e3838f523..b4d0d404c5c6 100644 --- a/awx/ui/src/screens/InstanceGroup/InstanceGroup.js +++ b/awx/ui/src/screens/InstanceGroup/InstanceGroup.js @@ -63,6 +63,7 @@ function InstanceGroup({ setBreadcrumb }) { ), link: '/instance_groups', id: 99, + isBackButton: true, }, { name: t`Details`, diff --git a/awx/ui/src/screens/InstanceGroup/InstanceGroups.js b/awx/ui/src/screens/InstanceGroup/InstanceGroups.js index d1ff418e1256..f5a4783c3321 100644 --- a/awx/ui/src/screens/InstanceGroup/InstanceGroups.js +++ b/awx/ui/src/screens/InstanceGroup/InstanceGroups.js @@ -9,6 +9,7 @@ import useRequest from 'hooks/useRequest'; import { SettingsAPI } from 'api'; import ScreenHeader from 'components/ScreenHeader'; import ContentLoading from 'components/ContentLoading'; +import PersistentFilters from 'components/PersistentFilters'; import InstanceGroupAdd from './InstanceGroupAdd'; import InstanceGroupList from './InstanceGroupList'; import InstanceGroup from './InstanceGroup'; @@ -103,11 +104,13 @@ function InstanceGroups() { - + + + )} diff --git a/awx/ui/src/screens/Instances/Instance.js b/awx/ui/src/screens/Instances/Instance.js index 585315d9464c..8efe4b55f6b3 100644 --- a/awx/ui/src/screens/Instances/Instance.js +++ b/awx/ui/src/screens/Instances/Instance.js @@ -20,6 +20,7 @@ function Instance({ setBreadcrumb }) { ), link: `/instances`, id: 99, + isBackButton: true, }, { name: t`Details`, link: `${match.url}/details`, id: 0 }, ]; diff --git a/awx/ui/src/screens/Instances/Instances.js b/awx/ui/src/screens/Instances/Instances.js index 271c9da420fa..a230fb9a6740 100644 --- a/awx/ui/src/screens/Instances/Instances.js +++ b/awx/ui/src/screens/Instances/Instances.js @@ -3,6 +3,7 @@ import React, { useCallback, useState } from 'react'; import { t } from '@lingui/macro'; import { Route, Switch } from 'react-router-dom'; import ScreenHeader from 'components/ScreenHeader'; +import PersistentFilters from 'components/PersistentFilters'; import { InstanceList } from './InstanceList'; import Instance from './Instance'; @@ -30,7 +31,9 @@ function Instances() { - + + + diff --git a/awx/ui/src/screens/Inventory/Inventories.js b/awx/ui/src/screens/Inventory/Inventories.js index cb4e51b712d0..49bf4d771017 100644 --- a/awx/ui/src/screens/Inventory/Inventories.js +++ b/awx/ui/src/screens/Inventory/Inventories.js @@ -5,6 +5,7 @@ import { Route, Switch } from 'react-router-dom'; import { Config } from 'contexts/Config'; import ScreenHeader from 'components/ScreenHeader/ScreenHeader'; +import PersistentFilters from 'components/PersistentFilters'; import { InventoryList } from './InventoryList'; import Inventory from './Inventory'; import SmartInventory from './SmartInventory'; @@ -119,7 +120,9 @@ function Inventories() { - + + + diff --git a/awx/ui/src/screens/Inventory/Inventory.js b/awx/ui/src/screens/Inventory/Inventory.js index c0bf58c39b57..d26c0fc5ce1a 100644 --- a/awx/ui/src/screens/Inventory/Inventory.js +++ b/awx/ui/src/screens/Inventory/Inventory.js @@ -59,6 +59,7 @@ function Inventory({ setBreadcrumb }) { ), link: `/inventories`, id: 99, + isBackButton: true, }, { name: t`Details`, link: `${match.url}/details`, id: 0 }, { name: t`Access`, link: `${match.url}/access`, id: 1 }, diff --git a/awx/ui/src/screens/Inventory/InventoryGroup/InventoryGroup.test.js b/awx/ui/src/screens/Inventory/InventoryGroup/InventoryGroup.test.js index 6a671420dbd4..ee468bf7d4d3 100644 --- a/awx/ui/src/screens/Inventory/InventoryGroup/InventoryGroup.test.js +++ b/awx/ui/src/screens/Inventory/InventoryGroup/InventoryGroup.test.js @@ -59,12 +59,14 @@ describe('', () => { }); test('expect all tabs to exist, including Back to Groups', async () => { - expect( - wrapper.find('button[link="/inventories/inventory/1/groups"]').length - ).toBe(1); - expect(wrapper.find('button[aria-label="Details"]').length).toBe(1); - expect(wrapper.find('button[aria-label="Related Groups"]').length).toBe(1); - expect(wrapper.find('button[aria-label="Hosts"]').length).toBe(1); + const routedTabs = wrapper.find('RoutedTabs'); + expect(routedTabs).toHaveLength(1); + + const tabs = routedTabs.prop('tabsArray'); + expect(tabs[0].link).toEqual('/inventories/inventory/1/groups'); + expect(tabs[1].name).toEqual('Details'); + expect(tabs[2].name).toEqual('Related Groups'); + expect(tabs[3].name).toEqual('Hosts'); }); test('should show content error when user attempts to navigate to erroneous route', async () => { diff --git a/awx/ui/src/screens/Job/Job.js b/awx/ui/src/screens/Job/Job.js index ef0986bd1d27..5ac0a3c34774 100644 --- a/awx/ui/src/screens/Job/Job.js +++ b/awx/ui/src/screens/Job/Job.js @@ -111,6 +111,7 @@ function Job({ setBreadcrumb }) { ), link: `/jobs`, + isBackButton: true, id: 99, }, { name: t`Details`, link: `${match.url}/details`, id: 0 }, diff --git a/awx/ui/src/screens/Job/Jobs.js b/awx/ui/src/screens/Job/Jobs.js index 1b28bf2eebc6..aed47b5cd12d 100644 --- a/awx/ui/src/screens/Job/Jobs.js +++ b/awx/ui/src/screens/Job/Jobs.js @@ -5,6 +5,7 @@ import { t } from '@lingui/macro'; import { PageSection } from '@patternfly/react-core'; import ScreenHeader from 'components/ScreenHeader/ScreenHeader'; import JobList from 'components/JobList'; +import PersistentFilters from 'components/PersistentFilters'; import Job from './Job'; import JobTypeRedirect from './JobTypeRedirect'; import { JOB_TYPE_URL_SEGMENTS } from '../../constants'; @@ -41,7 +42,9 @@ function Jobs() { - + + + diff --git a/awx/ui/src/screens/ManagementJob/ManagementJob.js b/awx/ui/src/screens/ManagementJob/ManagementJob.js index 3673786f037d..2cb4909668a6 100644 --- a/awx/ui/src/screens/ManagementJob/ManagementJob.js +++ b/awx/ui/src/screens/ManagementJob/ManagementJob.js @@ -98,6 +98,7 @@ function ManagementJob({ setBreadcrumb }) { {t`Back to management jobs`} ), + isBackButton: true, }, ]; diff --git a/awx/ui/src/screens/ManagementJob/ManagementJobs.js b/awx/ui/src/screens/ManagementJob/ManagementJobs.js index d4dafa1a0106..6d0d13c3fb85 100644 --- a/awx/ui/src/screens/ManagementJob/ManagementJobs.js +++ b/awx/ui/src/screens/ManagementJob/ManagementJobs.js @@ -1,9 +1,8 @@ import React, { useState, useCallback } from 'react'; - import { t } from '@lingui/macro'; import { Route, Switch } from 'react-router-dom'; - import ScreenHeader from 'components/ScreenHeader'; +import PersistentFilters from 'components/PersistentFilters'; import ManagementJob from './ManagementJob'; import ManagementJobList from './ManagementJobList'; @@ -37,7 +36,9 @@ function ManagementJobs() { - + + + diff --git a/awx/ui/src/screens/NotificationTemplate/NotificationTemplate.js b/awx/ui/src/screens/NotificationTemplate/NotificationTemplate.js index 1e2eab210c58..90f20f9b85b2 100644 --- a/awx/ui/src/screens/NotificationTemplate/NotificationTemplate.js +++ b/awx/ui/src/screens/NotificationTemplate/NotificationTemplate.js @@ -78,6 +78,7 @@ function NotificationTemplate({ setBreadcrumb }) { ), link: `/notification_templates`, id: 99, + isBackButton: true, }, { name: t`Details`, diff --git a/awx/ui/src/screens/NotificationTemplate/NotificationTemplates.js b/awx/ui/src/screens/NotificationTemplate/NotificationTemplates.js index 99a605e09d45..37961f4bc269 100644 --- a/awx/ui/src/screens/NotificationTemplate/NotificationTemplates.js +++ b/awx/ui/src/screens/NotificationTemplate/NotificationTemplates.js @@ -3,6 +3,7 @@ import { Route, Switch, useRouteMatch } from 'react-router-dom'; import { t } from '@lingui/macro'; import ScreenHeader from 'components/ScreenHeader/ScreenHeader'; +import PersistentFilters from 'components/PersistentFilters'; import NotificationTemplateList from './NotificationTemplateList'; import NotificationTemplateAdd from './NotificationTemplateAdd'; import NotificationTemplate from './NotificationTemplate'; @@ -39,7 +40,9 @@ function NotificationTemplates() { - + + + diff --git a/awx/ui/src/screens/Organization/Organization.js b/awx/ui/src/screens/Organization/Organization.js index 255f1ec05e58..f47c32b2f552 100644 --- a/awx/ui/src/screens/Organization/Organization.js +++ b/awx/ui/src/screens/Organization/Organization.js @@ -118,6 +118,7 @@ function Organization({ setBreadcrumb, me }) { ), link: `/organizations`, id: 99, + isBackButton: true, }, { name: t`Details`, link: `${match.url}/details`, id: 0 }, { name: t`Access`, link: `${match.url}/access`, id: 1 }, diff --git a/awx/ui/src/screens/Organization/Organizations.js b/awx/ui/src/screens/Organization/Organizations.js index 8296105724bb..ce30bfac1cc7 100644 --- a/awx/ui/src/screens/Organization/Organizations.js +++ b/awx/ui/src/screens/Organization/Organizations.js @@ -5,7 +5,7 @@ import { t } from '@lingui/macro'; import { Config } from 'contexts/Config'; import ScreenHeader from 'components/ScreenHeader/ScreenHeader'; - +import PersistentFilters from 'components/PersistentFilters'; import OrganizationsList from './OrganizationList/OrganizationList'; import OrganizationAdd from './OrganizationAdd/OrganizationAdd'; import Organization from './Organization'; @@ -54,7 +54,9 @@ function Organizations() { - + + + diff --git a/awx/ui/src/screens/Project/Project.js b/awx/ui/src/screens/Project/Project.js index a3156ca15114..cc185a8f7492 100644 --- a/awx/ui/src/screens/Project/Project.js +++ b/awx/ui/src/screens/Project/Project.js @@ -99,6 +99,7 @@ function Project({ setBreadcrumb }) { ), link: `/projects`, id: 99, + isBackButton: true, }, { name: t`Details`, link: `/projects/${id}/details` }, { name: t`Access`, link: `/projects/${id}/access` }, diff --git a/awx/ui/src/screens/Project/Projects.js b/awx/ui/src/screens/Project/Projects.js index 4c80e7ecaf59..919da3d9f5d1 100644 --- a/awx/ui/src/screens/Project/Projects.js +++ b/awx/ui/src/screens/Project/Projects.js @@ -1,10 +1,8 @@ import React, { useState, useCallback } from 'react'; import { Route, withRouter, Switch } from 'react-router-dom'; - import { t } from '@lingui/macro'; - import ScreenHeader from 'components/ScreenHeader/ScreenHeader'; - +import PersistentFilters from 'components/PersistentFilters'; import ProjectsList from './ProjectList/ProjectList'; import ProjectAdd from './ProjectAdd/ProjectAdd'; import Project from './Project'; @@ -49,7 +47,9 @@ function Projects() { - + + + diff --git a/awx/ui/src/screens/Team/Team.js b/awx/ui/src/screens/Team/Team.js index 45caf2b45ebd..157aec583bf7 100644 --- a/awx/ui/src/screens/Team/Team.js +++ b/awx/ui/src/screens/Team/Team.js @@ -52,6 +52,7 @@ function Team({ setBreadcrumb }) { ), link: `/teams`, id: 99, + isBackButton: true, }, { name: t`Details`, link: `/teams/${id}/details`, id: 0 }, { name: t`Access`, link: `/teams/${id}/access`, id: 1 }, diff --git a/awx/ui/src/screens/Team/Teams.js b/awx/ui/src/screens/Team/Teams.js index 2302836e7e24..e50a363317a8 100644 --- a/awx/ui/src/screens/Team/Teams.js +++ b/awx/ui/src/screens/Team/Teams.js @@ -5,6 +5,7 @@ import { t } from '@lingui/macro'; import { Config } from 'contexts/Config'; import ScreenHeader from 'components/ScreenHeader'; +import PersistentFilters from 'components/PersistentFilters'; import TeamList from './TeamList'; import TeamAdd from './TeamAdd'; import Team from './Team'; @@ -43,9 +44,11 @@ function Teams() { - - {({ me }) => } - + + + {({ me }) => } + + diff --git a/awx/ui/src/screens/Template/Template.js b/awx/ui/src/screens/Template/Template.js index e084d1f8062b..2c7801ed6dec 100644 --- a/awx/ui/src/screens/Template/Template.js +++ b/awx/ui/src/screens/Template/Template.js @@ -129,6 +129,7 @@ function Template({ setBreadcrumb }) { ), link: `/templates`, + isBackButton: true, id: 99, }, { name: t`Details`, link: `${match.url}/details` }, diff --git a/awx/ui/src/screens/Template/Templates.js b/awx/ui/src/screens/Template/Templates.js index 6e8811aa9370..749094c32796 100644 --- a/awx/ui/src/screens/Template/Templates.js +++ b/awx/ui/src/screens/Template/Templates.js @@ -6,6 +6,7 @@ import { PageSection } from '@patternfly/react-core'; import ScreenHeader from 'components/ScreenHeader/ScreenHeader'; import TemplateList from 'components/TemplateList'; +import PersistentFilters from 'components/PersistentFilters'; import Template from './Template'; import WorkflowJobTemplate from './WorkflowJobTemplate'; import JobTemplateAdd from './JobTemplateAdd'; @@ -78,7 +79,9 @@ function Templates() { - + + + diff --git a/awx/ui/src/screens/Template/WorkflowJobTemplate.js b/awx/ui/src/screens/Template/WorkflowJobTemplate.js index 061124d357da..ef8225146f4c 100644 --- a/awx/ui/src/screens/Template/WorkflowJobTemplate.js +++ b/awx/ui/src/screens/Template/WorkflowJobTemplate.js @@ -111,6 +111,7 @@ function WorkflowJobTemplate({ setBreadcrumb }) { ), link: `/templates`, + isBackButton: true, id: 99, }, { name: t`Details`, link: `${match.url}/details` }, diff --git a/awx/ui/src/screens/User/User.js b/awx/ui/src/screens/User/User.js index 88b3e2173137..d1d5371b5a0c 100644 --- a/awx/ui/src/screens/User/User.js +++ b/awx/ui/src/screens/User/User.js @@ -59,6 +59,7 @@ function User({ setBreadcrumb, me }) { ), link: `/users`, id: 99, + isBackButton: true, }, { name: t`Details`, link: `${match.url}/details`, id: 0 }, { diff --git a/awx/ui/src/screens/User/Users.js b/awx/ui/src/screens/User/Users.js index e198d4e5ccf5..44ebd1a99ab4 100644 --- a/awx/ui/src/screens/User/Users.js +++ b/awx/ui/src/screens/User/Users.js @@ -4,8 +4,8 @@ import { Route, useRouteMatch, Switch } from 'react-router-dom'; import { t } from '@lingui/macro'; import ScreenHeader from 'components/ScreenHeader/ScreenHeader'; +import PersistentFilters from 'components/PersistentFilters'; import { Config } from 'contexts/Config'; - import UsersList from './UserList/UserList'; import UserAdd from './UserAdd/UserAdd'; import User from './User'; @@ -51,7 +51,9 @@ function Users() { - + + + diff --git a/awx/ui/src/screens/WorkflowApproval/WorkflowApproval.js b/awx/ui/src/screens/WorkflowApproval/WorkflowApproval.js index 92bdf9d633bd..07de41eb432c 100644 --- a/awx/ui/src/screens/WorkflowApproval/WorkflowApproval.js +++ b/awx/ui/src/screens/WorkflowApproval/WorkflowApproval.js @@ -70,6 +70,7 @@ function WorkflowApproval({ setBreadcrumb }) { ), link: `/workflow_approvals`, + isBackButton: true, id: 99, }, { diff --git a/awx/ui/src/screens/WorkflowApproval/WorkflowApprovals.js b/awx/ui/src/screens/WorkflowApproval/WorkflowApprovals.js index 45c72c9a1f5a..7749d12c0f49 100644 --- a/awx/ui/src/screens/WorkflowApproval/WorkflowApprovals.js +++ b/awx/ui/src/screens/WorkflowApproval/WorkflowApprovals.js @@ -3,6 +3,7 @@ import { Route, Switch, useRouteMatch } from 'react-router-dom'; import { t } from '@lingui/macro'; import ScreenHeader from 'components/ScreenHeader/ScreenHeader'; +import PersistentFilters from 'components/PersistentFilters'; import WorkflowApprovalList from './WorkflowApprovalList'; import WorkflowApproval from './WorkflowApproval'; @@ -35,7 +36,9 @@ function WorkflowApprovals() { - + + +