fix progress state on Vue nodes in subgraphs (#5842)
## Summary Fixes two errors with subgraph progress states: 1. Nodes inside subgraphs were not having progress state shown 2. Subgraph nodes (outer representation) themselves did not have a visible progress state 1 is fixed by using locator IDs instead of local node IDs. 2 is fixed by ensuring the subgraph title button does not wrap to a newline and thus block the progress bar under the node header. ## Changes - **What**: Updated `useNodeExecutionState` composable to use `nodeLocatorId` for tracking execution state across subgraph boundaries - **What**: Modified NodeHeader layout to fix subgraph enter button positioning with proper flexbox gap ## Review Focus Execution state tracking accuracy for nested subgraph nodes and NodeHeader layout consistency across different node types. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5842-fix-progress-state-on-Vue-nodes-in-subgraphs-27c6d73d365081cb8335c8bb5dbd74f7) by [Unito](https://www.unito.io) --------- Co-authored-by: github-actions <github-actions@github.com>
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 68 KiB |
@@ -211,7 +211,8 @@ const isSelected = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Use execution state composable
|
// Use execution state composable
|
||||||
const { executing, progress } = useNodeExecutionState(() => nodeData.id)
|
const nodeLocatorId = computed(() => getLocatorIdFromNodeData(nodeData))
|
||||||
|
const { executing, progress } = useNodeExecutionState(nodeLocatorId)
|
||||||
|
|
||||||
// Direct access to execution store for error state
|
// Direct access to execution store for error state
|
||||||
const executionStore = useExecutionStore()
|
const executionStore = useExecutionStore()
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
:data-testid="`node-header-${nodeData?.id || ''}`"
|
:data-testid="`node-header-${nodeData?.id || ''}`"
|
||||||
@dblclick="handleDoubleClick"
|
@dblclick="handleDoubleClick"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between relative">
|
<div class="flex items-center justify-between gap-2.5 relative">
|
||||||
<!-- Collapse/Expand Button -->
|
<!-- Collapse/Expand Button -->
|
||||||
<button
|
<button
|
||||||
v-show="!readonly"
|
v-show="!readonly"
|
||||||
@@ -43,24 +43,22 @@
|
|||||||
data-testid="node-pin-indicator"
|
data-testid="node-pin-indicator"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="!readonly" class="flex items-center lod-toggle shrink-0">
|
||||||
|
<IconButton
|
||||||
|
v-if="isSubgraphNode"
|
||||||
|
size="sm"
|
||||||
|
type="transparent"
|
||||||
|
class="text-stone-200 dark-theme:text-slate-300"
|
||||||
|
data-testid="subgraph-enter-button"
|
||||||
|
title="Enter Subgraph"
|
||||||
|
@click.stop="handleEnterSubgraph"
|
||||||
|
@dblclick.stop
|
||||||
|
>
|
||||||
|
<i class="pi pi-external-link"></i>
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
<LODFallback />
|
<LODFallback />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Title Buttons -->
|
|
||||||
<div v-if="!readonly" class="flex items-center lod-toggle">
|
|
||||||
<IconButton
|
|
||||||
v-if="isSubgraphNode"
|
|
||||||
size="sm"
|
|
||||||
type="transparent"
|
|
||||||
class="text-stone-200 dark-theme:text-slate-300"
|
|
||||||
data-testid="subgraph-enter-button"
|
|
||||||
title="Enter Subgraph"
|
|
||||||
@click.stop="handleEnterSubgraph"
|
|
||||||
@dblclick.stop
|
|
||||||
>
|
|
||||||
<i class="pi pi-external-link"></i>
|
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -9,27 +9,27 @@ import { useExecutionStore } from '@/stores/executionStore'
|
|||||||
* Provides reactive access to execution state and progress for a specific node
|
* Provides reactive access to execution state and progress for a specific node
|
||||||
* by injecting execution data from the parent GraphCanvas provider.
|
* by injecting execution data from the parent GraphCanvas provider.
|
||||||
*
|
*
|
||||||
* @param nodeIdMaybe - The ID of the node to track execution state for
|
* @param nodeLocatorIdMaybe - Locator ID (root or subgraph scoped) of the node to track
|
||||||
* @returns Object containing reactive execution state and progress
|
* @returns Object containing reactive execution state and progress
|
||||||
*/
|
*/
|
||||||
export const useNodeExecutionState = (
|
export const useNodeExecutionState = (
|
||||||
nodeIdMaybe: MaybeRefOrGetter<string>
|
nodeLocatorIdMaybe: MaybeRefOrGetter<string | undefined>
|
||||||
) => {
|
) => {
|
||||||
const nodeId = toValue(nodeIdMaybe)
|
const locatorId = computed(() => toValue(nodeLocatorIdMaybe) ?? '')
|
||||||
const { uniqueExecutingNodeIdStrings, nodeProgressStates } =
|
const { nodeLocationProgressStates } = storeToRefs(useExecutionStore())
|
||||||
storeToRefs(useExecutionStore())
|
|
||||||
|
|
||||||
const executing = computed(() => {
|
const progressState = computed(() => {
|
||||||
return uniqueExecutingNodeIdStrings.value.has(nodeId)
|
const id = locatorId.value
|
||||||
|
return id ? nodeLocationProgressStates.value[id] : undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const executing = computed(() => progressState.value?.state === 'running')
|
||||||
|
|
||||||
const progress = computed(() => {
|
const progress = computed(() => {
|
||||||
const state = nodeProgressStates.value[nodeId]
|
const state = progressState.value
|
||||||
return state?.max > 0 ? state.value / state.max : undefined
|
return state && state.max > 0 ? state.value / state.max : undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
const progressState = computed(() => nodeProgressStates.value[nodeId])
|
|
||||||
|
|
||||||
const progressPercentage = computed(() => {
|
const progressPercentage = computed(() => {
|
||||||
const prog = progress.value
|
const prog = progress.value
|
||||||
return prog !== undefined ? Math.round(prog * 100) : undefined
|
return prog !== undefined ? Math.round(prog * 100) : undefined
|
||||||
|
|||||||