mirror of
https://github.com/ansible/awx.git
synced 2026-01-15 03:40:42 -03:30
Merge pull request #9439 from mabashian/9410-workflow-node-disable-jt
Disable job templates in node modal that are missing inv or project
Reviewed-by: Mat Wilson <mawilson@redhat.com>
https://github.com/one-t
This commit is contained in:
commit
5bdf9a108c
@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
DataListItem,
|
||||
DataListItemRow,
|
||||
@ -9,6 +10,14 @@ import {
|
||||
} from '@patternfly/react-core';
|
||||
import DataListCell from '../DataListCell';
|
||||
|
||||
const Label = styled.label`
|
||||
${({ isDisabled }) =>
|
||||
isDisabled &&
|
||||
`
|
||||
opacity: 0.5;
|
||||
`}
|
||||
`;
|
||||
|
||||
const CheckboxListItem = ({
|
||||
isDisabled = false,
|
||||
isRadio = false,
|
||||
@ -32,7 +41,7 @@ const CheckboxListItem = ({
|
||||
aria-label={`check-action-item-${itemId}`}
|
||||
aria-labelledby={`check-action-item-${itemId}`}
|
||||
checked={isSelected}
|
||||
disabled={isDisabled}
|
||||
isDisabled={isDisabled}
|
||||
id={`selected-${itemId}`}
|
||||
isChecked={isSelected}
|
||||
name={name}
|
||||
@ -42,13 +51,14 @@ const CheckboxListItem = ({
|
||||
<DataListItemCells
|
||||
dataListCells={[
|
||||
<DataListCell key="name">
|
||||
<label
|
||||
<Label
|
||||
id={`check-action-item-${itemId}`}
|
||||
htmlFor={`selected-${itemId}`}
|
||||
className="check-action-item"
|
||||
isDisabled={isDisabled}
|
||||
>
|
||||
<b>{label}</b>
|
||||
</label>
|
||||
</Label>
|
||||
</DataListCell>,
|
||||
]}
|
||||
/>
|
||||
|
||||
@ -94,6 +94,7 @@ const mockJobTemplate = {
|
||||
},
|
||||
related: { webhook_receiver: '' },
|
||||
inventory: 1,
|
||||
project: 5,
|
||||
};
|
||||
|
||||
describe('NodeModal', () => {
|
||||
|
||||
@ -3,6 +3,7 @@ import { useLocation } from 'react-router-dom';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { func, shape } from 'prop-types';
|
||||
import { Tooltip } from '@patternfly/react-core';
|
||||
import { JobTemplatesAPI } from '../../../../../../api';
|
||||
import { getQSConfig, parseQueryString } from '../../../../../../util/qs';
|
||||
import useRequest from '../../../../../../util/useRequest';
|
||||
@ -56,26 +57,56 @@ function JobTemplatesList({ i18n, nodeResource, onUpdateNodeResource }) {
|
||||
fetchJobTemplates();
|
||||
}, [fetchJobTemplates]);
|
||||
|
||||
const onSelectRow = row => {
|
||||
if (
|
||||
row.project &&
|
||||
row.project !== null &&
|
||||
((row.inventory && row.inventory !== null) || row.ask_inventory_on_launch)
|
||||
) {
|
||||
onUpdateNodeResource(row);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<PaginatedDataList
|
||||
contentError={error}
|
||||
hasContentLoading={isLoading}
|
||||
itemCount={count}
|
||||
items={jobTemplates}
|
||||
onRowClick={row => onUpdateNodeResource(row)}
|
||||
onRowClick={row => onSelectRow(row)}
|
||||
qsConfig={QS_CONFIG}
|
||||
renderItem={item => (
|
||||
<CheckboxListItem
|
||||
isSelected={!!(nodeResource && nodeResource.id === item.id)}
|
||||
itemId={item.id}
|
||||
key={item.id}
|
||||
name={item.name}
|
||||
label={item.name}
|
||||
onSelect={() => onUpdateNodeResource(item)}
|
||||
onDeselect={() => onUpdateNodeResource(null)}
|
||||
isRadio
|
||||
/>
|
||||
)}
|
||||
renderItem={item => {
|
||||
const isDisabled =
|
||||
!item.project ||
|
||||
item.project === null ||
|
||||
((!item.inventory || item.inventory === null) &&
|
||||
!item.ask_inventory_on_launch);
|
||||
const listItem = (
|
||||
<CheckboxListItem
|
||||
isDisabled={isDisabled}
|
||||
isSelected={!!(nodeResource && nodeResource.id === item.id)}
|
||||
itemId={item.id}
|
||||
key={`${item.id}-listItem`}
|
||||
name={item.name}
|
||||
label={item.name}
|
||||
onSelect={() => onSelectRow(item)}
|
||||
onDeselect={() => onUpdateNodeResource(null)}
|
||||
isRadio
|
||||
/>
|
||||
);
|
||||
return isDisabled ? (
|
||||
<Tooltip
|
||||
key={`${item.id}-tooltip`}
|
||||
content={i18n._(
|
||||
t`Job Templates with a missing inventory or project cannot be selected when creating or editing nodes`
|
||||
)}
|
||||
>
|
||||
{listItem}
|
||||
</Tooltip>
|
||||
) : (
|
||||
listItem
|
||||
);
|
||||
}}
|
||||
renderToolbar={props => <DataListToolbar {...props} fillWidth />}
|
||||
showPageSizeOptions={false}
|
||||
toolbarSearchColumns={[
|
||||
|
||||
@ -16,6 +16,7 @@ const onUpdateNodeResource = jest.fn();
|
||||
describe('JobTemplatesList', () => {
|
||||
let wrapper;
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
wrapper.unmount();
|
||||
});
|
||||
test('Row selected when nodeResource id matches row id and clicking new row makes expected callback', async () => {
|
||||
@ -28,12 +29,16 @@ describe('JobTemplatesList', () => {
|
||||
name: 'Test Job Template',
|
||||
type: 'job_template',
|
||||
url: '/api/v2/job_templates/1',
|
||||
inventory: 1,
|
||||
project: 2,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Test Job Template 2',
|
||||
type: 'job_template',
|
||||
url: '/api/v2/job_templates/2',
|
||||
inventory: 1,
|
||||
project: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -60,10 +65,18 @@ describe('JobTemplatesList', () => {
|
||||
wrapper.find('CheckboxListItem[name="Test Job Template"]').props()
|
||||
.isSelected
|
||||
).toBe(true);
|
||||
expect(
|
||||
wrapper.find('CheckboxListItem[name="Test Job Template"]').props()
|
||||
.isDisabled
|
||||
).toBe(false);
|
||||
expect(
|
||||
wrapper.find('CheckboxListItem[name="Test Job Template 2"]').props()
|
||||
.isSelected
|
||||
).toBe(false);
|
||||
expect(
|
||||
wrapper.find('CheckboxListItem[name="Test Job Template 2"]').props()
|
||||
.isDisabled
|
||||
).toBe(false);
|
||||
wrapper
|
||||
.find('CheckboxListItem[name="Test Job Template 2"]')
|
||||
.simulate('click');
|
||||
@ -72,8 +85,75 @@ describe('JobTemplatesList', () => {
|
||||
name: 'Test Job Template 2',
|
||||
type: 'job_template',
|
||||
url: '/api/v2/job_templates/2',
|
||||
inventory: 1,
|
||||
project: 2,
|
||||
});
|
||||
});
|
||||
test('Row disabled when job template missing inventory or project', async () => {
|
||||
JobTemplatesAPI.read.mockResolvedValueOnce({
|
||||
data: {
|
||||
count: 2,
|
||||
results: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Test Job Template',
|
||||
type: 'job_template',
|
||||
url: '/api/v2/job_templates/1',
|
||||
inventory: 1,
|
||||
project: null,
|
||||
ask_inventory_on_launch: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Test Job Template 2',
|
||||
type: 'job_template',
|
||||
url: '/api/v2/job_templates/2',
|
||||
inventory: null,
|
||||
project: 2,
|
||||
ask_inventory_on_launch: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
JobTemplatesAPI.readOptions.mockResolvedValue({
|
||||
data: {
|
||||
actions: {
|
||||
GET: {},
|
||||
POST: {},
|
||||
},
|
||||
related_search_fields: [],
|
||||
},
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<JobTemplatesList
|
||||
nodeResource={nodeResource}
|
||||
onUpdateNodeResource={onUpdateNodeResource}
|
||||
/>
|
||||
);
|
||||
});
|
||||
wrapper.update();
|
||||
expect(
|
||||
wrapper.find('CheckboxListItem[name="Test Job Template"]').props()
|
||||
.isSelected
|
||||
).toBe(true);
|
||||
expect(
|
||||
wrapper.find('CheckboxListItem[name="Test Job Template"]').props()
|
||||
.isDisabled
|
||||
).toBe(true);
|
||||
expect(
|
||||
wrapper.find('CheckboxListItem[name="Test Job Template 2"]').props()
|
||||
.isSelected
|
||||
).toBe(false);
|
||||
expect(
|
||||
wrapper.find('CheckboxListItem[name="Test Job Template 2"]').props()
|
||||
.isDisabled
|
||||
).toBe(true);
|
||||
wrapper
|
||||
.find('CheckboxListItem[name="Test Job Template 2"]')
|
||||
.simulate('click');
|
||||
expect(onUpdateNodeResource).not.toHaveBeenCalled();
|
||||
});
|
||||
test('Error shown when read() request errors', async () => {
|
||||
JobTemplatesAPI.read.mockRejectedValue(new Error());
|
||||
JobTemplatesAPI.readOptions.mockResolvedValue({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user