Combines the two start node components into one. Removes use of document.getElementById in workflow components in favor of refs.

This commit is contained in:
mabashian 2020-01-24 11:24:46 -05:00
parent f98b274177
commit eddb6e1faf
12 changed files with 82 additions and 101 deletions

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useRef, useState } from 'react';
import styled from 'styled-components';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
@ -14,19 +14,19 @@ const StartG = styled.g`
pointer-events: ${props => (props.ignorePointerEvents ? 'none' : 'auto')};
`;
function VisualizerStartNode({
function WorkflowStartNode({
addingLink,
i18n,
nodePositions,
onAddNodeClick,
onUpdateHelpText,
readOnly,
showActionTooltip,
}) {
const ref = useRef(null);
const [hovering, setHovering] = useState(false);
const handleNodeMouseEnter = () => {
const nodeEl = document.getElementById('node-1');
nodeEl.parentNode.appendChild(nodeEl);
ref.current.parentNode.appendChild(ref.current);
setHovering(true);
};
@ -36,6 +36,7 @@ function VisualizerStartNode({
ignorePointerEvents={addingLink}
onMouseEnter={handleNodeMouseEnter}
onMouseLeave={() => setHovering(false)}
ref={ref}
transform={`translate(${nodePositions[1].x},0)`}
>
<rect
@ -50,7 +51,7 @@ function VisualizerStartNode({
<text x="13" y="30" dy=".35em" fill="white">
START
</text>
{!readOnly && hovering && (
{showActionTooltip && hovering && (
<WorkflowActionTooltip
actions={[
<WorkflowActionTooltipItem
@ -75,12 +76,18 @@ function VisualizerStartNode({
);
}
VisualizerStartNode.propTypes = {
addingLink: bool.isRequired,
WorkflowStartNode.propTypes = {
addingLink: bool,
nodePositions: shape().isRequired,
onAddNodeClick: func.isRequired,
readOnly: bool.isRequired,
onUpdateHelpText: func.isRequired,
onAddNodeClick: func,
showActionTooltip: bool.isRequired,
onUpdateHelpText: func,
};
export default withI18n()(VisualizerStartNode);
WorkflowStartNode.defaultProps = {
addingLink: false,
onAddNodeClick: () => {},
onUpdateHelpText: () => {},
};
export default withI18n()(WorkflowStartNode);

View File

@ -0,0 +1,36 @@
import React from 'react';
import { mount } from 'enzyme';
import WorkflowStartNode from './WorkflowStartNode';
const nodePositions = {
1: {
x: 0,
y: 0,
},
};
describe('WorkflowStartNode', () => {
test('mounts successfully', () => {
const wrapper = mount(
<svg>
<WorkflowStartNode
nodePositions={nodePositions}
showActionTooltip={false}
/>
</svg>
);
expect(wrapper).toHaveLength(1);
});
test('tooltip shown on hover', () => {
const wrapper = mount(
<svg>
<WorkflowStartNode nodePositions={nodePositions} showActionTooltip />
</svg>
);
expect(wrapper.find('WorkflowActionTooltip')).toHaveLength(0);
wrapper.find('WorkflowStartNode').simulate('mouseenter');
expect(wrapper.find('WorkflowActionTooltip')).toHaveLength(1);
wrapper.find('WorkflowStartNode').simulate('mouseleave');
expect(wrapper.find('WorkflowActionTooltip')).toHaveLength(0);
});
});

View File

@ -7,4 +7,5 @@ export { default as WorkflowKey } from './WorkflowKey';
export { default as WorkflowLinkHelp } from './WorkflowLinkHelp';
export { default as WorkflowNodeHelp } from './WorkflowNodeHelp';
export { default as WorkflowNodeTypeLetter } from './WorkflowNodeTypeLetter';
export { default as WorkflowStartNode } from './WorkflowStartNode';
export { default as WorkflowTools } from './WorkflowTools';

View File

@ -8,13 +8,13 @@ import {
import {
WorkflowOutputLink,
WorkflowOutputNode,
WorkflowOutputStartNode,
} from '@screens/Job/WorkflowOutput';
import {
WorkflowHelp,
WorkflowKey,
WorkflowLinkHelp,
WorkflowNodeHelp,
WorkflowStartNode,
WorkflowTools,
} from '@components/Workflow';
@ -75,8 +75,7 @@ function WorkflowOutputGraph({
};
const handlePanToMiddle = () => {
const svgElement = document.getElementById('workflow-svg');
const svgBoundingClientRect = svgElement.getBoundingClientRect();
const svgBoundingClientRect = svgRef.current.getBoundingClientRect();
d3.select(svgRef.current).call(
zoomRef.transform,
d3.zoomIdentity
@ -88,8 +87,7 @@ function WorkflowOutputGraph({
};
const handleZoomChange = newScale => {
const svgElement = document.getElementById('workflow-svg');
const svgBoundingClientRect = svgElement.getBoundingClientRect();
const svgBoundingClientRect = svgRef.current.getBoundingClientRect();
const currentScaleAndOffset = d3.zoomTransform(
d3.select(svgRef.current).node()
);
@ -121,8 +119,7 @@ function WorkflowOutputGraph({
.node()
.getBBox();
const svgElement = document.getElementById('workflow-svg');
const svgBoundingClientRect = svgElement.getBoundingClientRect();
const svgBoundingClientRect = svgRef.current.getBoundingClientRect();
const [scaleToFit, yTranslate] = getScaleAndOffsetToFit(
gBoundingClientRect,
@ -175,9 +172,10 @@ function WorkflowOutputGraph({
>
<g id="workflow-g" ref={gRef}>
{nodePositions && [
<WorkflowOutputStartNode
<WorkflowStartNode
key="start"
nodePositions={nodePositions}
showActionTooltip={false}
/>,
links.map(link => (
<WorkflowOutputLink

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { shape } from 'prop-types';
import {
generateLine,
@ -7,23 +7,18 @@ import {
} from '@util/workflow';
function WorkflowOutputLink({ link, nodePositions, onUpdateLinkHelp }) {
const ref = useRef(null);
const [hovering, setHovering] = useState(false);
const [pathD, setPathD] = useState();
const [pathStroke, setPathStroke] = useState('#CCCCCC');
const handleLinkMouseEnter = () => {
const linkEl = document.getElementById(
`link-${link.source.id}-${link.target.id}`
);
linkEl.parentNode.appendChild(linkEl);
ref.current.parentNode.appendChild(ref.current);
setHovering(true);
};
const handleLinkMouseLeave = () => {
const linkEl = document.getElementById(
`link-${link.source.id}-${link.target.id}`
);
linkEl.parentNode.prepend(linkEl);
ref.current.parentNode.prepend(ref.current);
setHovering(null);
};
@ -46,6 +41,7 @@ function WorkflowOutputLink({ link, nodePositions, onUpdateLinkHelp }) {
return (
<g
ref={ref}
id={`link-${link.source.id}-${link.target.id}`}
onMouseEnter={handleLinkMouseEnter}
onMouseLeave={handleLinkMouseLeave}

View File

@ -1,28 +0,0 @@
import React from 'react';
import { shape } from 'prop-types';
import { constants as wfConstants } from '@util/workflow';
function WorkflowOutputStartNode({ nodePositions }) {
return (
<g id="node-1" transform={`translate(${nodePositions[1].x},0)`}>
<rect
fill="#0279BC"
height={wfConstants.rootH}
rx="2"
ry="2"
width={wfConstants.rootW}
y="10"
/>
{/* TODO: We need to be able to handle translated text here */}
<text x="13" y="30" dy=".35em" fill="white">
START
</text>
</g>
);
}
WorkflowOutputStartNode.propTypes = {
nodePositions: shape().isRequired,
};
export default WorkflowOutputStartNode;

View File

@ -1,21 +0,0 @@
import React from 'react';
import { mount } from 'enzyme';
import WorkflowOutputStartNode from './WorkflowOutputStartNode';
const nodePositions = {
1: {
x: 0,
y: 0,
},
};
describe('WorkflowOutputStartNode', () => {
test('mounts successfully', () => {
const wrapper = mount(
<svg>
<WorkflowOutputStartNode nodePositions={nodePositions} />
</svg>
);
expect(wrapper).toHaveLength(1);
});
});

View File

@ -2,5 +2,4 @@ export { default as WorkflowOutput } from './WorkflowOutput';
export { default as WorkflowOutputGraph } from './WorkflowOutputGraph';
export { default as WorkflowOutputLink } from './WorkflowOutputLink';
export { default as WorkflowOutputNode } from './WorkflowOutputNode';
export { default as WorkflowOutputStartNode } from './WorkflowOutputStartNode';
export { default as WorkflowOutputToolbar } from './WorkflowOutputToolbar';

View File

@ -14,12 +14,12 @@ import {
WorkflowKey,
WorkflowLinkHelp,
WorkflowNodeHelp,
WorkflowStartNode,
WorkflowTools,
} from '@components/Workflow';
import {
VisualizerLink,
VisualizerNode,
VisualizerStartNode,
} from '@screens/Template/WorkflowJobTemplateVisualizer';
const PotentialLink = styled.polyline`
@ -148,8 +148,7 @@ function VisualizerGraph({
};
const handlePanToMiddle = () => {
const svgElement = document.getElementById('workflow-svg');
const svgBoundingClientRect = svgElement.getBoundingClientRect();
const svgBoundingClientRect = svgRef.current.getBoundingClientRect();
d3.select(svgRef.current).call(
zoomRef.transform,
d3.zoomIdentity
@ -161,8 +160,7 @@ function VisualizerGraph({
};
const handleZoomChange = newScale => {
const svgElement = document.getElementById('workflow-svg');
const svgBoundingClientRect = svgElement.getBoundingClientRect();
const svgBoundingClientRect = svgRef.current.getBoundingClientRect();
const currentScaleAndOffset = d3.zoomTransform(
d3.select(svgRef.current).node()
);
@ -194,8 +192,7 @@ function VisualizerGraph({
.node()
.getBBox();
const svgElement = document.getElementById('workflow-svg');
const svgBoundingClientRect = svgElement.getBoundingClientRect();
const svgBoundingClientRect = svgRef.current.getBoundingClientRect();
const [scaleToFit, yTranslate] = getScaleAndOffsetToFit(
gBoundingClientRect,
@ -276,12 +273,12 @@ function VisualizerGraph({
/>
<g id="workflow-g" ref={gRef}>
{nodePositions && [
<VisualizerStartNode
<WorkflowStartNode
addingLink={addingLink}
key="start"
nodePositions={nodePositions}
onAddNodeClick={onAddNodeClick}
readOnly={readOnly}
showActionTooltip={!readOnly}
onUpdateHelpText={setHelpText}
/>,
links.map(link => {

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import styled from 'styled-components';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
@ -30,6 +30,7 @@ function VisualizerLink({
onUpdateLinkHelp,
readOnly,
}) {
const ref = useRef(null);
const [hovering, setHovering] = useState(false);
const [pathD, setPathD] = useState();
const [pathStroke, setPathStroke] = useState('#CCCCCC');
@ -80,18 +81,12 @@ function VisualizerLink({
];
const handleLinkMouseEnter = () => {
const linkEl = document.getElementById(
`link-${link.source.id}-${link.target.id}`
);
linkEl.parentNode.appendChild(linkEl);
ref.current.parentNode.appendChild(ref.current);
setHovering(true);
};
const handleLinkMouseLeave = () => {
const linkEl = document.getElementById(
`link-${link.source.id}-${link.target.id}`
);
linkEl.parentNode.prepend(linkEl);
ref.current.parentNode.prepend(ref.current);
setHovering(null);
};
@ -120,6 +115,7 @@ function VisualizerLink({
ignorePointerEvents={addingLink}
onMouseEnter={handleLinkMouseEnter}
onMouseLeave={handleLinkMouseLeave}
ref={ref}
>
<polygon
fill="#E1E1E1"

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useRef, useState } from 'react';
import styled from 'styled-components';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
@ -54,11 +54,11 @@ function VisualizerNode({
onUpdateHelpText,
updateNodeHelp,
}) {
const ref = useRef(null);
const [hovering, setHovering] = useState(false);
const handleNodeMouseEnter = () => {
const nodeEl = document.getElementById(`node-${node.id}`);
nodeEl.parentNode.appendChild(nodeEl);
ref.current.parentNode.appendChild(ref.current);
setHovering(true);
if (addingLink) {
onUpdateHelpText(
@ -168,6 +168,7 @@ function VisualizerNode({
noPointerEvents={isAddLinkSourceNode}
onMouseEnter={handleNodeMouseEnter}
onMouseLeave={handleNodeMouseLeave}
ref={ref}
transform={`translate(${nodePositions[node.id].x},${nodePositions[node.id]
.y - nodePositions[1].y})`}
>

View File

@ -2,6 +2,5 @@ export { default as Visualizer } from './Visualizer';
export { default as VisualizerGraph } from './VisualizerGraph';
export { default as VisualizerLink } from './VisualizerLink';
export { default as VisualizerNode } from './VisualizerNode';
export { default as VisualizerStartNode } from './VisualizerStartNode';
export { default as VisualizerStartScreen } from './VisualizerStartScreen';
export { default as VisualizerToolbar } from './VisualizerToolbar';