Compare commits

..

4 Commits

Author SHA1 Message Date
Tanuj Mittal
84ea3796ec Remove enableGallery feature flag (#68)
* Remove enableGallery feature flag

* Fix bugs

* Add tests to increase coverage

* Move favorites functionality behind feature.enableGalleryPublish flag

* Show code cells in NotebookViewer

* Use cosmos db logo as persona image for sample notebook gallery cards

* Update gallery card snapshot to fix test
2020-07-06 12:10:26 -07:00
Laurent Nguyen
27024ef75c Initial implementation of a generic UI component (#61)
* Add generic component

* Add validation. Rename to widgetRenderer

* Remove test code from splash screen

* Clean up infobox

* Fix styling/layout

* Move test code into unit test

* Replace <input> and <labe> by <TextField> and <Text> respectively. Fix style.

* Replace InfoBoxComponent with UI fabric MessageBar. Fix styling for TextField

* Use MessageBar for error message

* Rename WdigetRendererComponent to SmartUiComponent
2020-07-06 17:16:43 +02:00
Laurent Nguyen
3f34936acd Add more files to strict compile. Update CONTRIBUTING.md (#63)
* Add more files to strict compile. Update CONTRIBUTING.md to recommend FluentUI use

* Remove eslint-disable and use non-null assertion
2020-07-06 17:16:24 +02:00
Steve Faulkner
c51a55013c Upload screenshot for runner failures (#72) 2020-07-02 09:58:36 -05:00
464 changed files with 48014 additions and 46732 deletions

View File

@@ -1,44 +1,44 @@
module.exports = { module.exports = {
env: { env: {
browser: true, browser: true,
es6: true, es6: true
}, },
plugins: ["@typescript-eslint", "no-null"], plugins: ["@typescript-eslint", "no-null"],
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
globals: { globals: {
Atomics: "readonly", Atomics: "readonly",
SharedArrayBuffer: "readonly", SharedArrayBuffer: "readonly"
}, },
parser: "@typescript-eslint/parser", parser: "@typescript-eslint/parser",
parserOptions: { parserOptions: {
ecmaFeatures: { ecmaFeatures: {
jsx: true, jsx: true
}, },
ecmaVersion: 2018, ecmaVersion: 2018,
sourceType: "module", sourceType: "module"
}, },
overrides: [ overrides: [
{ {
files: ["**/*.tsx"], files: ["**/*.tsx"],
env: { env: {
jest: true, jest: true
}, },
extends: ["plugin:react/recommended"], extends: ["plugin:react/recommended"],
plugins: ["react"], plugins: ["react"]
}, },
{ {
files: ["**/*.test.{ts,tsx}"], files: ["**/*.test.{ts,tsx}"],
env: { env: {
jest: true, jest: true
}, },
extends: ["plugin:jest/recommended"], extends: ["plugin:jest/recommended"],
plugins: ["jest"], plugins: ["jest"]
}, }
], ],
rules: { rules: {
curly: "error", curly: "error",
"@typescript-eslint/no-unused-vars": "error", "@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-extraneous-class": "error", "@typescript-eslint/no-extraneous-class": "error",
"no-null/no-null": "error", "no-null/no-null": "error"
}, }
}; };

View File

@@ -175,6 +175,7 @@ jobs:
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}" - run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
- run: nuget push -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg - run: nuget push -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
name: packages
with: with:
path: "*.nupkg" path: "*.nupkg"
nugetmpac: nugetmpac:
@@ -198,5 +199,6 @@ jobs:
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}" - run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
- run: nuget push -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg - run: nuget push -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
name: packages
with: with:
path: "*.nupkg" path: "*.nupkg"

View File

@@ -1,7 +1,7 @@
name: Runners name: Runners
on: on:
schedule: schedule:
- cron: "*/10 * * * *" - cron: "*/5 * * * *"
jobs: jobs:
sqlcreatecollection: sqlcreatecollection:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -18,3 +18,8 @@ jobs:
PORTAL_RUNNER_SUBSCRIPTION: 69e02f2d-f059-4409-9eac-97e8a276ae2c PORTAL_RUNNER_SUBSCRIPTION: 69e02f2d-f059-4409-9eac-97e8a276ae2c
PORTAL_RUNNER_RESOURCE_GROUP: runners PORTAL_RUNNER_RESOURCE_GROUP: runners
PORTAL_RUNNER_DATABASE_ACCOUNT: portal-sql-runner PORTAL_RUNNER_DATABASE_ACCOUNT: portal-sql-runner
- uses: actions/upload-artifact@v2
if: failure()
with:
name: screenshots
path: failure.png

1
.gitignore vendored
View File

@@ -17,3 +17,4 @@ Contracts/*
.DS_Store .DS_Store
.cache/ .cache/
.env .env
failure.png

View File

@@ -30,12 +30,13 @@ For IE support, polyfill is preferred over new usage of lodash or underscore. We
### Typescript ### Typescript
* Follow this [typescript style guide](https://github.com/excelmicro/typescript) which is based on [airbnb's style guide](https://github.com/airbnb/javascript). * Follow this [typescript style guide](https://github.com/excelmicro/typescript) which is based on [airbnb's style guide](https://github.com/airbnb/javascript).
* Conventions speficic to this project: * Conventions speficic to this project:
* Use double-quotes for string - Use double-quotes for string
* Don't use null, use undefined - Don't use `null`, use `undefined`
* Pascal case for private static readonly fields - Pascal case for private static readonly fields
* Camel case for classnames in markup - Camel case for classnames in markup
* Don't use class unless necessary * Don't use class unless necessary
* Code related to notebooks should be dynamically imported so that it is loaded from a separate bundle only if the account is notebook-enabled. There are already top-level notebook components which are dynamically imported and their dependencies can be statically imported from these files. * Code related to notebooks should be dynamically imported so that it is loaded from a separate bundle only if the account is notebook-enabled. There are already top-level notebook components which are dynamically imported and their dependencies can be statically imported from these files.
* Prefer using [Fluent UI controls](https://developer.microsoft.com/en-us/fluentui#/controls/web) over creating your own, in order to maintain consistency and support a11y.
### React ### React
* Prefer using React class components over function components and hooks unless you have a simple component and require no nested functions: * Prefer using React class components over function components and hooks unless you have a simple component and require no nested functions:

View File

@@ -1,3 +1,3 @@
module.exports = { module.exports = {
presets: [["@babel/preset-env", { targets: { node: "current" } }], "@babel/preset-react", "@babel/preset-typescript"], presets: [["@babel/preset-env", { targets: { node: "current" } }], "@babel/preset-react", "@babel/preset-typescript"]
}; };

View File

@@ -23,7 +23,7 @@ context("Cassandra API Test - createDatabase", () => {
const keyspaceId = `KeyspaceId${crypt.randomBytes(8).toString("hex")}`; const keyspaceId = `KeyspaceId${crypt.randomBytes(8).toString("hex")}`;
const tableId = `TableId112`; const tableId = `TableId112`;
cy.get("iframe").then(($element) => { cy.get("iframe").then($element => {
const $body = $element.contents().find("body"); const $body = $element.contents().find("body");
cy.wrap($body) cy.wrap($body)
.find('div[class="commandBarContainer"]') .find('div[class="commandBarContainer"]')
@@ -32,15 +32,27 @@ context("Cassandra API Test - createDatabase", () => {
.should("be.visible") .should("be.visible")
.click(); .click();
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]'); cy.wrap($body)
.find('div[class="contextual-pane-in"]')
.should("be.visible")
.find('span[id="containerTitle"]');
cy.wrap($body).find('input[id="keyspace-id"]').should("be.visible").type(keyspaceId); cy.wrap($body)
.find('input[id="keyspace-id"]')
.should("be.visible")
.type(keyspaceId);
cy.wrap($body).find('input[class="textfontclr"]').type(tableId); cy.wrap($body)
.find('input[class="textfontclr"]')
.type(tableId);
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400"); cy.wrap($body)
.find('input[data-test="databaseThroughputValue"]')
.should("have.value", "400");
cy.wrap($body).find('data-test="addCollection-createCollection"').click(); cy.wrap($body)
.find('data-test="addCollection-createCollection"')
.click();
cy.wait(10000); cy.wait(10000);

View File

@@ -24,7 +24,7 @@ context("Graph API Test", () => {
const graphId = `TestGraph${crypt.randomBytes(8).toString("hex")}`; const graphId = `TestGraph${crypt.randomBytes(8).toString("hex")}`;
const partitionKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`; const partitionKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
cy.get("iframe").then(($element) => { cy.get("iframe").then($element => {
const $body = $element.contents().find("body"); const $body = $element.contents().find("body");
cy.wrap($body) cy.wrap($body)
.find('div[class="commandBarContainer"]') .find('div[class="commandBarContainer"]')
@@ -33,21 +33,39 @@ context("Graph API Test", () => {
.should("be.visible") .should("be.visible")
.click(); .click();
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]'); cy.wrap($body)
.find('div[class="contextual-pane-in"]')
.should("be.visible")
.find('span[id="containerTitle"]');
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check(); cy.wrap($body)
.find('input[data-test="addCollection-createNewDatabase"]')
.check();
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').should("be.visible").type(dbId); cy.wrap($body)
.find('input[data-test="addCollection-newDatabaseId"]')
.should("be.visible")
.type(dbId);
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check(); cy.wrap($body)
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
.check();
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400"); cy.wrap($body)
.find('input[data-test="databaseThroughputValue"]')
.should("have.value", "400");
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(graphId); cy.wrap($body)
.find('input[data-test="addCollection-collectionId"]')
.type(graphId);
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(partitionKey); cy.wrap($body)
.find('input[data-test="addCollection-partitionKeyValue"]')
.type(partitionKey);
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click(); cy.wrap($body)
.find('input[data-test="addCollection-createCollection"]')
.click();
cy.wait(10000); cy.wait(10000);

View File

@@ -24,7 +24,7 @@ context("Mongo API Test - createDatabase", () => {
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`; const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`; const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
cy.get("iframe").then(($element) => { cy.get("iframe").then($element => {
const $body = $element.contents().find("body"); const $body = $element.contents().find("body");
cy.wrap($body) cy.wrap($body)
.find('div[class="commandBarContainer"]') .find('div[class="commandBarContainer"]')
@@ -33,21 +33,38 @@ context("Mongo API Test - createDatabase", () => {
.should("be.visible") .should("be.visible")
.click(); .click();
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]'); cy.wrap($body)
.find('div[class="contextual-pane-in"]')
.should("be.visible")
.find('span[id="containerTitle"]');
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check(); cy.wrap($body)
.find('input[data-test="addCollection-createNewDatabase"]')
.check();
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId); cy.wrap($body)
.find('input[data-test="addCollection-newDatabaseId"]')
.type(dbId);
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check(); cy.wrap($body)
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
.check();
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId); cy.wrap($body)
.find('input[data-test="addCollection-collectionId"]')
.type(collectionId);
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400"); cy.wrap($body)
.find('input[data-test="databaseThroughputValue"]')
.should("have.value", "400");
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey); cy.wrap($body)
.find('input[data-test="addCollection-partitionKeyValue"]')
.type(sharedKey);
cy.wrap($body).find("#submitBtnAddCollection").click(); cy.wrap($body)
.find("#submitBtnAddCollection")
.click();
cy.wait(10000); cy.wait(10000);

View File

@@ -24,7 +24,7 @@ context("Mongo API Test", () => {
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`; const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`; const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
cy.get("iframe").then(($element) => { cy.get("iframe").then($element => {
const $body = $element.contents().find("body"); const $body = $element.contents().find("body");
cy.wrap($body) cy.wrap($body)
.find('div[class="commandBarContainer"]') .find('div[class="commandBarContainer"]')
@@ -33,23 +33,34 @@ context("Mongo API Test", () => {
.should("be.visible") .should("be.visible")
.click(); .click();
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]'); cy.wrap($body)
.find('div[class="contextual-pane-in"]')
.should("be.visible")
.find('span[id="containerTitle"]');
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check(); cy.wrap($body)
.find('input[data-test="addCollection-createNewDatabase"]')
.check();
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId); cy.wrap($body)
.find('input[data-test="addCollection-newDatabaseId"]')
.type(dbId);
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check(); cy.wrap($body)
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
.check();
cy.wrap($body) cy.wrap($body)
.find('div[class="throughputModeContainer"]') .find('div[class="throughputModeContainer"]')
.should("be.visible") .should("be.visible")
.and((input) => { .and(input => {
expect(input.get(0).textContent, "first item").contains("Autopilot (preview)"); expect(input.get(0).textContent, "first item").contains("Autopilot (preview)");
expect(input.get(1).textContent, "second item").contains("Manual"); expect(input.get(1).textContent, "second item").contains("Manual");
}); });
cy.wrap($body).find('input[id="newContainer-databaseThroughput-autoPilotRadio"]').check(); cy.wrap($body)
.find('input[id="newContainer-databaseThroughput-autoPilotRadio"]')
.check();
cy.wrap($body) cy.wrap($body)
.find('select[name="autoPilotTiers"]') .find('select[name="autoPilotTiers"]')
@@ -57,13 +68,19 @@ context("Mongo API Test", () => {
// // .select('4,000 RU/s').should('have.value', '1'); // // .select('4,000 RU/s').should('have.value', '1');
.find('option[value="2"]') .find('option[value="2"]')
.then(($element) => $element.get(1).setAttribute("selected", "selected")); .then($element => $element.get(1).setAttribute("selected", "selected"));
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId); cy.wrap($body)
.find('input[data-test="addCollection-collectionId"]')
.type(collectionId);
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey); cy.wrap($body)
.find('input[data-test="addCollection-partitionKeyValue"]')
.type(sharedKey);
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click(); cy.wrap($body)
.find('input[data-test="addCollection-createCollection"]')
.click();
cy.wait(10000); cy.wait(10000);

View File

@@ -11,13 +11,13 @@ context("Mongo API Test", () => {
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`; const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`; const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
cy.get("iframe").then(($element) => { cy.get("iframe").then($element => {
const $body = $element.contents().find("body"); const $body = $element.contents().find("body");
cy.wrap($body) cy.wrap($body)
.find('span[class="nodeLabel"]') .find('span[class="nodeLabel"]')
.should("be.visible") .should("be.visible")
.then(($span) => { .then($span => {
const dbId1 = $span.text(); const dbId1 = $span.text();
cy.log("DBBB", dbId1); cy.log("DBBB", dbId1);
@@ -28,17 +28,30 @@ context("Mongo API Test", () => {
.should("be.visible") .should("be.visible")
.click(); .click();
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]'); cy.wrap($body)
.find('div[class="contextual-pane-in"]')
.should("be.visible")
.find('span[id="containerTitle"]');
cy.wrap($body).find('input[data-test="addCollection-existingDatabase"]').check(); cy.wrap($body)
.find('input[data-test="addCollection-existingDatabase"]')
.check();
cy.wrap($body).find('input[data-test="addCollection-existingDatabase"]').type(dbId1); cy.wrap($body)
.find('input[data-test="addCollection-existingDatabase"]')
.type(dbId1);
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId); cy.wrap($body)
.find('input[data-test="addCollection-collectionId"]')
.type(collectionId);
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey); cy.wrap($body)
.find('input[data-test="addCollection-partitionKeyValue"]')
.type(sharedKey);
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click(); cy.wrap($body)
.find('input[data-test="addCollection-createCollection"]')
.click();
cy.wait(10000); cy.wait(10000);

View File

@@ -12,7 +12,7 @@ context.skip("Mongo API Test", () => {
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`; const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`; const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
cy.get("iframe").then(($element) => { cy.get("iframe").then($element => {
const $body = $element.contents().find("body"); const $body = $element.contents().find("body");
cy.wrap($body) cy.wrap($body)
.find('div[class="commandBarContainer"]') .find('div[class="commandBarContainer"]')
@@ -21,31 +21,50 @@ context.skip("Mongo API Test", () => {
.should("be.visible") .should("be.visible")
.click(); .click();
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]'); cy.wrap($body)
.find('div[class="contextual-pane-in"]')
.should("be.visible")
.find('span[id="containerTitle"]');
cy.wrap($body) cy.wrap($body)
.find(".createNewDatabaseOrUseExisting") .find(".createNewDatabaseOrUseExisting")
.should("have.length", 2) .should("have.length", 2)
.and((input) => { .and(input => {
expect(input.get(0).textContent, "first item").contains("Create new"); expect(input.get(0).textContent, "first item").contains("Create new");
expect(input.get(1).textContent, "second item").contains("Use existing"); expect(input.get(1).textContent, "second item").contains("Use existing");
}); });
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check(); cy.wrap($body)
.find('input[data-test="addCollection-createNewDatabase"]')
.check();
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check(); cy.wrap($body)
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
.check();
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId); cy.wrap($body)
.find('input[data-test="addCollection-newDatabaseId"]')
.type(dbId);
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check(); cy.wrap($body)
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
.check();
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400"); cy.wrap($body)
.find('input[data-test="databaseThroughputValue"]')
.should("have.value", "400");
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId); cy.wrap($body)
.find('input[data-test="addCollection-collectionId"]')
.type(collectionId);
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey); cy.wrap($body)
.find('input[data-test="addCollection-partitionKeyValue"]')
.type(sharedKey);
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click(); cy.wrap($body)
.find('input[data-test="addCollection-createCollection"]')
.click();
cy.wait(10000); cy.wait(10000);
@@ -65,7 +84,7 @@ context.skip("Mongo API Test", () => {
const collectionIdTitle = `Add Collection`; const collectionIdTitle = `Add Collection`;
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`; const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
cy.get("iframe").then(($element) => { cy.get("iframe").then($element => {
const $body = $element.contents().find("body"); const $body = $element.contents().find("body");
cy.wrap($body) cy.wrap($body)
.find('div[class="commandBarContainer"]') .find('div[class="commandBarContainer"]')
@@ -74,23 +93,42 @@ context.skip("Mongo API Test", () => {
.should("be.visible") .should("be.visible")
.click(); .click();
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]'); cy.wrap($body)
.find('div[class="contextual-pane-in"]')
.should("be.visible")
.find('span[id="containerTitle"]');
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check(); cy.wrap($body)
.find('input[data-test="addCollection-createNewDatabase"]')
.check();
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId); cy.wrap($body)
.find('input[data-test="addCollection-newDatabaseId"]')
.type(dbId);
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').uncheck(); cy.wrap($body)
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
.uncheck();
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId); cy.wrap($body)
.find('input[data-test="addCollection-collectionId"]')
.type(collectionId);
cy.wrap($body).find('input[id="tab2"]').check({ force: true }); cy.wrap($body)
.find('input[id="tab2"]')
.check({ force: true });
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey); cy.wrap($body)
.find('input[data-test="addCollection-partitionKeyValue"]')
.type(sharedKey);
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400"); cy.wrap($body)
.find('input[data-test="databaseThroughputValue"]')
.should("have.value", "400");
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click(); cy.wrap($body)
.find('input[data-test="addCollection-createCollection"]')
.click();
cy.wait(10000); cy.wait(10000);
@@ -109,7 +147,7 @@ context.skip("Mongo API Test", () => {
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`; const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`; const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
cy.get("iframe").then(($element) => { cy.get("iframe").then($element => {
const $body = $element.contents().find("body"); const $body = $element.contents().find("body");
cy.wrap($body) cy.wrap($body)
.find('div[class="commandBarContainer"]') .find('div[class="commandBarContainer"]')
@@ -118,21 +156,38 @@ context.skip("Mongo API Test", () => {
.should("be.visible") .should("be.visible")
.click(); .click();
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]'); cy.wrap($body)
.find('div[class="contextual-pane-in"]')
.should("be.visible")
.find('span[id="containerTitle"]');
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check(); cy.wrap($body)
.find('input[data-test="addCollection-createNewDatabase"]')
.check();
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId); cy.wrap($body)
.find('input[data-test="addCollection-newDatabaseId"]')
.type(dbId);
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').uncheck(); cy.wrap($body)
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
.uncheck();
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId); cy.wrap($body)
.find('input[data-test="addCollection-collectionId"]')
.type(collectionId);
cy.wrap($body).find('input[id="tab1"]').check({ force: true }); cy.wrap($body)
.find('input[id="tab1"]')
.check({ force: true });
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400"); cy.wrap($body)
.find('input[data-test="databaseThroughputValue"]')
.should("have.value", "400");
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click(); cy.wrap($body)
.find('input[data-test="addCollection-createCollection"]')
.click();
cy.wait(10000); cy.wait(10000);

View File

@@ -25,7 +25,7 @@ context("SQL API Test", () => {
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`; const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
connectionString.loginUsingConnectionString(); connectionString.loginUsingConnectionString();
cy.get("iframe").then(($element) => { cy.get("iframe").then($element => {
const $body = $element.contents().find("body"); const $body = $element.contents().find("body");
cy.wrap($body) cy.wrap($body)
.find('div[class="commandBarContainer"]') .find('div[class="commandBarContainer"]')
@@ -34,21 +34,38 @@ context("SQL API Test", () => {
.should("be.visible") .should("be.visible")
.click(); .click();
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]'); cy.wrap($body)
.find('div[class="contextual-pane-in"]')
.should("be.visible")
.find('span[id="containerTitle"]');
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check(); cy.wrap($body)
.find('input[data-test="addCollection-createNewDatabase"]')
.check();
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check(); cy.wrap($body)
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
.check();
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId); cy.wrap($body)
.find('input[data-test="addCollection-newDatabaseId"]')
.type(dbId);
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId); cy.wrap($body)
.find('input[data-test="addCollection-collectionId"]')
.type(collectionId);
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400"); cy.wrap($body)
.find('input[data-test="databaseThroughputValue"]')
.should("have.value", "400");
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey); cy.wrap($body)
.find('input[data-test="addCollection-partitionKeyValue"]')
.type(sharedKey);
cy.wrap($body).find("#submitBtnAddCollection").click(); cy.wrap($body)
.find("#submitBtnAddCollection")
.click();
cy.wait(10000); cy.wait(10000);

View File

@@ -22,7 +22,7 @@ context("Table API Test", () => {
it("Create a new table in Table API", () => { it("Create a new table in Table API", () => {
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`; const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
cy.get("iframe").then(($element) => { cy.get("iframe").then($element => {
const $body = $element.contents().find("body"); const $body = $element.contents().find("body");
cy.wrap($body) cy.wrap($body)
.find('div[class="commandBarContainer"]') .find('div[class="commandBarContainer"]')
@@ -31,13 +31,22 @@ context("Table API Test", () => {
.should("be.visible") .should("be.visible")
.click(); .click();
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]'); cy.wrap($body)
.find('div[class="contextual-pane-in"]')
.should("be.visible")
.find('span[id="containerTitle"]');
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId); cy.wrap($body)
.find('input[data-test="addCollection-collectionId"]')
.type(collectionId);
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400"); cy.wrap($body)
.find('input[data-test="databaseThroughputValue"]')
.should("have.value", "400");
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click(); cy.wrap($body)
.find('input[data-test="addCollection-createCollection"]')
.click();
cy.wait(10000); cy.wait(10000);

View File

@@ -29,7 +29,7 @@ context("Emulator - createDatabase", () => {
cy.get(".createNewDatabaseOrUseExisting") cy.get(".createNewDatabaseOrUseExisting")
.should("have.length", 2) .should("have.length", 2)
.and((input) => { .and(input => {
expect(input.get(0).textContent, "first item").contains("Create new"); expect(input.get(0).textContent, "first item").contains("Create new");
expect(input.get(1).textContent, "second item").contains("Use existing"); expect(input.get(1).textContent, "second item").contains("Use existing");
}); });

View File

@@ -38,15 +38,27 @@ context("Emulator - Create database -> container -> item", () => {
cy.get("[data-test=addCollection-partitionKeyValue]").type("/pk"); cy.get("[data-test=addCollection-partitionKeyValue]").type("/pk");
cy.get('input[name="createCollection"]').click(); cy.get('input[name="createCollection"]').click();
cy.get(".dataResourceTree").should("contain", databaseId); cy.get(".dataResourceTree").should("contain", databaseId);
cy.get(".dataResourceTree").contains(databaseId).click(); cy.get(".dataResourceTree")
.contains(databaseId)
.click();
cy.get(".dataResourceTree").should("contain", collectionId); cy.get(".dataResourceTree").should("contain", collectionId);
cy.get(".dataResourceTree").contains(collectionId).click(); cy.get(".dataResourceTree")
cy.get(".dataResourceTree").contains("Items").click(); .contains(collectionId)
cy.get(".dataResourceTree").contains("Items").click(); .click();
cy.get(".dataResourceTree")
.contains("Items")
.click();
cy.get(".dataResourceTree")
.contains("Items")
.click();
cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting
cy.get(".commandBarContainer").contains("New Item").click(); cy.get(".commandBarContainer")
.contains("New Item")
.click();
cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting
cy.get(".commandBarContainer").contains("Save").click(); cy.get(".commandBarContainer")
.contains("Save")
.click();
cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting
cy.get(".documentsGridHeaderContainer").should("contain", "replace_with_new_document_id"); cy.get(".documentsGridHeaderContainer").should("contain", "replace_with_new_document_id");
}); });

View File

@@ -14,18 +14,25 @@ context("Emulator - deleteCollection", () => {
}); });
it("Delete a collection", () => { it("Delete a collection", () => {
cy.get(".databaseId").last().click(); cy.get(".databaseId")
.last()
.click();
cy.get(".collectionList") cy.get(".collectionList")
.last() .last()
.then(($id) => { .then($id => {
const collectionId = $id.text(); const collectionId = $id.text();
cy.get('span[data-test="collectionEllipsisMenu"]').should("exist"); cy.get('span[data-test="collectionEllipsisMenu"]').should("exist");
cy.get('span[data-test="collectionEllipsisMenu"]').invoke("show").last().click(); cy.get('span[data-test="collectionEllipsisMenu"]')
.invoke("show")
.last()
.click();
cy.get('div[data-test="collectionContextMenu"]').contains("Delete Container").click({ force: true }); cy.get('div[data-test="collectionContextMenu"]')
.contains("Delete Container")
.click({ force: true });
cy.get('input[data-test="confirmCollectionId"]').type(collectionId.trim()); cy.get('input[data-test="confirmCollectionId"]').type(collectionId.trim());

View File

@@ -22,10 +22,10 @@ context("Emulator - deleteDatabase", () => {
url: "https://localhost:8081/_explorer/authorization/post/dbs/", url: "https://localhost:8081/_explorer/authorization/post/dbs/",
headers: { headers: {
"x-ms-date": date, "x-ms-date": date,
authorization: "-", authorization: "-"
}, }
}) })
.then((response) => { .then(response => {
authToken = response.body.Token; // Getting auth token for collection creation authToken = response.body.Token; // Getting auth token for collection creation
return new Cypress.Promise((resolve, reject) => { return new Cypress.Promise((resolve, reject) => {
return resolve(); return resolve();
@@ -38,12 +38,12 @@ context("Emulator - deleteDatabase", () => {
headers: { headers: {
"x-ms-date": date, "x-ms-date": date,
authorization: authToken, authorization: authToken,
"x-ms-version": "2018-12-31", "x-ms-version": "2018-12-31"
}, },
body: { body: {
id: dbId, id: dbId
}, }
}).then((response) => { }).then(response => {
cy.log("Response", response); cy.log("Response", response);
db_rid = response.body._rid; db_rid = response.body._rid;
return new Cypress.Promise((resolve, reject) => { return new Cypress.Promise((resolve, reject) => {
@@ -59,14 +59,19 @@ context("Emulator - deleteDatabase", () => {
cy.get(".databaseId") cy.get(".databaseId")
.last() .last()
.then(($id) => { .then($id => {
const dbId = $id.text(); const dbId = $id.text();
cy.get('span[data-test="databaseEllipsisMenu"]').should("exist"); cy.get('span[data-test="databaseEllipsisMenu"]').should("exist");
cy.get('span[data-test="databaseEllipsisMenu"]').invoke("show").last().click(); cy.get('span[data-test="databaseEllipsisMenu"]')
.invoke("show")
.last()
.click();
cy.get('div[data-test="databaseContextMenu"]').contains("Delete Database").click({ force: true }); cy.get('div[data-test="databaseContextMenu"]')
.contains("Delete Database")
.click({ force: true });
cy.get('input[data-test="confirmDatabaseId"]').type(dbId.trim()); cy.get('input[data-test="confirmDatabaseId"]').type(dbId.trim());

View File

@@ -21,25 +21,29 @@ context("New Notebook smoke test", () => {
cy.contains("New Notebook").click(); cy.contains("New Notebook").click();
// Check tab name // Check tab name
cy.get("li.tabList .tabNavText").should(($span) => { cy.get("li.tabList .tabNavText").should($span => {
const text = $span.text(); const text = $span.text();
expect(text).to.match(/^Untitled.*\.ipynb$/); expect(text).to.match(/^Untitled.*\.ipynb$/);
}); });
// Wait for python3 | idle status // Wait for python3 | idle status
cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should(($p) => { cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should($p => {
const text = $p.text(); const text = $p.text();
expect(text).to.match(/^python3.*idle$/); expect(text).to.match(/^python3.*idle$/);
}); });
// Click on a cell // Click on a cell
cy.get(".cell-container").as("cellContainer").click(); cy.get(".cell-container")
.as("cellContainer")
.click();
// Type in some code // Type in some code
cy.get("@cellContainer").type("2+4"); cy.get("@cellContainer").type("2+4");
// Execute // Execute
cy.get('[data-test="Run"]').first().click(); cy.get('[data-test="Run"]')
.first()
.click();
// Verify results // Verify results
cy.get("@cellContainer").within(() => { cy.get("@cellContainer").within(() => {
@@ -47,29 +51,39 @@ context("New Notebook smoke test", () => {
}); });
// Restart kernel // Restart kernel
cy.get('[data-test="Run"] button').eq(-1).click(); cy.get('[data-test="Run"] button')
cy.get("li").contains("Restart Kernel").click(); .eq(-1)
.click();
cy.get("li")
.contains("Restart Kernel")
.click();
// Wait for python3 | restarting status // Wait for python3 | restarting status
cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should(($p) => { cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should($p => {
const text = $p.text(); const text = $p.text();
expect(text).to.match(/^python3.*restarting$/); expect(text).to.match(/^python3.*restarting$/);
}); });
// Wait for python3 | idle status // Wait for python3 | idle status
cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should(($p) => { cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should($p => {
const text = $p.text(); const text = $p.text();
expect(text).to.match(/^python3.*idle$/); expect(text).to.match(/^python3.*idle$/);
}); });
// Click on a cell // Click on a cell
cy.get(".cell-container").as("cellContainer").find(".input").as("codeInput").click(); cy.get(".cell-container")
.as("cellContainer")
.find(".input")
.as("codeInput")
.click();
// Type in some code // Type in some code
cy.get("@codeInput").type("{backspace}{backspace}{backspace}4+5"); cy.get("@codeInput").type("{backspace}{backspace}{backspace}4+5");
// Execute // Execute
cy.get('[data-test="Run"]').first().click(); cy.get('[data-test="Run"]')
.first()
.click();
// Verify results // Verify results
cy.get("@cellContainer").within(() => { cy.get("@cellContainer").within(() => {

View File

@@ -11,11 +11,15 @@ context("Resource tree notebook file manipulation", () => {
}; };
const clickContextMenuAndSelectOption = (nodeLabel, option) => { const clickContextMenuAndSelectOption = (nodeLabel, option) => {
cy.get(`.treeNodeHeader[data-test="${nodeLabel}"]`).find("button.treeMenuEllipsis").click(); cy.get(`.treeNodeHeader[data-test="${nodeLabel}"]`)
cy.get('[data-test="treeComponentMenuItemContainer"]').contains(option).click(); .find("button.treeMenuEllipsis")
.click();
cy.get('[data-test="treeComponentMenuItemContainer"]')
.contains(option)
.click();
}; };
const createFolder = (folder) => { const createFolder = folder => {
clickContextMenuAndSelectOption("My Notebooks/", "New Directory"); clickContextMenuAndSelectOption("My Notebooks/", "New Directory");
cy.get("#stringInputPane").within(() => { cy.get("#stringInputPane").within(() => {
@@ -24,9 +28,11 @@ context("Resource tree notebook file manipulation", () => {
}); });
}; };
const deleteItem = (nodeName) => { const deleteItem = nodeName => {
clickContextMenuAndSelectOption(`${nodeName}`, "Delete"); clickContextMenuAndSelectOption(`${nodeName}`, "Delete");
cy.get(".ms-Dialog-main").contains("Delete").click(); cy.get(".ms-Dialog-main")
.contains("Delete")
.click();
}; };
beforeEach(() => { beforeEach(() => {
@@ -50,7 +56,9 @@ context("Resource tree notebook file manipulation", () => {
// Rename // Rename
clickContextMenuAndSelectOption(`${folder}/`, "Rename"); clickContextMenuAndSelectOption(`${folder}/`, "Rename");
cy.get("#stringInputPane").within(() => { cy.get("#stringInputPane").within(() => {
cy.get('input[name="collectionIdConfirmation"]').clear().type(renamedFolder); cy.get('input[name="collectionIdConfirmation"]')
.clear()
.type(renamedFolder);
cy.get("form").submit(); cy.get("form").submit();
}); });
cy.get(`.treeNodeHeader[data-test="${renamedFolder}/"]`).should("exist"); cy.get(`.treeNodeHeader[data-test="${renamedFolder}/"]`).should("exist");
@@ -67,12 +75,16 @@ context("Resource tree notebook file manipulation", () => {
clickContextMenuAndSelectOption(`${folder}/`, "New Notebook"); clickContextMenuAndSelectOption(`${folder}/`, "New Notebook");
// Verify tab is open // Verify tab is open
cy.get(".tabList").contains(newNotebookName).should("exist"); cy.get(".tabList")
.contains(newNotebookName)
.should("exist");
// Close tab // Close tab
cy.get(`.tabList[title="notebooks/${folder}/${newNotebookName}"]`).find(".cancelButton").click(); cy.get(`.tabList[title="notebooks/${folder}/${newNotebookName}"]`)
.find(".cancelButton")
.click();
// When running from command line, closing the tab is too fast // When running from command line, closing the tab is too fast
cy.get("body").then(($body) => { cy.get("body").then($body => {
if ($body.find(".ms-Dialog-main").length) { if ($body.find(".ms-Dialog-main").length) {
// For some reason, this does not work // For some reason, this does not work
// cy.get(".ms-Dialog-main").contains("Close").click(); // cy.get(".ms-Dialog-main").contains("Close").click();
@@ -88,10 +100,14 @@ context("Resource tree notebook file manipulation", () => {
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`) cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`)
.find("button.treeMenuEllipsis") .find("button.treeMenuEllipsis")
.click(); .click();
cy.get('[data-test="treeComponentMenuItemContainer"]').contains("Delete").click(); cy.get('[data-test="treeComponentMenuItemContainer"]')
.contains("Delete")
.click();
// Confirm // Confirm
cy.get(".ms-Dialog-main").contains("Delete").click(); cy.get(".ms-Dialog-main")
.contains("Delete")
.click();
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`).should("not.exist"); cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`).should("not.exist");
deleteItem(`${folder}/`); deleteItem(`${folder}/`);
@@ -105,8 +121,10 @@ context("Resource tree notebook file manipulation", () => {
clickContextMenuAndSelectOption(`${folder}/`, "New Notebook"); clickContextMenuAndSelectOption(`${folder}/`, "New Notebook");
// Close tab // Close tab
cy.get(`.tabList[title="notebooks/${folder}/${newNotebookName}"]`).find(".cancelButton").click(); cy.get(`.tabList[title="notebooks/${folder}/${newNotebookName}"]`)
cy.get("body").then(($body) => { .find(".cancelButton")
.click();
cy.get("body").then($body => {
if ($body.find(".ms-Dialog-main").length) { if ($body.find(".ms-Dialog-main").length) {
// For some reason, this does not work // For some reason, this does not work
// cy.get(".ms-Dialog-main").contains("Close").click(); // cy.get(".ms-Dialog-main").contains("Close").click();
@@ -122,10 +140,14 @@ context("Resource tree notebook file manipulation", () => {
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`) cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`)
.find("button.treeMenuEllipsis") .find("button.treeMenuEllipsis")
.click(); .click();
cy.get('[data-test="treeComponentMenuItemContainer"]').contains("Rename").click(); cy.get('[data-test="treeComponentMenuItemContainer"]')
.contains("Rename")
.click();
cy.get("#stringInputPane").within(() => { cy.get("#stringInputPane").within(() => {
cy.get('input[name="collectionIdConfirmation"]').clear().type(renamedNotebookName); cy.get('input[name="collectionIdConfirmation"]')
.clear()
.type(renamedNotebookName);
cy.get("form").submit(); cy.get("form").submit();
}); });
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`).should("not.exist"); cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`).should("not.exist");
@@ -135,10 +157,14 @@ context("Resource tree notebook file manipulation", () => {
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${renamedNotebookName}"]`) cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${renamedNotebookName}"]`)
.find("button.treeMenuEllipsis") .find("button.treeMenuEllipsis")
.click(); .click();
cy.get('[data-test="treeComponentMenuItemContainer"]').contains("Delete").click(); cy.get('[data-test="treeComponentMenuItemContainer"]')
.contains("Delete")
.click();
// Confirm // Confirm
cy.get(".ms-Dialog-main").contains("Delete").click(); cy.get(".ms-Dialog-main")
.contains("Delete")
.click();
// Give it time to settle // Give it time to settle
cy.wait(1000); cy.wait(1000);
deleteItem(`${folder}/`); deleteItem(`${folder}/`);

22
images/CosmosDB-logo.svg Normal file
View File

@@ -0,0 +1,22 @@
<svg id="b089cfca-0de1-451c-a1ca-6680ea50cb4f" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<defs>
<radialGradient id="b25d0836-964a-4c84-8c20-855f66e8345e" cx="-105.006" cy="-10.409" r="5.954" gradientTransform="translate(117.739 19.644) scale(1.036 1.027)" gradientUnits="userSpaceOnUse">
<stop offset="0.183" stop-color="#5ea0ef"/>
<stop offset="1" stop-color="#0078d4"/>
</radialGradient>
<clipPath id="b36c7f5d-2ef1-4760-8a25-eeb9661f4e47">
<path d="M14.969,7.53A6.137,6.137,0,1,1,7.574,2.987,6.137,6.137,0,0,1,14.969,7.53Z" fill="none"/>
</clipPath>
</defs>
<title>Icon-databases-121</title>
<path d="M2.954,5.266a.175.175,0,0,1-.176-.176h0A2.012,2.012,0,0,0,.769,3.081a.176.176,0,0,1-.176-.175h0a.176.176,0,0,1,.176-.176A2.012,2.012,0,0,0,2.778.72.175.175,0,0,1,2.954.544h0A.175.175,0,0,1,3.13.72h0A2.012,2.012,0,0,0,5.139,2.729a.175.175,0,0,1,.176.176h0a.175.175,0,0,1-.176.176h0A2.011,2.011,0,0,0,3.13,5.09.177.177,0,0,1,2.954,5.266Z" fill="#50e6ff"/>
<path d="M15.611,17.456a.141.141,0,0,1-.141-.141h0a1.609,1.609,0,0,0-1.607-1.607.141.141,0,0,1-.141-.14h0a.141.141,0,0,1,.141-.141h0a1.608,1.608,0,0,0,1.607-1.607.141.141,0,0,1,.141-.141h0a.141.141,0,0,1,.141.141h0a1.608,1.608,0,0,0,1.607,1.607.141.141,0,1,1,0,.282h0a1.609,1.609,0,0,0-1.607,1.607A.141.141,0,0,1,15.611,17.456Z" fill="#50e6ff"/>
<g>
<path d="M14.969,7.53A6.137,6.137,0,1,1,7.574,2.987,6.137,6.137,0,0,1,14.969,7.53Z" fill="url(#b25d0836-964a-4c84-8c20-855f66e8345e)"/>
<g clip-path="url(#b36c7f5d-2ef1-4760-8a25-eeb9661f4e47)">
<path d="M5.709,13.115A1.638,1.638,0,1,0,5.714,9.84,1.307,1.307,0,0,0,5.721,9.7,1.651,1.651,0,0,0,4.06,8.064H2.832a6.251,6.251,0,0,0,1.595,5.051Z" fill="#f2f2f2"/>
<path d="M15.045,7.815c0-.015,0-.03-.007-.044a5.978,5.978,0,0,0-1.406-2.88,1.825,1.825,0,0,0-.289-.09,1.806,1.806,0,0,0-2.3,1.663,2,2,0,0,0-.2-.013,1.737,1.737,0,0,0-.581,3.374,1.451,1.451,0,0,0,.541.1h2.03A13.453,13.453,0,0,0,15.045,7.815Z" fill="#f2f2f2"/>
</g>
</g>
<path d="M17.191,3.832c-.629-1.047-2.1-1.455-4.155-1.149a14.606,14.606,0,0,0-2.082.452,6.456,6.456,0,0,1,1.528.767c.241-.053.483-.116.715-.151A7.49,7.49,0,0,1,14.3,3.662a2.188,2.188,0,0,1,1.959.725h0c.383.638.06,1.729-.886,3a16.723,16.723,0,0,1-4.749,4.051A16.758,16.758,0,0,1,4.8,13.7c-1.564.234-2.682,0-3.065-.636s-.06-1.73.886-2.995c.117-.157.146-.234.279-.392a6.252,6.252,0,0,1,.026-1.63A11.552,11.552,0,0,0,1.756,9.419C.517,11.076.181,12.566.809,13.613a3.165,3.165,0,0,0,2.9,1.249,8.434,8.434,0,0,0,1.251-.1,17.855,17.855,0,0,0,6.219-2.4,17.808,17.808,0,0,0,5.061-4.332C17.483,6.369,17.819,4.88,17.191,3.832Z" fill="#50e6ff"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -4,6 +4,6 @@ module.exports = {
launch: { launch: {
headless: isCI, headless: isCI,
slowMo: isCI ? null : 20, slowMo: isCI ? null : 20,
defaultViewport: null, defaultViewport: null
}, }
}; };

View File

@@ -1,5 +1,5 @@
module.exports = { module.exports = {
preset: "jest-puppeteer", preset: "jest-puppeteer",
testMatch: ["<rootDir>/test/**/*.spec.[jt]s?(x)"], testMatch: ["<rootDir>/test/**/*.spec.[jt]s?(x)"],
setupFiles: ["dotenv/config"], setupFiles: ["dotenv/config"]
}; };

