diff --git a/apps/design-system/package.json b/apps/design-system/package.json index 63f51f9041..7cf4c7ae23 100644 --- a/apps/design-system/package.json +++ b/apps/design-system/package.json @@ -20,15 +20,15 @@ "clsx": "^2.1.1", "lodash-es": "^4.17.21", "monaco-editor": "^0.40.0", - "react": "^17.0.2", - "react-dom": "^17.0.2", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-live": "^4.1.8", "react-router-dom": "^6.26.0", "vite-plugin-monaco-editor": "^1.1.0" }, "devDependencies": { - "@types/react": "^17.0.3", - "@types/react-dom": "^17.0.3", + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", "@vitejs/plugin-react-swc": "^3.7.2", "eslint": "^8.57.1", "eslint-plugin-react-hooks": "^4.6.2", diff --git a/apps/design-system/src/main.tsx b/apps/design-system/src/main.tsx index a21247bc3a..03a1f3f7ba 100644 --- a/apps/design-system/src/main.tsx +++ b/apps/design-system/src/main.tsx @@ -1,11 +1,13 @@ import { StrictMode } from 'react' -import { render } from 'react-dom' +import { createRoot } from 'react-dom/client' import App from './App' -render( +const container = document.getElementById('root') +const root = createRoot(container!) + +root.render( - , - document.getElementById('root') + ) diff --git a/apps/design-system/src/subjects/views/execution/execution-graph.tsx b/apps/design-system/src/subjects/views/execution/execution-graph.tsx index 97caeee6fc..2c406cb657 100644 --- a/apps/design-system/src/subjects/views/execution/execution-graph.tsx +++ b/apps/design-system/src/subjects/views/execution/execution-graph.tsx @@ -7,14 +7,13 @@ export const ExecutionGraphView = () => {
{ const [enableStream, setEnableStream] = useState(false) const [logs, setLogs] = useState([]) const [selectedStep, setSelectedStep] = useState(null) + const [status, setStatus] = useState(ExecutionState.RUNNING) + const [elapsedTime, setElapsedTime] = useState('00:00') + const [createdTimeElapsed, setCreatedTimeElapsed] = useState('00:00') + const createdStartRef = useRef(Date.now()) + const elapsedStartRef = useRef(Date.now()) const { updatedElements, currentNode } = useAnimateTree({ elements, delay: 2 }) // Animates the execution tree @@ -56,20 +61,59 @@ export const ExecutionLogsView = () => { } }, [selectedStep]) - const updateHighLevelStatus = (elements: TreeViewElement[]) => { - if (elements.every(node => node.status === ExecutionState.SUCCESS)) { - return ExecutionState.SUCCESS - } - return ExecutionState.RUNNING + const isAllSuccess = useMemo( + () => updatedElements.every(node => node.status === ExecutionState.SUCCESS), + [updatedElements] + ) + + useEffect(() => { + setStatus(isAllSuccess ? ExecutionState.SUCCESS : ExecutionState.RUNNING) + }, [isAllSuccess]) + + const formatTime = (seconds: number) => { + const minutes = Math.floor(seconds / 60) + const secs = seconds % 60 + return `${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}` } + // Created timer (always counts up from 0) + useEffect(() => { + createdStartRef.current = Date.now() + + const interval = setInterval(() => { + const now = Date.now() + const totalDiff = Math.floor((now - createdStartRef.current) / 1000) + setCreatedTimeElapsed(formatTime(totalDiff)) + }, 1000) + + return () => clearInterval(interval) + }, []) + + // Elapsed timer (stops when status changes to success) + useEffect(() => { + elapsedStartRef.current = Date.now() + + const interval = setInterval(() => { + if (status === 'success') return + + const now = Date.now() + const elapsedDiff = Math.floor((now - elapsedStartRef.current) / 1000) + setElapsedTime(formatTime(elapsedDiff)) + }, 1000) + + return () => clearInterval(interval) + }, [status]) + return (
{ dataTransfer="4.21 kB/5 GB" branch="master" commit="b8bruh99h" - status={updateHighLevelStatus(updatedElements)} - buildTime="1h 30m" - createdTime="10 mins ago" + status={status} + buildTime={elapsedTime} + createdTime={createdTimeElapsed} pipelineName="build scan push test - k8s - Clone 2" /> -
-
+
+
{ }} />
-
+
{}} onDownload={() => {}} onEdit={() => {}} />
diff --git a/apps/design-system/src/subjects/views/execution/pipeline-execution-graph.tsx b/apps/design-system/src/subjects/views/execution/pipeline-execution-graph.tsx index 10ce4a064f..fd1a43bd60 100644 --- a/apps/design-system/src/subjects/views/execution/pipeline-execution-graph.tsx +++ b/apps/design-system/src/subjects/views/execution/pipeline-execution-graph.tsx @@ -18,7 +18,7 @@ import { Button, Drawer, Icon, PipelineNodes } from '@harnessio/ui/components' import '@harnessio/pipeline-graph/dist/index.css' -import { ExecutionInfo, LivelogLine } from '@harnessio/ui/views' +import { ExecutionHeader, ExecutionInfo, ExecutionState, LivelogLine } from '@harnessio/ui/views' import { logs } from './mocks/mock-data' @@ -78,19 +78,28 @@ export function StepNodeComponent({ return ( {stepNode} - + - Logs - {`View ${name} execution logs`} + -
- ({ logs })} onCopy={() => {}} onDownload={() => {}} onEdit={() => {}} /> +
+ ({ logs })} + onCopy={() => {}} + onDownload={() => {}} + onEdit={() => {}} + />
- - - - - ) @@ -361,7 +370,7 @@ const data: AnyContainerNodeType[] = [ const PipelineExecutionGraph = () => { return ( - + ) } diff --git a/apps/design-system/src/subjects/views/pipeline-edit/context-menu/stage-group-add-in-node-context-menu.tsx b/apps/design-system/src/subjects/views/pipeline-edit/context-menu/stage-group-add-in-node-context-menu.tsx index f43a95b9b8..ebe3021d57 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/context-menu/stage-group-add-in-node-context-menu.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/context-menu/stage-group-add-in-node-context-menu.tsx @@ -21,7 +21,7 @@ export const StageGroupAddInNodeContextMenu = () => { style={{ left: `${contextMenuData?.position.x}px`, top: `${contextMenuData?.position.y}px` }} > { onAddIntention(contextMenuData.nodeData, 'in', YamlEntityType.Stage) @@ -32,7 +32,7 @@ export const StageGroupAddInNodeContextMenu = () => { { onAddIntention(contextMenuData.nodeData, 'in', YamlEntityType.SerialStageGroup) @@ -42,7 +42,7 @@ export const StageGroupAddInNodeContextMenu = () => { Add Serial group { onAddIntention(contextMenuData.nodeData, 'in', YamlEntityType.ParallelStageGroup) diff --git a/apps/design-system/src/subjects/views/pipeline-edit/context-menu/stage-group-node-context-menu.tsx b/apps/design-system/src/subjects/views/pipeline-edit/context-menu/stage-group-node-context-menu.tsx index 10f37671eb..7ca0b6188b 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/context-menu/stage-group-node-context-menu.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/context-menu/stage-group-node-context-menu.tsx @@ -33,7 +33,7 @@ export const StageGroupNodeContextMenu = () => { { onAddIntention(contextMenuData.nodeData, 'before', YamlEntityType.SerialStageGroup) @@ -43,7 +43,7 @@ export const StageGroupNodeContextMenu = () => { Add Serial Stages Group before { onAddIntention(contextMenuData.nodeData, 'after', YamlEntityType.SerialStageGroup) @@ -54,7 +54,7 @@ export const StageGroupNodeContextMenu = () => { { onAddIntention(contextMenuData.nodeData, 'before', YamlEntityType.ParallelStageGroup) @@ -64,7 +64,7 @@ export const StageGroupNodeContextMenu = () => { Add Parallel Stages Group before { onAddIntention(contextMenuData.nodeData, 'after', YamlEntityType.ParallelStageGroup) diff --git a/apps/design-system/src/subjects/views/pipeline-edit/context-menu/stage-node-context-menu.tsx b/apps/design-system/src/subjects/views/pipeline-edit/context-menu/stage-node-context-menu.tsx index a23a5d48bd..34bbed2376 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/context-menu/stage-node-context-menu.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/context-menu/stage-node-context-menu.tsx @@ -33,7 +33,7 @@ export const StageNodeContextMenu = (): (() => React.ReactNode)[] | null | any = { onAddIntention(contextMenuData.nodeData, 'before', YamlEntityType.Stage) // TODO what to add @@ -43,7 +43,7 @@ export const StageNodeContextMenu = (): (() => React.ReactNode)[] | null | any = Add stage before { onAddIntention(contextMenuData.nodeData, 'after', YamlEntityType.Stage) // TODO what to add diff --git a/apps/design-system/src/subjects/views/pipeline-edit/context-menu/step-group-node-context-menu.tsx b/apps/design-system/src/subjects/views/pipeline-edit/context-menu/step-group-node-context-menu.tsx index 3d9d80bb50..1c5009f5ba 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/context-menu/step-group-node-context-menu.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/context-menu/step-group-node-context-menu.tsx @@ -33,7 +33,7 @@ export const StepGroupNodeContextMenu = () => { { onAddIntention(contextMenuData.nodeData, 'before') @@ -43,7 +43,7 @@ export const StepGroupNodeContextMenu = () => { Add Step/Group before { onAddIntention(contextMenuData.nodeData, 'after') diff --git a/apps/design-system/src/subjects/views/pipeline-edit/mocks/demoExecutionMock.tsx b/apps/design-system/src/subjects/views/pipeline-edit/mocks/demoExecutionMock.tsx index a707199c7c..4c83f1a5ce 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/mocks/demoExecutionMock.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/mocks/demoExecutionMock.tsx @@ -4,8 +4,8 @@ export const demoExecutionMock = [ { type: 'Start', config: { - width: 40, - height: 40, + width: 36, + height: 36, hideDeleteButton: true, hideBeforeAdd: true, hideLeftPort: true @@ -188,7 +188,7 @@ export const demoExecutionMock = [ yamlPath: 'pipeline.stages.1.steps.0', yamlEntityType: 'Step', name: 'docker', - icon: , + icon: , selected: false, state: 'warning', warningMessage: 'Timeout' @@ -209,7 +209,7 @@ export const demoExecutionMock = [ yamlPath: 'pipeline.stages.1.steps.1', yamlEntityType: 'Step', name: 'Step 2', - icon: , + icon: , selected: false, state: 'success' }, @@ -223,8 +223,8 @@ export const demoExecutionMock = [ { type: 'End', config: { - width: 40, - height: 40, + width: 36, + height: 36, hideDeleteButton: true, hideAfterAdd: true, hideRightPort: true diff --git a/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-parallel-stage-group-content-node.tsx b/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-parallel-stage-group-content-node.tsx index 85be56ddd5..6cdaa3336d 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-parallel-stage-group-content-node.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-parallel-stage-group-content-node.tsx @@ -6,6 +6,7 @@ import { CommonNodeDataType, usePipelineStudioNodeContext } from '@harnessio/ui/ import { StageGroupAddInNodeContextMenu } from '../context-menu/stage-group-add-in-node-context-menu' import { StageGroupNodeContextMenu } from '../context-menu/stage-group-node-context-menu' import { PipelineNodes } from '../pipeline-nodes' +import { GlobalData } from '../types/common' export interface CustomParallelStageGroupContentNodeDataType extends CommonNodeDataType { icon?: React.ReactElement @@ -22,7 +23,10 @@ export function CustomParallelStageGroupContentNode(props: { const { node, children, collapsed, isFirst, parentNodeType } = props const data = node.data as CustomParallelStageGroupContentNodeDataType - const { selectionPath, showContextMenu, onSelectIntention, onAddIntention } = usePipelineStudioNodeContext() + const { selectionPath, showContextMenu, onSelectIntention, onAddIntention, globalData } = + usePipelineStudioNodeContext() + + const { hideContextMenu, hideFloatingButtons } = globalData ?? {} const selected = useMemo(() => selectionPath === data.yamlPath, [selectionPath]) @@ -35,6 +39,8 @@ export function CustomParallelStageGroupContentNode(props: { isFirst={isFirst} parentNodeType={parentNodeType} node={node} + hideContextMenu={hideContextMenu} + hideFloatingButtons={hideFloatingButtons} onAddInClick={e => { e.stopPropagation() showContextMenu(StageGroupAddInNodeContextMenu, data, e.currentTarget, true) diff --git a/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-parallel-step-group-content-node.tsx b/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-parallel-step-group-content-node.tsx index 828abe34a0..64117a47de 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-parallel-step-group-content-node.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-parallel-step-group-content-node.tsx @@ -5,6 +5,7 @@ import { CommonNodeDataType, usePipelineStudioNodeContext } from '@harnessio/ui/ import { StepGroupNodeContextMenu } from '../context-menu/step-group-node-context-menu' import { PipelineNodes } from '../pipeline-nodes' +import { GlobalData } from '../types/common' export interface CustomParallelStepGroupContentNodeDataType extends CommonNodeDataType { icon?: React.ReactElement @@ -21,7 +22,10 @@ export function CustomParallelStepGroupContentNode(props: { const { node, children, collapsed, isFirst, parentNodeType } = props const data = node.data as CustomParallelStepGroupContentNodeDataType - const { selectionPath, showContextMenu, onSelectIntention, onAddIntention } = usePipelineStudioNodeContext() + const { selectionPath, showContextMenu, onSelectIntention, onAddIntention, globalData } = + usePipelineStudioNodeContext() + + const { hideContextMenu, hideFloatingButtons } = globalData ?? {} const selected = useMemo(() => selectionPath === data.yamlPath, [selectionPath]) @@ -34,6 +38,8 @@ export function CustomParallelStepGroupContentNode(props: { isFirst={isFirst} parentNodeType={parentNodeType} node={node} + hideContextMenu={hideContextMenu} + hideFloatingButtons={hideFloatingButtons} onAddInClick={e => { e.stopPropagation() onAddIntention(data, 'in') diff --git a/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-serial-stage-group-content-node.tsx b/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-serial-stage-group-content-node.tsx index e6770065a9..7495cdab2b 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-serial-stage-group-content-node.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-serial-stage-group-content-node.tsx @@ -6,6 +6,7 @@ import { CommonNodeDataType, usePipelineStudioNodeContext } from '@harnessio/ui/ import { StageGroupAddInNodeContextMenu } from '../context-menu/stage-group-add-in-node-context-menu' import { StageGroupNodeContextMenu } from '../context-menu/stage-group-node-context-menu' import { PipelineNodes } from '../pipeline-nodes' +import { GlobalData } from '../types/common' export interface CustomSerialStageGroupContentNodeDataType extends CommonNodeDataType { icon?: React.ReactElement @@ -22,7 +23,10 @@ export function CustomSerialStageGroupContentNode(props: { const { node, children, collapsed, isFirst, parentNodeType } = props const data = node.data as CustomSerialStageGroupContentNodeDataType - const { selectionPath, showContextMenu, onSelectIntention, onAddIntention } = usePipelineStudioNodeContext() + const { selectionPath, showContextMenu, onSelectIntention, onAddIntention, globalData } = + usePipelineStudioNodeContext() + + const { hideContextMenu, hideFloatingButtons } = globalData ?? {} const selected = useMemo(() => selectionPath === data.yamlPath, [selectionPath]) @@ -35,6 +39,8 @@ export function CustomSerialStageGroupContentNode(props: { isFirst={isFirst} parentNodeType={parentNodeType} node={node} + hideContextMenu={hideContextMenu} + hideFloatingButtons={hideFloatingButtons} onAddInClick={e => { e.stopPropagation() showContextMenu(StageGroupAddInNodeContextMenu, data, e.currentTarget, true) diff --git a/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-serial-step-group-content-node.tsx b/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-serial-step-group-content-node.tsx index 9fa89a8ee7..0cf12c8095 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-serial-step-group-content-node.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-serial-step-group-content-node.tsx @@ -5,6 +5,7 @@ import { CommonNodeDataType, usePipelineStudioNodeContext } from '@harnessio/ui/ import { StepGroupNodeContextMenu } from '../context-menu/step-group-node-context-menu' import { PipelineNodes } from '../pipeline-nodes' +import { GlobalData } from '../types/common' export interface CustomSerialStepGroupContentNodeDataType extends CommonNodeDataType { icon?: React.ReactElement @@ -21,7 +22,10 @@ export function CustomSerialStepGroupContentNode(props: { const { node, children, collapsed, isFirst, parentNodeType } = props const data = node.data as CustomSerialStepGroupContentNodeDataType - const { selectionPath, showContextMenu, onSelectIntention, onAddIntention } = usePipelineStudioNodeContext() + const { selectionPath, showContextMenu, onSelectIntention, onAddIntention, globalData } = + usePipelineStudioNodeContext() + + const { hideContextMenu, hideFloatingButtons } = globalData ?? {} const selected = useMemo(() => selectionPath === data.yamlPath, [selectionPath]) @@ -34,6 +38,8 @@ export function CustomSerialStepGroupContentNode(props: { isFirst={isFirst} parentNodeType={parentNodeType} node={node} + hideContextMenu={hideContextMenu} + hideFloatingButtons={hideFloatingButtons} onAddInClick={e => { e.stopPropagation() onAddIntention(data, 'in') diff --git a/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-stage-content-node.tsx b/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-stage-content-node.tsx index eeb89f86b5..3a9c120194 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-stage-content-node.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-stage-content-node.tsx @@ -5,6 +5,7 @@ import { CommonNodeDataType, usePipelineStudioNodeContext } from '@harnessio/ui/ import { StageNodeContextMenu } from '../context-menu/stage-node-context-menu' import { PipelineNodes } from '../pipeline-nodes' +import { GlobalData } from '../types/common' export interface CustomStageContentNodeDataType extends CommonNodeDataType { icon?: React.ReactElement @@ -21,7 +22,10 @@ export function CustomStageContentNode(props: { const { node, children, collapsed, isFirst, parentNodeType } = props const data = node.data as CustomStageContentNodeDataType - const { selectionPath, showContextMenu, onAddIntention, onSelectIntention } = usePipelineStudioNodeContext() + const { selectionPath, showContextMenu, onAddIntention, onSelectIntention, globalData } = + usePipelineStudioNodeContext() + + const { hideContextMenu, hideFloatingButtons } = globalData ?? {} const selected = useMemo(() => selectionPath === data.yamlPath, [selectionPath]) @@ -34,6 +38,8 @@ export function CustomStageContentNode(props: { isFirst={isFirst} parentNodeType={parentNodeType} node={node} + hideContextMenu={hideContextMenu} + hideFloatingButtons={hideFloatingButtons} onAddInClick={() => { onAddIntention(data, 'in') }} diff --git a/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-step-node.tsx b/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-step-node.tsx index 2f8984cae5..d14aab544f 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-step-node.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/nodes/custom-step-node.tsx @@ -5,6 +5,7 @@ import { CommonNodeDataType, usePipelineStudioNodeContext } from '@harnessio/ui/ import { StepNodeContextMenu } from '../context-menu/step-node-context-menu' import { PipelineNodes } from '../pipeline-nodes' +import { GlobalData } from '../types/common' export interface StepNodeDataType extends CommonNodeDataType { icon?: React.ReactElement @@ -22,7 +23,10 @@ export function CustomStepContentNode(props: { const { node, isFirst, parentNodeType } = props const { data } = node - const { selectionPath, showContextMenu, onSelectIntention, onAddIntention } = usePipelineStudioNodeContext() + const { selectionPath, showContextMenu, onSelectIntention, onAddIntention, globalData } = + usePipelineStudioNodeContext() + + const { hideContextMenu, hideFloatingButtons } = globalData ?? {} const selected = useMemo(() => selectionPath === data.yamlPath, [selectionPath]) @@ -34,6 +38,8 @@ export function CustomStepContentNode(props: { isFirst={isFirst} parentNodeType={parentNodeType} node={node} + hideContextMenu={hideContextMenu} + hideFloatingButtons={hideFloatingButtons} onEllipsisClick={e => { e.stopPropagation() showContextMenu(StepNodeContextMenu, data, e.currentTarget) diff --git a/apps/design-system/src/subjects/views/pipeline-edit/pipeline-edit.tsx b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-edit.tsx index 6494eb368d..ee42d47186 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/pipeline-edit.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-edit.tsx @@ -1,5 +1,7 @@ import { useState } from 'react' +import { CollapseButton } from '@subjects/views/pipeline-edit/pipeline-nodes/components/collapse-button.tsx' + import { Button, ButtonGroup, Icon } from '@harnessio/ui/components' import { CommonNodeDataType, @@ -74,7 +76,7 @@ const PipelineStudioWrapper = () => { return (
{ setSelectedPath(undefined) }} @@ -118,6 +120,7 @@ const PipelineStudioWrapper = () => { return { level1: staticPath, level2: '' } }} portComponent={CustomPort} + collapseButtonComponent={CollapseButton} edgesConfig={{ radius: 10, parallelNodeOffset: 10, serialNodeOffset: 10 }} yamlRevision={yamlRevision} onYamlRevisionChange={setYamlRevision} diff --git a/apps/design-system/src/subjects/views/pipeline-edit/pipeline-execution.tsx b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-execution.tsx index 740230b723..963c972563 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/pipeline-execution.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-execution.tsx @@ -1,5 +1,7 @@ import { useMemo } from 'react' +import { CollapseButton } from '@subjects/views/pipeline-edit/pipeline-nodes/components/collapse-button.tsx' + import { CanvasProvider, PipelineGraph } from '@harnessio/pipeline-graph' import { PipelineStudioNodeContextMenu, PipelineStudioNodeContextProvider } from '@harnessio/ui/views' @@ -9,6 +11,12 @@ import { demoExecutionMock } from './mocks/animations/demoExecutionMock' import { parallelContainerConfig, serialContainerConfig } from './mocks/pipelineExecutionMock' import { contentNodeFactory } from './nodes-factory' import CustomPort from './pipeline-nodes/components/custom-port' +import { GlobalData } from './types/common' + +const globalDataConfigForExecution: GlobalData = { + hideContextMenu: true, + hideFloatingButtons: true +} const PipelineExecution = () => { return ( @@ -20,6 +28,7 @@ const PipelineExecution = () => { onEditIntention={() => undefined} onRevealInYaml={() => undefined} onSelectIntention={() => undefined} + globalData={globalDataConfigForExecution} > @@ -46,6 +55,7 @@ const PipelineExecutionInner = () => { return { level1: staticPath, level2: '' } }} portComponent={CustomPort} + collapseButtonComponent={CollapseButton} edgesConfig={{ radius: 10, parallelNodeOffset: 10, serialNodeOffset: 10 }} data={animatedNodes} nodes={nodes} diff --git a/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/components/collapse-button.tsx b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/components/collapse-button.tsx new file mode 100644 index 0000000000..423e9fca51 --- /dev/null +++ b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/components/collapse-button.tsx @@ -0,0 +1,16 @@ +import { CollapseButtonProps } from '@harnessio/pipeline-graph' +import { Button, Icon } from '@harnessio/ui/components' + +export const CollapseButton = ({ collapsed, onToggle }: CollapseButtonProps) => { + return ( + + ) +} diff --git a/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/components/collapsed-group-node.tsx b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/components/collapsed-group-node.tsx index 4886c75fef..7d3169c343 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/components/collapsed-group-node.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/components/collapsed-group-node.tsx @@ -1,3 +1,5 @@ +import { getNestedStepsCount } from '@subjects/views/pipeline-edit/utils/common-step-utils' + import { LeafNodeInternalType, ParallelNodeInternalType, SerialNodeInternalType } from '@harnessio/pipeline-graph' import { cn } from '@harnessio/ui/views' @@ -6,8 +8,6 @@ import { CustomParallelStepGroupContentNodeDataType } from '../../nodes/custom-p import { CustomSerialStepGroupContentNodeDataType } from '../../nodes/custom-serial-step-group-content-node' import { StepNodeDataType } from '../../nodes/custom-step-node' -export type ExecutionStatusAlign = 'left' | 'right' - export function CollapsedGroupNode({ node, containerNodeType @@ -26,6 +26,10 @@ export function CollapsedGroupNode({ const zIndexProp = ['-1', '-2'] const firstNode = nodesToShow.shift() + const counter = + !!firstNode && 'children' in firstNode && Array.isArray(firstNode.children) + ? getNestedStepsCount(firstNode?.children) + : undefined return ( <> @@ -42,6 +46,8 @@ export function CollapsedGroupNode({ node={firstNode as LeafNodeInternalType} icon={firstNode?.data?.icon} name={firstNode?.data?.name} + counter={counter} + isCollapsedNode /> {/* other nodes without content*/} diff --git a/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/components/floating-add-button.tsx b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/components/floating-add-button.tsx index d965e0cb1a..5fe8192ffc 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/components/floating-add-button.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/components/floating-add-button.tsx @@ -1,18 +1,24 @@ import { CSSProperties } from 'react' +import { + parallelContainerConfig, + serialContainerConfig +} from '@subjects/views/pipeline-edit/mocks/pipelineExecutionMock' + import { Button, Icon } from '@harnessio/ui/components' -const CONTAINER_WIDTH = '36' -const CONTAINER_HEIGHT = '36' +const CONTAINER_WIDTH = '40' +const CONTAINER_HEIGHT = '40' export interface FloatingAddButtonProp { parentNodeType?: 'leaf' | 'serial' | 'parallel' position: 'before' | 'after' onClick: (event: React.MouseEvent) => void + collapsed?: boolean } export function FloatingAddButton(props: FloatingAddButtonProp) { - const { onClick, position, parentNodeType } = props + const { onClick, position, parentNodeType, collapsed } = props const style: CSSProperties = {} if (position === 'before' && parentNodeType === 'serial') { @@ -38,20 +44,31 @@ export function FloatingAddButton(props: FloatingAddButtonProp) { style.right = '0px' } + const typeStyleConf = + parentNodeType === 'parallel' + ? parallelContainerConfig + : parentNodeType === 'serial' + ? serialContainerConfig + : undefined + const buttonMarginTopValue = + !!typeStyleConf && collapsed === false ? typeStyleConf.paddingTop - typeStyleConf.paddingBottom : 0 + return (
) diff --git a/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/components/node-menu-trigger.tsx b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/components/node-menu-trigger.tsx new file mode 100644 index 0000000000..46afbb23bb --- /dev/null +++ b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/components/node-menu-trigger.tsx @@ -0,0 +1,26 @@ +import { FC } from 'react' + +import { SerialGroupNodeProps } from '@subjects/views/pipeline-edit/pipeline-nodes/serial-group-node.tsx' + +import { Button, Icon } from '@harnessio/ui/components' + +export interface NodeMenuTriggerProps { + onEllipsisClick?: SerialGroupNodeProps['onEllipsisClick'] +} + +export const NodeMenuTrigger: FC = ({ onEllipsisClick }) => { + if (!onEllipsisClick) return <> + + return ( + + ) +} diff --git a/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/components/node-title.tsx b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/components/node-title.tsx new file mode 100644 index 0000000000..42faf115e2 --- /dev/null +++ b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/components/node-title.tsx @@ -0,0 +1,23 @@ +import { FC } from 'react' + +import { SerialGroupNodeProps } from '@subjects/views/pipeline-edit/pipeline-nodes/serial-group-node.tsx' + +export interface NodeTitleProps extends Pick { + counter?: number +} + +export const NodeTitle: FC = ({ name, onHeaderClick, counter }) => { + return ( +
+
+ {name} ({counter}) +
+
+ ) +} diff --git a/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/end-node.tsx b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/end-node.tsx index 61808a5f29..b3b05115ab 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/end-node.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/end-node.tsx @@ -1,8 +1,9 @@ +import { Icon } from '@harnessio/ui/components' + export function EndNode() { return ( -
- {/* TODO: replace with icon */} -
+
+
) } diff --git a/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/parallel-group-node.tsx b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/parallel-group-node.tsx index b1e3eedb49..8c32a9bb7e 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/parallel-group-node.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/pipeline-nodes/parallel-group-node.tsx @@ -1,3 +1,7 @@ +import { NodeMenuTrigger } from '@subjects/views/pipeline-edit/pipeline-nodes/components/node-menu-trigger.tsx' +import { NodeTitle } from '@subjects/views/pipeline-edit/pipeline-nodes/components/node-title.tsx' +import { getNestedStepsCount } from '@subjects/views/pipeline-edit/utils/common-step-utils' + import { ParallelNodeInternalType } from '@harnessio/pipeline-graph' import { Button, Icon } from '@harnessio/ui/components' import { cn } from '@harnessio/ui/views' @@ -16,6 +20,8 @@ export interface ParallelGroupNodeProps { isFirst?: boolean parentNodeType?: 'leaf' | 'serial' | 'parallel' node: ParallelNodeInternalType + hideContextMenu?: boolean + hideFloatingButtons?: boolean onEllipsisClick: (e: React.MouseEvent) => void onAddInClick: (e: React.MouseEvent) => void onHeaderClick: (e: React.MouseEvent) => void @@ -35,45 +41,29 @@ export function ParallelGroupNode(props: ParallelGroupNodeProps) { onEllipsisClick, onAddInClick, onHeaderClick, - onAddClick + onAddClick, + hideContextMenu, + hideFloatingButtons } = props const nodeData = node.data - - // console.log(name) + const counter = getNestedStepsCount(node.children) return ( <>
-
-
- {name} -
-
+ - + {!hideContextMenu && } {!collapsed && isEmpty && ( + {!hideContextMenu && } {!collapsed && isEmpty && ( - )} + {!hideContextMenu && } {!collapsed && isEmpty && ( - )} - {isFirst && ( + {!hideContextMenu && } + + {!hideFloatingButtons && isFirst && !isCollapsedNode && ( )} - { - onAddClick?.('after', e) - }} - /> + {!hideFloatingButtons && !isCollapsedNode && ( + { + onAddClick?.('after', e) + }} + /> + )} {!!icon &&
{icon}
} - {name} + + {name} + {!!counter && ({counter})} + {nodeData.warningMessage && {nodeData.warningMessage}}
diff --git a/apps/design-system/src/subjects/views/pipeline-edit/types/common.ts b/apps/design-system/src/subjects/views/pipeline-edit/types/common.ts new file mode 100644 index 0000000000..354de2ecd4 --- /dev/null +++ b/apps/design-system/src/subjects/views/pipeline-edit/types/common.ts @@ -0,0 +1,4 @@ +export interface GlobalData { + hideContextMenu?: boolean + hideFloatingButtons?: boolean +} diff --git a/apps/design-system/src/subjects/views/pipeline-edit/utils/common-step-utils.ts b/apps/design-system/src/subjects/views/pipeline-edit/utils/common-step-utils.ts index da9f885f55..838f17a414 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/utils/common-step-utils.ts +++ b/apps/design-system/src/subjects/views/pipeline-edit/utils/common-step-utils.ts @@ -1,3 +1,6 @@ +import { AnyNodeInternal } from '@harnessio/pipeline-graph' +import { YamlEntityType } from '@harnessio/ui/views' + export const getIsRunStep = (step: Record) => typeof step === 'object' && 'run' in step export const getIsRunTestStep = (step: Record) => typeof step === 'object' && 'run-test' in step @@ -7,3 +10,21 @@ export const getIsBackgroundStep = (step: Record) => typeof step == export const getIsActionStep = (step: Record) => typeof step === 'object' && 'action' in step export const getIsTemplateStep = (step: Record) => typeof step === 'object' && 'template' in step + +export const getNestedStepsCount = (children?: AnyNodeInternal[]): number => { + let count = 0 + + if (!children) return 0 + + for (const child of children) { + if (child.type === YamlEntityType.Step) { + count += 1 + } + + if ('children' in child && Array.isArray(child.children)) { + count += getNestedStepsCount(child.children) + } + } + + return count +} diff --git a/apps/design-system/src/subjects/views/pipeline-edit/utils/step-icon-utils.tsx b/apps/design-system/src/subjects/views/pipeline-edit/utils/step-icon-utils.tsx index 746361a46d..224d3c7d6c 100644 --- a/apps/design-system/src/subjects/views/pipeline-edit/utils/step-icon-utils.tsx +++ b/apps/design-system/src/subjects/views/pipeline-edit/utils/step-icon-utils.tsx @@ -20,10 +20,10 @@ const getIconNameBasedOnStep = (step: any): IconProps['name'] => { if (getIsTemplateStep(step)) { switch (step.template.uses) { case 'slack': - return 'rocket' + return 'slack' break case 'docker': - return 'star' + return 'docker' break } } diff --git a/apps/design-system/src/subjects/views/pipeline-graph/pipeline-graph-minimal.tsx b/apps/design-system/src/subjects/views/pipeline-graph/pipeline-graph-minimal.tsx index 6be0e86026..c1e5bb613a 100644 --- a/apps/design-system/src/subjects/views/pipeline-graph/pipeline-graph-minimal.tsx +++ b/apps/design-system/src/subjects/views/pipeline-graph/pipeline-graph-minimal.tsx @@ -18,6 +18,10 @@ import { Icon, Text } from '@harnessio/ui/components' import '@harnessio/pipeline-graph/dist/index.css' +import { useState } from 'react' + +import { VisualYamlToggle, type VisualYamlValue } from '@harnessio/ui/views' + // ***************************************************** // 2. Define content nodes types // ***************************************************** @@ -219,9 +223,13 @@ const data: AnyContainerNodeType[] = [ ] const PipelineGraphMinimalWrapper = () => { + const [view, setView] = useState('visual') return ( - +
+ +
+
) } diff --git a/apps/design-system/src/subjects/views/pipeline-graph/pipeline-graph.tsx b/apps/design-system/src/subjects/views/pipeline-graph/pipeline-graph.tsx index f07e07f608..9172692ddf 100644 --- a/apps/design-system/src/subjects/views/pipeline-graph/pipeline-graph.tsx +++ b/apps/design-system/src/subjects/views/pipeline-graph/pipeline-graph.tsx @@ -289,7 +289,7 @@ const PipelineGraphWrapper = () => { return ( <> - + , document.getElementById('root')) +const container = document.getElementById('root') +const root = createRoot(container!) + +root.render() diff --git a/apps/gitness/src/pages-v2/pipeline/pipeline-edit/components/pipeline-studio-graph-view.tsx b/apps/gitness/src/pages-v2/pipeline/pipeline-edit/components/pipeline-studio-graph-view.tsx index 219a7bd575..0e75ceba7d 100644 --- a/apps/gitness/src/pages-v2/pipeline/pipeline-edit/components/pipeline-studio-graph-view.tsx +++ b/apps/gitness/src/pages-v2/pipeline/pipeline-edit/components/pipeline-studio-graph-view.tsx @@ -129,7 +129,7 @@ export const PipelineStudioGraphView = (): React.ReactElement => {
- + diff --git a/apps/portal/package.json b/apps/portal/package.json index 152c07c95b..214491cf73 100644 --- a/apps/portal/package.json +++ b/apps/portal/package.json @@ -25,8 +25,8 @@ "clsx": "^2.1.1", "lucide-react": "^0.407.0", "prism-react-renderer": "^2.4.1", - "react": "^17.0.2", - "react-dom": "^17.0.2", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-live": "^4.1.8", "react-router-dom": "^6.26.0", "sharp": "^0.32.5", @@ -36,7 +36,7 @@ }, "devDependencies": { "@astrojs/check": "^0.9.4", - "@types/react": "^17.0.3", + "@types/react": "^18.3.18", "lint-staged": "^15.2.9", "prettier": "^3.4.2", "prettier-plugin-astro": "^0.14.1", diff --git a/package.json b/package.json index 065efc8932..734fe0be62 100644 --- a/package.json +++ b/package.json @@ -18,12 +18,7 @@ "node": ">=18.17.1" }, "license": "Apache-2.0", - "resolutions": { - "@types/react": "^17.0.3", - "@types/react-dom": "^17.0.3", - "react": "17.0.2", - "react-dom": "17.0.2" - }, + "packageManager": "pnpm@9.5.0+sha512.140036830124618d624a2187b50d04289d5a087f326c9edfc0ccd733d76c4f52c3a313d4fc148794a2a9d81553016004e6742e8cf850670268a7387fc220c903", "devDependencies": { "@ianvs/prettier-plugin-sort-imports": "^4.4.0", diff --git a/packages/canary/package.json b/packages/canary/package.json index 1d1af0f7bb..e5ef9939d2 100644 --- a/packages/canary/package.json +++ b/packages/canary/package.json @@ -79,9 +79,9 @@ "embla-carousel-react": "^8.1.5", "input-otp": "^1.2.4", "next-themes": "^0.3.0", - "react": "^17.0.2", + "react": "^18.3.1", "react-day-picker": "^8.10.1", - "react-dom": "^17.0.2", + "react-dom": "^18.3.1", "react-hook-form": "^7.28.0", "react-resizable-panels": "^2.0.19", "sonner": "^1.5.0", @@ -95,8 +95,8 @@ "@types/jest": "^27.5.2", "@types/lodash-es": "^4.17.12", "@types/node": "^20.14.9", - "@types/react": "^17.0.3", - "@types/react-dom": "^17.0.3", + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", "@vitejs/plugin-react-swc": "^3.7.2", "autoprefixer": "^10.4.19", "eslint": "^8.57.1", diff --git a/packages/filters/package.json b/packages/filters/package.json index 62a78b6a24..6e07c0da82 100644 --- a/packages/filters/package.json +++ b/packages/filters/package.json @@ -1,6 +1,6 @@ { "name": "@harnessio/filters", - "version": "1.0.0", + "version": "1.0.0-react18.alpha.1", "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { @@ -24,13 +24,12 @@ }, "peerDependencies": { "react": ">=17.0.0 <19.0.0", - "react-dom": ">=17.0.0 <19.0.0", - "react-router-dom": ">=5.0.0 <7.0.0" + "react-dom": ">=17.0.0 <19.0.0" }, "devDependencies": { "@types/node": "^16.18.84", - "@types/react": "^17.0.3", - "@types/react-dom": "^17.0.3", + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", "@vitejs/plugin-react-swc": "^3.7.2", "dts-bundle-generator": "^6.4.0", "eslint": "^8.57.1", @@ -38,6 +37,7 @@ "jest": "^29.7.0", "lint-staged": "^15.2.9", "npm-run-all": "^4.1.5", + "react-router-dom": "^6.26.0", "ts-jest": "^29.1.2", "typescript": "^5.3.3", "vite": "^6.0.3", diff --git a/packages/forms/package.json b/packages/forms/package.json index c58e3c864e..f9114b5f14 100644 --- a/packages/forms/package.json +++ b/packages/forms/package.json @@ -1,6 +1,6 @@ { "name": "@harnessio/forms", - "version": "0.0.1", + "version": "0.0.1-react18.alpha.1", "description": "Harness Forms Library", "scripts": { "dev": "run-p build:watch", @@ -48,8 +48,8 @@ "@types/jest": "^27.5.2", "@types/lodash-es": "^4.17.3", "@types/node": "^16.18.84", - "@types/react": "^17.0.3", - "@types/react-dom": "^17.0.3", + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", "@types/uuid": "^8.3.0", "@types/yup": "^0.29.0", "@vitejs/plugin-react-swc": "^3.7.2", diff --git a/packages/pipeline-graph/examples/src/canvas/CanvasControls.tsx b/packages/pipeline-graph/examples/src/canvas/CanvasControls.tsx index b7742027b9..16f4e640be 100644 --- a/packages/pipeline-graph/examples/src/canvas/CanvasControls.tsx +++ b/packages/pipeline-graph/examples/src/canvas/CanvasControls.tsx @@ -2,7 +2,7 @@ import { useCanvasContext } from '../../../src/context/canvas-provider' import { CanvasButton } from './CanvasButton' export function CanvasControls() { - const { increase, decrease, reset } = useCanvasContext() + const { increase, decrease, reset, fit } = useCanvasContext() return (
increase()}>+ decrease()}>- - reset()}>[] + reset()}>RES + fit()}>FIT
) } diff --git a/packages/pipeline-graph/package.json b/packages/pipeline-graph/package.json index 36e4adf7ea..a6e84774ba 100644 --- a/packages/pipeline-graph/package.json +++ b/packages/pipeline-graph/package.json @@ -1,6 +1,6 @@ { "name": "@harnessio/pipeline-graph", - "version": "1.0.0-alpha.2", + "version": "1.0.0-react18.alpha.6", "private": false, "author": "Harness Inc.", "license": "Apache-2.0", @@ -14,19 +14,18 @@ "scripts": { "examples": "vite dev --config vite.config.dev.ts", "dev": "vite build --watch", - "build": "vite build" + "build": "vite build", + "prepublishOnly": "pnpm build" }, "peerDependencies": { - "@types/react": "^17.0.3", - "@types/react-dom": "^17.0.3", - "react": "^17.0.2", - "react-dom": "^17.0.2" + "react": "^18.3.1", + "react-dom": "^18.3.1" }, "devDependencies": { "@types/lodash-es": "^4.17.12", "@types/node": "^22.10.1", - "@types/react": "^17.0.3", - "@types/react-dom": "^17.0.3", + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", "@vitejs/plugin-react-swc": "^3.7.2", "vite": "^6.0.2", "vite-plugin-dts": "^4.3.0", diff --git a/packages/pipeline-graph/src/components/components/collapse.tsx b/packages/pipeline-graph/src/components/components/collapse.tsx index f325bd7e22..49b7190124 100644 --- a/packages/pipeline-graph/src/components/components/collapse.tsx +++ b/packages/pipeline-graph/src/components/components/collapse.tsx @@ -1,6 +1,19 @@ -// TODO: move this component outside of library -export default function CollapseButton(props: { collapsed: boolean; onToggle?: () => void }) { - const { collapsed, onToggle } = props +import {useContainerNodeContext} from "../../context/container-node-provider"; + +export interface CollapseButtonProps { + collapsed: boolean + onToggle?: () => void +} + +export default function CollapseButton({ + collapsed, + onToggle, +}: CollapseButtonProps) { + const { collapseButtonComponent } = useContainerNodeContext() + + if (!!collapseButtonComponent) { + return collapseButtonComponent({ collapsed, onToggle }) + } return ( setTargetEl: (el: HTMLDivElement) => void setCanvasTransform: ( - canvasTransform: CanvasTransform & { rootContainer?: HTMLDivElement; isInitial: boolean } + canvasTransform: CanvasTransform & { rootContainer?: HTMLDivElement; isInitial?: boolean } ) => void fit: () => void reset: () => void @@ -75,28 +75,34 @@ export const CanvasProvider = ({ children, config: configFromProps }: CanvasProv targetElRef.current = targetEl }, []) - const scaleInc = useCallback((scaleDiff: number) => { - const rootContainerEl = targetElRef?.current - const parentEl = rootContainerEl?.parentElement + const scaleInc = useCallback((scaleIncValue: number) => { + const targetEl = targetElRef?.current + const parentEl = targetEl?.parentElement - if (!rootContainerEl || !parentEl) return + if (!targetEl || !parentEl) return - let newScale = canvasTransformRef.current.scale + scaleDiff + let newScale = canvasTransformRef.current.scale + scaleIncValue newScale = Math.max(newScale, config.minScale) + newScale = Math.min(newScale, config.maxScale) - const rect = parentEl.getBoundingClientRect() + const scaleDiff = newScale / canvasTransformRef.current.scale - let originX = rect.width / 2 - let originY = rect.height / 2 + const parentElRect = parentEl.getBoundingClientRect() + const targetElRect = targetEl.getBoundingClientRect() - const currentRect = rootContainerEl.getBoundingClientRect() - originX -= currentRect.left - originY -= currentRect.top + const centerX = parentElRect.left + parentElRect.width / 2 + const centerY = parentElRect.top + parentElRect.height / 2 + + let originX = centerX - targetElRect.left + let originY = centerY - targetElRect.top const newTransform = calculateTransform({ - scaleDiff: newScale / canvasTransformRef.current.scale, - originX: originX, - originY: originY + scaleDiff, + originX, + originY, + currentScale: canvasTransformRef.current.scale, + currentTranslateX: canvasTransformRef.current.translateX, + currentTranslateY: canvasTransformRef.current.translateY }) setCanvasTransform(newTransform) @@ -116,20 +122,22 @@ export const CanvasProvider = ({ children, config: configFromProps }: CanvasProv translateX: initialTransformRef.current.translateX, translateY: initialTransformRef.current.translateY }) - }, [scaleInc]) + }, [setCanvasTransform]) const fit = useCallback(() => { - const rootContainerEl = targetElRef?.current - const parentEl = rootContainerEl?.parentElement - const nodesContainerEl = rootContainerEl?.getElementsByClassName( - 'PipelineGraph-NodesContainer' - )[0] as HTMLDivElement - const { width: parentWidth, height: parentHeight } = parentEl?.getBoundingClientRect() ?? new DOMRect() - const { width: graphWidth, height: graphHeight } = nodesContainerEl?.getBoundingClientRect() ?? new DOMRect() + const targetEl = targetElRef?.current + const parentEl = targetEl?.parentElement + const nodesContainerEl = targetEl?.getElementsByClassName('PipelineGraph-NodesContainer')[0] as + | HTMLDivElement + | undefined + + if (!parentEl || !nodesContainerEl) return + + const { width: parentWidth, height: parentHeight } = parentEl.getBoundingClientRect() + const { width: graphWidth, height: graphHeight } = nodesContainerEl.getBoundingClientRect() let scaleH = ((parentHeight - config.paddingForFit * 2) / graphHeight) * canvasTransformRef.current.scale let scaleW = ((parentWidth - config.paddingForFit * 2) / graphWidth) * canvasTransformRef.current.scale - scaleH = Math.max(scaleH, config.minScale) scaleW = Math.max(scaleW, config.minScale) diff --git a/packages/pipeline-graph/src/context/container-node-provider.tsx b/packages/pipeline-graph/src/context/container-node-provider.tsx index d7b36b952c..c08c1db211 100644 --- a/packages/pipeline-graph/src/context/container-node-provider.tsx +++ b/packages/pipeline-graph/src/context/container-node-provider.tsx @@ -1,6 +1,7 @@ import { createContext, useContext, useMemo } from 'react' import { ParallelContainerConfig, SerialContainerConfig } from '../types/container-node' +import {CollapseButtonProps} from "../components/components/collapse"; export const defaultSerialContainerConfig = { paddingLeft: 42, @@ -22,6 +23,7 @@ interface ContainerNodeContextProps { serialContainerConfig: SerialContainerConfig parallelContainerConfig: ParallelContainerConfig portComponent?: (props: { side: 'left' | 'right'; id?: string; adjustment?: number }) => JSX.Element + collapseButtonComponent?: (props: CollapseButtonProps) => JSX.Element } const ContainerNodeContext = createContext({ @@ -33,12 +35,14 @@ export interface ContainerNodeProviderProps { serialContainerConfig?: Partial parallelContainerConfig?: Partial portComponent?: (props: { side: 'left' | 'right'; id?: string; adjustment?: number }) => JSX.Element + collapseButtonComponent?: (props: CollapseButtonProps) => JSX.Element } const ContainerNodeProvider = ({ serialContainerConfig, parallelContainerConfig, portComponent, + collapseButtonComponent, children }: React.PropsWithChildren) => { const serialConfig: SerialContainerConfig = useMemo(() => { @@ -58,7 +62,8 @@ const ContainerNodeProvider = ({ value={{ serialContainerConfig: serialConfig, parallelContainerConfig: parallelConfig, - portComponent + portComponent, + collapseButtonComponent }} > {children} diff --git a/packages/pipeline-graph/src/index.ts b/packages/pipeline-graph/src/index.ts index f2ede42e34..7823f9159b 100644 --- a/packages/pipeline-graph/src/index.ts +++ b/packages/pipeline-graph/src/index.ts @@ -4,3 +4,4 @@ export * from './types/nodes-internal' export * from './types/node-content' export * from './context/canvas-provider' +export { type CollapseButtonProps } from './components/components/collapse' diff --git a/packages/pipeline-graph/src/pipeline-graph-internal.tsx b/packages/pipeline-graph/src/pipeline-graph-internal.tsx index f95a47f884..b010a6fb8b 100644 --- a/packages/pipeline-graph/src/pipeline-graph-internal.tsx +++ b/packages/pipeline-graph/src/pipeline-graph-internal.tsx @@ -14,7 +14,6 @@ export interface PipelineGraphInternalProps { data: AnyContainerNodeType[] customCreateSVGPath?: CreateSVGPathType config?: { - edgeClassName?: string leftGap?: number mode?: 'Edit' | 'Execution' } diff --git a/packages/pipeline-graph/src/pipeline-graph.tsx b/packages/pipeline-graph/src/pipeline-graph.tsx index 6aaf1f2af1..bf1d4bd839 100644 --- a/packages/pipeline-graph/src/pipeline-graph.tsx +++ b/packages/pipeline-graph/src/pipeline-graph.tsx @@ -8,12 +8,13 @@ import './pipeline-graph.css' import ContainerNodeProvider, { ContainerNodeProviderProps } from './context/container-node-provider' import { ParallelContainerConfig, SerialContainerConfig } from './types/container-node' -export interface PipelineGraphProps extends PipelineGraphInternalProps { - nodes: NodeContent[] - serialContainerConfig?: Partial - parallelContainerConfig?: Partial - portComponent?: ContainerNodeProviderProps['portComponent'] -} +export interface PipelineGraphProps extends + PipelineGraphInternalProps, + Pick { + nodes: NodeContent[] + serialContainerConfig?: Partial + parallelContainerConfig?: Partial + } export function PipelineGraph(props: PipelineGraphProps) { const { @@ -24,7 +25,8 @@ export function PipelineGraph(props: PipelineGraphProps) { parallelContainerConfig, customCreateSVGPath, edgesConfig, - portComponent + portComponent, + collapseButtonComponent } = props return ( @@ -33,6 +35,7 @@ export function PipelineGraph(props: PipelineGraphProps) { serialContainerConfig={serialContainerConfig} parallelContainerConfig={parallelContainerConfig} portComponent={portComponent} + collapseButtonComponent={collapseButtonComponent} > + return } case 'parallel': { - return + return } case 'leaf': { - return + return } } } diff --git a/packages/pipeline-graph/src/render/render-svg-lines.ts b/packages/pipeline-graph/src/render/render-svg-lines.ts index 012a3d3202..b2fa585b77 100644 --- a/packages/pipeline-graph/src/render/render-svg-lines.ts +++ b/packages/pipeline-graph/src/render/render-svg-lines.ts @@ -120,7 +120,6 @@ function getPath({ serial?: { position: 'left' | 'right' } - edgeClassName?: string targetNode?: AnyNodeInternal edgesConfig: { radius: number diff --git a/packages/ui/package.json b/packages/ui/package.json index bf53c14198..9e4d6afa11 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,7 +1,7 @@ { "name": "@harnessio/ui", "description": "Harness Canary UI component library", - "version": "0.0.1-alpha.20", + "version": "0.0.1-react18.alpha.18", "private": false, "type": "module", "main": "./dist/index.js", @@ -119,19 +119,19 @@ "sonner": "^1.5.0", "tailwind-merge": "^2.3.0", "tailwindcss-animate": "^1.0.7", - "yaml": "^2.7.0", "vaul": "^1.1.2", + "yaml": "^2.7.0", "zod": "^3.23.8" }, "peerDependencies": { - "react": ">=17" + "react": ">=18" }, "devDependencies": { "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.0.1", "@types/lodash-es": "^4.17.12", "@types/node": "^22.9.0", - "@types/react": "^17.0.3", + "@types/react": "^18.3.18", "@vitejs/plugin-react-swc": "^3.7.2", "@vitest/coverage-istanbul": "^2.1.8", "@vitest/ui": "^2.1.8", diff --git a/packages/ui/src/components/chat/chat-diff-viewer.tsx b/packages/ui/src/components/chat/chat-diff-viewer.tsx index f548a7d209..cded6a1ac3 100644 --- a/packages/ui/src/components/chat/chat-diff-viewer.tsx +++ b/packages/ui/src/components/chat/chat-diff-viewer.tsx @@ -67,7 +67,9 @@ export const ChatDiffViewer = ({ data, mode = 4, lang = 'go', fileName }: ChatDi return (
{fileName && ( - {fileName} + + {fileName} + )} {diffFileInstance && ( // eslint-disable-next-line @typescript-eslint/ban-ts-comment diff --git a/packages/ui/src/components/chat/chat-empty-preview-wrapper.tsx b/packages/ui/src/components/chat/chat-empty-preview-wrapper.tsx index 9783a41d63..af7e71a27e 100644 --- a/packages/ui/src/components/chat/chat-empty-preview-wrapper.tsx +++ b/packages/ui/src/components/chat/chat-empty-preview-wrapper.tsx @@ -4,7 +4,7 @@ import { Chat } from '@/components' export const ChatEmptyPreviewWrapper: FC = () => { return ( -
+
diff --git a/packages/ui/src/components/chat/chat-preview-wrapper.tsx b/packages/ui/src/components/chat/chat-preview-wrapper.tsx index a8aa25780d..6291c9232c 100644 --- a/packages/ui/src/components/chat/chat-preview-wrapper.tsx +++ b/packages/ui/src/components/chat/chat-preview-wrapper.tsx @@ -21,6 +21,7 @@ export const ChatPreviewWrapper: FC = () => { return (
+ {}} /> Hey Olivia! I've finished with the requirements doc! I made some notes in the gdoc as well for Phoenix diff --git a/packages/ui/src/components/chat/chat.tsx b/packages/ui/src/components/chat/chat.tsx index 13fe30ba73..11eb340b5d 100644 --- a/packages/ui/src/components/chat/chat.tsx +++ b/packages/ui/src/components/chat/chat.tsx @@ -4,22 +4,23 @@ import { Button, Icon, Input } from '@/components' import ChatAvatarIcon from '@/icons/chat-avatar.svg' import { cn } from '@utils/cn' -const Root: FC = ({ children }: PropsWithChildren>) => { +const Root: FC>> = ({ children }) => { + return
{children}
+} + +const Header: FC<{ onClose: () => void }> = ({ onClose }) => { return ( -
-
-

AI Assistant

- -
- {children} +
+

AI Assistant

+
) } -const Body: FC = ({ children }: PropsWithChildren>) => { +const Body: FC>> = ({ children }) => { return (
{children} @@ -27,7 +28,7 @@ const Body: FC = ({ children }: PropsWithChildren>) ) } -const Footer: FC = ({ children }: PropsWithChildren>) => { +const Footer: FC>> = ({ children }) => { return
{children}
} @@ -64,7 +65,7 @@ const Message: FC = ({ self, avatar, actions, children }) => { ) } -const CodeBlock: FC<{ className?: string }> = ({ children, className }) => { +const CodeBlock: FC> = ({ children, className }) => { return ( = ({ export const Chat = { Root, Body, + Header, Footer, Message, Typing, diff --git a/packages/ui/src/components/chat_deprecated.tsx b/packages/ui/src/components/chat_deprecated.tsx index 82066a6291..b88bdaa09d 100644 --- a/packages/ui/src/components/chat_deprecated.tsx +++ b/packages/ui/src/components/chat_deprecated.tsx @@ -5,17 +5,17 @@ import { cn } from '@/utils/cn' // Root Container const Root: React.FC<{ children: ReactNode }> = ({ children }) => { - return
{children}
+ return
{children}
} // Body const Body: React.FC<{ children: ReactNode }> = ({ children }) => { - return
{children}
+ return
{children}
} // Footer const Footer: React.FC<{ children: ReactNode }> = ({ children }) => { - return
{children}
+ return
{children}
} // Message Component @@ -48,8 +48,8 @@ const Message: React.FC = ({ self, time, avatar, actions, children > {children}
-
-
{actions &&
{actions}
}
+
+
{actions &&
{actions}
}
{time && ( {time} @@ -68,9 +68,9 @@ interface TypingProps { const Typing: React.FC = ({ avatar }) => { return ( -
+
{avatar} -
+
· · @@ -115,10 +115,10 @@ const InputField: React.FC = ({ sendIcon = }) => { return ( -
+
= ({ variant="outline" size="icon" disabled={disabled} - className="absolute right-3.5 bottom-3.5 z-10 w-6 h-6" + className="absolute bottom-3.5 right-3.5 z-10 size-6" > {sendIcon} diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index 8194104113..55b4e0ae3d 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -40,6 +40,7 @@ import CircleArrowTopRight from '../icons/circle-arrow-top-right.svg' import CircleArrowTop from '../icons/circle-arrow-top.svg' import CircleArrowsUpDown from '../icons/circle-arrows-updown.svg' import CirclePlus from '../icons/circle-plus.svg' +import CircleWithSector from '../icons/circle-with-sector.svg' import Circle from '../icons/circle.svg' import ClockIcon from '../icons/clock-icon.svg' import Clock from '../icons/clock.svg' @@ -52,6 +53,9 @@ import Code from '../icons/code.svg' import Cog6 from '../icons/cog-6.svg' import CollapseComment from '../icons/collapse-comment.svg' import CollapseDiff from '../icons/collapse-diff.svg' +import CollapseIn from '../icons/collapse-in.svg' +import CollapseOut from '../icons/collapse-out.svg' +import CommandSymbol from '../icons/command-symbol.svg' import Comments from '../icons/comments.svg' import Compare from '../icons/compare.svg' import Connectors from '../icons/connectors-icon.svg' @@ -68,6 +72,7 @@ import DevInsightsGradient from '../icons/dev-insights-gradient.svg' import DevInsights from '../icons/dev-insights-icon.svg' import DevPortalGradient from '../icons/dev-portal-gradient.svg' import DevPortal from '../icons/dev-portal-icon.svg' +import Docker from '../icons/docker.svg' import MoreDotsFill from '../icons/dots-icon.svg' import DoubleTick from '../icons/double-tick.svg' import Download from '../icons/download-icon.svg' @@ -164,14 +169,17 @@ import SidebarIcon from '../icons/sidebar-icon.svg' import SidebarLeft from '../icons/sidebar-left.svg' import SidebarRight from '../icons/sidebar-right.svg' import Signpost from '../icons/signpost.svg' +import Slack from '../icons/slack.svg' import Snow from '../icons/snow-icon.svg' import SparksGradientDark from '../icons/sparks-gradient-dark.svg' import SparksGradientLight from '../icons/sparks-gradient-light.svg' import SparksGradient from '../icons/sparks-gradient.svg' import Sparks from '../icons/sparks.svg' +import SquareDashed from '../icons/square-dashed.svg' import SshKey from '../icons/ssh-key.svg' import Stack from '../icons/stack-icon.svg' import Star from '../icons/star-icon.svg' +import Stop from '../icons/stop-icon.svg' import SubMenuEllipse from '../icons/sub-menu-ellipse.svg' import Success from '../icons/success.svg' import Suggestion from '../icons/suggestion.svg' @@ -392,8 +400,16 @@ const IconNameMap = { 'arrow-short': ArrowShort, 'bold-plus': BoldPlus, 'checkbox-circle': CheckboxCircle, + 'circle-with-sector': CircleWithSector, + 'command-symbol': CommandSymbol, 'cross-circle': CrossCircle, - 'warning-triangle-outline': WarningTriangleOutline + 'warning-triangle-outline': WarningTriangleOutline, + slack: Slack, + docker: Docker, + stop: Stop, + 'collapse-out': CollapseOut, + 'collapse-in': CollapseIn, + 'square-dashed': SquareDashed } satisfies Record>> export interface IconProps { diff --git a/packages/ui/src/components/language-selector/language-dialog.tsx b/packages/ui/src/components/language-selector/language-dialog.tsx index 1179b162aa..32569c9ee8 100644 --- a/packages/ui/src/components/language-selector/language-dialog.tsx +++ b/packages/ui/src/components/language-selector/language-dialog.tsx @@ -40,7 +40,7 @@ const LanguageDialog: FC = ({ }} >
-
+
{lang.code}
({ )} - + {placeholder} diff --git a/packages/ui/src/components/navbar-project-chooser.tsx b/packages/ui/src/components/navbar-project-chooser.tsx index 9863d57dd2..e7910f7d8c 100644 --- a/packages/ui/src/components/navbar-project-chooser.tsx +++ b/packages/ui/src/components/navbar-project-chooser.tsx @@ -24,7 +24,7 @@ function Root({ logo }: ProjectProps) { return (
-
{logo}
+
{logo}
{title && (
-

{title}

+

{title}

)} {children} diff --git a/packages/ui/src/components/node-group.tsx b/packages/ui/src/components/node-group.tsx index 2370554c70..89703bf717 100644 --- a/packages/ui/src/components/node-group.tsx +++ b/packages/ui/src/components/node-group.tsx @@ -62,7 +62,7 @@ function Connector({ first, last, className }: { first?: boolean; last?: boolean className={cn('absolute bottom-0 left-2.5 top-0 z-10 w-1', { 'top-3': first }, { 'bottom-8': last }, className)} data-connector > - +
) } diff --git a/packages/ui/src/components/problems.tsx b/packages/ui/src/components/problems.tsx index 2c3433d54b..114988a30f 100644 --- a/packages/ui/src/components/problems.tsx +++ b/packages/ui/src/components/problems.tsx @@ -41,7 +41,7 @@ const ProblemsComponent = { role="button" tabIndex={0} onClick={onClick} - className={`width-100 flex flex-1 cursor-pointer items-center gap-2 text-nowrap py-0.5 justify-between px-4 ${rowClasses}`} + className={`width-100 flex flex-1 cursor-pointer items-center justify-between gap-2 text-nowrap px-4 py-0.5 ${rowClasses}`} > {children}
diff --git a/packages/ui/src/components/table.tsx b/packages/ui/src/components/table.tsx index 37dd6b15f4..9c540dfe8f 100644 --- a/packages/ui/src/components/table.tsx +++ b/packages/ui/src/components/table.tsx @@ -8,7 +8,7 @@ const tableVariants = cva('w-full text-sm', { variant: { default: 'caption-bottom', asStackedList: - 'bg-background-surface rounded-md border [&_td]:px-4 [&_td]:py-2.5 [&_td]:align-top [&_th]:px-4 [&_thead]:bg-background-2' + 'rounded-md border bg-background-surface [&_td]:px-4 [&_td]:py-2.5 [&_td]:align-top [&_th]:px-4 [&_thead]:bg-background-2' } }, defaultVariants: { diff --git a/packages/ui/src/components/tabnav.tsx b/packages/ui/src/components/tabnav.tsx index 711a9e0e1b..7b85897fbe 100644 --- a/packages/ui/src/components/tabnav.tsx +++ b/packages/ui/src/components/tabnav.tsx @@ -1,8 +1,9 @@ +import { PropsWithChildren } from 'react' import { NavLink, NavLinkProps } from 'react-router-dom' import { cn } from '@utils/cn' -const TabNavRoot: React.FC = ({ children }) => { +const TabNavRoot: React.FC = ({ children }) => { return (