From 56919c5d321d9df74f51fee9b3d2f8f88af976d0 Mon Sep 17 00:00:00 2001 From: Alex Corey Date: Wed, 18 Mar 2020 14:12:51 -0400 Subject: [PATCH 1/2] Moves template.jsx over to a functional component. --- awx/ui_next/src/screens/Template/Template.jsx | 400 ++++++++---------- .../src/screens/Template/Template.test.jsx | 128 +++--- 2 files changed, 237 insertions(+), 291 deletions(-) diff --git a/awx/ui_next/src/screens/Template/Template.jsx b/awx/ui_next/src/screens/Template/Template.jsx index fe997c5176..f8d932bb47 100644 --- a/awx/ui_next/src/screens/Template/Template.jsx +++ b/awx/ui_next/src/screens/Template/Template.jsx @@ -1,8 +1,18 @@ -import React, { Component } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { t } from '@lingui/macro'; import { withI18n } from '@lingui/react'; import { Card, CardActions, PageSection } from '@patternfly/react-core'; -import { Switch, Route, Redirect, withRouter, Link } from 'react-router-dom'; +import { + Switch, + Route, + Redirect, + withRouter, + Link, + useLocation, + useParams, + useRouteMatch, +} from 'react-router-dom'; +import useRequest from '@util/useRequest'; import { TabbedCardHeader } from '@components/Card'; import CardCloseButton from '@components/CardCloseButton'; @@ -17,261 +27,195 @@ import JobTemplateEdit from './JobTemplateEdit'; import { JobTemplatesAPI, OrganizationsAPI } from '@api'; import TemplateSurvey from './TemplateSurvey'; -class Template extends Component { - constructor(props) { - super(props); +function Template({ i18n, me, setBreadcrumb }) { + const location = useLocation(); + const { id: templateId } = useParams(); + const match = useRouteMatch(); - this.state = { - contentError: null, - hasContentLoading: true, - template: null, - isNotifAdmin: false, - }; - this.loadTemplate = this.loadTemplate.bind(this); - this.loadTemplateAndRoles = this.loadTemplateAndRoles.bind(this); - this.loadSchedules = this.loadSchedules.bind(this); - this.loadScheduleOptions = this.loadScheduleOptions.bind(this); - } - - async componentDidMount() { - await this.loadTemplateAndRoles(); - } - - async componentDidUpdate(prevProps) { - const { location } = this.props; - if (location !== prevProps.location) { - await this.loadTemplate(); - } - } - - async loadTemplateAndRoles() { - const { match, setBreadcrumb } = this.props; - const id = parseInt(match.params.id, 10); - - this.setState({ contentError: null, hasContentLoading: true }); - try { + const { + result: { isNotifAdmin, template }, + isLoading: hasRolesandTemplateLoading, + error: rolesAndTemplateError, + request: loadTempplateAndRoles, + } = useRequest( + useCallback(async () => { const [{ data }, notifAdminRes] = await Promise.all([ - JobTemplatesAPI.readDetail(id), + JobTemplatesAPI.readDetail(templateId), OrganizationsAPI.read({ page_size: 1, role_level: 'notification_admin_role', }), ]); setBreadcrumb(data); - this.setState({ + + return { template: data, isNotifAdmin: notifAdminRes.data.results.length > 0, - }); - } catch (err) { - this.setState({ contentError: err }); - } finally { - this.setState({ hasContentLoading: false }); - } - } + }; + }, [setBreadcrumb, templateId]), + { isNotifAdmin: false, template: null } + ); + useEffect(() => { + loadTempplateAndRoles(); + }, [loadTempplateAndRoles]); - async loadTemplate() { - const { setBreadcrumb, match } = this.props; - const { id } = match.params; + const loadScheduleOptions = () => { + return JobTemplatesAPI.readScheduleOptions(templateId); + }; - this.setState({ contentError: null, hasContentLoading: true }); - try { - const { data } = await JobTemplatesAPI.readDetail(id); - setBreadcrumb(data); - this.setState({ template: data }); - } catch (err) { - this.setState({ contentError: err }); - } finally { - this.setState({ hasContentLoading: false }); - } - } + const loadSchedules = params => { + return JobTemplatesAPI.readSchedules(templateId, params); + }; - loadScheduleOptions() { - const { template } = this.state; - return JobTemplatesAPI.readScheduleOptions(template.id); - } + const canSeeNotificationsTab = me.is_system_auditor || isNotifAdmin; - loadSchedules(params) { - const { template } = this.state; - return JobTemplatesAPI.readSchedules(template.id, params); - } + const tabsArray = [ + { name: i18n._(t`Details`), link: `${match.url}/details` }, + { name: i18n._(t`Access`), link: `${match.url}/access` }, + ]; - render() { - const { i18n, location, match, me, setBreadcrumb } = this.props; - const { - contentError, - hasContentLoading, - isNotifAdmin, - template, - } = this.state; - - const canSeeNotificationsTab = me.is_system_auditor || isNotifAdmin; - - const tabsArray = [ - { name: i18n._(t`Details`), link: `${match.url}/details` }, - { name: i18n._(t`Access`), link: `${match.url}/access` }, - ]; - - if (canSeeNotificationsTab) { - tabsArray.push({ - name: i18n._(t`Notifications`), - link: `${match.url}/notifications`, - }); - } - - if (template) { - tabsArray.push({ - name: i18n._(t`Schedules`), - link: `${match.url}/schedules`, - }); - } - - tabsArray.push( - { - name: i18n._(t`Completed Jobs`), - link: `${match.url}/completed_jobs`, - }, - { - name: i18n._(t`Survey`), - link: `${match.url}/survey`, - } - ); - - tabsArray.forEach((tab, n) => { - tab.id = n; + if (canSeeNotificationsTab) { + tabsArray.push({ + name: i18n._(t`Notifications`), + link: `${match.url}/notifications`, }); + } - let cardHeader = ( - - - - - - - ); + if (template) { + tabsArray.push({ + name: i18n._(t`Schedules`), + link: `${match.url}/schedules`, + }); + } - if ( - location.pathname.endsWith('edit') || - location.pathname.includes('schedules/') - ) { - cardHeader = null; + tabsArray.push( + { + name: i18n._(t`Completed Jobs`), + link: `${match.url}/completed_jobs`, + }, + { + name: i18n._(t`Survey`), + link: `${match.url}/survey`, } + ); - if (!hasContentLoading && contentError) { - return ( - - - - {contentError.response.status === 404 && ( - - {i18n._(`Template not found.`)}{' '} - {i18n._(`View all Templates.`)} - - )} - - - - ); - } + tabsArray.forEach((tab, n) => { + tab.id = n; + }); + let cardHeader = ( + + + + + + + ); + if ( + location.pathname.endsWith('edit') || + location.pathname.includes('schedules/') + ) { + cardHeader = null; + } + + const contentError = rolesAndTemplateError; + if (!hasRolesandTemplateLoading && contentError) { return ( - {cardHeader} - - - {template && ( - ( - - )} - /> + + {contentError.response.status === 404 && ( + + {i18n._(`Template not found.`)}{' '} + {i18n._(`View all Templates.`)} + )} - {template && ( - } - /> - )} - {template && ( - ( - - )} - /> - )} - {template && ( - ( - - )} - /> - )} - {canSeeNotificationsTab && ( - ( - - )} - /> - )} - {template?.id && ( - - - - )} - {template && ( - - - - )} - - !hasContentLoading && ( - - {match.params.id && ( - - {i18n._(`View Template Details`)} - - )} - - ) - } - /> - + ); } + + return ( + + + {cardHeader} + + + {template && ( + + + + )} + {template && ( + + + + )} + {template && ( + + + + )} + {template && ( + + + + )} + {canSeeNotificationsTab && ( + + + + )} + {template?.id && ( + + + + )} + {template && ( + + + + )} + {!hasRolesandTemplateLoading && ( + + + {match.params.id && ( + + {i18n._(`View Template Details`)} + + )} + + + )} + + + + ); } export { Template as _Template }; diff --git a/awx/ui_next/src/screens/Template/Template.test.jsx b/awx/ui_next/src/screens/Template/Template.test.jsx index 26583f5f0b..1d9b3651f4 100644 --- a/awx/ui_next/src/screens/Template/Template.test.jsx +++ b/awx/ui_next/src/screens/Template/Template.test.jsx @@ -1,63 +1,58 @@ import React from 'react'; import { createMemoryHistory } from 'history'; import { JobTemplatesAPI, OrganizationsAPI } from '@api'; +import { act } from 'react-dom/test-utils'; +import { sleep } from '@testUtils/testUtils'; + import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; -import Template, { _Template } from './Template'; +import Template from './Template'; import mockJobTemplateData from './shared/data.job_template.json'; -jest.mock('@api'); - -JobTemplatesAPI.readDetail.mockResolvedValue({ - data: mockJobTemplateData, -}); - -OrganizationsAPI.read.mockResolvedValue({ - data: { - count: 1, - next: null, - previous: null, - results: [ - { - id: 1, - }, - ], - }, -}); +jest.mock('@api/models/JobTemplates'); +jest.mock('@api/models/Organizations'); const mockMe = { is_super_user: true, is_system_auditor: false, }; - describe('