View File

@@ -42,8 +42,8 @@ module.exports = {
branches: 18, branches: 18,
functions: 22, functions: 22,
lines: 28, lines: 28,
statements: 27, statements: 27
}, }
}, },
// Make calling deprecated APIs throw helpful error messages // Make calling deprecated APIs throw helpful error messages
@@ -76,7 +76,7 @@ module.exports = {
"office-ui-fabric-react/lib/(.*)$": "office-ui-fabric-react/lib-commonjs/$1", // https://github.com/OfficeDev/office-ui-fabric-react/wiki/Fabric-6-Release-Notes "office-ui-fabric-react/lib/(.*)$": "office-ui-fabric-react/lib-commonjs/$1", // https://github.com/OfficeDev/office-ui-fabric-react/wiki/Fabric-6-Release-Notes
"^dnd-core$": "dnd-core/dist/cjs", "^dnd-core$": "dnd-core/dist/cjs",
"^react-dnd$": "react-dnd/dist/cjs", "^react-dnd$": "react-dnd/dist/cjs",
"^react-dnd-html5-backend$": "react-dnd-html5-backend/dist/cjs", "^react-dnd-html5-backend$": "react-dnd-html5-backend/dist/cjs"
}, },
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
@@ -164,11 +164,11 @@ module.exports = {
// A map from regular expressions to paths to transformers // A map from regular expressions to paths to transformers
transform: { transform: {
"^.+\\.html?$": "html-loader-jest", "^.+\\.html?$": "html-loader-jest",
"^.+\\.[t|j]sx?$": "babel-jest", "^.+\\.[t|j]sx?$": "babel-jest"
}, },
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
transformIgnorePatterns: ["/node_modules/", "/externals/"], transformIgnorePatterns: ["/node_modules/", "/externals/"]
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined, // unmockedModulePathPatterns: undefined,

6
package-lock.json generated
View File

@@ -20463,9 +20463,9 @@
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
}, },
"prettier": { "prettier": {
"version": "2.0.5", "version": "1.19.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
"integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
"dev": true "dev": true
}, },
"pretty-error": { "pretty-error": {

View File

@@ -146,7 +146,7 @@
"less-vars-loader": "1.1.0", "less-vars-loader": "1.1.0",
"mini-css-extract-plugin": "0.4.3", "mini-css-extract-plugin": "0.4.3",
"monaco-editor-webpack-plugin": "1.7.0", "monaco-editor-webpack-plugin": "1.7.0",
"prettier": "2.0.5", "prettier": "1.19.1",
"puppeteer": "4.0.0", "puppeteer": "4.0.0",
"raw-loader": "0.5.1", "raw-loader": "0.5.1",
"rimraf": "3.0.0", "rimraf": "3.0.0",

View File

@@ -2,5 +2,5 @@ export enum AuthType {
AAD = "aad", AAD = "aad",
EncryptedToken = "encryptedtoken", EncryptedToken = "encryptedtoken",
MasterKey = "masterkey", MasterKey = "masterkey",
ResourceToken = "resourcetoken", ResourceToken = "resourcetoken"
} }

View File

@@ -1,11 +1,6 @@
import * as ko from "knockout"; import * as ko from "knockout";
import * as ReactBindingHandler from "./ReactBindingHandler"; import * as ReactBindingHandler from "./ReactBindingHandler";
interface RestorePoint {
readonly element: JQuery;
readonly width: number;
}
export class BindingHandlersRegisterer { export class BindingHandlersRegisterer {
public static registerBindingHandlers() { public static registerBindingHandlers() {
ko.bindingHandlers.setTemplateReady = { ko.bindingHandlers.setTemplateReady = {
@@ -17,8 +12,8 @@ export class BindingHandlersRegisterer {
bindingContext?: ko.BindingContext bindingContext?: ko.BindingContext
) { ) {
const value = ko.unwrap(wrappedValueAccessor()); const value = ko.unwrap(wrappedValueAccessor());
bindingContext.$data.isTemplateReady(value); bindingContext?.$data.isTemplateReady(value);
}, }
} as ko.BindingHandler; } as ko.BindingHandler;
ReactBindingHandler.Registerer.register(); ReactBindingHandler.Registerer.register();

View File

@@ -42,7 +42,7 @@ export class Registerer {
// Initial rendering at mount point // Initial rendering at mount point
ReactDOM.render(adapter.renderComponent(), element); ReactDOM.render(adapter.renderComponent(), element);
}, }
} as ko.BindingHandler; } as ko.BindingHandler;
} }
} }

View File

@@ -40,7 +40,7 @@ export class ArrayHashMap<T> {
public forEach(key: string, iteratorFct: (value: T) => void) { public forEach(key: string, iteratorFct: (value: T) => void) {
const values = this.store.get(key); const values = this.store.get(key);
if (values) { if (values) {
values.forEach((value) => iteratorFct(value)); values.forEach(value => iteratorFct(value));
} }
} }

View File

@@ -111,7 +111,6 @@ export class Features {
public static readonly hostedDataExplorer = "hosteddataexplorerenabled"; public static readonly hostedDataExplorer = "hosteddataexplorerenabled";
public static readonly enableTtl = "enablettl"; public static readonly enableTtl = "enablettl";
public static readonly enableNotebooks = "enablenotebooks"; public static readonly enableNotebooks = "enablenotebooks";
public static readonly enableGallery = "enablegallery";
public static readonly enableGalleryPublish = "enablegallerypublish"; public static readonly enableGalleryPublish = "enablegallerypublish";
public static readonly enableSpark = "enablespark"; public static readonly enableSpark = "enablespark";
public static readonly livyEndpoint = "livyendpoint"; public static readonly livyEndpoint = "livyendpoint";
@@ -140,7 +139,7 @@ export class Spark {
"Cosmos.Spark.D8s": "D8s / 8 cores / 32GB RAM", "Cosmos.Spark.D8s": "D8s / 8 cores / 32GB RAM",
"Cosmos.Spark.D16s": "D16s / 16 cores / 64GB RAM", "Cosmos.Spark.D16s": "D16s / 16 cores / 64GB RAM",
"Cosmos.Spark.D32s": "D32s / 32 cores / 128GB RAM", "Cosmos.Spark.D32s": "D32s / 32 cores / 128GB RAM",
"Cosmos.Spark.D64s": "D64s / 64 cores / 256GB RAM", "Cosmos.Spark.D64s": "D64s / 64 cores / 256GB RAM"
}); });
} }
@@ -155,7 +154,7 @@ export class MongoDBAccounts {
export enum MongoBackendEndpointType { export enum MongoBackendEndpointType {
local, local,
remote, remote
} }
export class MongoBackend { export class MongoBackend {
@@ -180,14 +179,14 @@ export class MongoBackend {
koreasouth: MongoBackend.southEastAsiaEndpoint, koreasouth: MongoBackend.southEastAsiaEndpoint,
southeastasia: MongoBackend.southEastAsiaEndpoint, southeastasia: MongoBackend.southEastAsiaEndpoint,
southindia: MongoBackend.southEastAsiaEndpoint, southindia: MongoBackend.southEastAsiaEndpoint,
westindia: MongoBackend.southEastAsiaEndpoint, westindia: MongoBackend.southEastAsiaEndpoint
}; };
public static endpointsByEnvironment: any = { public static endpointsByEnvironment: any = {
default: MongoBackendEndpointType.local, default: MongoBackendEndpointType.local,
localhost: MongoBackendEndpointType.local, localhost: MongoBackendEndpointType.local,
prod1: MongoBackendEndpointType.remote, prod1: MongoBackendEndpointType.remote,
prod2: MongoBackendEndpointType.remote, prod2: MongoBackendEndpointType.remote
}; };
} }
@@ -238,7 +237,7 @@ export class CassandraBackend {
chinaeast: CassandraBackend.mc_default, chinaeast: CassandraBackend.mc_default,
chinaeast2: CassandraBackend.mc_default, chinaeast2: CassandraBackend.mc_default,
chinanorth: CassandraBackend.mc_default, chinanorth: CassandraBackend.mc_default,
chinanorth2: CassandraBackend.mc_default, chinanorth2: CassandraBackend.mc_default
}; };
public static readonly createOrDeleteApi: string = "api/cassandra/createordelete"; public static readonly createOrDeleteApi: string = "api/cassandra/createordelete";
@@ -368,7 +367,7 @@ export class HttpStatusCodes {
HttpStatusCodes.InternalServerError, // TODO: Handle all 500s on Portal backend and remove from retries list HttpStatusCodes.InternalServerError, // TODO: Handle all 500s on Portal backend and remove from retries list
HttpStatusCodes.BadGateway, HttpStatusCodes.BadGateway,
HttpStatusCodes.ServiceUnavailable, HttpStatusCodes.ServiceUnavailable,
HttpStatusCodes.GatewayTimeout, HttpStatusCodes.GatewayTimeout
]; ];
} }
@@ -422,7 +421,10 @@ export class HashRoutePrefixes {
public static docsWithIds(databaseId: string, collectionId: string, docId: string) { public static docsWithIds(databaseId: string, collectionId: string, docId: string) {
const transformedDatabasePrefix: string = this.docs.replace("{db_id}", databaseId); const transformedDatabasePrefix: string = this.docs.replace("{db_id}", databaseId);
return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("{doc_id}", docId).replace("/", ""); // strip the first slash since hasher adds it return transformedDatabasePrefix
.replace("{coll_id}", collectionId)
.replace("{doc_id}", docId)
.replace("/", ""); // strip the first slash since hasher adds it
} }
} }
@@ -441,6 +443,17 @@ export class KeyCodes {
public static Tab: number = 9; public static Tab: number = 9;
} }
// Normalized per: https://www.w3.org/TR/uievents-key/#named-key-attribute-values
export class NormalizedEventKey {
public static readonly Space = " ";
public static readonly Enter = "Enter";
public static readonly Escape = "Escape";
public static readonly UpArrow = "ArrowUp";
public static readonly DownArrow = "ArrowDown";
public static readonly LeftArrow = "ArrowLeft";
public static readonly RightArrow = "ArrowRight";
}
export class TryCosmosExperience { export class TryCosmosExperience {
public static extendUrl: string = "https://trycosmosdb.azure.com/api/resource/extendportal?userId={0}"; public static extendUrl: string = "https://trycosmosdb.azure.com/api/resource/extendportal?userId={0}";
public static deleteUrl: string = "https://trycosmosdb.azure.com/api/resource/deleteportal?userId={0}"; public static deleteUrl: string = "https://trycosmosdb.azure.com/api/resource/deleteportal?userId={0}";
@@ -457,7 +470,7 @@ export class OfferVersions {
export enum ConflictOperationType { export enum ConflictOperationType {
Replace = "replace", Replace = "replace",
Create = "create", Create = "create",
Delete = "delete", Delete = "delete"
} }
export class AutoPilot { export class AutoPilot {
@@ -470,28 +483,28 @@ export class AutoPilot {
[AutopilotTier.Tier1]: "Tier 1", [AutopilotTier.Tier1]: "Tier 1",
[AutopilotTier.Tier2]: "Tier 2", [AutopilotTier.Tier2]: "Tier 2",
[AutopilotTier.Tier3]: "Tier 3", [AutopilotTier.Tier3]: "Tier 3",
[AutopilotTier.Tier4]: "Tier 4", [AutopilotTier.Tier4]: "Tier 4"
}; };
public static tierMaxRus = { public static tierMaxRus = {
[AutopilotTier.Tier1]: 2000, [AutopilotTier.Tier1]: 2000,
[AutopilotTier.Tier2]: 20000, [AutopilotTier.Tier2]: 20000,
[AutopilotTier.Tier3]: 100000, [AutopilotTier.Tier3]: 100000,
[AutopilotTier.Tier4]: 500000, [AutopilotTier.Tier4]: 500000
}; };
public static tierMinRus = { public static tierMinRus = {
[AutopilotTier.Tier1]: 0, [AutopilotTier.Tier1]: 0,
[AutopilotTier.Tier2]: 0, [AutopilotTier.Tier2]: 0,
[AutopilotTier.Tier3]: 0, [AutopilotTier.Tier3]: 0,
[AutopilotTier.Tier4]: 0, [AutopilotTier.Tier4]: 0
}; };
public static tierStorageInGB = { public static tierStorageInGB = {
[AutopilotTier.Tier1]: 50, [AutopilotTier.Tier1]: 50,
[AutopilotTier.Tier2]: 200, [AutopilotTier.Tier2]: 200,
[AutopilotTier.Tier3]: 1000, [AutopilotTier.Tier3]: 1000,
[AutopilotTier.Tier4]: 5000, [AutopilotTier.Tier4]: 5000
}; };
} }
@@ -505,7 +518,7 @@ export class DataExplorerFeatures {
} }
export const DataExplorerFeaturesVersions: any = { export const DataExplorerFeaturesVersions: any = {
OfferCache: DataExplorerVersions.v_1_0_1, OfferCache: DataExplorerVersions.v_1_0_1
}; };
export const EmulatorMasterKey = export const EmulatorMasterKey =

View File

@@ -9,7 +9,7 @@ describe("tokenProvider", () => {
resourceId: "", resourceId: "",
resourceType: "dbs" as ResourceType, resourceType: "dbs" as ResourceType,
headers: {}, headers: {},
getAuthorizationTokenUsingMasterKey: () => "", getAuthorizationTokenUsingMasterKey: () => ""
}; };
beforeEach(() => { beforeEach(() => {
@@ -17,7 +17,7 @@ describe("tokenProvider", () => {
window.fetch = jest.fn().mockImplementation(() => { window.fetch = jest.fn().mockImplementation(() => {
return { return {
json: () => "{}", json: () => "{}",
headers: new Map(), headers: new Map()
}; };
}); });
}); });
@@ -45,7 +45,7 @@ describe("getTokenFromAuthService", () => {
window.fetch = jest.fn().mockImplementation(() => { window.fetch = jest.fn().mockImplementation(() => {
return { return {
json: () => "{}", json: () => "{}",
headers: new Map(), headers: new Map()
}; };
}); });
}); });
@@ -86,8 +86,8 @@ describe("endpoint", () => {
documentEndpoint: "bar", documentEndpoint: "bar",
gremlinEndpoint: "foo", gremlinEndpoint: "foo",
tableEndpoint: "foo", tableEndpoint: "foo",
cassandraEndpoint: "foo", cassandraEndpoint: "foo"
}, }
}); });
expect(endpoint()).toEqual("bar"); expect(endpoint()).toEqual("bar");
}); });

