diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionNodeViews.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionNodeViews.tsx index 006db91d3..2ee22cb3f 100644 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionNodeViews.tsx +++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionNodeViews.tsx @@ -18,11 +18,10 @@ import { useNodeExecutionFiltersState } from '../filters/useExecutionFiltersStat import { NodeExecutionsTable } from '../Tables/NodeExecutionsTable'; import { tabs } from './constants'; import { useExecutionNodeViewsState } from './useExecutionNodeViewsState'; -import { ExecutionNodesTimeline } from './Timeline'; import { fetchTaskExecutionList } from '../taskExecutionQueries'; import { getGroupedLogs } from '../TaskExecutionsList/utils'; import { useAllTreeNodeExecutionGroupsQuery } from '../nodeExecutionQueries'; -import { ExecutionWorkflowGraph } from './ExecutionWorkflowGraph'; +import { ExecutionTab } from './ExecutionTab'; const useStyles = makeStyles((theme: Theme) => ({ filters: { @@ -148,24 +147,13 @@ export const ExecutionNodeViews: React.FC = ({ executio ); - const renderExecutionChildrenLoader = () => - nodeExecutions.length > 0 ? : null; - - const renderExecutionLoader = () => { - return ( - - {renderExecutionChildrenLoader} - - ); - }; - - const renderExecutionsTimeline = () => ( + const renderTab = (tabType) => ( - {() => } + {() => } ); @@ -186,28 +174,24 @@ export const ExecutionNodeViews: React.FC = ({ executio -
- {tabState.value === tabs.nodes.id && ( - <> -
- -
+ {nodeExecutions.length > 0 ? ( +
+ {tabState.value === tabs.nodes.id ? ( + <> +
+ +
+ + {renderNodeExecutionsTable} + + + ) : ( - {renderNodeExecutionsTable} + {() => renderTab(tabState.value)} - - )} - {tabState.value === tabs.graph.id && ( - - {renderExecutionLoader} - - )} - {tabState.value === tabs.timeline.id && ( - - {renderExecutionsTimeline} - - )} -
+ )} +
+ ) : null}
diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionTab.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionTab.tsx new file mode 100644 index 000000000..32d8dda32 --- /dev/null +++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionTab.tsx @@ -0,0 +1,144 @@ +import { makeStyles } from '@material-ui/core'; +import { DetailsPanel } from 'components/common/DetailsPanel'; +import { WaitForQuery } from 'components/common/WaitForQuery'; +import { DataError } from 'components/Errors/DataError'; +import { makeWorkflowQuery } from 'components/Workflow/workflowQueries'; +import { WorkflowGraph } from 'components/WorkflowGraph/WorkflowGraph'; +import { TaskExecutionPhase } from 'models/Execution/enums'; +import { NodeExecutionIdentifier } from 'models/Execution/types'; +import { startNodeId, endNodeId } from 'models/Node/constants'; +import { Workflow } from 'models/Workflow/types'; +import * as React from 'react'; +import { useContext, useEffect, useMemo, useState } from 'react'; +import { useQuery, useQueryClient } from 'react-query'; +import { useNodeExecutionContext } from '../contextProvider/NodeExecutionDetails'; +import { NodeExecutionsByIdContext } from '../contexts'; +import { tabs } from './constants'; +import { NodeExecutionDetailsPanelContent } from './NodeExecutionDetailsPanelContent'; +import { NodeExecutionsTimelineContext } from './Timeline/context'; +import { ExecutionTimeline } from './Timeline/ExecutionTimeline'; +import { ExecutionTimelineFooter } from './Timeline/ExecutionTimelineFooter'; +import { TimeZone } from './Timeline/helpers'; +import { ScaleProvider } from './Timeline/scaleContext'; + +export interface ExecutionTabProps { + tabType: string; +} + +const useStyles = makeStyles(() => ({ + wrapper: { + display: 'flex', + flexDirection: 'column', + flex: '1 1 100%', + }, + container: { + display: 'flex', + flex: '1 1 0', + overflowY: 'auto', + }, +})); + +/** Contains the available ways to visualize the nodes of a WorkflowExecution */ +export const ExecutionTab: React.FC = ({ tabType }) => { + const styles = useStyles(); + const queryClient = useQueryClient(); + const { workflowId } = useNodeExecutionContext(); + const workflowQuery = useQuery(makeWorkflowQuery(queryClient, workflowId)); + + const [selectedNodes, setSelectedNodes] = useState([]); + const nodeExecutionsById = useContext(NodeExecutionsByIdContext); + + // Note: flytegraph allows multiple selection, but we only support showing + // a single item in the details panel + const [selectedExecution, setSelectedExecution] = useState( + selectedNodes.length + ? nodeExecutionsById[selectedNodes[0]] + ? nodeExecutionsById[selectedNodes[0]].id + : { + nodeId: selectedNodes[0], + executionId: nodeExecutionsById[Object.keys(nodeExecutionsById)[0]].id.executionId, + } + : null, + ); + + const [selectedPhase, setSelectedPhase] = useState(undefined); + const [isDetailsTabClosed, setIsDetailsTabClosed] = useState(!selectedExecution); + + useEffect(() => { + setIsDetailsTabClosed(!selectedExecution); + }, [selectedExecution]); + + const onNodeSelectionChanged = (newSelection: string[]) => { + const validSelection = newSelection.filter((nodeId) => { + if (nodeId === startNodeId || nodeId === endNodeId) { + return false; + } + return true; + }); + setSelectedNodes(validSelection); + const newSelectedExecution = validSelection.length + ? nodeExecutionsById[validSelection[0]] + ? nodeExecutionsById[validSelection[0]].id + : { + nodeId: validSelection[0], + executionId: nodeExecutionsById[Object.keys(nodeExecutionsById)[0]].id.executionId, + } + : null; + setSelectedExecution(newSelectedExecution); + }; + + const onCloseDetailsPanel = () => { + setSelectedExecution(null); + setSelectedPhase(undefined); + setSelectedNodes([]); + }; + + const [chartTimezone, setChartTimezone] = useState(TimeZone.Local); + + const handleTimezoneChange = (tz) => setChartTimezone(tz); + + const timelineContext = useMemo( + () => ({ selectedExecution, setSelectedExecution }), + [selectedExecution, setSelectedExecution], + ); + + const renderGraph = (workflow: Workflow) => ( + + ); + + return ( + + {tabType === tabs.timeline.id && ( +
+
+ + ; + +
+ +
+ )} + {tabType === tabs.graph.id && ( + + {renderGraph} + + )} + {/* Side panel, shows information for specific node */} + + {!isDetailsTabClosed && selectedExecution && ( + + )} + +
+ ); +}; diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionWorkflowGraph.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionWorkflowGraph.tsx deleted file mode 100644 index 72165b735..000000000 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionWorkflowGraph.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { DetailsPanel } from 'components/common/DetailsPanel'; -import { WaitForQuery } from 'components/common/WaitForQuery'; -import { DataError } from 'components/Errors/DataError'; -import { makeWorkflowQuery } from 'components/Workflow/workflowQueries'; -import { WorkflowGraph } from 'components/WorkflowGraph/WorkflowGraph'; -import { TaskExecutionPhase } from 'models/Execution/enums'; -import { endNodeId, startNodeId } from 'models/Node/constants'; -import { Workflow, WorkflowId } from 'models/Workflow/types'; -import * as React from 'react'; -import { useContext, useEffect, useState } from 'react'; -import { useQuery, useQueryClient } from 'react-query'; -import { NodeExecutionsByIdContext } from '../contexts'; -import { NodeExecutionDetailsPanelContent } from './NodeExecutionDetailsPanelContent'; - -export interface ExecutionWorkflowGraphProps { - workflowId: WorkflowId; -} - -/** Wraps a WorkflowGraph, customizing it to also show execution statuses */ -export const ExecutionWorkflowGraph: React.FC = ({ workflowId }) => { - const queryClient = useQueryClient(); - const workflowQuery = useQuery(makeWorkflowQuery(queryClient, workflowId)); - - const [selectedNodes, setSelectedNodes] = useState([]); - const nodeExecutionsById = useContext(NodeExecutionsByIdContext); - - // Note: flytegraph allows multiple selection, but we only support showing - // a single item in the details panel - const selectedExecution = selectedNodes.length - ? nodeExecutionsById[selectedNodes[0]] - ? nodeExecutionsById[selectedNodes[0]].id - : { - nodeId: selectedNodes[0], - executionId: nodeExecutionsById[Object.keys(nodeExecutionsById)[0]].id.executionId, - } - : null; - - const [selectedPhase, setSelectedPhase] = useState(undefined); - const [isDetailsTabClosed, setIsDetailsTabClosed] = useState(!selectedExecution); - - useEffect(() => { - setIsDetailsTabClosed(!selectedExecution); - }, [selectedExecution]); - - const onNodeSelectionChanged = (newSelection: string[]) => { - const validSelection = newSelection.filter((nodeId) => { - if (nodeId === startNodeId || nodeId === endNodeId) { - return false; - } - return true; - }); - setSelectedNodes(validSelection); - }; - - const onCloseDetailsPanel = () => { - setSelectedPhase(undefined); - setIsDetailsTabClosed(true); - setSelectedNodes([]); - }; - - const renderGraph = (workflow: Workflow) => ( - - ); - - return ( - <> - - {renderGraph} - - - {selectedExecution && ( - - )} - - - ); -}; diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimeline.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimeline.tsx index 14af5b17e..caf430e7a 100644 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimeline.tsx +++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimeline.tsx @@ -114,13 +114,14 @@ export const ExecutionTimeline: React.FC = ({ chartTimezone }) => { const execution = nodeExecutionsById[node.scopedId]; return { ...node, + startedAt: execution?.closure.startedAt, execution, }; }); setShowNodes(updatedShownNodesMap); // set startTime for all timeline offset and duration calculations. - const firstStartedAt = updatedShownNodesMap[0]?.execution?.closure.startedAt; + const firstStartedAt = updatedShownNodesMap[0]?.startedAt; if (firstStartedAt) { setStartedAt(timestampToDate(firstStartedAt)); } diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/NodeExecutionName.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/NodeExecutionName.tsx index 45fc7ad43..b62367f54 100644 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/NodeExecutionName.tsx +++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/NodeExecutionName.tsx @@ -5,12 +5,12 @@ import { SelectNodeExecutionLink } from 'components/Executions/Tables/SelectNode import { isEqual } from 'lodash'; import { NodeExecution } from 'models/Execution/types'; import * as React from 'react'; -import { NodeExecutionsTimelineContextData } from './context'; +import { useContext } from 'react'; +import { NodeExecutionsTimelineContext } from './context'; interface NodeExecutionTimelineNameData { name: string; execution: NodeExecution; - state: NodeExecutionsTimelineContextData; } const useStyles = makeStyles((_theme: Theme) => ({ @@ -19,22 +19,19 @@ const useStyles = makeStyles((_theme: Theme) => ({ }, })); -export const NodeExecutionName: React.FC = ({ - name, - execution, - state, -}) => { +export const NodeExecutionName: React.FC = ({ name, execution }) => { const commonStyles = useCommonStyles(); const styles = useStyles(); + const { selectedExecution, setSelectedExecution } = useContext(NodeExecutionsTimelineContext); + if (!execution) { // to avoid crash - disable items which do not have associated execution. // as we won't be able to provide task info for them anyway. return {name}; } - const isSelected = - state.selectedExecution != null && isEqual(execution.id, state.selectedExecution); + const isSelected = selectedExecution != null && isEqual(execution.id, selectedExecution); return isSelected ? ( {name} @@ -44,7 +41,7 @@ export const NodeExecutionName: React.FC = ({ className={commonStyles.primaryLink} execution={execution} linkText={name} - state={state} + setSelectedExecution={setSelectedExecution} /> ); }; diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/TaskNames.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/TaskNames.tsx index a2bcd4187..f2d9f4533 100644 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/TaskNames.tsx +++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/TaskNames.tsx @@ -5,7 +5,6 @@ import { RowExpander } from 'components/Executions/Tables/RowExpander'; import { getNodeTemplateName } from 'components/WorkflowGraph/utils'; import { dNode } from 'models/Graph/types'; import { NodeExecutionName } from './NodeExecutionName'; -import { NodeExecutionsTimelineContext } from './context'; const useStyles = makeStyles((theme: Theme) => ({ taskNamesList: { @@ -55,7 +54,6 @@ interface TaskNamesProps { } export const TaskNames = React.forwardRef((props, ref) => { - const state = React.useContext(NodeExecutionsTimelineContext); const { nodes, onScroll, onToggle } = props; const styles = useStyles(); @@ -85,7 +83,6 @@ export const TaskNames = React.forwardRef((props {templateName} diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/chartHeader.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/chartHeader.tsx deleted file mode 100644 index 1fafd08c8..000000000 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/chartHeader.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import * as React from 'react'; -import * as moment from 'moment-timezone'; -import makeStyles from '@material-ui/core/styles/makeStyles'; -import { COLOR_SPECTRUM } from 'components/Theme/colorSpectrum'; -import { useScaleContext } from './scaleContext'; -import { TimeZone } from './helpers'; - -interface StyleProps { - chartWidth: number; - labelInterval: number; -} - -const useStyles = makeStyles((_theme) => ({ - chartHeader: (props: StyleProps) => ({ - height: 41, - display: 'flex', - alignItems: 'center', - width: `${props.chartWidth}px`, - }), - taskDurationsLabelItem: (props: StyleProps) => ({ - fontSize: 12, - fontFamily: 'Open Sans', - fontWeight: 'bold', - color: COLOR_SPECTRUM.gray40.color, - paddingLeft: 10, - width: `${props.labelInterval}px`, - }), -})); - -interface HeaderProps extends StyleProps { - chartTimezone: string; - totalDurationSec: number; - startedAt: Date; -} - -export const ChartHeader = (props: HeaderProps) => { - const styles = useStyles(props); - - const { chartInterval: chartTimeInterval, setMaxValue } = useScaleContext(); - const { startedAt, chartTimezone, totalDurationSec } = props; - - React.useEffect(() => { - setMaxValue(props.totalDurationSec); - }, [props.totalDurationSec, setMaxValue]); - - const labels = React.useMemo(() => { - const len = Math.ceil(totalDurationSec / chartTimeInterval); - const lbs = len > 0 ? new Array(len).fill('') : []; - return lbs.map((_, idx) => { - const time = moment.utc(new Date(startedAt.getTime() + idx * chartTimeInterval * 1000)); - return chartTimezone === TimeZone.UTC - ? time.format('hh:mm:ss A') - : time.local().format('hh:mm:ss A'); - }); - }, [chartTimezone, startedAt, chartTimeInterval, totalDurationSec]); - - return ( -
- {labels.map((label) => { - return ( -
- {label} -
- ); - })} -
- ); -}; diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/index.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/index.tsx deleted file mode 100644 index e0180b7a1..000000000 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/index.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import * as React from 'react'; -import { makeStyles } from '@material-ui/core'; -import { NodeExecutionIdentifier } from 'models/Execution/types'; -import { DetailsPanel } from 'components/common/DetailsPanel'; -import { useMemo, useState } from 'react'; -import { NodeExecutionDetailsPanelContent } from '../NodeExecutionDetailsPanelContent'; -import { NodeExecutionsTimelineContext } from './context'; -import { ExecutionTimelineFooter } from './ExecutionTimelineFooter'; -import { ExecutionTimeline } from './ExecutionTimeline'; -import { TimeZone } from './helpers'; -import { ScaleProvider } from './scaleContext'; - -const useStyles = makeStyles(() => ({ - wrapper: { - display: 'flex', - flexDirection: 'column', - flex: '1 1 100%', - }, - container: { - display: 'flex', - flex: '1 1 0', - overflowY: 'auto', - }, -})); - -export const ExecutionNodesTimeline = () => { - const styles = useStyles(); - - const [selectedExecution, setSelectedExecution] = useState(null); - const [chartTimezone, setChartTimezone] = useState(TimeZone.Local); - - const onCloseDetailsPanel = () => setSelectedExecution(null); - const handleTimezoneChange = (tz) => setChartTimezone(tz); - - const timelineContext = useMemo( - () => ({ selectedExecution, setSelectedExecution }), - [selectedExecution, setSelectedExecution], - ); - - return ( - -
-
- - ; - -
- -
- - {/* Side panel, shows information for specific node */} - - {selectedExecution && ( - - )} - -
- ); -}; diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/taskNames.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/taskNames.tsx deleted file mode 100644 index a2bcd4187..000000000 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/taskNames.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import * as React from 'react'; -import { makeStyles, Theme, Typography } from '@material-ui/core'; - -import { RowExpander } from 'components/Executions/Tables/RowExpander'; -import { getNodeTemplateName } from 'components/WorkflowGraph/utils'; -import { dNode } from 'models/Graph/types'; -import { NodeExecutionName } from './NodeExecutionName'; -import { NodeExecutionsTimelineContext } from './context'; - -const useStyles = makeStyles((theme: Theme) => ({ - taskNamesList: { - overflowY: 'scroll', - flex: 1, - }, - namesContainer: { - display: 'flex', - flexDirection: 'row', - alignItems: 'flex-start', - justifyContent: 'left', - padding: '0 10px', - height: 56, - width: 256, - borderBottom: `1px solid ${theme.palette.divider}`, - whiteSpace: 'nowrap', - }, - namesContainerExpander: { - display: 'flex', - marginTop: 'auto', - marginBottom: 'auto', - }, - namesContainerBody: { - display: 'flex', - flexDirection: 'column', - alignItems: 'flex-start', - justifyContent: 'center', - whiteSpace: 'nowrap', - height: '100%', - overflow: 'hidden', - }, - displayName: { - marginTop: 4, - textOverflow: 'ellipsis', - width: '100%', - overflow: 'hidden', - }, - leaf: { - width: 30, - }, -})); - -interface TaskNamesProps { - nodes: dNode[]; - onScroll: () => void; - onToggle: (id: string, scopeId: string, level: number) => void; -} - -export const TaskNames = React.forwardRef((props, ref) => { - const state = React.useContext(NodeExecutionsTimelineContext); - const { nodes, onScroll, onToggle } = props; - const styles = useStyles(); - - return ( -
- {nodes.map((node) => { - const templateName = getNodeTemplateName(node); - const nodeLevel = node?.level ?? 0; - return ( -
-
- {node.nodes?.length ? ( - onToggle(node.id, node.scopedId, nodeLevel)} - /> - ) : ( -
- )} -
- -
- - - {templateName} - -
-
- ); - })} -
- ); -}); diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/test/ExecutionNodeViews.test.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/test/ExecutionNodeViews.test.tsx index 874dc7557..fd42e9a50 100644 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/test/ExecutionNodeViews.test.tsx +++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/test/ExecutionNodeViews.test.tsx @@ -11,12 +11,6 @@ import { createTestQueryClient } from 'test/utils'; import { tabs } from '../constants'; import { ExecutionNodeViews } from '../ExecutionNodeViews'; -// We don't need to verify the content of the graph component here and it is -// difficult to make it work correctly in a test environment. -jest.mock('../ExecutionWorkflowGraph.tsx', () => ({ - ExecutionWorkflowGraph: () => null, -})); - jest.mock('chart.js', () => ({ Chart: { register: () => null }, Tooltip: { positioners: { cursor: () => null } }, diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/test/Timeline.test.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/test/Timeline.test.tsx deleted file mode 100644 index f99d01f8d..000000000 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/test/Timeline.test.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import ThemeProvider from '@material-ui/styles/ThemeProvider'; -import { render, waitFor } from '@testing-library/react'; -import { NodeExecutionsByIdContext } from 'components/Executions/contexts'; -import { muiTheme } from 'components/Theme/muiTheme'; -import { oneFailedTaskWorkflow } from 'mocks/data/fixtures/oneFailedTaskWorkflow'; -import { insertFixture } from 'mocks/data/insertFixture'; -import { mockServer } from 'mocks/server'; -import { Execution } from 'models/Execution/types'; -import * as React from 'react'; -import { QueryClient, QueryClientProvider } from 'react-query'; -import { createTestQueryClient } from 'test/utils'; -import { ExecutionNodesTimeline } from '../Timeline'; - -// We don't need to verify the content of the graph component here and it is -// difficult to make it work correctly in a test environment. -jest.mock('../ExecutionWorkflowGraph.tsx', () => ({ - ExecutionWorkflowGraph: () => null, -})); - -jest.mock('../Timeline/ExecutionTimeline.tsx', () => ({ - ExecutionTimeline: () => null, -})); - -jest.mock('chart.js', () => ({ - Chart: { register: () => null }, - Tooltip: { positioners: { cursor: () => null } }, - registerables: [], -})); - -jest.mock('chartjs-plugin-datalabels', () => ({ - ChartDataLabels: null, -})); - -const baseQueryParams = { - filters: '', - 'sort_by.direction': 'ASCENDING', - 'sort_by.key': 'created_at', -}; - -describe('ExecutionDetails > Timeline', () => { - let queryClient: QueryClient; - let execution: Execution; - let fixture: ReturnType; - - beforeEach(() => { - fixture = oneFailedTaskWorkflow.generate(); - execution = fixture.workflowExecutions.top.data; - insertFixture(mockServer, fixture); - const nodeExecutions = fixture.workflowExecutions.top.nodeExecutions; - - mockServer.insertNodeExecutionList( - execution.id, - Object.values(nodeExecutions).map(({ data }) => data), - baseQueryParams, - ); - mockServer.insertNodeExecutionList(execution.id, [nodeExecutions.failedNode.data], { - ...baseQueryParams, - filters: 'value_in(phase,FAILED)', - }); - queryClient = createTestQueryClient(); - }); - - const renderViews = () => - render( - - - - - - - , - ); - - it('Render Timeline with empty nodeExecution info will not crash', async () => { - const { getByText } = renderViews(); - await waitFor(() => { - expect(getByText('Local Time')).toBeTruthy(); - }); - }); -}); diff --git a/packages/zapp/console/src/components/Executions/Tables/SelectNodeExecutionLink.tsx b/packages/zapp/console/src/components/Executions/Tables/SelectNodeExecutionLink.tsx index 89172d487..6c6c86a1a 100644 --- a/packages/zapp/console/src/components/Executions/Tables/SelectNodeExecutionLink.tsx +++ b/packages/zapp/console/src/components/Executions/Tables/SelectNodeExecutionLink.tsx @@ -1,7 +1,6 @@ import { Link } from '@material-ui/core'; -import { NodeExecution } from 'models/Execution/types'; +import { NodeExecution, NodeExecutionIdentifier } from 'models/Execution/types'; import * as React from 'react'; -import { NodeExecutionsTableState } from './types'; /** Renders a link that, when clicked, will trigger selection of the * given NodeExecution. @@ -10,14 +9,14 @@ export const SelectNodeExecutionLink: React.FC<{ className?: string; execution: NodeExecution; linkText: string; - state: NodeExecutionsTableState; -}> = ({ className, execution, linkText, state }) => { + setSelectedExecution: (selectedExecutionId: NodeExecutionIdentifier | null) => void; +}> = ({ className, execution, linkText, setSelectedExecution }) => { // open the side panel for selected execution's detail const onClick = (e: React.MouseEvent) => { // prevent the parent row body onClick event trigger e.stopPropagation(); // use null in case if there is no execution provided - when it is null will close panel - state.setSelectedExecution(execution?.id ?? null); + setSelectedExecution(execution?.id ?? null); }; return ( diff --git a/packages/zapp/console/src/components/Executions/Tables/nodeExecutionColumns.tsx b/packages/zapp/console/src/components/Executions/Tables/nodeExecutionColumns.tsx index f674002b9..1160df913 100644 --- a/packages/zapp/console/src/components/Executions/Tables/nodeExecutionColumns.tsx +++ b/packages/zapp/console/src/components/Executions/Tables/nodeExecutionColumns.tsx @@ -5,6 +5,7 @@ import { useCommonStyles } from 'components/common/styles'; import { isEqual } from 'lodash'; import { NodeExecutionPhase } from 'models/Execution/enums'; import * as React from 'react'; +import { useEffect, useState } from 'react'; import { useNodeExecutionContext } from '../contextProvider/NodeExecutionDetails'; import { ExecutionStatusBadge } from '../ExecutionStatusBadge'; import { NodeExecutionCacheStatus } from '../NodeExecutionCacheStatus'; @@ -16,9 +17,9 @@ import { NodeExecutionCellRendererData, NodeExecutionColumnDefinition } from './ const ExecutionName: React.FC = ({ execution, state }) => { const detailsContext = useNodeExecutionContext(); - const [displayName, setDisplayName] = React.useState(); + const [displayName, setDisplayName] = useState(); - React.useEffect(() => { + useEffect(() => { let isCurrent = true; detailsContext.getNodeExecutionDetails(execution).then((res) => { if (isCurrent) { @@ -32,9 +33,9 @@ const ExecutionName: React.FC = ({ execution, sta const commonStyles = useCommonStyles(); const styles = useColumnStyles(); + const { selectedExecution, setSelectedExecution } = state; - const isSelected = - state.selectedExecution != null && isEqual(execution.id, state.selectedExecution); + const isSelected = state.selectedExecution != null && isEqual(execution.id, selectedExecution); const name = displayName ?? execution.id.nodeId; const truncatedName = name?.split('.').pop() || name; @@ -48,7 +49,7 @@ const ExecutionName: React.FC = ({ execution, sta className={commonStyles.primaryLink} execution={execution} linkText={truncatedName || ''} - state={state} + setSelectedExecution={setSelectedExecution} /> ); @@ -65,9 +66,9 @@ const ExecutionName: React.FC = ({ execution, sta const DisplayId: React.FC = ({ execution }) => { const commonStyles = useCommonStyles(); const detailsContext = useNodeExecutionContext(); - const [displayId, setDisplayId] = React.useState(); + const [displayId, setDisplayId] = useState(); - React.useEffect(() => { + useEffect(() => { let isCurrent = true; detailsContext.getNodeExecutionDetails(execution).then((res) => { if (isCurrent) { @@ -89,9 +90,9 @@ const DisplayId: React.FC = ({ execution }) => { const DisplayType: React.FC = ({ execution }) => { const detailsContext = useNodeExecutionContext(); - const [type, setType] = React.useState(); + const [type, setType] = useState(); - React.useEffect(() => { + useEffect(() => { let isCurrent = true; detailsContext.getNodeExecutionDetails(execution).then((res) => { if (isCurrent) { diff --git a/packages/zapp/console/src/components/WorkflowGraph/test/utils.test.ts b/packages/zapp/console/src/components/WorkflowGraph/test/utils.test.ts index 3ece5f0c8..7a5776680 100644 --- a/packages/zapp/console/src/components/WorkflowGraph/test/utils.test.ts +++ b/packages/zapp/console/src/components/WorkflowGraph/test/utils.test.ts @@ -8,7 +8,6 @@ import { dTypes } from 'models/Graph/types'; import { DISPLAY_NAME_START, DISPLAY_NAME_END, - checkIfObjectsAreSame, getDisplayName, getNodeTypeFromCompiledNode, isStartNode, @@ -24,36 +23,6 @@ describe('getDisplayName', () => { }); }); -describe('checkIfObjectsAreSame', () => { - const a = { - red: 'red', - blue: 'blue', - green: 'green', - }; - const b = { - red: 'red', - blue: 'blue', - green: 'green', - }; - const fail_a = { - red: 'red', - blue: 'not blue', - green: 'green', - }; - const fail_b = { - red: 'red', - green: 'green', - orange: 'orange', - }; - it('should return true when a-keys match b-values', () => { - expect(checkIfObjectsAreSame(a, b)).toEqual(true); - }); - it("should return false when a-keys don't match b-values", () => { - expect(checkIfObjectsAreSame(fail_a, b)).toEqual(false); - expect(checkIfObjectsAreSame(a, fail_b)).toEqual(false); - }); -}); - describe('getNodeTypeFromCompiledNode', () => { const branchNode = { branchNode: {}, diff --git a/tsconfig.base.json b/tsconfig.base.json index f0edb785b..99707ca45 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -10,6 +10,7 @@ "resolveJsonModule": true, "removeComments": false, "importHelpers": true, + "forceConsistentCasingInFileNames": false, "jsx": "react", "lib": ["dom", "dom.iterable", "es6", "es2021"],