mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-11-02 03:29:03 +00:00
* #14023 Unable to Zoom on graph view of tasks under airflow dag * added dependency * updated style as per the mock * updated controls as per new style and addressing review comment
This commit is contained in:
parent
4b1befacaf
commit
65a910f885
@ -75,6 +75,7 @@ import EntityRightPanel from '../Entity/EntityRightPanel/EntityRightPanel';
|
||||
import { ModalWithMarkdownEditor } from '../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor';
|
||||
import { usePermissionProvider } from '../PermissionProvider/PermissionProvider';
|
||||
import { ResourceEntity } from '../PermissionProvider/PermissionProvider.interface';
|
||||
import './pipeline-details.style.less';
|
||||
import { PipeLineDetailsProp } from './PipelineDetails.interface';
|
||||
|
||||
const PipelineDetails = ({
|
||||
@ -524,7 +525,7 @@ const PipelineDetails = ({
|
||||
const tasksDAGView = useMemo(
|
||||
() =>
|
||||
!isEmpty(pipelineDetails.tasks) && !isUndefined(pipelineDetails.tasks) ? (
|
||||
<Card headStyle={{ background: '#fafafa' }} title={t('label.dag-view')}>
|
||||
<Card className="task-dag-view-card" title={t('label.dag-view')}>
|
||||
<div className="h-100">
|
||||
<TasksDAGView
|
||||
selectedExec={selectedExecution}
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2023 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@import (reference) url('../../styles/variables.less');
|
||||
.task-dag-view-card {
|
||||
.ant-card-body {
|
||||
padding: 0px;
|
||||
}
|
||||
.ant-card-head {
|
||||
background: @grey-1;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2023 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { NodeProps } from 'reactflow';
|
||||
import TaskNode from './TaskNode';
|
||||
|
||||
jest.mock('reactflow', () => ({
|
||||
...jest.requireActual('reactflow'),
|
||||
Handle: jest.fn().mockImplementation(() => <div>Handle</div>),
|
||||
}));
|
||||
|
||||
const mockProps = {
|
||||
data: {
|
||||
label: 'Test Label',
|
||||
taskStatus: 'Success',
|
||||
},
|
||||
} as NodeProps;
|
||||
|
||||
describe('TaskNode', () => {
|
||||
it('component should render', async () => {
|
||||
render(<TaskNode {...mockProps} />);
|
||||
|
||||
expect(
|
||||
await screen.findByTestId('task-node-container')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByTestId('node-label')).toBeInTheDocument();
|
||||
expect(await screen.findByTestId('node-label-status')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render correct label', async () => {
|
||||
render(<TaskNode {...mockProps} />);
|
||||
|
||||
expect(await screen.findByText(mockProps.data.label)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should append class based on status', async () => {
|
||||
render(<TaskNode {...mockProps} />);
|
||||
const status = await screen.findByTestId('node-label-status');
|
||||
|
||||
expect(status).toHaveClass(mockProps.data.taskStatus);
|
||||
});
|
||||
});
|
||||
@ -11,16 +11,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Space } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import React, { CSSProperties, Fragment } from 'react';
|
||||
import { Handle, HandleType, NodeProps, Position } from 'reactflow';
|
||||
import { EntityLineageNodeType } from '../../enums/entity.enum';
|
||||
import { EntityLineageNodeType } from '../../../enums/entity.enum';
|
||||
import './task-node.style.less';
|
||||
|
||||
const handleStyles = {
|
||||
width: '8px',
|
||||
height: '8px',
|
||||
borderRadius: '50%',
|
||||
position: 'absolute',
|
||||
top: 10,
|
||||
opacity: 0,
|
||||
height: '1px',
|
||||
width: '1px',
|
||||
};
|
||||
|
||||
const renderHandle = (position: Position, isConnectable: boolean) => {
|
||||
@ -28,8 +29,10 @@ const renderHandle = (position: Position, isConnectable: boolean) => {
|
||||
let type: HandleType;
|
||||
if (position === Position.Left) {
|
||||
type = 'target';
|
||||
styles.left = '10px';
|
||||
} else {
|
||||
type = 'source';
|
||||
styles.right = '10px';
|
||||
}
|
||||
|
||||
return (
|
||||
@ -59,15 +62,24 @@ const getHandle = (nodeType: string, isConnectable: boolean) => {
|
||||
|
||||
const TaskNode = (props: NodeProps) => {
|
||||
const { data, type, isConnectable } = props;
|
||||
const { label } = data;
|
||||
const { label, taskStatus } = data;
|
||||
|
||||
return (
|
||||
<div className="task-node relative nowheel bg-primary-lite border border-primary rounded-6 p-x-sm">
|
||||
<div
|
||||
className="task-node relative nowheel border rounded-6 p-x-sm"
|
||||
data-testid="task-node-container">
|
||||
{getHandle(type, isConnectable)}
|
||||
{/* Node label could be simple text or reactNode */}
|
||||
<div className="p-x-sm p-y-sm" data-testid="node-label">
|
||||
<Space className="p-x-sm p-y-sm w-full" data-testid="node-label">
|
||||
<div
|
||||
className={classNames(
|
||||
'custom-node-label',
|
||||
taskStatus ? taskStatus : 'bg-primary'
|
||||
)}
|
||||
data-testid="node-label-status"
|
||||
/>
|
||||
{label}
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2023 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
@import (reference) url('../../../styles/variables.less');
|
||||
|
||||
.custom-node-label {
|
||||
border-radius: 50%;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.leaf-node .task-node .Successful {
|
||||
background-color: @green-3;
|
||||
}
|
||||
|
||||
.leaf-node .task-node .Failed {
|
||||
background-color: @red-3;
|
||||
}
|
||||
|
||||
.leaf-node .task-node .Pending {
|
||||
background-color: @yellow-2;
|
||||
}
|
||||
@ -11,21 +11,27 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import classNames from 'classnames';
|
||||
import React, { Fragment, useCallback, useEffect, useMemo } from 'react';
|
||||
import ReactFlow, {
|
||||
Background,
|
||||
Controls,
|
||||
Edge,
|
||||
MarkerType,
|
||||
useEdgesState,
|
||||
useNodesState,
|
||||
} from 'reactflow';
|
||||
import {
|
||||
MAX_ZOOM_VALUE,
|
||||
MIN_ZOOM_VALUE,
|
||||
} from '../../constants/Lineage.constants';
|
||||
import { EntityLineageNodeType } from '../../enums/entity.enum';
|
||||
import { PipelineStatus, Task } from '../../generated/entity/data/pipeline';
|
||||
import { replaceSpaceWith_ } from '../../utils/CommonUtils';
|
||||
import { getLayoutedElements, onLoad } from '../../utils/EntityLineageUtils';
|
||||
import { getEntityName } from '../../utils/EntityUtils';
|
||||
import { getTaskExecStatus } from '../../utils/PipelineDetailsUtils';
|
||||
import TaskNode from './TaskNode';
|
||||
import TaskNode from './TaskNode/TaskNode';
|
||||
import './tasks-dag-view.style.less';
|
||||
|
||||
export interface Props {
|
||||
tasks: Task[];
|
||||
@ -72,11 +78,12 @@ const TasksDAGView = ({ tasks, selectedExec }: Props) => {
|
||||
);
|
||||
|
||||
return {
|
||||
className: classNames('leaf-node', taskStatus),
|
||||
className: 'leaf-node',
|
||||
id: replaceSpaceWith_(task.name),
|
||||
type: getNodeType(task),
|
||||
data: {
|
||||
label: getEntityName(task),
|
||||
taskStatus,
|
||||
},
|
||||
position: { x: 0, y: 0 },
|
||||
isConnectable: false,
|
||||
@ -114,8 +121,8 @@ const TasksDAGView = ({ tasks, selectedExec }: Props) => {
|
||||
<ReactFlow
|
||||
data-testid="react-flow-component"
|
||||
edges={edgesData}
|
||||
maxZoom={2}
|
||||
minZoom={0.5}
|
||||
maxZoom={MAX_ZOOM_VALUE}
|
||||
minZoom={MIN_ZOOM_VALUE}
|
||||
nodeTypes={nodeTypes}
|
||||
nodes={nodesData}
|
||||
selectNodesOnDrag={false}
|
||||
@ -125,8 +132,14 @@ const TasksDAGView = ({ tasks, selectedExec }: Props) => {
|
||||
onInit={(reactFlowInstance) => {
|
||||
onLoad(reactFlowInstance);
|
||||
}}
|
||||
onNodesChange={onNodesChange}
|
||||
/>
|
||||
onNodesChange={onNodesChange}>
|
||||
<Background gap={12} size={1} />
|
||||
<Controls
|
||||
className="task-dag-control-btn"
|
||||
position="bottom-right"
|
||||
showInteractive={false}
|
||||
/>
|
||||
</ReactFlow>
|
||||
) : (
|
||||
<Fragment />
|
||||
);
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2024 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
.task-dag-control-btn {
|
||||
.react-flow__controls-button {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
}
|
||||
@ -605,35 +605,6 @@ a[href].link-text-grey,
|
||||
padding-right: 60px !important;
|
||||
}
|
||||
|
||||
// ********* Leaf Node **********
|
||||
|
||||
.leaf-node.Successful .task-node {
|
||||
border-color: @green-1;
|
||||
background-color: @success-color;
|
||||
}
|
||||
|
||||
.leaf-node.Failed .task-node {
|
||||
border-color: @failed-color;
|
||||
background-color: @error-light-color;
|
||||
}
|
||||
|
||||
.leaf-node.Pending .task-node {
|
||||
border-color: @primary-color;
|
||||
background-color: @link-color;
|
||||
}
|
||||
|
||||
.leaf-node.Successful .react-flow__handle {
|
||||
background-color: @green-1 !important;
|
||||
}
|
||||
|
||||
.leaf-node.Failed .react-flow__handle {
|
||||
background-color: @failed-color !important;
|
||||
}
|
||||
|
||||
.leaf-node.Pending .react-flow__handle {
|
||||
background-color: @primary-color !important;
|
||||
}
|
||||
|
||||
.ProseMirror .placeholder {
|
||||
color: @text-grey-muted !important;
|
||||
}
|
||||
@ -658,12 +629,6 @@ a[href].link-text-grey,
|
||||
.react-flow__node {
|
||||
min-width: max-content;
|
||||
}
|
||||
.leaf-node .react-flow__handle {
|
||||
background-color: @text-grey-muted;
|
||||
}
|
||||
.leaf-node.core .react-flow__handle {
|
||||
background-color: @primary-color;
|
||||
}
|
||||
.react-flow__edge {
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
|
||||
@ -87,6 +87,9 @@
|
||||
.top-1 {
|
||||
top: 4px;
|
||||
}
|
||||
.top-4 {
|
||||
top: 16px;
|
||||
}
|
||||
.top-6 {
|
||||
top: 24px;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user