Skip to content

refactor(networks): Refactor visualization suite to enable download of PNG files#173

Merged
tonywu1999 merged 10 commits intodevelfrom
refactor-png
Mar 3, 2026
Merged

refactor(networks): Refactor visualization suite to enable download of PNG files#173
tonywu1999 merged 10 commits intodevelfrom
refactor-png

Conversation

@tonywu1999
Copy link
Contributor

@tonywu1999 tonywu1999 commented Feb 26, 2026

Motivation and Context

This PR refactors the network visualization module to decouple Cytoscape rendering from Shiny-specific logic and to enable downloading/exporting networks as standalone HTML/PNG artifacts. Previously the module generated inline JavaScript with embedded Shiny bindings and used custom message passing and event wiring. The refactor delegates rendering to MSstatsBioNet (using cytoscapeNetwork and renderCytoscapeNetwork), simplifies UI containers, and exposes a reactive network output so the visualization can be saved via htmlwidgets::saveWidget (enabling PNG/HTML export workflows).

Detailed Changes

  • R/module-visualize-network-server.R

    • Removed generateCytoscapeJSForShiny(...) and openEvidenceLink(session, evidence_link).
    • Stopped generating and sending inline Cytoscape JS; introduced a reactive output$network that returns an MSstatsBioNet cytoscapeNetwork widget via MSstatsBioNet::renderCytoscapeNetwork.
    • Renamed client-side event inputs: edgeClicked → network_edge_clicked, nodeClicked → network_node_clicked; updated server observers to use these inputs and to highlight corresponding rows in node/edge DTs.
    • Reworked code-export/preview flow to construct a cytoscapeNetwork object and save with htmlwidgets::saveWidget instead of embedding custom JS/HTML.
    • Removed custom session customMessage dispatch for Cytoscape; rendering now happens through the public output binding.
    • Added/adjusted helpers to produce node/edge tables and to sync selections (highlightNodeInTable/highlightEdgeInTable or similar reactive wiring).
    • Overall line changes: +15 / -97 (refactor-heavy on server-side).
  • R/module-visualize-network-ui.R

    • Deleted createCytoscapeScripts() (external Cytoscape loader) and createNetworkLegends().
    • createNetworkVisualizationBox() simplified to use MSstatsBioNet::cytoscapeNetworkOutput(ns("network"), height = "600px") instead of a custom div.
    • createEdgesTableBox() and createNodesTableBox() simplified to return DTOutput() directly (removed overflow-x wrapper divs).
    • Removed the UI initialization call to createCytoscapeScripts().
    • Overall line changes: +4 / -147 (UI simplification).
  • NAMESPACE

    • Removed importFrom MSstatsBioNet,generateCytoscapeConfig (one importFrom line deleted). No other public exports were added.
  • Documentation

    • Deleted man/generateCytoscapeJSForShiny.Rd (documentation for removed helper removed).
  • Tests

    • tests/testthat/test_network_visualization.R updated to remove Shiny-specific/integration tests that referenced generateCytoscapeJSForShiny and related custom-JS behaviors. Remaining tests focus on data-processing helpers, UI structure, and mocked MSstatsBioNet interactions. Line changes indicate removal of ~53 lines of tests.
  • Public API / Contract changes

    • Removed exported helpers: generateCytoscapeJSForShiny and openEvidenceLink (and their docs).
    • Input IDs for network events changed: edgeClicked → network_edge_clicked, nodeClicked → network_node_clicked.
    • Rendering pathway changed from custom JS messages to output$network via MSstatsBioNet.
    • Code-export path now uses cytoscapeNetwork + htmlwidgets::saveWidget.

Unit tests added or modified

  • Removed tests that asserted presence/behavior of generateCytoscapeJSForShiny and Shiny-specific inline-JS event wiring.
  • Retained and/or adjusted tests covering:
    • Data filtering and processing helpers used by the network module.
    • UI component structure (presence of MSstatsBioNet output and DT outputs).
    • Helper functions for annotation/subnetwork extraction with MSstatsBioNet functions mocked.
  • Missing/changed tests noted:
    • No explicit new tests added to verify integration with MSstatsBioNet::renderCytoscapeNetwork or to assert the new event input IDs trigger table highlighting; existing tests appear to rely on mocking rather than full integration.

