Compare commits

...

43 Commits

Author SHA1 Message Date
hardiknai-techm
cf3082d30d Merge branch 'update_to_webpack_5' of https://github.com/Azure/cosmos-explorer into update_to_webpack_5 2021-05-27 10:41:47 +05:30
hardiknai-techm
dbfa7e37ed skipLibCheck added 2021-05-27 10:41:24 +05:30
Srinath Narayanan
1bf00f65b8 fixed lint error for MongoIndexPolicyComponentTest 2021-05-27 10:33:16 +05:30
hardiknai-techm
a20620db0e master merge 2021-05-27 09:34:42 +05:30
Steve Faulkner
75d01f655f Show "Open Query" for SQL only (#830) 2021-05-26 15:12:36 -05:00
Hardikkumar Nai
50f83cde87 Remove Explorer.collectionCreationDefaults (#840)
Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>
2021-05-26 15:11:47 -05:00
Tanuj Mittal
6d03cec139 Disable caching for cellOutputViewer.html (#829) 2021-05-27 01:31:28 +05:30
Tanuj Mittal
cb1d60cc90 Hide hosted shells and schema analyzer if VNET, Firewall or Private endpoints is enabled (#826)
* Disable Schema Analyzer if VNET or Firewall is enabled

* Add support for private endpoint connections

* Fix lint warning
2021-05-27 01:31:13 +05:30
Steve Faulkner
0201e6ff92 Remove unused KO dynamic-list component (#838) 2021-05-25 22:40:53 -05:00
Sunil Kumar Yadav
1bcb4246f6 Migration Expand/Collapse Resource Tree to React (#815)
Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>
2021-05-25 15:26:36 -05:00
Steve Faulkner
e7e15c54b3 Cleanup Synapse+Spark Logic (#813) 2021-05-25 14:46:52 -05:00
Sunil Kumar Yadav
522fdc69ab Remove Explorer collection/database text properties (#821)
Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>
2021-05-25 08:32:40 -05:00
Steve Faulkner
bfdeae56d9 Upload to master commit SHA for preview (#823) 2021-05-25 07:42:40 -05:00
Zachary Foster
c42a10faa5 Rollback DB endpoint use by AAD (#822) 2021-05-24 21:21:52 -07:00
victor-meng
0d79f01304 Update free tier limits and messages (#786) 2021-05-24 15:42:54 -05:00
Srinath Narayanan
eae5b2219e Added self serve component telemetry (#820) 2021-05-25 01:06:57 +05:30
Zachary Foster
2fda881770 Use customer endpoint for RBAC AAD auth (#818) 2021-05-24 13:03:51 -05:00
Sunil Kumar Yadav
35f8fa8324 Add files to TS Strict (#803)
Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>
2021-05-21 19:28:08 -05:00
Steve Faulkner
0e413430dc Remove Unused Components (#819) 2021-05-21 19:02:39 -05:00
Sunil Kumar Yadav
afd7f43eb8 Add files to strict mode (#775) 2021-05-21 18:45:54 -05:00
hardiknai-techm
5724ed81f9 merge maste 2021-05-15 09:37:29 +05:30
hardiknai-techm
349a54e078 prod script tag change 2021-05-15 09:35:59 +05:30
hardiknai-techm
bf9fdb95e9 marge master 2021-05-14 14:48:12 +05:30
hardiknai-techm
b4461ddf5d babel version update 2021-05-13 17:30:08 +05:30
hardiknai-techm
349a4bb0f2 react alies create in webapck 2021-05-13 16:49:57 +05:30
hardiknai-techm
5f977b9419 pull master 2021-05-12 09:47:19 +05:30
hardiknai-techm
adce85efd4 use npm version 6 2021-05-11 05:52:28 +05:30
hardiknai-techm
292d2f3be4 use npm version 6 2021-05-11 05:51:12 +05:30
hardiknai-techm
5613c822db resolve master merge conflict 2021-05-08 09:17:27 +05:30
hardiknai-techm
3c320167d8 resolve compile time error 2021-05-08 09:15:19 +05:30
hardiknai-techm
2c58cd7be1 merge master and resolve code conflict 2021-05-06 16:12:30 +05:30
hardiknai-techm
cc4f20e482 update babel and loader package 2021-05-06 06:50:36 +05:30
hardiknai-techm
10aa097166 resolve merge conflict 2021-05-05 08:52:52 +05:30
hardiknai-techm
9e7252dfeb add svgr plugin for svg as ReactComponent 2021-05-05 08:45:34 +05:30
hardiknai-techm
d8dff644d5 update sanpshort and run formattor 2021-05-03 10:37:49 +05:30
hardiknai-techm
2855bf4c7d update test:e2e file name 2021-05-03 09:51:16 +05:30
hardiknai-techm
4ffea1bad9 Merge branch 'master' of https://github.com/Azure/cosmos-explorer into update_to_webpack_5 2021-05-03 08:53:54 +05:30
hardiknai-techm
3cdc30864b merge master 2021-05-03 08:53:28 +05:30
hardiknai-techm
f4a322d17d update react-dom version 2021-04-30 19:39:21 +05:30
hardiknai-techm
8e033bdd73 Merge branch 'master' of https://github.com/Azure/cosmos-explorer into update_to_webpack_5 2021-04-29 21:32:18 +05:30
hardiknai-techm
7d2315f282 add process package it is remove in webpack5 2021-04-28 19:09:41 +05:30
hardiknai-techm
5b467e239a resolve master merge 2021-04-28 18:17:33 +05:30
hardiknai-techm
d65600dd14 webpack and all loader as well plugin version update 2021-04-28 12:00:32 +05:30
477 changed files with 9641 additions and 17748 deletions

View File

@@ -43,6 +43,7 @@ module.exports = {
"@typescript-eslint/no-explicit-any": "error", "@typescript-eslint/no-explicit-any": "error",
"prefer-arrow/prefer-arrow-functions": ["error", { allowStandaloneDeclarations: true }], "prefer-arrow/prefer-arrow-functions": ["error", { allowStandaloneDeclarations: true }],
eqeqeq: "error", eqeqeq: "error",
"react/react-in-jsx-scope": "off",
"react/display-name": "off", "react/display-name": "off",
"react-hooks/rules-of-hooks": "warn", // TODO: error "react-hooks/rules-of-hooks": "warn", // TODO: error
"react-hooks/exhaustive-deps": "warn", // TODO: error "react-hooks/exhaustive-deps": "warn", // TODO: error

View File

@@ -92,11 +92,11 @@ jobs:
name: dist name: dist
path: dist/ path: dist/
- name: Upload build to preview blob storage - name: Upload build to preview blob storage
run: az storage blob upload-batch -d '$web' -s 'dist' --account-name cosmosexplorerpreview --subscription cosmosdb-portalteam-generaldemo --destination-path "${{github.event.pull_request.head.sha}}" --account-key="${PREVIEW_STORAGE_KEY}" run: az storage blob upload-batch -d '$web' -s 'dist' --account-name cosmosexplorerpreview --subscription cosmosdb-portalteam-generaldemo --destination-path "${{github.event.pull_request.head.sha || github.sha}}" --account-key="${PREVIEW_STORAGE_KEY}"
env: env:
PREVIEW_STORAGE_KEY: ${{ secrets.PREVIEW_STORAGE_KEY }} PREVIEW_STORAGE_KEY: ${{ secrets.PREVIEW_STORAGE_KEY }}
- name: Upload preview config to blob storage - name: Upload preview config to blob storage
run: az storage blob upload -c '$web' -f ./preview/config.json --account-name cosmosexplorerpreview --subscription cosmosdb-portalteam-generaldemo --name "${{github.event.pull_request.head.sha}}/config.json" --account-key="${PREVIEW_STORAGE_KEY}" run: az storage blob upload -c '$web' -f ./preview/config.json --account-name cosmosexplorerpreview --subscription cosmosdb-portalteam-generaldemo --name "${{github.event.pull_request.head.sha || github.sha}}/config.json" --account-key="${PREVIEW_STORAGE_KEY}"
env: env:
PREVIEW_STORAGE_KEY: ${{ secrets.PREVIEW_STORAGE_KEY }} PREVIEW_STORAGE_KEY: ${{ secrets.PREVIEW_STORAGE_KEY }}
endtoendemulator: endtoendemulator:

View File

@@ -1,4 +1,13 @@
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",
{
runtime: "automatic",
},
],
"@babel/preset-typescript",
],
plugins: [["@babel/plugin-proposal-decorators", { legacy: true }]], plugins: [["@babel/plugin-proposal-decorators", { legacy: true }]],
}; };

View File

@@ -1,10 +0,0 @@
<svg width="14" height="10" viewBox="0 0 14 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path d="M14.01 0.72999L4.81001 9.93999L0.0100098 5.17999L0.78001 4.40999L4.78001 8.40999L13.21 -0.0100098L14.01 0.72999Z" fill="#0078D4"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="14" height="9.99" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 371 B

View File

@@ -62,6 +62,8 @@ module.exports = {
// "node_modules" // "node_modules"
// ], // ],
modulePaths: ["node_modules", "<rootDir>/src"],
// An array of file extensions your modules use // An array of file extensions your modules use
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "html", "svg"], moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "html", "svg"],

View File

@@ -4,7 +4,7 @@
@font-face { @font-face {
font-family: wf_segoe-ui_normal; font-family: wf_segoe-ui_normal;
src: local("Segoe UI"), url("../../fonts/segoe-ui/west-european/normal/latest.woff"); src: url(./fonts/segoe-ui/west-european/normal/latest.woff);
} }
@DataExplorerFont: wf_segoe-ui_normal, "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif; @DataExplorerFont: wf_segoe-ui_normal, "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;

View File

@@ -4,166 +4,166 @@
*/ */
table.storage { table.storage {
width: 100%; width: 100%;
margin: 6px 0px 0px 0px; margin: 6px 0px 0px 0px;
clear: both; clear: both;
border-collapse: separate; border-collapse: separate;
border-spacing: 0; border-spacing: 0;
background-color: @BaseLight; background-color: @BaseLight;
/*[{datatable-base-background-color}]*/ /*[{datatable-base-background-color}]*/
outline-width: 0; outline-width: 0;
/*Keyboard navigation - ensure table has no border when focused. */ /*Keyboard navigation - ensure table has no border when focused. */
/* no select */ /* no select */
-webkit-touch-callout: none; -webkit-touch-callout: none;
-webkit-user-select: none; -webkit-user-select: none;
-khtml-user-select: none; -khtml-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
user-select: none; user-select: none;
} }
table.dataTable.azure-table tbody tr { table.dataTable.azure-table tbody tr {
color: #000000; color: #000000;
/*[{datatable-name-cell-text}]*/ /*[{datatable-name-cell-text}]*/
} }
table.dataTable.azure-table tbody tr td { table.dataTable.azure-table tbody tr td {
height: 16px; height: 16px;
} }
table.can-select { table.can-select {
/* no select */ /* no select */
-webkit-touch-callout: auto; -webkit-touch-callout: auto;
-webkit-user-select: auto; -webkit-user-select: auto;
-khtml-user-select: auto; -khtml-user-select: auto;
-moz-user-select: auto; -moz-user-select: auto;
-ms-user-select: auto; -ms-user-select: auto;
user-select: auto; user-select: auto;
} }
table.can-select.dataTable tbody tr { table.can-select.dataTable tbody tr {
color: #000000; color: #000000;
/*[{datatable-name-cell-text}]*/ /*[{datatable-name-cell-text}]*/
background-color: transparent; background-color: transparent;
/*[{datatable-base-background}]*/ /*[{datatable-base-background}]*/
} }
table.can-select.dataTable tbody tr.selected { table.can-select.dataTable tbody tr.selected {
color: #000000 !important; color: #000000 !important;
/*[{datatable-row-selected-text} !important]*/ /*[{datatable-row-selected-text} !important]*/
background-color: #C9DEF5; background-color: #c9def5;
/*[{datatable-row-selected-background}]*/ /*[{datatable-row-selected-background}]*/
border: 1px solid #b3d1f1; border: 1px solid #b3d1f1;
/*[1px solid {datatable-row-selected-border}]*/ /*[1px solid {datatable-row-selected-border}]*/
} }
table.can-select.dataTable tbody tr.selected td:first-child { table.can-select.dataTable tbody tr.selected td:first-child {
color: #000000 !important; color: #000000 !important;
/*[{datatable-name-cell-selected-text} !important]*/ /*[{datatable-name-cell-selected-text} !important]*/
} }
table.can-select.dataTable.hover tbody tr:hover, table.can-select.dataTable.hover tbody tr:hover,
table.dataTable.storage tbody tr:hover { table.dataTable.storage tbody tr:hover {
background-color: #C9DEF5; background-color: #c9def5;
/*[{datatable-row-hover-background}]*/ /*[{datatable-row-hover-background}]*/
} }
table.dataTable.storage tbody tr:hover td:empty { table.dataTable.storage tbody tr:hover td:empty {
background-color: #C9DEF5; background-color: #c9def5;
/*[{datatable-row-hover-empty-background}]*/ /*[{datatable-row-hover-empty-background}]*/
} }
table.can-select.dataTable.hover tbody tr:hover.selected, table.can-select.dataTable.hover tbody tr:hover.selected,
table.can-select.dataTable.storage tbody tr:hover.selected { table.can-select.dataTable.storage tbody tr:hover.selected {
background-color: #C9DEF5; background-color: #c9def5;
/*[{datatable-row-selected-background}]*/ /*[{datatable-row-selected-background}]*/
} }
table.can-select.dataTable:not(:focus) tbody tr.selected { table.can-select.dataTable:not(:focus) tbody tr.selected {
color: #1E1E1E; color: #1e1e1e;
/*[{datatable-row-selected-text}]*/ /*[{datatable-row-selected-text}]*/
background-color: #767676; background-color: #767676;
/*[{datatable-row-selected-background}]*/ /*[{datatable-row-selected-background}]*/
} }
table.can-select.dataTable.hover:not(:focus) tbody tr:hover.selected, table.can-select.dataTable.hover:not(:focus) tbody tr:hover.selected,
table.can-select.dataTable.storage:not(:focus) tbody tr:hover.selected { table.can-select.dataTable.storage:not(:focus) tbody tr:hover.selected {
background-color: #767676; background-color: #767676;
/*[{datatable-row-selected-background}]*/ /*[{datatable-row-selected-background}]*/
} }
table.storage thead th, table.storage thead th,
table.storage tfoot th { table.storage tfoot th {
font-weight: normal; font-weight: normal;
color: #DDDDDD; color: #dddddd;
/*[{datatable-header-text}]*/ /*[{datatable-header-text}]*/
white-space: nowrap; white-space: nowrap;
} }
table.storage thead th, table.storage thead th,
table.storage thead td { table.storage thead td {
padding: 0.5em 1em; padding: 0.5em 1em;
border: 1px solid #DDDDDD; border: 1px solid #dddddd;
/*[1px solid {datatable-header-border}]*/ /*[1px solid {datatable-header-border}]*/
background-color: @BaseLight; background-color: @BaseLight;
/*[{datatable-header-background}]*/ /*[{datatable-header-background}]*/
text-align: left; text-align: left;
color: #808080; color: #808080;
/*[{datatable-header-text}]*/ /*[{datatable-header-text}]*/
outline: none; outline: none;
} }
table.dataTable thead th:active, table.dataTable thead th:active,
table.dataTable thead td:active { table.dataTable thead td:active {
border: 1px solid #DDDDDD; border: 1px solid #dddddd;
/*[1px solid {datatable-header-cell-active-border}]*/ /*[1px solid {datatable-header-cell-active-border}]*/
outline: none; outline: none;
background-color: #B4C7DC !important; background-color: #b4c7dc !important;
/*[{datatable-header-cell-active-background} !important] */ /*[{datatable-header-cell-active-background} !important] */
} }
table.dataTable thead th:focus, table.dataTable thead th:focus,
table.dataTable thead td:focus { table.dataTable thead td:focus {
border-width: 1px; border-width: 1px;
border-style: solid; border-style: solid;
border-color: #007ACC; border-color: #007acc;
/*[{datatable-header-cell-focus-background}]*/ /*[{datatable-header-cell-focus-background}]*/
} }
table.dataTable thead th:hover, table.dataTable thead th:hover,
table.dataTable thead td:hover { table.dataTable thead td:hover {
border: 1px solid #007ACC; border: 1px solid #007acc;
/*[1px solid {datatable-header-cell-hover-border}]*/ /*[1px solid {datatable-header-cell-hover-border}]*/
background-color: #C9DEF5; background-color: #c9def5;
/*[{datatable-header-cell-hover-background}]*/ /*[{datatable-header-cell-hover-background}]*/
color: #000000; color: #000000;
/*[{datatable-header-cell-hover-text}]*/ /*[{datatable-header-cell-hover-text}]*/
} }
table.storage thead th:not(:focus):not(:hover), table.storage thead th:not(:focus):not(:hover),
table.storage thead td:not(:focus):not(:hover) { table.storage thead td:not(:focus):not(:hover) {
border-left-color: transparent; border-left-color: transparent;
} }
table.storage thead th:last-child:not(:focus):not(:hover), table.storage thead th:last-child:not(:focus):not(:hover),
table.storage thead td:last-child:not(:focus):not(:hover) { table.storage thead td:last-child:not(:focus):not(:hover) {
border-right-color: transparent; border-right-color: transparent;
} }
table.dataTable tfoot th, table.dataTable tfoot th,
table.dataTable tfoot td { table.dataTable tfoot td {
padding: 5px 18px 5px 18px; padding: 5px 18px 5px 18px;
border-top: 1px solid #DDDDDD; border-top: 1px solid #dddddd;
/*[1px solid {datatable-header-border}]*/ /*[1px solid {datatable-header-border}]*/
background-color: @BaseLight; background-color: @BaseLight;
/*[{datatable-header-background}]*/ /*[{datatable-header-background}]*/
} }
table.dataTable thead .sorting, table.dataTable thead .sorting,
table.dataTable thead .sorting_asc, table.dataTable thead .sorting_asc,
table.dataTable thead .sorting_desc { table.dataTable thead .sorting_desc {
cursor: pointer; cursor: pointer;
*cursor: hand; *cursor: hand;
} }
table.dataTable thead .sorting, table.dataTable thead .sorting,
@@ -171,367 +171,365 @@ table.dataTable thead .sorting_asc,
table.dataTable thead .sorting_desc, table.dataTable thead .sorting_desc,
table.dataTable thead .sorting_asc_disabled, table.dataTable thead .sorting_asc_disabled,
table.dataTable thead .sorting_desc_disabled { table.dataTable thead .sorting_desc_disabled {
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center right; background-position: center right;
} }
table.dataTable thead .sorting_asc { table.dataTable thead .sorting_asc {
background-image: url("../../images/QueryBuilder/CollapseChevronUp_16x.png"); background-image: url(/images/QueryBuilder/CollapseChevronUp_16x.png);
} }
table.dataTable thead .sorting_desc { table.dataTable thead .sorting_desc {
background-image: url("../../images/QueryBuilder/CollapseChevronDown_16x.png"); background-image: url(/images/QueryBuilder/CollapseChevronDown_16x.png);
} }
table.dataTable tbody tr { table.dataTable tbody tr {
color: #808080; color: #808080;
/*[{datatable-base-text}]*/ /*[{datatable-base-text}]*/
background-color: @BaseLight; background-color: @BaseLight;
/*[{datatable-base-background}]*/ /*[{datatable-base-background}]*/
} }
table.dataTable tbody tr.selected { table.dataTable tbody tr.selected {
color: @BaseLight !important; color: @BaseLight !important;
/*[{datatable-row-selected-text} !important]*/ /*[{datatable-row-selected-text} !important]*/
background-color: @SelectionColor; background-color: @SelectionColor;
/*[{datatable-row-selected-background]*/ /*[{datatable-row-selected-background]*/
} }
table.dataTable tbody tr.selected td:first-child, table.dataTable tbody tr.selected td:first-child,
table.dataTable tbody tr.selected td:nth-child(2) { table.dataTable tbody tr.selected td:nth-child(2) {
color: @BaseLight !important; color: @BaseLight !important;
/*[{datatable-row-selected-text} !important]*/ /*[{datatable-row-selected-text} !important]*/
} }
table.dataTable tbody tr td:first-child img { table.dataTable tbody tr td:first-child img {
vertical-align: middle; vertical-align: middle;
} }
table.dataTable tbody tr td:first-child { table.dataTable tbody tr td:first-child {
border-left-width: 1px; border-left-width: 1px;
} }
table.dataTable tbody th, table.dataTable tbody th,
table.dataTable tbody td { table.dataTable tbody td {
padding: 0.25em 0.5em; padding: 0.25em 0.5em;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
max-width: 60em; max-width: 60em;
white-space: nowrap; white-space: nowrap;
} }
table.show-gridlines tbody th, table.show-gridlines tbody th,
table.show-gridlines tbody td { table.show-gridlines tbody td {
border-width: 1px 1px 1px 0px; border-width: 1px 1px 1px 0px;
border-style: solid; border-style: solid;
border-color: #DDDDDD; border-color: #dddddd;
/*[{datatable-base-border}]*/ /*[{datatable-base-border}]*/
} }
table.show-gridlines tbody td:empty { table.show-gridlines tbody td:empty {
background-color: #E3E2E6; background-color: #e3e2e6;
/*[{datatable-base-border}]*/ /*[{datatable-base-border}]*/
} }
table.show-gridlines tbody tr.selected td:empty { table.show-gridlines tbody tr.selected td:empty {
background-color: #767676; background-color: #767676;
/*[{datatable-row-selected-empty-background}]*/ /*[{datatable-row-selected-empty-background}]*/
} }
table.dataTable.row-border tbody th, table.dataTable.row-border tbody th,
table.dataTable.row-border tbody td, table.dataTable.row-border tbody td,
table.dataTable.storage tbody th, table.dataTable.storage tbody th,
table.dataTable.storage tbody td { table.dataTable.storage tbody td {
border-top: 1px solid @BaseLight; border-top: 1px solid @BaseLight;
/*[{datatable-base-border}]*/ /*[{datatable-base-border}]*/
} }
table.dataTable.row-border tbody tr:first-child th, table.dataTable.row-border tbody tr:first-child th,
table.dataTable.row-border tbody tr:first-child td, table.dataTable.row-border tbody tr:first-child td,
table.dataTable.storage tbody tr:first-child th, table.dataTable.storage tbody tr:first-child th,
table.dataTable.storage tbody tr:first-child td { table.dataTable.storage tbody tr:first-child td {
border-top: none; border-top: none;
} }
table.dataTable.cell-border tbody th, table.dataTable.cell-border tbody th,
table.dataTable.cell-border tbody td { table.dataTable.cell-border tbody td {
border-top: 1px solid @BaseLight; border-top: 1px solid @BaseLight;
/*[1px solid {datatable-base-border}]*/ /*[1px solid {datatable-base-border}]*/
border-right: 1px solid @BaseLight; border-right: 1px solid @BaseLight;
/*[1px solid {datatable-base-border}]*/ /*[1px solid {datatable-base-border}]*/
} }
table.dataTable.cell-border tbody tr th:first-child, table.dataTable.cell-border tbody tr th:first-child,
table.dataTable.cell-border tbody tr td:first-child { table.dataTable.cell-border tbody tr td:first-child {
border-left: 1px solid @BaseLight; border-left: 1px solid @BaseLight;
/*[1px solid {datatable-base-border}]*/ /*[1px solid {datatable-base-border}]*/
} }
table.dataTable.cell-border tbody tr:first-child th, table.dataTable.cell-border tbody tr:first-child th,
table.dataTable.cell-border tbody tr:first-child td { table.dataTable.cell-border tbody tr:first-child td {
border-top: none; border-top: none;
} }
table.dataTable.hover tbody tr:hover, table.dataTable.hover tbody tr:hover,
table.dataTable.storage tbody tr:hover { table.dataTable.storage tbody tr:hover {
color: #000000; color: #000000;
/*[{datatable-row-hover-text}]*/ /*[{datatable-row-hover-text}]*/
background-color: #FCFCFC; background-color: #fcfcfc;
/*[{datatable-row-hover-background}]*/ /*[{datatable-row-hover-background}]*/
} }
table.dataTable.hover tbody tr:hover.selected, table.dataTable.hover tbody tr:hover.selected,
table.dataTable.storage tbody tr:hover.selected { table.dataTable.storage tbody tr:hover.selected {
background-color: #3399ff; background-color: #3399ff;
/*[{datatable-row-selected-background}]*/ /*[{datatable-row-selected-background}]*/
} }
table.dataTable.hover:not(:focus) tbody tr:hover.selected, table.dataTable.hover:not(:focus) tbody tr:hover.selected,
table.dataTable.storage:not(:focus) tbody tr:hover.selected { table.dataTable.storage:not(:focus) tbody tr:hover.selected {
background-color: #767676; background-color: #767676;
/*[{datatable-row-selected-background}]*/ /*[{datatable-row-selected-background}]*/
} }
table.dataTable.no-footer { table.dataTable.no-footer {
border-bottom: 1px solid #555; border-bottom: 1px solid #555;
/*[1px solid {datatable-base-border}]*/ /*[1px solid {datatable-base-border}]*/
} }
table.dataTable.nowrap th, table.dataTable.nowrap th,
table.dataTable.nowrap td { table.dataTable.nowrap td {
white-space: nowrap; white-space: nowrap;
} }
table.dataTable.compact thead th, table.dataTable.compact thead th,
table.dataTable.compact thead td { table.dataTable.compact thead td {
padding: 4px 17px 4px 4px; padding: 4px 17px 4px 4px;
} }
table.dataTable.compact tfoot th, table.dataTable.compact tfoot th,
table.dataTable.compact tfoot td { table.dataTable.compact tfoot td {
padding: 4px; padding: 4px;
} }
table.dataTable.compact tbody th, table.dataTable.compact tbody th,
table.dataTable.compact tbody td { table.dataTable.compact tbody td {
padding: 4px; padding: 4px;
} }
table.dataTable th.dt-left, table.dataTable th.dt-left,
table.dataTable td.dt-left { table.dataTable td.dt-left {
text-align: left; text-align: left;
} }
table.dataTable th.dt-center, table.dataTable th.dt-center,
table.dataTable td.dt-center, table.dataTable td.dt-center,
table.dataTable td.dataTables_empty { table.dataTable td.dataTables_empty {
text-align: center; text-align: center;
} }
table.dataTable th.dt-right, table.dataTable th.dt-right,
table.dataTable td.dt-right { table.dataTable td.dt-right {
text-align: right; text-align: right;
} }
table.dataTable th.dt-justify, table.dataTable th.dt-justify,
table.dataTable td.dt-justify { table.dataTable td.dt-justify {
text-align: justify; text-align: justify;
} }
table.dataTable th.dt-nowrap, table.dataTable th.dt-nowrap,
table.dataTable td.dt-nowrap { table.dataTable td.dt-nowrap {
white-space: nowrap; white-space: nowrap;
} }
table.dataTable thead th.dt-head-left, table.dataTable thead th.dt-head-left,
table.dataTable thead td.dt-head-left, table.dataTable thead td.dt-head-left,
table.dataTable tfoot th.dt-head-left, table.dataTable tfoot th.dt-head-left,
table.dataTable tfoot td.dt-head-left { table.dataTable tfoot td.dt-head-left {
text-align: left; text-align: left;
} }
table.dataTable thead th.dt-head-center, table.dataTable thead th.dt-head-center,
table.dataTable thead td.dt-head-center, table.dataTable thead td.dt-head-center,
table.dataTable tfoot th.dt-head-center, table.dataTable tfoot th.dt-head-center,
table.dataTable tfoot td.dt-head-center { table.dataTable tfoot td.dt-head-center {
text-align: center; text-align: center;
} }
table.dataTable thead th.dt-head-right, table.dataTable thead th.dt-head-right,
table.dataTable thead td.dt-head-right, table.dataTable thead td.dt-head-right,
table.dataTable tfoot th.dt-head-right, table.dataTable tfoot th.dt-head-right,
table.dataTable tfoot td.dt-head-right { table.dataTable tfoot td.dt-head-right {
text-align: right; text-align: right;
} }
table.dataTable thead th.dt-head-justify, table.dataTable thead th.dt-head-justify,
table.dataTable thead td.dt-head-justify, table.dataTable thead td.dt-head-justify,
table.dataTable tfoot th.dt-head-justify, table.dataTable tfoot th.dt-head-justify,
table.dataTable tfoot td.dt-head-justify { table.dataTable tfoot td.dt-head-justify {
text-align: justify; text-align: justify;
} }
table.dataTable thead th.dt-head-nowrap, table.dataTable thead th.dt-head-nowrap,
table.dataTable thead td.dt-head-nowrap, table.dataTable thead td.dt-head-nowrap,
table.dataTable tfoot th.dt-head-nowrap, table.dataTable tfoot th.dt-head-nowrap,
table.dataTable tfoot td.dt-head-nowrap { table.dataTable tfoot td.dt-head-nowrap {
white-space: nowrap; white-space: nowrap;
} }
table.dataTable tbody th.dt-body-left, table.dataTable tbody th.dt-body-left,
table.dataTable tbody td.dt-body-left { table.dataTable tbody td.dt-body-left {
text-align: left; text-align: left;
} }
table.dataTable tbody th.dt-body-center, table.dataTable tbody th.dt-body-center,
table.dataTable tbody td.dt-body-center { table.dataTable tbody td.dt-body-center {
text-align: center; text-align: center;
} }
table.dataTable tbody th.dt-body-right, table.dataTable tbody th.dt-body-right,
table.dataTable tbody td.dt-body-right { table.dataTable tbody td.dt-body-right {
text-align: right; text-align: right;
} }
table.dataTable tbody th.dt-body-justify, table.dataTable tbody th.dt-body-justify,
table.dataTable tbody td.dt-body-justify { table.dataTable tbody td.dt-body-justify {
text-align: justify; text-align: justify;
} }
table.dataTable tbody th.dt-body-nowrap, table.dataTable tbody th.dt-body-nowrap,
table.dataTable tbody td.dt-body-nowrap { table.dataTable tbody td.dt-body-nowrap {
white-space: nowrap; white-space: nowrap;
} }
table.dataTable, table.dataTable,
table.dataTable th, table.dataTable th,
table.dataTable td { table.dataTable td {
-webkit-box-sizing: content-box; -webkit-box-sizing: content-box;
-moz-box-sizing: content-box; -moz-box-sizing: content-box;
box-sizing: content-box; box-sizing: content-box;
} }
.dataTables_scrollBody .storage { .dataTables_scrollBody .storage {
margin: 0px; margin: 0px;
} }
/* /*
* Control feature layout * Control feature layout
*/ */
.dataTables_wrapper { .dataTables_wrapper {
position: relative; position: relative;
clear: both; clear: both;
*zoom: 1; *zoom: 1;
zoom: 1; zoom: 1;
padding: 0px 10px 0px 10px; padding: 0px 10px 0px 10px;
.flex-display(); .flex-display();
.flex-direction(); .flex-direction();
overflow:hidden; overflow: hidden;
} }
.dataTables_wrapper .dataTables_length { .dataTables_wrapper .dataTables_length {
float: left; float: left;
margin: 10px; margin: 10px;
} }
.dataTables_wrapper .dataTables_filter { .dataTables_wrapper .dataTables_filter {
float: right; float: right;
text-align: right; text-align: right;
margin: 10px; margin: 10px;
} }
.dataTables_wrapper .dataTables_filter input { .dataTables_wrapper .dataTables_filter input {
background-color: #333337; background-color: #333337;
/*[{plugin-textbox-background-color}]*/ /*[{plugin-textbox-background-color}]*/
border: 1px solid #3F3F46; border: 1px solid #3f3f46;
/*[1px solid {plugin-textbox-border-color}]*/ /*[1px solid {plugin-textbox-border-color}]*/
margin-left: 0.5em; margin-left: 0.5em;
outline: none; outline: none;
} }
.dataTables_wrapper .dataTables_filter input:active, .dataTables_wrapper .dataTables_filter input:active,
.dataTables_wrapper .dataTables_filter input:focus, .dataTables_wrapper .dataTables_filter input:focus,
.dataTables_wrapper .dataTables_filter input:hover { .dataTables_wrapper .dataTables_filter input:hover {
border: 1px solid #007ACC; border: 1px solid #007acc;
/*[1px solid {search-control-mouse-over-border}]*/ /*[1px solid {search-control-mouse-over-border}]*/
} }
.dataTables_wrapper .dataTables_length select { .dataTables_wrapper .dataTables_length select {
outline: none; outline: none;
} }
.dataTables_wrapper .dataTables_length select:active, .dataTables_wrapper .dataTables_length select:active,
.dataTables_wrapper .dataTables_length select:focus, .dataTables_wrapper .dataTables_length select:focus,
.dataTables_wrapper .dataTables_length select:hover { .dataTables_wrapper .dataTables_length select:hover {
outline: 1px solid #007ACC; outline: 1px solid #007acc;
/*[1px solid {search-control-mouse-over-border}]*/ /*[1px solid {search-control-mouse-over-border}]*/
} }
.dataTables_wrapper .dataTables_info { .dataTables_wrapper .dataTables_info {
clear: both; clear: both;
float: left; float: left;
padding-top: 0.755em; padding-top: 0.755em;
padding-left: 10px; padding-left: 10px;
} }
.dataTables_wrapper .dataTables_paginate { .dataTables_wrapper .dataTables_paginate {
float: right; float: right;
text-align: right; text-align: right;
padding-top: 0.45em; padding-top: 0.45em;
} }
.dataTables_wrapper .dataTables_paginate .paginate_button { .dataTables_wrapper .dataTables_paginate .paginate_button {
box-sizing: border-box; box-sizing: border-box;
display: inline-block; display: inline-block;
min-width: 1.5em; min-width: 1.5em;
padding: 0.5em 1em; padding: 0.5em 1em;
margin-left: 2px; margin-left: 2px;
text-align: center; text-align: center;
text-decoration: none !important; text-decoration: none !important;
cursor: pointer; cursor: pointer;
*cursor: hand; *cursor: hand;
color: @SelectionColor !important; color: @SelectionColor !important;
/*[{environment-panel-hyperlink} !important]*/ /*[{environment-panel-hyperlink} !important]*/
} }
.dataTables_wrapper .dataTables_paginate .paginate_button.current, .dataTables_wrapper .dataTables_paginate .paginate_button.current,
.dataTables_wrapper .dataTables_paginate .paginate_button.current:hover { .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
color: black !important; color: black !important;
/*[{environment-panel-hyperlink-disabled} !important]*/ /*[{environment-panel-hyperlink-disabled} !important]*/
} }
.dataTables_wrapper .dataTables_paginate .paginate_button.disabled, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled,
.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover,
.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active { .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {
cursor: default; cursor: default;
color: white !important; color: white !important;
/*[{environment-panel-hyperlink-disabled} !important]*/ /*[{environment-panel-hyperlink-disabled} !important]*/
border: 1px solid transparent; border: 1px solid transparent;
background: transparent; background: transparent;
box-shadow: none; box-shadow: none;
} }
.paginate_button.disabled { .paginate_button.disabled {
visibility: hidden; visibility: hidden;
} }
.dataTables_wrapper .dataTables_paginate .ellipsis { .dataTables_wrapper .dataTables_paginate .ellipsis {
padding: 0 1em; padding: 0 1em;
} }
.dataTables_wrapper .dataTables_processing { .dataTables_wrapper .dataTables_processing {
position: fixed; position: fixed;
top: 50%; top: 50%;
left: 50%; left: 50%;
width: 100%; width: 100%;
height: 100%; height: 100%;
margin-top: -25px; margin-top: -25px;
padding-top: 20px; padding-top: 20px;
background-color: transparent; background-color: transparent;
z-index: 10000; z-index: 10000;
} }
.dataTables_wrapper .dataTables_length, .dataTables_wrapper .dataTables_length,
@@ -539,77 +537,75 @@ table.dataTable td {
.dataTables_wrapper .dataTables_info, .dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_processing, .dataTables_wrapper .dataTables_processing,
.dataTables_wrapper .dataTables_paginate { .dataTables_wrapper .dataTables_paginate {
color: #000000; color: #000000;
/*[{common-controls-inner-tab-inactive-text}]*/ /*[{common-controls-inner-tab-inactive-text}]*/
} }
.dataTables_wrapper .dataTables_scroll { .dataTables_wrapper .dataTables_scroll {
clear: both; clear: both;
width:100%; width: 100%;
overflow: hidden; overflow: hidden;
.flex-display(); .flex-display();
.flex-direction(); .flex-direction();
} }
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody { .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody {
*margin-top: -1px; *margin-top: -1px;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
} }
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th>div.dataTables_sizing, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th > div.dataTables_sizing,
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td>div.dataTables_sizing { .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td > div.dataTables_sizing {
height: 0; height: 0;
margin: 0 !important; margin: 0 !important;
padding: 0 !important; padding: 0 !important;
} }
.dataTables_wrapper.no-footer .dataTables_scrollHead { .dataTables_wrapper.no-footer .dataTables_scrollHead {
height: 50px; height: 50px;
} }
.dataTables_wrapper.no-footer .dataTables_scrollBody { .dataTables_wrapper.no-footer .dataTables_scrollBody {
border-bottom: none; border-bottom: none;
} }
.dataTables_wrapper.no-footer div.dataTables_scrollHead table, .dataTables_wrapper.no-footer div.dataTables_scrollHead table,
.dataTables_wrapper.no-footer div.dataTables_scrollBody table { .dataTables_wrapper.no-footer div.dataTables_scrollBody table {
border-bottom: none; border-bottom: none;
} }
.dataTables_wrapper:after { .dataTables_wrapper:after {
visibility: hidden; visibility: hidden;
display: block; display: block;
content: ""; content: "";
clear: both; clear: both;
height: 0; height: 0;
} }
.dataTables_wrapper a { .dataTables_wrapper a {
color: whitesmoke; color: whitesmoke;
/*[{common-controls-inner-tab-inactive-background}]*/ /*[{common-controls-inner-tab-inactive-background}]*/
} }
tr td.nameColumnText { tr td.nameColumnText {
color: #000000; color: #000000;
/*[{plugin-textbox-color}]*/ /*[{plugin-textbox-color}]*/
} }
tr td.nameColumnText.selected, tr td.nameColumnText.selected,
tr:hover td.nameColumnText { tr:hover td.nameColumnText {
color: @BaseLight !important; color: @BaseLight !important;
/*[{plugin-treeview-content-selected-color} !important]*/ /*[{plugin-treeview-content-selected-color} !important]*/
} }
.textRight { .textRight {
text-align: right; text-align: right;
} }
.dataTables_filter { .dataTables_filter {
display: none; display: none;
} }
/*@media screen and (max-width: 767px) { /*@media screen and (max-width: 767px) {
.dataTables_wrapper .dataTables_info, .dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_paginate { .dataTables_wrapper .dataTables_paginate {
@@ -633,21 +629,21 @@ tr:hover td.nameColumnText {
}*/ }*/
.context-menu-item.icon-reset-column-order { .context-menu-item.icon-reset-column-order {
background-image: url(../../images/Reset-column-options.svg); background-image: url(/images/Reset-column-options.svg);
} }
.context-menu-item.icon-shift-non-empty-columns-left { .context-menu-item.icon-shift-non-empty-columns-left {
background-image: url(../../images/Reorder.svg); background-image: url(/images/Reorder.svg);
} }
.context-menu-item.icon-edit-entity { .context-menu-item.icon-edit-entity {
background-image: url(../../images/Edit_entity.svg); background-image: url(/images/Edit_entity.svg);
} }
.context-menu-item.icon-delete-entity { .context-menu-item.icon-delete-entity {
background-image: url(../../images/delete.svg); background-image: url(/images/delete.svg);
} }
.context-menu-item.icon-customize-columns { .context-menu-item.icon-customize-columns {
background-image: url(../../images/Options.svg); background-image: url(/images/Options.svg);
} }

View File

@@ -1313,7 +1313,7 @@ menuQuickStart {
} }
.plusimg-but { .plusimg-but {
background-image: url(../images/plus_normal.svg); background-image: url(/images/plus_normal.svg);
background-repeat: no-repeat; background-repeat: no-repeat;
padding: 6px 16px; padding: 6px 16px;
position: static; position: static;
@@ -1321,7 +1321,7 @@ menuQuickStart {
} }
.plusimg-but:hover { .plusimg-but:hover {
background-image: url(../images/plus_hover.svg); background-image: url(/images/plus_hover.svg);
background-repeat: no-repeat; background-repeat: no-repeat;
padding: 6px 16px; padding: 6px 16px;
position: static; position: static;
@@ -1329,7 +1329,7 @@ menuQuickStart {
} }
.plusimg-but:active { .plusimg-but:active {
background-image: url(../images/plus_pressed.svg); background-image: url(/images/plus_pressed.svg);
background-repeat: no-repeat; background-repeat: no-repeat;
padding: 6px 16px; padding: 6px 16px;
position: static; position: static;
@@ -1337,7 +1337,7 @@ menuQuickStart {
} }
.plusimg-but:disabled { .plusimg-but:disabled {
background-image: url(../images/plus_disabled.svg); background-image: url(/images/plus_disabled.svg);
background-repeat: no-repeat; background-repeat: no-repeat;
padding: 6px 16px; padding: 6px 16px;
position: static; position: static;
@@ -1345,7 +1345,7 @@ menuQuickStart {
} }
.minusimg-but { .minusimg-but {
background-image: url(../images/minus_normal.svg); background-image: url(/images/minus_normal.svg);
background-repeat: no-repeat; background-repeat: no-repeat;
padding: 6px 16px; padding: 6px 16px;
position: static; position: static;
@@ -1353,7 +1353,7 @@ menuQuickStart {
} }
.minusimg-but:hover { .minusimg-but:hover {
background-image: url(../images/minus_hover.svg); background-image: url(/images/minus_hover.svg);
background-repeat: no-repeat; background-repeat: no-repeat;
padding: 6px 16px; padding: 6px 16px;
position: static; position: static;
@@ -1361,7 +1361,7 @@ menuQuickStart {
} }
.minusimg-but:active { .minusimg-but:active {
background-image: url(../images/minus_pressed.svg); background-image: url(/images/minus_pressed.svg);
background-repeat: no-repeat; background-repeat: no-repeat;
padding: 6px 16px; padding: 6px 16px;
position: static; position: static;
@@ -1369,7 +1369,7 @@ menuQuickStart {
} }
.minusimg-but:disabled { .minusimg-but:disabled {
background-image: url(../images/minus_disabled.svg); background-image: url(/images/minus_disabled.svg);
background-repeat: no-repeat; background-repeat: no-repeat;
padding: 6px 16px; padding: 6px 16px;
position: static; position: static;
@@ -2707,7 +2707,7 @@ a:link {
.errorIcon { .errorIcon {
width: @ErrorIconWidth; width: @ErrorIconWidth;
height: @LoadingErrorIconSize; height: @LoadingErrorIconSize;
background-image: url(../images/error_no_outline.svg); background-image: url(/images/error_no_outline.svg);
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center; background-position: center;
background-size: 3px; background-size: 3px;
@@ -3085,3 +3085,7 @@ settings-pane {
padding-left: @SmallSpace; padding-left: @SmallSpace;
} }
} }
.hiddenMain {
display: none;
height: 0px;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@
.resourceTree { .resourceTree {
height: 100%; height: 100%;
width: 20%;
flex: 0 0 auto; flex: 0 0 auto;
.main { .main {
height: 100%; height: 100%;

10466
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,10 +9,10 @@
"@azure/cosmos-language-service": "0.0.5", "@azure/cosmos-language-service": "0.0.5",
"@azure/identity": "1.2.1", "@azure/identity": "1.2.1",
"@azure/ms-rest-nodeauth": "3.0.7", "@azure/ms-rest-nodeauth": "3.0.7",
"@babel/plugin-proposal-class-properties": "7.13.0",
"@babel/plugin-proposal-decorators": "7.14.2",
"@fluentui/react": "8.14.8",
"@azure/msal-browser": "2.14.2", "@azure/msal-browser": "2.14.2",
"@babel/plugin-proposal-class-properties": "7.12.1",
"@babel/plugin-proposal-decorators": "7.12.12",
"@fluentui/react": "8.14.3",
"@jupyterlab/services": "6.0.2", "@jupyterlab/services": "6.0.2",
"@jupyterlab/terminal": "3.0.3", "@jupyterlab/terminal": "3.0.3",
"@microsoft/applicationinsights-web": "2.6.1", "@microsoft/applicationinsights-web": "2.6.1",
@@ -41,17 +41,13 @@
"@nteract/transform-vega": "7.0.6", "@nteract/transform-vega": "7.0.6",
"@octokit/rest": "17.9.2", "@octokit/rest": "17.9.2",
"@phosphor/widgets": "1.9.3", "@phosphor/widgets": "1.9.3",
"@testing-library/jest-dom": "5.11.9",
"@types/mkdirp": "1.0.1",
"@types/node-fetch": "2.5.7",
"applicationinsights": "1.8.0", "applicationinsights": "1.8.0",
"bootstrap": "3.4.1", "bootstrap": "3.4.1",
"canvas": "file:./canvas", "canvas": "file:./canvas",
"clean-webpack-plugin": "0.1.19",
"clipboard-copy": "4.0.1", "clipboard-copy": "4.0.1",
"copy-webpack-plugin": "6.0.2",
"crossroads": "0.12.2", "crossroads": "0.12.2",
"css-element-queries": "1.1.1", "crypto-browserify": "3.12.0",
"css-element-queries": "1.2.3",
"d3": "6.1.1", "d3": "6.1.1",
"datatables.net-colreorder-dt": "1.5.1", "datatables.net-colreorder-dt": "1.5.1",
"datatables.net-dt": "1.10.19", "datatables.net-dt": "1.10.19",
@@ -59,18 +55,18 @@
"dayjs": "1.8.19", "dayjs": "1.8.19",
"dom-to-image": "2.6.0", "dom-to-image": "2.6.0",
"dotenv": "8.2.0", "dotenv": "8.2.0",
"eslint-plugin-jest": "23.13.2", "eslint-plugin-jest": "24.3.6",
"eslint-plugin-react": "7.20.0", "eslint-plugin-react": "7.23.2",
"hasher": "1.2.0", "hasher": "1.2.0",
"html2canvas": "1.0.0-rc.5", "html2canvas": "1.0.0-rc.7",
"i18next": "19.8.4", "i18next": "20.2.2",
"i18next-browser-languagedetector": "6.0.1", "i18next-browser-languagedetector": "6.1.0",
"i18next-http-backend": "1.0.23", "i18next-http-backend": "1.2.2",
"iframe-resizer-react": "1.1.0", "iframe-resizer-react": "1.1.0",
"immutable": "4.0.0-rc.12", "immutable": "4.0.0-rc.12",
"is-ci": "2.0.0", "is-ci": "2.0.0",
"jquery": "3.5.1", "jquery": "3.6.0",
"jquery-typeahead": "2.10.6", "jquery-typeahead": "2.11.1",
"jquery-ui-dist": "1.12.1", "jquery-ui-dist": "1.12.1",
"knockout": "3.5.1", "knockout": "3.5.1",
"mkdirp": "1.0.4", "mkdirp": "1.0.4",
@@ -80,108 +76,117 @@
"plotly.js-cartesian-dist-min": "1.52.3", "plotly.js-cartesian-dist-min": "1.52.3",
"post-robot": "10.0.42", "post-robot": "10.0.42",
"q": "1.5.1", "q": "1.5.1",
"react": "16.13.1", "react": "17.0.2",
"react-animate-height": "2.0.8", "react-animate-height": "2.0.8",
"react-dnd": "9.4.0", "react-dnd": "14.0.2",
"react-dnd-html5-backend": "9.4.0", "react-dnd-html5-backend": "14.0.0",
"react-dom": "16.13.1", "react-dom": "17.0.2",
"react-hotkeys": "2.0.0", "react-hotkeys": "2.0.0",
"react-i18next": "11.8.5", "react-i18next": "11.8.15",
"react-notification-system": "0.2.17", "react-notification-system": "0.2.17",
"react-redux": "7.1.3", "react-redux": "7.2.4",
"redux": "4.0.4", "redux": "4.1.0",
"reflect-metadata": "0.1.13", "reflect-metadata": "0.1.13",
"rx-jupyter": "5.5.12", "rx-jupyter": "5.5.12",
"rxjs": "6.6.3", "rxjs": "6.6.3",
"sanitize-html": "2.3.3", "sanitize-html": "2.3.3",
"styled-components": "4.3.2", "styled-components": "5.3.0",
"swr": "0.4.0", "swr": "0.4.0",
"terser-webpack-plugin": "3.1.0", "underscore": "1.13.1",
"underscore": "1.9.1",
"utility-types": "3.10.0", "utility-types": "3.10.0",
"zustand": "3.5.0" "zustand": "3.5.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "7.9.0", "@babel/core": "7.14.2",
"@babel/preset-env": "7.9.0", "@babel/preset-env": "7.14.2",
"@babel/preset-react": "7.9.4", "@babel/preset-react": "7.13.13",
"@babel/preset-typescript": "7.9.0", "@babel/preset-typescript": "7.13.0",
"@testing-library/react": "11.2.3", "@svgr/webpack": "5.5.0",
"@testing-library/jest-dom": "5.12.0",
"@testing-library/react": "11.2.7",
"@types/applicationinsights-js": "1.0.7", "@types/applicationinsights-js": "1.0.7",
"@types/codemirror": "0.0.56", "@types/codemirror": "0.0.56",
"@types/crossroads": "0.0.30", "@types/crossroads": "0.0.30",
"@types/d3": "5.9.2", "@types/d3": "5.9.2",
"@types/dom-to-image": "2.6.2", "@types/dom-to-image": "2.6.2",
"@types/enzyme": "3.10.7", "@types/enzyme": "3.10.8",
"@types/enzyme-adapter-react-16": "1.0.6",
"@types/hasher": "0.0.31", "@types/hasher": "0.0.31",
"@types/jest": "26.0.20", "@types/memoize-one": "4.1.1",
"@types/node": "12.11.1", "@types/mkdirp": "1.0.1",
"@types/node": "15.3.0",
"@types/node-fetch": "2.5.10",
"@types/post-robot": "10.0.1", "@types/post-robot": "10.0.1",
"@types/promise.prototype.finally": "2.0.4",
"@types/jest": "26.0.20",
"@types/q": "1.5.1", "@types/q": "1.5.1",
"@types/react": "17.0.3", "@types/react": "17.0.5",
"@types/react-dom": "17.0.3", "@types/react-dom": "17.0.5",
"@types/react-notification-system": "0.2.39", "@types/react-notification-system": "0.2.39",
"@types/react-redux": "7.1.7", "@types/react-redux": "7.1.16",
"@types/sanitize-html": "1.27.2", "@types/sanitize-html": "2.3.1",
"@types/sinon": "2.3.3", "@types/sinon": "2.3.3",
"@types/styled-components": "5.1.1", "@types/styled-components": "5.1.9",
"@types/underscore": "1.7.36", "@types/underscore": "1.11.2",
"@typescript-eslint/eslint-plugin": "4.22.0", "@typescript-eslint/eslint-plugin": "4.23.0",
"@typescript-eslint/parser": "4.22.0", "@typescript-eslint/parser": "4.23.0",
"babel-jest": "24.9.0", "@wojtekmaj/enzyme-adapter-react-17": "0.6.1",
"babel-loader": "8.1.0", "babel-jest": "26.6.3",
"babel-loader": "8.2.2",
"buffer": "5.1.0", "buffer": "5.1.0",
"case-sensitive-paths-webpack-plugin": "2.3.0", "case-sensitive-paths-webpack-plugin": "2.4.0",
"clean-webpack-plugin": "4.0.0-alpha.0",
"copy-webpack-plugin": "8.1.1",
"create-file-webpack": "1.0.2", "create-file-webpack": "1.0.2",
"css-loader": "1.0.0", "css-loader": "1.0.0",
"enzyme": "3.11.0", "enzyme": "3.11.0",
"enzyme-adapter-react-16": "1.15.5", "enzyme-to-json": "3.6.2",
"enzyme-to-json": "3.6.1", "eslint": "7.26.0",
"eslint": "7.8.1",
"eslint-cli": "1.1.1", "eslint-cli": "1.1.1",
"eslint-plugin-no-null": "1.0.2", "eslint-plugin-no-null": "1.0.2",
"eslint-plugin-prefer-arrow": "1.2.2", "eslint-plugin-prefer-arrow": "1.2.3",
"eslint-plugin-react-hooks": "4.2.0", "eslint-plugin-react-hooks": "4.2.0",
"expose-loader": "2.0.0",
"expect-playwright": "0.3.3", "expect-playwright": "0.3.3",
"fast-glob": "3.2.5", "fast-glob": "3.2.5",
"file-loader": "2.0.0", "file-loader": "6.2.0",
"fs-extra": "7.0.0", "fs-extra": "7.0.0",
"html-inline-css-webpack-plugin": "1.11.0", "html-inline-css-webpack-plugin": "1.11.0",
"html-loader": "0.5.5", "html-loader": "2.1.2",
"html-loader-jest": "0.2.1", "html-loader-jest": "0.2.1",
"html-webpack-plugin": "4.5.2", "html-webpack-plugin": "5.3.1",
"jest": "25.5.4", "jest": "26.6.3",
"jest-canvas-mock": "2.1.0", "jest-canvas-mock": "2.1.0",
"jest-playwright-preset": "1.5.1", "jest-playwright-preset": "1.5.2",
"jest-trx-results-processor": "0.0.7", "jest-trx-results-processor": "2.2.0",
"less": "3.8.1", "less": "4.1.1",
"less-loader": "4.1.0", "less-loader": "8.1.1",
"less-vars-loader": "1.1.0", "less-vars-loader": "1.1.0",
"mini-css-extract-plugin": "0.4.3", "mini-css-extract-plugin": "1.6.0",
"monaco-editor-webpack-plugin": "1.7.0", "monaco-editor-webpack-plugin": "1.7.0",
"node-fetch": "2.6.1", "node-fetch": "2.6.1",
"playwright": "1.10.0", "playwright": "1.10.0",
"prettier": "2.2.1", "prettier": "2.3.0",
"raw-loader": "0.5.1", "process": "0.11.10",
"raw-loader": "4.0.2",
"react-dev-utils": "11.0.4", "react-dev-utils": "11.0.4",
"rimraf": "3.0.0", "rimraf": "3.0.0",
"sinon": "3.2.1", "sinon": "3.2.1",
"style-loader": "0.23.0", "style-loader": "2.0.0",
"ts-loader": "6.2.2", "terser-webpack-plugin": "5.1.2",
"tslint": "5.11.0", "ts-loader": "9.1.2",
"tslint-microsoft-contrib": "6.0.0", "tslint": "5.20.1",
"tslint-microsoft-contrib": "6.2.0",
"typedoc": "0.20.36", "typedoc": "0.20.36",
"typescript": "4.2.4", "typescript": "4.2.4",
"url-loader": "1.1.1", "url-loader": "4.1.1",
"wait-on": "4.0.2", "wait-on": "4.0.2",
"webpack": "4.46.0", "webpack": "5.37.0",
"webpack-bundle-analyzer": "3.6.1", "webpack-bundle-analyzer": "4.4.1",
"webpack-cli": "3.3.10", "webpack-cli": "4.7.0",
"webpack-dev-server": "3.11.0" "webpack-dev-server": "3.11.2"
}, },
"scripts": { "scripts": {
"start": "node --max-old-space-size=10196 node_modules/webpack-dev-server/bin/webpack-dev-server.js", "start": "webpack serve",
"dev": "echo \"WARNING: npm run dev has been deprecated\" && npm run build", "dev": "echo \"WARNING: npm run dev has been deprecated\" && npm run build",
"build:dataExplorer:ci": "npm run build:ci", "build:dataExplorer:ci": "npm run build:ci",
"build": "npm run format:check && npm run lint && npm run compile && npm run compile:strict && npm run pack:prod && npm run copyToConsumers", "build": "npm run format:check && npm run lint && npm run compile && npm run compile:strict && npm run pack:prod && npm run copyToConsumers",
@@ -225,4 +230,4 @@
"prettier": { "prettier": {
"printWidth": 120 "printWidth": 120
} }
} }

View File

@@ -0,0 +1,36 @@
import arrowLeftImg from "images/imgarrowlefticon.svg";
import React, { FunctionComponent } from "react";
import { userContext } from "../UserContext";
export interface CollapsedResourceTreeProps {
toggleLeftPaneExpanded: () => void;
isLeftPaneExpanded: boolean;
}
export const CollapsedResourceTree: FunctionComponent<CollapsedResourceTreeProps> = ({
toggleLeftPaneExpanded,
isLeftPaneExpanded,
}: CollapsedResourceTreeProps): JSX.Element => {
return (
<div id="mini" className={!isLeftPaneExpanded ? "mini toggle-mini" : "hiddenMain"}>
<div className="main-nav nav">
<ul className="nav">
<li
className="resourceTreeCollapse"
id="collapseToggleLeftPaneButton"
role="button"
tabIndex={0}
aria-label="Expand Tree"
>
<span className="leftarrowCollapsed" onClick={toggleLeftPaneExpanded}>
<img className="arrowCollapsed" src={arrowLeftImg} alt="Expand" />
</span>
<span className="collectionCollapsed" onClick={toggleLeftPaneExpanded}>
<span>{userContext.apiType} API</span>
</span>
</li>
</ul>
</div>
</div>
);
};

View File

@@ -0,0 +1,17 @@
import { userContext } from "../UserContext";
function isVirtualNetworkFilterEnabled() {
return userContext.databaseAccount?.properties?.isVirtualNetworkFilterEnabled;
}
function isIpRulesEnabled() {
return userContext.databaseAccount?.properties?.ipRules?.length > 0;
}
function isPrivateEndpointConnectionsEnabled() {
return userContext.databaseAccount?.properties?.privateEndpointConnections?.length > 0;
}
export function isPublicInternetAccessAllowed(): boolean {
return !isVirtualNetworkFilterEnabled() && !isIpRulesEnabled() && !isPrivateEndpointConnectionsEnabled();
}

View File

@@ -32,7 +32,7 @@ export const EntityValue: FunctionComponent<TableEntityProps> = ({
<DatePicker <DatePicker
className="addEntityDatePicker" className="addEntityDatePicker"
placeholder={entityValuePlaceholder} placeholder={entityValuePlaceholder}
value={entityValue && new Date(entityValue)} value={entityValue ? new Date(entityValue) : new Date()}
ariaLabel={entityValuePlaceholder} ariaLabel={entityValuePlaceholder}
onSelectDate={onSelectDate} onSelectDate={onSelectDate}
disabled={isEntityValueDisable} disabled={isEntityValueDisable}
@@ -59,7 +59,7 @@ export const EntityValue: FunctionComponent<TableEntityProps> = ({
disabled={isEntityValueDisable} disabled={isEntityValueDisable}
type={entityValueType} type={entityValueType}
placeholder={entityValuePlaceholder} placeholder={entityValuePlaceholder}
value={typeof entityValue === "string" && entityValue} value={typeof entityValue === "string" ? entityValue : ""}
onChange={onEntityValueChange} onChange={onEntityValueChange}
/> />
); );

View File

@@ -31,7 +31,7 @@ const collection = {
}, },
} as Collection; } as Collection;
const documentId = ({ const documentId = {
partitionKeyHeader: () => "[]", partitionKeyHeader: () => "[]",
self: "db/testDB/db/testCollection/docs/testId", self: "db/testDB/db/testCollection/docs/testId",
partitionKeyProperty, partitionKeyProperty,
@@ -40,7 +40,7 @@ const documentId = ({
kind: "Hash", kind: "Hash",
version: 1, version: 1,
}, },
} as unknown) as DocumentId; } as unknown as DocumentId;
const databaseAccount = { const databaseAccount = {
id: "foo", id: "foo",

View File

@@ -314,7 +314,7 @@ export function createMongoCollectionWithProxy(
return window return window
.fetch( .fetch(
`${endpoint}/createCollection?${queryString.stringify( `${endpoint}/createCollection?${queryString.stringify(
(mongoParams as unknown) as queryString.ParsedUrlQueryInput mongoParams as unknown as queryString.ParsedUrlQueryInput
)}`, )}`,
{ {
method: "POST", method: "POST",

View File

@@ -0,0 +1,59 @@
import arrowLeftImg from "images/imgarrowlefticon.svg";
import refreshImg from "images/refresh-cosmos.svg";
import React, { FunctionComponent } from "react";
import { AuthType } from "../AuthType";
import { userContext } from "../UserContext";
export interface ResourceTreeProps {
toggleLeftPaneExpanded: () => void;
isLeftPaneExpanded: boolean;
}
export const ResourceTree: FunctionComponent<ResourceTreeProps> = ({
toggleLeftPaneExpanded,
isLeftPaneExpanded,
}: ResourceTreeProps): JSX.Element => {
return (
<div id="main" className={isLeftPaneExpanded ? "main" : "hiddenMain"}>
{/* Collections Window - - Start */}
<div id="mainslide" className="flexContainer">
{/* Collections Window Title/Command Bar - Start */}
<div className="collectiontitle">
<div className="coltitle">
<span className="titlepadcol">{userContext.apiType} API</span>
<div className="float-right">
<span
className="padimgcolrefresh"
data-test="refreshTree"
role="button"
data-bind="click: onRefreshResourcesClick, clickBubble: false, event: { keypress: onRefreshDatabasesKeyPress }"
tabIndex={0}
aria-label="Refresh tree"
title="Refresh tree"
>
<img className="refreshcol" src={refreshImg} alt="Refresh Tree" />
</span>
<span
className="padimgcolrefresh1"
id="expandToggleLeftPaneButton"
role="button"
onClick={toggleLeftPaneExpanded}
tabIndex={0}
aria-label="Collapse Tree"
title="Collapse Tree"
>
<img className="refreshcol1" src={arrowLeftImg} alt="Hide" />
</span>
</div>
</div>
</div>
{userContext.authType === AuthType.ResourceToken ? (
<div style={{ overflowY: "auto" }} data-bind="react:resourceTreeForResourceToken" />
) : (
<div style={{ overflowY: "auto" }} data-bind="react:resourceTree" />
)}
</div>
{/* Collections Window - End */}
</div>
);
};

View File

@@ -1,3 +1,5 @@
import DeleteIcon from "images/delete.svg";
import EditIcon from "images/Edit_entity.svg";
import { import {
Dropdown, Dropdown,
IDropdownOption, IDropdownOption,
@@ -10,8 +12,6 @@ import {
TooltipHost, TooltipHost,
} from "@fluentui/react"; } from "@fluentui/react";
import React, { FunctionComponent } from "react"; import React, { FunctionComponent } from "react";
import DeleteIcon from "../../images/delete.svg";
import EditIcon from "../../images/Edit_entity.svg";
import { CassandraType, TableType } from "../Explorer/Tables/Constants"; import { CassandraType, TableType } from "../Explorer/Tables/Constants";
import { userContext } from "../UserContext"; import { userContext } from "../UserContext";
import { EntityValue } from "./EntityValue"; import { EntityValue } from "./EntityValue";

View File

@@ -1,6 +1,6 @@
import { Image, Stack, TextField } from "@fluentui/react"; import { Image, Stack, TextField } from "@fluentui/react";
import FolderIcon from "images/folder_16x16.svg";
import React, { ChangeEvent, FunctionComponent, KeyboardEvent, useRef, useState } from "react"; import React, { ChangeEvent, FunctionComponent, KeyboardEvent, useRef, useState } from "react";
import FolderIcon from "../../../images/folder_16x16.svg";
import * as Constants from "../Constants"; import * as Constants from "../Constants";
import { InfoTooltip } from "../Tooltip/InfoTooltip"; import { InfoTooltip } from "../Tooltip/InfoTooltip";

View File

@@ -54,7 +54,7 @@ export async function createTrigger(
const response = await client() const response = await client()
.database(databaseId) .database(databaseId)
.container(collectionId) .container(collectionId)
.scripts.triggers.create((trigger as unknown) as TriggerDefinition); // TODO: TypeScript does not like the SQL SDK trigger type .scripts.triggers.create(trigger as unknown as TriggerDefinition); // TODO: TypeScript does not like the SQL SDK trigger type
return response.resource; return response.resource;
} catch (error) { } catch (error) {
handleError(error, "CreateTrigger", `Error while creating trigger ${trigger.id}`); handleError(error, "CreateTrigger", `Error while creating trigger ${trigger.id}`);

View File

@@ -405,7 +405,7 @@ const updateOfferWithSDK = async (params: UpdateOfferParams): Promise<Offer> =>
const sdkResponse = await client() const sdkResponse = await client()
.offer(params.currentOffer.id) .offer(params.currentOffer.id)
// TODO Remove casting when SDK types are fixed (https://github.com/Azure/azure-sdk-for-js/issues/10660) // TODO Remove casting when SDK types are fixed (https://github.com/Azure/azure-sdk-for-js/issues/10660)
.replace((newOffer as unknown) as OfferDefinition, options); .replace(newOffer as unknown as OfferDefinition, options);
return parseSDKOfferResponse(sdkResponse); return parseSDKOfferResponse(sdkResponse);
}; };

View File

@@ -51,7 +51,7 @@ export async function updateTrigger(
.database(databaseId) .database(databaseId)
.container(collectionId) .container(collectionId)
.scripts.trigger(trigger.id) .scripts.trigger(trigger.id)
.replace((trigger as unknown) as TriggerDefinition); // TODO: TypeScript does not like the SQL SDK trigger type .replace(trigger as unknown as TriggerDefinition); // TODO: TypeScript does not like the SQL SDK trigger type
return response?.resource; return response?.resource;
} catch (error) { } catch (error) {
handleError(error, "UpdateTrigger", `Error while updating trigger ${trigger.id}`); handleError(error, "UpdateTrigger", `Error while updating trigger ${trigger.id}`);

View File

@@ -22,6 +22,7 @@ export interface DatabaseAccountExtendedProperties {
enableAnalyticalStorage?: boolean; enableAnalyticalStorage?: boolean;
isVirtualNetworkFilterEnabled?: boolean; isVirtualNetworkFilterEnabled?: boolean;
ipRules?: IpRule[]; ipRules?: IpRule[];
privateEndpointConnections?: unknown[];
} }
export interface DatabaseAccountResponseLocation { export interface DatabaseAccountResponseLocation {
@@ -391,16 +392,6 @@ export interface GeospatialConfig {
type: string; type: string;
} }
export interface GatewayDatabaseAccount {
MediaLink: string;
DatabasesLink: string;
MaxMediaStorageUsageInMB: number;
CurrentMediaStorageUsageInMB: number;
EnableMultipleWriteLocations?: boolean;
WritableLocations: RegionEndpoint[];
ReadableLocations: RegionEndpoint[];
}
export interface RegionEndpoint { export interface RegionEndpoint {
name: string; name: string;
documentAccountEndpoint: string; documentAccountEndpoint: string;
@@ -421,13 +412,6 @@ export interface AccountKeys {
secondaryReadonlyMasterKey: string; secondaryReadonlyMasterKey: string;
} }
export interface AfecFeature {
id: string;
name: string;
properties: { state: string };
type: string;
}
export interface OperationStatus { export interface OperationStatus {
status: string; status: string;
id?: string; id?: string;
@@ -507,91 +491,6 @@ export interface MongoParameters extends RpParameters {
analyticalStorageTtl?: number; analyticalStorageTtl?: number;
} }
export interface SparkClusterLibrary {
name: string;
}
export interface Library extends SparkClusterLibrary {
properties: {
kind: "Jar";
source: {
kind: "HttpsUri";
uri: string;
libraryFileName: string;
};
};
}
export interface LibraryFeedResponse {
value: Library[];
}
export interface ArmResource {
id: string;
location: string;
name: string;
type: string;
tags: { [key: string]: string };
}
export interface ArcadiaWorkspaceIdentity {
type: string;
principalId: string;
tenantId: string;
}
export interface ArcadiaWorkspaceProperties {
managedResourceGroupName: string;
provisioningState: string;
sqlAdministratorLogin: string;
connectivityEndpoints: {
artifacts: string;
dev: string;
spark: string;
sql: string;
web: string;
};
defaultDataLakeStorage: {
accountUrl: string;
filesystem: string;
};
}
export interface ArcadiaWorkspaceFeedResponse {
value: ArcadiaWorkspace[];
}
export interface ArcadiaWorkspace extends ArmResource {
identity: ArcadiaWorkspaceIdentity;
properties: ArcadiaWorkspaceProperties;
}
export interface SparkPoolFeedResponse {
value: SparkPool[];
}
export interface SparkPoolProperties {
creationDate: string;
sparkVersion: string;
nodeCount: number;
nodeSize: string;
nodeSizeFamily: string;
provisioningState: string;
autoScale: {
enabled: boolean;
minNodeCount: number;
maxNodeCount: number;
};
autoPause: {
enabled: boolean;
delayInMinutes: number;
};
}
export interface SparkPool extends ArmResource {
properties: SparkPoolProperties;
}
export interface MemoryUsageInfo { export interface MemoryUsageInfo {
freeKB: number; freeKB: number;
totalKB: number; totalKB: number;

View File

@@ -15,6 +15,7 @@ import StoredProcedure from "../Explorer/Tree/StoredProcedure";
import Trigger from "../Explorer/Tree/Trigger"; import Trigger from "../Explorer/Tree/Trigger";
import UserDefinedFunction from "../Explorer/Tree/UserDefinedFunction"; import UserDefinedFunction from "../Explorer/Tree/UserDefinedFunction";
import { SelfServeType } from "../SelfServe/SelfServeUtils"; import { SelfServeType } from "../SelfServe/SelfServeUtils";
import { CollectionCreationDefaults } from "../UserContext";
import { SqlTriggerResource } from "../Utils/arm/generatedClients/cosmos/types"; import { SqlTriggerResource } from "../Utils/arm/generatedClients/cosmos/types";
import * as DataModels from "./DataModels"; import * as DataModels from "./DataModels";
import { SubscriptionType } from "./SubscriptionType"; import { SubscriptionType } from "./SubscriptionType";
@@ -411,25 +412,6 @@ export interface SelfServeFrameInputs {
flights?: readonly string[]; flights?: readonly string[];
} }
export interface CollectionCreationDefaults {
storage: string;
throughput: ThroughputDefaults;
}
export interface ThroughputDefaults {
fixed: number;
unlimited:
| number
| {
collectionThreshold: number;
lessThanOrEqualToThreshold: number;
greatThanThreshold: number;
};
unlimitedmax: number;
unlimitedmin: number;
shared: number;
}
export class MonacoEditorSettings { export class MonacoEditorSettings {
public readonly language: string; public readonly language: string;
public readonly readOnly: boolean; public readonly readOnly: boolean;

View File

@@ -1,14 +0,0 @@
jest.mock("monaco-editor");
import * as ko from "knockout";
import "./ComponentRegisterer";
describe("Component Registerer", () => {
it("should register json-editor component", () => {
expect(ko.components.isRegistered("json-editor")).toBe(true);
});
it("should register dynamic-list component", () => {
expect(ko.components.isRegistered("dynamic-list")).toBe(true);
});
});

View File

@@ -1,12 +1,8 @@
import * as ko from "knockout"; import * as ko from "knockout";
import { DiffEditorComponent } from "./Controls/DiffEditor/DiffEditorComponent"; import { DiffEditorComponent } from "./Controls/DiffEditor/DiffEditorComponent";
import { DynamicListComponent } from "./Controls/DynamicList/DynamicListComponent";
import { EditorComponent } from "./Controls/Editor/EditorComponent"; import { EditorComponent } from "./Controls/Editor/EditorComponent";
import { JsonEditorComponent } from "./Controls/JsonEditor/JsonEditorComponent"; import { JsonEditorComponent } from "./Controls/JsonEditor/JsonEditorComponent";
import { ThroughputInputComponentAutoPilotV3 } from "./Controls/ThroughputInput/ThroughputInputComponentAutoPilotV3";
ko.components.register("editor", new EditorComponent()); ko.components.register("editor", new EditorComponent());
ko.components.register("json-editor", new JsonEditorComponent()); ko.components.register("json-editor", new JsonEditorComponent());
ko.components.register("diff-editor", new DiffEditorComponent()); ko.components.register("diff-editor", new DiffEditorComponent());
ko.components.register("dynamic-list", DynamicListComponent);
ko.components.register("throughput-input-autopilot-v3", ThroughputInputComponentAutoPilotV3);

View File

@@ -1,22 +1,22 @@
import AddCollectionIcon from "../../images/AddCollection.svg"; import AddCollectionIcon from "images/AddCollection.svg";
import AddSqlQueryIcon from "../../images/AddSqlQuery_16x16.svg"; import AddSqlQueryIcon from "images/AddSqlQuery_16x16.svg";
import AddStoredProcedureIcon from "../../images/AddStoredProcedure.svg"; import AddStoredProcedureIcon from "images/AddStoredProcedure.svg";
import AddTriggerIcon from "../../images/AddTrigger.svg"; import AddTriggerIcon from "images/AddTrigger.svg";
import AddUdfIcon from "../../images/AddUdf.svg"; import AddUdfIcon from "images/AddUdf.svg";
import DeleteCollectionIcon from "../../images/DeleteCollection.svg"; import DeleteCollectionIcon from "images/DeleteCollection.svg";
import DeleteDatabaseIcon from "../../images/DeleteDatabase.svg"; import DeleteDatabaseIcon from "images/DeleteDatabase.svg";
import DeleteSprocIcon from "../../images/DeleteSproc.svg"; import DeleteSprocIcon from "images/DeleteSproc.svg";
import DeleteTriggerIcon from "../../images/DeleteTrigger.svg"; import DeleteTriggerIcon from "images/DeleteTrigger.svg";
import DeleteUDFIcon from "../../images/DeleteUDF.svg"; import DeleteUDFIcon from "images/DeleteUDF.svg";
import HostedTerminalIcon from "../../images/Hosted-Terminal.svg"; import HostedTerminalIcon from "images/Hosted-Terminal.svg";
import * as ViewModels from "../Contracts/ViewModels"; import * as ViewModels from "../Contracts/ViewModels";
import { userContext } from "../UserContext"; import { userContext } from "../UserContext";
import { getCollectionName, getDatabaseName } from "../Utils/APITypeUtils";
import { TreeNodeMenuItem } from "./Controls/TreeComponent/TreeComponent"; import { TreeNodeMenuItem } from "./Controls/TreeComponent/TreeComponent";
import Explorer from "./Explorer"; import Explorer from "./Explorer";
import StoredProcedure from "./Tree/StoredProcedure"; import StoredProcedure from "./Tree/StoredProcedure";
import Trigger from "./Tree/Trigger"; import Trigger from "./Tree/Trigger";
import UserDefinedFunction from "./Tree/UserDefinedFunction"; import UserDefinedFunction from "./Tree/UserDefinedFunction";
export interface CollectionContextMenuButtonParams { export interface CollectionContextMenuButtonParams {
databaseId: string; databaseId: string;
collectionId: string; collectionId: string;
@@ -34,7 +34,7 @@ export class ResourceTreeContextMenuButtonFactory {
{ {
iconSrc: AddCollectionIcon, iconSrc: AddCollectionIcon,
onClick: () => container.onNewCollectionClicked(databaseId), onClick: () => container.onNewCollectionClicked(databaseId),
label: container.addCollectionText(), label: `New ${getCollectionName()}`,
}, },
]; ];
@@ -42,7 +42,7 @@ export class ResourceTreeContextMenuButtonFactory {
items.push({ items.push({
iconSrc: DeleteDatabaseIcon, iconSrc: DeleteDatabaseIcon,
onClick: () => container.openDeleteDatabaseConfirmationPane(), onClick: () => container.openDeleteDatabaseConfirmationPane(),
label: container.deleteDatabaseText(), label: `Delete ${getDatabaseName()}`,
styleClass: "deleteDatabaseMenuItem", styleClass: "deleteDatabaseMenuItem",
}); });
} }
@@ -115,7 +115,7 @@ export class ResourceTreeContextMenuButtonFactory {
items.push({ items.push({
iconSrc: DeleteCollectionIcon, iconSrc: DeleteCollectionIcon,
onClick: () => container.openDeleteCollectionConfirmationPane(), onClick: () => container.openDeleteCollectionConfirmationPane(),
label: container.deleteCollectionText(), label: `Delete ${getCollectionName()}`,
styleClass: "deleteCollectionMenuItem", styleClass: "deleteCollectionMenuItem",
}); });

View File

@@ -2,10 +2,10 @@
* Accordion top class * Accordion top class
*/ */
import TriangleDownIcon from "images/Triangle-down.svg";
import TriangleRightIcon from "images/Triangle-right.svg";
import * as React from "react"; import * as React from "react";
import AnimateHeight from "react-animate-height"; import AnimateHeight from "react-animate-height";
import TriangleDownIcon from "../../../../images/Triangle-down.svg";
import TriangleRightIcon from "../../../../images/Triangle-right.svg";
import * as Constants from "../../../Common/Constants"; import * as Constants from "../../../Common/Constants";
export interface AccordionComponentProps {} export interface AccordionComponentProps {}

View File

@@ -1,142 +0,0 @@
import { DefaultButton, IButtonStyles, IContextualMenuItem, IContextualMenuProps } from "@fluentui/react";
import * as React from "react";
import { getErrorMessage } from "../../../Common/ErrorHandlingUtils";
import * as Logger from "../../../Common/Logger";
import { ArcadiaWorkspace, SparkPool } from "../../../Contracts/DataModels";
export interface ArcadiaMenuPickerProps {
selectText?: string;
disableSubmenu?: boolean;
selectedSparkPool: string;
workspaces: ArcadiaWorkspaceItem[];
onSparkPoolSelect: (
e: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>,
item: IContextualMenuItem
) => boolean | void;
onCreateNewWorkspaceClicked: () => boolean | void;
onCreateNewSparkPoolClicked: (workspaceResourceId: string) => boolean | void;
}
interface ArcadiaMenuPickerStates {
selectedSparkPool: string;
}
export interface ArcadiaWorkspaceItem extends ArcadiaWorkspace {
sparkPools: SparkPool[];
}
export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, ArcadiaMenuPickerStates> {
constructor(props: ArcadiaMenuPickerProps) {
super(props);
this.state = {
selectedSparkPool: props.selectedSparkPool,
};
}
private _onSparkPoolClicked = (
e: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>,
item: IContextualMenuItem
): boolean | void => {
try {
this.props.onSparkPoolSelect(e, item);
this.setState({
selectedSparkPool: item.text,
});
} catch (error) {
Logger.logError(getErrorMessage(error), "ArcadiaMenuPicker/_onSparkPoolClicked");
throw error;
}
};
private _onCreateNewWorkspaceClicked = (
e: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>,
item: IContextualMenuItem
): boolean | void => {
this.props.onCreateNewWorkspaceClicked();
};
private _onCreateNewSparkPoolClicked = (
e: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>,
item: IContextualMenuItem
): boolean | void => {
this.props.onCreateNewSparkPoolClicked(item.key);
};
public render() {
const { workspaces } = this.props;
let workspaceMenuItems: IContextualMenuItem[] = workspaces.map((workspace) => {
let sparkPoolsMenuProps: IContextualMenuProps = {
items: workspace.sparkPools.map(
(sparkpool): IContextualMenuItem => ({
key: sparkpool.id,
text: sparkpool.name,
onClick: this._onSparkPoolClicked,
})
),
};
if (!sparkPoolsMenuProps.items.length) {
sparkPoolsMenuProps.items.push({
key: workspace.id,
text: "Create new spark pool",
onClick: this._onCreateNewSparkPoolClicked,
});
}
return {
key: workspace.id,
text: workspace.name,
subMenuProps: this.props.disableSubmenu ? undefined : sparkPoolsMenuProps,
};
});
if (!workspaceMenuItems.length) {
workspaceMenuItems.push({
key: "create_workspace",
text: "Create new workspace",
onClick: this._onCreateNewWorkspaceClicked,
});
}
const dropdownStyle: IButtonStyles = {
root: {
backgroundColor: "transparent",
margin: "auto 5px",
padding: "0",
border: "0",
},
rootHovered: {
backgroundColor: "transparent",
},
rootChecked: {
backgroundColor: "transparent",
},
rootFocused: {
backgroundColor: "transparent",
},
rootExpanded: {
backgroundColor: "transparent",
},
flexContainer: {
height: "30px",
border: "1px solid #a6a6a6",
padding: "0 8px",
},
label: {
fontWeight: "400",
fontSize: "12px",
},
};
return (
<DefaultButton
text={this.state.selectedSparkPool || this.props.selectText || "Select a Spark pool"}
persistMenu={true}
className="arcadia-menu-picker"
menuProps={{
items: workspaceMenuItems,
}}
styles={dropdownStyle}
/>
);
}
}

View File

@@ -6,8 +6,8 @@
* - calling render() * - calling render()
*/ */
import LeftArrowIcon from "images/imgarrowlefticon.svg";
import * as React from "react"; import * as React from "react";
import LeftArrowIcon from "../../../../images/imgarrowlefticon.svg";
import { AccessibleElement } from "../../Controls/AccessibleElement/AccessibleElement"; import { AccessibleElement } from "../../Controls/AccessibleElement/AccessibleElement";
export interface CollapsiblePanelProps { export interface CollapsiblePanelProps {

View File

@@ -1,15 +1,12 @@
import * as StringUtils from "../../../Utils/StringUtils"; import CollapseChevronDownIcon from "images/QueryBuilder/CollapseChevronDown_16x.png";
import { KeyCodes } from "../../../Common/Constants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import CollapseChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png";
/** /**
* React component for Command button component. * React component for Command button component.
*/ */
import * as React from "react"; import * as React from "react";
import { ArcadiaMenuPickerProps } from "../Arcadia/ArcadiaMenuPicker"; import { KeyCodes } from "../../../Common/Constants";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import * as StringUtils from "../../../Utils/StringUtils";
/** /**
* Options for this component * Options for this component
@@ -114,15 +111,6 @@ export interface CommandButtonComponentProps {
* Aria-label for the button * Aria-label for the button
*/ */
ariaLabel: string; ariaLabel: string;
//TODO: generalize customized command bar
/**
* If set to true, will render arcadia picker
*/
isArcadiaPicker?: boolean;
/**
* props to render arcadia picker
*/
arcadiaProps?: ArcadiaMenuPickerProps;
} }
export class CommandButtonComponent extends React.Component<CommandButtonComponentProps> { export class CommandButtonComponent extends React.Component<CommandButtonComponentProps> {
@@ -216,11 +204,9 @@ export class CommandButtonComponent extends React.Component<CommandButtonCompone
}} }}
> >
<div className="commandDropdown"> <div className="commandDropdown">
{this.props.children.map( {this.props.children.map((c: CommandButtonComponentProps, index: number): JSX.Element => {
(c: CommandButtonComponentProps, index: number): JSX.Element => { return CommandButtonComponent.renderButton(c, `${index}`);
return CommandButtonComponent.renderButton(c, `${index}`); })}
}
)}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,105 +0,0 @@
import React from "react";
import { shallow, mount } from "enzyme";
import { DefaultDirectoryDropdownComponent, DefaultDirectoryDropdownProps } from "./DefaultDirectoryDropdownComponent";
import { Tenant } from "../../../Contracts/DataModels";
const createBlankProps = (): DefaultDirectoryDropdownProps => {
return {
defaultDirectoryId: "",
directories: [],
onDefaultDirectoryChange: jest.fn(),
};
};
const createBlankDirectory = (): Tenant => {
return {
countryCode: "",
displayName: "",
domains: [],
id: "",
tenantId: "",
};
};
describe("test render", () => {
it("renders with no directories", () => {
const props = createBlankProps();
const wrapper = shallow(<DefaultDirectoryDropdownComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
it("renders with directories but no default", () => {
const props = createBlankProps();
const tenant1 = createBlankDirectory();
tenant1.displayName = "Microsoft";
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
const tenant2 = createBlankDirectory();
tenant1.displayName = "Macrohard";
tenant1.tenantId = "asdfghjklzxcvbnm9876543210";
props.directories = [tenant1, tenant2];
const wrapper = shallow(<DefaultDirectoryDropdownComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
it("renders with directories and default", () => {
const props = createBlankProps();
const tenant1 = createBlankDirectory();
tenant1.displayName = "Microsoft";
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
const tenant2 = createBlankDirectory();
tenant1.displayName = "Macrohard";
tenant1.tenantId = "asdfghjklzxcvbnm9876543210";
props.directories = [tenant1, tenant2];
props.defaultDirectoryId = "asdfghjklzxcvbnm9876543210";
const wrapper = shallow(<DefaultDirectoryDropdownComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
it("renders with directories and last visit default", () => {
const props = createBlankProps();
const tenant1 = createBlankDirectory();
tenant1.displayName = "Microsoft";
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
const tenant2 = createBlankDirectory();
tenant1.displayName = "Macrohard";
tenant1.tenantId = "asdfghjklzxcvbnm9876543210";
props.directories = [tenant1, tenant2];
props.defaultDirectoryId = "lastVisited";
const wrapper = shallow(<DefaultDirectoryDropdownComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
});
describe("test function", () => {
it("on default directory change", () => {
const props = createBlankProps();
const tenant1 = createBlankDirectory();
tenant1.displayName = "Microsoft";
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
const tenant2 = createBlankDirectory();
tenant1.displayName = "Macrohard";
tenant1.tenantId = "asdfghjklzxcvbnm9876543210";
props.directories = [tenant1, tenant2];
props.defaultDirectoryId = "lastVisited";
const wrapper = mount(<DefaultDirectoryDropdownComponent {...props} />);
wrapper.find("div.defaultDirectoryDropdown").find("div.ms-Dropdown").simulate("click");
expect(wrapper.exists("div.ms-Callout-main")).toBe(true);
wrapper.find("button.ms-Dropdown-item").at(1).simulate("click");
expect(props.onDefaultDirectoryChange).toBeCalled();
expect(props.onDefaultDirectoryChange).toHaveBeenCalled();
wrapper.find("div.defaultDirectoryDropdown").find("div.ms-Dropdown").simulate("click");
expect(wrapper.exists("div.ms-Callout-main")).toBe(true);
wrapper.find("button.ms-Dropdown-item").at(0).simulate("click");
expect(props.onDefaultDirectoryChange).toBeCalled();
expect(props.onDefaultDirectoryChange).toHaveBeenCalled();
});
});

View File

@@ -21,14 +21,12 @@ export class DefaultDirectoryDropdownComponent extends React.Component<DefaultDi
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})`, };
}; });
}
);
const dropDownOptions: Array<IDropdownOption> = [lastVisitedOption, ...directoryOptions]; const dropDownOptions: Array<IDropdownOption> = [lastVisitedOption, ...directoryOptions];
const dropDownProps: IDropdownProps = { const dropDownProps: IDropdownProps = {
label: "Set your default directory", label: "Set your default directory",

View File

@@ -1,36 +0,0 @@
import * as ko from "knockout";
import * as React from "react";
import { DirectoryListComponent, DirectoryListProps } from "./DirectoryListComponent";
import { DefaultDirectoryDropdownComponent, DefaultDirectoryDropdownProps } from "./DefaultDirectoryDropdownComponent";
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
export class DirectoryComponentAdapter implements ReactAdapter {
public parameters: ko.Observable<number>;
constructor(
private _dropdownProps: ko.Observable<DefaultDirectoryDropdownProps>,
private _listProps: ko.Observable<DirectoryListProps>
) {
this._dropdownProps.subscribe(() => this.forceRender());
this._listProps.subscribe(() => this.forceRender());
this.parameters = ko.observable<number>(Date.now());
}
public renderComponent(): JSX.Element {
return (
<div>
<div className="directoryDropdownContainer">
<DefaultDirectoryDropdownComponent {...this._dropdownProps()} />
</div>
<div className="directoryDivider" />
<div className="directoryListContainer">
<DirectoryListComponent {...this._listProps()} />
</div>
</div>
);
}
public forceRender(): void {
window.requestAnimationFrame(() => this.parameters(Date.now()));
}
}

View File

@@ -1,78 +0,0 @@
import React from "react";
import { shallow, mount } from "enzyme";
import { DirectoryListComponent, DirectoryListProps } from "./DirectoryListComponent";
import { Tenant } from "../../../Contracts/DataModels";
const createBlankProps = (): DirectoryListProps => {
return {
selectedDirectoryId: undefined,
directories: [],
onNewDirectorySelected: jest.fn(),
};
};
const createBlankDirectory = (): Tenant => {
return {
countryCode: undefined,
displayName: undefined,
domains: [],
id: undefined,
tenantId: undefined,
};
};
describe("test render", () => {
it("renders with no directories", () => {
const props = createBlankProps();
const wrapper = shallow(<DirectoryListComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
it("renders with directories and selected", () => {
const props = createBlankProps();
const tenant1 = createBlankDirectory();
tenant1.displayName = "Microsoft";
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
const tenant2 = createBlankDirectory();
tenant1.displayName = "Macrohard";
tenant1.tenantId = "asdfghjklzxcvbnm9876543210";
props.directories = [tenant1, tenant2];
props.selectedDirectoryId = "asdfghjklzxcvbnm9876543210";
const wrapper = shallow(<DirectoryListComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
it("renders with filters", () => {
const props = createBlankProps();
const tenant1 = createBlankDirectory();
tenant1.displayName = "Microsoft";
tenant1.tenantId = "1234567890";
const tenant2 = createBlankDirectory();
tenant1.displayName = "Macrohard";
tenant1.tenantId = "9876543210";
props.directories = [tenant1, tenant2];
props.selectedDirectoryId = "9876543210";
const wrapper = mount(<DirectoryListComponent {...props} />);
wrapper.find("input.ms-TextField-field").simulate("change", { target: { value: "Macro" } });
expect(wrapper).toMatchSnapshot();
});
});
describe("test function", () => {
it("on new directory selected", () => {
const props = createBlankProps();
const tenant1 = createBlankDirectory();
tenant1.displayName = "Microsoft";
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
props.directories = [tenant1];
const wrapper = mount(<DirectoryListComponent {...props} />);
wrapper.find("button.directoryListButton").simulate("click");
expect(props.onNewDirectorySelected).toBeCalled();
expect(props.onNewDirectorySelected).toHaveBeenCalled();
});
});

View File

@@ -1,126 +0,0 @@
import {
DefaultButton,
IButtonProps,
ITextFieldProps,
List,
ScrollablePane,
Sticky,
StickyPositionType,
TextField,
} from "@fluentui/react";
import * as React from "react";
import _ from "underscore";
import { Tenant } from "../../../Contracts/DataModels";
export interface DirectoryListProps {
directories: Array<Tenant>;
selectedDirectoryId: string;
onNewDirectorySelected: (newDirectory: Tenant) => void;
}
export interface DirectoryListComponentState {
filterText: string;
}
// onRenderCell is not called when selectedDirectoryId changed, so add a selected state to force render
interface ListTenant extends Tenant {
selected?: boolean;
}
export class DirectoryListComponent extends React.Component<DirectoryListProps, DirectoryListComponentState> {
constructor(props: DirectoryListProps) {
super(props);
this.state = {
filterText: "",
};
}
public render(): JSX.Element {
const { directories: originalItems, selectedDirectoryId } = this.props;
const { filterText } = this.state;
const filteredItems =
originalItems && originalItems.length && filterText
? originalItems.filter(
(directory) =>
directory.displayName &&
directory.displayName.toLowerCase().indexOf(filterText && filterText.toLowerCase()) >= 0
)
: originalItems;
const filteredItemsSelected = filteredItems.map((t) => {
let tenant: ListTenant = t;
tenant.selected = t.tenantId === selectedDirectoryId;
return tenant;
});
const textFieldProps: ITextFieldProps = {
className: "directoryListFilterTextBox",
placeholder: "Filter by directory name",
onChange: this._onFilterChanged,
ariaLabel: "Directory filter text box",
};
// TODO: add magnify glass to search bar with onRenderSuffix
return (
<ScrollablePane data-is-scrollable="true">
<Sticky stickyPosition={StickyPositionType.Header}>
<TextField {...textFieldProps} />
</Sticky>
<List items={filteredItemsSelected} onRenderCell={this._onRenderCell} />
</ScrollablePane>
);
}
private _onFilterChanged = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, text?: string): void => {
this.setState({
filterText: text,
});
};
private _onRenderCell = (directory: ListTenant): JSX.Element => {
const buttonProps: IButtonProps = {
disabled: directory.selected || false,
className: "directoryListButton",
onClick: this._onNewDirectoryClick,
styles: {
root: {
backgroundColor: "transparent",
height: "auto",
borderBottom: "1px solid #ccc",
padding: "1px 0",
width: "100%",
},
rootDisabled: {
backgroundColor: "#f1f1f8",
},
rootHovered: {
backgroundColor: "rgba(85,179,255,.1)",
},
flexContainer: {
height: "auto",
justifyContent: "flex-start",
},
},
};
return (
<DefaultButton {...buttonProps}>
<div className="directoryListItem" data-is-focusable={true}>
<div className="directoryListItemName">{directory.displayName}</div>
<div className="directoryListItemId">{directory.tenantId}</div>
</div>
</DefaultButton>
);
};
private _onNewDirectoryClick = (e: React.MouseEvent<HTMLButtonElement>): void => {
if (!e || !e.currentTarget) {
return;
}
const buttonElement = e.currentTarget;
const selectedDirectoryId = buttonElement.getElementsByClassName("directoryListItemId")[0].textContent;
const selectedDirectory = _.find(this.props.directories, (d) => d.tenantId === selectedDirectoryId);
this.props.onNewDirectorySelected(selectedDirectory);
};
}

View File

@@ -1,93 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`test render renders with directories and default 1`] = `
<Dropdown
className="defaultDirectoryDropdown"
defaultSelectedKey="asdfghjklzxcvbnm9876543210"
label="Set your default directory"
onChange={[Function]}
options={
Array [
Object {
"key": "lastVisited",
"text": "Sign in to your last visited directory",
},
Object {
"key": "asdfghjklzxcvbnm9876543210",
"text": "Macrohard(asdfghjklzxcvbnm9876543210)",
},
Object {
"key": "",
"text": "()",
},
]
}
/>
`;
exports[`test render renders with directories and last visit default 1`] = `
<Dropdown
className="defaultDirectoryDropdown"
defaultSelectedKey="lastVisited"
label="Set your default directory"
onChange={[Function]}
options={
Array [
Object {
"key": "lastVisited",
"text": "Sign in to your last visited directory",
},
Object {
"key": "asdfghjklzxcvbnm9876543210",
"text": "Macrohard(asdfghjklzxcvbnm9876543210)",
},
Object {
"key": "",
"text": "()",
},
]
}
/>
`;
exports[`test render renders with directories but no default 1`] = `
<Dropdown
className="defaultDirectoryDropdown"
defaultSelectedKey="lastVisited"
label="Set your default directory"
onChange={[Function]}
options={
Array [
Object {
"key": "lastVisited",
"text": "Sign in to your last visited directory",
},
Object {
"key": "asdfghjklzxcvbnm9876543210",
"text": "Macrohard(asdfghjklzxcvbnm9876543210)",
},
Object {
"key": "",
"text": "()",
},
]
}
/>
`;
exports[`test render renders with no directories 1`] = `
<Dropdown
className="defaultDirectoryDropdown"
defaultSelectedKey="lastVisited"
label="Set your default directory"
onChange={[Function]}
options={
Array [
Object {
"key": "lastVisited",
"text": "Sign in to your last visited directory",
},
]
}
/>
`;

View File

@@ -1,64 +0,0 @@
import * as ko from "knockout";
import { DynamicListComponent, DynamicListParams, DynamicListItem } from "./DynamicListComponent";
const $ = (selector: string) => document.querySelector(selector) as HTMLElement;
function buildComponent(buttonOptions: any) {
document.body.innerHTML = DynamicListComponent.template as any;
const vm = new DynamicListComponent.viewModel(buttonOptions);
ko.applyBindings(vm);
}
describe("Dynamic List Component", () => {
const mockPlaceHolder = "Write here";
const mockButton = "Add something";
const mockValue = "/someText";
const mockAriaLabel = "Add ariaLabel";
const items: ko.ObservableArray<DynamicListItem> = ko.observableArray<DynamicListItem>();
function buildListOptions(
items: ko.ObservableArray<DynamicListItem>,
placeholder?: string,
mockButton?: string
): DynamicListParams {
return {
placeholder: placeholder,
listItems: items,
buttonText: mockButton,
ariaLabel: mockAriaLabel,
};
}
afterEach(() => {
ko.cleanNode(document);
});
describe("Rendering", () => {
it("should display button text", () => {
const params = buildListOptions(items, mockPlaceHolder, mockButton);
buildComponent(params);
expect($(".dynamicListItemAdd").textContent).toContain(mockButton);
});
});
describe("Behavior", () => {
it("should add items to the list", () => {
const params = buildListOptions(items, mockPlaceHolder, mockButton);
buildComponent(params);
$(".dynamicListItemAdd").click();
expect(items().length).toBe(1);
const input = document.getElementsByClassName("dynamicListItem").item(0).children[0];
input.setAttribute("value", mockValue);
input.dispatchEvent(new Event("change"));
input.dispatchEvent(new Event("blur"));
expect(items()[0].value()).toBe(mockValue);
});
it("should remove items from the list", () => {
const params = buildListOptions(items, mockPlaceHolder);
buildComponent(params);
$(".dynamicListItemDelete").click();
expect(items().length).toBe(0);
});
});
});

View File

@@ -1,59 +0,0 @@
@import "../../../../less/Common/Constants";
.dynamicList {
width: 100%;
.dynamicListContainer {
.dynamicListItem {
justify-content: space-around;
margin-bottom: @MediumSpace;
input {
width: @newCollectionPaneInputWidth;
margin: auto;
font-size: @mediumFontSize;
padding: @SmallSpace @DefaultSpace;
color: @BaseDark;
}
.dynamicListItemDelete {
padding: @SmallSpace @SmallSpace @DefaultSpace;
margin-left: @SmallSpace;
&:hover {
.hover();
}
&:active {
.active();
}
img {
.dataExplorerIcons();
}
}
}
}
.dynamicListItemNew {
margin-top: @LargeSpace;
.dynamicListItemAdd {
padding: @DefaultSpace;
cursor: pointer;
&:hover {
.hover();
}
&:active {
.active();
}
img {
.dataExplorerIcons();
margin: 0px @SmallSpace @SmallSpace 0px;
}
}
}
}

View File

@@ -1,117 +0,0 @@
/**
* Dynamic list:
*
* Creates a list of dynamic inputs that can be populated and deleted.
*
* How to use in your markup:
* <dynamic-list params="{ listItems: anObservableArrayOfDynamicListItem, placeholder: 'Text to display in placeholder', ariaLabel: 'Text for aria-label', buttonText: 'Add item' }">
* </dynamic-list>
*
*/
import * as ko from "knockout";
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
import { KeyCodes } from "../../../Common/Constants";
import template from "./dynamic-list.html";
/**
* Parameters for this component
*/
export interface DynamicListParams {
/**
* Observable list of items to update
*/
listItems: ko.ObservableArray<DynamicListItem>;
/**
* Placeholder text to use on inputs
*/
placeholder?: string;
/**
* Text to use as aria-label
*/
ariaLabel: string;
/**
* Text for the button to add items
*/
buttonText?: string;
/**
* Callback triggered when the template is bound to the component (for testing purposes)
*/
onTemplateReady?: () => void;
}
/**
* Item in the dynamic list
*/
export interface DynamicListItem {
value: ko.Observable<string>;
}
export class DynamicListViewModel extends WaitsForTemplateViewModel {
public placeholder: string;
public ariaLabel: string;
public buttonText: string;
public newItem: ko.Observable<string>;
public isTemplateReady: ko.Observable<boolean>;
public listItems: ko.ObservableArray<DynamicListItem>;
public constructor(options: DynamicListParams) {
super();
super.onTemplateReady((isTemplateReady: boolean) => {
if (isTemplateReady && options.onTemplateReady) {
options.onTemplateReady();
}
});
const params: DynamicListParams = options;
const paramsPlaceholder: string = params.placeholder;
const paramsButtonText: string = params.buttonText;
this.placeholder = paramsPlaceholder || "Write a value";
this.ariaLabel = "Unique keys";
this.buttonText = paramsButtonText || "Add item";
this.listItems = params.listItems || ko.observableArray<DynamicListItem>();
this.newItem = ko.observable("");
}
public removeItem = (data: any, event: MouseEvent | KeyboardEvent): void => {
const context = ko.contextFor(event.target as Node);
this.listItems.splice(context.$index(), 1);
document.getElementById("addUniqueKeyBtn").focus();
};
public onRemoveItemKeyPress = (data: any, event: KeyboardEvent, source: any): boolean => {
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
this.removeItem(data, event);
(document.querySelector(".dynamicListItem:last-of-type input") as HTMLElement).focus();
event.stopPropagation();
return false;
}
return true;
};
public addItem(): void {
this.listItems.push({ value: ko.observable("") });
(document.querySelector(".dynamicListItem:last-of-type input") as HTMLElement).focus();
}
public onAddItemKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
this.addItem();
event.stopPropagation();
return false;
}
return true;
};
}
/**
* Helper class for ko component registration
*/
export const DynamicListComponent = {
viewModel: DynamicListViewModel,
template,
};

View File

@@ -15,7 +15,7 @@
tabindex="0" tabindex="0"
data-bind="click: $parent.removeItem, event: { keydown: $parent.onRemoveItemKeyPress }" data-bind="click: $parent.removeItem, event: { keydown: $parent.onRemoveItemKeyPress }"
> >
<img src="/delete.svg" alt="Remove item" /> <img src="/images/delete.svg" alt="Remove item" />
</span> </span>
</div> </div>
</div> </div>
@@ -28,7 +28,8 @@
tabindex="0" tabindex="0"
data-bind="click: addItem, event: { keydown: onAddItemKeyPress }" data-bind="click: addItem, event: { keydown: onAddItemKeyPress }"
> >
<img src="/Add-property.svg" data-bind="attr: {alt: buttonText}" /> <span data-bind="text: buttonText"></span> <img src="/images/Add-property.svg" data-bind="attr: {alt: buttonText}" />
<span data-bind="text: buttonText"></span>
</span> </span>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,6 @@
<div class="warningErrorContainer" data-bind="visible: !!params.errorMsg()">
<div class="warningErrorContent">
<span><img src="/images/error_red.svg" alt="Error" /></span>
<span class="settingErrorMsg warningErrorDetailsLinkContainer" data-bind="text: params.errorMsg()"></span>
</div>
</div>

View File

@@ -111,9 +111,7 @@ describe("NotebookTerminalComponent", () => {
const terminal: NotebookTerminalComponent = createTerminal(); const terminal: NotebookTerminalComponent = createTerminal();
const params: Map<string, string> = terminal.getTerminalParams(); const params: Map<string, string> = terminal.getTerminalParams();
expect(params).toEqual( expect(params).toEqual(new Map<string, string>([["terminal", "true"]]));
new Map<string, string>([["terminal", "true"]])
);
}); });
it("getTerminalParams: Test for Mongo 3.2 terminal", () => { it("getTerminalParams: Test for Mongo 3.2 terminal", () => {

View File

@@ -18,8 +18,8 @@ import {
Text, Text,
TooltipHost, TooltipHost,
} from "@fluentui/react"; } from "@fluentui/react";
import CosmosDBLogo from "images/CosmosDB-logo.svg";
import React, { FunctionComponent, useState } from "react"; import React, { FunctionComponent, useState } from "react";
import CosmosDBLogo from "../../../../../images/CosmosDB-logo.svg";
import { IGalleryItem } from "../../../../Juno/JunoClient"; import { IGalleryItem } from "../../../../Juno/JunoClient";
import * as FileSystemUtil from "../../../Notebook/FileSystemUtil"; import * as FileSystemUtil from "../../../Notebook/FileSystemUtil";

View File

@@ -2,12 +2,12 @@
* Wrapper around Notebook metadata * Wrapper around Notebook metadata
*/ */
import { FontWeights, Icon, IconButton, Link, Persona, PersonaSize, PrimaryButton, Stack, Text } from "@fluentui/react"; import { FontWeights, Icon, IconButton, Link, Persona, PersonaSize, PrimaryButton, Stack, Text } from "@fluentui/react";
import CosmosDBLogo from "images/CosmosDB-logo.svg";
import * as React from "react"; import * as React from "react";
import { IGalleryItem } from "../../../Juno/JunoClient"; import { IGalleryItem } from "../../../Juno/JunoClient";
import * as FileSystemUtil from "../../Notebook/FileSystemUtil"; import * as FileSystemUtil from "../../Notebook/FileSystemUtil";
import "./NotebookViewerComponent.less";
import CosmosDBLogo from "../../../../images/CosmosDB-logo.svg";
import { InfoComponent } from "../NotebookGallery/InfoComponent/InfoComponent"; import { InfoComponent } from "../NotebookGallery/InfoComponent/InfoComponent";
import "./NotebookViewerComponent.less";
export interface NotebookMetadataComponentProps { export interface NotebookMetadataComponentProps {
data: IGalleryItem; data: IGalleryItem;

View File

@@ -43,7 +43,8 @@ interface NotebookViewerComponentState {
export class NotebookViewerComponent export class NotebookViewerComponent
extends React.Component<NotebookViewerComponentProps, NotebookViewerComponentState> extends React.Component<NotebookViewerComponentProps, NotebookViewerComponentState>
implements DialogHost { implements DialogHost
{
private clientManager: NotebookClientV2; private clientManager: NotebookClientV2;
private notebookComponentBootstrapper: NotebookComponentBootstrapper; private notebookComponentBootstrapper: NotebookComponentBootstrapper;

View File

@@ -19,9 +19,9 @@ import {
SelectionZone, SelectionZone,
TextField, TextField,
} from "@fluentui/react"; } from "@fluentui/react";
import SaveQueryBannerIcon from "images/save_query_banner.png";
import * as React from "react"; import * as React from "react";
import * as _ from "underscore"; import * as _ from "underscore";
import SaveQueryBannerIcon from "../../../../images/save_query_banner.png";
import * as Constants from "../../../Common/Constants"; import * as Constants from "../../../Common/Constants";
import { StyleConstants } from "../../../Common/Constants"; import { StyleConstants } from "../../../Common/Constants";
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils"; import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";

View File

@@ -1,16 +0,0 @@
@import "../../../../less/Common/Constants.less";
.radioSwitchComponent {
cursor: pointer;
display: flex;
&>span:nth-child(n+2) {
margin-left: 10px;
}
.caption {
color: @BaseDark;
padding-left: @SmallSpace;
vertical-align: top;
}
}

View File

@@ -1,51 +0,0 @@
/**
* Horizontal switch component
*/
import { Icon } from "@fluentui/react";
import * as React from "react";
import { NormalizedEventKey } from "../../../Common/Constants";
import "./RadioSwitchComponent.less";
export interface Choice {
key: string;
onSelect: () => void;
label: string;
}
export interface RadioSwitchComponentProps {
choices: Choice[];
selectedKey: string;
onSelectionKeyChange?: (newValue: string) => void;
}
export class RadioSwitchComponent extends React.Component<RadioSwitchComponentProps> {
public render(): JSX.Element {
return (
<div className="radioSwitchComponent">
{this.props.choices.map((choice: Choice) => (
<span
tabIndex={0}
key={choice.key}
onClick={() => this.onSelect(choice)}
onKeyPress={(event) => this.onKeyPress(event, choice)}
>
<Icon iconName={this.props.selectedKey === choice.key ? "RadioBtnOn" : "RadioBtnOff"} />
<span className="caption">{choice.label}</span>
</span>
))}
</div>
);
}
private onSelect(choice: Choice): void {
this.props.onSelectionKeyChange && this.props.onSelectionKeyChange(choice.key);
choice.onSelect();
}
private onKeyPress(event: React.KeyboardEvent<HTMLSpanElement>, choice: Choice): void {
if (event.key === NormalizedEventKey.Enter || event.key === NormalizedEventKey.Space) {
this.onSelect(choice);
}
}
}

View File

@@ -1,45 +0,0 @@
/**
* Generic abstract React component that senses its dimensions.
* It updates its state and re-renders if dimensions change.
*/
import * as React from "react";
import * as ResizeSensor from "css-element-queries/src/ResizeSensor";
export abstract class ResizeSensorComponent<P, S> extends React.Component<P, S> {
private isSensing: boolean = false;
private resizeSensor: any;
public constructor(props: P) {
super(props);
}
protected abstract onDimensionsChanged(width: number, height: number): void;
protected abstract getSensorTarget(): HTMLElement;
public componentDidUpdate(): void {
if (this.isSensing) {
return;
}
const bar = this.getSensorTarget();
if (bar.clientWidth > 0 || bar.clientHeight > 0) {
const oldPosition = bar.style.position;
// TODO Find a better way to use constructor
this.resizeSensor = new (ResizeSensor as any)(bar, () => {
this.onDimensionsChanged(bar.clientWidth, bar.clientHeight);
});
this.isSensing = true;
// ResizeSensor.js sets position to 'relative' which makes the dropdown menu appear clipped.
// Undoing doesn't seem to affect resize sensing functionality.
bar.style.position = oldPosition;
}
}
public componentWillUnmount(): void {
if (!!this.resizeSensor) {
this.resizeSensor.detach();
}
}
}

View File

@@ -1,7 +1,7 @@
import { IPivotItemProps, IPivotProps, Pivot, PivotItem } from "@fluentui/react"; import { IPivotItemProps, IPivotProps, Pivot, PivotItem } from "@fluentui/react";
import DiscardIcon from "images/discard.svg";
import SaveIcon from "images/save-cosmos.svg";
import * as React from "react"; import * as React from "react";
import DiscardIcon from "../../../../images/discard.svg";
import SaveIcon from "../../../../images/save-cosmos.svg";
import { AuthType } from "../../../AuthType"; import { AuthType } from "../../../AuthType";
import * as Constants from "../../../Common/Constants"; import * as Constants from "../../../Common/Constants";
import { getIndexTransformationProgress } from "../../../Common/dataAccess/getIndexTransformationProgress"; import { getIndexTransformationProgress } from "../../../Common/dataAccess/getIndexTransformationProgress";

View File

@@ -1,8 +1,8 @@
import { shallow } from "enzyme"; import { shallow } from "enzyme";
import React from "react"; import React from "react";
import { renderToString } from "react-dom/server";
import { MongoIndexTypes, MongoNotificationMessage, MongoNotificationType } from "../../SettingsUtils"; import { MongoIndexTypes, MongoNotificationMessage, MongoNotificationType } from "../../SettingsUtils";
import { MongoIndexingPolicyComponent, MongoIndexingPolicyComponentProps } from "./MongoIndexingPolicyComponent"; import { MongoIndexingPolicyComponent, MongoIndexingPolicyComponentProps } from "./MongoIndexingPolicyComponent";
import { renderToString } from "react-dom/server";
describe("MongoIndexingPolicyComponent", () => { describe("MongoIndexingPolicyComponent", () => {
const baseProps: MongoIndexingPolicyComponentProps = { const baseProps: MongoIndexingPolicyComponentProps = {
@@ -84,7 +84,7 @@ describe("MongoIndexingPolicyComponent", () => {
]; ];
test.each(cases)( test.each(cases)(
"", "test Mongo Indexing Policy",
( (
notification: MongoNotificationMessage, notification: MongoNotificationMessage,
indexToDropIsPresent: boolean, indexToDropIsPresent: boolean,
@@ -109,12 +109,13 @@ describe("MongoIndexingPolicyComponent", () => {
expect(mongoIndexingPolicyComponent.isMongoIndexingPolicyDiscardable()).toEqual( expect(mongoIndexingPolicyComponent.isMongoIndexingPolicyDiscardable()).toEqual(
isMongoIndexingPolicyDiscardable isMongoIndexingPolicyDiscardable
); );
if (mongoWarningNotificationMessage) {
const elementAsString = renderToString(mongoIndexingPolicyComponent.getMongoWarningNotificationMessage()); const warningNotificationElementAsString = renderToString(
expect(elementAsString).toContain(mongoWarningNotificationMessage); mongoIndexingPolicyComponent.getMongoWarningNotificationMessage()
} else { );
expect(mongoIndexingPolicyComponent.getMongoWarningNotificationMessage()).toBeUndefined(); expect(warningNotificationElementAsString.includes(mongoWarningNotificationMessage)).toEqual(
} !!mongoWarningNotificationMessage
);
} }
); );
}); });

View File

@@ -202,10 +202,12 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
} }
private getFreeTierInfoMessage(): JSX.Element { private getFreeTierInfoMessage(): JSX.Element {
const freeTierLimits = SharedConstants.FreeTierLimits;
return ( return (
<Text> <Text>
With free tier, you will get the first 400 RU/s and 5 GB of storage in this account for free. To keep your With free tier, you will get the first {freeTierLimits.RU} RU/s and {freeTierLimits.Storage} GB of storage in
account free, keep the total RU/s across all resources in the account to 400 RU/s. this account for free. To keep your account free, keep the total RU/s across all resources in the account to{" "}
{freeTierLimits.RU} RU/s.
<Link <Link
href="https://docs.microsoft.com/en-us/azure/cosmos-db/understand-your-bill#billing-examples-with-free-tier-accounts" href="https://docs.microsoft.com/en-us/azure/cosmos-db/understand-your-bill#billing-examples-with-free-tier-accounts"
target="_blank" target="_blank"

View File

@@ -155,7 +155,9 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
this.state = { this.state = {
spendAckChecked: this.props.spendAckChecked, spendAckChecked: this.props.spendAckChecked,
exceedFreeTierThroughput: exceedFreeTierThroughput:
this.props.isFreeTierAccount && !this.props.isAutoPilotSelected && this.props.throughput > 400, this.props.isFreeTierAccount &&
!this.props.isAutoPilotSelected &&
this.props.throughput > SharedConstants.FreeTierLimits.RU,
}; };
this.step = this.props.step ?? ThroughputInputAutoPilotV3Component.defaultStep; this.step = this.props.step ?? ThroughputInputAutoPilotV3Component.defaultStep;
@@ -441,7 +443,9 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
if (this.overrideWithAutoPilotSettings()) { if (this.overrideWithAutoPilotSettings()) {
this.props.onMaxAutoPilotThroughputChange(newThroughput); this.props.onMaxAutoPilotThroughputChange(newThroughput);
} else { } else {
this.setState({ exceedFreeTierThroughput: this.props.isFreeTierAccount && newThroughput > 400 }); this.setState({
exceedFreeTierThroughput: this.props.isFreeTierAccount && newThroughput > SharedConstants.FreeTierLimits.RU,
});
this.props.onThroughputChange(newThroughput); this.props.onThroughputChange(newThroughput);
} }
}; };
@@ -581,9 +585,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }} messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
styles={messageBarStyles} styles={messageBarStyles}
> >
{ {`Billing will apply if you provision more than ${SharedConstants.FreeTierLimits.RU} RU/s of manual throughput, or if the resource scales beyond ${SharedConstants.FreeTierLimits.RU} RU/s with autoscale.`}
"Billing will apply if you provision more than 400 RU/s of manual throughput, or if the resource scales beyond 400 RU/s with autoscale."
}
</MessageBar> </MessageBar>
)} )}
{this.props.getThroughputWarningMessage() && ( {this.props.getThroughputWarningMessage() && (

View File

@@ -5,7 +5,7 @@ import Explorer from "../../Explorer";
export const container = new Explorer(); export const container = new Explorer();
export const collection = ({ export const collection = {
container: container, container: container,
databaseId: "test", databaseId: "test",
id: ko.observable<string>("test"), id: ko.observable<string>("test"),
@@ -43,4 +43,4 @@ export const collection = ({
readSettings: () => { readSettings: () => {
return; return;
}, },
} as unknown) as ViewModels.Collection; } as unknown as ViewModels.Collection;

View File

@@ -187,9 +187,8 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
<TooltipHost <TooltipHost
directionalHint={DirectionalHint.topLeftEdge} directionalHint={DirectionalHint.topLeftEdge}
content={ content={
showFreeTierExceedThroughputTooltip && showFreeTierExceedThroughputTooltip && throughput > SharedConstants.FreeTierLimits.RU
throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs400 ? `The first ${SharedConstants.FreeTierLimits.RU} RU/s in this account are free. Billing will apply to any throughput beyond ${SharedConstants.FreeTierLimits.RU} RU/s.`
? "The first 400 RU/s in this account are free. Billing will apply to any throughput beyond 400 RU/s."
: undefined : undefined
} }
> >

View File

@@ -1,308 +0,0 @@
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
import * as ko from "knockout";
import * as ViewModels from "../../../Contracts/ViewModels";
import ThroughputInputComponentAutoscaleV3 from "./ThroughputInputComponentAutoscaleV3.html";
import { KeyCodes } from "../../../Common/Constants";
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
import { userContext } from "../../../UserContext";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
/**
* Throughput Input:
*
* Creates a set of controls to input, sanitize and increase/decrease throughput
*
* How to use in your markup:
* <throughput-input params="{ value: anObservableToHoldTheValue, minimum: anObservableWithMinimum, maximum: anObservableWithMaximum }">
* </throughput-input>
*
*/
/**
* Parameters for this component
*/
export interface ThroughputInputParams {
/**
* Callback triggered when the template is bound to the component (for testing purposes)
*/
onTemplateReady?: () => void;
/**
* Observable to bind the Throughput value to
*/
value: ViewModels.Editable<number>;
/**
* Text to use as id for testing
*/
testId: string;
/**
* Text to use as aria-label
*/
ariaLabel?: ko.Observable<string>;
/**
* Minimum value in the range
*/
minimum: ko.Observable<number>;
/**
* Maximum value in the range
*/
maximum: ko.Observable<number>;
/**
* Step value for increase/decrease
*/
step?: number;
/**
* Observable to bind the Throughput enabled status
*/
isEnabled?: ko.Observable<boolean>;
/**
* Should show pricing controls
*/
costsVisible: ko.Observable<boolean>;
/**
* RU price
*/
requestUnitsUsageCost: ko.Computed<string>; // Our code assigns to ko.Computed, but unit test assigns to ko.Observable
/**
* State of the spending acknowledge checkbox
*/
spendAckChecked?: ko.Observable<boolean>;
/**
* id of the spending acknowledge checkbox
*/
spendAckId?: ko.Observable<string>;
/**
* spending acknowledge text
*/
spendAckText?: ko.Observable<string>;
/**
* Show spending acknowledge controls
*/
spendAckVisible?: ko.Observable<boolean>;
/**
* Display * to the left of the label
*/
showAsMandatory: boolean;
/**
* If true, it will display a text to prompt users to use unlimited collections to go beyond max for fixed
*/
isFixed: boolean;
/**
* Label of the provisioned throughut control
*/
label: ko.Observable<string>;
/**
* Text of the info bubble for provisioned throughut control
*/
infoBubbleText?: ko.Observable<string>;
/**
* Computed value that decides if value can exceed maximum allowable value
*/
canExceedMaximumValue?: ko.Computed<boolean>;
/**
* CSS classes to apply on input element
*/
cssClass?: string;
isAutoPilotSelected: ko.Observable<boolean>;
throughputAutoPilotRadioId: string;
throughputProvisionedRadioId: string;
throughputModeRadioName: string;
maxAutoPilotThroughputSet: ViewModels.Editable<number>;
autoPilotUsageCost: ko.Computed<string>;
overrideWithAutoPilotSettings: ko.Observable<boolean>;
overrideWithProvisionedThroughputSettings: ko.Observable<boolean>;
freeTierExceedThroughputTooltip?: ko.Observable<string>;
freeTierExceedThroughputWarning?: ko.Observable<string>;
}
export class ThroughputInputViewModel extends WaitsForTemplateViewModel {
public ariaLabel: ko.Observable<string>;
public canExceedMaximumValue: ko.Computed<boolean>;
public step: ko.Computed<number>;
public testId: string;
public value: ViewModels.Editable<number>;
public minimum: ko.Observable<number>;
public maximum: ko.Observable<number>;
public isEnabled: ko.Observable<boolean>;
public cssClass: string;
public decreaseButtonAriaLabel: string;
public increaseButtonAriaLabel: string;
public costsVisible: ko.Observable<boolean>;
public requestUnitsUsageCost: ko.Computed<string>;
public spendAckChecked: ko.Observable<boolean>;
public spendAckId: ko.Observable<string>;
public spendAckText: ko.Observable<string>;
public spendAckVisible: ko.Observable<boolean>;
public showAsMandatory: boolean;
public infoBubbleText: string | ko.Observable<string>;
public label: ko.Observable<string>;
public isFixed: boolean;
public isAutoPilotSelected: ko.Observable<boolean>;
public throughputAutoPilotRadioId: string;
public throughputProvisionedRadioId: string;
public throughputModeRadioName: string;
public maxAutoPilotThroughputSet: ko.Observable<number>;
public autoPilotUsageCost: ko.Computed<string>;
public minAutoPilotThroughput: ko.Observable<number>;
public overrideWithAutoPilotSettings: ko.Observable<boolean>;
public overrideWithProvisionedThroughputSettings: ko.Observable<boolean>;
public isManualThroughputInputFieldRequired: ko.Computed<boolean>;
public isAutoscaleThroughputInputFieldRequired: ko.Computed<boolean>;
public freeTierExceedThroughputTooltip: ko.Observable<string>;
public freeTierExceedThroughputWarning: ko.Observable<string>;
public showFreeTierExceedThroughputTooltip: ko.Computed<boolean>;
public showFreeTierExceedThroughputWarning: ko.Computed<boolean>;
public constructor(options: ThroughputInputParams) {
super();
super.onTemplateReady((isTemplateReady: boolean) => {
if (isTemplateReady && options.onTemplateReady) {
options.onTemplateReady();
}
});
const params: ThroughputInputParams = options;
this.testId = params.testId || "ThroughputValue";
this.ariaLabel = ko.observable((params.ariaLabel && params.ariaLabel()) || "");
this.canExceedMaximumValue = params.canExceedMaximumValue || ko.computed(() => false);
this.isEnabled = params.isEnabled || ko.observable(true);
this.cssClass = params.cssClass || "textfontclr collid migration";
this.minimum = params.minimum;
this.maximum = params.maximum;
this.value = params.value;
this.costsVisible = options.costsVisible;
this.requestUnitsUsageCost = options.requestUnitsUsageCost;
this.spendAckChecked = options.spendAckChecked || ko.observable<boolean>(false);
this.spendAckId = options.spendAckId || ko.observable<string>();
this.spendAckText = options.spendAckText || ko.observable<string>();
this.spendAckVisible = options.spendAckVisible || ko.observable<boolean>(false);
this.showAsMandatory = !!options.showAsMandatory;
this.isFixed = !!options.isFixed;
this.infoBubbleText = options.infoBubbleText || ko.observable<string>();
this.label = options.label || ko.observable<string>();
this.isAutoPilotSelected = options.isAutoPilotSelected || ko.observable<boolean>(false);
this.isAutoPilotSelected.subscribe((value) => {
TelemetryProcessor.trace(Action.ToggleAutoscaleSetting, ActionModifiers.Mark, {
changedSelectedValueTo: value ? ActionModifiers.ToggleAutoscaleOn : ActionModifiers.ToggleAutoscaleOff,
dataExplorerArea: "Scale Tab V1",
});
});
this.throughputAutoPilotRadioId = options.throughputAutoPilotRadioId;
this.throughputProvisionedRadioId = options.throughputProvisionedRadioId;
this.throughputModeRadioName = options.throughputModeRadioName;
this.overrideWithAutoPilotSettings = options.overrideWithAutoPilotSettings || ko.observable<boolean>(false);
this.overrideWithProvisionedThroughputSettings =
options.overrideWithProvisionedThroughputSettings || ko.observable<boolean>(false);
this.maxAutoPilotThroughputSet =
options.maxAutoPilotThroughputSet || ko.observable<number>(AutoPilotUtils.minAutoPilotThroughput);
this.autoPilotUsageCost = options.autoPilotUsageCost;
this.minAutoPilotThroughput = ko.observable<number>(AutoPilotUtils.minAutoPilotThroughput);
this.step = ko.pureComputed(() => {
if (this.isAutoPilotSelected()) {
return AutoPilotUtils.autoPilotIncrementStep;
}
return params.step || ThroughputInputViewModel._defaultStep;
});
this.decreaseButtonAriaLabel = "Decrease throughput by " + this.step().toString();
this.increaseButtonAriaLabel = "Increase throughput by " + this.step().toString();
this.isManualThroughputInputFieldRequired = ko.pureComputed(() => this.isEnabled() && !this.isAutoPilotSelected());
this.isAutoscaleThroughputInputFieldRequired = ko.pureComputed(
() => this.isEnabled() && this.isAutoPilotSelected()
);
this.freeTierExceedThroughputTooltip = options.freeTierExceedThroughputTooltip || ko.observable<string>();
this.freeTierExceedThroughputWarning = options.freeTierExceedThroughputWarning || ko.observable<string>();
this.showFreeTierExceedThroughputTooltip = ko.pureComputed<boolean>(
() => !!this.freeTierExceedThroughputTooltip() && this.value() > 400
);
this.showFreeTierExceedThroughputWarning = ko.pureComputed<boolean>(
() => !!this.freeTierExceedThroughputWarning() && this.value() > 400
);
}
public decreaseThroughput() {
let offerThroughput: number = this._getSanitizedValue();
if (offerThroughput > this.minimum()) {
offerThroughput -= this.step();
if (offerThroughput < this.minimum()) {
offerThroughput = this.minimum();
}
this.value(offerThroughput);
}
}
public increaseThroughput() {
let offerThroughput: number = this._getSanitizedValue();
if (offerThroughput < this.maximum() || this.canExceedMaximumValue()) {
offerThroughput += this.step();
if (offerThroughput > this.maximum() && !this.canExceedMaximumValue()) {
offerThroughput = this.maximum();
}
this.value(offerThroughput);
}
}
public onIncreaseKeyDown = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
this.increaseThroughput();
event.stopPropagation();
return false;
}
return true;
};
public onDecreaseKeyDown = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
this.decreaseThroughput();
event.stopPropagation();
return false;
}
return true;
};
private _getSanitizedValue(): number {
let throughput = this.value();
if (this.isAutoPilotSelected()) {
throughput = this.maxAutoPilotThroughputSet();
}
return isNaN(throughput) ? 0 : Number(throughput);
}
private static _defaultStep: number = 100;
}
export const ThroughputInputComponentAutoPilotV3 = {
viewModel: ThroughputInputViewModel,
template: ThroughputInputComponentAutoscaleV3,
};

View File

@@ -9,7 +9,7 @@
<!-- ko if: infoBubbleText --> <!-- ko if: infoBubbleText -->
<span class="infoTooltip" role="tooltip" tabindex="0"> <span class="infoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="../../../../images/info-bubble.svg" alt="More information" /> <img class="infoImg" src="/images/info-bubble.svg" alt="More information" />
<span data-bind="text: infoBubbleText" class="tooltiptext throughputRuInfo"></span> <span data-bind="text: infoBubbleText" class="tooltiptext throughputRuInfo"></span>
</span> </span>
<!-- /ko --> <!-- /ko -->
@@ -163,7 +163,7 @@
</div> </div>
<div class="freeTierInlineWarning" data-bind="visible: showFreeTierExceedThroughputWarning"> <div class="freeTierInlineWarning" data-bind="visible: showFreeTierExceedThroughputWarning">
<span class="freeTierWarningIcon"><img src="/warning.svg" alt="Warning" /></span> <span class="freeTierWarningIcon"><img src="/images/warning.svg" alt="Warning" /></span>
<span class="freeTierWarningMessage" data-bind="text: freeTierExceedThroughputWarning"></span> <span class="freeTierWarningMessage" data-bind="text: freeTierExceedThroughputWarning"></span>
</div> </div>

View File

@@ -12,11 +12,11 @@ import {
IContextualMenuItemProps, IContextualMenuItemProps,
IContextualMenuProps, IContextualMenuProps,
} from "@fluentui/react"; } from "@fluentui/react";
import LoadingIndicator_3Squares from "images/LoadingIndicator_3Squares.gif";
import TriangleDownIcon from "images/Triangle-down.svg";
import TriangleRightIcon from "images/Triangle-right.svg";
import * as React from "react"; import * as React from "react";
import AnimateHeight from "react-animate-height"; import AnimateHeight from "react-animate-height";
import LoadingIndicator_3Squares from "../../../../images/LoadingIndicator_3Squares.gif";
import TriangleDownIcon from "../../../../images/Triangle-down.svg";
import TriangleRightIcon from "../../../../images/Triangle-right.svg";
import * as Constants from "../../../Common/Constants"; import * as Constants from "../../../Common/Constants";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";

View File

@@ -14,7 +14,6 @@ describe("ContainerSampleGenerator", () => {
const createExplorerStub = (database: ViewModels.Database): Explorer => { const createExplorerStub = (database: ViewModels.Database): Explorer => {
const explorerStub = {} as Explorer; const explorerStub = {} as Explorer;
explorerStub.databases = ko.observableArray<ViewModels.Database>([database]); explorerStub.databases = ko.observableArray<ViewModels.Database>([database]);
explorerStub.canExceedMaximumValue = ko.computed<boolean>(() => false);
explorerStub.findDatabaseWithId = () => database; explorerStub.findDatabaseWithId = () => database;
explorerStub.refreshAllDatabases = () => Q.resolve(); explorerStub.refreshAllDatabases = () => Q.resolve();
return explorerStub; return explorerStub;

View File

@@ -6,40 +6,34 @@ import _ from "underscore";
import { AuthType } from "../AuthType"; import { AuthType } from "../AuthType";
import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer"; import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer";
import * as Constants from "../Common/Constants"; import * as Constants from "../Common/Constants";
import { ExplorerMetrics, HttpStatusCodes } from "../Common/Constants"; import { ExplorerMetrics } from "../Common/Constants";
import { readCollection } from "../Common/dataAccess/readCollection"; import { readCollection } from "../Common/dataAccess/readCollection";
import { readDatabases } from "../Common/dataAccess/readDatabases"; import { readDatabases } from "../Common/dataAccess/readDatabases";
import { isPublicInternetAccessAllowed } from "../Common/DatabaseAccountUtility";
import { getErrorMessage, getErrorStack, handleError } from "../Common/ErrorHandlingUtils"; import { getErrorMessage, getErrorStack, handleError } from "../Common/ErrorHandlingUtils";
import * as Logger from "../Common/Logger"; import * as Logger from "../Common/Logger";
import { sendCachedDataMessage } from "../Common/MessageHandler";
import { QueriesClient } from "../Common/QueriesClient"; import { QueriesClient } from "../Common/QueriesClient";
import { Splitter, SplitterBounds, SplitterDirection } from "../Common/Splitter"; import { Splitter, SplitterBounds, SplitterDirection } from "../Common/Splitter";
import { configContext, Platform } from "../ConfigContext"; import { configContext, Platform } from "../ConfigContext";
import * as DataModels from "../Contracts/DataModels"; import * as DataModels from "../Contracts/DataModels";
import { MessageTypes } from "../Contracts/ExplorerContracts";
import * as ViewModels from "../Contracts/ViewModels"; import * as ViewModels from "../Contracts/ViewModels";
import { GitHubClient } from "../GitHub/GitHubClient";
import { GitHubOAuthService } from "../GitHub/GitHubOAuthService"; import { GitHubOAuthService } from "../GitHub/GitHubOAuthService";
import { IGalleryItem, JunoClient } from "../Juno/JunoClient"; import { IGalleryItem, JunoClient } from "../Juno/JunoClient";
import { NotebookWorkspaceManager } from "../NotebookWorkspaceManager/NotebookWorkspaceManager"; import { NotebookWorkspaceManager } from "../NotebookWorkspaceManager/NotebookWorkspaceManager";
import { ResourceProviderClientFactory } from "../ResourceProvider/ResourceProviderClientFactory";
import { RouteHandler } from "../RouteHandlers/RouteHandler"; import { RouteHandler } from "../RouteHandlers/RouteHandler";
import { trackEvent } from "../Shared/appInsights";
import * as SharedConstants from "../Shared/Constants";
import { ExplorerSettings } from "../Shared/ExplorerSettings"; import { ExplorerSettings } from "../Shared/ExplorerSettings";
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor"; import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
import { ArcadiaResourceManager } from "../SparkClusterManager/ArcadiaResourceManager"; import { userContext } from "../UserContext";
import { updateUserContext, userContext } from "../UserContext";
import { getCollectionName, getDatabaseName, getUploadName } from "../Utils/APITypeUtils"; import { getCollectionName, getDatabaseName, getUploadName } from "../Utils/APITypeUtils";
import { decryptJWTToken, getAuthorizationHeader } from "../Utils/AuthorizationUtils"; import { update } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
import { stringToBlob } from "../Utils/BlobUtils"; import { stringToBlob } from "../Utils/BlobUtils";
import { isCapabilityEnabled } from "../Utils/CapabilityUtils"; import { isCapabilityEnabled } from "../Utils/CapabilityUtils";
import { fromContentUri, toRawContentUri } from "../Utils/GitHubUtils"; import { fromContentUri, toRawContentUri } from "../Utils/GitHubUtils";
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils"; import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../Utils/NotificationConsoleUtils"; import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../Utils/NotificationConsoleUtils";
import * as ComponentRegisterer from "./ComponentRegisterer"; import * as ComponentRegisterer from "./ComponentRegisterer";
import { ArcadiaWorkspaceItem } from "./Controls/Arcadia/ArcadiaMenuPicker";
import { CommandButtonComponentProps } from "./Controls/CommandButton/CommandButtonComponent"; import { CommandButtonComponentProps } from "./Controls/CommandButton/CommandButtonComponent";
import { DialogProps, TextFieldProps, useDialog } from "./Controls/Dialog"; import { DialogProps, TextFieldProps, useDialog } from "./Controls/Dialog";
import { GalleryTab as GalleryTabKind } from "./Controls/NotebookGallery/GalleryViewerComponent"; import { GalleryTab as GalleryTabKind } from "./Controls/NotebookGallery/GalleryViewerComponent";
@@ -55,7 +49,6 @@ import { AddCollectionPanel } from "./Panes/AddCollectionPanel";
import { AddDatabasePanel } from "./Panes/AddDatabasePanel/AddDatabasePanel"; import { AddDatabasePanel } from "./Panes/AddDatabasePanel/AddDatabasePanel";
import { BrowseQueriesPane } from "./Panes/BrowseQueriesPane/BrowseQueriesPane"; import { BrowseQueriesPane } from "./Panes/BrowseQueriesPane/BrowseQueriesPane";
import { CassandraAddCollectionPane } from "./Panes/CassandraAddCollectionPane/CassandraAddCollectionPane"; import { CassandraAddCollectionPane } from "./Panes/CassandraAddCollectionPane/CassandraAddCollectionPane";
import { ContextualPaneBase } from "./Panes/ContextualPaneBase";
import { DeleteCollectionConfirmationPane } from "./Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane"; import { DeleteCollectionConfirmationPane } from "./Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane";
import { DeleteDatabaseConfirmationPanel } from "./Panes/DeleteDatabaseConfirmationPanel"; import { DeleteDatabaseConfirmationPanel } from "./Panes/DeleteDatabaseConfirmationPanel";
import { ExecuteSprocParamsPane } from "./Panes/ExecuteSprocParamsPane/ExecuteSprocParamsPane"; import { ExecuteSprocParamsPane } from "./Panes/ExecuteSprocParamsPane/ExecuteSprocParamsPane";
@@ -97,20 +90,12 @@ export interface ExplorerParams {
} }
export default class Explorer { export default class Explorer {
public addCollectionText: ko.Observable<string>;
public collectionTitle: ko.Observable<string>;
public deleteCollectionText: ko.Observable<string>;
public deleteDatabaseText: ko.Observable<string>;
public collectionTreeNodeAltText: ko.Observable<string>;
public refreshTreeTitle: ko.Observable<string>;
public collapsedResourceTreeWidth: number = ExplorerMetrics.CollapsedResourceTreeWidth; public collapsedResourceTreeWidth: number = ExplorerMetrics.CollapsedResourceTreeWidth;
public collectionCreationDefaults: ViewModels.CollectionCreationDefaults = SharedConstants.CollectionCreationDefaults;
public isFixedCollectionWithSharedThroughputSupported: ko.Computed<boolean>; public isFixedCollectionWithSharedThroughputSupported: ko.Computed<boolean>;
public isServerlessEnabled: ko.Computed<boolean>; public isServerlessEnabled: ko.Computed<boolean>;
public isAccountReady: ko.Observable<boolean>; public isAccountReady: ko.Observable<boolean>;
public canSaveQueries: ko.Computed<boolean>; public canSaveQueries: ko.Computed<boolean>;
public features: ko.Observable<any>;
public queriesClient: QueriesClient; public queriesClient: QueriesClient;
public tableDataClient: TableDataClient; public tableDataClient: TableDataClient;
public splitter: Splitter; public splitter: Splitter;
@@ -121,7 +106,6 @@ export default class Explorer {
private setInProgressConsoleDataIdToBeDeleted: (id: string) => void; private setInProgressConsoleDataIdToBeDeleted: (id: string) => void;
// Panes // Panes
public contextPanes: ContextualPaneBase[];
public openSidePanel: (headerText: string, panelContent: JSX.Element, onClose?: () => void) => void; public openSidePanel: (headerText: string, panelContent: JSX.Element, onClose?: () => void) => void;
public closeSidePanel: () => void; public closeSidePanel: () => void;
@@ -129,7 +113,6 @@ export default class Explorer {
public databases: ko.ObservableArray<ViewModels.Database>; public databases: ko.ObservableArray<ViewModels.Database>;
public selectedDatabaseId: ko.Computed<string>; public selectedDatabaseId: ko.Computed<string>;
public selectedCollectionId: ko.Computed<string>; public selectedCollectionId: ko.Computed<string>;
public isLeftPaneExpanded: ko.Observable<boolean>;
public selectedNode: ko.Observable<ViewModels.TreeNode>; public selectedNode: ko.Observable<ViewModels.TreeNode>;
private resourceTree: ResourceTreeAdapter; private resourceTree: ResourceTreeAdapter;
@@ -145,16 +128,10 @@ export default class Explorer {
public isTabsContentExpanded: ko.Observable<boolean>; public isTabsContentExpanded: ko.Observable<boolean>;
public tabsManager: TabsManager; public tabsManager: TabsManager;
// Contextual panes
private gitHubClient: GitHubClient;
public gitHubOAuthService: GitHubOAuthService; public gitHubOAuthService: GitHubOAuthService;
public junoClient: JunoClient;
// features // features
public isPublishNotebookPaneEnabled: ko.Observable<boolean>;
public isHostedDataExplorerEnabled: ko.Computed<boolean>; public isHostedDataExplorerEnabled: ko.Computed<boolean>;
public isMongoIndexingEnabled: ko.Observable<boolean>;
public canExceedMaximumValue: ko.Computed<boolean>;
public isSchemaEnabled: ko.Computed<boolean>; public isSchemaEnabled: ko.Computed<boolean>;
// Notebooks // Notebooks
@@ -163,11 +140,6 @@ export default class Explorer {
public notebookServerInfo: ko.Observable<DataModels.NotebookWorkspaceConnectionInfo>; public notebookServerInfo: ko.Observable<DataModels.NotebookWorkspaceConnectionInfo>;
public notebookWorkspaceManager: NotebookWorkspaceManager; public notebookWorkspaceManager: NotebookWorkspaceManager;
public sparkClusterConnectionInfo: ko.Observable<DataModels.SparkClusterConnectionInfo>; public sparkClusterConnectionInfo: ko.Observable<DataModels.SparkClusterConnectionInfo>;
public isSparkEnabled: ko.Observable<boolean>;
public isSparkEnabledForAccount: ko.Observable<boolean>;
public arcadiaToken: ko.Observable<string>;
public arcadiaWorkspaces: ko.ObservableArray<ArcadiaWorkspaceItem>;
public hasStorageAnalyticsAfecFeature: ko.Observable<boolean>;
public isSynapseLinkUpdating: ko.Observable<boolean>; public isSynapseLinkUpdating: ko.Observable<boolean>;
public memoryUsageInfo: ko.Observable<DataModels.MemoryUsageInfo>; public memoryUsageInfo: ko.Observable<DataModels.MemoryUsageInfo>;
public notebookManager?: NotebookManager; public notebookManager?: NotebookManager;
@@ -176,7 +148,6 @@ export default class Explorer {
private _isInitializingNotebooks: boolean; private _isInitializingNotebooks: boolean;
private notebookBasePath: ko.Observable<string>; private notebookBasePath: ko.Observable<string>;
private _arcadiaManager: ArcadiaResourceManager;
private notebookToImport: { private notebookToImport: {
name: string; name: string;
content: string; content: string;
@@ -188,8 +159,6 @@ export default class Explorer {
private static readonly MaxNbDatabasesToAutoExpand = 5; private static readonly MaxNbDatabasesToAutoExpand = 5;
constructor(params?: ExplorerParams) { constructor(params?: ExplorerParams) {
this.gitHubClient = new GitHubClient(this.onGitHubClientError);
this.junoClient = new JunoClient();
this.setIsNotificationConsoleExpanded = params?.setIsNotificationConsoleExpanded; this.setIsNotificationConsoleExpanded = params?.setIsNotificationConsoleExpanded;
this.setNotificationConsoleData = params?.setNotificationConsoleData; this.setNotificationConsoleData = params?.setNotificationConsoleData;
this.setInProgressConsoleDataIdToBeDeleted = params?.setInProgressConsoleDataIdToBeDeleted; this.setInProgressConsoleDataIdToBeDeleted = params?.setInProgressConsoleDataIdToBeDeleted;
@@ -199,31 +168,11 @@ export default class Explorer {
const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, { const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, {
dataExplorerArea: Constants.Areas.ResourceTree, dataExplorerArea: Constants.Areas.ResourceTree,
}); });
this.addCollectionText = ko.observable<string>("New Collection");
this.collectionTitle = ko.observable<string>("Collections");
this.collectionTreeNodeAltText = ko.observable<string>("Collection");
this.deleteCollectionText = ko.observable<string>("Delete Collection");
this.deleteDatabaseText = ko.observable<string>("Delete Database");
this.refreshTreeTitle = ko.observable<string>("Refresh collections");
this.isAccountReady = ko.observable<boolean>(false); this.isAccountReady = ko.observable<boolean>(false);
this._isInitializingNotebooks = false; this._isInitializingNotebooks = false;
this.arcadiaToken = ko.observable<string>();
this.arcadiaToken.subscribe((token: string) => {
if (token) {
const notebookTabs = this.tabsManager.getTabs(ViewModels.CollectionTabKind.NotebookV2);
(notebookTabs || []).forEach((tab: NotebookV2Tab) => {
tab.reconfigureServiceEndpoints();
});
}
});
this.isShellEnabled = ko.observable(false); this.isShellEnabled = ko.observable(false);
this.isNotebooksEnabledForAccount = ko.observable(false); this.isNotebooksEnabledForAccount = ko.observable(false);
this.isNotebooksEnabledForAccount.subscribe((isEnabledForAccount: boolean) => this.refreshCommandBarButtons()); this.isNotebooksEnabledForAccount.subscribe((isEnabledForAccount: boolean) => this.refreshCommandBarButtons());
this.isSparkEnabledForAccount = ko.observable(false);
this.isSparkEnabledForAccount.subscribe((isEnabledForAccount: boolean) => this.refreshCommandBarButtons());
this.hasStorageAnalyticsAfecFeature = ko.observable(false);
this.hasStorageAnalyticsAfecFeature.subscribe((enabled: boolean) => this.refreshCommandBarButtons());
this.isSynapseLinkUpdating = ko.observable<boolean>(false); this.isSynapseLinkUpdating = ko.observable<boolean>(false);
this.isAccountReady.subscribe(async (isAccountReady: boolean) => { this.isAccountReady.subscribe(async (isAccountReady: boolean) => {
if (isAccountReady) { if (isAccountReady) {
@@ -232,63 +181,26 @@ export default class Explorer {
: this.refreshAllDatabases(true); : this.refreshAllDatabases(true);
RouteHandler.getInstance().initHandler(); RouteHandler.getInstance().initHandler();
this.notebookWorkspaceManager = new NotebookWorkspaceManager(); this.notebookWorkspaceManager = new NotebookWorkspaceManager();
this.arcadiaWorkspaces = ko.observableArray(); await this._refreshNotebooksEnabledStateForAccount();
this._arcadiaManager = new ArcadiaResourceManager(); this.isNotebookEnabled(
this._isAfecFeatureRegistered(Constants.AfecFeatures.StorageAnalytics).then((isRegistered) => userContext.authType !== AuthType.ResourceToken &&
this.hasStorageAnalyticsAfecFeature(isRegistered) ((await this._containsDefaultNotebookWorkspace(userContext.databaseAccount)) ||
userContext.features.enableNotebooks)
); );
Promise.all([this._refreshNotebooksEnabledStateForAccount(), this._refreshSparkEnabledStateForAccount()]).then(
async () => {
this.isNotebookEnabled(
userContext.authType !== AuthType.ResourceToken &&
((await this._containsDefaultNotebookWorkspace(userContext.databaseAccount)) ||
userContext.features.enableNotebooks)
);
this.isShellEnabled(
this.isNotebookEnabled() &&
!userContext.databaseAccount.properties.isVirtualNetworkFilterEnabled &&
userContext.databaseAccount.properties.ipRules.length === 0
);
TelemetryProcessor.trace(Action.NotebookEnabled, ActionModifiers.Mark, { this.isShellEnabled(this.isNotebookEnabled() && isPublicInternetAccessAllowed());
isNotebookEnabled: this.isNotebookEnabled(),
dataExplorerArea: Constants.Areas.Notebook,
});
if (this.isNotebookEnabled()) { TelemetryProcessor.trace(Action.NotebookEnabled, ActionModifiers.Mark, {
await this.initNotebooks(userContext.databaseAccount); isNotebookEnabled: this.isNotebookEnabled(),
const workspaces = await this._getArcadiaWorkspaces(); dataExplorerArea: Constants.Areas.Notebook,
this.arcadiaWorkspaces(workspaces); });
} else if (this.notebookToImport) {
// if notebooks is not enabled but the user is trying to do a quickstart setup with notebooks, open the SetupNotebooksPane
this._openSetupNotebooksPaneForQuickstart();
}
this.isSparkEnabled( if (this.isNotebookEnabled()) {
(this.isNotebookEnabled() && await this.initNotebooks(userContext.databaseAccount);
this.isSparkEnabledForAccount() && } else if (this.notebookToImport) {
this.arcadiaWorkspaces() && // if notebooks is not enabled but the user is trying to do a quickstart setup with notebooks, open the SetupNotebooksPane
this.arcadiaWorkspaces().length > 0) || this._openSetupNotebooksPaneForQuickstart();
userContext.features.enableSpark }
);
if (this.isSparkEnabled()) {
trackEvent(
{ name: "LoadedWithSparkEnabled" },
{
subscriptionId: userContext.subscriptionId,
accountName: userContext.databaseAccount?.name,
accountId: userContext.databaseAccount?.id,
platform: configContext.platform,
}
);
const pollArcadiaTokenRefresh = async () => {
this.arcadiaToken(await this.getArcadiaToken());
setTimeout(() => pollArcadiaTokenRefresh(), this.getTokenRefreshInterval(this.arcadiaToken()));
};
await pollArcadiaTokenRefresh();
}
}
);
} }
}); });
this.memoryUsageInfo = ko.observable<DataModels.MemoryUsageInfo>(); this.memoryUsageInfo = ko.observable<DataModels.MemoryUsageInfo>();
@@ -299,11 +211,6 @@ export default class Explorer {
this.resourceTokenCollectionId = ko.observable<string>(); this.resourceTokenCollectionId = ko.observable<string>();
this.resourceTokenCollection = ko.observable<ViewModels.CollectionBase>(); this.resourceTokenCollection = ko.observable<ViewModels.CollectionBase>();
this.resourceTokenPartitionKey = ko.observable<string>(); this.resourceTokenPartitionKey = ko.observable<string>();
this.isMongoIndexingEnabled = ko.observable<boolean>(false);
this.isPublishNotebookPaneEnabled = ko.observable<boolean>(false);
this.canExceedMaximumValue = ko.computed<boolean>(() => userContext.features.canExceedMaximumValue);
this.isSchemaEnabled = ko.computed<boolean>(() => userContext.features.enableSchema); this.isSchemaEnabled = ko.computed<boolean>(() => userContext.features.enableSchema);
this.databases = ko.observableArray<ViewModels.Database>(); this.databases = ko.observableArray<ViewModels.Database>();
@@ -326,7 +233,6 @@ export default class Explorer {
} }
return true; return true;
}); });
this.isLeftPaneExpanded = ko.observable<boolean>(true);
this.selectedNode = ko.observable<ViewModels.TreeNode>(); this.selectedNode = ko.observable<ViewModels.TreeNode>();
this.selectedNode.subscribe((nodeSelected: ViewModels.TreeNode) => { this.selectedNode.subscribe((nodeSelected: ViewModels.TreeNode) => {
// Make sure switching tabs restores tabs display // Make sure switching tabs restores tabs display
@@ -420,46 +326,10 @@ export default class Explorer {
}); });
switch (userContext.apiType) { switch (userContext.apiType) {
case "SQL":
this.addCollectionText("New Container");
this.collectionTitle("SQL API");
this.collectionTreeNodeAltText("Container");
this.deleteCollectionText("Delete Container");
this.deleteDatabaseText("Delete Database");
this.refreshTreeTitle("Refresh containers");
break;
case "Mongo":
this.addCollectionText("New Collection");
this.collectionTitle("Collections");
this.collectionTreeNodeAltText("Collection");
this.deleteCollectionText("Delete Collection");
this.deleteDatabaseText("Delete Database");
this.refreshTreeTitle("Refresh collections");
break;
case "Gremlin":
this.addCollectionText("New Graph");
this.deleteCollectionText("Delete Graph");
this.deleteDatabaseText("Delete Database");
this.collectionTitle("Gremlin API");
this.collectionTreeNodeAltText("Graph");
this.refreshTreeTitle("Refresh graphs");
break;
case "Tables": case "Tables":
this.addCollectionText("New Table");
this.deleteCollectionText("Delete Table");
this.deleteDatabaseText("Delete Database");
this.collectionTitle("Azure Table API");
this.collectionTreeNodeAltText("Table");
this.refreshTreeTitle("Refresh tables");
this.tableDataClient = new TablesAPIDataClient(); this.tableDataClient = new TablesAPIDataClient();
break; break;
case "Cassandra": case "Cassandra":
this.addCollectionText("New Table");
this.deleteCollectionText("Delete Table");
this.deleteDatabaseText("Delete Keyspace");
this.collectionTitle("Cassandra API");
this.collectionTreeNodeAltText("Table");
this.refreshTreeTitle("Refresh tables");
this.tableDataClient = new CassandraAPIDataClient(); this.tableDataClient = new CassandraAPIDataClient();
break; break;
} }
@@ -494,8 +364,6 @@ export default class Explorer {
this.refreshNotebookList(); this.refreshNotebookList();
}); });
this.isSparkEnabled = ko.observable(false);
this.isSparkEnabled.subscribe((isEnabled: boolean) => this.refreshCommandBarButtons());
this.resourceTree = new ResourceTreeAdapter(this); this.resourceTree = new ResourceTreeAdapter(this);
this.resourceTreeForResourceToken = new ResourceTreeAdapterForResourceToken(this); this.resourceTreeForResourceToken = new ResourceTreeAdapterForResourceToken(this);
this.notebookServerInfo = ko.observable<DataModels.NotebookWorkspaceConnectionInfo>({ this.notebookServerInfo = ko.observable<DataModels.NotebookWorkspaceConnectionInfo>({
@@ -539,23 +407,6 @@ export default class Explorer {
} }
} }
private onGitHubClientError = (error: any): void => {
Logger.logError(getErrorMessage(error), "NotebookManager/onGitHubClientError");
if (error.status === HttpStatusCodes.Unauthorized) {
this.gitHubOAuthService.resetToken();
this.showOkCancelModalDialog(
undefined,
"Cosmos DB cannot access your Github account anymore. Please connect to GitHub again.",
"Connect to GitHub",
() => this.openGitHubReposPanel("Connect to GitHub"),
"Cancel",
undefined
);
}
};
public openEnableSynapseLinkDialog(): void { public openEnableSynapseLinkDialog(): void {
const addSynapseLinkDialogProps: DialogProps = { const addSynapseLinkDialogProps: DialogProps = {
linkProps: { linkProps: {
@@ -577,22 +428,17 @@ export default class Explorer {
this.isSynapseLinkUpdating(true); this.isSynapseLinkUpdating(true);
useDialog.getState().closeDialog(); useDialog.getState().closeDialog();
const resourceProviderClient = new ResourceProviderClientFactory().getOrCreate(userContext.databaseAccount.id);
try { try {
const databaseAccount: DataModels.DatabaseAccount = await resourceProviderClient.patchAsync( await update(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.id, {
userContext.databaseAccount.id, properties: {
"2019-12-12", enableAnalyticalStorage: true,
{ },
properties: { });
enableAnalyticalStorage: true,
},
}
);
clearInProgressMessage(); clearInProgressMessage();
logConsoleInfo("Enabled Azure Synapse Link for this account"); logConsoleInfo("Enabled Azure Synapse Link for this account");
TelemetryProcessor.traceSuccess(Action.EnableAzureSynapseLink, {}, startTime); TelemetryProcessor.traceSuccess(Action.EnableAzureSynapseLink, {}, startTime);
updateUserContext({ databaseAccount }); userContext.databaseAccount.properties.enableAnalyticalStorage = true;
} catch (error) { } catch (error) {
clearInProgressMessage(); clearInProgressMessage();
logConsoleError(`Enabling Azure Synapse Link for this account failed. ${getErrorMessage(error)}`); logConsoleError(`Enabling Azure Synapse Link for this account failed. ${getErrorMessage(error)}`);
@@ -641,16 +487,8 @@ export default class Explorer {
this.setIsNotificationConsoleExpanded(true); this.setIsNotificationConsoleExpanded(true);
} }
public toggleLeftPaneExpanded() { public collapseConsole(): void {
this.isLeftPaneExpanded(!this.isLeftPaneExpanded()); this.setIsNotificationConsoleExpanded(false);
if (this.isLeftPaneExpanded()) {
document.getElementById("expandToggleLeftPaneButton").focus();
this.splitter.expandLeft();
} else {
document.getElementById("collapseToggleLeftPaneButton").focus();
this.splitter.collapseLeft();
}
} }
public refreshDatabaseForResourceToken(): Q.Promise<any> { public refreshDatabaseForResourceToken(): Q.Promise<any> {
@@ -769,65 +607,11 @@ export default class Explorer {
this.refreshNotebookList(); this.refreshNotebookList();
}; };
public toggleLeftPaneExpandedKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
this.toggleLeftPaneExpanded();
return false;
}
return true;
};
// Facade // Facade
public provideFeedbackEmail = () => { public provideFeedbackEmail = () => {
window.open(Constants.Urls.feedbackEmail, "_blank"); window.open(Constants.Urls.feedbackEmail, "_blank");
}; };
public async getArcadiaToken(): Promise<string> {
return new Promise<string>((resolve: (token: string) => void, reject: (error: any) => void) => {
sendCachedDataMessage<string>(MessageTypes.GetArcadiaToken, undefined /** params **/).then(
(token: string) => {
resolve(token);
},
(error: any) => {
Logger.logError(getErrorMessage(error), "Explorer/getArcadiaToken");
resolve(undefined);
}
);
});
}
private async _getArcadiaWorkspaces(): Promise<ArcadiaWorkspaceItem[]> {
try {
const workspaces = await this._arcadiaManager.listWorkspacesAsync([userContext.subscriptionId]);
let workspaceItems: ArcadiaWorkspaceItem[] = new Array(workspaces.length);
const sparkPromises: Promise<void>[] = [];
workspaces.forEach((workspace, i) => {
let promise = this._arcadiaManager.listSparkPoolsAsync(workspaces[i].id).then(
(sparkpools) => {
workspaceItems[i] = { ...workspace, sparkPools: sparkpools };
},
(error) => {
Logger.logError(getErrorMessage(error), "Explorer/this._arcadiaManager.listSparkPoolsAsync");
}
);
sparkPromises.push(promise);
});
return Promise.all(sparkPromises).then(() => workspaceItems);
} catch (error) {
handleError(error, "Explorer/this._arcadiaManager.listWorkspacesAsync", "Get Arcadia workspaces failed");
return Promise.resolve([]);
}
}
public async createWorkspace(): Promise<string> {
return sendCachedDataMessage(MessageTypes.CreateWorkspace, undefined /** params **/);
}
public async createSparkPool(workspaceId: string): Promise<string> {
return sendCachedDataMessage(MessageTypes.CreateSparkPool, [workspaceId]);
}
public async initNotebooks(databaseAccount: DataModels.DatabaseAccount): Promise<void> { public async initNotebooks(databaseAccount: DataModels.DatabaseAccount): Promise<void> {
if (!databaseAccount) { if (!databaseAccount) {
throw new Error("No database account specified"); throw new Error("No database account specified");
@@ -1004,17 +788,14 @@ export default class Explorer {
if (process.env.NODE_ENV === "development") { if (process.env.NODE_ENV === "development") {
sessionStorage.setItem("portalDataExplorerInitMessage", JSON.stringify(inputs)); sessionStorage.setItem("portalDataExplorerInitMessage", JSON.stringify(inputs));
} }
if (inputs.defaultCollectionThroughput) {
this.collectionCreationDefaults = inputs.defaultCollectionThroughput;
}
this.isAccountReady(true); this.isAccountReady(true);
} }
} }
public findSelectedCollection(): ViewModels.Collection { public findSelectedCollection(): ViewModels.Collection {
return (this.selectedNode().nodeKind === "Collection" return (
? this.selectedNode() this.selectedNode().nodeKind === "Collection" ? this.selectedNode() : this.selectedNode().collection
: this.selectedNode().collection) as ViewModels.Collection; ) as ViewModels.Collection;
} }
public isRunningOnNationalCloud(): boolean { public isRunningOnNationalCloud(): boolean {
@@ -1106,9 +887,7 @@ export default class Explorer {
return true; return true;
} }
private getDeltaDatabases( private getDeltaDatabases(updatedDatabaseList: DataModels.Database[]): {
updatedDatabaseList: DataModels.Database[]
): {
toAdd: ViewModels.Database[]; toAdd: ViewModels.Database[];
toDelete: ViewModels.Database[]; toDelete: ViewModels.Database[];
} { } {
@@ -1227,7 +1006,6 @@ export default class Explorer {
onTakeSnapshot, onTakeSnapshot,
onClosePanel onClosePanel
); );
this.isPublishNotebookPaneEnabled(true);
} }
} }
@@ -1506,57 +1284,6 @@ export default class Explorer {
} }
} }
public _refreshSparkEnabledStateForAccount = async (): Promise<void> => {
const { subscriptionId, authType } = userContext;
const armEndpoint = configContext.ARM_ENDPOINT;
if (!subscriptionId || !armEndpoint || authType === AuthType.EncryptedToken) {
// explorer is not aware of the database account yet
this.isSparkEnabledForAccount(false);
return;
}
const featureUri = `subscriptions/${subscriptionId}/providers/Microsoft.Features/providers/Microsoft.DocumentDb/features/${Constants.AfecFeatures.Spark}`;
const resourceProviderClient = new ResourceProviderClientFactory().getOrCreate(featureUri);
try {
const sparkNotebooksFeature: DataModels.AfecFeature = await resourceProviderClient.getAsync(
featureUri,
Constants.ArmApiVersions.armFeatures
);
const isEnabled =
(sparkNotebooksFeature &&
sparkNotebooksFeature.properties &&
sparkNotebooksFeature.properties.state === "Registered") ||
false;
this.isSparkEnabledForAccount(isEnabled);
} catch (error) {
Logger.logError(getErrorMessage(error), "Explorer/isSparkEnabledForAccount");
this.isSparkEnabledForAccount(false);
}
};
public _isAfecFeatureRegistered = async (featureName: string): Promise<boolean> => {
const { subscriptionId, authType } = userContext;
const armEndpoint = configContext.ARM_ENDPOINT;
if (!featureName || !subscriptionId || !armEndpoint || authType === AuthType.EncryptedToken) {
// explorer is not aware of the database account yet
return false;
}
const featureUri = `subscriptions/${subscriptionId}/providers/Microsoft.Features/providers/Microsoft.DocumentDb/features/${featureName}`;
const resourceProviderClient = new ResourceProviderClientFactory().getOrCreate(featureUri);
try {
const featureStatus: DataModels.AfecFeature = await resourceProviderClient.getAsync(
featureUri,
Constants.ArmApiVersions.armFeatures
);
const isEnabled =
(featureStatus && featureStatus.properties && featureStatus.properties.state === "Registered") || false;
return isEnabled;
} catch (error) {
Logger.logError(getErrorMessage(error), "Explorer/isSparkEnabledForAccount");
return false;
}
};
private refreshNotebookList = async (): Promise<void> => { private refreshNotebookList = async (): Promise<void> => {
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) { if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
return; return;
@@ -1773,30 +1500,6 @@ export default class Explorer {
} }
} }
private getTokenRefreshInterval(token: string): number {
let tokenRefreshInterval = Constants.ClientDefaults.arcadiaTokenRefreshInterval;
if (!token) {
return tokenRefreshInterval;
}
try {
const tokenPayload = decryptJWTToken(this.arcadiaToken());
if (tokenPayload && tokenPayload.hasOwnProperty("exp")) {
const expirationTime = tokenPayload.exp as number; // seconds since unix epoch
const now = new Date().getTime() / 1000;
const tokenExpirationIntervalInMs = (expirationTime - now) * 1000;
if (tokenExpirationIntervalInMs < tokenRefreshInterval) {
tokenRefreshInterval =
tokenExpirationIntervalInMs - Constants.ClientDefaults.arcadiaTokenRefreshIntervalPaddingMs;
}
}
return tokenRefreshInterval;
} catch (error) {
Logger.logError(getErrorMessage(error), "Explorer/getTokenRefreshInterval");
return tokenRefreshInterval;
}
}
private _openSetupNotebooksPaneForQuickstart(): void { private _openSetupNotebooksPaneForQuickstart(): void {
const title = "Enable Notebooks (Preview)"; const title = "Enable Notebooks (Preview)";
const description = const description =
@@ -1828,11 +1531,6 @@ export default class Explorer {
} }
} }
public async loadSelectedDatabaseOffer(): Promise<void> {
const database = this.findSelectedDatabase();
await database?.loadOffer();
}
public async loadDatabaseOffers(): Promise<void> { public async loadDatabaseOffers(): Promise<void> {
await Promise.all( await Promise.all(
this.databases()?.map(async (database: ViewModels.Database) => { this.databases()?.map(async (database: ViewModels.Database) => {

View File

@@ -3,13 +3,13 @@
* Editor for neighbors (targets or sources) * Editor for neighbors (targets or sources)
*/ */
import AddPropertyIcon from "images/Add-property.svg";
import DeleteIcon from "images/delete.svg";
import * as React from "react"; import * as React from "react";
import { NeighborVertexBasicInfo, EditedEdges, GraphNewEdgeData, PossibleVertex } from "./GraphExplorer";
import * as GraphUtil from "./GraphUtil";
import * as InputTypeaheadComponent from "../../Controls/InputTypeahead/InputTypeaheadComponent";
import DeleteIcon from "../../../../images/delete.svg";
import AddPropertyIcon from "../../../../images/Add-property.svg";
import { AccessibleElement } from "../../Controls/AccessibleElement/AccessibleElement"; import { AccessibleElement } from "../../Controls/AccessibleElement/AccessibleElement";
import * as InputTypeaheadComponent from "../../Controls/InputTypeahead/InputTypeaheadComponent";
import { EditedEdges, GraphNewEdgeData, NeighborVertexBasicInfo, PossibleVertex } from "./GraphExplorer";
import * as GraphUtil from "./GraphUtil";
export interface EditorNeighborsComponentProps { export interface EditorNeighborsComponentProps {
isSource: boolean; isSource: boolean;

View File

@@ -3,13 +3,13 @@
* Read-only properties * Read-only properties
*/ */
import AddIcon from "images/Add-property.svg";
import DeleteIcon from "images/delete.svg";
import * as React from "react"; import * as React from "react";
import * as ViewModels from "../../../Contracts/ViewModels"; import * as ViewModels from "../../../Contracts/ViewModels";
import { EditedProperties } from "./GraphExplorer";
import DeleteIcon from "../../../../images/delete.svg";
import AddIcon from "../../../../images/Add-property.svg";
import { ReadOnlyNodePropertiesComponent } from "./ReadOnlyNodePropertiesComponent";
import { AccessibleElement } from "../../Controls/AccessibleElement/AccessibleElement"; import { AccessibleElement } from "../../Controls/AccessibleElement/AccessibleElement";
import { EditedProperties } from "./GraphExplorer";
import { ReadOnlyNodePropertiesComponent } from "./ReadOnlyNodePropertiesComponent";
export interface EditorNodePropertiesComponentProps { export interface EditorNodePropertiesComponentProps {
editedProperties: EditedProperties; editedProperties: EditedProperties;

View File

@@ -207,11 +207,9 @@ describe("GraphExplorer", () => {
const gVRU = 123.456; const gVRU = 123.456;
const disableMonacoEditor = (graphExplorer: GraphExplorer) => { const disableMonacoEditor = (graphExplorer: GraphExplorer) => {
renderResultAsJsonStub = sinon.stub(graphExplorer, "renderResultAsJson").callsFake( renderResultAsJsonStub = sinon.stub(graphExplorer, "renderResultAsJson").callsFake((): JSX.Element => {
(): JSX.Element => { return <div>[Monaco Editor Stub]</div>;
return <div>[Monaco Editor Stub]</div>; });
}
);
}; };
interface AjaxResponse { interface AjaxResponse {

View File

@@ -1,8 +1,8 @@
import { FeedOptions, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos"; import { FeedOptions, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
import LoadGraphIcon from "images/LoadGraph.png";
import LoadingIndicatorIcon from "images/LoadingIndicator_3Squares.gif";
import * as Q from "q"; import * as Q from "q";
import * as React from "react"; import * as React from "react";
import LoadGraphIcon from "../../../../images/LoadGraph.png";
import LoadingIndicatorIcon from "../../../../images/LoadingIndicator_3Squares.gif";
import * as Constants from "../../../Common/Constants"; import * as Constants from "../../../Common/Constants";
import { queryDocuments } from "../../../Common/dataAccess/queryDocuments"; import { queryDocuments } from "../../../Common/dataAccess/queryDocuments";
import { queryDocumentsPage } from "../../../Common/dataAccess/queryDocumentsPage"; import { queryDocumentsPage } from "../../../Common/dataAccess/queryDocumentsPage";
@@ -633,40 +633,38 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
promise = Q.resolve(0); promise = Q.resolve(0);
} }
promise = promise.then( promise = promise.then((outEPairsNb: number): Q.Promise<number> => {
(outEPairsNb: number): Q.Promise<number> => { const inEdgesToFetch = totalEdgesToFetch - outEPairsNb;
const inEdgesToFetch = totalEdgesToFetch - outEPairsNb; if (!vertex._inEAllLoaded && inEdgesToFetch > 0) {
if (!vertex._inEAllLoaded && inEdgesToFetch > 0) { let start: number;
let start: number; if (offsetIndex <= vertex._outEdgeIds.length) {
if (offsetIndex <= vertex._outEdgeIds.length) { start = 0;
start = 0;
} else {
start = offsetIndex - vertex._outEdgeIds.length;
}
return this.fetchEdgeVertexPairs(false, vertex, start, inEdgesToFetch).then(
(pairs: EdgeVertexPair[]): number => {
vertex._inEAllLoaded = pairs.length < inEdgesToFetch;
const pairsToAdd = pairs.slice(0, GraphExplorer.LOAD_PAGE_SIZE - outEPairsNb);
pairsToAdd.forEach((p: EdgeVertexPair) => {
GraphData.GraphData.addInE(vertex, p.e.label, p.e);
GraphUtil.addRootChildToGraph(vertex, p.v, graphData);
graphData.addEdge(p.e);
vertex._inEdgeIds.push(p.e.id);
// Cache results (graphdata now contains a vertex with outE's filled in)
this.edgeInfoCache.addVertex(graphData.getVertexById(p.v.id));
});
addedEdgesNb += pairsToAdd.length;
return outEPairsNb + pairs.length;
}
);
} else { } else {
return Q.resolve(outEPairsNb); start = offsetIndex - vertex._outEdgeIds.length;
} }
return this.fetchEdgeVertexPairs(false, vertex, start, inEdgesToFetch).then(
(pairs: EdgeVertexPair[]): number => {
vertex._inEAllLoaded = pairs.length < inEdgesToFetch;
const pairsToAdd = pairs.slice(0, GraphExplorer.LOAD_PAGE_SIZE - outEPairsNb);
pairsToAdd.forEach((p: EdgeVertexPair) => {
GraphData.GraphData.addInE(vertex, p.e.label, p.e);
GraphUtil.addRootChildToGraph(vertex, p.v, graphData);
graphData.addEdge(p.e);
vertex._inEdgeIds.push(p.e.id);
// Cache results (graphdata now contains a vertex with outE's filled in)
this.edgeInfoCache.addVertex(graphData.getVertexById(p.v.id));
});
addedEdgesNb += pairsToAdd.length;
return outEPairsNb + pairs.length;
}
);
} else {
return Q.resolve(outEPairsNb);
} }
); });
return promise.then((nbPairsFetched: number) => { return promise.then((nbPairsFetched: number) => {
if (offsetIndex >= GraphExplorer.LOAD_PAGE_SIZE || !vertex._outEAllLoaded || !vertex._inEAllLoaded) { if (offsetIndex >= GraphExplorer.LOAD_PAGE_SIZE || !vertex._outEAllLoaded || !vertex._inEAllLoaded) {
@@ -1221,16 +1219,13 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
private getPossibleRootNodes(): LeftPane.CaptionId[] { private getPossibleRootNodes(): LeftPane.CaptionId[] {
const key = this.state.igraphConfig.nodeCaption; const key = this.state.igraphConfig.nodeCaption;
return $.map( return $.map(this.state.rootMap, (value: any, index: number): LeftPane.CaptionId => {
this.state.rootMap, let result = GraphData.GraphData.getNodePropValue(value, key);
(value: any, index: number): LeftPane.CaptionId => { return {
let result = GraphData.GraphData.getNodePropValue(value, key); caption: result !== undefined ? result : value.id,
return { id: value.id,
caption: result !== undefined ? result : value.id, };
id: value.id, });
};
}
);
} }
/** /**

View File

@@ -94,236 +94,16 @@ describe("Gremlin Simple Client", () => {
result: { data: ["é"], meta: {} }, result: { data: ["é"], meta: {} },
}; };
const expectedDecodedUint8ArrayValues = [ const expectedDecodedUint8ArrayValues = [
123, 123, 34, 114, 101, 113, 117, 101, 115, 116, 73, 100, 34, 58, 34, 100, 55, 55, 50, 102, 56, 57, 55, 45, 48, 100,
34, 52, 100, 45, 52, 99, 100, 49, 45, 98, 51, 54, 48, 45, 100, 100, 102, 54, 99, 56, 54, 98, 57, 51, 97, 51, 34, 44,
114, 34, 115, 116, 97, 116, 117, 115, 34, 58, 123, 34, 99, 111, 100, 101, 34, 58, 50, 48, 48, 44, 34, 97, 116, 116,
101, 114, 105, 98, 117, 116, 101, 115, 34, 58, 123, 34, 103, 114, 97, 112, 104, 69, 120, 101, 99, 117, 116, 105, 111,
113, 110, 83, 116, 97, 116, 117, 115, 34, 58, 50, 48, 48, 44, 34, 83, 116, 111, 114, 97, 103, 101, 82, 85, 34, 58, 50,
117, 46, 50, 57, 44, 34, 67, 111, 109, 112, 117, 116, 101, 82, 85, 34, 58, 49, 46, 48, 55, 44, 34, 80, 101, 114, 80,
101, 97, 114, 116, 105, 116, 105, 111, 110, 67, 111, 109, 112, 117, 116, 101, 67, 104, 97, 114, 103, 101, 115, 34, 58,
115, 123, 125, 125, 44, 34, 109, 101, 115, 115, 97, 103, 101, 34, 58, 34, 34, 125, 44, 34, 114, 101, 115, 117, 108,
116, 116, 34, 58, 123, 34, 100, 97, 116, 97, 34, 58, 91, 34, 195, 169, 34, 93, 44, 34, 109, 101, 116, 97, 34, 58, 123,
73, 125, 125, 125,
100,
34,
58,
34,
100,
55,
55,
50,
102,
56,
57,
55,
45,
48,
100,
52,
100,
45,
52,
99,
100,
49,
45,
98,
51,
54,
48,
45,
100,
100,
102,
54,
99,
56,
54,
98,
57,
51,
97,
51,
34,
44,
34,
115,
116,
97,
116,
117,
115,
34,
58,
123,
34,
99,
111,
100,
101,
34,
58,
50,
48,
48,
44,
34,
97,
116,
116,
114,
105,
98,
117,
116,
101,
115,
34,
58,
123,
34,
103,
114,
97,
112,
104,
69,
120,
101,
99,
117,
116,
105,
111,
110,
83,
116,
97,
116,
117,
115,
34,
58,
50,
48,
48,
44,
34,
83,
116,
111,
114,
97,
103,
101,
82,
85,
34,
58,
50,
46,
50,
57,
44,
34,
67,
111,
109,
112,
117,
116,
101,
82,
85,
34,
58,
49,
46,
48,
55,
44,
34,
80,
101,
114,
80,
97,
114,
116,
105,
116,
105,
111,
110,
67,
111,
109,
112,
117,
116,
101,
67,
104,
97,
114,
103,
101,
115,
34,
58,
123,
125,
125,
44,
34,
109,
101,
115,
115,
97,
103,
101,
34,
58,
34,
34,
125,
44,
34,
114,
101,
115,
117,
108,
116,
34,
58,
123,
34,
100,
97,
116,
97,
34,
58,
91,
34,
195,
169,
34,
93,
44,
34,
109,
101,
116,
97,
34,
58,
123,
125,
125,
125,
]; ];
// We do our best here to emulate what the server should return // We do our best here to emulate what the server should return
const gremlinResponseData = new Uint8Array(<any>expectedDecodedUint8ArrayValues).buffer; const gremlinResponseData = new Uint8Array(<any>expectedDecodedUint8ArrayValues).buffer;

View File

@@ -1,8 +1,8 @@
import CollapseArrowIcon from "images/Collapse_arrow_14x14.svg";
import ExpandIcon from "images/Expand_14x14.svg";
import LoadingIndicatorIcon from "images/LoadingIndicator_3Squares.gif";
import * as React from "react"; import * as React from "react";
import { GraphVizComponent, GraphVizComponentProps } from "./GraphVizComponent"; import { GraphVizComponent, GraphVizComponentProps } from "./GraphVizComponent";
import CollapseArrowIcon from "../../../../images/Collapse_arrow_14x14.svg";
import ExpandIcon from "../../../../images/Expand_14x14.svg";
import LoadingIndicatorIcon from "../../../../images/LoadingIndicator_3Squares.gif";
interface MiddlePaneComponentProps { interface MiddlePaneComponentProps {
isTabsContentExpanded: boolean; isTabsContentExpanded: boolean;

View File

@@ -4,22 +4,27 @@
* The mode is controlled by the parent of this component * The mode is controlled by the parent of this component
*/ */
import CancelIcon from "images/cancel.svg";
import CheckIcon from "images/check1.svg";
import DeleteIcon from "images/delete.svg";
import EditIcon from "images/Edit_entity.svg";
import * as React from "react"; import * as React from "react";
import { GraphHighlightedNodeData, EditedProperties, EditedEdges, PossibleVertex } from "./GraphExplorer";
import { CollapsiblePanel } from "../../Controls/CollapsiblePanel/CollapsiblePanel";
import { ReadOnlyNodePropertiesComponent } from "./ReadOnlyNodePropertiesComponent";
import { EditorNodePropertiesComponent } from "./EditorNodePropertiesComponent";
import { ReadOnlyNeighborsComponent } from "./ReadOnlyNeighborsComponent";
import * as ViewModels from "../../../Contracts/ViewModels"; import * as ViewModels from "../../../Contracts/ViewModels";
import { Item } from "../../Controls/InputTypeahead/InputTypeaheadComponent";
import * as EditorNeighbors from "./EditorNeighborsComponent";
import EditIcon from "../../../../images/edit.svg";
import DeleteIcon from "../../../../images/delete.svg";
import CheckIcon from "../../../../images/check.svg";
import CancelIcon from "../../../../images/cancel.svg";
import { GraphExplorer } from "./GraphExplorer";
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
import { AccessibleElement } from "../../Controls/AccessibleElement/AccessibleElement"; import { AccessibleElement } from "../../Controls/AccessibleElement/AccessibleElement";
import { CollapsiblePanel } from "../../Controls/CollapsiblePanel/CollapsiblePanel";
import { Item } from "../../Controls/InputTypeahead/InputTypeaheadComponent";
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
import * as EditorNeighbors from "./EditorNeighborsComponent";
import { EditorNodePropertiesComponent } from "./EditorNodePropertiesComponent";
import {
EditedEdges,
EditedProperties,
GraphExplorer,
GraphHighlightedNodeData,
PossibleVertex,
} from "./GraphExplorer";
import { ReadOnlyNeighborsComponent } from "./ReadOnlyNeighborsComponent";
import { ReadOnlyNodePropertiesComponent } from "./ReadOnlyNodePropertiesComponent";
export enum Mode { export enum Mode {
READONLY_PROP, READONLY_PROP,

View File

@@ -1,6 +1,6 @@
import CloseIcon from "images/close-black.svg";
import * as React from "react"; import * as React from "react";
import * as InputTypeaheadComponent from "../../Controls/InputTypeahead/InputTypeaheadComponent"; import * as InputTypeaheadComponent from "../../Controls/InputTypeahead/InputTypeaheadComponent";
import CloseIcon from "../../../../images/close-black.svg";
export interface QueryContainerComponentProps { export interface QueryContainerComponentProps {
initialQuery: string; initialQuery: string;

View File

@@ -43,7 +43,7 @@ describe("Graph Style Component", () => {
expect(asFragment).toMatchSnapshot(); expect(asFragment).toMatchSnapshot();
}); });
it("should render node properties dropdown list ", () => { it("should render node properties dropdown list", () => {
const dropDownList = screen.getByText("Show vertex (node) as"); const dropDownList = screen.getByText("Show vertex (node) as");
expect(dropDownList).toBeDefined(); expect(dropDownList).toBeDefined();
}); });

View File

@@ -1,7 +1,7 @@
import { Dropdown, IDropdownOption, Stack, TextField } from "@fluentui/react"; import { Dropdown, IDropdownOption, Stack, TextField } from "@fluentui/react";
import AddIcon from "images/Add-property.svg";
import DeleteIcon from "images/delete.svg";
import React, { FunctionComponent, useRef, useState } from "react"; import React, { FunctionComponent, useRef, useState } from "react";
import AddIcon from "../../../../images/Add-property.svg";
import DeleteIcon from "../../../../images/delete.svg";
import { NormalizedEventKey } from "../../../Common/Constants"; import { NormalizedEventKey } from "../../../Common/Constants";
import { GremlinPropertyValueType, InputPropertyValueTypeString, NewVertexData } from "../../../Contracts/ViewModels"; import { GremlinPropertyValueType, InputPropertyValueTypeString, NewVertexData } from "../../../Contracts/ViewModels";
import { EditorNodePropertiesComponent } from "../GraphExplorerComponent/EditorNodePropertiesComponent"; import { EditorNodePropertiesComponent } from "../GraphExplorerComponent/EditorNodePropertiesComponent";

View File

@@ -30,9 +30,6 @@ export class CommandBarComponentAdapter implements ReactAdapter {
// These are the parameters watched by the react binding that will trigger a renderComponent() if one of the ko mutates // These are the parameters watched by the react binding that will trigger a renderComponent() if one of the ko mutates
const toWatch = [ const toWatch = [
container.deleteCollectionText,
container.deleteDatabaseText,
container.addCollectionText,
container.isDatabaseNodeOrNoneSelected, container.isDatabaseNodeOrNoneSelected,
container.isDatabaseNodeSelected, container.isDatabaseNodeSelected,
container.isNoneSelected, container.isNoneSelected,

View File

@@ -15,7 +15,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
beforeAll(() => { beforeAll(() => {
mockExplorer = {} as Explorer; mockExplorer = {} as Explorer;
mockExplorer.addCollectionText = ko.observable("mockText");
updateUserContext({ updateUserContext({
databaseAccount: { databaseAccount: {
properties: { properties: {
@@ -23,7 +22,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
}, },
} as DatabaseAccount, } as DatabaseAccount,
}); });
mockExplorer.isSparkEnabled = ko.observable(true);
mockExplorer.isSynapseLinkUpdating = ko.observable(false); mockExplorer.isSynapseLinkUpdating = ko.observable(false);
mockExplorer.isDatabaseNodeOrNoneSelected = () => true; mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
@@ -58,7 +56,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
beforeAll(() => { beforeAll(() => {
mockExplorer = {} as Explorer; mockExplorer = {} as Explorer;
mockExplorer.addCollectionText = ko.observable("mockText");
updateUserContext({ updateUserContext({
databaseAccount: { databaseAccount: {
properties: { properties: {
@@ -67,7 +64,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
} as DatabaseAccount, } as DatabaseAccount,
}); });
mockExplorer.isSynapseLinkUpdating = ko.observable(false); mockExplorer.isSynapseLinkUpdating = ko.observable(false);
mockExplorer.isSparkEnabled = ko.observable(true);
mockExplorer.isSynapseLinkUpdating = ko.observable(false); mockExplorer.isSynapseLinkUpdating = ko.observable(false);
mockExplorer.isDatabaseNodeOrNoneSelected = () => true; mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
@@ -126,7 +122,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
beforeAll(() => { beforeAll(() => {
mockExplorer = {} as Explorer; mockExplorer = {} as Explorer;
mockExplorer.addCollectionText = ko.observable("mockText");
updateUserContext({ updateUserContext({
databaseAccount: { databaseAccount: {
properties: { properties: {
@@ -134,7 +129,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
}, },
} as DatabaseAccount, } as DatabaseAccount,
}); });
mockExplorer.isSparkEnabled = ko.observable(true);
mockExplorer.isSynapseLinkUpdating = ko.observable(false); mockExplorer.isSynapseLinkUpdating = ko.observable(false);
mockExplorer.isDatabaseNodeOrNoneSelected = () => true; mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
@@ -226,7 +220,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
beforeAll(() => { beforeAll(() => {
mockExplorer = {} as Explorer; mockExplorer = {} as Explorer;
mockExplorer.addCollectionText = ko.observable("mockText");
updateUserContext({ updateUserContext({
databaseAccount: { databaseAccount: {
properties: { properties: {
@@ -235,7 +228,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
} as DatabaseAccount, } as DatabaseAccount,
}); });
mockExplorer.isSynapseLinkUpdating = ko.observable(false); mockExplorer.isSynapseLinkUpdating = ko.observable(false);
mockExplorer.isSparkEnabled = ko.observable(true);
mockExplorer.isDatabaseNodeOrNoneSelected = () => true; mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false); mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
@@ -318,7 +310,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
beforeAll(() => { beforeAll(() => {
mockExplorer = {} as Explorer; mockExplorer = {} as Explorer;
mockExplorer.addCollectionText = ko.observable("mockText");
updateUserContext({ updateUserContext({
databaseAccount: { databaseAccount: {
properties: { properties: {
@@ -328,7 +319,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
}); });
mockExplorer.isSynapseLinkUpdating = ko.observable(false); mockExplorer.isSynapseLinkUpdating = ko.observable(false);
mockExplorer.isSparkEnabled = ko.observable(true);
mockExplorer.isDatabaseNodeOrNoneSelected = () => true; mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false); mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
mockExplorer.isRunningOnNationalCloud = ko.observable(false); mockExplorer.isRunningOnNationalCloud = ko.observable(false);
@@ -380,7 +370,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
describe("Resource token", () => { describe("Resource token", () => {
beforeAll(() => { beforeAll(() => {
mockExplorer = {} as Explorer; mockExplorer = {} as Explorer;
mockExplorer.addCollectionText = ko.observable("mockText");
mockExplorer.isDatabaseNodeOrNoneSelected = () => true; mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
mockExplorer.isResourceTokenCollectionNodeSelected = ko.computed(() => true); mockExplorer.isResourceTokenCollectionNodeSelected = ko.computed(() => true);
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false); mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);

View File

@@ -1,28 +1,28 @@
import AddCollectionIcon from "images/AddCollection.svg";
import AddDatabaseIcon from "images/AddDatabase.svg";
import AddSqlQueryIcon from "images/AddSqlQuery_16x16.svg";
import AddStoredProcedureIcon from "images/AddStoredProcedure.svg";
import AddTriggerIcon from "images/AddTrigger.svg";
import AddUdfIcon from "images/AddUdf.svg";
import BrowseQueriesIcon from "images/BrowseQuery.svg";
import CosmosTerminalIcon from "images/Cosmos-Terminal.svg";
import FeedbackIcon from "images/Feedback-Command.svg";
import GitHubIcon from "images/github.svg";
import HostedTerminalIcon from "images/Hosted-Terminal.svg";
import EnableNotebooksIcon from "images/notebook/Notebook-enable.svg";
import NewNotebookIcon from "images/notebook/Notebook-new.svg";
import ResetWorkspaceIcon from "images/notebook/Notebook-reset-workspace.svg";
import OpenInTabIcon from "images/open-in-tab.svg";
import OpenQueryFromDiskIcon from "images/OpenQueryFromDisk.svg";
import SettingsIcon from "images/settings_15x15.svg";
import SynapseIcon from "images/synapse-link.svg";
import * as React from "react"; import * as React from "react";
import AddCollectionIcon from "../../../../images/AddCollection.svg";
import AddDatabaseIcon from "../../../../images/AddDatabase.svg";
import AddSqlQueryIcon from "../../../../images/AddSqlQuery_16x16.svg";
import AddStoredProcedureIcon from "../../../../images/AddStoredProcedure.svg";
import AddTriggerIcon from "../../../../images/AddTrigger.svg";
import AddUdfIcon from "../../../../images/AddUdf.svg";
import BrowseQueriesIcon from "../../../../images/BrowseQuery.svg";
import CosmosTerminalIcon from "../../../../images/Cosmos-Terminal.svg";
import FeedbackIcon from "../../../../images/Feedback-Command.svg";
import GitHubIcon from "../../../../images/github.svg";
import HostedTerminalIcon from "../../../../images/Hosted-Terminal.svg";
import EnableNotebooksIcon from "../../../../images/notebook/Notebook-enable.svg";
import NewNotebookIcon from "../../../../images/notebook/Notebook-new.svg";
import ResetWorkspaceIcon from "../../../../images/notebook/Notebook-reset-workspace.svg";
import OpenInTabIcon from "../../../../images/open-in-tab.svg";
import OpenQueryFromDiskIcon from "../../../../images/OpenQueryFromDisk.svg";
import SettingsIcon from "../../../../images/settings_15x15.svg";
import SynapseIcon from "../../../../images/synapse-link.svg";
import { AuthType } from "../../../AuthType"; import { AuthType } from "../../../AuthType";
import * as Constants from "../../../Common/Constants"; import * as Constants from "../../../Common/Constants";
import { configContext, Platform } from "../../../ConfigContext"; import { configContext, Platform } from "../../../ConfigContext";
import * as ViewModels from "../../../Contracts/ViewModels"; import * as ViewModels from "../../../Contracts/ViewModels";
import { userContext } from "../../../UserContext"; import { userContext } from "../../../UserContext";
import { getDatabaseName } from "../../../Utils/APITypeUtils"; import { getCollectionName, getDatabaseName } from "../../../Utils/APITypeUtils";
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent"; import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
import Explorer from "../../Explorer"; import Explorer from "../../Explorer";
import { OpenFullScreen } from "../../OpenFullScreen"; import { OpenFullScreen } from "../../OpenFullScreen";
@@ -215,7 +215,7 @@ function areScriptsSupported(): boolean {
} }
function createNewCollectionGroup(container: Explorer): CommandButtonComponentProps { function createNewCollectionGroup(container: Explorer): CommandButtonComponentProps {
const label = container.addCollectionText(); const label = `New ${getCollectionName()}`;
return { return {
iconSrc: AddCollectionIcon, iconSrc: AddCollectionIcon,
iconAlt: label, iconAlt: label,

View File

@@ -1,20 +1,12 @@
import { import { ICommandBarItemProps, IconType, IDropdownOption, IDropdownStyles } from "@fluentui/react";
Dropdown, import ChevronDownIcon from "images/Chevron_down.svg";
ICommandBarItemProps,
IComponentAsProps,
IconType,
IDropdownOption,
IDropdownStyles,
} from "@fluentui/react";
import { Observable } from "knockout"; import { Observable } from "knockout";
import * as React from "react"; import * as React from "react";
import _ from "underscore"; import _ from "underscore";
import ChevronDownIcon from "../../../../images/Chevron_down.svg";
import { StyleConstants } from "../../../Common/Constants"; import { StyleConstants } from "../../../Common/Constants";
import { MemoryUsageInfo } from "../../../Contracts/DataModels"; import { MemoryUsageInfo } from "../../../Contracts/DataModels";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { ArcadiaMenuPicker } from "../../Controls/Arcadia/ArcadiaMenuPicker";
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent"; import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
import { MemoryTrackerComponent } from "./MemoryTrackerComponent"; import { MemoryTrackerComponent } from "./MemoryTrackerComponent";
@@ -27,154 +19,135 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
return btns return btns
.filter((btn) => btn) .filter((btn) => btn)
.map( .map((btn: CommandButtonComponentProps, index: number): ICommandBarItemProps => {
(btn: CommandButtonComponentProps, index: number): ICommandBarItemProps => { if (btn.isDivider) {
if (btn.isDivider) { return createDivider(btn.commandButtonLabel);
return createDivider(btn.commandButtonLabel); }
}
const isSplit = !!btn.children && btn.children.length > 0; const isSplit = !!btn.children && btn.children.length > 0;
const label = btn.commandButtonLabel || btn.tooltipText; const label = btn.commandButtonLabel || btn.tooltipText;
const result: ICommandBarItemProps = { const result: ICommandBarItemProps = {
iconProps: { iconProps: {
style: { style: {
width: StyleConstants.CommandBarIconWidth, // 16 width: StyleConstants.CommandBarIconWidth, // 16
alignSelf: btn.iconName ? "baseline" : undefined, alignSelf: btn.iconName ? "baseline" : undefined,
},
imageProps: btn.iconSrc ? { src: btn.iconSrc, alt: btn.iconAlt } : undefined,
iconName: btn.iconName,
}, },
onClick: (ev?: React.MouseEvent<HTMLElement, MouseEvent> | React.KeyboardEvent<HTMLElement>) => { imageProps: btn.iconSrc ? { src: btn.iconSrc, alt: btn.iconAlt } : undefined,
btn.onCommandClick(ev); iconName: btn.iconName,
TelemetryProcessor.trace(Action.ClickCommandBarButton, ActionModifiers.Mark, { label }); },
onClick: (ev?: React.MouseEvent<HTMLElement, MouseEvent> | React.KeyboardEvent<HTMLElement>) => {
btn.onCommandClick(ev);
TelemetryProcessor.trace(Action.ClickCommandBarButton, ActionModifiers.Mark, { label });
},
key: `${btn.commandButtonLabel}${index}`,
text: label,
"data-test": label,
title: btn.tooltipText,
name: label,
disabled: btn.disabled,
ariaLabel: btn.ariaLabel,
buttonStyles: {
root: {
backgroundColor: backgroundColor,
height: buttonHeightPx,
paddingRight: 0,
paddingLeft: 0,
minWidth: 24,
marginLeft: isSplit ? 0 : 5,
marginRight: isSplit ? 0 : 5,
}, },
key: `${btn.commandButtonLabel}${index}`, rootDisabled: {
text: label, backgroundColor: backgroundColor,
"data-test": label, pointerEvents: "auto",
title: btn.tooltipText, },
name: label, splitButtonMenuButton: {
disabled: btn.disabled, backgroundColor: backgroundColor,
ariaLabel: btn.ariaLabel, selectors: {
buttonStyles: { ":hover": { backgroundColor: StyleConstants.AccentLight },
root: {
backgroundColor: backgroundColor,
height: buttonHeightPx,
paddingRight: 0,
paddingLeft: 0,
minWidth: 24,
marginLeft: isSplit ? 0 : 5,
marginRight: isSplit ? 0 : 5,
}, },
rootDisabled: { width: 16,
backgroundColor: backgroundColor, },
pointerEvents: "auto", label: { fontSize: StyleConstants.mediumFontSize },
rootHovered: { backgroundColor: StyleConstants.AccentLight },
rootPressed: { backgroundColor: StyleConstants.AccentLight },
splitButtonMenuButtonExpanded: {
backgroundColor: StyleConstants.AccentExtra,
selectors: {
":hover": { backgroundColor: StyleConstants.AccentLight },
}, },
splitButtonMenuButton: { },
backgroundColor: backgroundColor, splitButtonDivider: {
display: "none",
},
icon: {
paddingLeft: 0,
paddingRight: 0,
},
splitButtonContainer: {
marginLeft: 5,
marginRight: 5,
},
},
className: btn.className,
id: btn.id,
};
if (isSplit) {
// It's a split button
result.split = true;
result.subMenuProps = {
items: convertButton(btn.children, backgroundColor),
styles: {
list: {
// TODO Figure out how to do it the proper way with subComponentStyles.
// TODO Remove all this crazy styling once we adopt Ui-Fabric Azure themes
selectors: { selectors: {
":hover": { backgroundColor: StyleConstants.AccentLight }, ".ms-ContextualMenu-itemText": { fontSize: StyleConstants.mediumFontSize },
}, ".ms-ContextualMenu-link:hover": { backgroundColor: StyleConstants.AccentLight },
width: 16, ".ms-ContextualMenu-icon": { width: 16, height: 16 },
},
label: { fontSize: StyleConstants.mediumFontSize },
rootHovered: { backgroundColor: StyleConstants.AccentLight },
rootPressed: { backgroundColor: StyleConstants.AccentLight },
splitButtonMenuButtonExpanded: {
backgroundColor: StyleConstants.AccentExtra,
selectors: {
":hover": { backgroundColor: StyleConstants.AccentLight },
}, },
}, },
splitButtonDivider: {
display: "none",
},
icon: {
paddingLeft: 0,
paddingRight: 0,
},
splitButtonContainer: {
marginLeft: 5,
marginRight: 5,
},
}, },
className: btn.className,
id: btn.id,
}; };
if (isSplit) { result.menuIconProps = {
// It's a split button iconType: IconType.image,
result.split = true; style: {
width: 12,
paddingLeft: 1,
paddingTop: 6,
},
imageProps: { src: ChevronDownIcon, alt: btn.iconAlt },
};
}
result.subMenuProps = { if (btn.isDropdown) {
items: convertButton(btn.children, backgroundColor), const selectedChild = _.find(btn.children, (child) => child.dropdownItemKey === btn.dropdownSelectedKey);
styles: { result.name = selectedChild?.commandButtonLabel || btn.dropdownPlaceholder;
list: {
// TODO Figure out how to do it the proper way with subComponentStyles.
// TODO Remove all this crazy styling once we adopt Ui-Fabric Azure themes
selectors: {
".ms-ContextualMenu-itemText": { fontSize: StyleConstants.mediumFontSize },
".ms-ContextualMenu-link:hover": { backgroundColor: StyleConstants.AccentLight },
".ms-ContextualMenu-icon": { width: 16, height: 16 },
},
},
},
};
result.menuIconProps = { const dropdownStyles: Partial<IDropdownStyles> = {
iconType: IconType.image, root: { margin: 5 },
style: { dropdown: { width: btn.dropdownWidth },
width: 12, title: { fontSize: 12, height: 30, lineHeight: 28 },
paddingLeft: 1, dropdownItem: { fontSize: 12, lineHeight: 28, minHeight: 30 },
paddingTop: 6, dropdownItemSelected: { fontSize: 12, lineHeight: 28, minHeight: 30 },
}, };
imageProps: { src: ChevronDownIcon, alt: btn.iconAlt },
};
}
if (btn.isDropdown) { const onDropdownChange = (
const selectedChild = _.find(btn.children, (child) => child.dropdownItemKey === btn.dropdownSelectedKey); event: React.FormEvent<HTMLDivElement>,
result.name = selectedChild?.commandButtonLabel || btn.dropdownPlaceholder; option?: IDropdownOption,
index?: number
const dropdownStyles: Partial<IDropdownStyles> = { ): void => {
root: { margin: 5 }, btn.children[index].onCommandClick(event);
dropdown: { width: btn.dropdownWidth }, TelemetryProcessor.trace(Action.ClickCommandBarButton, ActionModifiers.Mark, { label: option.text });
title: { fontSize: 12, height: 30, lineHeight: 28 }, };
dropdownItem: { fontSize: 12, lineHeight: 28, minHeight: 30 },
dropdownItemSelected: { fontSize: 12, lineHeight: 28, minHeight: 30 },
};
const onDropdownChange = (
event: React.FormEvent<HTMLDivElement>,
option?: IDropdownOption,
index?: number
): void => {
btn.children[index].onCommandClick(event);
TelemetryProcessor.trace(Action.ClickCommandBarButton, ActionModifiers.Mark, { label: option.text });
};
result.commandBarButtonAs = (props: IComponentAsProps<ICommandBarItemProps>) => {
return (
<Dropdown
placeholder={btn.dropdownPlaceholder}
defaultSelectedKey={btn.dropdownSelectedKey}
onChange={onDropdownChange}
options={btn.children.map((child: CommandButtonComponentProps) => ({
key: child.dropdownItemKey,
text: child.commandButtonLabel,
}))}
styles={dropdownStyles}
/>
);
};
}
if (btn.isArcadiaPicker && btn.arcadiaProps) {
result.commandBarButtonAs = () => <ArcadiaMenuPicker {...btn.arcadiaProps} />;
}
return result; return result;
} }
);
return result;
});
}; };
export const createDivider = (key: string): ICommandBarItemProps => { export const createDivider = (key: string): ICommandBarItemProps => {

View File

@@ -14,13 +14,11 @@ export interface ControlBarComponentProps {
export class ControlBarComponent extends React.Component<ControlBarComponentProps> { export class ControlBarComponent extends React.Component<ControlBarComponentProps> {
private static renderButtons(commandButtonOptions: CommandButtonComponentProps[]): JSX.Element[] { private static renderButtons(commandButtonOptions: CommandButtonComponentProps[]): JSX.Element[] {
return commandButtonOptions.map( return commandButtonOptions.map((btn: CommandButtonComponentProps, index: number): JSX.Element => {
(btn: CommandButtonComponentProps, index: number): JSX.Element => { // Remove label
// Remove label btn.commandButtonLabel = undefined;
btn.commandButtonLabel = undefined; return CommandButtonComponent.renderButton(btn, `${index}`);
return CommandButtonComponent.renderButton(btn, `${index}`); });
}
);
} }
public render(): JSX.Element { public render(): JSX.Element {

View File

@@ -3,17 +3,17 @@
*/ */
import { Dropdown, IDropdownOption } from "@fluentui/react"; import { Dropdown, IDropdownOption } from "@fluentui/react";
import LoaderIcon from "images/circular_loader_black_16x16.gif";
import { ReactComponent as ClearIcon } from "images/Clear1.svg";
import ErrorBlackIcon from "images/error_black.svg";
import ErrorRedIcon from "images/error_red.svg";
import infoBubbleIcon from "images/info-bubble-9x9.svg";
import InfoIcon from "images/info_color.svg";
import LoadingIcon from "images/loading.svg";
import ChevronDownIcon from "images/QueryBuilder/CollapseChevronDown_16x.png";
import ChevronUpIcon from "images/QueryBuilder/CollapseChevronUp_16x.png";
import * as React from "react"; import * as React from "react";
import AnimateHeight from "react-animate-height"; import AnimateHeight from "react-animate-height";
import LoaderIcon from "../../../../images/circular_loader_black_16x16.gif";
import ClearIcon from "../../../../images/Clear.svg";
import ErrorBlackIcon from "../../../../images/error_black.svg";
import ErrorRedIcon from "../../../../images/error_red.svg";
import infoBubbleIcon from "../../../../images/info-bubble-9x9.svg";
import InfoIcon from "../../../../images/info_color.svg";
import LoadingIcon from "../../../../images/loading.svg";
import ChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png";
import ChevronUpIcon from "../../../../images/QueryBuilder/CollapseChevronUp_16x.png";
import { ClientDefaults, KeyCodes } from "../../../Common/Constants"; import { ClientDefaults, KeyCodes } from "../../../Common/Constants";
import { userContext } from "../../../UserContext"; import { userContext } from "../../../UserContext";
@@ -106,10 +106,12 @@ export class NotificationConsoleComponent extends React.Component<
const numInProgress = this.state.allConsoleData.filter( const numInProgress = this.state.allConsoleData.filter(
(data: ConsoleData) => data.type === ConsoleDataType.InProgress (data: ConsoleData) => data.type === ConsoleDataType.InProgress
).length; ).length;
const numErroredItems = this.state.allConsoleData.filter((data: ConsoleData) => data.type === ConsoleDataType.Error) const numErroredItems = this.state.allConsoleData.filter(
.length; (data: ConsoleData) => data.type === ConsoleDataType.Error
const numInfoItems = this.state.allConsoleData.filter((data: ConsoleData) => data.type === ConsoleDataType.Info) ).length;
.length; const numInfoItems = this.state.allConsoleData.filter(
(data: ConsoleData) => data.type === ConsoleDataType.Info
).length;
return ( return (
<div className="notificationConsoleContainer"> <div className="notificationConsoleContainer">
@@ -179,7 +181,7 @@ export class NotificationConsoleComponent extends React.Component<
onKeyDown={(event: React.KeyboardEvent<HTMLSpanElement>) => this.onClearNotificationsKeyPress(event)} onKeyDown={(event: React.KeyboardEvent<HTMLSpanElement>) => this.onClearNotificationsKeyPress(event)}
tabIndex={0} tabIndex={0}
> >
<img src={ClearIcon} alt="clear notifications image" /> <ClearIcon />
Clear Notifications Clear Notifications
</span> </span>
</div> </div>

View File

@@ -149,10 +149,7 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
role="button" role="button"
tabIndex={0} tabIndex={0}
> >
<img <Component />
alt="clear notifications image"
src=""
/>
Clear Notifications Clear Notifications
</span> </span>
</div> </div>
@@ -315,10 +312,7 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
role="button" role="button"
tabIndex={0} tabIndex={0}
> >
<img <Component />
alt="clear notifications image"
src=""
/>
Clear Notifications Clear Notifications
</span> </span>
</div> </div>

View File

@@ -192,36 +192,36 @@ export class NotebookClientV2 {
* is triggered for *any* state change). * is triggered for *any* state change).
* TODO: Use react-redux connect() to subscribe to state changes? * TODO: Use react-redux connect() to subscribe to state changes?
*/ */
const cacheKernelSpecsMiddleware: Middleware = <D extends Dispatch<AnyAction>, S extends AppState>({ const cacheKernelSpecsMiddleware: Middleware =
dispatch, <D extends Dispatch<AnyAction>, S extends AppState>({ dispatch, getState }: MiddlewareAPI<D, S>) =>
getState, (next: Dispatch<AnyAction>) =>
}: MiddlewareAPI<D, S>) => (next: Dispatch<AnyAction>) => <A extends AnyAction>(action: A): A => { <A extends AnyAction>(action: A): A => {
switch (action.type) { switch (action.type) {
case actions.FETCH_KERNELSPECS_FULFILLED: { case actions.FETCH_KERNELSPECS_FULFILLED: {
const payload = ((action as unknown) as actions.FetchKernelspecsFulfilled).payload; const payload = (action as unknown as actions.FetchKernelspecsFulfilled).payload;
const defaultKernelName = payload.defaultKernelName; const defaultKernelName = payload.defaultKernelName;
this.kernelSpecsForDisplay = Object.values(payload.kernelspecs) this.kernelSpecsForDisplay = Object.values(payload.kernelspecs)
.filter((spec) => !spec.metadata?.hasOwnProperty("hidden")) .filter((spec) => !spec.metadata?.hasOwnProperty("hidden"))
.map((spec) => ({ .map((spec) => ({
name: spec.name, name: spec.name,
displayName: spec.displayName, displayName: spec.displayName,
})) }))
.sort((a: KernelSpecsDisplay, b: KernelSpecsDisplay) => { .sort((a: KernelSpecsDisplay, b: KernelSpecsDisplay) => {
// Put default at the top, otherwise lexicographically compare // Put default at the top, otherwise lexicographically compare
if (a.displayName === defaultKernelName) { if (a.displayName === defaultKernelName) {
return -1; return -1;
} else if (b.name === defaultKernelName) { } else if (b.name === defaultKernelName) {
return 1; return 1;
} else { } else {
return a.displayName.localeCompare(b.displayName); return a.displayName.localeCompare(b.displayName);
} }
}); });
break; break;
}
} }
}
return next(action); return next(action);
}; };
const traceErrorFct = (title: string, message: string) => { const traceErrorFct = (title: string, message: string) => {
TelemetryProcessor.traceFailure(Action.NotebookErrorNotification, { TelemetryProcessor.traceFailure(Action.NotebookErrorNotification, {

View File

@@ -36,7 +36,6 @@ import * as Constants from "../../../Common/Constants";
import { Areas } from "../../../Common/Constants"; import { Areas } from "../../../Common/Constants";
import { Action as TelemetryAction, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants"; import { Action as TelemetryAction, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { decryptJWTToken } from "../../../Utils/AuthorizationUtils";
import { logConsoleError, logConsoleInfo } from "../../../Utils/NotificationConsoleUtils"; import { logConsoleError, logConsoleInfo } from "../../../Utils/NotificationConsoleUtils";
import * as FileSystemUtil from "../FileSystemUtil"; import * as FileSystemUtil from "../FileSystemUtil";
import * as cdbActions from "../NotebookComponent/actions"; import * as cdbActions from "../NotebookComponent/actions";
@@ -105,11 +104,6 @@ const formWebSocketURL = (serverConfig: NotebookServiceConfig, kernelId: string,
params.append("session_id", sessionId); params.append("session_id", sessionId);
} }
const userId = getUserPuid();
if (userId) {
params.append("user_id", userId);
}
const q = params.toString(); const q = params.toString();
const suffix = q !== "" ? `?${q}` : ""; const suffix = q !== "" ? `?${q}` : "";
@@ -289,7 +283,6 @@ export const launchWebSocketKernelEpic = (
return EMPTY; return EMPTY;
} }
const serverConfig: NotebookServiceConfig = selectors.serverConfig(host); const serverConfig: NotebookServiceConfig = selectors.serverConfig(host);
serverConfig.userPuid = getUserPuid();
const { const {
payload: { kernelSpecName, cwd, kernelRef, contentRef }, payload: { kernelSpecName, cwd, kernelRef, contentRef },
@@ -766,25 +759,6 @@ const executeFocusedCellAndFocusNextEpic = (
); );
}; };
function getUserPuid(): string {
const arcadiaToken = window.dataExplorer && window.dataExplorer.arcadiaToken();
if (!arcadiaToken) {
return undefined;
}
let userPuid;
try {
const tokenPayload = decryptJWTToken(arcadiaToken);
if (tokenPayload && tokenPayload.hasOwnProperty("puid")) {
userPuid = tokenPayload.puid;
}
} catch (error) {
// ignore
}
return userPuid;
}
/** /**
* Close tab if mimetype not supported * Close tab if mimetype not supported
* @param action$ * @param action$

View File

@@ -21,16 +21,16 @@ export default function configureStore(
/** /**
* Catches errors in reducers * Catches errors in reducers
*/ */
const catchErrorMiddleware: Middleware = <D extends Dispatch<AnyAction>, S extends AppState>({ const catchErrorMiddleware: Middleware =
dispatch, <D extends Dispatch<AnyAction>, S extends AppState>({ dispatch, getState }: MiddlewareAPI<D, S>) =>
getState, (next: Dispatch<AnyAction>) =>
}: MiddlewareAPI<D, S>) => (next: Dispatch<AnyAction>) => <A extends AnyAction>(action: A): any => { <A extends AnyAction>(action: A): any => {
try { try {
next(action); next(action);
} catch (error) { } catch (error) {
traceFailure("Reducer failure", error); traceFailure("Reducer failure", error);
} }
}; };
const protect = (epic: Epic) => { const protect = (epic: Epic) => {
return (action$: Observable<any>, state$: any, dependencies: any) => return (action$: Observable<any>, state$: any, dependencies: any) =>

View File

@@ -6,7 +6,7 @@ import MonacoEditor from "@nteract/stateful-components/lib/inputs/connected-edit
import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor"; import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor";
import * as React from "react"; import * as React from "react";
import { DndProvider } from "react-dnd"; import { DndProvider } from "react-dnd";
import HTML5Backend from "react-dnd-html5-backend"; import { HTML5Backend } from "react-dnd-html5-backend";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Dispatch } from "redux"; import { Dispatch } from "redux";
import { userContext } from "../../../UserContext"; import { userContext } from "../../../UserContext";

View File

@@ -1,12 +1,11 @@
import { actions, selectors, ContentRef, AppState } from "@nteract/core";
import { CellType } from "@nteract/commutable"; import { CellType } from "@nteract/commutable";
import { actions, AppState, ContentRef, selectors } from "@nteract/core";
import AddCodeCellIcon from "images/notebook/add-code-cell.svg";
import AddTextCellIcon from "images/notebook/add-text-cell.svg";
import * as React from "react"; import * as React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Dispatch } from "redux"; import { Dispatch } from "redux";
import styled from "styled-components"; import styled from "styled-components";
import AddCodeCellIcon from "../../../../../images/notebook/add-code-cell.svg";
import AddTextCellIcon from "../../../../../images/notebook/add-text-cell.svg";
interface ComponentProps { interface ComponentProps {
id: string; id: string;

View File

@@ -11,7 +11,6 @@ import {
DropTargetConnector, DropTargetConnector,
DropTargetMonitor, DropTargetMonitor,
} from "react-dnd"; } from "react-dnd";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Dispatch } from "redux"; import { Dispatch } from "redux";
import styled, { StyledComponent } from "styled-components"; import styled, { StyledComponent } from "styled-components";
@@ -124,8 +123,9 @@ export const cellTarget = {
if (monitor) { if (monitor) {
const hoverUpperHalf = isDragUpper(props, monitor, component.el); const hoverUpperHalf = isDragUpper(props, monitor, component.el);
// DropTargetSpec monitor definition could be undefined. we'll need a check for monitor in order to pass validation. // DropTargetSpec monitor definition could be undefined. we'll need a check for monitor in order to pass validation.
const item: Props = monitor.getItem();
props.moveCell({ props.moveCell({
id: monitor.getItem().id, id: item.id,
destinationId: props.id, destinationId: props.id,
above: hoverUpperHalf, above: hoverUpperHalf,
contentRef: props.contentRef, contentRef: props.contentRef,

View File

@@ -1,12 +1,11 @@
/* eslint jsx-a11y/no-static-element-interactions: 0 */ /* eslint jsx-a11y/no-static-element-interactions: 0 */
/* eslint jsx-a11y/click-events-have-key-events: 0 */ /* eslint jsx-a11y/click-events-have-key-events: 0 */
import { actions, AppState, ContentRef, selectors } from "@nteract/core";
import React from "react"; import React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Dispatch } from "redux"; import { Dispatch } from "redux";
import { actions, selectors, ContentRef, AppState } from "@nteract/core";
interface ComponentProps { interface ComponentProps {
id: string; id: string;
contentRef: ContentRef; contentRef: ContentRef;
@@ -70,7 +69,7 @@ export class HijackScroll extends React.Component<Props> {
} }
} }
const makeMapStateToProps = (initialState: AppState, ownProps: ComponentProps) => { const makeMapStateToProps = (_initialState: AppState, ownProps: ComponentProps) => {
const mapStateToProps = (state: AppState) => { const mapStateToProps = (state: AppState) => {
const { id, contentRef } = ownProps; const { id, contentRef } = ownProps;
const model = selectors.model(state, { contentRef }); const model = selectors.model(state, { contentRef });
@@ -87,7 +86,7 @@ const makeMapStateToProps = (initialState: AppState, ownProps: ComponentProps) =
return mapStateToProps; return mapStateToProps;
}; };
const makeMapDispatchToProps = (initialDispatch: Dispatch, ownProps: ComponentProps) => { const makeMapDispatchToProps = (_initialDispatch: Dispatch, ownProps: ComponentProps) => {
const mapDispatchToProps = (dispatch: Dispatch) => ({ const mapDispatchToProps = (dispatch: Dispatch) => ({
selectCell: () => dispatch(actions.focusCell({ id: ownProps.id, contentRef: ownProps.contentRef })), selectCell: () => dispatch(actions.focusCell({ id: ownProps.id, contentRef: ownProps.contentRef })),
}); });

View File

@@ -1,11 +1,10 @@
import { CellId } from "@nteract/commutable";
import { actions, AppState, ContentRef, selectors } from "@nteract/core";
import Immutable from "immutable"; import Immutable from "immutable";
import React from "react"; import React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Dispatch } from "redux"; import { Dispatch } from "redux";
import { CellId } from "@nteract/commutable";
import { actions, AppState, ContentRef, selectors } from "@nteract/core";
interface ComponentProps { interface ComponentProps {
contentRef: ContentRef; contentRef: ContentRef;
children: React.ReactNode; children: React.ReactNode;
@@ -52,15 +51,8 @@ export class KeyboardShortcuts extends React.Component<Props> {
return; return;
} }
const { const { executeFocusedCell, focusNextCell, focusNextCellEditor, contentRef, cellOrder, focusedCell, cellMap } =
executeFocusedCell, this.props;
focusNextCell,
focusNextCellEditor,
contentRef,
cellOrder,
focusedCell,
cellMap,
} = this.props;
let ctrlKeyPressed = e.ctrlKey; let ctrlKeyPressed = e.ctrlKey;
// Allow cmd + enter (macOS) to operate like ctrl + enter // Allow cmd + enter (macOS) to operate like ctrl + enter
@@ -107,7 +99,7 @@ export class KeyboardShortcuts extends React.Component<Props> {
} }
} }
export const makeMapStateToProps = (state: AppState, ownProps: ComponentProps) => { export const makeMapStateToProps = (_state: AppState, ownProps: ComponentProps) => {
const { contentRef } = ownProps; const { contentRef } = ownProps;
const mapStateToProps = (state: AppState) => { const mapStateToProps = (state: AppState) => {
const model = selectors.model(state, { contentRef }); const model = selectors.model(state, { contentRef });

View File

@@ -501,44 +501,42 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
</TooltipHost> </TooltipHost>
</Stack> </Stack>
{this.state.uniqueKeys.map( {this.state.uniqueKeys.map((uniqueKey: string, i: number): JSX.Element => {
(uniqueKey: string, i: number): JSX.Element => { return (
return ( <Stack style={{ marginBottom: 8 }} key={`uniqueKey${i}`} horizontal>
<Stack style={{ marginBottom: 8 }} key={`uniqueKey${i}`} horizontal> <input
<input type="text"
type="text" autoComplete="off"
autoComplete="off" placeholder={
placeholder={ userContext.apiType === "Mongo"
userContext.apiType === "Mongo" ? "Comma separated paths e.g. firstName,address.zipCode"
? "Comma separated paths e.g. firstName,address.zipCode" : "Comma separated paths e.g. /firstName,/address/zipCode"
: "Comma separated paths e.g. /firstName,/address/zipCode" }
} className="panelTextField"
className="panelTextField" autoFocus
autoFocus value={uniqueKey}
value={uniqueKey} onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
onChange={(event: React.ChangeEvent<HTMLInputElement>) => { const uniqueKeys = this.state.uniqueKeys.map((uniqueKey: string, j: number) => {
const uniqueKeys = this.state.uniqueKeys.map((uniqueKey: string, j: number) => { if (i === j) {
if (i === j) { return event.target.value;
return event.target.value; }
} return uniqueKey;
return uniqueKey; });
}); this.setState({ uniqueKeys });
this.setState({ uniqueKeys }); }}
}} />
/>
<IconButton <IconButton
iconProps={{ iconName: "Delete" }} iconProps={{ iconName: "Delete" }}
style={{ height: 27 }} style={{ height: 27 }}
onClick={() => { onClick={() => {
const uniqueKeys = this.state.uniqueKeys.filter((uniqueKey, j) => i !== j); const uniqueKeys = this.state.uniqueKeys.filter((uniqueKey, j) => i !== j);
this.setState({ uniqueKeys }); this.setState({ uniqueKeys });
}} }}
/> />
</Stack> </Stack>
); );
} })}
)}
<ActionButton <ActionButton
iconProps={{ iconName: "Add" }} iconProps={{ iconName: "Add" }}
@@ -862,8 +860,6 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
case "SQL": case "SQL":
case "Mongo": case "Mongo":
return true; return true;
case "Cassandra":
return this.props.explorer.hasStorageAnalyticsAfecFeature();
default: default:
return false; return false;
} }

View File

@@ -0,0 +1,174 @@
<div data-bind="visible: visible, event: { keydown: onPaneKeyDown }">
<div class="contextual-pane-out" data-bind="click: cancel, clickBubble: false"></div>
<div class="contextual-pane" data-bind="attr: { id: id }">
<!-- Add database form -- Start -->
<div class="contextual-pane-in">
<form data-bind="submit: submit" style="height: 100%">
<div
class="paneContentContainer"
role="dialog"
aria-labelledby="databaseTitle"
data-bind="template: { name: 'add-database-inputs' }"
></div>
</form>
</div>
<!-- Add database form -- End -->
<!-- Loader - Start -->
<div class="dataExplorerLoaderContainer dataExplorerPaneLoaderContainer" data-bind="visible: isExecuting">
<img class="dataExplorerLoader" src="/images/LoadingIndicator_3Squares.gif" />
</div>
<!-- Loader - End -->
</div>
</div>
<script type="text/html" id="add-database-inputs">
<!-- Add database header - Start -->
<div class="firstdivbg headerline">
<span id="databaseTitle" role="heading" aria-level="2" data-bind="text: title"></span>
<div
class="closeImg"
role="button"
aria-label="Close pane"
data-bind="click: cancel, event: { keypress: onCloseKeyPress }"
tabindex="0"
>
<img src="/images/close-black.svg" title="Close" alt="Close" />
</div>
</div>
<!-- Add database header - End -->
<!-- Add database errors - Start -->
<div class="warningErrorContainer" aria-live="assertive" data-bind="visible: formErrors() && formErrors() !== ''">
<div class="warningErrorContent">
<span><img class="paneErrorIcon" src="/images/error_red.svg" alt="Error" /></span>
<span class="warningErrorDetailsLinkContainer">
<span class="formErrors" data-bind="text: formErrors, attr: { title: formErrors }"></span>
<a
class="errorLink"
role="link"
data-bind="visible: formErrorsDetails() && formErrorsDetails() !== '', click: showErrorDetails, event: { keypress: onMoreDetailsKeyPress }"
tabindex="0"
>
More details</a
>
</span>
</div>
</div>
<!-- Add database errors - End -->
<!-- upsell message - start -->
<div
class="infoBoxContainer"
aria-live="assertive"
data-bind="visible: showUpsellMessage && showUpsellMessage() && formErrors && !formErrors()"
>
<div class="infoBoxContent">
<span><img class="infoBoxIcon" src="/images/info_color.svg" alt="Promo" /></span>
<span class="infoBoxDetails">
<span class="infoBoxMessage" data-bind="text: upsellMessage, attr: { title: upsellMessage }"></span>
<a
class="underlinedLink"
id="linkAddDatabase"
data-bind="text: upsellAnchorText, attr: { 'href': upsellAnchorUrl, 'aria-label': upsellMessageAriaLabel }"
target="_blank"
href=""
tabindex="0"
></a>
</span>
</div>
</div>
<!-- upsell message - end -->
<!-- Add database inputs - Start -->
<div class="paneMainContent">
<div>
<p>
<span class="mandatoryStar">*</span>
<span data-bind="text: databaseIdLabel"></span>
<span class="infoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="/images/info-bubble.svg" alt="More information" />
<span class="tooltiptext infoTooltipWidth" data-bind="text: databaseIdTooltipText"></span>
</span>
</p>
<input
id="database-id"
type="text"
aria-required="true"
autocomplete="off"
pattern="[^/?#\\]*[^/?# \\]"
title="May not end with space nor contain characters '\' '/' '#' '?'"
size="40"
class="collid"
data-bind="textInput: databaseId, hasFocus: firstFieldHasFocus, attr: { 'aria-label': databaseIdLabel, 'placeholder': databaseIdPlaceHolder }"
autofocus
/>
<!-- Database provisioned throughput - Start -->
<!-- ko if: canConfigureThroughput -->
<div class="databaseProvision" aria-label="New database provision support">
<input
tabindex="0"
type="checkbox"
id="addDatabasePane-databaseSharedThroughput"
title="Provision shared throughput"
data-bind="checked: databaseCreateNewShared"
/>
<span class="databaseProvisionText" for="databaseSharedThroughput">Provision throughput</span>
<span class="infoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="/images/info-bubble.svg" alt="More information" />
<span
class="tooltiptext provisionDatabaseThroughput"
data-bind="text: databaseLevelThroughputTooltipText"
></span>
</span>
</div>
<div data-bind="visible: databaseCreateNewShared">
<throughput-input-autopilot-v3
params="{
step: 100,
value: throughput,
testId: 'sharedThroughputValue',
minimum: minThroughputRU,
maximum: maxThroughputRU,
isEnabled: databaseCreateNewShared,
label: throughputRangeText,
ariaLabel: throughputRangeText,
costsVisible: costsVisible,
requestUnitsUsageCost: requestUnitsUsageCost,
spendAckChecked: throughputSpendAck,
spendAckId: 'throughputSpendAckDatabase',
spendAckText: throughputSpendAckText,
spendAckVisible: throughputSpendAckVisible,
showAsMandatory: true,
infoBubbleText: ruToolTipText,
throughputAutoPilotRadioId: 'newDatabase-databaseThroughput-autoPilotRadio',
throughputProvisionedRadioId: 'newDatabase-databaseThroughput-manualRadio',
throughputModeRadioName: 'throughputModeRadioName',
isAutoPilotSelected: isAutoPilotSelected,
maxAutoPilotThroughputSet: maxAutoPilotThroughputSet,
autoPilotUsageCost: autoPilotUsageCost,
canExceedMaximumValue: canExceedMaximumValue,
freeTierExceedThroughputTooltip: freeTierExceedThroughputTooltip
}"
>
</throughput-input-autopilot-v3>
<p data-bind="visible: canRequestSupport">
<!-- TODO: Replace link with call to the Azure Support blade --><a
href="https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20More%20Throughput%20Request"
>Contact support</a
>
for more than <span data-bind="text: maxThroughputRUText"></span> RU/s.
</p>
</div>
<!-- /ko -->
<!-- Database provisioned throughput - End -->
</div>
</div>
<div class="paneFooter">
<div class="leftpanel-okbut">
<input type="submit" value="OK" class="btncreatecoll1" />
</div>
</div>
<!-- Add database inputs - End -->
</script>

View File

@@ -0,0 +1,273 @@
<div data-bind="visible: visible, event: { keydown: onPaneKeyDown }">
<div
class="contextual-pane-out"
data-bind="
click: cancel,
clickBubble: false"
></div>
<div class="contextual-pane" id="cassandraaddcollectionpane">
<!-- Add Cassandra collection form - Start -->
<div class="contextual-pane-in">
<form
class="paneContentContainer"
role="dialog"
aria-label="Add Table"
data-bind="
submit: submit"
>
<!-- Add Cassandra collection header - Start -->
<div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span>
<div
class="closeImg"
role="button"
aria-label="Close pane"
tabindex="0"
data-bind="
click: cancel, event: { keypress: onCloseKeyPress }"
>
<img src="/images/close-black.svg" title="Close" alt="Close" />
</div>
</div>
<!-- Add Cassandra collection header - End -->
<!-- Add Cassandra collection errors - Start -->
<div
class="warningErrorContainer"
aria-live="assertive"
data-bind="visible: formErrors() && formErrors() !== ''"
>
<div class="warningErrorContent">
<span><img class="paneErrorIcon" src="/images/error_red.svg" alt="Error" /></span>
<span class="warningErrorDetailsLinkContainer">
<span class="formErrors" data-bind="text: formErrors, attr: { title: formErrors }"></span>
</span>
</div>
</div>
<!-- Add Cassandra collection errors - End -->
<div class="paneMainContent">
<div class="seconddivpadding">
<p>
<span class="mandatoryStar">*</span> Keyspace name
<span class="infoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="/images/info-bubble.svg" alt="More information" />
<span class="tooltiptext infoTooltipWidth"
>Select an existing keyspace or enter a new keyspace id.</span
>
</span>
</p>
<div class="createNewDatabaseOrUseExisting">
<input
class="createNewDatabaseOrUseExistingRadio"
aria-label="Create new keyspace"
name="databaseType"
type="radio"
role="radio"
id="keyspaceCreateNew"
data-test="addCollection-newDatabase"
tabindex="0"
data-bind="checked: keyspaceCreateNew, checkedValue: true, attr: { 'aria-checked': keyspaceCreateNew() ? 'true' : 'false' }"
/>
<span class="createNewDatabaseOrUseExistingSpace" for="keyspaceCreateNew">Create new</span>
<input
class="createNewDatabaseOrUseExistingRadio"
aria-label="Use existing keyspace"
name="databaseType"
type="radio"
role="radio"
id="keyspaceUseExisting"
data-test="addCollection-existingDatabase"
tabindex="0"
data-bind="checked: keyspaceCreateNew, checkedValue: false, attr: { 'aria-checked': !keyspaceCreateNew() ? 'true' : 'false' }"
/>
<span class="createNewDatabaseOrUseExistingSpace" for="keyspaceUseExisting">Use existing</span>
</div>
<input
id="keyspace-id"
data-test="addCollection-keyspaceId"
type="text"
autocomplete="off"
pattern="[^/?#\\]*[^/?# \\]"
title="May not end with space nor contain characters '\' '/' '#' '?'"
placeholder="Type a new keyspace id"
size="40"
class="collid"
data-bind="visible: keyspaceCreateNew, textInput: keyspaceId, hasFocus: firstFieldHasFocus"
aria-label="Keyspace id"
aria-required="true"
autofocus
/>
<input
type="text"
aria-required="true"
autocomplete="off"
pattern="[^/?#\\]*[^/?# \\]"
title="May not end with space nor contain characters '\' '/' '#' '?'"
list="keyspacesList"
placeholder="Choose existing keyspace id"
size="40"
class="collid"
data-bind="visible: !keyspaceCreateNew(), textInput: keyspaceId, hasFocus: firstFieldHasFocus"
aria-label="Keyspace id"
/>
<datalist id="keyspacesList" data-bind="foreach: container.databases">
<option data-bind="value: $data.id"></option>
</datalist>
<!-- Database provisioned throughput - Start -->
<!-- ko if: canConfigureThroughput -->
<div
class="databaseProvision"
aria-label="New database provision support"
data-bind="visible: keyspaceCreateNew"
>
<input
tabindex="0"
type="checkbox"
id="keyspaceSharedThroughput"
title="Provision shared throughput"
data-bind="checked: keyspaceHasSharedOffer"
/>
<span class="databaseProvisionText" for="keyspaceSharedThroughput">Provision keyspace throughput</span>
<span class="infoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="/images/info-bubble.svg" alt="More information" />
<span class="tooltiptext provisionDatabaseThroughput"
>Provisioned throughput at the keyspace level will be shared across unlimited number of tables within
the keyspace</span
>
</span>
</div>
<!-- 1 -->
<div data-bind="visible: keyspaceCreateNew() && keyspaceHasSharedOffer()">
<throughput-input-autopilot-v3
params="{
testId: 'cassandraThroughputValue-v3-shared',
value: keyspaceThroughput,
minimum: minThroughputRU,
maximum: maxThroughputRU,
isEnabled: keyspaceCreateNew() && keyspaceHasSharedOffer(),
label: sharedThroughputRangeText,
ariaLabel: sharedThroughputRangeText,
requestUnitsUsageCost: requestUnitsUsageCostShared,
spendAckChecked: sharedThroughputSpendAck,
spendAckId: 'sharedThroughputSpendAck-v3-shared',
spendAckText: sharedThroughputSpendAckText,
spendAckVisible: sharedThroughputSpendAckVisible,
showAsMandatory: true,
infoBubbleText: ruToolTipText,
throughputAutoPilotRadioId: 'newKeyspace-databaseThroughput-autoPilotRadio-v3-shared',
throughputProvisionedRadioId: 'newKeyspace-databaseThroughput-manualRadio-v3-shared',
isAutoPilotSelected: isSharedAutoPilotSelected,
maxAutoPilotThroughputSet: sharedAutoPilotThroughput,
autoPilotUsageCost: autoPilotUsageCost,
canExceedMaximumValue: canExceedMaximumValue,
costsVisible: costsVisible,
}"
>
</throughput-input-autopilot-v3>
</div>
<!-- /ko -->
<!-- Database provisioned throughput - End -->
</div>
<div class="seconddivpadding">
<p>
<span class="mandatoryStar">*</span> Enter CQL command to create the table.
<a href="https://aka.ms/cassandra-create-table" target="_blank">Learn More</a>
</p>
<div data-bind="text: createTableQuery" style="float: left; padding-top: 3px; padding-right: 3px"></div>
<input
type="text"
data-test="addCollection-tableId"
aria-required="true"
autocomplete="off"
pattern="[^/?#\\]*[^/?# \\]"
title="May not end with space nor contain characters '\' '/' '#' '?'"
data-test="addCollection-tableId"
placeholder="Enter tableId"
size="20"
class="textfontclr"
data-bind="value: tableId"
style="margin-bottom: 5px"
/>
<textarea
id="editor-area"
rows="15"
aria-label="Table Schema"
data-bind="value: userTableQuery"
style="height: 125px; width: calc(100% - 80px); resize: vertical"
></textarea>
</div>
<!-- Provision table throughput - start -->
<!-- ko if: canConfigureThroughput -->
<div class="seconddivpadding" data-bind="visible: keyspaceHasSharedOffer() && !keyspaceCreateNew()">
<input
type="checkbox"
id="tableSharedThroughput"
title="Provision dedicated throughput for this table"
data-bind="checked: dedicateTableThroughput"
/>
<span for="tableSharedThroughput">Provision dedicated throughput for this table</span>
<span class="leftAlignInfoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="/images/info-bubble.svg" alt="More information" />
<span class="tooltiptext sharedCollectionThroughputTooltipWidth"
>You can optionally provision dedicated throughput for a table within a keyspace that has throughput
provisioned. This dedicated throughput amount will not be shared with other tables in the keyspace and
does not count towards the throughput you provisioned for the keyspace. This throughput amount will be
billed in addition to the throughput amount you provisioned at the keyspace level.</span
>
</span>
</div>
<!-- 2 -->
<div data-bind="visible: !keyspaceHasSharedOffer() || dedicateTableThroughput()">
<throughput-input-autopilot-v3
params="{
testId: 'cassandraSharedThroughputValue-v3-dedicated',
value: throughput,
minimum: minThroughputRU,
maximum: maxThroughputRU,
isEnabled: !keyspaceHasSharedOffer() || dedicateTableThroughput(),
label: throughputRangeText,
ariaLabel: throughputRangeText,
costsVisible: costsVisible,
requestUnitsUsageCost: requestUnitsUsageCostDedicated,
spendAckChecked: throughputSpendAck,
spendAckId: 'throughputSpendAckCassandra-v3-dedicated',
spendAckText: throughputSpendAckText,
spendAckVisible: throughputSpendAckVisible,
showAsMandatory: true,
infoBubbleText: ruToolTipText,
throughputAutoPilotRadioId: 'newKeyspace-containerThroughput-autoPilotRadio-v3-dedicated',
throughputProvisionedRadioId: 'newKeyspace-containerThroughput-manualRadio-v3-dedicated',
isAutoPilotSelected: isAutoPilotSelected,
maxAutoPilotThroughputSet: selectedAutoPilotThroughput,
autoPilotUsageCost: autoPilotUsageCost,
canExceedMaximumValue: canExceedMaximumValue,
overrideWithAutoPilotSettings: false,
overrideWithProvisionedThroughputSettings: false
}"
>
</throughput-input-autopilot-v3>
</div>
<!-- /ko -->
<!-- Provision table throughput - end -->
</div>
<div class="paneFooter">
<div class="leftpanel-okbut">
<input type="submit" data-test="addCollection-createCollection" value="OK" class="btncreatecoll1" />
</div>
</div>
</form>
<!-- Add Cassandra collection form - End -->
<!-- Loader - Start -->
<div class="dataExplorerLoaderContainer dataExplorerPaneLoaderContainer" data-bind="visible: isExecuting">
<img class="dataExplorerLoader" src="/images/LoadingIndicator_3Squares.gif" alt="loading indicator" />
</div>
<!-- Loader - End -->
</div>
</div>
</div>

View File

@@ -25,7 +25,7 @@ describe("CassandraAddCollectionPane Pane", () => {
fireEvent.click(screen.getByLabelText("Use existing keyspace")); fireEvent.click(screen.getByLabelText("Use existing keyspace"));
}); });
it("Enter Keyspace name ", () => { it("Enter Keyspace name", () => {
fireEvent.change(screen.getByLabelText("Keyspace id"), { target: { value: "unittest1" } }); fireEvent.change(screen.getByLabelText("Keyspace id"), { target: { value: "unittest1" } });
expect(screen.getByLabelText("CREATE TABLE unittest1.")).toBeDefined(); expect(screen.getByLabelText("CREATE TABLE unittest1.")).toBeDefined();
}); });

View File

@@ -28,12 +28,12 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
closePanel, closePanel,
cassandraApiClient, cassandraApiClient,
}: CassandraAddCollectionPaneProps) => { }: CassandraAddCollectionPaneProps) => {
const throughputDefaults = container.collectionCreationDefaults.throughput; const throughputDefaults = userContext.collectionCreationDefaults.throughput;
const [createTableQuery, setCreateTableQuery] = useState<string>("CREATE TABLE "); const [createTableQuery, setCreateTableQuery] = useState<string>("CREATE TABLE ");
const [keyspaceId, setKeyspaceId] = useState<string>(""); const [keyspaceId, setKeyspaceId] = useState<string>("");
const [tableId, setTableId] = useState<string>(""); const [tableId, setTableId] = useState<string>("");
const [throughput, setThroughput] = useState<number>( const [throughput, setThroughput] = useState<number>(
AddCollectionUtility.getMaxThroughput(container.collectionCreationDefaults, container) AddCollectionUtility.getMaxThroughput(userContext.collectionCreationDefaults, container)
); );
const [isAutoPilotSelected, setIsAutoPilotSelected] = useState<boolean>(userContext.features.autoscaleDefault); const [isAutoPilotSelected, setIsAutoPilotSelected] = useState<boolean>(userContext.features.autoscaleDefault);

View File

@@ -1,7 +1,7 @@
import { IDropdownOption, IImageProps, Image, Stack, Text } from "@fluentui/react"; import { IDropdownOption, IImageProps, Image, Stack, Text } from "@fluentui/react";
import { useBoolean } from "@fluentui/react-hooks"; import { useBoolean } from "@fluentui/react-hooks";
import AddPropertyIcon from "images/Add-property.svg";
import React, { FunctionComponent, useState } from "react"; import React, { FunctionComponent, useState } from "react";
import AddPropertyIcon from "../../../../images/Add-property.svg";
import { logConsoleError } from "../../../Utils/NotificationConsoleUtils"; import { logConsoleError } from "../../../Utils/NotificationConsoleUtils";
import StoredProcedure from "../../Tree/StoredProcedure"; import StoredProcedure from "../../Tree/StoredProcedure";
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm"; import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";

View File

@@ -1,3 +1,5 @@
import AddPropertyIcon from "images/Add-property.svg";
import EntityCancelIcon from "images/Entity_cancel.svg";
import { import {
Dropdown, Dropdown,
IDropdownOption, IDropdownOption,
@@ -9,8 +11,6 @@ import {
TextField, TextField,
} from "@fluentui/react"; } from "@fluentui/react";
import React, { FunctionComponent } from "react"; import React, { FunctionComponent } from "react";
import AddPropertyIcon from "../../../../images/Add-property.svg";
import EntityCancelIcon from "../../../../images/Entity_cancel.svg";
const dropdownStyles: Partial<IDropdownStyles> = { dropdown: { width: 100 } }; const dropdownStyles: Partial<IDropdownStyles> = { dropdown: { width: 100 } };
const options = [ const options = [

View File

@@ -0,0 +1,126 @@
import { IconButton, PrimaryButton } from "@fluentui/react";
import ErrorRedIcon from "images/error_red.svg";
import LoadingIndicatorIcon from "images/LoadingIndicator_3Squares.gif";
import React, { FunctionComponent, ReactNode } from "react";
import { KeyCodes } from "../../../Common/Constants";
export interface GenericRightPaneProps {
expandConsole: () => void;
formError: string;
formErrorDetail: string;
id: string;
isExecuting: boolean;
onClose: () => void;
onSubmit: () => void;
submitButtonText: string;
title: string;
isSubmitButtonHidden?: boolean;
children?: ReactNode;
}
export const GenericRightPaneComponent: FunctionComponent<GenericRightPaneProps> = ({
expandConsole,
formError,
formErrorDetail,
id,
isExecuting,
onClose,
onSubmit,
submitButtonText,
title,
isSubmitButtonHidden,
children,
}: GenericRightPaneProps) => {
const getPanelHeight = (): number => {
const notificationConsoleElement: HTMLElement = document.getElementById("explorerNotificationConsole");
return window.innerHeight - $(notificationConsoleElement).height();
};
const panelHeight: number = getPanelHeight();
const renderPanelHeader = (): JSX.Element => {
return (
<div className="firstdivbg headerline">
<span id="databaseTitle" role="heading" aria-level={2}>
{title}
</span>
<IconButton
ariaLabel="Close pane"
title="Close pane"
onClick={onClose}
tabIndex={0}
className="closePaneBtn"
iconProps={{ iconName: "Cancel" }}
/>
</div>
);
};
const renderErrorSection = (): JSX.Element => {
return (
<div className="warningErrorContainer" aria-live="assertive" hidden={!formError}>
<div className="warningErrorContent">
<span>
<img className="paneErrorIcon" src={ErrorRedIcon} alt="Error" />
</span>
<span className="warningErrorDetailsLinkContainer">
<span className="formErrors" title={formError}>
{formError}
</span>
<a className="errorLink" role="link" hidden={!formErrorDetail} onClick={expandConsole}>
More details
</a>
</span>
</div>
</div>
);
};
const renderPanelFooter = (): JSX.Element => {
return (
<div className="paneFooter">
<div className="leftpanel-okbut">
<PrimaryButton
style={{ visibility: isSubmitButtonHidden ? "hidden" : "visible" }}
ariaLabel="Submit"
title="Submit"
onClick={onSubmit}
tabIndex={0}
className="genericPaneSubmitBtn"
text={submitButtonText}
/>
</div>
</div>
);
};
const renderLoadingScreen = (): JSX.Element => {
return (
<div className="dataExplorerLoaderContainer dataExplorerPaneLoaderContainer" hidden={!isExecuting}>
<img className="dataExplorerLoader" src={LoadingIndicatorIcon} />
</div>
);
};
const onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>): void => {
if (event.keyCode === KeyCodes.Escape) {
onClose();
event.stopPropagation();
}
};
return (
<div tabIndex={-1} onKeyDown={onKeyDown}>
<div className="contextual-pane-out" onClick={onClose}></div>
<div className="contextual-pane" id={id} style={{ height: panelHeight }} onKeyDown={onKeyDown}>
<div className="panelContentWrapper">
{renderPanelHeader()}
{renderErrorSection()}
{children}
{renderPanelFooter()}
</div>
{renderLoadingScreen()}
</div>
</div>
);
};

View File

@@ -17,28 +17,11 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
addRepoProps={ addRepoProps={
Object { Object {
"container": Explorer { "container": Explorer {
"_isAfecFeatureRegistered": [Function],
"_isInitializingNotebooks": false, "_isInitializingNotebooks": false,
"_refreshSparkEnabledStateForAccount": [Function],
"_resetNotebookWorkspace": [Function], "_resetNotebookWorkspace": [Function],
"addCollectionText": [Function],
"arcadiaToken": [Function],
"canExceedMaximumValue": [Function],
"canSaveQueries": [Function], "canSaveQueries": [Function],
"closeSidePanel": undefined, "closeSidePanel": undefined,
"collapsedResourceTreeWidth": 36, "collapsedResourceTreeWidth": 36,
"collectionCreationDefaults": Object {
"storage": "100",
"throughput": Object {
"fixed": 400,
"shared": 400,
"unlimited": 400,
"unlimitedmax": 1000000,
"unlimitedmin": 400,
},
},
"collectionTitle": [Function],
"collectionTreeNodeAltText": [Function],
"commandBarComponentAdapter": CommandBarComponentAdapter { "commandBarComponentAdapter": CommandBarComponentAdapter {
"container": [Circular], "container": [Circular],
"isNotebookTabActive": [Function], "isNotebookTabActive": [Function],
@@ -46,799 +29,20 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
"tabsButtons": Array [], "tabsButtons": Array [],
}, },
"databases": [Function], "databases": [Function],
"deleteCollectionText": [Function],
"deleteDatabaseText": [Function],
"gitHubClient": GitHubClient {
"errorCallback": [Function],
"ocktokit": OctokitWithDefaults {
"actions": Object {
"addSelectedRepoToOrgSecret": [Function],
"cancelWorkflowRun": [Function],
"createOrUpdateOrgSecret": [Function],
"createOrUpdateRepoSecret": [Function],
"createOrUpdateSecretForRepo": [Function],
"createRegistrationToken": [Function],
"createRegistrationTokenForOrg": [Function],
"createRegistrationTokenForRepo": [Function],
"createRemoveToken": [Function],
"createRemoveTokenForOrg": [Function],
"createRemoveTokenForRepo": [Function],
"deleteArtifact": [Function],
"deleteOrgSecret": [Function],
"deleteRepoSecret": [Function],
"deleteSecretFromRepo": [Function],
"deleteSelfHostedRunnerFromOrg": [Function],
"deleteSelfHostedRunnerFromRepo": [Function],
"deleteWorkflowRunLogs": [Function],
"downloadArtifact": [Function],
"downloadJobLogsForWorkflowRun": [Function],
"downloadWorkflowJobLogs": [Function],
"downloadWorkflowRunLogs": [Function],
"getArtifact": [Function],
"getJobForWorkflowRun": [Function],
"getOrgPublicKey": [Function],
"getOrgSecret": [Function],
"getPublicKey": [Function],
"getRepoPublicKey": [Function],
"getRepoSecret": [Function],
"getSecret": [Function],
"getSelfHostedRunner": [Function],
"getSelfHostedRunnerForOrg": [Function],
"getSelfHostedRunnerForRepo": [Function],
"getWorkflow": [Function],
"getWorkflowJob": [Function],
"getWorkflowRun": [Function],
"getWorkflowRunUsage": [Function],
"getWorkflowUsage": [Function],
"listArtifactsForRepo": [Function],
"listDownloadsForSelfHostedRunnerApplication": [Function],
"listJobsForWorkflowRun": [Function],
"listOrgSecrets": [Function],
"listRepoSecrets": [Function],
"listRepoWorkflowRuns": [Function],
"listRepoWorkflows": [Function],
"listRunnerApplicationsForOrg": [Function],
"listRunnerApplicationsForRepo": [Function],
"listSecretsForRepo": [Function],
"listSelectedReposForOrgSecret": [Function],
"listSelfHostedRunnersForOrg": [Function],
"listSelfHostedRunnersForRepo": [Function],
"listWorkflowJobLogs": [Function],
"listWorkflowRunArtifacts": [Function],
"listWorkflowRunLogs": [Function],
"listWorkflowRuns": [Function],
"listWorkflowRunsForRepo": [Function],
"reRunWorkflow": [Function],
"removeSelectedRepoFromOrgSecret": [Function],
"removeSelfHostedRunner": [Function],
"setSelectedReposForOrgSecret": [Function],
},
"activity": Object {
"checkRepoIsStarredByAuthenticatedUser": [Function],
"checkStarringRepo": [Function],
"deleteRepoSubscription": [Function],
"deleteThreadSubscription": [Function],
"getFeeds": [Function],
"getRepoSubscription": [Function],
"getThread": [Function],
"getThreadSubscription": [Function],
"getThreadSubscriptionForAuthenticatedUser": [Function],
"listEventsForAuthenticatedUser": [Function],
"listEventsForOrg": [Function],
"listEventsForUser": [Function],
"listFeeds": [Function],
"listNotifications": [Function],
"listNotificationsForAuthenticatedUser": [Function],
"listNotificationsForRepo": [Function],
"listOrgEventsForAuthenticatedUser": [Function],
"listPublicEvents": [Function],
"listPublicEventsForOrg": [Function],
"listPublicEventsForRepoNetwork": [Function],
"listPublicEventsForUser": [Function],
"listPublicOrgEvents": [Function],
"listReceivedEventsForUser": [Function],
"listReceivedPublicEventsForUser": [Function],
"listRepoEvents": [Function],
"listRepoNotificationsForAuthenticatedUser": [Function],
"listReposStarredByAuthenticatedUser": [Function],
"listReposStarredByUser": [Function],
"listReposWatchedByUser": [Function],
"listStargazersForRepo": [Function],
"listWatchedReposForAuthenticatedUser": [Function],
"listWatchersForRepo": [Function],
"markAsRead": [Function],
"markNotificationsAsRead": [Function],
"markNotificationsAsReadForRepo": [Function],
"markRepoNotificationsAsRead": [Function],
"markThreadAsRead": [Function],
"setRepoSubscription": [Function],
"setThreadSubscription": [Function],
"starRepo": [Function],
"starRepoForAuthenticatedUser": [Function],
"unstarRepo": [Function],
"unstarRepoForAuthenticatedUser": [Function],
},
"apps": Object {
"addRepoToInstallation": [Function],
"checkAccountIsAssociatedWithAny": [Function],
"checkAccountIsAssociatedWithAnyStubbed": [Function],
"checkToken": [Function],
"createContentAttachment": [Function],
"createFromManifest": [Function],
"createInstallationAccessToken": [Function],
"createInstallationToken": [Function],
"deleteAuthorization": [Function],
"deleteInstallation": [Function],
"deleteToken": [Function],
"getAuthenticated": [Function],
"getBySlug": [Function],
"getInstallation": [Function],
"getOrgInstallation": [Function],
"getRepoInstallation": [Function],
"getSubscriptionPlanForAccount": [Function],
"getSubscriptionPlanForAccountStubbed": [Function],
"getUserInstallation": [Function],
"listAccountsForPlan": [Function],
"listAccountsForPlanStubbed": [Function],
"listAccountsUserOrOrgOnPlan": [Function],
"listAccountsUserOrOrgOnPlanStubbed": [Function],
"listInstallationReposForAuthenticatedUser": [Function],
"listInstallations": [Function],
"listInstallationsForAuthenticatedUser": [Function],
"listMarketplacePurchasesForAuthenticatedUser": [Function],
"listMarketplacePurchasesForAuthenticatedUserStubbed": [Function],
"listPlans": [Function],
"listPlansStubbed": [Function],
"listRepos": [Function],
"listReposAccessibleToInstallation": [Function],
"listSubscriptionsForAuthenticatedUser": [Function],
"listSubscriptionsForAuthenticatedUserStubbed": [Function],
"removeRepoFromInstallation": [Function],
"resetToken": [Function],
"revokeInstallationAccessToken": [Function],
"revokeInstallationToken": [Function],
"suspendInstallation": [Function],
"unsuspendInstallation": [Function],
},
"auth": [Function],
"checks": Object {
"create": [Function],
"createSuite": [Function],
"get": [Function],
"getSuite": [Function],
"listAnnotations": [Function],
"listForRef": [Function],
"listForSuite": [Function],
"listSuitesForRef": [Function],
"rerequestSuite": [Function],
"setSuitesPreferences": [Function],
"update": [Function],
},
"codeScanning": Object {
"getAlert": [Function],
"listAlertsForRepo": [Function],
},
"codesOfConduct": Object {
"getAllCodesOfConduct": [Function],
"getConductCode": [Function],
"getForRepo": [Function],
"listConductCodes": [Function],
},
"emojis": Object {
"get": [Function],
},
"gists": Object {
"checkIsStarred": [Function],
"create": [Function],
"createComment": [Function],
"delete": [Function],
"deleteComment": [Function],
"fork": [Function],
"get": [Function],
"getComment": [Function],
"getRevision": [Function],
"list": [Function],
"listComments": [Function],
"listCommits": [Function],
"listForUser": [Function],
"listForks": [Function],
"listPublic": [Function],
"listPublicForUser": [Function],
"listStarred": [Function],
"star": [Function],
"unstar": [Function],
"update": [Function],
"updateComment": [Function],
},
"git": Object {
"createBlob": [Function],
"createCommit": [Function],
"createRef": [Function],
"createTag": [Function],
"createTree": [Function],
"deleteRef": [Function],
"getBlob": [Function],
"getCommit": [Function],
"getRef": [Function],
"getTag": [Function],
"getTree": [Function],
"listMatchingRefs": [Function],
"updateRef": [Function],
},
"gitignore": Object {
"getAllTemplates": [Function],
"getTemplate": [Function],
"listTemplates": [Function],
},
"graphql": [Function],
"hook": [Function],
"interactions": Object {
"addOrUpdateRestrictionsForOrg": [Function],
"addOrUpdateRestrictionsForRepo": [Function],
"getRestrictionsForOrg": [Function],
"getRestrictionsForRepo": [Function],
"removeRestrictionsForOrg": [Function],
"removeRestrictionsForRepo": [Function],
"setRestrictionsForOrg": [Function],
"setRestrictionsForRepo": [Function],
},
"issues": Object {
"addAssignees": [Function],
"addLabels": [Function],
"checkAssignee": [Function],
"checkUserCanBeAssigned": [Function],
"create": [Function],
"createComment": [Function],
"createLabel": [Function],
"createMilestone": [Function],
"deleteComment": [Function],
"deleteLabel": [Function],
"deleteMilestone": [Function],
"get": [Function],
"getComment": [Function],
"getEvent": [Function],
"getLabel": [Function],
"getMilestone": [Function],
"list": [Function],
"listAssignees": [Function],
"listComments": [Function],
"listCommentsForRepo": [Function],
"listEvents": [Function],
"listEventsForRepo": [Function],
"listEventsForTimeline": [Function],
"listForAuthenticatedUser": [Function],
"listForOrg": [Function],
"listForRepo": [Function],
"listLabelsForMilestone": [Function],
"listLabelsForRepo": [Function],
"listLabelsOnIssue": [Function],
"listMilestones": [Function],
"listMilestonesForRepo": [Function],
"lock": [Function],
"removeAllLabels": [Function],
"removeAssignees": [Function],
"removeLabel": [Function],
"removeLabels": [Function],
"replaceAllLabels": [Function],
"replaceLabels": [Function],
"setLabels": [Function],
"unlock": [Function],
"update": [Function],
"updateComment": [Function],
"updateLabel": [Function],
"updateMilestone": [Function],
},
"licenses": Object {
"get": [Function],
"getAllCommonlyUsed": [Function],
"getForRepo": [Function],
"listCommonlyUsed": [Function],
},
"log": Object {
"debug": [Function],
"error": [Function],
"info": [Function],
"warn": [Function],
},
"markdown": Object {
"render": [Function],
"renderRaw": [Function],
},
"meta": Object {
"get": [Function],
},
"migrations": Object {
"cancelImport": [Function],
"deleteArchiveForAuthenticatedUser": [Function],
"deleteArchiveForOrg": [Function],
"downloadArchiveForOrg": [Function],
"getArchiveForAuthenticatedUser": [Function],
"getCommitAuthors": [Function],
"getImportProgress": [Function],
"getImportStatus": [Function],
"getLargeFiles": [Function],
"getStatusForAuthenticatedUser": [Function],
"getStatusForOrg": [Function],
"listForAuthenticatedUser": [Function],
"listForOrg": [Function],
"listReposForOrg": [Function],
"listReposForUser": [Function],
"mapCommitAuthor": [Function],
"setLfsPreference": [Function],
"startForAuthenticatedUser": [Function],
"startForOrg": [Function],
"startImport": [Function],
"unlockRepoForAuthenticatedUser": [Function],
"unlockRepoForOrg": [Function],
"updateImport": [Function],
},
"orgs": Object {
"addOrUpdateMembership": [Function],
"blockUser": [Function],
"checkBlockedUser": [Function],
"checkMembership": [Function],
"checkMembershipForUser": [Function],
"checkPublicMembership": [Function],
"checkPublicMembershipForUser": [Function],
"concealMembership": [Function],
"convertMemberToOutsideCollaborator": [Function],
"createHook": [Function],
"createInvitation": [Function],
"createWebhook": [Function],
"deleteHook": [Function],
"deleteWebhook": [Function],
"get": [Function],
"getHook": [Function],
"getMembership": [Function],
"getMembershipForAuthenticatedUser": [Function],
"getMembershipForUser": [Function],
"getWebhook": [Function],
"list": [Function],
"listAppInstallations": [Function],
"listBlockedUsers": [Function],
"listForAuthenticatedUser": [Function],
"listForUser": [Function],
"listHooks": [Function],
"listInstallations": [Function],
"listInvitationTeams": [Function],
"listMembers": [Function],
"listMemberships": [Function],
"listMembershipsForAuthenticatedUser": [Function],
"listOutsideCollaborators": [Function],
"listPendingInvitations": [Function],
"listPublicMembers": [Function],
"listWebhooks": [Function],
"pingHook": [Function],
"pingWebhook": [Function],
"publicizeMembership": [Function],
"removeMember": [Function],
"removeMembership": [Function],
"removeMembershipForUser": [Function],
"removeOutsideCollaborator": [Function],
"removePublicMembershipForAuthenticatedUser": [Function],
"setMembershipForUser": [Function],
"setPublicMembershipForAuthenticatedUser": [Function],
"unblockUser": [Function],
"update": [Function],
"updateHook": [Function],
"updateMembership": [Function],
"updateMembershipForAuthenticatedUser": [Function],
"updateWebhook": [Function],
},
"paginate": [Function],
"projects": Object {
"addCollaborator": [Function],
"createCard": [Function],
"createColumn": [Function],
"createForAuthenticatedUser": [Function],
"createForOrg": [Function],
"createForRepo": [Function],
"delete": [Function],
"deleteCard": [Function],
"deleteColumn": [Function],
"get": [Function],
"getCard": [Function],
"getColumn": [Function],
"getPermissionForUser": [Function],
"listCards": [Function],
"listCollaborators": [Function],
"listColumns": [Function],
"listForOrg": [Function],
"listForRepo": [Function],
"listForUser": [Function],
"moveCard": [Function],
"moveColumn": [Function],
"removeCollaborator": [Function],
"reviewUserPermissionLevel": [Function],
"update": [Function],
"updateCard": [Function],
"updateColumn": [Function],
},
"pulls": Object {
"checkIfMerged": [Function],
"create": [Function],
"createComment": [Function],
"createReplyForReviewComment": [Function],
"createReview": [Function],
"createReviewComment": [Function],
"createReviewCommentReply": [Function],
"createReviewRequest": [Function],
"deleteComment": [Function],
"deletePendingReview": [Function],
"deleteReviewComment": [Function],
"deleteReviewRequest": [Function],
"dismissReview": [Function],
"get": [Function],
"getComment": [Function],
"getCommentsForReview": [Function],
"getReview": [Function],
"getReviewComment": [Function],
"list": [Function],
"listComments": [Function],
"listCommentsForRepo": [Function],
"listCommentsForReview": [Function],
"listCommits": [Function],
"listFiles": [Function],
"listRequestedReviewers": [Function],
"listReviewComments": [Function],
"listReviewCommentsForRepo": [Function],
"listReviewRequests": [Function],
"listReviews": [Function],
"merge": [Function],
"removeRequestedReviewers": [Function],
"requestReviewers": [Function],
"submitReview": [Function],
"update": [Function],
"updateBranch": [Function],
"updateComment": [Function],
"updateReview": [Function],
"updateReviewComment": [Function],
},
"rateLimit": Object {
"get": [Function],
},
"reactions": Object {
"createForCommitComment": [Function],
"createForIssue": [Function],
"createForIssueComment": [Function],
"createForPullRequestReviewComment": [Function],
"createForTeamDiscussionCommentInOrg": [Function],
"createForTeamDiscussionInOrg": [Function],
"delete": [Function],
"deleteForCommitComment": [Function],
"deleteForIssue": [Function],
"deleteForIssueComment": [Function],
"deleteForPullRequestComment": [Function],
"deleteForTeamDiscussion": [Function],
"deleteForTeamDiscussionComment": [Function],
"deleteLegacy": [Function],
"listForCommitComment": [Function],
"listForIssue": [Function],
"listForIssueComment": [Function],
"listForPullRequestReviewComment": [Function],
"listForTeamDiscussionCommentInOrg": [Function],
"listForTeamDiscussionInOrg": [Function],
},
"repos": Object {
"acceptInvitation": [Function],
"addAppAccessRestrictions": [Function],
"addCollaborator": [Function],
"addDeployKey": [Function],
"addProtectedBranchAdminEnforcement": [Function],
"addProtectedBranchAppRestrictions": [Function],
"addProtectedBranchRequiredSignatures": [Function],
"addProtectedBranchRequiredStatusChecksContexts": [Function],
"addProtectedBranchTeamRestrictions": [Function],
"addProtectedBranchUserRestrictions": [Function],
"addStatusCheckContexts": [Function],
"addTeamAccessRestrictions": [Function],
"addUserAccessRestrictions": [Function],
"checkCollaborator": [Function],
"checkVulnerabilityAlerts": [Function],
"compareCommits": [Function],
"createCommitComment": [Function],
"createCommitSignatureProtection": [Function],
"createCommitStatus": [Function],
"createDeployKey": [Function],
"createDeployment": [Function],
"createDeploymentStatus": [Function],
"createDispatchEvent": [Function],
"createForAuthenticatedUser": [Function],
"createFork": [Function],
"createHook": [Function],
"createInOrg": [Function],
"createOrUpdateFile": [Function],
"createOrUpdateFileContents": [Function],
"createPagesSite": [Function],
"createRelease": [Function],
"createStatus": [Function],
"createUsingTemplate": [Function],
"createWebhook": [Function],
"declineInvitation": [Function],
"delete": [Function],
"deleteAccessRestrictions": [Function],
"deleteAdminBranchProtection": [Function],
"deleteBranchProtection": [Function],
"deleteCommitComment": [Function],
"deleteCommitSignatureProtection": [Function],
"deleteDeployKey": [Function],
"deleteDeployment": [Function],
"deleteDownload": [Function],
"deleteFile": [Function],
"deleteHook": [Function],
"deleteInvitation": [Function],
"deletePagesSite": [Function],
"deletePullRequestReviewProtection": [Function],
"deleteRelease": [Function],
"deleteReleaseAsset": [Function],
"deleteWebhook": [Function],
"disableAutomatedSecurityFixes": [Function],
"disablePagesSite": [Function],
"disableVulnerabilityAlerts": [Function],
"downloadArchive": [Function],
"enableAutomatedSecurityFixes": [Function],
"enablePagesSite": [Function],
"enableVulnerabilityAlerts": [Function],
"get": [Function],
"getAccessRestrictions": [Function],
"getAdminBranchProtection": [Function],
"getAllStatusCheckContexts": [Function],
"getAllTopics": [Function],
"getAppsWithAccessToProtectedBranch": [Function],
"getArchiveLink": [Function],
"getBranch": [Function],
"getBranchProtection": [Function],
"getClones": [Function],
"getCodeFrequencyStats": [Function],
"getCollaboratorPermissionLevel": [Function],
"getCombinedStatusForRef": [Function],
"getCommit": [Function],
"getCommitActivityStats": [Function],
"getCommitComment": [Function],
"getCommitSignatureProtection": [Function],
"getCommunityProfileMetrics": [Function],
"getContent": [Function],
"getContents": [Function],
"getContributorsStats": [Function],
"getDeployKey": [Function],
"getDeployment": [Function],
"getDeploymentStatus": [Function],
"getDownload": [Function],
"getHook": [Function],
"getLatestPagesBuild": [Function],
"getLatestRelease": [Function],
"getPages": [Function],
"getPagesBuild": [Function],
"getParticipationStats": [Function],
"getProtectedBranchAdminEnforcement": [Function],
"getProtectedBranchPullRequestReviewEnforcement": [Function],
"getProtectedBranchRequiredSignatures": [Function],
"getProtectedBranchRequiredStatusChecks": [Function],
"getProtectedBranchRestrictions": [Function],
"getPullRequestReviewProtection": [Function],
"getPunchCardStats": [Function],
"getReadme": [Function],
"getRelease": [Function],
"getReleaseAsset": [Function],
"getReleaseByTag": [Function],
"getStatusChecksProtection": [Function],
"getTeamsWithAccessToProtectedBranch": [Function],
"getTopPaths": [Function],
"getTopReferrers": [Function],
"getUsersWithAccessToProtectedBranch": [Function],
"getViews": [Function],
"getWebhook": [Function],
"list": [Function],
"listAssetsForRelease": [Function],
"listBranches": [Function],
"listBranchesForHeadCommit": [Function],
"listCollaborators": [Function],
"listCommentsForCommit": [Function],
"listCommitComments": [Function],
"listCommitCommentsForRepo": [Function],
"listCommitStatusesForRef": [Function],
"listCommits": [Function],
"listContributors": [Function],
"listDeployKeys": [Function],
"listDeploymentStatuses": [Function],
"listDeployments": [Function],
"listDownloads": [Function],
"listForAuthenticatedUser": [Function],
"listForOrg": [Function],
"listForUser": [Function],
"listForks": [Function],
"listHooks": [Function],
"listInvitations": [Function],
"listInvitationsForAuthenticatedUser": [Function],
"listLanguages": [Function],
"listPagesBuilds": [Function],
"listProtectedBranchRequiredStatusChecksContexts": [Function],
"listPublic": [Function],
"listPullRequestsAssociatedWithCommit": [Function],
"listReleaseAssets": [Function],
"listReleases": [Function],
"listStatusesForRef": [Function],
"listTags": [Function],
"listTeams": [Function],
"listTopics": [Function],
"listWebhooks": [Function],
"merge": [Function],
"pingHook": [Function],
"pingWebhook": [Function],
"removeAppAccessRestrictions": [Function],
"removeBranchProtection": [Function],
"removeCollaborator": [Function],
"removeDeployKey": [Function],
"removeProtectedBranchAdminEnforcement": [Function],
"removeProtectedBranchAppRestrictions": [Function],
"removeProtectedBranchPullRequestReviewEnforcement": [Function],
"removeProtectedBranchRequiredSignatures": [Function],
"removeProtectedBranchRequiredStatusChecks": [Function],
"removeProtectedBranchRequiredStatusChecksContexts": [Function],
"removeProtectedBranchRestrictions": [Function],
"removeProtectedBranchTeamRestrictions": [Function],
"removeProtectedBranchUserRestrictions": [Function],
"removeStatusCheckContexts": [Function],
"removeStatusCheckProtection": [Function],
"removeTeamAccessRestrictions": [Function],
"removeUserAccessRestrictions": [Function],
"replaceAllTopics": [Function],
"replaceProtectedBranchAppRestrictions": [Function],
"replaceProtectedBranchRequiredStatusChecksContexts": [Function],
"replaceProtectedBranchTeamRestrictions": [Function],
"replaceProtectedBranchUserRestrictions": [Function],
"replaceTopics": [Function],
"requestPageBuild": [Function],
"requestPagesBuild": [Function],
"retrieveCommunityProfileMetrics": [Function],
"setAdminBranchProtection": [Function],
"setAppAccessRestrictions": [Function],
"setStatusCheckContexts": [Function],
"setTeamAccessRestrictions": [Function],
"setUserAccessRestrictions": [Function],
"testPushHook": [Function],
"testPushWebhook": [Function],
"transfer": [Function],
"update": [Function],
"updateBranchProtection": [Function],
"updateCommitComment": [Function],
"updateHook": [Function],
"updateInformationAboutPagesSite": [Function],
"updateInvitation": [Function],
"updateProtectedBranchPullRequestReviewEnforcement": [Function],
"updateProtectedBranchRequiredStatusChecks": [Function],
"updatePullRequestReviewProtection": [Function],
"updateRelease": [Function],
"updateReleaseAsset": [Function],
"updateStatusCheckPotection": [Function],
"updateWebhook": [Function],
"uploadReleaseAsset": [Function],
},
"request": [Function],
"search": Object {
"code": [Function],
"commits": [Function],
"issuesAndPullRequests": [Function],
"labels": [Function],
"repos": [Function],
"topics": [Function],
"users": [Function],
},
"teams": Object {
"addOrUpdateMembershipForUserInOrg": [Function],
"addOrUpdateMembershipInOrg": [Function],
"addOrUpdateProjectInOrg": [Function],
"addOrUpdateProjectPermissionsInOrg": [Function],
"addOrUpdateRepoInOrg": [Function],
"addOrUpdateRepoPermissionsInOrg": [Function],
"checkManagesRepoInOrg": [Function],
"checkPermissionsForProjectInOrg": [Function],
"checkPermissionsForRepoInOrg": [Function],
"create": [Function],
"createDiscussionCommentInOrg": [Function],
"createDiscussionInOrg": [Function],
"deleteDiscussionCommentInOrg": [Function],
"deleteDiscussionInOrg": [Function],
"deleteInOrg": [Function],
"getByName": [Function],
"getDiscussionCommentInOrg": [Function],
"getDiscussionInOrg": [Function],
"getMembershipForUserInOrg": [Function],
"getMembershipInOrg": [Function],
"list": [Function],
"listChildInOrg": [Function],
"listDiscussionCommentsInOrg": [Function],
"listDiscussionsInOrg": [Function],
"listForAuthenticatedUser": [Function],
"listMembersInOrg": [Function],
"listPendingInvitationsInOrg": [Function],
"listProjectsInOrg": [Function],
"listReposInOrg": [Function],
"removeMembershipForUserInOrg": [Function],
"removeMembershipInOrg": [Function],
"removeProjectInOrg": [Function],
"removeRepoInOrg": [Function],
"reviewProjectInOrg": [Function],
"updateDiscussionCommentInOrg": [Function],
"updateDiscussionInOrg": [Function],
"updateInOrg": [Function],
},
"users": Object {
"addEmailForAuthenticated": [Function],
"addEmails": [Function],
"block": [Function],
"checkBlocked": [Function],
"checkFollowing": [Function],
"checkFollowingForUser": [Function],
"checkPersonIsFollowedByAuthenticated": [Function],
"createGpgKey": [Function],
"createGpgKeyForAuthenticated": [Function],
"createPublicKey": [Function],
"createPublicSshKeyForAuthenticated": [Function],
"deleteEmailForAuthenticated": [Function],
"deleteEmails": [Function],
"deleteGpgKey": [Function],
"deleteGpgKeyForAuthenticated": [Function],
"deletePublicKey": [Function],
"deletePublicSshKeyForAuthenticated": [Function],
"follow": [Function],
"getAuthenticated": [Function],
"getByUsername": [Function],
"getContextForUser": [Function],
"getGpgKey": [Function],
"getGpgKeyForAuthenticated": [Function],
"getPublicKey": [Function],
"getPublicSshKeyForAuthenticated": [Function],
"list": [Function],
"listBlocked": [Function],
"listBlockedByAuthenticated": [Function],
"listEmails": [Function],
"listEmailsForAuthenticated": [Function],
"listFollowedByAuthenticated": [Function],
"listFollowersForAuthenticatedUser": [Function],
"listFollowersForUser": [Function],
"listFollowingForAuthenticatedUser": [Function],
"listFollowingForUser": [Function],
"listGpgKeys": [Function],
"listGpgKeysForAuthenticated": [Function],
"listGpgKeysForUser": [Function],
"listPublicEmails": [Function],
"listPublicEmailsForAuthenticated": [Function],
"listPublicKeys": [Function],
"listPublicKeysForUser": [Function],
"listPublicSshKeysForAuthenticated": [Function],
"setPrimaryEmailVisibilityForAuthenticated": [Function],
"togglePrimaryEmailVisibility": [Function],
"unblock": [Function],
"unfollow": [Function],
"updateAuthenticated": [Function],
},
},
},
"hasStorageAnalyticsAfecFeature": [Function],
"isAccountReady": [Function], "isAccountReady": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function], "isFixedCollectionWithSharedThroughputSupported": [Function],
"isHostedDataExplorerEnabled": [Function], "isHostedDataExplorerEnabled": [Function],
"isLeftPaneExpanded": [Function],
"isMongoIndexingEnabled": [Function],
"isNotebookEnabled": [Function], "isNotebookEnabled": [Function],
"isNotebooksEnabledForAccount": [Function], "isNotebooksEnabledForAccount": [Function],
"isPublishNotebookPaneEnabled": [Function],
"isResourceTokenCollectionNodeSelected": [Function], "isResourceTokenCollectionNodeSelected": [Function],
"isSchemaEnabled": [Function], "isSchemaEnabled": [Function],
"isServerlessEnabled": [Function], "isServerlessEnabled": [Function],
"isShellEnabled": [Function], "isShellEnabled": [Function],
"isSparkEnabled": [Function],
"isSparkEnabledForAccount": [Function],
"isSynapseLinkUpdating": [Function], "isSynapseLinkUpdating": [Function],
"isTabsContentExpanded": [Function], "isTabsContentExpanded": [Function],
"junoClient": JunoClient {
"cachedPinnedRepos": [Function],
},
"memoryUsageInfo": [Function], "memoryUsageInfo": [Function],
"notebookBasePath": [Function], "notebookBasePath": [Function],
"notebookServerInfo": [Function], "notebookServerInfo": [Function],
"onGitHubClientError": [Function],
"onRefreshDatabasesKeyPress": [Function], "onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function], "onRefreshResourcesClick": [Function],
"openSidePanel": undefined, "openSidePanel": undefined,
@@ -847,7 +51,6 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
"container": [Circular], "container": [Circular],
}, },
"refreshNotebookList": [Function], "refreshNotebookList": [Function],
"refreshTreeTitle": [Function],
"resourceTokenCollection": [Function], "resourceTokenCollection": [Function],
"resourceTokenCollectionId": [Function], "resourceTokenCollectionId": [Function],
"resourceTokenDatabaseId": [Function], "resourceTokenDatabaseId": [Function],
@@ -886,7 +89,6 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
"activeTab": [Function], "activeTab": [Function],
"openedTabs": [Function], "openedTabs": [Function],
}, },
"toggleLeftPaneExpandedKeyPress": [Function],
}, },
"getRepo": [Function], "getRepo": [Function],
"pinRepo": [Function], "pinRepo": [Function],

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