mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 00:45:19 +00:00
Display loading icon until host details are saved (#1376)
This commit is contained in:
parent
84ffd1d5a3
commit
b23ab83336
@ -1,3 +1,5 @@
|
||||
* Show loading spinner while newly added Host Details are saved
|
||||
|
||||
* Show a generic computer icon when when referring to hosts with an unknown platform instead of the text "All"
|
||||
|
||||
* Kolide will now warn on startup if there are database migrations not yet completed.
|
||||
|
@ -4,6 +4,7 @@ import Button from 'components/buttons/Button';
|
||||
import hostInterface from 'interfaces/host';
|
||||
import Icon from 'components/icons/Icon';
|
||||
import PlatformIcon from 'components/icons/PlatformIcon';
|
||||
import CircleLoader from 'components/loaders/Circle';
|
||||
import { humanMemory, humanUptime } from './helpers';
|
||||
|
||||
const baseClass = 'host-details';
|
||||
@ -29,7 +30,7 @@ const ActionButton = ({ host, onDestroyHost, onQueryHost }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const HostDetails = ({ host, onDestroyHost, onQueryHost }) => {
|
||||
const HostDetails = ({ host, onDestroyHost, onQueryHost, isLoading }) => {
|
||||
const {
|
||||
host_cpu: hostCpu,
|
||||
host_mac: hostMac,
|
||||
@ -46,16 +47,17 @@ const HostDetails = ({ host, onDestroyHost, onQueryHost }) => {
|
||||
return (
|
||||
<div className={`${baseClass} ${baseClass}--${status}`}>
|
||||
<header className={`${baseClass}__header`}>
|
||||
<span className={`${baseClass}__cta-host`}>
|
||||
{!isLoading && <span className={`${baseClass}__cta-host`}>
|
||||
<ActionButton host={host} onDestroyHost={onDestroyHost} onQueryHost={onQueryHost} />
|
||||
</span>
|
||||
</span>}
|
||||
|
||||
<p className={`${baseClass}__hostname`}>{hostname}</p>
|
||||
<p className={`${baseClass}__hostname`}>{hostname || 'incoming host'}</p>
|
||||
</header>
|
||||
|
||||
<ul className={`${baseClass}__details-list`}>
|
||||
{isLoading && <div className={`${baseClass}__loader`}><CircleLoader /></div>}
|
||||
{!isLoading && <ul className={`${baseClass}__details-list`}>
|
||||
<li className={` ${baseClass}__detail ${baseClass}__detail--os`}>
|
||||
{platform && <PlatformIcon name={platform} className={`${baseClass}__icon`} title="Operating System & Version" />}
|
||||
<PlatformIcon name={platform} className={`${baseClass}__icon`} title="Operating System & Version" />
|
||||
<span className={`${baseClass}__host-content`}>{osVersion || '--'}</span>
|
||||
</li>
|
||||
|
||||
@ -88,7 +90,7 @@ const HostDetails = ({ host, onDestroyHost, onQueryHost }) => {
|
||||
<Icon name="world" className={`${baseClass}__icon`} title="IP Address" />
|
||||
<span className={`${baseClass}__host-content ${baseClass}__host-content--mono`}>{hostIpAddress || '--'}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -103,6 +105,7 @@ HostDetails.propTypes = {
|
||||
host: hostInterface.isRequired,
|
||||
onDestroyHost: PropTypes.func.isRequired,
|
||||
onQueryHost: PropTypes.func.isRequired,
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default HostDetails;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import expect, { createSpy, restoreSpies } from 'expect';
|
||||
import { mount } from 'enzyme';
|
||||
import { noop } from 'lodash';
|
||||
|
||||
import { hostStub } from 'test/stubs';
|
||||
import HostDetails from 'components/hosts/HostDetails';
|
||||
@ -18,6 +19,7 @@ describe('HostDetails - component', () => {
|
||||
host={offlineHost}
|
||||
onDestroyHost={destroySpy}
|
||||
onQueryHost={querySpy}
|
||||
isLoading={false}
|
||||
/>
|
||||
);
|
||||
const btn = offlineComponent.find('Button');
|
||||
@ -40,6 +42,7 @@ describe('HostDetails - component', () => {
|
||||
host={miaHost}
|
||||
onDestroyHost={destroySpy}
|
||||
onQueryHost={querySpy}
|
||||
isLoading={false}
|
||||
/>
|
||||
);
|
||||
const btn = miaComponent.find('Button');
|
||||
@ -62,6 +65,7 @@ describe('HostDetails - component', () => {
|
||||
host={onlineHost}
|
||||
onDestroyHost={destroySpy}
|
||||
onQueryHost={querySpy}
|
||||
isLoading={false}
|
||||
/>
|
||||
);
|
||||
const btn = onlineComponent.find('Button');
|
||||
@ -73,5 +77,19 @@ describe('HostDetails - component', () => {
|
||||
expect(destroySpy).toNotHaveBeenCalled();
|
||||
expect(querySpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders a spinner while hosts details are loaded', () => {
|
||||
const loadingComponent = mount(
|
||||
<HostDetails
|
||||
host={{ ...hostStub }}
|
||||
onDestroyHost={noop}
|
||||
onQueryHost={noop}
|
||||
isLoading
|
||||
/>
|
||||
);
|
||||
|
||||
expect(loadingComponent.find('Circle').length).toEqual(1);
|
||||
expect(loadingComponent.find('.host-details__details-list').length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
@include flex-direction(column);
|
||||
@include flex-grow(1);
|
||||
@include flex-basis(250px);
|
||||
justify-content: center;
|
||||
background-color: $white;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 12px 17px 0 rgba(47, 47, 91, 0.07), 0 3px 8px 0 rgba(0, 0, 0, 0.08), 0 -2px 0 0 rgba(32, 36, 50, 0.04);
|
||||
@ -84,6 +85,14 @@
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
&__loader {
|
||||
flex-grow: 1;
|
||||
align-self: center;
|
||||
align-content: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__detail {
|
||||
font-size: 15px;
|
||||
line-height: 15px;
|
||||
|
11
frontend/components/loaders/Circle/Circle.jsx
Normal file
11
frontend/components/loaders/Circle/Circle.jsx
Normal file
@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
const baseClass = 'kolide-circle-loader';
|
||||
|
||||
const Circle = () => {
|
||||
return (
|
||||
<div className={baseClass} />
|
||||
);
|
||||
};
|
||||
|
||||
export default Circle;
|
19
frontend/components/loaders/Circle/_styles.scss
Normal file
19
frontend/components/loaders/Circle/_styles.scss
Normal file
@ -0,0 +1,19 @@
|
||||
$circle-slice: #48c586;
|
||||
$circle-background: rgba($circle-slice, 0.2);
|
||||
$circle-size: 60px;
|
||||
|
||||
.kolide-circle-loader {
|
||||
@include size($circle-size);
|
||||
border-radius: 50%;
|
||||
border: #{$circle-size / 12} solid $circle-background;
|
||||
border-left-color: $circle-slice;
|
||||
transform: rotate(0deg);
|
||||
animation: spin-circle 1s linear infinite;
|
||||
}
|
||||
|
||||
|
||||
@keyframes spin-circle {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
1
frontend/components/loaders/Circle/index.js
Normal file
1
frontend/components/loaders/Circle/index.js
Normal file
@ -0,0 +1 @@
|
||||
export default from './Circle';
|
@ -522,12 +522,15 @@ export class ManageHostsPage extends Component {
|
||||
|
||||
if (display === 'Grid') {
|
||||
return sortedHosts.map((host) => {
|
||||
const isLoading = !host.hostname;
|
||||
|
||||
return (
|
||||
<HostDetails
|
||||
host={host}
|
||||
key={`host-${host.id}-details`}
|
||||
onDestroyHost={toggleDeleteHostModal}
|
||||
onQueryHost={onQueryHost}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user