Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
04de65d
Apply post release steps
FlorentinD Jul 10, 2026
2250097
Use render_widget for from_snowflake
orazve Aug 12, 2026
67fc380
Merge pull request #423 from neo4j/use-render-widget
FlorentinD Aug 12, 2026
74afb04
Add pictures to the notebook
orazve Aug 12, 2026
a3137a0
Some cleanups
orazve Aug 12, 2026
c5b65e2
Merge pull request #424 from neo4j/add-pict-snowflake
FlorentinD Aug 12, 2026
ea792c5
Pin GHA to sha instead of version
FlorentinD Aug 14, 2026
51d062c
Merge pull request #425 from neo4j/gha-pinning
FlorentinD Aug 14, 2026
badd87b
Bump python dependencies
FlorentinD Aug 14, 2026
83089d7
Fix style
FlorentinD Aug 14, 2026
5781b4e
Merge pull request #426 from neo4j/deps/bump-python-dependencies
FlorentinD Aug 14, 2026
a3d8783
Bump the python-patch-and-minor group in /python-wrapper with 2 updates
dependabot[bot] Aug 14, 2026
3c020fa
Merge pull request #427 from neo4j/dependabot/uv/python-wrapper/pytho…
FlorentinD Aug 14, 2026
d1fbcf6
Fix js injection issue in VG.render method
FlorentinD Aug 14, 2026
3bd0e3a
Merge pull request #428 from neo4j/gds-320-fix-injection-issue-if-dat…
FlorentinD Aug 14, 2026
3a8d615
Fix correctness defects from security audit (GDS-321)
FlorentinD Aug 14, 2026
c6f57a7
Merge pull request #429 from neo4j/gds-321-fix-minor-bugs-discovered-…
FlorentinD Aug 14, 2026
8d3da3a
chore(js-applet): bump patch/minor deps (Phase A)
FlorentinD Aug 14, 2026
ec6201f
chore(js-applet): bump typescript to 7.0.2 (Phase B1)
FlorentinD Aug 14, 2026
747614e
chore(js-applet): bump vite toolchain to v8 (Phase B2)
FlorentinD Aug 14, 2026
dc07ada
chore(js-applet): bump jsdom/concurrently/@types/node (Phase B3)
FlorentinD Aug 14, 2026
67997f9
chore(js-applet): bump @neo4j-ndl/react-graph to 2.1.3 (Phase B4)
FlorentinD Aug 14, 2026
09584c7
Adjust scripts to use uv
FlorentinD Aug 14, 2026
a6e9f27
Format code (incl js code via oxlint)
FlorentinD Aug 14, 2026
55a7f61
Apply read permissions guard
FlorentinD Aug 14, 2026
65eabce
Merge pull request #430 from neo4j/js-depsupdates
FlorentinD Aug 14, 2026
584797d
Wait 3 days for new releases
FlorentinD Aug 14, 2026
28fd922
Fix clicking issue observed on streamlit
FlorentinD Aug 17, 2026
b4e3e19
Add js-sytle target
FlorentinD Aug 17, 2026
2b86004
Shorten change log entry
FlorentinD Aug 17, 2026
a5ff494
Merge pull request #433 from neo4j/fix-streamlit-clicking-issue
FlorentinD Aug 17, 2026
df197bc
Support latest gds alpha release
FlorentinD Aug 17, 2026
cd38e61
Allow reacting to double-click events
FlorentinD Jul 17, 2026
a31473f
Handle duplicates on add_data
FlorentinD Jul 17, 2026
0748fb3
Handle duplicates on add_data
FlorentinD Jul 17, 2026
ae6cc88
Support different interaction events
FlorentinD Aug 18, 2026
02f722e
Fix dependabot cooldown
FlorentinD Aug 18, 2026
9af7152
Merge pull request #404 from neo4j/gds-293-react-on-double-clicking-a…
FlorentinD Aug 18, 2026
8e9f3a1
Fix gds tests for latest alpha release
FlorentinD Aug 18, 2026
3cc050a
Fix token permissions for GHA
FlorentinD Aug 18, 2026
15a2226
Merge pull request #434 from neo4j/fix-gds-version-support
FlorentinD Aug 18, 2026
2532e72
Update package version
FlorentinD Aug 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@

version: 2
updates:
- package-ecosystem: "pip"
- package-ecosystem: "uv"
directory: "/python-wrapper"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
cooldown:
default-days: 3
groups:
python-patch-and-minor:
update-types:
Expand All @@ -21,6 +24,9 @@ updates:
directory: "/js-applet"
schedule:
interval: "weekly"
cooldown:
default-day: 3
enabled: true
groups:
js-patch-and-minor:
update-types:
Expand All @@ -29,3 +35,18 @@ updates:
js-major:
update-types:
- "major"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
cooldown:
default-day: 3
enabled: true
groups:
github-actions-patch-and-minor:
update-types:
- "patch"
- "minor"
github-actions-major:
update-types:
- "major"
8 changes: 4 additions & 4 deletions .github/workflows/gds-integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,22 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
gds-version: ["1.22", "2.0.0a1"]
gds-version: ["1.22", "2.0.0a4"]
defaults:
run:
working-directory: python-wrapper

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install uv and set the Python version
uses: astral-sh/setup-uv@v6
uses: astral-sh/setup-uv@ae62891fec2bb8e7d6c99fc78c9fec3a63790f8d # v10.0.0
with:
python-version: "3.11"
enable-cache: true
- name: Install just
uses: extractions/setup-just@v2
uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4

