mirror of
https://github.com/ansible/awx.git
synced 2026-02-28 08:18:43 -03:30
Merge pull request #5923 from mabashian/4967-prompt-on-launch-checkboxes
[POC] Adds FieldWithPrompt component to handle fields that are also promptable Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
@@ -0,0 +1,71 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { bool, node, string } from 'prop-types';
|
||||||
|
import { withI18n } from '@lingui/react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { CheckboxField, FieldTooltip } from '@components/FormField';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const FieldHeader = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-bottom: var(--pf-c-form__label--PaddingBottom);
|
||||||
|
|
||||||
|
label {
|
||||||
|
--pf-c-form__label--PaddingBottom: 0px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledCheckboxField = styled(CheckboxField)`
|
||||||
|
--pf-c-check__label--FontSize: var(--pf-c-form__label--FontSize);
|
||||||
|
`;
|
||||||
|
|
||||||
|
function FieldWithPrompt({
|
||||||
|
children,
|
||||||
|
fieldId,
|
||||||
|
i18n,
|
||||||
|
isRequired,
|
||||||
|
label,
|
||||||
|
promptId,
|
||||||
|
promptName,
|
||||||
|
tooltip,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="pf-c-form__group">
|
||||||
|
<FieldHeader>
|
||||||
|
<div>
|
||||||
|
<label className="pf-c-form__label" htmlFor={fieldId}>
|
||||||
|
<span className="pf-c-form__label-text">{label}</span>
|
||||||
|
{isRequired && (
|
||||||
|
<span className="pf-c-form__label-required" aria-hidden="true">
|
||||||
|
*
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</label>
|
||||||
|
{tooltip && <FieldTooltip content={tooltip} />}
|
||||||
|
</div>
|
||||||
|
<StyledCheckboxField
|
||||||
|
id={promptId}
|
||||||
|
label={i18n._(t`Prompt On Launch`)}
|
||||||
|
name={promptName}
|
||||||
|
/>
|
||||||
|
</FieldHeader>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldWithPrompt.propTypes = {
|
||||||
|
fieldId: string.isRequired,
|
||||||
|
isRequired: bool,
|
||||||
|
label: string.isRequired,
|
||||||
|
promptId: string.isRequired,
|
||||||
|
promptName: string.isRequired,
|
||||||
|
tooltip: node,
|
||||||
|
};
|
||||||
|
|
||||||
|
FieldWithPrompt.defaultProps = {
|
||||||
|
isRequired: false,
|
||||||
|
tooltip: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withI18n()(FieldWithPrompt);
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||||
|
import { Field, Formik } from 'formik';
|
||||||
|
import FieldWithPrompt from './FieldWithPrompt';
|
||||||
|
|
||||||
|
describe('FieldWithPrompt', () => {
|
||||||
|
let wrapper;
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Required asterisk and Tooltip hidden when not required and tooltip not provided', () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
ask_limit_on_launch: false,
|
||||||
|
limit: '',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{() => (
|
||||||
|
<FieldWithPrompt
|
||||||
|
fieldId="job-template-limit"
|
||||||
|
label="Limit"
|
||||||
|
promptId="job-template-ask-limit-on-launch"
|
||||||
|
promptName="ask_limit_on_launch"
|
||||||
|
>
|
||||||
|
<Field name="limit">
|
||||||
|
{() => <input id="job-template-limit" type="text" />}
|
||||||
|
</Field>
|
||||||
|
</FieldWithPrompt>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
);
|
||||||
|
expect(wrapper.find('.pf-c-form__label-required')).toHaveLength(0);
|
||||||
|
expect(wrapper.find('Tooltip')).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Required asterisk and Tooltip shown when required and tooltip provided', () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
ask_limit_on_launch: false,
|
||||||
|
limit: '',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{() => (
|
||||||
|
<FieldWithPrompt
|
||||||
|
fieldId="job-template-limit"
|
||||||
|
label="Limit"
|
||||||
|
promptId="job-template-ask-limit-on-launch"
|
||||||
|
promptName="ask_limit_on_launch"
|
||||||
|
tooltip="Help text"
|
||||||
|
isRequired
|
||||||
|
>
|
||||||
|
<Field name="limit">
|
||||||
|
{() => <input id="job-template-limit" type="text" />}
|
||||||
|
</Field>
|
||||||
|
</FieldWithPrompt>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
);
|
||||||
|
expect(wrapper.find('.pf-c-form__label-required')).toHaveLength(1);
|
||||||
|
expect(wrapper.find('Tooltip')).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
1
awx/ui_next/src/components/FieldWithPrompt/index.js
Normal file
1
awx/ui_next/src/components/FieldWithPrompt/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default } from './FieldWithPrompt';
|
||||||
@@ -8,25 +8,26 @@ import { JobTemplatesAPI, LabelsAPI } from '@api';
|
|||||||
jest.mock('@api');
|
jest.mock('@api');
|
||||||
|
|
||||||
const jobTemplateData = {
|
const jobTemplateData = {
|
||||||
name: 'Foo',
|
|
||||||
description: 'Baz',
|
|
||||||
job_type: 'run',
|
|
||||||
inventory: 1,
|
|
||||||
project: 2,
|
|
||||||
playbook: 'Bar',
|
|
||||||
forks: 0,
|
|
||||||
limit: '',
|
|
||||||
verbosity: '0',
|
|
||||||
job_slice_count: 1,
|
|
||||||
timeout: 0,
|
|
||||||
job_tags: '',
|
|
||||||
skip_tags: '',
|
|
||||||
diff_mode: false,
|
|
||||||
allow_callbacks: false,
|
allow_callbacks: false,
|
||||||
allow_simultaneous: false,
|
allow_simultaneous: false,
|
||||||
use_fact_cache: false,
|
ask_job_type_on_launch: false,
|
||||||
|
description: 'Baz',
|
||||||
|
diff_mode: false,
|
||||||
|
forks: 0,
|
||||||
host_config_key: '',
|
host_config_key: '',
|
||||||
|
inventory: 1,
|
||||||
|
job_slice_count: 1,
|
||||||
|
job_tags: '',
|
||||||
|
job_type: 'run',
|
||||||
|
limit: '',
|
||||||
|
name: 'Foo',
|
||||||
|
playbook: 'Bar',
|
||||||
|
project: 2,
|
||||||
scm_branch: '',
|
scm_branch: '',
|
||||||
|
skip_tags: '',
|
||||||
|
timeout: 0,
|
||||||
|
use_fact_cache: false,
|
||||||
|
verbosity: '0',
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('<JobTemplateAdd />', () => {
|
describe('<JobTemplateAdd />', () => {
|
||||||
|
|||||||
@@ -9,27 +9,24 @@ import JobTemplateEdit from './JobTemplateEdit';
|
|||||||
jest.mock('@api');
|
jest.mock('@api');
|
||||||
|
|
||||||
const mockJobTemplate = {
|
const mockJobTemplate = {
|
||||||
id: 1,
|
|
||||||
name: 'Foo',
|
|
||||||
description: 'Bar',
|
|
||||||
job_type: 'run',
|
|
||||||
inventory: 2,
|
|
||||||
project: 3,
|
|
||||||
playbook: 'Baz',
|
|
||||||
type: 'job_template',
|
|
||||||
forks: 0,
|
|
||||||
limit: '',
|
|
||||||
verbosity: '0',
|
|
||||||
job_slice_count: 1,
|
|
||||||
timeout: 0,
|
|
||||||
job_tags: '',
|
|
||||||
skip_tags: '',
|
|
||||||
diff_mode: false,
|
|
||||||
allow_callbacks: false,
|
allow_callbacks: false,
|
||||||
allow_simultaneous: false,
|
allow_simultaneous: false,
|
||||||
use_fact_cache: false,
|
ask_job_type_on_launch: false,
|
||||||
|
description: 'Bar',
|
||||||
|
diff_mode: false,
|
||||||
|
forks: 0,
|
||||||
host_config_key: '',
|
host_config_key: '',
|
||||||
|
id: 1,
|
||||||
|
inventory: 2,
|
||||||
|
job_slice_count: 1,
|
||||||
|
job_tags: '',
|
||||||
|
job_type: 'run',
|
||||||
|
limit: '',
|
||||||
|
name: 'Foo',
|
||||||
|
playbook: 'Baz',
|
||||||
|
project: 3,
|
||||||
scm_branch: '',
|
scm_branch: '',
|
||||||
|
skip_tags: '',
|
||||||
summary_fields: {
|
summary_fields: {
|
||||||
user_capabilities: {
|
user_capabilities: {
|
||||||
edit: true,
|
edit: true,
|
||||||
@@ -50,6 +47,10 @@ const mockJobTemplate = {
|
|||||||
name: 'Boo',
|
name: 'Boo',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
timeout: 0,
|
||||||
|
type: 'job_template',
|
||||||
|
use_fact_cache: false,
|
||||||
|
verbosity: '0',
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockRelatedCredentials = {
|
const mockRelatedCredentials = {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import FormField, {
|
|||||||
FieldTooltip,
|
FieldTooltip,
|
||||||
FormSubmitError,
|
FormSubmitError,
|
||||||
} from '@components/FormField';
|
} from '@components/FormField';
|
||||||
|
import FieldWithPrompt from '@components/FieldWithPrompt';
|
||||||
import FormRow from '@components/FormRow';
|
import FormRow from '@components/FormRow';
|
||||||
import CollapsibleSection from '@components/CollapsibleSection';
|
import CollapsibleSection from '@components/CollapsibleSection';
|
||||||
import { required } from '@util/validators';
|
import { required } from '@util/validators';
|
||||||
@@ -227,37 +228,35 @@ class JobTemplateForm extends Component {
|
|||||||
type="text"
|
type="text"
|
||||||
label={i18n._(t`Description`)}
|
label={i18n._(t`Description`)}
|
||||||
/>
|
/>
|
||||||
<Field
|
<FieldWithPrompt
|
||||||
name="job_type"
|
fieldId="template-job-type"
|
||||||
validate={required(null, i18n)}
|
isRequired
|
||||||
onBlur={handleBlur}
|
label={i18n._(t`Job Type`)}
|
||||||
|
promptId="template-ask-job-type-on-launch"
|
||||||
|
promptName="ask_job_type_on_launch"
|
||||||
|
tooltip={i18n._(t`For job templates, select run to execute
|
||||||
|
the playbook. Select check to only check playbook syntax,
|
||||||
|
test environment setup, and report problems without
|
||||||
|
executing the playbook.`)}
|
||||||
>
|
>
|
||||||
{({ form, field }) => {
|
<Field
|
||||||
const isValid = !form.touched.job_type || !form.errors.job_type;
|
name="job_type"
|
||||||
return (
|
validate={required(null, i18n)}
|
||||||
<FormGroup
|
onBlur={handleBlur}
|
||||||
fieldId="template-job-type"
|
>
|
||||||
helperTextInvalid={form.errors.job_type}
|
{({ form, field }) => {
|
||||||
isRequired
|
const isValid = !form.touched.job_type || !form.errors.job_type;
|
||||||
isValid={isValid}
|
return (
|
||||||
label={i18n._(t`Job Type`)}
|
|
||||||
>
|
|
||||||
<FieldTooltip
|
|
||||||
content={i18n._(t`For job templates, select run to execute
|
|
||||||
the playbook. Select check to only check playbook syntax,
|
|
||||||
test environment setup, and report problems without
|
|
||||||
executing the playbook.`)}
|
|
||||||
/>
|
|
||||||
<AnsibleSelect
|
<AnsibleSelect
|
||||||
isValid={isValid}
|
isValid={isValid}
|
||||||
id="template-job-type"
|
id="template-job-type"
|
||||||
data={jobTypeOptions}
|
data={jobTypeOptions}
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
);
|
||||||
);
|
}}
|
||||||
}}
|
</Field>
|
||||||
</Field>
|
</FieldWithPrompt>
|
||||||
<Field
|
<Field
|
||||||
name="inventory"
|
name="inventory"
|
||||||
validate={required(i18n._(t`Select a value for this field`), i18n)}
|
validate={required(i18n._(t`Select a value for this field`), i18n)}
|
||||||
@@ -613,6 +612,7 @@ const FormikApp = withFormik({
|
|||||||
? summary_fields.inventory.organization_id
|
? summary_fields.inventory.organization_id
|
||||||
: null;
|
: null;
|
||||||
return {
|
return {
|
||||||
|
ask_job_type_on_launch: template.ask_job_type_on_launch || false,
|
||||||
name: template.name || '',
|
name: template.name || '',
|
||||||
description: template.description || '',
|
description: template.description || '',
|
||||||
job_type: template.job_type || 'run',
|
job_type: template.job_type || 'run',
|
||||||
|
|||||||
Reference in New Issue
Block a user