View File

@@ -63,13 +63,13 @@ export async function getTokenFromAuthService(verb: string, resourceType: string
method: "POST", method: "POST",
headers: { headers: {
"content-type": "application/json", "content-type": "application/json",
"x-ms-encrypted-auth-token": _accessToken, "x-ms-encrypted-auth-token": _accessToken
}, },
body: JSON.stringify({ body: JSON.stringify({
verb, verb,
resourceType, resourceType,
resourceId, resourceId
}), })
}); });
//TODO I am not sure why we have to parse the JSON again here. fetch should do it for us when we call .json() //TODO I am not sure why we have to parse the JSON again here. fetch should do it for us when we call .json()
const result = JSON.parse(await response.json()); const result = JSON.parse(await response.json());
@@ -93,9 +93,9 @@ export const CosmosClient = {
key: _masterKey, key: _masterKey,
tokenProvider, tokenProvider,
connectionPolicy: { connectionPolicy: {
enableEndpointDiscovery: false, enableEndpointDiscovery: false
}, },
userAgentSuffix: "Azure Portal", userAgentSuffix: "Azure Portal"
}; };
// In development we proxy requests to the backend via webpack. This is removed in production bundles. // In development we proxy requests to the backend via webpack. This is removed in production bundles.
@@ -176,5 +176,5 @@ export const CosmosClient = {
_client = null; _client = null;
_resourceToken = value; _resourceToken = value;
return value; return value;
}, }
}; };

View File

@@ -14,7 +14,7 @@ import {
PartitionKeyDefinition, PartitionKeyDefinition,
QueryIterator, QueryIterator,
Resource, Resource,
TriggerDefinition, TriggerDefinition
} from "@azure/cosmos"; } from "@azure/cosmos";
import { ContainerRequest } from "@azure/cosmos/dist-esm/client/Container/ContainerRequest"; import { ContainerRequest } from "@azure/cosmos/dist-esm/client/Container/ContainerRequest";
import { CosmosClient } from "./CosmosClient"; import { CosmosClient } from "./CosmosClient";
@@ -68,7 +68,7 @@ export abstract class DataAccessUtilityBase {
.container(collection.id()) .container(collection.id())
.scripts.storedProcedures.readAll(options) .scripts.storedProcedures.readAll(options)
.fetchAll() .fetchAll()
.then((response) => response.resources as DataModels.StoredProcedure[]) .then(response => response.resources as DataModels.StoredProcedure[])
); );
} }
@@ -83,7 +83,7 @@ export abstract class DataAccessUtilityBase {
.container(collection.id()) .container(collection.id())
.scripts.storedProcedure(requestedResource.id) .scripts.storedProcedure(requestedResource.id)
.read(options) .read(options)
.then((response) => response.resource as DataModels.StoredProcedure) .then(response => response.resource as DataModels.StoredProcedure)
); );
} }
public readUserDefinedFunctions( public readUserDefinedFunctions(
@@ -96,7 +96,7 @@ export abstract class DataAccessUtilityBase {
.container(collection.id()) .container(collection.id())
.scripts.userDefinedFunctions.readAll(options) .scripts.userDefinedFunctions.readAll(options)
.fetchAll() .fetchAll()
.then((response) => response.resources as DataModels.UserDefinedFunction[]) .then(response => response.resources as DataModels.UserDefinedFunction[])
); );
} }
public readUserDefinedFunction( public readUserDefinedFunction(
@@ -110,7 +110,7 @@ export abstract class DataAccessUtilityBase {
.container(collection.id()) .container(collection.id())
.scripts.userDefinedFunction(requestedResource.id) .scripts.userDefinedFunction(requestedResource.id)
.read(options) .read(options)
.then((response) => response.resource as DataModels.UserDefinedFunction) .then(response => response.resource as DataModels.UserDefinedFunction)
); );
} }
@@ -121,7 +121,7 @@ export abstract class DataAccessUtilityBase {
.container(collection.id()) .container(collection.id())
.scripts.triggers.readAll(options) .scripts.triggers.readAll(options)
.fetchAll() .fetchAll()
.then((response) => response.resources as DataModels.Trigger[]) .then(response => response.resources as DataModels.Trigger[])
); );
} }
@@ -136,7 +136,7 @@ export abstract class DataAccessUtilityBase {
.container(collection.id()) .container(collection.id())
.scripts.trigger(requestedResource.id) .scripts.trigger(requestedResource.id)
.read(options) .read(options)
.then((response) => response.resource as DataModels.Trigger) .then(response => response.resource as DataModels.Trigger)
); );
} }
@@ -154,13 +154,13 @@ export abstract class DataAccessUtilityBase {
.container(collection.id()) .container(collection.id())
.scripts.storedProcedure(storedProcedure.id()) .scripts.storedProcedure(storedProcedure.id())
.execute(partitionKeyValue, params, { enableScriptLogging: true }) .execute(partitionKeyValue, params, { enableScriptLogging: true })
.then((response) => .then(response =>
deferred.resolve({ deferred.resolve({
result: response.resource, result: response.resource,
scriptLogs: response.headers[Constants.HttpHeaders.scriptLogResults], scriptLogs: response.headers[Constants.HttpHeaders.scriptLogResults]
}) })
) )
.catch((error) => deferred.reject(error)); .catch(error => deferred.reject(error));
return deferred.promise.timeout( return deferred.promise.timeout(
Constants.ClientDefaults.requestTimeoutMs, Constants.ClientDefaults.requestTimeoutMs,
@@ -177,7 +177,7 @@ export abstract class DataAccessUtilityBase {
.container(collection.id()) .container(collection.id())
.item(documentId.id(), partitionKey) .item(documentId.id(), partitionKey)
.read() .read()
.then((response) => response.resource) .then(response => response.resource)
); );
} }
@@ -230,7 +230,7 @@ export abstract class DataAccessUtilityBase {
.container(collection.id()) .container(collection.id())
.item(documentId.id(), partitionKey) .item(documentId.id(), partitionKey)
.replace(newDocument) .replace(newDocument)
.then((response) => response.resource) .then(response => response.resource)
); );
} }
@@ -243,7 +243,7 @@ export abstract class DataAccessUtilityBase {
CosmosClient.client() CosmosClient.client()
.offer(offer.id) .offer(offer.id)
.replace(newOffer, options) .replace(newOffer, options)
.then((response) => { .then(response => {
return Promise.all([this.refreshCachedOffers(), this.refreshCachedResources()]).then(() => response.resource); return Promise.all([this.refreshCachedOffers(), this.refreshCachedResources()]).then(() => response.resource);
}) })
); );
@@ -260,7 +260,7 @@ export abstract class DataAccessUtilityBase {
.container(collection.id()) .container(collection.id())
.scripts.storedProcedure(storedProcedure.id) .scripts.storedProcedure(storedProcedure.id)
.replace(storedProcedure, options) .replace(storedProcedure, options)
.then((response) => response.resource as DataModels.StoredProcedure) .then(response => response.resource as DataModels.StoredProcedure)
); );
} }
@@ -275,7 +275,7 @@ export abstract class DataAccessUtilityBase {
.container(collection.id()) .container(collection.id())
.scripts.userDefinedFunction(userDefinedFunction.id) .scripts.userDefinedFunction(userDefinedFunction.id)
.replace(userDefinedFunction, options) .replace(userDefinedFunction, options)
.then((response) => response.resource as DataModels.StoredProcedure) .then(response => response.resource as DataModels.StoredProcedure)
); );
} }
@@ -290,7 +290,7 @@ export abstract class DataAccessUtilityBase {
.container(collection.id()) .container(collection.id())
.scripts.trigger(trigger.id) .scripts.trigger(trigger.id)
.replace(trigger as TriggerDefinition, options) .replace(trigger as TriggerDefinition, options)
.then((response) => response.resource as DataModels.Trigger) .then(response => response.resource as DataModels.Trigger)
); );
} }
@@ -300,7 +300,7 @@ export abstract class DataAccessUtilityBase {
.database(collection.databaseId) .database(collection.databaseId)
.container(collection.id()) .container(collection.id())
.items.create(newDocument) .items.create(newDocument)
.then((response) => response.resource as DataModels.StoredProcedure) .then(response => response.resource as DataModels.StoredProcedure)
); );
} }
@@ -314,7 +314,7 @@ export abstract class DataAccessUtilityBase {
.database(collection.databaseId) .database(collection.databaseId)
.container(collection.id()) .container(collection.id())
.scripts.storedProcedures.create(newStoredProcedure, options) .scripts.storedProcedures.create(newStoredProcedure, options)
.then((response) => response.resource as DataModels.StoredProcedure) .then(response => response.resource as DataModels.StoredProcedure)
); );
} }
@@ -328,7 +328,7 @@ export abstract class DataAccessUtilityBase {
.database(collection.databaseId) .database(collection.databaseId)
.container(collection.id()) .container(collection.id())
.scripts.userDefinedFunctions.create(newUserDefinedFunction, options) .scripts.userDefinedFunctions.create(newUserDefinedFunction, options)
.then((response) => response.resource as DataModels.UserDefinedFunction) .then(response => response.resource as DataModels.UserDefinedFunction)
); );
} }
@@ -342,7 +342,7 @@ export abstract class DataAccessUtilityBase {
.database(collection.databaseId) .database(collection.databaseId)
.container(collection.id()) .container(collection.id())
.scripts.triggers.create(newTrigger as TriggerDefinition, options) .scripts.triggers.create(newTrigger as TriggerDefinition, options)
.then((response) => response.resource as DataModels.Trigger) .then(response => response.resource as DataModels.Trigger)
); );
} }
@@ -437,7 +437,7 @@ export abstract class DataAccessUtilityBase {
.database(database.id()) .database(database.id())
.containers.readAll() .containers.readAll()
.fetchAll() .fetchAll()
.then((response) => response.resources as DataModels.Collection[]) .then(response => response.resources as DataModels.Collection[])
); );
} }
@@ -447,7 +447,7 @@ export abstract class DataAccessUtilityBase {
.database(databaseId) .database(databaseId)
.container(collectionId) .container(collectionId)
.read() .read()
.then((response) => response.resource) .then(response => response.resource)
); );
} }
@@ -491,7 +491,7 @@ export abstract class DataAccessUtilityBase {
CosmosClient.client() CosmosClient.client()
.offers.readAll() .offers.readAll()
.fetchAll() .fetchAll()
.then((response) => response.resources) .then(response => response.resources)
); );
} }
@@ -506,7 +506,7 @@ export abstract class DataAccessUtilityBase {
CosmosClient.client() CosmosClient.client()
.offer(requestedResource.id) .offer(requestedResource.id)
.read(options) .read(options)
.then((response) => ({ ...response.resource, headers: response.headers })) .then(response => ({ ...response.resource, headers: response.headers }))
); );
} }
@@ -515,7 +515,7 @@ export abstract class DataAccessUtilityBase {
CosmosClient.client() CosmosClient.client()
.databases.readAll() .databases.readAll()
.fetchAll() .fetchAll()
.then((response) => response.resources as DataModels.Database[]) .then(response => response.resources as DataModels.Database[])
); );
} }
@@ -533,11 +533,11 @@ export abstract class DataAccessUtilityBase {
uniqueKeyPolicy, uniqueKeyPolicy,
offerThroughput, offerThroughput,
analyticalStorageTtl, analyticalStorageTtl,
hasAutoPilotV2FeatureFlag, hasAutoPilotV2FeatureFlag
} = request; } = request;
const createBody: DatabaseRequest = { const createBody: DatabaseRequest = {
id: databaseId, id: databaseId
}; };
// TODO: replace when SDK support autopilot // TODO: replace when SDK support autopilot
@@ -545,11 +545,11 @@ export abstract class DataAccessUtilityBase {
? !hasAutoPilotV2FeatureFlag ? !hasAutoPilotV2FeatureFlag
? { ? {
[Constants.HttpHeaders.autoPilotThroughputSDK]: JSON.stringify({ [Constants.HttpHeaders.autoPilotThroughputSDK]: JSON.stringify({
maxThroughput: request.autoPilot.maxThroughput, maxThroughput: request.autoPilot.maxThroughput
}), })
} }
: { : {
[Constants.HttpHeaders.autoPilotTier]: request.autoPilot.autopilotTier, [Constants.HttpHeaders.autoPilotTier]: request.autoPilot.autopilotTier
} }
: undefined; : undefined;
if (databaseLevelThroughput) { if (databaseLevelThroughput) {
@@ -562,7 +562,7 @@ export abstract class DataAccessUtilityBase {
return Q( return Q(
CosmosClient.client() CosmosClient.client()
.databases.createIfNotExists(createBody, databaseOptions) .databases.createIfNotExists(createBody, databaseOptions)
.then((response) => { .then(response => {
return response.database.containers.create( return response.database.containers.create(
{ {
id: collectionId, id: collectionId,
@@ -570,14 +570,14 @@ export abstract class DataAccessUtilityBase {
indexingPolicy: indexingPolicy ? indexingPolicy : undefined, indexingPolicy: indexingPolicy ? indexingPolicy : undefined,
uniqueKeyPolicy: uniqueKeyPolicy ? uniqueKeyPolicy : undefined, uniqueKeyPolicy: uniqueKeyPolicy ? uniqueKeyPolicy : undefined,
analyticalStorageTtl: analyticalStorageTtl, analyticalStorageTtl: analyticalStorageTtl,
throughput: databaseLevelThroughput || request.autoPilot ? undefined : offerThroughput, throughput: databaseLevelThroughput || request.autoPilot ? undefined : offerThroughput
} as ContainerRequest, // TODO: remove cast when https://github.com/Azure/azure-cosmos-js/issues/423 is fixed } as ContainerRequest, // TODO: remove cast when https://github.com/Azure/azure-cosmos-js/issues/423 is fixed
{ {
initialHeaders: databaseLevelThroughput ? undefined : initialHeaders, initialHeaders: databaseLevelThroughput ? undefined : initialHeaders
} }
); );
}) })
.then((containerResponse) => containerResponse.resource) .then(containerResponse => containerResponse.resource)
.finally(() => this.refreshCachedResources(options)) .finally(() => this.refreshCachedResources(options))
); );
} }
@@ -591,7 +591,7 @@ export abstract class DataAccessUtilityBase {
deferred.resolve(createdDatabase); deferred.resolve(createdDatabase);
}); });
}, },
(_createDatabaseError) => { _createDatabaseError => {
deferred.reject(_createDatabaseError); deferred.reject(_createDatabaseError);
} }
); );
@@ -654,10 +654,10 @@ export abstract class DataAccessUtilityBase {
const initialHeaders = autoPilot const initialHeaders = autoPilot
? !hasAutoPilotV2FeatureFlag ? !hasAutoPilotV2FeatureFlag
? { ? {
[Constants.HttpHeaders.autoPilotThroughputSDK]: JSON.stringify({ maxThroughput: autoPilot.maxThroughput }), [Constants.HttpHeaders.autoPilotThroughputSDK]: JSON.stringify({ maxThroughput: autoPilot.maxThroughput })
} }
: { : {
[Constants.HttpHeaders.autoPilotTier]: autoPilot.autopilotTier, [Constants.HttpHeaders.autoPilotTier]: autoPilot.autopilotTier
} }
: undefined; : undefined;
if (!!databaseLevelThroughput) { if (!!databaseLevelThroughput) {

View File

@@ -376,7 +376,7 @@ export default class DocumentClientUtilityBase {
JSON.stringify({ JSON.stringify({
oldOffer: offer, oldOffer: offer,
newOffer: newOffer, newOffer: newOffer,
error: error, error: error
}), }),
"UpdateOffer", "UpdateOffer",
error.code error.code
@@ -583,7 +583,7 @@ export default class DocumentClientUtilityBase {
); );
deferred.resolve(createdStoredProcedure); deferred.resolve(createdStoredProcedure);
}, },
(error) => { error => {
NotificationConsoleUtils.logConsoleMessage( NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error, ConsoleDataType.Error,
`Error while creating stored procedure for container ${collection.id()}:\n ${JSON.stringify(error)}` `Error while creating stored procedure for container ${collection.id()}:\n ${JSON.stringify(error)}`
@@ -620,7 +620,7 @@ export default class DocumentClientUtilityBase {
); );
deferred.resolve(createdUserDefinedFunction); deferred.resolve(createdUserDefinedFunction);
}, },
(error) => { error => {
NotificationConsoleUtils.logConsoleMessage( NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error, ConsoleDataType.Error,
`Error while creating user defined function for container ${collection.id()}:\n ${JSON.stringify(error)}` `Error while creating user defined function for container ${collection.id()}:\n ${JSON.stringify(error)}`
@@ -1176,7 +1176,7 @@ export default class DocumentClientUtilityBase {
} }
MessageHandler.sendMessage({ MessageHandler.sendMessage({
type: MessageTypes.ForbiddenError, type: MessageTypes.ForbiddenError,
reason: error && error.message ? error.message : error, reason: error && error.message ? error.message : error
}); });
} }
} }

View File

@@ -73,7 +73,7 @@ export default class EditableUtility {
return false; return false;
}); });
observable.subscribe((edit) => { observable.subscribe(edit => {
var edits = observable.edits && observable.edits(); var edits = observable.edits && observable.edits();
if (!edits) { if (!edits) {
return; return;
@@ -83,9 +83,9 @@ export default class EditableUtility {
}); });
observable.editableIsValid = ko.observable<boolean>(true); observable.editableIsValid = ko.observable<boolean>(true);
observable.subscribe((value) => { observable.subscribe(value => {
const validations: ((value: T) => boolean)[] = (observable.validations && observable.validations()) || []; const validations: ((value: T) => boolean)[] = (observable.validations && observable.validations()) || [];
const isValid = validations.every((validate) => validate(value)); const isValid = validations.every(validate => validate(value));
observable.editableIsValid(isValid); observable.editableIsValid(isValid);
}); });

View File

@@ -11,7 +11,7 @@ describe("Error Parser Utility", () => {
err.code = 400; err.code = 400;
err.body = { err.body = {
code: "BadRequest", code: "BadRequest",
message, message
}; };
err.headers = {}; err.headers = {};
err.activityId = "97b2e684-7505-4921-85f6-2513b9b28220"; err.activityId = "97b2e684-7505-4921-85f6-2513b9b28220";

View File

@@ -26,7 +26,7 @@ function _parse(err: any): DataModels.ErrorDataModel[] {
normalizedErrors.push(err); normalizedErrors.push(err);
} else { } else {
const innerErrors: any[] = _getInnerErrors(err.message); const innerErrors: any[] = _getInnerErrors(err.message);
normalizedErrors = innerErrors.map((innerError) => normalizedErrors = innerErrors.map(innerError =>
typeof innerError === "string" ? { message: innerError } : innerError typeof innerError === "string" ? { message: innerError } : innerError
); );
} }

View File

@@ -11,8 +11,8 @@ describe("nextPage", () => {
queryMetrics: {}, queryMetrics: {},
requestCharge: 1, requestCharge: 1,
headers: {}, headers: {},
activityId: "foo", activityId: "foo"
}), })
}; };
expect(await nextPage(fakeIterator, 10)).toMatchSnapshot(); expect(await nextPage(fakeIterator, 10)).toMatchSnapshot();

View File

@@ -14,7 +14,7 @@ export interface MinimalQueryIterator {
// Pick<QueryIterator<any>, "fetchNext">; // Pick<QueryIterator<any>, "fetchNext">;
export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise<QueryResults> { export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise<QueryResults> {
return documentsIterator.fetchNext().then((response) => { return documentsIterator.fetchNext().then(response => {
const documents = response.resources; const documents = response.resources;
const headers = (response as any).headers || {}; // TODO this is a private key. Remove any const headers = (response as any).headers || {}; // TODO this is a private key. Remove any
const itemCount = (documents && documents.length) || 0; const itemCount = (documents && documents.length) || 0;
@@ -26,7 +26,7 @@ export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex
lastItemIndex: Number(firstItemIndex) + Number(itemCount), lastItemIndex: Number(firstItemIndex) + Number(itemCount),
headers, headers,
activityId: response.activityId, activityId: response.activityId,
requestCharge: response.requestCharge, requestCharge: response.requestCharge
}; };
}); });
} }

View File

@@ -35,7 +35,7 @@ export function logError(message: string | Error, area: string, code?: number):
function _logEntry(entry: Diagnostics.LogEntry): void { function _logEntry(entry: Diagnostics.LogEntry): void {
MessageHandler.sendMessage({ MessageHandler.sendMessage({
type: MessageTypes.LogInfo, type: MessageTypes.LogInfo,
data: JSON.stringify(entry), data: JSON.stringify(entry)
}); });
const severityLevel = ((level: Diagnostics.LogEntryLevel): SeverityLevel => { const severityLevel = ((level: Diagnostics.LogEntryLevel): SeverityLevel => {
@@ -59,13 +59,13 @@ function _generateLogEntry(
level: Diagnostics.LogEntryLevel, level: Diagnostics.LogEntryLevel,
message: string, message: string,
area: string, area: string,
code: number code?: number
): Diagnostics.LogEntry { ): Diagnostics.LogEntry {
return { return {
timestamp: new Date().getUTCSeconds(), timestamp: new Date().getUTCSeconds(),
level: level, level,
message: message, message,
area: area, area,
code: code, code
}; };
} }

View File

@@ -41,7 +41,7 @@ describe("Message Handler", () => {
let mockPromise: CachedDataPromise<any> = { let mockPromise: CachedDataPromise<any> = {
id: "123", id: "123",
startTime: new Date(), startTime: new Date(),
deferred: Q.defer<any>(), deferred: Q.defer<any>()
}; };
let mockMessage = { message: { id: "123", data: "{}" } }; let mockMessage = { message: { id: "123", data: "{}" } };
@@ -54,7 +54,7 @@ describe("Message Handler", () => {
let mockPromise: CachedDataPromise<any> = { let mockPromise: CachedDataPromise<any> = {
id: "123", id: "123",
startTime: new Date(), startTime: new Date(),
deferred: Q.defer<any>(), deferred: Q.defer<any>()
}; };
MockMessageHandler.addToMap(mockPromise.id, mockPromise); MockMessageHandler.addToMap(mockPromise.id, mockPromise);

View File

@@ -46,7 +46,7 @@ export class MessageHandler {
let cachedDataPromise: CachedDataPromise<TResponseDataModel> = { let cachedDataPromise: CachedDataPromise<TResponseDataModel> = {
deferred: Q.defer<TResponseDataModel>(), deferred: Q.defer<TResponseDataModel>(),
startTime: new Date(), startTime: new Date(),
id: _.uniqueId(), id: _.uniqueId()
}; };
MessageHandler.RequestMap[cachedDataPromise.id] = cachedDataPromise; MessageHandler.RequestMap[cachedDataPromise.id] = cachedDataPromise;
MessageHandler.sendMessage({ type: messageType, params: params, id: cachedDataPromise.id }); MessageHandler.sendMessage({ type: messageType, params: params, id: cachedDataPromise.id });
@@ -63,7 +63,7 @@ export class MessageHandler {
window.parent.postMessage( window.parent.postMessage(
{ {
signature: "pcIframe", signature: "pcIframe",
data: data, data: data
}, },
window.document.referrer window.document.referrer
); );

View File

@@ -4,7 +4,7 @@ import {
getEndpoint, getEndpoint,
queryDocuments, queryDocuments,
readDocument, readDocument,
updateDocument, updateDocument
} from "./MongoProxyClient"; } from "./MongoProxyClient";
import { AuthType } from "../AuthType"; import { AuthType } from "../AuthType";
import { Collection, DatabaseAccount, DocumentId } from "../Contracts/ViewModels"; import { Collection, DatabaseAccount, DocumentId } from "../Contracts/ViewModels";
@@ -20,7 +20,7 @@ const fetchMock = () => {
ok: true, ok: true,
text: () => "{}", text: () => "{}",
json: () => "{}", json: () => "{}",
headers: new Map(), headers: new Map()
}); });
}; };
@@ -33,8 +33,8 @@ const collection = {
partitionKey: { partitionKey: {
paths: ["/pk"], paths: ["/pk"],
kind: "Hash", kind: "Hash",
version: 1, version: 1
}, }
} as Collection; } as Collection;
const documentId = ({ const documentId = ({
@@ -44,8 +44,8 @@ const documentId = ({
partitionKey: { partitionKey: {
paths: ["/pk"], paths: ["/pk"],
kind: "Hash", kind: "Hash",
version: 1, version: 1
}, }
} as unknown) as DocumentId; } as unknown) as DocumentId;
const databaseAccount = { const databaseAccount = {
@@ -58,8 +58,8 @@ const databaseAccount = {
documentEndpoint: "bar", documentEndpoint: "bar",
gremlinEndpoint: "foo", gremlinEndpoint: "foo",
tableEndpoint: "foo", tableEndpoint: "foo",
cassandraEndpoint: "foo", cassandraEndpoint: "foo"
}, }
}; };
describe("MongoProxyClient", () => { describe("MongoProxyClient", () => {
@@ -69,7 +69,7 @@ describe("MongoProxyClient", () => {
CosmosClient.databaseAccount(databaseAccount as any); CosmosClient.databaseAccount(databaseAccount as any);
window.dataExplorer = { window.dataExplorer = {
extensionEndpoint: () => "https://main.documentdb.ext.azure.com", extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
serverId: () => "", serverId: () => ""
} as any; } as any;
window.fetch = jest.fn().mockImplementation(fetchMock); window.fetch = jest.fn().mockImplementation(fetchMock);
}); });
@@ -100,7 +100,7 @@ describe("MongoProxyClient", () => {
CosmosClient.databaseAccount(databaseAccount as any); CosmosClient.databaseAccount(databaseAccount as any);
window.dataExplorer = { window.dataExplorer = {
extensionEndpoint: () => "https://main.documentdb.ext.azure.com", extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
serverId: () => "", serverId: () => ""
} as any; } as any;
window.fetch = jest.fn().mockImplementation(fetchMock); window.fetch = jest.fn().mockImplementation(fetchMock);
}); });
@@ -131,7 +131,7 @@ describe("MongoProxyClient", () => {
CosmosClient.databaseAccount(databaseAccount as any); CosmosClient.databaseAccount(databaseAccount as any);
window.dataExplorer = { window.dataExplorer = {
extensionEndpoint: () => "https://main.documentdb.ext.azure.com", extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
serverId: () => "", serverId: () => ""
} as any; } as any;
window.fetch = jest.fn().mockImplementation(fetchMock); window.fetch = jest.fn().mockImplementation(fetchMock);
}); });
@@ -162,7 +162,7 @@ describe("MongoProxyClient", () => {
CosmosClient.databaseAccount(databaseAccount as any); CosmosClient.databaseAccount(databaseAccount as any);
window.dataExplorer = { window.dataExplorer = {
extensionEndpoint: () => "https://main.documentdb.ext.azure.com", extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
serverId: () => "", serverId: () => ""
} as any; } as any;
window.fetch = jest.fn().mockImplementation(fetchMock); window.fetch = jest.fn().mockImplementation(fetchMock);
}); });
@@ -193,7 +193,7 @@ describe("MongoProxyClient", () => {
CosmosClient.databaseAccount(databaseAccount as any); CosmosClient.databaseAccount(databaseAccount as any);
window.dataExplorer = { window.dataExplorer = {
extensionEndpoint: () => "https://main.documentdb.ext.azure.com", extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
serverId: () => "", serverId: () => ""
} as any; } as any;
window.fetch = jest.fn().mockImplementation(fetchMock); window.fetch = jest.fn().mockImplementation(fetchMock);
}); });
@@ -225,7 +225,7 @@ describe("MongoProxyClient", () => {
CosmosClient.databaseAccount(databaseAccount as any); CosmosClient.databaseAccount(databaseAccount as any);
window.dataExplorer = { window.dataExplorer = {
extensionEndpoint: () => "https://main.documentdb.ext.azure.com", extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
serverId: () => "", serverId: () => ""
} as any; } as any;
}); });
@@ -259,7 +259,7 @@ describe("MongoProxyClient", () => {
sid: "a2", sid: "a2",
rg: "c1", rg: "c1",
dba: "main", dba: "main",
is: false, is: false
}; };
_createMongoCollectionWithARM("management.azure.com", properties, { "x-ms-cosmos-offer-autopilot-tier": "1" }); _createMongoCollectionWithARM("management.azure.com", properties, { "x-ms-cosmos-offer-autopilot-tier": "1" });
expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith( expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith(
@@ -268,8 +268,8 @@ describe("MongoProxyClient", () => {
{ {
properties: { properties: {
options: { "x-ms-cosmos-offer-autopilot-tier": "1" }, options: { "x-ms-cosmos-offer-autopilot-tier": "1" },
resource: { id: "abc-collection" }, resource: { id: "abc-collection" }
}, }
} }
); );
}); });
@@ -285,7 +285,7 @@ describe("MongoProxyClient", () => {
rg: "c1", rg: "c1",
dba: "main", dba: "main",
is: false, is: false,
offerThroughput: 400, offerThroughput: 400
}; };
_createMongoCollectionWithARM("management.azure.com", properties, undefined); _createMongoCollectionWithARM("management.azure.com", properties, undefined);
expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith( expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith(
@@ -294,8 +294,8 @@ describe("MongoProxyClient", () => {
{ {
properties: { properties: {
options: { throughput: "400" }, options: { throughput: "400" },
resource: { id: "abc-collection" }, resource: { id: "abc-collection" }
}, }
} }
); );
}); });

