mirror of
https://github.com/ansible/awx.git
synced 2026-05-19 23:07:42 -02:30
fix credential chips in SelectedList, MultiCredential cleanup
This commit is contained in:
@@ -36,12 +36,12 @@ class AnsibleSelect extends React.Component {
|
|||||||
aria-label={i18n._(t`Select Input`)}
|
aria-label={i18n._(t`Select Input`)}
|
||||||
isValid={isValid}
|
isValid={isValid}
|
||||||
>
|
>
|
||||||
{data.map(datum => (
|
{data.map(option => (
|
||||||
<FormSelectOption
|
<FormSelectOption
|
||||||
key={datum.key}
|
key={option.id}
|
||||||
value={datum.value}
|
value={option.value}
|
||||||
label={datum.label}
|
label={option.label}
|
||||||
isDisabled={datum.isDisabled}
|
isDisabled={option.isDisabled}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</FormSelect>
|
</FormSelect>
|
||||||
@@ -49,6 +49,13 @@ class AnsibleSelect extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Option = shape({
|
||||||
|
id: oneOfType([string, number]).isRequired,
|
||||||
|
value: oneOfType([string, number]).isRequired,
|
||||||
|
label: string.isRequired,
|
||||||
|
isDisabled: bool,
|
||||||
|
});
|
||||||
|
|
||||||
AnsibleSelect.defaultProps = {
|
AnsibleSelect.defaultProps = {
|
||||||
data: [],
|
data: [],
|
||||||
isValid: true,
|
isValid: true,
|
||||||
@@ -56,7 +63,7 @@ AnsibleSelect.defaultProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
AnsibleSelect.propTypes = {
|
AnsibleSelect.propTypes = {
|
||||||
data: arrayOf(shape()),
|
data: arrayOf(Option),
|
||||||
id: string.isRequired,
|
id: string.isRequired,
|
||||||
isValid: bool,
|
isValid: bool,
|
||||||
onBlur: func,
|
onBlur: func,
|
||||||
|
|||||||
@@ -1,331 +0,0 @@
|
|||||||
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, 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);
|
|
||||||
return (
|
|
||||||
<Fragment>
|
|
||||||
<InputGroup onBlur={onBlur}>
|
|
||||||
<SearchButton
|
|
||||||
aria-label="Search"
|
|
||||||
id={id}
|
|
||||||
onClick={this.handleModalToggle}
|
|
||||||
variant={ButtonVariant.tertiary}
|
|
||||||
>
|
|
||||||
<SearchIcon />
|
|
||||||
</SearchButton>
|
|
||||||
<ChipHolder className="pf-c-form-control">
|
|
||||||
<ChipGroup>
|
|
||||||
{(multiple ? value : [value]).map(chip => (
|
|
||||||
<CredentialChip
|
|
||||||
key={chip.id}
|
|
||||||
onClick={() => this.removeItemAndSave(chip)}
|
|
||||||
isReadOnly={!canDelete}
|
|
||||||
credential={chip}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</ChipGroup>
|
|
||||||
</ChipHolder>
|
|
||||||
</InputGroup>
|
|
||||||
<Modal
|
|
||||||
className="awx-c-modal"
|
|
||||||
title={i18n._(t`Select ${header}`)}
|
|
||||||
isOpen={isModalOpen}
|
|
||||||
onClose={this.handleModalToggle}
|
|
||||||
actions={[
|
|
||||||
<Button
|
|
||||||
key="select"
|
|
||||||
variant="primary"
|
|
||||||
onClick={this.saveModal}
|
|
||||||
style={selectedItems.length === 0 ? { display: 'none' } : {}}
|
|
||||||
>
|
|
||||||
{i18n._(t`Select`)}
|
|
||||||
</Button>,
|
|
||||||
<Button
|
|
||||||
key="cancel"
|
|
||||||
variant="secondary"
|
|
||||||
onClick={this.handleModalToggle}
|
|
||||||
>
|
|
||||||
{i18n._(t`Cancel`)}
|
|
||||||
</Button>,
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{selectCategoryOptions && selectCategoryOptions.length > 0 && (
|
|
||||||
<ToolbarItem css=" display: flex; align-items: center;">
|
|
||||||
<span css="flex: 0 0 25%;">Selected Category</span>
|
|
||||||
<VerticalSeperator />
|
|
||||||
<AnsibleSelect
|
|
||||||
css="flex: 1 1 75%;"
|
|
||||||
id="multiCredentialsLookUp-select"
|
|
||||||
label="Selected Category"
|
|
||||||
data={selectCategoryOptions}
|
|
||||||
value={selectedCategory.label}
|
|
||||||
onChange={selectCategory}
|
|
||||||
/>
|
|
||||||
</ToolbarItem>
|
|
||||||
)}
|
|
||||||
{selectedItems.length > 0 && (
|
|
||||||
<SelectedList
|
|
||||||
label={i18n._(t`Selected`)}
|
|
||||||
selected={selectedItems}
|
|
||||||
showOverflowAfter={5}
|
|
||||||
onRemove={this.removeItem}
|
|
||||||
isReadOnly={!canDelete}
|
|
||||||
isCredentialList={
|
|
||||||
selectCategoryOptions && selectCategoryOptions.length > 0
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<PaginatedDataList
|
|
||||||
items={items}
|
|
||||||
itemCount={count}
|
|
||||||
pluralizedItemName={lookupHeader}
|
|
||||||
qsConfig={qsConfig}
|
|
||||||
toolbarColumns={columns}
|
|
||||||
renderItem={item => (
|
|
||||||
<CheckboxListItem
|
|
||||||
key={item.id}
|
|
||||||
itemId={item.id}
|
|
||||||
name={multiple ? item.name : name}
|
|
||||||
label={item.name}
|
|
||||||
isSelected={selectedItems.some(i => i.id === item.id)}
|
|
||||||
onSelect={() => this.addItem(item)}
|
|
||||||
isRadio={
|
|
||||||
!multiple ||
|
|
||||||
(selectCategoryOptions &&
|
|
||||||
selectCategoryOptions.length &&
|
|
||||||
selectedCategory.value !== 'Vault')
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
renderToolbar={props => <DataListToolbar {...props} fillWidth />}
|
|
||||||
showPageSizeOptions={false}
|
|
||||||
/>
|
|
||||||
{error ? <div>error: {error.message}</div> : ''}
|
|
||||||
</Modal>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { Fragment, useState, useEffect } from 'react';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
@@ -12,7 +12,6 @@ import VerticalSeperator from '@components/VerticalSeparator';
|
|||||||
import { getQSConfig, parseQueryString } from '@util/qs';
|
import { getQSConfig, parseQueryString } from '@util/qs';
|
||||||
import Lookup from './NewLookup';
|
import Lookup from './NewLookup';
|
||||||
import SelectList from './shared/SelectList';
|
import SelectList from './shared/SelectList';
|
||||||
import multiCredentialReducer from './shared/multiCredentialReducer';
|
|
||||||
|
|
||||||
const QS_CONFIG = getQSConfig('credentials', {
|
const QS_CONFIG = getQSConfig('credentials', {
|
||||||
page: 1,
|
page: 1,
|
||||||
@@ -20,50 +19,10 @@ const QS_CONFIG = getQSConfig('credentials', {
|
|||||||
order_by: 'name',
|
order_by: 'name',
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: move into reducer
|
|
||||||
function toggleCredentialSelection(credentialsToUpdate, newCredential) {
|
|
||||||
let newCredentialsList;
|
|
||||||
const isSelectedCredentialInState =
|
|
||||||
credentialsToUpdate.filter(cred => cred.id === newCredential.id).length > 0;
|
|
||||||
|
|
||||||
if (isSelectedCredentialInState) {
|
|
||||||
newCredentialsList = credentialsToUpdate.filter(
|
|
||||||
cred => cred.id !== newCredential.id
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
newCredentialsList = credentialsToUpdate.filter(
|
|
||||||
credential =>
|
|
||||||
credential.kind === 'vault' || credential.kind !== newCredential.kind
|
|
||||||
);
|
|
||||||
newCredentialsList = [...newCredentialsList, newCredential];
|
|
||||||
}
|
|
||||||
return newCredentialsList;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadCredentialTypes() {
|
async function loadCredentialTypes() {
|
||||||
const { data } = await CredentialTypesAPI.read();
|
const { data } = await CredentialTypesAPI.read();
|
||||||
const acceptableTypes = ['machine', 'cloud', 'net', 'ssh', 'vault'];
|
const acceptableTypes = ['machine', 'cloud', 'net', 'ssh', 'vault'];
|
||||||
const credentialTypes = [];
|
return data.results.filter(type => acceptableTypes.includes(type.kind));
|
||||||
// TODO: cleanup
|
|
||||||
data.results.forEach(cred => {
|
|
||||||
acceptableTypes.forEach(aT => {
|
|
||||||
if (aT === cred.kind) {
|
|
||||||
// This object has several repeated values as some of it's children
|
|
||||||
// require different field values.
|
|
||||||
cred = {
|
|
||||||
id: cred.id,
|
|
||||||
key: cred.id,
|
|
||||||
kind: cred.kind,
|
|
||||||
type: cred.namespace,
|
|
||||||
value: cred.name,
|
|
||||||
label: cred.name,
|
|
||||||
isDisabled: false,
|
|
||||||
};
|
|
||||||
credentialTypes.push(cred);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return credentialTypes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadCredentials(params, selectedCredentialTypeId) {
|
async function loadCredentials(params, selectedCredentialTypeId) {
|
||||||
@@ -78,13 +37,16 @@ function MultiCredentialsLookup(props) {
|
|||||||
const [selectedType, setSelectedType] = useState(null);
|
const [selectedType, setSelectedType] = useState(null);
|
||||||
const [credentials, setCredentials] = useState([]);
|
const [credentials, setCredentials] = useState([]);
|
||||||
const [credentialsCount, setCredentialsCount] = useState(0);
|
const [credentialsCount, setCredentialsCount] = useState(0);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const types = await loadCredentialTypes();
|
const types = await loadCredentialTypes();
|
||||||
setCredentialTypes(types);
|
setCredentialTypes(types);
|
||||||
setSelectedType(types[0]);
|
setSelectedType(
|
||||||
|
types.find(type => type.name === 'Machine') || types[0]
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
onError(err);
|
onError(err);
|
||||||
}
|
}
|
||||||
@@ -98,10 +60,12 @@ function MultiCredentialsLookup(props) {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const params = parseQueryString(QS_CONFIG, history.location.search);
|
const params = parseQueryString(QS_CONFIG, history.location.search);
|
||||||
|
setIsLoading(true);
|
||||||
const { results, count } = await loadCredentials(
|
const { results, count } = await loadCredentials(
|
||||||
params,
|
params,
|
||||||
selectedType.id
|
selectedType.id
|
||||||
);
|
);
|
||||||
|
setIsLoading(false);
|
||||||
setCredentials(results);
|
setCredentials(results);
|
||||||
setCredentialsCount(count);
|
setCredentialsCount(count);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -111,29 +75,29 @@ function MultiCredentialsLookup(props) {
|
|||||||
}, [selectedType]);
|
}, [selectedType]);
|
||||||
|
|
||||||
const isMultiple = selectedType && selectedType.value === 'Vault';
|
const isMultiple = selectedType && selectedType.value === 'Vault';
|
||||||
|
const renderChip = ({ item, removeItem, canDelete }) => (
|
||||||
|
<CredentialChip
|
||||||
|
key={item.id}
|
||||||
|
onClick={() => removeItem(item)}
|
||||||
|
isReadOnly={!canDelete}
|
||||||
|
credential={item}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormGroup label={i18n._(t`Credentials`)} fieldId="multiCredential">
|
<FormGroup label={i18n._(t`Credentials`)} fieldId="multiCredential">
|
||||||
{tooltip && <FieldTooltip content={tooltip} />}
|
{tooltip && <FieldTooltip content={tooltip} />}
|
||||||
<Lookup
|
<Lookup
|
||||||
reducer={multiCredentialReducer}
|
|
||||||
onToggleItem={toggleCredentialSelection}
|
|
||||||
id="multiCredential"
|
id="multiCredential"
|
||||||
lookupHeader={i18n._(t`Credentials`)}
|
lookupHeader={i18n._(t`Credentials`)}
|
||||||
value={value}
|
value={value}
|
||||||
multiple
|
multiple
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
renderItemChip={({ item, removeItem, canDelete }) => (
|
renderItemChip={renderChip}
|
||||||
<CredentialChip
|
|
||||||
key={item.id}
|
|
||||||
onClick={() => removeItem(item)}
|
|
||||||
isReadOnly={!canDelete}
|
|
||||||
credential={item}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
renderSelectList={({ state, dispatch, canDelete }) => {
|
renderSelectList={({ state, dispatch, canDelete }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<Fragment>
|
||||||
{credentialTypes && credentialTypes.length > 0 && (
|
{credentialTypes && credentialTypes.length > 0 && (
|
||||||
<ToolbarItem css=" display: flex; align-items: center;">
|
<ToolbarItem css=" display: flex; align-items: center;">
|
||||||
<div css="flex: 0 0 25%;">{i18n._(t`Selected Category`)}</div>
|
<div css="flex: 0 0 25%;">{i18n._(t`Selected Category`)}</div>
|
||||||
@@ -142,11 +106,16 @@ function MultiCredentialsLookup(props) {
|
|||||||
css="flex: 1 1 75%;"
|
css="flex: 1 1 75%;"
|
||||||
id="multiCredentialsLookUp-select"
|
id="multiCredentialsLookUp-select"
|
||||||
label={i18n._(t`Selected Category`)}
|
label={i18n._(t`Selected Category`)}
|
||||||
data={credentialTypes}
|
data={credentialTypes.map(type => ({
|
||||||
value={selectedType && selectedType.label}
|
id: type.id,
|
||||||
onChange={(e, label) => {
|
value: type.id,
|
||||||
|
label: type.name,
|
||||||
|
isDisabled: false,
|
||||||
|
}))}
|
||||||
|
value={selectedType && selectedType.id}
|
||||||
|
onChange={(e, id) => {
|
||||||
setSelectedType(
|
setSelectedType(
|
||||||
credentialTypes.find(o => o.label === label)
|
credentialTypes.find(o => o.id === parseInt(id, 10))
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -183,8 +152,9 @@ function MultiCredentialsLookup(props) {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
deselectItem={item => dispatch({ type: 'DESELECT_ITEM', item })}
|
deselectItem={item => dispatch({ type: 'DESELECT_ITEM', item })}
|
||||||
|
renderItemChip={renderChip}
|
||||||
/>
|
/>
|
||||||
</>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -50,13 +50,9 @@ const ChipHolder = styled.div`
|
|||||||
function Lookup(props) {
|
function Lookup(props) {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
// items,
|
|
||||||
// count,
|
|
||||||
header,
|
header,
|
||||||
// name,
|
|
||||||
onChange,
|
onChange,
|
||||||
onBlur,
|
onBlur,
|
||||||
// columns,
|
|
||||||
value,
|
value,
|
||||||
multiple,
|
multiple,
|
||||||
required,
|
required,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
export { default } from './Lookup';
|
export { default } from './Lookup';
|
||||||
export { default as CategoryLookup } from './CategoryLookup';
|
|
||||||
export { default as InstanceGroupsLookup } from './InstanceGroupsLookup';
|
export { default as InstanceGroupsLookup } from './InstanceGroupsLookup';
|
||||||
export { default as InventoryLookup } from './InventoryLookup';
|
export { default as InventoryLookup } from './InventoryLookup';
|
||||||
export { default as ProjectLookup } from './ProjectLookup';
|
export { default as ProjectLookup } from './ProjectLookup';
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ function SelectList({
|
|||||||
readOnly,
|
readOnly,
|
||||||
selectItem,
|
selectItem,
|
||||||
deselectItem,
|
deselectItem,
|
||||||
|
renderItemChip,
|
||||||
i18n,
|
i18n,
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
@@ -39,6 +40,7 @@ function SelectList({
|
|||||||
showOverflowAfter={5}
|
showOverflowAfter={5}
|
||||||
onRemove={item => deselectItem(item)}
|
onRemove={item => deselectItem(item)}
|
||||||
isReadOnly={readOnly}
|
isReadOnly={readOnly}
|
||||||
|
renderItemChip={renderItemChip}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<PaginatedDataList
|
<PaginatedDataList
|
||||||
@@ -78,9 +80,11 @@ SelectList.propTypes = {
|
|||||||
qsConfig: QSConfig.isRequired,
|
qsConfig: QSConfig.isRequired,
|
||||||
selectItem: func.isRequired,
|
selectItem: func.isRequired,
|
||||||
deselectItem: func.isRequired,
|
deselectItem: func.isRequired,
|
||||||
|
renderItemChip: func,
|
||||||
};
|
};
|
||||||
SelectList.defaultProps = {
|
SelectList.defaultProps = {
|
||||||
multiple: false,
|
multiple: false,
|
||||||
|
renderItemChip: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withI18n()(SelectList);
|
export default withI18n()(SelectList);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Split as PFSplit, SplitItem } from '@patternfly/react-core';
|
import { Split as PFSplit, SplitItem } from '@patternfly/react-core';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { ChipGroup, Chip, CredentialChip } from '../Chip';
|
import { ChipGroup, Chip } from '../Chip';
|
||||||
import VerticalSeparator from '../VerticalSeparator';
|
import VerticalSeparator from '../VerticalSeparator';
|
||||||
|
|
||||||
const Split = styled(PFSplit)`
|
const Split = styled(PFSplit)`
|
||||||
@@ -26,35 +26,31 @@ class SelectedList extends Component {
|
|||||||
onRemove,
|
onRemove,
|
||||||
displayKey,
|
displayKey,
|
||||||
isReadOnly,
|
isReadOnly,
|
||||||
isCredentialList,
|
renderItemChip,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
// TODO: replace isCredentialList with renderChip ?
|
|
||||||
const chips = isCredentialList
|
const renderChip =
|
||||||
? selected.map(item => (
|
renderItemChip ||
|
||||||
<CredentialChip
|
(({ item, removeItem }) => (
|
||||||
key={item.id}
|
<Chip key={item.id} onClick={removeItem} isReadOnly={isReadOnly}>
|
||||||
isReadOnly={isReadOnly}
|
{item[displayKey]}
|
||||||
onClick={() => onRemove(item)}
|
</Chip>
|
||||||
credential={item}
|
));
|
||||||
>
|
|
||||||
{item[displayKey]}
|
|
||||||
</CredentialChip>
|
|
||||||
))
|
|
||||||
: selected.map(item => (
|
|
||||||
<Chip
|
|
||||||
key={item.id}
|
|
||||||
isReadOnly={isReadOnly}
|
|
||||||
onClick={() => onRemove(item)}
|
|
||||||
>
|
|
||||||
{item[displayKey]}
|
|
||||||
</Chip>
|
|
||||||
));
|
|
||||||
return (
|
return (
|
||||||
<Split>
|
<Split>
|
||||||
<SplitLabelItem>{label}</SplitLabelItem>
|
<SplitLabelItem>{label}</SplitLabelItem>
|
||||||
<VerticalSeparator />
|
<VerticalSeparator />
|
||||||
<SplitItem>
|
<SplitItem>
|
||||||
<ChipGroup numChips={5}>{chips}</ChipGroup>
|
<ChipGroup numChips={5}>
|
||||||
|
{selected.map(item =>
|
||||||
|
renderChip({
|
||||||
|
item,
|
||||||
|
removeItem: () => onRemove(item),
|
||||||
|
canDelete: !isReadOnly,
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</ChipGroup>
|
||||||
</SplitItem>
|
</SplitItem>
|
||||||
</Split>
|
</Split>
|
||||||
);
|
);
|
||||||
@@ -67,7 +63,7 @@ SelectedList.propTypes = {
|
|||||||
onRemove: PropTypes.func,
|
onRemove: PropTypes.func,
|
||||||
selected: PropTypes.arrayOf(PropTypes.object).isRequired,
|
selected: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
isReadOnly: PropTypes.bool,
|
isReadOnly: PropTypes.bool,
|
||||||
isCredentialList: PropTypes.bool,
|
renderItemChip: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
SelectedList.defaultProps = {
|
SelectedList.defaultProps = {
|
||||||
@@ -75,7 +71,7 @@ SelectedList.defaultProps = {
|
|||||||
label: 'Selected',
|
label: 'Selected',
|
||||||
onRemove: () => null,
|
onRemove: () => null,
|
||||||
isReadOnly: false,
|
isReadOnly: false,
|
||||||
isCredentialList: false,
|
renderItemChip: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SelectedList;
|
export default SelectedList;
|
||||||
|
|||||||
Reference in New Issue
Block a user