Coding Guidelines Violations

  • Stale tests: Tests referencing removed functions (generateCytoscapeJSForShiny) were deleted but the test-suite should be audited to ensure full coverage for the new contract (missing tests for new event IDs).
  • Insufficient integration tests: There are no end-to-end tests validating the rendered cytoscapeNetwork widget output or the htmlwidgets::saveWidget export path; integration tests for MSstatsBioNet interactions are limited/mocked.
  • Public API change without explicit deprecation: Two previously available helpers and their docs were removed; if they were part of the public API, deprecation notices / changelog entries are absent in this PR.
  • Event naming change risk: Renaming input IDs (edgeClicked/nodeClicked → network_edge_clicked/network_node_clicked) is a breaking change for any external code expecting the old IDs; no migration guidance included in code or docs.

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4274365 and cd5dcbd.

📒 Files selected for processing (3)
  • NAMESPACE
  • man/generateCytoscapeJSForShiny.Rd
  • tests/testthat/test_network_visualization.R
💤 Files with no reviewable changes (3)
  • NAMESPACE
  • man/generateCytoscapeJSForShiny.Rd
  • tests/testthat/test_network_visualization.R

📝 Walkthrough

Walkthrough

Refactors the network visualization module to remove Shiny-specific inline Cytoscape JS and custom message wiring. The server now exposes a reactive output$network rendered via MSstatsBioNet::renderCytoscapeNetwork from node/edge tables, event input IDs were renamed, and code-export switched to htmlwidgets::saveWidget.

Changes

Cohort / File(s) Summary
Network Visualization Server Refactor
R/module-visualize-network-server.R
Removed generateCytoscapeJSForShiny and openEvidenceLink; replaced inline JS/customMessage flow with output$network using MSstatsBioNet::renderCytoscapeNetwork and cytoscapeNetwork; renamed client inputs edgeClicked/nodeClickednetwork_edge_clicked/network_node_clicked; export/preview now use htmlwidgets::saveWidget on a cytoscapeNetwork.
Network Visualization UI Simplification
R/module-visualize-network-ui.R
Deleted createCytoscapeScripts() and createNetworkLegends(); createNetworkVisualizationBox() now returns MSstatsBioNet::cytoscapeNetworkOutput; tables return DTOutput directly (removed extra div wrappers).
Tests and Docs Cleanup
man/generateCytoscapeJSForShiny.Rd, tests/testthat/test_network_visualization.R
Removed RD doc for generateCytoscapeJSForShiny; removed/updated Shiny-specific tests that depended on inline JS and custom messaging; tests now rely on MSstatsBioNet-mocked behavior and data-processing checks.
Namespace / Manifest
NAMESPACE, DESCRIPTION
Removed importFrom entry for MSstatsBioNet::generateCytoscapeConfig; updated DESCRIPTION to align with architecture changes.

Sequence Diagram(s)

sequenceDiagram
    participant Browser as Client (Browser)
    participant Shiny as Server (visualizeNetworkServer)
    participant MSBN as MSstatsBioNet
    participant FS as File / HTML widget

    Browser->>Shiny: load UI (cytoscapeNetworkOutput)
    Shiny->>MSBN: renderCytoscapeNetwork(nodes, edges, displayLabelType)
    MSBN-->>Browser: HTML widget (interactive network)
    Browser->>Shiny: user clicks node/edge -> inputs `network_node_clicked` / `network_edge_clicked`
    Shiny->>Shiny: observe inputs -> highlight table rows / update state
    Shiny->>FS: export action -> htmlwidgets::saveWidget(cytoscapeNetwork, file.html)
    FS-->>Browser: download / open preview
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

Review effort 2/5

Poem

🐰 Hopped through code, I cleared the tangled vines,
Swapped inline scripts for tidy network signs.
Events renamed, widgets now take the stage,
A cleaner hop toward a brighter page. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title claims to enable PNG file downloads, but the changeset refactors the visualization from Shiny-specific JS to MSstatsBioNet rendering without introducing PNG export functionality. Update the title to accurately reflect the actual changes, such as: 'refactor(networks): Migrate network visualization to MSstatsBioNet rendering' or clarify if PNG download functionality is actually being implemented.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor-png

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Event Wiring