View File

@@ -20,7 +20,7 @@ import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClie
const defaultHeaders = { const defaultHeaders = {
[HttpHeaders.apiType]: ApiType.MongoDB.toString(), [HttpHeaders.apiType]: ApiType.MongoDB.toString(),
[CosmosSDKConstants.HttpHeaders.MaxEntityCount]: "100", [CosmosSDKConstants.HttpHeaders.MaxEntityCount]: "100",
[CosmosSDKConstants.HttpHeaders.Version]: "2017-11-15", [CosmosSDKConstants.HttpHeaders.Version]: "2017-11-15"
}; };
function authHeaders(): any { function authHeaders(): any {
@@ -35,7 +35,7 @@ export function queryIterator(databaseId: string, collection: Collection, query:
let continuationToken: string; let continuationToken: string;
return { return {
fetchNext: () => { fetchNext: () => {
return queryDocuments(databaseId, collection, false, query).then((response) => { return queryDocuments(databaseId, collection, false, query).then(response => {
continuationToken = response.continuationToken; continuationToken = response.continuationToken;
const headers = {} as any; const headers = {} as any;
response.headers.forEach((value: any, key: any) => { response.headers.forEach((value: any, key: any) => {
@@ -46,10 +46,10 @@ export function queryIterator(databaseId: string, collection: Collection, query:
headers, headers,
requestCharge: headers[CosmosSDKConstants.HttpHeaders.RequestCharge], requestCharge: headers[CosmosSDKConstants.HttpHeaders.RequestCharge],
activityId: headers[CosmosSDKConstants.HttpHeaders.ActivityId], activityId: headers[CosmosSDKConstants.HttpHeaders.ActivityId],
hasMoreResults: !!continuationToken, hasMoreResults: !!continuationToken
}; };
}); });
}, }
}; };
} }
@@ -78,9 +78,7 @@ export function queryDocuments(
rg: CosmosClient.resourceGroup(), rg: CosmosClient.resourceGroup(),
dba: databaseAccount.name, dba: databaseAccount.name,
pk: pk:
collection && collection.partitionKey && !collection.partitionKey.systemKey collection && collection.partitionKey && !collection.partitionKey.systemKey ? collection.partitionKeyProperty : ""
? collection.partitionKeyProperty
: "",
}; };
const endpoint = getEndpoint(databaseAccount) || ""; const endpoint = getEndpoint(databaseAccount) || "";
@@ -93,7 +91,7 @@ export function queryDocuments(
[CosmosSDKConstants.HttpHeaders.EnableScanInQuery]: "true", [CosmosSDKConstants.HttpHeaders.EnableScanInQuery]: "true",
[CosmosSDKConstants.HttpHeaders.EnableCrossPartitionQuery]: "true", [CosmosSDKConstants.HttpHeaders.EnableCrossPartitionQuery]: "true",
[CosmosSDKConstants.HttpHeaders.ParallelizeCrossPartitionQuery]: "true", [CosmosSDKConstants.HttpHeaders.ParallelizeCrossPartitionQuery]: "true",
[HttpHeaders.contentType]: "application/query+json", [HttpHeaders.contentType]: "application/query+json"
}; };
if (continuationToken) { if (continuationToken) {
@@ -106,14 +104,14 @@ export function queryDocuments(
.fetch(`${endpoint}${path}?${queryString.stringify(params)}`, { .fetch(`${endpoint}${path}?${queryString.stringify(params)}`, {
method: "POST", method: "POST",
body: JSON.stringify({ query }), body: JSON.stringify({ query }),
headers, headers
}) })
.then(async (response) => { .then(async response => {
if (response.ok) { if (response.ok) {
return { return {
continuationToken: response.headers.get(CosmosSDKConstants.HttpHeaders.Continuation), continuationToken: response.headers.get(CosmosSDKConstants.HttpHeaders.Continuation),
documents: (await response.json()).Documents as DataModels.DocumentId[], documents: (await response.json()).Documents as DataModels.DocumentId[],
headers: response.headers, headers: response.headers
}; };
} }
return errorHandling(response, "querying documents", params); return errorHandling(response, "querying documents", params);
@@ -140,9 +138,7 @@ export function readDocument(
rg: CosmosClient.resourceGroup(), rg: CosmosClient.resourceGroup(),
dba: databaseAccount.name, dba: databaseAccount.name,
pk: pk:
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
? documentId.partitionKeyProperty
: "",
}; };
const endpoint = getEndpoint(databaseAccount); const endpoint = getEndpoint(databaseAccount);
@@ -154,10 +150,10 @@ export function readDocument(
...authHeaders(), ...authHeaders(),
[CosmosSDKConstants.HttpHeaders.PartitionKey]: encodeURIComponent( [CosmosSDKConstants.HttpHeaders.PartitionKey]: encodeURIComponent(
JSON.stringify(documentId.partitionKeyHeader()) JSON.stringify(documentId.partitionKeyHeader())
), )
}, }
}) })
.then((response) => { .then(response => {
if (response.ok) { if (response.ok) {
return response.json(); return response.json();
} }
@@ -182,7 +178,7 @@ export function createDocument(
sid: CosmosClient.subscriptionId(), sid: CosmosClient.subscriptionId(),
rg: CosmosClient.resourceGroup(), rg: CosmosClient.resourceGroup(),
dba: databaseAccount.name, dba: databaseAccount.name,
pk: collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : "", pk: collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : ""
}; };
const endpoint = getEndpoint(databaseAccount); const endpoint = getEndpoint(databaseAccount);
@@ -193,10 +189,10 @@ export function createDocument(
body: JSON.stringify(documentContent), body: JSON.stringify(documentContent),
headers: { headers: {
...defaultHeaders, ...defaultHeaders,
...authHeaders(), ...authHeaders()
}, }
}) })
.then((response) => { .then(response => {
if (response.ok) { if (response.ok) {
return response.json(); return response.json();
} }
@@ -225,9 +221,7 @@ export function updateDocument(
rg: CosmosClient.resourceGroup(), rg: CosmosClient.resourceGroup(),
dba: databaseAccount.name, dba: databaseAccount.name,
pk: pk:
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
? documentId.partitionKeyProperty
: "",
}; };
const endpoint = getEndpoint(databaseAccount); const endpoint = getEndpoint(databaseAccount);
@@ -239,10 +233,10 @@ export function updateDocument(
...defaultHeaders, ...defaultHeaders,
...authHeaders(), ...authHeaders(),
[HttpHeaders.contentType]: "application/json", [HttpHeaders.contentType]: "application/json",
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()), [CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader())
}, }
}) })
.then((response) => { .then(response => {
if (response.ok) { if (response.ok) {
return response.json(); return response.json();
} }
@@ -270,9 +264,7 @@ export function deleteDocument(
rg: CosmosClient.resourceGroup(), rg: CosmosClient.resourceGroup(),
dba: databaseAccount.name, dba: databaseAccount.name,
pk: pk:
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
? documentId.partitionKeyProperty
: "",
}; };
const endpoint = getEndpoint(databaseAccount); const endpoint = getEndpoint(databaseAccount);
@@ -283,10 +275,10 @@ export function deleteDocument(
...defaultHeaders, ...defaultHeaders,
...authHeaders(), ...authHeaders(),
[HttpHeaders.contentType]: "application/json", [HttpHeaders.contentType]: "application/json",
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()), [CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader())
}, }
}) })
.then((response) => { .then(response => {
if (response.ok) { if (response.ok) {
return undefined; return undefined;
} }
@@ -319,7 +311,7 @@ export function createMongoCollectionWithProxy(
sid: CosmosClient.subscriptionId(), sid: CosmosClient.subscriptionId(),
rg: CosmosClient.resourceGroup(), rg: CosmosClient.resourceGroup(),
dba: databaseAccount.name, dba: databaseAccount.name,
isAutoPilot: false, isAutoPilot: false
}; };
if (autopilotOptions) { if (autopilotOptions) {
@@ -337,11 +329,11 @@ export function createMongoCollectionWithProxy(
headers: { headers: {
...defaultHeaders, ...defaultHeaders,
...authHeaders(), ...authHeaders(),
[HttpHeaders.contentType]: "application/json", [HttpHeaders.contentType]: "application/json"
}, }
} }
) )
.then((response) => { .then(response => {
if (response.ok) { if (response.ok) {
return undefined; return undefined;
} }
@@ -376,7 +368,7 @@ export function createMongoCollectionWithARM(
sid: CosmosClient.subscriptionId(), sid: CosmosClient.subscriptionId(),
rg: CosmosClient.resourceGroup(), rg: CosmosClient.resourceGroup(),
dba: databaseAccount.name, dba: databaseAccount.name,
analyticalStorageTtl, analyticalStorageTtl
}; };
if (createDatabase) { if (createDatabase) {
@@ -432,10 +424,10 @@ export async function _createMongoCollectionWithARM(
const rpPayloadToCreateCollection: DataModels.MongoCreationRequest = { const rpPayloadToCreateCollection: DataModels.MongoCreationRequest = {
properties: { properties: {
resource: { resource: {
id: params.coll, id: params.coll
},
options: {},
}, },
options: {}
}
}; };
if (params.is) { if (params.is) {

View File

@@ -28,7 +28,7 @@ export class NotificationsClientBase implements ViewModels.NotificationsClient {
url: url, url: url,
type: "GET", type: "GET",
headers: headers, headers: headers,
cache: false, cache: false
}).then( }).then(
(notifications: DataModels.Notification[], textStatus: string, xhr: JQueryXHR<any>) => { (notifications: DataModels.Notification[], textStatus: string, xhr: JQueryXHR<any>) => {
deferred.resolve(notifications); deferred.resolve(notifications);

View File

@@ -15,7 +15,7 @@ export class QueriesClient implements ViewModels.QueriesClient {
private static readonly PartitionKey: DataModels.PartitionKey = { private static readonly PartitionKey: DataModels.PartitionKey = {
paths: [`/${SavedQueries.PartitionKeyProperty}`], paths: [`/${SavedQueries.PartitionKeyProperty}`],
kind: BackendDefaults.partitionKeyKind, kind: BackendDefaults.partitionKeyKind,
version: BackendDefaults.partitionKeyVersion, version: BackendDefaults.partitionKeyVersion
}; };
private static readonly FetchQuery: string = "SELECT * FROM c"; private static readonly FetchQuery: string = "SELECT * FROM c";
private static readonly FetchMongoQuery: string = "{}"; private static readonly FetchMongoQuery: string = "{}";
@@ -38,7 +38,7 @@ export class QueriesClient implements ViewModels.QueriesClient {
databaseId: SavedQueries.DatabaseName, databaseId: SavedQueries.DatabaseName,
partitionKey: QueriesClient.PartitionKey, partitionKey: QueriesClient.PartitionKey,
offerThroughput: SavedQueries.OfferThroughput, offerThroughput: SavedQueries.OfferThroughput,
databaseLevelThroughput: undefined, databaseLevelThroughput: undefined
}) })
.then( .then(
(collection: DataModels.Collection) => { (collection: DataModels.Collection) => {
@@ -152,7 +152,7 @@ export class QueriesClient implements ViewModels.QueriesClient {
resourceId: resourceId, resourceId: resourceId,
queryName: queryName, queryName: queryName,
query: query, query: query,
id: id, id: id
}; };
try { try {
this.validateQuery(parsedQuery); this.validateQuery(parsedQuery);
@@ -219,7 +219,7 @@ export class QueriesClient implements ViewModels.QueriesClient {
const documentId: ViewModels.DocumentId = new DocumentId( const documentId: ViewModels.DocumentId = new DocumentId(
{ {
partitionKey: QueriesClient.PartitionKey, partitionKey: QueriesClient.PartitionKey,
partitionKeyProperty: "id", partitionKeyProperty: "id"
} as ViewModels.DocumentsTab, } as ViewModels.DocumentsTab,
query, query,
query.queryName query.queryName

View File

@@ -4,7 +4,7 @@ import { SplitterMetrics } from "./Constants";
export enum SplitterDirection { export enum SplitterDirection {
Horizontal = "horizontal", Horizontal = "horizontal",
Vertical = "vertical", Vertical = "vertical"
} }
export interface SplitterBounds { export interface SplitterBounds {
@@ -50,7 +50,7 @@ export class Splitter {
animate: true, animate: true,
animateDuration: "fast", animateDuration: "fast",
start: this.onResizeStart, start: this.onResizeStart,
stop: this.onResizeStop, stop: this.onResizeStop
}; };
if (isVerticalSplitter) { if (isVerticalSplitter) {
@@ -90,7 +90,9 @@ export class Splitter {
this.lastWidth = $(this.leftSide).width(); this.lastWidth = $(this.leftSide).width();
$(this.splitter).css("left", SplitterMetrics.CollapsedPositionLeft); $(this.splitter).css("left", SplitterMetrics.CollapsedPositionLeft);
$(this.leftSide).css("width", ""); $(this.leftSide).css("width", "");
$(this.leftSide).resizable("option", "disabled", true).removeClass("ui-resizable-disabled"); // remove class so splitter is visible $(this.leftSide)
.resizable("option", "disabled", true)
.removeClass("ui-resizable-disabled"); // remove class so splitter is visible
$(this.splitter).removeClass("ui-resizable-e"); $(this.splitter).removeClass("ui-resizable-e");
this.isCollapsed(true); this.isCollapsed(true);
} }

View File

@@ -32,8 +32,8 @@ export default class UrlUtility {
type: type, type: type,
objectBody: { objectBody: {
id: id, id: id,
self: resourcePath, self: resourcePath
}, }
}; };
return result; return result;

View File

@@ -1,7 +1,7 @@
export enum Platform { export enum Platform {
Portal = "Portal", Portal = "Portal",
Hosted = "Hosted", Hosted = "Hosted",
Emulator = "Emulator", Emulator = "Emulator"
} }
interface Config { interface Config {
@@ -16,7 +16,6 @@ interface Config {
ARM_API_VERSION: string; ARM_API_VERSION: string;
GRAPH_ENDPOINT: string; GRAPH_ENDPOINT: string;
GRAPH_API_VERSION: string; GRAPH_API_VERSION: string;
AZURESAMPLESCOSMOSDBPAT: string;
ARCADIA_ENDPOINT: string; ARCADIA_ENDPOINT: string;
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: string; ARCADIA_LIVY_ENDPOINT_DNS_ZONE: string;
BACKEND_ENDPOINT?: string; BACKEND_ENDPOINT?: string;
@@ -44,8 +43,7 @@ let config: Config = {
ARCADIA_ENDPOINT: "https://workspaceartifacts.projectarcadia.net", ARCADIA_ENDPOINT: "https://workspaceartifacts.projectarcadia.net",
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net", ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/settings/applications/1189306 GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/settings/applications/1189306
JUNO_ENDPOINT: "https://tools.cosmos.azure.com", JUNO_ENDPOINT: "https://tools.cosmos.azure.com"
AZURESAMPLESCOSMOSDBPAT: "99e38770e29b4a61d7c49f188780504efd35cc86", //[SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification=" // this PAT is a "no scopes" PAT with zero access to any projects, this is just used to get around the dev.github.com rate limit when accessing public samples repo.")]
}; };
// Injected for local develpment. These will be removed in the production bundle by webpack // Injected for local develpment. These will be removed in the production bundle by webpack

View File

@@ -7,7 +7,7 @@ export enum TabKind {
TableEntities, TableEntities,
Graph, Graph,
SQLQuery, SQLQuery,
ScaleSettings, ScaleSettings
} }
/** /**
@@ -20,7 +20,7 @@ export enum PaneKind {
DeleteDatabase, DeleteDatabase,
GlobalSettings, GlobalSettings,
AdHocAccess, AdHocAccess,
SwitchDirectory, SwitchDirectory
} }
/** /**
@@ -79,5 +79,5 @@ export enum ActionType {
OpenCollectionTab, OpenCollectionTab,
OpenPane, OpenPane,
TransmitCachedData, TransmitCachedData,
OpenSampleNotebook, OpenSampleNotebook
} }

View File

@@ -56,7 +56,7 @@ export enum ApiKind {
Table, Table,
Cassandra, Cassandra,
Graph, Graph,
MongoDBCompute, MongoDBCompute
} }
export interface GenerateTokenResponse { export interface GenerateTokenResponse {
@@ -297,7 +297,7 @@ export enum AutopilotTier {
Tier1 = 1, Tier1 = 1,
Tier2 = 2, Tier2 = 2,
Tier3 = 3, Tier3 = 3,
Tier4 = 4, Tier4 = 4
} }
export interface RpOptions { export interface RpOptions {
@@ -363,7 +363,7 @@ export interface Notification {
export enum ConflictResolutionMode { export enum ConflictResolutionMode {
Custom = "Custom", Custom = "Custom",
LastWriterWins = "LastWriterWins", LastWriterWins = "LastWriterWins"
} }
/** /**
@@ -522,7 +522,7 @@ export interface SparkClusterEndpoint {
export enum SparkClusterEndpointKind { export enum SparkClusterEndpointKind {
SparkUI = "SparkUI", SparkUI = "SparkUI",
HistoryServerUI = "HistoryServerUI", HistoryServerUI = "HistoryServerUI",
Livy = "Livy", Livy = "Livy"
} }
export interface RpParameters { export interface RpParameters {

View File

@@ -21,7 +21,7 @@ export enum LogEntryLevel {
/** /**
* Error level. * Error level.
*/ */
Error = 2, Error = 2
} }
/** /**
* Schema of a log entry. * Schema of a log entry.
@@ -46,7 +46,7 @@ export interface LogEntry {
/** /**
* The message code. * The message code.
*/ */
code: number; code?: number;
/** /**
* Any additional data to be logged. * Any additional data to be logged.
*/ */

View File

@@ -32,7 +32,7 @@ export enum MessageTypes {
GetArcadiaToken, GetArcadiaToken,
CreateWorkspace, CreateWorkspace,
CreateSparkPool, CreateSparkPool,
RefreshDatabaseAccount, RefreshDatabaseAccount
} }
export { Versions, ActionContracts, Diagnostics }; export { Versions, ActionContracts, Diagnostics };

View File

@@ -83,7 +83,6 @@ export interface Explorer {
extensionEndpoint: ko.Observable<string>; extensionEndpoint: ko.Observable<string>;
armEndpoint: ko.Observable<string>; armEndpoint: ko.Observable<string>;
isFeatureEnabled: (feature: string) => boolean; isFeatureEnabled: (feature: string) => boolean;
isGalleryEnabled: ko.Computed<boolean>;
isGalleryPublishEnabled: ko.Computed<boolean>; isGalleryPublishEnabled: ko.Computed<boolean>;
isGitHubPaneEnabled: ko.Observable<boolean>; isGitHubPaneEnabled: ko.Observable<boolean>;
isPublishNotebookPaneEnabled: ko.Observable<boolean>; isPublishNotebookPaneEnabled: ko.Observable<boolean>;
@@ -230,7 +229,7 @@ export interface Explorer {
openNotebook(notebookContentItem: NotebookContentItem): Promise<boolean>; // True if it was opened, false otherwise openNotebook(notebookContentItem: NotebookContentItem): Promise<boolean>; // True if it was opened, false otherwise
resetNotebookWorkspace(): void; resetNotebookWorkspace(): void;
importAndOpen: (path: string) => Promise<boolean>; importAndOpen: (path: string) => Promise<boolean>;
importAndOpenFromGallery: (name: string, content: string) => Promise<boolean>; importAndOpenContent: (name: string, content: string) => Promise<boolean>;
publishNotebook: (name: string, content: string) => void; publishNotebook: (name: string, content: string) => void;
openNotebookTerminal: (kind: TerminalKind) => void; openNotebookTerminal: (kind: TerminalKind) => void;
openGallery: (notebookUrl?: string, galleryItem?: IGalleryItem, isFavorite?: boolean) => void; openGallery: (notebookUrl?: string, galleryItem?: IGalleryItem, isFavorite?: boolean) => void;
@@ -711,7 +710,7 @@ export interface UploadFilePane extends ContextualPane {
export enum NeighborType { export enum NeighborType {
SOURCES_ONLY, SOURCES_ONLY,
TARGETS_ONLY, TARGETS_ONLY,
BOTH, BOTH
} }
/** /**
@@ -1183,14 +1182,14 @@ export enum DocumentExplorerState {
newDocumentInvalid, newDocumentInvalid,
exisitingDocumentNoEdits, exisitingDocumentNoEdits,
exisitingDocumentDirtyValid, exisitingDocumentDirtyValid,
exisitingDocumentDirtyInvalid, exisitingDocumentDirtyInvalid
} }
export enum IndexingPolicyEditorState { export enum IndexingPolicyEditorState {
noCollectionSelected, noCollectionSelected,
noEdits, noEdits,
dirtyValid, dirtyValid,
dirtyInvalid, dirtyInvalid
} }
export enum ScriptEditorState { export enum ScriptEditorState {
@@ -1198,7 +1197,7 @@ export enum ScriptEditorState {
newValid, newValid,
exisitingNoEdits, exisitingNoEdits,
exisitingDirtyValid, exisitingDirtyValid,
exisitingDirtyInvalid, exisitingDirtyInvalid
} }
export enum CollectionTabKind { export enum CollectionTabKind {
@@ -1218,13 +1217,13 @@ export enum CollectionTabKind {
NotebookV2 = 15, NotebookV2 = 15,
SparkMasterTab = 16, SparkMasterTab = 16,
Gallery = 17, Gallery = 17,
NotebookViewer = 18, NotebookViewer = 18
} }
export enum TerminalKind { export enum TerminalKind {
Default = 0, Default = 0,
Mongo = 1, Mongo = 1,
Cassandra = 2, Cassandra = 2
} }
export interface DataExplorerInputsFrame { export interface DataExplorerInputsFrame {
@@ -1276,7 +1275,7 @@ export enum SubscriptionType {
EA, EA,
Free, Free,
Internal, Internal,
PAYG, PAYG
} }
export class MonacoEditorSettings { export class MonacoEditorSettings {

View File

@@ -6,19 +6,19 @@ describe("The Heatmap Control", () => {
const dataPoints = { const dataPoints = {
"1": { "1": {
"2019-06-19T00:59:10Z": { "2019-06-19T00:59:10Z": {
"Normalized Throughput": 0.35, "Normalized Throughput": 0.35
}, },
"2019-06-19T00:48:10Z": { "2019-06-19T00:48:10Z": {
"Normalized Throughput": 0.25, "Normalized Throughput": 0.25
}, }
}, }
}; };
const chartCaptions = { const chartCaptions = {
chartTitle: "chart title", chartTitle: "chart title",
yAxisTitle: "YAxisTitle", yAxisTitle: "YAxisTitle",
tooltipText: "Tooltip text", tooltipText: "Tooltip text",
timeWindow: 123456789, timeWindow: 123456789
}; };
let heatmap: Heatmap; let heatmap: Heatmap;
@@ -75,12 +75,12 @@ describe("The Heatmap Control", () => {
if (dayjs().utcOffset()) { if (dayjs().utcOffset()) {
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).not.toEqual([ expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).not.toEqual([
"2019-06-19T00:48:10Z", "2019-06-19T00:48:10Z",
"2019-06-19T00:59:10Z", "2019-06-19T00:59:10Z"
]); ]);
} else { } else {
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).toEqual([ expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).toEqual([
"2019-06-19T00:48:10Z", "2019-06-19T00:48:10Z",
"2019-06-19T00:59:10Z", "2019-06-19T00:59:10Z"
]); ]);
} }
}); });
@@ -106,9 +106,9 @@ describe("iframe rendering when there is no data", () => {
data: { data: {
chartData: {}, chartData: {},
chartSettings: {}, chartSettings: {},
theme: 4, theme: 4
}, }
}, }
}; };
const divElement: string = `<div id="${Heatmap.elementId}"></div>`; const divElement: string = `<div id="${Heatmap.elementId}"></div>`;
@@ -126,9 +126,9 @@ describe("iframe rendering when there is no data", () => {
data: { data: {
chartData: {}, chartData: {},
chartSettings: {}, chartSettings: {},
theme: 2, theme: 2
}, }
}, }
}; };
const divElement: string = `<div id="${Heatmap.elementId}"></div>`; const divElement: string = `<div id="${Heatmap.elementId}"></div>`;

View File

@@ -9,7 +9,7 @@ import {
HeatmapData, HeatmapData,
LayoutSettings, LayoutSettings,
PartitionTimeStampToData, PartitionTimeStampToData,
PortalTheme, PortalTheme
} from "./HeatmapDatatypes"; } from "./HeatmapDatatypes";
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation"; import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
import { MessageHandler } from "../../Common/MessageHandler"; import { MessageHandler } from "../../Common/MessageHandler";
@@ -43,7 +43,7 @@ export class Heatmap {
return { return {
family: StyleConstants.DataExplorerFont, family: StyleConstants.DataExplorerFont,
size, size,
color, color
}; };
} }
@@ -73,7 +73,7 @@ export class Heatmap {
return 0; return 0;
} }
} }
}), })
}; };
// go thru all rows and create 2d matrix for heatmap... // go thru all rows and create 2d matrix for heatmap...
for (let i = 0; i < rows.length; i++) { for (let i = 0; i < rows.length; i++) {
@@ -115,7 +115,7 @@ export class Heatmap {
[0.7, "#E46612"], [0.7, "#E46612"],
[0.8, "#E64914"], [0.8, "#E64914"],
[0.9, "#B80016"], [0.9, "#B80016"],
[1.0, "#B80016"], [1.0, "#B80016"]
], ],
name: "", name: "",
hovertemplate: this._heatmapCaptions.tooltipText, hovertemplate: this._heatmapCaptions.tooltipText,
@@ -123,11 +123,11 @@ export class Heatmap {
thickness: 15, thickness: 15,
outlinewidth: 0, outlinewidth: 0,
tickcolor: StyleConstants.BaseDark, tickcolor: StyleConstants.BaseDark,
tickfont: this._getFontStyles(10, this._defaultFontColor), tickfont: this._getFontStyles(10, this._defaultFontColor)
}, },
y: this._chartData.yAxisPoints, y: this._chartData.yAxisPoints,
x: this._chartData.xAxisPoints, x: this._chartData.xAxisPoints
}, }
]; ];
} }
@@ -138,7 +138,7 @@ export class Heatmap {
r: 10, r: 10,
b: 35, b: 35,
t: 30, t: 30,
pad: 0, pad: 0
}, },
paper_bgcolor: "transparent", paper_bgcolor: "transparent",
plot_bgcolor: "transparent", plot_bgcolor: "transparent",
@@ -154,7 +154,7 @@ export class Heatmap {
autotick: true, autotick: true,
fixedrange: true, fixedrange: true,
ticks: "", ticks: "",
showticklabels: false, showticklabels: false
}, },
xaxis: { xaxis: {
fixedrange: true, fixedrange: true,
@@ -167,13 +167,13 @@ export class Heatmap {
autotick: true, autotick: true,
tickformat: this._heatmapCaptions.timeWindow > 7 ? "%I:%M %p" : "%b %e", tickformat: this._heatmapCaptions.timeWindow > 7 ? "%I:%M %p" : "%b %e",
showticklabels: true, showticklabels: true,
tickfont: this._getFontStyles(10), tickfont: this._getFontStyles(10)
}, },
title: { title: {
text: this._heatmapCaptions.chartTitle, text: this._heatmapCaptions.chartTitle,
x: 0.01, x: 0.01,
font: this._getFontStyles(13, this._defaultFontColor), font: this._getFontStyles(13, this._defaultFontColor)
}, }
}; };
} }
@@ -181,7 +181,7 @@ export class Heatmap {
return { return {
/* heatmap can be fully responsive however the min-height needed in that case is greater than the iframe portal height, hence explicit width + height have been set in _getLayoutSettings /* heatmap can be fully responsive however the min-height needed in that case is greater than the iframe portal height, hence explicit width + height have been set in _getLayoutSettings
responsive: true,*/ responsive: true,*/
displayModeBar: false, displayModeBar: false
}; };
} }
@@ -198,7 +198,7 @@ export class Heatmap {
let timeSelected: string = data.points[0].x; let timeSelected: string = data.points[0].x;
timeSelected = timeSelected.replace(" ", "T"); timeSelected = timeSelected.replace(" ", "T");
timeSelected = `${timeSelected}Z`; timeSelected = `${timeSelected}Z`;
let xAxisIndex; let xAxisIndex = 0;
for (let i = 0; i < this._chartData.xAxisPoints.length; i++) { for (let i = 0; i < this._chartData.xAxisPoints.length; i++) {
if (this._chartData.xAxisPoints[i] === timeSelected) { if (this._chartData.xAxisPoints[i] === timeSelected) {
xAxisIndex = i; xAxisIndex = i;
@@ -234,7 +234,8 @@ export function handleMessage(event: MessageEvent) {
return; return;
} }
Plotly.purge(Heatmap.elementId); Plotly.purge(Heatmap.elementId);
document.getElementById(Heatmap.elementId).innerHTML = "";
document.getElementById(Heatmap.elementId)!.innerHTML = "";
const data = event.data.data; const data = event.data.data;
const chartData: DataPayload = data.chartData; const chartData: DataPayload = data.chartData;
const chartSettings: HeatmapCaptions = data.chartSettings; const chartSettings: HeatmapCaptions = data.chartSettings;
@@ -259,8 +260,8 @@ export function handleMessage(event: MessageEvent) {
noDataMessageContent.classList.add("dark-theme"); noDataMessageContent.classList.add("dark-theme");
} }
document.getElementById(Heatmap.elementId).appendChild(chartTitleElement); document.getElementById(Heatmap.elementId)!.appendChild(chartTitleElement);
document.getElementById(Heatmap.elementId).appendChild(noDataMessageElement); document.getElementById(Heatmap.elementId)!.appendChild(noDataMessageElement);
} }
} }

View File

@@ -8,7 +8,7 @@ export enum PortalTheme {
blue = 1, blue = 1,
azure, azure,
light, light,
dark, dark
} }
export interface HeatmapData { export interface HeatmapData {

View File

@@ -32,13 +32,13 @@ export class ResourceTreeContextMenuButtonFactory {
const newCollectionMenuItem: TreeNodeMenuItem = { const newCollectionMenuItem: TreeNodeMenuItem = {
iconSrc: AddCollectionIcon, iconSrc: AddCollectionIcon,
onClick: () => container.onNewCollectionClicked(), onClick: () => container.onNewCollectionClicked(),
label: container.addCollectionText(), label: container.addCollectionText()
}; };
const deleteDatabaseMenuItem = { const deleteDatabaseMenuItem = {
iconSrc: DeleteDatabaseIcon, iconSrc: DeleteDatabaseIcon,
onClick: () => container.deleteDatabaseConfirmationPane.open(), onClick: () => container.deleteDatabaseConfirmationPane.open(),
label: container.deleteDatabaseText(), label: container.deleteDatabaseText()
}; };
return [newCollectionMenuItem, deleteDatabaseMenuItem]; return [newCollectionMenuItem, deleteDatabaseMenuItem];
} }
@@ -52,7 +52,7 @@ export class ResourceTreeContextMenuButtonFactory {
items.push({ items.push({
iconSrc: AddSqlQueryIcon, iconSrc: AddSqlQueryIcon,
onClick: () => selectedCollection && selectedCollection.onNewQueryClick(selectedCollection, null), onClick: () => selectedCollection && selectedCollection.onNewQueryClick(selectedCollection, null),
label: "New SQL Query", label: "New SQL Query"
}); });
} }
@@ -60,7 +60,7 @@ export class ResourceTreeContextMenuButtonFactory {
items.push({ items.push({
iconSrc: AddSqlQueryIcon, iconSrc: AddSqlQueryIcon,
onClick: () => selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection, null), onClick: () => selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection, null),
label: "New Query", label: "New Query"
}); });
items.push({ items.push({
@@ -69,7 +69,7 @@ export class ResourceTreeContextMenuButtonFactory {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection(); const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onNewMongoShellClick(); selectedCollection && selectedCollection.onNewMongoShellClick();
}, },
label: "New Shell", label: "New Shell"
}); });
} }
@@ -80,7 +80,7 @@ export class ResourceTreeContextMenuButtonFactory {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection(); const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection, null); selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection, null);
}, },
label: "New Stored Procedure", label: "New Stored Procedure"
}); });
items.push({ items.push({
@@ -89,7 +89,7 @@ export class ResourceTreeContextMenuButtonFactory {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection(); const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection, null); selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection, null);
}, },
label: "New UDF", label: "New UDF"
}); });
items.push({ items.push({
@@ -98,7 +98,7 @@ export class ResourceTreeContextMenuButtonFactory {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection(); const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection, null); selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection, null);
}, },
label: "New Trigger", label: "New Trigger"
}); });
} }
@@ -108,7 +108,7 @@ export class ResourceTreeContextMenuButtonFactory {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection(); const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onDeleteCollectionContextMenuClick(selectedCollection, null); selectedCollection && selectedCollection.onDeleteCollectionContextMenuClick(selectedCollection, null);
}, },
label: container.deleteCollectionText(), label: container.deleteCollectionText()
}); });
return items; return items;
@@ -126,8 +126,8 @@ export class ResourceTreeContextMenuButtonFactory {
{ {
iconSrc: DeleteSprocIcon, iconSrc: DeleteSprocIcon,
onClick: () => storedProcedure.delete(), onClick: () => storedProcedure.delete(),
label: "Delete Store Procedure", label: "Delete Store Procedure"
}, }
]; ];
} }
@@ -143,8 +143,8 @@ export class ResourceTreeContextMenuButtonFactory {
{ {
iconSrc: DeleteTriggerIcon, iconSrc: DeleteTriggerIcon,
onClick: () => trigger.delete(), onClick: () => trigger.delete(),
label: "Delete Trigger", label: "Delete Trigger"
}, }
]; ];
} }
@@ -160,8 +160,8 @@ export class ResourceTreeContextMenuButtonFactory {
{ {
iconSrc: DeleteUDFIcon, iconSrc: DeleteUDFIcon,
onClick: () => userDefinedFunction.delete(), onClick: () => userDefinedFunction.delete(),
label: "Delete User Defined Function", label: "Delete User Defined Function"
}, }
]; ];
} }
} }

