refactor: migrate remaining locate buttons to shared LocateNodeButton#13795
refactor: migrate remaining locate buttons to shared LocateNodeButton#13795mattmillerai wants to merge 1 commit into
Conversation
Replace the five inline lucide--locate Button usages in SwapNodeGroupRow, MissingModelRow, and MissingMediaCard with the shared LocateNodeButton component, passing node-specific accessible labels via rightSidePanel.locateNodeFor to match the errors-panel siblings. This also gives the swap-node and missing-model locate buttons click.stop (harmless: no ancestor binds a DOM click handler) and node-specific aria-labels in place of the previous generic label.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
🎭 Playwright: ❌ 1710 passed, 2 failed · 2 flaky❌ Failed Tests📊 Browser Reports
🎨 Storybook: ✅ Built — View Storybook📦 Bundle Size
⚡ Performance Report
Absolute values
Raw data{
"timestamp": "2026-07-18T14:00:36.959Z",
"gitSha": "adc479d5192c87a545f3b83458d9d78335e106f3",
"branch": "matt/be-3475-locate-node-button-migration",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2086.0409999999947,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.461999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 436.89399999999995,
"heapDeltaBytes": 3935664,
"heapUsedBytes": 72857588,
"domNodes": 18,
"jsHeapTotalBytes": 19677184,
"scriptDurationMs": 21.178,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-idle",
"durationMs": 2073.120000000017,
"styleRecalcs": 9,
"styleRecalcDurationMs": 14.609999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 434.464,
"heapDeltaBytes": -20922168,
"heapUsedBytes": 48084592,
"domNodes": -266,
"jsHeapTotalBytes": 18755584,
"scriptDurationMs": 18.266,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1832.868999999988,
"styleRecalcs": 73,
"styleRecalcDurationMs": 40.186,
"layouts": 12,
"layoutDurationMs": 3.875,
"taskDurationMs": 801.6450000000001,
"heapDeltaBytes": -15160772,
"heapUsedBytes": 53767068,
"domNodes": -263,
"jsHeapTotalBytes": 21114880,
"scriptDurationMs": 112.949,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1828.125,
"styleRecalcs": 74,
"styleRecalcDurationMs": 36.888999999999996,
"layouts": 12,
"layoutDurationMs": 3.9390000000000005,
"taskDurationMs": 797.5070000000001,
"heapDeltaBytes": -14465376,
"heapUsedBytes": 54368904,
"domNodes": -264,
"jsHeapTotalBytes": 20590592,
"scriptDurationMs": 109.416,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1664.1180000000304,
"styleRecalcs": 30,
"styleRecalcDurationMs": 16.518,
"layouts": 6,
"layoutDurationMs": 0.5690000000000001,
"taskDurationMs": 308.774,
"heapDeltaBytes": 7127484,
"heapUsedBytes": 75884432,
"domNodes": 79,
"jsHeapTotalBytes": 19677184,
"scriptDurationMs": 18.362000000000002,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1722.912000000008,
"styleRecalcs": 32,
"styleRecalcDurationMs": 16.88,
"layouts": 6,
"layoutDurationMs": 0.727,
"taskDurationMs": 310.86400000000003,
"heapDeltaBytes": 7480792,
"heapUsedBytes": 76458032,
"domNodes": 78,
"jsHeapTotalBytes": 19415040,
"scriptDurationMs": 20.135,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 581.3150000000178,
"styleRecalcs": 13,
"styleRecalcDurationMs": 8.361000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 342.38,
"heapDeltaBytes": -11794468,
"heapUsedBytes": 57136964,
"domNodes": 22,
"jsHeapTotalBytes": 19939328,
"scriptDurationMs": 54.544000000000004,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666682,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 561.5829999999846,
"styleRecalcs": 12,
"styleRecalcDurationMs": 9.455000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 333.982,
"heapDeltaBytes": -11865876,
"heapUsedBytes": 56859148,
"domNodes": 20,
"jsHeapTotalBytes": 19677184,
"scriptDurationMs": 52.616,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999727
},
{
"name": "large-graph-idle",
"durationMs": 2006.8049999999857,
"styleRecalcs": 11,
"styleRecalcDurationMs": 9.536000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 549.342,
"heapDeltaBytes": 4676996,
"heapUsedBytes": 65641732,
"domNodes": -265,
"jsHeapTotalBytes": 5193728,
"scriptDurationMs": 84.14099999999999,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2024.6669999999654,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.356,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 548.621,
"heapDeltaBytes": 5365312,
"heapUsedBytes": 64416812,
"domNodes": -267,
"jsHeapTotalBytes": 4931584,
"scriptDurationMs": 85.253,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2142.1919999999946,
"styleRecalcs": 69,
"styleRecalcDurationMs": 14.232000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1088.4930000000002,
"heapDeltaBytes": 24857920,
"heapUsedBytes": 85123380,
"domNodes": -268,
"jsHeapTotalBytes": 5136384,
"scriptDurationMs": 375.967,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-pan",
"durationMs": 2090.8469999999966,
"styleRecalcs": 67,
"styleRecalcDurationMs": 12.33,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1072.492,
"heapDeltaBytes": -4583128,
"heapUsedBytes": 55811488,
"domNodes": -271,
"jsHeapTotalBytes": 4931584,
"scriptDurationMs": 359.993,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3136.1870000000067,
"styleRecalcs": 66,
"styleRecalcDurationMs": 15.616000000000001,
"layouts": 60,
"layoutDurationMs": 7.681,
"taskDurationMs": 1295.569,
"heapDeltaBytes": -5220780,
"heapUsedBytes": 56623404,
"domNodes": -273,
"jsHeapTotalBytes": 8077312,
"scriptDurationMs": 444.821,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3152.0830000000046,
"styleRecalcs": 65,
"styleRecalcDurationMs": 14.677,
"layouts": 60,
"layoutDurationMs": 7.772999999999999,
"taskDurationMs": 1336.923,
"heapDeltaBytes": -5502528,
"heapUsedBytes": 56363460,
"domNodes": -273,
"jsHeapTotalBytes": 8339456,
"scriptDurationMs": 458.09700000000004,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2032.3480000000131,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.977999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 533.3090000000001,
"heapDeltaBytes": 5681684,
"heapUsedBytes": 67513748,
"domNodes": -267,
"jsHeapTotalBytes": 4931584,
"scriptDurationMs": 82.996,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "minimap-idle",
"durationMs": 2017.1059999999557,
"styleRecalcs": 8,
"styleRecalcDurationMs": 6.710000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 538.932,
"heapDeltaBytes": 5011960,
"heapUsedBytes": 65965360,
"domNodes": -269,
"jsHeapTotalBytes": 5455872,
"scriptDurationMs": 85.463,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 602.3610000000303,
"styleRecalcs": 46,
"styleRecalcDurationMs": 9.621,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 363.453,
"heapDeltaBytes": -11076504,
"heapUsedBytes": 57794052,
"domNodes": 18,
"jsHeapTotalBytes": 20725760,
"scriptDurationMs": 120.26200000000001,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 574.6489999999653,
"styleRecalcs": 49,
"styleRecalcDurationMs": 12.74,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 367.39300000000003,
"heapDeltaBytes": -11280120,
"heapUsedBytes": 57539108,
"domNodes": 24,
"jsHeapTotalBytes": 20987904,
"scriptDurationMs": 115.82000000000001,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2019.8450000000037,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.863000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 400.47200000000004,
"heapDeltaBytes": -21212340,
"heapUsedBytes": 47767988,
"domNodes": -262,
"jsHeapTotalBytes": 19804160,
"scriptDurationMs": 14.191,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2032.5300000000084,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.122999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 408.004,
"heapDeltaBytes": -19668844,
"heapUsedBytes": 49201920,
"domNodes": -261,
"jsHeapTotalBytes": 20066304,
"scriptDurationMs": 14.746,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1749.0319999999997,
"styleRecalcs": 78,
"styleRecalcDurationMs": 39.25,
"layouts": 16,
"layoutDurationMs": 4.5040000000000004,
"taskDurationMs": 733.43,
"heapDeltaBytes": -17779056,
"heapUsedBytes": 51140636,
"domNodes": -263,
"jsHeapTotalBytes": 20328448,
"scriptDurationMs": 93.741,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1715.2130000000056,
"styleRecalcs": 76,
"styleRecalcDurationMs": 35.939,
"layouts": 16,
"layoutDurationMs": 4.144,
"taskDurationMs": 733.0859999999999,
"heapDeltaBytes": -17842040,
"heapUsedBytes": 51150160,
"domNodes": -263,
"jsHeapTotalBytes": 20328448,
"scriptDurationMs": 85.87899999999999,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-transition-enter",
"durationMs": 914.8979999999938,
"styleRecalcs": 19,
"styleRecalcDurationMs": 28.022999999999993,
"layouts": 15,
"layoutDurationMs": 12.431,
"taskDurationMs": 694.2600000000001,
"heapDeltaBytes": 1166696,
"heapUsedBytes": 85154724,
"domNodes": 13673,
"jsHeapTotalBytes": 15990784,
"scriptDurationMs": 26.644000000000002,
"eventListeners": 2369,
"totalBlockingTimeMs": 128,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "viewport-pan-sweep",
"durationMs": 8206.929000000002,
"styleRecalcs": 251,
"styleRecalcDurationMs": 38.800000000000004,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3734.6650000000004,
"heapDeltaBytes": 16346584,
"heapUsedBytes": 77558284,
"domNodes": -264,
"jsHeapTotalBytes": 6971392,
"scriptDurationMs": 1163.6419999999998,
"eventListeners": -113,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.80000000000109
},
{
"name": "viewport-pan-sweep",
"durationMs": 8137.034999999969,
"styleRecalcs": 250,
"styleRecalcDurationMs": 38.065000000000005,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3679.7819999999992,
"heapDeltaBytes": 14211752,
"heapUsedBytes": 73471076,
"domNodes": -265,
"jsHeapTotalBytes": 5136384,
"scriptDurationMs": 1153.988,
"eventListeners": -113,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 11049.216999999999,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 11033.335000000001,
"heapDeltaBytes": -35322184,
"heapUsedBytes": 170731624,
"domNodes": -8311,
"jsHeapTotalBytes": -10395648,
"scriptDurationMs": 563.013,
"eventListeners": -16386,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 11151.402999999958,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 11137.455000000002,
"heapDeltaBytes": -49993164,
"heapUsedBytes": 175027028,
"domNodes": -8311,
"jsHeapTotalBytes": -9900032,
"scriptDurationMs": 521.909,
"eventListeners": -16386,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 13618.743999999992,
"styleRecalcs": 67,
"styleRecalcDurationMs": 12.205999999999994,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 13594.025000000001,
"heapDeltaBytes": -31532976,
"heapUsedBytes": 177075272,
"domNodes": -8311,
"jsHeapTotalBytes": -17063936,
"scriptDurationMs": 855.193,
"eventListeners": -16384,
"totalBlockingTimeMs": 56,
"frameDurationMs": 17.219999999999953,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 13609.797999999955,
"styleRecalcs": 67,
"styleRecalcDurationMs": 11.943000000000008,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 13590.131,
"heapDeltaBytes": -51478176,
"heapUsedBytes": 166954900,
"domNodes": -8311,
"jsHeapTotalBytes": -13307904,
"scriptDurationMs": 800.455,
"eventListeners": -16380,
"totalBlockingTimeMs": 11,
"frameDurationMs": 17.216666666666665,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "workflow-execution",
"durationMs": 456.0989999999947,
"styleRecalcs": 16,
"styleRecalcDurationMs": 21.182,
"layouts": 3,
"layoutDurationMs": 0.6689999999999999,
"taskDurationMs": 121.191,
"heapDeltaBytes": -16110376,
"heapUsedBytes": 51871236,
"domNodes": 134,
"jsHeapTotalBytes": 6569984,
"scriptDurationMs": 9.577000000000002,
"eventListeners": 65,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "workflow-execution",
"durationMs": 461.3650000000007,
"styleRecalcs": 16,
"styleRecalcDurationMs": 20.071,
"layouts": 3,
"layoutDurationMs": 0.5820000000000001,
"taskDurationMs": 118.451,
"heapDeltaBytes": -16275452,
"heapUsedBytes": 51796524,
"domNodes": 121,
"jsHeapTotalBytes": 7094272,
"scriptDurationMs": 9.863,
"eventListeners": 65,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.699999999999818
}
]
} |
There was a problem hiding this comment.
🔍 Cursor Review — Consolidated panel
Triggered by @mattmillerai.
✅ No high-signal findings.
Panel: 8/8 reviewers contributed findings.
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## matt/be-2256-locate-node-button #13795 +/- ##
===================================================================
- Coverage 77.81% 77.81% -0.01%
===================================================================
Files 1687 1687
Lines 95618 95613 -5
Branches 33646 32894 -752
===================================================================
- Hits 74409 74404 -5
Misses 20775 20775
Partials 434 434
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 1 file with indirect coverage changes 🚀 New features to boost your workflow:
|
ELI-5
The error / missing-resource panels have several little "locate this node on
the canvas" buttons. #13401 turned that button into one shared
LocateNodeButtoncomponent for the errors panel. This PR swaps the fiveremaining hand-rolled copies (in the swap-node, missing-model, and
missing-media rows) over to that same shared component so they all look and
behave identically — including a screen-reader label that names the specific
node ("Locate LoadImage" instead of a generic "Locate node on canvas").
Summary
Stacked on #13401 (base branch
matt/be-2256-locate-node-button). That PRextracted
LocateNodeButton.vuefor the errors panel but intentionally leftthe platform-sibling rows untouched. This PR migrates the remaining five inline
Button+icon-[lucide--locate]usages:SwapNodeGroupRow.vue(2 — header + per-node list)MissingModelRow.vue(2 — header + per-reference list)MissingMediaCard.vue(1)Each inline button becomes
<LocateNodeButton :label @locate />, with anode-specific accessible label built from
rightSidePanel.locateNodeFor(
Locate {item}), matching the pattern already used inErrorNodeCard/ErrorGroupList/MissingPackGroupRow:MissingMediaCardalready usedlocateNodeFor— label unchanged.SwapNodeGroupRowandMissingModelRowpreviously used a generic"Locate node on canvas" label; they now name the node type / display name.
Behavior note:
click.stopLocateNodeButtonstops click propagation, whereas the swap-node andmissing-model rows previously used a plain
@click. Stopping propagation isthe desired, consistent behavior and is safe here: the parent components
(
SwapNodesCard,MissingModelCard) only bind the custom@locate-node/@locate-modelevents and no ancestor binds a DOMclickhandler on the row,so nothing relied on the native click bubbling.
Tests
SwapNodeGroupRow.test.ts: updated the locate-button assertions to the newnode-specific accessible names and added a test asserting each control gets a
node-specific label.
MissingModelRow.test.ts: added a test asserting the header locate buttonexposes the node-specific aria-label and emits
locateModel.MissingMediaCard.test.ts: added a locate emit test (the existingspecial-characters aria-label test already covers the node-specific label).
All three files' tests plus the parent-card and
LocateNodeButtontests pass;typecheckandeslintare clean on the changed files.Notes / judgment calls
rightSidePanel.missingModels.locateNodestring is now unused but kept inmain.jsonto avoid churning the other locale files; happy to drop it ifpreferred.
SectionWidgets.vuealso has alucide--locatebutton, but it lives in theparameters panel (a different context, generic label) and is not one of the
five usages this ticket scopes — left untouched.