diff --git a/packages/frontend/editor-ui/src/components/RunData.test.ts b/packages/frontend/editor-ui/src/components/RunData.test.ts
index b1648f74b4..39a3163878 100644
--- a/packages/frontend/editor-ui/src/components/RunData.test.ts
+++ b/packages/frontend/editor-ui/src/components/RunData.test.ts
@@ -606,6 +606,128 @@ describe('RunData', () => {
expect(getByTestId('ndv-items-count')).toBeInTheDocument();
});
+ describe('computed properties for branch handling', () => {
+ it('no run selector when no run data exists', () => {
+ const { container } = render({
+ displayMode: 'json',
+ runs: [],
+ });
+
+ // Component instance checks would need to be done differently in Vue 3
+ // For now, we verify the behavior through the UI
+ expect(container.querySelector('.run-selector')).not.toBeInTheDocument();
+ });
+
+ it('Show run selector when branch switch is shown (with all runs)', async () => {
+ // Create multiple runs with data in different outputs
+ const multipleRuns = [
+ {
+ startTime: Date.now(),
+ executionIndex: 0,
+ executionTime: 1,
+ data: {
+ main: [
+ [{ json: { value: 1 } }], // output 0
+ [{ json: { value: 2 } }], // output 1
+ ],
+ },
+ source: [null],
+ },
+ {
+ startTime: Date.now(),
+ executionIndex: 1,
+ executionTime: 1,
+ data: {
+ main: [
+ [{ json: { value: 3 } }], // output 0
+ [], // output 1 empty
+ ],
+ },
+ source: [null],
+ },
+ {
+ startTime: Date.now(),
+ executionIndex: 2,
+ executionTime: 1,
+ data: {
+ main: [
+ [], // output 0 empty
+ [{ json: { value: 4 } }], // output 1
+ ],
+ },
+ source: [null],
+ },
+ ];
+
+ const { getByTestId, findAllByTestId } = render({
+ displayMode: 'json',
+ runs: multipleRuns,
+ });
+
+ // When there are multiple branches and outputs, the run selector should be visible
+ const runSelector = getByTestId('run-selector');
+ expect(runSelector).toBeInTheDocument();
+
+ const runSelectorOptionsCount = await findAllByTestId('run-selection-option');
+ expect(runSelectorOptionsCount.length).toBe(3);
+ });
+
+ it('Show run selector when there is no branch selector (only runs for branch with data)', async () => {
+ // Create multiple runs with data in different outputs
+ const multipleRuns = [
+ {
+ startTime: Date.now(),
+ executionIndex: 0,
+ executionTime: 1,
+ data: {
+ main: [
+ [{ json: { value: 1 } }], // output 0
+ [{ json: { value: 2 } }], // output 1
+ ],
+ },
+ source: [null],
+ },
+ {
+ startTime: Date.now(),
+ executionIndex: 1,
+ executionTime: 1,
+ data: {
+ main: [
+ [{ json: { value: 3 } }], // output 0
+ [], // output 1 empty
+ ],
+ },
+ source: [null],
+ },
+ {
+ startTime: Date.now(),
+ executionIndex: 2,
+ executionTime: 1,
+ data: {
+ main: [
+ [], // output 0 empty
+ [{ json: { value: 4 } }], // output 1
+ ],
+ },
+ source: [null],
+ },
+ ];
+
+ const { getByTestId, findAllByTestId } = render({
+ displayMode: 'json',
+ runs: multipleRuns,
+ overrideOutputs: [1],
+ });
+
+ // Should show run selector since there are multiple runs
+ const runSelector = getByTestId('run-selector');
+ expect(runSelector).toBeInTheDocument();
+
+ const runSelectorOptionsCount = await findAllByTestId('run-selection-option');
+ expect(runSelectorOptionsCount.length).toBe(2);
+ });
+ });
+
// Default values for the render function
const nodes = [
{
@@ -627,6 +749,7 @@ describe('RunData', () => {
paneType = 'output',
metadata,
runs,
+ overrideOutputs,
}: {
defaultRunItems?: INodeExecutionData[];
workflowId?: string;
@@ -636,6 +759,7 @@ describe('RunData', () => {
paneType?: NodePanelType;
metadata?: ITaskMetadata;
runs?: ITaskData[];
+ overrideOutputs?: number[];
}) => {
const defaultRun: ITaskData = {
startTime: Date.now(),
@@ -735,6 +859,7 @@ describe('RunData', () => {
tooMuchDataTitle: '',
executingMessage: '',
noDataInBranchMessage: '',
+ overrideOutputs,
},
pinia,
});
diff --git a/packages/frontend/editor-ui/src/components/RunData.vue b/packages/frontend/editor-ui/src/components/RunData.vue
index 8ff8a3e61f..7f27182421 100644
--- a/packages/frontend/editor-ui/src/components/RunData.vue
+++ b/packages/frontend/editor-ui/src/components/RunData.vue
@@ -388,6 +388,9 @@ const maxOutputIndex = computed(() => {
return 0;
});
const currentPageOffset = computed(() => pageSize.value * (currentPage.value - 1));
+const showBranchSwitch = computed(
+ () => maxOutputIndex.value > 0 && branches.value.length > 1 && !displaysMultipleNodes.value,
+);
const maxRunIndex = computed(() => {
if (!node.value) {
return 0;
@@ -406,6 +409,29 @@ const maxRunIndex = computed(() => {
return 0;
});
+const runSelectorOptionsCount = computed(() => {
+ if (!node.value) {
+ return 0;
+ }
+
+ const runData: IRunData | null = workflowRunData.value;
+
+ if (!runData?.hasOwnProperty(node.value.name)) {
+ return 0;
+ }
+
+ // If there is branch selector – we show all runs in the run selector
+ if (showBranchSwitch.value) {
+ return maxRunIndex.value + 1;
+ }
+
+ // If there is only one branch - we show only the runs containing the data in the connected branch
+ return runData[node.value.name].filter((nodeRun) => {
+ const nodeOutput = nodeRun?.data?.[connectionType.value]?.[currentOutputIndex.value];
+ return nodeOutput && nodeOutput?.length > 0;
+ }).length;
+});
+
const rawInputData = computed(() =>
getRawInputData(props.runIndex, currentOutputIndex.value, connectionType.value),
);
@@ -1136,7 +1162,7 @@ function getRunLabel(option: number) {
: '';
const itemsLabel = itemsCount > 0 ? ` (${items}${subexecutions})` : '';
- return option + i18n.baseText('ndv.output.of') + (maxRunIndex.value + 1) + itemsLabel;
+ return option + i18n.baseText('ndv.output.of') + runSelectorOptionsCount.value + itemsLabel;
}
function getRawInputData(
@@ -1510,10 +1536,11 @@ defineExpose({ enterEditMode });
>
{{ i18n.baseText('ndv.output.run') }}