View File

@@ -31,7 +31,7 @@ export class AccessibleElement extends React.Component<AccessibleElementProps> {
...elementProps, ...elementProps,
onKeyPress: this.onKeyPress, onKeyPress: this.onKeyPress,
onClick: this.props.onActivated, onClick: this.props.onActivated,
tabIndex, tabIndex
}); });
} }
} }

View File

@@ -38,7 +38,7 @@ export class AccordionItemComponent extends React.Component<AccordionItemCompone
super(props); super(props);
this.isExpanded = props.isExpanded; this.isExpanded = props.isExpanded;
this.state = { this.state = {
isExpanded: true, isExpanded: true
}; };
} }
@@ -46,7 +46,7 @@ export class AccordionItemComponent extends React.Component<AccordionItemCompone
if (this.props.isExpanded !== this.isExpanded) { if (this.props.isExpanded !== this.isExpanded) {
this.isExpanded = this.props.isExpanded; this.isExpanded = this.props.isExpanded;
this.setState({ this.setState({
isExpanded: this.props.isExpanded, isExpanded: this.props.isExpanded
}); });
} }
} }

View File

@@ -16,7 +16,7 @@ const createBlankProps = (): AccountSwitchComponentProps => {
subscriptions: [], subscriptions: [],
selectedSubscriptionId: null, selectedSubscriptionId: null,
isLoadingSubscriptions: false, isLoadingSubscriptions: false,
onSubscriptionChange: jest.fn(), onSubscriptionChange: jest.fn()
}; };
}; };
@@ -28,7 +28,7 @@ const createBlankAccount = (): DatabaseAccount => {
properties: null, properties: null,
location: "", location: "",
tags: null, tags: null,
type: "", type: ""
}; };
}; };
@@ -40,7 +40,7 @@ const createBlankSubscription = (): Subscription => {
state: "", state: "",
subscriptionPolicies: null, subscriptionPolicies: null,
tenantId: "", tenantId: "",
uniqueDisplayName: "", uniqueDisplayName: ""
}; };
}; };

View File

@@ -34,13 +34,13 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
items: [ items: [
{ {
key: "switchSubscription", key: "switchSubscription",
onRender: this._renderSubscriptionDropdown.bind(this), onRender: this._renderSubscriptionDropdown.bind(this)
}, },
{ {
key: "switchAccount", key: "switchAccount",
onRender: this._renderAccountDropDown.bind(this), onRender: this._renderAccountDropDown.bind(this)
}, }
], ]
}; };
const buttonStyles: IButtonStyles = { const buttonStyles: IButtonStyles = {
@@ -51,27 +51,27 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
paddingLeft: 10, paddingLeft: 10,
marginRight: 5, marginRight: 5,
backgroundColor: StyleConstants.BaseDark, backgroundColor: StyleConstants.BaseDark,
color: StyleConstants.BaseLight, color: StyleConstants.BaseLight
}, },
rootHovered: { rootHovered: {
backgroundColor: StyleConstants.BaseHigh, backgroundColor: StyleConstants.BaseHigh,
color: StyleConstants.BaseLight, color: StyleConstants.BaseLight
}, },
rootFocused: { rootFocused: {
backgroundColor: StyleConstants.BaseHigh, backgroundColor: StyleConstants.BaseHigh,
color: StyleConstants.BaseLight, color: StyleConstants.BaseLight
}, },
rootPressed: { rootPressed: {
backgroundColor: StyleConstants.BaseHigh, backgroundColor: StyleConstants.BaseHigh,
color: StyleConstants.BaseLight, color: StyleConstants.BaseLight
}, },
rootExpanded: { rootExpanded: {
backgroundColor: StyleConstants.BaseHigh, backgroundColor: StyleConstants.BaseHigh,
color: StyleConstants.BaseLight, color: StyleConstants.BaseLight
}, },
textContainer: { textContainer: {
flexGrow: "initial", flexGrow: "initial"
}, }
}; };
const buttonProps: IButtonProps = { const buttonProps: IButtonProps = {
@@ -79,7 +79,7 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
menuProps: menuProps, menuProps: menuProps,
styles: buttonStyles, styles: buttonStyles,
className: "accountSwitchButton", className: "accountSwitchButton",
id: "accountSwitchButton", id: "accountSwitchButton"
}; };
return <DefaultButton {...buttonProps} />; return <DefaultButton {...buttonProps} />;
@@ -87,11 +87,11 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
private _renderSubscriptionDropdown(): JSX.Element { private _renderSubscriptionDropdown(): JSX.Element {
const { subscriptions, selectedSubscriptionId, isLoadingSubscriptions } = this.props; const { subscriptions, selectedSubscriptionId, isLoadingSubscriptions } = this.props;
const options: IDropdownOption[] = subscriptions.map((sub) => { const options: IDropdownOption[] = subscriptions.map(sub => {
return { return {
key: sub.subscriptionId, key: sub.subscriptionId,
text: sub.displayName, text: sub.displayName,
data: sub, data: sub
}; };
}); });
@@ -109,8 +109,8 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
defaultSelectedKey: selectedSubscriptionId, defaultSelectedKey: selectedSubscriptionId,
placeholder: placeHolderText, placeholder: placeHolderText,
styles: { styles: {
callout: "accountSwitchSubscriptionDropdownMenu", callout: "accountSwitchSubscriptionDropdownMenu"
}, }
}; };
return <Dropdown {...dropdownProps} />; return <Dropdown {...dropdownProps} />;
@@ -126,11 +126,11 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
private _renderAccountDropDown(): JSX.Element { private _renderAccountDropDown(): JSX.Element {
const { accounts, selectedAccountName, isLoadingAccounts } = this.props; const { accounts, selectedAccountName, isLoadingAccounts } = this.props;
const options: IDropdownOption[] = accounts.map((account) => { const options: IDropdownOption[] = accounts.map(account => {
return { return {
key: account.name, key: account.name,
text: account.name, text: account.name,
data: account, data: account
}; };
}); });
// Fabric UI will also try to select the first non-disabled option from dropdown. // Fabric UI will also try to select the first non-disabled option from dropdown.
@@ -138,7 +138,7 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
options.unshift({ options.unshift({
key: "select from list", key: "select from list",
text: "Select Cosmos DB account from list", text: "Select Cosmos DB account from list",
data: undefined, data: undefined
}); });
const placeHolderText = isLoadingAccounts const placeHolderText = isLoadingAccounts
@@ -155,8 +155,8 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
defaultSelectedKey: selectedAccountName, defaultSelectedKey: selectedAccountName,
placeholder: placeHolderText, placeholder: placeHolderText,
styles: { styles: {
callout: "accountSwitchAccountDropdownMenu", callout: "accountSwitchAccountDropdownMenu"
}, }
}; };
return <Dropdown {...dropdownProps} />; return <Dropdown {...dropdownProps} />;

View File

@@ -4,7 +4,7 @@ import { DefaultButton, IButtonStyles } from "office-ui-fabric-react/lib/Button"
import { import {
IContextualMenuItem, IContextualMenuItem,
IContextualMenuProps, IContextualMenuProps,
ContextualMenuItemType, ContextualMenuItemType
} from "office-ui-fabric-react/lib/ContextualMenu"; } from "office-ui-fabric-react/lib/ContextualMenu";
import * as Logger from "../../../Common/Logger"; import * as Logger from "../../../Common/Logger";
@@ -33,7 +33,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
constructor(props: ArcadiaMenuPickerProps) { constructor(props: ArcadiaMenuPickerProps) {
super(props); super(props);
this.state = { this.state = {
selectedSparkPool: props.selectedSparkPool, selectedSparkPool: props.selectedSparkPool
}; };
} }
@@ -44,7 +44,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
try { try {
this.props.onSparkPoolSelect(e, item); this.props.onSparkPoolSelect(e, item);
this.setState({ this.setState({
selectedSparkPool: item.text, selectedSparkPool: item.text
}); });
} catch (error) { } catch (error) {
Logger.logError(error, "ArcadiaMenuPicker/_onSparkPoolClicked"); Logger.logError(error, "ArcadiaMenuPicker/_onSparkPoolClicked");
@@ -68,28 +68,28 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
public render() { public render() {
const { workspaces } = this.props; const { workspaces } = this.props;
let workspaceMenuItems: IContextualMenuItem[] = workspaces.map((workspace) => { let workspaceMenuItems: IContextualMenuItem[] = workspaces.map(workspace => {
let sparkPoolsMenuProps: IContextualMenuProps = { let sparkPoolsMenuProps: IContextualMenuProps = {
items: workspace.sparkPools.map( items: workspace.sparkPools.map(
(sparkpool): IContextualMenuItem => ({ (sparkpool): IContextualMenuItem => ({
key: sparkpool.id, key: sparkpool.id,
text: sparkpool.name, text: sparkpool.name,
onClick: this._onSparkPoolClicked, onClick: this._onSparkPoolClicked
}) })
), )
}; };
if (!sparkPoolsMenuProps.items.length) { if (!sparkPoolsMenuProps.items.length) {
sparkPoolsMenuProps.items.push({ sparkPoolsMenuProps.items.push({
key: workspace.id, key: workspace.id,
text: "Create new spark pool", text: "Create new spark pool",
onClick: this._onCreateNewSparkPoolClicked, onClick: this._onCreateNewSparkPoolClicked
}); });
} }
return { return {
key: workspace.id, key: workspace.id,
text: workspace.name, text: workspace.name,
subMenuProps: this.props.disableSubmenu ? undefined : sparkPoolsMenuProps, subMenuProps: this.props.disableSubmenu ? undefined : sparkPoolsMenuProps
}; };
}); });
@@ -97,7 +97,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
workspaceMenuItems.push({ workspaceMenuItems.push({
key: "create_workspace", key: "create_workspace",
text: "Create new workspace", text: "Create new workspace",
onClick: this._onCreateNewWorkspaceClicked, onClick: this._onCreateNewWorkspaceClicked
}); });
} }
@@ -106,29 +106,29 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
backgroundColor: "transparent", backgroundColor: "transparent",
margin: "auto 5px", margin: "auto 5px",
padding: "0", padding: "0",
border: "0", border: "0"
}, },
rootHovered: { rootHovered: {
backgroundColor: "transparent", backgroundColor: "transparent"
}, },
rootChecked: { rootChecked: {
backgroundColor: "transparent", backgroundColor: "transparent"
}, },
rootFocused: { rootFocused: {
backgroundColor: "transparent", backgroundColor: "transparent"
}, },
rootExpanded: { rootExpanded: {
backgroundColor: "transparent", backgroundColor: "transparent"
}, },
flexContainer: { flexContainer: {
height: "30px", height: "30px",
border: "1px solid #a6a6a6", border: "1px solid #a6a6a6",
padding: "0 8px", padding: "0 8px"
}, },
label: { label: {
fontWeight: "400", fontWeight: "400",
fontSize: "12px", fontSize: "12px"
}, }
}; };
return ( return (
@@ -137,7 +137,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
persistMenu={true} persistMenu={true}
className="arcadia-menu-picker" className="arcadia-menu-picker"
menuProps={{ menuProps={{
items: workspaceMenuItems, items: workspaceMenuItems
}} }}
styles={dropdownStyle} styles={dropdownStyle}
/> />

View File

@@ -8,7 +8,7 @@ export class CollapsiblePanelComponent {
constructor() { constructor() {
return { return {
viewModel: CollapsiblePanelViewModel, viewModel: CollapsiblePanelViewModel,
template, template
}; };
} }
} }

View File

@@ -149,7 +149,9 @@ export class CommandButtonComponent extends React.Component<CommandButtonCompone
private onLauncherKeyDown(event: React.KeyboardEvent<HTMLDivElement>): boolean { private onLauncherKeyDown(event: React.KeyboardEvent<HTMLDivElement>): boolean {
if (event.keyCode === KeyCodes.DownArrow) { if (event.keyCode === KeyCodes.DownArrow) {
$(this.dropdownElt).hide(); $(this.dropdownElt).hide();
$(this.dropdownElt).show().focus(); $(this.dropdownElt)
.show()
.focus();
event.stopPropagation(); event.stopPropagation();
return false; return false;
} }
@@ -185,7 +187,7 @@ export class CommandButtonComponent extends React.Component<CommandButtonCompone
} }
this.props.onCommandClick(e); this.props.onCommandClick(e);
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, { TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
commandButtonClicked: this.props.commandButtonLabel, commandButtonClicked: this.props.commandButtonLabel
}); });
} }

View File

@@ -53,26 +53,26 @@ export class DialogComponent extends React.Component<DialogProps, {}> {
subText: this.props.subText, subText: this.props.subText,
styles: { styles: {
title: { fontSize: DIALOG_TITLE_FONT_SIZE, fontWeight: DIALOG_TITLE_FONT_WEIGHT }, title: { fontSize: DIALOG_TITLE_FONT_SIZE, fontWeight: DIALOG_TITLE_FONT_WEIGHT },
subText: { fontSize: DIALOG_SUBTEXT_FONT_SIZE }, subText: { fontSize: DIALOG_SUBTEXT_FONT_SIZE }
}, },
showCloseButton: false, showCloseButton: false
}, },
modalProps: { isBlocking: this.props.isModal }, modalProps: { isBlocking: this.props.isModal },
minWidth: DIALOG_MIN_WIDTH, minWidth: DIALOG_MIN_WIDTH,
maxWidth: DIALOG_MAX_WIDTH, maxWidth: DIALOG_MAX_WIDTH
}; };
const textFieldProps: ITextFieldProps = this.props.textFieldProps; const textFieldProps: ITextFieldProps = this.props.textFieldProps;
const linkProps: LinkProps = this.props.linkProps; const linkProps: LinkProps = this.props.linkProps;
const primaryButtonProps: IButtonProps = { const primaryButtonProps: IButtonProps = {
text: this.props.primaryButtonText, text: this.props.primaryButtonText,
disabled: this.props.primaryButtonDisabled || false, disabled: this.props.primaryButtonDisabled || false,
onClick: this.props.onPrimaryButtonClick, onClick: this.props.onPrimaryButtonClick
}; };
const secondaryButtonProps: IButtonProps = const secondaryButtonProps: IButtonProps =
this.props.secondaryButtonText && this.props.onSecondaryButtonClick this.props.secondaryButtonText && this.props.onSecondaryButtonClick
? { ? {
text: this.props.secondaryButtonText, text: this.props.secondaryButtonText,
onClick: this.props.onSecondaryButtonClick, onClick: this.props.onSecondaryButtonClick
} }
: undefined; : undefined;

View File

@@ -9,7 +9,7 @@ export class DiffEditorComponent {
constructor() { constructor() {
return { return {
viewModel: DiffEditorViewModel, viewModel: DiffEditorViewModel,
template, template
}; };
} }
} }
@@ -103,7 +103,7 @@ export class DiffEditorViewModel {
lineNumbers: this.params.lineNumbers || "off", lineNumbers: this.params.lineNumbers || "off",
fontSize: 12, fontSize: 12,
ariaLabel: this.params.ariaLabel, ariaLabel: this.params.ariaLabel,
theme: this.params.theme, theme: this.params.theme
}; };
if (this.params.renderSideBySide !== undefined) { if (this.params.renderSideBySide !== undefined) {
@@ -120,7 +120,7 @@ export class DiffEditorViewModel {
); );
diffEditor.setModel({ diffEditor.setModel({
original: originalModel, original: originalModel,
modified: modifiedModel, modified: modifiedModel
}); });
createCallback(diffEditor); createCallback(diffEditor);
@@ -147,7 +147,7 @@ export class DiffEditorViewModel {
this.observer.observe(document.body, { this.observer.observe(document.body, {
attributes: true, attributes: true,
subtree: true, subtree: true,
childList: true, childList: true
}); });
this.editor.focus(); this.editor.focus();
} }

View File

@@ -7,7 +7,7 @@ const createBlankProps = (): DefaultDirectoryDropdownProps => {
return { return {
defaultDirectoryId: "", defaultDirectoryId: "",
directories: [], directories: [],
onDefaultDirectoryChange: jest.fn(), onDefaultDirectoryChange: jest.fn()
}; };
}; };
@@ -17,7 +17,7 @@ const createBlankDirectory = (): Tenant => {
displayName: "", displayName: "",
domains: [], domains: [],
id: "", id: "",
tenantId: "", tenantId: ""
}; };
}; };
@@ -90,15 +90,27 @@ describe("test function", () => {
const wrapper = mount(<DefaultDirectoryDropdownComponent {...props} />); const wrapper = mount(<DefaultDirectoryDropdownComponent {...props} />);
wrapper.find("div.defaultDirectoryDropdown").find("div.ms-Dropdown").simulate("click"); wrapper
.find("div.defaultDirectoryDropdown")
.find("div.ms-Dropdown")
.simulate("click");
expect(wrapper.exists("div.ms-Callout-main")).toBe(true); expect(wrapper.exists("div.ms-Callout-main")).toBe(true);
wrapper.find("button.ms-Dropdown-item").at(1).simulate("click"); wrapper
.find("button.ms-Dropdown-item")
.at(1)
.simulate("click");
expect(props.onDefaultDirectoryChange).toBeCalled(); expect(props.onDefaultDirectoryChange).toBeCalled();
expect(props.onDefaultDirectoryChange).toHaveBeenCalled(); expect(props.onDefaultDirectoryChange).toHaveBeenCalled();
wrapper.find("div.defaultDirectoryDropdown").find("div.ms-Dropdown").simulate("click"); wrapper
.find("div.defaultDirectoryDropdown")
.find("div.ms-Dropdown")
.simulate("click");
expect(wrapper.exists("div.ms-Callout-main")).toBe(true); expect(wrapper.exists("div.ms-Callout-main")).toBe(true);
wrapper.find("button.ms-Dropdown-item").at(0).simulate("click"); wrapper
.find("button.ms-Dropdown-item")
.at(0)
.simulate("click");
expect(props.onDefaultDirectoryChange).toBeCalled(); expect(props.onDefaultDirectoryChange).toBeCalled();
expect(props.onDefaultDirectoryChange).toHaveBeenCalled(); expect(props.onDefaultDirectoryChange).toHaveBeenCalled();
}); });

View File