The new observers rely on input$network_edge_clicked and input$network_node_clicked. Verify that MSstatsBioNet::cytoscapeNetworkOutput()/renderCytoscapeNetwork() actually emits these exact input names (and payload shape) under module namespacing; otherwise edge/node clicks may silently stop highlighting.

observeEvent(input$network_edge_clicked, {
  edge_data <- input$network_edge_clicked
  network_data <- renderNetwork()
  req(network_data)
  edges_table <- network_data$edges_table
  highlightEdgeInTable(output, edge_data, edges_table)
})

# Observe node click events
observeEvent(input$network_node_clicked, {
  node_data <- input$network_node_clicked
  network_data <- renderNetwork()
  req(network_data)
  nodes_table <- network_data$nodes_table
  highlightNodeInTable(output, node_data, nodes_table)
})
Codegen Bug

The generated code uses multiple paste() calls with inconsistent sep usage; one line omits sep = "", which will insert an extra space and can subtly corrupt the emitted R script. Also the duplicated cytoscapeNetwork(...) call (one unassigned, one assigned to widget) looks redundant—confirm intended behavior.

codes <- paste(codes, "cytoscapeNetwork(subnetwork$nodes, subnetwork$edges, displayLabelType=", displayLabelTypeStr, ")\n", sep = "")
codes <- paste(codes, "widget = cytoscapeNetwork(subnetwork$nodes, subnetwork$edges, displayLabelType=", displayLabelTypeStr, ")\n", sep = "")
codes <- paste(codes, "htmlwidgets::saveWidget(widget,\n   file = \"network.html\",\n    selfcontained = TRUE\n)")
UI Regression

Removing the overflow wrapper around DTOutput() may reintroduce horizontal clipping for wide tables depending on CSS/layout. Confirm the tables remain usable with long column names/values and on smaller screens.

createEdgesTableBox <- function(ns) {
  box(
    title = "Edges Table",
    status = "warning",
    solidHeader = TRUE,
    width = 12,
    DTOutput(ns("edgesTable"))
  )
}

