Move HostToggle into shared components directory

This commit is contained in:
Marliana Lara
2020-02-26 23:00:12 -05:00
parent e096ad18cb
commit 3bf1ad3028
11 changed files with 50 additions and 112 deletions

View File

@@ -0,0 +1 @@
export { default } from './HostToggle';

View File

@@ -12,7 +12,7 @@ import { VariablesDetail } from '@components/CodeMirrorInput';
import Sparkline from '@components/Sparkline'; import Sparkline from '@components/Sparkline';
import DeleteButton from '@components/DeleteButton'; import DeleteButton from '@components/DeleteButton';
import { HostsAPI } from '@api'; import { HostsAPI } from '@api';
import HostToggle from '../shared/HostToggle'; import HostToggle from '@components/HostToggle';
function HostDetail({ host, i18n, onUpdateHost }) { function HostDetail({ host, i18n, onUpdateHost }) {
const { const {

View File

@@ -121,7 +121,7 @@ describe('<HostList />', () => {
expect(wrapper.find('HostListItem')).toHaveLength(3); expect(wrapper.find('HostListItem')).toHaveLength(3);
}); });
test('should select single item', async () => { test('should select and deselect a single item', async () => {
let wrapper; let wrapper;
await act(async () => { await act(async () => {
wrapper = mountWithContexts(<HostList />); wrapper = mountWithContexts(<HostList />);
@@ -141,6 +141,19 @@ describe('<HostList />', () => {
.first() .first()
.prop('isSelected') .prop('isSelected')
).toEqual(true); ).toEqual(true);
act(() => {
wrapper
.find('input#select-host-1')
.closest('DataListCheck')
.invoke('onChange')();
});
wrapper.update();
expect(
wrapper
.find('HostListItem')
.first()
.prop('isSelected')
).toEqual(false);
}); });
test('should select all items', async () => { test('should select all items', async () => {

View File

@@ -18,7 +18,7 @@ import { PencilAltIcon } from '@patternfly/react-icons';
import Sparkline from '@components/Sparkline'; import Sparkline from '@components/Sparkline';
import { Host } from '@types'; import { Host } from '@types';
import styled from 'styled-components'; import styled from 'styled-components';
import HostToggle from '../shared/HostToggle'; import HostToggle from '@components/HostToggle';
const DataListAction = styled(_DataListAction)` const DataListAction = styled(_DataListAction)`
align-items: center; align-items: center;

View File

@@ -3,8 +3,6 @@ import { mountWithContexts } from '@testUtils/enzymeHelpers';
import HostsListItem from './HostListItem'; import HostsListItem from './HostListItem';
const onToggleHost = jest.fn();
const mockHost = { const mockHost = {
id: 1, id: 1,
name: 'Host 1', name: 'Host 1',
@@ -32,13 +30,12 @@ describe('<HostsListItem />', () => {
detailUrl="/host/1" detailUrl="/host/1"
onSelect={() => {}} onSelect={() => {}}
host={mockHost} host={mockHost}
onToggleHost={onToggleHost}
/> />
); );
}); });
afterEach(() => { afterEach(() => {
jest.clearAllMocks(); wrapper.unmount();
}); });
test('edit button shown to users with edit capabilities', () => { test('edit button shown to users with edit capabilities', () => {
@@ -54,7 +51,6 @@ describe('<HostsListItem />', () => {
detailUrl="/host/1" detailUrl="/host/1"
onSelect={() => {}} onSelect={() => {}}
host={copyMockHost} host={copyMockHost}
onToggleHost={onToggleHost}
/> />
); );
expect(wrapper.find('PencilAltIcon').exists()).toBeFalsy(); expect(wrapper.find('PencilAltIcon').exists()).toBeFalsy();

View File

@@ -10,11 +10,11 @@ import {
DataListItem, DataListItem,
DataListItemCells, DataListItemCells,
DataListItemRow, DataListItemRow,
Switch,
Tooltip, Tooltip,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { PencilAltIcon } from '@patternfly/react-icons'; import { PencilAltIcon } from '@patternfly/react-icons';
import HostToggle from '@components/HostToggle';
import Sparkline from '@components/Sparkline'; import Sparkline from '@components/Sparkline';
import { Host } from '@types'; import { Host } from '@types';
import styled from 'styled-components'; import styled from 'styled-components';
@@ -27,16 +27,7 @@ const DataListAction = styled(_DataListAction)`
`; `;
function InventoryHostItem(props) { function InventoryHostItem(props) {
const { const { detailUrl, editUrl, host, i18n, isSelected, onSelect } = props;
detailUrl,
editUrl,
host,
i18n,
isSelected,
onSelect,
toggleHost,
toggleLoading,
} = props;
const recentPlaybookJobs = host.summary_fields.recent_jobs.map(job => ({ const recentPlaybookJobs = host.summary_fields.recent_jobs.map(job => ({
...job, ...job,
@@ -71,27 +62,7 @@ function InventoryHostItem(props) {
aria-labelledby={labelId} aria-labelledby={labelId}
id={labelId} id={labelId}
> >
<Tooltip <HostToggle host={host} />
content={i18n._(
t`Indicates if a host is available and should be included
in running jobs. For hosts that are part of an external
inventory, this may be reset by the inventory sync process.`
)}
position="top"
>
<Switch
css="display: inline-flex;"
id={`host-${host.id}-toggle`}
label={i18n._(t`On`)}
labelOff={i18n._(t`Off`)}
isChecked={host.enabled}
isDisabled={
toggleLoading || !host.summary_fields.user_capabilities?.edit
}
onChange={() => toggleHost(host)}
aria-label={i18n._(t`Toggle host`)}
/>
</Tooltip>
{host.summary_fields.user_capabilities?.edit && ( {host.summary_fields.user_capabilities?.edit && (
<Tooltip content={i18n._(t`Edit Host`)} position="top"> <Tooltip content={i18n._(t`Edit Host`)} position="top">
<Button variant="plain" component={Link} to={`${editUrl}`}> <Button variant="plain" component={Link} to={`${editUrl}`}>
@@ -110,8 +81,6 @@ InventoryHostItem.propTypes = {
host: Host.isRequired, host: Host.isRequired,
isSelected: bool.isRequired, isSelected: bool.isRequired,
onSelect: func.isRequired, onSelect: func.isRequired,
toggleHost: func.isRequired,
toggleLoading: bool.isRequired,
}; };
export default withI18n()(InventoryHostItem); export default withI18n()(InventoryHostItem);

View File

@@ -2,8 +2,6 @@ import React from 'react';
import { mountWithContexts } from '@testUtils/enzymeHelpers'; import { mountWithContexts } from '@testUtils/enzymeHelpers';
import InventoryHostItem from './InventoryHostItem'; import InventoryHostItem from './InventoryHostItem';
let toggleHost;
const mockHost = { const mockHost = {
id: 1, id: 1,
name: 'Host 1', name: 'Host 1',
@@ -17,65 +15,54 @@ const mockHost = {
user_capabilities: { user_capabilities: {
edit: true, edit: true,
}, },
recent_jobs: [], recent_jobs: [
{
id: 123,
name: 'Demo Job Template',
status: 'failed',
finished: '2020-02-26T22:38:41.037991Z',
},
],
}, },
}; };
describe('<InventoryHostItem />', () => { describe('<InventoryHostItem />', () => {
let wrapper;
beforeEach(() => { beforeEach(() => {
toggleHost = jest.fn(); wrapper = mountWithContexts(
});
afterEach(() => {
jest.clearAllMocks();
});
test('edit button shown to users with edit capabilities', () => {
const wrapper = mountWithContexts(
<InventoryHostItem <InventoryHostItem
isSelected={false} isSelected={false}
detailUrl="/host/1" detailUrl="/host/1"
onSelect={() => {}} onSelect={() => {}}
host={mockHost} host={mockHost}
toggleHost={toggleHost}
toggleLoading={false}
/> />
); );
});
afterEach(() => {
wrapper.unmount();
});
test('edit button shown to users with edit capabilities', () => {
expect(wrapper.find('PencilAltIcon').exists()).toBeTruthy(); expect(wrapper.find('PencilAltIcon').exists()).toBeTruthy();
}); });
test('edit button hidden from users without edit capabilities', () => { test('edit button hidden from users without edit capabilities', () => {
const copyMockHost = Object.assign({}, mockHost); const copyMockHost = Object.assign({}, mockHost);
copyMockHost.summary_fields.user_capabilities.edit = false; copyMockHost.summary_fields.user_capabilities.edit = false;
const wrapper = mountWithContexts( wrapper = mountWithContexts(
<InventoryHostItem <InventoryHostItem
isSelected={false} isSelected={false}
detailUrl="/host/1" detailUrl="/host/1"
onSelect={() => {}} onSelect={() => {}}
host={copyMockHost} host={copyMockHost}
toggleHost={toggleHost}
toggleLoading={false}
/> />
); );
expect(wrapper.find('PencilAltIcon').exists()).toBeFalsy(); expect(wrapper.find('PencilAltIcon').exists()).toBeFalsy();
}); });
test('handles toggle click when host is enabled', () => { test('should display host toggle', () => {
const wrapper = mountWithContexts( expect(wrapper.find('HostToggle').length).toBe(1);
<InventoryHostItem
isSelected={false}
detailUrl="/host/1"
onSelect={() => {}}
host={mockHost}
toggleHost={toggleHost}
toggleLoading={false}
/>
);
wrapper
.find('Switch')
.first()
.find('input')
.simulate('change');
expect(toggleHost).toHaveBeenCalledWith(mockHost);
}); });
}); });

View File

@@ -28,8 +28,6 @@ function InventoryHostList({ i18n, location, match }) {
const [hosts, setHosts] = useState([]); const [hosts, setHosts] = useState([]);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [selected, setSelected] = useState([]); const [selected, setSelected] = useState([]);
const [toggleError, setToggleError] = useState(null);
const [toggleLoading, setToggleLoading] = useState(null);
const fetchHosts = (id, queryString) => { const fetchHosts = (id, queryString) => {
const params = parseQueryString(QS_CONFIG, queryString); const params = parseQueryString(QS_CONFIG, queryString);
@@ -100,24 +98,6 @@ function InventoryHostList({ i18n, location, match }) {
} }
}; };
const handleToggle = async hostToToggle => {
setToggleLoading(hostToToggle.id);
try {
const { data: updatedHost } = await HostsAPI.update(hostToToggle.id, {
enabled: !hostToToggle.enabled,
});
setHosts(
hosts.map(host => (host.id === updatedHost.id ? updatedHost : host))
);
} catch (error) {
setToggleError(error);
} finally {
setToggleLoading(null);
}
};
const canAdd = const canAdd =
actions && Object.prototype.hasOwnProperty.call(actions, 'POST'); actions && Object.prototype.hasOwnProperty.call(actions, 'POST');
const isAllSelected = selected.length > 0 && selected.length === hosts.length; const isAllSelected = selected.length > 0 && selected.length === hosts.length;
@@ -184,8 +164,6 @@ function InventoryHostList({ i18n, location, match }) {
editUrl={`/inventories/inventory/${match.params.id}/hosts/${o.id}/edit`} editUrl={`/inventories/inventory/${match.params.id}/hosts/${o.id}/edit`}
isSelected={selected.some(row => row.id === o.id)} isSelected={selected.some(row => row.id === o.id)}
onSelect={() => handleSelect(o)} onSelect={() => handleSelect(o)}
toggleHost={handleToggle}
toggleLoading={toggleLoading === o.id}
/> />
)} )}
emptyStateControls={ emptyStateControls={
@@ -197,19 +175,6 @@ function InventoryHostList({ i18n, location, match }) {
) )
} }
/> />
{toggleError && !toggleLoading && (
<AlertModal
variant="error"
title={i18n._(t`Error!`)}
isOpen={toggleError && !toggleLoading}
onClose={() => setToggleError(false)}
>
{i18n._(t`Failed to toggle host.`)}
<ErrorDetail error={toggleError} />
</AlertModal>
)}
{deletionError && ( {deletionError && (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={deletionError}

View File

@@ -60,7 +60,14 @@ const mockHosts = [
delete: false, delete: false,
update: false, update: false,
}, },
recent_jobs: [], recent_jobs: [
{
id: 123,
name: 'Recent Job',
status: 'success',
finished: '2020-01-27T19:40:36.208728Z',
},
],
}, },
}, },
]; ];