Compare commits

..

27 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
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
480 changed files with 10277 additions and 8391 deletions

View File

@@ -111,6 +111,9 @@ src/Explorer/OpenActionsStubs.ts
src/Explorer/Panes/AddDatabasePane.ts
src/Explorer/Panes/AddDatabasePane.test.ts
src/Explorer/Panes/BrowseQueriesPane.ts
src/Explorer/Panes/ContextualPaneBase.ts
# src/Explorer/Panes/GraphStylingPane.ts
# src/Explorer/Panes/NewVertexPane.ts
src/Explorer/Panes/RenewAdHocAccessPane.ts
src/Explorer/Panes/SetupNotebooksPane.ts
src/Explorer/Panes/SwitchDirectoryPane.ts

View File

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

View File

@@ -7,7 +7,7 @@ on:
workflow_dispatch:
schedule:
# Once every hour
- cron: "0 15 * * *"
- cron: "0 * * * *"
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:

View File

@@ -1,4 +1,13 @@
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 }]],
};

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"
// ],
modulePaths: ["node_modules", "<rootDir>/src"],
// An array of file extensions your modules use
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "html", "svg"],

View File

@@ -4,7 +4,7 @@
@font-face {
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;

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

10509
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/identity": "1.2.1",
"@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",
"@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/terminal": "3.0.3",
"@microsoft/applicationinsights-web": "2.6.1",
@@ -41,17 +41,13 @@
"@nteract/transform-vega": "7.0.6",
"@octokit/rest": "17.9.2",
"@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",
"bootstrap": "3.4.1",
"canvas": "file:./canvas",
"clean-webpack-plugin": "0.1.19",
"clipboard-copy": "4.0.1",
"copy-webpack-plugin": "6.0.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",
"datatables.net-colreorder-dt": "1.5.1",
"datatables.net-dt": "1.10.19",
@@ -59,129 +55,138 @@
"dayjs": "1.8.19",
"dom-to-image": "2.6.0",
"dotenv": "8.2.0",
"eslint-plugin-jest": "23.13.2",
"eslint-plugin-react": "7.20.0",
"eslint-plugin-jest": "24.3.6",
"eslint-plugin-react": "7.23.2",
"hasher": "1.2.0",
"html2canvas": "1.0.0-rc.5",
"i18next": "19.8.4",
"i18next-browser-languagedetector": "6.0.1",
"i18next-http-backend": "1.0.23",
"html2canvas": "1.0.0-rc.7",
"i18next": "20.2.2",
"i18next-browser-languagedetector": "6.1.0",
"i18next-http-backend": "1.2.2",
"iframe-resizer-react": "1.1.0",
"immutable": "4.0.0-rc.12",
"is-ci": "2.0.0",
"jquery": "3.5.1",
"jquery-typeahead": "2.10.6",
"jquery": "3.6.0",
"jquery-typeahead": "2.11.1",
"jquery-ui-dist": "1.12.1",
"knockout": "3.5.1",
"mkdirp": "1.0.4",
"monaco-editor": "0.24.0",
"monaco-editor": "0.18.1",
"ms": "2.1.3",
"p-retry": "4.2.0",
"plotly.js-cartesian-dist-min": "1.52.3",
"post-robot": "10.0.42",
"q": "1.5.1",
"react": "16.13.1",
"react": "17.0.2",
"react-animate-height": "2.0.8",
"react-dnd": "9.4.0",
"react-dnd-html5-backend": "9.4.0",
"react-dom": "16.13.1",
"react-dnd": "14.0.2",
"react-dnd-html5-backend": "14.0.0",
"react-dom": "17.0.2",
"react-hotkeys": "2.0.0",
"react-i18next": "11.8.5",
"react-i18next": "11.8.15",
"react-notification-system": "0.2.17",
"react-redux": "7.1.3",
"redux": "4.0.4",
"react-redux": "7.2.4",
"redux": "4.1.0",
"reflect-metadata": "0.1.13",
"rx-jupyter": "5.5.12",
"rxjs": "6.6.3",
"sanitize-html": "2.3.3",
"styled-components": "4.3.2",
"styled-components": "5.3.0",
"swr": "0.4.0",
"terser-webpack-plugin": "3.1.0",
"underscore": "1.9.1",
"underscore": "1.13.1",
"utility-types": "3.10.0",
"zustand": "3.5.0"
},
"devDependencies": {
"@babel/core": "7.9.0",
"@babel/preset-env": "7.9.0",
"@babel/preset-react": "7.9.4",
"@babel/preset-typescript": "7.9.0",
"@testing-library/react": "11.2.3",
"@babel/core": "7.14.2",
"@babel/preset-env": "7.14.2",
"@babel/preset-react": "7.13.13",
"@babel/preset-typescript": "7.13.0",
"@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/codemirror": "0.0.56",
"@types/crossroads": "0.0.30",
"@types/d3": "5.9.2",
"@types/dom-to-image": "2.6.2",
"@types/enzyme": "3.10.7",
"@types/enzyme-adapter-react-16": "1.0.6",
"@types/enzyme": "3.10.8",
"@types/hasher": "0.0.31",
"@types/jest": "26.0.20",
"@types/node": "12.11.1",
"@types/memoize-one": "4.1.1",
"@types/mkdirp": "1.0.1",
"@types/node": "15.3.0",
"@types/node-fetch": "2.5.10",
"@types/post-robot": "10.0.1",
"@types/promise.prototype.finally": "2.0.4",
"@types/jest": "26.0.20",
"@types/q": "1.5.1",
"@types/react": "17.0.3",
"@types/react-dom": "17.0.3",
"@types/react": "17.0.5",
"@types/react-dom": "17.0.5",
"@types/react-notification-system": "0.2.39",
"@types/react-redux": "7.1.7",
"@types/sanitize-html": "1.27.2",
"@types/react-redux": "7.1.16",
"@types/sanitize-html": "2.3.1",
"@types/sinon": "2.3.3",
"@types/styled-components": "5.1.1",
"@types/underscore": "1.7.36",
"@typescript-eslint/eslint-plugin": "4.22.0",
"@typescript-eslint/parser": "4.22.0",
"babel-jest": "24.9.0",
"babel-loader": "8.1.0",
"@types/styled-components": "5.1.9",
"@types/underscore": "1.11.2",
"@typescript-eslint/eslint-plugin": "4.23.0",
"@typescript-eslint/parser": "4.23.0",
"@wojtekmaj/enzyme-adapter-react-17": "0.6.1",
"babel-jest": "26.6.3",
"babel-loader": "8.2.2",
"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",
"css-loader": "1.0.0",
"enzyme": "3.11.0",
"enzyme-adapter-react-16": "1.15.5",
"enzyme-to-json": "3.6.1",
"eslint": "7.8.1",
"enzyme-to-json": "3.6.2",
"eslint": "7.26.0",
"eslint-cli": "1.1.1",
"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",
"expose-loader": "2.0.0",
"expect-playwright": "0.3.3",
"fast-glob": "3.2.5",
"file-loader": "2.0.0",
"file-loader": "6.2.0",
"fs-extra": "7.0.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-webpack-plugin": "4.5.2",
"jest": "25.5.4",
"html-webpack-plugin": "5.3.1",
"jest": "26.6.3",
"jest-canvas-mock": "2.1.0",
"jest-playwright-preset": "1.5.1",
"jest-trx-results-processor": "0.0.7",
"less": "3.8.1",
"less-loader": "4.1.0",
"jest-playwright-preset": "1.5.2",
"jest-trx-results-processor": "2.2.0",
"less": "4.1.1",
"less-loader": "8.1.1",
"less-vars-loader": "1.1.0",
"mini-css-extract-plugin": "0.4.3",
"monaco-editor-webpack-plugin": "3.1.0",
"mini-css-extract-plugin": "1.6.0",
"monaco-editor-webpack-plugin": "1.7.0",
"node-fetch": "2.6.1",
"playwright": "1.10.0",
"prettier": "2.2.1",
"raw-loader": "0.5.1",
"prettier": "2.3.0",
"process": "0.11.10",
"raw-loader": "4.0.2",
"react-dev-utils": "11.0.4",
"rimraf": "3.0.0",
"sinon": "3.2.1",
"style-loader": "0.23.0",
"ts-loader": "6.2.2",
"tslint": "5.11.0",
"tslint-microsoft-contrib": "6.0.0",
"style-loader": "2.0.0",
"terser-webpack-plugin": "5.1.2",
"ts-loader": "9.1.2",
"tslint": "5.20.1",
"tslint-microsoft-contrib": "6.2.0",
"typedoc": "0.20.36",
"typescript": "4.2.4",
"url-loader": "1.1.1",
"url-loader": "4.1.1",
"wait-on": "4.0.2",
"webpack": "4.46.0",
"webpack-bundle-analyzer": "3.6.1",
"webpack-cli": "3.3.10",
"webpack-dev-server": "3.11.0"
"webpack": "5.37.0",
"webpack-bundle-analyzer": "4.4.1",
"webpack-cli": "4.7.0",
"webpack-dev-server": "3.11.2"
},
"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",
"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",
@@ -225,4 +230,4 @@
"prettier": {
"printWidth": 120
}
}
}

View File

@@ -1,4 +1,3 @@
{
"PROXY_PATH": "/proxy",
"msalRedirectURI": "https://cosmos-explorer-preview.azurewebsites.net/"
"PROXY_PATH": "/proxy"
}

View File

