Fixing bug in Query Table sidebar dropdown (#691)

This commit is contained in:
Kyle Knight
2016-12-22 13:26:18 -06:00
committed by GitHub
parent f4a830175a
commit 8f07fe2ed2
39 changed files with 150 additions and 46 deletions
@@ -2,7 +2,7 @@ import React, { Component, PropTypes } from 'react';
import { noop } from 'lodash';
import classnames from 'classnames';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
class Rocker extends Component {
@@ -1,6 +1,6 @@
import React, { Component, PropTypes } from 'react';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import Button from 'components/buttons/Button';
import Dropdown from 'components/forms/fields/Dropdown';
import Form from 'components/forms/Form';
@@ -3,7 +3,7 @@ import classnames from 'classnames';
import Button from 'components/buttons/Button';
import formDataInterface from 'interfaces/registration_form_data';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import Checkbox from 'components/forms/fields/Checkbox';
const baseClass = 'confirm-user-reg';
@@ -1,7 +1,7 @@
import React, { PropTypes } from 'react';
import classnames from 'classnames';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import InputField from '../InputField';
const baseClass = 'input-icon-field';
@@ -4,7 +4,7 @@ import AceEditor from 'react-ace';
import classnames from 'classnames';
import hostHelpers from 'components/hosts/HostDetails/helpers';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import targetInterface from 'interfaces/target';
const baseClass = 'target-details';
@@ -1,6 +1,6 @@
import React from 'react';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import targetInterface from 'interfaces/target';
const baseClass = 'target-option';
@@ -1,7 +1,7 @@
import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import targetInterface from 'interfaces/target';
import TargetIcon from './TargetIcon';
@@ -3,7 +3,7 @@ import { noop } from 'lodash';
import EllipsisMenu from 'components/buttons/EllipsisMenu';
import hostInterface from 'interfaces/host';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import { humanMemory, humanUptime, platformIconClass } from './helpers';
const baseClass = 'host-details';
@@ -1,7 +1,7 @@
import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import hostInterface from 'interfaces/host';
import { platformIconClass, statusIconClass } from 'utilities/icon_class';
@@ -0,0 +1,59 @@
import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
import Icon from 'components/icons/Icon';
const baseClass = 'platform-icon';
export class PlatformIcon extends Component {
static propTypes = {
className: PropTypes.string,
fw: PropTypes.bool,
name: PropTypes.string.isRequired,
size: PropTypes.string,
title: PropTypes.string,
};
findIcon = () => {
const { name } = this.props;
switch (name.toLowerCase()) {
case 'macos': return 'apple';
case 'mac os x': return 'apple';
case 'mac osx': return 'apple';
case 'mac os': return 'apple';
case 'darwin': return 'apple';
case 'centos': return 'centos';
case 'centos linux': return 'centos';
case 'ubuntu': return 'ubuntu';
case 'ubuntu linux': return 'ubuntu';
case 'linux': return 'linux';
case 'windows': return 'windows';
case 'ms windows': return 'windows';
default: return false;
}
};
render () {
const { findIcon } = this;
const { className, fw, name, size, title } = this.props;
const iconClasses = classnames(baseClass, className);
const iconName = findIcon();
if (!iconName) {
return <span className={iconClasses}>{name}</span>;
}
return (
<Icon
className={iconClasses}
fw={fw}
name={iconName}
size={size}
title={title}
/>
);
}
}
export default PlatformIcon;
@@ -0,0 +1,19 @@
import React from 'react';
import expect from 'expect';
import { mount } from 'enzyme';
import PlatformIcon from './PlatformIcon';
describe('PlatformIcon - component', () => {
it('renders', () => {
expect(mount(<PlatformIcon name="linux" />).length).toEqual(1);
});
it('renders text if no icon', () => {
const component = mount(<PlatformIcon name="blah" />);
expect(component.find('span').length).toEqual(1);
expect(component.text()).toInclude('blah');
expect(component.find('Icon').length).toEqual(0);
});
});
@@ -0,0 +1 @@
export default from './PlatformIcon';
+1 -1
View File
@@ -1,7 +1,7 @@
import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
const baseClass = 'modal';
@@ -3,7 +3,7 @@ import { noop } from 'lodash';
import Button from 'components/buttons/Button';
import EditPackForm from 'components/forms/packs/EditPackForm';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import packInterface from 'interfaces/pack';
import SelectTargetsDropdown from 'components/forms/fields/SelectTargetsDropdown';
@@ -3,7 +3,7 @@ import classnames from 'classnames';
import { includes, size } from 'lodash';
import queryInterface from 'interfaces/query';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import QueriesListItem from 'components/queries/QueriesList/QueriesListItem';
import Checkbox from 'components/forms/fields/Checkbox';
@@ -1,9 +1,9 @@
import React, { Component, PropTypes } from 'react';
import Checkbox from 'components/forms/fields/Checkbox';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import PlatformIcon from 'components/icons/PlatformIcon';
import { isEqual } from 'lodash';
import { platformIconClass } from 'utilities/icon_class';
import scheduledQueryInterface from 'interfaces/scheduled_query';
class QueriesListItem extends Component {
@@ -60,7 +60,7 @@ class QueriesListItem extends Component {
</td>
<td>{name}</td>
<td>{interval}</td>
<td><Icon name={platformIconClass(platform)} /></td>
<td><PlatformIcon name={platform} /></td>
<td>{version}</td>
<td><Icon name={loggingTypeString()} /></td>
</tr>
@@ -13,7 +13,7 @@ describe('QueriesListItem - component', () => {
const component = mount(<QueriesListItem checked={false} onSelect={noop} scheduledQuery={scheduledQueryStub} />);
expect(component.text()).toInclude(scheduledQueryStub.name);
expect(component.text()).toInclude(scheduledQueryStub.interval);
expect(component.find('.kolidecon-apple').length).toEqual(1);
expect(component.find('PlatformIcon').length).toEqual(1);
});
it('renders a Checkbox component', () => {
@@ -4,7 +4,7 @@ import { get, keys, omit, values } from 'lodash';
import campaignInterface from 'interfaces/campaign';
import filterArrayByHash from 'utilities/filter_array_by_hash';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import InputField from 'components/forms/fields/InputField';
import ProgressBar from 'components/ProgressBar';
@@ -1,7 +1,7 @@
import React, { Component, PropTypes } from 'react';
import { filter } from 'lodash';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import InputField from 'components/forms/fields/InputField';
import labelInterface from 'interfaces/label';
import PanelGroup from 'components/side_panels/HostSidePanel/PanelGroup';
@@ -50,18 +50,21 @@ class HostSidePanel extends Component {
groupItems={allHostLabels}
onLabelClick={onLabelClick}
selectedLabel={selectedLabel}
type="all-hosts"
/>
<hr className={`${baseClass}__hr`} />
<PanelGroup
groupItems={hostStatusLabels}
onLabelClick={onLabelClick}
selectedLabel={selectedLabel}
type="status"
/>
<hr className={`${baseClass}__hr`} />
<PanelGroup
groupItems={hostPlatformLabels}
onLabelClick={onLabelClick}
selectedLabel={selectedLabel}
type="platform"
/>
<hr className={`${baseClass}__hr`} />
<div className={`${baseClass}__panel-group-item`}>
@@ -82,6 +85,7 @@ class HostSidePanel extends Component {
groupItems={customLabels}
onLabelClick={onLabelClick}
selectedLabel={selectedLabel}
type="label"
/>
<hr className={`${baseClass}__hr`} />
<button className={`${baseClass}__add-label-btn button button--unstyled`} onClick={onAddLabelClick}>
@@ -9,6 +9,7 @@ class PanelGroup extends Component {
groupItems: PropTypes.arrayOf(labelInterface),
onLabelClick: PropTypes.func,
selectedLabel: labelInterface,
type: PropTypes.string,
};
static defaultProps = {
@@ -19,6 +20,7 @@ class PanelGroup extends Component {
const {
onLabelClick,
selectedLabel,
type,
} = this.props;
const selected = isEqual(selectedLabel, item);
@@ -28,6 +30,7 @@ class PanelGroup extends Component {
item={item}
key={item.display_text}
onLabelClick={onLabelClick(item)}
type={type}
/>
);
}
@@ -1,8 +1,9 @@
import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import iconClassForLabel from 'utilities/icon_class_for_label';
import PlatformIcon from 'components/icons/PlatformIcon';
const baseClass = 'panel-group-item';
@@ -16,9 +17,21 @@ class PanelGroupItem extends Component {
}).isRequired,
onLabelClick: PropTypes.func,
isSelected: PropTypes.bool,
type: PropTypes.string,
};
renderIcon = () => {
const { item, type } = this.props;
if (type === 'platform') {
return <PlatformIcon name={item.display_text} className={`${baseClass}__icon`} />;
}
return <Icon name={iconClassForLabel(item)} className={`${baseClass}__icon`} />;
}
render () {
const { renderIcon } = this;
const { item, onLabelClick, isSelected } = this.props;
const {
count,
@@ -40,7 +53,7 @@ class PanelGroupItem extends Component {
return (
<button className={wrapperClassName} onClick={onLabelClick}>
<div className={`${baseClass}__flexy`}>
<Icon name={iconClassForLabel(item)} className={`${baseClass}__icon`} />
{renderIcon()}
<span className={`${baseClass}__name`}>
{displayText}
{description && <span className={`${baseClass}__description`}>{description}</span>}
@@ -11,21 +11,26 @@ describe('PanelGroupItem - component', () => {
type: 'all',
};
const component = mount(
const labelComponent = mount(
<PanelGroupItem item={validPanelGroupItem} />
);
it('renders the icon', () => {
const icon = component.find('i.kolidecon-hosts');
const platformComponent = mount(
<PanelGroupItem item={validPanelGroupItem} type="platform" />
);
expect(icon.length).toEqual(1);
it('renders the appropriate icon', () => {
expect(labelComponent.find('PlatformIcon').length).toEqual(0);
expect(labelComponent.find('Icon').length).toEqual(1);
expect(platformComponent.find('PlatformIcon').length).toEqual(1);
});
it('renders the item text', () => {
expect(component.text()).toContain(validPanelGroupItem.display_text);
expect(labelComponent.text()).toContain(validPanelGroupItem.display_text);
});
it('renders the item count', () => {
expect(component.text()).toContain(validPanelGroupItem.count);
expect(labelComponent.text()).toContain(validPanelGroupItem.count);
});
});
@@ -1,6 +1,6 @@
import React from 'react';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import SecondarySidePanelContainer from '../SecondarySidePanelContainer';
const baseClass = 'pack-info-side-panel';
@@ -2,9 +2,9 @@ import React, { Component, PropTypes } from 'react';
import osqueryTableInterface from 'interfaces/osquery_table';
import { osqueryTableNames } from 'utilities/osquery_tables';
import iconClassForLabel from 'utilities/icon_class_for_label';
import Dropdown from 'components/forms/fields/Dropdown';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import PlatformIcon from 'components/icons/PlatformIcon';
import SecondarySidePanelContainer from '../SecondarySidePanelContainer';
import {
@@ -43,7 +43,7 @@ class QuerySidePanel extends Component {
return false;
}
onSelectTable = ({ value }) => {
onSelectTable = (value) => {
const { onOsqueryTableSelect } = this.props;
onOsqueryTableSelect(value);
@@ -140,7 +140,11 @@ class QuerySidePanel extends Component {
<h2 className={`${baseClass}__header`}>OS Availability</h2>
<ul className={`${baseClass}__platforms`}>
{platformArr.map((os, idx) => {
return <li key={idx}><Icon name={iconClassForLabel(os)} /> {os.display_text}</li>;
if (os.type === 'all') {
return <li key={idx}><Icon name="hosts" /> {os.display_text}</li>;
}
return <li key={idx}><PlatformIcon name={os.type} /> {os.display_text}</li>;
})}
</ul>
</div>
@@ -49,7 +49,7 @@ describe('QuerySidePanel - component', () => {
it('calls the onOsqueryTableSelect prop when a new table is selected in the dropdown', () => {
const component = mount(<QuerySidePanel {...props} />);
component.node.onSelectTable({ value: 'groups' });
component.node.onSelectTable('groups');
expect(onOsqueryTableSelect).toHaveBeenCalledWith('groups');
});
@@ -2,7 +2,7 @@ import React, { Component, PropTypes } from 'react';
import AceEditor from 'react-ace';
import { isEqual } from 'lodash';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import queryInterface from 'interfaces/query';
import Dropdown from 'components/forms/fields/Dropdown';
@@ -3,7 +3,7 @@ import classnames from 'classnames';
import configInterface from 'interfaces/config';
import userInterface from 'interfaces/user';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import kolideLogo from '../../../../assets/images/kolide-logo.svg';
import UserMenu from './UserMenu';
@@ -2,7 +2,7 @@ import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
import Avatar from 'components/Avatar';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
class UserMenu extends Component {
static propTypes = {
@@ -2,7 +2,7 @@ import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
import userInterface from 'interfaces/user';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import navItems from './navItems';
@@ -9,7 +9,7 @@ import {
} from 'redux/nodes/components/ForgotPasswordPage/actions';
import debounce from 'utilities/debounce';
import ForgotPasswordForm from 'components/forms/ForgotPasswordForm';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import StackedWhiteBoxes from 'components/StackedWhiteBoxes';
export class ForgotPasswordPage extends Component {
@@ -1,7 +1,7 @@
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
class LoginSuccessfulPage extends Component {
static propTypes = {
@@ -6,7 +6,7 @@ import moment from 'moment';
import Avatar from 'components/Avatar';
import Button from 'components/buttons/Button';
import ChangePasswordForm from 'components/forms/ChangePasswordForm';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import { logoutUser } from 'redux/nodes/auth/actions';
import Modal from 'components/modals/Modal';
import { renderFlash } from 'redux/nodes/notifications/actions';
@@ -12,7 +12,7 @@ import HostDetails from 'components/hosts/HostDetails';
import hostInterface from 'interfaces/host';
import HostSidePanel from 'components/side_panels/HostSidePanel';
import HostsTable from 'components/hosts/HostsTable';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import osqueryTableInterface from 'interfaces/osquery_table';
import paths from 'router/paths';
import QueryComposer from 'components/queries/QueryComposer';
@@ -4,7 +4,7 @@ import { noop } from 'lodash';
import classnames from 'classnames';
import { renderFlash } from 'redux/nodes/notifications/actions';
import Icon from 'components/Icon';
import Icon from 'components/icons/Icon';
import { copyText } from './helpers';
import AnsibleImage from '../../../../assets/images/Ansible.png';
import ChefImage from '../../../../assets/images/Chef.png';
+1 -1
View File
@@ -320,7 +320,7 @@
content: '\f060'
}
.kolidecon-penguin:before {
.kolidecon-linux:before {
content: '\f061'
}
@@ -8,10 +8,6 @@ export const iconClassForLabel = (label) => {
case 'offline': return 'offline';
case 'online': return 'success-check';
case 'mia': return 'mia';
case 'macos': return 'apple';
case 'centos': return 'centos';
case 'ubuntu': return 'ubuntu';
case 'windows': return 'windows';
case 'unknown': return 'single-host';
default: return 'label';
}