diff --git a/awx/ui_next/src/components/Lookup/CategoryLookup.jsx b/awx/ui_next/src/components/Lookup/CategoryLookup.jsx
new file mode 100644
index 0000000000..f3e8935bdf
--- /dev/null
+++ b/awx/ui_next/src/components/Lookup/CategoryLookup.jsx
@@ -0,0 +1,348 @@
+import React, { Fragment } from 'react';
+import {
+ string,
+ bool,
+ arrayOf,
+ func,
+ number,
+ oneOfType,
+ shape,
+} from 'prop-types';
+import { withRouter } from 'react-router-dom';
+import { SearchIcon } from '@patternfly/react-icons';
+import {
+ Button,
+ ButtonVariant,
+ InputGroup as PFInputGroup,
+ Modal,
+ ToolbarItem,
+} from '@patternfly/react-core';
+import { withI18n } from '@lingui/react';
+import { t } from '@lingui/macro';
+import styled from 'styled-components';
+
+import AnsibleSelect from '../AnsibleSelect';
+import PaginatedDataList from '../PaginatedDataList';
+import VerticalSeperator from '../VerticalSeparator';
+import DataListToolbar from '../DataListToolbar';
+import CheckboxListItem from '../CheckboxListItem';
+import SelectedList from '../SelectedList';
+import { ChipGroup, Chip, CredentialChip } from '../Chip';
+import { QSConfig } from '@types';
+
+const SearchButton = styled(Button)`
+ ::after {
+ border: var(--pf-c-button--BorderWidth) solid
+ var(--pf-global--BorderColor--200);
+ }
+`;
+
+const InputGroup = styled(PFInputGroup)`
+ ${props =>
+ props.multiple &&
+ `
+ --pf-c-form-control--Height: 90px;
+ overflow-y: auto;
+ `}
+`;
+
+const ChipHolder = styled.div`
+ --pf-c-form-control--BorderTopColor: var(--pf-global--BorderColor--200);
+ --pf-c-form-control--BorderRightColor: var(--pf-global--BorderColor--200);
+ border-top-right-radius: 3px;
+ border-bottom-right-radius: 3px;
+`;
+
+class CategoryLookup extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.assertCorrectValueType();
+ let selectedItems = [];
+ if (props.value) {
+ selectedItems = props.multiple ? [...props.value] : [props.value];
+ }
+ this.state = {
+ isModalOpen: false,
+ selectedItems,
+ error: null,
+ };
+ this.handleModalToggle = this.handleModalToggle.bind(this);
+ this.addItem = this.addItem.bind(this);
+ this.removeItem = this.removeItem.bind(this);
+ this.saveModal = this.saveModal.bind(this);
+ this.clearQSParams = this.clearQSParams.bind(this);
+ }
+
+ assertCorrectValueType() {
+ const { multiple, value, selectCategoryOptions } = this.props;
+ if (selectCategoryOptions) {
+ return;
+ }
+ if (!multiple && Array.isArray(value)) {
+ throw new Error(
+ 'CategoryLookup value must not be an array unless `multiple` is set'
+ );
+ }
+ if (multiple && !Array.isArray(value)) {
+ throw new Error(
+ 'CategoryLookup value must be an array if `multiple` is set'
+ );
+ }
+ }
+
+ removeItem(row) {
+ const { selectedItems } = this.state;
+ const { onToggleItem } = this.props;
+ if (onToggleItem) {
+ this.setState({ selectedItems: onToggleItem(selectedItems, row) });
+ return;
+ }
+ this.setState({
+ selectedItems: selectedItems.filter(item => item.id !== row.id),
+ });
+ }
+
+ addItem(row) {
+ const { selectedItems } = this.state;
+ const { multiple, onToggleItem } = this.props;
+ if (onToggleItem) {
+ this.setState({ selectedItems: onToggleItem(selectedItems, row) });
+ return;
+ }
+ const index = selectedItems.findIndex(item => item.id === row.id);
+
+ if (!multiple) {
+ this.setState({ selectedItems: [row] });
+ return;
+ }
+ if (index > -1) {
+ return;
+ }
+ this.setState({ selectedItems: [...selectedItems, row] });
+ }
+
+ // TODO: clean up
+ handleModalToggle() {
+ const { isModalOpen } = this.state;
+ const { value, multiple, selectCategory } = this.props;
+ // Resets the selected items from parent state whenever modal is opened
+ // This handles the case where the user closes/cancels the modal and
+ // opens it again
+ if (!isModalOpen) {
+ let selectedItems = [];
+ if (value) {
+ selectedItems = multiple ? [...value] : [value];
+ }
+ this.setState({ selectedItems });
+ } else {
+ this.clearQSParams();
+ if (selectCategory) {
+ selectCategory(null, 'Machine');
+ }
+ }
+ this.setState(prevState => ({
+ isModalOpen: !prevState.isModalOpen,
+ }));
+ }
+
+ removeItemAndSave(row) {
+ const { value, onChange, multiple } = this.props;
+ if (multiple) {
+ onChange(value.filter(item => item.id !== row.id));
+ } else if (value.id === row.id) {
+ onChange(null);
+ }
+ }
+
+ saveModal() {
+ const { onChange, multiple } = this.props;
+ const { selectedItems } = this.state;
+ const value = multiple ? selectedItems : selectedItems[0] || null;
+
+ this.handleModalToggle();
+ onChange(value);
+ }
+
+ clearQSParams() {
+ const { qsConfig, history } = this.props;
+ const parts = history.location.search.replace(/^\?/, '').split('&');
+ const ns = qsConfig.namespace;
+ const otherParts = parts.filter(param => !param.startsWith(`${ns}.`));
+ history.push(`${history.location.pathname}?${otherParts.join('&')}`);
+ }
+
+ render() {
+ const { isModalOpen, selectedItems, error } = this.state;
+ const {
+ id,
+ items,
+ count,
+ lookupHeader,
+ value,
+ columns,
+ multiple,
+ name,
+ onBlur,
+ qsConfig,
+ required,
+ selectCategory,
+ selectCategoryOptions,
+ selectedCategory,
+ i18n,
+ } = this.props;
+ const header = lookupHeader || i18n._(t`Items`);
+ const canDelete = !required || (multiple && value.length > 1);
+ const chips = () => {
+ return selectCategoryOptions && selectCategoryOptions.length > 0 ? (
+
+ {(multiple ? value : [value]).map(chip => (
+ this.removeItemAndSave(chip)}
+ isReadOnly={!canDelete}
+ credential={chip}
+ />
+ ))}
+
+ ) : (
+
+ {(multiple ? value : [value]).map(chip => (
+ this.removeItemAndSave(chip)}
+ isReadOnly={!canDelete}
+ >
+ {chip.name}
+
+ ))}
+
+ );
+ };
+ return (
+
+
+
+
+
+
+ {value ? chips(value) : null}
+
+
+
+ {i18n._(t`Select`)}
+ ,
+ ,
+ ]}
+ >
+ {selectCategoryOptions && selectCategoryOptions.length > 0 && (
+
+ Selected Category
+
+
+
+ )}
+ {selectedItems.length > 0 && (
+ 0
+ }
+ />
+ )}
+ (
+ i.id === item.id)}
+ onSelect={() => this.addItem(item)}
+ isRadio={
+ !multiple ||
+ (selectCategoryOptions &&
+ selectCategoryOptions.length &&
+ selectedCategory.value !== 'Vault')
+ }
+ />
+ )}
+ renderToolbar={props => }
+ showPageSizeOptions={false}
+ />
+ {error ? error: {error.message}
: ''}
+
+
+ );
+ }
+}
+
+const Item = shape({
+ id: number.isRequired,
+});
+
+CategoryLookup.propTypes = {
+ id: string,
+ items: arrayOf(shape({})).isRequired,
+ // TODO: change to `header`
+ lookupHeader: string,
+ name: string,
+ onChange: func.isRequired,
+ value: oneOfType([Item, arrayOf(Item)]),
+ multiple: bool,
+ required: bool,
+ qsConfig: QSConfig.isRequired,
+ selectCategory: func.isRequired,
+ selectCategoryOptions: oneOfType(shape({})).isRequired,
+ selectedCategory: shape({}).isRequired,
+};
+
+CategoryLookup.defaultProps = {
+ id: 'lookup-search',
+ lookupHeader: null,
+ name: null,
+ value: null,
+ multiple: false,
+ required: false,
+};
+
+export { CategoryLookup as _CategoryLookup };
+export default withI18n()(withRouter(CategoryLookup));
diff --git a/awx/ui_next/src/components/Lookup/InstanceGroupsLookup.jsx b/awx/ui_next/src/components/Lookup/InstanceGroupsLookup.jsx
index c720ae2363..6641294235 100644
--- a/awx/ui_next/src/components/Lookup/InstanceGroupsLookup.jsx
+++ b/awx/ui_next/src/components/Lookup/InstanceGroupsLookup.jsx
@@ -1,84 +1,114 @@
-import React from 'react';
-import PropTypes from 'prop-types';
+import React, { useState, useEffect } from 'react';
+import { arrayOf, string, func, object } from 'prop-types';
+import { withRouter } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { FormGroup, Tooltip } from '@patternfly/react-core';
import { QuestionCircleIcon as PFQuestionCircleIcon } from '@patternfly/react-icons';
import styled from 'styled-components';
-
import { InstanceGroupsAPI } from '@api';
import Lookup from '@components/Lookup';
+import { getQSConfig, parseQueryString } from '@util/qs';
const QuestionCircleIcon = styled(PFQuestionCircleIcon)`
margin-left: 10px;
`;
-const getInstanceGroups = async params => InstanceGroupsAPI.read(params);
+const QS_CONFIG = getQSConfig('instance-groups', {
+ page: 1,
+ page_size: 5,
+ order_by: 'name',
+});
+// const getInstanceGroups = async params => InstanceGroupsAPI.read(params);
-class InstanceGroupsLookup extends React.Component {
- render() {
- const { value, tooltip, onChange, className, i18n } = this.props;
+function InstanceGroupsLookup({
+ value,
+ onChange,
+ tooltip,
+ className,
+ history,
+ i18n,
+}) {
+ const [instanceGroups, setInstanceGroups] = useState([]);
+ const [count, setCount] = useState(0);
+ const [error, setError] = useState(null);
- /*
+ useEffect(() => {
+ (async () => {
+ const params = parseQueryString(QS_CONFIG, history.location.search);
+ try {
+ const { data } = await InstanceGroupsAPI.read(params);
+ setInstanceGroups(data.results);
+ setCount(data.count);
+ } catch (err) {
+ setError(err);
+ }
+ })();
+ }, [history.location]);
+
+ /*
Wrapping
added to workaround PF bug:
https://github.com/patternfly/patternfly-react/issues/2855
*/
- return (
-
-
- {tooltip && (
-
-
-
- )}
-
-
-
- );
- }
+ return (
+
+
+ {tooltip && (
+
+
+
+ )}
+
+ {error ? error {error.message}
: ''}
+
+
+ );
}
InstanceGroupsLookup.propTypes = {
- value: PropTypes.arrayOf(PropTypes.object).isRequired,
- tooltip: PropTypes.string,
- onChange: PropTypes.func.isRequired,
+ value: arrayOf(object).isRequired,
+ tooltip: string,
+ onChange: func.isRequired,
+ className: string,
};
InstanceGroupsLookup.defaultProps = {
tooltip: '',
+ className: '',
};
-export default withI18n()(InstanceGroupsLookup);
+export default withI18n()(withRouter(InstanceGroupsLookup));
diff --git a/awx/ui_next/src/components/Lookup/Lookup.jsx b/awx/ui_next/src/components/Lookup/Lookup.jsx
index b1913e0100..7446d8f09f 100644
--- a/awx/ui_next/src/components/Lookup/Lookup.jsx
+++ b/awx/ui_next/src/components/Lookup/Lookup.jsx
@@ -15,20 +15,17 @@ import {
ButtonVariant,
InputGroup as PFInputGroup,
Modal,
- ToolbarItem,
} from '@patternfly/react-core';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import styled from 'styled-components';
-import AnsibleSelect from '../AnsibleSelect';
import PaginatedDataList from '../PaginatedDataList';
-import VerticalSeperator from '../VerticalSeparator';
import DataListToolbar from '../DataListToolbar';
import CheckboxListItem from '../CheckboxListItem';
import SelectedList from '../SelectedList';
-import { ChipGroup, Chip, CredentialChip } from '../Chip';
-import { getQSConfig, parseQueryString } from '../../util/qs';
+import { ChipGroup, Chip } from '../Chip';
+import { QSConfig } from '@types';
const SearchButton = styled(Button)`
::after {
@@ -66,42 +63,16 @@ class Lookup extends React.Component {
this.state = {
isModalOpen: false,
selectedItems,
- results: [],
- count: 0,
- error: null,
};
- this.qsConfig = getQSConfig(props.qsNamespace, {
- page: 1,
- page_size: 5,
- order_by: props.sortedColumnKey,
- });
this.handleModalToggle = this.handleModalToggle.bind(this);
this.addItem = this.addItem.bind(this);
this.removeItem = this.removeItem.bind(this);
this.saveModal = this.saveModal.bind(this);
- this.getData = this.getData.bind(this);
this.clearQSParams = this.clearQSParams.bind(this);
}
- componentDidMount() {
- this.getData();
- }
-
- componentDidUpdate(prevProps) {
- const { location, selectedCategory } = this.props;
- if (
- location !== prevProps.location ||
- prevProps.selectedCategory !== selectedCategory
- ) {
- this.getData();
- }
- }
-
assertCorrectValueType() {
- const { multiple, value, selectCategoryOptions } = this.props;
- if (selectCategoryOptions) {
- return;
- }
+ const { multiple, value } = this.props;
if (!multiple && Array.isArray(value)) {
throw new Error(
'Lookup value must not be an array unless `multiple` is set'
@@ -112,27 +83,6 @@ class Lookup extends React.Component {
}
}
- async getData() {
- const {
- getItems,
- location: { search },
- } = this.props;
- const queryParams = parseQueryString(this.qsConfig, search);
-
- this.setState({ error: false });
- try {
- const { data } = await getItems(queryParams);
- const { results, count } = data;
-
- this.setState({
- results,
- count,
- });
- } catch (err) {
- this.setState({ error: true });
- }
- }
-
removeItem(row) {
const { selectedItems } = this.state;
const { onToggleItem } = this.props;
@@ -163,55 +113,11 @@ class Lookup extends React.Component {
}
this.setState({ selectedItems: [...selectedItems, row] });
}
- // toggleSelected(row) {
- // const {
- // name,
- // onChange,
- // multiple,
- // onToggleItem,
- // selectCategoryOptions,
- // onChange,
- // value
- // } = this.props;
- // const {
- // selectedItems: updatedSelectedItems,
- // isModalOpen,
- // } = this.state;
-
- // const selectedIndex = updatedSelectedItems.findIndex(
- // selectedRow => selectedRow.id === row.id
- // );
- //
- // if (multiple) {
- //
- // if (selectCategoryOptions) {
- //
- // onToggleItem(row, isModalOpen);
- // }
- // if (selectedIndex > -1) {
- //
- // const valueToUpdate = value.filter(itemValue => itemValue.id !==row.id );
- // onChange(valueToUpdate)
- // } else {
- //
- // onChange([...value, row])
- // }
- // } else {
- //
- // onChange(row)
- // }
-
- // Updates the selected items from parent state
- // This handles the case where the user removes chips from the lookup input
- // while the modal is closed
- // if (!isModalOpen) {
- // onChange(updatedSelectedItems, name);
- // }
- // }
+ // TODO: cleanup
handleModalToggle() {
const { isModalOpen } = this.state;
- const { value, multiple, selectCategory } = this.props;
+ const { value, multiple } = this.props;
// Resets the selected items from parent state whenever modal is opened
// This handles the case where the user closes/cancels the modal and
// opens it again
@@ -223,20 +129,17 @@ class Lookup extends React.Component {
this.setState({ selectedItems });
} else {
this.clearQSParams();
- if (selectCategory) {
- selectCategory(null, 'Machine');
- }
}
this.setState(prevState => ({
isModalOpen: !prevState.isModalOpen,
}));
}
- removeItemAndSave(row) {
+ removeItemAndSave(item) {
const { value, onChange, multiple } = this.props;
if (multiple) {
- onChange(value.filter(item => item.id !== row.id));
- } else if (value.id === row.id) {
+ onChange(value.filter(i => i.id !== item.id));
+ } else if (value.id === item.id) {
onChange(null);
}
}
@@ -251,58 +154,31 @@ class Lookup extends React.Component {
}
clearQSParams() {
- const { history } = this.props;
+ const { qsConfig, history } = this.props;
const parts = history.location.search.replace(/^\?/, '').split('&');
- const ns = this.qsConfig.namespace;
+ const ns = qsConfig.namespace;
const otherParts = parts.filter(param => !param.startsWith(`${ns}.`));
history.push(`${history.location.pathname}?${otherParts.join('&')}`);
}
render() {
- const { isModalOpen, selectedItems, error, results, count } = this.state;
+ const { isModalOpen, selectedItems } = this.state;
const {
- form,
id,
lookupHeader,
value,
+ items,
+ count,
columns,
multiple,
name,
onBlur,
- selectCategory,
required,
+ qsConfig,
i18n,
- selectCategoryOptions,
- selectedCategory,
} = this.props;
const header = lookupHeader || i18n._(t`Items`);
const canDelete = !required || (multiple && value.length > 1);
- const chips = () => {
- return selectCategoryOptions && selectCategoryOptions.length > 0 ? (
-
- {(multiple ? value : [value]).map(chip => (
- this.removeItemAndSave(chip)}
- isReadOnly={!canDelete}
- credential={chip}
- />
- ))}
-
- ) : (
-
- {(multiple ? value : [value]).map(chip => (
- this.removeItemAndSave(chip)}
- isReadOnly={!canDelete}
- >
- {chip.name}
-
- ))}
-
- );
- };
return (
@@ -315,7 +191,17 @@ class Lookup extends React.Component {
- {value ? chips(value) : null}
+
+ {(multiple ? value : [value]).map(chip => (
+ this.removeItemAndSave(chip)}
+ isReadOnly={!canDelete}
+ >
+ {chip.name}
+
+ ))}
+
,
]}
>
- {selectCategoryOptions && selectCategoryOptions.length > 0 && (
-
- Selected Category
-
-
-
- )}
{selectedItems.length > 0 && (
0
- }
/>
)}
(
i.id === item.id)}
onSelect={() => this.addItem(item)}
- isRadio={
- !multiple ||
- (selectCategoryOptions &&
- selectCategoryOptions.length &&
- selectedCategory.value !== 'Vault')
- }
+ isRadio={!multiple}
/>
)}
renderToolbar={props => }
showPageSizeOptions={false}
/>
- {error ? error
: ''}
);
@@ -406,15 +268,16 @@ const Item = shape({
Lookup.propTypes = {
id: string,
- getItems: func.isRequired,
+ items: arrayOf(shape({})).isRequired,
+ count: number.isRequired,
+ // TODO: change to `header`
lookupHeader: string,
name: string,
onChange: func.isRequired,
value: oneOfType([Item, arrayOf(Item)]),
- sortedColumnKey: string.isRequired,
multiple: bool,
required: bool,
- qsNamespace: string,
+ qsConfig: QSConfig.isRequired,
};
Lookup.defaultProps = {
@@ -424,7 +287,6 @@ Lookup.defaultProps = {
value: null,
multiple: false,
required: false,
- qsNamespace: 'lookup',
};
export { Lookup as _Lookup };
diff --git a/awx/ui_next/src/components/Lookup/MultiCredentialsLookup.jsx b/awx/ui_next/src/components/Lookup/MultiCredentialsLookup.jsx
index 9e40ebccec..0277aa0ad3 100644
--- a/awx/ui_next/src/components/Lookup/MultiCredentialsLookup.jsx
+++ b/awx/ui_next/src/components/Lookup/MultiCredentialsLookup.jsx
@@ -1,22 +1,29 @@
import React from 'react';
+import { withRouter } from 'react-router-dom';
import PropTypes from 'prop-types';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { FormGroup, Tooltip } from '@patternfly/react-core';
import { QuestionCircleIcon as PFQuestionCircleIcon } from '@patternfly/react-icons';
import styled from 'styled-components';
-
import { CredentialsAPI, CredentialTypesAPI } from '@api';
-import Lookup from '@components/Lookup';
+import CategoryLookup from '@components/Lookup/CategoryLookup';
+import { getQSConfig, parseQueryString } from '@util/qs';
const QuestionCircleIcon = styled(PFQuestionCircleIcon)`
margin-left: 10px;
`;
+
+const QS_CONFIG = getQSConfig('credentials', {
+ page: 1,
+ page_size: 5,
+ order_by: 'name',
+});
+
function toggleCredentialSelection(credentialsToUpdate, newCredential) {
let newCredentialsList;
const isSelectedCredentialInState =
- credentialsToUpdate.filter(cred => cred.id === newCredential.id).length >
- 0;
+ credentialsToUpdate.filter(cred => cred.id === newCredential.id).length > 0;
if (isSelectedCredentialInState) {
newCredentialsList = credentialsToUpdate.filter(
@@ -31,6 +38,7 @@ function toggleCredentialSelection(credentialsToUpdate, newCredential) {
}
return newCredentialsList;
}
+
class MultiCredentialsLookup extends React.Component {
constructor(props) {
super(props);
@@ -48,6 +56,7 @@ class MultiCredentialsLookup extends React.Component {
componentDidMount() {
this.loadCredentialTypes();
+ this.loadCredentials();
}
async loadCredentialTypes() {
@@ -80,23 +89,38 @@ class MultiCredentialsLookup extends React.Component {
}
}
- async loadCredentials(params) {
+ async loadCredentials() {
+ const { history, onError } = this.props;
const { selectedCredentialType } = this.state;
+ const params = parseQueryString(QS_CONFIG, history.location.search);
params.credential_type = selectedCredentialType.id || 1;
- return CredentialsAPI.read(params);
+ try {
+ const { data } = await CredentialsAPI.read(params);
+ this.setState({
+ credentials: data.results,
+ count: data.count,
+ });
+ } catch (err) {
+ onError(err);
+ }
}
-
-
handleCredentialTypeSelect(value, type) {
const { credentialTypes } = this.state;
const selectedType = credentialTypes.filter(item => item.label === type);
- this.setState({ selectedCredentialType: selectedType[0] });
+ this.setState({ selectedCredentialType: selectedType[0] }, () => {
+ this.loadCredentials();
+ });
}
render() {
- const { selectedCredentialType, credentialTypes } = this.state;
- const { tooltip, i18n, credentials, onChange } = this.props;
+ const {
+ selectedCredentialType,
+ credentialTypes,
+ credentials,
+ count,
+ } = this.state;
+ const { tooltip, i18n, value, onChange } = this.props;
return (
{tooltip && (
@@ -105,7 +129,7 @@ class MultiCredentialsLookup extends React.Component {
)}
{credentialTypes && (
-
)}
@@ -137,7 +161,7 @@ class MultiCredentialsLookup extends React.Component {
MultiCredentialsLookup.propTypes = {
tooltip: PropTypes.string,
- credentials: PropTypes.arrayOf(
+ value: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number,
name: PropTypes.string,
@@ -152,8 +176,8 @@ MultiCredentialsLookup.propTypes = {
MultiCredentialsLookup.defaultProps = {
tooltip: '',
- credentials: [],
+ value: [],
};
export { MultiCredentialsLookup as _MultiCredentialsLookup };
-export default withI18n()(MultiCredentialsLookup);
+export default withI18n()(withRouter(MultiCredentialsLookup));
diff --git a/awx/ui_next/src/components/Lookup/index.js b/awx/ui_next/src/components/Lookup/index.js
index cde48e2bcd..5e2959c00e 100644
--- a/awx/ui_next/src/components/Lookup/index.js
+++ b/awx/ui_next/src/components/Lookup/index.js
@@ -1,4 +1,5 @@
export { default } from './Lookup';
+export { default as CategoryLookup } from './CategoryLookup';
export { default as InstanceGroupsLookup } from './InstanceGroupsLookup';
export { default as InventoryLookup } from './InventoryLookup';
export { default as ProjectLookup } from './ProjectLookup';
diff --git a/awx/ui_next/src/components/SelectedList/SelectedList.jsx b/awx/ui_next/src/components/SelectedList/SelectedList.jsx
index 8d7c716ef9..683404d35f 100644
--- a/awx/ui_next/src/components/SelectedList/SelectedList.jsx
+++ b/awx/ui_next/src/components/SelectedList/SelectedList.jsx
@@ -28,6 +28,7 @@ class SelectedList extends Component {
isReadOnly,
isCredentialList,
} = this.props;
+ // TODO: replace isCredentialList with renderChip ?
const chips = isCredentialList
? selected.map(item => (
null,
isReadOnly: false,
+ isCredentialList: false,
};
export default SelectedList;
diff --git a/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx b/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx
index 846d7f4fb8..67e94ced20 100644
--- a/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx
+++ b/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx
@@ -317,7 +317,7 @@ class JobTemplateForm extends Component {
fieldId="template-credentials"
render={({ field }) => (
setFieldValue('credentials', newCredentials)
}