mirror of
https://github.com/ansible/awx.git
synced 2026-02-16 18:50:04 -03:30
updates tooltip component, fixes formik configuration on ad hoc qizard
This commit is contained in:
@@ -65,7 +65,7 @@ function AdHocCommands({ children, apiModule, adHocItems, itemId, i18n }) {
|
|||||||
useCallback(
|
useCallback(
|
||||||
async values => {
|
async values => {
|
||||||
const { data } = await apiModule.launchAdHocCommands(itemId, values);
|
const { data } = await apiModule.launchAdHocCommands(itemId, values);
|
||||||
history.push(`/jobs/${data.module_name}/${data.id}/output`);
|
history.push(`/jobs/command/${data.id}/output`);
|
||||||
},
|
},
|
||||||
|
|
||||||
[apiModule, itemId, history]
|
[apiModule, itemId, history]
|
||||||
|
|||||||
@@ -157,12 +157,12 @@ describe('<AdHocCommands />', () => {
|
|||||||
).toBe(true);
|
).toBe(true);
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('AnsibleSelect[name="module_args"]').prop('onChange')(
|
wrapper.find('AnsibleSelect[name="module_name"]').prop('onChange')(
|
||||||
{},
|
{},
|
||||||
'command'
|
'command'
|
||||||
);
|
);
|
||||||
wrapper.find('input#arguments').simulate('change', {
|
wrapper.find('input#module_args').simulate('change', {
|
||||||
target: { value: 'foo', name: 'arguments' },
|
target: { value: 'foo', name: 'module_args' },
|
||||||
});
|
});
|
||||||
wrapper.find('AnsibleSelect[name="verbosity"]').prop('onChange')({}, 1);
|
wrapper.find('AnsibleSelect[name="verbosity"]').prop('onChange')({}, 1);
|
||||||
});
|
});
|
||||||
@@ -194,14 +194,15 @@ describe('<AdHocCommands />', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(InventoriesAPI.launchAdHocCommands).toBeCalledWith(1, {
|
expect(InventoriesAPI.launchAdHocCommands).toBeCalledWith(1, {
|
||||||
arguments: 'foo',
|
module_args: 'foo',
|
||||||
changes: false,
|
diff_mode: false,
|
||||||
credential: 4,
|
credential: 4,
|
||||||
escalation: false,
|
job_type: 'run',
|
||||||
|
become_enabled: '',
|
||||||
extra_vars: '---',
|
extra_vars: '---',
|
||||||
forks: 0,
|
forks: 0,
|
||||||
limit: 'Inventory 1 Org 0, Inventory 2 Org 0',
|
limit: 'Inventory 1 Org 0, Inventory 2 Org 0',
|
||||||
module_args: 'command',
|
module_name: 'command',
|
||||||
verbosity: 1,
|
verbosity: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -271,12 +272,12 @@ describe('<AdHocCommands />', () => {
|
|||||||
).toBe(true);
|
).toBe(true);
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('AnsibleSelect[name="module_args"]').prop('onChange')(
|
wrapper.find('AnsibleSelect[name="module_name"]').prop('onChange')(
|
||||||
{},
|
{},
|
||||||
'command'
|
'command'
|
||||||
);
|
);
|
||||||
wrapper.find('input#arguments').simulate('change', {
|
wrapper.find('input#module_args').simulate('change', {
|
||||||
target: { value: 'foo', name: 'arguments' },
|
target: { value: 'foo', name: 'module_args' },
|
||||||
});
|
});
|
||||||
wrapper.find('AnsibleSelect[name="verbosity"]').prop('onChange')({}, 1);
|
wrapper.find('AnsibleSelect[name="verbosity"]').prop('onChange')({}, 1);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ function AdHocCommandsWizard({
|
|||||||
const { values } = useFormikContext();
|
const { values } = useFormikContext();
|
||||||
|
|
||||||
const enabledNextOnDetailsStep = () => {
|
const enabledNextOnDetailsStep = () => {
|
||||||
if (!values.module_args) {
|
if (!values.module_name) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values.module_args === 'shell' || values.module_args === 'command') {
|
if (values.module_name === 'shell' || values.module_name === 'command') {
|
||||||
if (values.arguments) {
|
if (values.module_args) {
|
||||||
return true;
|
return true;
|
||||||
// eslint-disable-next-line no-else-return
|
// eslint-disable-next-line no-else-return
|
||||||
} else {
|
} else {
|
||||||
@@ -90,15 +90,16 @@ const FormikApp = withFormik({
|
|||||||
mapPropsToValues({ adHocItems, verbosityOptions }) {
|
mapPropsToValues({ adHocItems, verbosityOptions }) {
|
||||||
const adHocItemStrings = adHocItems.map(item => item.name).join(', ');
|
const adHocItemStrings = adHocItems.map(item => item.name).join(', ');
|
||||||
return {
|
return {
|
||||||
limit: adHocItemStrings || [],
|
limit: adHocItemStrings || 'all',
|
||||||
credential: [],
|
credential: [],
|
||||||
module_args: '',
|
module_args: '',
|
||||||
arguments: '',
|
|
||||||
verbosity: verbosityOptions[0].value,
|
verbosity: verbosityOptions[0].value,
|
||||||
forks: 0,
|
forks: 0,
|
||||||
changes: false,
|
diff_mode: false,
|
||||||
escalation: false,
|
become_enabled: '',
|
||||||
|
module_name: '',
|
||||||
extra_vars: '---',
|
extra_vars: '---',
|
||||||
|
job_type: 'run',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
})(AdHocCommandsWizard);
|
})(AdHocCommandsWizard);
|
||||||
|
|||||||
@@ -73,12 +73,12 @@ describe('<AdHocCommandsWizard/>', () => {
|
|||||||
await waitForElement(wrapper, 'WizardNavItem', el => el.length > 0);
|
await waitForElement(wrapper, 'WizardNavItem', el => el.length > 0);
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('AnsibleSelect[name="module_args"]').prop('onChange')(
|
wrapper.find('AnsibleSelect[name="module_name"]').prop('onChange')(
|
||||||
{},
|
{},
|
||||||
'command'
|
'command'
|
||||||
);
|
);
|
||||||
wrapper.find('input#arguments').simulate('change', {
|
wrapper.find('input#module_args').simulate('change', {
|
||||||
target: { value: 'foo', name: 'arguments' },
|
target: { value: 'foo', name: 'module_args' },
|
||||||
});
|
});
|
||||||
wrapper.find('AnsibleSelect[name="verbosity"]').prop('onChange')({}, 1);
|
wrapper.find('AnsibleSelect[name="verbosity"]').prop('onChange')({}, 1);
|
||||||
});
|
});
|
||||||
@@ -105,12 +105,12 @@ describe('<AdHocCommandsWizard/>', () => {
|
|||||||
await waitForElement(wrapper, 'WizardNavItem', el => el.length > 0);
|
await waitForElement(wrapper, 'WizardNavItem', el => el.length > 0);
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('AnsibleSelect[name="module_args"]').prop('onChange')(
|
wrapper.find('AnsibleSelect[name="module_name"]').prop('onChange')(
|
||||||
{},
|
{},
|
||||||
'command'
|
'command'
|
||||||
);
|
);
|
||||||
wrapper.find('input#arguments').simulate('change', {
|
wrapper.find('input#module_args').simulate('change', {
|
||||||
target: { value: 'foo', name: 'arguments' },
|
target: { value: 'foo', name: 'module_args' },
|
||||||
});
|
});
|
||||||
wrapper.find('AnsibleSelect[name="verbosity"]').prop('onChange')({}, 1);
|
wrapper.find('AnsibleSelect[name="verbosity"]').prop('onChange')({}, 1);
|
||||||
});
|
});
|
||||||
@@ -165,12 +165,12 @@ describe('<AdHocCommandsWizard/>', () => {
|
|||||||
await waitForElement(wrapper, 'WizardNavItem', el => el.length > 0);
|
await waitForElement(wrapper, 'WizardNavItem', el => el.length > 0);
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('AnsibleSelect[name="module_args"]').prop('onChange')(
|
wrapper.find('AnsibleSelect[name="module_name"]').prop('onChange')(
|
||||||
{},
|
{},
|
||||||
'command'
|
'command'
|
||||||
);
|
);
|
||||||
wrapper.find('input#arguments').simulate('change', {
|
wrapper.find('input#module_args').simulate('change', {
|
||||||
target: { value: 'foo', name: 'arguments' },
|
target: { value: 'foo', name: 'module_args' },
|
||||||
});
|
});
|
||||||
wrapper.find('AnsibleSelect[name="verbosity"]').prop('onChange')({}, 1);
|
wrapper.find('AnsibleSelect[name="verbosity"]').prop('onChange')({}, 1);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,29 +28,36 @@ const TooltipWrapper = styled.div`
|
|||||||
const brandName = BrandName;
|
const brandName = BrandName;
|
||||||
|
|
||||||
function CredentialStep({ i18n, verbosityOptions, moduleOptions }) {
|
function CredentialStep({ i18n, verbosityOptions, moduleOptions }) {
|
||||||
const [moduleField, moduleMeta, moduleHelpers] = useField({
|
const [module_nameField, module_nameMeta, module_nameHelpers] = useField({
|
||||||
|
name: 'module_name',
|
||||||
|
validate: required(null, i18n),
|
||||||
|
});
|
||||||
|
const [module_argsField] = useField({
|
||||||
name: 'module_args',
|
name: 'module_args',
|
||||||
validate: required(null, i18n),
|
validate: required(null, i18n),
|
||||||
});
|
});
|
||||||
const [variablesField] = useField('extra_vars');
|
const [variablesField] = useField('extra_vars');
|
||||||
const [changesField, , changesHelpers] = useField('changes');
|
const [diff_modeField, , diff_modeHelpers] = useField('diff_mode');
|
||||||
const [escalationField, , escalationHelpers] = useField('escalation');
|
const [become_enabledField, , become_enabledHelpers] = useField(
|
||||||
|
'become_enabled'
|
||||||
|
);
|
||||||
const [verbosityField, verbosityMeta, verbosityHelpers] = useField({
|
const [verbosityField, verbosityMeta, verbosityHelpers] = useField({
|
||||||
name: 'verbosity',
|
name: 'verbosity',
|
||||||
validate: required(null, i18n),
|
validate: required(null, i18n),
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form>
|
||||||
<FormColumnLayout>
|
<FormColumnLayout>
|
||||||
<FormFullWidthLayout>
|
<FormFullWidthLayout>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
fieldId="module"
|
fieldId="module_name"
|
||||||
label={i18n._(t`Module`)}
|
label={i18n._(t`Module`)}
|
||||||
isRequired
|
isRequired
|
||||||
helperTextInvalid={moduleMeta.error}
|
helperTextInvalid={module_nameMeta.error}
|
||||||
validated={
|
validated={
|
||||||
!moduleMeta.touched || !moduleMeta.error ? 'default' : 'error'
|
!module_nameMeta.touched || !module_nameMeta.error
|
||||||
|
? 'default'
|
||||||
|
: 'error'
|
||||||
}
|
}
|
||||||
labelIcon={
|
labelIcon={
|
||||||
<FieldTooltip
|
<FieldTooltip
|
||||||
@@ -61,26 +68,43 @@ function CredentialStep({ i18n, verbosityOptions, moduleOptions }) {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<AnsibleSelect
|
<AnsibleSelect
|
||||||
{...moduleField}
|
{...module_nameField}
|
||||||
isValid={!moduleMeta.touched || !moduleMeta.error}
|
isValid={!module_nameMeta.touched || !module_nameMeta.error}
|
||||||
id="module"
|
id="module_name"
|
||||||
data={moduleOptions || []}
|
data={moduleOptions || []}
|
||||||
onChange={(event, value) => {
|
onChange={(event, value) => {
|
||||||
moduleHelpers.setValue(value);
|
module_nameHelpers.setValue(value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormField
|
<FormField
|
||||||
id="arguments"
|
id="module_args"
|
||||||
name="arguments"
|
name="module_args"
|
||||||
type="text"
|
type="text"
|
||||||
label={i18n._(t`Arguments`)}
|
label={i18n._(t`Arguments`)}
|
||||||
isRequired={
|
isRequired={
|
||||||
moduleField.value === 'command' || moduleField.value === 'shell'
|
module_nameField.value === 'command' ||
|
||||||
|
module_nameField.value === 'shell'
|
||||||
|
}
|
||||||
|
tooltip={
|
||||||
|
module_nameField.value ? (
|
||||||
|
<>
|
||||||
|
{i18n._(
|
||||||
|
t`These arguments are used with the specified module. You can find information about the ${module_argsField.value} by clicking `
|
||||||
|
)}
|
||||||
|
<a
|
||||||
|
href={`https://docs.ansible.com/ansible/latest/modules/${module_argsField.value}_module.html`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{' '}
|
||||||
|
{i18n._(t`here.`)}
|
||||||
|
</a>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
i18n._(t`These arguments are used with the specified module.`)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
tooltip={i18n._(
|
|
||||||
t`These arguments are used with the specified module.`
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
fieldId="verbosity"
|
fieldId="verbosity"
|
||||||
@@ -106,7 +130,7 @@ function CredentialStep({ i18n, verbosityOptions, moduleOptions }) {
|
|||||||
id="verbosity"
|
id="verbosity"
|
||||||
data={verbosityOptions || []}
|
data={verbosityOptions || []}
|
||||||
onChange={(event, value) => {
|
onChange={(event, value) => {
|
||||||
verbosityHelpers.setValue(value);
|
verbosityHelpers.setValue(parseInt(value, 10));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
@@ -164,20 +188,17 @@ function CredentialStep({ i18n, verbosityOptions, moduleOptions }) {
|
|||||||
>
|
>
|
||||||
<Switch
|
<Switch
|
||||||
css="display: inline-flex;"
|
css="display: inline-flex;"
|
||||||
id="changes"
|
id="diff_mode"
|
||||||
label={i18n._(t`On`)}
|
label={i18n._(t`On`)}
|
||||||
labelOff={i18n._(t`Off`)}
|
labelOff={i18n._(t`Off`)}
|
||||||
isChecked={changesField.value}
|
isChecked={diff_modeField.value}
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
changesHelpers.setValue(!changesField.value);
|
diff_modeHelpers.setValue(!diff_modeField.value);
|
||||||
}}
|
}}
|
||||||
aria-label={i18n._(t`toggle changes`)}
|
aria-label={i18n._(t`toggle changes`)}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup
|
<FormGroup name="become_enabled" fieldId="become_enabled">
|
||||||
name={i18n._(t`enable privilege escalation`)}
|
|
||||||
fieldId="escalation"
|
|
||||||
>
|
|
||||||
<FormCheckboxLayout>
|
<FormCheckboxLayout>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
aria-label={i18n._(t`Enable privilege escalation`)}
|
aria-label={i18n._(t`Enable privilege escalation`)}
|
||||||
@@ -202,10 +223,10 @@ function CredentialStep({ i18n, verbosityOptions, moduleOptions }) {
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
id="escalation"
|
id="become_enabled"
|
||||||
isChecked={escalationField.value}
|
isChecked={become_enabledField.value}
|
||||||
onChange={checked => {
|
onChange={checked => {
|
||||||
escalationHelpers.setValue(checked);
|
become_enabledHelpers.setValue(checked);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormCheckboxLayout>
|
</FormCheckboxLayout>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const initialValues = {
|
|||||||
extra_vars: '---',
|
extra_vars: '---',
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('<DetailsStep />', () => {
|
describe('<AdHocDetailsStep />', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -64,14 +64,12 @@ describe('<DetailsStep />', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
expect(wrapper.find('FormGroup[label="Module"]').length).toBe(1);
|
expect(wrapper.find('FormGroup[label="Module"]').length).toBe(1);
|
||||||
expect(wrapper.find('FormField[name="arguments"]').length).toBe(1);
|
expect(wrapper.find('FormField[label="Arguments"]').length).toBe(1);
|
||||||
expect(wrapper.find('FormGroup[label="Verbosity"]').length).toBe(1);
|
expect(wrapper.find('FormGroup[label="Verbosity"]').length).toBe(1);
|
||||||
expect(wrapper.find('FormField[label="Limit"]').length).toBe(1);
|
expect(wrapper.find('FormField[label="Limit"]').length).toBe(1);
|
||||||
expect(wrapper.find('FormField[name="forks"]').length).toBe(1);
|
expect(wrapper.find('FormField[name="forks"]').length).toBe(1);
|
||||||
expect(wrapper.find('FormGroup[label="Show changes"]').length).toBe(1);
|
expect(wrapper.find('FormGroup[label="Show changes"]').length).toBe(1);
|
||||||
expect(
|
expect(wrapper.find('FormGroup[name="become_enabled"]').length).toBe(1);
|
||||||
wrapper.find('FormGroup[name="enable privilege escalation"]').length
|
|
||||||
).toBe(1);
|
|
||||||
expect(wrapper.find('VariablesField').length).toBe(1);
|
expect(wrapper.find('VariablesField').length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -89,12 +87,12 @@ describe('<DetailsStep />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('AnsibleSelect[name="module_args"]').prop('onChange')(
|
wrapper.find('AnsibleSelect[name="module_name"]').prop('onChange')(
|
||||||
{},
|
{},
|
||||||
'command'
|
'command'
|
||||||
);
|
);
|
||||||
wrapper.find('input#arguments').simulate('change', {
|
wrapper.find('input#module_args').simulate('change', {
|
||||||
target: { value: 'foo', name: 'arguments' },
|
target: { value: 'foo', name: 'module_args' },
|
||||||
});
|
});
|
||||||
wrapper.find('input#limit').simulate('change', {
|
wrapper.find('input#limit').simulate('change', {
|
||||||
target: {
|
target: {
|
||||||
@@ -116,9 +114,9 @@ describe('<DetailsStep />', () => {
|
|||||||
});
|
});
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
expect(
|
expect(
|
||||||
wrapper.find('AnsibleSelect[name="module_args"]').prop('value')
|
wrapper.find('AnsibleSelect[name="module_name"]').prop('value')
|
||||||
).toBe('command');
|
).toBe('command');
|
||||||
expect(wrapper.find('input#arguments').prop('value')).toBe('foo');
|
expect(wrapper.find('input#module_args').prop('value')).toBe('foo');
|
||||||
expect(wrapper.find('AnsibleSelect[name="verbosity"]').prop('value')).toBe(
|
expect(wrapper.find('AnsibleSelect[name="verbosity"]').prop('value')).toBe(
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ describe('VariablesField', () => {
|
|||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
);
|
);
|
||||||
expect(wrapper.find('Tooltip').length).toBe(1);
|
expect(wrapper.find('Popover').length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should submit value through Formik', async () => {
|
it('should submit value through Formik', async () => {
|
||||||
|
|||||||
@@ -61,6 +61,6 @@ describe('FieldWithPrompt', () => {
|
|||||||
</Formik>
|
</Formik>
|
||||||
);
|
);
|
||||||
expect(wrapper.find('.pf-c-form__label-required')).toHaveLength(1);
|
expect(wrapper.find('.pf-c-form__label-required')).toHaveLength(1);
|
||||||
expect(wrapper.find('Tooltip')).toHaveLength(1);
|
expect(wrapper.find('Popover')).toHaveLength(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { node } from 'prop-types';
|
import { node } from 'prop-types';
|
||||||
import { Tooltip } from '@patternfly/react-core';
|
import { Popover } from '@patternfly/react-core';
|
||||||
import { QuestionCircleIcon as PFQuestionCircleIcon } from '@patternfly/react-icons';
|
import { QuestionCircleIcon as PFQuestionCircleIcon } from '@patternfly/react-icons';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
@@ -9,18 +9,20 @@ const QuestionCircleIcon = styled(PFQuestionCircleIcon)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
function FieldTooltip({ content, ...rest }) {
|
function FieldTooltip({ content, ...rest }) {
|
||||||
|
const [showTooltip, setShowTooltip] = useState(false);
|
||||||
if (!content) {
|
if (!content) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Popover
|
||||||
position="right"
|
bodyContent={content}
|
||||||
content={content}
|
isVisible={showTooltip}
|
||||||
trigger="click mouseenter focus"
|
hideOnOutsideClick
|
||||||
|
shouldClose={() => setShowTooltip(false)}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
<QuestionCircleIcon />
|
<QuestionCircleIcon onClick={() => setShowTooltip(!showTooltip)} />
|
||||||
</Tooltip>
|
</Popover>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
FieldTooltip.propTypes = {
|
FieldTooltip.propTypes = {
|
||||||
|
|||||||
Reference in New Issue
Block a user