Add new icons for Hosts page. Fix hosts list width on wide screens. (#128)

- Add new PNG files for the new icons in the left side navigation and the right side labels on the Hosts page.
- Rename the old `<Icon />` component to `<KolideIcon />` and create a new `<Icon />` component. The ultimate goal is to get rid of the `<KolideIcon />` and `<PlatformIcon />` components and use the encompassing `<Icon />` component for all icons. The full transition will be made when we have icon assets to replace all the kolide icons and platform icons. Currently, we don't.
- Rename the `icon_name_for_label.js` utility to `icon_name.js` because the utility now includes `iconNameForLabel()` and `iconNameForPlatform()` functions.
- Fixes issue #127.
This commit is contained in:
noahtalerman 2020-12-14 18:24:16 -08:00 committed by GitHub
parent 92d5ecf71a
commit 49e71e4ed6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 292 additions and 212 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 777 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 919 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 898 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 747 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 B

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { Link } from 'react-router';
import classnames from 'classnames';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
const baseClass = 'stacked-white-boxes';
@ -77,7 +77,7 @@ class StackedWhiteBoxes extends Component {
return (
<div className={`${baseClass}__back`}>
<Link to={previousLocation} className={`${baseClass}__back-link`} onClick={nowLeaving}>
<Icon name="x" />
<KolideIcon name="x" />
</Link>
</div>
);

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import classnames from 'classnames';
import Button from 'components/buttons/Button';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
const baseClass = 'warning-banner';
@ -19,7 +19,7 @@ const WarningBanner = ({ className, message, labelText, shouldShowWarning, onDis
return (
<div className={fullClassName}>
<div className={`${baseClass}__icon-wrap`}>
<Icon name="warning-filled" />
<KolideIcon name="warning-filled" />
<span className={`${baseClass}__label`}>{label}</span>
</div>
<span className={`${baseClass}__text`}>{message}</span>

View File

@ -8,7 +8,7 @@ describe('WarningBanner - component', () => {
const props = { shouldShowWarning: true, message: 'message' };
const component = shallow(<WarningBanner {...props} />);
expect(component.length).toEqual(1);
expect(component.find('Icon').props().name).toEqual('warning-filled');
expect(component.find('KolideIcon').props().name).toEqual('warning-filled');
expect(component.find('.warning-banner__label').text()).toEqual('Warning:');
expect(component.find('.warning-banner__text').text()).toEqual('message');
});

View File

@ -4,7 +4,7 @@ import { noop } from 'lodash';
import classnames from 'classnames';
import ClickOutside from 'components/ClickOutside';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import Button from 'components/buttons/Button';
const baseClass = 'dropdown-button';
@ -92,7 +92,7 @@ export class DropdownButton extends Component {
type={type}
variant={variant}
>
{children} <Icon name="downcarat" className={`${baseClass}__carat`} />
{children} <KolideIcon name="downcarat" className={`${baseClass}__carat`} />
</Button>
<ul className={optionsClass}>

View File

@ -5,7 +5,7 @@ import FileSaver from 'file-saver';
import Button from 'components/buttons/Button';
import enrollSecretInterface from 'interfaces/enroll_secret';
import InputField from 'components/forms/fields/InputField';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import { stringToClipboard } from 'utilities/copy_text';
import EyeIcon from '../../../../assets/images/icon-eye-16x16@2x.png';
import DownloadIcon from '../../../../assets/images/icon-download-12x12@2x.png';
@ -76,7 +76,7 @@ class EnrollSecretRow extends Component {
className={`${baseClass}__secret-copy-icon`}
onClick={onCopySecret}
>
<Icon name="clipboard" />
<KolideIcon name="clipboard" />
</Button>
<a
href="#showSecret"

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import classnames from 'classnames';
import notificationInterface from 'interfaces/notification';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import Button from 'components/buttons/Button';
const baseClass = 'flash-message';
@ -23,7 +23,7 @@ const FlashMessage = ({ fullWidth, notification, onRemoveFlash, onUndoActionClic
return (
<div className={klass}>
<div className={`${baseClass}__content`}>
<Icon name={alertIcon} /> <span>{message}</span>
<KolideIcon name={alertIcon} /> <span>{message}</span>
{undoAction &&
<Button
@ -41,7 +41,7 @@ const FlashMessage = ({ fullWidth, notification, onRemoveFlash, onUndoActionClic
variant="unstyled"
onClick={onRemoveFlash}
>
<Icon name="x" />
<KolideIcon name="x" />
</Button>
</div>
</div>

View File

@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
const baseClass = 'persistent-flash';
@ -12,7 +12,7 @@ const PersistentFlash = ({ message }) => {
return (
<div className={klass}>
<div className={`${baseClass}__content`}>
<Icon name="warning-filled" /> <span>{message}</span>
<KolideIcon name="warning-filled" /> <span>{message}</span>
</div>
</div>
);

View File

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { pull } from 'lodash';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import Button from 'components/buttons/Button';
import Dropdown from 'components/forms/fields/Dropdown';
import Form from 'components/forms/Form';
@ -142,7 +142,7 @@ export class ConfigurePackQueryForm extends Component {
{...fields.version}
options={minOsqueryVersionOptions}
placeholder="- - -"
label={['minimum ', <Icon name="osquery" key="min-osquery-vers" />, ' version']}
label={['minimum ', <KolideIcon name="osquery" key="min-osquery-vers" />, ' version']}
wrapperClassName={`${baseClass}__form-field ${baseClass}__form-field--osquer-vers`}
/>
<Dropdown

View File

@ -8,7 +8,7 @@ import Form from 'components/forms/Form';
import formFieldInterface from 'interfaces/form_field';
import enrollSecretInterface from 'interfaces/enroll_secret';
import EnrollSecretTable from 'components/config/EnrollSecretTable';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import InputField from 'components/forms/fields/InputField';
import OrgLogoIcon from 'components/icons/OrgLogoIcon';
import Slider from 'components/forms/fields/Slider';
@ -32,7 +32,7 @@ const formFields = [
'live_query_disabled',
];
const Header = ({ showAdvancedOptions }) => {
const CaratIcon = <Icon name={showAdvancedOptions ? 'downcarat' : 'upcarat'} />;
const CaratIcon = <KolideIcon name={showAdvancedOptions ? 'downcarat' : 'upcarat'} />;
return <span>Advanced Options {CaratIcon} <small>Most users do not need to modify these options.</small></span>;
};

View File

@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import InputField from '../InputField';
const baseClass = 'input-icon-field';
@ -74,7 +74,7 @@ class InputFieldWithIcon extends InputField {
type={type}
value={value}
/>
{iconName && <Icon name={iconName} className={iconClasses} />}
{iconName && <KolideIcon name={iconName} className={iconClasses} />}
{renderHint()}
</div>
);

View File

@ -1,7 +1,7 @@
import React from 'react';
import classnames from 'classnames';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import targetInterface from 'interfaces/target';
const baseClass = 'target-option';
@ -21,7 +21,7 @@ const TargetIcon = ({ target }) => {
const targetClasses = classnames(`${baseClass}__icon`, `${baseClass}__icon--${status}`);
return <Icon name={iconName()} className={targetClasses} />;
return <KolideIcon name={iconName()} className={targetClasses} />;
};
TargetIcon.propTypes = { target: targetInterface.isRequired };

View File

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import targetInterface from 'interfaces/target';
import TargetIcon from './TargetIcon';
@ -66,7 +66,7 @@ class TargetOption extends Component {
{renderTargetDetail()}
</button>
<button className={`button button--unstyled ${baseClass}__add-btn`} onClick={handleSelect}>
<Icon name="add-button" />
<KolideIcon name="add-button" />
</button>
</div>
);

View File

@ -7,7 +7,7 @@ import Button from 'components/buttons/Button';
import configInterface from 'interfaces/config';
import enrollSecretInterface from 'interfaces/enroll_secret';
import EnrollSecretTable from 'components/config/EnrollSecretTable';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import DownloadIcon from '../../../../assets/images/icon-download-12x12@2x.png';
const baseClass = 'add-host-modal';
@ -113,7 +113,7 @@ class AddHostModal extends Component {
target="_blank"
rel="noopener noreferrer"
>
Add Hosts Documentation <Icon name="external-link" />
Add Hosts Documentation <KolideIcon name="external-link" />
</a>
</h4>
</div>

View File

@ -1,6 +1,4 @@
.host-container {
// We set this equal to the max-width of header-wrap to preserve visual consistency
max-width: 1206px;
&--no-hosts {
display: flex;

View File

@ -3,9 +3,9 @@ import PropTypes from 'prop-types';
import classnames from 'classnames';
import Button from 'components/buttons/Button';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import hostInterface from 'interfaces/host';
import iconClassForLabel from 'utilities/icon_class_for_label';
import { iconNameForLabel } from 'utilities/icon_name';
import { humanMemory, humanUptime, humanLastSeen } from './helpers';
@ -15,14 +15,14 @@ const ActionButton = ({ host, onDestroyHost, onQueryHost }) => {
if (host.status === 'online') {
return (
<Button onClick={onQueryHost(host)} variant="unstyled">
<Icon name="query" />
<KolideIcon name="query" />
</Button>
);
}
return (
<Button onClick={onDestroyHost(host)} variant="unstyled">
<Icon name="trash" />
<KolideIcon name="trash" />
</Button>
);
};
@ -64,7 +64,7 @@ class HostsTable extends Component {
{host.hostname}
</td>
<td className={statusClassName}>
<Icon name={iconClassForLabel(host.status)} />
<KolideIcon name={iconNameForLabel(host.status)} />
</td>
<td>{host.os_version}</td>
<td>{host.osquery_version}</td>

View File

@ -21,7 +21,7 @@ describe('HostsTable - component', () => {
);
const btn = offlineComponent.find('Button');
expect(btn.find('Icon').prop('name')).toEqual('trash');
expect(btn.find('KolideIcon').prop('name')).toEqual('trash');
btn.simulate('click');
@ -46,7 +46,7 @@ describe('HostsTable - component', () => {
);
const btn = miaComponent.find('Button');
expect(btn.find('Icon').prop('name')).toEqual('trash');
expect(btn.find('KolideIcon').prop('name')).toEqual('trash');
btn.simulate('click');
@ -71,7 +71,7 @@ describe('HostsTable - component', () => {
);
const btn = onlineComponent.find('Button');
expect(btn.find('Icon').prop('name')).toEqual('query');
expect(btn.find('KolideIcon').prop('name')).toEqual('query');
btn.simulate('click');

View File

@ -7,6 +7,7 @@
}
&__table {
width: 100%;
border-collapse: collapse;
color: $core-black;
font-size: $x-small;

View File

@ -2,26 +2,24 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
const baseClass = 'kolidecon';
const baseClass = 'icon';
export class Icon extends Component {
static propTypes = {
className: PropTypes.string,
fw: PropTypes.bool,
name: PropTypes.string.isRequired,
size: PropTypes.string,
title: PropTypes.string,
size: PropTypes.string.isRequired,
};
render () {
const { className, fw, name, size, title } = this.props;
const iconClasses = classnames(baseClass, `${baseClass}-${name}`, className, {
[`${baseClass}-fw`]: fw,
const { className, name, size } = this.props;
const src = `../../../assets/images/icon-${name}-${size}x${size}@2x.png`;
const iconClasses = classnames(baseClass, className, {
[`${baseClass}-${size}`]: size,
});
return (
<i className={iconClasses} title={title} />
<img src={src} alt={`${name} icon`} className={iconClasses} />
);
}
}

View File

@ -5,6 +5,6 @@ import Icon from './Icon';
describe('Icon - component', () => {
it('renders', () => {
expect(mount(<Icon name="success-check" />)).toBeTruthy();
expect(mount(<Icon name="main-hosts" size="24" />)).toBeTruthy();
});
});

View File

@ -0,0 +1,11 @@
.icon {
&-24 {
width: 24px;
height: 24px;
}
&-20 {
width: 20px;
height: 20px;
}
}

View File

@ -0,0 +1,29 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
const baseClass = 'kolidecon';
export class KolideIcon extends Component {
static propTypes = {
className: PropTypes.string,
fw: PropTypes.bool,
name: PropTypes.string.isRequired,
size: PropTypes.string,
title: PropTypes.string,
};
render () {
const { className, fw, name, size, title } = this.props;
const iconClasses = classnames(baseClass, `${baseClass}-${name}`, className, {
[`${baseClass}-fw`]: fw,
[`${baseClass}-${size}`]: size,
});
return (
<i className={iconClasses} title={title} />
);
}
}
export default KolideIcon;

View File

@ -0,0 +1,10 @@
import React from 'react';
import { mount } from 'enzyme';
import KolideIcon from './KolideIcon';
describe('KolideIcon - component', () => {
it('renders', () => {
expect(mount(<KolideIcon name="success-check" />)).toBeTruthy();
});
});

View File

@ -0,0 +1 @@
export default from './KolideIcon';

View File

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import platformIconClass from 'utilities/platform_icon_class';
const baseClass = 'platform-icon';
@ -26,7 +26,7 @@ export class PlatformIcon extends Component {
}
return (
<Icon
<KolideIcon
className={iconClasses}
fw={fw}
name={iconName}

View File

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
const baseClass = 'modal';
@ -25,7 +25,7 @@ class Modal extends Component {
<span>{title}</span>
<div className={`${baseClass}__ex`}>
<button className="button button--unstyled" onClick={onExit}>
<Icon name="x" />
<KolideIcon name="x" />
</button>
</div>
</div>

View File

@ -6,7 +6,7 @@ import moment from 'moment';
import Checkbox from 'components/forms/fields/Checkbox';
import ClickableTableRow from 'components/ClickableTableRow';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import packInterface from 'interfaces/pack';
const baseClass = 'packs-list-row';
@ -53,7 +53,7 @@ class Row extends Component {
if (disabled) {
return (
<td className={`${baseClass}__td`}>
<Icon className={iconClassName} name="offline" />
<KolideIcon className={iconClassName} name="offline" />
<span className={`${baseClass}__status-text`}>Disabled</span>
</td>
);
@ -61,7 +61,7 @@ class Row extends Component {
return (
<td className={`${baseClass}__td`}>
<Icon className={iconClassName} name="success-check" />
<KolideIcon className={iconClassName} name="success-check" />
<span className={`${baseClass}__status-text`}>Enabled</span>
</td>
);

View File

@ -6,7 +6,7 @@ import { keys, omit } from 'lodash';
import Button from 'components/buttons/Button';
import campaignInterface from 'interfaces/campaign';
import filterArrayByHash from 'utilities/filter_array_by_hash';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import InputField from 'components/forms/fields/InputField';
import QueryResultsRow from 'components/queries/QueryResultsTable/QueryResultsRow';
import QueryProgressDetails from 'components/queries/QueryProgressDetails';
@ -66,7 +66,7 @@ class QueryResultsTable extends Component {
return (
<th key={`query-results-table-header-${index}`}>
<span><Icon className={filterIconClassName} name="filter" />{column}</span>
<span><KolideIcon className={filterIconClassName} name="filter" />{column}</span>
<InputField
name={column}
onChange={onFilterAttribute(filterable)}
@ -180,7 +180,7 @@ class QueryResultsTable extends Component {
onClick={onToggleQueryFullScreen}
variant="muted"
>
<Icon name={isQueryFullScreen ? 'windowed' : 'fullscreen'} />
<KolideIcon name={isQueryFullScreen ? 'windowed' : 'fullscreen'} />
</Button>
<Button
className={`${baseClass}__export-btn`}

View File

@ -4,7 +4,7 @@ import classnames from 'classnames';
import { includes, sortBy, size } from 'lodash';
import queryInterface from 'interfaces/query';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import QueriesListItem from 'components/queries/ScheduledQueriesList/ScheduledQueriesListItem';
import Checkbox from 'components/forms/fields/Checkbox';
@ -89,12 +89,12 @@ class ScheduledQueriesList extends Component {
<h2>Then we&apos;ll set the following:</h2>
<p><strong>interval:</strong> the amount of time, in seconds, the query waits before running</p>
<p><strong>platform:</strong> the computer platform where this query will run (other platforms ignored)</p>
<p><strong>minimum <Icon name="osquery" /> version:</strong> the minimum required <strong>osqueryd</strong> version installed on a host</p>
<p><strong>minimum <KolideIcon name="osquery" /> version:</strong> the minimum required <strong>osqueryd</strong> version installed on a host</p>
<p><strong>logging type:</strong></p>
<ul>
<li><strong><Icon name="plus-minus" /> differential:</strong> show only whats added from last run</li>
<li><strong><Icon name="bold-plus" /> differential (ignore removals):</strong> show only whats been added since the last run</li>
<li><strong><Icon name="camera" /> snapshot:</strong> show everything in its current state</li>
<li><strong><KolideIcon name="plus-minus" /> differential:</strong> show only whats added from last run</li>
<li><strong><KolideIcon name="bold-plus" /> differential (ignore removals):</strong> show only whats been added since the last run</li>
<li><strong><KolideIcon name="camera" /> snapshot:</strong> show everything in its current state</li>
</ul>
</div>
</td>
@ -124,7 +124,7 @@ class ScheduledQueriesList extends Component {
<th>Query name</th>
<th>Interval(s)</th>
<th>Platform</th>
<th><Icon name="osquery" /> Ver.</th>
<th><KolideIcon name="osquery" /> Ver.</th>
<th>Shard</th>
<th>Logging</th>
</tr>

View File

@ -4,7 +4,7 @@ import classnames from 'classnames';
import Checkbox from 'components/forms/fields/Checkbox';
import ClickableTableRow from 'components/ClickableTableRow';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import PlatformIcon from 'components/icons/PlatformIcon';
import { includes, isEmpty, isEqual } from 'lodash';
import scheduledQueryInterface from 'interfaces/scheduled_query';
@ -97,7 +97,7 @@ class ScheduledQueriesListItem extends Component {
<td>{renderPlatformIcon()}</td>
<td>{version ? `${version}+` : 'Any'}</td>
<td>{shard}</td>
<td><Icon name={loggingTypeString()} /></td>
<td><KolideIcon name={loggingTypeString()} /></td>
</ClickableTableRow>
);
}

View File

@ -73,26 +73,26 @@ describe('ScheduledQueriesListItem - component', () => {
const props = { ...defaultProps, scheduledQuery: query };
let component = shallow(<ScheduledQueriesListItem {...props} />);
expect(component.find('Icon').last().props().name).toEqual('camera');
expect(component.find('KolideIcon').last().props().name).toEqual('camera');
query.snapshot = false;
query.removed = false;
component = shallow(<ScheduledQueriesListItem {...props} />);
expect(component.find('Icon').last().props().name).toEqual('bold-plus');
expect(component.find('KolideIcon').last().props().name).toEqual('bold-plus');
query.snapshot = false;
query.removed = null;
component = shallow(<ScheduledQueriesListItem {...props} />);
expect(component.find('Icon').last().props().name).toEqual('plus-minus');
expect(component.find('KolideIcon').last().props().name).toEqual('plus-minus');
query.snapshot = false;
query.removed = true;
component = shallow(<ScheduledQueriesListItem {...props} />);
expect(component.find('Icon').last().props().name).toEqual('plus-minus');
expect(component.find('KolideIcon').last().props().name).toEqual('plus-minus');
query.snapshot = null;
query.removed = true;
component = shallow(<ScheduledQueriesListItem {...props} />);
expect(component.find('Icon').last().props().name).toEqual('plus-minus');
expect(component.find('KolideIcon').last().props().name).toEqual('plus-minus');
});
});

View File

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { filter } from 'lodash';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import Button from 'components/buttons/Button';
import InputField from 'components/forms/fields/InputField';
import labelInterface from 'interfaces/label';
@ -86,7 +86,7 @@ class HostSidePanel extends Component {
value={labelFilter}
inputWrapperClass={`${baseClass}__filter-labels`}
/>
<Icon name="search" />
<KolideIcon name="search" />
</div>
<PanelGroup
groupItems={customLabels}

View File

@ -3,8 +3,7 @@ import PropTypes from 'prop-types';
import classnames from 'classnames';
import Icon from 'components/icons/Icon';
import iconClassForLabel from 'utilities/icon_class_for_label';
import PlatformIcon from 'components/icons/PlatformIcon';
import { iconNameForLabel, iconNameForPlatform } from 'utilities/icon_name';
import statusLabelsInterface from 'interfaces/status_labels';
const baseClass = 'panel-group-item';
@ -40,12 +39,11 @@ class PanelGroupItem extends Component {
renderIcon = () => {
const { item, type } = this.props;
if (type === 'platform') {
return <PlatformIcon name={item.display_text} title={item.display_text} className={`${baseClass}__icon`} />;
return <Icon name={iconNameForPlatform(item)} size="20" className={`${baseClass}__icon`} />;
}
return <Icon name={iconClassForLabel(item)} className={`${baseClass}__icon`} />;
return <Icon name={iconNameForLabel(item)} size="20" className={`${baseClass}__icon`} />;
}
render () {

View File

@ -46,7 +46,8 @@ describe('PanelGroupItem - component', () => {
expect(labelComponent.find('PlatformIcon').length).toEqual(0);
expect(labelComponent.find('Icon').length).toEqual(1);
expect(platformComponent.find('PlatformIcon').length).toEqual(1);
expect(platformComponent.find('PlatformIcon').length).toEqual(0);
expect(platformComponent.find('Icon').length).toEqual(1);
});
it('renders the item text', () => {

View File

@ -5,7 +5,7 @@ $base-class: 'panel-group-item';
width: 100%;
font-size: $x-small;
font-weight: $regular;
color: $black;
color: $core-dark-blue-grey;
text-align: left;
padding: 10px 0;
margin: 2px 0;

View File

@ -17,7 +17,7 @@
h3 {
font-size: $x-small;
font-weight: $bold;
color: $black;
color: $core-dark-blue-grey;
margin: 24px 0 10px 8px;
}

View File

@ -1,6 +1,6 @@
import React from 'react';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import SecondarySidePanelContainer from '../SecondarySidePanelContainer';
const baseClass = 'pack-info-side-panel';
@ -9,7 +9,7 @@ const PackInfoSidePanel = () => {
return (
<SecondarySidePanelContainer className={baseClass}>
<h3 className={`${baseClass}__title`}>
<Icon name="packs" />
<KolideIcon name="packs" />
&nbsp;
What&apos;s a query pack?
</h3>
@ -30,10 +30,10 @@ const PackInfoSidePanel = () => {
</p>
<dl>
<dt><Icon name="plus-minus" /> <span>Differential</span></dt>
<dt><KolideIcon name="plus-minus" /> <span>Differential</span></dt>
<dd>Only record data that has changed.</dd>
<dt><Icon name="camera" /> <span>Snapshot</span></dt>
<dt><KolideIcon name="camera" /> <span>Snapshot</span></dt>
<dd>Record full query result each time.</dd>
</dl>

View File

@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import osqueryTableInterface from 'interfaces/osquery_table';
import { osqueryTableNames } from 'utilities/osquery_tables';
import Dropdown from 'components/forms/fields/Dropdown';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import PlatformIcon from 'components/icons/PlatformIcon';
import SecondarySidePanelContainer from '../SecondarySidePanelContainer';
@ -48,7 +48,7 @@ class QuerySidePanel extends Component {
<span className={`${columnBaseClass}__name`}>{column.name}</span>
<div className={`${columnBaseClass}__description`}>
<span className={`${columnBaseClass}__type`}>{displayTypeForDataType(column.type)}</span>
<Icon name="help-solid" className={`${columnBaseClass}__help`} title={column.description} />
<KolideIcon name="help-solid" className={`${columnBaseClass}__help`} title={column.description} />
</div>
</li>
);
@ -93,7 +93,7 @@ class QuerySidePanel extends Component {
<ul className={`${baseClass}__platforms`}>
{platforms.map((platform) => {
if (platform === 'all') {
return <li key={platform}><Icon name="hosts" /> {platform}</li>;
return <li key={platform}><KolideIcon name="hosts" /> {platform}</li>;
}
return <li key={platform}><PlatformIcon name={platform} title={platform} /> {platform}</li>;

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import AceEditor from 'react-ace';
import { isEqual, sortBy } from 'lodash';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import queryInterface from 'interfaces/query';
import Dropdown from 'components/forms/fields/Dropdown';
@ -59,7 +59,7 @@ class SearchPackQuery extends Component {
if (selectedQuery) {
return (
<h1 className={`${baseClass}__title`}>
<Icon name="query" /> {selectedQuery.name}
<KolideIcon name="query" /> {selectedQuery.name}
</h1>
);
}
@ -117,7 +117,7 @@ class SearchPackQuery extends Component {
<Dropdown
options={queryDropdownOptions}
onChange={onSelectQuery}
placeholder={[<Icon name="search" size="lg" key="search-pack-query" />, ' Select query']}
placeholder={[<KolideIcon name="search" size="lg" key="search-pack-query" />, ' Select query']}
/>
{renderQuery()}
{renderDescription()}

View File

@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import classnames from 'classnames';
import userInterface from 'interfaces/user';
import KolideIcon from 'components/icons/KolideIcon';
import Icon from 'components/icons/Icon';
import UserMenu from 'components/side_panels/UserMenu';
@ -52,7 +53,7 @@ class SiteNavSidePanel extends Component {
}
renderNavItem = (navItem) => {
const { icon, name, subItems } = navItem;
const { name, iconName, subItems } = navItem;
const { onNavItemClick, pathname } = this.props;
const { renderSubItems } = this;
const active = navItem.location.regex.test(pathname);
@ -62,22 +63,21 @@ class SiteNavSidePanel extends Component {
`${navItemBaseClass}`,
{
[`${navItemBaseClass}--active`]: active,
[`${navItemBaseClass}--single`]: subItems.length === 0,
[`${navItemBaseClass}--multiple`]: subItems.length !== 0,
},
);
return (
<li className={navItemClasses} key={`nav-item-${name}`}>
<button
className={`${navItemBaseClass}__button button button--unstyled`}
<a
className={`${navItemBaseClass}__link`}
onClick={onNavItemClick(navItem.location.pathname)}
style={{ width: '100%' }}
>
<Icon name={icon} className={`${navItemBaseClass}__icon`} />
<Icon name={iconName} size="24" className={`${navItemBaseClass}__icon`} />
<span className={`${navItemBaseClass}__name`}>
{name}
</span>
</button>
</a>
{active && renderSubItems(subItems)}
</li>
);
@ -120,14 +120,14 @@ class SiteNavSidePanel extends Component {
key={name}
className={baseSubItemItemClass}
>
<button
className={`${baseSubItemClass}__button button button--unstyled`}
<a
className={`${baseSubItemClass}__link`}
key={`sub-item-${name}`}
onClick={onNavItemClick(subItem.location.pathname)}
>
<span className={`${baseSubItemClass}__name`}>{name}</span>
<span className={`${baseSubItemClass}__icon`}><Icon name={icon} /></span>
</button>
<span className={`${baseSubItemClass}__icon`}><KolideIcon name={icon} /></span>
</a>
</li>
);
}

View File

@ -1,11 +1,39 @@
.site-nav-item {
position: relative;
transition: color 200ms ease-in-out;
cursor: pointer;
&:hover {
background-color: $core-black;
}
@include breakpoint(smalldesk) {
justify-content: center;
}
&--multiple.site-nav-item--active {
background-color: transparent;
border-right: 0;
.site-nav-item__link {
border-right: 3px solid $core-blue;
background-color: $core-black;
}
}
&__icon {
position: relative;
font-size: $large;
margin-right: 24px;
vertical-align: sub;
@at-root .site-nav--small & {
margin-right: 0;
}
@include breakpoint(smalldesk) {
margin-right: 0;
}
}
&__name {
@ -23,54 +51,57 @@
}
}
&__button {
transition: color 200ms ease-in-out;
border-radius: 0;
line-height: 40px;
position: relative;
a {
color: $white;
cursor: pointer;
font-size: $x-small;
letter-spacing: 0.5px;
padding: 4px 20px;
text-align: left;
&:hover {
background-color: $core-black;
}
text-align: center;
display: flex;
align-items: center;
padding: 14px 20px;
@include breakpoint(smalldesk) {
padding: 4px 10px;
padding: 11px 14px;
}
}
&--active {
.site-nav-item__button {
background-color: $core-black;
border-right: 3px solid $core-blue;
background-color: $core-black;
@at-root .site-nav--small & {
border-right: 3px solid $core-blue;
}
@at-root .site-nav--small & {
border-right: 3px solid $core-blue;
padding: 4px 10px;
}
@include breakpoint(smalldesk) {
border-right: 3px solid $core-blue;
padding: 4px 10px;
}
@include breakpoint(smalldesk) {
border-right: 3px solid $core-blue;
}
.site-nav-item__name {
font-weight: $bold;
}
.site-nav-item__icon {
.site-sub-item {
a {
padding: 0;
@at-root .site-nav--small & {
padding: 5px 14px;
}
@include breakpoint(smalldesk) {
padding: 5px 14px;
}
}
@at-root .site-nav--small & {
margin-right: 0;
display: flex;
justify-content: center;
}
@include breakpoint(smalldesk) {
margin-right: 0;
display: flex;
justify-content: center;
}
}
}
@ -107,8 +138,16 @@
text-transform: none;
padding: 6px 0;
a {
padding: 0;
}
i {
color: $ui-medium-grey;
}
&--active {
.site-sub-item__button {
.site-sub-item__link {
color: $white;
font-size: $x-small;
font-weight: $bold;
@ -116,6 +155,10 @@
&:hover {
color: $white;
}
i {
color: $white;
}
}
&::before {
@ -139,29 +182,6 @@
}
}
&__button {
transition: color 150ms ease-in-out;
color: rgba($white, 0.75);
font-size: $x-small;
text-transform: none;
cursor: pointer;
font-weight: $regular;
width: 100%;
text-align: left;
&:hover {
color: rgba($white, 0.9);
}
@at-root .site-nav--small & {
padding: 4px 10px;
}
@include breakpoint(smalldesk) {
padding: 4px 10px;
}
}
&__name {
@at-root .site-nav--small & {
display: none;
@ -225,20 +245,18 @@
}
&__list {
padding: 12px 0 0 42px;
padding: 12px 0 0 64px;
margin: 0;
list-style: none;
@at-root .site-nav--small & {
padding: 0;
text-align: center;
width: 100%;
}
@include breakpoint(smalldesk) {
padding: 0;
text-align: center;
width: 100%;
}
&--expanded {

View File

@ -6,6 +6,7 @@ export default (admin) => {
{
icon: 'admin',
name: 'Admin',
iconName: 'main-admin-white',
location: {
regex: new RegExp(`^${URL_PREFIX}/admin/`),
pathname: PATHS.ADMIN_USERS,
@ -45,6 +46,7 @@ export default (admin) => {
{
icon: 'hosts',
name: 'Hosts',
iconName: 'main-hosts-white',
location: {
regex: new RegExp(`^${URL_PREFIX}/hosts/`),
pathname: PATHS.MANAGE_HOSTS,
@ -54,6 +56,7 @@ export default (admin) => {
{
icon: 'query',
name: 'Queries',
iconName: 'main-query-white',
location: {
regex: new RegExp(`^${URL_PREFIX}/queries/`),
pathname: PATHS.MANAGE_QUERIES,
@ -63,6 +66,7 @@ export default (admin) => {
{
icon: 'packs',
name: 'Packs',
iconName: 'main-packs-white',
location: {
regex: new RegExp(`^${URL_PREFIX}/packs/`),
pathname: PATHS.MANAGE_PACKS,

View File

@ -41,9 +41,9 @@ class UserMenu extends Component {
<div className={userMenuClass}>
<nav className={`${baseClass}__nav`}>
<ul className={`${baseClass}__nav-list`}>
<li className={settingsNavItemBaseClass}><a href="#settings" onClick={onNavItemClick(PATHS.USER_SETTINGS)}><Icon name="user-settings" /><span>Account</span></a></li>
<li className={`${baseClass}__nav-item`}><a href="https://github.com/fleetdm/fleet/blob/master/docs/README.md" target="_blank" rel="noreferrer"><Icon name="help" /><span>Help</span></a></li>
<li className={`${baseClass}__nav-item`}><a href="#logout" onClick={onLogout}><Icon name="logout" /><span>Log out</span></a></li>
<li className={settingsNavItemBaseClass}><a href="#settings" onClick={onNavItemClick(PATHS.USER_SETTINGS)}><Icon name="main-settings-white" size="24" /><span>Account</span></a></li>
<li className={`${baseClass}__nav-item`}><a href="https://github.com/fleetdm/fleet/blob/master/docs/README.md" target="_blank" rel="noreferrer"><Icon name="main-help-white" size="24" /><span>Help</span></a></li>
<li className={`${baseClass}__nav-item`}><a href="#logout" onClick={onLogout}><Icon name="main-logout-white" size="24" /><span>Log out</span></a></li>
</ul>
</nav>
</div>

View File

@ -11,6 +11,7 @@
font-size: $x-small;
font-weight: $regular;
padding: 11px 20px;
cursor: pointer;
&:hover {
background-color: $core-black;
@ -25,24 +26,20 @@
a {
transition: opacity 75ms $ease-in-quad, background 75ms $ease-in-quad;
color: $white;
display: block;
display: flex;
align-items: center;
text-decoration: none;
text-transform: none;
}
i {
margin-right: 24px;
font-size: $large;
vertical-align: text-bottom;
}
.icon {
margin-right: 24px;
}
@include breakpoint(smalldesk) {
padding: 11px 14px;
a {
i {
margin-right: 0;
}
span {
display: none;

View File

@ -15,7 +15,7 @@ import Button from 'components/buttons/Button';
import ChangeEmailForm from 'components/forms/ChangeEmailForm';
import ChangePasswordForm from 'components/forms/ChangePasswordForm';
import deepDifference from 'utilities/deep_difference';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import InputField from 'components/forms/fields/InputField';
import { logoutUser, updateUser } from 'redux/nodes/auth/actions';
import Modal from 'components/modals/Modal';
@ -258,7 +258,7 @@ export class UserSettingsPage extends Component {
className={`${baseClass}__secret-copy-icon`}
onClick={onCopySecret(`.${baseClass}__secret-input`)}
>
<Icon name="clipboard" />
<KolideIcon name="clipboard" />
</Button>
</div>
<div className={`${baseClass}__button-wrap`}>

View File

@ -40,7 +40,7 @@ describe('AppSettingsPage - component', () => {
const smtpWarning = page.find('WarningBanner');
expect(smtpWarning.length).toEqual(1);
expect(smtpWarning.find('Icon').length).toEqual(1);
expect(smtpWarning.find('KolideIcon').length).toEqual(1);
expect(smtpWarning.text()).toContain('Warning:SMTP is not currently configured in Fleet. The "Add new user" feature requires that SMTP is configured in order to send invitation emails. Users may also be added with "fleetctl user create".');
});

View File

@ -5,16 +5,9 @@
display: flex;
align-items: center;
justify-content: space-between;
// We set this equal to the max-width of the host-container to preserve visual consistency
max-width: 1206px;
margin-bottom: 16px;
}
.ace-kolide {
// We set this equal to the max-width of the host-container to preserve visual consistency
max-width: 1206px;
}
&__header {
display: flex;
align-items: center;

View File

@ -6,7 +6,7 @@ import { push } from 'react-router-redux';
import Button from 'components/buttons/Button';
import entityGetter from 'redux/utilities/entityGetter';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import InputField from 'components/forms/fields/InputField';
import Modal from 'components/modals/Modal';
import packActions from 'redux/nodes/entities/packs/actions';
@ -227,21 +227,21 @@ export class AllPacksPage extends Component {
onClick={onBulkAction('disable')}
variant="unstyled"
>
<Icon name="offline" /> Disable
<KolideIcon name="offline" /> Disable
</Button>
<Button
className={`${btnClass} ${btnClass}--enable`}
onClick={onBulkAction('enable')}
variant="unstyled"
>
<Icon name="success-check" /> Enable
<KolideIcon name="success-check" /> Enable
</Button>
<Button
className={`${btnClass} ${btnClass}--delete`}
onClick={onToggleModal}
variant="unstyled"
>
<Icon name="trash" /> Delete
<KolideIcon name="trash" /> Delete
</Button>
</div>
);
@ -329,7 +329,7 @@ export class AllPacksPage extends Component {
placeholder="Filter packs"
value={packFilter}
/>
<Icon name="search" />
<KolideIcon name="search" />
</div>
<p className={`${baseClass}__pack-count`}>{packsTotalDisplay}</p>
<PacksList

View File

@ -8,7 +8,7 @@ import Button from 'components/buttons/Button';
import entityGetter from 'redux/utilities/entityGetter';
import InputField from 'components/forms/fields/InputField';
import Modal from 'components/modals/Modal';
import Icon from 'components/icons/Icon';
import KolideIcon from 'components/icons/KolideIcon';
import SecondarySidePanelContainer from 'components/side_panels/SecondarySidePanelContainer';
import PATHS from 'router/paths';
import QueryDetailsSidePanel from 'components/side_panels/QueryDetailsSidePanel';
@ -283,7 +283,7 @@ export class ManageQueriesPage extends Component {
placeholder="Filter queries"
value={queriesFilter}
/>
<Icon name="search" />
<KolideIcon name="search" />
</div>
</div>
<p className={`${baseClass}__query-count`}>{queriesTotalDisplay}</p>

View File

@ -1,17 +0,0 @@
export const iconClassForLabel = (label) => {
const lowerType = label.type && label.type.toLowerCase();
const lowerDisplayText = label.display_text && label.display_text.toLowerCase();
if (lowerType === 'all') return 'hosts';
switch (lowerDisplayText || label) {
case 'offline': return 'offline';
case 'online': return 'success-check';
case 'mia': return 'mia';
case 'new': return 'clock';
case 'unknown': return 'single-host';
default: return 'label';
}
};
export default iconClassForLabel;

View File

@ -0,0 +1,38 @@
export const iconNameForLabel = (label) => {
const lowerType = label.type && label.type.toLowerCase();
const lowerDisplayText = label.display_text && label.display_text.toLowerCase();
if (lowerType === 'all') return 'hosts-3';
switch (lowerDisplayText || label) {
case 'offline': return 'offline';
case 'online': return 'online';
case 'mia': return 'mia';
case 'new': return 'new';
case 'unknown': return 'hosts-2';
default: return 'label';
}
};
export const iconNameForPlatform = (platform = '') => {
if (!platform.name) return false;
const lowerPlatform = platform.name.toLowerCase();
switch (lowerPlatform) {
case 'macos': return 'apple-dark';
case 'mac os x': return 'apple-dark';
case 'mac osx': return 'apple-dark';
case 'mac os': return 'apple-dark';
case 'darwin': return 'apple-dark';
case 'apple': return 'apple-dark';
case 'centos': return 'centos-dark';
case 'centos linux': return 'centos-dark';
case 'ubuntu': return 'ubuntu-dark';
case 'ubuntu linux': return 'ubuntu-dark';
case 'linux': return 'linux-dark';
case 'windows': return 'windows-dark';
case 'ms windows': return 'windows-dark';
default: return false;
}
};

View File

@ -4,19 +4,19 @@ export const platformIconClass = (platform = '') => {
const lowerPlatform = platform.toLowerCase();
switch (lowerPlatform) {
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 'apple': 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';
case 'macos': return 'icon-apple-dark-20x20@2x.png';
case 'mac os x': return 'icon-apple-dark-20x20@2x.png';
case 'mac osx': return 'icon-apple-dark-20x20@2x.png';
case 'mac os': return 'icon-apple-dark-20x20@2x.png';
case 'darwin': return 'icon-apple-dark-20x20@2x.png';
case 'apple': return 'icon-apple-dark-20x20@2x.png';
case 'centos': return 'icon-centos-dark-20x20@2x.png';
case 'centos linux': return 'icon-centos-dark-20x20@2x.png';
case 'ubuntu': return 'icon-ubuntu-dark-20x20@2x.png';
case 'ubuntu linux': return 'icon-ubuntu-dark-20x20@2x.png';
case 'linux': return 'icon-linux-dark-20x20@2x.png';
case 'windows': return 'icon-windows-dark-20x20@2x.png';
case 'ms windows': return 'icon-windows-dark-20x20@2x.png';
default: return false;
}
};