createNodesTableBox <- function(ns) {
  box(
    title = "Nodes Table",
    status = "info",
    solidHeader = TRUE,
    width = 12,
    DTOutput(ns("nodesTable"))
  )

@github-actions
Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Preserve reactive updates in render

Don’t close over the one-time local render_data list inside the render function, or
the widget won’t reflect later reactive changes unless the button is pressed again.
Instead, read from networkVisualization() inside the render expression and gate
rendering with req(input$showNetwork).

R/module-visualize-network-server.R [568-575]

 output$network <- MSstatsBioNet::renderCytoscapeNetwork({
+  req(input$showNetwork)
+  rd <- networkVisualization()
+  req(rd)
+
   MSstatsBioNet::cytoscapeNetwork(
-    nodes        = render_data$nodes_table,
-    edges        = render_data$edges_table,
-    nodeFontSize = 12,
+    nodes            = rd$nodes_table,
+    edges            = rd$edges_table,
+    nodeFontSize     = 12,
     displayLabelType = input$displayLabelType
   )
 })
Suggestion importance[1-10]: 5

__

Why: The current renderCytoscapeNetwork closes over a one-time render_data, so later changes (e.g., to inputs affecting renderNetwork()) won’t update the widget unless the button handler re-runs. Reading from networkVisualization() inside the render expression is a reasonable way to restore reactivity, though it may alter the intended “update only on button click” UX.

Low
Normalize click event payload

The widget click payload structure may vary (e.g., nested under data), and passing
the wrong shape can break table highlighting. Normalize the payload before calling
highlightEdgeInTable() so it works for both flat and nested event objects.

R/module-visualize-network-server.R [612-618]

 observeEvent(input$network_edge_clicked, {
-  edge_data <- input$network_edge_clicked
+  edge_evt <- input$network_edge_clicked
+  edge_data <- if (is.list(edge_evt) && !is.null(edge_evt$data)) edge_evt$data else edge_evt
+
   network_data <- renderNetwork()
   req(network_data)
   edges_table <- network_data$edges_table
+
   highlightEdgeInTable(output, edge_data, edges_table)
 })
Suggestion importance[1-10]: 4

__

Why: Defensively handling both flat and nested ($data) click payloads can prevent highlightEdgeInTable() from breaking if MSstatsBioNet emits a different event structure. This is a minor robustness improvement and doesn’t conflict with the surrounding server logic.

Low

@tonywu1999 tonywu1999 marked this pull request as ready for review February 26, 2026 22:15
@tonywu1999 tonywu1999 changed the title Refactor png refactor(networks): Refactor visualization suite to enable download of PNG files Feb 26, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@R/module-visualize-network-server.R`:
- Around line 568-575: The rendered network uses a snapshot (render_data) for
display but the click handlers read the live renderNetwork() inputs, causing
mismatches; persist the displayed network snapshot (e.g., assign the snapshot
produced for output$network to a reactiveVal or reactiveValues like
displayedNetworkSnapshot) when creating the cytoscapeNetwork in output$network,
and update the click/selection observers to read from that persisted
displayedNetworkSnapshot instead of calling renderNetwork() (also update the
other observers referenced around the block handling clicks at the later section
currently reading renderNetwork()); ensure the snapshot contains both
nodes_table and edges_table and is updated only when the Display Network action
is performed so clicks map to the shown graph.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 94155dc and 4274365.

📒 Files selected for processing (2)
  • R/module-visualize-network-server.R
  • R/module-visualize-network-ui.R

Comment on lines +568 to +575
output$network <- MSstatsBioNet::renderCytoscapeNetwork({
MSstatsBioNet::cytoscapeNetwork(
nodes = render_data$nodes_table,
edges = render_data$edges_table,
nodeFontSize = 12,
displayLabelType = input$displayLabelType
)
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Persist rendered network data to keep click highlighting in sync.

output$network is rendered from a snapshot created on Display Network, but the click handlers read renderNetwork() (live inputs). If inputs change after rendering, edge/node clicks can map against a different dataset than what is shown.

💡 Suggested fix (cache displayed network snapshot and reuse it in click observers)
   # Reactive value to store search results
   proteinSearchResults <- reactiveVal(NULL)
+  
+  # Snapshot of the network currently displayed in the UI
+  displayedNetworkData <- reactiveVal(NULL)

@@
   render_data <- networkVisualization()
   if (is.null(render_data)) {
     # Hide loading indicator and re-enable button if there's an error
     shinyjs::hide("loadingIndicator")
     shinyjs::enable("showNetwork")
     return()
   }
+  displayedNetworkData(render_data)

@@
   observeEvent(input$network_edge_clicked, {
     edge_data <- input$network_edge_clicked
-    network_data <- renderNetwork()
+    network_data <- displayedNetworkData()
     req(network_data)
     edges_table <- network_data$edges_table
     highlightEdgeInTable(output, edge_data, edges_table)
   })

@@
   observeEvent(input$network_node_clicked, {
     node_data <- input$network_node_clicked
-    network_data <- renderNetwork()
+    network_data <- displayedNetworkData()
     req(network_data)
     nodes_table <- network_data$nodes_table
     highlightNodeInTable(output, node_data, nodes_table)
   })

Also applies to: 612-627

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@R/module-visualize-network-server.R` around lines 568 - 575, The rendered
network uses a snapshot (render_data) for display but the click handlers read
the live renderNetwork() inputs, causing mismatches; persist the displayed
network snapshot (e.g., assign the snapshot produced for output$network to a
reactiveVal or reactiveValues like displayedNetworkSnapshot) when creating the
cytoscapeNetwork in output$network, and update the click/selection observers to
read from that persisted displayedNetworkSnapshot instead of calling
renderNetwork() (also update the other observers referenced around the block
handling clicks at the later section currently reading renderNetwork()); ensure
the snapshot contains both nodes_table and edges_table and is updated only when
the Display Network action is performed so clicks map to the shown graph.

@tonywu1999 tonywu1999 merged commit ddcfcd9 into devel Mar 3, 2026
2 checks passed
@tonywu1999 tonywu1999 deleted the refactor-png branch March 3, 2026 22:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant