Allow users to select multiple platforms for pack query (#1074)

This commit is contained in:
Kyle Knight
2017-01-24 18:52:48 -05:00
committed by Jason Meller
parent 8749633b85
commit 3ec6b10324
8 changed files with 106 additions and 12 deletions
@@ -11,7 +11,7 @@ import validate from 'components/forms/ConfigurePackQueryForm/validate';
const baseClass = 'configure-pack-query-form';
const fieldNames = ['query_id', 'interval', 'logging_type', 'platform', 'version'];
const platformOptions = [
{ label: 'All', value: '' },
{ label: 'All', value: 'all' },
{ label: 'Windows', value: 'windows' },
{ label: 'Linux', value: 'linux' },
{ label: 'macOS', value: 'darwin' },
@@ -73,6 +73,7 @@ class ConfigurePackQueryForm extends Component {
options={platformOptions}
placeholder="- - -"
label="Platform"
multi
wrapperClassName={`${baseClass}__form-field ${baseClass}__form-field--platform`}
/>
<Dropdown
@@ -44,7 +44,7 @@ describe('ConfigurePackQueryForm - component', () => {
expect(spy).toHaveBeenCalledWith({
interval: 123,
logging_type: 'differential',
platform: '',
platform: 'all',
query_id: 1,
version: '',
});
@@ -16,11 +16,12 @@ class Dropdown extends Component {
error: PropTypes.string,
label: PropTypes.oneOfType([PropTypes.array, PropTypes.string]),
labelClassName: PropTypes.string,
multi: PropTypes.bool,
name: PropTypes.string,
onChange: PropTypes.func,
options: PropTypes.arrayOf(dropdownOptionInterface).isRequired,
placeholder: PropTypes.oneOfType([PropTypes.array, PropTypes.string]),
value: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.array, PropTypes.string]),
wrapperClassName: PropTypes.string,
};
@@ -28,14 +29,19 @@ class Dropdown extends Component {
onChange: noop,
clearable: false,
disabled: false,
multi: false,
name: 'targets',
placeholder: 'Select One...',
};
handleChange = ({ value }) => {
const { onChange } = this.props;
handleChange = (selected) => {
const { multi, onChange } = this.props;
return onChange(value);
if (multi) {
return onChange(selected.map(obj => obj.value).join(','));
}
return onChange(selected.value);
};
renderLabel = () => {
@@ -62,7 +68,7 @@ class Dropdown extends Component {
render () {
const { handleChange } = this;
const { error, className, clearable, disabled, name, options, placeholder, value, wrapperClassName } = this.props;
const { error, className, clearable, disabled, multi, name, options, placeholder, value, wrapperClassName } = this.props;
const formFieldProps = pick(this.props, ['hint', 'label', 'error', 'name']);
const selectClasses = classnames(className, `${baseClass}__select`, {
@@ -75,6 +81,7 @@ class Dropdown extends Component {
className={selectClasses}
clearable={clearable}
disabled={disabled}
multi={multi}
name={`${name}-select`}
onChange={handleChange}
options={options}
@@ -36,6 +36,47 @@
.Select-value {
font-size: $medium;
border-radius: 2px;
background-color: #fff;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12), 0 2px 0 0 #b8c2e3;
border: solid 1px #b8c2e3;
.Select-value-icon {
border: 0;
float: right;
visibility: hidden;
position: relative;
line-height: 28px;
width: 25px;
padding: 0;
margin: 0 5px;
&::after {
@extend %kolidecon;
@include transition(color 150ms ease-in-out);
@include transform(translate(-50%, -50%));
content: '\f036';
position: absolute;
top: 50%;
left: 50%;
visibility: visible;
font-size: 16px;
color: $accent-dark;
}
&:hover {
&::after {
color: $accent-text;
}
}
}
.Select-value-label {
font-size: 16px;
font-weight: $normal;
color: $text-dark;
line-height: 28px;
}
}
&--single {
@@ -124,6 +165,21 @@
}
&.Select--multi {
.Select-control {
@include display(flex);
@include align-items(center);
height: auto;
min-height: 40px;
}
.Select-multi-value-wrapper {
@include flex-grow(1);
}
.Select-arrow-zone {
display: block;
}
.Select-option {
&.is-disabled {
color: $text-dark;
@@ -135,5 +191,19 @@
}
}
}
.Select-value {
margin-top: 3px;
}
.Select-value-label {
padding: 0 0 0 1rem;
}
&.has-value {
.Select-multi-value-wrapper {
padding: 5px 0;
}
}
}
}
@@ -19,9 +19,10 @@ export class PlatformIcon extends Component {
const { className, name, fw, size, title } = this.props;
const iconClasses = classnames(baseClass, className);
const iconName = platformIconClass(name);
const properNameCase = name.charAt(0).toUpperCase() + name.slice(1);
if (!iconName) {
return <span className={iconClasses}>{name || 'All'}</span>;
return <span className={iconClasses}>{properNameCase || 'All'}</span>;
}
return (
@@ -42,10 +42,21 @@ class ScheduledQueriesListItem extends Component {
return 'bold-plus';
}
renderPlatformIcon = () => {
const { scheduledQuery: { platform } } = this.props;
const platformArr = platform.split(',');
if (platformArr.includes('all')) {
return <PlatformIcon name="" />;
}
return platformArr.map((pltf, idx) => <PlatformIcon name={pltf} key={`${idx}-${pltf}`} />);
}
render () {
const { checked, disabled, scheduledQuery } = this.props;
const { onCheck } = this;
const { id, name, interval, platform, version } = scheduledQuery;
const { onCheck, renderPlatformIcon } = this;
const { id, name, interval, version } = scheduledQuery;
const { loggingTypeString } = this;
return (
@@ -60,7 +71,7 @@ class ScheduledQueriesListItem extends Component {
</td>
<td className="scheduled-queries-list__query-name">{name}</td>
<td>{interval}</td>
<td><PlatformIcon name={platform} /></td>
<td>{renderPlatformIcon()}</td>
<td>{version ? `${version}+` : 'Any'}</td>
<td><Icon name={loggingTypeString()} /></td>
</tr>
@@ -93,7 +93,7 @@ class SearchPackQuery extends Component {
return (
<div className={`${baseClass}__description`}>
<h2>description</h2>
<p>{selectedQuery.description}</p>
<p>{selectedQuery.description || <em>No description available.</em>}</p>
</div>
);
}
@@ -160,6 +160,10 @@ export class EditPackPage extends Component {
}
handleConfigurePackQuerySubmit = (formData) => {
if (formData.platform.indexOf('all') !== -1) {
formData.platform = '';
}
const { create } = scheduledQueryActions;
const { dispatch, packID } = this.props;
const scheduledQueryData = {