Sfoglia il codice sorgente

Add support for loading scrolling and input hiding

Saul Shanabrook 7 anni fa
parent
commit
0a0d216294

+ 7 - 3
packages/cells/src/widget.ts

@@ -174,6 +174,7 @@ class Cell extends Widget {
     super();
     this.addClass(CELL_CLASS);
     let model = this._model = options.model;
+    this._inputHidden = (model.metadata.get('source_hidden') || false) as boolean;
     let contentFactory = this.contentFactory = (
       options.contentFactory || Cell.defaultContentFactory
     );
@@ -380,7 +381,7 @@ class Cell extends Widget {
   private _model: ICellModel = null;
   private _header: ICellHeader = null;
   private _footer: ICellFooter = null;
-  private _inputHidden = false;
+  private _inputHidden: boolean;
   private _input: InputArea = null;
   private _inputCollapser: InputCollapser = null;
   private _inputWrapper: Widget = null;
@@ -545,6 +546,9 @@ class CodeCell extends Cell {
     let contentFactory = this.contentFactory;
     let model = this.model;
 
+    const metadataScrolled = model.metadata.get('scrolled') as boolean | 'auto' | undefined;
+    this._outputsScrolled = metadataScrolled === 'auto' ? false : metadataScrolled || false;
+
     // Insert the output before the cell footer.
     let outputWrapper = this._outputWrapper = new Panel();
     outputWrapper.addClass(CELL_OUTPUT_WRAPPER_CLASS);
@@ -572,7 +576,7 @@ class CodeCell extends Cell {
     });
 
     // Modify state
-    this.outputHidden = (this.model.metadata.get('collapsed') as boolean | undefined) || false;
+    this.outputHidden = (this.model.metadata.get('collapsed') || this.model.metadata.get('outputs_hidden') || false) as boolean;
     this.setPrompt(`${model.executionCount || ''}`);
     model.stateChanged.connect(this.onStateChanged, this);
     model.metadata.changed.connect(this.onMetadataChanged, this);
@@ -736,7 +740,7 @@ class CodeCell extends Cell {
 
   private _rendermime: RenderMimeRegistry = null;
   private _outputHidden: boolean;
-  private _outputsScrolled = false;
+  private _outputsScrolled: boolean;
   private _outputWrapper: Widget = null;
   private _outputCollapser: OutputCollapser = null;
   private _outputPlaceholder: OutputPlaceholder = null;

+ 2 - 2
packages/notebook-extension/src/index.ts

@@ -1502,7 +1502,7 @@ function addCommands(app: JupyterLab, services: ServiceManager, tracker: Noteboo
       const current = getCurrent(args);
 
       if (current) {
-        NotebookActions.persistOutputsCollapsed(current.notebook);
+        NotebookActions.persistViewState(current.notebook);
         app.commands.execute('docmanager:save');
       }
     },
@@ -1645,7 +1645,7 @@ function populateMenus(app: JupyterLab, mainMenu: IMainMenu, tracker: INotebookT
     action: 'with View State',
     name: 'Notebook',
     persistAndSave: (current: NotebookPanel) => {
-      NotebookActions.persistOutputsCollapsed(current.notebook);
+      NotebookActions.persistViewState(current.notebook);
       return app.commands.execute('docmanager:save');
     }
   } as IFileMenu.IPersistAndSave<NotebookPanel>);

+ 29 - 2
packages/notebook/src/actions.tsx

@@ -1089,21 +1089,48 @@ namespace NotebookActions {
    * @param widget - The target notebook widget.
    */
   export
-  function persistOutputsCollapsed(widget: Notebook): void {
+  function persistViewState(widget: Notebook): void {
     if (!widget.model) {
       return;
     }
     let state = Private.getState(widget);
     let cells = widget.widgets;
     each(cells, (cell: Cell) => {
+      const {model, inputHidden} = cell;
+      const metadata = model.metadata;
+      const jupyter = metadata.get('jupyter') as any || {};
+
+      if (inputHidden) {
+        jupyter.source_hidden = true;
+      } else {
+        delete jupyter.source_hidden;
+      }
+
       if (cell.model.type === 'code') {
-        const {outputHidden, model} = (cell as CodeCell);
+        const {outputHidden, outputsScrolled} = (cell as CodeCell);
+
+        // set both metadata keys
+        // https://github.com/jupyterlab/jupyterlab/pull/3981#issuecomment-391139167
         if (outputHidden) {
           model.metadata.set('collapsed', true);
+          jupyter.source_hidden = true;
         } else {
           model.metadata.delete('collapsed');
+          delete jupyter.source_hidden;
+        }
+
+        if (outputsScrolled) {
+          model.metadata.set('scrolled', true);
+        } else {
+          model.metadata.delete('scrolled');
         }
       }
+
+      if (Object.keys(jupyter).length === 0) {
+        metadata.delete('jupyter');
+      } else {
+        metadata.set('jupyter', jupyter);
+      }
     });
     Private.handleState(widget, state);
   }

+ 32 - 1
tests/test-cells/src/widget.spec.ts

@@ -388,8 +388,17 @@ describe('cells/widget', () => {
 
       it('should initialize from the model', () => {
         const collapsedModel = new CodeCellModel({});
-        collapsedModel.metadata.set('collapsed', true);
         let widget = new CodeCell({ model: collapsedModel, rendermime });
+        expect(widget.outputHidden).to.be(false);
+
+        collapsedModel.metadata.set('collapsed', true);
+        collapsedModel.metadata.set('outputs_hidden', false);
+        widget = new CodeCell({ model: collapsedModel, rendermime });
+        expect(widget.outputHidden).to.be(true);
+
+        collapsedModel.metadata.set('collapsed', false);
+        collapsedModel.metadata.set('outputs_hidden', true);
+        widget = new CodeCell({ model: collapsedModel, rendermime });
         expect(widget.outputHidden).to.be(true);
       });
 
@@ -402,6 +411,28 @@ describe('cells/widget', () => {
 
     });
 
+    describe('#outputsScrolled', () => {
+
+      it('should initialize from the model', () => {
+        const collapsedModel = new CodeCellModel({});
+        let widget = new CodeCell({ model: collapsedModel, rendermime });
+        expect(widget.outputsScrolled).to.be(false);
+
+        collapsedModel.metadata.set('scrolled', false);
+        widget = new CodeCell({ model: collapsedModel, rendermime });
+        expect(widget.outputsScrolled).to.be(false);
+
+        collapsedModel.metadata.set('scrolled', 'auto');
+        widget = new CodeCell({ model: collapsedModel, rendermime });
+        expect(widget.outputsScrolled).to.be(false);
+
+        collapsedModel.metadata.set('scrolled', true);
+        widget = new CodeCell({ model: collapsedModel, rendermime });
+        expect(widget.outputsScrolled).to.be(true);
+      });
+
+    });
+
     describe('#dispose()', () => {
 
       it('should dispose of the resources held by the widget', () => {

+ 54 - 12
tests/test-notebook/src/actions.spec.ts

@@ -1335,28 +1335,70 @@ describe('@jupyterlab/notebook', () => {
 
     });
 
-    describe('#persistOutputsCollapsed()', () => {
-      it('should make sure the model reflects the view', () => {
-        let changedACell = false;
+    describe('#persistViewState()', () => {
+      it('input hidden, output hidden and scrolled', () => {
         for (const cell of widget.widgets) {
+          cell.inputHidden = true;
           if (cell instanceof CodeCell) {
-            cell.outputHidden = !cell.outputHidden;
-            changedACell = true;
+            cell.outputHidden = true;
+            cell.outputsScrolled = true;
           }
         }
-        expect(changedACell).to.be(true);
-        NotebookActions.persistOutputsCollapsed(widget);
+        NotebookActions.persistViewState(widget);
         for (const cell of widget.widgets) {
           if (cell instanceof CodeCell) {
-            if (cell.outputHidden) {
-              expect(cell.model.metadata.get('collapsed')).to.be(true);
-            } else {
-              expect(cell.model.metadata.has('collapsed')).to.be(false);
-            }
+            expect(cell.model.metadata.get('collapsed')).to.be(true);
+            expect(cell.model.metadata.get('scrolled')).to.be(true);
+            expect(cell.model.metadata.get('jupyter')).to.eql({
+              source_hidden: true,
+              outputs_hidden: true
+            });
+          } else {
+            expect(cell.model.metadata.get('jupyter')).to.eql({
+              source_hidden: true,
+            });
           }
         }
       });
 
+      it('input hidden, output hidden and not scrolled', () => {
+        for (const cell of widget.widgets) {
+          cell.inputHidden = false;
+          if (cell instanceof CodeCell) {
+            cell.outputHidden = false;
+            cell.outputsScrolled = false;
+          }
+        }
+        NotebookActions.persistViewState(widget);
+        for (const cell of widget.widgets) {
+          if (cell instanceof CodeCell) {
+            expect(cell.model.metadata.has('collapsed')).to.be(false);
+            expect(cell.model.metadata.has('scrolled')).to.be(false);
+          }
+          expect(cell.model.metadata.has('jupyter')).to.be(false);
+        }
+      });
+
+      it('input hidden, output shown and not scrolled', () => {
+
+        for (const cell of widget.widgets) {
+          cell.inputHidden = true;
+          if (cell instanceof CodeCell) {
+            cell.outputHidden = false;
+            cell.outputsScrolled = false;
+          }
+        }
+        NotebookActions.persistViewState(widget);
+        for (const cell of widget.widgets) {
+          if (cell instanceof CodeCell) {
+            expect(cell.model.metadata.has('collapsed')).to.be(false);
+            expect(cell.model.metadata.has('scrolled')).to.be(false);
+          }
+          expect(cell.model.metadata.has('jupyter')).to.eql({
+            source_hidden: true
+          });
+        }
+      });
     });
 
     describe('#setMarkdownHeader()', () => {