diff --git a/awx/ui_next/src/api/mixins/InstanceGroups.mixin.js b/awx/ui_next/src/api/mixins/InstanceGroups.mixin.js index e3b2be1cb5..e6745ac522 100644 --- a/awx/ui_next/src/api/mixins/InstanceGroups.mixin.js +++ b/awx/ui_next/src/api/mixins/InstanceGroups.mixin.js @@ -1,10 +1,9 @@ const InstanceGroupsMixin = parent => class extends parent { readInstanceGroups(resourceId, params) { - return this.http.get( - `${this.baseUrl}${resourceId}/instance_groups/`, - params - ); + return this.http.get(`${this.baseUrl}${resourceId}/instance_groups/`, { + params, + }); } associateInstanceGroup(resourceId, instanceGroupId) { diff --git a/awx/ui_next/src/api/models/WorkflowJobTemplates.js b/awx/ui_next/src/api/models/WorkflowJobTemplates.js index 691c444379..a71fe68cbc 100644 --- a/awx/ui_next/src/api/models/WorkflowJobTemplates.js +++ b/awx/ui_next/src/api/models/WorkflowJobTemplates.js @@ -6,6 +6,32 @@ class WorkflowJobTemplates extends Base { this.baseUrl = '/api/v2/workflow_job_templates/'; } + readWebhookKey(id) { + return this.http.get(`${this.baseUrl}${id}/webhook_key/`); + } + + updateWebhookKey(id) { + return this.http.post(`${this.baseUrl}${id}/webhook_key/`); + } + + associateLabel(id, label, orgId) { + return this.http.post(`${this.baseUrl}${id}/labels/`, { + name: label.name, + organization: orgId, + }); + } + + createNode(id, data) { + return this.http.post(`${this.baseUrl}${id}/workflow_nodes/`, data); + } + + disassociateLabel(id, label) { + return this.http.post(`${this.baseUrl}${id}/labels/`, { + id: label.id, + disassociate: true, + }); + } + launch(id, data) { return this.http.post(`${this.baseUrl}${id}/launch/`, data); } @@ -20,16 +46,10 @@ class WorkflowJobTemplates extends Base { }); } - readWebhookKey(id) { - return this.http.get(`${this.baseUrl}${id}/webhook_key/`); - } - - createNode(id, data) { - return this.http.post(`${this.baseUrl}${id}/workflow_nodes/`, data); - } - readScheduleList(id, params) { - return this.http.get(`${this.baseUrl}${id}/schedules/`, { params }); + return this.http.get(`${this.baseUrl}${id}/schedules/`, { + params, + }); } } diff --git a/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx b/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx index d27b85d328..c8049328a8 100644 --- a/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx +++ b/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx @@ -5,7 +5,7 @@ import { t } from '@lingui/macro'; import { useField } from 'formik'; import styled from 'styled-components'; import { Split, SplitItem } from '@patternfly/react-core'; -import { CheckboxField } from '@components/FormField'; +import { CheckboxField, FieldTooltip } from '@components/FormField'; import MultiButtonToggle from '@components/MultiButtonToggle'; import { yamlToJson, jsonToYaml, isJson } from '@util/yaml'; import CodeMirrorInput from './CodeMirrorInput'; @@ -20,7 +20,15 @@ const StyledCheckboxField = styled(CheckboxField)` --pf-c-check__label--FontSize: var(--pf-c-form__label--FontSize); `; -function VariablesField({ i18n, id, name, label, readOnly, promptId }) { +function VariablesField({ + i18n, + id, + name, + label, + readOnly, + promptId, + tooltip, +}) { const [field, meta, helpers] = useField(name); const [mode, setMode] = useState(isJson(field.value) ? JSON_MODE : YAML_MODE); @@ -32,6 +40,7 @@ function VariablesField({ i18n, id, name, label, readOnly, promptId }) { + {tooltip && } { expect(field.prop('hasErrors')).toEqual(true); expect(wrapper.find('.pf-m-error')).toHaveLength(1); }); + it('should render tooltip', () => { + const value = '---\n'; + const wrapper = mount( + + {() => ( + + )} + + ); + expect(wrapper.find('Tooltip').length).toBe(1); + }); it('should submit value through Formik', async () => { const value = '---\nfoo: bar\n'; diff --git a/awx/ui_next/src/components/Lookup/CredentialLookup.jsx b/awx/ui_next/src/components/Lookup/CredentialLookup.jsx index bd3dbe3a5c..37f8a2e3bb 100644 --- a/awx/ui_next/src/components/Lookup/CredentialLookup.jsx +++ b/awx/ui_next/src/components/Lookup/CredentialLookup.jsx @@ -6,6 +6,7 @@ import { t } from '@lingui/macro'; import { CredentialsAPI } from '@api'; import { Credential } from '@types'; import { getQSConfig, parseQueryString, mergeParams } from '@util/qs'; +import { FieldTooltip } from '@components/FormField'; import { FormGroup } from '@patternfly/react-core'; import Lookup from '@components/Lookup'; import OptionsList from './shared/OptionsList'; @@ -28,6 +29,7 @@ function CredentialLookup({ value, history, i18n, + tooltip, }) { const [credentials, setCredentials] = useState([]); const [count, setCount] = useState(0); @@ -60,6 +62,7 @@ function CredentialLookup({ label={label} helperTextInvalid={helperTextInvalid} > + {tooltip && } dispatch({ type: 'SELECT_ITEM', item })} deselectItem={item => dispatch({ type: 'DESELECT_ITEM', item })} /> diff --git a/awx/ui_next/src/screens/Template/JobTemplateAdd/JobTemplateAdd.jsx b/awx/ui_next/src/screens/Template/JobTemplateAdd/JobTemplateAdd.jsx index 69a55550f1..5863abe8ea 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateAdd/JobTemplateAdd.jsx +++ b/awx/ui_next/src/screens/Template/JobTemplateAdd/JobTemplateAdd.jsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; -import { Card } from '@patternfly/react-core'; +import { Card, PageSection } from '@patternfly/react-core'; import { CardBody } from '@components/Card'; import JobTemplateForm from '../shared/JobTemplateForm'; import { JobTemplatesAPI } from '@api'; @@ -61,15 +61,17 @@ function JobTemplateAdd() { } return ( - - - - - + + + + + + + ); } diff --git a/awx/ui_next/src/screens/Template/Template.jsx b/awx/ui_next/src/screens/Template/Template.jsx index 84839c1a3f..61f63e70e4 100644 --- a/awx/ui_next/src/screens/Template/Template.jsx +++ b/awx/ui_next/src/screens/Template/Template.jsx @@ -164,87 +164,91 @@ class Template extends Component { } return ( - - {cardHeader} - - - {template && ( - ( - - )} + + + {cardHeader} + + - )} - {template && ( + {template && ( + ( + + )} + /> + )} + {template && ( + } + /> + )} + {template && ( + ( + + )} + /> + )} + {canSeeNotificationsTab && ( + ( + + )} + /> + )} + {template?.id && ( + + + + )} + {template && ( + ( + + )} + /> + )} } + key="not-found" + path="*" + render={() => + !hasContentLoading && ( + + {match.params.id && ( + + {i18n._(`View Template Details`)} + + )} + + ) + } /> - )} - {template && ( - ( - - )} - /> - )} - {canSeeNotificationsTab && ( - ( - - )} - /> - )} - {template?.id && ( - - - - )} - {template && ( - } - /> - )} - - !hasContentLoading && ( - - {match.params.id && ( - - {i18n._(`View Template Details`)} - - )} - - ) - } - /> - - + + + ); } } diff --git a/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx b/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx index 717b1b5f89..913fcfb648 100644 --- a/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx +++ b/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx @@ -2,7 +2,7 @@ import React, { useEffect, useState, useCallback } from 'react'; import { useParams, useLocation } from 'react-router-dom'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; -import { Card } from '@patternfly/react-core'; +import { Card, PageSection } from '@patternfly/react-core'; import { JobTemplatesAPI, @@ -141,7 +141,7 @@ function TemplateList({ i18n }) { ); return ( - <> + - + ); } diff --git a/awx/ui_next/src/screens/Template/Templates.jsx b/awx/ui_next/src/screens/Template/Templates.jsx index c8eb2f243e..f6572a54ab 100644 --- a/awx/ui_next/src/screens/Template/Templates.jsx +++ b/awx/ui_next/src/screens/Template/Templates.jsx @@ -2,7 +2,6 @@ import React, { Component } from 'react'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { Route, withRouter, Switch } from 'react-router-dom'; -import { PageSection } from '@patternfly/react-core'; import { Config } from '@contexts/Config'; import Breadcrumbs from '@components/Breadcrumbs/Breadcrumbs'; @@ -10,6 +9,7 @@ import { TemplateList } from './TemplateList'; import Template from './Template'; import WorkflowJobTemplate from './WorkflowJobTemplate'; import JobTemplateAdd from './JobTemplateAdd'; +import WorkflowJobTemplateAdd from './WorkflowJobTemplateAdd'; class Templates extends Component { constructor(props) { @@ -20,6 +20,9 @@ class Templates extends Component { breadcrumbConfig: { '/templates': i18n._(t`Templates`), '/templates/job_template/add': i18n._(t`Create New Job Template`), + '/templates/workflow_job_template/add': i18n._( + t`Create New Workflow Template` + ), }, }; } @@ -32,6 +35,9 @@ class Templates extends Component { const breadcrumbConfig = { '/templates': i18n._(t`Templates`), '/templates/job_template/add': i18n._(t`Create New Job Template`), + '/templates/workflow_job_template/add': i18n._( + t`Create New Workflow Template` + ), [`/templates/${template.type}/${template.id}`]: `${template.name}`, [`/templates/${template.type}/${template.id}/details`]: i18n._( t`Details` @@ -56,47 +62,48 @@ class Templates extends Component { return ( <> - - - } - /> - ( - - {({ me }) => ( -