Moves tooltip and link logic out to the sparkline from the job status icon

This commit is contained in:
mabashian
2019-08-08 11:53:47 -04:00
parent fba0da4c58
commit 3d98d98d3c
4 changed files with 45 additions and 105 deletions

View File

@@ -1,8 +1,6 @@
import React, { Fragment } from 'react'; import React from 'react';
import { node, number, shape, string } from 'prop-types'; import { string } from 'prop-types';
import { Link } from 'react-router-dom';
import styled, { keyframes } from 'styled-components'; import styled, { keyframes } from 'styled-components';
import { Tooltip } from '@patternfly/react-core';
const Pulse = keyframes` const Pulse = keyframes`
from { from {
@@ -58,59 +56,31 @@ const FailedBottom = styled.div`
background-color: #d9534f; background-color: #d9534f;
`; `;
const JobStatusIcon = ({ job, link, tooltip, ...props }) => { const JobStatusIcon = ({ status, ...props }) => {
let Icon = ( return (
<Fragment> <div {...props}>
{job.status === 'running' && <RunningJob />} {status === 'running' && <RunningJob />}
{(job.status === 'new' || {(status === 'new' || status === 'pending' || status === 'waiting') && (
job.status === 'pending' || <WaitingJob />
job.status === 'waiting') && <WaitingJob />} )}
{(job.status === 'failed' || {(status === 'failed' || status === 'error' || status === 'canceled') && (
job.status === 'error' ||
job.status === 'canceled') && (
<FinishedJob> <FinishedJob>
<FailedTop /> <FailedTop />
<FailedBottom /> <FailedBottom />
</FinishedJob> </FinishedJob>
)} )}
{job.status === 'successful' && ( {status === 'successful' && (
<FinishedJob> <FinishedJob>
<SuccessfulTop /> <SuccessfulTop />
<SuccessfulBottom /> <SuccessfulBottom />
</FinishedJob> </FinishedJob>
)} )}
</Fragment> </div>
); );
if (link) {
Icon = <Link to={link}>{Icon}</Link>;
}
if (tooltip) {
return (
<div {...props}>
<Tooltip position="top" content={tooltip}>
{Icon}
</Tooltip>
</div>
);
}
return <div {...props}>{Icon}</div>;
}; };
JobStatusIcon.propTypes = { JobStatusIcon.propTypes = {
job: shape({ status: string.isRequired,
id: number.isRequired,
status: string.isRequired,
}).isRequired,
link: string,
tooltip: node,
};
JobStatusIcon.defaultProps = {
link: null,
tooltip: null,
}; };
export default JobStatusIcon; export default JobStatusIcon;

View File

@@ -1,63 +1,28 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
import JobStatusIcon from './JobStatusIcon'; import JobStatusIcon from './JobStatusIcon';
describe('JobStatusIcon', () => { describe('JobStatusIcon', () => {
const job = { test('renders the successful job', () => {
id: 1, const wrapper = mount(<JobStatusIcon status="successful" />);
status: 'successful',
};
test('renders the expected content', () => {
const wrapper = mount(<JobStatusIcon job={job} />);
expect(wrapper).toHaveLength(1); expect(wrapper).toHaveLength(1);
expect(wrapper.find('Tooltip')).toHaveLength(0);
expect(wrapper.find('Link')).toHaveLength(0);
});
test('renders with tooltip if tooltip passed', () => {
const wrapper = mount(<JobStatusIcon job={job} tooltip="Foo Bar" />);
expect(wrapper).toHaveLength(1);
expect(wrapper.find('Tooltip')).toHaveLength(1);
expect(wrapper.find('Link')).toHaveLength(0);
});
test('renders with link if link passed', () => {
const wrapper = mountWithContexts(
<JobStatusIcon job={job} link="/jobs/playbook/1" />
);
expect(wrapper).toHaveLength(1);
expect(wrapper.find('Tooltip')).toHaveLength(0);
expect(wrapper.find('Link')).toHaveLength(1);
});
test('renders running job', () => {
const runningJob = {
id: 2,
status: 'running',
};
const wrapper = mount(<JobStatusIcon job={runningJob} />);
expect(wrapper.find('JobStatusIcon__RunningJob')).toHaveLength(1);
});
test('renders waiting job', () => {
const waitingJob = {
id: 3,
status: 'waiting',
};
const wrapper = mount(<JobStatusIcon job={waitingJob} />);
expect(wrapper.find('JobStatusIcon__WaitingJob')).toHaveLength(1);
});
test('renders failed job', () => {
const failedJob = {
id: 4,
status: 'failed',
};
const wrapper = mount(<JobStatusIcon job={failedJob} />);
expect(wrapper.find('JobStatusIcon__FailedTop')).toHaveLength(1);
expect(wrapper.find('JobStatusIcon__FailedBottom')).toHaveLength(1);
});
test('renders successful job', () => {
const wrapper = mount(<JobStatusIcon job={job} />);
expect(wrapper.find('JobStatusIcon__SuccessfulTop')).toHaveLength(1); expect(wrapper.find('JobStatusIcon__SuccessfulTop')).toHaveLength(1);
expect(wrapper.find('JobStatusIcon__SuccessfulBottom')).toHaveLength(1); expect(wrapper.find('JobStatusIcon__SuccessfulBottom')).toHaveLength(1);
}); });
test('renders running job', () => {
const wrapper = mount(<JobStatusIcon status="running" />);
expect(wrapper).toHaveLength(1);
expect(wrapper.find('JobStatusIcon__RunningJob')).toHaveLength(1);
});
test('renders waiting job', () => {
const wrapper = mount(<JobStatusIcon status="waiting" />);
expect(wrapper).toHaveLength(1);
expect(wrapper.find('JobStatusIcon__WaitingJob')).toHaveLength(1);
});
test('renders failed job', () => {
const wrapper = mount(<JobStatusIcon status="failed" />);
expect(wrapper).toHaveLength(1);
expect(wrapper.find('JobStatusIcon__FailedTop')).toHaveLength(1);
expect(wrapper.find('JobStatusIcon__FailedBottom')).toHaveLength(1);
});
}); });

View File

@@ -1,13 +1,15 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import { arrayOf, object } from 'prop-types'; import { arrayOf, object } from 'prop-types';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { JobStatusIcon as _JobStatusIcon } from '@components/Sparkline'; import { Link as _Link } from 'react-router-dom';
import { JobStatusIcon } from '@components/Sparkline';
import { Tooltip } from '@patternfly/react-core';
import styled from 'styled-components'; import styled from 'styled-components';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { JOB_TYPE_URL_SEGMENTS } from '../../constants'; import { JOB_TYPE_URL_SEGMENTS } from '../../constants';
/* eslint-disable react/jsx-pascal-case */ /* eslint-disable react/jsx-pascal-case */
const JobStatusIcon = styled(props => <_JobStatusIcon {...props} />)` const Link = styled(props => <_Link {...props} />)`
margin-right: 5px; margin-right: 5px;
`; `;
/* eslint-enable react/jsx-pascal-case */ /* eslint-enable react/jsx-pascal-case */
@@ -30,12 +32,11 @@ const Sparkline = ({ i18n, jobs }) => {
); );
return jobs.map(job => ( return jobs.map(job => (
<JobStatusIcon <Tooltip position="top" content={generateTooltip(job)} key={job.id}>
key={job.id} <Link to={`/jobs/${JOB_TYPE_URL_SEGMENTS[job.type]}/${job.id}`}>
job={job} <JobStatusIcon status={job.status} />
link={`/jobs/${JOB_TYPE_URL_SEGMENTS[job.type]}/${job.id}`} </Link>
tooltip={generateTooltip(job)} </Tooltip>
/>
)); ));
}; };

View File

@@ -9,18 +9,22 @@ describe('Sparkline', () => {
const wrapper = mount(<Sparkline />); const wrapper = mount(<Sparkline />);
expect(wrapper).toHaveLength(1); expect(wrapper).toHaveLength(1);
}); });
test('renders an icon for each job', () => { test('renders an icon with tooltips and links for each job', () => {
const jobs = [ const jobs = [
{ {
id: 1, id: 1,
status: 'successful', status: 'successful',
finished: '2019-08-08T15:27:57.320120Z',
}, },
{ {
id: 2, id: 2,
status: 'failed', status: 'failed',
finished: '2019-08-09T15:27:57.320120Z',
}, },
]; ];
const wrapper = mountWithContexts(<Sparkline jobs={jobs} />); const wrapper = mountWithContexts(<Sparkline jobs={jobs} />);
expect(wrapper.find('JobStatusIcon')).toHaveLength(2); expect(wrapper.find('JobStatusIcon')).toHaveLength(2);
expect(wrapper.find('Tooltip')).toHaveLength(2);
expect(wrapper.find('Link')).toHaveLength(2);
}); });
}); });