From 55ce409a127aeafad09db12792cab7035de34744 Mon Sep 17 00:00:00 2001 From: mabashian Date: Fri, 28 Jun 2019 09:26:11 -0400 Subject: [PATCH] Run prettier on all the files in awx/ui_next --- awx/ui_next/src/App.jsx | 80 ++++--- awx/ui_next/src/App.test.jsx | 39 ++-- awx/ui_next/src/RootProvider.jsx | 20 +- awx/ui_next/src/RootProvider.test.jsx | 12 +- awx/ui_next/src/api/Base.js | 18 +- awx/ui_next/src/api/Base.test.jsx | 36 +-- awx/ui_next/src/api/index.js | 2 +- .../src/api/mixins/InstanceGroups.mixin.js | 30 ++- awx/ui_next/src/api/models/Config.js | 2 +- awx/ui_next/src/api/models/InstanceGroups.js | 2 +- awx/ui_next/src/api/models/JobTemplates.js | 6 +- awx/ui_next/src/api/models/Jobs.js | 2 +- awx/ui_next/src/api/models/Me.js | 2 +- awx/ui_next/src/api/models/Organizations.js | 6 +- .../src/api/models/Organizations.test.jsx | 26 ++- awx/ui_next/src/api/models/Root.js | 6 +- awx/ui_next/src/api/models/Root.test.jsx | 19 +- awx/ui_next/src/api/models/Teams.js | 11 +- awx/ui_next/src/api/models/Teams.test.jsx | 22 +- .../src/api/models/UnifiedJobTemplates.js | 2 +- awx/ui_next/src/api/models/UnifiedJobs.js | 2 +- awx/ui_next/src/api/models/Users.js | 11 +- awx/ui_next/src/api/models/Users.test.jsx | 22 +- .../src/api/models/WorkflowJobTemplates.js | 2 +- awx/ui_next/src/app.scss | 1 - awx/ui_next/src/components/About/About.jsx | 20 +- .../src/components/About/About.test.jsx | 8 +- .../components/AddRole/AddResourceRole.jsx | 79 ++++--- .../AddRole/AddResourceRole.test.jsx | 105 ++++----- .../src/components/AddRole/CheckboxCard.jsx | 27 ++- .../components/AddRole/CheckboxCard.test.jsx | 7 +- .../components/AddRole/SelectResourceStep.jsx | 31 ++- .../AddRole/SelectResourceStep.test.jsx | 41 ++-- .../src/components/AddRole/SelectRoleStep.jsx | 23 +- .../AddRole/SelectRoleStep.test.jsx | 16 +- .../src/components/AddRole/SelectableCard.jsx | 25 +- .../AddRole/SelectableCard.test.jsx | 13 +- .../src/components/AlertModal/AlertModal.jsx | 33 ++- .../components/AlertModal/AlertModal.test.jsx | 4 +- .../AnsibleSelect/AnsibleSelect.jsx | 13 +- .../AnsibleSelect/AnsibleSelect.test.jsx | 12 +- .../src/components/Background/Background.jsx | 7 +- .../components/Background/Background.test.jsx | 6 +- .../src/components/BrandLogo/BrandLogo.jsx | 66 +++--- .../components/BrandLogo/BrandLogo.test.jsx | 4 +- .../components/Breadcrumbs/Breadcrumbs.jsx | 32 +-- .../Breadcrumbs/Breadcrumbs.test.jsx | 14 +- awx/ui_next/src/components/ButtonGroup.jsx | 8 +- .../CardCloseButton/CardCloseButton.jsx | 16 +- .../CheckboxListItem/CheckboxListItem.jsx | 36 ++- awx/ui_next/src/components/Chip/Chip.jsx | 7 +- awx/ui_next/src/components/Chip/ChipGroup.jsx | 7 +- .../src/components/Chip/ChipGroup.test.jsx | 7 +- .../CodeMirrorInput/CodeMirrorInput.jsx | 10 +- .../CodeMirrorInput/CodeMirrorInput.test.jsx | 13 +- .../CodeMirrorInput/VariablesField.jsx | 18 +- .../CodeMirrorInput/VariablesField.test.jsx | 37 ++- .../components/ContentEmpty/ContentEmpty.jsx | 10 +- .../components/ContentError/ContentError.jsx | 16 +- .../ContentError/ContentError.test.jsx | 23 +- .../ContentLoading/ContentLoading.jsx | 9 +- .../DataListToolbar/DataListToolbar.jsx | 19 +- .../DataListToolbar/DataListToolbar.test.jsx | 24 +- .../src/components/DetailList/Detail.jsx | 18 +- .../src/components/DetailList/Detail.test.jsx | 4 +- .../src/components/DetailList/DetailList.jsx | 9 +- .../components/ErrorDetail/ErrorDetail.jsx | 41 ++-- .../ErrorDetail/ErrorDetail.test.jsx | 23 +- .../ExpandCollapse/ExpandCollapse.jsx | 36 ++- .../FormActionGroup/FormActionGroup.jsx | 24 +- .../FormActionGroup/FormActionGroup.test.jsx | 5 +- .../src/components/FormField/FormField.jsx | 13 +- .../src/components/FormRow/FormRow.jsx | 10 +- .../components/LaunchButton/LaunchButton.jsx | 30 ++- .../LaunchButton/LaunchButton.test.jsx | 56 +++-- awx/ui_next/src/components/Lookup/Lookup.jsx | 62 +++-- .../src/components/Lookup/Lookup.test.jsx | 99 ++++---- .../NavExpandableGroup/NavExpandableGroup.jsx | 17 +- .../NavExpandableGroup.test.jsx | 8 +- .../NotificationListItem.jsx | 94 ++++---- .../NotificationListItem.test.jsx | 24 +- .../PageHeaderToolbar/PageHeaderToolbar.jsx | 37 +-- .../PaginatedDataList/PaginatedDataList.jsx | 81 ++++--- .../PaginatedDataList.test.jsx | 9 +- .../PaginatedDataListItem.jsx | 28 +-- .../PaginatedDataList/ToolbarAddButton.jsx | 19 +- .../ToolbarAddButton.test.jsx | 8 +- .../PaginatedDataList/ToolbarDeleteButton.jsx | 56 +++-- .../ToolbarDeleteButton.test.jsx | 32 +-- .../src/components/Pagination/Pagination.jsx | 18 +- .../components/Pagination/Pagination.test.jsx | 7 +- .../src/components/RoutedTabs/RoutedTabs.jsx | 28 +-- .../components/RoutedTabs/RoutedTabs.test.jsx | 17 +- awx/ui_next/src/components/Search/Search.jsx | 50 ++-- .../src/components/Search/Search.test.jsx | 24 +- .../components/SelectedList/SelectedList.jsx | 12 +- .../SelectedList/SelectedList.test.jsx | 20 +- awx/ui_next/src/components/Sort/Sort.jsx | 58 ++--- awx/ui_next/src/components/Sort/Sort.test.jsx | 16 +- awx/ui_next/src/index.jsx | 86 +++---- awx/ui_next/src/index.test.jsx | 6 +- .../src/screens/Application/Applications.jsx | 6 +- .../src/screens/AuthSetting/AuthSettings.jsx | 6 +- .../src/screens/Credential/Credentials.jsx | 6 +- .../CredentialType/CredentialTypes.jsx | 6 +- .../src/screens/Dashboard/Dashboard.jsx | 6 +- .../screens/InstanceGroup/InstanceGroups.jsx | 6 +- .../src/screens/Inventory/Inventories.jsx | 6 +- .../InventoryScript/InventoryScripts.jsx | 6 +- awx/ui_next/src/screens/Job/Job.jsx | 66 ++---- .../src/screens/Job/JobDetail/JobDetail.jsx | 7 +- .../screens/Job/JobDetail/JobDetail.test.jsx | 10 +- .../src/screens/Job/JobList/JobList.jsx | 49 ++-- .../src/screens/Job/JobList/JobList.test.jsx | 96 ++++---- .../src/screens/Job/JobList/JobListItem.jsx | 33 ++- .../screens/Job/JobList/JobListItem.test.jsx | 2 +- .../src/screens/Job/JobOutput/JobOutput.jsx | 6 +- .../screens/Job/JobOutput/JobOutput.test.jsx | 6 +- .../src/screens/Job/JobOutput/index.js | 1 - awx/ui_next/src/screens/Job/Jobs.jsx | 18 +- awx/ui_next/src/screens/Job/Jobs.test.jsx | 29 +-- .../src/screens/JobsSetting/JobsSettings.jsx | 6 +- awx/ui_next/src/screens/License/License.jsx | 6 +- awx/ui_next/src/screens/Login/Login.jsx | 29 ++- awx/ui_next/src/screens/Login/Login.test.jsx | 175 +++++++++----- .../screens/ManagementJob/ManagementJobs.jsx | 6 +- .../NotificationTemplates.jsx | 6 +- .../src/screens/Organization/Organization.jsx | 89 ++++--- .../Organization/Organization.test.jsx | 83 ++++--- .../DeleteRoleConfirmationModal.jsx | 30 ++- .../OrganizationAccess/OrganizationAccess.jsx | 76 +++--- .../OrganizationAccess.test.jsx | 181 ++++++++------ .../OrganizationAccessItem.jsx | 118 +++++----- .../OrganizationAccessItem.test.jsx | 26 ++- .../Organization/OrganizationAccess/index.js | 4 +- .../OrganizationAdd/OrganizationAdd.jsx | 22 +- .../OrganizationAdd/OrganizationAdd.test.jsx | 76 +++--- .../OrganizationDetail/OrganizationDetail.jsx | 67 +++--- .../OrganizationDetail.test.jsx | 42 ++-- .../OrganizationEdit/OrganizationEdit.jsx | 32 ++- .../OrganizationEdit.test.jsx | 31 ++- .../OrganizationList/OrganizationList.jsx | 73 +++--- .../OrganizationList.test.jsx | 156 +++++++------ .../OrganizationList/OrganizationListItem.jsx | 73 +++--- .../OrganizationListItem.test.jsx | 10 +- .../OrganizationNotifications.jsx | 35 +-- .../OrganizationNotifications.test.jsx | 70 ++++-- .../OrganizationTeams/OrganizationTeams.jsx | 12 +- .../OrganizationTeams.test.jsx | 18 +- .../screens/Organization/Organizations.jsx | 31 +-- .../shared/InstanceGroupsLookup.jsx | 40 ++-- .../Organization/shared/OrganizationForm.jsx | 95 ++++---- .../shared/OrganizationForm.test.jsx | 180 +++++++------- awx/ui_next/src/screens/Portal/Portal.jsx | 6 +- awx/ui_next/src/screens/Project/Projects.jsx | 6 +- .../src/screens/Schedule/Schedules.jsx | 6 +- .../screens/SystemSetting/SystemSettings.jsx | 6 +- awx/ui_next/src/screens/Team/Teams.jsx | 6 +- .../JobTemplateDetail/JobTemplateDetail.jsx | 173 ++++++-------- .../JobTemplateDetail.test.jsx | 65 +++--- .../JobTemplateEdit/JobTemplateEdit.jsx | 24 +- .../JobTemplateEdit/JobTemplateEdit.test.jsx | 31 +-- awx/ui_next/src/screens/Template/Template.jsx | 61 ++--- .../src/screens/Template/Template.test.jsx | 14 +- .../Template/TemplateList/TemplateList.jsx | 79 ++++--- .../TemplateList/TemplateListItem.jsx | 45 ++-- .../TemplateList/TemplatesList.test.jsx | 221 +++++++++++------- .../TemplateList/TemplatesListItem.test.jsx | 56 ++--- .../src/screens/Template/Templates.jsx | 27 ++- .../screens/Template/shared/TemplateForm.jsx | 41 ++-- .../Template/shared/TemplateForm.test.jsx | 14 +- .../src/screens/UISetting/UISettings.jsx | 6 +- awx/ui_next/src/screens/User/Users.jsx | 6 +- awx/ui_next/src/types.js | 5 +- awx/ui_next/src/util/auth.js | 11 +- awx/ui_next/src/util/auth.test.js | 6 +- awx/ui_next/src/util/qs.js | 43 ++-- awx/ui_next/src/util/qs.test.js | 40 ++-- awx/ui_next/src/util/strings.js | 21 +- awx/ui_next/src/util/validators.jsx | 13 +- awx/ui_next/src/util/validators.test.js | 18 +- awx/ui_next/src/util/yaml.js | 16 +- 182 files changed, 3000 insertions(+), 2747 deletions(-) diff --git a/awx/ui_next/src/App.jsx b/awx/ui_next/src/App.jsx index db0bee52f3..82fb7e99af 100644 --- a/awx/ui_next/src/App.jsx +++ b/awx/ui_next/src/App.jsx @@ -35,12 +35,13 @@ const PageHeader = styled(PFPageHeader)` `; class App extends Component { - constructor (props) { + constructor(props) { super(props); // initialize with a closed navbar if window size is small - const isNavOpen = typeof window !== 'undefined' - && window.innerWidth >= parseInt(global_breakpoint_md.value, 10); + const isNavOpen = + typeof window !== 'undefined' && + window.innerWidth >= parseInt(global_breakpoint_md.value, 10); this.state = { ansible_version: null, @@ -49,7 +50,7 @@ class App extends Component { version: null, isAboutModalOpen: false, isNavOpen, - configError: null + configError: null, }; this.handleLogout = this.handleLogout.bind(this); @@ -59,39 +60,48 @@ class App extends Component { this.handleConfigErrorClose = this.handleConfigErrorClose.bind(this); } - async componentDidMount () { + async componentDidMount() { await this.loadConfig(); } // eslint-disable-next-line class-methods-use-this - async handleLogout () { + async handleLogout() { await RootAPI.logout(); window.location.replace('/#/login'); } - handleAboutOpen () { + handleAboutOpen() { this.setState({ isAboutModalOpen: true }); } - handleAboutClose () { + handleAboutClose() { this.setState({ isAboutModalOpen: false }); } - handleNavToggle () { + handleNavToggle() { this.setState(({ isNavOpen }) => ({ isNavOpen: !isNavOpen })); } - handleConfigErrorClose () { + handleConfigErrorClose() { this.setState({ - configError: null + configError: null, }); } - async loadConfig () { + async loadConfig() { try { - const [configRes, meRes] = await Promise.all([ConfigAPI.read(), MeAPI.read()]); - const { data: { ansible_version, custom_virtualenvs, version } } = configRes; - const { data: { results: [me] } } = meRes; + const [configRes, meRes] = await Promise.all([ + ConfigAPI.read(), + MeAPI.read(), + ]); + const { + data: { ansible_version, custom_virtualenvs, version }, + } = configRes; + const { + data: { + results: [me], + }, + } = meRes; this.setState({ ansible_version, custom_virtualenvs, version, me }); } catch (err) { @@ -99,7 +109,7 @@ class App extends Component { } } - render () { + render() { const { ansible_version, custom_virtualenvs, @@ -107,7 +117,7 @@ class App extends Component { isNavOpen, me, version, - configError + configError, } = this.state; const { i18n, @@ -122,47 +132,43 @@ class App extends Component { onNavToggle={this.handleNavToggle} logo={} logoProps={{ href: '/' }} - toolbar={( + toolbar={ - )} + } /> ); const sidebar = ( - {routeGroups.map( - ({ groupId, groupTitle, routes }) => ( - - ) - )} + {routeGroups.map(({ groupId, groupTitle, routes }) => ( + + ))} - )} + } /> ); return ( - - + + {render({ routeGroups })} diff --git a/awx/ui_next/src/App.test.jsx b/awx/ui_next/src/App.test.jsx index 5a69647dfa..88a1f394a9 100644 --- a/awx/ui_next/src/App.test.jsx +++ b/awx/ui_next/src/App.test.jsx @@ -14,13 +14,14 @@ describe('', () => { const version = '222'; beforeEach(() => { - ConfigAPI.read = () => Promise.resolve({ - data: { - ansible_version, - custom_virtualenvs, - version - } - }); + ConfigAPI.read = () => + Promise.resolve({ + data: { + ansible_version, + custom_virtualenvs, + version, + }, + }); MeAPI.read = () => Promise.resolve({ data: { results: [{}] } }); }); @@ -43,15 +44,13 @@ describe('', () => { { groupTitle: 'Group Two', groupId: 'group_two', - routes: [ - { title: 'Fiz', path: '/fiz' }, - ] - } + routes: [{ title: 'Fiz', path: '/fiz' }], + }, ]} - render={({ routeGroups }) => ( - routeGroups.map(({ groupId }) => (
)) - )} - />, + render={({ routeGroups }) => + routeGroups.map(({ groupId }) =>
) + } + /> ); // page components @@ -70,7 +69,7 @@ describe('', () => { expect(appWrapper.find('#group_two').length).toBe(1); }); - test('opening the about modal renders prefetched config data', async (done) => { + test('opening the about modal renders prefetched config data', async done => { const wrapper = mountWithContexts(); wrapper.update(); @@ -83,7 +82,11 @@ describe('', () => { await waitForElement(wrapper, aboutDropdown); wrapper.find(aboutDropdown).simulate('click'); - const button = await waitForElement(wrapper, aboutButton, el => !el.props().disabled); + const button = await waitForElement( + wrapper, + aboutButton, + el => !el.props().disabled + ); button.simulate('click'); // check about modal content @@ -108,7 +111,7 @@ describe('', () => { }); }); - test('onLogout makes expected call to api client', async (done) => { + test('onLogout makes expected call to api client', async done => { const appWrapper = mountWithContexts().find('App'); appWrapper.instance().handleLogout(); await asyncFlush(); diff --git a/awx/ui_next/src/RootProvider.jsx b/awx/ui_next/src/RootProvider.jsx index 9165ae4d23..48fdf3200c 100644 --- a/awx/ui_next/src/RootProvider.jsx +++ b/awx/ui_next/src/RootProvider.jsx @@ -1,24 +1,21 @@ import React, { Component } from 'react'; -import { - I18nProvider, -} from '@lingui/react'; +import { I18nProvider } from '@lingui/react'; -import { - HashRouter -} from 'react-router-dom'; +import { HashRouter } from 'react-router-dom'; import ja from '../build/locales/ja/messages'; import en from '../build/locales/en/messages'; -export function getLanguage (nav) { - const language = (nav.languages && nav.languages[0]) || nav.language || nav.userLanguage; +export function getLanguage(nav) { + const language = + (nav.languages && nav.languages[0]) || nav.language || nav.userLanguage; const languageWithoutRegionCode = language.toLowerCase().split(/[_-]+/)[0]; return languageWithoutRegionCode; } class RootProvider extends Component { - render () { + render() { const { children } = this.props; const catalogs = { en, ja }; @@ -26,10 +23,7 @@ class RootProvider extends Component { return ( - + {children} diff --git a/awx/ui_next/src/RootProvider.test.jsx b/awx/ui_next/src/RootProvider.test.jsx index e89f37a8f7..45b87782f6 100644 --- a/awx/ui_next/src/RootProvider.test.jsx +++ b/awx/ui_next/src/RootProvider.test.jsx @@ -3,8 +3,16 @@ import { getLanguage } from './RootProvider'; describe('RootProvider.jsx', () => { test('getLanguage returns the expected language code', () => { expect(getLanguage({ languages: ['es-US'] })).toEqual('es'); - expect(getLanguage({ languages: ['es-US'], language: 'fr-FR', userLanguage: 'en-US' })).toEqual('es'); - expect(getLanguage({ language: 'fr-FR', userLanguage: 'en-US' })).toEqual('fr'); + expect( + getLanguage({ + languages: ['es-US'], + language: 'fr-FR', + userLanguage: 'en-US', + }) + ).toEqual('es'); + expect(getLanguage({ language: 'fr-FR', userLanguage: 'en-US' })).toEqual( + 'fr' + ); expect(getLanguage({ userLanguage: 'en-US' })).toEqual('en'); }); }); diff --git a/awx/ui_next/src/api/Base.js b/awx/ui_next/src/api/Base.js index bc26bc0657..d884f8061c 100644 --- a/awx/ui_next/src/api/Base.js +++ b/awx/ui_next/src/api/Base.js @@ -2,40 +2,40 @@ import axios from 'axios'; const defaultHttp = axios.create({ xsrfCookieName: 'csrftoken', - xsrfHeaderName: 'X-CSRFToken' + xsrfHeaderName: 'X-CSRFToken', }); class Base { - constructor (http = defaultHttp, baseURL) { + constructor(http = defaultHttp, baseURL) { this.http = http; this.baseUrl = baseURL; } - create (data) { + create(data) { return this.http.post(this.baseUrl, data); } - destroy (id) { + destroy(id) { return this.http.delete(`${this.baseUrl}${id}/`); } - read (params = {}) { + read(params = {}) { return this.http.get(this.baseUrl, { params }); } - readDetail (id) { + readDetail(id) { return this.http.get(`${this.baseUrl}${id}/`); } - readOptions () { + readOptions() { return this.http.options(this.baseUrl); } - replace (id, data) { + replace(id, data) { return this.http.put(`${this.baseUrl}${id}/`, data); } - update (id, data) { + update(id, data) { return this.http.patch(`${this.baseUrl}${id}/`, data); } } diff --git a/awx/ui_next/src/api/Base.test.jsx b/awx/ui_next/src/api/Base.test.jsx index 6cd21c6be5..df6d874bd0 100644 --- a/awx/ui_next/src/api/Base.test.jsx +++ b/awx/ui_next/src/api/Base.test.jsx @@ -3,14 +3,14 @@ import Base from './Base'; describe('Base', () => { const createPromise = () => Promise.resolve(); const mockBaseURL = '/api/v2/organizations/'; - const mockHttp = ({ + const mockHttp = { delete: jest.fn(createPromise), get: jest.fn(createPromise), options: jest.fn(createPromise), patch: jest.fn(createPromise), post: jest.fn(createPromise), - put: jest.fn(createPromise) - }); + put: jest.fn(createPromise), + }; const BaseAPI = new Base(mockHttp, mockBaseURL); @@ -18,7 +18,7 @@ describe('Base', () => { jest.clearAllMocks(); }); - test('create calls http method with expected data', async (done) => { + test('create calls http method with expected data', async done => { const data = { name: 'test ' }; await BaseAPI.create(data); @@ -28,17 +28,19 @@ describe('Base', () => { done(); }); - test('destroy calls http method with expected data', async (done) => { + test('destroy calls http method with expected data', async done => { const resourceId = 1; await BaseAPI.destroy(resourceId); expect(mockHttp.delete).toHaveBeenCalledTimes(1); - expect(mockHttp.delete.mock.calls[0][0]).toEqual(`${mockBaseURL}${resourceId}/`); + expect(mockHttp.delete.mock.calls[0][0]).toEqual( + `${mockBaseURL}${resourceId}/` + ); done(); }); - test('read calls http method with expected data', async (done) => { + test('read calls http method with expected data', async done => { const defaultParams = {}; const testParams = { foo: 'bar' }; @@ -51,17 +53,19 @@ describe('Base', () => { done(); }); - test('readDetail calls http method with expected data', async (done) => { + test('readDetail calls http method with expected data', async done => { const resourceId = 1; await BaseAPI.readDetail(resourceId); expect(mockHttp.get).toHaveBeenCalledTimes(1); - expect(mockHttp.get.mock.calls[0][0]).toEqual(`${mockBaseURL}${resourceId}/`); + expect(mockHttp.get.mock.calls[0][0]).toEqual( + `${mockBaseURL}${resourceId}/` + ); done(); }); - test('readOptions calls http method with expected data', async (done) => { + test('readOptions calls http method with expected data', async done => { await BaseAPI.readOptions(); expect(mockHttp.options).toHaveBeenCalledTimes(1); @@ -69,27 +73,31 @@ describe('Base', () => { done(); }); - test('replace calls http method with expected data', async (done) => { + test('replace calls http method with expected data', async done => { const resourceId = 1; const data = { name: 'test ' }; await BaseAPI.replace(resourceId, data); expect(mockHttp.put).toHaveBeenCalledTimes(1); - expect(mockHttp.put.mock.calls[0][0]).toEqual(`${mockBaseURL}${resourceId}/`); + expect(mockHttp.put.mock.calls[0][0]).toEqual( + `${mockBaseURL}${resourceId}/` + ); expect(mockHttp.put.mock.calls[0][1]).toEqual(data); done(); }); - test('update calls http method with expected data', async (done) => { + test('update calls http method with expected data', async done => { const resourceId = 1; const data = { name: 'test ' }; await BaseAPI.update(resourceId, data); expect(mockHttp.patch).toHaveBeenCalledTimes(1); - expect(mockHttp.patch.mock.calls[0][0]).toEqual(`${mockBaseURL}${resourceId}/`); + expect(mockHttp.patch.mock.calls[0][0]).toEqual( + `${mockBaseURL}${resourceId}/` + ); expect(mockHttp.patch.mock.calls[0][1]).toEqual(data); done(); diff --git a/awx/ui_next/src/api/index.js b/awx/ui_next/src/api/index.js index c87e753f2b..204985b31d 100644 --- a/awx/ui_next/src/api/index.js +++ b/awx/ui_next/src/api/index.js @@ -36,5 +36,5 @@ export { UnifiedJobTemplatesAPI, UnifiedJobsAPI, UsersAPI, - WorkflowJobTemplatesAPI + WorkflowJobTemplatesAPI, }; diff --git a/awx/ui_next/src/api/mixins/InstanceGroups.mixin.js b/awx/ui_next/src/api/mixins/InstanceGroups.mixin.js index 2317fb9270..3deff44bf7 100644 --- a/awx/ui_next/src/api/mixins/InstanceGroups.mixin.js +++ b/awx/ui_next/src/api/mixins/InstanceGroups.mixin.js @@ -1,15 +1,23 @@ -const InstanceGroupsMixin = (parent) => class extends parent { - readInstanceGroups (resourceId, params = {}) { - return this.http.get(`${this.baseUrl}${resourceId}/instance_groups/`, { params }); - } +const InstanceGroupsMixin = parent => + class extends parent { + readInstanceGroups(resourceId, params = {}) { + return this.http.get(`${this.baseUrl}${resourceId}/instance_groups/`, { + params, + }); + } - associateInstanceGroup (resourceId, instanceGroupId) { - return this.http.post(`${this.baseUrl}${resourceId}/instance_groups/`, { id: instanceGroupId }); - } + associateInstanceGroup(resourceId, instanceGroupId) { + return this.http.post(`${this.baseUrl}${resourceId}/instance_groups/`, { + id: instanceGroupId, + }); + } - disassociateInstanceGroup (resourceId, instanceGroupId) { - return this.http.post(`${this.baseUrl}${resourceId}/instance_groups/`, { id: instanceGroupId, disassociate: true }); - } -}; + disassociateInstanceGroup(resourceId, instanceGroupId) { + return this.http.post(`${this.baseUrl}${resourceId}/instance_groups/`, { + id: instanceGroupId, + disassociate: true, + }); + } + }; export default InstanceGroupsMixin; diff --git a/awx/ui_next/src/api/models/Config.js b/awx/ui_next/src/api/models/Config.js index a944f4b994..f8e89df245 100644 --- a/awx/ui_next/src/api/models/Config.js +++ b/awx/ui_next/src/api/models/Config.js @@ -1,7 +1,7 @@ import Base from '../Base'; class Config extends Base { - constructor (http) { + constructor(http) { super(http); this.baseUrl = '/api/v2/config/'; } diff --git a/awx/ui_next/src/api/models/InstanceGroups.js b/awx/ui_next/src/api/models/InstanceGroups.js index 7eaad069e3..94464e0f42 100644 --- a/awx/ui_next/src/api/models/InstanceGroups.js +++ b/awx/ui_next/src/api/models/InstanceGroups.js @@ -1,7 +1,7 @@ import Base from '../Base'; class InstanceGroups extends Base { - constructor (http) { + constructor(http) { super(http); this.baseUrl = '/api/v2/instance_groups/'; } diff --git a/awx/ui_next/src/api/models/JobTemplates.js b/awx/ui_next/src/api/models/JobTemplates.js index dd1e5269fa..de24e298a7 100644 --- a/awx/ui_next/src/api/models/JobTemplates.js +++ b/awx/ui_next/src/api/models/JobTemplates.js @@ -2,7 +2,7 @@ import Base from '../Base'; import InstanceGroupsMixin from '../mixins/InstanceGroups.mixin'; class JobTemplates extends InstanceGroupsMixin(Base) { - constructor (http) { + constructor(http) { super(http); this.baseUrl = '/api/v2/job_templates/'; @@ -10,11 +10,11 @@ class JobTemplates extends InstanceGroupsMixin(Base) { this.readLaunch = this.readLaunch.bind(this); } - launch (id, data) { + launch(id, data) { return this.http.post(`${this.baseUrl}${id}/launch/`, data); } - readLaunch (id) { + readLaunch(id) { return this.http.get(`${this.baseUrl}${id}/launch/`); } } diff --git a/awx/ui_next/src/api/models/Jobs.js b/awx/ui_next/src/api/models/Jobs.js index cea169b72a..295ee815e8 100644 --- a/awx/ui_next/src/api/models/Jobs.js +++ b/awx/ui_next/src/api/models/Jobs.js @@ -1,7 +1,7 @@ import Base from '../Base'; class Jobs extends Base { - constructor (http) { + constructor(http) { super(http); this.baseUrl = '/api/v2/jobs/'; } diff --git a/awx/ui_next/src/api/models/Me.js b/awx/ui_next/src/api/models/Me.js index ca0b336e26..77663567e9 100644 --- a/awx/ui_next/src/api/models/Me.js +++ b/awx/ui_next/src/api/models/Me.js @@ -1,7 +1,7 @@ import Base from '../Base'; class Me extends Base { - constructor (http) { + constructor(http) { super(http); this.baseUrl = '/api/v2/me/'; } diff --git a/awx/ui_next/src/api/models/Organizations.js b/awx/ui_next/src/api/models/Organizations.js index 77970ad00c..c8531b3ecd 100644 --- a/awx/ui_next/src/api/models/Organizations.js +++ b/awx/ui_next/src/api/models/Organizations.js @@ -3,16 +3,16 @@ import NotificationsMixin from '../mixins/Notifications.mixin'; import InstanceGroupsMixin from '../mixins/InstanceGroups.mixin'; class Organizations extends InstanceGroupsMixin(NotificationsMixin(Base)) { - constructor (http) { + constructor(http) { super(http); this.baseUrl = '/api/v2/organizations/'; } - readAccessList (id, params = {}) { + readAccessList(id, params = {}) { return this.http.get(`${this.baseUrl}${id}/access_list/`, { params }); } - readTeams (id, params = {}) { + readTeams(id, params = {}) { return this.http.get(`${this.baseUrl}${id}/teams/`, { params }); } } diff --git a/awx/ui_next/src/api/models/Organizations.test.jsx b/awx/ui_next/src/api/models/Organizations.test.jsx index 31f6148c60..19a1958dae 100644 --- a/awx/ui_next/src/api/models/Organizations.test.jsx +++ b/awx/ui_next/src/api/models/Organizations.test.jsx @@ -5,7 +5,7 @@ describe('OrganizationsAPI', () => { const orgId = 1; const searchParams = { foo: 'bar' }; const createPromise = () => Promise.resolve(); - const mockHttp = ({ get: jest.fn(createPromise) }); + const mockHttp = { get: jest.fn(createPromise) }; const OrganizationsAPI = new Organizations(mockHttp); @@ -13,24 +13,36 @@ describe('OrganizationsAPI', () => { jest.clearAllMocks(); }); - test('read access list calls get with expected params', async (done) => { + test('read access list calls get with expected params', async done => { await OrganizationsAPI.readAccessList(orgId); await OrganizationsAPI.readAccessList(orgId, searchParams); expect(mockHttp.get).toHaveBeenCalledTimes(2); - expect(mockHttp.get.mock.calls[0]).toContainEqual(`/api/v2/organizations/${orgId}/access_list/`, { params: {} }); - expect(mockHttp.get.mock.calls[1]).toContainEqual(`/api/v2/organizations/${orgId}/access_list/`, { params: searchParams }); + expect(mockHttp.get.mock.calls[0]).toContainEqual( + `/api/v2/organizations/${orgId}/access_list/`, + { params: {} } + ); + expect(mockHttp.get.mock.calls[1]).toContainEqual( + `/api/v2/organizations/${orgId}/access_list/`, + { params: searchParams } + ); done(); }); - test('read teams calls get with expected params', async (done) => { + test('read teams calls get with expected params', async done => { await OrganizationsAPI.readTeams(orgId); await OrganizationsAPI.readTeams(orgId, searchParams); expect(mockHttp.get).toHaveBeenCalledTimes(2); - expect(mockHttp.get.mock.calls[0]).toContainEqual(`/api/v2/organizations/${orgId}/teams/`, { params: {} }); - expect(mockHttp.get.mock.calls[1]).toContainEqual(`/api/v2/organizations/${orgId}/teams/`, { params: searchParams }); + expect(mockHttp.get.mock.calls[0]).toContainEqual( + `/api/v2/organizations/${orgId}/teams/`, + { params: {} } + ); + expect(mockHttp.get.mock.calls[1]).toContainEqual( + `/api/v2/organizations/${orgId}/teams/`, + { params: searchParams } + ); done(); }); diff --git a/awx/ui_next/src/api/models/Root.js b/awx/ui_next/src/api/models/Root.js index 6e2bd0cfd9..ffba170c41 100644 --- a/awx/ui_next/src/api/models/Root.js +++ b/awx/ui_next/src/api/models/Root.js @@ -1,13 +1,13 @@ import Base from '../Base'; class Root extends Base { - constructor (http) { + constructor(http) { super(http); this.baseUrl = '/api/'; this.redirectURL = '/api/v2/config/'; } - async login (username, password, redirect = this.redirectURL) { + async login(username, password, redirect = this.redirectURL) { const loginUrl = `${this.baseUrl}login/`; const un = encodeURIComponent(username); const pw = encodeURIComponent(password); @@ -22,7 +22,7 @@ class Root extends Base { return response; } - logout () { + logout() { return this.http.get(`${this.baseUrl}logout/`); } } diff --git a/awx/ui_next/src/api/models/Root.test.jsx b/awx/ui_next/src/api/models/Root.test.jsx index 4b3c3401ba..3fb79cdfc9 100644 --- a/awx/ui_next/src/api/models/Root.test.jsx +++ b/awx/ui_next/src/api/models/Root.test.jsx @@ -2,7 +2,10 @@ import Root from './Root'; describe('RootAPI', () => { const createPromise = () => Promise.resolve(); - const mockHttp = ({ get: jest.fn(createPromise), post: jest.fn(createPromise) }); + const mockHttp = { + get: jest.fn(createPromise), + post: jest.fn(createPromise), + }; const RootAPI = new Root(mockHttp); @@ -10,7 +13,7 @@ describe('RootAPI', () => { jest.clearAllMocks(); }); - test('login calls get and post with expected content headers', async (done) => { + test('login calls get and post with expected content headers', async done => { const headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; await RootAPI.login('username', 'password'); @@ -24,18 +27,22 @@ describe('RootAPI', () => { done(); }); - test('login sends expected data', async (done) => { + test('login sends expected data', async done => { await RootAPI.login('foo', 'bar'); await RootAPI.login('foo', 'bar', 'baz'); expect(mockHttp.post).toHaveBeenCalledTimes(2); - expect(mockHttp.post.mock.calls[0]).toContainEqual('username=foo&password=bar&next=%2Fapi%2Fv2%2Fconfig%2F'); - expect(mockHttp.post.mock.calls[1]).toContainEqual('username=foo&password=bar&next=baz'); + expect(mockHttp.post.mock.calls[0]).toContainEqual( + 'username=foo&password=bar&next=%2Fapi%2Fv2%2Fconfig%2F' + ); + expect(mockHttp.post.mock.calls[1]).toContainEqual( + 'username=foo&password=bar&next=baz' + ); done(); }); - test('logout calls expected http method', async (done) => { + test('logout calls expected http method', async done => { await RootAPI.logout(); expect(mockHttp.get).toHaveBeenCalledTimes(1); diff --git a/awx/ui_next/src/api/models/Teams.js b/awx/ui_next/src/api/models/Teams.js index fb373bb3bc..585eb1086d 100644 --- a/awx/ui_next/src/api/models/Teams.js +++ b/awx/ui_next/src/api/models/Teams.js @@ -1,17 +1,20 @@ import Base from '../Base'; class Teams extends Base { - constructor (http) { + constructor(http) { super(http); this.baseUrl = '/api/v2/teams/'; } - associateRole (teamId, roleId) { + associateRole(teamId, roleId) { return this.http.post(`${this.baseUrl}${teamId}/roles/`, { id: roleId }); } - disassociateRole (teamId, roleId) { - return this.http.post(`${this.baseUrl}${teamId}/roles/`, { id: roleId, disassociate: true }); + disassociateRole(teamId, roleId) { + return this.http.post(`${this.baseUrl}${teamId}/roles/`, { + id: roleId, + disassociate: true, + }); } } diff --git a/awx/ui_next/src/api/models/Teams.test.jsx b/awx/ui_next/src/api/models/Teams.test.jsx index 88b832d7ba..9bcbe65413 100644 --- a/awx/ui_next/src/api/models/Teams.test.jsx +++ b/awx/ui_next/src/api/models/Teams.test.jsx @@ -4,7 +4,7 @@ describe('TeamsAPI', () => { const teamId = 1; const roleId = 7; const createPromise = () => Promise.resolve(); - const mockHttp = ({ post: jest.fn(createPromise) }); + const mockHttp = { post: jest.fn(createPromise) }; const TeamsAPI = new Teams(mockHttp); @@ -12,23 +12,29 @@ describe('TeamsAPI', () => { jest.clearAllMocks(); }); - test('associate role calls post with expected params', async (done) => { + test('associate role calls post with expected params', async done => { await TeamsAPI.associateRole(teamId, roleId); expect(mockHttp.post).toHaveBeenCalledTimes(1); - expect(mockHttp.post.mock.calls[0]).toContainEqual(`/api/v2/teams/${teamId}/roles/`, { id: roleId }); + expect(mockHttp.post.mock.calls[0]).toContainEqual( + `/api/v2/teams/${teamId}/roles/`, + { id: roleId } + ); done(); }); - test('read teams calls post with expected params', async (done) => { + test('read teams calls post with expected params', async done => { await TeamsAPI.disassociateRole(teamId, roleId); expect(mockHttp.post).toHaveBeenCalledTimes(1); - expect(mockHttp.post.mock.calls[0]).toContainEqual(`/api/v2/teams/${teamId}/roles/`, { - id: roleId, - disassociate: true - }); + expect(mockHttp.post.mock.calls[0]).toContainEqual( + `/api/v2/teams/${teamId}/roles/`, + { + id: roleId, + disassociate: true, + } + ); done(); }); diff --git a/awx/ui_next/src/api/models/UnifiedJobTemplates.js b/awx/ui_next/src/api/models/UnifiedJobTemplates.js index 80b5f5af45..79d70610eb 100644 --- a/awx/ui_next/src/api/models/UnifiedJobTemplates.js +++ b/awx/ui_next/src/api/models/UnifiedJobTemplates.js @@ -1,7 +1,7 @@ import Base from '../Base'; class UnifiedJobTemplates extends Base { - constructor (http) { + constructor(http) { super(http); this.baseUrl = '/api/v2/unified_job_templates/'; } diff --git a/awx/ui_next/src/api/models/UnifiedJobs.js b/awx/ui_next/src/api/models/UnifiedJobs.js index 0ad142201c..4ba18fdd00 100644 --- a/awx/ui_next/src/api/models/UnifiedJobs.js +++ b/awx/ui_next/src/api/models/UnifiedJobs.js @@ -1,7 +1,7 @@ import Base from '../Base'; class UnifiedJobs extends Base { - constructor (http) { + constructor(http) { super(http); this.baseUrl = '/api/v2/unified_jobs/'; } diff --git a/awx/ui_next/src/api/models/Users.js b/awx/ui_next/src/api/models/Users.js index d386e2333c..a76c4e49b7 100644 --- a/awx/ui_next/src/api/models/Users.js +++ b/awx/ui_next/src/api/models/Users.js @@ -1,17 +1,20 @@ import Base from '../Base'; class Users extends Base { - constructor (http) { + constructor(http) { super(http); this.baseUrl = '/api/v2/users/'; } - associateRole (userId, roleId) { + associateRole(userId, roleId) { return this.http.post(`${this.baseUrl}${userId}/roles/`, { id: roleId }); } - disassociateRole (userId, roleId) { - return this.http.post(`${this.baseUrl}${userId}/roles/`, { id: roleId, disassociate: true }); + disassociateRole(userId, roleId) { + return this.http.post(`${this.baseUrl}${userId}/roles/`, { + id: roleId, + disassociate: true, + }); } } diff --git a/awx/ui_next/src/api/models/Users.test.jsx b/awx/ui_next/src/api/models/Users.test.jsx index 469f7a01d9..10ec37411d 100644 --- a/awx/ui_next/src/api/models/Users.test.jsx +++ b/awx/ui_next/src/api/models/Users.test.jsx @@ -4,7 +4,7 @@ describe('UsersAPI', () => { const userId = 1; const roleId = 7; const createPromise = () => Promise.resolve(); - const mockHttp = ({ post: jest.fn(createPromise) }); + const mockHttp = { post: jest.fn(createPromise) }; const UsersAPI = new Users(mockHttp); @@ -12,23 +12,29 @@ describe('UsersAPI', () => { jest.clearAllMocks(); }); - test('associate role calls post with expected params', async (done) => { + test('associate role calls post with expected params', async done => { await UsersAPI.associateRole(userId, roleId); expect(mockHttp.post).toHaveBeenCalledTimes(1); - expect(mockHttp.post.mock.calls[0]).toContainEqual(`/api/v2/users/${userId}/roles/`, { id: roleId }); + expect(mockHttp.post.mock.calls[0]).toContainEqual( + `/api/v2/users/${userId}/roles/`, + { id: roleId } + ); done(); }); - test('read users calls post with expected params', async (done) => { + test('read users calls post with expected params', async done => { await UsersAPI.disassociateRole(userId, roleId); expect(mockHttp.post).toHaveBeenCalledTimes(1); - expect(mockHttp.post.mock.calls[0]).toContainEqual(`/api/v2/users/${userId}/roles/`, { - id: roleId, - disassociate: true - }); + expect(mockHttp.post.mock.calls[0]).toContainEqual( + `/api/v2/users/${userId}/roles/`, + { + id: roleId, + disassociate: true, + } + ); done(); }); diff --git a/awx/ui_next/src/api/models/WorkflowJobTemplates.js b/awx/ui_next/src/api/models/WorkflowJobTemplates.js index 8f553d4eb1..92bbccd9b0 100644 --- a/awx/ui_next/src/api/models/WorkflowJobTemplates.js +++ b/awx/ui_next/src/api/models/WorkflowJobTemplates.js @@ -1,7 +1,7 @@ import Base from '../Base'; class WorkflowJobTemplates extends Base { - constructor (http) { + constructor(http) { super(http); this.baseUrl = '/api/v2/workflow_job_templates/'; } diff --git a/awx/ui_next/src/app.scss b/awx/ui_next/src/app.scss index b15ebc0a61..22a874d349 100644 --- a/awx/ui_next/src/app.scss +++ b/awx/ui_next/src/app.scss @@ -80,7 +80,6 @@ --pf-c-data-list__cell--PaddingTop: 16px; --pf-c-data-list__cell-cell--PaddingTop: 16px; - &.pf-c-data-list__cell--divider { --pf-c-data-list__cell-cell--MarginRight: 0; --pf-c-data-list__cell--PaddingTop: 12px; diff --git a/awx/ui_next/src/components/About/About.jsx b/awx/ui_next/src/components/About/About.jsx index 3b75c233d7..785ecf4a19 100644 --- a/awx/ui_next/src/components/About/About.jsx +++ b/awx/ui_next/src/components/About/About.jsx @@ -6,14 +6,14 @@ import { AboutModal, TextContent, TextList, - TextListItem + TextListItem, } from '@patternfly/react-core'; import { BrandName } from '../../variables'; import brandLogoImg from '../../../images/brand-logo.svg'; class About extends React.Component { - static createSpeechBubble (version) { + static createSpeechBubble(version) { let text = `${BrandName} ${version}`; let top = ''; let bottom = ''; @@ -30,20 +30,14 @@ class About extends React.Component { return top + text + bottom; } - constructor (props) { + constructor(props) { super(props); this.createSpeechBubble = this.constructor.createSpeechBubble.bind(this); } - render () { - const { - ansible_version, - version, - isOpen, - onClose, - i18n - } = this.props; + render() { + const { ansible_version, version, isOpen, onClose, i18n } = this.props; const speechBubble = this.createSpeechBubble(version); @@ -57,7 +51,7 @@ class About extends React.Component { brandImageAlt={i18n._(t`Brand Image`)} >
-          { speechBubble }
+          {speechBubble}
           {`
           \\
           \\   ^__^
@@ -72,7 +66,7 @@ class About extends React.Component {
             
               {i18n._(t`Ansible Version`)}
             
-            { ansible_version }
+            {ansible_version}
           
         
       
diff --git a/awx/ui_next/src/components/About/About.test.jsx b/awx/ui_next/src/components/About/About.test.jsx
index 28ef329ab9..caed85e454 100644
--- a/awx/ui_next/src/components/About/About.test.jsx
+++ b/awx/ui_next/src/components/About/About.test.jsx
@@ -7,17 +7,13 @@ describe('', () => {
   let closeButton;
   const onClose = jest.fn();
   test('initially renders without crashing', () => {
-    aboutWrapper = mountWithContexts(
-      
-    );
+    aboutWrapper = mountWithContexts();
     expect(aboutWrapper.length).toBe(1);
     aboutWrapper.unmount();
   });
 
   test('close button calls onClose handler', () => {
-    aboutWrapper = mountWithContexts(
-      
-    );
+    aboutWrapper = mountWithContexts();
     closeButton = aboutWrapper.find('AboutModalBoxCloseButton Button');
     closeButton.simulate('click');
     expect(onClose).toBeCalled();
diff --git a/awx/ui_next/src/components/AddRole/AddResourceRole.jsx b/awx/ui_next/src/components/AddRole/AddResourceRole.jsx
index d9dc7642f5..7bf58c3cb7 100644
--- a/awx/ui_next/src/components/AddRole/AddResourceRole.jsx
+++ b/awx/ui_next/src/components/AddRole/AddResourceRole.jsx
@@ -8,14 +8,13 @@ import SelectRoleStep from './SelectRoleStep';
 import SelectableCard from './SelectableCard';
 import { TeamsAPI, UsersAPI } from '../../api';
 
-const readUsers = async (queryParams) => UsersAPI.read(
-  Object.assign(queryParams, { is_superuser: false })
-);
+const readUsers = async queryParams =>
+  UsersAPI.read(Object.assign(queryParams, { is_superuser: false }));
 
-const readTeams = async (queryParams) => TeamsAPI.read(queryParams);
+const readTeams = async queryParams => TeamsAPI.read(queryParams);
 
 class AddResourceRole extends React.Component {
-  constructor (props) {
+  constructor(props) {
     super(props);
 
     this.state = {
@@ -25,67 +24,69 @@ class AddResourceRole extends React.Component {
       currentStepId: 1,
     };
 
-    this.handleResourceCheckboxClick = this.handleResourceCheckboxClick.bind(this);
+    this.handleResourceCheckboxClick = this.handleResourceCheckboxClick.bind(
+      this
+    );
     this.handleResourceSelect = this.handleResourceSelect.bind(this);
     this.handleRoleCheckboxClick = this.handleRoleCheckboxClick.bind(this);
     this.handleWizardNext = this.handleWizardNext.bind(this);
     this.handleWizardSave = this.handleWizardSave.bind(this);
   }
 
-  handleResourceCheckboxClick (user) {
+  handleResourceCheckboxClick(user) {
     const { selectedResourceRows } = this.state;
 
-    const selectedIndex = selectedResourceRows
-      .findIndex(selectedRow => selectedRow.id === user.id);
+    const selectedIndex = selectedResourceRows.findIndex(
+      selectedRow => selectedRow.id === user.id
+    );
 
     if (selectedIndex > -1) {
       selectedResourceRows.splice(selectedIndex, 1);
       this.setState({ selectedResourceRows });
     } else {
       this.setState(prevState => ({
-        selectedResourceRows: [...prevState.selectedResourceRows, user]
+        selectedResourceRows: [...prevState.selectedResourceRows, user],
       }));
     }
   }
 
-  handleRoleCheckboxClick (role) {
+  handleRoleCheckboxClick(role) {
     const { selectedRoleRows } = this.state;
 
-    const selectedIndex = selectedRoleRows
-      .findIndex(selectedRow => selectedRow.id === role.id);
+    const selectedIndex = selectedRoleRows.findIndex(
+      selectedRow => selectedRow.id === role.id
+    );
 
     if (selectedIndex > -1) {
       selectedRoleRows.splice(selectedIndex, 1);
       this.setState({ selectedRoleRows });
     } else {
       this.setState(prevState => ({
-        selectedRoleRows: [...prevState.selectedRoleRows, role]
+        selectedRoleRows: [...prevState.selectedRoleRows, role],
       }));
     }
   }
 
-  handleResourceSelect (resourceType) {
+  handleResourceSelect(resourceType) {
     this.setState({
       selectedResource: resourceType,
       selectedResourceRows: [],
-      selectedRoleRows: []
+      selectedRoleRows: [],
     });
   }
 
-  handleWizardNext (step) {
+  handleWizardNext(step) {
     this.setState({
       currentStepId: step.id,
     });
   }
 
-  async handleWizardSave () {
-    const {
-      onSave
-    } = this.props;
+  async handleWizardSave() {
+    const { onSave } = this.props;
     const {
       selectedResourceRows,
       selectedRoleRows,
-      selectedResource
+      selectedResource,
     } = this.state;
 
     try {
@@ -95,11 +96,17 @@ class AddResourceRole extends React.Component {
         for (let j = 0; j < selectedRoleRows.length; j++) {
           if (selectedResource === 'users') {
             roleRequests.push(
-              UsersAPI.associateRole(selectedResourceRows[i].id, selectedRoleRows[j].id)
+              UsersAPI.associateRole(
+                selectedResourceRows[i].id,
+                selectedRoleRows[j].id
+              )
             );
           } else if (selectedResource === 'teams') {
             roleRequests.push(
-              TeamsAPI.associateRole(selectedResourceRows[i].id, selectedRoleRows[j].id)
+              TeamsAPI.associateRole(
+                selectedResourceRows[i].id,
+                selectedRoleRows[j].id
+              )
             );
           }
         }
@@ -112,25 +119,21 @@ class AddResourceRole extends React.Component {
     }
   }
 
-  render () {
+  render() {
     const {
       selectedResource,
       selectedResourceRows,
       selectedRoleRows,
       currentStepId,
     } = this.state;
-    const {
-      onClose,
-      roles,
-      i18n
-    } = this.props;
+    const { onClose, roles, i18n } = this.props;
 
     const userColumns = [
-      { name: i18n._(t`Username`), key: 'username', isSortable: true }
+      { name: i18n._(t`Username`), key: 'username', isSortable: true },
     ];
 
     const teamColumns = [
-      { name: i18n._(t`Name`), key: 'name', isSortable: true }
+      { name: i18n._(t`Name`), key: 'name', isSortable: true },
     ];
 
     let wizardTitle = '';
@@ -164,7 +167,7 @@ class AddResourceRole extends React.Component {
             />
           
), - enableNext: selectedResource !== null + enableNext: selectedResource !== null, }, { id: 2, @@ -195,7 +198,7 @@ class AddResourceRole extends React.Component { )} ), - enableNext: selectedResourceRows.length > 0 + enableNext: selectedResourceRows.length > 0, }, { id: 3, @@ -211,8 +214,8 @@ class AddResourceRole extends React.Component { /> ), nextButtonText: i18n._(t`Save`), - enableNext: selectedRoleRows.length > 0 - } + enableNext: selectedRoleRows.length > 0, + }, ]; const currentStep = steps.find(step => step.id === currentStepId); @@ -236,11 +239,11 @@ class AddResourceRole extends React.Component { AddResourceRole.propTypes = { onClose: PropTypes.func.isRequired, onSave: PropTypes.func.isRequired, - roles: PropTypes.shape() + roles: PropTypes.shape(), }; AddResourceRole.defaultProps = { - roles: {} + roles: {}, }; export { AddResourceRole as _AddResourceRole }; diff --git a/awx/ui_next/src/components/AddRole/AddResourceRole.test.jsx b/awx/ui_next/src/components/AddRole/AddResourceRole.test.jsx index 00b3d50123..52f222bef4 100644 --- a/awx/ui_next/src/components/AddRole/AddResourceRole.test.jsx +++ b/awx/ui_next/src/components/AddRole/AddResourceRole.test.jsx @@ -11,23 +11,20 @@ describe('<_AddResourceRole />', () => { UsersAPI.read.mockResolvedValue({ data: { count: 2, - results: [ - { id: 1, username: 'foo' }, - { id: 2, username: 'bar' } - ] - } + results: [{ id: 1, username: 'foo' }, { id: 2, username: 'bar' }], + }, }); const roles = { admin_role: { description: 'Can manage all aspects of the organization', id: 1, - name: 'Admin' + name: 'Admin', }, execute_role: { description: 'May run any executable resources in the organization', id: 2, - name: 'Execute' - } + name: 'Execute', + }, }; test('initially renders without crashing', () => { shallow( @@ -53,26 +50,28 @@ describe('<_AddResourceRole />', () => { { description: 'Can manage all aspects of the organization', name: 'Admin', - id: 1 - } - ] + id: 1, + }, + ], }); wrapper.instance().handleRoleCheckboxClick({ description: 'Can manage all aspects of the organization', name: 'Admin', - id: 1 + id: 1, }); expect(wrapper.state('selectedRoleRows')).toEqual([]); wrapper.instance().handleRoleCheckboxClick({ description: 'Can manage all aspects of the organization', name: 'Admin', - id: 1 + id: 1, }); - expect(wrapper.state('selectedRoleRows')).toEqual([{ - description: 'Can manage all aspects of the organization', - name: 'Admin', - id: 1 - }]); + expect(wrapper.state('selectedRoleRows')).toEqual([ + { + description: 'Can manage all aspects of the organization', + name: 'Admin', + id: 1, + }, + ]); }); test('handleResourceCheckboxClick properly updates state', () => { const wrapper = shallow( @@ -87,32 +86,31 @@ describe('<_AddResourceRole />', () => { selectedResourceRows: [ { id: 1, - username: 'foobar' - } - ] + username: 'foobar', + }, + ], }); wrapper.instance().handleResourceCheckboxClick({ id: 1, - username: 'foobar' + username: 'foobar', }); expect(wrapper.state('selectedResourceRows')).toEqual([]); wrapper.instance().handleResourceCheckboxClick({ id: 1, - username: 'foobar' + username: 'foobar', }); - expect(wrapper.state('selectedResourceRows')).toEqual([{ - id: 1, - username: 'foobar' - }]); + expect(wrapper.state('selectedResourceRows')).toEqual([ + { + id: 1, + username: 'foobar', + }, + ]); }); test('clicking user/team cards updates state', () => { const spy = jest.spyOn(_AddResourceRole.prototype, 'handleResourceSelect'); const wrapper = mountWithContexts( - {}} - onSave={() => {}} - roles={roles} - />, { context: { network: { handleHttpError: () => {} } } } + {}} onSave={() => {}} roles={roles} />, + { context: { network: { handleHttpError: () => {} } } } ).find('AddResourceRole'); const selectableCardWrapper = wrapper.find('SelectableCard'); expect(selectableCardWrapper.length).toBe(2); @@ -137,16 +135,16 @@ describe('<_AddResourceRole />', () => { selectedResourceRows: [ { id: 1, - username: 'foobar' - } + username: 'foobar', + }, ], selectedRoleRows: [ { description: 'Can manage all aspects of the organization', id: 1, - name: 'Admin' - } - ] + name: 'Admin', + }, + ], }); wrapper.instance().handleResourceSelect('users'); expect(wrapper.state()).toEqual({ @@ -160,38 +158,35 @@ describe('<_AddResourceRole />', () => { selectedResource: 'teams', selectedResourceRows: [], selectedRoleRows: [], - currentStepId: 1 + currentStepId: 1, }); }); test('handleWizardSave makes correct api calls, calls onSave when done', async () => { const handleSave = jest.fn(); const wrapper = mountWithContexts( - {}} - onSave={handleSave} - roles={roles} - />, { context: { network: { handleHttpError: () => {} } } } + {}} onSave={handleSave} roles={roles} />, + { context: { network: { handleHttpError: () => {} } } } ).find('AddResourceRole'); wrapper.setState({ selectedResource: 'users', selectedResourceRows: [ { id: 1, - username: 'foobar' - } + username: 'foobar', + }, ], selectedRoleRows: [ { description: 'Can manage all aspects of the organization', id: 1, - name: 'Admin' + name: 'Admin', }, { description: 'May run any executable resources in the organization', id: 2, - name: 'Execute' - } - ] + name: 'Execute', + }, + ], }); await wrapper.instance().handleWizardSave(); expect(UsersAPI.associateRole).toHaveBeenCalledTimes(2); @@ -201,21 +196,21 @@ describe('<_AddResourceRole />', () => { selectedResourceRows: [ { id: 1, - name: 'foobar' - } + name: 'foobar', + }, ], selectedRoleRows: [ { description: 'Can manage all aspects of the organization', id: 1, - name: 'Admin' + name: 'Admin', }, { description: 'May run any executable resources in the organization', id: 2, - name: 'Execute' - } - ] + name: 'Execute', + }, + ], }); await wrapper.instance().handleWizardSave(); expect(TeamsAPI.associateRole).toHaveBeenCalledTimes(2); diff --git a/awx/ui_next/src/components/AddRole/CheckboxCard.jsx b/awx/ui_next/src/components/AddRole/CheckboxCard.jsx index 375cf2d193..cd7a04114c 100644 --- a/awx/ui_next/src/components/AddRole/CheckboxCard.jsx +++ b/awx/ui_next/src/components/AddRole/CheckboxCard.jsx @@ -1,31 +1,30 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; -import { - Checkbox -} from '@patternfly/react-core'; +import { Checkbox } from '@patternfly/react-core'; class CheckboxCard extends Component { - render () { + render() { const { name, description, isSelected, onSelect, itemId } = this.props; return ( -
{name}
{description}
- )} + } value={itemId} />
@@ -38,13 +37,13 @@ CheckboxCard.propTypes = { description: PropTypes.string, isSelected: PropTypes.bool, onSelect: PropTypes.func, - itemId: PropTypes.number.isRequired + itemId: PropTypes.number.isRequired, }; CheckboxCard.defaultProps = { description: '', isSelected: false, - onSelect: null + onSelect: null, }; export default CheckboxCard; diff --git a/awx/ui_next/src/components/AddRole/CheckboxCard.test.jsx b/awx/ui_next/src/components/AddRole/CheckboxCard.test.jsx index ef38209101..637cbac403 100644 --- a/awx/ui_next/src/components/AddRole/CheckboxCard.test.jsx +++ b/awx/ui_next/src/components/AddRole/CheckboxCard.test.jsx @@ -5,12 +5,7 @@ import CheckboxCard from './CheckboxCard'; describe('', () => { let wrapper; test('initially renders without crashing', () => { - wrapper = shallow( - - ); + wrapper = shallow(); expect(wrapper.length).toBe(1); }); }); diff --git a/awx/ui_next/src/components/AddRole/SelectResourceStep.jsx b/awx/ui_next/src/components/AddRole/SelectResourceStep.jsx index 0d85052736..77c06e99b5 100644 --- a/awx/ui_next/src/components/AddRole/SelectResourceStep.jsx +++ b/awx/ui_next/src/components/AddRole/SelectResourceStep.jsx @@ -10,7 +10,7 @@ import SelectedList from '../SelectedList'; import { getQSConfig, parseNamespacedQueryString } from '../../util/qs'; class SelectResourceStep extends React.Component { - constructor (props) { + constructor(props) { super(props); this.state = { @@ -27,20 +27,23 @@ class SelectResourceStep extends React.Component { }); } - componentDidMount () { + componentDidMount() { this.readResourceList(); } - componentDidUpdate (prevProps) { + componentDidUpdate(prevProps) { const { location } = this.props; if (location !== prevProps.location) { this.readResourceList(); } } - async readResourceList () { + async readResourceList() { const { onSearch, location } = this.props; - const queryParams = parseNamespacedQueryString(this.qsConfig, location.search); + const queryParams = parseNamespacedQueryString( + this.qsConfig, + location.search + ); this.setState({ isLoading: true, @@ -65,14 +68,8 @@ class SelectResourceStep extends React.Component { } } - render () { - const { - isInitialized, - isLoading, - count, - error, - resources, - } = this.state; + render() { + const { isInitialized, isLoading, count, error, resources } = this.state; const { columns, @@ -81,12 +78,12 @@ class SelectResourceStep extends React.Component { selectedLabel, selectedResourceRows, itemName, - i18n + i18n, } = this.props; return ( - {isLoading && (
{i18n._(t`Loading...`)}
)} + {isLoading &&
{i18n._(t`Loading...`)}
} {isInitialized && ( {selectedResourceRows.length > 0 && ( @@ -113,14 +110,14 @@ class SelectResourceStep extends React.Component { onSelect={() => onRowClick(item)} /> )} - renderToolbar={(props) => ( + renderToolbar={props => ( )} showPageSizeOptions={false} /> )} - { error ?
error
: '' } + {error ?
error
: ''}
); } diff --git a/awx/ui_next/src/components/AddRole/SelectResourceStep.test.jsx b/awx/ui_next/src/components/AddRole/SelectResourceStep.test.jsx index 0a43bffa75..aab32c7df6 100644 --- a/awx/ui_next/src/components/AddRole/SelectResourceStep.test.jsx +++ b/awx/ui_next/src/components/AddRole/SelectResourceStep.test.jsx @@ -6,9 +6,7 @@ import { sleep } from '../../../testUtils/testUtils'; import SelectResourceStep from './SelectResourceStep'; describe('', () => { - const columns = [ - { name: 'Username', key: 'username', isSortable: true } - ]; + const columns = [{ name: 'Username', key: 'username', isSortable: true }]; afterEach(() => { jest.restoreAllMocks(); }); @@ -30,9 +28,9 @@ describe('', () => { count: 2, results: [ { id: 1, username: 'foo', url: 'item/1' }, - { id: 2, username: 'bar', url: 'item/2' } - ] - } + { id: 2, username: 'bar', url: 'item/2' }, + ], + }, }); mountWithContexts( ', () => { expect(handleSearch).toHaveBeenCalledWith({ order_by: 'username', page: 1, - page_size: 5 + page_size: 5, }); }); test('readResourceList properly adds rows to state', async () => { - const selectedResourceRows = [ - { id: 1, username: 'foo', url: 'item/1' } - ]; + const selectedResourceRows = [{ id: 1, username: 'foo', url: 'item/1' }]; const handleSearch = jest.fn().mockResolvedValue({ data: { count: 2, results: [ { id: 1, username: 'foo', url: 'item/1' }, - { id: 2, username: 'bar', url: 'item/2' } - ] - } + { id: 2, username: 'bar', url: 'item/2' }, + ], + }, }); const history = createMemoryHistory({ - initialEntries: ['/organizations/1/access?resource.page=1&resource.order_by=-username'], + initialEntries: [ + '/organizations/1/access?resource.page=1&resource.order_by=-username', + ], }); const wrapper = await mountWithContexts( ', () => { onSearch={handleSearch} selectedResourceRows={selectedResourceRows} sortedColumnKey="username" - />, { context: { router: { history, route: { location: history.location } } } } + />, + { + context: { router: { history, route: { location: history.location } } }, + } ).find('SelectResourceStep'); await wrapper.instance().readResourceList(); expect(handleSearch).toHaveBeenCalledWith({ @@ -84,7 +85,7 @@ describe('', () => { }); expect(wrapper.state('resources')).toEqual([ { id: 1, username: 'foo', url: 'item/1' }, - { id: 2, username: 'bar', url: 'item/2' } + { id: 2, username: 'bar', url: 'item/2' }, ]); }); @@ -94,8 +95,8 @@ describe('', () => { count: 2, results: [ { id: 1, username: 'foo', url: 'item/1' }, - { id: 2, username: 'bar', url: 'item/2' } - ] + { id: 2, username: 'bar', url: 'item/2' }, + ], }; const wrapper = mountWithContexts( ', () => { wrapper.update(); const checkboxListItemWrapper = wrapper.find('CheckboxListItem'); expect(checkboxListItemWrapper.length).toBe(2); - checkboxListItemWrapper.first().find('input[type="checkbox"]') + checkboxListItemWrapper + .first() + .find('input[type="checkbox"]') .simulate('change', { target: { checked: true } }); expect(handleRowClick).toHaveBeenCalledWith(data.results[0]); }); diff --git a/awx/ui_next/src/components/AddRole/SelectRoleStep.jsx b/awx/ui_next/src/components/AddRole/SelectRoleStep.jsx index 98caad4728..9af70da8d7 100644 --- a/awx/ui_next/src/components/AddRole/SelectRoleStep.jsx +++ b/awx/ui_next/src/components/AddRole/SelectRoleStep.jsx @@ -8,7 +8,7 @@ import CheckboxCard from './CheckboxCard'; import SelectedList from '../SelectedList'; class RolesStep extends React.Component { - render () { + render() { const { onRolesClick, roles, @@ -16,7 +16,7 @@ class RolesStep extends React.Component { selectedListLabel, selectedResourceRows, selectedRoleRows, - i18n + i18n, } = this.props; return ( @@ -32,14 +32,21 @@ class RolesStep extends React.Component { /> )}
-
+
{Object.keys(roles).map(role => ( item.id === roles[role].id) - } + isSelected={selectedRoleRows.some( + item => item.id === roles[role].id + )} key={roles[role].id} name={roles[role].name} onSelect={() => onRolesClick(roles[role])} @@ -57,7 +64,7 @@ RolesStep.propTypes = { selectedListKey: PropTypes.string, selectedListLabel: PropTypes.string, selectedResourceRows: PropTypes.arrayOf(PropTypes.object), - selectedRoleRows: PropTypes.arrayOf(PropTypes.object) + selectedRoleRows: PropTypes.arrayOf(PropTypes.object), }; RolesStep.defaultProps = { @@ -65,7 +72,7 @@ RolesStep.defaultProps = { selectedListKey: 'name', selectedListLabel: null, selectedResourceRows: [], - selectedRoleRows: [] + selectedRoleRows: [], }; export default withI18n()(RolesStep); diff --git a/awx/ui_next/src/components/AddRole/SelectRoleStep.test.jsx b/awx/ui_next/src/components/AddRole/SelectRoleStep.test.jsx index b8c799a1a9..f280604328 100644 --- a/awx/ui_next/src/components/AddRole/SelectRoleStep.test.jsx +++ b/awx/ui_next/src/components/AddRole/SelectRoleStep.test.jsx @@ -9,26 +9,26 @@ describe('', () => { project_admin_role: { id: 1, name: 'Project Admin', - description: 'Can manage all projects of the organization' + description: 'Can manage all projects of the organization', }, execute_role: { id: 2, name: 'Execute', - description: 'May run any executable resources in the organization' - } + description: 'May run any executable resources in the organization', + }, }; const selectedRoles = [ { id: 1, name: 'Project Admin', - description: 'Can manage all projects of the organization' - } + description: 'Can manage all projects of the organization', + }, ]; const selectedResourceRows = [ { id: 1, - name: 'foo' - } + name: 'foo', + }, ]; test('initially renders without crashing', () => { wrapper = shallow( @@ -57,7 +57,7 @@ describe('', () => { expect(onRolesClick).toBeCalledWith({ id: 1, name: 'Project Admin', - description: 'Can manage all projects of the organization' + description: 'Can manage all projects of the organization', }); wrapper.unmount(); }); diff --git a/awx/ui_next/src/components/AddRole/SelectableCard.jsx b/awx/ui_next/src/components/AddRole/SelectableCard.jsx index f3276ea03c..cebc795a0e 100644 --- a/awx/ui_next/src/components/AddRole/SelectableCard.jsx +++ b/awx/ui_next/src/components/AddRole/SelectableCard.jsx @@ -7,7 +7,10 @@ const SelectableItem = styled.div` border: 1px solid var(--pf-global--BorderColor--200); border-radius: var(--pf-global--BorderRadius--sm); border: 1px solid; - border-color: ${props => (props.isSelected ? 'var(--pf-global--active-color--100)' : 'var(--pf-global--BorderColor--200)')}; + border-color: ${props => + props.isSelected + ? 'var(--pf-global--active-color--100)' + : 'var(--pf-global--BorderColor--200)'}; margin-right: 20px; font-weight: bold; display: flex; @@ -17,7 +20,8 @@ const SelectableItem = styled.div` const Indicator = styled.div` display: flex; flex: 0 0 5px; - background-color: ${props => (props.isSelected ? 'var(--pf-global--active-color--100)' : null)}; + background-color: ${props => + props.isSelected ? 'var(--pf-global--active-color--100)' : null}; `; const Label = styled.div` @@ -28,12 +32,8 @@ const Label = styled.div` `; class SelectableCard extends Component { - render () { - const { - label, - onClick, - isSelected - } = this.props; + render() { + const { label, onClick, isSelected } = this.props; return ( - - + ); @@ -56,12 +53,12 @@ class SelectableCard extends Component { SelectableCard.propTypes = { label: PropTypes.string, onClick: PropTypes.func.isRequired, - isSelected: PropTypes.bool + isSelected: PropTypes.bool, }; SelectableCard.defaultProps = { label: '', - isSelected: false + isSelected: false, }; export default SelectableCard; diff --git a/awx/ui_next/src/components/AddRole/SelectableCard.test.jsx b/awx/ui_next/src/components/AddRole/SelectableCard.test.jsx index beef8f24cf..a273161f55 100644 --- a/awx/ui_next/src/components/AddRole/SelectableCard.test.jsx +++ b/awx/ui_next/src/components/AddRole/SelectableCard.test.jsx @@ -6,21 +6,12 @@ describe('', () => { let wrapper; const onClick = jest.fn(); test('initially renders without crashing when not selected', () => { - wrapper = shallow( - - ); + wrapper = shallow(); expect(wrapper.length).toBe(1); wrapper.unmount(); }); test('initially renders without crashing when selected', () => { - wrapper = shallow( - - ); + wrapper = shallow(); expect(wrapper.length).toBe(1); wrapper.unmount(); }); diff --git a/awx/ui_next/src/components/AlertModal/AlertModal.jsx b/awx/ui_next/src/components/AlertModal/AlertModal.jsx index bdfd4e9761..783f08f0a5 100644 --- a/awx/ui_next/src/components/AlertModal/AlertModal.jsx +++ b/awx/ui_next/src/components/AlertModal/AlertModal.jsx @@ -1,21 +1,26 @@ import React from 'react'; +import { Modal } from '@patternfly/react-core'; + import { - Modal -} from '@patternfly/react-core'; + ExclamationTriangleIcon, + ExclamationCircleIcon, + InfoCircleIcon, + CheckCircleIcon, +} from '@patternfly/react-icons'; -import { ExclamationTriangleIcon, ExclamationCircleIcon, InfoCircleIcon, CheckCircleIcon } from '@patternfly/react-icons'; - -const getIcon = (variant) => { +const getIcon = variant => { let icon; if (variant === 'warning') { - icon = (); + icon = ; } else if (variant === 'danger') { - icon = (); - } if (variant === 'info') { - icon = (); - } if (variant === 'success') { - icon = (); + icon = ; + } + if (variant === 'info') { + icon = ; + } + if (variant === 'success') { + icon = ; } return icon; }; @@ -24,7 +29,11 @@ export default ({ variant, children, ...props }) => { const { isOpen = null } = props; props.isOpen = Boolean(isOpen); return ( - + {children} {getIcon(variant)} diff --git a/awx/ui_next/src/components/AlertModal/AlertModal.test.jsx b/awx/ui_next/src/components/AlertModal/AlertModal.test.jsx index f5f2d3a574..4c9389543f 100644 --- a/awx/ui_next/src/components/AlertModal/AlertModal.test.jsx +++ b/awx/ui_next/src/components/AlertModal/AlertModal.test.jsx @@ -5,9 +5,7 @@ import AlertModal from './AlertModal'; describe('AlertModal', () => { test('renders the expected content', () => { - const wrapper = mount( - - ); + const wrapper = mount(); expect(wrapper).toHaveLength(1); }); }); diff --git a/awx/ui_next/src/components/AnsibleSelect/AnsibleSelect.jsx b/awx/ui_next/src/components/AnsibleSelect/AnsibleSelect.jsx index 60bf385c88..87f31d7bc5 100644 --- a/awx/ui_next/src/components/AnsibleSelect/AnsibleSelect.jsx +++ b/awx/ui_next/src/components/AnsibleSelect/AnsibleSelect.jsx @@ -2,24 +2,21 @@ import React from 'react'; import PropTypes from 'prop-types'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; -import { - FormSelect, - FormSelectOption, -} from '@patternfly/react-core'; +import { FormSelect, FormSelectOption } from '@patternfly/react-core'; class AnsibleSelect extends React.Component { - constructor (props) { + constructor(props) { super(props); this.onSelectChange = this.onSelectChange.bind(this); } - onSelectChange (val, event) { + onSelectChange(val, event) { const { onChange, name } = this.props; event.target.name = name; onChange(event, val); } - render () { + render() { const { value, data, i18n } = this.props; return ( @@ -28,7 +25,7 @@ class AnsibleSelect extends React.Component { onChange={this.onSelectChange} aria-label={i18n._(t`Select Input`)} > - {data.map((datum) => ( + {data.map(datum => ( ', () => { @@ -21,7 +21,7 @@ describe('', () => { { }} + onChange={() => {}} data={mockData} /> ); @@ -33,7 +33,7 @@ describe('', () => { { }} + onChange={() => {}} data={mockData} /> ); @@ -47,7 +47,7 @@ describe('', () => { { }} + onChange={() => {}} data={mockData} /> ); diff --git a/awx/ui_next/src/components/Background/Background.jsx b/awx/ui_next/src/components/Background/Background.jsx index 91a4fd733a..d44711fa17 100644 --- a/awx/ui_next/src/components/Background/Background.jsx +++ b/awx/ui_next/src/components/Background/Background.jsx @@ -1,9 +1,6 @@ import React, { Fragment } from 'react'; -import { - BackgroundImage, - BackgroundImageSrc, -} from '@patternfly/react-core'; +import { BackgroundImage, BackgroundImageSrc } from '@patternfly/react-core'; import bgFilter from '@patternfly/patternfly/assets/images/background-filter.svg'; const backgroundImageConfig = { @@ -18,6 +15,6 @@ const backgroundImageConfig = { export default ({ children }) => ( - { children } + {children} ); diff --git a/awx/ui_next/src/components/Background/Background.test.jsx b/awx/ui_next/src/components/Background/Background.test.jsx index ffab5fb164..382bdba66c 100644 --- a/awx/ui_next/src/components/Background/Background.test.jsx +++ b/awx/ui_next/src/components/Background/Background.test.jsx @@ -5,7 +5,11 @@ import Background from './Background'; describe('Background', () => { test('renders the expected content', () => { - const wrapper = mount(
); + const wrapper = mount( + +
+ + ); expect(wrapper).toHaveLength(1); expect(wrapper.find('BackgroundImage')).toHaveLength(1); expect(wrapper.find('#test')).toHaveLength(1); diff --git a/awx/ui_next/src/components/BrandLogo/BrandLogo.jsx b/awx/ui_next/src/components/BrandLogo/BrandLogo.jsx index 08bdc8c5a3..7943d1c287 100644 --- a/awx/ui_next/src/components/BrandLogo/BrandLogo.jsx +++ b/awx/ui_next/src/components/BrandLogo/BrandLogo.jsx @@ -5,57 +5,57 @@ import { t } from '@lingui/macro'; import styled from 'styled-components'; const ST0 = styled.g` - display:none; + display: none; `; const ST1 = styled.path` - display:inline; - fill:#ED1C24; + display: inline; + fill: #ed1c24; `; const ST2 = styled.path` - fill:#42210B; + fill: #42210b; `; const ST3 = styled.path` - fill:#FFFFFF; + fill: #ffffff; `; const ST4 = styled.path` - fill:#C69C6D; - stroke:#8C6239; - stroke-width:5; - stroke-miterlimit:10; + fill: #c69c6d; + stroke: #8c6239; + stroke-width: 5; + stroke-miterlimit: 10; `; const ST5 = styled.path` - fill:#FFFFFF; - stroke:#42210B; - stroke-width:3; - stroke-miterlimit:10; + fill: #ffffff; + stroke: #42210b; + stroke-width: 3; + stroke-miterlimit: 10; `; const ST6 = styled.ellipse` - fill:#ED1C24; - stroke:#8C6239; - stroke-width:5; - stroke-miterlimit:10; + fill: #ed1c24; + stroke: #8c6239; + stroke-width: 5; + stroke-miterlimit: 10; `; const ST7 = styled.path` - fill:#A67C52; + fill: #a67c52; `; const ST8 = styled.path` - fill: #ED1C24; + fill: #ed1c24; `; const ST9 = styled.ellipse` - fill:#42210B; + fill: #42210b; `; class BrandLogo extends Component { - render () { + render() { const { i18n } = this.props; return ( - - - - - - + + + + + + { describe('', () => { test('initially renders without crashing', () => { - logoWrapper = mountWithContexts( - - ); + logoWrapper = mountWithContexts(); findChildren(); expect(logoWrapper.length).toBe(1); expect(brandLogoElem.length).toBe(1); diff --git a/awx/ui_next/src/components/Breadcrumbs/Breadcrumbs.jsx b/awx/ui_next/src/components/Breadcrumbs/Breadcrumbs.jsx index 503c54ee03..65b5978c92 100644 --- a/awx/ui_next/src/components/Breadcrumbs/Breadcrumbs.jsx +++ b/awx/ui_next/src/components/Breadcrumbs/Breadcrumbs.jsx @@ -5,13 +5,9 @@ import { PageSectionVariants, Breadcrumb, BreadcrumbItem, - BreadcrumbHeading as PFBreadcrumbHeading + BreadcrumbHeading as PFBreadcrumbHeading, } from '@patternfly/react-core'; -import { - Link, - Route, - withRouter -} from 'react-router-dom'; +import { Link, Route, withRouter } from 'react-router-dom'; import styled from 'styled-components'; const PageSection = styled(PFPageSection)` @@ -20,7 +16,7 @@ const PageSection = styled(PFPageSection)` `; const BreadcrumbHeading = styled(PFBreadcrumbHeading)` - --pf-c-breadcrumb__heading--FontSize: 20px; + --pf-c-breadcrumb__heading--FontSize: 20px; line-height: 24px; flex: 100%; `; @@ -29,13 +25,13 @@ const Breadcrumbs = ({ breadcrumbConfig }) => { const { light } = PageSectionVariants; return ( - + } + render={props => ( + + )} /> @@ -47,19 +43,13 @@ const Crumb = ({ breadcrumbConfig, match }) => { let crumbElement = ( - - {crumb} - + {crumb} ); if (match.isExact) { crumbElement = ( - - {crumb} - + {crumb} ); } @@ -72,7 +62,9 @@ const Crumb = ({ breadcrumbConfig, match }) => { {crumbElement} } + render={props => ( + + )} /> ); diff --git a/awx/ui_next/src/components/Breadcrumbs/Breadcrumbs.test.jsx b/awx/ui_next/src/components/Breadcrumbs/Breadcrumbs.test.jsx index 71d1265fe9..83e5e8c3fe 100644 --- a/awx/ui_next/src/components/Breadcrumbs/Breadcrumbs.test.jsx +++ b/awx/ui_next/src/components/Breadcrumbs/Breadcrumbs.test.jsx @@ -13,7 +13,7 @@ describe('', () => { '/foo': 'Foo', '/foo/1': 'One', '/foo/1/bar': 'Bar', - '/foo/1/bar/fiz': 'Fiz' + '/foo/1/bar/fiz': 'Fiz', }; const findChildren = () => { @@ -25,9 +25,7 @@ describe('', () => { test('initially renders succesfully', () => { breadcrumbWrapper = mount( - + ); @@ -55,13 +53,13 @@ describe('', () => { routes.forEach(([location, crumbLength]) => { breadcrumbWrapper = mount( - + ); - expect(breadcrumbWrapper.find('BreadcrumbItem')).toHaveLength(crumbLength); + expect(breadcrumbWrapper.find('BreadcrumbItem')).toHaveLength( + crumbLength + ); breadcrumbWrapper.unmount(); }); }); diff --git a/awx/ui_next/src/components/ButtonGroup.jsx b/awx/ui_next/src/components/ButtonGroup.jsx index bc6a8c8506..c350047b49 100644 --- a/awx/ui_next/src/components/ButtonGroup.jsx +++ b/awx/ui_next/src/components/ButtonGroup.jsx @@ -21,12 +21,8 @@ const Group = styled.div` } `; -function ButtonGroup ({ children }) { - return ( - - {children} - - ); +function ButtonGroup({ children }) { + return {children}; } export default ButtonGroup; diff --git a/awx/ui_next/src/components/CardCloseButton/CardCloseButton.jsx b/awx/ui_next/src/components/CardCloseButton/CardCloseButton.jsx index c41de8132c..3802d1e9e0 100644 --- a/awx/ui_next/src/components/CardCloseButton/CardCloseButton.jsx +++ b/awx/ui_next/src/components/CardCloseButton/CardCloseButton.jsx @@ -8,13 +8,13 @@ import { t } from '@lingui/macro'; import styled from 'styled-components'; const Link = styled(RRLink)` - position: absolute; - top: 5px; - right: 4px; - color: var(--pf-c-button--m-plain--Color); + position: absolute; + top: 5px; + right: 4px; + color: var(--pf-c-button--m-plain--Color); `; -function CardCloseButton ({ linkTo, i18n, i18nHash, ...props }) { +function CardCloseButton({ linkTo, i18n, i18nHash, ...props }) { if (linkTo) { return ( + ); diff --git a/awx/ui_next/src/components/CheckboxListItem/CheckboxListItem.jsx b/awx/ui_next/src/components/CheckboxListItem/CheckboxListItem.jsx index 088d3cae39..903ff83606 100644 --- a/awx/ui_next/src/components/CheckboxListItem/CheckboxListItem.jsx +++ b/awx/ui_next/src/components/CheckboxListItem/CheckboxListItem.jsx @@ -10,12 +10,7 @@ import { import VerticalSeparator from '../VerticalSeparator'; -const CheckboxListItem = ({ - itemId, - name, - isSelected, - onSelect, -}) => ( +const CheckboxListItem = ({ itemId, name, isSelected, onSelect }) => ( - - - , - - - - ]} + + + , + + + , + ]} /> diff --git a/awx/ui_next/src/components/Chip/Chip.jsx b/awx/ui_next/src/components/Chip/Chip.jsx index 0186f0a081..f8837812f0 100644 --- a/awx/ui_next/src/components/Chip/Chip.jsx +++ b/awx/ui_next/src/components/Chip/Chip.jsx @@ -1,4 +1,3 @@ - import { Chip } from '@patternfly/react-core'; import styled from 'styled-components'; @@ -12,7 +11,9 @@ export default styled(Chip)` padding: 3px 8px; } - ${props => (props.isOverflowChip && ` + ${props => + props.isOverflowChip && + ` padding: 0; - `)} + `} `; diff --git a/awx/ui_next/src/components/Chip/ChipGroup.jsx b/awx/ui_next/src/components/Chip/ChipGroup.jsx index 6e9ff98156..df00f86eaa 100644 --- a/awx/ui_next/src/components/Chip/ChipGroup.jsx +++ b/awx/ui_next/src/components/Chip/ChipGroup.jsx @@ -7,10 +7,11 @@ const ChipGroup = ({ children, className, showOverflowAfter, ...props }) => { const [isExpanded, setIsExpanded] = useState(!showOverflowAfter); const toggleIsOpen = () => setIsExpanded(!isExpanded); - const mappedChildren = React.Children.map(children, c => ( + const mappedChildren = React.Children.map(children, c => React.cloneElement(c, { component: 'li' }) - )); - const showOverflowToggle = showOverflowAfter && children.length > showOverflowAfter; + ); + const showOverflowToggle = + showOverflowAfter && children.length > showOverflowAfter; const numToShow = isExpanded ? children.length : Math.min(showOverflowAfter, children.length); diff --git a/awx/ui_next/src/components/Chip/ChipGroup.test.jsx b/awx/ui_next/src/components/Chip/ChipGroup.test.jsx index 836d0f8ba6..f0b4a701fa 100644 --- a/awx/ui_next/src/components/Chip/ChipGroup.test.jsx +++ b/awx/ui_next/src/components/Chip/ChipGroup.test.jsx @@ -57,7 +57,12 @@ describe('', () => { }); wrapper.update(); expect(wrapper.find(Chip)).toHaveLength(8); - expect(wrapper.find(Chip).at(7).text()).toEqual('Show Less'); + expect( + wrapper + .find(Chip) + .at(7) + .text() + ).toEqual('Show Less'); act(() => { const toggle2 = wrapper.find(Chip).at(7); expect(toggle2.prop('isOverflowChip')).toBe(true); diff --git a/awx/ui_next/src/components/CodeMirrorInput/CodeMirrorInput.jsx b/awx/ui_next/src/components/CodeMirrorInput/CodeMirrorInput.jsx index 9c01c8a231..06596aa8a9 100644 --- a/awx/ui_next/src/components/CodeMirrorInput/CodeMirrorInput.jsx +++ b/awx/ui_next/src/components/CodeMirrorInput/CodeMirrorInput.jsx @@ -17,11 +17,13 @@ const CodeMirror = styled(ReactCodeMirror)` } & > .CodeMirror { - height: ${props => (props.rows * LINE_HEIGHT + PADDING)}px; + height: ${props => props.rows * LINE_HEIGHT + PADDING}px; font-family: var(--pf-global--FontFamily--monospace); } - ${props => props.hasErrors && ` + ${props => + props.hasErrors && + ` && { --pf-c-form-control--PaddingRight: var(--pf-c-form-control--invalid--PaddingRight); --pf-c-form-control--BorderBottomColor: var(--pf-c-form-control--invalid--BorderBottomColor); @@ -32,7 +34,7 @@ const CodeMirror = styled(ReactCodeMirror)` }`} `; -function CodeMirrorInput ({ value, onChange, mode, readOnly, hasErrors, rows }) { +function CodeMirrorInput({ value, onChange, mode, readOnly, hasErrors, rows }) { return ( diff --git a/awx/ui_next/src/components/CodeMirrorInput/CodeMirrorInput.test.jsx b/awx/ui_next/src/components/CodeMirrorInput/CodeMirrorInput.test.jsx index 9198328543..aaf9d33118 100644 --- a/awx/ui_next/src/components/CodeMirrorInput/CodeMirrorInput.test.jsx +++ b/awx/ui_next/src/components/CodeMirrorInput/CodeMirrorInput.test.jsx @@ -10,11 +10,7 @@ describe('CodeMirrorInput', () => { it('should trigger onChange prop', () => { const onChange = jest.fn(); const wrapper = mount( - + ); const codemirror = wrapper.find('Controlled'); expect(codemirror.prop('mode')).toEqual('yaml'); @@ -26,12 +22,7 @@ describe('CodeMirrorInput', () => { it('should render in read only mode', () => { const onChange = jest.fn(); const wrapper = mount( - + ); const codemirror = wrapper.find('Controlled'); expect(codemirror.prop('options').readOnly).toEqual(true); diff --git a/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx b/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx index 0d88e795d5..f7452d915f 100644 --- a/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx +++ b/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx @@ -15,7 +15,7 @@ const SmallButton = styled(Button)` font-size: var(--pf-global--FontSize--xs); `; -function VariablesField ({ id, name, label, readOnly }) { +function VariablesField({ id, name, label, readOnly }) { const [mode, setMode] = useState(YAML_MODE); return ( @@ -25,13 +25,17 @@ function VariablesField ({ id, name, label, readOnly }) {
- + { - if (mode === YAML_MODE) { return; } + if (mode === YAML_MODE) { + return; + } try { form.setFieldValue(name, jsonToYaml(field.value)); setMode(YAML_MODE); @@ -45,7 +49,9 @@ function VariablesField ({ id, name, label, readOnly }) { { - if (mode === JSON_MODE) { return; } + if (mode === JSON_MODE) { + return; + } try { form.setFieldValue(name, yamlToJson(field.value)); setMode(JSON_MODE); @@ -64,7 +70,7 @@ function VariablesField ({ id, name, label, readOnly }) { mode={mode} readOnly={readOnly} {...field} - onChange={(value) => { + onChange={value => { form.setFieldValue(name, value); }} hasErrors={!!form.errors[field.name]} @@ -76,7 +82,7 @@ function VariablesField ({ id, name, label, readOnly }) { > {form.errors[field.name]}
- ) : null } + ) : null}
)} /> diff --git a/awx/ui_next/src/components/CodeMirrorInput/VariablesField.test.jsx b/awx/ui_next/src/components/CodeMirrorInput/VariablesField.test.jsx index e1fb8221af..07a9772118 100644 --- a/awx/ui_next/src/components/CodeMirrorInput/VariablesField.test.jsx +++ b/awx/ui_next/src/components/CodeMirrorInput/VariablesField.test.jsx @@ -15,11 +15,7 @@ describe('VariablesField', () => { ( - + )} /> ); @@ -33,11 +29,7 @@ describe('VariablesField', () => { ( - + )} /> ); @@ -63,15 +55,14 @@ describe('VariablesField', () => { ( - + )} /> ); - wrapper.find('Button').at(1).simulate('click'); + wrapper + .find('Button') + .at(1) + .simulate('click'); wrapper.update(); const field = wrapper.find('CodeMirrorInput'); @@ -86,14 +77,12 @@ describe('VariablesField', () => { ( + render={formik => (
- - + + )} /> @@ -105,7 +94,7 @@ describe('VariablesField', () => { expect(handleSubmit).toHaveBeenCalled(); expect(handleSubmit.mock.calls[0][0]).toEqual({ - variables: '---\nnewval: changed' + variables: '---\nnewval: changed', }); }); }); diff --git a/awx/ui_next/src/components/ContentEmpty/ContentEmpty.jsx b/awx/ui_next/src/components/ContentEmpty/ContentEmpty.jsx index c234845898..ebb0d94fd8 100644 --- a/awx/ui_next/src/components/ContentEmpty/ContentEmpty.jsx +++ b/awx/ui_next/src/components/ContentEmpty/ContentEmpty.jsx @@ -5,19 +5,15 @@ import { Title, EmptyState, EmptyStateIcon, - EmptyStateBody + EmptyStateBody, } from '@patternfly/react-core'; import { CubesIcon } from '@patternfly/react-icons'; const ContentEmpty = ({ i18n, title = '', message = '' }) => ( - - {title || i18n._(t`No items found.`)} - - - {message} - + {title || i18n._(t`No items found.`)} + {message} ); diff --git a/awx/ui_next/src/components/ContentError/ContentError.jsx b/awx/ui_next/src/components/ContentError/ContentError.jsx index c9988c3b72..e3723dc48d 100644 --- a/awx/ui_next/src/components/ContentError/ContentError.jsx +++ b/awx/ui_next/src/components/ContentError/ContentError.jsx @@ -6,7 +6,7 @@ import { Title, EmptyState as PFEmptyState, EmptyStateIcon, - EmptyStateBody + EmptyStateBody, } from '@patternfly/react-core'; import { ExclamationTriangleIcon } from '@patternfly/react-icons'; @@ -17,20 +17,18 @@ const EmptyState = styled(PFEmptyState)` `; class ContentError extends React.Component { - render () { + render() { const { error, i18n } = this.props; return ( - - {i18n._(t`Something went wrong...`)} - + {i18n._(t`Something went wrong...`)} - {i18n._(t`There was an error loading this content. Please reload the page.`)} + {i18n._( + t`There was an error loading this content. Please reload the page.` + )} - {error && ( - - )} + {error && } ); } diff --git a/awx/ui_next/src/components/ContentError/ContentError.test.jsx b/awx/ui_next/src/components/ContentError/ContentError.test.jsx index 484bbd91d8..518f0ae8c6 100644 --- a/awx/ui_next/src/components/ContentError/ContentError.test.jsx +++ b/awx/ui_next/src/components/ContentError/ContentError.test.jsx @@ -5,15 +5,20 @@ import ContentError from './ContentError'; describe('ContentError', () => { test('renders the expected content', () => { - const wrapper = mountWithContexts(); + const wrapper = mountWithContexts( + + ); expect(wrapper).toHaveLength(1); }); }); diff --git a/awx/ui_next/src/components/ContentLoading/ContentLoading.jsx b/awx/ui_next/src/components/ContentLoading/ContentLoading.jsx index 050ec82cb1..f242bbca10 100644 --- a/awx/ui_next/src/components/ContentLoading/ContentLoading.jsx +++ b/awx/ui_next/src/components/ContentLoading/ContentLoading.jsx @@ -1,17 +1,12 @@ import React from 'react'; import { t } from '@lingui/macro'; import { withI18n } from '@lingui/react'; -import { - EmptyState, - EmptyStateBody -} from '@patternfly/react-core'; +import { EmptyState, EmptyStateBody } from '@patternfly/react-core'; // TODO: Better loading state - skeleton lines / spinner, etc. const ContentLoading = ({ i18n }) => ( - - {i18n._(t`Loading...`)} - + {i18n._(t`Loading...`)} ); diff --git a/awx/ui_next/src/components/DataListToolbar/DataListToolbar.jsx b/awx/ui_next/src/components/DataListToolbar/DataListToolbar.jsx index cf035931f1..d7fb75ab1d 100644 --- a/awx/ui_next/src/components/DataListToolbar/DataListToolbar.jsx +++ b/awx/ui_next/src/components/DataListToolbar/DataListToolbar.jsx @@ -24,7 +24,8 @@ const AWXToolbar = styled.div` --pf-global--target-size--MinWidth: 0; --pf-global--FontSize--md: 14px; - border-bottom: var(--awx-toolbar--BorderWidth) solid var(--awx-toolbar--BorderColor); + border-bottom: var(--awx-toolbar--BorderWidth) solid + var(--awx-toolbar--BorderColor); background-color: var(--awx-toolbar--BackgroundColor); display: flex; min-height: 70px; @@ -70,13 +71,13 @@ const AdditionalControlsWrapper = styled.div` justify-content: flex-end; align-items: center; - & > :not(:first-child) { - margin-left: 20px; + & > :not(:first-child) { + margin-left: 20px; } `; class DataListToolbar extends React.Component { - render () { + render() { const { columns, showSelectAll, @@ -91,15 +92,15 @@ class DataListToolbar extends React.Component { sortOrder, sortedColumnKey, additionalControls, - i18n + i18n, } = this.props; - const showExpandCollapse = (onCompact && onExpand); + const showExpandCollapse = onCompact && onExpand; return ( - { showSelectAll && ( + {showSelectAll && ( - { additionalControls && ( - - )} + {additionalControls && } )} diff --git a/awx/ui_next/src/components/DataListToolbar/DataListToolbar.test.jsx b/awx/ui_next/src/components/DataListToolbar/DataListToolbar.test.jsx index de1d1d8624..9bf646ba96 100644 --- a/awx/ui_next/src/components/DataListToolbar/DataListToolbar.test.jsx +++ b/awx/ui_next/src/components/DataListToolbar/DataListToolbar.test.jsx @@ -65,7 +65,7 @@ describe('', () => { { name: 'Foo', key: 'foo', isSortable: true }, { name: 'Bar', key: 'bar', isSortable: true }, { name: 'Bakery', key: 'bakery', isSortable: true }, - { name: 'Baz', key: 'baz' } + { name: 'Baz', key: 'baz' }, ]; const onSort = jest.fn(); @@ -97,12 +97,16 @@ describe('', () => { ); toolbar.update(); - const sortDropdownToggleDescending = toolbar.find(sortDropdownToggleSelector); + const sortDropdownToggleDescending = toolbar.find( + sortDropdownToggleSelector + ); expect(sortDropdownToggleDescending.length).toBe(1); sortDropdownToggleDescending.simulate('click'); toolbar.update(); - const sortDropdownItemsDescending = toolbar.find(dropdownMenuItems).children(); + const sortDropdownItemsDescending = toolbar + .find(dropdownMenuItems) + .children(); expect(sortDropdownItemsDescending.length).toBe(2); sortDropdownToggleDescending.simulate('click'); // toggle close the sort dropdown @@ -128,8 +132,12 @@ describe('', () => { const downAlphaIconSelector = 'SortAlphaDownIcon'; const upAlphaIconSelector = 'SortAlphaUpIcon'; - const numericColumns = [{ name: 'ID', key: 'id', isSortable: true, isNumeric: true }]; - const alphaColumns = [{ name: 'Name', key: 'name', isSortable: true, isNumeric: false }]; + const numericColumns = [ + { name: 'ID', key: 'id', isSortable: true, isNumeric: true }, + ]; + const alphaColumns = [ + { name: 'Name', key: 'name', isSortable: true, isNumeric: false }, + ]; toolbar = mountWithContexts( ', () => { onSearch={onSearch} onSort={onSort} onSelectAll={onSelectAll} - additionalControls={[]} + additionalControls={[ + , + ]} /> ); diff --git a/awx/ui_next/src/components/DetailList/Detail.jsx b/awx/ui_next/src/components/DetailList/Detail.jsx index 9f09fd4ed2..ea65fdc288 100644 --- a/awx/ui_next/src/components/DetailList/Detail.jsx +++ b/awx/ui_next/src/components/DetailList/Detail.jsx @@ -7,7 +7,9 @@ const DetailName = styled(({ fullWidth, ...props }) => ( ))` font-weight: var(--pf-global--FontWeight--bold); - ${props => props.fullWidth && ` + ${props => + props.fullWidth && + ` grid-column: 1; `} `; @@ -16,7 +18,9 @@ const DetailValue = styled(({ fullWidth, ...props }) => ( ))` word-break: break-all; - ${props => props.fullWidth && ` + ${props => + props.fullWidth && + ` grid-column: 2 / -1; `} `; @@ -25,16 +29,10 @@ const Detail = ({ label, value, fullWidth }) => { if (!value) return null; return ( - + {label} - + {value} diff --git a/awx/ui_next/src/components/DetailList/Detail.test.jsx b/awx/ui_next/src/components/DetailList/Detail.test.jsx index 136d6bfcb5..d78cf28566 100644 --- a/awx/ui_next/src/components/DetailList/Detail.test.jsx +++ b/awx/ui_next/src/components/DetailList/Detail.test.jsx @@ -5,9 +5,7 @@ import Detail from './Detail'; describe('Detail', () => { test('renders the expected content', () => { - const wrapper = mount( - - ); + const wrapper = mount(); expect(wrapper).toHaveLength(1); }); }); diff --git a/awx/ui_next/src/components/DetailList/DetailList.jsx b/awx/ui_next/src/components/DetailList/DetailList.jsx index 8925d76043..593fb9eea6 100644 --- a/awx/ui_next/src/components/DetailList/DetailList.jsx +++ b/awx/ui_next/src/components/DetailList/DetailList.jsx @@ -11,9 +11,12 @@ const DetailList = ({ children, stacked, ...props }) => ( export default styled(DetailList)` display: grid; grid-gap: 20px; - ${props => (props.stacked ? (` + ${props => + props.stacked + ? ` grid-template-columns: auto 1fr; - `) : (` + ` + : ` --column-count: 1; grid-template-columns: repeat(var(--column-count), auto minmax(10em, 1fr)); @@ -24,5 +27,5 @@ export default styled(DetailList)` @media (min-width: 1210px) { --column-count: 3; } - `))} + `} `; diff --git a/awx/ui_next/src/components/ErrorDetail/ErrorDetail.jsx b/awx/ui_next/src/components/ErrorDetail/ErrorDetail.jsx index 8c11aac176..ed3d468315 100644 --- a/awx/ui_next/src/components/ErrorDetail/ErrorDetail.jsx +++ b/awx/ui_next/src/components/ErrorDetail/ErrorDetail.jsx @@ -7,7 +7,7 @@ import { t } from '@lingui/macro'; import { Card as PFCard, CardBody as PFCardBody, - Expandable as PFExpandable + Expandable as PFExpandable, } from '@patternfly/react-core'; const Card = styled(PFCard)` @@ -25,11 +25,11 @@ const Expandable = styled(PFExpandable)` `; class ErrorDetail extends Component { - constructor (props) { + constructor(props) { super(props); this.state = { - isExpanded: false + isExpanded: false, }; this.handleToggle = this.handleToggle.bind(this); @@ -37,51 +37,48 @@ class ErrorDetail extends Component { this.renderStack = this.renderStack.bind(this); } - handleToggle () { + handleToggle() { const { isExpanded } = this.state; this.setState({ isExpanded: !isExpanded }); } - renderNetworkError () { + renderNetworkError() { const { error } = this.props; const { response } = error; - const message = typeof response.data === 'string' - ? response.data - : response.data.detail; + const message = + typeof response.data === 'string' ? response.data : response.data.detail; return ( - {response.config.method.toUpperCase()} - {' '} - {response.config.url} - {' '} - - {response.status} - + {response.config.method.toUpperCase()} {response.config.url}{' '} + {response.status} {message} ); } - renderStack () { + renderStack() { const { error } = this.props; - return ({error.stack}); + return {error.stack}; } - render () { + render() { const { isExpanded } = this.state; const { error, i18n } = this.props; return ( - + {Object.prototype.hasOwnProperty.call(error, 'response') ? this.renderNetworkError() - : this.renderStack() - } + : this.renderStack()} ); @@ -89,7 +86,7 @@ class ErrorDetail extends Component { } ErrorDetail.propTypes = { - error: PropTypes.instanceOf(Error).isRequired + error: PropTypes.instanceOf(Error).isRequired, }; export default withI18n()(ErrorDetail); diff --git a/awx/ui_next/src/components/ErrorDetail/ErrorDetail.test.jsx b/awx/ui_next/src/components/ErrorDetail/ErrorDetail.test.jsx index 3e7452f5ba..b6499766dc 100644 --- a/awx/ui_next/src/components/ErrorDetail/ErrorDetail.test.jsx +++ b/awx/ui_next/src/components/ErrorDetail/ErrorDetail.test.jsx @@ -5,15 +5,20 @@ import ErrorDetail from './ErrorDetail'; describe('ErrorDetail', () => { test('renders the expected content', () => { - const wrapper = mountWithContexts(); + const wrapper = mountWithContexts( + + ); expect(wrapper).toHaveLength(1); }); }); diff --git a/awx/ui_next/src/components/ExpandCollapse/ExpandCollapse.jsx b/awx/ui_next/src/components/ExpandCollapse/ExpandCollapse.jsx index b884c0871b..6d06c8acd6 100644 --- a/awx/ui_next/src/components/ExpandCollapse/ExpandCollapse.jsx +++ b/awx/ui_next/src/components/ExpandCollapse/ExpandCollapse.jsx @@ -4,40 +4,34 @@ import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { Button as PFButton, - ToolbarItem as PFToolbarItem + ToolbarItem as PFToolbarItem, } from '@patternfly/react-core'; -import { - BarsIcon, - EqualsIcon, -} from '@patternfly/react-icons'; +import { BarsIcon, EqualsIcon } from '@patternfly/react-icons'; import styled from 'styled-components'; const Button = styled(PFButton)` - padding: 0; - margin: 0; - height: 30px; - width: 30px; - ${props => (props.isActive ? ` + padding: 0; + margin: 0; + height: 30px; + width: 30px; + ${props => + props.isActive + ? ` background-color: #007bba; --pf-c-button--m-plain--active--Color: white; --pf-c-button--m-plain--focus--Color: white;` - : null)}; + : null}; `; const ToolbarItem = styled(PFToolbarItem)` & :not(:last-child) { - margin-right: 20px; + margin-right: 20px; } `; class ExpandCollapse extends React.Component { - render () { - const { - isCompact, - onCompact, - onExpand, - i18n - } = this.props; + render() { + const { isCompact, onCompact, onExpand, i18n } = this.props; return ( @@ -69,11 +63,11 @@ class ExpandCollapse extends React.Component { ExpandCollapse.propTypes = { onCompact: PropTypes.func.isRequired, onExpand: PropTypes.func.isRequired, - isCompact: PropTypes.bool + isCompact: PropTypes.bool, }; ExpandCollapse.defaultProps = { - isCompact: true + isCompact: true, }; export default withI18n()(ExpandCollapse); diff --git a/awx/ui_next/src/components/FormActionGroup/FormActionGroup.jsx b/awx/ui_next/src/components/FormActionGroup/FormActionGroup.jsx index 634212ff07..16c88d8341 100644 --- a/awx/ui_next/src/components/FormActionGroup/FormActionGroup.jsx +++ b/awx/ui_next/src/components/FormActionGroup/FormActionGroup.jsx @@ -2,10 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; -import { - ActionGroup as PFActionGroup, - Button -} from '@patternfly/react-core'; +import { ActionGroup as PFActionGroup, Button } from '@patternfly/react-core'; import styled from 'styled-components'; const ActionGroup = styled(PFActionGroup)` @@ -27,8 +24,23 @@ const ActionGroup = styled(PFActionGroup)` const FormActionGroup = ({ onSubmit, submitDisabled, onCancel, i18n }) => ( - - + + ); diff --git a/awx/ui_next/src/components/FormActionGroup/FormActionGroup.test.jsx b/awx/ui_next/src/components/FormActionGroup/FormActionGroup.test.jsx index 0e35b4b879..efa3d43632 100644 --- a/awx/ui_next/src/components/FormActionGroup/FormActionGroup.test.jsx +++ b/awx/ui_next/src/components/FormActionGroup/FormActionGroup.test.jsx @@ -6,10 +6,7 @@ import FormActionGroup from './FormActionGroup'; describe('FormActionGroup', () => { test('renders the expected content', () => { const wrapper = mountWithContexts( - {}} - onCancel={() => {}} - /> + {}} onCancel={() => {}} /> ); expect(wrapper).toHaveLength(1); }); diff --git a/awx/ui_next/src/components/FormField/FormField.jsx b/awx/ui_next/src/components/FormField/FormField.jsx index 2833226a26..999ccd531a 100644 --- a/awx/ui_next/src/components/FormField/FormField.jsx +++ b/awx/ui_next/src/components/FormField/FormField.jsx @@ -9,7 +9,7 @@ const QuestionCircleIcon = styled(PFQuestionCircleIcon)` margin-left: 10px; `; -function FormField (props) { +function FormField(props) { const { id, name, label, tooltip, validate, isRequired, ...rest } = props; return ( @@ -17,7 +17,8 @@ function FormField (props) { name={name} validate={validate} render={({ field, form }) => { - const isValid = form && (!form.touched[field.name] || !form.errors[field.name]); + const isValid = + form && (!form.touched[field.name] || !form.errors[field.name]); return ( {tooltip && ( - + - )} {}, isRequired: false, - tooltip: null + tooltip: null, }; export default FormField; diff --git a/awx/ui_next/src/components/FormRow/FormRow.jsx b/awx/ui_next/src/components/FormRow/FormRow.jsx index 1279e53c63..de06a481ab 100644 --- a/awx/ui_next/src/components/FormRow/FormRow.jsx +++ b/awx/ui_next/src/components/FormRow/FormRow.jsx @@ -5,11 +5,7 @@ const Row = styled.div` display: grid; grid-gap: 20px; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - `; -export default function FormRow ({ children }) { - return ( - - {children} - - ); +`; +export default function FormRow({ children }) { + return {children}; } diff --git a/awx/ui_next/src/components/LaunchButton/LaunchButton.jsx b/awx/ui_next/src/components/LaunchButton/LaunchButton.jsx index 8db00f36c1..7d8b878f47 100644 --- a/awx/ui_next/src/components/LaunchButton/LaunchButton.jsx +++ b/awx/ui_next/src/components/LaunchButton/LaunchButton.jsx @@ -25,12 +25,12 @@ class LaunchButton extends React.Component { templateId: number.isRequired, }; - constructor (props) { + constructor(props) { super(props); this.state = { launchError: null, - promptError: false + promptError: false, }; this.handleLaunch = this.handleLaunch.bind(this); @@ -38,18 +38,20 @@ class LaunchButton extends React.Component { this.handlePromptErrorClose = this.handlePromptErrorClose.bind(this); } - handleLaunchErrorClose () { + handleLaunchErrorClose() { this.setState({ launchError: null }); } - handlePromptErrorClose () { + handlePromptErrorClose() { this.setState({ promptError: false }); } - async handleLaunch () { + async handleLaunch() { const { history, templateId } = this.props; try { - const { data: launchConfig } = await JobTemplatesAPI.readLaunch(templateId); + const { data: launchConfig } = await JobTemplatesAPI.readLaunch( + templateId + ); if (launchConfig.can_start_without_user_input) { const { data: job } = await JobTemplatesAPI.launch(templateId); history.push(`/jobs/${job.id}/details`); @@ -61,18 +63,12 @@ class LaunchButton extends React.Component { } } - render () { - const { - launchError, - promptError - } = this.state; + render() { + const { launchError, promptError } = this.state; const { i18n } = this.props; return ( - +
- {i18n._(t`Launching jobs with promptable fields is not supported at this time.`)} + {i18n._( + t`Launching jobs with promptable fields is not supported at this time.` + )} ); diff --git a/awx/ui_next/src/components/LaunchButton/LaunchButton.test.jsx b/awx/ui_next/src/components/LaunchButton/LaunchButton.test.jsx index 738cae0131..0bf4831ebf 100644 --- a/awx/ui_next/src/components/LaunchButton/LaunchButton.test.jsx +++ b/awx/ui_next/src/components/LaunchButton/LaunchButton.test.jsx @@ -10,30 +10,28 @@ jest.mock('@api'); describe('LaunchButton', () => { JobTemplatesAPI.readLaunch.mockResolvedValue({ data: { - can_start_without_user_input: true - } + can_start_without_user_input: true, + }, }); test('renders the expected content', () => { const wrapper = mountWithContexts(); expect(wrapper).toHaveLength(1); }); - test('redirects to details after successful launch', async (done) => { + test('redirects to details after successful launch', async done => { const history = { push: jest.fn(), }; JobTemplatesAPI.launch.mockResolvedValue({ data: { - id: 9000 - } + id: 9000, + }, + }); + const wrapper = mountWithContexts(, { + context: { + router: { history }, + }, }); - const wrapper = mountWithContexts( - , { - context: { - router: { history } - } - } - ); const launchButton = wrapper.find('LaunchButton__StyledLaunchButton'); launchButton.simulate('click'); await sleep(0); @@ -42,24 +40,34 @@ describe('LaunchButton', () => { expect(history.push).toHaveBeenCalledWith('/jobs/9000/details'); done(); }); - test('displays error modal after unsuccessful launch', async (done) => { - JobTemplatesAPI.launch.mockRejectedValue(new Error({ - response: { - config: { - method: 'post', - url: '/api/v2/job_templates/1/launch' + test('displays error modal after unsuccessful launch', async done => { + JobTemplatesAPI.launch.mockRejectedValue( + new Error({ + response: { + config: { + method: 'post', + url: '/api/v2/job_templates/1/launch', + }, + data: 'An error occurred', + status: 403, }, - data: 'An error occurred', - status: 403 - } - })); + }) + ); const wrapper = mountWithContexts(); const launchButton = wrapper.find('LaunchButton__StyledLaunchButton'); launchButton.simulate('click'); - await waitForElement(wrapper, 'Modal.at-c-alertModal--danger', (el) => el.props().isOpen === true && el.props().title === 'Error!'); + await waitForElement( + wrapper, + 'Modal.at-c-alertModal--danger', + el => el.props().isOpen === true && el.props().title === 'Error!' + ); const modalCloseButton = wrapper.find('ModalBoxCloseButton'); modalCloseButton.simulate('click'); - await waitForElement(wrapper, 'Modal.at-c-alertModal--danger', (el) => el.props().isOpen === false); + await waitForElement( + wrapper, + 'Modal.at-c-alertModal--danger', + el => el.props().isOpen === false + ); done(); }); }); diff --git a/awx/ui_next/src/components/Lookup/Lookup.jsx b/awx/ui_next/src/components/Lookup/Lookup.jsx index 63d9291914..9f08a81ad2 100644 --- a/awx/ui_next/src/components/Lookup/Lookup.jsx +++ b/awx/ui_next/src/components/Lookup/Lookup.jsx @@ -19,7 +19,7 @@ import { ChipGroup, Chip } from '../Chip'; import { getQSConfig, parseNamespacedQueryString } from '../../util/qs'; class Lookup extends React.Component { - constructor (props) { + constructor(props) { super(props); this.state = { @@ -40,19 +40,22 @@ class Lookup extends React.Component { this.getData = this.getData.bind(this); } - componentDidMount () { + componentDidMount() { this.getData(); } - componentDidUpdate (prevProps) { + componentDidUpdate(prevProps) { const { location } = this.props; if (location !== prevProps.location) { this.getData(); } } - async getData () { - const { getItems, location: { search } } = this.props; + async getData() { + const { + getItems, + location: { search }, + } = this.props; const queryParams = parseNamespacedQueryString(this.qsConfig, search); this.setState({ error: false }); @@ -62,26 +65,30 @@ class Lookup extends React.Component { this.setState({ results, - count + count, }); } catch (err) { this.setState({ error: true }); } } - toggleSelected (row) { + toggleSelected(row) { const { name, onLookupSave } = this.props; - const { lookupSelectedItems: updatedSelectedItems, isModalOpen } = this.state; + const { + lookupSelectedItems: updatedSelectedItems, + isModalOpen, + } = this.state; - const selectedIndex = updatedSelectedItems - .findIndex(selectedRow => selectedRow.id === row.id); + const selectedIndex = updatedSelectedItems.findIndex( + selectedRow => selectedRow.id === row.id + ); if (selectedIndex > -1) { updatedSelectedItems.splice(selectedIndex, 1); this.setState({ lookupSelectedItems: updatedSelectedItems }); } else { this.setState(prevState => ({ - lookupSelectedItems: [...prevState.lookupSelectedItems, row] + lookupSelectedItems: [...prevState.lookupSelectedItems, row], })); } @@ -93,7 +100,7 @@ class Lookup extends React.Component { } } - handleModalToggle () { + handleModalToggle() { const { isModalOpen } = this.state; const { value } = this.props; // Resets the selected items from parent state whenever modal is opened @@ -102,19 +109,19 @@ class Lookup extends React.Component { if (!isModalOpen) { this.setState({ lookupSelectedItems: [...value] }); } - this.setState((prevState) => ({ + this.setState(prevState => ({ isModalOpen: !prevState.isModalOpen, })); } - saveModal () { + saveModal() { const { onLookupSave, name } = this.props; const { lookupSelectedItems } = this.state; onLookupSave(lookupSelectedItems, name); this.handleModalToggle(); } - render () { + render() { const { isModalOpen, lookupSelectedItems, @@ -147,9 +154,7 @@ class Lookup extends React.Component { > -
- {chips} -
+
{chips}
{i18n._(t`Save`)}, - + , + , ]} > this.toggleSelected(item)} /> )} - renderToolbar={(props) => ( + renderToolbar={props => ( )} showPageSizeOptions={false} @@ -189,7 +207,7 @@ class Lookup extends React.Component { onRemove={this.toggleSelected} /> )} - { error ?
error
: '' } + {error ?
error
: ''}
); diff --git a/awx/ui_next/src/components/Lookup/Lookup.test.jsx b/awx/ui_next/src/components/Lookup/Lookup.test.jsx index 3a651c8178..ccf14d7154 100644 --- a/awx/ui_next/src/components/Lookup/Lookup.test.jsx +++ b/awx/ui_next/src/components/Lookup/Lookup.test.jsx @@ -5,9 +5,7 @@ import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; import Lookup, { _Lookup } from './Lookup'; let mockData = [{ name: 'foo', id: 1, isChecked: false }]; -const mockColumns = [ - { name: 'Name', key: 'name', isSortable: true } -]; +const mockColumns = [{ name: 'Name', key: 'name', isSortable: true }]; describe('', () => { test('initially renders succesfully', () => { mountWithContexts( @@ -15,29 +13,33 @@ describe('', () => { lookupHeader="Foo Bar" name="fooBar" value={mockData} - onLookupSave={() => { }} - getItems={() => { }} + onLookupSave={() => {}} + getItems={() => {}} columns={mockColumns} sortedColumnKey="name" /> ); }); - test('API response is formatted properly', (done) => { + test('API response is formatted properly', done => { const wrapper = mountWithContexts( { }} - getItems={() => ({ data: { results: [{ name: 'test instance', id: 1 }] } })} + onLookupSave={() => {}} + getItems={() => ({ + data: { results: [{ name: 'test instance', id: 1 }] }, + })} columns={mockColumns} sortedColumnKey="name" /> ).find('Lookup'); setImmediate(() => { - expect(wrapper.state().results).toEqual([{ id: 1, name: 'test instance' }]); + expect(wrapper.state().results).toEqual([ + { id: 1, name: 'test instance' }, + ]); done(); }); }); @@ -51,8 +53,8 @@ describe('', () => { lookupHeader="Foo Bar" name="fooBar" value={mockSelected} - onLookupSave={() => { }} - getItems={() => { }} + onLookupSave={() => {}} + getItems={() => {}} columns={mockColumns} sortedColumnKey="name" /> @@ -62,20 +64,20 @@ describe('', () => { const searchItem = wrapper.find('button[aria-label="Search"]'); searchItem.first().simulate('click'); expect(spy).toHaveBeenCalled(); - expect(wrapper.state('lookupSelectedItems')).toEqual([{ - id: 1, - name: 'foo' - }]); + expect(wrapper.state('lookupSelectedItems')).toEqual([ + { + id: 1, + name: 'foo', + }, + ]); expect(wrapper.state('isModalOpen')).toEqual(true); }); - test('calls "toggleSelected" when a user changes a checkbox', (done) => { + test('calls "toggleSelected" when a user changes a checkbox', done => { const spy = jest.spyOn(_Lookup.prototype, 'toggleSelected'); const mockSelected = [{ name: 'foo', id: 1 }]; const data = { - results: [ - { name: 'test instance', id: 1, url: '/foo' } - ], + results: [{ name: 'test instance', id: 1, url: '/foo' }], count: 1, }; const wrapper = mountWithContexts( @@ -84,7 +86,7 @@ describe('', () => { lookupHeader="Foo Bar" name="fooBar" value={mockSelected} - onLookupSave={() => { }} + onLookupSave={() => {}} getItems={() => ({ data })} columns={mockColumns} sortedColumnKey="name" @@ -103,9 +105,7 @@ describe('', () => { const spy = jest.spyOn(_Lookup.prototype, 'toggleSelected'); mockData = [{ name: 'foo', id: 1 }, { name: 'bar', id: 2 }]; const data = { - results: [ - { name: 'test instance', id: 1, url: '/foo' } - ], + results: [{ name: 'test instance', id: 1, url: '/foo' }], count: 1, }; const wrapper = mountWithContexts( @@ -114,7 +114,7 @@ describe('', () => { lookupHeader="Foo Bar" name="fooBar" value={mockData} - onLookupSave={() => { }} + onLookupSave={() => {}} getItems={() => ({ data })} columns={mockColumns} sortedColumnKey="name" @@ -130,10 +130,10 @@ describe('', () => { const wrapper = mountWithContexts( { }} + onLookupSave={() => {}} value={mockData} selected={[]} - getItems={() => { }} + getItems={() => {}} columns={mockColumns} sortedColumnKey="name" /> @@ -147,24 +147,26 @@ describe('', () => { const wrapper = mountWithContexts( { }} + onLookupSave={() => {}} value={mockData} - getItems={() => { }} + getItems={() => {}} columns={mockColumns} sortedColumnKey="name" /> ).find('Lookup'); wrapper.instance().toggleSelected({ id: 1, - name: 'foo' + name: 'foo', }); - expect(wrapper.state('lookupSelectedItems')).toEqual([{ - id: 1, - name: 'foo' - }]); + expect(wrapper.state('lookupSelectedItems')).toEqual([ + { + id: 1, + name: 'foo', + }, + ]); wrapper.instance().toggleSelected({ id: 1, - name: 'foo' + name: 'foo', }); expect(wrapper.state('lookupSelectedItems')).toEqual([]); }); @@ -178,23 +180,30 @@ describe('', () => { name="fooBar" value={mockData} onLookupSave={onLookupSaveFn} - getItems={() => { }} + getItems={() => {}} sortedColumnKey="name" /> ).find('Lookup'); wrapper.instance().toggleSelected({ id: 1, - name: 'foo' + name: 'foo', }); - expect(wrapper.state('lookupSelectedItems')).toEqual([{ - id: 1, - name: 'foo' - }]); + expect(wrapper.state('lookupSelectedItems')).toEqual([ + { + id: 1, + name: 'foo', + }, + ]); wrapper.instance().saveModal(); - expect(onLookupSaveFn).toHaveBeenCalledWith([{ - id: 1, - name: 'foo' - }], 'fooBar'); + expect(onLookupSaveFn).toHaveBeenCalledWith( + [ + { + id: 1, + name: 'foo', + }, + ], + 'fooBar' + ); }); test('should re-fetch data when URL params change', async () => { @@ -205,7 +214,7 @@ describe('', () => { const wrapper = mountWithContexts( <_Lookup lookupHeader="Foo Bar" - onLookupSave={() => { }} + onLookupSave={() => {}} value={mockData} selected={[]} columns={mockColumns} diff --git a/awx/ui_next/src/components/NavExpandableGroup/NavExpandableGroup.jsx b/awx/ui_next/src/components/NavExpandableGroup/NavExpandableGroup.jsx index 8901d069ee..6403e617f7 100644 --- a/awx/ui_next/src/components/NavExpandableGroup/NavExpandableGroup.jsx +++ b/awx/ui_next/src/components/NavExpandableGroup/NavExpandableGroup.jsx @@ -1,15 +1,10 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { - withRouter -} from 'react-router-dom'; -import { - NavExpandable, - NavItem, -} from '@patternfly/react-core'; +import { withRouter } from 'react-router-dom'; +import { NavExpandable, NavItem } from '@patternfly/react-core'; class NavExpandableGroup extends Component { - constructor (props) { + constructor(props) { super(props); const { routes } = this.props; // Extract a list of paths from the route params and store them for later. This creates @@ -20,17 +15,17 @@ class NavExpandableGroup extends Component { this.isActivePath = this.isActivePath.bind(this); } - isActiveGroup () { + isActiveGroup() { return this.navItemPaths.some(this.isActivePath); } - isActivePath (path) { + isActivePath(path) { const { history } = this.props; return history.location.pathname.startsWith(path); } - render () { + render() { const { groupId, groupTitle, routes } = this.props; const isActive = this.isActiveGroup(); diff --git a/awx/ui_next/src/components/NavExpandableGroup/NavExpandableGroup.test.jsx b/awx/ui_next/src/components/NavExpandableGroup/NavExpandableGroup.test.jsx index 8c5e5aaf72..486cda9e6d 100644 --- a/awx/ui_next/src/components/NavExpandableGroup/NavExpandableGroup.test.jsx +++ b/awx/ui_next/src/components/NavExpandableGroup/NavExpandableGroup.test.jsx @@ -21,7 +21,9 @@ describe('NavExpandableGroup', () => { /> - ).find('NavExpandableGroup').instance(); + ) + .find('NavExpandableGroup') + .instance(); expect(component.navItemPaths).toEqual(['/foo', '/bar', '/fiz']); expect(component.isActiveGroup()).toEqual(true); @@ -54,7 +56,9 @@ describe('NavExpandableGroup', () => { /> - ).find('NavExpandableGroup').instance(); + ) + .find('NavExpandableGroup') + .instance(); expect(component.isActivePath(path)).toEqual(expected); }); diff --git a/awx/ui_next/src/components/NotificationsList/NotificationListItem.jsx b/awx/ui_next/src/components/NotificationsList/NotificationListItem.jsx index 1d3122ce1a..f8e243ad39 100644 --- a/awx/ui_next/src/components/NotificationsList/NotificationListItem.jsx +++ b/awx/ui_next/src/components/NotificationsList/NotificationListItem.jsx @@ -18,7 +18,7 @@ const DataListCell = styled(PFDataListCell)` display: flex; justify-content: ${props => (props.righthalf ? 'flex-start' : 'inherit')}; padding-bottom: ${props => (props.righthalf ? '16px' : '8px')}; - + @media screen and (min-width: 768px) { justify-content: ${props => (props.righthalf ? 'flex-end' : 'inherit')}; padding-bottom: 0; @@ -30,7 +30,7 @@ const Switch = styled(PFSwitch)` flex-wrap: no-wrap; `; -function NotificationListItem (props) { +function NotificationListItem(props) { const { canToggleNotifications, notification, @@ -38,7 +38,7 @@ function NotificationListItem (props) { successTurnedOn, errorTurnedOn, toggleNotification, - i18n + i18n, } = props; return ( @@ -47,50 +47,50 @@ function NotificationListItem (props) { key={notification.id} > - - - {notification.name} - - - {notification.notification_type} - - , - - toggleNotification( - notification.id, - successTurnedOn, - 'success' - )} - aria-label={i18n._(t`Toggle notification success`)} - /> - toggleNotification( - notification.id, - errorTurnedOn, - 'error' - )} - aria-label={i18n._(t`Toggle notification failure`)} - /> - - ]} + + + + {notification.name} + + + + {notification.notification_type} + + , + + + toggleNotification( + notification.id, + successTurnedOn, + 'success' + ) + } + aria-label={i18n._(t`Toggle notification success`)} + /> + + toggleNotification(notification.id, errorTurnedOn, 'error') + } + aria-label={i18n._(t`Toggle notification failure`)} + /> + , + ]} /> diff --git a/awx/ui_next/src/components/NotificationsList/NotificationListItem.test.jsx b/awx/ui_next/src/components/NotificationsList/NotificationListItem.test.jsx index 358d3e307f..8c2d5afd47 100644 --- a/awx/ui_next/src/components/NotificationsList/NotificationListItem.test.jsx +++ b/awx/ui_next/src/components/NotificationsList/NotificationListItem.test.jsx @@ -48,7 +48,11 @@ describe('', () => { canToggleNotifications /> ); - wrapper.find('Switch').first().find('input').simulate('change'); + wrapper + .find('Switch') + .first() + .find('input') + .simulate('change'); expect(toggleNotification).toHaveBeenCalledWith(9000, true, 'success'); }); @@ -66,7 +70,11 @@ describe('', () => { canToggleNotifications /> ); - wrapper.find('Switch').first().find('input').simulate('change'); + wrapper + .find('Switch') + .first() + .find('input') + .simulate('change'); expect(toggleNotification).toHaveBeenCalledWith(9000, false, 'success'); }); @@ -84,7 +92,11 @@ describe('', () => { canToggleNotifications /> ); - wrapper.find('Switch').at(1).find('input').simulate('change'); + wrapper + .find('Switch') + .at(1) + .find('input') + .simulate('change'); expect(toggleNotification).toHaveBeenCalledWith(9000, true, 'error'); }); @@ -102,7 +114,11 @@ describe('', () => { canToggleNotifications /> ); - wrapper.find('Switch').at(1).find('input').simulate('change'); + wrapper + .find('Switch') + .at(1) + .find('input') + .simulate('change'); expect(toggleNotification).toHaveBeenCalledWith(9000, false, 'error'); }); }); diff --git a/awx/ui_next/src/components/PageHeaderToolbar/PageHeaderToolbar.jsx b/awx/ui_next/src/components/PageHeaderToolbar/PageHeaderToolbar.jsx index f0f948b91f..3e9ca35ebe 100644 --- a/awx/ui_next/src/components/PageHeaderToolbar/PageHeaderToolbar.jsx +++ b/awx/ui_next/src/components/PageHeaderToolbar/PageHeaderToolbar.jsx @@ -10,18 +10,19 @@ import { Toolbar, ToolbarGroup, ToolbarItem, - Tooltip + Tooltip, } from '@patternfly/react-core'; import { QuestionCircleIcon, UserIcon } from '@patternfly/react-icons'; -const DOCLINK = 'https://docs.ansible.com/ansible-tower/latest/html/userguide/index.html'; +const DOCLINK = + 'https://docs.ansible.com/ansible-tower/latest/html/userguide/index.html'; class PageHeaderToolbar extends Component { - constructor (props) { + constructor(props) { super(props); this.state = { isHelpOpen: false, - isUserOpen: false + isUserOpen: false, }; this.handleHelpSelect = this.handleHelpSelect.bind(this); @@ -30,34 +31,34 @@ class PageHeaderToolbar extends Component { this.handleUserToggle = this.handleUserToggle.bind(this); } - handleHelpSelect () { + handleHelpSelect() { const { isHelpOpen } = this.state; this.setState({ isHelpOpen: !isHelpOpen }); } - handleUserSelect () { + handleUserSelect() { const { isUserOpen } = this.state; this.setState({ isUserOpen: !isUserOpen }); } - handleHelpToggle (isOpen) { + handleHelpToggle(isOpen) { this.setState({ isHelpOpen: isOpen }); } - handleUserToggle (isOpen) { + handleUserToggle(isOpen) { this.setState({ isUserOpen: isOpen }); } - render () { + render() { const { isHelpOpen, isUserOpen } = this.state; const { isAboutDisabled, onAboutClick, onLogoutClick, loggedInUser, - i18n + i18n, } = this.props; return ( @@ -70,11 +71,11 @@ class PageHeaderToolbar extends Component { isOpen={isHelpOpen} position={DropdownPosition.right} onSelect={this.handleHelpSelect} - toggle={( + toggle={ - )} + } dropdownItems={[ {i18n._(t`Help`)} @@ -86,7 +87,7 @@ class PageHeaderToolbar extends Component { onClick={onAboutClick} > {i18n._(t`About`)} - + , ]} /> @@ -98,7 +99,7 @@ class PageHeaderToolbar extends Component { isOpen={isUserOpen} position={DropdownPosition.right} onSelect={this.handleUserSelect} - toggle={( + toggle={ {loggedInUser && ( @@ -107,7 +108,7 @@ class PageHeaderToolbar extends Component { )} - )} + } dropdownItems={[ {i18n._(t`User Details`)} @@ -118,7 +119,7 @@ class PageHeaderToolbar extends Component { onClick={onLogoutClick} > {i18n._(t`Logout`)} - + , ]} /> @@ -132,11 +133,11 @@ class PageHeaderToolbar extends Component { PageHeaderToolbar.propTypes = { isAboutDisabled: PropTypes.bool, onAboutClick: PropTypes.func.isRequired, - onLogoutClick: PropTypes.func.isRequired + onLogoutClick: PropTypes.func.isRequired, }; PageHeaderToolbar.defaultProps = { - isAboutDisabled: false + isAboutDisabled: false, }; export default withI18n()(PageHeaderToolbar); diff --git a/awx/ui_next/src/components/PaginatedDataList/PaginatedDataList.jsx b/awx/ui_next/src/components/PaginatedDataList/PaginatedDataList.jsx index dbb0d4af50..f273971034 100644 --- a/awx/ui_next/src/components/PaginatedDataList/PaginatedDataList.jsx +++ b/awx/ui_next/src/components/PaginatedDataList/PaginatedDataList.jsx @@ -26,19 +26,19 @@ const EmptyStateControlsWrapper = styled.div` margin-bottom: 20px; justify-content: flex-end; - & > :not(:first-child) { + & > :not(:first-child) { margin-left: 20px; } `; class PaginatedDataList extends React.Component { - constructor (props) { + constructor(props) { super(props); this.handleSetPage = this.handleSetPage.bind(this); this.handleSetPageSize = this.handleSetPageSize.bind(this); this.handleSort = this.handleSort.bind(this); } - getSortOrder () { + getSortOrder() { const { qsConfig, location } = this.props; const queryParams = parseNamespacedQueryString(qsConfig, location.search); if (queryParams.order_by && queryParams.order_by.startsWith('-')) { @@ -47,29 +47,30 @@ class PaginatedDataList extends React.Component { return [queryParams.order_by, 'ascending']; } - handleSetPage (event, pageNumber) { + handleSetPage(event, pageNumber) { this.pushHistoryState({ page: pageNumber }); } - handleSetPageSize (event, pageSize) { + handleSetPageSize(event, pageSize) { this.pushHistoryState({ page_size: pageSize }); } - handleSort (sortedColumnKey, sortOrder) { + handleSort(sortedColumnKey, sortOrder) { this.pushHistoryState({ - order_by: sortOrder === 'ascending' ? sortedColumnKey : `-${sortedColumnKey}`, + order_by: + sortOrder === 'ascending' ? sortedColumnKey : `-${sortedColumnKey}`, page: null, }); } - pushHistoryState (newParams) { + pushHistoryState(newParams) { const { history, qsConfig } = this.props; const { pathname, search } = history.location; const qs = updateNamespacedQueryString(qsConfig, search, newParams); history.push(`${pathname}?${qs}`); } - render () { + render() { const [orderBy, sortOrder] = this.getSortOrder(); const { contentError, @@ -87,25 +88,35 @@ class PaginatedDataList extends React.Component { i18n, renderToolbar, } = this.props; - const columns = toolbarColumns.length ? toolbarColumns : [{ name: i18n._(t`Name`), key: 'name', isSortable: true }]; + const columns = toolbarColumns.length + ? toolbarColumns + : [{ name: i18n._(t`Name`), key: 'name', isSortable: true }]; const queryParams = parseNamespacedQueryString(qsConfig, location.search); const itemDisplayName = ucFirst(pluralize(itemName)); - const itemDisplayNamePlural = ucFirst(itemNamePlural || pluralize(itemName)); + const itemDisplayNamePlural = ucFirst( + itemNamePlural || pluralize(itemName) + ); const dataListLabel = i18n._(t`${itemDisplayName} List`); - const emptyContentMessage = i18n._(t`Please add ${itemDisplayNamePlural} to populate this list `); + const emptyContentMessage = i18n._( + t`Please add ${itemDisplayNamePlural} to populate this list ` + ); const emptyContentTitle = i18n._(t`No ${itemDisplayNamePlural} Found `); let Content; if (hasContentLoading && items.length <= 0) { - Content = (); + Content = ; } else if (contentError) { - Content = (); + Content = ; } else if (items.length <= 0) { - Content = (); + Content = ( + + ); } else { - Content = ({items.map(renderItem)}); + Content = ( + {items.map(renderItem)} + ); } if (items.length <= 0) { @@ -116,9 +127,7 @@ class PaginatedDataList extends React.Component { {emptyStateControls} )} - {emptyStateControls && ( -
- )} + {emptyStateControls &&
} {Content} ); @@ -130,7 +139,7 @@ class PaginatedDataList extends React.Component { sortedColumnKey: orderBy, sortOrder, columns, - onSearch: () => { }, + onSearch: () => {}, onSort: this.handleSort, })} {Content} @@ -139,12 +148,16 @@ class PaginatedDataList extends React.Component { itemCount={itemCount} page={queryParams.page || 1} perPage={queryParams.page_size} - perPageOptions={showPageSizeOptions ? [ - { title: '5', value: 5 }, - { title: '10', value: 10 }, - { title: '20', value: 20 }, - { title: '50', value: 50 } - ] : []} + perPageOptions={ + showPageSizeOptions + ? [ + { title: '5', value: 5 }, + { title: '10', value: 10 }, + { title: '20', value: 20 }, + { title: '50', value: 50 }, + ] + : [] + } onSetPage={this.handleSetPage} onPerPageSelect={this.handleSetPageSize} /> @@ -166,11 +179,13 @@ PaginatedDataList.propTypes = { itemNamePlural: PropTypes.string, qsConfig: QSConfig.isRequired, renderItem: PropTypes.func, - toolbarColumns: arrayOf(shape({ - name: string.isRequired, - key: string.isRequired, - isSortable: bool, - })), + toolbarColumns: arrayOf( + shape({ + name: string.isRequired, + key: string.isRequired, + isSortable: bool, + }) + ), showPageSizeOptions: PropTypes.bool, renderToolbar: PropTypes.func, hasContentLoading: PropTypes.bool, @@ -184,8 +199,8 @@ PaginatedDataList.defaultProps = { itemName: 'item', itemNamePlural: '', showPageSizeOptions: true, - renderItem: (item) => (), - renderToolbar: (props) => (), + renderItem: item => , + renderToolbar: props => , }; export { PaginatedDataList as _PaginatedDataList }; diff --git a/awx/ui_next/src/components/PaginatedDataList/PaginatedDataList.test.jsx b/awx/ui_next/src/components/PaginatedDataList/PaginatedDataList.test.jsx index 3538148a3b..e8182d7616 100644 --- a/awx/ui_next/src/components/PaginatedDataList/PaginatedDataList.test.jsx +++ b/awx/ui_next/src/components/PaginatedDataList/PaginatedDataList.test.jsx @@ -52,7 +52,8 @@ describe('', () => { order_by: 'name', }} qsConfig={qsConfig} - />, { context: { router: { history } } } + />, + { context: { router: { history } } } ); const toolbar = wrapper.find('DataListToolbar'); @@ -85,7 +86,8 @@ describe('', () => { order_by: 'name', }} qsConfig={qsConfig} - />, { context: { router: { history } } } + />, + { context: { router: { history } } } ); const pagination = wrapper.find('Pagination'); @@ -110,7 +112,8 @@ describe('', () => { order_by: 'name', }} qsConfig={qsConfig} - />, { context: { router: { history } } } + />, + { context: { router: { history } } } ); const pagination = wrapper.find('Pagination'); diff --git a/awx/ui_next/src/components/PaginatedDataList/PaginatedDataListItem.jsx b/awx/ui_next/src/components/PaginatedDataList/PaginatedDataListItem.jsx index f0133a47e3..0afd1df1db 100644 --- a/awx/ui_next/src/components/PaginatedDataList/PaginatedDataListItem.jsx +++ b/awx/ui_next/src/components/PaginatedDataList/PaginatedDataListItem.jsx @@ -17,24 +17,20 @@ const DetailWrapper = styled(TextContent)` grid-gap: 10px; `; -export default function PaginatedDataListItem ({ item }) { +export default function PaginatedDataListItem({ item }) { return ( - + - - - - - {item.name} - - - - - ]} + + + + {item.name} + + + , + ]} /> diff --git a/awx/ui_next/src/components/PaginatedDataList/ToolbarAddButton.jsx b/awx/ui_next/src/components/PaginatedDataList/ToolbarAddButton.jsx index c4d9bfecad..95f46a1dd8 100644 --- a/awx/ui_next/src/components/PaginatedDataList/ToolbarAddButton.jsx +++ b/awx/ui_next/src/components/PaginatedDataList/ToolbarAddButton.jsx @@ -15,16 +15,15 @@ const Button = styled(PFButton)` } `; -function ToolbarAddButton ({ linkTo, onClick, i18n }) { +function ToolbarAddButton({ linkTo, onClick, i18n }) { if (!linkTo && !onClick) { - throw new Error('ToolbarAddButton requires either `linkTo` or `onClick` prop'); + throw new Error( + 'ToolbarAddButton requires either `linkTo` or `onClick` prop' + ); } if (linkTo) { return ( - + ); @@ -52,7 +47,7 @@ ToolbarAddButton.propTypes = { }; ToolbarAddButton.defaultProps = { linkTo: null, - onClick: null + onClick: null, }; export default withI18n()(ToolbarAddButton); diff --git a/awx/ui_next/src/components/PaginatedDataList/ToolbarAddButton.test.jsx b/awx/ui_next/src/components/PaginatedDataList/ToolbarAddButton.test.jsx index 09e1a1d7d2..ad5bde5bd4 100644 --- a/awx/ui_next/src/components/PaginatedDataList/ToolbarAddButton.test.jsx +++ b/awx/ui_next/src/components/PaginatedDataList/ToolbarAddButton.test.jsx @@ -5,9 +5,7 @@ import ToolbarAddButton from './ToolbarAddButton'; describe('', () => { test('should render button', () => { const onClick = jest.fn(); - const wrapper = mountWithContexts( - - ); + const wrapper = mountWithContexts(); const button = wrapper.find('button'); expect(button).toHaveLength(1); button.simulate('click'); @@ -15,9 +13,7 @@ describe('', () => { }); test('should render link', () => { - const wrapper = mountWithContexts( - - ); + const wrapper = mountWithContexts(); const link = wrapper.find('Link'); expect(link).toHaveLength(1); expect(link.prop('to')).toBe('/foo'); diff --git a/awx/ui_next/src/components/PaginatedDataList/ToolbarDeleteButton.jsx b/awx/ui_next/src/components/PaginatedDataList/ToolbarDeleteButton.jsx index e6b926aeef..868fcbe684 100644 --- a/awx/ui_next/src/components/PaginatedDataList/ToolbarDeleteButton.jsx +++ b/awx/ui_next/src/components/PaginatedDataList/ToolbarDeleteButton.jsx @@ -12,7 +12,7 @@ const DeleteButton = styled(Button)` padding: 5px 8px; &:hover { - background-color:#d9534f; + background-color: #d9534f; color: white; } @@ -33,7 +33,7 @@ const ItemToDelete = shape({ }).isRequired, }); -function cannotDelete (item) { +function cannotDelete(item) { return !item.summary_fields.user_capabilities.delete; } @@ -48,7 +48,7 @@ class ToolbarDeleteButton extends React.Component { itemName: 'item', }; - constructor (props) { + constructor(props) { super(props); this.state = { @@ -60,34 +60,34 @@ class ToolbarDeleteButton extends React.Component { this.handleDelete = this.handleDelete.bind(this); } - handleConfirmDelete () { + handleConfirmDelete() { this.setState({ isModalOpen: true }); } - handleCancelDelete () { - this.setState({ isModalOpen: false, }); + handleCancelDelete() { + this.setState({ isModalOpen: false }); } - handleDelete () { + handleDelete() { const { onDelete } = this.props; onDelete(); this.setState({ isModalOpen: false }); } - renderTooltip () { + renderTooltip() { const { itemsToDelete, itemName, i18n } = this.props; const itemsUnableToDelete = itemsToDelete .filter(cannotDelete) - .map(item => ( -
- {item.name} -
- )); + .map(item =>
{item.name}
); if (itemsToDelete.some(cannotDelete)) { return (
- {i18n._(t`You do not have permission to delete the following ${pluralize(itemName)}: ${itemsUnableToDelete}`)} + {i18n._( + t`You do not have permission to delete the following ${pluralize( + itemName + )}: ${itemsUnableToDelete}` + )}
); } @@ -97,22 +97,19 @@ class ToolbarDeleteButton extends React.Component { return i18n._(t`Select a row to delete`); } - render () { + render() { const { itemsToDelete, itemName, i18n } = this.props; const { isModalOpen } = this.state; - const isDisabled = itemsToDelete.length === 0 - || itemsToDelete.some(cannotDelete); + const isDisabled = + itemsToDelete.length === 0 || itemsToDelete.some(cannotDelete); // NOTE: Once PF supports tooltips on disabled elements, // we can delete the extra
around the below. // See: https://github.com/patternfly/patternfly-react/issues/1894 return ( - +
- { isModalOpen && ( + {isModalOpen && ( {i18n._(t`Cancel`)} - + , ]} > {i18n._(t`Are you sure you want to delete:`)}
- {itemsToDelete.map((item) => ( + {itemsToDelete.map(item => ( - - {item.name} - + {item.name}
))} diff --git a/awx/ui_next/src/components/PaginatedDataList/ToolbarDeleteButton.test.jsx b/awx/ui_next/src/components/PaginatedDataList/ToolbarDeleteButton.test.jsx index 549d2778ad..fb85c05bfa 100644 --- a/awx/ui_next/src/components/PaginatedDataList/ToolbarDeleteButton.test.jsx +++ b/awx/ui_next/src/components/PaginatedDataList/ToolbarDeleteButton.test.jsx @@ -16,10 +16,7 @@ const itemB = { describe('', () => { test('should render button', () => { const wrapper = mountWithContexts( - {}} - itemsToDelete={[]} - /> + {}} itemsToDelete={[]} /> ); expect(wrapper.find('button')).toHaveLength(1); expect(wrapper.find('ToolbarDeleteButton')).toMatchSnapshot(); @@ -27,14 +24,10 @@ describe('', () => { test('should open confirmation modal', () => { const wrapper = mountWithContexts( - {}} - itemsToDelete={[itemA]} - /> + {}} itemsToDelete={[itemA]} /> ); wrapper.find('button').simulate('click'); - expect(wrapper.find('ToolbarDeleteButton').state('isModalOpen')) - .toBe(true); + expect(wrapper.find('ToolbarDeleteButton').state('isModalOpen')).toBe(true); wrapper.update(); expect(wrapper.find('Modal')).toHaveLength(1); }); @@ -42,33 +35,26 @@ describe('', () => { test('should invoke onDelete prop', () => { const onDelete = jest.fn(); const wrapper = mountWithContexts( - + ); wrapper.find('ToolbarDeleteButton').setState({ isModalOpen: true }); wrapper.find('button.pf-m-danger').simulate('click'); expect(onDelete).toHaveBeenCalled(); - expect(wrapper.find('ToolbarDeleteButton').state('isModalOpen')).toBe(false); + expect(wrapper.find('ToolbarDeleteButton').state('isModalOpen')).toBe( + false + ); }); test('should disable button when no delete permissions', () => { const wrapper = mountWithContexts( - {}} - itemsToDelete={[itemB]} - /> + {}} itemsToDelete={[itemB]} /> ); expect(wrapper.find('button[disabled]')).toHaveLength(1); }); test('should render tooltip', () => { const wrapper = mountWithContexts( - {}} - itemsToDelete={[itemA]} - /> + {}} itemsToDelete={[itemA]} /> ); expect(wrapper.find('Tooltip')).toHaveLength(1); expect(wrapper.find('Tooltip').prop('content')).toEqual('Delete'); diff --git a/awx/ui_next/src/components/Pagination/Pagination.jsx b/awx/ui_next/src/components/Pagination/Pagination.jsx index 926b9d73be..61b495812a 100644 --- a/awx/ui_next/src/components/Pagination/Pagination.jsx +++ b/awx/ui_next/src/components/Pagination/Pagination.jsx @@ -1,18 +1,24 @@ import React from 'react'; import styled, { css } from 'styled-components'; -import { Pagination as PFPagination, DropdownDirection } from '@patternfly/react-core'; +import { + Pagination as PFPagination, + DropdownDirection, +} from '@patternfly/react-core'; import { I18n } from '@lingui/react'; import { t } from '@lingui/macro'; const AWXPagination = styled(PFPagination)` - ${props => (props.perPageOptions && !props.perPageOptions.length && css` - .pf-c-options-menu__toggle-button { + ${props => + props.perPageOptions && + !props.perPageOptions.length && + css` + .pf-c-options-menu__toggle-button { display: none; } - `)} + `} `; -export default (props) => ( +export default props => ( {({ i18n }) => ( ( toNextPage: i18n._(t`Go to next page`), optionsToggle: i18n._(t`Select`), currPage: i18n._(t`Current page`), - paginationTitle: i18n._(t`Pagination`) + paginationTitle: i18n._(t`Pagination`), }} dropDirection={DropdownDirection.up} {...props} diff --git a/awx/ui_next/src/components/Pagination/Pagination.test.jsx b/awx/ui_next/src/components/Pagination/Pagination.test.jsx index beba9a917e..20e3d3c482 100644 --- a/awx/ui_next/src/components/Pagination/Pagination.test.jsx +++ b/awx/ui_next/src/components/Pagination/Pagination.test.jsx @@ -5,12 +5,7 @@ import Pagination from './Pagination'; describe('Pagination', () => { test('renders the expected content', () => { - const wrapper = mountWithContexts( - - ); + const wrapper = mountWithContexts(); expect(wrapper).toHaveLength(1); }); }); diff --git a/awx/ui_next/src/components/RoutedTabs/RoutedTabs.jsx b/awx/ui_next/src/components/RoutedTabs/RoutedTabs.jsx index 2e2b5b4991..396005c4af 100644 --- a/awx/ui_next/src/components/RoutedTabs/RoutedTabs.jsx +++ b/awx/ui_next/src/components/RoutedTabs/RoutedTabs.jsx @@ -25,13 +25,14 @@ const Tabs = styled(PFTabs)` right: 0; bottom: 0; left: 0; - content: ""; + content: ''; border: solid var(--pf-c-tabs__item--BorderColor); - border-width: var(--pf-c-tabs__item--BorderWidth) 0 var(--pf-c-tabs__item--BorderWidth) 0; + border-width: var(--pf-c-tabs__item--BorderWidth) 0 + var(--pf-c-tabs__item--BorderWidth) 0; } `; -function RoutedTabs (props) { +function RoutedTabs(props) { const { history, tabsArray } = props; const getActiveTabId = () => { @@ -42,7 +43,7 @@ function RoutedTabs (props) { return 0; }; - function handleTabSelect (event, eventKey) { + function handleTabSelect(event, eventKey) { const match = tabsArray.find(tab => tab.id === eventKey); if (match) { history.push(match.link); @@ -50,10 +51,7 @@ function RoutedTabs (props) { } return ( - + {tabsArray.map(tab => ( ', () => { @@ -24,21 +24,14 @@ describe('', () => { }); test('RoutedTabs renders successfully', () => { - wrapper = shallow( - <_RoutedTabs - tabsArray={tabs} - history={history} - /> - ); + wrapper = shallow(<_RoutedTabs tabsArray={tabs} history={history} />); expect(wrapper.find(Tab)).toHaveLength(4); }); test('Given a URL the correct tab is active', async () => { wrapper = mount( - + ); @@ -49,9 +42,7 @@ describe('', () => { test('should update history when new tab selected', async () => { wrapper = mount( - + ); diff --git a/awx/ui_next/src/components/Search/Search.jsx b/awx/ui_next/src/components/Search/Search.jsx index 3ffb4aab8a..59cbe74658 100644 --- a/awx/ui_next/src/components/Search/Search.jsx +++ b/awx/ui_next/src/components/Search/Search.jsx @@ -8,11 +8,9 @@ import { DropdownPosition, DropdownToggle, DropdownItem, - TextInput as PFTextInput + TextInput as PFTextInput, } from '@patternfly/react-core'; -import { - SearchIcon -} from '@patternfly/react-icons'; +import { SearchIcon } from '@patternfly/react-icons'; import styled from 'styled-components'; @@ -27,7 +25,8 @@ const Button = styled(PFButton)` `; const Dropdown = styled(PFDropdown)` - &&& { /* Higher specificity required because we are selecting unclassed elements */ + &&& { + /* Higher specificity required because we are selecting unclassed elements */ > button { min-height: 30px; min-width: 70px; @@ -35,20 +34,22 @@ const Dropdown = styled(PFDropdown)` padding: 0 10px; margin: 0px; - > span { /* text element */ + > span { + /* text element */ width: auto; } - > svg { /* caret icon */ + > svg { + /* caret icon */ margin: 0px; padding-top: 3px; padding-left: 3px; } } - } + } `; class Search extends React.Component { - constructor (props) { + constructor(props) { super(props); const { sortedColumnKey } = this.props; @@ -64,11 +65,11 @@ class Search extends React.Component { this.handleSearch = this.handleSearch.bind(this); } - handleDropdownToggle (isSearchDropdownOpen) { + handleDropdownToggle(isSearchDropdownOpen) { this.setState({ isSearchDropdownOpen }); } - handleDropdownSelect ({ target }) { + handleDropdownSelect({ target }) { const { columns } = this.props; const { innerText } = target; @@ -76,30 +77,25 @@ class Search extends React.Component { this.setState({ isSearchDropdownOpen: false, searchKey }); } - handleSearch () { + handleSearch() { const { searchValue } = this.state; const { onSearch } = this.props; onSearch(searchValue); } - handleSearchInputChange (searchValue) { + handleSearchInputChange(searchValue) { this.setState({ searchValue }); } - render () { + render() { const { up } = DropdownPosition; - const { - columns, - i18n - } = this.props; - const { - isSearchDropdownOpen, - searchKey, - searchValue, - } = this.state; + const { columns, i18n } = this.props; + const { isSearchDropdownOpen, searchKey, searchValue } = this.state; - const { name: searchColumnName } = columns.find(({ key }) => key === searchKey); + const { name: searchColumnName } = columns.find( + ({ key }) => key === searchKey + ); const searchDropdownItems = columns .filter(({ key }) => key !== searchKey) @@ -116,14 +112,14 @@ class Search extends React.Component { onSelect={this.handleDropdownSelect} direction={up} isOpen={isSearchDropdownOpen} - toggle={( + toggle={ {searchColumnName} - )} + } dropdownItems={searchDropdownItems} /> ', () => { const onSearch = jest.fn(); search = mountWithContexts( - + ); search.find(searchTextInput).instance().value = 'test-321'; @@ -39,11 +35,7 @@ describe('', () => { const columns = [{ name: 'Name', key: 'name', isSortable: true }]; const onSearch = jest.fn(); const wrapper = mountWithContexts( - + ).find('Search'); expect(wrapper.state('isSearchDropdownOpen')).toEqual(false); wrapper.instance().handleDropdownToggle(true); @@ -53,18 +45,16 @@ describe('', () => { test('handleDropdownSelect properly updates state', async () => { const columns = [ { name: 'Name', key: 'name', isSortable: true }, - { name: 'Description', key: 'description', isSortable: true } + { name: 'Description', key: 'description', isSortable: true }, ]; const onSearch = jest.fn(); const wrapper = mountWithContexts( - + ).find('Search'); expect(wrapper.state('searchKey')).toEqual('name'); - wrapper.instance().handleDropdownSelect({ target: { innerText: 'Description' } }); + wrapper + .instance() + .handleDropdownSelect({ target: { innerText: 'Description' } }); expect(wrapper.state('searchKey')).toEqual('description'); }); }); diff --git a/awx/ui_next/src/components/SelectedList/SelectedList.jsx b/awx/ui_next/src/components/SelectedList/SelectedList.jsx index 24d1b5ab53..6fcaf05939 100644 --- a/awx/ui_next/src/components/SelectedList/SelectedList.jsx +++ b/awx/ui_next/src/components/SelectedList/SelectedList.jsx @@ -19,20 +19,18 @@ const SplitLabelItem = styled(SplitItem)` `; class SelectedList extends Component { - render () { + render() { const { label, selected, showOverflowAfter, onRemove, displayKey, - isReadOnly + isReadOnly, } = this.props; return ( - - {label} - + {label} @@ -58,7 +56,7 @@ SelectedList.propTypes = { onRemove: PropTypes.func, selected: PropTypes.arrayOf(PropTypes.object).isRequired, showOverflowAfter: PropTypes.number, - isReadOnly: PropTypes.bool + isReadOnly: PropTypes.bool, }; SelectedList.defaultProps = { @@ -66,7 +64,7 @@ SelectedList.defaultProps = { label: 'Selected', onRemove: () => null, showOverflowAfter: 5, - isReadOnly: false + isReadOnly: false, }; export default SelectedList; diff --git a/awx/ui_next/src/components/SelectedList/SelectedList.test.jsx b/awx/ui_next/src/components/SelectedList/SelectedList.test.jsx index 41d5444a5c..e8955e077d 100644 --- a/awx/ui_next/src/components/SelectedList/SelectedList.test.jsx +++ b/awx/ui_next/src/components/SelectedList/SelectedList.test.jsx @@ -8,11 +8,12 @@ describe('', () => { const mockSelected = [ { id: 1, - name: 'foo' - }, { + name: 'foo', + }, + { id: 2, - name: 'bar' - } + name: 'bar', + }, ]; mount( ', () => { const mockSelected = [ { id: 1, - name: 'foo' - } + name: 'foo', + }, ]; const wrapper = mount( ', () => { onRemove={onRemove} /> ); - wrapper.find('.pf-c-chip button').first().simulate('click'); + wrapper + .find('.pf-c-chip button') + .first() + .simulate('click'); expect(onRemove).toBeCalledWith({ id: 1, - name: 'foo' + name: 'foo', }); }); }); diff --git a/awx/ui_next/src/components/Sort/Sort.jsx b/awx/ui_next/src/components/Sort/Sort.jsx index 2e8b06d2de..ed22597f14 100644 --- a/awx/ui_next/src/components/Sort/Sort.jsx +++ b/awx/ui_next/src/components/Sort/Sort.jsx @@ -7,13 +7,13 @@ import { Dropdown as PFDropdown, DropdownPosition, DropdownToggle, - DropdownItem + DropdownItem, } from '@patternfly/react-core'; import { SortAlphaDownIcon, SortAlphaUpIcon, SortNumericDownIcon, - SortNumericUpIcon + SortNumericUpIcon, } from '@patternfly/react-icons'; import styled from 'styled-components'; @@ -27,17 +27,19 @@ const Dropdown = styled(PFDropdown)` padding: 0 10px; margin: 0px; - > span { /* text element within dropdown */ + > span { + /* text element within dropdown */ width: auto; } - > svg { /* caret icon */ + > svg { + /* caret icon */ margin: 0px; padding-top: 3px; padding-left: 3px; } } - } + } `; const IconWrapper = styled.span` @@ -47,7 +49,7 @@ const IconWrapper = styled.span` `; class Sort extends React.Component { - constructor (props) { + constructor(props) { super(props); this.state = { @@ -59,40 +61,36 @@ class Sort extends React.Component { this.handleSort = this.handleSort.bind(this); } - handleDropdownToggle (isSortDropdownOpen) { + handleDropdownToggle(isSortDropdownOpen) { this.setState({ isSortDropdownOpen }); } - handleDropdownSelect ({ target }) { + handleDropdownSelect({ target }) { const { columns, onSort, sortOrder } = this.props; const { innerText } = target; - const [{ key: searchKey }] = columns.filter(({ name }) => name === innerText); + const [{ key: searchKey }] = columns.filter( + ({ name }) => name === innerText + ); this.setState({ isSortDropdownOpen: false }); onSort(searchKey, sortOrder); } - handleSort () { + handleSort() { const { onSort, sortedColumnKey, sortOrder } = this.props; const newSortOrder = sortOrder === 'ascending' ? 'descending' : 'ascending'; onSort(sortedColumnKey, newSortOrder); } - render () { + render() { const { up } = DropdownPosition; - const { - columns, - sortedColumnKey, - sortOrder, - i18n - } = this.props; - const { - isSortDropdownOpen - } = this.state; - const [{ name: sortedColumnName, isNumeric }] = columns - .filter(({ key }) => key === sortedColumnKey); + const { columns, sortedColumnKey, sortOrder, i18n } = this.props; + const { isSortDropdownOpen } = this.state; + const [{ name: sortedColumnName, isNumeric }] = columns.filter( + ({ key }) => key === sortedColumnKey + ); const sortDropdownItems = columns .filter(({ key, isSortable }) => isSortable && key !== sortedColumnKey) @@ -104,28 +102,30 @@ class Sort extends React.Component { let SortIcon; if (isNumeric) { - SortIcon = sortOrder === 'ascending' ? SortNumericUpIcon : SortNumericDownIcon; + SortIcon = + sortOrder === 'ascending' ? SortNumericUpIcon : SortNumericDownIcon; } else { - SortIcon = sortOrder === 'ascending' ? SortAlphaUpIcon : SortAlphaDownIcon; + SortIcon = + sortOrder === 'ascending' ? SortAlphaUpIcon : SortAlphaDownIcon; } return ( - { sortDropdownItems.length > 1 && ( + {sortDropdownItems.length > 1 && ( {sortedColumnName} - )} + } dropdownItems={sortDropdownItems} /> )} @@ -148,13 +148,13 @@ Sort.propTypes = { columns: PropTypes.arrayOf(PropTypes.object).isRequired, onSort: PropTypes.func, sortOrder: PropTypes.string, - sortedColumnKey: PropTypes.string + sortedColumnKey: PropTypes.string, }; Sort.defaultProps = { onSort: null, sortOrder: 'ascending', - sortedColumnKey: 'name' + sortedColumnKey: 'name', }; export default withI18n()(Sort); diff --git a/awx/ui_next/src/components/Sort/Sort.test.jsx b/awx/ui_next/src/components/Sort/Sort.test.jsx index 8cf5ae07f1..d23c75fa2f 100644 --- a/awx/ui_next/src/components/Sort/Sort.test.jsx +++ b/awx/ui_next/src/components/Sort/Sort.test.jsx @@ -38,7 +38,7 @@ describe('', () => { { name: 'Foo', key: 'foo', isSortable: true }, { name: 'Bar', key: 'bar', isSortable: true }, { name: 'Bakery', key: 'bakery', isSortable: true }, - { name: 'Baz', key: 'baz' } + { name: 'Baz', key: 'baz' }, ]; const onSort = jest.fn(); @@ -62,7 +62,7 @@ describe('', () => { { name: 'Foo', key: 'foo', isSortable: true }, { name: 'Bar', key: 'bar', isSortable: true }, { name: 'Bakery', key: 'bakery', isSortable: true }, - { name: 'Baz', key: 'baz' } + { name: 'Baz', key: 'baz' }, ]; const onSort = jest.fn(); @@ -86,7 +86,7 @@ describe('', () => { { name: 'Foo', key: 'foo', isSortable: true }, { name: 'Bar', key: 'bar', isSortable: true }, { name: 'Bakery', key: 'bakery', isSortable: true }, - { name: 'Baz', key: 'baz' } + { name: 'Baz', key: 'baz' }, ]; const onSort = jest.fn(); @@ -109,7 +109,7 @@ describe('', () => { { name: 'Foo', key: 'foo', isSortable: true }, { name: 'Bar', key: 'bar', isSortable: true }, { name: 'Bakery', key: 'bakery', isSortable: true }, - { name: 'Baz', key: 'baz' } + { name: 'Baz', key: 'baz' }, ]; const onSort = jest.fn(); @@ -133,8 +133,12 @@ describe('', () => { const downAlphaIconSelector = 'SortAlphaDownIcon'; const upAlphaIconSelector = 'SortAlphaUpIcon'; - const numericColumns = [{ name: 'ID', key: 'id', isSortable: true, isNumeric: true }]; - const alphaColumns = [{ name: 'Name', key: 'name', isSortable: true, isNumeric: false }]; + const numericColumns = [ + { name: 'ID', key: 'id', isSortable: true, isNumeric: true }, + ]; + const alphaColumns = [ + { name: 'Name', key: 'name', isSortable: true, isNumeric: false }, + ]; const onSort = jest.fn(); sort = mountWithContexts( diff --git a/awx/ui_next/src/index.jsx b/awx/ui_next/src/index.jsx index afb0ef7248..dfec0c4c49 100644 --- a/awx/ui_next/src/index.jsx +++ b/awx/ui_next/src/index.jsx @@ -1,13 +1,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { - Route, - Switch, - Redirect -} from 'react-router-dom'; -import { - I18n -} from '@lingui/react'; +import { Route, Switch, Redirect } from 'react-router-dom'; +import { I18n } from '@lingui/react'; import { t } from '@lingui/macro'; import '@patternfly/react-core/dist/styles/base.css'; @@ -44,19 +38,21 @@ import RootProvider from './RootProvider'; import { BrandName } from './variables'; // eslint-disable-next-line import/prefer-default-export -export function main (render) { +export function main(render) { const el = document.getElementById('app'); document.title = `Ansible ${BrandName}`; - const defaultRedirect = () => (); + const defaultRedirect = () => ; const removeTrailingSlash = ( ( - - )} + render={({ + history: { + location: { pathname, search, hash }, + }, + }) => } /> ); const loginRoutes = ( @@ -64,9 +60,7 @@ export function main (render) { {removeTrailingSlash} ( - - )} + render={() => } /> @@ -77,7 +71,9 @@ export function main (render) { {({ i18n }) => ( - {!isAuthenticated(document.cookie) ? loginRoutes : ( + {!isAuthenticated(document.cookie) ? ( + loginRoutes + ) : ( {removeTrailingSlash} @@ -94,22 +90,22 @@ export function main (render) { { title: i18n._(t`Dashboard`), path: '/home', - component: Dashboard + component: Dashboard, }, { title: i18n._(t`Jobs`), path: '/jobs', - component: Jobs + component: Jobs, }, { title: i18n._(t`Schedules`), path: '/schedules', - component: Schedules + component: Schedules, }, { title: i18n._(t`My View`), path: '/portal', - component: Portal + component: Portal, }, ], }, @@ -120,27 +116,27 @@ export function main (render) { { title: i18n._(t`Templates`), path: '/templates', - component: Templates + component: Templates, }, { title: i18n._(t`Credentials`), path: '/credentials', - component: Credentials + component: Credentials, }, { title: i18n._(t`Projects`), path: '/projects', - component: Projects + component: Projects, }, { title: i18n._(t`Inventories`), path: '/inventories', - component: Inventories + component: Inventories, }, { title: i18n._(t`Inventory Scripts`), path: '/inventory_scripts', - component: InventoryScripts + component: InventoryScripts, }, ], }, @@ -151,17 +147,17 @@ export function main (render) { { title: i18n._(t`Organizations`), path: '/organizations', - component: Organizations + component: Organizations, }, { title: i18n._(t`Users`), path: '/users', - component: Users + component: Users, }, { title: i18n._(t`Teams`), path: '/teams', - component: Teams + component: Teams, }, ], }, @@ -172,27 +168,27 @@ export function main (render) { { title: i18n._(t`Credential Types`), path: '/credential_types', - component: CredentialTypes + component: CredentialTypes, }, { title: i18n._(t`Notifications`), path: '/notification_templates', - component: NotificationTemplates + component: NotificationTemplates, }, { title: i18n._(t`Management Jobs`), path: '/management_jobs', - component: ManagementJobs + component: ManagementJobs, }, { title: i18n._(t`Instance Groups`), path: '/instance_groups', - component: InstanceGroups + component: InstanceGroups, }, { title: i18n._(t`Integrations`), path: '/applications', - component: Applications + component: Applications, }, ], }, @@ -203,34 +199,37 @@ export function main (render) { { title: i18n._(t`Authentication`), path: '/auth_settings', - component: AuthSettings + component: AuthSettings, }, { title: i18n._(t`Jobs`), path: '/jobs_settings', - component: JobsSettings + component: JobsSettings, }, { title: i18n._(t`System`), path: '/system_settings', - component: SystemSettings + component: SystemSettings, }, { title: i18n._(t`User Interface`), path: '/ui_settings', - component: UISettings + component: UISettings, }, { title: i18n._(t`License`), path: '/license', - component: License + component: License, }, ], }, ]} - render={({ routeGroups }) => ( + render={({ routeGroups }) => routeGroups - .reduce((allRoutes, { routes }) => allRoutes.concat(routes), []) + .reduce( + (allRoutes, { routes }) => allRoutes.concat(routes), + [] + ) .map(({ component: PageComponent, path }) => ( )) - )} + } /> )} /> @@ -249,7 +248,8 @@ export function main (render) { )} - , el || document.createElement('div') + , + el || document.createElement('div') ); } diff --git a/awx/ui_next/src/index.test.jsx b/awx/ui_next/src/index.test.jsx index 99f764cd32..085e86ac66 100644 --- a/awx/ui_next/src/index.test.jsx +++ b/awx/ui_next/src/index.test.jsx @@ -3,11 +3,7 @@ import { mount } from 'enzyme'; import { MemoryRouter } from 'react-router-dom'; import { main } from './index'; -const render = template => mount( - - {template} - -); +const render = template => mount({template}); describe('index.jsx', () => { test('index.jsx loads without issue', () => { diff --git a/awx/ui_next/src/screens/Application/Applications.jsx b/awx/ui_next/src/screens/Application/Applications.jsx index 8d6bf4eba5..af58ee354b 100644 --- a/awx/ui_next/src/screens/Application/Applications.jsx +++ b/awx/ui_next/src/screens/Application/Applications.jsx @@ -8,16 +8,14 @@ import { } from '@patternfly/react-core'; class Applications extends Component { - render () { + render() { const { i18n } = this.props; const { light, medium } = PageSectionVariants; return ( - - {i18n._(t`Applications`)} - + {i18n._(t`Applications`)} diff --git a/awx/ui_next/src/screens/AuthSetting/AuthSettings.jsx b/awx/ui_next/src/screens/AuthSetting/AuthSettings.jsx index c2c45e1d17..ae66cb30be 100644 --- a/awx/ui_next/src/screens/AuthSetting/AuthSettings.jsx +++ b/awx/ui_next/src/screens/AuthSetting/AuthSettings.jsx @@ -8,16 +8,14 @@ import { } from '@patternfly/react-core'; class AuthSettings extends Component { - render () { + render() { const { i18n } = this.props; const { light, medium } = PageSectionVariants; return ( - - {i18n._(t`Authentication Settings`)} - + {i18n._(t`Authentication Settings`)} diff --git a/awx/ui_next/src/screens/Credential/Credentials.jsx b/awx/ui_next/src/screens/Credential/Credentials.jsx index c3384a40b1..55182bed66 100644 --- a/awx/ui_next/src/screens/Credential/Credentials.jsx +++ b/awx/ui_next/src/screens/Credential/Credentials.jsx @@ -8,16 +8,14 @@ import { } from '@patternfly/react-core'; class Credentials extends Component { - render () { + render() { const { i18n } = this.props; const { light, medium } = PageSectionVariants; return ( - - {i18n._(t`Credentials`)} - + {i18n._(t`Credentials`)} diff --git a/awx/ui_next/src/screens/CredentialType/CredentialTypes.jsx b/awx/ui_next/src/screens/CredentialType/CredentialTypes.jsx index b711560509..8a5669c1f6 100644 --- a/awx/ui_next/src/screens/CredentialType/CredentialTypes.jsx +++ b/awx/ui_next/src/screens/CredentialType/CredentialTypes.jsx @@ -8,16 +8,14 @@ import { } from '@patternfly/react-core'; class CredentialTypes extends Component { - render () { + render() { const { i18n } = this.props; const { light, medium } = PageSectionVariants; return ( - - {i18n._(t`Credential Types`)} - + {i18n._(t`Credential Types`)} diff --git a/awx/ui_next/src/screens/Dashboard/Dashboard.jsx b/awx/ui_next/src/screens/Dashboard/Dashboard.jsx index 1c0ae88339..bebc503618 100644 --- a/awx/ui_next/src/screens/Dashboard/Dashboard.jsx +++ b/awx/ui_next/src/screens/Dashboard/Dashboard.jsx @@ -8,16 +8,14 @@ import { } from '@patternfly/react-core'; class Dashboard extends Component { - render () { + render() { const { i18n } = this.props; const { light, medium } = PageSectionVariants; return ( - - {i18n._(t`Dashboard`)} - + {i18n._(t`Dashboard`)} diff --git a/awx/ui_next/src/screens/InstanceGroup/InstanceGroups.jsx b/awx/ui_next/src/screens/InstanceGroup/InstanceGroups.jsx index 7e05873691..49a9dc5456 100644 --- a/awx/ui_next/src/screens/InstanceGroup/InstanceGroups.jsx +++ b/awx/ui_next/src/screens/InstanceGroup/InstanceGroups.jsx @@ -8,16 +8,14 @@ import { } from '@patternfly/react-core'; class InstanceGroups extends Component { - render () { + render() { const { i18n } = this.props; const { light, medium } = PageSectionVariants; return ( - - {i18n._(t`Instance Groups`)} - + {i18n._(t`Instance Groups`)} diff --git a/awx/ui_next/src/screens/Inventory/Inventories.jsx b/awx/ui_next/src/screens/Inventory/Inventories.jsx index d0457dd00a..d69bc68ceb 100644 --- a/awx/ui_next/src/screens/Inventory/Inventories.jsx +++ b/awx/ui_next/src/screens/Inventory/Inventories.jsx @@ -8,16 +8,14 @@ import { } from '@patternfly/react-core'; class Inventories extends Component { - render () { + render() { const { i18n } = this.props; const { light, medium } = PageSectionVariants; return ( - - {i18n._(t`Inventories`)} - + {i18n._(t`Inventories`)} diff --git a/awx/ui_next/src/screens/InventoryScript/InventoryScripts.jsx b/awx/ui_next/src/screens/InventoryScript/InventoryScripts.jsx index aab83da691..d3cc7a2a55 100644 --- a/awx/ui_next/src/screens/InventoryScript/InventoryScripts.jsx +++ b/awx/ui_next/src/screens/InventoryScript/InventoryScripts.jsx @@ -8,16 +8,14 @@ import { } from '@patternfly/react-core'; class InventoryScripts extends Component { - render () { + render() { const { i18n } = this.props; const { light, medium } = PageSectionVariants; return ( - - {i18n._(t`Inventory Scripts`)} - + {i18n._(t`Inventory Scripts`)} diff --git a/awx/ui_next/src/screens/Job/Job.jsx b/awx/ui_next/src/screens/Job/Job.jsx index c83a9477a3..67b33f6fc5 100644 --- a/awx/ui_next/src/screens/Job/Job.jsx +++ b/awx/ui_next/src/screens/Job/Job.jsx @@ -3,7 +3,11 @@ import { Route, withRouter, Switch, Redirect } from 'react-router-dom'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import styled from 'styled-components'; -import { Card, CardHeader as PFCardHeader, PageSection } from '@patternfly/react-core'; +import { + Card, + CardHeader as PFCardHeader, + PageSection, +} from '@patternfly/react-core'; import { JobsAPI } from '@api'; import ContentError from '@components/ContentError'; @@ -14,36 +18,33 @@ import JobDetail from './JobDetail'; import JobOutput from './JobOutput'; class Job extends Component { - constructor (props) { + constructor(props) { super(props); this.state = { job: null, contentError: null, hasContentLoading: true, - isInitialized: false + isInitialized: false, }; this.loadJob = this.loadJob.bind(this); } - async componentDidMount () { + async componentDidMount() { await this.loadJob(); this.setState({ isInitialized: true }); } - async componentDidUpdate (prevProps) { + async componentDidUpdate(prevProps) { const { location } = this.props; if (location !== prevProps.location) { await this.loadJob(); } } - async loadJob () { - const { - match, - setBreadcrumb, - } = this.props; + async loadJob() { + const { match, setBreadcrumb } = this.props; const id = parseInt(match.params.id, 10); this.setState({ contentError: null, hasContentLoading: true }); @@ -58,23 +59,14 @@ class Job extends Component { } } - render () { - const { - history, - match, - i18n - } = this.props; + render() { + const { history, match, i18n } = this.props; - const { - job, - contentError, - hasContentLoading, - isInitialized - } = this.state; + const { job, contentError, hasContentLoading, isInitialized } = this.state; const tabsArray = [ { name: i18n._(t`Details`), link: `${match.url}/details`, id: 0 }, - { name: i18n._(t`Output`), link: `${match.url}/output`, id: 1 } + { name: i18n._(t`Output`), link: `${match.url}/output`, id: 1 }, ]; const CardHeader = styled(PFCardHeader)` @@ -86,11 +78,7 @@ class Job extends Component { let cardHeader = ( - + ); @@ -116,33 +104,19 @@ class Job extends Component { return ( - { cardHeader } + {cardHeader} - + {job && ( ( - - )} + render={() => } /> )} {job && ( ( - - )} + render={() => } /> )} diff --git a/awx/ui_next/src/screens/Job/JobDetail/JobDetail.jsx b/awx/ui_next/src/screens/Job/JobDetail/JobDetail.jsx index fa6522382c..a4b1ea8651 100644 --- a/awx/ui_next/src/screens/Job/JobDetail/JobDetail.jsx +++ b/awx/ui_next/src/screens/Job/JobDetail/JobDetail.jsx @@ -10,11 +10,8 @@ const ActionButtonWrapper = styled.div` justify-content: flex-end; `; class JobDetail extends Component { - render () { - const { - job, - i18n - } = this.props; + render() { + const { job, i18n } = this.props; return ( diff --git a/awx/ui_next/src/screens/Job/JobDetail/JobDetail.test.jsx b/awx/ui_next/src/screens/Job/JobDetail/JobDetail.test.jsx index a0961b1a93..e8eaef4344 100644 --- a/awx/ui_next/src/screens/Job/JobDetail/JobDetail.test.jsx +++ b/awx/ui_next/src/screens/Job/JobDetail/JobDetail.test.jsx @@ -6,19 +6,15 @@ import JobDetail from './JobDetail'; describe('', () => { const mockDetails = { - name: 'Foo' + name: 'Foo', }; test('initially renders succesfully', () => { - mountWithContexts( - - ); + mountWithContexts(); }); test('should display a Close button', () => { - const wrapper = mountWithContexts( - - ); + const wrapper = mountWithContexts(); expect(wrapper.find('Button[aria-label="close"]').length).toBe(1); wrapper.unmount(); diff --git a/awx/ui_next/src/screens/Job/JobList/JobList.jsx b/awx/ui_next/src/screens/Job/JobList/JobList.jsx index 7b8faef76c..f519639c94 100644 --- a/awx/ui_next/src/screens/Job/JobList/JobList.jsx +++ b/awx/ui_next/src/screens/Job/JobList/JobList.jsx @@ -2,18 +2,14 @@ import React, { Component } from 'react'; import { withRouter } from 'react-router-dom'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; -import { - Card, - PageSection, - PageSectionVariants, -} from '@patternfly/react-core'; +import { Card, PageSection, PageSectionVariants } from '@patternfly/react-core'; import { UnifiedJobsAPI } from '@api'; import AlertModal from '@components/AlertModal'; import DatalistToolbar from '@components/DataListToolbar'; import ErrorDetail from '@components/ErrorDetail'; import PaginatedDataList, { - ToolbarDeleteButton + ToolbarDeleteButton, } from '@components/PaginatedDataList'; import { getQSConfig, parseNamespacedQueryString } from '@util/qs'; @@ -27,7 +23,7 @@ const QS_CONFIG = getQSConfig('job', { }); class JobList extends Component { - constructor (props) { + constructor(props) { super(props); this.state = { @@ -45,28 +41,28 @@ class JobList extends Component { this.handleDeleteErrorClose = this.handleDeleteErrorClose.bind(this); } - componentDidMount () { + componentDidMount() { this.loadJobs(); } - componentDidUpdate (prevProps) { + componentDidUpdate(prevProps) { const { location } = this.props; if (location !== prevProps.location) { this.loadJobs(); } } - handleDeleteErrorClose () { + handleDeleteErrorClose() { this.setState({ deletionError: null }); } - handleSelectAll (isSelected) { + handleSelectAll(isSelected) { const { jobs } = this.state; const selected = isSelected ? [...jobs] : []; this.setState({ selected }); } - handleSelect (item) { + handleSelect(item) { const { selected } = this.state; if (selected.some(s => s.id === item.id)) { this.setState({ selected: selected.filter(s => s.id !== item.id) }); @@ -75,7 +71,7 @@ class JobList extends Component { } } - async handleDelete () { + async handleDelete() { const { selected } = this.state; this.setState({ hasContentLoading: true }); try { @@ -87,13 +83,15 @@ class JobList extends Component { } } - async loadJobs () { + async loadJobs() { const { location } = this.props; const params = parseNamespacedQueryString(QS_CONFIG, location.search); this.setState({ contentError: null, hasContentLoading: true }); try { - const { data: { count, results } } = await UnifiedJobsAPI.read(params); + const { + data: { count, results }, + } = await UnifiedJobsAPI.read(params); this.setState({ itemCount: count, jobs: results, @@ -106,7 +104,7 @@ class JobList extends Component { } } - render () { + render() { const { contentError, hasContentLoading, @@ -115,10 +113,7 @@ class JobList extends Component { itemCount, selected, } = this.state; - const { - match, - i18n - } = this.props; + const { match, i18n } = this.props; const { medium } = PageSectionVariants; const isAllSelected = selected.length === jobs.length; const itemName = i18n._(t`Job`); @@ -134,10 +129,14 @@ class JobList extends Component { qsConfig={QS_CONFIG} toolbarColumns={[ { name: i18n._(t`Name`), key: 'name', isSortable: true }, - { name: i18n._(t`Finished`), key: 'finished', isSortable: true, isNumeric: true }, - + { + name: i18n._(t`Finished`), + key: 'finished', + isSortable: true, + isNumeric: true, + }, ]} - renderToolbar={(props) => ( + renderToolbar={props => ( + />, ]} /> )} - renderItem={(job) => ( + renderItem={job => ( ', () => { - test('initially renders succesfully', async (done) => { + test('initially renders succesfully', async done => { const wrapper = mountWithContexts(); - await waitForElement(wrapper, 'JobList', (el) => el.state('jobs').length === 3); + await waitForElement( + wrapper, + 'JobList', + el => el.state('jobs').length === 3 + ); done(); }); - test('select makes expected state updates', async (done) => { + test('select makes expected state updates', async done => { const [mockItem] = mockResults; const wrapper = mountWithContexts(); - await waitForElement(wrapper, 'JobListItem', (el) => el.length === 3); + await waitForElement(wrapper, 'JobListItem', el => el.length === 3); - wrapper.find('JobListItem').first().prop('onSelect')(mockItem); + wrapper + .find('JobListItem') + .first() + .prop('onSelect')(mockItem); expect(wrapper.find('JobList').state('selected').length).toEqual(1); - wrapper.find('JobListItem').first().prop('onSelect')(mockItem); + wrapper + .find('JobListItem') + .first() + .prop('onSelect')(mockItem); expect(wrapper.find('JobList').state('selected').length).toEqual(0); done(); }); - test('select-all-delete makes expected state updates and api calls', async (done) => { + test('select-all-delete makes expected state updates and api calls', async done => { const wrapper = mountWithContexts(); - await waitForElement(wrapper, 'JobListItem', (el) => el.length === 3); + await waitForElement(wrapper, 'JobListItem', el => el.length === 3); wrapper.find('DataListToolbar').prop('onSelectAll')(true); expect(wrapper.find('JobList').state('selected').length).toEqual(3); diff --git a/awx/ui_next/src/screens/Job/JobList/JobListItem.jsx b/awx/ui_next/src/screens/Job/JobList/JobListItem.jsx index eedf1c7d11..4d2fe70f53 100644 --- a/awx/ui_next/src/screens/Job/JobList/JobListItem.jsx +++ b/awx/ui_next/src/screens/Job/JobList/JobListItem.jsx @@ -12,12 +12,8 @@ import VerticalSeparator from '@components/VerticalSeparator'; import { toTitleCase } from '@util/strings'; class JobListItem extends Component { - render () { - const { - job, - isSelected, - onSelect, - } = this.props; + render() { + const { job, isSelected, onSelect } = this.props; return ( - - - - - {job.name} - - - , - {toTitleCase(job.type)}, - {job.finished}, - ]} + + + + + {job.name} + + + , + {toTitleCase(job.type)}, + {job.finished}, + ]} /> diff --git a/awx/ui_next/src/screens/Job/JobList/JobListItem.test.jsx b/awx/ui_next/src/screens/Job/JobList/JobListItem.test.jsx index 8150d7803c..12c9e89864 100644 --- a/awx/ui_next/src/screens/Job/JobList/JobListItem.test.jsx +++ b/awx/ui_next/src/screens/Job/JobList/JobListItem.test.jsx @@ -15,7 +15,7 @@ describe('', () => { job={{ id: 1, name: 'Job', - type: 'project update' + type: 'project update', }} detailUrl="/organization/1" isSelected diff --git a/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx b/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx index dc8ae07947..1dbdb581d6 100644 --- a/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx +++ b/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx @@ -2,10 +2,8 @@ import React, { Component } from 'react'; import { CardBody } from '@patternfly/react-core'; class JobOutput extends Component { - render () { - const { - job - } = this.props; + render() { + const { job } = this.props; return ( diff --git a/awx/ui_next/src/screens/Job/JobOutput/JobOutput.test.jsx b/awx/ui_next/src/screens/Job/JobOutput/JobOutput.test.jsx index 416cdabc09..04b6c3e9d4 100644 --- a/awx/ui_next/src/screens/Job/JobOutput/JobOutput.test.jsx +++ b/awx/ui_next/src/screens/Job/JobOutput/JobOutput.test.jsx @@ -6,12 +6,10 @@ import JobOutput from './JobOutput'; describe('', () => { const mockDetails = { - name: 'Foo' + name: 'Foo', }; test('initially renders succesfully', () => { - mountWithContexts( - - ); + mountWithContexts(); }); }); diff --git a/awx/ui_next/src/screens/Job/JobOutput/index.js b/awx/ui_next/src/screens/Job/JobOutput/index.js index 816ea6c6a9..26d1b68b82 100644 --- a/awx/ui_next/src/screens/Job/JobOutput/index.js +++ b/awx/ui_next/src/screens/Job/JobOutput/index.js @@ -1,2 +1 @@ export { default } from './JobOutput'; - diff --git a/awx/ui_next/src/screens/Job/Jobs.jsx b/awx/ui_next/src/screens/Job/Jobs.jsx index 6f4a1208a8..fc4311b2bb 100644 --- a/awx/ui_next/src/screens/Job/Jobs.jsx +++ b/awx/ui_next/src/screens/Job/Jobs.jsx @@ -9,19 +9,19 @@ import Job from './Job'; import JobList from './JobList/JobList'; class Jobs extends Component { - constructor (props) { + constructor(props) { super(props); const { i18n } = props; this.state = { breadcrumbConfig: { - '/jobs': i18n._(t`Jobs`) - } + '/jobs': i18n._(t`Jobs`), + }, }; } - setBreadcrumbConfig = (job) => { + setBreadcrumbConfig = job => { const { i18n } = this.props; if (!job) { @@ -32,21 +32,19 @@ class Jobs extends Component { '/jobs': i18n._(t`Jobs`), [`/jobs/${job.id}`]: `${job.name}`, [`/jobs/${job.id}/details`]: i18n._(t`Details`), - [`/jobs/${job.id}/output`]: i18n._(t`Output`) + [`/jobs/${job.id}/output`]: i18n._(t`Output`), }; this.setState({ breadcrumbConfig }); - } + }; - render () { + render() { const { match, history, location } = this.props; const { breadcrumbConfig } = this.state; return ( - + ', () => { test('initially renders succesfully', () => { - mountWithContexts( - - ); + mountWithContexts(); }); test('should display a breadcrumb heading', () => { @@ -18,20 +16,17 @@ describe('', () => { }); const match = { path: '/jobs', url: '/jobs', isExact: true }; - const wrapper = mountWithContexts( - , - { - context: { - router: { - history, - route: { - location: history.location, - match - } - } - } - } - ); + const wrapper = mountWithContexts(, { + context: { + router: { + history, + route: { + location: history.location, + match, + }, + }, + }, + }); expect(wrapper.find('BreadcrumbHeading').length).toBe(1); wrapper.unmount(); }); diff --git a/awx/ui_next/src/screens/JobsSetting/JobsSettings.jsx b/awx/ui_next/src/screens/JobsSetting/JobsSettings.jsx index 5327f74d48..bb0674682a 100644 --- a/awx/ui_next/src/screens/JobsSetting/JobsSettings.jsx +++ b/awx/ui_next/src/screens/JobsSetting/JobsSettings.jsx @@ -8,16 +8,14 @@ import { } from '@patternfly/react-core'; class JobsSettings extends Component { - render () { + render() { const { i18n } = this.props; const { light, medium } = PageSectionVariants; return ( - - {i18n._(t`Jobs Settings`)} - + {i18n._(t`Jobs Settings`)} diff --git a/awx/ui_next/src/screens/License/License.jsx b/awx/ui_next/src/screens/License/License.jsx index db74e17fb0..cde48e190e 100644 --- a/awx/ui_next/src/screens/License/License.jsx +++ b/awx/ui_next/src/screens/License/License.jsx @@ -8,16 +8,14 @@ import { } from '@patternfly/react-core'; class License extends Component { - render () { + render() { const { i18n } = this.props; const { light, medium } = PageSectionVariants; return ( - - {i18n._(t`License`)} - + {i18n._(t`License`)} diff --git a/awx/ui_next/src/screens/Login/Login.jsx b/awx/ui_next/src/screens/Login/Login.jsx index 2d47a8fc7c..8abece15e9 100644 --- a/awx/ui_next/src/screens/Login/Login.jsx +++ b/awx/ui_next/src/screens/Login/Login.jsx @@ -3,10 +3,7 @@ import { Redirect, withRouter } from 'react-router-dom'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import styled from 'styled-components'; -import { - LoginForm, - LoginPage as PFLoginPage, -} from '@patternfly/react-core'; +import { LoginForm, LoginPage as PFLoginPage } from '@patternfly/react-core'; import { RootAPI } from '../../api'; import { BrandName } from '../../variables'; @@ -19,7 +16,7 @@ const LoginPage = styled(PFLoginPage)` `; class AWXLogin extends Component { - constructor (props) { + constructor(props) { super(props); this.state = { @@ -39,14 +36,16 @@ class AWXLogin extends Component { this.loadCustomLoginInfo = this.loadCustomLoginInfo.bind(this); } - async componentDidMount () { + async componentDidMount() { await this.loadCustomLoginInfo(); } - async loadCustomLoginInfo () { + async loadCustomLoginInfo() { this.setState({ isLoading: true }); try { - const { data: { custom_logo, custom_login_info } } = await RootAPI.read(); + const { + data: { custom_logo, custom_login_info }, + } = await RootAPI.read(); const logo = custom_logo ? `data:image/jpeg;${custom_logo}` : brandLogo; this.setState({ logo, loginInfo: custom_login_info }); @@ -57,7 +56,7 @@ class AWXLogin extends Component { } } - async handleLoginButtonClick (event) { + async handleLoginButtonClick(event) { const { username, password, isAuthenticating } = this.state; event.preventDefault(); @@ -82,15 +81,15 @@ class AWXLogin extends Component { } } - handleChangeUsername (value) { + handleChangeUsername(value) { this.setState({ username: value, hasValidationError: false }); } - handleChangePassword (value) { + handleChangePassword(value) { this.setState({ password: value, hasValidationError: false }); } - render () { + render() { const { hasAuthError, hasValidationError, @@ -111,7 +110,7 @@ class AWXLogin extends Component { } if (isAuthenticated(document.cookie)) { - return (); + return ; } let helperText; @@ -129,10 +128,10 @@ class AWXLogin extends Component { textContent={loginInfo} > ', () => { - async function findChildren (wrapper) { + async function findChildren(wrapper) { const [ awxLogin, loginPage, @@ -18,13 +18,21 @@ describe('', () => { submitButton, loginHeaderLogo, ] = await Promise.all([ - waitForElement(wrapper, 'AWXLogin', (el) => el.length === 1), - waitForElement(wrapper, 'LoginPage', (el) => el.length === 1), - waitForElement(wrapper, 'LoginForm', (el) => el.length === 1), - waitForElement(wrapper, 'input#pf-login-username-id', (el) => el.length === 1), - waitForElement(wrapper, 'input#pf-login-password-id', (el) => el.length === 1), - waitForElement(wrapper, 'Button[type="submit"]', (el) => el.length === 1), - waitForElement(wrapper, 'img', (el) => el.length === 1), + waitForElement(wrapper, 'AWXLogin', el => el.length === 1), + waitForElement(wrapper, 'LoginPage', el => el.length === 1), + waitForElement(wrapper, 'LoginForm', el => el.length === 1), + waitForElement( + wrapper, + 'input#pf-login-username-id', + el => el.length === 1 + ), + waitForElement( + wrapper, + 'input#pf-login-password-id', + el => el.length === 1 + ), + waitForElement(wrapper, 'Button[type="submit"]', el => el.length === 1), + waitForElement(wrapper, 'img', el => el.length === 1), ]); return { awxLogin, @@ -41,8 +49,8 @@ describe('', () => { RootAPI.read.mockResolvedValue({ data: { custom_login_info: '', - custom_logo: 'images/foo.jpg' - } + custom_logo: 'images/foo.jpg', + }, }); }); @@ -50,7 +58,7 @@ describe('', () => { jest.clearAllMocks(); }); - test('initially renders without crashing', async (done) => { + test('initially renders without crashing', async done => { const loginWrapper = mountWithContexts( false} /> ); @@ -67,17 +75,20 @@ describe('', () => { done(); }); - test('custom logo renders Brand component with correct src and alt', async (done) => { + test('custom logo renders Brand component with correct src and alt', async done => { const loginWrapper = mountWithContexts( false} /> ); const { loginHeaderLogo } = await findChildren(loginWrapper); const { alt, src } = loginHeaderLogo.props(); - expect([alt, src]).toEqual(['Foo Application', 'data:image/jpeg;images/foo.jpg']); + expect([alt, src]).toEqual([ + 'Foo Application', + 'data:image/jpeg;images/foo.jpg', + ]); done(); }); - test('default logo renders Brand component with correct src and alt', async (done) => { + test('default logo renders Brand component with correct src and alt', async done => { RootAPI.read.mockResolvedValue({ data: {} }); const loginWrapper = mountWithContexts( false} /> @@ -88,7 +99,7 @@ describe('', () => { done(); }); - test('default logo renders on data initialization error', async (done) => { + test('default logo renders on data initialization error', async done => { RootAPI.read.mockRejectedValueOnce({ response: { status: 500 } }); const loginWrapper = mountWithContexts( false} /> @@ -99,74 +110,122 @@ describe('', () => { done(); }); - test('state maps to un/pw input value props', async (done) => { + test('state maps to un/pw input value props', async done => { const loginWrapper = mountWithContexts( false} /> ); const { usernameInput, passwordInput } = await findChildren(loginWrapper); usernameInput.props().onChange({ currentTarget: { value: 'un' } }); passwordInput.props().onChange({ currentTarget: { value: 'pw' } }); - await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('username') === 'un'); - await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('password') === 'pw'); + await waitForElement( + loginWrapper, + 'AWXLogin', + el => el.state('username') === 'un' + ); + await waitForElement( + loginWrapper, + 'AWXLogin', + el => el.state('password') === 'pw' + ); done(); }); - test('handles input validation errors and clears on input value change', async (done) => { + test('handles input validation errors and clears on input value change', async done => { const formError = '.pf-c-form__helper-text.pf-m-error'; const loginWrapper = mountWithContexts( false} /> ); - const { - usernameInput, - passwordInput, - submitButton - } = await findChildren(loginWrapper); + const { usernameInput, passwordInput, submitButton } = await findChildren( + loginWrapper + ); RootAPI.login.mockRejectedValueOnce({ response: { status: 401 } }); usernameInput.props().onChange({ currentTarget: { value: 'invalid' } }); passwordInput.props().onChange({ currentTarget: { value: 'invalid' } }); submitButton.simulate('click'); - await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('username') === 'invalid'); - await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('password') === 'invalid'); - await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('hasValidationError') === true); - await waitForElement(loginWrapper, formError, (el) => el.length === 1); + await waitForElement( + loginWrapper, + 'AWXLogin', + el => el.state('username') === 'invalid' + ); + await waitForElement( + loginWrapper, + 'AWXLogin', + el => el.state('password') === 'invalid' + ); + await waitForElement( + loginWrapper, + 'AWXLogin', + el => el.state('hasValidationError') === true + ); + await waitForElement(loginWrapper, formError, el => el.length === 1); usernameInput.props().onChange({ currentTarget: { value: 'dsarif' } }); passwordInput.props().onChange({ currentTarget: { value: 'freneticpny' } }); - await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('username') === 'dsarif'); - await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('password') === 'freneticpny'); - await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('hasValidationError') === false); - await waitForElement(loginWrapper, formError, (el) => el.length === 0); + await waitForElement( + loginWrapper, + 'AWXLogin', + el => el.state('username') === 'dsarif' + ); + await waitForElement( + loginWrapper, + 'AWXLogin', + el => el.state('password') === 'freneticpny' + ); + await waitForElement( + loginWrapper, + 'AWXLogin', + el => el.state('hasValidationError') === false + ); + await waitForElement(loginWrapper, formError, el => el.length === 0); done(); }); - test('handles other errors and clears on resubmit', async (done) => { + test('handles other errors and clears on resubmit', async done => { const loginWrapper = mountWithContexts( false} /> ); - const { - usernameInput, - passwordInput, - submitButton - } = await findChildren(loginWrapper); + const { usernameInput, passwordInput, submitButton } = await findChildren( + loginWrapper + ); RootAPI.login.mockRejectedValueOnce({ response: { status: 500 } }); submitButton.simulate('click'); - await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('hasAuthError') === true); + await waitForElement( + loginWrapper, + 'AWXLogin', + el => el.state('hasAuthError') === true + ); usernameInput.props().onChange({ currentTarget: { value: 'sgrimes' } }); passwordInput.props().onChange({ currentTarget: { value: 'ovid' } }); - await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('username') === 'sgrimes'); - await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('password') === 'ovid'); - await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('hasAuthError') === true); + await waitForElement( + loginWrapper, + 'AWXLogin', + el => el.state('username') === 'sgrimes' + ); + await waitForElement( + loginWrapper, + 'AWXLogin', + el => el.state('password') === 'ovid' + ); + await waitForElement( + loginWrapper, + 'AWXLogin', + el => el.state('hasAuthError') === true + ); submitButton.simulate('click'); - await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('hasAuthError') === false); + await waitForElement( + loginWrapper, + 'AWXLogin', + el => el.state('hasAuthError') === false + ); done(); }); - test('no login requests are made when already authenticating', async (done) => { + test('no login requests are made when already authenticating', async done => { const loginWrapper = mountWithContexts( false} /> ); @@ -185,33 +244,39 @@ describe('', () => { done(); }); - test('submit calls api.login successfully', async (done) => { + test('submit calls api.login successfully', async done => { const loginWrapper = mountWithContexts( false} /> ); - const { - usernameInput, - passwordInput, - submitButton, - } = await findChildren(loginWrapper); + const { usernameInput, passwordInput, submitButton } = await findChildren( + loginWrapper + ); usernameInput.props().onChange({ currentTarget: { value: 'gthorpe' } }); passwordInput.props().onChange({ currentTarget: { value: 'hydro' } }); submitButton.simulate('click'); - await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('isAuthenticating') === true); - await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('isAuthenticating') === false); + await waitForElement( + loginWrapper, + 'AWXLogin', + el => el.state('isAuthenticating') === true + ); + await waitForElement( + loginWrapper, + 'AWXLogin', + el => el.state('isAuthenticating') === false + ); expect(RootAPI.login).toHaveBeenCalledTimes(1); expect(RootAPI.login).toHaveBeenCalledWith('gthorpe', 'hydro'); done(); }); - test('render Redirect to / when already authenticated', async (done) => { + test('render Redirect to / when already authenticated', async done => { const loginWrapper = mountWithContexts( true} /> ); - await waitForElement(loginWrapper, 'Redirect', (el) => el.length === 1); - await waitForElement(loginWrapper, 'Redirect', (el) => el.props().to === '/'); + await waitForElement(loginWrapper, 'Redirect', el => el.length === 1); + await waitForElement(loginWrapper, 'Redirect', el => el.props().to === '/'); done(); }); }); diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobs.jsx b/awx/ui_next/src/screens/ManagementJob/ManagementJobs.jsx index c74b484314..06936e53dd 100644 --- a/awx/ui_next/src/screens/ManagementJob/ManagementJobs.jsx +++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobs.jsx @@ -8,16 +8,14 @@ import { } from '@patternfly/react-core'; class ManagementJobs extends Component { - render () { + render() { const { i18n } = this.props; const { light, medium } = PageSectionVariants; return ( - - {i18n._(t`Management Jobs`)} - + {i18n._(t`Management Jobs`)} diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplates.jsx b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplates.jsx index 9b28ecb9af..b679f51c8b 100644 --- a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplates.jsx +++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplates.jsx @@ -8,16 +8,14 @@ import { } from '@patternfly/react-core'; class NotificationTemplates extends Component { - render () { + render() { const { i18n } = this.props; const { light, medium } = PageSectionVariants; return ( - - {i18n._(t`Notification Templates`)} - + {i18n._(t`Notification Templates`)} diff --git a/awx/ui_next/src/screens/Organization/Organization.jsx b/awx/ui_next/src/screens/Organization/Organization.jsx index 108c040cf4..b957cbc67a 100644 --- a/awx/ui_next/src/screens/Organization/Organization.jsx +++ b/awx/ui_next/src/screens/Organization/Organization.jsx @@ -2,7 +2,11 @@ import React, { Component } from 'react'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { Switch, Route, withRouter, Redirect } from 'react-router-dom'; -import { Card, CardHeader as PFCardHeader, PageSection } from '@patternfly/react-core'; +import { + Card, + CardHeader as PFCardHeader, + PageSection, +} from '@patternfly/react-core'; import styled from 'styled-components'; import CardCloseButton from '@components/CardCloseButton'; import RoutedTabs from '@components/RoutedTabs'; @@ -15,7 +19,7 @@ import OrganizationTeams from './OrganizationTeams'; import { OrganizationsAPI } from '@api'; class Organization extends Component { - constructor (props) { + constructor(props) { super(props); this.state = { @@ -31,52 +35,51 @@ class Organization extends Component { this.loadOrganizationAndRoles = this.loadOrganizationAndRoles.bind(this); } - async componentDidMount () { + async componentDidMount() { await this.loadOrganizationAndRoles(); this.setState({ isInitialized: true }); } - async componentDidUpdate (prevProps) { + async componentDidUpdate(prevProps) { const { location } = this.props; if (location !== prevProps.location) { await this.loadOrganization(); } } - async loadOrganizationAndRoles () { - const { - match, - setBreadcrumb, - } = this.props; + async loadOrganizationAndRoles() { + const { match, setBreadcrumb } = this.props; const id = parseInt(match.params.id, 10); this.setState({ contentError: null, hasContentLoading: true }); try { - const [{ data }, notifAdminRes, auditorRes, adminRes] = await Promise.all([ - OrganizationsAPI.readDetail(id), - OrganizationsAPI.read({ page_size: 1, role_level: 'notification_admin_role' }), - OrganizationsAPI.read({ id, role_level: 'auditor_role' }), - OrganizationsAPI.read({ id, role_level: 'admin_role' }), - ]); + const [{ data }, notifAdminRes, auditorRes, adminRes] = await Promise.all( + [ + OrganizationsAPI.readDetail(id), + OrganizationsAPI.read({ + page_size: 1, + role_level: 'notification_admin_role', + }), + OrganizationsAPI.read({ id, role_level: 'auditor_role' }), + OrganizationsAPI.read({ id, role_level: 'admin_role' }), + ] + ); setBreadcrumb(data); this.setState({ organization: data, isNotifAdmin: notifAdminRes.data.results.length > 0, isAuditorOfThisOrg: auditorRes.data.results.length > 0, - isAdminOfThisOrg: adminRes.data.results.length > 0 + isAdminOfThisOrg: adminRes.data.results.length > 0, }); } catch (err) { - this.setState(({ contentError: err })); + this.setState({ contentError: err }); } finally { this.setState({ hasContentLoading: false }); } } - async loadOrganization () { - const { - match, - setBreadcrumb, - } = this.props; + async loadOrganization() { + const { match, setBreadcrumb } = this.props; const id = parseInt(match.params.id, 10); this.setState({ contentError: null, hasContentLoading: true }); @@ -85,20 +88,14 @@ class Organization extends Component { setBreadcrumb(data); this.setState({ organization: data }); } catch (err) { - this.setState(({ contentError: err })); + this.setState({ contentError: err }); } finally { this.setState({ hasContentLoading: false }); } } - render () { - const { - location, - match, - me, - history, - i18n - } = this.props; + render() { + const { location, match, me, history, i18n } = this.props; const { organization, @@ -107,27 +104,26 @@ class Organization extends Component { isInitialized, isNotifAdmin, isAuditorOfThisOrg, - isAdminOfThisOrg + isAdminOfThisOrg, } = this.state; - const canSeeNotificationsTab = me.is_system_auditor || isNotifAdmin || isAuditorOfThisOrg; - const canToggleNotifications = isNotifAdmin && ( - me.is_system_auditor - || isAuditorOfThisOrg - || isAdminOfThisOrg - ); + const canSeeNotificationsTab = + me.is_system_auditor || isNotifAdmin || isAuditorOfThisOrg; + const canToggleNotifications = + isNotifAdmin && + (me.is_system_auditor || isAuditorOfThisOrg || isAdminOfThisOrg); const tabsArray = [ { name: i18n._(t`Details`), link: `${match.url}/details`, id: 0 }, { name: i18n._(t`Access`), link: `${match.url}/access`, id: 1 }, - { name: i18n._(t`Teams`), link: `${match.url}/teams`, id: 2 } + { name: i18n._(t`Teams`), link: `${match.url}/teams`, id: 2 }, ]; if (canSeeNotificationsTab) { tabsArray.push({ name: i18n._(t`Notifications`), link: `${match.url}/notifications`, - id: 3 + id: 3, }); } @@ -186,10 +182,7 @@ class Organization extends Component { ( - + )} /> )} @@ -208,17 +201,13 @@ class Organization extends Component { ( - + )} /> )} ( - - )} + render={() => } /> {canSeeNotificationsTab && ( ', () => { mountWithContexts( {}} me={mockMe} />); }); - test('notifications tab shown for admins', async (done) => { + test('notifications tab shown for admins', async done => { OrganizationsAPI.readDetail.mockResolvedValue(mockDetails); OrganizationsAPI.read.mockImplementation(getOrganizations); - const wrapper = mountWithContexts( {}} me={mockMe} />); - const tabs = await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 4); + const wrapper = mountWithContexts( + {}} me={mockMe} /> + ); + const tabs = await waitForElement( + wrapper, + '.pf-c-tabs__item', + el => el.length === 4 + ); expect(tabs.last().text()).toEqual('Notifications'); done(); }); - test('notifications tab hidden with reduced permissions', async (done) => { + test('notifications tab hidden with reduced permissions', async done => { OrganizationsAPI.readDetail.mockResolvedValue(mockDetails); OrganizationsAPI.read.mockResolvedValue(mockNoResults); - const wrapper = mountWithContexts( {}} me={mockMe} />); - const tabs = await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 3); + const wrapper = mountWithContexts( + {}} me={mockMe} /> + ); + const tabs = await waitForElement( + wrapper, + '.pf-c-tabs__item', + el => el.length === 3 + ); tabs.forEach(tab => expect(tab.text()).not.toEqual('Notifications')); done(); }); diff --git a/awx/ui_next/src/screens/Organization/OrganizationAccess/DeleteRoleConfirmationModal.jsx b/awx/ui_next/src/screens/Organization/OrganizationAccess/DeleteRoleConfirmationModal.jsx index 0b791b1f20..4c5aaaa69a 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationAccess/DeleteRoleConfirmationModal.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationAccess/DeleteRoleConfirmationModal.jsx @@ -13,20 +13,22 @@ class DeleteRoleConfirmationModal extends React.Component { username: string, onCancel: func.isRequired, onConfirm: func.isRequired, - } + }; static defaultProps = { username: '', - } + }; - isTeamRole () { + isTeamRole() { const { role } = this.props; return typeof role.team_id !== 'undefined'; } - render () { + render() { const { role, username, onCancel, onConfirm, i18n } = this.props; - const title = i18n._(t`Remove ${this.isTeamRole() ? i18n._(t`Team`) : i18n._(t`User`)} Access`); + const title = i18n._( + t`Remove ${this.isTeamRole() ? i18n._(t`Team`) : i18n._(t`User`)} Access` + ); return ( , + , ]} > {this.isTeamRole() ? ( - {i18n._(t`Are you sure you want to remove ${role.name} access from ${role.team_name}? Doing so affects all members of the team.`)} + {i18n._( + t`Are you sure you want to remove ${role.name} access from ${role.team_name}? Doing so affects all members of the team.` + )}

- {i18n._(t`If you ${(only)} want to remove access for this particular user, please remove them from the team.`)} + {i18n._( + t`If you ${( + + only + + )} want to remove access for this particular user, please remove them from the team.` + )}
) : ( - {i18n._(t`Are you sure you want to remove ${role.name} access from ${username}?`)} + {i18n._( + t`Are you sure you want to remove ${role.name} access from ${username}?` + )} )}
diff --git a/awx/ui_next/src/screens/Organization/OrganizationAccess/OrganizationAccess.jsx b/awx/ui_next/src/screens/Organization/OrganizationAccess/OrganizationAccess.jsx index 26079c34a6..6961cf9fd5 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationAccess/OrganizationAccess.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationAccess/OrganizationAccess.jsx @@ -8,11 +8,13 @@ import AddResourceRole from '@components/AddRole/AddResourceRole'; import AlertModal from '@components/AlertModal'; import DataListToolbar from '@components/DataListToolbar'; import ErrorDetail from '@components/ErrorDetail'; -import PaginatedDataList, { ToolbarAddButton } from '@components/PaginatedDataList'; +import PaginatedDataList, { + ToolbarAddButton, +} from '@components/PaginatedDataList'; import { getQSConfig, encodeQueryString, - parseNamespacedQueryString + parseNamespacedQueryString, } from '@util/qs'; import { Organization } from '@types'; @@ -30,7 +32,7 @@ class OrganizationAccess extends React.Component { organization: Organization.isRequired, }; - constructor (props) { + constructor(props) { super(props); this.state = { accessRecords: [], @@ -52,32 +54,35 @@ class OrganizationAccess extends React.Component { this.handleDeleteOpen = this.handleDeleteOpen.bind(this); } - componentDidMount () { + componentDidMount() { this.loadAccessList(); } - componentDidUpdate (prevProps) { + componentDidUpdate(prevProps) { const { location } = this.props; - const prevParams = parseNamespacedQueryString(QS_CONFIG, prevProps.location.search); - const currentParams = parseNamespacedQueryString(QS_CONFIG, location.search); + const prevParams = parseNamespacedQueryString( + QS_CONFIG, + prevProps.location.search + ); + const currentParams = parseNamespacedQueryString( + QS_CONFIG, + location.search + ); if (encodeQueryString(currentParams) !== encodeQueryString(prevParams)) { this.loadAccessList(); } } - async loadAccessList () { + async loadAccessList() { const { organization, location } = this.props; const params = parseNamespacedQueryString(QS_CONFIG, location.search); this.setState({ contentError: null, hasContentLoading: true }); try { const { - data: { - results: accessRecords = [], - count: itemCount = 0 - } + data: { results: accessRecords = [], count: itemCount = 0 }, } = await OrganizationsAPI.readAccessList(organization.id, params); this.setState({ itemCount, accessRecords }); } catch (err) { @@ -87,23 +92,23 @@ class OrganizationAccess extends React.Component { } } - handleDeleteOpen (deletionRole, deletionRecord) { + handleDeleteOpen(deletionRole, deletionRecord) { this.setState({ deletionRole, deletionRecord }); } - handleDeleteCancel () { + handleDeleteCancel() { this.setState({ deletionRole: null, deletionRecord: null }); } - handleDeleteErrorClose () { + handleDeleteErrorClose() { this.setState({ deletionError: null, deletionRecord: null, - deletionRole: null + deletionRole: null, }); } - async handleDeleteConfirm () { + async handleDeleteConfirm() { const { deletionRole, deletionRecord } = this.state; if (!deletionRole || !deletionRecord) { @@ -112,7 +117,10 @@ class OrganizationAccess extends React.Component { let promise; if (typeof deletionRole.team_id !== 'undefined') { - promise = TeamsAPI.disassociateRole(deletionRole.team_id, deletionRole.id); + promise = TeamsAPI.disassociateRole( + deletionRole.team_id, + deletionRole.id + ); } else { promise = UsersAPI.disassociateRole(deletionRecord.id, deletionRole.id); } @@ -122,30 +130,30 @@ class OrganizationAccess extends React.Component { await promise.then(this.loadAccessList); this.setState({ deletionRole: null, - deletionRecord: null + deletionRecord: null, }); } catch (err) { this.setState({ hasContentLoading: false, - deletionError: err + deletionError: err, }); } } - handleAddClose () { + handleAddClose() { this.setState({ isAddModalOpen: false }); } - handleAddOpen () { + handleAddOpen() { this.setState({ isAddModalOpen: true }); } - handleAddSuccess () { + handleAddSuccess() { this.setState({ isAddModalOpen: false }); this.loadAccessList(); } - render () { + render() { const { organization, i18n } = this.props; const { accessRecords, @@ -155,10 +163,11 @@ class OrganizationAccess extends React.Component { deletionRecord, deletionError, itemCount, - isAddModalOpen + isAddModalOpen, } = this.state; const canEdit = organization.summary_fields.user_capabilities.edit; - const isDeleteModalOpen = !hasContentLoading && !deletionError && deletionRole; + const isDeleteModalOpen = + !hasContentLoading && !deletionError && deletionRole; return ( @@ -174,12 +183,19 @@ class OrganizationAccess extends React.Component { { name: i18n._(t`Username`), key: 'username', isSortable: true }, { name: i18n._(t`Last Name`), key: 'last_name', isSortable: true }, ]} - renderToolbar={(props) => ( + renderToolbar={props => ( - ] : null} + additionalControls={ + canEdit + ? [ + , + ] + : null + } /> )} renderItem={accessRecord => ( diff --git a/awx/ui_next/src/screens/Organization/OrganizationAccess/OrganizationAccess.test.jsx b/awx/ui_next/src/screens/Organization/OrganizationAccess/OrganizationAccess.test.jsx index 8634c1030d..b0d0b1bb33 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationAccess/OrganizationAccess.test.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationAccess/OrganizationAccess.test.jsx @@ -16,52 +16,59 @@ describe('', () => { summary_fields: { object_roles: {}, user_capabilities: { - edit: true - } - } + edit: true, + }, + }, }; const data = { count: 2, - results: [{ - id: 1, - username: 'joe', - url: '/foo', - first_name: 'joe', - last_name: 'smith', - summary_fields: { - direct_access: [{ - role: { - id: 1, - name: 'Member', - resource_name: 'Org', - resource_type: 'organization', - user_capabilities: { unattach: true }, - } - }], - indirect_access: [], - } - }, { - id: 2, - username: 'jane', - url: '/bar', - first_name: 'jane', - last_name: 'brown', - summary_fields: { - direct_access: [{ - role: { - id: 3, - name: 'Member', - resource_name: 'Org', - resource_type: 'organization', - team_id: 5, - team_name: 'The Team', - user_capabilities: { unattach: true }, - } - }], - indirect_access: [], - } - }] + results: [ + { + id: 1, + username: 'joe', + url: '/foo', + first_name: 'joe', + last_name: 'smith', + summary_fields: { + direct_access: [ + { + role: { + id: 1, + name: 'Member', + resource_name: 'Org', + resource_type: 'organization', + user_capabilities: { unattach: true }, + }, + }, + ], + indirect_access: [], + }, + }, + { + id: 2, + username: 'jane', + url: '/bar', + first_name: 'jane', + last_name: 'brown', + summary_fields: { + direct_access: [ + { + role: { + id: 3, + name: 'Member', + resource_name: 'Org', + resource_type: 'organization', + team_id: 5, + team_name: 'The Team', + user_capabilities: { unattach: true }, + }, + }, + ], + indirect_access: [], + }, + }, + ], }; beforeEach(() => { @@ -75,21 +82,35 @@ describe('', () => { }); test('initially renders succesfully', () => { - const wrapper = mountWithContexts(); + const wrapper = mountWithContexts( + + ); expect(wrapper.find('OrganizationAccess')).toMatchSnapshot(); }); - test('should fetch and display access records on mount', async (done) => { - const wrapper = mountWithContexts(); - await waitForElement(wrapper, 'OrganizationAccessItem', el => el.length === 2); - expect(wrapper.find('PaginatedDataList').prop('items')).toEqual(data.results); - expect(wrapper.find('OrganizationAccess').state('hasContentLoading')).toBe(false); + test('should fetch and display access records on mount', async done => { + const wrapper = mountWithContexts( + + ); + await waitForElement( + wrapper, + 'OrganizationAccessItem', + el => el.length === 2 + ); + expect(wrapper.find('PaginatedDataList').prop('items')).toEqual( + data.results + ); + expect(wrapper.find('OrganizationAccess').state('hasContentLoading')).toBe( + false + ); expect(wrapper.find('OrganizationAccess').state('contentError')).toBe(null); done(); }); - test('should open confirmation dialog when deleting role', async (done) => { - const wrapper = mountWithContexts(); + test('should open confirmation dialog when deleting role', async done => { + const wrapper = mountWithContexts( + + ); await sleep(0); wrapper.update(); @@ -98,16 +119,18 @@ describe('', () => { wrapper.update(); const component = wrapper.find('OrganizationAccess'); - expect(component.state('deletionRole')) - .toEqual(data.results[0].summary_fields.direct_access[0].role); - expect(component.state('deletionRecord')) - .toEqual(data.results[0]); + expect(component.state('deletionRole')).toEqual( + data.results[0].summary_fields.direct_access[0].role + ); + expect(component.state('deletionRecord')).toEqual(data.results[0]); expect(component.find('DeleteRoleConfirmationModal')).toHaveLength(1); done(); }); - it('should close dialog when cancel button clicked', async (done) => { - const wrapper = mountWithContexts(); + it('should close dialog when cancel button clicked', async done => { + const wrapper = mountWithContexts( + + ); await sleep(0); wrapper.update(); const button = wrapper.find('ChipButton').at(0); @@ -123,14 +146,27 @@ describe('', () => { done(); }); - it('should delete user role', async (done) => { - const wrapper = mountWithContexts(); - const button = await waitForElement(wrapper, 'ChipButton', el => el.length === 2); + it('should delete user role', async done => { + const wrapper = mountWithContexts( + + ); + const button = await waitForElement( + wrapper, + 'ChipButton', + el => el.length === 2 + ); button.at(0).prop('onClick')(); - const confirmation = await waitForElement(wrapper, 'DeleteRoleConfirmationModal'); + const confirmation = await waitForElement( + wrapper, + 'DeleteRoleConfirmationModal' + ); confirmation.prop('onConfirm')(); - await waitForElement(wrapper, 'DeleteRoleConfirmationModal', el => el.length === 0); + await waitForElement( + wrapper, + 'DeleteRoleConfirmationModal', + el => el.length === 0 + ); await sleep(0); wrapper.update(); @@ -143,14 +179,27 @@ describe('', () => { done(); }); - it('should delete team role', async (done) => { - const wrapper = mountWithContexts(); - const button = await waitForElement(wrapper, 'ChipButton', el => el.length === 2); + it('should delete team role', async done => { + const wrapper = mountWithContexts( + + ); + const button = await waitForElement( + wrapper, + 'ChipButton', + el => el.length === 2 + ); button.at(1).prop('onClick')(); - const confirmation = await waitForElement(wrapper, 'DeleteRoleConfirmationModal'); + const confirmation = await waitForElement( + wrapper, + 'DeleteRoleConfirmationModal' + ); confirmation.prop('onConfirm')(); - await waitForElement(wrapper, 'DeleteRoleConfirmationModal', el => el.length === 0); + await waitForElement( + wrapper, + 'DeleteRoleConfirmationModal', + el => el.length === 0 + ); await sleep(0); wrapper.update(); diff --git a/awx/ui_next/src/screens/Organization/OrganizationAccess/OrganizationAccessItem.jsx b/awx/ui_next/src/screens/Organization/OrganizationAccess/OrganizationAccessItem.jsx index 7ee78ec76c..80198b9cdf 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationAccess/OrganizationAccessItem.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationAccess/OrganizationAccessItem.jsx @@ -28,17 +28,17 @@ class OrganizationAccessItem extends React.Component { onRoleDelete: func.isRequired, }; - constructor (props) { + constructor(props) { super(props); this.renderChip = this.renderChip.bind(this); } - getRoleLists () { + getRoleLists() { const { accessRecord } = this.props; const teamRoles = []; const userRoles = []; - function sort (item) { + function sort(item) { const { role } = item; if (role.team_id) { teamRoles.push(role); @@ -52,85 +52,79 @@ class OrganizationAccessItem extends React.Component { return [teamRoles, userRoles]; } - renderChip (role) { + renderChip(role) { const { accessRecord, onRoleDelete } = this.props; return ( { onRoleDelete(role, accessRecord); }} + onClick={() => { + onRoleDelete(role, accessRecord); + }} > {role.name} ); } - render () { + render() { const { accessRecord, i18n } = this.props; const [teamRoles, userRoles] = this.getRoleLists(); return ( - - {accessRecord.username && ( - - {accessRecord.url ? ( - - + + {accessRecord.username && ( + + {accessRecord.url ? ( + + + {accessRecord.username} + + + ) : ( + {accessRecord.username} - - - ) : ( - - {accessRecord.username} - - )} - - )} - {accessRecord.first_name || accessRecord.last_name ? ( + + )} + + )} + {accessRecord.first_name || accessRecord.last_name ? ( + + + + ) : null} + , + - + {userRoles.length > 0 && ( + {userRoles.map(this.renderChip)}
+ } + /> + )} + {teamRoles.length > 0 && ( + {teamRoles.map(this.renderChip)} + } + /> + )} - ) : ( - null - )} - , - - - {userRoles.length > 0 && ( - - {userRoles.map(this.renderChip)} - - )} - /> - )} - {teamRoles.length > 0 && ( - - {teamRoles.map(this.renderChip)} - - )} - /> - )} - - - ]} + , + ]} /> diff --git a/awx/ui_next/src/screens/Organization/OrganizationAccess/OrganizationAccessItem.test.jsx b/awx/ui_next/src/screens/Organization/OrganizationAccess/OrganizationAccessItem.test.jsx index 1d8cbc0148..5c10a6edcf 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationAccess/OrganizationAccessItem.test.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationAccess/OrganizationAccessItem.test.jsx @@ -11,19 +11,21 @@ const accessRecord = { first_name: 'jane', last_name: 'brown', summary_fields: { - direct_access: [{ - role: { - id: 3, - name: 'Member', - resource_name: 'Org', - resource_type: 'organization', - team_id: 5, - team_name: 'The Team', - user_capabilities: { unattach: true }, - } - }], + direct_access: [ + { + role: { + id: 3, + name: 'Member', + resource_name: 'Org', + resource_type: 'organization', + team_id: 5, + team_name: 'The Team', + user_capabilities: { unattach: true }, + }, + }, + ], indirect_access: [], - } + }, }; describe('', () => { diff --git a/awx/ui_next/src/screens/Organization/OrganizationAccess/index.js b/awx/ui_next/src/screens/Organization/OrganizationAccess/index.js index fb20b55686..e6e4c2c884 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationAccess/index.js +++ b/awx/ui_next/src/screens/Organization/OrganizationAccess/index.js @@ -1,3 +1,5 @@ export { default as OrganizationAccess } from './OrganizationAccess'; export { default as OrganizationAccessItem } from './OrganizationAccessItem'; -export { default as DeleteRoleConfirmationModal } from './DeleteRoleConfirmationModal'; +export { + default as DeleteRoleConfirmationModal, +} from './DeleteRoleConfirmationModal'; diff --git a/awx/ui_next/src/screens/Organization/OrganizationAdd/OrganizationAdd.jsx b/awx/ui_next/src/screens/Organization/OrganizationAdd/OrganizationAdd.jsx index 83d71c94ac..32c368ee58 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationAdd/OrganizationAdd.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationAdd/OrganizationAdd.jsx @@ -18,31 +18,34 @@ import CardCloseButton from '@components/CardCloseButton'; import OrganizationForm from '../shared/OrganizationForm'; class OrganizationAdd extends React.Component { - constructor (props) { + constructor(props) { super(props); this.handleSubmit = this.handleSubmit.bind(this); this.handleCancel = this.handleCancel.bind(this); this.state = { error: '' }; } - async handleSubmit (values, groupsToAssociate) { + async handleSubmit(values, groupsToAssociate) { const { history } = this.props; try { const { data: response } = await OrganizationsAPI.create(values); - await Promise.all(groupsToAssociate.map(id => OrganizationsAPI - .associateInstanceGroup(response.id, id))); + await Promise.all( + groupsToAssociate.map(id => + OrganizationsAPI.associateInstanceGroup(response.id, id) + ) + ); history.push(`/organizations/${response.id}`); } catch (error) { this.setState({ error }); } } - handleCancel () { + handleCancel() { const { history } = this.props; history.push('/organizations'); } - render () { + render() { const { error } = this.state; const { i18n } = this.props; @@ -50,10 +53,7 @@ class OrganizationAdd extends React.Component { - + @@ -76,7 +76,7 @@ class OrganizationAdd extends React.Component { } OrganizationAdd.contextTypes = { - custom_virtualenvs: PropTypes.arrayOf(PropTypes.string) + custom_virtualenvs: PropTypes.arrayOf(PropTypes.string), }; export { OrganizationAdd as _OrganizationAdd }; diff --git a/awx/ui_next/src/screens/Organization/OrganizationAdd/OrganizationAdd.test.jsx b/awx/ui_next/src/screens/Organization/OrganizationAdd/OrganizationAdd.test.jsx index 386344f577..72bffd067d 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationAdd/OrganizationAdd.test.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationAdd/OrganizationAdd.test.jsx @@ -1,6 +1,9 @@ import React from 'react'; -import { mountWithContexts, waitForElement } from '../../../../testUtils/enzymeHelpers'; +import { + mountWithContexts, + waitForElement, +} from '../../../../testUtils/enzymeHelpers'; import OrganizationAdd from './OrganizationAdd'; import { OrganizationsAPI } from '../../../api'; @@ -15,7 +18,11 @@ describe('', () => { description: 'new description', custom_virtualenv: 'Buzz', }; - wrapper.find('OrganizationForm').prop('handleSubmit')(updatedOrgData, [], []); + wrapper.find('OrganizationForm').prop('handleSubmit')( + updatedOrgData, + [], + [] + ); expect(OrganizationsAPI.create).toHaveBeenCalledWith(updatedOrgData); }); @@ -23,10 +30,9 @@ describe('', () => { const history = { push: jest.fn(), }; - const wrapper = mountWithContexts( - , - { context: { router: { history } } } - ); + const wrapper = mountWithContexts(, { + context: { router: { history } }, + }); expect(history.push).not.toHaveBeenCalled(); wrapper.find('button[aria-label="Cancel"]').prop('onClick')(); expect(history.push).toHaveBeenCalledWith('/organizations'); @@ -36,16 +42,15 @@ describe('', () => { const history = { push: jest.fn(), }; - const wrapper = mountWithContexts( - , - { context: { router: { history } } } - ); + const wrapper = mountWithContexts(, { + context: { router: { history } }, + }); expect(history.push).not.toHaveBeenCalled(); wrapper.find('button[aria-label="Close"]').prop('onClick')(); expect(history.push).toHaveBeenCalledWith('/organizations'); }); - test('successful form submission should trigger redirect', async (done) => { + test('successful form submission should trigger redirect', async done => { const history = { push: jest.fn(), }; @@ -61,19 +66,22 @@ describe('', () => { instance_groups: '/bar', }, ...orgData, - } + }, + }); + const wrapper = mountWithContexts(, { + context: { router: { history } }, }); - const wrapper = mountWithContexts( - , - { context: { router: { history } } } - ); await waitForElement(wrapper, 'button[aria-label="Save"]'); - await wrapper.find('OrganizationForm').prop('handleSubmit')(orgData, [3], []); + await wrapper.find('OrganizationForm').prop('handleSubmit')( + orgData, + [3], + [] + ); expect(history.push).toHaveBeenCalledWith('/organizations/5'); done(); }); - test('handleSubmit should post instance groups', async (done) => { + test('handleSubmit should post instance groups', async done => { const orgData = { name: 'new name', description: 'new description', @@ -86,13 +94,16 @@ describe('', () => { instance_groups: '/api/v2/organizations/5/instance_groups', }, ...orgData, - } + }, }); const wrapper = mountWithContexts(); await waitForElement(wrapper, 'button[aria-label="Save"]'); - await wrapper.find('OrganizationForm').prop('handleSubmit')(orgData, [3], []); - expect(OrganizationsAPI.associateInstanceGroup) - .toHaveBeenCalledWith(5, 3); + await wrapper.find('OrganizationForm').prop('handleSubmit')( + orgData, + [3], + [] + ); + expect(OrganizationsAPI.associateInstanceGroup).toHaveBeenCalledWith(5, 3); done(); }); @@ -100,23 +111,26 @@ describe('', () => { const config = { custom_virtualenvs: ['foo', 'bar'], }; - const wrapper = mountWithContexts( - , - { context: { config } } - ).find('AnsibleSelect'); + const wrapper = mountWithContexts(, { + context: { config }, + }).find('AnsibleSelect'); expect(wrapper.find('FormSelect')).toHaveLength(1); expect(wrapper.find('FormSelectOption')).toHaveLength(3); - expect(wrapper.find('FormSelectOption').first().prop('value')).toEqual('/venv/ansible/'); + expect( + wrapper + .find('FormSelectOption') + .first() + .prop('value') + ).toEqual('/venv/ansible/'); }); test('AnsibleSelect component does not render if there are 0 virtual environments', () => { const config = { custom_virtualenvs: [], }; - const wrapper = mountWithContexts( - , - { context: { config } } - ).find('AnsibleSelect'); + const wrapper = mountWithContexts(, { + context: { config }, + }).find('AnsibleSelect'); expect(wrapper.find('FormSelect')).toHaveLength(0); }); }); diff --git a/awx/ui_next/src/screens/Organization/OrganizationDetail/OrganizationDetail.jsx b/awx/ui_next/src/screens/Organization/OrganizationDetail/OrganizationDetail.jsx index 31b30c03bb..29fda4aa71 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationDetail/OrganizationDetail.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationDetail/OrganizationDetail.jsx @@ -16,7 +16,7 @@ const CardBody = styled(PFCardBody)` `; class OrganizationDetail extends Component { - constructor (props) { + constructor(props) { super(props); this.state = { @@ -27,16 +27,22 @@ class OrganizationDetail extends Component { this.loadInstanceGroups = this.loadInstanceGroups.bind(this); } - componentDidMount () { + componentDidMount() { this.loadInstanceGroups(); } - async loadInstanceGroups () { - const { match: { params: { id } } } = this.props; + async loadInstanceGroups() { + const { + match: { + params: { id }, + }, + } = this.props; this.setState({ hasContentLoading: true }); try { - const { data: { results = [] } } = await OrganizationsAPI.readInstanceGroups(id); + const { + data: { results = [] }, + } = await OrganizationsAPI.readInstanceGroups(id); this.setState({ instanceGroups: [...results] }); } catch (err) { this.setState({ contentError: err }); @@ -45,12 +51,8 @@ class OrganizationDetail extends Component { } } - render () { - const { - hasContentLoading, - contentError, - instanceGroups, - } = this.state; + render() { + const { hasContentLoading, contentError, instanceGroups } = this.state; const { organization: { @@ -60,58 +62,45 @@ class OrganizationDetail extends Component { max_hosts, created, modified, - summary_fields + summary_fields, }, match, - i18n + i18n, } = this.props; if (hasContentLoading) { - return (); + return ; } if (contentError) { - return (); + return ; } return ( - - - + + + - - - {(instanceGroups && instanceGroups.length > 0) && ( + + + {instanceGroups && instanceGroups.length > 0 && ( {instanceGroups.map(ig => ( - {ig.name} + + {ig.name} + ))} - )} + } /> )} diff --git a/awx/ui_next/src/screens/Organization/OrganizationDetail/OrganizationDetail.test.jsx b/awx/ui_next/src/screens/Organization/OrganizationDetail/OrganizationDetail.test.jsx index d8b5f78e8d..6f4a17c1e5 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationDetail/OrganizationDetail.test.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationDetail/OrganizationDetail.test.jsx @@ -17,17 +17,14 @@ describe('', () => { modified: 'Boo', summary_fields: { user_capabilities: { - edit: true - } - } + edit: true, + }, + }, }; const mockInstanceGroups = { data: { - results: [ - { name: 'One', id: 1 }, - { name: 'Two', id: 2 } - ] - } + results: [{ name: 'One', id: 1 }, { name: 'Two', id: 2 }], + }, }; beforeEach(() => { @@ -47,17 +44,21 @@ describe('', () => { expect(OrganizationsAPI.readInstanceGroups).toHaveBeenCalledTimes(1); }); - test('should handle setting instance groups to state', async (done) => { + test('should handle setting instance groups to state', async done => { const wrapper = mountWithContexts( ); const component = await waitForElement(wrapper, 'OrganizationDetail'); - expect(component.state().instanceGroups).toEqual(mockInstanceGroups.data.results); + expect(component.state().instanceGroups).toEqual( + mockInstanceGroups.data.results + ); done(); }); - test('should render Details', async (done) => { - const wrapper = mountWithContexts(); + test('should render Details', async done => { + const wrapper = mountWithContexts( + + ); const testParams = [ { label: 'Name', value: 'Foo' }, { label: 'Description', value: 'Bar' }, @@ -76,18 +77,25 @@ describe('', () => { done(); }); - test('should show edit button for users with edit permission', async (done) => { - const wrapper = mountWithContexts(); - const editButton = await waitForElement(wrapper, 'OrganizationDetail Button'); + test('should show edit button for users with edit permission', async done => { + const wrapper = mountWithContexts( + + ); + const editButton = await waitForElement( + wrapper, + 'OrganizationDetail Button' + ); expect(editButton.text()).toEqual('Edit'); expect(editButton.prop('to')).toBe('/organizations/undefined/edit'); done(); }); - test('should hide edit button for users without edit permission', async (done) => { + test('should hide edit button for users without edit permission', async done => { const readOnlyOrg = { ...mockOrganization }; readOnlyOrg.summary_fields.user_capabilities.edit = false; - const wrapper = mountWithContexts(); + const wrapper = mountWithContexts( + + ); await waitForElement(wrapper, 'OrganizationDetail'); expect(wrapper.find('OrganizationDetail Button').length).toBe(0); done(); diff --git a/awx/ui_next/src/screens/Organization/OrganizationEdit/OrganizationEdit.jsx b/awx/ui_next/src/screens/Organization/OrganizationEdit/OrganizationEdit.jsx index 2fe857db4e..302ef6cab7 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationEdit/OrganizationEdit.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationEdit/OrganizationEdit.jsx @@ -9,7 +9,7 @@ import { Config } from '@contexts/Config'; import OrganizationForm from '../shared/OrganizationForm'; class OrganizationEdit extends Component { - constructor (props) { + constructor(props) { super(props); this.handleSubmit = this.handleSubmit.bind(this); @@ -22,7 +22,7 @@ class OrganizationEdit extends Component { }; } - async handleSubmit (values, groupsToAssociate, groupsToDisassociate) { + async handleSubmit(values, groupsToAssociate, groupsToDisassociate) { const { organization } = this.props; try { await OrganizationsAPI.update(organization.id, values); @@ -33,25 +33,33 @@ class OrganizationEdit extends Component { } } - handleCancel () { - const { organization: { id }, history } = this.props; + handleCancel() { + const { + organization: { id }, + history, + } = this.props; history.push(`/organizations/${id}`); } - handleSuccess () { - const { organization: { id }, history } = this.props; + handleSuccess() { + const { + organization: { id }, + history, + } = this.props; history.push(`/organizations/${id}`); } - async submitInstanceGroups (groupsToAssociate, groupsToDisassociate) { + async submitInstanceGroups(groupsToAssociate, groupsToDisassociate) { const { organization } = this.props; try { await Promise.all( - groupsToAssociate.map(id => OrganizationsAPI.associateInstanceGroup(organization.id, id)) + groupsToAssociate.map(id => + OrganizationsAPI.associateInstanceGroup(organization.id, id) + ) ); await Promise.all( - groupsToDisassociate.map( - id => OrganizationsAPI.disassociateInstanceGroup(organization.id, id) + groupsToDisassociate.map(id => + OrganizationsAPI.disassociateInstanceGroup(organization.id, id) ) ); } catch (err) { @@ -59,7 +67,7 @@ class OrganizationEdit extends Component { } } - render () { + render() { const { organization } = this.props; const { error } = this.state; @@ -86,7 +94,7 @@ OrganizationEdit.propTypes = { }; OrganizationEdit.contextTypes = { - custom_virtualenvs: PropTypes.arrayOf(PropTypes.string) + custom_virtualenvs: PropTypes.arrayOf(PropTypes.string), }; export { OrganizationEdit as _OrganizationEdit }; diff --git a/awx/ui_next/src/screens/Organization/OrganizationEdit/OrganizationEdit.test.jsx b/awx/ui_next/src/screens/Organization/OrganizationEdit/OrganizationEdit.test.jsx index c635652c99..2a73761a74 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationEdit/OrganizationEdit.test.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationEdit/OrganizationEdit.test.jsx @@ -7,7 +7,7 @@ import OrganizationEdit from './OrganizationEdit'; jest.mock('@api'); -const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); +const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); describe('', () => { const mockData = { @@ -16,37 +16,52 @@ describe('', () => { custom_virtualenv: 'Fizz', id: 1, related: { - instance_groups: '/api/v2/organizations/1/instance_groups' - } + instance_groups: '/api/v2/organizations/1/instance_groups', + }, }; test('handleSubmit should call api update', () => { - const wrapper = mountWithContexts(); + const wrapper = mountWithContexts( + + ); const updatedOrgData = { name: 'new name', description: 'new description', custom_virtualenv: 'Buzz', }; - wrapper.find('OrganizationForm').prop('handleSubmit')(updatedOrgData, [], []); + wrapper.find('OrganizationForm').prop('handleSubmit')( + updatedOrgData, + [], + [] + ); expect(OrganizationsAPI.update).toHaveBeenCalledWith(1, updatedOrgData); }); test('handleSubmit associates and disassociates instance groups', async () => { - const wrapper = mountWithContexts(); + const wrapper = mountWithContexts( + + ); const updatedOrgData = { name: 'new name', description: 'new description', custom_virtualenv: 'Buzz', }; - wrapper.find('OrganizationForm').prop('handleSubmit')(updatedOrgData, [3, 4], [2]); + wrapper.find('OrganizationForm').prop('handleSubmit')( + updatedOrgData, + [3, 4], + [2] + ); await sleep(1); expect(OrganizationsAPI.associateInstanceGroup).toHaveBeenCalledWith(1, 3); expect(OrganizationsAPI.associateInstanceGroup).toHaveBeenCalledWith(1, 4); - expect(OrganizationsAPI.disassociateInstanceGroup).toHaveBeenCalledWith(1, 2); + expect(OrganizationsAPI.disassociateInstanceGroup).toHaveBeenCalledWith( + 1, + 2 + ); }); test('should navigate to organization detail when cancel is clicked', () => { diff --git a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx index e0eaab93bb..fb07b0e7f1 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx @@ -2,11 +2,7 @@ import React, { Component, Fragment } from 'react'; import { withRouter } from 'react-router-dom'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; -import { - Card, - PageSection, - PageSectionVariants, -} from '@patternfly/react-core'; +import { Card, PageSection, PageSectionVariants } from '@patternfly/react-core'; import { OrganizationsAPI } from '@api'; import AlertModal from '@components/AlertModal'; @@ -27,7 +23,7 @@ const QS_CONFIG = getQSConfig('organization', { }); class OrganizationsList extends Component { - constructor (props) { + constructor(props) { super(props); this.state = { @@ -47,25 +43,25 @@ class OrganizationsList extends Component { this.loadOrganizations = this.loadOrganizations.bind(this); } - componentDidMount () { + componentDidMount() { this.loadOrganizations(); } - componentDidUpdate (prevProps) { + componentDidUpdate(prevProps) { const { location } = this.props; if (location !== prevProps.location) { this.loadOrganizations(); } } - handleSelectAll (isSelected) { + handleSelectAll(isSelected) { const { organizations } = this.state; const selected = isSelected ? [...organizations] : []; this.setState({ selected }); } - handleSelect (row) { + handleSelect(row) { const { selected } = this.state; if (selected.some(s => s.id === row.id)) { @@ -75,16 +71,16 @@ class OrganizationsList extends Component { } } - handleDeleteErrorClose () { + handleDeleteErrorClose() { this.setState({ deletionError: null }); } - async handleOrgDelete () { + async handleOrgDelete() { const { selected } = this.state; this.setState({ hasContentLoading: true }); try { - await Promise.all(selected.map((org) => OrganizationsAPI.destroy(org.id))); + await Promise.all(selected.map(org => OrganizationsAPI.destroy(org.id))); } catch (err) { this.setState({ deletionError: err }); } finally { @@ -92,7 +88,7 @@ class OrganizationsList extends Component { } } - async loadOrganizations () { + async loadOrganizations() { const { location } = this.props; const { actions: cachedActions } = this.state; const params = parseNamespacedQueryString(QS_CONFIG, location.search); @@ -111,7 +107,14 @@ class OrganizationsList extends Component { this.setState({ contentError: null, hasContentLoading: true }); try { - const [{ data: { count, results } }, { data: { actions } }] = await promises; + const [ + { + data: { count, results }, + }, + { + data: { actions }, + }, + ] = await promises; this.setState({ actions, itemCount: count, @@ -119,16 +122,14 @@ class OrganizationsList extends Component { selected: [], }); } catch (err) { - this.setState(({ contentError: err })); + this.setState({ contentError: err }); } finally { this.setState({ hasContentLoading: false }); } } - render () { - const { - medium, - } = PageSectionVariants; + render() { + const { medium } = PageSectionVariants; const { actions, itemCount, @@ -140,7 +141,8 @@ class OrganizationsList extends Component { } = this.state; const { match, i18n } = this.props; - const canAdd = actions && Object.prototype.hasOwnProperty.call(actions, 'POST'); + const canAdd = + actions && Object.prototype.hasOwnProperty.call(actions, 'POST'); const isAllSelected = selected.length === organizations.length; return ( @@ -156,10 +158,20 @@ class OrganizationsList extends Component { qsConfig={QS_CONFIG} toolbarColumns={[ { name: i18n._(t`Name`), key: 'name', isSortable: true }, - { name: i18n._(t`Modified`), key: 'modified', isSortable: true, isNumeric: true }, - { name: i18n._(t`Created`), key: 'created', isSortable: true, isNumeric: true }, + { + name: i18n._(t`Modified`), + key: 'modified', + isSortable: true, + isNumeric: true, + }, + { + name: i18n._(t`Created`), + key: 'created', + isSortable: true, + isNumeric: true, + }, ]} - renderToolbar={(props) => ( + renderToolbar={props => ( , - canAdd - ? - : null, + canAdd ? ( + + ) : null, ]} /> )} - renderItem={(o) => ( + renderItem={o => ( )} emptyStateControls={ - canAdd ? - : null + canAdd ? ( + + ) : null } /> diff --git a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.test.jsx b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.test.jsx index 00739b7c12..c56579b99c 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.test.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.test.jsx @@ -9,69 +9,73 @@ jest.mock('@api'); const mockAPIOrgsList = { data: { count: 3, - results: [{ - name: 'Organization 0', - id: 1, - url: '/organizations/1', - summary_fields: { - related_field_counts: { - teams: 3, - users: 4 + results: [ + { + name: 'Organization 0', + id: 1, + url: '/organizations/1', + summary_fields: { + related_field_counts: { + teams: 3, + users: 4, + }, + user_capabilities: { + delete: true, + }, }, - user_capabilities: { - delete: true - } }, - }, - { - name: 'Organization 1', - id: 2, - url: '/organizations/2', - summary_fields: { - related_field_counts: { - teams: 2, - users: 5 + { + name: 'Organization 1', + id: 2, + url: '/organizations/2', + summary_fields: { + related_field_counts: { + teams: 2, + users: 5, + }, + user_capabilities: { + delete: true, + }, }, - user_capabilities: { - delete: true - } }, - }, - { - name: 'Organization 2', - id: 3, - url: '/organizations/3', - summary_fields: { - related_field_counts: { - teams: 5, - users: 6 + { + name: 'Organization 2', + id: 3, + url: '/organizations/3', + summary_fields: { + related_field_counts: { + teams: 5, + users: 6, + }, + user_capabilities: { + delete: true, + }, }, - user_capabilities: { - delete: true - } }, - }] + ], }, isModalOpen: false, warningTitle: 'title', - warningMsg: 'message' + warningMsg: 'message', }; describe('', () => { let wrapper; beforeEach(() => { - OrganizationsAPI.read = () => Promise.resolve({ - data: { - count: 0, - results: [] - } - }); - OrganizationsAPI.readOptions = () => Promise.resolve({ - data: { - actions: [] - } - }); + OrganizationsAPI.read = () => + Promise.resolve({ + data: { + count: 0, + results: [], + }, + }); + OrganizationsAPI.readOptions = () => + Promise.resolve({ + data: { + actions: [], + }, + }); }); test('initially renders succesfully', () => { @@ -79,12 +83,14 @@ describe('', () => { }); test('Puts 1 selected Org in state when handleSelect is called.', () => { - wrapper = mountWithContexts().find('OrganizationsList'); + wrapper = mountWithContexts().find( + 'OrganizationsList' + ); wrapper.setState({ organizations: mockAPIOrgsList.data.results, itemCount: 3, - isInitialized: true + isInitialized: true, }); wrapper.update(); expect(wrapper.state('selected').length).toBe(0); @@ -98,13 +104,14 @@ describe('', () => { list.setState({ organizations: mockAPIOrgsList.data.results, itemCount: 3, - isInitialized: true + isInitialized: true, }); expect(list.state('selected').length).toBe(0); list.instance().handleSelectAll(true); wrapper.update(); - expect(list.state('selected').length) - .toEqual(list.state('organizations').length); + expect(list.state('selected').length).toEqual( + list.state('organizations').length + ); }); test('api is called to delete Orgs for each org in selected.', () => { @@ -115,47 +122,58 @@ describe('', () => { itemCount: 3, isInitialized: true, isModalOpen: mockAPIOrgsList.isModalOpen, - selected: mockAPIOrgsList.data.results + selected: mockAPIOrgsList.data.results, }); wrapper.find('ToolbarDeleteButton').prop('onDelete')(); - expect(OrganizationsAPI.destroy).toHaveBeenCalledTimes(component.state('selected').length); + expect(OrganizationsAPI.destroy).toHaveBeenCalledTimes( + component.state('selected').length + ); }); test('call loadOrganizations after org(s) have been deleted', () => { - const fetchOrgs = jest.spyOn(_OrganizationsList.prototype, 'loadOrganizations'); - const event = { preventDefault: () => { } }; + const fetchOrgs = jest.spyOn( + _OrganizationsList.prototype, + 'loadOrganizations' + ); + const event = { preventDefault: () => {} }; wrapper = mountWithContexts(); wrapper.find('OrganizationsList').setState({ organizations: mockAPIOrgsList.data.results, itemCount: 3, isInitialized: true, - selected: mockAPIOrgsList.data.results.slice(0, 1) + selected: mockAPIOrgsList.data.results.slice(0, 1), }); const component = wrapper.find('OrganizationsList'); component.instance().handleOrgDelete(event); expect(fetchOrgs).toBeCalled(); }); - test('error is shown when org not successfully deleted from api', async (done) => { - OrganizationsAPI.destroy.mockRejectedValue(new Error({ - response: { - config: { - method: 'delete', - url: '/api/v2/organizations/1' + test('error is shown when org not successfully deleted from api', async done => { + OrganizationsAPI.destroy.mockRejectedValue( + new Error({ + response: { + config: { + method: 'delete', + url: '/api/v2/organizations/1', + }, + data: 'An error occurred', }, - data: 'An error occurred' - } - })); + }) + ); wrapper = mountWithContexts(); wrapper.find('OrganizationsList').setState({ organizations: mockAPIOrgsList.data.results, itemCount: 3, isInitialized: true, - selected: mockAPIOrgsList.data.results.slice(0, 1) + selected: mockAPIOrgsList.data.results.slice(0, 1), }); wrapper.find('ToolbarDeleteButton').prop('onDelete')(); - await waitForElement(wrapper, 'Modal', (el) => el.props().isOpen === true && el.props().title === 'Error!'); + await waitForElement( + wrapper, + 'Modal', + el => el.props().isOpen === true && el.props().title === 'Error!' + ); done(); }); }); diff --git a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationListItem.jsx b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationListItem.jsx index ee785ba257..e5d7c7940d 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationListItem.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationListItem.jsx @@ -9,9 +9,7 @@ import { DataListItemCells, DataListCheck, } from '@patternfly/react-core'; -import { - Link -} from 'react-router-dom'; +import { Link } from 'react-router-dom'; import styled from 'styled-components'; import DataListCell from '@components/DataListCell'; @@ -43,17 +41,11 @@ class OrganizationListItem extends React.Component { organization: Organization.isRequired, detailUrl: string.isRequired, isSelected: bool.isRequired, - onSelect: func.isRequired - } + onSelect: func.isRequired, + }; - render () { - const { - organization, - isSelected, - onSelect, - detailUrl, - i18n - } = this.props; + render() { + const { organization, isSelected, onSelect, detailUrl, i18n } = this.props; const labelId = `check-action-${organization.id}`; return ( @@ -64,32 +56,35 @@ class OrganizationListItem extends React.Component { onChange={onSelect} aria-labelledby={labelId} /> - - - - - {organization.name} - - - , - - - {i18n._(t`Members`)} - - {organization.summary_fields.related_field_counts.users} - - - - {i18n._(t`Teams`)} - - {organization.summary_fields.related_field_counts.teams} - - - - ]} + + + + + {organization.name} + + + , + + + {i18n._(t`Members`)} + + {organization.summary_fields.related_field_counts.users} + + + + {i18n._(t`Teams`)} + + {organization.summary_fields.related_field_counts.teams} + + + , + ]} /> diff --git a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationListItem.test.jsx b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationListItem.test.jsx index 01eed82cb9..c67104f96e 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationListItem.test.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationListItem.test.jsx @@ -15,10 +15,12 @@ describe('', () => { organization={{ id: 1, name: 'Org', - summary_fields: { related_field_counts: { - users: 1, - teams: 1, - } } + summary_fields: { + related_field_counts: { + users: 1, + teams: 1, + }, + }, }} detailUrl="/organization/1" isSelected diff --git a/awx/ui_next/src/screens/Organization/OrganizationNotifications/OrganizationNotifications.jsx b/awx/ui_next/src/screens/Organization/OrganizationNotifications/OrganizationNotifications.jsx index 99e09ae5c5..0e9b24e353 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationNotifications/OrganizationNotifications.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationNotifications/OrganizationNotifications.jsx @@ -24,7 +24,7 @@ const COLUMNS = [ ]; class OrganizationNotifications extends Component { - constructor (props) { + constructor(props) { super(props); this.state = { contentError: null, @@ -37,32 +37,31 @@ class OrganizationNotifications extends Component { errorTemplateIds: [], }; this.handleNotificationToggle = this.handleNotificationToggle.bind(this); - this.handleNotificationErrorClose = this.handleNotificationErrorClose.bind(this); + this.handleNotificationErrorClose = this.handleNotificationErrorClose.bind( + this + ); this.loadNotifications = this.loadNotifications.bind(this); } - componentDidMount () { + componentDidMount() { this.loadNotifications(); } - componentDidUpdate (prevProps) { + componentDidUpdate(prevProps) { const { location } = this.props; if (location !== prevProps.location) { this.loadNotifications(); } } - async loadNotifications () { + async loadNotifications() { const { id, location } = this.props; const params = parseNamespacedQueryString(QS_CONFIG, location.search); this.setState({ contentError: null, hasContentLoading: true }); try { const { - data: { - count: itemCount = 0, - results: notifications = [], - } + data: { count: itemCount = 0, results: notifications = [] }, } = await OrganizationsAPI.readNotificationTemplates(id, params); let idMatchParams; @@ -93,7 +92,7 @@ class OrganizationNotifications extends Component { } } - async handleNotificationToggle (notificationId, isCurrentlyOn, status) { + async handleNotificationToggle(notificationId, isCurrentlyOn, status) { const { id } = this.props; let stateArrayName; @@ -106,13 +105,15 @@ class OrganizationNotifications extends Component { let stateUpdateFunction; if (isCurrentlyOn) { // when switching off, remove the toggled notification id from the array - stateUpdateFunction = (prevState) => ({ - [stateArrayName]: prevState[stateArrayName].filter(i => i !== notificationId) + stateUpdateFunction = prevState => ({ + [stateArrayName]: prevState[stateArrayName].filter( + i => i !== notificationId + ), }); } else { // when switching on, add the toggled notification id to the array - stateUpdateFunction = (prevState) => ({ - [stateArrayName]: prevState[stateArrayName].concat(notificationId) + stateUpdateFunction = prevState => ({ + [stateArrayName]: prevState[stateArrayName].concat(notificationId), }); } @@ -132,11 +133,11 @@ class OrganizationNotifications extends Component { } } - handleNotificationErrorClose () { + handleNotificationErrorClose() { this.setState({ toggleError: null }); } - render () { + render() { const { canToggleNotifications, i18n } = this.props; const { contentError, @@ -159,7 +160,7 @@ class OrganizationNotifications extends Component { itemName="notification" qsConfig={QS_CONFIG} toolbarColumns={COLUMNS} - renderItem={(notification) => ( + renderItem={notification => ( ', () => { beforeEach(() => { data = { count: 2, - results: [{ - id: 1, - name: 'Notification one', - url: '/api/v2/notification_templates/1/', - notification_type: 'email', - }, { - id: 2, - name: 'Notification two', - url: '/api/v2/notification_templates/2/', - notification_type: 'email', - }] + results: [ + { + id: 1, + name: 'Notification one', + url: '/api/v2/notification_templates/1/', + notification_type: 'email', + }, + { + id: 2, + name: 'Notification two', + url: '/api/v2/notification_templates/2/', + notification_type: 'email', + }, + ], }; OrganizationsAPI.readNotificationTemplates.mockReturnValue({ data }); OrganizationsAPI.readNotificationTemplatesSuccess.mockReturnValue({ @@ -55,8 +58,9 @@ describe('', () => { wrapper.update(); expect(OrganizationsAPI.readNotificationTemplates).toHaveBeenCalled(); - expect(wrapper.find('OrganizationNotifications').state('notifications')) - .toEqual(data.results); + expect( + wrapper.find('OrganizationNotifications').state('notifications') + ).toEqual(data.results); const items = wrapper.find('NotificationListItem'); expect(items).toHaveLength(2); expect(items.at(0).prop('successTurnedOn')).toEqual(true); @@ -76,8 +80,14 @@ describe('', () => { wrapper.find('OrganizationNotifications').state('successTemplateIds') ).toEqual([1]); const items = wrapper.find('NotificationListItem'); - items.at(1).find('Switch').at(0).prop('onChange')(); - expect(OrganizationsAPI.updateNotificationTemplateAssociation).toHaveBeenCalledWith(1, 2, 'success', true); + items + .at(1) + .find('Switch') + .at(0) + .prop('onChange')(); + expect( + OrganizationsAPI.updateNotificationTemplateAssociation + ).toHaveBeenCalledWith(1, 2, 'success', true); await sleep(0); wrapper.update(); expect( @@ -96,8 +106,14 @@ describe('', () => { wrapper.find('OrganizationNotifications').state('errorTemplateIds') ).toEqual([2]); const items = wrapper.find('NotificationListItem'); - items.at(0).find('Switch').at(1).prop('onChange')(); - expect(OrganizationsAPI.updateNotificationTemplateAssociation).toHaveBeenCalledWith(1, 1, 'error', true); + items + .at(0) + .find('Switch') + .at(1) + .prop('onChange')(); + expect( + OrganizationsAPI.updateNotificationTemplateAssociation + ).toHaveBeenCalledWith(1, 1, 'error', true); await sleep(0); wrapper.update(); expect( @@ -116,8 +132,14 @@ describe('', () => { wrapper.find('OrganizationNotifications').state('successTemplateIds') ).toEqual([1]); const items = wrapper.find('NotificationListItem'); - items.at(0).find('Switch').at(0).prop('onChange')(); - expect(OrganizationsAPI.updateNotificationTemplateAssociation).toHaveBeenCalledWith(1, 1, 'success', false); + items + .at(0) + .find('Switch') + .at(0) + .prop('onChange')(); + expect( + OrganizationsAPI.updateNotificationTemplateAssociation + ).toHaveBeenCalledWith(1, 1, 'success', false); await sleep(0); wrapper.update(); expect( @@ -136,8 +158,14 @@ describe('', () => { wrapper.find('OrganizationNotifications').state('errorTemplateIds') ).toEqual([2]); const items = wrapper.find('NotificationListItem'); - items.at(1).find('Switch').at(1).prop('onChange')(); - expect(OrganizationsAPI.updateNotificationTemplateAssociation).toHaveBeenCalledWith(1, 2, 'error', false); + items + .at(1) + .find('Switch') + .at(1) + .prop('onChange')(); + expect( + OrganizationsAPI.updateNotificationTemplateAssociation + ).toHaveBeenCalledWith(1, 2, 'error', false); await sleep(0); wrapper.update(); expect( diff --git a/awx/ui_next/src/screens/Organization/OrganizationTeams/OrganizationTeams.jsx b/awx/ui_next/src/screens/Organization/OrganizationTeams/OrganizationTeams.jsx index 44c01139e7..89dcadc219 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationTeams/OrganizationTeams.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationTeams/OrganizationTeams.jsx @@ -13,7 +13,7 @@ const QS_CONFIG = getQSConfig('team', { }); class OrganizationTeams extends React.Component { - constructor (props) { + constructor(props) { super(props); this.loadOrganizationTeamsList = this.loadOrganizationTeamsList.bind(this); @@ -26,18 +26,18 @@ class OrganizationTeams extends React.Component { }; } - componentDidMount () { + componentDidMount() { this.loadOrganizationTeamsList(); } - componentDidUpdate (prevProps) { + componentDidUpdate(prevProps) { const { location } = this.props; if (location !== prevProps.location) { this.loadOrganizationTeamsList(); } } - async loadOrganizationTeamsList () { + async loadOrganizationTeamsList() { const { id, location } = this.props; const params = parseNamespacedQueryString(QS_CONFIG, location.search); @@ -57,7 +57,7 @@ class OrganizationTeams extends React.Component { } } - render () { + render() { const { contentError, hasContentLoading, teams, itemCount } = this.state; return ( ', () => { @@ -42,12 +42,9 @@ describe('', () => { }); test('should load teams on mount', () => { - mountWithContexts( - - ).find('OrganizationTeams'); + mountWithContexts().find( + 'OrganizationTeams' + ); expect(OrganizationsAPI.readTeams).toHaveBeenCalledWith(1, { page: 1, page_size: 5, @@ -57,10 +54,7 @@ describe('', () => { test('should pass fetched teams to PaginatedDatalist', async () => { const wrapper = mountWithContexts( - + ); await sleep(0); diff --git a/awx/ui_next/src/screens/Organization/Organizations.jsx b/awx/ui_next/src/screens/Organization/Organizations.jsx index 56b0305a86..58dc8c2e2f 100644 --- a/awx/ui_next/src/screens/Organization/Organizations.jsx +++ b/awx/ui_next/src/screens/Organization/Organizations.jsx @@ -11,7 +11,7 @@ import OrganizationAdd from './OrganizationAdd/OrganizationAdd'; import Organization from './Organization'; class Organizations extends Component { - constructor (props) { + constructor(props) { super(props); const { i18n } = props; @@ -19,12 +19,12 @@ class Organizations extends Component { this.state = { breadcrumbConfig: { '/organizations': i18n._(t`Organizations`), - '/organizations/add': i18n._(t`Create New Organization`) - } + '/organizations/add': i18n._(t`Create New Organization`), + }, }; } - setBreadcrumbConfig = (organization) => { + setBreadcrumbConfig = organization => { const { i18n } = this.props; if (!organization) { @@ -39,27 +39,25 @@ class Organizations extends Component { [`/organizations/${organization.id}/details`]: i18n._(t`Details`), [`/organizations/${organization.id}/access`]: i18n._(t`Access`), [`/organizations/${organization.id}/teams`]: i18n._(t`Teams`), - [`/organizations/${organization.id}/notifications`]: i18n._(t`Notifications`), + [`/organizations/${organization.id}/notifications`]: i18n._( + t`Notifications` + ), }; this.setState({ breadcrumbConfig }); - } + }; - render () { + render() { const { match, history, location } = this.props; const { breadcrumbConfig } = this.state; return ( - + ( - - )} + render={() => } /> )} /> - ( - - )} - /> + } /> ); diff --git a/awx/ui_next/src/screens/Organization/shared/InstanceGroupsLookup.jsx b/awx/ui_next/src/screens/Organization/shared/InstanceGroupsLookup.jsx index 58fbeddd16..33a2fc2ea8 100644 --- a/awx/ui_next/src/screens/Organization/shared/InstanceGroupsLookup.jsx +++ b/awx/ui_next/src/screens/Organization/shared/InstanceGroupsLookup.jsx @@ -8,30 +8,24 @@ import { QuestionCircleIcon } from '@patternfly/react-icons'; import { InstanceGroupsAPI } from '@api'; import Lookup from '@components/Lookup'; -const getInstanceGroups = async (params) => InstanceGroupsAPI.read(params); +const getInstanceGroups = async params => InstanceGroupsAPI.read(params); class InstanceGroupsLookup extends React.Component { - render () { + render() { const { value, tooltip, onChange, i18n } = this.props; return ( - {i18n._(t`Instance Groups`)} - {' '} - { - tooltip && ( - - - - ) - } + {i18n._(t`Instance Groups`)}{' '} + {tooltip && ( + + + + )}
- )} + } fieldId="org-instance-groups" > diff --git a/awx/ui_next/src/screens/Organization/shared/OrganizationForm.jsx b/awx/ui_next/src/screens/Organization/shared/OrganizationForm.jsx index 8d58afb3d5..49b2f36d7c 100644 --- a/awx/ui_next/src/screens/Organization/shared/OrganizationForm.jsx +++ b/awx/ui_next/src/screens/Organization/shared/OrganizationForm.jsx @@ -7,11 +7,7 @@ import { Formik, Field } from 'formik'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; -import { - Tooltip, - Form, - FormGroup, -} from '@patternfly/react-core'; +import { Tooltip, Form, FormGroup } from '@patternfly/react-core'; import { OrganizationsAPI } from '@api'; import { Config } from '@contexts/Config'; @@ -24,11 +20,13 @@ import { required, minMaxValue } from '@util/validators'; import InstanceGroupsLookup from './InstanceGroupsLookup'; class OrganizationForm extends Component { - constructor (props) { + constructor(props) { super(props); this.getRelatedInstanceGroups = this.getRelatedInstanceGroups.bind(this); - this.handleInstanceGroupsChange = this.handleInstanceGroupsChange.bind(this); + this.handleInstanceGroupsChange = this.handleInstanceGroupsChange.bind( + this + ); this.handleSubmit = this.handleSubmit.bind(this); this.state = { @@ -38,7 +36,7 @@ class OrganizationForm extends Component { }; } - async componentDidMount () { + async componentDidMount() { let instanceGroups = []; if (!this.isEditingNewOrganization()) { @@ -55,48 +53,53 @@ class OrganizationForm extends Component { }); } - async getRelatedInstanceGroups () { + async getRelatedInstanceGroups() { const { - organization: { id } + organization: { id }, } = this.props; const { data } = await OrganizationsAPI.readInstanceGroups(id); return data.results; } - isEditingNewOrganization () { + isEditingNewOrganization() { const { organization } = this.props; return !organization.id; } - handleInstanceGroupsChange (instanceGroups) { + handleInstanceGroupsChange(instanceGroups) { this.setState({ instanceGroups }); } - handleSubmit (values) { + handleSubmit(values) { const { handleSubmit } = this.props; const { instanceGroups, initialInstanceGroups } = this.state; const initialIds = initialInstanceGroups.map(ig => ig.id); const updatedIds = instanceGroups.map(ig => ig.id); - const groupsToAssociate = [...updatedIds] - .filter(x => !initialIds.includes(x)); - const groupsToDisassociate = [...initialIds] - .filter(x => !updatedIds.includes(x)); + const groupsToAssociate = [...updatedIds].filter( + x => !initialIds.includes(x) + ); + const groupsToDisassociate = [...initialIds].filter( + x => !updatedIds.includes(x) + ); - if (typeof values.max_hosts !== 'number' || values.max_hosts === 'undefined') { + if ( + typeof values.max_hosts !== 'number' || + values.max_hosts === 'undefined' + ) { values.max_hosts = 0; } handleSubmit(values, groupsToAssociate, groupsToDisassociate); } - render () { + render() { const { organization, handleCancel, i18n, me } = this.props; const { instanceGroups, formIsValid, error } = this.state; const defaultVenv = { label: i18n._(t`Use Default Ansible Environment`), value: '/venv/ansible/', - key: 'default' + key: 'default', }; return ( @@ -130,31 +133,29 @@ class OrganizationForm extends Component { name="max_hosts" type="number" label={ - ( - - {i18n._(t`Max Hosts`)} - {' '} - {( - + {i18n._(t`Max Hosts`)}{' '} + { + - - - )} - - ) + > + + + } + } validate={minMaxValue(0, 2147483647, i18n)} me={me || {}} isDisabled={!me.is_superuser} /> - {({ custom_virtualenvs }) => ( - custom_virtualenvs && custom_virtualenvs.length > 1 && ( + {({ custom_virtualenvs }) => + custom_virtualenvs && + custom_virtualenvs.length > 1 && ( ( @@ -163,9 +164,15 @@ class OrganizationForm extends Component { label={i18n._(t`Ansible Environment`)} > datum !== defaultVenv.value) - .map(datum => ({ label: datum, value: datum, key: datum })) + data={[ + defaultVenv, + ...custom_virtualenvs + .filter(datum => datum !== defaultVenv.value) + .map(datum => ({ + label: datum, + value: datum, + key: datum, + })), ]} {...field} /> @@ -173,13 +180,15 @@ class OrganizationForm extends Component { )} /> ) - )} + } ', () => { const network = {}; const meConfig = { me: { - is_superuser: false - } + is_superuser: false, + }, }; const mockData = { id: 1, @@ -22,8 +22,8 @@ describe('', () => { max_hosts: 1, custom_virtualenv: 'Fizz', related: { - instance_groups: '/api/v2/organizations/1/instance_groups' - } + instance_groups: '/api/v2/organizations/1/instance_groups', + }, }; afterEach(() => { @@ -32,14 +32,13 @@ describe('', () => { test('should request related instance groups from api', () => { mountWithContexts( - ( - - ), { + , + { context: { network }, } ); @@ -48,31 +47,29 @@ describe('', () => { }); test('componentDidMount should set instanceGroups to state', async () => { - const mockInstanceGroups = [ - { name: 'One', id: 1 }, - { name: 'Two', id: 2 } - ]; + const mockInstanceGroups = [{ name: 'One', id: 1 }, { name: 'Two', id: 2 }]; OrganizationsAPI.readInstanceGroups.mockReturnValue({ data: { - results: mockInstanceGroups - } + results: mockInstanceGroups, + }, }); const wrapper = mountWithContexts( - ( - - ), { + , + { context: { network }, } ); await sleep(0); expect(OrganizationsAPI.readInstanceGroups).toHaveBeenCalled(); - expect(wrapper.find('OrganizationForm').state().instanceGroups).toEqual(mockInstanceGroups); + expect(wrapper.find('OrganizationForm').state().instanceGroups).toEqual( + mockInstanceGroups + ); }); test('changing instance group successfully sets instanceGroups state', () => { @@ -88,17 +85,20 @@ describe('', () => { const lookup = wrapper.find('InstanceGroupsLookup'); expect(lookup.length).toBe(1); - lookup.prop('onChange')([ - { - id: 1, - name: 'foo' - } - ], 'instanceGroups'); + lookup.prop('onChange')( + [ + { + id: 1, + name: 'foo', + }, + ], + 'instanceGroups' + ); expect(wrapper.find('OrganizationForm').state().instanceGroups).toEqual([ { id: 1, - name: 'foo' - } + name: 'foo', + }, ]); }); @@ -114,15 +114,15 @@ describe('', () => { const form = wrapper.find('Formik'); wrapper.find('input#org-name').simulate('change', { - target: { value: 'new foo', name: 'name' } + target: { value: 'new foo', name: 'name' }, }); expect(form.state('values').name).toEqual('new foo'); wrapper.find('input#org-description').simulate('change', { - target: { value: 'new bar', name: 'description' } + target: { value: 'new bar', name: 'description' }, }); expect(form.state('values').description).toEqual('new bar'); wrapper.find('input#org-max_hosts').simulate('change', { - target: { value: '134', name: 'max_hosts' } + target: { value: '134', name: 'max_hosts' }, }); expect(form.state('values').max_hosts).toEqual('134'); }); @@ -132,20 +132,24 @@ describe('', () => { custom_virtualenvs: ['foo', 'bar'], }; const wrapper = mountWithContexts( - ( - - ), { + , + { context: { config }, } ); expect(wrapper.find('FormSelect')).toHaveLength(1); expect(wrapper.find('FormSelectOption')).toHaveLength(3); - expect(wrapper.find('FormSelectOption').first().prop('value')).toEqual('/venv/ansible/'); + expect( + wrapper + .find('FormSelectOption') + .first() + .prop('value') + ).toEqual('/venv/ansible/'); }); test('calls handleSubmit when form submitted', async () => { @@ -161,23 +165,24 @@ describe('', () => { expect(handleSubmit).not.toHaveBeenCalled(); wrapper.find('button[aria-label="Save"]').simulate('click'); await sleep(1); - expect(handleSubmit).toHaveBeenCalledWith({ - name: 'Foo', - description: 'Bar', - max_hosts: 1, - custom_virtualenv: 'Fizz', - }, [], []); + expect(handleSubmit).toHaveBeenCalledWith( + { + name: 'Foo', + description: 'Bar', + max_hosts: 1, + custom_virtualenv: 'Fizz', + }, + [], + [] + ); }); test('handleSubmit associates and disassociates instance groups', async () => { - const mockInstanceGroups = [ - { name: 'One', id: 1 }, - { name: 'Two', id: 2 } - ]; + const mockInstanceGroups = [{ name: 'One', id: 1 }, { name: 'Two', id: 2 }]; OrganizationsAPI.readInstanceGroups.mockReturnValue({ data: { - results: mockInstanceGroups - } + results: mockInstanceGroups, + }, }); const mockDataForm = { name: 'Foo', @@ -190,22 +195,21 @@ describe('', () => { OrganizationsAPI.associateInstanceGroup.mockResolvedValue('done'); OrganizationsAPI.disassociateInstanceGroup.mockResolvedValue('done'); const wrapper = mountWithContexts( - ( - - ), { - context: { network } + , + { + context: { network }, } ); await sleep(0); - wrapper.find('InstanceGroupsLookup').prop('onChange')([ - { name: 'One', id: 1 }, - { name: 'Three', id: 3 } - ], 'instanceGroups'); + wrapper.find('InstanceGroupsLookup').prop('onChange')( + [{ name: 'One', id: 1 }, { name: 'Three', id: 3 }], + 'instanceGroups' + ); wrapper.find('button[aria-label="Save"]').simulate('click'); await sleep(0); @@ -226,12 +230,16 @@ describe('', () => { ); wrapper.find('button[aria-label="Save"]').simulate('click'); await sleep(0); - expect(handleSubmit).toHaveBeenCalledWith({ - name: 'Foo', - description: 'Bar', - max_hosts: 1, - custom_virtualenv: 'Fizz', - }, [], []); + expect(handleSubmit).toHaveBeenCalledWith( + { + name: 'Foo', + description: 'Bar', + max_hosts: 1, + custom_virtualenv: 'Fizz', + }, + [], + [] + ); }); test('handleSubmit does not get called if max_hosts value is out of range', async () => { @@ -284,12 +292,16 @@ describe('', () => { ); wrapper.find('button[aria-label="Save"]').simulate('click'); await sleep(0); - expect(handleSubmit).toHaveBeenCalledWith({ - name: 'Foo', - description: 'Bar', - max_hosts: 0, - custom_virtualenv: 'Fizz', - }, [], []); + expect(handleSubmit).toHaveBeenCalledWith( + { + name: 'Foo', + description: 'Bar', + max_hosts: 0, + custom_virtualenv: 'Fizz', + }, + [], + [] + ); }); test('calls "handleCancel" when Cancel button is clicked', () => { diff --git a/awx/ui_next/src/screens/Portal/Portal.jsx b/awx/ui_next/src/screens/Portal/Portal.jsx index 2073ebc33a..04862f71ac 100644 --- a/awx/ui_next/src/screens/Portal/Portal.jsx +++ b/awx/ui_next/src/screens/Portal/Portal.jsx @@ -8,16 +8,14 @@ import { } from '@patternfly/react-core'; class Portal extends Component { - render () { + render() { const { i18n } = this.props; const { light, medium } = PageSectionVariants; return ( - - {i18n._(t`My View`)} - + {i18n._(t`My View`)} diff --git a/awx/ui_next/src/screens/Project/Projects.jsx b/awx/ui_next/src/screens/Project/Projects.jsx index 079cb7fe36..5e5578a03e 100644 --- a/awx/ui_next/src/screens/Project/Projects.jsx +++ b/awx/ui_next/src/screens/Project/Projects.jsx @@ -8,16 +8,14 @@ import { } from '@patternfly/react-core'; class Projects extends Component { - render () { + render() { const { i18n } = this.props; const { light, medium } = PageSectionVariants; return ( - - {i18n._(t`Projects`)} - + {i18n._(t`Projects`)} diff --git a/awx/ui_next/src/screens/Schedule/Schedules.jsx b/awx/ui_next/src/screens/Schedule/Schedules.jsx index 2d8951685a..fd99e6975e 100644 --- a/awx/ui_next/src/screens/Schedule/Schedules.jsx +++ b/awx/ui_next/src/screens/Schedule/Schedules.jsx @@ -8,16 +8,14 @@ import { } from '@patternfly/react-core'; class Schedules extends Component { - render () { + render() { const { i18n } = this.props; const { light, medium } = PageSectionVariants; return ( - - {i18n._(t`Schedules`)} - + {i18n._(t`Schedules`)} diff --git a/awx/ui_next/src/screens/SystemSetting/SystemSettings.jsx b/awx/ui_next/src/screens/SystemSetting/SystemSettings.jsx index ac33293756..b4ff67a20b 100644 --- a/awx/ui_next/src/screens/SystemSetting/SystemSettings.jsx +++ b/awx/ui_next/src/screens/SystemSetting/SystemSettings.jsx @@ -8,16 +8,14 @@ import { } from '@patternfly/react-core'; class SystemSettings extends Component { - render () { + render() { const { i18n } = this.props; const { light, medium } = PageSectionVariants; return ( - - {i18n._(t`System Settings`)} - + {i18n._(t`System Settings`)} diff --git a/awx/ui_next/src/screens/Team/Teams.jsx b/awx/ui_next/src/screens/Team/Teams.jsx index e49bfca702..6bcfb8c797 100644 --- a/awx/ui_next/src/screens/Team/Teams.jsx +++ b/awx/ui_next/src/screens/Team/Teams.jsx @@ -8,16 +8,14 @@ import { } from '@patternfly/react-core'; class Teams extends Component { - render () { + render() { const { i18n } = this.props; const { light, medium } = PageSectionVariants; return ( - - {i18n._(t`Teams`)} - + {i18n._(t`Teams`)} diff --git a/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx b/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx index f7acaf5dee..6cfb5c4888 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx +++ b/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx @@ -1,7 +1,14 @@ import React, { Component } from 'react'; import { Link, withRouter } from 'react-router-dom'; import { withI18n } from '@lingui/react'; -import { CardBody, Button, TextList, TextListItem, TextListItemVariants, TextListVariants } from '@patternfly/react-core'; +import { + CardBody, + Button, + TextList, + TextListItem, + TextListItemVariants, + TextListVariants, +} from '@patternfly/react-core'; import styled from 'styled-components'; import { t } from '@lingui/macro'; @@ -16,29 +23,31 @@ const ButtonGroup = styled.div` display: flex; justify-content: flex-end; margin-top: 20px; - & > :not(:first-child){ + & > :not(:first-child) { margin-left: 20px; } `; class JobTemplateDetail extends Component { - constructor (props) { + constructor(props) { super(props); this.state = { contentError: null, hasContentLoading: true, - instanceGroups: [] + instanceGroups: [], }; this.readInstanceGroups = this.readInstanceGroups.bind(this); } - componentDidMount () { + componentDidMount() { this.readInstanceGroups(); } - async readInstanceGroups () { + async readInstanceGroups() { const { match } = this.props; try { - const { data } = await JobTemplatesAPI.readInstanceGroups(match.params.id); + const { data } = await JobTemplatesAPI.readInstanceGroups( + match.params.id + ); this.setState({ instanceGroups: [...data.results] }); } catch (err) { this.setState({ contentError: err }); @@ -47,7 +56,7 @@ class JobTemplateDetail extends Component { } } - render () { + render() { const { template: { allow_simultaneous, @@ -71,7 +80,7 @@ class JobTemplateDetail extends Component { summary_fields, use_fact_cache, url, - verbosity + verbosity, }, hasTemplateLoading, i18n, @@ -92,38 +101,31 @@ class JobTemplateDetail extends Component { const generateCallBackUrl = `${window.location.origin + url}callback/`; const isInitialized = !hasTemplateLoading && !hasContentLoading; - const credentialType = (c) => (c === 'aws' || c === 'ssh' ? c.toUpperCase() : toTitleCase(c)); + const credentialType = c => + c === 'aws' || c === 'ssh' ? c.toUpperCase() : toTitleCase(c); - const renderOptionsField = become_enabled || host_config_key || allow_simultaneous - || use_fact_cache; + const renderOptionsField = + become_enabled || host_config_key || allow_simultaneous || use_fact_cache; const renderOptions = ( {become_enabled && ( - + {i18n._(t`Enable Privilege Escalation`)} )} {host_config_key && ( - + {i18n._(t`Allow Provisioning Callbacks`)} )} {allow_simultaneous && ( - + {i18n._(t`Enable Concurrent Jobs`)} )} {use_fact_cache && ( - + {i18n._(t`Use Fact Cache`)} )} @@ -131,29 +133,20 @@ class JobTemplateDetail extends Component { ); if (contentError) { - return (); + return ; } if (hasContentLoading) { - return (); + return ; } return ( isInitialized && ( - - - + + + {inventory && ( )} - - - + + + - + - + {host_config_key && ( )} {renderOptionsField && ( - + )} - {(summary_fields.credentials && summary_fields.credentials.length > 0) && ( - - {summary_fields.credentials.map(c => ( - - - {c.kind ? credentialType(c.kind) : i18n._(t`Cloud`)} -: - - {` ${c.name}`} - - )) - } - - )} - /> - )} - {(summary_fields.labels && summary_fields.labels.results.length > 0) && ( + {summary_fields.credentials && + summary_fields.credentials.length > 0 && ( + + {summary_fields.credentials.map(c => ( + + + {c.kind ? credentialType(c.kind) : i18n._(t`Cloud`)} + : + + {` ${c.name}`} + + ))} + + } + /> + )} + {summary_fields.labels && summary_fields.labels.results.length > 0 && ( {summary_fields.labels.results.map(l => ( {l.name} - )) - } + ))} - )} + } /> )} - {(instanceGroups.length > 0) && ( + {instanceGroups.length > 0 && ( {instanceGroups.map(ig => ( @@ -268,39 +242,37 @@ class JobTemplateDetail extends Component { ))} - )} + } /> )} - {(job_tags && job_tags.length > 0) && ( + {job_tags && job_tags.length > 0 && ( {job_tags.split(',').map(jobTag => ( {jobTag} - )) - } + ))} - )} + } /> )} - {(skip_tags && skip_tags.length > 0) && ( + {skip_tags && skip_tags.length > 0 && ( {skip_tags.split(',').map(skipTag => ( {skipTag} - )) - } + ))} - )} + } /> )} @@ -310,7 +282,6 @@ class JobTemplateDetail extends Component { component={Link} to={`/templates/${match.params.templateType}/${match.params.id}/edit`} aria-label={i18n._(t`Edit`)} - > {i18n._(t`Edit`)} diff --git a/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.test.jsx b/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.test.jsx index 90489d942b..46dd3a5aef 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.test.jsx +++ b/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.test.jsx @@ -23,23 +23,24 @@ describe('', () => { modified_by: { username: 'Joe' }, credentials: [ { id: 1, kind: 'ssh', name: 'Credential 1' }, - { id: 2, kind: 'awx', name: 'Credential 2' } + { id: 2, kind: 'awx', name: 'Credential 2' }, ], inventory: { name: 'Inventory' }, - project: { name: 'Project' } - } + project: { name: 'Project' }, + }, }; const mockInstanceGroups = { count: 5, data: { - results: [ - { id: 1, name: 'IG1' }, - { id: 2, name: 'IG2' }] - } + results: [{ id: 1, name: 'IG1' }, { id: 2, name: 'IG2' }], + }, }; - const readInstanceGroups = jest.spyOn(_JobTemplateDetail.prototype, 'readInstanceGroups'); + const readInstanceGroups = jest.spyOn( + _JobTemplateDetail.prototype, + 'readInstanceGroups' + ); beforeEach(() => { JobTemplatesAPI.readInstanceGroups.mockResolvedValue(mockInstanceGroups); @@ -51,25 +52,29 @@ describe('', () => { test('initially renders succesfully', () => { const wrapper = mountWithContexts( - + ); expect(wrapper).toMatchSnapshot(); }); - test('When component mounts API is called to get instance groups', async (done) => { + test('When component mounts API is called to get instance groups', async done => { const wrapper = mountWithContexts( - + + ); + await waitForElement( + wrapper, + 'JobTemplateDetail', + el => el.state('hasContentLoading') === true ); - await waitForElement(wrapper, 'JobTemplateDetail', (el) => el.state('hasContentLoading') === true); expect(readInstanceGroups).toHaveBeenCalled(); - await waitForElement(wrapper, 'JobTemplateDetail', (el) => el.state('hasContentLoading') === false); + await waitForElement( + wrapper, + 'JobTemplateDetail', + el => el.state('hasContentLoading') === false + ); expect(JobTemplatesAPI.readInstanceGroups).toHaveBeenCalledTimes(1); done(); }); - test('Edit button is absent when user does not have edit privilege', async (done) => { + test('Edit button is absent when user does not have edit privilege', async done => { const regularUser = { forks: 1, host_config_key: 'ssh', @@ -90,34 +95,34 @@ describe('', () => { modified_by: { username: 'Joe' }, inventory: { name: 'Inventory' }, project: { name: 'Project' }, - labels: { count: 1, results: [{ name: 'Label', id: 1 }] } - } + labels: { count: 1, results: [{ name: 'Label', id: 1 }] }, + }, }; const wrapper = mountWithContexts( - + ); const jobTemplateDetail = wrapper.find('JobTemplateDetail'); const editButton = jobTemplateDetail.find('button[aria-label="Edit"]'); jobTemplateDetail.setState({ - instanceGroups: mockInstanceGroups, hasContentLoading: false, contentError: false + instanceGroups: mockInstanceGroups, + hasContentLoading: false, + contentError: false, }); expect(editButton.length).toBe(0); done(); }); - test('Credential type is Cloud if credential.kind is null', async (done) => { - template.summary_fields.credentials = [{ id: 1, name: 'cred', kind: null, }]; + test('Credential type is Cloud if credential.kind is null', async done => { + template.summary_fields.credentials = [{ id: 1, name: 'cred', kind: null }]; const wrapper = mountWithContexts( - + ); const jobTemplateDetail = wrapper.find('JobTemplateDetail'); jobTemplateDetail.setState({ - instanceGroups: mockInstanceGroups.data.results, hasContentLoading: false, contentError: false + instanceGroups: mockInstanceGroups.data.results, + hasContentLoading: false, + contentError: false, }); const cred = wrapper.find('strong.credential'); expect(cred.text()).toContain('Cloud:'); diff --git a/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.jsx b/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.jsx index 4ebffdca5b..494adf0569 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.jsx +++ b/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.jsx @@ -10,19 +10,22 @@ class JobTemplateEdit extends Component { template: JobTemplate.isRequired, }; - constructor (props) { + constructor(props) { super(props); this.state = { - error: '' + error: '', }; this.handleCancel = this.handleCancel.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } - async handleSubmit (values) { - const { template: { id, type }, history } = this.props; + async handleSubmit(values) { + const { + template: { id, type }, + history, + } = this.props; try { await JobTemplatesAPI.update(id, { ...values }); @@ -32,18 +35,23 @@ class JobTemplateEdit extends Component { } } - handleCancel () { - const { template: { id, type }, history } = this.props; + handleCancel() { + const { + template: { id, type }, + history, + } = this.props; history.push(`/templates/${type}/${id}/details`); } - render () { + render() { const { template } = this.props; const { error } = this.state; const canEdit = template.summary_fields.user_capabilities.edit; if (!canEdit) { - const { template: { id, type } } = this.props; + const { + template: { id, type }, + } = this.props; return ; } diff --git a/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.test.jsx b/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.test.jsx index ce55184c36..ab7fa3b69e 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.test.jsx +++ b/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.test.jsx @@ -17,25 +17,17 @@ describe('', () => { type: 'job_template', summary_fields: { user_capabilities: { - edit: true - } - } + edit: true, + }, + }, }; test('initially renders successfully', () => { - mountWithContexts( - - ); + mountWithContexts(); }); test('handleSubmit should call api update', () => { - const wrapper = mountWithContexts( - - ); + const wrapper = mountWithContexts(); const updatedTemplateData = { name: 'new name', description: 'new description', @@ -50,15 +42,14 @@ describe('', () => { const history = { push: jest.fn(), }; - const wrapper = mountWithContexts( - , - { context: { router: { history } } } - ); + const wrapper = mountWithContexts(, { + context: { router: { history } }, + }); expect(history.push).not.toHaveBeenCalled(); wrapper.find('button[aria-label="Cancel"]').prop('onClick')(); - expect(history.push).toHaveBeenCalledWith('/templates/job_template/1/details'); + expect(history.push).toHaveBeenCalledWith( + '/templates/job_template/1/details' + ); }); }); diff --git a/awx/ui_next/src/screens/Template/Template.jsx b/awx/ui_next/src/screens/Template/Template.jsx index af9a10e63f..adead1c6c0 100644 --- a/awx/ui_next/src/screens/Template/Template.jsx +++ b/awx/ui_next/src/screens/Template/Template.jsx @@ -1,17 +1,8 @@ import React, { Component } from 'react'; import { t } from '@lingui/macro'; import { withI18n } from '@lingui/react'; -import { - Card, - CardHeader, - PageSection, -} from '@patternfly/react-core'; -import { - Switch, - Route, - Redirect, - withRouter, -} from 'react-router-dom'; +import { Card, CardHeader, PageSection } from '@patternfly/react-core'; +import { Switch, Route, Redirect, withRouter } from 'react-router-dom'; import CardCloseButton from '@components/CardCloseButton'; import ContentError from '@components/ContentError'; import RoutedTabs from '@components/RoutedTabs'; @@ -20,7 +11,7 @@ import { JobTemplatesAPI } from '@api'; import JobTemplateEdit from './JobTemplateEdit'; class Template extends Component { - constructor (props) { + constructor(props) { super(props); this.state = { @@ -31,18 +22,18 @@ class Template extends Component { this.loadTemplate = this.loadTemplate.bind(this); } - async componentDidMount () { + async componentDidMount() { await this.loadTemplate(); } - async componentDidUpdate (prevProps) { + async componentDidUpdate(prevProps) { const { location } = this.props; if (location !== prevProps.location) { await this.loadTemplate(); } } - async loadTemplate () { + async loadTemplate() { const { setBreadcrumb, match } = this.props; const { id } = match.params; @@ -58,18 +49,9 @@ class Template extends Component { } } - render () { - const { - history, - i18n, - location, - match, - } = this.props; - const { - contentError, - hasContentLoading, - template - } = this.state; + render() { + const { history, i18n, location, match } = this.props; + const { contentError, hasContentLoading, template } = this.state; const tabsArray = [ { name: i18n._(t`Details`), link: `${match.url}/details`, id: 0 }, @@ -77,19 +59,14 @@ class Template extends Component { { name: i18n._(t`Notifications`), link: '/home', id: 2 }, { name: i18n._(t`Schedules`), link: '/home', id: 3 }, { name: i18n._(t`Completed Jobs`), link: '/home', id: 4 }, - { name: i18n._(t`Survey`), link: '/home', id: 5 } + { name: i18n._(t`Survey`), link: '/home', id: 5 }, ]; - let cardHeader = (hasContentLoading ? null - : ( - - - - - ) + let cardHeader = hasContentLoading ? null : ( + + + + ); if (location.pathname.endsWith('edit')) { @@ -108,7 +85,7 @@ class Template extends Component { return ( - { cardHeader } + {cardHeader} ( - - )} + render={() => } /> )} diff --git a/awx/ui_next/src/screens/Template/Template.test.jsx b/awx/ui_next/src/screens/Template/Template.test.jsx index 264c4c60aa..42fb581ee4 100644 --- a/awx/ui_next/src/screens/Template/Template.test.jsx +++ b/awx/ui_next/src/screens/Template/Template.test.jsx @@ -6,12 +6,20 @@ describe('