@@ -19,13 +19,13 @@ export class DefaultDirectoryDropdownComponent extends React.Component<DefaultDi
public render(): JSX.Element { public render(): JSX.Element {
const lastVisitedOption: IDropdownOption = { const lastVisitedOption: IDropdownOption = {
key: DefaultDirectoryDropdownComponent.lastVisitedKey, key: DefaultDirectoryDropdownComponent.lastVisitedKey,
text: "Sign in to your last visited directory", text: "Sign in to your last visited directory"
}; };
const directoryOptions: Array<IDropdownOption> = this.props.directories.map( const directoryOptions: Array<IDropdownOption> = this.props.directories.map(
(dirc): IDropdownOption => { (dirc): IDropdownOption => {
return { return {
key: dirc.tenantId, key: dirc.tenantId,
text: `${dirc.displayName}(${dirc.tenantId})`, text: `${dirc.displayName}(${dirc.tenantId})`
}; };
} }
); );
@@ -35,7 +35,7 @@ export class DefaultDirectoryDropdownComponent extends React.Component<DefaultDi
options: dropDownOptions, options: dropDownOptions,
defaultSelectedKey: this.props.defaultDirectoryId ? this.props.defaultDirectoryId : lastVisitedOption.key, defaultSelectedKey: this.props.defaultDirectoryId ? this.props.defaultDirectoryId : lastVisitedOption.key,
onChange: this._onDropdownChange, onChange: this._onDropdownChange,
className: "defaultDirectoryDropdown", className: "defaultDirectoryDropdown"
}; };
return <Dropdown {...dropDownProps} />; return <Dropdown {...dropDownProps} />;
@@ -56,12 +56,12 @@ export class DefaultDirectoryDropdownComponent extends React.Component<DefaultDi
countryCode: undefined, countryCode: undefined,
displayName: undefined, displayName: undefined,
domains: [], domains: [],
id: undefined, id: undefined
}); });
return; return;
} }
const selectedDirectory = _.find(this.props.directories, (d) => d.tenantId === option.key); const selectedDirectory = _.find(this.props.directories, d => d.tenantId === option.key);
if (!selectedDirectory) { if (!selectedDirectory) {
return; return;
} }

View File

@@ -7,7 +7,7 @@ const createBlankProps = (): DirectoryListProps => {
return { return {
selectedDirectoryId: undefined, selectedDirectoryId: undefined,
directories: [], directories: [],
onNewDirectorySelected: jest.fn(), onNewDirectorySelected: jest.fn()
}; };
}; };
@@ -17,7 +17,7 @@ const createBlankDirectory = (): Tenant => {
displayName: undefined, displayName: undefined,
domains: [], domains: [],
id: undefined, id: undefined,
tenantId: undefined, tenantId: undefined
}; };
}; };

View File

@@ -28,7 +28,7 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
super(props); super(props);
this.state = { this.state = {
filterText: "", filterText: ""
}; };
} }
@@ -38,12 +38,12 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
const filteredItems = const filteredItems =
originalItems && originalItems.length && filterText originalItems && originalItems.length && filterText
? originalItems.filter( ? originalItems.filter(
(directory) => directory =>
directory.displayName && directory.displayName &&
directory.displayName.toLowerCase().indexOf(filterText && filterText.toLowerCase()) >= 0 directory.displayName.toLowerCase().indexOf(filterText && filterText.toLowerCase()) >= 0
) )
: originalItems; : originalItems;
const filteredItemsSelected = filteredItems.map((t) => { const filteredItemsSelected = filteredItems.map(t => {
let tenant: ListTenant = t; let tenant: ListTenant = t;
tenant.selected = t.tenantId === selectedDirectoryId; tenant.selected = t.tenantId === selectedDirectoryId;
return tenant; return tenant;
@@ -53,7 +53,7 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
className: "directoryListFilterTextBox", className: "directoryListFilterTextBox",
placeholder: "Filter by directory name", placeholder: "Filter by directory name",
onChange: this._onFilterChanged, onChange: this._onFilterChanged,
ariaLabel: "Directory filter text box", ariaLabel: "Directory filter text box"
}; };
// TODO: add magnify glass to search bar with onRenderSuffix // TODO: add magnify glass to search bar with onRenderSuffix
@@ -69,7 +69,7 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
private _onFilterChanged = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, text?: string): void => { private _onFilterChanged = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, text?: string): void => {
this.setState({ this.setState({
filterText: text, filterText: text
}); });
}; };
@@ -84,19 +84,19 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
height: "auto", height: "auto",
borderBottom: "1px solid #ccc", borderBottom: "1px solid #ccc",
padding: "1px 0", padding: "1px 0",
width: "100%", width: "100%"
}, },
rootDisabled: { rootDisabled: {
backgroundColor: "#f1f1f8", backgroundColor: "#f1f1f8"
}, },
rootHovered: { rootHovered: {
backgroundColor: "rgba(85,179,255,.1)", backgroundColor: "rgba(85,179,255,.1)"
}, },
flexContainer: { flexContainer: {
height: "auto", height: "auto",
justifyContent: "flex-start", justifyContent: "flex-start"
}, }
}, }
}; };
return ( return (
@@ -115,7 +115,7 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
} }
const buttonElement = e.currentTarget; const buttonElement = e.currentTarget;
const selectedDirectoryId = buttonElement.getElementsByClassName("directoryListItemId")[0].textContent; const selectedDirectoryId = buttonElement.getElementsByClassName("directoryListItemId")[0].textContent;
const selectedDirectory = _.find(this.props.directories, (d) => d.tenantId === selectedDirectoryId); const selectedDirectory = _.find(this.props.directories, d => d.tenantId === selectedDirectoryId);
this.props.onNewDirectorySelected(selectedDirectory); this.props.onNewDirectorySelected(selectedDirectory);
}; };

View File

@@ -25,7 +25,7 @@ describe("Dynamic List Component", () => {
placeholder: placeholder, placeholder: placeholder,
listItems: items, listItems: items,
buttonText: mockButton, buttonText: mockButton,
ariaLabel: mockAriaLabel, ariaLabel: mockAriaLabel
}; };
} }

View File

@@ -112,5 +112,5 @@ export class DynamicListViewModel extends WaitsForTemplateViewModel {
*/ */
export const DynamicListComponent = { export const DynamicListComponent = {
viewModel: DynamicListViewModel, viewModel: DynamicListViewModel,
template, template
}; };

View File

@@ -10,7 +10,7 @@ export class EditorComponent {
constructor() { constructor() {
return { return {
viewModel: EditorViewModel, viewModel: EditorViewModel,
template, template
}; };
} }
} }

View File

@@ -70,7 +70,7 @@ export class EditorReact extends React.Component<EditorReactProps> {
fontSize: 12, fontSize: 12,
ariaLabel: this.props.ariaLabel, ariaLabel: this.props.ariaLabel,
theme: this.props.theme, theme: this.props.theme,
automaticLayout: true, automaticLayout: true
}; };
this.rootNode.innerHTML = ""; this.rootNode.innerHTML = "";

View File

@@ -10,7 +10,7 @@ export class ErrorDisplayComponent {
constructor() { constructor() {
return { return {
viewModel: ErrorDisplayViewModel, viewModel: ErrorDisplayViewModel,
template, template
}; };
} }
} }

View File

@@ -15,23 +15,23 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
const baseUrlOptions = [ const baseUrlOptions = [
{ key: "https://localhost:1234/explorer.html", text: "localhost:1234" }, { key: "https://localhost:1234/explorer.html", text: "localhost:1234" },
{ key: "https://cosmos.azure.com/explorer.html", text: "cosmos.azure.com" }, { key: "https://cosmos.azure.com/explorer.html", text: "cosmos.azure.com" },
{ key: "https://portal.azure.com", text: "portal" }, { key: "https://portal.azure.com", text: "portal" }
]; ];
const platformOptions = [ const platformOptions = [
{ key: "Hosted", text: "Hosted" }, { key: "Hosted", text: "Hosted" },
{ key: "Portal", text: "Portal" }, { key: "Portal", text: "Portal" },
{ key: "Emulator", text: "Emulator" }, { key: "Emulator", text: "Emulator" },
{ key: "", text: "None" }, { key: "", text: "None" }
]; ];
// React hooks to keep state // React hooks to keep state
const [baseUrl, setBaseUrl] = React.useState<IDropdownOption>( const [baseUrl, setBaseUrl] = React.useState<IDropdownOption>(
baseUrlOptions.find((o) => o.key === window.location.origin + window.location.pathname) || baseUrlOptions[0] baseUrlOptions.find(o => o.key === window.location.origin + window.location.pathname) || baseUrlOptions[0]
); );
const [platform, setPlatform] = React.useState<IDropdownOption>( const [platform, setPlatform] = React.useState<IDropdownOption>(
urlParams.has("platform") urlParams.has("platform")
? platformOptions.find((o) => o.key === urlParams.get("platform")) || platformOptions[0] ? platformOptions.find(o => o.key === urlParams.get("platform")) || platformOptions[0]
: platformOptions[0] : platformOptions[0]
); );
@@ -48,13 +48,12 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
{ key: "feature.dataexplorerexecutesproc", label: "Execute stored procedure", value: "true" }, { key: "feature.dataexplorerexecutesproc", label: "Execute stored procedure", value: "true" },
{ key: "feature.hosteddataexplorerenabled", label: "Hosted Data Explorer (deprecated?)", value: "true" }, { key: "feature.hosteddataexplorerenabled", label: "Hosted Data Explorer (deprecated?)", value: "true" },
{ key: "feature.enablettl", label: "Enable TTL", value: "true" }, { key: "feature.enablettl", label: "Enable TTL", value: "true" },
{ key: "feature.enablegallery", label: "Enable Notebook Gallery", value: "true" },
{ key: "feature.enablegallerypublish", label: "Enable Notebook Gallery Publishing", value: "true" }, { key: "feature.enablegallerypublish", label: "Enable Notebook Gallery Publishing", value: "true" },
{ key: "feature.canexceedmaximumvalue", label: "Can exceed max value", value: "true" }, { key: "feature.canexceedmaximumvalue", label: "Can exceed max value", value: "true" },
{ {
key: "feature.enablefixedcollectionwithsharedthroughput", key: "feature.enablefixedcollectionwithsharedthroughput",
label: "Enable fixed collection with shared throughput", label: "Enable fixed collection with shared throughput",
value: "true", value: "true"
}, },
{ key: "feature.ttl90days", label: "TTL 90 days", value: "true" }, { key: "feature.ttl90days", label: "TTL 90 days", value: "true" },
{ key: "feature.enablenotebooks", label: "Enable notebooks", value: "true" }, { key: "feature.enablenotebooks", label: "Enable notebooks", value: "true" },
@@ -62,10 +61,10 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
key: "feature.customportal", key: "feature.customportal",
label: "Force Production portal (portal only)", label: "Force Production portal (portal only)",
value: "false", value: "false",
disabled: (): boolean => baseUrl.key !== "https://portal.azure.com", disabled: (): boolean => baseUrl.key !== "https://portal.azure.com"
}, },
{ key: "feature.enablespark", label: "Enable Synapse", value: "true" }, { key: "feature.enablespark", label: "Enable Synapse", value: "true" },
{ key: "feature.enableautopilotv2", label: "Enable Auto-pilot V2", value: "true" }, { key: "feature.enableautopilotv2", label: "Enable Auto-pilot V2", value: "true" }
]; ];
const stringFeatures: { const stringFeatures: {
@@ -84,23 +83,23 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
key: "dataExplorerSource", key: "dataExplorerSource",
label: "Data Explorer Source (portal only)", label: "Data Explorer Source (portal only)",
placeholder: "https://localhost:1234/explorer.html", placeholder: "https://localhost:1234/explorer.html",
disabled: (): boolean => baseUrl.key !== "https://portal.azure.com", disabled: (): boolean => baseUrl.key !== "https://portal.azure.com"
}, },
{ key: "feature.livyendpoint", label: "Livy endpoint", placeholder: "" }, { key: "feature.livyendpoint", label: "Livy endpoint", placeholder: "" }
]; ];
booleanFeatures.forEach( booleanFeatures.forEach(
(f) => (f.reactState = React.useState<boolean>(urlParams.has(f.key) ? urlParams.get(f.key) === "true" : false)) f => (f.reactState = React.useState<boolean>(urlParams.has(f.key) ? urlParams.get(f.key) === "true" : false))
); );
stringFeatures.forEach( stringFeatures.forEach(
(f) => (f.reactState = React.useState<string>(urlParams.has(f.key) ? urlParams.get(f.key) : undefined)) f => (f.reactState = React.useState<string>(urlParams.has(f.key) ? urlParams.get(f.key) : undefined))
); );
const buildUrl = (): string => { const buildUrl = (): string => {
const fragments = (platform.key === "" ? [] : [`platform=${platform.key}`]) const fragments = (platform.key === "" ? [] : [`platform=${platform.key}`])
.concat(booleanFeatures.map((f) => (f.reactState[0] ? `${f.key}=${f.value}` : ""))) .concat(booleanFeatures.map(f => (f.reactState[0] ? `${f.key}=${f.value}` : "")))
.concat(stringFeatures.map((f) => (f.reactState[0] ? `${f.key}=${encodeURIComponent(f.reactState[0])}` : ""))) .concat(stringFeatures.map(f => (f.reactState[0] ? `${f.key}=${encodeURIComponent(f.reactState[0])}` : "")))
.filter((v) => v && v.length > 0); .filter(v => v && v.length > 0);
const paramString = fragments.length < 1 ? "" : `?${fragments.join("&")}`; const paramString = fragments.length < 1 ? "" : `?${fragments.join("&")}`;
return `${baseUrl.key}${paramString}`; return `${baseUrl.key}${paramString}`;
@@ -115,38 +114,38 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
}; };
booleanFeatures.forEach( booleanFeatures.forEach(
(f) => f =>
(f.onChange = (ev?: React.FormEvent<HTMLElement | HTMLInputElement>, checked?: boolean): void => { (f.onChange = (ev?: React.FormEvent<HTMLElement | HTMLInputElement>, checked?: boolean): void => {
f.reactState[1](checked); f.reactState[1](checked);
}) })
); );
stringFeatures.forEach( stringFeatures.forEach(
(f) => f =>
(f.onChange = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string): void => { (f.onChange = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string): void => {
f.reactState[1](newValue); f.reactState[1](newValue);
}) })
); );
const onNotebookShortcut = (): void => { const onNotebookShortcut = (): void => {
booleanFeatures.find((f) => f.key === "feature.enablenotebooks").reactState[1](true); booleanFeatures.find(f => f.key === "feature.enablenotebooks").reactState[1](true);
stringFeatures stringFeatures
.find((f) => f.key === "feature.notebookserverurl") .find(f => f.key === "feature.notebookserverurl")
.reactState[1]("https://localhost:10001/12345/notebook/"); .reactState[1]("https://localhost:10001/12345/notebook/");
stringFeatures.find((f) => f.key === "feature.notebookservertoken").reactState[1]("token"); stringFeatures.find(f => f.key === "feature.notebookservertoken").reactState[1]("token");
stringFeatures.find((f) => f.key === "feature.notebookbasepath").reactState[1]("./notebooks"); stringFeatures.find(f => f.key === "feature.notebookbasepath").reactState[1]("./notebooks");
setPlatform(platformOptions.find((o) => o.key === "Hosted")); setPlatform(platformOptions.find(o => o.key === "Hosted"));
}; };
const onPortalLocalDEShortcut = (): void => { const onPortalLocalDEShortcut = (): void => {
setBaseUrl(baseUrlOptions.find((o) => o.key === "https://portal.azure.com")); setBaseUrl(baseUrlOptions.find(o => o.key === "https://portal.azure.com"));
setPlatform(platformOptions.find((o) => o.key === "Portal")); setPlatform(platformOptions.find(o => o.key === "Portal"));
stringFeatures.find((f) => f.key === "dataExplorerSource").reactState[1]("https://localhost:1234/explorer.html"); stringFeatures.find(f => f.key === "dataExplorerSource").reactState[1]("https://localhost:1234/explorer.html");
}; };
const onReset = (): void => { const onReset = (): void => {
booleanFeatures.forEach((f) => f.reactState[1](false)); booleanFeatures.forEach(f => f.reactState[1](false));
stringFeatures.forEach((f) => f.reactState[1]("")); stringFeatures.forEach(f => f.reactState[1](""));
}; };
const stackTokens = { childrenGap: 10 }; const stackTokens = { childrenGap: 10 };
@@ -165,7 +164,7 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
const anchorOptions = { const anchorOptions = {
href: buildUrl(), href: buildUrl(),
target: "_blank", target: "_blank",
rel: "noopener", rel: "noopener"
}; };
return ( return (
@@ -197,7 +196,7 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
</Stack> </Stack>
<Stack horizontal> <Stack horizontal>
<Stack className="checkboxRow" horizontalAlign="space-between"> <Stack className="checkboxRow" horizontalAlign="space-between">
{leftBooleanFeatures.map((f) => ( {leftBooleanFeatures.map(f => (
<Checkbox <Checkbox
key={f.key} key={f.key}
label={f.label} label={f.label}
@@ -208,7 +207,7 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
))} ))}
</Stack> </Stack>
<Stack className="checkboxRow" horizontalAlign="space-between"> <Stack className="checkboxRow" horizontalAlign="space-between">
{rightBooleanFeatures.map((f) => ( {rightBooleanFeatures.map(f => (
<Checkbox <Checkbox
key={f.key} key={f.key}
label={f.label} label={f.label}
@@ -221,7 +220,7 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
</Stack> </Stack>
<Stack horizontal tokens={stackTokens}> <Stack horizontal tokens={stackTokens}>
<Stack horizontalAlign="space-between"> <Stack horizontalAlign="space-between">
{leftStringFeatures.map((f) => ( {leftStringFeatures.map(f => (
<TextField <TextField
key={f.key} key={f.key}
value={f.reactState[0]} value={f.reactState[0]}
@@ -234,7 +233,7 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
))} ))}
</Stack> </Stack>
<Stack horizontalAlign="space-between"> <Stack horizontalAlign="space-between">
{rightStringFeatures.map((f) => ( {rightStringFeatures.map(f => (
<TextField <TextField
key={f.key} key={f.key}
value={f.reactState[0]} value={f.reactState[0]}

View File

@@ -20,7 +20,7 @@ export const FeaturePanelLauncher: React.FunctionComponent = (): JSX.Element =>
container: { container: {
display: "flex", display: "flex",
flexFlow: "column nowrap", flexFlow: "column nowrap",
alignItems: "stretch", alignItems: "stretch"
}, },
header: [ header: [
// tslint:disable-next-line:deprecation // tslint:disable-next-line:deprecation
@@ -32,16 +32,16 @@ export const FeaturePanelLauncher: React.FunctionComponent = (): JSX.Element =>
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
fontWeight: FontWeights.semibold, fontWeight: FontWeights.semibold,
padding: "12px 12px 14px 24px", padding: "12px 12px 14px 24px"
}, }
], ],
body: { body: {
flex: "4 4 auto", flex: "4 4 auto",
overflowY: "hidden", overflowY: "hidden",
marginBottom: 40, marginBottom: 40,
height: "100%", height: "100%",
display: "flex", display: "flex"
}, }
}); });
const iconButtonStyles = { const iconButtonStyles = {
@@ -49,11 +49,11 @@ export const FeaturePanelLauncher: React.FunctionComponent = (): JSX.Element =>
color: theme.palette.neutralPrimary, color: theme.palette.neutralPrimary,
marginLeft: "auto", marginLeft: "auto",
marginTop: "4px", marginTop: "4px",
marginRight: "2px", marginRight: "2px"
}, },
rootHovered: { rootHovered: {
color: theme.palette.neutralDark, color: theme.palette.neutralDark
}, }
}; };
const cancelIcon: IIconProps = { iconName: "Cancel" }; const cancelIcon: IIconProps = { iconName: "Cancel" };
const hideModal = (): void => showModal(false); const hideModal = (): void => showModal(false);

View File

@@ -157,14 +157,14 @@ exports[`Feature panel renders all flags 1`] = `
/> />
<StyledCheckboxBase <StyledCheckboxBase
checked={false} checked={false}
key="feature.enablegallery" key="feature.enablegallerypublish"
label="Enable Notebook Gallery" label="Enable Notebook Gallery Publishing"
onChange={[Function]} onChange={[Function]}
/> />
<StyledCheckboxBase <StyledCheckboxBase
checked={false} checked={false}
key="feature.enablegallerypublish" key="feature.canexceedmaximumvalue"
label="Enable Notebook Gallery Publishing" label="Can exceed max value"
onChange={[Function]} onChange={[Function]}
/> />
</Stack> </Stack>
@@ -172,12 +172,6 @@ exports[`Feature panel renders all flags 1`] = `
className="checkboxRow" className="checkboxRow"
horizontalAlign="space-between" horizontalAlign="space-between"
> >
<StyledCheckboxBase
checked={false}
key="feature.canexceedmaximumvalue"
label="Can exceed max value"
onChange={[Function]}
/>
<StyledCheckboxBase <StyledCheckboxBase
checked={false} checked={false}
key="feature.enablefixedcollectionwithsharedthroughput" key="feature.enablefixedcollectionwithsharedthroughput"

View File

@@ -34,7 +34,7 @@ export class AddRepoComponent extends React.Component<AddRepoComponentProps, Add
this.state = { this.state = {
textFieldValue: "", textFieldValue: "",
textFieldErrorMessage: undefined, textFieldErrorMessage: undefined
}; };
} }
@@ -44,13 +44,13 @@ export class AddRepoComponent extends React.Component<AddRepoComponentProps, Add
autoFocus: true, autoFocus: true,
value: this.state.textFieldValue, value: this.state.textFieldValue,
errorMessage: this.state.textFieldErrorMessage, errorMessage: this.state.textFieldErrorMessage,
onChange: this.onTextFieldChange, onChange: this.onTextFieldChange
}; };
const buttonProps: IButtonProps = { const buttonProps: IButtonProps = {
text: AddRepoComponent.ButtonText, text: AddRepoComponent.ButtonText,
ariaLabel: AddRepoComponent.ButtonText, ariaLabel: AddRepoComponent.ButtonText,
onClick: this.onAddRepoButtonClick, onClick: this.onAddRepoButtonClick
}; };
return ( return (
@@ -68,7 +68,7 @@ export class AddRepoComponent extends React.Component<AddRepoComponentProps, Add
): void => { ): void => {
this.setState({ this.setState({
textFieldValue: newValue || "", textFieldValue: newValue || "",
textFieldErrorMessage: undefined, textFieldErrorMessage: undefined
}); });
}; };
@@ -76,7 +76,7 @@ export class AddRepoComponent extends React.Component<AddRepoComponentProps, Add
const startKey: number = TelemetryProcessor.traceStart(Action.NotebooksGitHubManualRepoAdd, { const startKey: number = TelemetryProcessor.traceStart(Action.NotebooksGitHubManualRepoAdd, {
databaseAccountName: this.props.container.databaseAccount() && this.props.container.databaseAccount().name, databaseAccountName: this.props.container.databaseAccount() && this.props.container.databaseAccount().name,
defaultExperience: this.props.container.defaultExperience && this.props.container.defaultExperience(), defaultExperience: this.props.container.defaultExperience && this.props.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Notebook, dataExplorerArea: Constants.Areas.Notebook
}); });
let enteredUrl = this.state.textFieldValue; let enteredUrl = this.state.textFieldValue;
if (enteredUrl.indexOf("/tree/") === -1) { if (enteredUrl.indexOf("/tree/") === -1) {
@@ -87,7 +87,7 @@ export class AddRepoComponent extends React.Component<AddRepoComponentProps, Add
if (repoInfo) { if (repoInfo) {
this.setState({ this.setState({
textFieldValue: "", textFieldValue: "",
textFieldErrorMessage: undefined, textFieldErrorMessage: undefined
}); });
const repo = await this.props.getRepo(repoInfo.owner, repoInfo.repo); const repo = await this.props.getRepo(repoInfo.owner, repoInfo.repo);
@@ -97,9 +97,9 @@ export class AddRepoComponent extends React.Component<AddRepoComponentProps, Add
repo, repo,
branches: [ branches: [
{ {
name: repoInfo.branch, name: repoInfo.branch
}, }
], ]
}; };
TelemetryProcessor.traceSuccess( TelemetryProcessor.traceSuccess(
@@ -107,7 +107,7 @@ export class AddRepoComponent extends React.Component<AddRepoComponentProps, Add
{ {
databaseAccountName: this.props.container.databaseAccount() && this.props.container.databaseAccount().name, databaseAccountName: this.props.container.databaseAccount() && this.props.container.databaseAccount().name,
defaultExperience: this.props.container.defaultExperience && this.props.container.defaultExperience(), defaultExperience: this.props.container.defaultExperience && this.props.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Notebook, dataExplorerArea: Constants.Areas.Notebook
}, },
startKey startKey
); );
@@ -116,7 +116,7 @@ export class AddRepoComponent extends React.Component<AddRepoComponentProps, Add
} }
this.setState({ this.setState({
textFieldErrorMessage: AddRepoComponent.TextFieldErrorMessage, textFieldErrorMessage: AddRepoComponent.TextFieldErrorMessage
}); });
TelemetryProcessor.traceFailure( TelemetryProcessor.traceFailure(
Action.NotebooksGitHubManualRepoAdd, Action.NotebooksGitHubManualRepoAdd,
@@ -124,7 +124,7 @@ export class AddRepoComponent extends React.Component<AddRepoComponentProps, Add
databaseAccountName: this.props.container.databaseAccount() && this.props.container.databaseAccount().name, databaseAccountName: this.props.container.databaseAccount() && this.props.container.databaseAccount().name,
defaultExperience: this.props.container.defaultExperience && this.props.container.defaultExperience(), defaultExperience: this.props.container.defaultExperience && this.props.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Notebook, dataExplorerArea: Constants.Areas.Notebook,
error: AddRepoComponent.TextFieldErrorMessage, error: AddRepoComponent.TextFieldErrorMessage
}, },
startKey startKey
); );

View File