- name: Run tests
env:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Check code style
name: Check JS code style

# Controls when the workflow will run
on:
Expand All @@ -7,33 +7,39 @@ on:
branches: ["main"]
pull_request:
paths:
- "python-wrapper/**" # python code + its resources
- "js-applet/**" # js/ts code + its resources
branches: ["main"]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

permissions:
contents: read

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
code-style:
js-code-style:
# The type of runner that the job will run on
runs-on: ubuntu-latest

defaults:
run:
working-directory: python-wrapper
working-directory: js-applet

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install uv and set the Python version
uses: astral-sh/setup-uv@v6
- name: Set up Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
python-version: "3.11"
enable-cache: true
- run: uv venv
- run: uv sync --group dev --extra pandas --extra neo4j --extra gds --extra snowflake
node-version: "lts/*"

- name: Install dependencies
run: yarn

- name: Lint
run: yarn lint

- name: Check code style
run: source .venv/bin/activate && cd ${GITHUB_WORKSPACE} && ./scripts/checkstyle.sh
- name: Check formatting
run: yarn format:check
4 changes: 2 additions & 2 deletions .github/workflows/nvl-entrypoint-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ jobs:
working-directory: js-applet

steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "lts/*"
- name: Setup
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish_pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ jobs:
working-directory: python-wrapper

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- uses: actions/setup-python@v5
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.x"

Expand Down
69 changes: 69 additions & 0 deletions .github/workflows/py-code-style.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Python - Check code style

# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the "main" branch
push:
branches: ["main"]
pull_request:
paths:
- "python-wrapper/**" # python code + its resources
branches: ["main"]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

permissions:
contents: read

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
code-style:
# The type of runner that the job will run on
runs-on: ubuntu-latest

defaults:
run:
working-directory: python-wrapper

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install uv and set the Python version
uses: astral-sh/setup-uv@ae62891fec2bb8e7d6c99fc78c9fec3a63790f8d # v10.0.0
with:
python-version: "3.11"
enable-cache: true
- run: uv venv
- run: uv sync --group dev --extra pandas --extra neo4j --extra gds --extra snowflake

- name: Check code style
run: source .venv/bin/activate && cd ${GITHUB_WORKSPACE} && ./scripts/checkstyle.sh

# Type-check the v1 (1.22) side of the GDS compat surface.
code-style-gds-v1:
runs-on: ubuntu-latest

defaults:
run:
working-directory: python-wrapper

steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install uv and set the Python version
uses: astral-sh/setup-uv@ae62891fec2bb8e7d6c99fc78c9fec3a63790f8d # v10.0.0
with:
python-version: "3.11"
enable-cache: true
- run: uv venv
- run: uv sync --group dev --extra pandas --extra neo4j --extra gds --extra snowflake
- run: uv pip install --python .venv/bin/python "graphdatascience==1.22"

- name: Check code style (GDS 1.22)
env:
UV_NO_SYNC: "1"
MYPY_TARGETS: "python-wrapper/src"
run: source .venv/bin/activate && cd ${GITHUB_WORKSPACE} && ./scripts/checkstyle.sh

6 changes: 3 additions & 3 deletions .github/workflows/render-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,17 @@ jobs:

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install uv and set the Python version
uses: astral-sh/setup-uv@v6
uses: astral-sh/setup-uv@ae62891fec2bb8e7d6c99fc78c9fec3a63790f8d # v10.0.0
with:
python-version: "3.11"
enable-cache: true
- run: uv sync --group dev --group docs --extra pandas --extra neo4j --extra gds --extra snowflake

