diff --git a/app/gui2/e2e/typesOnNodeHover.spec.ts b/app/gui2/e2e/typesOnNodeHover.spec.ts index 23ba744217..6433240c37 100644 --- a/app/gui2/e2e/typesOnNodeHover.spec.ts +++ b/app/gui2/e2e/typesOnNodeHover.spec.ts @@ -1,28 +1,30 @@ -import { expect, test, type Locator, type Page } from '@playwright/test' -import assert from 'assert' +import { test, type Locator, type Page } from '@playwright/test' import * as actions from './actions' +import { expect } from './customExpect' import { mockExpressionUpdate } from './expressionUpdates' import * as locate from './locate' -const DUMMY_INT_TYPE = 'Standard.Base.Data.Numbers.Integer' -const DUMMY_STRING_TYPE = 'Standard.Base.Data.Text.Text' -const DUMMY_FLOAT_TYPE = 'Standard.Base.Data.Numbers.Float' -const UNKNOWN_TYPE = 'Unknown' -async function assertTypeLabelOnNode(page: Page, node: Locator, type: string) { - const targetLabel = node.locator('.outputPortLabel').first() - await expect(targetLabel).toHaveText(type) - await expect(targetLabel).toHaveCSS('opacity', '0') - - const outputPortArea = await node.locator('.outputPortHoverArea').boundingBox() - assert(outputPortArea, 'The outputPortArea of the node is null') - const outputPortX = outputPortArea.x + outputPortArea.width / 2.0 - const outputPortY = outputPortArea.y + outputPortArea.height - 2.0 - await page.mouse.move(outputPortX, outputPortY) - await expect(targetLabel).toBeVisible() - await expect(targetLabel).toHaveCSS('opacity', '1') +const DUMMY_INT_TYPE = { full: 'Standard.Base.Data.Numbers.Integer', short: 'Integer' } +const DUMMY_STRING_TYPE = { full: 'Standard.Base.Data.Text.Text', short: 'Text' } +const DUMMY_FLOAT_TYPE = { full: 'Standard.Base.Data.Numbers.Float', short: 'Float' } +const UNKNOWN_TYPE = { full: 'Unknown', short: 'Unknown' } +async function assertTypeLabelOnNode( + page: Page, + node: Locator, + type: { full: string; short: string }, +) { + await node.hover({ position: { x: 8, y: 8 } }) + await locate.toggleVisualizationButton(node).click() + const targetLabel = node.locator('.node-type').first() + await expect(targetLabel).toHaveText(type.short) + await expect(targetLabel).toHaveAttribute('title', type.full) } -async function assertTypeLabelOnNodeByBinding(page: Page, label: string, type: string) { +async function assertTypeLabelOnNodeByBinding( + page: Page, + label: string, + type: { full: string; short: string }, +) { const node = locate.graphNodeByBinding(page, label) await assertTypeLabelOnNode(page, node, type) } @@ -31,10 +33,10 @@ test('shows the correct type when hovering a node', async ({ page }) => { await actions.goToGraph(page) // Note that the types don't have to make sense, they just have to be applied. - await mockExpressionUpdate(page, 'five', { type: DUMMY_INT_TYPE }) - await mockExpressionUpdate(page, 'ten', { type: DUMMY_STRING_TYPE }) - await mockExpressionUpdate(page, 'sum', { type: DUMMY_FLOAT_TYPE }) - await mockExpressionUpdate(page, 'prod', { type: DUMMY_INT_TYPE }) + await mockExpressionUpdate(page, 'five', { type: DUMMY_INT_TYPE.full }) + await mockExpressionUpdate(page, 'ten', { type: DUMMY_STRING_TYPE.full }) + await mockExpressionUpdate(page, 'sum', { type: DUMMY_FLOAT_TYPE.full }) + await mockExpressionUpdate(page, 'prod', { type: DUMMY_INT_TYPE.full }) await assertTypeLabelOnNodeByBinding(page, 'five', DUMMY_INT_TYPE) await assertTypeLabelOnNodeByBinding(page, 'ten', DUMMY_STRING_TYPE) diff --git a/app/gui2/src/components/GraphEditor/GraphNode.vue b/app/gui2/src/components/GraphEditor/GraphNode.vue index 0605e2f78b..199dca4733 100644 --- a/app/gui2/src/components/GraphEditor/GraphNode.vue +++ b/app/gui2/src/components/GraphEditor/GraphNode.vue @@ -373,7 +373,7 @@ const handleNodeClick = useDoubleClick( interface PortData { clipRange: [number, number] - label: string + label: string | undefined portId: AstId } @@ -381,12 +381,9 @@ const outputPorts = computed((): PortData[] => { const ports = outputPortsSet.value const numPorts = ports.size return Array.from(ports, (portId, index): PortData => { - const labelIdent = numPorts > 1 ? graph.db.getOutputPortIdentifier(portId) + ': ' : '' - const labelType = - graph.db.getExpressionInfo(numPorts > 1 ? portId : nodeId.value)?.typename ?? 'Unknown' return { clipRange: [index / numPorts, (index + 1) / numPorts], - label: labelIdent + labelType, + label: numPorts > 1 ? graph.db.getOutputPortIdentifier(portId) : undefined, portId, } }) diff --git a/app/gui2/src/components/GraphEditor/GraphVisualization.vue b/app/gui2/src/components/GraphEditor/GraphVisualization.vue index 551db0ca0a..254b012625 100644 --- a/app/gui2/src/components/GraphEditor/GraphVisualization.vue +++ b/app/gui2/src/components/GraphEditor/GraphVisualization.vue @@ -293,6 +293,9 @@ provideVisualizationConfig({ get icon() { return icon.value }, + get nodeType() { + return props.typename + }, hide: () => emit('update:visible', false), updateType: (id) => emit('update:id', id), createNodes: (...options) => emit('createNodes', options), diff --git a/app/gui2/src/components/VisualizationContainer.vue b/app/gui2/src/components/VisualizationContainer.vue index 619d4e2ff9..307a3057bb 100644 --- a/app/gui2/src/components/VisualizationContainer.vue +++ b/app/gui2/src/components/VisualizationContainer.vue @@ -4,7 +4,8 @@ import SvgIcon from '@/components/SvgIcon.vue' import VisualizationSelector from '@/components/VisualizationSelector.vue' import { PointerButtonMask, isTriggeredByKeyboard, usePointer } from '@/composables/events' import { useVisualizationConfig } from '@/providers/visualizationConfig' -import { onMounted, ref, watchEffect } from 'vue' +import { isQualifiedName, qnLastSegment } from '@/util/qualifiedName' +import { computed, onMounted, ref, watchEffect } from 'vue' const props = defineProps<{ /** If true, the visualization should be `overflow: visible` instead of `overflow: hidden`. */ @@ -55,39 +56,34 @@ function hideSelector() { requestAnimationFrame(() => (isSelectorVisible.value = false)) } -const resizeRight = usePointer((pos, _, type) => { - if (type !== 'move' || pos.delta.x === 0) { - return - } - const width = - (pos.absolute.x - (contentNode.value?.getBoundingClientRect().left ?? 0)) / config.scale - config.width = Math.max(width, MIN_WIDTH_PX) -}, PointerButtonMask.Main) +function resizeHandler(resizeX: boolean, resizeY: boolean) { + return usePointer((pos, _, type) => { + if (type !== 'move') { + return + } + if (resizeX && pos.delta.x !== 0) { + const width = + (pos.absolute.x - (contentNode.value?.getBoundingClientRect().left ?? 0)) / config.scale + config.width = Math.max(0, width) + } + if (resizeY && pos.delta.y !== 0) { + const height = + (pos.absolute.y - (contentNode.value?.getBoundingClientRect().top ?? 0)) / config.scale + config.height = Math.max(0, height) + } + }, PointerButtonMask.Main) +} -const resizeBottom = usePointer((pos, _, type) => { - if (type !== 'move' || pos.delta.y === 0) { - return - } - const height = - (pos.absolute.y - (contentNode.value?.getBoundingClientRect().top ?? 0)) / config.scale - config.height = Math.max(0, height) -}, PointerButtonMask.Main) +const resizeRight = resizeHandler(true, false) +const resizeBottom = resizeHandler(false, true) +const resizeBottomRight = resizeHandler(true, true) -const resizeBottomRight = usePointer((pos, _, type) => { - if (type !== 'move') { - return - } - if (pos.delta.x !== 0) { - const width = - (pos.absolute.x - (contentNode.value?.getBoundingClientRect().left ?? 0)) / config.scale - config.width = Math.max(0, width) - } - if (pos.delta.y !== 0) { - const height = - (pos.absolute.y - (contentNode.value?.getBoundingClientRect().top ?? 0)) / config.scale - config.height = Math.max(0, height) - } -}, PointerButtonMask.Main) +const UNKNOWN_TYPE = 'Unknown' +const nodeShortType = computed(() => + config.nodeType != null && isQualifiedName(config.nodeType) ? + qnLastSegment(config.nodeType) + : UNKNOWN_TYPE, +)