@@ -3,7 +3,7 @@ import {
IButtonProps, IButtonProps,
IChoiceGroupProps, IChoiceGroupProps,
PrimaryButton, PrimaryButton,
IChoiceGroupOption, IChoiceGroupOption
} from "office-ui-fabric-react"; } from "office-ui-fabric-react";
import * as React from "react"; import * as React from "react";
import { ChildrenMargin } from "./GitHubStyleConstants"; import { ChildrenMargin } from "./GitHubStyleConstants";
@@ -26,12 +26,12 @@ export class AuthorizeAccessComponent extends React.Component<
public static readonly Scopes = { public static readonly Scopes = {
Public: { Public: {
key: "public_repo", key: "public_repo",
text: "Public repos only", text: "Public repos only"
}, },
PublicAndPrivate: { PublicAndPrivate: {
key: "repo", key: "repo",
text: "Public and private repos", text: "Public and private repos"
}, }
}; };
private static readonly DescriptionPara1 = private static readonly DescriptionPara1 =
@@ -42,7 +42,7 @@ export class AuthorizeAccessComponent extends React.Component<
private onChoiceGroupChange = (event: React.SyntheticEvent<HTMLElement>, option: IChoiceGroupOption): void => private onChoiceGroupChange = (event: React.SyntheticEvent<HTMLElement>, option: IChoiceGroupOption): void =>
this.setState({ this.setState({
scope: option.key, scope: option.key
}); });
private onButtonClick = (): void => this.props.authorizeAccess(this.state.scope); private onButtonClick = (): void => this.props.authorizeAccess(this.state.scope);
@@ -51,7 +51,7 @@ export class AuthorizeAccessComponent extends React.Component<
super(props); super(props);
this.state = { this.state = {
scope: this.props.scope, scope: this.props.scope
}; };
} }
@@ -61,22 +61,22 @@ export class AuthorizeAccessComponent extends React.Component<
{ {
key: AuthorizeAccessComponent.Scopes.Public.key, key: AuthorizeAccessComponent.Scopes.Public.key,
text: AuthorizeAccessComponent.Scopes.Public.text, text: AuthorizeAccessComponent.Scopes.Public.text,
ariaLabel: AuthorizeAccessComponent.Scopes.Public.text, ariaLabel: AuthorizeAccessComponent.Scopes.Public.text
}, },
{ {
key: AuthorizeAccessComponent.Scopes.PublicAndPrivate.key, key: AuthorizeAccessComponent.Scopes.PublicAndPrivate.key,
text: AuthorizeAccessComponent.Scopes.PublicAndPrivate.text, text: AuthorizeAccessComponent.Scopes.PublicAndPrivate.text,
ariaLabel: AuthorizeAccessComponent.Scopes.PublicAndPrivate.text, ariaLabel: AuthorizeAccessComponent.Scopes.PublicAndPrivate.text
}, }
], ],
selectedKey: this.state.scope, selectedKey: this.state.scope,
onChange: this.onChoiceGroupChange, onChange: this.onChoiceGroupChange
}; };
const buttonProps: IButtonProps = { const buttonProps: IButtonProps = {
text: AuthorizeAccessComponent.AuthorizeButtonText, text: AuthorizeAccessComponent.AuthorizeButtonText,
ariaLabel: AuthorizeAccessComponent.AuthorizeButtonText, ariaLabel: AuthorizeAccessComponent.AuthorizeButtonText,
onClick: this.onButtonClick, onClick: this.onButtonClick
}; };
return ( return (

View File

@@ -55,13 +55,13 @@ export class GitHubReposComponent extends React.Component<GitHubReposComponentPr
const okProps: IButtonProps = { const okProps: IButtonProps = {
text: GitHubReposComponent.OKButtonText, text: GitHubReposComponent.OKButtonText,
ariaLabel: GitHubReposComponent.OKButtonText, ariaLabel: GitHubReposComponent.OKButtonText,
onClick: this.props.onOkClick, onClick: this.props.onOkClick
}; };
const cancelProps: IButtonProps = { const cancelProps: IButtonProps = {
text: GitHubReposComponent.CancelButtonText, text: GitHubReposComponent.CancelButtonText,
ariaLabel: GitHubReposComponent.CancelButtonText, ariaLabel: GitHubReposComponent.CancelButtonText,
onClick: this.props.onCancelClick, onClick: this.props.onCancelClick
}; };
return ( return (

View File

@@ -3,17 +3,17 @@ import {
ICheckboxStyleProps, ICheckboxStyleProps,
ICheckboxStyles, ICheckboxStyles,
IDropdownStyles, IDropdownStyles,
IDropdownStyleProps, IDropdownStyleProps
} from "office-ui-fabric-react"; } from "office-ui-fabric-react";
export const ButtonsFooterStyle: React.CSSProperties = { export const ButtonsFooterStyle: React.CSSProperties = {
padding: 14, padding: 14,
height: "auto", height: "auto"
}; };
export const ContentFooterStyle: React.CSSProperties = { export const ContentFooterStyle: React.CSSProperties = {
padding: "10px 24px 10px 24px", padding: "10px 24px 10px 24px",
height: "auto", height: "auto"
}; };
export const ChildrenMargin = 10; export const ChildrenMargin = 10;
@@ -22,35 +22,35 @@ export const FontSize = 12;
export const ReposListCheckboxStyles: IStyleFunctionOrObject<ICheckboxStyleProps, ICheckboxStyles> = { export const ReposListCheckboxStyles: IStyleFunctionOrObject<ICheckboxStyleProps, ICheckboxStyles> = {
label: { label: {
margin: 0, margin: 0,
padding: "2 0 2 0", padding: "2 0 2 0"
}, },
text: { text: {
fontSize: FontSize, fontSize: FontSize
}, }
}; };
export const BranchesDropdownCheckboxStyles: IStyleFunctionOrObject<ICheckboxStyleProps, ICheckboxStyles> = { export const BranchesDropdownCheckboxStyles: IStyleFunctionOrObject<ICheckboxStyleProps, ICheckboxStyles> = {
label: { label: {
margin: 0, margin: 0,
padding: 0, padding: 0,
fontSize: FontSize, fontSize: FontSize
}, },
root: { root: {
padding: 0, padding: 0
}, },
text: { text: {
fontSize: FontSize, fontSize: FontSize
}, }
}; };
export const BranchesDropdownStyles: IStyleFunctionOrObject<IDropdownStyleProps, IDropdownStyles> = { export const BranchesDropdownStyles: IStyleFunctionOrObject<IDropdownStyleProps, IDropdownStyles> = {
title: { title: {
fontSize: FontSize, fontSize: FontSize
}, }
}; };
export const BranchesDropdownOptionContainerStyle: React.CSSProperties = { export const BranchesDropdownOptionContainerStyle: React.CSSProperties = {
padding: 8, padding: 8
}; };
export const ReposListRepoColumnMinWidth = 192; export const ReposListRepoColumnMinWidth = 192;

View File

@@ -15,7 +15,7 @@ import {
Link, Link,
ResponsiveMode, ResponsiveMode,
SelectionMode, SelectionMode,
Text, Text
} from "office-ui-fabric-react"; } from "office-ui-fabric-react";
import * as React from "react"; import * as React from "react";
import { IGitHubBranch, IGitHubPageInfo } from "../../../GitHub/GitHubClient"; import { IGitHubBranch, IGitHubPageInfo } from "../../../GitHub/GitHubClient";
@@ -28,7 +28,7 @@ import {
ReposListRepoColumnMinWidth, ReposListRepoColumnMinWidth,
ReposListBranchesColumnWidth, ReposListBranchesColumnWidth,
BranchesDropdownWidth, BranchesDropdownWidth,
BranchesDropdownStyles, BranchesDropdownStyles
} from "./GitHubStyleConstants"; } from "./GitHubStyleConstants";
export interface ReposListComponentProps { export interface ReposListComponentProps {
@@ -71,8 +71,8 @@ export class ReposListComponent extends React.Component<ReposListComponentProps>
const pinnedReposListProps: IDetailsListProps = { const pinnedReposListProps: IDetailsListProps = {
styles: { styles: {
contentWrapper: { contentWrapper: {
height: this.props.pinnedReposProps.repos.length ? undefined : 0, height: this.props.pinnedReposProps.repos.length ? undefined : 0
}, }
}, },
items: this.props.pinnedReposProps.repos, items: this.props.pinnedReposProps.repos,
getKey: ReposListComponent.getKey, getKey: ReposListComponent.getKey,
@@ -84,7 +84,7 @@ export class ReposListComponent extends React.Component<ReposListComponentProps>
name: ReposListComponent.PinnedReposColumnName, name: ReposListComponent.PinnedReposColumnName,
ariaLabel: ReposListComponent.PinnedReposColumnName, ariaLabel: ReposListComponent.PinnedReposColumnName,
minWidth: ReposListRepoColumnMinWidth, minWidth: ReposListRepoColumnMinWidth,
onRender: this.onRenderPinnedReposColumnItem, onRender: this.onRenderPinnedReposColumnItem
}, },
{ {
key: ReposListComponent.BranchesColumnName, key: ReposListComponent.BranchesColumnName,
@@ -92,10 +92,10 @@ export class ReposListComponent extends React.Component<ReposListComponentProps>
ariaLabel: ReposListComponent.BranchesColumnName, ariaLabel: ReposListComponent.BranchesColumnName,
minWidth: ReposListBranchesColumnWidth, minWidth: ReposListBranchesColumnWidth,
maxWidth: ReposListBranchesColumnWidth, maxWidth: ReposListBranchesColumnWidth,
onRender: this.onRenderPinnedReposBranchesColumnItem, onRender: this.onRenderPinnedReposBranchesColumnItem
}, }
], ],
onRenderDetailsFooter: this.props.pinnedReposProps.repos.length ? undefined : this.onRenderReposFooter, onRenderDetailsFooter: this.props.pinnedReposProps.repos.length ? undefined : this.onRenderReposFooter
}; };
const unpinnedReposListProps: IDetailsListProps = { const unpinnedReposListProps: IDetailsListProps = {
@@ -109,7 +109,7 @@ export class ReposListComponent extends React.Component<ReposListComponentProps>
name: ReposListComponent.UnpinnedReposColumnName, name: ReposListComponent.UnpinnedReposColumnName,
ariaLabel: ReposListComponent.UnpinnedReposColumnName, ariaLabel: ReposListComponent.UnpinnedReposColumnName,
minWidth: ReposListRepoColumnMinWidth, minWidth: ReposListRepoColumnMinWidth,
onRender: this.onRenderUnpinnedReposColumnItem, onRender: this.onRenderUnpinnedReposColumnItem
}, },
{ {
key: ReposListComponent.BranchesColumnName, key: ReposListComponent.BranchesColumnName,
@@ -117,13 +117,13 @@ export class ReposListComponent extends React.Component<ReposListComponentProps>
ariaLabel: ReposListComponent.BranchesColumnName, ariaLabel: ReposListComponent.BranchesColumnName,
minWidth: ReposListBranchesColumnWidth, minWidth: ReposListBranchesColumnWidth,
maxWidth: ReposListBranchesColumnWidth, maxWidth: ReposListBranchesColumnWidth,
onRender: this.onRenderUnpinnedReposBranchesColumnItem, onRender: this.onRenderUnpinnedReposBranchesColumnItem
}, }
], ],
onRenderDetailsFooter: onRenderDetailsFooter:
this.props.unpinnedReposProps.isLoading || this.props.unpinnedReposProps.hasMore this.props.unpinnedReposProps.isLoading || this.props.unpinnedReposProps.hasMore
? this.onRenderReposFooter ? this.onRenderReposFooter
: undefined, : undefined
}; };
return ( return (
@@ -143,7 +143,7 @@ export class ReposListComponent extends React.Component<ReposListComponentProps>
...ReposListComponent.getCheckboxPropsForLabel(GitHubUtils.toRepoFullName(item.repo.owner, item.repo.name)), ...ReposListComponent.getCheckboxPropsForLabel(GitHubUtils.toRepoFullName(item.repo.owner, item.repo.name)),
styles: ReposListCheckboxStyles, styles: ReposListCheckboxStyles,
defaultChecked: true, defaultChecked: true,
onChange: () => this.props.unpinRepo(item), onChange: () => this.props.unpinRepo(item)
}; };
return <Checkbox {...checkboxProps} />; return <Checkbox {...checkboxProps} />;
@@ -155,12 +155,12 @@ export class ReposListComponent extends React.Component<ReposListComponentProps>
} }
const branchesProps = this.props.branchesProps[GitHubUtils.toRepoFullName(item.repo.owner, item.repo.name)]; const branchesProps = this.props.branchesProps[GitHubUtils.toRepoFullName(item.repo.owner, item.repo.name)];
const options: IDropdownOption[] = branchesProps.branches.map((branch) => ({ const options: IDropdownOption[] = branchesProps.branches.map(branch => ({
key: branch.name, key: branch.name,
text: branch.name, text: branch.name,
data: item, data: item,
disabled: item.branches.length === 1 && branch.name === item.branches[0].name, disabled: item.branches.length === 1 && branch.name === item.branches[0].name,
selected: item.branches.findIndex((element) => element.name === branch.name) !== -1, selected: item.branches.findIndex(element => element.name === branch.name) !== -1
})); }));
if (branchesProps.hasMore || branchesProps.isLoading) { if (branchesProps.hasMore || branchesProps.isLoading) {
@@ -169,7 +169,7 @@ export class ReposListComponent extends React.Component<ReposListComponentProps>
key: text, key: text,
text, text,
data: item, data: item,
index: ReposListComponent.FooterIndex, index: ReposListComponent.FooterIndex
}); });
} }
@@ -178,7 +178,7 @@ export class ReposListComponent extends React.Component<ReposListComponentProps>
dropdownWidth: BranchesDropdownWidth, dropdownWidth: BranchesDropdownWidth,
responsiveMode: ResponsiveMode.large, responsiveMode: ResponsiveMode.large,
options, options,
onRenderList: this.onRenderBranchesDropdownList, onRenderList: this.onRenderBranchesDropdownList
}; };
if (item.branches.length === 1) { if (item.branches.length === 1) {
@@ -199,7 +199,7 @@ export class ReposListComponent extends React.Component<ReposListComponentProps>
styles: BranchesDropdownStyles, styles: BranchesDropdownStyles,
options: [], options: [],
placeholder: ReposListComponent.DefaultBranchName, placeholder: ReposListComponent.DefaultBranchName,
disabled: true, disabled: true
}; };
return <Dropdown {...dropdownProps} />; return <Dropdown {...dropdownProps} />;
@@ -228,7 +228,7 @@ export class ReposListComponent extends React.Component<ReposListComponentProps>
if (option.index === ReposListComponent.FooterIndex) { if (option.index === ReposListComponent.FooterIndex) {
const linkProps: ILinkProps = { const linkProps: ILinkProps = {
disabled: branchesProps.isLoading, disabled: branchesProps.isLoading,
onClick: branchesProps.loadMore, onClick: branchesProps.loadMore
}; };
return <Link {...linkProps}>{option.text}</Link>; return <Link {...linkProps}>{option.text}</Link>;
@@ -242,13 +242,13 @@ export class ReposListComponent extends React.Component<ReposListComponentProps>
onChange: (event, checked) => { onChange: (event, checked) => {
const repoListItem = { ...item }; const repoListItem = { ...item };
const branch: IGitHubBranch = { name: option.text }; const branch: IGitHubBranch = { name: option.text };
repoListItem.branches = repoListItem.branches.filter((element) => element.name !== branch.name); repoListItem.branches = repoListItem.branches.filter(element => element.name !== branch.name);
if (checked) { if (checked) {
repoListItem.branches.push(branch); repoListItem.branches.push(branch);
} }
this.props.pinRepo(repoListItem); this.props.pinRepo(repoListItem);
}, }
}; };
return <Checkbox {...checkboxProps} />; return <Checkbox {...checkboxProps} />;
@@ -258,7 +258,7 @@ export class ReposListComponent extends React.Component<ReposListComponentProps>
if (index === ReposListComponent.FooterIndex) { if (index === ReposListComponent.FooterIndex) {
const linkProps: ILinkProps = { const linkProps: ILinkProps = {
disabled: this.props.unpinnedReposProps.isLoading, disabled: this.props.unpinnedReposProps.isLoading,
onClick: this.props.unpinnedReposProps.loadMore, onClick: this.props.unpinnedReposProps.loadMore
}; };
const linkText = this.props.unpinnedReposProps.isLoading const linkText = this.props.unpinnedReposProps.isLoading
@@ -274,7 +274,7 @@ export class ReposListComponent extends React.Component<ReposListComponentProps>
const repoListItem = { ...item }; const repoListItem = { ...item };
repoListItem.branches = [{ name: ReposListComponent.DefaultBranchName }]; repoListItem.branches = [{ name: ReposListComponent.DefaultBranchName }];
this.props.pinRepo(repoListItem); this.props.pinRepo(repoListItem);
}, }
}; };
return <Checkbox {...checkboxProps} />; return <Checkbox {...checkboxProps} />;
@@ -284,7 +284,7 @@ export class ReposListComponent extends React.Component<ReposListComponentProps>
const props: IDetailsRowBaseProps = { const props: IDetailsRowBaseProps = {
...detailsFooterProps, ...detailsFooterProps,
item: {}, item: {},
itemIndex: ReposListComponent.FooterIndex, itemIndex: ReposListComponent.FooterIndex
}; };
return <DetailsRow {...props} />; return <DetailsRow {...props} />;
@@ -294,7 +294,7 @@ export class ReposListComponent extends React.Component<ReposListComponentProps>
return { return {
label, label,
title: label, title: label,
ariaLabel: label, ariaLabel: label
}; };
} }

View File

@@ -28,7 +28,7 @@ export class InputTypeaheadComponent {
constructor() { constructor() {
return { return {
viewModel: InputTypeaheadViewModel, viewModel: InputTypeaheadViewModel,
template, template
}; };
} }
} }
@@ -109,7 +109,7 @@ class InputTypeaheadViewModel {
this.cache = { this.cache = {
inputValue: null, inputValue: null,
selection: null, selection: null
}; };
} }
@@ -128,7 +128,7 @@ class InputTypeaheadViewModel {
display: "caption", display: "caption",
data: () => { data: () => {
return this.params.choices(); return this.params.choices();
}, }
}, },
callback: { callback: {
onClick: (node: any, a: any, item: OnClickItem, event: any) => { onClick: (node: any, a: any, item: OnClickItem, event: any) => {
@@ -143,7 +143,7 @@ class InputTypeaheadViewModel {
if (params.inputValue) { if (params.inputValue) {
params.inputValue(query); params.inputValue(query);
} }
}, }
}, },
template: (query: string, item: any) => { template: (query: string, item: any) => {
// Don't display id if caption *IS* the id // Don't display id if caption *IS* the id
@@ -151,7 +151,7 @@ class InputTypeaheadViewModel {
? "<span>{{caption}}</span>" ? "<span>{{caption}}</span>"
: "<span><div>{{caption}}</div><div><small>{{value}}</small></div></span>"; : "<span><div>{{caption}}</div><div><small>{{value}}</small></div></span>";
}, },
dynamic: true, dynamic: true
}; };
// Override options // Override options

View File

@@ -8,10 +8,10 @@ describe("inputTypeahead", () => {
const props: InputTypeaheadComponentProps = { const props: InputTypeaheadComponentProps = {
choices: [ choices: [
{ caption: "item1", value: "value1" }, { caption: "item1", value: "value1" },
{ caption: "item2", value: "value2" }, { caption: "item2", value: "value2" }
], ],
placeholder: "placeholder", placeholder: "placeholder",
useTextarea: false, useTextarea: false
}; };
const wrapper = shallow(<InputTypeaheadComponent {...props} />); const wrapper = shallow(<InputTypeaheadComponent {...props} />);
@@ -22,10 +22,10 @@ describe("inputTypeahead", () => {
const props: InputTypeaheadComponentProps = { const props: InputTypeaheadComponentProps = {
choices: [ choices: [
{ caption: "item1", value: "value1" }, { caption: "item1", value: "value1" },
{ caption: "item2", value: "value2" }, { caption: "item2", value: "value2" }
], ],
placeholder: "placeholder", placeholder: "placeholder",
useTextarea: true, useTextarea: true
}; };
const wrapper = shallow(<InputTypeaheadComponent {...props} />); const wrapper = shallow(<InputTypeaheadComponent {...props} />);

View File

@@ -103,7 +103,7 @@ export class InputTypeaheadComponent extends React.Component<
super(props); super(props);
this.cache = { this.cache = {
inputValue: null, inputValue: null,
selection: null, selection: null
}; };
} }
@@ -138,7 +138,7 @@ export class InputTypeaheadComponent extends React.Component<
className="input-typehead" className="input-typehead"
onKeyDown={(event: React.KeyboardEvent<HTMLDivElement>) => this.onKeyDown(event)} onKeyDown={(event: React.KeyboardEvent<HTMLDivElement>) => this.onKeyDown(event)}
> >
<div className="typeahead__container" ref={(input) => (this.containerElt = input)}> <div className="typeahead__container" ref={input => (this.containerElt = input)}>
<div className="typeahead__field"> <div className="typeahead__field">
<span className="typeahead__query"> <span className="typeahead__query">
{this.props.useTextarea ? ( {this.props.useTextarea ? (
@@ -147,7 +147,7 @@ export class InputTypeaheadComponent extends React.Component<
name="q" name="q"
autoComplete="off" autoComplete="off"
aria-label="Input query" aria-label="Input query"
ref={(input) => (this.inputElt = input)} ref={input => (this.inputElt = input)}
defaultValue={this.props.defaultValue} defaultValue={this.props.defaultValue}
/> />
) : ( ) : (
@@ -156,7 +156,7 @@ export class InputTypeaheadComponent extends React.Component<
type="search" type="search"
autoComplete="off" autoComplete="off"
aria-label="Input query" aria-label="Input query"
ref={(input) => (this.inputElt = input)} ref={input => (this.inputElt = input)}
defaultValue={this.props.defaultValue} defaultValue={this.props.defaultValue}
/> />
)} )}
@@ -181,7 +181,9 @@ export class InputTypeaheadComponent extends React.Component<
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
this.props.submitFct(this.cache.inputValue, this.cache.selection); this.props.submitFct(this.cache.inputValue, this.cache.selection);
$(this.containerElt).children(".typeahead__result").hide(); $(this.containerElt)
.children(".typeahead__result")
.hide();
} }
} }
} }
@@ -201,7 +203,7 @@ export class InputTypeaheadComponent extends React.Component<
display: "caption", display: "caption",
data: () => { data: () => {
return props.choices; return props.choices;
}, }
}, },
callback: { callback: {
onClick: (node: any, a: any, item: OnClickItem, event: any) => { onClick: (node: any, a: any, item: OnClickItem, event: any) => {
@@ -216,7 +218,7 @@ export class InputTypeaheadComponent extends React.Component<
if (props.onNewValue) { if (props.onNewValue) {
props.onNewValue(query); props.onNewValue(query);
} }
}, }
}, },
template: (query: string, item: any) => { template: (query: string, item: any) => {
// Don't display id if caption *IS* the id // Don't display id if caption *IS* the id
@@ -224,7 +226,7 @@ export class InputTypeaheadComponent extends React.Component<
? "<span>{{caption}}</span>" ? "<span>{{caption}}</span>"
: "<span><div>{{caption}}</div><div><small>{{value}}</small></div></span>"; : "<span><div>{{caption}}</div><div><small>{{value}}</small></div></span>";
}, },
dynamic: true, dynamic: true
}; };
// Override options // Override options

View File

@@ -11,7 +11,7 @@ export class JsonEditorComponent {
constructor() { constructor() {
return { return {
viewModel: JsonEditorViewModel, viewModel: JsonEditorViewModel,
template, template
}; };
} }
} }
@@ -96,7 +96,7 @@ export class JsonEditorViewModel extends WaitsForTemplateViewModel {
lineNumbers: this.params.lineNumbers || "off", lineNumbers: this.params.lineNumbers || "off",
fontSize: 12, fontSize: 12,
ariaLabel: this.params.ariaLabel, ariaLabel: this.params.ariaLabel,
theme: this.params.theme, theme: this.params.theme
}; };
this.editorContainer.innerHTML = ""; this.editorContainer.innerHTML = "";
@@ -145,7 +145,7 @@ export class JsonEditorViewModel extends WaitsForTemplateViewModel {
this.observer.observe(document.body, { this.observer.observe(document.body, {
attributes: true, attributes: true,
subtree: true, subtree: true,
childList: true, childList: true
}); });
this.editor.getModel().onDidChangeContent(async (e: monaco.editor.IModelContentChangedEvent) => { this.editor.getModel().onDidChangeContent(async (e: monaco.editor.IModelContentChangedEvent) => {

View File

@@ -24,7 +24,7 @@ export function ClusterLibraryGrid(props: ClusterLibraryGridProps): JSX.Element
key: "name", key: "name",
name: "Name", name: "Name",
fieldName: "name", fieldName: "name",
minWidth: 150, minWidth: 150
}, },
{ {
key: "installed", key: "installed",
@@ -32,8 +32,8 @@ export function ClusterLibraryGrid(props: ClusterLibraryGridProps): JSX.Element
minWidth: 100, minWidth: 100,
onRender: (item: ClusterLibraryItem) => { onRender: (item: ClusterLibraryItem) => {
return <input type="checkbox" checked={item.installed} onChange={onInstalledChanged} data-name={item.name} />; return <input type="checkbox" checked={item.installed} onChange={onInstalledChanged} data-name={item.name} />;
}, }
}, }
]; ];
return <DetailsList columns={columns} items={props.libraryItems} selectionMode={SelectionMode.none} />; return <DetailsList columns={columns} items={props.libraryItems} selectionMode={SelectionMode.none} />;

View File

@@ -19,7 +19,7 @@ export interface LibraryManageComponentProps {
export function LibraryManageComponent(props: LibraryManageComponentProps): JSX.Element { export function LibraryManageComponent(props: LibraryManageComponentProps): JSX.Element {
const { const {
addProps: { nameProps, urlProps, buttonProps }, addProps: { nameProps, urlProps, buttonProps },
gridProps, gridProps
} = props; } = props;
return ( return (
<div> <div>
@@ -47,7 +47,7 @@ function LibraryManageGrid(props: LibraryManageGridProps): JSX.Element {
key: "name", key: "name",
name: "Name", name: "Name",
fieldName: "name", fieldName: "name",
minWidth: 200, minWidth: 200
}, },
{ {
key: "delete", key: "delete",
@@ -62,8 +62,8 @@ function LibraryManageGrid(props: LibraryManageGridProps): JSX.Element {
<img src={DeleteIcon} alt="Delete" onClick={onDelete} /> <img src={DeleteIcon} alt="Delete" onClick={onDelete} />
</span> </span>
); );
}, }
}, }
]; ];
return <DetailsList columns={columns} items={props.items} selectionMode={SelectionMode.none} />; return <DetailsList columns={columns} items={props.items} selectionMode={SelectionMode.none} />;
} }

View File

@@ -11,10 +11,10 @@ const createTestDatabaseAccount = (): DataModels.DatabaseAccount => {
cassandraEndpoint: null, cassandraEndpoint: null,
documentEndpoint: "https://testDocumentEndpoint.azure.com/", documentEndpoint: "https://testDocumentEndpoint.azure.com/",
gremlinEndpoint: null, gremlinEndpoint: null,
tableEndpoint: null, tableEndpoint: null
}, },
tags: "testTags", tags: "testTags",
type: "testType", type: "testType"
}; };
}; };
@@ -28,10 +28,10 @@ const createTestMongo32DatabaseAccount = (): DataModels.DatabaseAccount => {
cassandraEndpoint: null, cassandraEndpoint: null,
documentEndpoint: "https://testDocumentEndpoint.azure.com/", documentEndpoint: "https://testDocumentEndpoint.azure.com/",
gremlinEndpoint: null, gremlinEndpoint: null,
tableEndpoint: null, tableEndpoint: null
}, },
tags: "testTags", tags: "testTags",
type: "testType", type: "testType"
}; };
}; };
@@ -46,10 +46,10 @@ const createTestMongo36DatabaseAccount = (): DataModels.DatabaseAccount => {
documentEndpoint: "https://testDocumentEndpoint.azure.com/", documentEndpoint: "https://testDocumentEndpoint.azure.com/",
gremlinEndpoint: null, gremlinEndpoint: null,
tableEndpoint: null, tableEndpoint: null,
mongoEndpoint: "https://testMongoEndpoint.azure.com/", mongoEndpoint: "https://testMongoEndpoint.azure.com/"
}, },
tags: "testTags", tags: "testTags",
type: "testType", type: "testType"
}; };
}; };
@@ -63,10 +63,10 @@ const createTestCassandraDatabaseAccount = (): DataModels.DatabaseAccount => {
cassandraEndpoint: "https://testCassandraEndpoint.azure.com/", cassandraEndpoint: "https://testCassandraEndpoint.azure.com/",
documentEndpoint: null, documentEndpoint: null,
gremlinEndpoint: null, gremlinEndpoint: null,
tableEndpoint: null, tableEndpoint: null
}, },
tags: "testTags", tags: "testTags",
type: "testType", type: "testType"
}; };
}; };
@@ -74,9 +74,9 @@ const createTerminal = (): NotebookTerminalComponent => {
return new NotebookTerminalComponent({ return new NotebookTerminalComponent({
notebookServerInfo: { notebookServerInfo: {
authToken: "testAuthToken", authToken: "testAuthToken",
notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com/", notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com/"
}, },
databaseAccount: createTestDatabaseAccount(), databaseAccount: createTestDatabaseAccount()
}); });
}; };
@@ -84,9 +84,9 @@ const createMongo32Terminal = (): NotebookTerminalComponent => {
return new NotebookTerminalComponent({ return new NotebookTerminalComponent({
notebookServerInfo: { notebookServerInfo: {
authToken: "testAuthToken", authToken: "testAuthToken",
notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com/mongo", notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com/mongo"
}, },
databaseAccount: createTestMongo32DatabaseAccount(), databaseAccount: createTestMongo32DatabaseAccount()
}); });
}; };
@@ -94,9 +94,9 @@ const createMongo36Terminal = (): NotebookTerminalComponent => {
return new NotebookTerminalComponent({ return new NotebookTerminalComponent({
notebookServerInfo: { notebookServerInfo: {
authToken: "testAuthToken", authToken: "testAuthToken",
notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com/mongo", notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com/mongo"
}, },
databaseAccount: createTestMongo36DatabaseAccount(), databaseAccount: createTestMongo36DatabaseAccount()
}); });
}; };
@@ -104,9 +104,9 @@ const createCassandraTerminal = (): NotebookTerminalComponent => {
return new NotebookTerminalComponent({ return new NotebookTerminalComponent({
notebookServerInfo: { notebookServerInfo: {
authToken: "testAuthToken", authToken: "testAuthToken",
notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com/cassandra", notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com/cassandra"
}, },
databaseAccount: createTestCassandraDatabaseAccount(), databaseAccount: createTestCassandraDatabaseAccount()
}); });
}; };
@@ -127,7 +127,7 @@ describe("NotebookTerminalComponent", () => {
expect(params).toEqual( expect(params).toEqual(
new Map<string, string>([ new Map<string, string>([
["terminal", "true"], ["terminal", "true"],
["terminalEndpoint", new URL(terminal.props.databaseAccount.properties.documentEndpoint).host], ["terminalEndpoint", new URL(terminal.props.databaseAccount.properties.documentEndpoint).host]
]) ])
); );
}); });
@@ -139,7 +139,7 @@ describe("NotebookTerminalComponent", () => {
expect(params).toEqual( expect(params).toEqual(
new Map<string, string>([ new Map<string, string>([
["terminal", "true"], ["terminal", "true"],
["terminalEndpoint", new URL(terminal.props.databaseAccount.properties.mongoEndpoint).host], ["terminalEndpoint", new URL(terminal.props.databaseAccount.properties.mongoEndpoint).host]
]) ])
); );
}); });
@@ -151,7 +151,7 @@ describe("NotebookTerminalComponent", () => {
expect(params).toEqual( expect(params).toEqual(
new Map<string, string>([ new Map<string, string>([
["terminal", "true"], ["terminal", "true"],
["terminalEndpoint", new URL(terminal.props.databaseAccount.properties.cassandraEndpoint).host], ["terminalEndpoint", new URL(terminal.props.databaseAccount.properties.cassandraEndpoint).host]
]) ])
); );
}); });

