From e0ceddce0976ab18061b032ae6e8d702abc31e18 Mon Sep 17 00:00:00 2001
From: Chris LaFreniere <40371649+chlafreniere@users.noreply.github.com>
Date: Mon, 14 Jan 2019 17:29:06 -0800
Subject: [PATCH] Notebooks: Add Placeholder Cell, Fix Link Styling (#3728)
* Placeholder cell to add new real cells
* Fix links in notebooks to show correct color, rely on angular ngif for placeholder
* Fix failing test where one cell was expected by default
* Remove unnecessary TODO
---
.../parts/notebook/cellViews/placeholder.css | 20 +++++
.../cellViews/placeholderCell.component.html | 13 +++
.../cellViews/placeholderCell.component.ts | 82 +++++++++++++++++++
.../parts/notebook/models/notebookModel.ts | 7 +-
.../parts/notebook/notebook.component.html | 4 +
src/sql/parts/notebook/notebook.component.ts | 5 --
src/sql/parts/notebook/notebook.css | 1 +
src/sql/parts/notebook/notebook.module.ts | 4 +-
src/sql/parts/notebook/notebookStyles.ts | 14 +++-
.../parts/notebook/outputs/style/index.css | 3 -
.../notebook/model/notebookModel.test.ts | 7 +-
11 files changed, 140 insertions(+), 20 deletions(-)
create mode 100644 src/sql/parts/notebook/cellViews/placeholder.css
create mode 100644 src/sql/parts/notebook/cellViews/placeholderCell.component.html
create mode 100644 src/sql/parts/notebook/cellViews/placeholderCell.component.ts
diff --git a/src/sql/parts/notebook/cellViews/placeholder.css b/src/sql/parts/notebook/cellViews/placeholder.css
new file mode 100644
index 0000000000..976b01e323
--- /dev/null
+++ b/src/sql/parts/notebook/cellViews/placeholder.css
@@ -0,0 +1,20 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+ placeholder-cell-component {
+ height: 50px;
+ width: 100%;
+ display: block;
+ box-shadow: 0px 4px 6px 0px rgba(0,0,0,0.14);
+}
+
+placeholder-cell-component .text {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 50px;
+ -webkit-margin-before: 0em;
+ -webkit-margin-after: 0em;
+}
\ No newline at end of file
diff --git a/src/sql/parts/notebook/cellViews/placeholderCell.component.html b/src/sql/parts/notebook/cellViews/placeholderCell.component.html
new file mode 100644
index 0000000000..5679ce16bc
--- /dev/null
+++ b/src/sql/parts/notebook/cellViews/placeholderCell.component.html
@@ -0,0 +1,13 @@
+
+
\ No newline at end of file
diff --git a/src/sql/parts/notebook/cellViews/placeholderCell.component.ts b/src/sql/parts/notebook/cellViews/placeholderCell.component.ts
new file mode 100644
index 0000000000..1fe4ab13e3
--- /dev/null
+++ b/src/sql/parts/notebook/cellViews/placeholderCell.component.ts
@@ -0,0 +1,82 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the Source EULA. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+import 'vs/css!./placeholder';
+
+import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild, SimpleChange, OnChanges } from '@angular/core';
+import { CellView } from 'sql/parts/notebook/cellViews/interfaces';
+import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
+import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
+import { localize } from 'vs/nls';
+import { CellType } from 'sql/parts/notebook/models/contracts';
+
+
+export const PLACEHOLDER_SELECTOR: string = 'placeholder-cell-component';
+
+@Component({
+ selector: PLACEHOLDER_SELECTOR,
+ templateUrl: decodeURI(require.toUrl('./placeholderCell.component.html'))
+})
+
+export class PlaceholderCellComponent extends CellView implements OnInit, OnChanges {
+ @Input() cellModel: ICellModel;
+ @Input() set model(value: NotebookModel) {
+ this._model = value;
+ }
+
+ private _model: NotebookModel;
+
+ constructor(
+ @Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
+ ) {
+ super();
+ }
+
+ ngOnInit() {
+ if (this.cellModel) {
+ this._register(this.cellModel.onOutputsChanged(() => {
+ this._changeRef.detectChanges();
+ }));
+ }
+ }
+
+ ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
+ }
+
+ get model(): NotebookModel {
+ return this._model;
+ }
+
+ get clickOn(): string {
+ return localize('clickOn','Click on');
+ }
+
+ get plusCode(): string {
+ return localize('plusCode', '+ Code');
+ }
+
+ get or(): string {
+ return localize('or', 'or');
+ }
+
+ get plusText(): string {
+ return localize('plusText', '+ Text');
+ }
+
+ get toAddCell(): string {
+ return localize('toAddCell', 'to add a code or text cell');
+ }
+
+ public addCell(cellType: string): void {
+ let type: CellType = cellType;
+ if (!type) {
+ type = 'code';
+ }
+ this._model.addCell(cellType);
+ }
+
+ public layout() {
+
+ }
+}
diff --git a/src/sql/parts/notebook/models/notebookModel.ts b/src/sql/parts/notebook/models/notebookModel.ts
index e159cdccce..e264af8a24 100644
--- a/src/sql/parts/notebook/models/notebookModel.ts
+++ b/src/sql/parts/notebook/models/notebookModel.ts
@@ -217,7 +217,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
}
let factory = this.notebookOptions.factory;
// if cells already exist, create them with language info (if it is saved)
- this._cells = undefined;
+ this._cells = [];
this._defaultLanguageInfo = {
name: this._providerId === SQL_NOTEBOOK_PROVIDER ? 'sql' : 'python',
version: ''
@@ -230,9 +230,6 @@ export class NotebookModel extends Disposable implements INotebookModel {
this._cells = contents.cells.map(c => factory.createCell(c, { notebook: this, isTrusted: isTrusted }));
}
}
- if (!this._cells) {
- this._cells = [this.createCell(CellTypes.Code)];
- }
} catch (error) {
this._inErrorState = true;
throw error;
@@ -244,7 +241,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
}
public addCell(cellType: CellType, index?: number): ICellModel {
- if (this.inErrorState || !this._cells) {
+ if (this.inErrorState) {
return null;
}
let cell = this.createCell(cellType);
diff --git a/src/sql/parts/notebook/notebook.component.html b/src/sql/parts/notebook/notebook.component.html
index 6028e19591..230e0b40fa 100644
--- a/src/sql/parts/notebook/notebook.component.html
+++ b/src/sql/parts/notebook/notebook.component.html
@@ -15,5 +15,9 @@
+
diff --git a/src/sql/parts/notebook/notebook.component.ts b/src/sql/parts/notebook/notebook.component.ts
index edfb37625a..0bcdc965b5 100644
--- a/src/sql/parts/notebook/notebook.component.ts
+++ b/src/sql/parts/notebook/notebook.component.ts
@@ -250,11 +250,6 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
this.updateToolbarComponents(this._model.trustedMode);
this._modelRegisteredDeferred.resolve(this._model);
model.backgroundStartSession();
- // Set first cell as default active cell if user creates new notebook
- // Otherwise, don't select any cells by default
- if (this._model && this._model.cells && this._model.cells[0] && this._model.isNewNotebook) {
- this.selectCell(model.cells[0]);
- }
this._changeRef.detectChanges();
}
diff --git a/src/sql/parts/notebook/notebook.css b/src/sql/parts/notebook/notebook.css
index c8afd50d70..2cc8d13314 100644
--- a/src/sql/parts/notebook/notebook.css
+++ b/src/sql/parts/notebook/notebook.css
@@ -11,6 +11,7 @@
margin: 10px 20px 10px;
border-width: 1px;
border-style: solid;
+ border-radius: 3px;
}
.notebookEditor .notebook-info-label {
diff --git a/src/sql/parts/notebook/notebook.module.ts b/src/sql/parts/notebook/notebook.module.ts
index d60a84edbe..ac87813e23 100644
--- a/src/sql/parts/notebook/notebook.module.ts
+++ b/src/sql/parts/notebook/notebook.module.ts
@@ -11,7 +11,6 @@ import { CommonModule, APP_BASE_HREF } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
-import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/bootstrap/bootstrapService';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
@@ -22,12 +21,12 @@ import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
import { NotebookComponent } from 'sql/parts/notebook/notebook.component';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
-import { Registry } from 'vs/platform/registry/common/platform';
import { CodeComponent } from 'sql/parts/notebook/cellViews/code.component';
import { CodeCellComponent } from 'sql/parts/notebook/cellViews/codeCell.component';
import { TextCellComponent } from 'sql/parts/notebook/cellViews/textCell.component';
import { OutputAreaComponent } from 'sql/parts/notebook/cellViews/outputArea.component';
import { OutputComponent } from 'sql/parts/notebook/cellViews/output.component';
+import { PlaceholderCellComponent } from 'sql/parts/notebook/cellViews/placeholderCell.component';
import LoadingSpinner from 'sql/parts/modelComponents/loadingSpinner.component';
export const NotebookModule = (params, selector: string, instantiationService: IInstantiationService): any => {
@@ -41,6 +40,7 @@ export const NotebookModule = (params, selector: string, instantiationService: I
CodeComponent,
CodeCellComponent,
TextCellComponent,
+ PlaceholderCellComponent,
NotebookComponent,
ComponentHostDirective,
OutputAreaComponent,
diff --git a/src/sql/parts/notebook/notebookStyles.ts b/src/sql/parts/notebook/notebookStyles.ts
index 3f7bf648d9..16d4512d06 100644
--- a/src/sql/parts/notebook/notebookStyles.ts
+++ b/src/sql/parts/notebook/notebookStyles.ts
@@ -6,7 +6,7 @@ import 'vs/css!./notebook';
import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
-import { activeContrastBorder, buttonBackground } from 'vs/platform/theme/common/colorRegistry';
+import { activeContrastBorder, buttonBackground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
@@ -48,4 +48,16 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
}
`);
}
+
+ // Styling for all links in notebooks
+ const linkForeground = theme.getColor(textLinkForeground);
+ if (linkForeground) {
+ collector.addRule(`
+ .notebookEditor a:link {
+ text-decoration: none;
+ font-weight: bold;
+ color: ${linkForeground};
+ }
+ `);
+ }
});
diff --git a/src/sql/parts/notebook/outputs/style/index.css b/src/sql/parts/notebook/outputs/style/index.css
index dc234e6af3..c71a80a415 100644
--- a/src/sql/parts/notebook/outputs/style/index.css
+++ b/src/sql/parts/notebook/outputs/style/index.css
@@ -172,17 +172,14 @@ output-component .jp-RenderedHTMLCommon u {
output-component .jp-RenderedHTMLCommon a:link {
text-decoration: none;
- color: var(--jp-content-link-color);
}
output-component .jp-RenderedHTMLCommon a:hover {
text-decoration: underline;
- color: var(--jp-content-link-color);
}
output-component .jp-RenderedHTMLCommon a:visited {
text-decoration: none;
- color: var(--jp-content-link-color);
}
/* Headings */
diff --git a/src/sqltest/parts/notebook/model/notebookModel.test.ts b/src/sqltest/parts/notebook/model/notebookModel.test.ts
index f91d50c343..7f638870e2 100644
--- a/src/sqltest/parts/notebook/model/notebookModel.test.ts
+++ b/src/sqltest/parts/notebook/model/notebookModel.test.ts
@@ -103,7 +103,7 @@ describe('notebook model', function(): void {
});
});
- it('Should create single cell if model has no contents', async function(): Promise {
+ it('Should create no cells if model has no contents', async function(): Promise {
// Given an empty notebook
let emptyNotebook: nb.INotebookContents = {
cells: [],
@@ -125,9 +125,8 @@ describe('notebook model', function(): void {
let model = new NotebookModel(defaultModelOptions);
await model.requestModelLoad();
- // Then I expect to have 1 code cell as the contents
- should(model.cells).have.length(1);
- should(model.cells[0].source).be.empty();
+ // Then I expect to have 0 code cell as the contents
+ should(model.cells).have.length(0);
});
it('Should throw if model load fails', async function(): Promise {