@@ -62,17 +62,6 @@ app.get("/pull/:pr(\\d+)", (req, res) => {
})
.catch(() => res.sendStatus(500));
});
app.get("/", (req, res) => {
fetch("https://api.github.com/repos/Azure/cosmos-explorer/branches/master")
.then((response) => response.json())
.then(({ commit: { sha } }) => {
const explorer = new URL(
"https://cosmos-explorer-preview.azurewebsites.net/commit/" + sha + "/hostedExplorer.html"
);
return res.redirect(explorer.href);
})
.catch(() => res.sendStatus(500));
});
app.listen(port, () => {
console.log(`Example app listening on port: ${port}`);

View File

@@ -1,5 +1,5 @@
import arrowLeftImg from "images/imgarrowlefticon.svg";
import React, { FunctionComponent } from "react";
import arrowLeftImg from "../../images/imgarrowlefticon.svg";
import { userContext } from "../UserContext";
export interface CollapsedResourceTreeProps {

View File

@@ -83,7 +83,7 @@ export function client(): Cosmos.CosmosClient {
if (_client) return _client;
const options: Cosmos.CosmosClientOptions = {
endpoint: endpoint() || "https://cosmos.azure.com", // CosmosClient gets upset if we pass a bad URL. This should never actually get called
key: userContext.masterKey,
...(!userContext.features.enableAadDataPlane && { key: userContext.masterKey }),
tokenProvider,
connectionPolicy: {
enableEndpointDiscovery: false,

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
import arrowLeftImg from "images/imgarrowlefticon.svg";
import refreshImg from "images/refresh-cosmos.svg";
import React, { FunctionComponent } from "react";
import arrowLeftImg from "../../images/imgarrowlefticon.svg";
import refreshImg from "../../images/refresh-cosmos.svg";
import { AuthType } from "../AuthType";
import { userContext } from "../UserContext";

View File

@@ -1,3 +1,5 @@
import DeleteIcon from "images/delete.svg";
import EditIcon from "images/Edit_entity.svg";
import {
Dropdown,
IDropdownOption,
@@ -10,8 +12,6 @@ import {
TooltipHost,
} from "@fluentui/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 { userContext } from "../UserContext";
import { EntityValue } from "./EntityValue";

View File

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

View File

@@ -54,7 +54,7 @@ export async function createTrigger(
const response = await client()
.database(databaseId)
.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;
} catch (error) {
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()
.offer(params.currentOffer.id)
// 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);
};

View File

@@ -51,7 +51,7 @@ export async function updateTrigger(
.database(databaseId)
.container(collectionId)
.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;
} catch (error) {
handleError(error, "UpdateTrigger", `Error while updating trigger ${trigger.id}`);

View File

@@ -28,7 +28,6 @@ export interface ConfigContext {
armAPIVersion?: string;
allowedJunoOrigins: string[];
enableSchemaAnalyzer: boolean;
msalRedirectURI?: string;
}
// Default configuration

View File

@@ -1,14 +1,14 @@
import AddCollectionIcon from "../../images/AddCollection.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 DeleteCollectionIcon from "../../images/DeleteCollection.svg";
import DeleteDatabaseIcon from "../../images/DeleteDatabase.svg";
import DeleteSprocIcon from "../../images/DeleteSproc.svg";
import DeleteTriggerIcon from "../../images/DeleteTrigger.svg";
import DeleteUDFIcon from "../../images/DeleteUDF.svg";
import HostedTerminalIcon from "../../images/Hosted-Terminal.svg";
import AddCollectionIcon from "images/AddCollection.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 DeleteCollectionIcon from "images/DeleteCollection.svg";
import DeleteDatabaseIcon from "images/DeleteDatabase.svg";
import DeleteSprocIcon from "images/DeleteSproc.svg";
import DeleteTriggerIcon from "images/DeleteTrigger.svg";
import DeleteUDFIcon from "images/DeleteUDF.svg";
import HostedTerminalIcon from "images/Hosted-Terminal.svg";
import * as ViewModels from "../Contracts/ViewModels";
import { userContext } from "../UserContext";
import { getCollectionName, getDatabaseName } from "../Utils/APITypeUtils";

View File

@@ -2,10 +2,10 @@
* Accordion top class
*/
import TriangleDownIcon from "images/Triangle-down.svg";
import TriangleRightIcon from "images/Triangle-right.svg";
import * as React from "react";
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";
export interface AccordionComponentProps {}

View File

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

View File

@@ -1,8 +1,8 @@
import CollapseChevronDownIcon from "images/QueryBuilder/CollapseChevronDown_16x.png";
/**
* React component for Command button component.
*/
import * as React from "react";
import CollapseChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png";
import { KeyCodes } from "../../../Common/Constants";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
@@ -204,11 +204,9 @@ export class CommandButtonComponent extends React.Component<CommandButtonCompone
}}
>
<div className="commandDropdown">
{this.props.children.map(
(c: CommandButtonComponentProps, index: number): JSX.Element => {
return CommandButtonComponent.renderButton(c, `${index}`);
}
)}
{this.props.children.map((c: CommandButtonComponentProps, index: number): JSX.Element => {
return CommandButtonComponent.renderButton(c, `${index}`);
})}
</div>
</div>
</div>

View File

@@ -0,0 +1,69 @@
/**
* React component for Switch Directory
*/
import { Dropdown, IDropdownOption, IDropdownProps } from "@fluentui/react";
import * as React from "react";
import _ from "underscore";
import { Tenant } from "../../../Contracts/DataModels";
export interface DefaultDirectoryDropdownProps {
directories: Array<Tenant>;
defaultDirectoryId: string;
onDefaultDirectoryChange: (newDirectory: Tenant) => void;
}
export class DefaultDirectoryDropdownComponent extends React.Component<DefaultDirectoryDropdownProps> {
public static readonly lastVisitedKey: string = "lastVisited";
public render(): JSX.Element {
const lastVisitedOption: IDropdownOption = {
key: DefaultDirectoryDropdownComponent.lastVisitedKey,
text: "Sign in to your last visited directory",
};
const directoryOptions: Array<IDropdownOption> = this.props.directories.map((dirc): IDropdownOption => {
return {
key: dirc.tenantId,
text: `${dirc.displayName}(${dirc.tenantId})`,
};
});
const dropDownOptions: Array<IDropdownOption> = [lastVisitedOption, ...directoryOptions];
const dropDownProps: IDropdownProps = {
label: "Set your default directory",
options: dropDownOptions,
defaultSelectedKey: this.props.defaultDirectoryId ? this.props.defaultDirectoryId : lastVisitedOption.key,
onChange: this._onDropdownChange,
className: "defaultDirectoryDropdown",
};
return <Dropdown {...dropDownProps} />;
}
private _onDropdownChange = (e: React.FormEvent<HTMLDivElement>, option?: IDropdownOption, index?: number): void => {
if (!option || !option.key) {
return;
}
if (option.key === this.props.defaultDirectoryId) {
return;
}
if (option.key === DefaultDirectoryDropdownComponent.lastVisitedKey) {
this.props.onDefaultDirectoryChange({
tenantId: option.key,
countryCode: undefined,
displayName: undefined,
domains: [],
id: undefined,
});
return;
}
const selectedDirectory = _.find(this.props.directories, (d) => d.tenantId === option.key);
if (!selectedDirectory) {
return;
}
this.props.onDefaultDirectoryChange(selectedDirectory);
};
}

View File

@@ -0,0 +1,35 @@
<div class="dynamicList" data-bind="setTemplateReady: true">
<div class="dynamicListContainer" data-bind="foreach: listItems">
<div class="dynamicListItem">
<input
id="uniqueKeyItems"
type="text"
autocomplete="off"
data-bind="value: value, attr: {placeholder: $parent.placeholder, 'aria-label': $parent.ariaLabel}"
/>
<span
class="dynamicListItemDelete"
title="Remove item"
role="button"
aria-label="Remove item"
tabindex="0"
data-bind="click: $parent.removeItem, event: { keydown: $parent.onRemoveItemKeyPress }"
>
<img src="/images/delete.svg" alt="Remove item" />
</span>
</div>
</div>
<div class="dynamicListItemNew">
<span
class="dynamicListItemAdd"
id="addUniqueKeyBtn"
role="button"
aria-label="Add unique key"
tabindex="0"
data-bind="click: addItem, event: { keydown: onAddItemKeyPress }"
>
<img src="/images/Add-property.svg" data-bind="attr: {alt: buttonText}" />
<span data-bind="text: buttonText"></span>
</span>
</div>
</div>

View File

@@ -52,7 +52,7 @@ class EditorViewModel extends JsonEditorViewModel {
if (EditorViewModel.providerRegistered.indexOf("sql") < 0) {
const { SqlCompletionItemProvider } = await import("@azure/cosmos-language-service");
const monaco = await loadMonaco();
monaco.languages.registerCompletionItemProvider("sql", new SqlCompletionItemProvider() as any); //TODO: Remove any. The upstream cosmos language service needs to be fixed
monaco.languages.registerCompletionItemProvider("sql", new SqlCompletionItemProvider());
EditorViewModel.providerRegistered.push("sql");
}
}

View File

@@ -62,7 +62,7 @@ export class EditorReact extends React.Component<EditorReactProps> {
* Create the monaco editor and attach to DOM
*/
private async createEditor(createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) {
const options = {
const options: monaco.editor.IEditorConstructionOptions = {
value: this.props.content,
language: this.props.language,
readOnly: this.props.isReadOnly,

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

@@ -90,7 +90,7 @@ export class JsonEditorViewModel extends WaitsForTemplateViewModel {
protected async createEditor(content: string, createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) {
this.registerCompletionItemProvider();
this.editorContainer = document.getElementById(this.getEditorId());
const options = {
const options: monaco.editor.IEditorConstructionOptions = {
value: content,
language: this.getEditorLanguage(),
readOnly: this.params.isReadOnly,

View File

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

View File

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

View File

@@ -2,12 +2,12 @@
* Wrapper around Notebook metadata
*/
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 { IGalleryItem } from "../../../Juno/JunoClient";
import * as FileSystemUtil from "../../Notebook/FileSystemUtil";
import "./NotebookViewerComponent.less";
import CosmosDBLogo from "../../../../images/CosmosDB-logo.svg";
import { InfoComponent } from "../NotebookGallery/InfoComponent/InfoComponent";
import "./NotebookViewerComponent.less";
export interface NotebookMetadataComponentProps {
data: IGalleryItem;

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
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 DiscardIcon from "../../../../images/discard.svg";
import SaveIcon from "../../../../images/save-cosmos.svg";
import { AuthType } from "../../../AuthType";
import * as Constants from "../../../Common/Constants";
import { getIndexTransformationProgress } from "../../../Common/dataAccess/getIndexTransformationProgress";

View File

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

View File

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

View File

@@ -31,6 +31,7 @@ exports[`SettingsComponent renders 1`] = `
"_isInitializingNotebooks": false,
"_resetNotebookWorkspace": [Function],
"canSaveQueries": [Function],
"closeSidePanel": undefined,
"collapsedResourceTreeWidth": 36,
"commandBarComponentAdapter": CommandBarComponentAdapter {
"container": [Circular],
@@ -55,6 +56,7 @@ exports[`SettingsComponent renders 1`] = `
"notebookServerInfo": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"openSidePanel": undefined,
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {
"container": [Circular],
@@ -79,6 +81,7 @@ exports[`SettingsComponent renders 1`] = `
"selectedDatabaseId": [Function],
"selectedNode": [Function],
"setInProgressConsoleDataIdToBeDeleted": undefined,
"setIsNotificationConsoleExpanded": undefined,
"setNotificationConsoleData": undefined,
"sparkClusterConnectionInfo": [Function],
"splitter": Splitter {
@@ -121,6 +124,7 @@ exports[`SettingsComponent renders 1`] = `
"_isInitializingNotebooks": false,
"_resetNotebookWorkspace": [Function],
"canSaveQueries": [Function],
"closeSidePanel": undefined,
"collapsedResourceTreeWidth": 36,
"commandBarComponentAdapter": CommandBarComponentAdapter {
"container": [Circular],
@@ -145,6 +149,7 @@ exports[`SettingsComponent renders 1`] = `
"notebookServerInfo": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"openSidePanel": undefined,
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {
"container": [Circular],
@@ -169,6 +174,7 @@ exports[`SettingsComponent renders 1`] = `
"selectedDatabaseId": [Function],
"selectedNode": [Function],
"setInProgressConsoleDataIdToBeDeleted": undefined,
"setIsNotificationConsoleExpanded": undefined,
"setNotificationConsoleData": undefined,
"sparkClusterConnectionInfo": [Function],
"splitter": Splitter {
@@ -224,6 +230,7 @@ exports[`SettingsComponent renders 1`] = `
"_isInitializingNotebooks": false,
"_resetNotebookWorkspace": [Function],
"canSaveQueries": [Function],
"closeSidePanel": undefined,
"collapsedResourceTreeWidth": 36,
"commandBarComponentAdapter": CommandBarComponentAdapter {
"container": [Circular],
@@ -248,6 +255,7 @@ exports[`SettingsComponent renders 1`] = `
"notebookServerInfo": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"openSidePanel": undefined,
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {
"container": [Circular],
@@ -272,6 +280,7 @@ exports[`SettingsComponent renders 1`] = `
"selectedDatabaseId": [Function],
"selectedNode": [Function],
"setInProgressConsoleDataIdToBeDeleted": undefined,
"setIsNotificationConsoleExpanded": undefined,
"setNotificationConsoleData": undefined,
"sparkClusterConnectionInfo": [Function],
"splitter": Splitter {
@@ -314,6 +323,7 @@ exports[`SettingsComponent renders 1`] = `
"_isInitializingNotebooks": false,
"_resetNotebookWorkspace": [Function],
"canSaveQueries": [Function],
"closeSidePanel": undefined,
"collapsedResourceTreeWidth": 36,
"commandBarComponentAdapter": CommandBarComponentAdapter {
"container": [Circular],
@@ -338,6 +348,7 @@ exports[`SettingsComponent renders 1`] = `
"notebookServerInfo": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"openSidePanel": undefined,
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {
"container": [Circular],
@@ -362,6 +373,7 @@ exports[`SettingsComponent renders 1`] = `
"selectedDatabaseId": [Function],
"selectedNode": [Function],
"setInProgressConsoleDataIdToBeDeleted": undefined,
"setIsNotificationConsoleExpanded": undefined,
"setNotificationConsoleData": undefined,
"sparkClusterConnectionInfo": [Function],
"splitter": Splitter {

View File

@@ -0,0 +1,194 @@
<div>
<div>
<p class="pkPadding">
<!-- ko if: showAsMandatory -->
<span class="mandatoryStar">*</span>
<!-- /ko -->
<span data-bind="text: label"></span>
<!-- ko if: infoBubbleText -->
<span class="infoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="/images/info-bubble.svg" alt="More information" />
<span data-bind="text: infoBubbleText" class="tooltiptext throughputRuInfo"></span>
</span>
<!-- /ko -->
</p>
</div>
<!-- ko if: !isFixed -->
<div class="throughputModeContainer">
<input
class="throughputModeRadio"
aria-label="Autopilot mode"
type="radio"
role="radio"
tabindex="0"
data-bind="
checked: isAutoPilotSelected,
checkedValue: true,
attr: {
id: throughputAutoPilotRadioId,
name: throughputModeRadioName,
'aria-checked': isAutoPilotSelected() ? 'true' : 'false'
}"
/>
<span
class="throughputModeSpace"
data-bind="
attr: {
for: throughputAutoPilotRadioId
}"
>Autoscale
</span>
<input
class="throughputModeRadio nonFirstRadio"
aria-label="Manual mode"
type="radio"
role="radio"
tabindex="0"
data-bind="
checked: isAutoPilotSelected,
checkedValue: false,
attr: {
id: throughputProvisionedRadioId,
name: throughputModeRadioName,
'aria-checked': !isAutoPilotSelected() ? 'true' : 'false'
}"
/>
<span
class="throughputModeSpace"
data-bind="attr: {
for: throughputProvisionedRadioId
}"
>Manual
</span>
</div>
<!-- /ko -->
<div data-bind="visible: isAutoPilotSelected">
<p>
<span
>Provision maximum RU/s required by this resource. Estimate your required RU/s with
<a target="_blank" href="https://cosmos.azure.com/capacitycalculator/">capacity calculator</a>.</span
>
</p>
<p>
<span>Max RU/s</span>
</p>
<div data-bind="setTemplateReady: true">
<input
data-bind="textInput: overrideWithProvisionedThroughputSettings() ? '' : maxAutoPilotThroughputSet, attr:{
disabled: overrideWithProvisionedThroughputSettings(),
step: step,
'class':'migration collid select-font-size',
min: minAutoPilotThroughput,
'aria-label': 'Max request units per second',
type: isAutoscaleThroughputInputFieldRequired() ? 'number' : 'hidden',
css: {
dirty: maxAutoPilotThroughputSet.editableIsDirty
}
}"
/>
</div>
<p data-bind="visible: overrideWithProvisionedThroughputSettings && !overrideWithProvisionedThroughputSettings()">
<span
data-bind="
html: autoPilotUsageCost"
></span>
</p>
<p
data-bind="visible: costsVisible && overrideWithProvisionedThroughputSettings && !overrideWithProvisionedThroughputSettings()"
>
<span data-bind="html: requestUnitsUsageCost"></span>
</p>
<!-- ko if: spendAckVisible -->
<p class="pkPadding">
<input
type="checkbox"
aria-label="acknowledge spend throughput"
data-bind="
attr: {
title: spendAckText,
id: spendAckId
},
checked: spendAckChecked"
/>
<span data-bind="text: spendAckText, attr: { for: spendAckId }"></span>
</p>
<!-- /ko -->
<!-- ko if: isFixed -->
<p>Choose unlimited storage capacity for more than 10,000 RU/s.</p>
<!-- /ko -->
</div>
<div data-bind="visible: !isAutoPilotSelected()">
<p>
<span
>Estimate your required throughput with
<a target="_blank" href="https://cosmos.azure.com/capacitycalculator/">capacity calculator</a></span
>
</p>
<div class="inputTooltip">
<span
data-bind="text: freeTierExceedThroughputTooltip, visible: showFreeTierExceedThroughputTooltip"
class="inputTooltipText"
></span>
</div>
<div data-bind="setTemplateReady: true">
<input
data-bind="
textInput: overrideWithAutoPilotSettings() ? maxAutoPilotThroughputSet : value,
css: {
dirty: value.editableIsDirty
},
enable: isEnabled,
attr:{
type: isManualThroughputInputFieldRequired() ? 'number' : 'hidden',
'data-test': testId,
'class': cssClass,
step: step,
min: minimum,
max: canExceedMaximumValue() ? null : maximum,
'aria-label': ariaLabel,
disabled: overrideWithAutoPilotSettings(),
required: isManualThroughputInputFieldRequired()
}"
/>
</div>
<div class="freeTierInlineWarning" data-bind="visible: showFreeTierExceedThroughputWarning">
<span class="freeTierWarningIcon"><img src="/images/warning.svg" alt="Warning" /></span>
<span class="freeTierWarningMessage" data-bind="text: freeTierExceedThroughputWarning"></span>
</div>
<p data-bind="visible: costsVisible">
<span data-bind="html: requestUnitsUsageCost"></span>
</p>
<!-- ko if: spendAckVisible -->
<p class="pkPadding">
<input
type="checkbox"
aria-label="acknowledge spend throughput"
data-bind="
attr: {
title: spendAckText,
id: spendAckId
},
checked: spendAckChecked"
/>
<span data-bind="text: spendAckText, attr: { for: spendAckId }"></span>
</p>
<!-- /ko -->
<!-- ko if: isFixed -->
<p>Choose unlimited storage capacity for more than 10,000 RU/s.</p>
<!-- /ko -->
</div>
</div>

View File

@@ -12,11 +12,11 @@ import {
IContextualMenuItemProps,
IContextualMenuProps,
} 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 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 { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";

View File

@@ -18,7 +18,6 @@ import { configContext, Platform } from "../ConfigContext";
import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
import { GitHubOAuthService } from "../GitHub/GitHubOAuthService";
import { useSidePanel } from "../hooks/useSidePanel";
import { IGalleryItem, JunoClient } from "../Juno/JunoClient";
import { NotebookWorkspaceManager } from "../NotebookWorkspaceManager/NotebookWorkspaceManager";
import { RouteHandler } from "../RouteHandlers/RouteHandler";
@@ -54,6 +53,7 @@ import { DeleteCollectionConfirmationPane } from "./Panes/DeleteCollectionConfir
import { DeleteDatabaseConfirmationPanel } from "./Panes/DeleteDatabaseConfirmationPanel";
import { ExecuteSprocParamsPane } from "./Panes/ExecuteSprocParamsPane/ExecuteSprocParamsPane";
import { GitHubReposPanel } from "./Panes/GitHubReposPanel/GitHubReposPanel";
import { LoadQueryPane } from "./Panes/LoadQueryPane/LoadQueryPane";
import { SaveQueryPane } from "./Panes/SaveQueryPane/SaveQueryPane";
import { SettingsPane } from "./Panes/SettingsPane/SettingsPane";
import { SetupNoteBooksPanel } from "./Panes/SetupNotebooksPanel/SetupNotebooksPanel";
@@ -81,8 +81,11 @@ BindingHandlersRegisterer.registerBindingHandlers();
var tmp = ComponentRegisterer;
export interface ExplorerParams {
setIsNotificationConsoleExpanded: (isExpanded: boolean) => void;
setNotificationConsoleData: (consoleData: ConsoleData) => void;
setInProgressConsoleDataIdToBeDeleted: (id: string) => void;
openSidePanel: (headerText: string, panelContent: JSX.Element, onClose?: () => void) => void;
closeSidePanel: () => void;
tabsManager: TabsManager;
}
@@ -97,9 +100,15 @@ export default class Explorer {
public tableDataClient: TableDataClient;
public splitter: Splitter;
// Notification Console
private setIsNotificationConsoleExpanded: (isExpanded: boolean) => void;
private setNotificationConsoleData: (consoleData: ConsoleData) => void;
private setInProgressConsoleDataIdToBeDeleted: (id: string) => void;
// Panes
public openSidePanel: (headerText: string, panelContent: JSX.Element, onClose?: () => void) => void;
public closeSidePanel: () => void;
// Resource Tree
public databases: ko.ObservableArray<ViewModels.Database>;
public selectedDatabaseId: ko.Computed<string>;
@@ -150,8 +159,11 @@ export default class Explorer {
private static readonly MaxNbDatabasesToAutoExpand = 5;
constructor(params?: ExplorerParams) {
this.setIsNotificationConsoleExpanded = params?.setIsNotificationConsoleExpanded;
this.setNotificationConsoleData = params?.setNotificationConsoleData;
this.setInProgressConsoleDataIdToBeDeleted = params?.setInProgressConsoleDataIdToBeDeleted;
this.openSidePanel = params?.openSidePanel;
this.closeSidePanel = params?.closeSidePanel;
const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, {
dataExplorerArea: Constants.Areas.ResourceTree,
@@ -471,6 +483,14 @@ export default class Explorer {
this.setInProgressConsoleDataIdToBeDeleted(id);
}
public expandConsole(): void {
this.setIsNotificationConsoleExpanded(true);
}
public collapseConsole(): void {
this.setIsNotificationConsoleExpanded(false);
}
public refreshDatabaseForResourceToken(): Q.Promise<any> {
const databaseId = this.resourceTokenDatabaseId();
const collectionId = this.resourceTokenCollectionId();
@@ -773,9 +793,9 @@ export default class Explorer {
}
public findSelectedCollection(): ViewModels.Collection {
return (this.selectedNode().nodeKind === "Collection"
? this.selectedNode()
: this.selectedNode().collection) as ViewModels.Collection;
return (
this.selectedNode().nodeKind === "Collection" ? this.selectedNode() : this.selectedNode().collection
) as ViewModels.Collection;
}
public isRunningOnNationalCloud(): boolean {
@@ -867,9 +887,7 @@ export default class Explorer {
return true;
}
private getDeltaDatabases(
updatedDatabaseList: DataModels.Database[]
): {
private getDeltaDatabases(updatedDatabaseList: DataModels.Database[]): {
toAdd: ViewModels.Database[];
toDelete: ViewModels.Database[];
} {
@@ -1116,12 +1134,12 @@ export default class Explorer {
if (openedNotebookTabs.length > 0) {
this.showOkModalDialog("Unable to rename file", "This file is being edited. Please close the tab and try again.");
} else {
useSidePanel.getState().openSidePanel(
this.openSidePanel(
"Rename Notebook",
<StringInputPane
explorer={this}
closePanel={() => {
useSidePanel.getState().closeSidePanel();
this.closeSidePanel();
this.resourceTree.triggerRender();
}}
inputLabel="Enter new notebook name"
@@ -1147,12 +1165,12 @@ export default class Explorer {
throw new Error(error);
}
useSidePanel.getState().openSidePanel(
this.openSidePanel(
"Create new directory",
<StringInputPane
explorer={this}
closePanel={() => {
useSidePanel.getState().closeSidePanel();
this.closeSidePanel();
this.resourceTree.triggerRender();
}}
errorMessage="Could not create directory "
@@ -1541,115 +1559,160 @@ export default class Explorer {
return false;
});
}
public openDeleteCollectionConfirmationPane(): void {
useSidePanel
.getState()
.openSidePanel("Delete " + getCollectionName(), <DeleteCollectionConfirmationPane explorer={this} />);
this.openSidePanel(
"Delete " + getCollectionName(),
<DeleteCollectionConfirmationPane explorer={this} closePanel={this.closeSidePanel} />
);
}
public openDeleteDatabaseConfirmationPane(): void {
useSidePanel
.getState()
.openSidePanel(
"Delete " + getDatabaseName(),
<DeleteDatabaseConfirmationPanel explorer={this} selectedDatabase={this.findSelectedDatabase()} />
);
this.openSidePanel(
"Delete " + getDatabaseName(),
<DeleteDatabaseConfirmationPanel
explorer={this}
openNotificationConsole={() => this.expandConsole()}
closePanel={this.closeSidePanel}
selectedDatabase={this.findSelectedDatabase()}
/>
);
}
public openUploadItemsPanePane(): void {
useSidePanel.getState().openSidePanel("Upload " + getUploadName(), <UploadItemsPane explorer={this} />);
this.openSidePanel("Upload " + getUploadName(), <UploadItemsPane explorer={this} />);
}
public openSettingPane(): void {
this.openSidePanel(
"Setting",
<SettingsPane expandConsole={() => this.expandConsole()} closePanel={this.closeSidePanel} />
);
}
public openExecuteSprocParamsPanel(storedProcedure: StoredProcedure): void {
useSidePanel
.getState()
.openSidePanel("Input parameters", <ExecuteSprocParamsPane storedProcedure={storedProcedure} />);
this.openSidePanel(
"Input parameters",
<ExecuteSprocParamsPane
storedProcedure={storedProcedure}
expandConsole={() => this.expandConsole()}
closePanel={() => this.closeSidePanel()}
/>
);
}
public async openAddCollectionPanel(databaseId?: string): Promise<void> {
await this.loadDatabaseOffers();
useSidePanel
.getState()
.openSidePanel("New " + getCollectionName(), <AddCollectionPanel explorer={this} databaseId={databaseId} />);
this.openSidePanel(
"New " + getCollectionName(),
<AddCollectionPanel
explorer={this}
closePanel={() => this.closeSidePanel()}
openNotificationConsole={() => this.expandConsole()}
databaseId={databaseId}
/>
);
}
public openAddDatabasePane(): void {
useSidePanel.getState().openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={this} />);
this.openSidePanel(
"New " + getDatabaseName(),
<AddDatabasePanel
explorer={this}
openNotificationConsole={() => this.expandConsole()}
closePanel={this.closeSidePanel}
/>
);
}
public openBrowseQueriesPanel(): void {
useSidePanel.getState().openSidePanel("Open Saved Queries", <BrowseQueriesPane explorer={this} />);
this.openSidePanel("Open Saved Queries", <BrowseQueriesPane explorer={this} closePanel={this.closeSidePanel} />);
}
public openLoadQueryPanel(): void {
this.openSidePanel("Load Query", <LoadQueryPane explorer={this} closePanel={() => this.closeSidePanel()} />);
}
public openSaveQueryPanel(): void {
useSidePanel.getState().openSidePanel("Save Query", <SaveQueryPane explorer={this} />);
this.openSidePanel("Save Query", <SaveQueryPane explorer={this} closePanel={() => this.closeSidePanel()} />);
}
public openUploadFilePanel(parent?: NotebookContentItem): void {
parent = parent || this.resourceTree.myNotebooksContentRoot;
useSidePanel
.getState()
.openSidePanel(
"Upload file to notebook server",
<UploadFilePane uploadFile={(name: string, content: string) => this.uploadFile(name, content, parent)} />
);
this.openSidePanel(
"Upload file to notebook server",
<UploadFilePane
expandConsole={() => this.expandConsole()}
closePanel={this.closeSidePanel}
uploadFile={(name: string, content: string) => this.uploadFile(name, content, parent)}
/>
);
}
public openCassandraAddCollectionPane(): void {
useSidePanel
.getState()
.openSidePanel(
"Add Table",
<CassandraAddCollectionPane explorer={this} cassandraApiClient={new CassandraAPIDataClient()} />
);
this.openSidePanel(
"Add Table",
<CassandraAddCollectionPane
explorer={this}
closePanel={() => this.closeSidePanel()}
cassandraApiClient={new CassandraAPIDataClient()}
/>
);
}
public openGitHubReposPanel(header: string, junoClient?: JunoClient): void {
useSidePanel
.getState()
.openSidePanel(
header,
<GitHubReposPanel
explorer={this}
gitHubClientProp={this.notebookManager.gitHubClient}
junoClientProp={junoClient}
/>
);
this.openSidePanel(
header,
<GitHubReposPanel
explorer={this}
closePanel={this.closeSidePanel}
gitHubClientProp={this.notebookManager.gitHubClient}
junoClientProp={junoClient}
openNotificationConsole={() => this.expandConsole()}
/>
);
}
public openAddTableEntityPanel(queryTablesTab: QueryTablesTab, tableEntityListViewModel: TableListViewModal): void {
useSidePanel
.getState()
.openSidePanel(
"Add Table Entity",
<AddTableEntityPanel
tableDataClient={this.tableDataClient}
queryTablesTab={queryTablesTab}
tableEntityListViewModel={tableEntityListViewModel}
cassandraApiClient={new CassandraAPIDataClient()}
/>
);
this.openSidePanel(
"Add Table Entity",
<AddTableEntityPanel
explorer={this}
closePanel={this.closeSidePanel}
queryTablesTab={queryTablesTab}
tableEntityListViewModel={tableEntityListViewModel}
cassandraApiClient={new CassandraAPIDataClient()}
/>
);
}
public openSetupNotebooksPanel(title: string, description: string): void {
useSidePanel
.getState()
.openSidePanel(title, <SetupNoteBooksPanel explorer={this} panelTitle={title} panelDescription={description} />);
this.openSidePanel(
title,
<SetupNoteBooksPanel
explorer={this}
closePanel={this.closeSidePanel}
openNotificationConsole={() => this.expandConsole()}
panelTitle={title}
panelDescription={description}
/>
);
}
public openEditTableEntityPanel(queryTablesTab: QueryTablesTab, tableEntityListViewModel: TableListViewModal): void {
useSidePanel
.getState()
.openSidePanel(
"Edit Table Entity",
<EditTableEntityPanel
tableDataClient={this.tableDataClient}
queryTablesTab={queryTablesTab}
tableEntityListViewModel={tableEntityListViewModel}
cassandraApiClient={new CassandraAPIDataClient()}
/>
);
this.openSidePanel(
"Edit Table Entity",
<EditTableEntityPanel
explorer={this}
closePanel={this.closeSidePanel}
queryTablesTab={queryTablesTab}
tableEntityListViewModel={tableEntityListViewModel}
cassandraApiClient={new CassandraAPIDataClient()}
/>
);
}
public openTableSelectQueryPanel(queryViewModal: QueryViewModel): void {
useSidePanel.getState().openSidePanel("Select Column", <TableQuerySelectPanel queryViewModel={queryViewModal} />);
}
public openSettingPane(): void {
useSidePanel.getState().openSidePanel("Settings", <SettingsPane />);
this.openSidePanel(
"Select Column",
<TableQuerySelectPanel explorer={this} closePanel={this.closeSidePanel} queryViewModel={queryViewModal} />
);
}
}

View File

@@ -3,13 +3,13 @@
* 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 { 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 * as InputTypeaheadComponent from "../../Controls/InputTypeahead/InputTypeaheadComponent";
import { EditedEdges, GraphNewEdgeData, NeighborVertexBasicInfo, PossibleVertex } from "./GraphExplorer";
import * as GraphUtil from "./GraphUtil";
export interface EditorNeighborsComponentProps {
isSource: boolean;

View File

@@ -3,13 +3,13 @@
* Read-only properties
*/
import AddIcon from "images/Add-property.svg";
import DeleteIcon from "images/delete.svg";
import * as React from "react";
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 { EditedProperties } from "./GraphExplorer";
import { ReadOnlyNodePropertiesComponent } from "./ReadOnlyNodePropertiesComponent";
export interface EditorNodePropertiesComponentProps {
editedProperties: EditedProperties;

View File

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

View File

@@ -1,8 +1,8 @@
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 React from "react";
import LoadGraphIcon from "../../../../images/LoadGraph.png";
import LoadingIndicatorIcon from "../../../../images/LoadingIndicator_3Squares.gif";
import * as Constants from "../../../Common/Constants";
import { queryDocuments } from "../../../Common/dataAccess/queryDocuments";
import { queryDocumentsPage } from "../../../Common/dataAccess/queryDocumentsPage";
@@ -633,40 +633,38 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
promise = Q.resolve(0);
}
promise = promise.then(
(outEPairsNb: number): Q.Promise<number> => {
const inEdgesToFetch = totalEdgesToFetch - outEPairsNb;
if (!vertex._inEAllLoaded && inEdgesToFetch > 0) {
let start: number;
if (offsetIndex <= vertex._outEdgeIds.length) {
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;
}
);
promise = promise.then((outEPairsNb: number): Q.Promise<number> => {
const inEdgesToFetch = totalEdgesToFetch - outEPairsNb;
if (!vertex._inEAllLoaded && inEdgesToFetch > 0) {
let start: number;
if (offsetIndex <= vertex._outEdgeIds.length) {
start = 0;
} 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) => {
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[] {
const key = this.state.igraphConfig.nodeCaption;
return $.map(
this.state.rootMap,
(value: any, index: number): LeftPane.CaptionId => {
let result = GraphData.GraphData.getNodePropValue(value, key);
return {
caption: result !== undefined ? result : value.id,
id: value.id,
};
}
);
return $.map(this.state.rootMap, (value: any, index: number): LeftPane.CaptionId => {
let result = GraphData.GraphData.getNodePropValue(value, key);
return {
caption: result !== undefined ? result : value.id,
id: value.id,
};
});
}
/**

View File

@@ -94,236 +94,16 @@ describe("Gremlin Simple Client", () => {
result: { data: ["é"], meta: {} },
};
const expectedDecodedUint8ArrayValues = [
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,
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,
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,
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
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 { 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 {
isTabsContentExpanded: boolean;

View File

@@ -4,22 +4,27 @@
* 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 { 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 { 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 { 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 {
READONLY_PROP,

View File

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

View File

@@ -43,7 +43,7 @@ describe("Graph Style Component", () => {
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");
expect(dropDownList).toBeDefined();
});

View File

@@ -1,7 +1,7 @@
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 AddIcon from "../../../../images/Add-property.svg";
import DeleteIcon from "../../../../images/delete.svg";
import { NormalizedEventKey } from "../../../Common/Constants";
import { GremlinPropertyValueType, InputPropertyValueTypeString, NewVertexData } from "../../../Contracts/ViewModels";
import { EditorNodePropertiesComponent } from "../GraphExplorerComponent/EditorNodePropertiesComponent";

View File

@@ -1,33 +1,31 @@
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 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 * as Constants from "../../../Common/Constants";
import { configContext, Platform } from "../../../ConfigContext";
import * as ViewModels from "../../../Contracts/ViewModels";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { userContext } from "../../../UserContext";
import { getCollectionName, getDatabaseName } from "../../../Utils/APITypeUtils";
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
import Explorer from "../../Explorer";
import { OpenFullScreen } from "../../OpenFullScreen";
import { LoadQueryPane } from "../../Panes/LoadQueryPane/LoadQueryPane";
let counter = 0;
@@ -154,7 +152,7 @@ export function createControlCommandBarButtons(container: Explorer): CommandButt
{
iconSrc: SettingsIcon,
iconAlt: "Settings",
onCommandClick: container.openSettingPane,
onCommandClick: () => container.openSettingPane(),
commandButtonLabel: undefined,
ariaLabel: "Settings",
tooltipText: "Settings",
@@ -169,7 +167,7 @@ export function createControlCommandBarButtons(container: Explorer): CommandButt
iconSrc: OpenInTabIcon,
iconAlt: label,
onCommandClick: () => {
useSidePanel.getState().openSidePanel("Open Full Screen", <OpenFullScreen />);
container.openSidePanel("Open Full Screen", <OpenFullScreen />);
},
commandButtonLabel: undefined,
ariaLabel: label,
@@ -410,7 +408,7 @@ function createOpenQueryFromDiskButton(container: Explorer): CommandButtonCompon
return {
iconSrc: OpenQueryFromDiskIcon,
iconAlt: label,
onCommandClick: () => useSidePanel.getState().openSidePanel("Load Query", <LoadQueryPane explorer={container} />),
onCommandClick: () => container.openLoadQueryPanel(),
commandButtonLabel: label,
ariaLabel: label,
hasPopup: true,

View File

@@ -1,15 +1,8 @@
import {
Dropdown,
ICommandBarItemProps,
IComponentAsProps,
IconType,
IDropdownOption,
IDropdownStyles,
} from "@fluentui/react";
import { ICommandBarItemProps, IconType, IDropdownOption, IDropdownStyles } from "@fluentui/react";
import ChevronDownIcon from "images/Chevron_down.svg";
import { Observable } from "knockout";
import * as React from "react";
import _ from "underscore";
import ChevronDownIcon from "../../../../images/Chevron_down.svg";
import { StyleConstants } from "../../../Common/Constants";
import { MemoryUsageInfo } from "../../../Contracts/DataModels";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
@@ -26,150 +19,135 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
return btns
.filter((btn) => btn)
.map(
(btn: CommandButtonComponentProps, index: number): ICommandBarItemProps => {
if (btn.isDivider) {
return createDivider(btn.commandButtonLabel);
}
.map((btn: CommandButtonComponentProps, index: number): ICommandBarItemProps => {
if (btn.isDivider) {
return createDivider(btn.commandButtonLabel);
}
const isSplit = !!btn.children && btn.children.length > 0;
const label = btn.commandButtonLabel || btn.tooltipText;
const result: ICommandBarItemProps = {
iconProps: {
style: {
width: StyleConstants.CommandBarIconWidth, // 16
alignSelf: btn.iconName ? "baseline" : undefined,
},
imageProps: btn.iconSrc ? { src: btn.iconSrc, alt: btn.iconAlt } : undefined,
iconName: btn.iconName,
const isSplit = !!btn.children && btn.children.length > 0;
const label = btn.commandButtonLabel || btn.tooltipText;
const result: ICommandBarItemProps = {
iconProps: {
style: {
width: StyleConstants.CommandBarIconWidth, // 16
alignSelf: btn.iconName ? "baseline" : undefined,
},
onClick: (ev?: React.MouseEvent<HTMLElement, MouseEvent> | React.KeyboardEvent<HTMLElement>) => {
btn.onCommandClick(ev);
TelemetryProcessor.trace(Action.ClickCommandBarButton, ActionModifiers.Mark, { label });
imageProps: btn.iconSrc ? { src: btn.iconSrc, alt: btn.iconAlt } : undefined,
iconName: btn.iconName,
},
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}`,
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,
rootDisabled: {
backgroundColor: backgroundColor,
pointerEvents: "auto",
},
splitButtonMenuButton: {
backgroundColor: backgroundColor,
selectors: {
":hover": { backgroundColor: StyleConstants.AccentLight },
},
rootDisabled: {
backgroundColor: backgroundColor,
pointerEvents: "auto",
width: 16,
},
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: {
":hover": { backgroundColor: StyleConstants.AccentLight },
},
width: 16,
},
label: { fontSize: StyleConstants.mediumFontSize },
rootHovered: { backgroundColor: StyleConstants.AccentLight },
rootPressed: { backgroundColor: StyleConstants.AccentLight },
splitButtonMenuButtonExpanded: {
backgroundColor: StyleConstants.AccentExtra,
selectors: {
":hover": { backgroundColor: StyleConstants.AccentLight },
".ms-ContextualMenu-itemText": { fontSize: StyleConstants.mediumFontSize },
".ms-ContextualMenu-link:hover": { backgroundColor: StyleConstants.AccentLight },
".ms-ContextualMenu-icon": { width: 16, height: 16 },
},
},
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.menuIconProps = {
iconType: IconType.image,
style: {
width: 12,
paddingLeft: 1,
paddingTop: 6,
},
imageProps: { src: ChevronDownIcon, alt: btn.iconAlt },
};
}
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: {
".ms-ContextualMenu-itemText": { fontSize: StyleConstants.mediumFontSize },
".ms-ContextualMenu-link:hover": { backgroundColor: StyleConstants.AccentLight },
".ms-ContextualMenu-icon": { width: 16, height: 16 },
},
},
},
};
if (btn.isDropdown) {
const selectedChild = _.find(btn.children, (child) => child.dropdownItemKey === btn.dropdownSelectedKey);
result.name = selectedChild?.commandButtonLabel || btn.dropdownPlaceholder;
result.menuIconProps = {
iconType: IconType.image,
style: {
width: 12,
paddingLeft: 1,
paddingTop: 6,
},
imageProps: { src: ChevronDownIcon, alt: btn.iconAlt },
};
}
const dropdownStyles: Partial<IDropdownStyles> = {
root: { margin: 5 },
dropdown: { width: btn.dropdownWidth },
title: { fontSize: 12, height: 30, lineHeight: 28 },
dropdownItem: { fontSize: 12, lineHeight: 28, minHeight: 30 },
dropdownItemSelected: { fontSize: 12, lineHeight: 28, minHeight: 30 },
};
if (btn.isDropdown) {
const selectedChild = _.find(btn.children, (child) => child.dropdownItemKey === btn.dropdownSelectedKey);
result.name = selectedChild?.commandButtonLabel || btn.dropdownPlaceholder;
const dropdownStyles: Partial<IDropdownStyles> = {
root: { margin: 5 },
dropdown: { width: btn.dropdownWidth },
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}
/>
);
};
}
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 });
};
return result;
}
);
return result;
});
};
export const createDivider = (key: string): ICommandBarItemProps => {

View File

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

View File

@@ -3,19 +3,18 @@
*/
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 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 { useNotificationConsole } from "../../../hooks/useNotificationConsole";
import { userContext } from "../../../UserContext";
/**
@@ -107,10 +106,12 @@ export class NotificationConsoleComponent extends React.Component<
const numInProgress = this.state.allConsoleData.filter(
(data: ConsoleData) => data.type === ConsoleDataType.InProgress
).length;
const numErroredItems = this.state.allConsoleData.filter((data: ConsoleData) => data.type === ConsoleDataType.Error)
.length;
const numInfoItems = this.state.allConsoleData.filter((data: ConsoleData) => data.type === ConsoleDataType.Info)
.length;
const numErroredItems = this.state.allConsoleData.filter(
(data: ConsoleData) => data.type === ConsoleDataType.Error
).length;
const numInfoItems = this.state.allConsoleData.filter(
(data: ConsoleData) => data.type === ConsoleDataType.Info
).length;
return (
<div className="notificationConsoleContainer">
@@ -180,7 +181,7 @@ export class NotificationConsoleComponent extends React.Component<
onKeyDown={(event: React.KeyboardEvent<HTMLSpanElement>) => this.onClearNotificationsKeyPress(event)}
tabIndex={0}
>
<img src={ClearIcon} alt="clear notifications image" />
<ClearIcon />
Clear Notifications
</span>
</div>
@@ -322,23 +323,3 @@ const PrPreview = (props: { pr: string }) => {
</>
);
};
export const NotificationConsole: React.FC<
Pick<NotificationConsoleComponentProps, "consoleData" | "inProgressConsoleDataIdToBeDeleted">
> = ({
consoleData,
inProgressConsoleDataIdToBeDeleted,
}: Pick<NotificationConsoleComponentProps, "consoleData" | "inProgressConsoleDataIdToBeDeleted">) => {
const setIsExpanded = useNotificationConsole((state) => state.setIsExpanded);
const isExpanded = useNotificationConsole((state) => state.isExpanded);
// TODO Refactor NotificationConsoleComponent into a functional component and remove this wrapper
// This component only exists so we can use hooks and pass them down to a non-functional component
return (
<NotificationConsoleComponent
consoleData={consoleData}
inProgressConsoleDataIdToBeDeleted={inProgressConsoleDataIdToBeDeleted}
isConsoleExpanded={isExpanded}
setIsConsoleExpanded={setIsExpanded}
/>
);
};

View File

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

View File

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

View File

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

View File

@@ -14,13 +14,13 @@ import { MemoryUsageInfo } from "../../Contracts/DataModels";
import { GitHubClient } from "../../GitHub/GitHubClient";
import { GitHubContentProvider } from "../../GitHub/GitHubContentProvider";
import { GitHubOAuthService } from "../../GitHub/GitHubOAuthService";
import { useSidePanel } from "../../hooks/useSidePanel";
import { JunoClient } from "../../Juno/JunoClient";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../UserContext";
import { getFullName } from "../../Utils/UserUtils";
import Explorer from "../Explorer";
import { ContextualPaneBase } from "../Panes/ContextualPaneBase";
import { CopyNotebookPane } from "../Panes/CopyNotebookPane/CopyNotebookPane";
import { PublishNotebookPane } from "../Panes/PublishNotebookPane/PublishNotebookPane";
import { ResourceTreeAdapter } from "../Tree/ResourceTreeAdapter";
@@ -56,6 +56,8 @@ export default class NotebookManager {
public gitHubOAuthService: GitHubOAuthService;
public gitHubClient: GitHubClient;
public gitHubReposPane: ContextualPaneBase;
public initialize(params: NotebookManagerOptions): void {
this.params = params;
this.junoClient = new JunoClient();
@@ -96,7 +98,7 @@ export default class NotebookManager {
this.gitHubOAuthService.getTokenObservable().subscribe((token) => {
this.gitHubClient.setToken(token?.access_token);
if (this?.gitHubOAuthService.isLoggedIn()) {
useSidePanel.getState().closeSidePanel();
this.params.container.closeSidePanel();
this.params.container.openGitHubReposPanel("Manager GitHub settings", this.junoClient);
}
@@ -125,37 +127,37 @@ export default class NotebookManager {
onTakeSnapshot: (request: SnapshotRequest) => void,
onClosePanel: () => void
): Promise<void> {
useSidePanel
.getState()
.openSidePanel(
"Publish Notebook",
<PublishNotebookPane
explorer={this.params.container}
junoClient={this.junoClient}
name={name}
author={getFullName()}
notebookContent={content}
notebookContentRef={notebookContentRef}
onTakeSnapshot={onTakeSnapshot}
/>,
onClosePanel
);
const explorer = this.params.container;
explorer.openSidePanel(
"Publish Notebook",
<PublishNotebookPane
explorer={this.params.container}
junoClient={this.junoClient}
closePanel={this.params.container.closeSidePanel}
openNotificationConsole={this.params.container.expandConsole}
name={name}
author={getFullName()}
notebookContent={content}
notebookContentRef={notebookContentRef}
onTakeSnapshot={onTakeSnapshot}
/>,
onClosePanel
);
}
public openCopyNotebookPane(name: string, content: string): void {
const { container } = this.params;
useSidePanel
.getState()
.openSidePanel(
"Copy Notebook",
<CopyNotebookPane
container={container}
junoClient={this.junoClient}
gitHubOAuthService={this.gitHubOAuthService}
name={name}
content={content}
/>
);
container.openSidePanel(
"Copy Notebook",
<CopyNotebookPane
container={container}
closePanel={container.closeSidePanel}
junoClient={this.junoClient}
gitHubOAuthService={this.gitHubOAuthService}
name={name}
content={content}
/>
);
}
// Octokit's error handler uses 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 * as React from "react";
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 { Dispatch } from "redux";
import { userContext } from "../../../UserContext";

View File

@@ -1,12 +1,11 @@
import { actions, selectors, ContentRef, AppState } from "@nteract/core";
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 { connect } from "react-redux";
import { Dispatch } from "redux";
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 {
id: string;

View File

@@ -11,7 +11,6 @@ import {
DropTargetConnector,
DropTargetMonitor,
} from "react-dnd";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import styled, { StyledComponent } from "styled-components";
@@ -124,8 +123,9 @@ export const cellTarget = {
if (monitor) {
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.
const item: Props = monitor.getItem();
props.moveCell({
id: monitor.getItem().id,
id: item.id,
destinationId: props.id,
above: hoverUpperHalf,
contentRef: props.contentRef,

View File

@@ -51,15 +51,8 @@ export class KeyboardShortcuts extends React.Component<Props> {
return;
}
const {
executeFocusedCell,
focusNextCell,
focusNextCellEditor,
contentRef,
cellOrder,
focusedCell,
cellMap,
} = this.props;
const { executeFocusedCell, focusNextCell, focusNextCellEditor, contentRef, cellOrder, focusedCell, cellMap } =
this.props;
let ctrlKeyPressed = e.ctrlKey;
// Allow cmd + enter (macOS) to operate like ctrl + enter

View File

@@ -20,7 +20,6 @@ import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"
import { configContext, Platform } from "../../ConfigContext";
import * as DataModels from "../../Contracts/DataModels";
import { SubscriptionType } from "../../Contracts/SubscriptionType";
import { useSidePanel } from "../../hooks/useSidePanel";
import { CollectionCreation } from "../../Shared/Constants";
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
@@ -37,6 +36,8 @@ import { PanelLoadingScreen } from "./PanelLoadingScreen";
export interface AddCollectionPanelProps {
explorer: Explorer;
closePanel: () => void;
openNotificationConsole: () => void;
databaseId?: string;
}
@@ -132,6 +133,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
message={this.state.errorMessage}
messageType="error"
showErrorDetails={this.state.showErrorDetails}
openNotificationConsole={this.props.openNotificationConsole}
/>
)}
@@ -140,6 +142,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
message={getUpsellMessage(userContext.portalEnv, true, this.props.explorer.isFirstResourceCreated(), true)}
messageType="info"
showErrorDetails={false}
openNotificationConsole={this.props.openNotificationConsole}
link={Constants.Urls.freeTierInformation}
linkText="Learn more"
/>
@@ -498,44 +501,42 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
</TooltipHost>
</Stack>
{this.state.uniqueKeys.map(
(uniqueKey: string, i: number): JSX.Element => {
return (
<Stack style={{ marginBottom: 8 }} key={`uniqueKey${i}`} horizontal>
<input
type="text"
autoComplete="off"
placeholder={
userContext.apiType === "Mongo"
? "Comma separated paths e.g. firstName,address.zipCode"
: "Comma separated paths e.g. /firstName,/address/zipCode"
}
className="panelTextField"
autoFocus
value={uniqueKey}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
const uniqueKeys = this.state.uniqueKeys.map((uniqueKey: string, j: number) => {
if (i === j) {
return event.target.value;
}
return uniqueKey;
});
this.setState({ uniqueKeys });
}}
/>
{this.state.uniqueKeys.map((uniqueKey: string, i: number): JSX.Element => {
return (
<Stack style={{ marginBottom: 8 }} key={`uniqueKey${i}`} horizontal>
<input
type="text"
autoComplete="off"
placeholder={
userContext.apiType === "Mongo"
? "Comma separated paths e.g. firstName,address.zipCode"
: "Comma separated paths e.g. /firstName,/address/zipCode"
}
className="panelTextField"
autoFocus
value={uniqueKey}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
const uniqueKeys = this.state.uniqueKeys.map((uniqueKey: string, j: number) => {
if (i === j) {
return event.target.value;
}
return uniqueKey;
});
this.setState({ uniqueKeys });
}}
/>
<IconButton
iconProps={{ iconName: "Delete" }}
style={{ height: 27 }}
onClick={() => {
const uniqueKeys = this.state.uniqueKeys.filter((uniqueKey, j) => i !== j);
this.setState({ uniqueKeys });
}}
/>
</Stack>
);
}
)}
<IconButton
iconProps={{ iconName: "Delete" }}
style={{ height: 27 }}
onClick={() => {
const uniqueKeys = this.state.uniqueKeys.filter((uniqueKey, j) => i !== j);
this.setState({ uniqueKeys });
}}
/>
</Stack>
);
})}
<ActionButton
iconProps={{ iconName: "Add" }}
@@ -1059,7 +1060,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
this.setState({ isExecuting: false });
this.props.explorer.refreshAllDatabases();
TelemetryProcessor.traceSuccess(Action.CreateCollection, telemetryData, startKey);
useSidePanel.getState().closeSidePanel();
this.props.closePanel();
} catch (error) {
const errorMessage: string = getErrorMessage(error);
this.setState({ isExecuting: false, errorMessage, showErrorDetails: true });

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

@@ -6,7 +6,6 @@ import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUti
import { InfoTooltip } from "../../../Common/Tooltip/InfoTooltip";
import * as DataModels from "../../../Contracts/DataModels";
import { SubscriptionType } from "../../../Contracts/SubscriptionType";
import { useSidePanel } from "../../../hooks/useSidePanel";
import * as SharedConstants from "../../../Shared/Constants";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
@@ -21,12 +20,15 @@ import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneFor
export interface AddDatabasePaneProps {
explorer: Explorer;
closePanel: () => void;
openNotificationConsole: () => void;
}
export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
explorer: container,
closePanel,
openNotificationConsole,
}: AddDatabasePaneProps) => {
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
let throughput: number;
let isAutoscaleSelected: boolean;
let isCostAcknowledged: boolean;
@@ -112,7 +114,7 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
const _onCreateDatabaseSuccess = (offerThroughput: number, startKey: number): void => {
setIsExecuting(false);
closeSidePanel();
closePanel();
container.refreshAllDatabases();
const addDatabasePaneSuccessMessage = {
...addDatabasePaneMessage,
@@ -161,6 +163,7 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
);
const props: RightPaneFormProps = {
expandConsole: openNotificationConsole,
formError: formErrors,
isExecuting,
submitButtonText: "OK",
@@ -174,6 +177,7 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
message={getUpsellMessage(userContext.portalEnv, true, container.isFirstResourceCreated(), true)}
messageType="info"
showErrorDetails={false}
openNotificationConsole={openNotificationConsole}
link={Constants.Urls.freeTierInformation}
linkText="Learn more"
/>

View File

@@ -2,6 +2,7 @@
exports[`AddDatabasePane Pane should render Default properly 1`] = `
<RightPaneForm
expandConsole={[Function]}
formError=""
isExecuting={false}
onSubmit={[Function]}

View File

@@ -3,7 +3,6 @@ import { Areas } from "../../../Common/Constants";
import { logError } from "../../../Common/Logger";
import { Query } from "../../../Contracts/DataModels";
import { Collection } from "../../../Contracts/ViewModels";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import { trace } from "../../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../../UserContext";
@@ -16,12 +15,13 @@ import QueryTab from "../../Tabs/QueryTab";
interface BrowseQueriesPaneProps {
explorer: Explorer;
closePanel: () => void;
}
export const BrowseQueriesPane: FunctionComponent<BrowseQueriesPaneProps> = ({
explorer,
closePanel,
}: BrowseQueriesPaneProps): JSX.Element => {
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
const loadSavedQuery = (savedQuery: Query): void => {
const selectedCollection: Collection = explorer && explorer.findSelectedCollection();
if (!selectedCollection) {
@@ -43,7 +43,7 @@ export const BrowseQueriesPane: FunctionComponent<BrowseQueriesPaneProps> = ({
queryName: savedQuery.queryName,
paneTitle: "Open Saved Queries",
});
closeSidePanel();
closePanel();
};
const props: QueriesGridComponentProps = {

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"));
});
it("Enter Keyspace name ", () => {
it("Enter Keyspace name", () => {
fireEvent.change(screen.getByLabelText("Keyspace id"), { target: { value: "unittest1" } });
expect(screen.getByLabelText("CREATE TABLE unittest1.")).toBeDefined();
});

View File

@@ -6,7 +6,6 @@ import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUti
import { InfoTooltip } from "../../../Common/Tooltip/InfoTooltip";
import * as DataModels from "../../../Contracts/DataModels";
import * as ViewModels from "../../../Contracts/ViewModels";
import { useSidePanel } from "../../../hooks/useSidePanel";
import * as AddCollectionUtility from "../../../Shared/AddCollectionUtility";
import * as SharedConstants from "../../../Shared/Constants";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
@@ -20,14 +19,15 @@ import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneFor
export interface CassandraAddCollectionPaneProps {
explorer: Explorer;
closePanel: () => void;
cassandraApiClient: CassandraAPIDataClient;
}
export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectionPaneProps> = ({
explorer: container,
closePanel,
cassandraApiClient,
}: CassandraAddCollectionPaneProps) => {
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
const throughputDefaults = userContext.collectionCreationDefaults.throughput;
const [createTableQuery, setCreateTableQuery] = useState<string>("CREATE TABLE ");
const [keyspaceId, setKeyspaceId] = useState<string>("");
@@ -241,7 +241,7 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
}
container.refreshAllDatabases();
setIsExecuting(false);
closeSidePanel();
closePanel();
TelemetryProcessor.traceSuccess(Action.CreateCollection, addCollectionPaneStartMessage, startKey);
} catch (error) {
@@ -261,6 +261,7 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
};
const props: RightPaneFormProps = {
expandConsole: () => container.expandConsole(),
formError: formErrors,
isExecuting,
submitButtonText: "Apply",

View File

@@ -2,6 +2,7 @@
exports[`CassandraAddCollectionPane Pane should render Default properly 1`] = `
<RightPaneForm
expandConsole={[Function]}
formError=""
onSubmit={[Function]}
submitButtonText="Apply"

View File

@@ -0,0 +1,126 @@
import * as ko from "knockout";
import * as ViewModels from "../../Contracts/ViewModels";
import * as Constants from "../../Common/Constants";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import { KeyCodes } from "../../Common/Constants";
import { WaitsForTemplateViewModel } from "../WaitsForTemplateViewModel";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import Explorer from "../Explorer";
// TODO: Use specific actions for logging telemetry data
export abstract class ContextualPaneBase extends WaitsForTemplateViewModel {
private initalFocusedElement: HTMLElement | undefined;
public id: string;
public container: Explorer;
public firstFieldHasFocus: ko.Observable<boolean>;
public formErrorsDetails: ko.Observable<string>;
public formErrors: ko.Observable<string>;
public title: ko.Observable<string>;
public visible: ko.Observable<boolean>;
public isExecuting: ko.Observable<boolean>;
constructor(options: ViewModels.PaneOptions) {
super();
this.id = options.id;
this.container = options.container;
this.visible = options.visible || ko.observable(false);
this.firstFieldHasFocus = ko.observable<boolean>(false);
this.formErrors = ko.observable<string>();
this.title = ko.observable<string>();
this.formErrorsDetails = ko.observable<string>();
this.isExecuting = ko.observable<boolean>(false);
}
public cancel() {
this.close();
this.container.isAccountReady() &&
TelemetryProcessor.trace(Action.ContextualPane, ActionModifiers.Close, {
dataExplorerArea: Constants.Areas.ContextualPane,
paneTitle: this.title(),
});
}
public close() {
this.visible(false);
this.isExecuting(false);
this.resetData();
this.resetFocus();
}
public open() {
this.initalFocusedElement = document.activeElement as HTMLElement;
this.visible(true);
this.firstFieldHasFocus(true);
this.resizePane();
this.container.isAccountReady() &&
TelemetryProcessor.trace(Action.ContextualPane, ActionModifiers.Open, {
dataExplorerArea: Constants.Areas.ContextualPane,
paneTitle: this.title(),
});
}
public resetData() {
this.firstFieldHasFocus(false);
this.formErrors(null);
this.formErrorsDetails(null);
}
public showErrorDetails() {
this.container.expandConsole();
}
public submit() {
this.close();
event.stopPropagation();
this.container.isAccountReady() &&
TelemetryProcessor.trace(Action.ContextualPane, ActionModifiers.Submit, {
dataExplorerArea: Constants.Areas.ContextualPane,
paneTitle: this.title(),
});
}
public onCloseKeyPress(source: any, event: KeyboardEvent): boolean {
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
this.close();
event.stopPropagation();
return false;
}
return true;
}
public onPaneKeyDown(source: any, event: KeyboardEvent): boolean {
if (event.keyCode === KeyCodes.Escape) {
this.close();
event.stopPropagation();
return false;
}
return true;
}
public onSubmitKeyPress(source: any, event: KeyboardEvent): boolean {
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
this.submit();
event.stopPropagation();
return false;
}
return true;
}
private resizePane(): void {
const paneElement: HTMLElement = document.getElementById(this.id);
const notificationConsoleElement: HTMLElement = document.getElementById("explorerNotificationConsole");
const newPaneElementHeight = window.innerHeight - $(notificationConsoleElement).height();
$(paneElement).height(newPaneElementHeight);
}
private resetFocus(): void {
if (this.initalFocusedElement) {
this.initalFocusedElement.focus();
this.initalFocusedElement = undefined;
}
}
}

View File

@@ -3,7 +3,6 @@ import React, { FormEvent, FunctionComponent, useEffect, useState } from "react"
import { HttpStatusCodes } from "../../../Common/Constants";
import { getErrorMessage, handleError } from "../../../Common/ErrorHandlingUtils";
import { GitHubOAuthService } from "../../../GitHub/GitHubOAuthService";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { IPinnedRepo, JunoClient } from "../../../Juno/JunoClient";
import * as GitHubUtils from "../../../Utils/GitHubUtils";
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
@@ -27,6 +26,7 @@ export interface CopyNotebookPanelProps {
container: Explorer;
junoClient: JunoClient;
gitHubOAuthService: GitHubOAuthService;
closePanel: () => void;
}
export const CopyNotebookPane: FunctionComponent<CopyNotebookPanelProps> = ({
@@ -35,8 +35,8 @@ export const CopyNotebookPane: FunctionComponent<CopyNotebookPanelProps> = ({
container,
junoClient,
gitHubOAuthService,
closePanel,
}: CopyNotebookPanelProps) => {
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
const [isExecuting, setIsExecuting] = useState<boolean>();
const [formError, setFormError] = useState<string>("");
const [pinnedRepos, setPinnedRepos] = useState<IPinnedRepo[]>();
@@ -84,7 +84,7 @@ export const CopyNotebookPane: FunctionComponent<CopyNotebookPanelProps> = ({
}
NotificationConsoleUtils.logConsoleInfo(`Successfully copied ${name} to ${destination}`);
closeSidePanel();
closePanel();
} catch (error) {
const errorMessage = getErrorMessage(error);
setFormError(`Failed to copy ${name} to ${destination}`);
@@ -130,6 +130,7 @@ export const CopyNotebookPane: FunctionComponent<CopyNotebookPanelProps> = ({
isExecuting: isExecuting,
submitButtonText: "OK",
onSubmit: () => submit(),
expandConsole: () => container.expandConsole(),
};
const copyNotebookPaneProps: CopyNotebookPaneProps = {

View File

@@ -5,7 +5,6 @@ import { deleteCollection } from "../../../Common/dataAccess/deleteCollection";
import DeleteFeedback from "../../../Common/DeleteFeedback";
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
import { Collection } from "../../../Contracts/ViewModels";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { DefaultExperienceUtility } from "../../../Shared/DefaultExperienceUtility";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
@@ -16,12 +15,13 @@ import Explorer from "../../Explorer";
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
export interface DeleteCollectionConfirmationPaneProps {
explorer: Explorer;
closePanel: () => void;
}
export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectionConfirmationPaneProps> = ({
explorer,
closePanel,
}: DeleteCollectionConfirmationPaneProps) => {
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
const [deleteCollectionFeedback, setDeleteCollectionFeedback] = useState<string>("");
const [inputCollectionName, setInputCollectionName] = useState<string>("");
const [formError, setFormError] = useState<string>("");
@@ -79,7 +79,7 @@ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectio
});
}
closeSidePanel();
closePanel();
} catch (error) {
const errorMessage = getErrorMessage(error);
@@ -102,6 +102,7 @@ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectio
isExecuting,
submitButtonText: "OK",
onSubmit,
expandConsole: () => explorer.expandConsole(),
};
return (
<RightPaneForm {...props}>

View File

@@ -16,6 +16,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
}
>
<RightPaneForm
expandConsole={[Function]}
formError=""
isExecuting={false}
onSubmit={[Function]}

View File

@@ -6,7 +6,6 @@ import { deleteDatabase } from "../../Common/dataAccess/deleteDatabase";
import DeleteFeedback from "../../Common/DeleteFeedback";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
import { Collection, Database } from "../../Contracts/ViewModels";
import { useSidePanel } from "../../hooks/useSidePanel";
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
@@ -18,14 +17,17 @@ import { RightPaneForm, RightPaneFormProps } from "./RightPaneForm/RightPaneForm
interface DeleteDatabaseConfirmationPanelProps {
explorer: Explorer;
closePanel: () => void;
openNotificationConsole: () => void;
selectedDatabase: Database;
}
export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseConfirmationPanelProps> = ({
explorer,
openNotificationConsole,
closePanel,
selectedDatabase,
}: DeleteDatabaseConfirmationPanelProps): JSX.Element => {
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false);
const [formError, setFormError] = useState<string>("");
@@ -49,7 +51,7 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
try {
await deleteDatabase(selectedDatabase.id());
closeSidePanel();
closePanel();
explorer.refreshAllDatabases();
explorer.tabsManager.closeTabsByComparator((tab) => tab.node?.id() === selectedDatabase.id());
explorer.selectedNode(undefined);
@@ -109,6 +111,7 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
isExecuting: isLoading,
submitButtonText: "OK",
onSubmit: () => submit(),
expandConsole: openNotificationConsole,
};
const errorProps: PanelInfoErrorProps = {

View File

@@ -1,15 +1,16 @@
import { IDropdownOption, IImageProps, Image, Stack, Text } from "@fluentui/react";
import { useBoolean } from "@fluentui/react-hooks";
import AddPropertyIcon from "images/Add-property.svg";
import React, { FunctionComponent, useState } from "react";
import AddPropertyIcon from "../../../../images/Add-property.svg";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { logConsoleError } from "../../../Utils/NotificationConsoleUtils";
import StoredProcedure from "../../Tree/StoredProcedure";
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
import { InputParameter } from "./InputParameter";
interface ExecuteSprocParamsPaneProps {
expandConsole: () => void;
storedProcedure: StoredProcedure;
closePanel: () => void;
}
const imageProps: IImageProps = {
@@ -23,9 +24,10 @@ interface UnwrappedExecuteSprocParam {
}
export const ExecuteSprocParamsPane: FunctionComponent<ExecuteSprocParamsPaneProps> = ({
expandConsole,
storedProcedure,
closePanel,
}: ExecuteSprocParamsPaneProps): JSX.Element => {
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false);
const [paramKeyValues, setParamKeyValues] = useState<UnwrappedExecuteSprocParam[]>([{ key: "string", text: "" }]);
const [partitionValue, setPartitionValue] = useState<string>(); // Defaulting to undefined here is important. It is not the same partition key as ""
@@ -74,7 +76,7 @@ export const ExecuteSprocParamsPane: FunctionComponent<ExecuteSprocParamsPanePro
});
storedProcedure.execute(sprocParams, partitionKey === "custom" ? JSON.parse(partitionValue) : partitionValue);
setLoadingFalse();
closeSidePanel();
closePanel();
};
const deleteParamAtIndex = (indexToRemove: number): void => {
@@ -112,6 +114,7 @@ export const ExecuteSprocParamsPane: FunctionComponent<ExecuteSprocParamsPanePro
};
const props: RightPaneFormProps = {
expandConsole,
formError: formError,
isExecuting: isLoading,
submitButtonText: "Execute",

View File

@@ -1,3 +1,5 @@
import AddPropertyIcon from "images/Add-property.svg";
import EntityCancelIcon from "images/Entity_cancel.svg";
import {
Dropdown,
IDropdownOption,
@@ -9,8 +11,6 @@ import {
TextField,
} from "@fluentui/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 options = [

View File

@@ -7,6 +7,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
storedProcedure={Object {}}
>
<RightPaneForm
expandConsole={[Function]}
formError=""
isExecuting={false}
onSubmit={[Function]}

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

@@ -2,7 +2,6 @@ import React from "react";
import { Areas, HttpStatusCodes } from "../../../Common/Constants";
import { handleError } from "../../../Common/ErrorHandlingUtils";
import { GitHubClient, IGitHubPageInfo, IGitHubRepo } from "../../../GitHub/GitHubClient";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { IPinnedRepo, JunoClient } from "../../../Juno/JunoClient";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
@@ -22,8 +21,10 @@ import { PanelLoadingScreen } from "../PanelLoadingScreen";
interface IGitHubReposPanelProps {
explorer: Explorer;
closePanel: () => void;
gitHubClientProp: GitHubClient;
junoClientProp: JunoClient;
openNotificationConsole: () => void;
}
interface IGitHubReposPanelState {
@@ -90,7 +91,7 @@ export class GitHubReposPanel extends React.Component<IGitHubReposPanelProps, IG
},
resetConnection: (): void => this.setup(true),
onOkClick: (): Promise<void> => this.submit(),
onCancelClick: (): void => useSidePanel.getState().closeSidePanel(),
onCancelClick: (): void => this.props.closePanel(),
},
};
this.gitHubClient = this.props.gitHubClientProp;
@@ -438,6 +439,7 @@ export class GitHubReposPanel extends React.Component<IGitHubReposPanelProps, IG
message={this.state.errorMessage}
messageType="error"
showErrorDetails={this.state.showErrorDetails}
openNotificationConsole={this.props.openNotificationConsole}
/>
)}
<div className="panelMainContent" style={ContentMainStyle}>

View File

@@ -20,6 +20,7 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
"_isInitializingNotebooks": false,
"_resetNotebookWorkspace": [Function],
"canSaveQueries": [Function],
"closeSidePanel": undefined,
"collapsedResourceTreeWidth": 36,
"commandBarComponentAdapter": CommandBarComponentAdapter {
"container": [Circular],
@@ -44,6 +45,7 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
"notebookServerInfo": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"openSidePanel": undefined,
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {
"container": [Circular],
@@ -68,6 +70,7 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
"selectedDatabaseId": [Function],
"selectedNode": [Function],
"setInProgressConsoleDataIdToBeDeleted": undefined,
"setIsNotificationConsoleExpanded": undefined,
"setNotificationConsoleData": undefined,
"sparkClusterConnectionInfo": [Function],
"splitter": Splitter {

View File

@@ -0,0 +1,59 @@
<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 }">
<!-- Graph Styling form - Start -->
<div class="contextual-pane-in">
<form class="paneContentContainer" data-bind="submit: submit">
<!-- Graph Styling header - Start -->
<div class="firstdivbg headerline">
<span role="heading" aria-level="2">Graph Styling</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>
<!-- Graph Styling header - End -->
<!-- Graph Styling errors - Start -->
<div
aria-live="assertive"
class="warningErrorContainer"
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"
>
More details
</a>
</span>
</div>
</div>
<!-- Graph Styling errors - End -->
<!-- Add graph configuration - Start -->
<div class="paneMainContent">
<graph-style
id="graphStyleComponent"
params="{ config:graphConfigUIData, firstFieldHasFocus: firstFieldHasFocus }"
></graph-style>
</div>
<div class="paneFooter">
<div class="leftpanel-okbut"><input type="submit" value="OK" class="btncreatecoll1" /></div>
</div>
<!-- Add Graph configuration - End -->
</form>
</div>
<!-- Graph Styling form - End -->
</div>
</div>

View File

@@ -1,26 +1,25 @@
import React, { FunctionComponent } from "react";
import * as ViewModels from "../../../Contracts/ViewModels";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { GraphStyleComponent } from "../../Graph/GraphStyleComponent/GraphStyleComponent";
import { IGraphConfig } from "../../Tabs/GraphTab";
import { PanelFooterComponent } from "../PanelFooterComponent";
interface GraphStylingProps {
closePanel: () => void;
igraphConfigUiData: ViewModels.IGraphConfigUiData;
igraphConfig: IGraphConfig;
getValues: (igraphConfig?: IGraphConfig) => void;
}
export const GraphStylingPanel: FunctionComponent<GraphStylingProps> = ({
closePanel,
igraphConfigUiData,
igraphConfig,
getValues,
}: GraphStylingProps): JSX.Element => {
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
const buttonLabel = "Ok";
const submit = () => {
closeSidePanel();
closePanel();
};
return (

View File

@@ -1,10 +1,9 @@
import { IImageProps, Image, ImageFit, Stack, TextField } from "@fluentui/react";
import { useBoolean } from "@fluentui/react-hooks";
import folderIcon from "images/folder_16x16.svg";
import React, { FunctionComponent, useState } from "react";
import folderIcon from "../../../../images/folder_16x16.svg";
import { logError } from "../../../Common/Logger";
import { Collection } from "../../../Contracts/ViewModels";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { userContext } from "../../../UserContext";
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../../Utils/NotificationConsoleUtils";
import Explorer from "../../Explorer";
@@ -13,10 +12,13 @@ import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneFor
interface LoadQueryPaneProps {
explorer: Explorer;
closePanel: () => void;
}
export const LoadQueryPane: FunctionComponent<LoadQueryPaneProps> = ({ explorer }: LoadQueryPaneProps): JSX.Element => {
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
export const LoadQueryPane: FunctionComponent<LoadQueryPaneProps> = ({
explorer,
closePanel,
}: LoadQueryPaneProps): JSX.Element => {
const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false);
const [formError, setFormError] = useState<string>("");
const [selectedFileName, setSelectedFileName] = useState<string>("");
@@ -49,7 +51,7 @@ export const LoadQueryPane: FunctionComponent<LoadQueryPaneProps> = ({ explorer
try {
await loadQueryFromFile(file);
logConsoleInfo(`Successfully loaded query from file ${file.name}`);
closeSidePanel();
closePanel();
setLoadingFalse();
} catch (error) {
setLoadingFalse();
@@ -87,6 +89,7 @@ export const LoadQueryPane: FunctionComponent<LoadQueryPaneProps> = ({ explorer
isExecuting: isLoading,
submitButtonText: "Load",
onSubmit: () => submit(),
expandConsole: () => explorer.expandConsole(),
};
return (

View File

@@ -2,6 +2,7 @@
exports[`Load Query Pane should render Default properly 1`] = `
<RightPaneForm
expandConsole={[Function]}
formError=""
isExecuting={false}
onSubmit={[Function]}

View File

@@ -36,7 +36,14 @@ describe("New Vertex Panel", () => {
it("should call form submit method", () => {
const onSubmitSpy = jest.fn();
const newWrapper = mount(<NewVertexPanel partitionKeyPropertyProp={undefined} onSubmit={onSubmitSpy} />);
const newWrapper = mount(
<NewVertexPanel
explorer={fakeExplorer}
partitionKeyPropertyProp={undefined}
openNotificationConsole={(): void => undefined}
onSubmit={onSubmitSpy}
/>
);
//eslint-disable-next-line
newWrapper.find("form").simulate("submit", { preventDefault: () => {} });
@@ -54,7 +61,14 @@ describe("New Vertex Panel", () => {
const result = onSubmitSpy(fakeNewVertexData, onErrorSpy, onSuccessSpy);
const newWrapper = mount(<NewVertexPanel partitionKeyPropertyProp={undefined} onSubmit={onSubmitSpy} />);
const newWrapper = mount(
<NewVertexPanel
explorer={fakeExplorer}
partitionKeyPropertyProp={undefined}
openNotificationConsole={(): void => undefined}
onSubmit={onSubmitSpy}
/>
);
//eslint-disable-next-line
newWrapper.find("form").simulate("submit", { preventDefault: () => {} });

View File

@@ -1,17 +1,21 @@
import { useBoolean } from "@fluentui/react-hooks";
import React, { FunctionComponent, useState } from "react";
import * as ViewModels from "../../../Contracts/ViewModels";
import { useSidePanel } from "../../../hooks/useSidePanel";
import Explorer from "../../Explorer";
import { NewVertexComponent } from "../../Graph/NewVertexComponent/NewVertexComponent";
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
export interface INewVertexPanelProps {
explorer: Explorer;
partitionKeyPropertyProp: string;
onSubmit: (result: ViewModels.NewVertexData, onError: (errorMsg: string) => void, onSuccess: () => void) => void;
openNotificationConsole: () => void;
}
export const NewVertexPanel: FunctionComponent<INewVertexPanelProps> = ({
explorer,
partitionKeyPropertyProp,
onSubmit,
openNotificationConsole,
}: INewVertexPanelProps): JSX.Element => {
let newVertexDataValue: ViewModels.NewVertexData;
const [errorMessage, setErrorMessage] = useState<string>("");
@@ -29,10 +33,10 @@ export const NewVertexPanel: FunctionComponent<INewVertexPanelProps> = ({
setErrorMessage(errorMsg);
setLoadingFalse();
};
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
const onSuccess = () => {
setLoadingFalse();
closeSidePanel();
explorer.closeSidePanel();
};
const onChange = (newVertexData: ViewModels.NewVertexData) => {
@@ -43,6 +47,7 @@ export const NewVertexPanel: FunctionComponent<INewVertexPanelProps> = ({
isExecuting: isLoading,
submitButtonText: "OK",
onSubmit: () => submit(),
expandConsole: openNotificationConsole,
};
return (

View File

@@ -2,6 +2,7 @@
exports[`New Vertex Panel should render default property 1`] = `
<RightPaneForm
expandConsole={[Function]}
formError=""
isExecuting={false}
onSubmit={[Function]}

View File

@@ -9,6 +9,7 @@ describe("PaneContainerComponent test", () => {
panelContent: <div></div>,
isOpen: true,
isConsoleExpanded: false,
closePanel: undefined,
};
const wrapper = shallow(<PanelContainerComponent {...panelContainerProps} />);
expect(wrapper).toMatchSnapshot();
@@ -20,6 +21,7 @@ describe("PaneContainerComponent test", () => {
panelContent: undefined,
isOpen: true,
isConsoleExpanded: false,
closePanel: undefined,
};
const wrapper = shallow(<PanelContainerComponent {...panelContainerProps} />);
expect(wrapper).toMatchSnapshot();
@@ -31,6 +33,7 @@ describe("PaneContainerComponent test", () => {
panelContent: <div></div>,
isOpen: true,
isConsoleExpanded: true,
closePanel: undefined,
};
const wrapper = shallow(<PanelContainerComponent {...panelContainerProps} />);
expect(wrapper).toMatchSnapshot();

View File

@@ -1,13 +1,12 @@
import { IPanelProps, IRenderFunction, Panel, PanelType } from "@fluentui/react";
import * as React from "react";
import { useNotificationConsole } from "../../hooks/useNotificationConsole";
import { useSidePanel } from "../../hooks/useSidePanel";
export interface PanelContainerProps {
headerText: string;
panelContent: JSX.Element;
isConsoleExpanded: boolean;
isOpen: boolean;
closePanel: () => void;
panelWidth?: string;
onRenderNavigationContent?: IRenderFunction<IPanelProps>;
}
@@ -70,7 +69,7 @@ export class PanelContainerComponent extends React.Component<PanelContainerProps
if ((ev.target as HTMLElement).id === "notificationConsoleHeader") {
ev.preventDefault();
} else {
useSidePanel.getState().closeSidePanel();
this.props.closePanel();
}
};
@@ -82,24 +81,3 @@ export class PanelContainerComponent extends React.Component<PanelContainerProps
return panelHeight + "px";
};
}
export const SidePanel: React.FC = () => {
const isConsoleExpanded = useNotificationConsole((state) => state.isExpanded);
const { isOpen, panelContent, headerText } = useSidePanel((state) => {
return {
isOpen: state.isOpen,
panelContent: state.panelContent,
headerText: state.headerText,
};
});
// TODO Refactor PanelContainerComponent into a functional component and remove this wrapper
// This component only exists so we can use hooks and pass them down to a non-functional component
return (
<PanelContainerComponent
isOpen={isOpen}
panelContent={panelContent}
headerText={headerText}
isConsoleExpanded={isConsoleExpanded}
/>
);
};

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