View File

@@ -4,22 +4,22 @@ import { IIconStyles, ITextStyles } from "office-ui-fabric-react";
export const siteTextStyles: ITextStyles = { export const siteTextStyles: ITextStyles = {
root: { root: {
color: "#025F52", color: "#025F52",
fontWeight: FontWeights.semibold, fontWeight: FontWeights.semibold
}, }
}; };
export const descriptionTextStyles: ITextStyles = { export const descriptionTextStyles: ITextStyles = {
root: { root: {
color: "#333333", color: "#333333",
fontWeight: FontWeights.semibold, fontWeight: FontWeights.semibold
}, }
}; };
export const subtleHelpfulTextStyles: ITextStyles = { export const subtleHelpfulTextStyles: ITextStyles = {
root: { root: {
color: "#ccc", color: "#ccc",
fontWeight: FontWeights.regular, fontWeight: FontWeights.regular
}, }
}; };
export const iconButtonStyles: IIconStyles = { export const iconButtonStyles: IIconStyles = {
@@ -32,10 +32,10 @@ export const iconButtonStyles: IIconStyles = {
display: "inline-block", display: "inline-block",
selectors: { selectors: {
":hover .ms-Button-icon": { ":hover .ms-Button-icon": {
color: "#ccc", color: "#ccc"
}, }
}, }
}, }
}; };
export const iconStyles: IIconStyles = { export const iconStyles: IIconStyles = {
@@ -45,28 +45,28 @@ export const iconStyles: IIconStyles = {
backgroundColor: "#FFF", backgroundColor: "#FFF",
fontSize: 16, fontSize: 16,
fontWeight: FontWeights.regular, fontWeight: FontWeights.regular,
display: "inline-block", display: "inline-block"
}, }
}; };
export const mainHelpfulTextStyles: ITextStyles = { export const mainHelpfulTextStyles: ITextStyles = {
root: { root: {
color: "#000", color: "#000",
fontWeight: FontWeights.regular, fontWeight: FontWeights.regular
}, }
}; };
export const subtleIconStyles: IIconStyles = { export const subtleIconStyles: IIconStyles = {
root: { root: {
color: "#ddd", color: "#ddd",
fontSize: 12, fontSize: 12,
fontWeight: FontWeights.regular, fontWeight: FontWeights.regular
}, }
}; };
export const helpfulTextStyles: ITextStyles = { export const helpfulTextStyles: ITextStyles = {
root: { root: {
color: "#333333", color: "#333333",
fontWeight: FontWeights.regular, fontWeight: FontWeights.regular
}, }
}; };

View File

@@ -17,7 +17,7 @@ describe("GalleryCardComponent", () => {
isSample: false, isSample: false,
downloads: 0, downloads: 0,
favorites: 0, favorites: 0,
views: 0, views: 0
}, },
isFavorite: false, isFavorite: false,
showDelete: true, showDelete: true,
@@ -26,7 +26,7 @@ describe("GalleryCardComponent", () => {
onFavoriteClick: undefined, onFavoriteClick: undefined,
onUnfavoriteClick: undefined, onUnfavoriteClick: undefined,
onDownloadClick: undefined, onDownloadClick: undefined,
onDeleteClick: undefined, onDeleteClick: undefined
}; };
const wrapper = shallow(<GalleryCardComponent {...props} />); const wrapper = shallow(<GalleryCardComponent {...props} />);

View File

@@ -12,11 +12,12 @@ import {
Button, Button,
LinkBase, LinkBase,
Separator, Separator,
TooltipHost, TooltipHost
} from "office-ui-fabric-react"; } from "office-ui-fabric-react";
import * as React from "react"; import * as React from "react";
import { IGalleryItem } from "../../../../Juno/JunoClient"; import { IGalleryItem } from "../../../../Juno/JunoClient";
import { FileSystemUtil } from "../../../Notebook/FileSystemUtil"; import { FileSystemUtil } from "../../../Notebook/FileSystemUtil";
import CosmosDBLogo from "../../../../../images/CosmosDB-logo.svg";
export interface GalleryCardComponentProps { export interface GalleryCardComponentProps {
data: IGalleryItem; data: IGalleryItem;
@@ -40,14 +41,14 @@ export class GalleryCardComponent extends React.Component<GalleryCardComponentPr
width: GalleryCardComponent.CARD_WIDTH, width: GalleryCardComponent.CARD_WIDTH,
height: GalleryCardComponent.CARD_HEIGHT, height: GalleryCardComponent.CARD_HEIGHT,
childrenGap: 8, childrenGap: 8,
childrenMargin: 10, childrenMargin: 10
}; };
public render(): JSX.Element { public render(): JSX.Element {
const options: Intl.DateTimeFormatOptions = { const options: Intl.DateTimeFormatOptions = {
year: "numeric", year: "numeric",
month: "short", month: "short",
day: "numeric", day: "numeric"
}; };
const dateString = new Date(this.props.data.created).toLocaleString("default", options); const dateString = new Date(this.props.data.created).toLocaleString("default", options);
@@ -55,7 +56,11 @@ export class GalleryCardComponent extends React.Component<GalleryCardComponentPr
return ( return (
<Card aria-label="Notebook Card" tokens={GalleryCardComponent.cardTokens} onClick={this.props.onClick}> <Card aria-label="Notebook Card" tokens={GalleryCardComponent.cardTokens} onClick={this.props.onClick}>
<Card.Item> <Card.Item>
<Persona text={this.props.data.author} secondaryText={dateString} /> <Persona
imageUrl={this.props.data.isSample && CosmosDBLogo}
text={this.props.data.author}
secondaryText={dateString}
/>
</Card.Item> </Card.Item>
<Card.Item fill> <Card.Item fill>
@@ -89,15 +94,9 @@ export class GalleryCardComponent extends React.Component<GalleryCardComponentPr
</Card.Section> </Card.Section>
<Card.Section horizontal styles={{ root: { alignItems: "flex-end" } }}> <Card.Section horizontal styles={{ root: { alignItems: "flex-end" } }}>
<Text variant="tiny" styles={{ root: { color: "#ccc" } }}> {this.generateIconText("RedEye", this.props.data.views.toString())}
<Icon iconName="RedEye" styles={{ root: { verticalAlign: "middle" } }} /> {this.props.data.views} {this.generateIconText("Download", this.props.data.downloads.toString())}
</Text> {this.props.isFavorite !== undefined && this.generateIconText("Heart", this.props.data.favorites.toString())}
<Text variant="tiny" styles={{ root: { color: "#ccc" } }}>
<Icon iconName="Download" styles={{ root: { verticalAlign: "middle" } }} /> {this.props.data.downloads}
</Text>
<Text variant="tiny" styles={{ root: { color: "#ccc" } }}>
<Icon iconName="Heart" styles={{ root: { verticalAlign: "middle" } }} /> {this.props.data.favorites}
</Text>
</Card.Section> </Card.Section>
<Card.Item> <Card.Item>
@@ -105,7 +104,8 @@ export class GalleryCardComponent extends React.Component<GalleryCardComponentPr
</Card.Item> </Card.Item>
<Card.Section horizontal styles={{ root: { marginTop: 0 } }}> <Card.Section horizontal styles={{ root: { marginTop: 0 } }}>
{this.generateIconButtonWithTooltip( {this.props.isFavorite !== undefined &&
this.generateIconButtonWithTooltip(
this.props.isFavorite ? "HeartFill" : "Heart", this.props.isFavorite ? "HeartFill" : "Heart",
this.props.isFavorite ? "Unlike" : "Like", this.props.isFavorite ? "Unlike" : "Like",
this.props.isFavorite ? this.onUnfavoriteClick : this.onFavoriteClick this.props.isFavorite ? this.onUnfavoriteClick : this.onFavoriteClick
@@ -115,7 +115,7 @@ export class GalleryCardComponent extends React.Component<GalleryCardComponentPr
{this.props.showDelete && ( {this.props.showDelete && (
<div style={{ width: "100%", textAlign: "right" }}> <div style={{ width: "100%", textAlign: "right" }}>
{this.generateIconButtonWithTooltip("Delete", "Remove", this.props.onDeleteClick)} {this.generateIconButtonWithTooltip("Delete", "Remove", this.onDeleteClick)}
</div> </div>
)} )}
</Card.Section> </Card.Section>
@@ -123,6 +123,14 @@ export class GalleryCardComponent extends React.Component<GalleryCardComponentPr
); );
} }
private generateIconText = (iconName: string, text: string): JSX.Element => {
return (
<Text variant="tiny" styles={{ root: { color: "#ccc" } }}>
<Icon iconName={iconName} styles={{ root: { verticalAlign: "middle" } }} /> {text}
</Text>
);
};
/* /*
* Fluent UI doesn't support tooltips on IconButtons out of the box. In the meantime the recommendation is * Fluent UI doesn't support tooltips on IconButtons out of the box. In the meantime the recommendation is
* to do the following (from https://developer.microsoft.com/en-us/fluentui#/controls/web/button) * to do the following (from https://developer.microsoft.com/en-us/fluentui#/controls/web/button)

View File

@@ -14,6 +14,7 @@ exports[`GalleryCardComponent renders 1`] = `
> >
<CardItem> <CardItem>
<StyledPersonaBase <StyledPersonaBase
imageUrl={false}
secondaryText="Invalid Date" secondaryText="Invalid Date"
text="author" text="author"
/> />
@@ -256,6 +257,7 @@ exports[`GalleryCardComponent renders 1`] = `
"iconName": "Delete", "iconName": "Delete",
} }
} }
onClick={[Function]}
title="Remove" title="Remove"
/> />
</StyledTooltipHostBase> </StyledTooltipHostBase>

View File

@@ -11,7 +11,7 @@ describe("GalleryViewerComponent", () => {
searchText: undefined, searchText: undefined,
onSelectedTabChange: undefined, onSelectedTabChange: undefined,
onSortByChange: undefined, onSortByChange: undefined,
onSearchTextChange: undefined, onSearchTextChange: undefined
}; };
const wrapper = shallow(<GalleryViewerComponent {...props} />); const wrapper = shallow(<GalleryViewerComponent {...props} />);

View File

@@ -11,7 +11,7 @@ import {
Pivot, Pivot,
PivotItem, PivotItem,
SearchBox, SearchBox,
Stack, Stack
} from "office-ui-fabric-react"; } from "office-ui-fabric-react";
import * as React from "react"; import * as React from "react";
import * as Logger from "../../../Common/Logger"; import * as Logger from "../../../Common/Logger";
@@ -40,14 +40,14 @@ export enum GalleryTab {
OfficialSamples, OfficialSamples,
PublicGallery, PublicGallery,
Favorites, Favorites,
Published, Published
} }
export enum SortBy { export enum SortBy {
MostViewed, MostViewed,
MostDownloaded, MostDownloaded,
MostFavorited, MostFavorited,
MostRecent, MostRecent
} }
interface GalleryViewerComponentState { interface GalleryViewerComponentState {
@@ -75,27 +75,10 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
private static readonly mostViewedText = "Most viewed"; private static readonly mostViewedText = "Most viewed";
private static readonly mostDownloadedText = "Most downloaded"; private static readonly mostDownloadedText = "Most downloaded";
private static readonly mostFavoritedText = "Most favorited"; private static readonly mostFavoritedText = "Most liked";
private static readonly mostRecentText = "Most recent"; private static readonly mostRecentText = "Most recent";
private static readonly sortingOptions: IDropdownOption[] = [ private readonly sortingOptions: IDropdownOption[];
{
key: SortBy.MostViewed,
text: GalleryViewerComponent.mostViewedText,
},
{
key: SortBy.MostDownloaded,
text: GalleryViewerComponent.mostDownloadedText,
},
{
key: SortBy.MostFavorited,
text: GalleryViewerComponent.mostFavoritedText,
},
{
key: SortBy.MostRecent,
text: GalleryViewerComponent.mostRecentText,
},
];
private sampleNotebooks: IGalleryItem[]; private sampleNotebooks: IGalleryItem[];
private publicNotebooks: IGalleryItem[]; private publicNotebooks: IGalleryItem[];
@@ -115,11 +98,32 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
selectedTab: props.selectedTab, selectedTab: props.selectedTab,
sortBy: props.sortBy, sortBy: props.sortBy,
searchText: props.searchText, searchText: props.searchText,
dialogProps: undefined, dialogProps: undefined
}; };
this.sortingOptions = [
{
key: SortBy.MostViewed,
text: GalleryViewerComponent.mostViewedText
},
{
key: SortBy.MostDownloaded,
text: GalleryViewerComponent.mostDownloadedText
},
{
key: SortBy.MostRecent,
text: GalleryViewerComponent.mostRecentText
}
];
if (this.props.container?.isGalleryPublishEnabled()) {
this.sortingOptions.push({
key: SortBy.MostFavorited,
text: GalleryViewerComponent.mostFavoritedText
});
}
this.loadTabContent(this.state.selectedTab, this.state.searchText, this.state.sortBy, false); this.loadTabContent(this.state.selectedTab, this.state.searchText, this.state.sortBy, false);
if (this.props.container) { if (this.props.container?.isGalleryPublishEnabled()) {
this.loadFavoriteNotebooks(this.state.searchText, this.state.sortBy, false); // Need this to show correct favorite button state this.loadFavoriteNotebooks(this.state.searchText, this.state.sortBy, false); // Need this to show correct favorite button state
} }
} }
@@ -131,28 +135,22 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
public render(): JSX.Element { public render(): JSX.Element {
const tabs: GalleryTabInfo[] = [this.createTab(GalleryTab.OfficialSamples, this.state.sampleNotebooks)]; const tabs: GalleryTabInfo[] = [this.createTab(GalleryTab.OfficialSamples, this.state.sampleNotebooks)];
if (this.props.container) { if (this.props.container?.isGalleryPublishEnabled()) {
if (this.props.container.isGalleryPublishEnabled()) {
tabs.push(this.createTab(GalleryTab.PublicGallery, this.state.publicNotebooks)); tabs.push(this.createTab(GalleryTab.PublicGallery, this.state.publicNotebooks));
}
tabs.push(this.createTab(GalleryTab.Favorites, this.state.favoriteNotebooks)); tabs.push(this.createTab(GalleryTab.Favorites, this.state.favoriteNotebooks));
if (this.props.container.isGalleryPublishEnabled()) {
tabs.push(this.createTab(GalleryTab.Published, this.state.publishedNotebooks)); tabs.push(this.createTab(GalleryTab.Published, this.state.publishedNotebooks));
} }
}
const pivotProps: IPivotProps = { const pivotProps: IPivotProps = {
onLinkClick: this.onPivotChange, onLinkClick: this.onPivotChange,
selectedKey: GalleryTab[this.state.selectedTab], selectedKey: GalleryTab[this.state.selectedTab]
}; };
const pivotItems = tabs.map((tab) => { const pivotItems = tabs.map(tab => {
const pivotItemProps: IPivotItemProps = { const pivotItemProps: IPivotItemProps = {
itemKey: GalleryTab[tab.tab], itemKey: GalleryTab[tab.tab],
style: { marginTop: 20 }, style: { marginTop: 20 },
headerText: GalleryUtils.getTabTitle(tab.tab), headerText: GalleryUtils.getTabTitle(tab.tab)
}; };
return ( return (
@@ -174,7 +172,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
private createTab(tab: GalleryTab, data: IGalleryItem[]): GalleryTabInfo { private createTab(tab: GalleryTab, data: IGalleryItem[]): GalleryTabInfo {
return { return {
tab, tab,
content: this.createTabContent(data), content: this.createTabContent(data)
}; };
} }
@@ -189,11 +187,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
<Label>Sort by</Label> <Label>Sort by</Label>
</Stack.Item> </Stack.Item>
<Stack.Item styles={{ root: { minWidth: 200 } }}> <Stack.Item styles={{ root: { minWidth: 200 } }}>
<Dropdown <Dropdown options={this.sortingOptions} selectedKey={this.state.sortBy} onChange={this.onDropdownChange} />
options={GalleryViewerComponent.sortingOptions}
selectedKey={this.state.sortBy}
onChange={this.onDropdownChange}
/>
</Stack.Item> </Stack.Item>
</Stack> </Stack>
@@ -255,7 +249,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
} }
this.setState({ this.setState({
sampleNotebooks: this.sampleNotebooks && [...this.sort(sortBy, this.search(searchText, this.sampleNotebooks))], sampleNotebooks: this.sampleNotebooks && [...this.sort(sortBy, this.search(searchText, this.sampleNotebooks))]
}); });
} }
@@ -276,7 +270,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
} }
this.setState({ this.setState({
publicNotebooks: this.publicNotebooks && [...this.sort(sortBy, this.search(searchText, this.publicNotebooks))], publicNotebooks: this.publicNotebooks && [...this.sort(sortBy, this.search(searchText, this.publicNotebooks))]
}); });
} }
@@ -298,8 +292,8 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
this.setState({ this.setState({
favoriteNotebooks: this.favoriteNotebooks && [ favoriteNotebooks: this.favoriteNotebooks && [
...this.sort(sortBy, this.search(searchText, this.favoriteNotebooks)), ...this.sort(sortBy, this.search(searchText, this.favoriteNotebooks))
], ]
}); });
// Refresh favorite button state // Refresh favorite button state
@@ -326,14 +320,14 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
this.setState({ this.setState({
publishedNotebooks: this.publishedNotebooks && [ publishedNotebooks: this.publishedNotebooks && [
...this.sort(sortBy, this.search(searchText, this.publishedNotebooks)), ...this.sort(sortBy, this.search(searchText, this.publishedNotebooks))
], ]
}); });
} }
private search(searchText: string, data: IGalleryItem[]): IGalleryItem[] { private search(searchText: string, data: IGalleryItem[]): IGalleryItem[] {
if (searchText) { if (searchText) {
return data?.filter((item) => this.isGalleryItemPresent(searchText, item)); return data?.filter(item => this.isGalleryItemPresent(searchText, item));
} }
return data; return data;
@@ -345,7 +339,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
item.author.toUpperCase(), item.author.toUpperCase(),
item.description.toUpperCase(), item.description.toUpperCase(),
item.name.toUpperCase(), item.name.toUpperCase(),
...item.tags?.map((tag) => tag.toUpperCase()), ...item.tags?.map(tag => tag.toUpperCase())
]; ];
for (const data of searchData) { for (const data of searchData) {
@@ -388,7 +382,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
} }
private replaceGalleryItem(item: IGalleryItem, items?: IGalleryItem[]): void { private replaceGalleryItem(item: IGalleryItem, items?: IGalleryItem[]): void {
const index = items?.findIndex((value) => value.id === item.id); const index = items?.findIndex(value => value.id === item.id);
if (index !== -1) { if (index !== -1) {
items?.splice(index, 1, item); items?.splice(index, 1, item);
} }
@@ -400,12 +394,15 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
return { return {
height: visibleRect.height, height: visibleRect.height,
itemCount: this.columnCount * this.rowCount, itemCount: this.columnCount * this.rowCount
}; };
}; };
private onRenderCell = (data?: IGalleryItem): JSX.Element => { private onRenderCell = (data?: IGalleryItem): JSX.Element => {
const isFavorite = this.favoriteNotebooks?.find((item) => item.id === data.id) !== undefined; let isFavorite: boolean;
if (this.props.container?.isGalleryPublishEnabled()) {
isFavorite = this.favoriteNotebooks?.find(item => item.id === data.id) !== undefined;
}
const props: GalleryCardComponentProps = { const props: GalleryCardComponentProps = {
data, data,
isFavorite, isFavorite,
@@ -415,7 +412,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
onFavoriteClick: () => this.favoriteItem(data), onFavoriteClick: () => this.favoriteItem(data),
onUnfavoriteClick: () => this.unfavoriteItem(data), onUnfavoriteClick: () => this.unfavoriteItem(data),
onDownloadClick: () => this.downloadItem(data), onDownloadClick: () => this.downloadItem(data),
onDeleteClick: () => this.deleteItem(data), onDeleteClick: () => this.deleteItem(data)
}; };
return ( return (
@@ -431,17 +428,18 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
} else { } else {
const params = new URLSearchParams({ const params = new URLSearchParams({
[GalleryUtils.NotebookViewerParams.NotebookUrl]: this.props.junoClient.getNotebookContentUrl(data.id), [GalleryUtils.NotebookViewerParams.NotebookUrl]: this.props.junoClient.getNotebookContentUrl(data.id),
[GalleryUtils.NotebookViewerParams.GalleryItemId]: data.id, [GalleryUtils.NotebookViewerParams.GalleryItemId]: data.id
}); });
window.open(`/notebookViewer.html?${params.toString()}`); const location = new URL("./notebookViewer.html", window.location.href).href;
window.open(`${location}?${params.toString()}`);
} }
}; };
private loadTaggedItems = (tag: string): void => { private loadTaggedItems = (tag: string): void => {
const searchText = tag; const searchText = tag;
this.setState({ this.setState({
searchText, searchText
}); });
this.loadTabContent(this.state.selectedTab, searchText, this.state.sortBy, true); this.loadTabContent(this.state.selectedTab, searchText, this.state.sortBy, true);
@@ -461,20 +459,20 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
private unfavoriteItem = async (data: IGalleryItem): Promise<void> => { private unfavoriteItem = async (data: IGalleryItem): Promise<void> => {
GalleryUtils.unfavoriteItem(this.props.container, this.props.junoClient, data, (item: IGalleryItem) => { GalleryUtils.unfavoriteItem(this.props.container, this.props.junoClient, data, (item: IGalleryItem) => {
this.favoriteNotebooks = this.favoriteNotebooks?.filter((value) => value.id !== item.id); this.favoriteNotebooks = this.favoriteNotebooks?.filter(value => value.id !== item.id);
this.refreshSelectedTab(item); this.refreshSelectedTab(item);
}); });
}; };
private downloadItem = async (data: IGalleryItem): Promise<void> => { private downloadItem = async (data: IGalleryItem): Promise<void> => {
GalleryUtils.downloadItem(this, this.props.container, this.props.junoClient, data, (item) => GalleryUtils.downloadItem(this, this.props.container, this.props.junoClient, data, item =>
this.refreshSelectedTab(item) this.refreshSelectedTab(item)
); );
}; };
private deleteItem = async (data: IGalleryItem): Promise<void> => { private deleteItem = async (data: IGalleryItem): Promise<void> => {
GalleryUtils.deleteItem(this.props.container, this.props.junoClient, data, (item) => { GalleryUtils.deleteItem(this.props.container, this.props.junoClient, data, item => {
this.publishedNotebooks = this.publishedNotebooks.filter((notebook) => item.id !== notebook.id); this.publishedNotebooks = this.publishedNotebooks.filter(notebook => item.id !== notebook.id);
this.refreshSelectedTab(item); this.refreshSelectedTab(item);
}); });
}; };
@@ -484,7 +482,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
const searchText: string = undefined; const searchText: string = undefined;
this.setState({ this.setState({
selectedTab, selectedTab,
searchText, searchText
}); });
this.loadTabContent(selectedTab, searchText, this.state.sortBy, false); this.loadTabContent(selectedTab, searchText, this.state.sortBy, false);
@@ -494,7 +492,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
private onSearchBoxChange = (event?: React.ChangeEvent<HTMLInputElement>, newValue?: string): void => { private onSearchBoxChange = (event?: React.ChangeEvent<HTMLInputElement>, newValue?: string): void => {
const searchText = newValue; const searchText = newValue;
this.setState({ this.setState({
searchText, searchText
}); });
this.loadTabContent(this.state.selectedTab, searchText, this.state.sortBy, true); this.loadTabContent(this.state.selectedTab, searchText, this.state.sortBy, true);
@@ -504,7 +502,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
private onDropdownChange = (event: React.FormEvent<HTMLDivElement>, option?: IDropdownOption): void => { private onDropdownChange = (event: React.FormEvent<HTMLDivElement>, option?: IDropdownOption): void => {
const sortBy = option.key as SortBy; const sortBy = option.key as SortBy;
this.setState({ this.setState({
sortBy, sortBy
}); });
this.loadTabContent(this.state.selectedTab, this.state.searchText, sortBy, true); this.loadTabContent(this.state.selectedTab, this.state.searchText, sortBy, true);

View File

@@ -67,10 +67,6 @@ exports[`GalleryViewerComponent renders 1`] = `
"key": 1, "key": 1,
"text": "Most downloaded", "text": "Most downloaded",
}, },
Object {
"key": 2,
"text": "Most favorited",
},
Object { Object {
"key": 3, "key": 3,
"text": "Most recent", "text": "Most recent",

View File

@@ -17,14 +17,14 @@ describe("NotebookMetadataComponent", () => {
isSample: false, isSample: false,
downloads: 0, downloads: 0,
favorites: 0, favorites: 0,
views: 0, views: 0
}, },
isFavorite: false, isFavorite: false,
downloadButtonText: "Download", downloadButtonText: "Download",
onTagClick: undefined, onTagClick: undefined,
onDownloadClick: undefined, onDownloadClick: undefined,
onFavoriteClick: undefined, onFavoriteClick: undefined,
onUnfavoriteClick: undefined, onUnfavoriteClick: undefined
}; };
const wrapper = shallow(<NotebookMetadataComponent {...props} />); const wrapper = shallow(<NotebookMetadataComponent {...props} />);
@@ -45,14 +45,14 @@ describe("NotebookMetadataComponent", () => {
isSample: false, isSample: false,
downloads: 0, downloads: 0,
favorites: 0, favorites: 0,
views: 0, views: 0
}, },
isFavorite: true, isFavorite: true,
downloadButtonText: "Download", downloadButtonText: "Download",
onTagClick: undefined, onTagClick: undefined,
onDownloadClick: undefined, onDownloadClick: undefined,
onFavoriteClick: undefined, onFavoriteClick: undefined,
onUnfavoriteClick: undefined, onUnfavoriteClick: undefined
}; };
const wrapper = shallow(<NotebookMetadataComponent {...props} />); const wrapper = shallow(<NotebookMetadataComponent {...props} />);

Some files were not shown because too many files have changed in this diff Show More