Skip to content

Commit

Permalink
Merge pull request #138 from splunk/feature/update-singleselect-and-m…
Browse files Browse the repository at this point in the history
…ultiselect

Feature/update singleselect and multiselect
  • Loading branch information
mchavda-splunk authored Mar 23, 2021
2 parents 51f5646 + b57f889 commit aaa0667
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -201,21 +201,21 @@ class BaseFormView extends PureComponent {
}

if (!error) {
const params = new URLSearchParams();
const body = new URLSearchParams();

Object.keys(datadict).forEach((key) => {
if (datadict[key]) {
params.append(key, datadict[key]);
if (datadict[key] != null) {
body.append(key, datadict[key]);
}
});

if (this.props.mode === MODE_EDIT) {
params.delete('name');
body.delete('name');
}

axiosCallWrapper({
serviceName: this.endpoint,
params,
body,
customHeaders: { 'Content-Type': 'application/x-www-form-urlencoded' },
method: 'post',
handleError: false,
Expand Down Expand Up @@ -256,7 +256,7 @@ class BaseFormView extends PureComponent {
const changes = {};
if (this.dependencyMap.has(field)) {
const value = this.dependencyMap.get(field);
for (const loadField in value) {
Object.keys(value).forEach((loadField) => {
const data = {};
let load = true;

Expand All @@ -265,19 +265,23 @@ class BaseFormView extends PureComponent {
return e.field === dependency;
}).required;

const value =
dependency == field ? targetValue : this.state.data[dependency]['value'];
if (required && !value) {
const currentValue =
dependency === field ? targetValue : this.state.data[dependency].value;
if (required && !currentValue) {
load = false;
data[dependency] = null;
} else {
data[dependency] = value;
data[dependency] = currentValue;
}
});

if (load) {
changes[loadField] = { dependencyValues: { $set: data } };
changes[loadField] = {
dependencyValues: { $set: data },
value: { $set: null },
};
}
}
});
}
changes[field] = { value: { $set: targetValue } };

Expand All @@ -296,7 +300,7 @@ class BaseFormView extends PureComponent {

addCustomValidator = (field, validatorFunc) => {
const index = this.entities.findIndex((x) => x.field === field);
const validator = [{ type: 'custom', validatorFunc: validatorFunc }];
const validator = [{ type: 'custom', validatorFunc }];
this.entities[index].validators = validator;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,135 @@
import React from 'react';
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import Multiselect from '@splunk/react-ui/Multiselect';
import { _ } from '@splunk/ui-utils/i18n';
import axios from 'axios';
import { axiosCallWrapper } from '../util/axiosCallWrapper';
import { filterResponse } from '../util/util';

function MultiInputComponent(props) {
const { field, disabled = false, value, controlOptions, ...restProps } = props;
const { items, placeholder, createSearchChoice, delimiter = ',' } = controlOptions;
const {
field,
disabled = false,
error = false,
value,
controlOptions,
dependencyValues,
...restProps
} = props;
const {
endpointUrl,
denyList,
allowList,
items,
dependencies,
referenceName,
placeholder,
createSearchChoice,
labelField,
delimiter = ',',
} = controlOptions;

function handleChange(e, { values }) {
restProps.handleChange(field, values.join(delimiter));
}

function generateOptions(items) {
return items.map((item) => (
<Multiselect.Option label={item.label} value={item.value} key={item.value} />
));
}

const [loading, setLoading] = useState(false);
const [options, setOptions] = useState(null);

useEffect(() => {
if (items) {
setOptions(generateOptions(items));
return;
}

let current = true;
const source = axios.CancelToken.source();

const options = { CancelToken: source.token, handleError: true };
if (referenceName) {
options.serviceName = referenceName;
} else if (endpointUrl) {
options.endpointUrl = endpointUrl;
}

if (dependencyValues) {
options.params = dependencyValues;
}
if (!dependencies || dependencyValues) {
setLoading(true);
axiosCallWrapper(options)
.then((response) => {
if (current) {
setOptions(
generateOptions(
filterResponse(response.data.entry, labelField, allowList, denyList)
)
);
setLoading(false);
}
})
.catch((error) => {
if (current) {
setLoading(false);
}
});
}
return () => {
source.cancel('Operation canceled.');
current = false;
};
}, [dependencyValues]);

const effectiveDisabled = loading ? true : disabled;
const effectivePlaceholder = loading ? _('Loading') : placeholder;

const valueList = value ? value.split(delimiter) : [];

return (
<Multiselect
values={valueList}
error={error}
name={field}
placeholder={placeholder}
disabled={disabled}
placeholder={effectivePlaceholder}
disabled={effectiveDisabled}
allowNewValues={createSearchChoice}
onChange={handleChange}
inline
>
{items.map((item) => (
<Multiselect.Option label={item.label} value={item.value} key={item.value} />
))}
{options && options.length > 0 && options}
</Multiselect>
);
}

MultiInputComponent.propTypes = {
disabled: PropTypes.bool,
value: PropTypes.string,
error: PropTypes.bool,
handleChange: PropTypes.func.isRequired,
field: PropTypes.string,
dependencyValues: PropTypes.object,
controlOptions: PropTypes.shape({
delimiter: PropTypes.string,
placeholder: PropTypes.string,
createSearchChoice: PropTypes.bool,
referenceName: PropTypes.string,
dependencies: PropTypes.array,
endpointUrl: PropTypes.string,
denyList: PropTypes.string,
allowList: PropTypes.string,
labelField: PropTypes.string,
items: PropTypes.arrayOf(
PropTypes.shape({
label: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
})
).isRequired,
),
}),
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,41 @@
import React from 'react';
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import Select from '@splunk/react-ui/Select';
import { _ } from '@splunk/ui-utils/i18n';
import axios from 'axios';
import { axiosCallWrapper } from '../util/axiosCallWrapper';
import { filterResponse } from '../util/util';

function SingleInputComponent(props) {
const { field, disabled = false, value, controlOptions, ...restProps } = props;
const { autoCompleteFields=[] } = controlOptions;
const {
field,
disabled = false,
error = false,
value,
controlOptions,
dependencyValues,
...restProps
} = props;
const {
endpointUrl,
denyList,
allowList,
placeholder = _('Select a value'),
dependencies,
createSearchChoice,
referenceName,
disableSearch,
labelField,
autoCompleteFields,
} = controlOptions;

function handleChange(e, { value }) {
restProps.handleChange(field, value);
}

function generateOptions() {
function generateOptions(items) {
const data = [];
autoCompleteFields.forEach((item) => {
items.forEach((item) => {
if (item.value && item.label) {
data.push(<Select.Option label={item.label} value={item.value} key={item.value} />);
}
Expand All @@ -28,20 +51,89 @@ function SingleInputComponent(props) {
return data;
}

const [loading, setLoading] = useState(false);
const [options, setOptions] = useState(null);

useEffect(() => {
if (autoCompleteFields) {
setOptions(generateOptions(autoCompleteFields));
return;
}

let current = true;
const source = axios.CancelToken.source();

const options = { CancelToken: source.token, handleError: true };
if (referenceName) {
options.serviceName = referenceName;
} else if (endpointUrl) {
options.endpointUrl = endpointUrl;
}

if (dependencyValues) {
options.params = dependencyValues;
}
if (!dependencies || dependencyValues) {
setLoading(true);
axiosCallWrapper(options)
.then((response) => {
if (current) {
setOptions(
generateOptions(
filterResponse(response.data.entry, labelField, allowList, denyList)
)
);
setLoading(false);
}
})
.catch((error) => {
if (current) {
setLoading(false);
}
});
}
return () => {
source.cancel('Operation canceled.');
current = false;
};
}, [dependencyValues]);

const effectiveDisabled = loading ? true : disabled;
const effectivePlaceholder = loading ? _('Loading') : placeholder;

return (
<Select value={value} name={field} disabled={disabled} onChange={handleChange} style={{ "minWidth": "200px" }}>
{generateOptions()}
<Select
value={value}
name={field}
error={error}
placeholder={effectivePlaceholder}
disabled={effectiveDisabled}
onChange={handleChange}
inline
>
{options && options.length > 0 && options}
</Select>
);
}

SingleInputComponent.propTypes = {
disabled: PropTypes.bool,
value: PropTypes.string,
error: PropTypes.bool,
handleChange: PropTypes.func.isRequired,
field: PropTypes.string,
dependencyValues: PropTypes.object,
controlOptions: PropTypes.shape({
autoCompleteFields: PropTypes.array.isRequired,
autoCompleteFields: PropTypes.array,
endpointUrl: PropTypes.string,
denyList: PropTypes.string,
allowList: PropTypes.string,
placeholder: PropTypes.string,
dependencies: PropTypes.array,
createSearchChoice: PropTypes.bool, // TODO: Not supported yet
referenceName: PropTypes.string,
disableSearch: PropTypes.bool, // TODO: Not supported yet
labelField: PropTypes.string,
}),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ function TableWrapper({ page, serviceName, handleRequestModalOpen }) {
},
});
});
const params = new URLSearchParams();
params.append('disabled', !row.disabled);
const body = new URLSearchParams();
body.append('disabled', !row.disabled);

axiosCallWrapper({
serviceName: `${row.serviceName}/${row.name}`,
params,
body,
customHeaders: { 'Content-Type': 'application/x-www-form-urlencoded' },
method: 'post',
handleError: true,
Expand Down
Loading

0 comments on commit aaa0667

Please sign in to comment.