Fix some styling discrepancies between Chrome and Firefox in the workflow viz/output graphs.

Cleans up deleted job/job template use cases.  Show message indicating that the ujt associated with a node has been deleted.
This commit is contained in:
mabashian
2020-01-28 16:01:46 -05:00
parent 2bbcd2d663
commit fc3f19bd2b
6 changed files with 122 additions and 85 deletions

View File

@@ -1,7 +1,8 @@
import React, { Fragment } from 'react'; import React from 'react';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t, Trans } from '@lingui/macro';
import styled from 'styled-components'; import styled from 'styled-components';
import { ExclamationTriangleIcon } from '@patternfly/react-icons';
import { shape } from 'prop-types'; import { shape } from 'prop-types';
import { secondsToHHMMSS } from '@util/dates'; import { secondsToHHMMSS } from '@util/dates';
@@ -18,11 +19,23 @@ const GridDL = styled.dl`
} }
`; `;
const ResourceDeleted = styled.p`
margin-bottom: ${props => (props.job ? '10px' : '0px')};
`;
const StyledExclamationTriangleIcon = styled(ExclamationTriangleIcon)`
color: #f0ad4d;
height: 20px;
margin-right: 10px;
width: 20px;
`;
function WorkflowNodeHelp({ node, i18n }) { function WorkflowNodeHelp({ node, i18n }) {
let nodeType; let nodeType;
if (node.unifiedJobTemplate) { if (node.unifiedJobTemplate || node.job) {
const type = const type = node.unifiedJobTemplate
node.unifiedJobTemplate.unified_job_type || node.unifiedJobTemplate.type; ? node.unifiedJobTemplate.unified_job_type || node.unifiedJobTemplate.type
: node.job.type;
switch (type) { switch (type) {
case 'job_template': case 'job_template':
case 'job': case 'job':
@@ -97,43 +110,59 @@ function WorkflowNodeHelp({ node, i18n }) {
} }
return ( return (
<Fragment> <>
<GridDL> {!node.unifiedJobTemplate && (
{node.unifiedJobTemplate && ( <>
<Fragment> <ResourceDeleted job={node.job}>
<dt> <StyledExclamationTriangleIcon />
<b>{i18n._(t`Name`)}</b> <Trans>
</dt> The resource associated with this node has been deleted.
<dd id="workflow-node-help-name">{node.unifiedJobTemplate.name}</dd> </Trans>
<dt> </ResourceDeleted>
<b>{i18n._(t`Type`)}</b> </>
</dt> )}
<dd id="workflow-node-help-type">{nodeType}</dd> {node.job && (
</Fragment> <GridDL>
)} <dt>
{node.job && ( <b>{i18n._(t`Name`)}</b>
<Fragment> </dt>
<dt> <dd id="workflow-node-help-name">{node.job.name}</dd>
<b>{i18n._(t`Job Status`)}</b> <dt>
</dt> <b>{i18n._(t`Type`)}</b>
<dd id="workflow-node-help-status">{jobStatus}</dd> </dt>
{node.job.elapsed && ( <dd id="workflow-node-help-type">{nodeType}</dd>
<Fragment> <dt>
<dt> <b>{i18n._(t`Job Status`)}</b>
<b>{i18n._(t`Elapsed`)}</b> </dt>
</dt> <dd id="workflow-node-help-status">{jobStatus}</dd>
<dd id="workflow-node-help-elapsed"> {node.job.elapsed && (
{secondsToHHMMSS(node.job.elapsed)} <>
</dd> <dt>
</Fragment> <b>{i18n._(t`Elapsed`)}</b>
)} </dt>
</Fragment> <dd id="workflow-node-help-elapsed">
)} {secondsToHHMMSS(node.job.elapsed)}
</GridDL> </dd>
</>
)}
</GridDL>
)}
{node.unifiedJobTemplate && !node.job && (
<GridDL>
<dt>
<b>{i18n._(t`Name`)}</b>
</dt>
<dd id="workflow-node-help-name">{node.unifiedJobTemplate.name}</dd>
<dt>
<b>{i18n._(t`Type`)}</b>
</dt>
<dd id="workflow-node-help-type">{nodeType}</dd>
</GridDL>
)}
{node.job && ( {node.job && (
<p css="margin-top: 10px">{i18n._(t`Click to view job details`)}</p> <p css="margin-top: 10px">{i18n._(t`Click to view job details`)}</p>
)} )}
</Fragment> </>
); );
} }

View File

@@ -10,6 +10,7 @@ describe('WorkflowNodeHelp', () => {
test('renders the expected content for a completed job template job', () => { test('renders the expected content for a completed job template job', () => {
const node = { const node = {
job: { job: {
name: 'Foo Job Template',
elapsed: 9000, elapsed: 9000,
status: 'successful', status: 'successful',
}, },

View File

@@ -3,13 +3,15 @@ import styled from 'styled-components';
import { shape } from 'prop-types'; import { shape } from 'prop-types';
import { PauseIcon } from '@patternfly/react-icons'; import { PauseIcon } from '@patternfly/react-icons';
const NodeTypeLetter = styled.foreignObject` const NodeTypeLetter = styled.div`
background-color: #393f43; background-color: #393f43;
border-radius: 50%; border-radius: 50%;
color: white; color: white;
font-size: 10px; font-size: 10px;
line-height: 20px; line-height: 20px;
text-align: center; text-align: center;
height: 20px;
width: 20px;
`; `;
const CenteredPauseIcon = styled(PauseIcon)` const CenteredPauseIcon = styled(PauseIcon)`
@@ -19,11 +21,14 @@ const CenteredPauseIcon = styled(PauseIcon)`
function WorkflowNodeTypeLetter({ node }) { function WorkflowNodeTypeLetter({ node }) {
let nodeTypeLetter; let nodeTypeLetter;
if ( if (
node.unifiedJobTemplate && (node.unifiedJobTemplate &&
(node.unifiedJobTemplate.type || node.unifiedJobTemplate.unified_job_type) (node.unifiedJobTemplate.type ||
node.unifiedJobTemplate.unified_job_type)) ||
(node.job && node.job.type)
) { ) {
const ujtType = const ujtType = node.unifiedJobTemplate
node.unifiedJobTemplate.type || node.unifiedJobTemplate.unified_job_type; ? node.unifiedJobTemplate.type || node.unifiedJobTemplate.unified_job_type
: node.job.type;
switch (ujtType) { switch (ujtType) {
case 'job_template': case 'job_template':
case 'job': case 'job':
@@ -51,9 +56,9 @@ function WorkflowNodeTypeLetter({ node }) {
} }
return ( return (
<NodeTypeLetter y="50" x="-10" height="20" width="20"> <foreignObject y="50" x="-10" height="20" width="20">
{nodeTypeLetter} <NodeTypeLetter>{nodeTypeLetter}</NodeTypeLetter>
</NodeTypeLetter> </foreignObject>
); );
} }

View File

@@ -43,7 +43,7 @@ const Elapsed = styled.div`
} }
`; `;
const NodeContents = styled.foreignObject` const NodeContents = styled.div`
font-size: 13px; font-size: 13px;
padding: 0px 10px; padding: 0px 10px;
`; `;
@@ -98,29 +98,28 @@ function WorkflowOutputNode({ history, i18n, mouseEnter, mouseLeave, node }) {
strokeWidth="2px" strokeWidth="2px"
width={wfConstants.nodeW} width={wfConstants.nodeW}
/> />
<NodeContents height="60" width="180"> <foreignObject height="58" width="178" x="1" y="1">
{node.job ? ( <NodeContents>
<> {node.job ? (
<JobTopLine> <>
<StatusIcon status={node.job.status} /> <JobTopLine>
<p> <StatusIcon status={node.job.status} />
{node.unifiedJobTemplate <p>{node.job.name}</p>
? node.unifiedJobTemplate.name </JobTopLine>
: i18n._(t`DELETED`)} <Elapsed>{secondsToHHMMSS(node.job.elapsed)}</Elapsed>
</p> </>
</JobTopLine> ) : (
<Elapsed>{secondsToHHMMSS(node.job.elapsed)}</Elapsed> <NodeDefaultLabel>
</> {node.unifiedJobTemplate
) : ( ? node.unifiedJobTemplate.name
<NodeDefaultLabel> : i18n._(t`DELETED`)}
{node.unifiedJobTemplate </NodeDefaultLabel>
? node.unifiedJobTemplate.name )}
: i18n._(t`DELETED`)} </NodeContents>
</NodeDefaultLabel> </foreignObject>
)} {(node.unifiedJobTemplate || node.job) && (
</NodeContents> <WorkflowNodeTypeLetter node={node} />
<circle cy="60" r="10" fill="#393F43" /> )}
{node.unifiedJobTemplate && <WorkflowNodeTypeLetter node={node} />}
</NodeG> </NodeG>
); );
} }

View File

@@ -24,7 +24,7 @@ const nodeWithoutJT = {
job: { job: {
elapsed: 7, elapsed: 7,
id: 9000, id: 9000,
name: 'Automation JT', name: 'Automation JT 2',
status: 'successful', status: 'successful',
type: 'job', type: 'job',
}, },
@@ -87,7 +87,7 @@ describe('WorkflowOutputNode', () => {
</WorkflowStateContext.Provider> </WorkflowStateContext.Provider>
</svg> </svg>
); );
expect(wrapper.contains(<p>DELETED</p>)).toEqual(true); expect(wrapper.contains(<p>Automation JT 2</p>)).toEqual(true);
expect(wrapper.find('WorkflowOutputNode__Elapsed').text()).toBe('00:00:07'); expect(wrapper.find('WorkflowOutputNode__Elapsed').text()).toBe('00:00:07');
}); });
test('node contents displayed correctly when Job deleted', () => { test('node contents displayed correctly when Job deleted', () => {

View File

@@ -26,7 +26,7 @@ const NodeG = styled.g`
cursor: ${props => (props.job ? 'pointer' : 'default')}; cursor: ${props => (props.job ? 'pointer' : 'default')};
`; `;
const NodeContents = styled.foreignObject` const NodeContents = styled.div`
font-size: 13px; font-size: 13px;
padding: 0px 10px; padding: 0px 10px;
background-color: ${props => background-color: ${props =>
@@ -183,25 +183,28 @@ function VisualizerNode({
? '#007ABC' ? '#007ABC'
: '#93969A' : '#93969A'
} }
strokeWidth="4px" strokeWidth="2px"
width={wfConstants.nodeW} width={wfConstants.nodeW}
/> />
<NodeContents <foreignObject
height="60" height="58"
isInvalidLinkTarget={node.isInvalidLinkTarget}
{...(!addingLink && { {...(!addingLink && {
onMouseEnter: () => updateNodeHelp(node), onMouseEnter: () => updateNodeHelp(node),
onMouseLeave: () => updateNodeHelp(null), onMouseLeave: () => updateNodeHelp(null),
})} })}
onClick={() => handleNodeClick()} onClick={() => handleNodeClick()}
width="180" width="178"
x="1"
y="1"
> >
<NodeDefaultLabel> <NodeContents isInvalidLinkTarget={node.isInvalidLinkTarget}>
{node.unifiedJobTemplate <NodeDefaultLabel>
? node.unifiedJobTemplate.name {node.unifiedJobTemplate
: i18n._(t`DELETED`)} ? node.unifiedJobTemplate.name
</NodeDefaultLabel> : i18n._(t`DELETED`)}
</NodeContents> </NodeDefaultLabel>
</NodeContents>
</foreignObject>
{node.unifiedJobTemplate && <WorkflowNodeTypeLetter node={node} />} {node.unifiedJobTemplate && <WorkflowNodeTypeLetter node={node} />}
{hovering && !addingLink && ( {hovering && !addingLink && (
<WorkflowActionTooltip <WorkflowActionTooltip