- name: Setup pandoc
uses: pandoc/actions/setup@v1
uses: pandoc/actions/setup@86321b6dd4675f5014c611e05088e10d4939e09e # v1
with:
version: 3.6.2

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/snowflake-integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ jobs:

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install uv and set the Python version
uses: astral-sh/setup-uv@v6
uses: astral-sh/setup-uv@ae62891fec2bb8e7d6c99fc78c9fec3a63790f8d # v10.0.0
with:
python-version: "3.11"
enable-cache: true
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ jobs:

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install uv and set the Python version
uses: astral-sh/setup-uv@v6
uses: astral-sh/setup-uv@ae62891fec2bb8e7d6c99fc78c9fec3a63790f8d # v10.0.0
with:
python-version: ${{ matrix.python-version }}
enable-cache: true
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ out/*
.virtual_documents
# ignore top-level yarn files, the js-applet files are relevant
yarn.lock

5 changes: 0 additions & 5 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,8 @@

## New features

* Added a color legend overlay to the visualization. It is captured automatically from `color_nodes`/`color_relationships`, can be set explicitly via `set_legend`, and toggled via `show_legend`.
* Added `neo4j_viz.streamlit.display_widget` to embed an interactive `GraphWidget` in a Streamlit app with two-way state sync (selection and options flow back to Python), following the Streamlit light/dark theme. Install with the `streamlit` extra (`pip install neo4j-viz[streamlit]`).

## Bug fixes

* Fixed a bug where nodes and relationships could not be selected on using `VG.render()`.

## Improvements

## Other changes
3 changes: 2 additions & 1 deletion docs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ build

# Antora docs
antora/package-lock.json
antora/node_modules/
antora/node_modules/
source/tutorials/*.png
49 changes: 49 additions & 0 deletions docs/antora/modules/ROOT/pages/customizing.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,55 @@ widget.on_selection_change(on_selection_change)
`on_selection_change` returns the registered handler, which you can pass to `widget.unobserve(handler, names=["selected"])`
to stop reacting. You can also read `widget.selected` directly at any point for the current selection.

== Reacting to interaction events

Beyond reading the selection, the widget also exposes interaction events whenever the user clicks, double-clicks,
or right-clicks a node, a relationship, or the canvas.
The most recent event is available on the widget's `last_event` attribute, a typed
link:{api-docs-uri}/widget[`InteractionEvent`] with a `type` and an `id`.

To react to a specific event, register a callback with one of `widget.on_node_event(...)`,
`widget.on_relationship_event(...)`, or `widget.on_canvas_event(...)`, depending on what was
interacted with.
Each takes a `MouseEvent` (one of `"click"`, `"double_click"`, `"right_click"` -- the category
node/relationship/canvas is implied by the method) and a callback.
The node and relationship callbacks receive the resolved link:{api-docs-uri}/node[Node] or
link:{api-docs-uri}/relationship[Relationship] directly (matched by id from the widget's current
`nodes`/`relationships`), or `None` if that id is no longer in the graph.
Canvas events target the empty graph background rather than an entity, so the canvas callback takes
no arguments.
An unknown `MouseEvent` string raises `ValueError` when the callback is registered, so typos are
caught early.

One use case is growing the graph on demand -- for example fetching a node's neighborhood from a database when it
is double-clicked:

[source, python]
----
# VG is a VisualizationGraph object
widget = VG.render_widget()


def expand_neighborhood(node):
if node is None:
return
# ... fetch the double-clicked node's neighbors, then widget.add_data(...)


widget.on_node_event("double_click", expand_neighborhood)
----

These methods return the registered handler, which you can pass to `widget.unobserve(handler, names=["last_event"])`
to stop reacting.
You can also read `widget.last_event` directly at any point for the most recent event.

Note that firing the *same* event on the *same* entity twice in a row calls the callback only once, because the
underlying `last_event` value does not change.
For the raw event (`type` and `id`) on every occurrence, observe `last_event` directly instead.

In a Streamlit app, callbacks are not run live; instead read `widget.last_event` after `display_widget` returns on each
rerun, the same way you read `widget.selected` (see xref:rendering.adoc[Using with Streamlit]).

== Direct modification of nodes and relationships

Nodes and relationships can also be modified directly by accessing the `nodes` and `relationships` fields of an
Expand Down
7 changes: 5 additions & 2 deletions docs/antora/modules/ROOT/pages/integration/snowflake.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ The nodes will be colored so that nodes from the same table have the same color,

== Example

In this small example, we import a toy graph representing a social network from two tables in Snowflake.
In this small example, we import a toy graph representing a social network from two tables in Snowflake, and display it
as an interactive widget using link:{api-docs-uri}/visualization-graph/#neo4j_viz.VisualizationGraph.render_widget[`render_widget`].

[source, python]
----
from snowflake.snowpark import Session
from neo4j_viz.snowflake import from_dfs
from neo4j_viz.snowflake import from_snowflake

# Configure according to your own setup
connection_parameters = {
Expand Down Expand Up @@ -82,6 +83,8 @@ VG = from_snowflake(
},
},
)

VG.render_widget()
----

For a full example of the `from_snowflake` importer in action, please see the link:{tutorials-docs-uri}/snowflake-example/[Visualizing Snowflake Tables tutorial].
4 changes: 4 additions & 0 deletions docs/source/api-reference/widget.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@
.. autoclass:: neo4j_viz.GraphSelection
:members:
:exclude-members: model_config

.. autoclass:: neo4j_viz.InteractionEvent
:members:
:exclude-members: model_config
Loading
Loading