notebook icon indicating copy to clipboard operation
notebook copied to clipboard

Can't save the notebook programmatically from JavaScript by simulating a click on the save button

Open chrispyles opened this issue 1 year ago • 4 comments

Description

When simulating a click on the save button element or its parent using JavaScript, the notebook is not saved.

This is needed in an R client library for otter-grader, where we need to programmatically save a notebook before exporting it into a zip file for submission. We have similar functionality in our Python client which was migrated to ipylab but there is no analog that I can find in R, so we need to use JavaScript to perform the save.

The issue also occurs in JupyterLab and from my understanding notebook v7 is basically a reskin of JupyterLab so I wasn't sure which repo to open this under, but the user who opened our bug about this (https://github.com/ucbds-infra/otter-grader/issues/767) is using notebook, so I figured I'd open it here. Please let me know if it should be moved.

Reproduce

  1. Create a new notebook with a R kernel
  2. Run
IRdisplay::display_javascript("
    // the save button has the data-command=\"docmanager:save\" attribute
    document.querySelector('[data-command=\"docmanager:save\"]').click()
")
  1. Note that the notebook is not saved

This can also be reproduced in the browser's console by just running the JS:

document.querySelector('[data-command="docmanager:save"]').click()

Expected behavior

When a click is simulated on the button element with JavaScript, the notebook should be saved.

Context

  • Operating System and version: MacOS 14.2.1
  • Browser and version: Firefox 121.0.1
  • Jupyter Notebook version: 7.0.7

Output of jupyter --version:

IPython          : 8.14.0
ipykernel        : 6.25.1
ipywidgets       : 8.1.0
jupyter_client   : 7.4.9
jupyter_core     : 5.3.1
jupyter_server   : 2.12.5
jupyterlab       : 4.0.11
nbclient         : 0.8.0
nbconvert        : 7.8.0
nbformat         : 5.9.2
notebook         : 7.0.7
qtconsole        : not installed
traitlets        : 5.9.0
Troubleshoot Output

See attachment: troubleshoot.txt

Command Line Output

See attachment: jupyter logs.txt

Browser Output
>> document.querySelector('[data-command="docmanager:save"]').click()
undefined 

chrispyles avatar Jan 26 '24 02:01 chrispyles

In triage, I'm accepting this issue. Your use case might be better suited to a Jupyter Notebook extension that actually runs the "save" command, rather than relying on JavaScript and queries for a specific button that might change in the future.

JasonWeill avatar Jan 30 '24 17:01 JasonWeill

@chrispyles - the action on the button is actually triggered on mousedown event not on click. As you are emulating the event, you are not generating the mousedown event as it happens when a user is really clicking.

fcollonval avatar Jan 31 '24 15:01 fcollonval

@chrispyles - the action on the button is actually triggered on mousedown event not on click. As you are emulating the event, you are not generating the mousedown event as it happens when a user is really clicking.

@fcollonval I tried emulating mousedown with document.querySelector('[data-command="docmanager:save"]').dispatchEvent(new MouseEvent('mousedown')) and that also does not work. Am I missing some required params in the event or is there a sequence of events (e.g. mousedown then mouseup) that needs to happen?

chrispyles avatar Feb 01 '24 01:02 chrispyles

When run with --expose-app-in-browser, this should work:

window.jupyterapp.commands.execute('docmanager:save');

From within an extension:

const plugin: JupyterFrontEndPlugin<void> = {
  id: 'some-integration:plugin',
  description: 'A JupyterLab extension',
  autoStart: true,
  requires: [],
  activate: async (app: JupyterFrontEnd) => {
    app.commands.execute('docmanager:save');
  }
};

paulrutter avatar Mar 13 '24 12:03 paulrutter