mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-26 12:21:23 +00:00
Compare commits
27 Commits
users/srna
...
update_to_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf3082d30d | ||
|
|
dbfa7e37ed | ||
|
|
1bf00f65b8 | ||
|
|
a20620db0e | ||
|
|
5724ed81f9 | ||
|
|
349a54e078 | ||
|
|
bf9fdb95e9 | ||
|
|
b4461ddf5d | ||
|
|
349a4bb0f2 | ||
|
|
5f977b9419 | ||
|
|
adce85efd4 | ||
|
|
292d2f3be4 | ||
|
|
5613c822db | ||
|
|
3c320167d8 | ||
|
|
2c58cd7be1 | ||
|
|
cc4f20e482 | ||
|
|
10aa097166 | ||
|
|
9e7252dfeb | ||
|
|
d8dff644d5 | ||
|
|
2855bf4c7d | ||
|
|
4ffea1bad9 | ||
|
|
3cdc30864b | ||
|
|
f4a322d17d | ||
|
|
8e033bdd73 | ||
|
|
7d2315f282 | ||
|
|
5b467e239a | ||
|
|
d65600dd14 |
@@ -44,6 +44,7 @@ src/Definitions/png.d.ts
|
||||
src/Definitions/svg.d.ts
|
||||
src/Explorer/ComponentRegisterer.test.ts
|
||||
src/Explorer/ComponentRegisterer.ts
|
||||
src/Explorer/ContextMenuButtonFactory.ts
|
||||
src/Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.ts
|
||||
src/Explorer/Controls/DiffEditor/DiffEditorComponent.ts
|
||||
src/Explorer/Controls/DynamicList/DynamicList.test.ts
|
||||
@@ -104,10 +105,15 @@ src/Explorer/Notebook/NotebookContainerClient.ts
|
||||
src/Explorer/Notebook/NotebookContentClient.ts
|
||||
src/Explorer/Notebook/NotebookContentItem.ts
|
||||
src/Explorer/Notebook/NotebookUtil.ts
|
||||
src/Explorer/OpenActions.test.ts
|
||||
src/Explorer/OpenActions.ts
|
||||
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
|
||||
@@ -132,6 +138,7 @@ src/Explorer/Tables/QueryBuilder/ClauseGroupViewModel.ts
|
||||
src/Explorer/Tables/QueryBuilder/CustomTimestampHelper.ts
|
||||
src/Explorer/Tables/QueryBuilder/QueryBuilderViewModel.ts
|
||||
src/Explorer/Tables/QueryBuilder/QueryClauseViewModel.ts
|
||||
src/Explorer/Tables/QueryBuilder/QueryViewModel.ts
|
||||
src/Explorer/Tables/TableDataClient.ts
|
||||
src/Explorer/Tables/TableEntityProcessor.ts
|
||||
src/Explorer/Tables/Utilities.ts
|
||||
@@ -141,11 +148,14 @@ src/Explorer/Tabs/DocumentsTab.test.ts
|
||||
src/Explorer/Tabs/DocumentsTab.ts
|
||||
src/Explorer/Tabs/GraphTab.ts
|
||||
src/Explorer/Tabs/MongoDocumentsTab.ts
|
||||
# src/Explorer/Tabs/MongoQueryTab.ts
|
||||
# src/Explorer/Tabs/MongoShellTab.ts
|
||||
src/Explorer/Tabs/MongoQueryTab.ts
|
||||
src/Explorer/Tabs/MongoShellTab.ts
|
||||
src/Explorer/Tabs/NotebookV2Tab.ts
|
||||
src/Explorer/Tabs/QueryTab.test.ts
|
||||
src/Explorer/Tabs/QueryTab.ts
|
||||
src/Explorer/Tabs/QueryTablesTab.ts
|
||||
src/Explorer/Tabs/ScriptTabBase.ts
|
||||
# src/Explorer/Tabs/StoredProcedureTab.ts
|
||||
src/Explorer/Tabs/StoredProcedureTab.ts
|
||||
src/Explorer/Tabs/TabComponents.ts
|
||||
src/Explorer/Tabs/TabsBase.ts
|
||||
src/Explorer/Tabs/TriggerTab.ts
|
||||
@@ -198,6 +208,9 @@ src/ResourceProvider/IResourceProviderClient.test.ts
|
||||
src/ResourceProvider/IResourceProviderClient.ts
|
||||
src/ResourceProvider/ResourceProviderClient.ts
|
||||
src/ResourceProvider/ResourceProviderClientFactory.ts
|
||||
src/RouteHandlers/RouteHandler.ts
|
||||
src/RouteHandlers/TabRouteHandler.test.ts
|
||||
src/RouteHandlers/TabRouteHandler.ts
|
||||
src/Shared/Constants.ts
|
||||
src/Shared/DefaultExperienceUtility.test.ts
|
||||
src/Shared/DefaultExperienceUtility.ts
|
||||
@@ -253,6 +266,7 @@ src/Explorer/Graph/GraphExplorerComponent/EditorNodePropertiesComponent.test.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/EditorNodePropertiesComponent.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.test.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/GraphExplorerAdapter.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/GraphVizComponent.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/LeftPaneComponent.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/MiddlePaneComponent.tsx
|
||||
|
||||
@@ -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
|
||||
|
||||
2
.github/workflows/cleanup.yml
vendored
2
.github/workflows/cleanup.yml
vendored
@@ -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:
|
||||
|
||||
@@ -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 }]],
|
||||
};
|
||||
|
||||
@@ -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 |
@@ -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"],
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -724,24 +724,45 @@ execute-sproc-params-pane {
|
||||
|
||||
.results-container,
|
||||
.errors-container {
|
||||
padding: @MediumSpace 0px 0px @MediumSpace;
|
||||
height: 100%;
|
||||
.flex-display();
|
||||
.flex-direction();
|
||||
overflow: hidden;
|
||||
|
||||
.enterInputParameters {
|
||||
padding: @LargeSpace @MediumSpace;
|
||||
.toggles {
|
||||
height: @ToggleHeight;
|
||||
width: @ToggleWidth;
|
||||
margin-left: @MediumSpace;
|
||||
|
||||
&:focus {
|
||||
.focus();
|
||||
}
|
||||
|
||||
.tab {
|
||||
margin-right: @MediumSpace;
|
||||
}
|
||||
|
||||
.toggleSwitch {
|
||||
.toggleSwitch();
|
||||
}
|
||||
|
||||
.selectedToggle {
|
||||
.selectedToggle();
|
||||
}
|
||||
|
||||
.unselectedToggle {
|
||||
.unselectedToggle();
|
||||
}
|
||||
}
|
||||
|
||||
div[role="tabpanel"] {
|
||||
height: 100%;
|
||||
padding-bottom: 50px;
|
||||
.enterInputParameters {
|
||||
padding: @LargeSpace @MediumSpace;
|
||||
}
|
||||
}
|
||||
|
||||
.errors-container {
|
||||
padding-left: (2 * @MediumSpace);
|
||||
padding: @MediumSpace 0px 0px @MediumSpace;
|
||||
.errors-header {
|
||||
font-weight: 700;
|
||||
font-size: @DefaultFontSize;
|
||||
@@ -1292,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;
|
||||
@@ -1300,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;
|
||||
@@ -1308,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;
|
||||
@@ -1316,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;
|
||||
@@ -1324,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;
|
||||
@@ -1332,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;
|
||||
@@ -1340,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;
|
||||
@@ -1348,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;
|
||||
@@ -2686,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;
|
||||
@@ -3067,11 +3088,4 @@ settings-pane {
|
||||
.hiddenMain {
|
||||
display: none;
|
||||
height: 0px;
|
||||
}
|
||||
.spinner {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
background: white;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
@@ -200,12 +200,4 @@
|
||||
|
||||
.migration:disabled {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
.trigger-field {
|
||||
width: 40%;
|
||||
margin-top: 10px
|
||||
}
|
||||
.trigger-form {
|
||||
padding: 10px 30px 10px 30px;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
1318
less/quickstart.less
1318
less/quickstart.less
File diff suppressed because it is too large
Load Diff
10596
package-lock.json
generated
10596
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
157
package.json
157
package.json
@@ -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",
|
||||
@@ -22,7 +22,7 @@
|
||||
"@nteract/data-explorer": "8.0.3",
|
||||
"@nteract/directory-listing": "2.0.6",
|
||||
"@nteract/dropdown-menu": "1.0.1",
|
||||
"@nteract/editor": "10.1.12",
|
||||
"@nteract/editor": "10.1.2",
|
||||
"@nteract/fixtures": "2.3.0",
|
||||
"@nteract/iron-icons": "1.0.0",
|
||||
"@nteract/jupyter-widgets": "2.0.0",
|
||||
@@ -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,18 +55,18 @@
|
||||
"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",
|
||||
@@ -80,110 +76,117 @@
|
||||
"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",
|
||||
"react-splitter-layout": "4.0.0",
|
||||
"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/react-splitter-layout": "3.0.1",
|
||||
"@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",
|
||||
"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.3.4",
|
||||
"url-loader": "1.1.1",
|
||||
"typescript": "4.2.4",
|
||||
"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",
|
||||
@@ -227,4 +230,4 @@
|
||||
"prettier": {
|
||||
"printWidth": 120
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
{
|
||||
"PROXY_PATH": "/proxy",
|
||||
"msalRedirectURI": "https://cosmos-explorer-preview.azurewebsites.net/"
|
||||
"PROXY_PATH": "/proxy"
|
||||
}
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -158,6 +158,16 @@ export class DocumentsGridMetrics {
|
||||
public static DocumentEditorMaxWidthRatio: number = 0.4;
|
||||
}
|
||||
|
||||
export class ExplorerMetrics {
|
||||
public static SplitterMinWidth: number = 240;
|
||||
public static SplitterMaxWidth: number = 400;
|
||||
public static CollapsedResourceTreeWidth: number = 36;
|
||||
}
|
||||
|
||||
export class SplitterMetrics {
|
||||
public static CollapsedPositionLeft: number = ExplorerMetrics.CollapsedResourceTreeWidth;
|
||||
}
|
||||
|
||||
export class Areas {
|
||||
public static ResourceTree: string = "Resource Tree";
|
||||
public static ContextualPane: string = "Contextual Pane";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Collection } from "../Contracts/ViewModels";
|
||||
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||
import { updateUserContext } from "../UserContext";
|
||||
import { deleteDocument, getEndpoint, queryDocuments, readDocument, updateDocument } from "./MongoProxyClient";
|
||||
jest.mock("../ResourceProvider/ResourceProviderClient.ts");
|
||||
|
||||
const databaseId = "testDB";
|
||||
|
||||
@@ -30,7 +31,7 @@ const collection = {
|
||||
},
|
||||
} as Collection;
|
||||
|
||||
const documentId = ({
|
||||
const documentId = {
|
||||
partitionKeyHeader: () => "[]",
|
||||
self: "db/testDB/db/testCollection/docs/testId",
|
||||
partitionKeyProperty,
|
||||
@@ -39,7 +40,7 @@ const documentId = ({
|
||||
kind: "Hash",
|
||||
version: 1,
|
||||
},
|
||||
} as unknown) as DocumentId;
|
||||
} as unknown as DocumentId;
|
||||
|
||||
const databaseAccount = {
|
||||
id: "foo",
|
||||
|
||||
@@ -111,7 +111,7 @@ export function queryDocuments(
|
||||
headers: response.headers,
|
||||
};
|
||||
}
|
||||
await errorHandling(response, "querying documents", params);
|
||||
errorHandling(response, "querying documents", params);
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
@@ -153,11 +153,11 @@ export function readDocument(
|
||||
),
|
||||
},
|
||||
})
|
||||
.then(async (response) => {
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
return await errorHandling(response, "reading document", params);
|
||||
return errorHandling(response, "reading document", params);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -192,11 +192,11 @@ export function createDocument(
|
||||
...authHeaders(),
|
||||
},
|
||||
})
|
||||
.then(async (response) => {
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
return await errorHandling(response, "creating document", params);
|
||||
return errorHandling(response, "creating document", params);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -238,11 +238,11 @@ export function updateDocument(
|
||||
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
|
||||
},
|
||||
})
|
||||
.then(async (response) => {
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
return await errorHandling(response, "updating document", params);
|
||||
return errorHandling(response, "updating document", params);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -278,11 +278,11 @@ export function deleteDocument(databaseId: string, collection: Collection, docum
|
||||
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
|
||||
},
|
||||
})
|
||||
.then(async (response) => {
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
return undefined;
|
||||
}
|
||||
return await errorHandling(response, "deleting document", params);
|
||||
return errorHandling(response, "deleting document", params);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
@@ -325,11 +325,11 @@ export function createMongoCollectionWithProxy(
|
||||
},
|
||||
}
|
||||
)
|
||||
.then(async (response) => {
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
return await errorHandling(response, "creating collection", mongoParams);
|
||||
return errorHandling(response, "creating collection", mongoParams);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import { ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||
import * as _ from "underscore";
|
||||
import * as DataModels from "../Contracts/DataModels";
|
||||
import * as ViewModels from "../Contracts/ViewModels";
|
||||
import Explorer from "../Explorer/Explorer";
|
||||
import DocumentsTab from "../Explorer/Tabs/DocumentsTab";
|
||||
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||
import { useDatabases } from "../Explorer/useDatabases";
|
||||
import { userContext } from "../UserContext";
|
||||
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
||||
import * as QueryUtils from "../Utils/QueryUtils";
|
||||
import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
|
||||
import { createCollection } from "./dataAccess/createCollection";
|
||||
import { createDocument } from "./dataAccess/createDocument";
|
||||
import { deleteDocument } from "./dataAccess/deleteDocument";
|
||||
import { queryDocuments } from "./dataAccess/queryDocuments";
|
||||
import { queryDocumentsPage } from "./dataAccess/queryDocumentsPage";
|
||||
import { handleError } from "./ErrorHandlingUtils";
|
||||
|
||||
export class QueriesClient {
|
||||
@@ -98,35 +100,45 @@ export class QueriesClient {
|
||||
|
||||
const options: any = { enableCrossPartitionQuery: true };
|
||||
const clearMessage = NotificationConsoleUtils.logConsoleProgress("Fetching saved queries");
|
||||
const results = await queryDocuments(
|
||||
const queryIterator: QueryIterator<ItemDefinition & Resource> = queryDocuments(
|
||||
SavedQueries.DatabaseName,
|
||||
SavedQueries.CollectionName,
|
||||
this.fetchQueriesQuery(),
|
||||
options
|
||||
).fetchAll();
|
||||
|
||||
let queries: DataModels.Query[] = _.map(results.resources, (document: DataModels.Query) => {
|
||||
if (!document) {
|
||||
return undefined;
|
||||
}
|
||||
const { id, resourceId, query, queryName } = document;
|
||||
const parsedQuery: DataModels.Query = {
|
||||
resourceId: resourceId,
|
||||
queryName: queryName,
|
||||
query: query,
|
||||
id: id,
|
||||
};
|
||||
try {
|
||||
this.validateQuery(parsedQuery);
|
||||
return parsedQuery;
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
queries = _.reject(queries, (parsedQuery: DataModels.Query) => !parsedQuery);
|
||||
NotificationConsoleUtils.logConsoleInfo("Successfully fetched saved queries");
|
||||
clearMessage();
|
||||
return queries;
|
||||
);
|
||||
const fetchQueries = async (firstItemIndex: number): Promise<ViewModels.QueryResults> =>
|
||||
await queryDocumentsPage(queriesCollection.id(), queryIterator, firstItemIndex);
|
||||
return QueryUtils.queryAllPages(fetchQueries)
|
||||
.then(
|
||||
(results: ViewModels.QueryResults) => {
|
||||
let queries: DataModels.Query[] = _.map(results.documents, (document: DataModels.Query) => {
|
||||
if (!document) {
|
||||
return undefined;
|
||||
}
|
||||
const { id, resourceId, query, queryName } = document;
|
||||
const parsedQuery: DataModels.Query = {
|
||||
resourceId: resourceId,
|
||||
queryName: queryName,
|
||||
query: query,
|
||||
id: id,
|
||||
};
|
||||
try {
|
||||
this.validateQuery(parsedQuery);
|
||||
return parsedQuery;
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
queries = _.reject(queries, (parsedQuery: DataModels.Query) => !parsedQuery);
|
||||
NotificationConsoleUtils.logConsoleInfo("Successfully fetched saved queries");
|
||||
return Promise.resolve(queries);
|
||||
},
|
||||
(error: any) => {
|
||||
handleError(error, "getSavedQueries", "Failed to fetch saved queries");
|
||||
return Promise.reject(error);
|
||||
}
|
||||
)
|
||||
.finally(() => clearMessage());
|
||||
}
|
||||
|
||||
public async deleteQuery(query: DataModels.Query): Promise<void> {
|
||||
@@ -177,7 +189,7 @@ export class QueriesClient {
|
||||
|
||||
private findQueriesCollection(): ViewModels.Collection {
|
||||
const queriesDatabase: ViewModels.Database = _.find(
|
||||
useDatabases.getState().databases,
|
||||
this.container.databases(),
|
||||
(database: ViewModels.Database) => database.id() === SavedQueries.DatabaseName
|
||||
);
|
||||
if (!queriesDatabase) {
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import * as ko from "knockout";
|
||||
|
||||
import { SplitterMetrics } from "./Constants";
|
||||
|
||||
export enum SplitterDirection {
|
||||
Horizontal = "horizontal",
|
||||
Vertical = "vertical",
|
||||
@@ -24,12 +28,14 @@ export class Splitter {
|
||||
public lastX!: number;
|
||||
public lastWidth!: number;
|
||||
|
||||
private isCollapsed: ko.Observable<boolean>;
|
||||
private bounds: SplitterBounds;
|
||||
private direction: SplitterDirection;
|
||||
|
||||
constructor(options: SplitterOptions) {
|
||||
this.splitterId = options.splitterId;
|
||||
this.leftSideId = options.leftId;
|
||||
this.isCollapsed = ko.observable<boolean>(false);
|
||||
this.bounds = options.bounds;
|
||||
this.direction = options.direction;
|
||||
this.initialize();
|
||||
@@ -77,4 +83,23 @@ export class Splitter {
|
||||
};
|
||||
|
||||
private onResizeStop: JQueryUI.ResizableEvent = () => $("iframe").css("pointer-events", "auto");
|
||||
|
||||
public collapseLeft() {
|
||||
this.lastX = $(this.splitter).position().left;
|
||||
this.lastWidth = $(this.leftSide).width();
|
||||
$(this.splitter).css("left", SplitterMetrics.CollapsedPositionLeft);
|
||||
$(this.leftSide).css("width", "");
|
||||
$(this.leftSide).resizable("option", "disabled", true).removeClass("ui-resizable-disabled"); // remove class so splitter is visible
|
||||
$(this.splitter).removeClass("ui-resizable-e");
|
||||
this.isCollapsed(true);
|
||||
}
|
||||
|
||||
public expandLeft() {
|
||||
$(this.splitter).addClass("ui-resizable-e");
|
||||
$(this.leftSide).css("width", this.lastWidth);
|
||||
$(this.splitter).css("left", this.lastX);
|
||||
$(this.splitter).css("left", ""); // this ensures the splitter's position is not fixed and enables movement during resizing
|
||||
$(this.leftSide).resizable("enable");
|
||||
this.isCollapsed(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
@@ -28,7 +28,6 @@ export interface ConfigContext {
|
||||
armAPIVersion?: string;
|
||||
allowedJunoOrigins: string[];
|
||||
enableSchemaAnalyzer: boolean;
|
||||
msalRedirectURI?: string;
|
||||
}
|
||||
|
||||
// Default configuration
|
||||
@@ -120,14 +119,6 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
|
||||
const armAPIVersion = params.get("armAPIVersion") || "";
|
||||
updateConfigContext({ armAPIVersion });
|
||||
}
|
||||
if (params.has("armEndpoint")) {
|
||||
const ARM_ENDPOINT = params.get("armEndpoint") || "";
|
||||
updateConfigContext({ ARM_ENDPOINT });
|
||||
}
|
||||
if (params.has("aadEndpoint")) {
|
||||
const AAD_ENDPOINT = params.get("aadEndpoint") || "";
|
||||
updateConfigContext({ AAD_ENDPOINT });
|
||||
}
|
||||
if (params.has("platform")) {
|
||||
const platform = params.get("platform");
|
||||
switch (platform) {
|
||||
|
||||
@@ -3,10 +3,11 @@ import {
|
||||
Resource,
|
||||
StoredProcedureDefinition,
|
||||
TriggerDefinition,
|
||||
UserDefinedFunctionDefinition
|
||||
UserDefinedFunctionDefinition,
|
||||
} from "@azure/cosmos";
|
||||
import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||
import Explorer from "../Explorer/Explorer";
|
||||
import { ConsoleData } from "../Explorer/Menus/NotificationConsole/ConsoleData";
|
||||
import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||
import { CassandraTableKey, CassandraTableKeys } from "../Explorer/Tables/TableDataClient";
|
||||
import ConflictId from "../Explorer/Tree/ConflictId";
|
||||
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||
@@ -89,6 +90,7 @@ export interface Database extends TreeNode {
|
||||
|
||||
selectedSubnodeKind: ko.Observable<CollectionTabKind>;
|
||||
|
||||
selectDatabase(): void;
|
||||
expandDatabase(): Promise<void>;
|
||||
collapseDatabase(): void;
|
||||
|
||||
@@ -274,6 +276,8 @@ export interface TabOptions {
|
||||
tabKind: CollectionTabKind;
|
||||
title: string;
|
||||
tabPath: string;
|
||||
hashLocation: string;
|
||||
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]) => void;
|
||||
isTabsContentExpanded?: ko.Observable<boolean>;
|
||||
onLoadStartKey?: number;
|
||||
|
||||
@@ -284,7 +288,6 @@ export interface TabOptions {
|
||||
rid?: string;
|
||||
node?: TreeNode;
|
||||
theme?: string;
|
||||
index?: number;
|
||||
}
|
||||
|
||||
export interface DocumentsTabOptions extends TabOptions {
|
||||
@@ -370,7 +373,6 @@ export enum TerminalKind {
|
||||
Default = 0,
|
||||
Mongo = 1,
|
||||
Cassandra = 2,
|
||||
PostgreSQL = 3
|
||||
}
|
||||
|
||||
export interface DataExplorerInputsFrame {
|
||||
|
||||
172
src/Explorer/ContextMenuButtonFactory.ts
Normal file
172
src/Explorer/ContextMenuButtonFactory.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
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";
|
||||
import { TreeNodeMenuItem } from "./Controls/TreeComponent/TreeComponent";
|
||||
import Explorer from "./Explorer";
|
||||
import StoredProcedure from "./Tree/StoredProcedure";
|
||||
import Trigger from "./Tree/Trigger";
|
||||
import UserDefinedFunction from "./Tree/UserDefinedFunction";
|
||||
export interface CollectionContextMenuButtonParams {
|
||||
databaseId: string;
|
||||
collectionId: string;
|
||||
}
|
||||
|
||||
export interface DatabaseContextMenuButtonParams {
|
||||
databaseId: string;
|
||||
}
|
||||
/**
|
||||
* New resource tree (in ReactJS)
|
||||
*/
|
||||
export class ResourceTreeContextMenuButtonFactory {
|
||||
public static createDatabaseContextMenu(container: Explorer, databaseId: string): TreeNodeMenuItem[] {
|
||||
const items: TreeNodeMenuItem[] = [
|
||||
{
|
||||
iconSrc: AddCollectionIcon,
|
||||
onClick: () => container.onNewCollectionClicked(databaseId),
|
||||
label: `New ${getCollectionName()}`,
|
||||
},
|
||||
];
|
||||
|
||||
if (userContext.apiType !== "Tables") {
|
||||
items.push({
|
||||
iconSrc: DeleteDatabaseIcon,
|
||||
onClick: () => container.openDeleteDatabaseConfirmationPane(),
|
||||
label: `Delete ${getDatabaseName()}`,
|
||||
styleClass: "deleteDatabaseMenuItem",
|
||||
});
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
public static createCollectionContextMenuButton(
|
||||
container: Explorer,
|
||||
selectedCollection: ViewModels.Collection
|
||||
): TreeNodeMenuItem[] {
|
||||
const items: TreeNodeMenuItem[] = [];
|
||||
if (userContext.apiType === "SQL" || userContext.apiType === "Gremlin") {
|
||||
items.push({
|
||||
iconSrc: AddSqlQueryIcon,
|
||||
onClick: () => selectedCollection && selectedCollection.onNewQueryClick(selectedCollection, null),
|
||||
label: "New SQL Query",
|
||||
});
|
||||
}
|
||||
|
||||
if (userContext.apiType === "Mongo") {
|
||||
items.push({
|
||||
iconSrc: AddSqlQueryIcon,
|
||||
onClick: () => selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection, null),
|
||||
label: "New Query",
|
||||
});
|
||||
|
||||
items.push({
|
||||
iconSrc: HostedTerminalIcon,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
if (container.isShellEnabled()) {
|
||||
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
||||
} else {
|
||||
selectedCollection && selectedCollection.onNewMongoShellClick();
|
||||
}
|
||||
},
|
||||
label: container.isShellEnabled() ? "Open Mongo Shell" : "New Shell",
|
||||
});
|
||||
}
|
||||
|
||||
if (userContext.apiType === "SQL" || userContext.apiType === "Gremlin") {
|
||||
items.push({
|
||||
iconSrc: AddStoredProcedureIcon,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection, null);
|
||||
},
|
||||
label: "New Stored Procedure",
|
||||
});
|
||||
|
||||
items.push({
|
||||
iconSrc: AddUdfIcon,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection, null);
|
||||
},
|
||||
label: "New UDF",
|
||||
});
|
||||
|
||||
items.push({
|
||||
iconSrc: AddTriggerIcon,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection, null);
|
||||
},
|
||||
label: "New Trigger",
|
||||
});
|
||||
}
|
||||
|
||||
items.push({
|
||||
iconSrc: DeleteCollectionIcon,
|
||||
onClick: () => container.openDeleteCollectionConfirmationPane(),
|
||||
label: `Delete ${getCollectionName()}`,
|
||||
styleClass: "deleteCollectionMenuItem",
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
public static createStoreProcedureContextMenuItems(
|
||||
container: Explorer,
|
||||
storedProcedure: StoredProcedure
|
||||
): TreeNodeMenuItem[] {
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
iconSrc: DeleteSprocIcon,
|
||||
onClick: () => storedProcedure.delete(),
|
||||
label: "Delete Store Procedure",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
public static createTriggerContextMenuItems(container: Explorer, trigger: Trigger): TreeNodeMenuItem[] {
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
iconSrc: DeleteTriggerIcon,
|
||||
onClick: () => trigger.delete(),
|
||||
label: "Delete Trigger",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
public static createUserDefinedFunctionContextMenuItems(
|
||||
container: Explorer,
|
||||
userDefinedFunction: UserDefinedFunction
|
||||
): TreeNodeMenuItem[] {
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
iconSrc: DeleteUDFIcon,
|
||||
onClick: () => userDefinedFunction.delete(),
|
||||
label: "Delete User Defined Function",
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
import React from "react";
|
||||
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 { useSidePanel } from "../hooks/useSidePanel";
|
||||
import { userContext } from "../UserContext";
|
||||
import { getCollectionName, getDatabaseName } from "../Utils/APITypeUtils";
|
||||
import { TreeNodeMenuItem } from "./Controls/TreeComponent/TreeComponent";
|
||||
import Explorer from "./Explorer";
|
||||
import { DeleteCollectionConfirmationPane } from "./Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane";
|
||||
import { DeleteDatabaseConfirmationPanel } from "./Panes/DeleteDatabaseConfirmationPanel";
|
||||
import StoredProcedure from "./Tree/StoredProcedure";
|
||||
import Trigger from "./Tree/Trigger";
|
||||
import UserDefinedFunction from "./Tree/UserDefinedFunction";
|
||||
import { useSelectedNode } from "./useSelectedNode";
|
||||
|
||||
export interface CollectionContextMenuButtonParams {
|
||||
databaseId: string;
|
||||
collectionId: string;
|
||||
}
|
||||
|
||||
export interface DatabaseContextMenuButtonParams {
|
||||
databaseId: string;
|
||||
}
|
||||
/**
|
||||
* New resource tree (in ReactJS)
|
||||
*/
|
||||
export const createDatabaseContextMenu = (container: Explorer, databaseId: string): TreeNodeMenuItem[] => {
|
||||
const items: TreeNodeMenuItem[] = [
|
||||
{
|
||||
iconSrc: AddCollectionIcon,
|
||||
onClick: () => container.onNewCollectionClicked(databaseId),
|
||||
label: `New ${getCollectionName()}`,
|
||||
},
|
||||
];
|
||||
|
||||
if (userContext.apiType !== "Tables") {
|
||||
items.push({
|
||||
iconSrc: DeleteDatabaseIcon,
|
||||
onClick: () =>
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel("Delete " + getDatabaseName(), <DeleteDatabaseConfirmationPanel explorer={container} />),
|
||||
label: `Delete ${getDatabaseName()}`,
|
||||
styleClass: "deleteDatabaseMenuItem",
|
||||
});
|
||||
}
|
||||
return items;
|
||||
};
|
||||
|
||||
export const createCollectionContextMenuButton = (
|
||||
container: Explorer,
|
||||
selectedCollection: ViewModels.Collection
|
||||
): TreeNodeMenuItem[] => {
|
||||
const items: TreeNodeMenuItem[] = [];
|
||||
if (userContext.apiType === "SQL" || userContext.apiType === "Gremlin") {
|
||||
items.push({
|
||||
iconSrc: AddSqlQueryIcon,
|
||||
onClick: () => selectedCollection && selectedCollection.onNewQueryClick(selectedCollection, undefined),
|
||||
label: "New SQL Query",
|
||||
});
|
||||
}
|
||||
|
||||
if (userContext.apiType === "Mongo") {
|
||||
items.push({
|
||||
iconSrc: AddSqlQueryIcon,
|
||||
onClick: () => selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection, undefined),
|
||||
label: "New Query",
|
||||
});
|
||||
|
||||
items.push({
|
||||
iconSrc: HostedTerminalIcon,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
if (container.isShellEnabled()) {
|
||||
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
||||
} else {
|
||||
selectedCollection && selectedCollection.onNewMongoShellClick();
|
||||
}
|
||||
},
|
||||
label: container.isShellEnabled() ? "Open Mongo Shell" : "New Shell",
|
||||
});
|
||||
}
|
||||
|
||||
if (userContext.apiType === "SQL" || userContext.apiType === "Gremlin") {
|
||||
items.push({
|
||||
iconSrc: AddStoredProcedureIcon,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection, undefined);
|
||||
},
|
||||
label: "New Stored Procedure",
|
||||
});
|
||||
|
||||
items.push({
|
||||
iconSrc: AddUdfIcon,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection, undefined);
|
||||
},
|
||||
label: "New UDF",
|
||||
});
|
||||
|
||||
items.push({
|
||||
iconSrc: AddTriggerIcon,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection, undefined);
|
||||
},
|
||||
label: "New Trigger",
|
||||
});
|
||||
}
|
||||
|
||||
items.push({
|
||||
iconSrc: DeleteCollectionIcon,
|
||||
onClick: () =>
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel("Delete " + getCollectionName(), <DeleteCollectionConfirmationPane explorer={container} />),
|
||||
label: `Delete ${getCollectionName()}`,
|
||||
styleClass: "deleteCollectionMenuItem",
|
||||
});
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
export const createStoreProcedureContextMenuItems = (
|
||||
container: Explorer,
|
||||
storedProcedure: StoredProcedure
|
||||
): TreeNodeMenuItem[] => {
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
iconSrc: DeleteSprocIcon,
|
||||
onClick: () => storedProcedure.delete(),
|
||||
label: "Delete Store Procedure",
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export const createTriggerContextMenuItems = (container: Explorer, trigger: Trigger): TreeNodeMenuItem[] => {
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
iconSrc: DeleteTriggerIcon,
|
||||
onClick: () => trigger.delete(),
|
||||
label: "Delete Trigger",
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export const createUserDefinedFunctionContextMenuItems = (
|
||||
container: Explorer,
|
||||
userDefinedFunction: UserDefinedFunction
|
||||
): TreeNodeMenuItem[] => {
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
iconSrc: DeleteUDFIcon,
|
||||
onClick: () => userDefinedFunction.delete(),
|
||||
label: "Delete User Defined Function",
|
||||
},
|
||||
];
|
||||
};
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
35
src/Explorer/Controls/DynamicList/dynamic-list.html
Normal file
35
src/Explorer/Controls/DynamicList/dynamic-list.html
Normal 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>
|
||||
@@ -1,11 +1,6 @@
|
||||
import { Spinner, SpinnerSize } from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import { loadMonaco, monaco } from "../../LazyMonaco";
|
||||
// import "./EditorReact.less";
|
||||
|
||||
interface EditorReactStates {
|
||||
showEditor: boolean;
|
||||
}
|
||||
export interface EditorReactProps {
|
||||
language: string;
|
||||
content: string;
|
||||
@@ -17,26 +12,22 @@ export interface EditorReactProps {
|
||||
theme?: string; // Monaco editor theme
|
||||
}
|
||||
|
||||
export class EditorReact extends React.Component<EditorReactProps, EditorReactStates> {
|
||||
export class EditorReact extends React.Component<EditorReactProps> {
|
||||
private rootNode: HTMLElement;
|
||||
private editor: monaco.editor.IStandaloneCodeEditor;
|
||||
private selectionListener: monaco.IDisposable;
|
||||
|
||||
public constructor(props: EditorReactProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showEditor: false,
|
||||
};
|
||||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
this.createEditor(this.configureEditor.bind(this));
|
||||
}
|
||||
|
||||
public componentDidUpdate(previous: EditorReactProps) {
|
||||
if (this.props.content !== previous.content) {
|
||||
this.editor.setValue(this.props.content);
|
||||
}
|
||||
public shouldComponentUpdate(): boolean {
|
||||
// Prevents component re-rendering
|
||||
return false;
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
@@ -44,12 +35,7 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{!this.state.showEditor && <Spinner size={SpinnerSize.large} className="spinner" />}
|
||||
<div className="jsonEditor" ref={(elt: HTMLElement) => this.setRef(elt)} />
|
||||
</React.Fragment>
|
||||
);
|
||||
return <div className="jsonEditor" ref={(elt: HTMLElement) => this.setRef(elt)} />;
|
||||
}
|
||||
|
||||
protected configureEditor(editor: monaco.editor.IStandaloneCodeEditor) {
|
||||
@@ -90,12 +76,6 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
||||
this.rootNode.innerHTML = "";
|
||||
const monaco = await loadMonaco();
|
||||
createCallback(monaco.editor.create(this.rootNode, options));
|
||||
|
||||
if (this.rootNode.innerHTML) {
|
||||
this.setState({
|
||||
showEditor: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private setRef(element: HTMLElement): void {
|
||||
|
||||
@@ -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>
|
||||
20
src/Explorer/Controls/GitHub/GitHubReposComponentAdapter.tsx
Normal file
20
src/Explorer/Controls/GitHub/GitHubReposComponentAdapter.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import ko from "knockout";
|
||||
import * as React from "react";
|
||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
||||
import { GitHubReposComponent, GitHubReposComponentProps } from "./GitHubReposComponent";
|
||||
|
||||
export class GitHubReposComponentAdapter implements ReactAdapter {
|
||||
public parameters: ko.Observable<number>;
|
||||
|
||||
constructor(private props: GitHubReposComponentProps) {
|
||||
this.parameters = ko.observable<number>(Date.now());
|
||||
}
|
||||
|
||||
public renderComponent(): JSX.Element {
|
||||
return <GitHubReposComponent {...this.props} />;
|
||||
}
|
||||
|
||||
public triggerRender(): void {
|
||||
window.requestAnimationFrame(() => this.parameters(Date.now()));
|
||||
}
|
||||
}
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -43,7 +43,8 @@ interface NotebookViewerComponentState {
|
||||
|
||||
export class NotebookViewerComponent
|
||||
extends React.Component<NotebookViewerComponentProps, NotebookViewerComponentState>
|
||||
implements DialogHost {
|
||||
implements DialogHost
|
||||
{
|
||||
private clientManager: NotebookClientV2;
|
||||
private notebookComponentBootstrapper: NotebookComponentBootstrapper;
|
||||
|
||||
@@ -55,7 +56,7 @@ export class NotebookViewerComponent
|
||||
databaseAccountName: undefined,
|
||||
defaultExperience: "NotebookViewer",
|
||||
isReadOnly: true,
|
||||
cellEditorType: "codemirror",
|
||||
cellEditorType: "monaco",
|
||||
autoSaveInterval: 365 * 24 * 3600 * 1000, // There is no way to turn off auto-save, set to 1 year
|
||||
contentProvider: contents.JupyterContentProvider, // NotebookViewer only knows how to talk to Jupyter contents API
|
||||
});
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -38,6 +38,8 @@ describe("SettingsComponent", () => {
|
||||
title: "Scale & Settings",
|
||||
tabPath: "",
|
||||
node: undefined,
|
||||
hashLocation: "settings",
|
||||
onUpdateTabsButtons: undefined,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -126,6 +128,7 @@ describe("SettingsComponent", () => {
|
||||
isDatabaseExpanded: undefined,
|
||||
isDatabaseShared: ko.computed(() => true),
|
||||
selectedSubnodeKind: undefined,
|
||||
selectDatabase: undefined,
|
||||
expandDatabase: undefined,
|
||||
collapseDatabase: undefined,
|
||||
loadCollections: undefined,
|
||||
|
||||
@@ -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";
|
||||
@@ -16,9 +16,8 @@ import { trace, traceFailure, traceStart, traceSuccess } from "../../../Shared/T
|
||||
import { userContext } from "../../../UserContext";
|
||||
import { MongoDBCollectionResource, MongoIndex } from "../../../Utils/arm/generatedClients/cosmos/types";
|
||||
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
||||
import { logConsoleError } from "../../../Utils/NotificationConsoleUtils";
|
||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
import { useCommandBar } from "../../Menus/CommandBar/CommandBarComponentAdapter";
|
||||
import Explorer from "../../Explorer";
|
||||
import { SettingsTabV2 } from "../../Tabs/SettingsTabV2";
|
||||
import "./SettingsComponent.less";
|
||||
import { mongoIndexingPolicyAADError } from "./SettingsRenderUtils";
|
||||
@@ -110,7 +109,6 @@ export interface SettingsComponentState {
|
||||
|
||||
initialNotification: DataModels.Notification;
|
||||
selectedTab: SettingsV2TabTypes;
|
||||
offerLoaded: boolean;
|
||||
}
|
||||
|
||||
export class SettingsComponent extends React.Component<SettingsComponentProps, SettingsComponentState> {
|
||||
@@ -123,6 +121,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
private collection: ViewModels.Collection;
|
||||
private database: ViewModels.Database;
|
||||
private offer: DataModels.Offer;
|
||||
private container: Explorer;
|
||||
private changeFeedPolicyVisible: boolean;
|
||||
private isFixedContainer: boolean;
|
||||
private shouldShowIndexingPolicyEditor: boolean;
|
||||
@@ -134,6 +133,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
this.isCollectionSettingsTab = this.props.settingsTab.tabKind === ViewModels.CollectionTabKind.CollectionSettingsV2;
|
||||
if (this.isCollectionSettingsTab) {
|
||||
this.collection = this.props.settingsTab.collection as ViewModels.Collection;
|
||||
this.container = this.collection?.container;
|
||||
this.offer = this.collection?.offer();
|
||||
this.isAnalyticalStorageEnabled = !!this.collection?.analyticalStorageTtl();
|
||||
this.shouldShowIndexingPolicyEditor = userContext.apiType !== "Cassandra" && userContext.apiType !== "Mongo";
|
||||
@@ -145,6 +145,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
userContext.apiType === "Mongo" && (!this.collection?.partitionKey || this.collection?.partitionKey.systemKey);
|
||||
} else {
|
||||
this.database = this.props.settingsTab.database;
|
||||
this.container = this.database?.container;
|
||||
this.offer = this.database?.offer();
|
||||
}
|
||||
|
||||
@@ -195,7 +196,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
|
||||
initialNotification: undefined,
|
||||
selectedTab: SettingsV2TabTypes.ScaleTab,
|
||||
offerLoaded: !!this.offer,
|
||||
};
|
||||
|
||||
this.saveSettingsButton = {
|
||||
@@ -217,19 +217,18 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
if (this.isCollectionSettingsTab) {
|
||||
this.refreshIndexTransformationProgress();
|
||||
this.loadMongoIndexes();
|
||||
this.loadCollectionOffer();
|
||||
}
|
||||
|
||||
this.setAutoPilotStates();
|
||||
this.setBaseline();
|
||||
if (this.props.settingsTab.isActive()) {
|
||||
useCommandBar.getState().setContextButtons(this.getTabsButtons());
|
||||
this.props.settingsTab.getContainer().onUpdateTabsButtons(this.getTabsButtons());
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
if (this.props.settingsTab.isActive()) {
|
||||
useCommandBar.getState().setContextButtons(this.getTabsButtons());
|
||||
this.props.settingsTab.getContainer().onUpdateTabsButtons(this.getTabsButtons());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +293,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
this.state.wasAutopilotOriginallySet !== this.state.isAutoPilotSelected;
|
||||
|
||||
public shouldShowKeyspaceSharedThroughputMessage = (): boolean =>
|
||||
userContext.apiType === "Cassandra" && hasDatabaseSharedThroughput(this.collection);
|
||||
this.container && userContext.apiType === "Cassandra" && hasDatabaseSharedThroughput(this.collection);
|
||||
|
||||
public hasConflictResolution = (): boolean =>
|
||||
userContext?.databaseAccount?.properties?.enableMultipleWriteLocations &&
|
||||
@@ -372,34 +371,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
});
|
||||
};
|
||||
|
||||
private async loadCollectionOffer() {
|
||||
try {
|
||||
this.props.settingsTab.isExecuting(true);
|
||||
await this.collection.loadOffer();
|
||||
this.props.settingsTab.tabTitle(this.collection.offer() ? "Settings" : "Scale & Settings");
|
||||
this.setState({ offerLoaded: true });
|
||||
} catch (error) {
|
||||
this.props.settingsTab.isExecutionError(true);
|
||||
const errorMessage = getErrorMessage(error);
|
||||
traceFailure(
|
||||
Action.Tab,
|
||||
{
|
||||
databaseName: this.collection.databaseId,
|
||||
collectionName: this.collection.id(),
|
||||
|
||||
dataExplorerArea: Constants.Areas.Tab,
|
||||
tabTitle: this.props.settingsTab.tabTitle,
|
||||
error: errorMessage,
|
||||
errorStack: getErrorStack(error),
|
||||
},
|
||||
this.props.settingsTab.onLoadStartKey
|
||||
);
|
||||
logConsoleError(`Error while fetching container settings for container ${this.collection.id()}: ${errorMessage}`);
|
||||
} finally {
|
||||
this.props.settingsTab.isExecuting(false);
|
||||
}
|
||||
}
|
||||
|
||||
private getMongoIndexesToSave = (): MongoIndex[] => {
|
||||
let finalIndexes: MongoIndex[] = [];
|
||||
this.state.currentMongoIndexes?.map((mongoIndex: MongoIndex, index: number) => {
|
||||
@@ -912,6 +883,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
const scaleComponentProps: ScaleComponentProps = {
|
||||
collection: this.collection,
|
||||
database: this.database,
|
||||
container: this.container,
|
||||
isFixedContainer: this.isFixedContainer,
|
||||
onThroughputChange: this.onThroughputChange,
|
||||
throughput: this.state.throughput,
|
||||
@@ -937,12 +909,9 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.state.offerLoaded) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const subSettingsComponentProps: SubSettingsComponentProps = {
|
||||
collection: this.collection,
|
||||
container: this.container,
|
||||
isAnalyticalStorageEnabled: this.isAnalyticalStorageEnabled,
|
||||
changeFeedPolicyVisible: this.changeFeedPolicyVisible,
|
||||
timeToLive: this.state.timeToLive,
|
||||
@@ -995,6 +964,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
|
||||
const conflictResolutionPolicyComponentProps: ConflictResolutionComponentProps = {
|
||||
collection: this.collection,
|
||||
container: this.container,
|
||||
conflictResolutionPolicyMode: this.state.conflictResolutionPolicyMode,
|
||||
conflictResolutionPolicyModeBaseline: this.state.conflictResolutionPolicyModeBaseline,
|
||||
onConflictResolutionPolicyModeChange: this.onConflictResolutionPolicyModeChange,
|
||||
|
||||
14
src/Explorer/Controls/Settings/SettingsComponentAdapter.tsx
Normal file
14
src/Explorer/Controls/Settings/SettingsComponentAdapter.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import ko from "knockout";
|
||||
import * as React from "react";
|
||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
||||
import { SettingsComponent, SettingsComponentProps } from "./SettingsComponent";
|
||||
|
||||
export class SettingsComponentAdapter implements ReactAdapter {
|
||||
public parameters: ko.Computed<boolean>;
|
||||
|
||||
constructor(private props: SettingsComponentProps) {}
|
||||
|
||||
public renderComponent(): JSX.Element {
|
||||
return this.parameters() ? <SettingsComponent {...this.props} /> : <></>;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { ConflictResolutionComponentProps, ConflictResolutionComponent } from "./ConflictResolutionComponent";
|
||||
import { container, collection } from "../TestUtils";
|
||||
import * as DataModels from "../../../../Contracts/DataModels";
|
||||
import { collection } from "../TestUtils";
|
||||
import { ConflictResolutionComponent, ConflictResolutionComponentProps } from "./ConflictResolutionComponent";
|
||||
|
||||
describe("ConflictResolutionComponent", () => {
|
||||
const baseProps: ConflictResolutionComponentProps = {
|
||||
collection: collection,
|
||||
container: container,
|
||||
conflictResolutionPolicyMode: DataModels.ConflictResolutionMode.Custom,
|
||||
conflictResolutionPolicyModeBaseline: DataModels.ConflictResolutionMode.Custom,
|
||||
onConflictResolutionPolicyModeChange: () => {
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import { ChoiceGroup, IChoiceGroupOption, ITextFieldProps, Stack, TextField } from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import * as DataModels from "../../../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||
import * as DataModels from "../../../../Contracts/DataModels";
|
||||
import Explorer from "../../../Explorer";
|
||||
import {
|
||||
conflictResolutionCustomToolTip,
|
||||
conflictResolutionLwwTooltip,
|
||||
getChoiceGroupStyles,
|
||||
getTextFieldStyles,
|
||||
conflictResolutionLwwTooltip,
|
||||
conflictResolutionCustomToolTip,
|
||||
subComponentStackProps,
|
||||
getChoiceGroupStyles,
|
||||
} from "../SettingsRenderUtils";
|
||||
import { isDirty } from "../SettingsUtils";
|
||||
import { TextField, ITextFieldProps, Stack, IChoiceGroupOption, ChoiceGroup } from "@fluentui/react";
|
||||
import { ToolTipLabelComponent } from "./ToolTipLabelComponent";
|
||||
import { isDirty } from "../SettingsUtils";
|
||||
|
||||
export interface ConflictResolutionComponentProps {
|
||||
collection: ViewModels.Collection;
|
||||
container: Explorer;
|
||||
conflictResolutionPolicyMode: DataModels.ConflictResolutionMode;
|
||||
conflictResolutionPolicyModeBaseline: DataModels.ConflictResolutionMode;
|
||||
onConflictResolutionPolicyModeChange: (newMode: DataModels.ConflictResolutionMode) => void;
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -7,17 +7,20 @@ import * as SharedConstants from "../../../../Shared/Constants";
|
||||
import { updateUserContext } from "../../../../UserContext";
|
||||
import Explorer from "../../../Explorer";
|
||||
import { throughputUnit } from "../SettingsRenderUtils";
|
||||
import { collection } from "../TestUtils";
|
||||
import { collection, container } from "../TestUtils";
|
||||
import { ScaleComponent, ScaleComponentProps } from "./ScaleComponent";
|
||||
import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents/ThroughputInputAutoPilotV3Component";
|
||||
|
||||
describe("ScaleComponent", () => {
|
||||
const nonNationalCloudContainer = new Explorer();
|
||||
nonNationalCloudContainer.isRunningOnNationalCloud = () => false;
|
||||
|
||||
const targetThroughput = 6000;
|
||||
|
||||
const baseProps: ScaleComponentProps = {
|
||||
collection: collection,
|
||||
database: undefined,
|
||||
container: container,
|
||||
isFixedContainer: false,
|
||||
onThroughputChange: () => {
|
||||
return;
|
||||
@@ -108,7 +111,7 @@ describe("ScaleComponent", () => {
|
||||
let scaleComponent = new ScaleComponent(baseProps);
|
||||
expect(scaleComponent.getThroughputTitle()).toEqual("Throughput (6,000 - unlimited RU/s)");
|
||||
|
||||
let newProps = { ...baseProps };
|
||||
let newProps = { ...baseProps, container: nonNationalCloudContainer };
|
||||
scaleComponent = new ScaleComponent(newProps);
|
||||
expect(scaleComponent.getThroughputTitle()).toEqual("Throughput (6,000 - unlimited RU/s)");
|
||||
|
||||
@@ -121,7 +124,7 @@ describe("ScaleComponent", () => {
|
||||
let scaleComponent = new ScaleComponent(baseProps);
|
||||
expect(scaleComponent.canThroughputExceedMaximumValue()).toEqual(true);
|
||||
|
||||
const newProps = { ...baseProps };
|
||||
const newProps = { ...baseProps, container: nonNationalCloudContainer };
|
||||
scaleComponent = new ScaleComponent(newProps);
|
||||
expect(scaleComponent.canThroughputExceedMaximumValue()).toEqual(true);
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||
import * as SharedConstants from "../../../../Shared/Constants";
|
||||
import { userContext } from "../../../../UserContext";
|
||||
import * as AutoPilotUtils from "../../../../Utils/AutoPilotUtils";
|
||||
import { isRunningOnNationalCloud } from "../../../../Utils/CloudUtils";
|
||||
import Explorer from "../../../Explorer";
|
||||
import {
|
||||
getTextFieldStyles,
|
||||
getThroughputApplyLongDelayMessage,
|
||||
@@ -23,6 +23,7 @@ import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents
|
||||
export interface ScaleComponentProps {
|
||||
collection: ViewModels.Collection;
|
||||
database: ViewModels.Database;
|
||||
container: Explorer;
|
||||
isFixedContainer: boolean;
|
||||
onThroughputChange: (newThroughput: number) => void;
|
||||
throughput: number;
|
||||
@@ -108,7 +109,11 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
||||
};
|
||||
|
||||
public canThroughputExceedMaximumValue = (): boolean => {
|
||||
return !this.props.isFixedContainer && configContext.platform === Platform.Portal && !isRunningOnNationalCloud();
|
||||
return (
|
||||
!this.props.isFixedContainer &&
|
||||
configContext.platform === Platform.Portal &&
|
||||
!this.props.container.isRunningOnNationalCloud()
|
||||
);
|
||||
};
|
||||
|
||||
public getInitialNotificationElement = (): JSX.Element => {
|
||||
|
||||
@@ -4,12 +4,14 @@ import { DatabaseAccount } from "../../../../Contracts/DataModels";
|
||||
import { updateUserContext } from "../../../../UserContext";
|
||||
import Explorer from "../../../Explorer";
|
||||
import { ChangeFeedPolicyState, GeospatialConfigType, TtlOff, TtlOn, TtlOnNoDefault, TtlType } from "../SettingsUtils";
|
||||
import { collection } from "../TestUtils";
|
||||
import { collection, container } from "../TestUtils";
|
||||
import { SubSettingsComponent, SubSettingsComponentProps } from "./SubSettingsComponent";
|
||||
|
||||
describe("SubSettingsComponent", () => {
|
||||
const baseProps: SubSettingsComponentProps = {
|
||||
collection: collection,
|
||||
container: container,
|
||||
|
||||
timeToLive: TtlType.On,
|
||||
timeToLiveBaseline: TtlType.On,
|
||||
onTtlChange: () => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ChoiceGroup, IChoiceGroupOption, Label, Link, MessageBar, Stack, Text,
|
||||
import * as React from "react";
|
||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||
import { userContext } from "../../../../UserContext";
|
||||
import Explorer from "../../../Explorer";
|
||||
import { Int32 } from "../../../Panes/Tables/Validators/EntityPropertyValidationCommon";
|
||||
import {
|
||||
changeFeedPolicyToolTip,
|
||||
@@ -27,6 +28,8 @@ import { ToolTipLabelComponent } from "./ToolTipLabelComponent";
|
||||
|
||||
export interface SubSettingsComponentProps {
|
||||
collection: ViewModels.Collection;
|
||||
container: Explorer;
|
||||
|
||||
timeToLive: TtlType;
|
||||
timeToLiveBaseline: TtlType;
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ describe("SettingsUtils", () => {
|
||||
isDatabaseExpanded: ko.observable(false),
|
||||
isDatabaseShared: ko.computed(() => true),
|
||||
selectedSubnodeKind: ko.observable(undefined),
|
||||
selectDatabase: undefined,
|
||||
expandDatabase: undefined,
|
||||
collapseDatabase: undefined,
|
||||
loadCollections: undefined,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -30,11 +30,24 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"container": Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"canSaveQueries": [Function],
|
||||
"closeSidePanel": undefined,
|
||||
"collapsedResourceTreeWidth": 36,
|
||||
"commandBarComponentAdapter": CommandBarComponentAdapter {
|
||||
"container": [Circular],
|
||||
"isNotebookTabActive": [Function],
|
||||
"parameters": [Function],
|
||||
"tabsButtons": Array [],
|
||||
},
|
||||
"databases": [Function],
|
||||
"isAccountReady": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isHostedDataExplorerEnabled": [Function],
|
||||
"isNotebookEnabled": [Function],
|
||||
"isNotebooksEnabledForAccount": [Function],
|
||||
"isResourceTokenCollectionNodeSelected": [Function],
|
||||
"isSchemaEnabled": [Function],
|
||||
"isServerlessEnabled": [Function],
|
||||
"isShellEnabled": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
@@ -43,22 +56,46 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"notebookServerInfo": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
"onRefreshResourcesClick": [Function],
|
||||
"openSidePanel": undefined,
|
||||
"provideFeedbackEmail": [Function],
|
||||
"queriesClient": QueriesClient {
|
||||
"container": [Circular],
|
||||
},
|
||||
"refreshNotebookList": [Function],
|
||||
"resourceTokenCollection": [Function],
|
||||
"resourceTokenCollectionId": [Function],
|
||||
"resourceTokenDatabaseId": [Function],
|
||||
"resourceTokenPartitionKey": [Function],
|
||||
"resourceTree": ResourceTreeAdapter {
|
||||
"container": [Circular],
|
||||
"copyNotebook": [Function],
|
||||
"databaseCollectionIdMap": Map {},
|
||||
"koSubsCollectionIdMap": Map {},
|
||||
"koSubsDatabaseIdMap": Map {},
|
||||
"parameters": [Function],
|
||||
},
|
||||
"resourceTreeForResourceToken": ResourceTreeAdapterForResourceToken {
|
||||
"container": [Circular],
|
||||
"parameters": [Function],
|
||||
},
|
||||
"selectedDatabaseId": [Function],
|
||||
"selectedNode": [Function],
|
||||
"setInProgressConsoleDataIdToBeDeleted": undefined,
|
||||
"setIsNotificationConsoleExpanded": undefined,
|
||||
"setNotificationConsoleData": undefined,
|
||||
"sparkClusterConnectionInfo": [Function],
|
||||
"splitter": Splitter {
|
||||
"bounds": Object {
|
||||
"max": 400,
|
||||
"min": 240,
|
||||
},
|
||||
"direction": "vertical",
|
||||
"isCollapsed": [Function],
|
||||
"leftSideId": "resourcetree",
|
||||
"onResizeStart": [Function],
|
||||
"onResizeStop": [Function],
|
||||
"splitterId": "h_splitter1",
|
||||
},
|
||||
"tabsManager": TabsManager {
|
||||
"activeTab": [Function],
|
||||
"openedTabs": [Function],
|
||||
@@ -82,6 +119,82 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"usageSizeInKB": [Function],
|
||||
}
|
||||
}
|
||||
container={
|
||||
Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"canSaveQueries": [Function],
|
||||
"closeSidePanel": undefined,
|
||||
"collapsedResourceTreeWidth": 36,
|
||||
"commandBarComponentAdapter": CommandBarComponentAdapter {
|
||||
"container": [Circular],
|
||||
"isNotebookTabActive": [Function],
|
||||
"parameters": [Function],
|
||||
"tabsButtons": Array [],
|
||||
},
|
||||
"databases": [Function],
|
||||
"isAccountReady": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isHostedDataExplorerEnabled": [Function],
|
||||
"isNotebookEnabled": [Function],
|
||||
"isNotebooksEnabledForAccount": [Function],
|
||||
"isResourceTokenCollectionNodeSelected": [Function],
|
||||
"isSchemaEnabled": [Function],
|
||||
"isServerlessEnabled": [Function],
|
||||
"isShellEnabled": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"memoryUsageInfo": [Function],
|
||||
"notebookBasePath": [Function],
|
||||
"notebookServerInfo": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
"onRefreshResourcesClick": [Function],
|
||||
"openSidePanel": undefined,
|
||||
"provideFeedbackEmail": [Function],
|
||||
"queriesClient": QueriesClient {
|
||||
"container": [Circular],
|
||||
},
|
||||
"refreshNotebookList": [Function],
|
||||
"resourceTokenCollection": [Function],
|
||||
"resourceTokenCollectionId": [Function],
|
||||
"resourceTokenDatabaseId": [Function],
|
||||
"resourceTokenPartitionKey": [Function],
|
||||
"resourceTree": ResourceTreeAdapter {
|
||||
"container": [Circular],
|
||||
"copyNotebook": [Function],
|
||||
"databaseCollectionIdMap": Map {},
|
||||
"koSubsCollectionIdMap": Map {},
|
||||
"koSubsDatabaseIdMap": Map {},
|
||||
"parameters": [Function],
|
||||
},
|
||||
"resourceTreeForResourceToken": ResourceTreeAdapterForResourceToken {
|
||||
"container": [Circular],
|
||||
"parameters": [Function],
|
||||
},
|
||||
"selectedDatabaseId": [Function],
|
||||
"selectedNode": [Function],
|
||||
"setInProgressConsoleDataIdToBeDeleted": undefined,
|
||||
"setIsNotificationConsoleExpanded": undefined,
|
||||
"setNotificationConsoleData": undefined,
|
||||
"sparkClusterConnectionInfo": [Function],
|
||||
"splitter": Splitter {
|
||||
"bounds": Object {
|
||||
"max": 400,
|
||||
"min": 240,
|
||||
},
|
||||
"direction": "vertical",
|
||||
"isCollapsed": [Function],
|
||||
"leftSideId": "resourcetree",
|
||||
"onResizeStart": [Function],
|
||||
"onResizeStop": [Function],
|
||||
"splitterId": "h_splitter1",
|
||||
},
|
||||
"tabsManager": TabsManager {
|
||||
"activeTab": [Function],
|
||||
"openedTabs": [Function],
|
||||
},
|
||||
}
|
||||
}
|
||||
isAutoPilotSelected={false}
|
||||
isFixedContainer={false}
|
||||
onAutoPilotSelected={[Function]}
|
||||
@@ -116,11 +229,24 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"container": Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"canSaveQueries": [Function],
|
||||
"closeSidePanel": undefined,
|
||||
"collapsedResourceTreeWidth": 36,
|
||||
"commandBarComponentAdapter": CommandBarComponentAdapter {
|
||||
"container": [Circular],
|
||||
"isNotebookTabActive": [Function],
|
||||
"parameters": [Function],
|
||||
"tabsButtons": Array [],
|
||||
},
|
||||
"databases": [Function],
|
||||
"isAccountReady": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isHostedDataExplorerEnabled": [Function],
|
||||
"isNotebookEnabled": [Function],
|
||||
"isNotebooksEnabledForAccount": [Function],
|
||||
"isResourceTokenCollectionNodeSelected": [Function],
|
||||
"isSchemaEnabled": [Function],
|
||||
"isServerlessEnabled": [Function],
|
||||
"isShellEnabled": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
@@ -129,22 +255,46 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"notebookServerInfo": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
"onRefreshResourcesClick": [Function],
|
||||
"openSidePanel": undefined,
|
||||
"provideFeedbackEmail": [Function],
|
||||
"queriesClient": QueriesClient {
|
||||
"container": [Circular],
|
||||
},
|
||||
"refreshNotebookList": [Function],
|
||||
"resourceTokenCollection": [Function],
|
||||
"resourceTokenCollectionId": [Function],
|
||||
"resourceTokenDatabaseId": [Function],
|
||||
"resourceTokenPartitionKey": [Function],
|
||||
"resourceTree": ResourceTreeAdapter {
|
||||
"container": [Circular],
|
||||
"copyNotebook": [Function],
|
||||
"databaseCollectionIdMap": Map {},
|
||||
"koSubsCollectionIdMap": Map {},
|
||||
"koSubsDatabaseIdMap": Map {},
|
||||
"parameters": [Function],
|
||||
},
|
||||
"resourceTreeForResourceToken": ResourceTreeAdapterForResourceToken {
|
||||
"container": [Circular],
|
||||
"parameters": [Function],
|
||||
},
|
||||
"selectedDatabaseId": [Function],
|
||||
"selectedNode": [Function],
|
||||
"setInProgressConsoleDataIdToBeDeleted": undefined,
|
||||
"setIsNotificationConsoleExpanded": undefined,
|
||||
"setNotificationConsoleData": undefined,
|
||||
"sparkClusterConnectionInfo": [Function],
|
||||
"splitter": Splitter {
|
||||
"bounds": Object {
|
||||
"max": 400,
|
||||
"min": 240,
|
||||
},
|
||||
"direction": "vertical",
|
||||
"isCollapsed": [Function],
|
||||
"leftSideId": "resourcetree",
|
||||
"onResizeStart": [Function],
|
||||
"onResizeStop": [Function],
|
||||
"splitterId": "h_splitter1",
|
||||
},
|
||||
"tabsManager": TabsManager {
|
||||
"activeTab": [Function],
|
||||
"openedTabs": [Function],
|
||||
@@ -168,6 +318,82 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"usageSizeInKB": [Function],
|
||||
}
|
||||
}
|
||||
container={
|
||||
Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"canSaveQueries": [Function],
|
||||
"closeSidePanel": undefined,
|
||||
"collapsedResourceTreeWidth": 36,
|
||||
"commandBarComponentAdapter": CommandBarComponentAdapter {
|
||||
"container": [Circular],
|
||||
"isNotebookTabActive": [Function],
|
||||
"parameters": [Function],
|
||||
"tabsButtons": Array [],
|
||||
},
|
||||
"databases": [Function],
|
||||
"isAccountReady": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isHostedDataExplorerEnabled": [Function],
|
||||
"isNotebookEnabled": [Function],
|
||||
"isNotebooksEnabledForAccount": [Function],
|
||||
"isResourceTokenCollectionNodeSelected": [Function],
|
||||
"isSchemaEnabled": [Function],
|
||||
"isServerlessEnabled": [Function],
|
||||
"isShellEnabled": [Function],
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"memoryUsageInfo": [Function],
|
||||
"notebookBasePath": [Function],
|
||||
"notebookServerInfo": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
"onRefreshResourcesClick": [Function],
|
||||
"openSidePanel": undefined,
|
||||
"provideFeedbackEmail": [Function],
|
||||
"queriesClient": QueriesClient {
|
||||
"container": [Circular],
|
||||
},
|
||||
"refreshNotebookList": [Function],
|
||||
"resourceTokenCollection": [Function],
|
||||
"resourceTokenCollectionId": [Function],
|
||||
"resourceTokenDatabaseId": [Function],
|
||||
"resourceTokenPartitionKey": [Function],
|
||||
"resourceTree": ResourceTreeAdapter {
|
||||
"container": [Circular],
|
||||
"copyNotebook": [Function],
|
||||
"databaseCollectionIdMap": Map {},
|
||||
"koSubsCollectionIdMap": Map {},
|
||||
"koSubsDatabaseIdMap": Map {},
|
||||
"parameters": [Function],
|
||||
},
|
||||
"resourceTreeForResourceToken": ResourceTreeAdapterForResourceToken {
|
||||
"container": [Circular],
|
||||
"parameters": [Function],
|
||||
},
|
||||
"selectedDatabaseId": [Function],
|
||||
"selectedNode": [Function],
|
||||
"setInProgressConsoleDataIdToBeDeleted": undefined,
|
||||
"setIsNotificationConsoleExpanded": undefined,
|
||||
"setNotificationConsoleData": undefined,
|
||||
"sparkClusterConnectionInfo": [Function],
|
||||
"splitter": Splitter {
|
||||
"bounds": Object {
|
||||
"max": 400,
|
||||
"min": 240,
|
||||
},
|
||||
"direction": "vertical",
|
||||
"isCollapsed": [Function],
|
||||
"leftSideId": "resourcetree",
|
||||
"onResizeStart": [Function],
|
||||
"onResizeStop": [Function],
|
||||
"splitterId": "h_splitter1",
|
||||
},
|
||||
"tabsManager": TabsManager {
|
||||
"activeTab": [Function],
|
||||
"openedTabs": [Function],
|
||||
},
|
||||
}
|
||||
}
|
||||
geospatialConfigType="Geometry"
|
||||
geospatialConfigTypeBaseline="Geometry"
|
||||
isAnalyticalStorageEnabled={false}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ThroughputInput } from "./ThroughputInput";
|
||||
const props = {
|
||||
isDatabase: false,
|
||||
showFreeTierExceedThroughputTooltip: true,
|
||||
isSharded: true,
|
||||
isSharded: false,
|
||||
setThroughputValue: () => jest.fn(),
|
||||
setIsAutoscale: () => jest.fn(),
|
||||
onCostAcknowledgeChange: () => jest.fn(),
|
||||
|
||||
@@ -41,16 +41,9 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
||||
throughputHeaderText = AutoPilotUtils.getAutoPilotHeaderText().toLocaleLowerCase();
|
||||
} else {
|
||||
const minRU: string = SharedConstants.CollectionCreation.DefaultCollectionRUs400.toLocaleString();
|
||||
|
||||
let maxRU: string;
|
||||
if (userContext.isTryCosmosDBSubscription) {
|
||||
maxRU = Constants.TryCosmosExperience.maxRU.toLocaleString();
|
||||
} else if (!isSharded) {
|
||||
maxRU = "10000";
|
||||
} else {
|
||||
maxRU = "unlimited";
|
||||
}
|
||||
|
||||
const maxRU: string = userContext.isTryCosmosDBSubscription
|
||||
? Constants.TryCosmosExperience.maxRU.toLocaleString()
|
||||
: "unlimited";
|
||||
throughputHeaderText = `throughput (${minRU} - ${maxRU} RU/s)`;
|
||||
}
|
||||
return `${isDatabase ? "Database" : getCollectionName()} ${throughputHeaderText}`;
|
||||
|
||||
@@ -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>
|
||||
@@ -3,7 +3,7 @@
|
||||
exports[`ThroughputInput Pane should render Default properly 1`] = `
|
||||
<ThroughputInput
|
||||
isDatabase={false}
|
||||
isSharded={true}
|
||||
isSharded={false}
|
||||
onCostAcknowledgeChange={[Function]}
|
||||
setIsAutoscale={[Function]}
|
||||
setThroughputValue={[Function]}
|
||||
|
||||
@@ -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";
|
||||
@@ -58,7 +58,7 @@ export interface TreeComponentProps {
|
||||
export class TreeComponent extends React.Component<TreeComponentProps> {
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<div style={this.props.style} className={`treeComponent ${this.props.className}`} role="tree">
|
||||
<div style={this.props.style} className={`treeComponent ${this.props.className}`}>
|
||||
<TreeNodeComponent paddingLeft={0} node={this.props.rootNode} generation={0} />
|
||||
</div>
|
||||
);
|
||||
@@ -172,7 +172,6 @@ export class TreeNodeComponent extends React.Component<TreeNodeComponentProps, T
|
||||
className={`${this.props.node.className || ""} main${generation} nodeItem ${showSelected ? "selected" : ""}`}
|
||||
onClick={(event: React.MouseEvent<HTMLDivElement>) => this.onNodeClick(event, node)}
|
||||
onKeyPress={(event: React.KeyboardEvent<HTMLDivElement>) => this.onNodeKeyPress(event, node)}
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
className={`treeNodeHeader ${this.state.isMenuShowing ? "showingMenu" : ""}`}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
exports[`TreeComponent renders a simple tree 1`] = `
|
||||
<div
|
||||
className="treeComponent tree"
|
||||
role="tree"
|
||||
>
|
||||
<TreeNodeComponent
|
||||
generation={0}
|
||||
@@ -38,7 +37,6 @@ exports[`TreeNodeComponent does not render children by default 1`] = `
|
||||
className=" main2 nodeItem "
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
className="treeNodeHeader "
|
||||
@@ -139,7 +137,6 @@ exports[`TreeNodeComponent renders a simple node (sorted children, expanded) 1`]
|
||||
className="nodeClassname main12 nodeItem "
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
className="treeNodeHeader "
|
||||
@@ -288,7 +285,6 @@ exports[`TreeNodeComponent renders loading icon 1`] = `
|
||||
className=" main2 nodeItem "
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
className="treeNodeHeader "
|
||||
@@ -360,7 +356,6 @@ exports[`TreeNodeComponent renders sorted children, expanded, leaves and parents
|
||||
className="nodeClassname main12 nodeItem "
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
className="treeNodeHeader "
|
||||
@@ -528,7 +523,6 @@ exports[`TreeNodeComponent renders unsorted children by default 1`] = `
|
||||
className=" main2 nodeItem "
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
className="treeNodeHeader "
|
||||
|
||||
@@ -2,22 +2,22 @@ jest.mock("../Graph/GraphExplorerComponent/GremlinClient");
|
||||
jest.mock("../../Common/dataAccess/createCollection");
|
||||
jest.mock("../../Common/dataAccess/createDocument");
|
||||
import * as ko from "knockout";
|
||||
import Q from "q";
|
||||
import { createDocument } from "../../Common/dataAccess/createDocument";
|
||||
import { DatabaseAccount } from "../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { updateUserContext } from "../../UserContext";
|
||||
import Explorer from "../Explorer";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
|
||||
|
||||
describe("ContainerSampleGenerator", () => {
|
||||
let explorerStub: Explorer;
|
||||
|
||||
beforeAll(() => {
|
||||
explorerStub = {
|
||||
refreshAllDatabases: () => {},
|
||||
} as Explorer;
|
||||
});
|
||||
const createExplorerStub = (database: ViewModels.Database): Explorer => {
|
||||
const explorerStub = {} as Explorer;
|
||||
explorerStub.databases = ko.observableArray<ViewModels.Database>([database]);
|
||||
explorerStub.findDatabaseWithId = () => database;
|
||||
explorerStub.refreshAllDatabases = () => Q.resolve();
|
||||
return explorerStub;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
(createDocument as jest.Mock).mockResolvedValue(undefined);
|
||||
@@ -59,7 +59,8 @@ describe("ContainerSampleGenerator", () => {
|
||||
loadCollections: () => {},
|
||||
} as ViewModels.Database;
|
||||
database.findCollectionWithId = () => collection;
|
||||
useDatabases.getState().addDatabases([database]);
|
||||
|
||||
const explorerStub = createExplorerStub(database);
|
||||
|
||||
const generator = await ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub);
|
||||
generator.setData(sampleData);
|
||||
@@ -107,8 +108,8 @@ describe("ContainerSampleGenerator", () => {
|
||||
} as ViewModels.Database;
|
||||
database.findCollectionWithId = () => collection;
|
||||
collection.databaseId = database.id();
|
||||
useDatabases.getState().addDatabases([database]);
|
||||
|
||||
const explorerStub = createExplorerStub(database);
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
@@ -125,6 +126,7 @@ describe("ContainerSampleGenerator", () => {
|
||||
|
||||
it("should not create any sample for Mongo API account", async () => {
|
||||
const experience = "Sample generation not supported for this API Mongo";
|
||||
const explorerStub = createExplorerStub(undefined);
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
@@ -139,6 +141,7 @@ describe("ContainerSampleGenerator", () => {
|
||||
|
||||
it("should not create any sample for Table API account", async () => {
|
||||
const experience = "Sample generation not supported for this API Tables";
|
||||
const explorerStub = createExplorerStub(undefined);
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
@@ -160,6 +163,7 @@ describe("ContainerSampleGenerator", () => {
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
const explorerStub = createExplorerStub(undefined);
|
||||
// Rejects with error that contains experience
|
||||
await expect(ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub)).rejects.toMatch(experience);
|
||||
});
|
||||
|
||||
@@ -7,7 +7,6 @@ import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils"
|
||||
import GraphTab from ".././Tabs/GraphTab";
|
||||
import Explorer from "../Explorer";
|
||||
import { GremlinClient } from "../Graph/GraphExplorerComponent/GremlinClient";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
|
||||
interface SampleDataFile extends DataModels.CreateCollectionParams {
|
||||
data: any[];
|
||||
@@ -60,7 +59,7 @@ export class ContainerSampleGenerator {
|
||||
|
||||
await createCollection(createRequest);
|
||||
await this.container.refreshAllDatabases();
|
||||
const database = useDatabases.getState().findDatabaseWithId(this.sampleDataFile.databaseId);
|
||||
const database = this.container.findDatabaseWithId(this.sampleDataFile.databaseId);
|
||||
if (!database) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import * as ko from "knockout";
|
||||
import * as sinon from "sinon";
|
||||
import { Collection, Database } from "../../Contracts/ViewModels";
|
||||
import Explorer from "../Explorer";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
|
||||
import { DataSamplesUtil } from "./DataSamplesUtil";
|
||||
|
||||
@@ -17,8 +16,8 @@ describe("DataSampleUtils", () => {
|
||||
collections: ko.observableArray<Collection>([collection]),
|
||||
} as Database;
|
||||
const explorer = {} as Explorer;
|
||||
explorer.databases = ko.observableArray<Database>([database]);
|
||||
explorer.showOkModalDialog = () => {};
|
||||
useDatabases.getState().addDatabases([database]);
|
||||
const dataSamplesUtil = new DataSamplesUtil(explorer);
|
||||
|
||||
const fakeGenerator = sinon.createStubInstance<ContainerSampleGenerator>(ContainerSampleGenerator as any);
|
||||
|
||||
@@ -2,7 +2,6 @@ import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { logConsoleError, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
|
||||
import Explorer from "../Explorer";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
|
||||
|
||||
export class DataSamplesUtil {
|
||||
@@ -18,7 +17,7 @@ export class DataSamplesUtil {
|
||||
|
||||
const databaseName = generator.getDatabaseId();
|
||||
const containerName = generator.getCollectionId();
|
||||
if (this.hasContainer(databaseName, containerName, useDatabases.getState().databases)) {
|
||||
if (this.hasContainer(databaseName, containerName, this.container.databases())) {
|
||||
const msg = `The container ${containerName} in database ${databaseName} already exists. Please delete it and retry.`;
|
||||
this.container.showOkModalDialog(DataSamplesUtil.DialogTitle, msg);
|
||||
logConsoleError(msg);
|
||||
|
||||
43
src/Explorer/Explorer.test.tsx
Normal file
43
src/Explorer/Explorer.test.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
jest.mock("./../Common/dataAccess/deleteDatabase");
|
||||
jest.mock("./../Shared/Telemetry/TelemetryProcessor");
|
||||
import * as ko from "knockout";
|
||||
import { deleteDatabase } from "./../Common/dataAccess/deleteDatabase";
|
||||
import * as ViewModels from "./../Contracts/ViewModels";
|
||||
import Explorer from "./Explorer";
|
||||
|
||||
describe("Explorer.isLastDatabase() and Explorer.isLastNonEmptyDatabase()", () => {
|
||||
let explorer: Explorer;
|
||||
beforeAll(() => {
|
||||
(deleteDatabase as jest.Mock).mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
explorer = new Explorer();
|
||||
});
|
||||
|
||||
it("should be true if only 1 database", () => {
|
||||
const database = {} as ViewModels.Database;
|
||||
explorer.databases = ko.observableArray<ViewModels.Database>([database]);
|
||||
expect(explorer.isLastDatabase()).toBe(true);
|
||||
});
|
||||
|
||||
it("should be false if only 2 databases", () => {
|
||||
const database = {} as ViewModels.Database;
|
||||
const database2 = {} as ViewModels.Database;
|
||||
explorer.databases = ko.observableArray<ViewModels.Database>([database, database2]);
|
||||
expect(explorer.isLastDatabase()).toBe(false);
|
||||
});
|
||||
|
||||
it("should be false if not last empty database", () => {
|
||||
const database = {} as ViewModels.Database;
|
||||
explorer.databases = ko.observableArray<ViewModels.Database>([database]);
|
||||
expect(explorer.isLastNonEmptyDatabase()).toBe(false);
|
||||
});
|
||||
|
||||
it("should be true if last non empty database", () => {
|
||||
const database = {} as ViewModels.Database;
|
||||
database.collections = ko.observableArray<ViewModels.Collection>([{} as ViewModels.Collection]);
|
||||
explorer.databases = ko.observableArray<ViewModels.Database>([database]);
|
||||
expect(explorer.isLastNonEmptyDatabase()).toBe(true);
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -10,7 +10,7 @@ import { queryDocumentsPage } from "../../../Common/dataAccess/queryDocumentsPag
|
||||
import * as DataModels from "../../../Contracts/DataModels";
|
||||
import * as StorageUtility from "../../../Shared/StorageUtility";
|
||||
import { TabComponent } from "../../Controls/Tabs/TabComponent";
|
||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/ConsoleData";
|
||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||
import GraphTab from "../../Tabs/GraphTab";
|
||||
import * as D3ForceGraph from "./D3ForceGraph";
|
||||
import { GraphData } from "./GraphData";
|
||||
@@ -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 {
|
||||
|
||||
@@ -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";
|
||||
@@ -18,7 +18,7 @@ import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../../Ut
|
||||
import { EditorReact } from "../../Controls/Editor/EditorReact";
|
||||
import * as InputTypeaheadComponent from "../../Controls/InputTypeahead/InputTypeaheadComponent";
|
||||
import * as TabComponent from "../../Controls/Tabs/TabComponent";
|
||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/ConsoleData";
|
||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||
import { IGraphConfig } from "../../Tabs/GraphTab";
|
||||
import { ArraysByKeyCache } from "./ArraysByKeyCache";
|
||||
import * as D3ForceGraph from "./D3ForceGraph";
|
||||
@@ -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,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import * as React from "react";
|
||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { IGraphConfig } from "../../Tabs/GraphTab";
|
||||
import { GraphAccessor, GraphExplorer } from "./GraphExplorer";
|
||||
interface Parameter {
|
||||
onIsNewVertexDisabledChange: (isEnabled: boolean) => void;
|
||||
onGraphAccessorCreated: (instance: GraphAccessor) => void;
|
||||
onIsFilterQueryLoading: (isFilterQueryLoading: boolean) => void;
|
||||
onIsValidQuery: (isValidQuery: boolean) => void;
|
||||
onIsPropertyEditing: (isEditing: boolean) => void;
|
||||
onIsGraphDisplayed: (isDisplayed: boolean) => void;
|
||||
onResetDefaultGraphConfigValues: () => void;
|
||||
|
||||
collectionPartitionKeyProperty: string;
|
||||
graphBackendEndpoint: string;
|
||||
databaseId: string;
|
||||
collectionId: string;
|
||||
masterKey: string;
|
||||
|
||||
onLoadStartKey: number;
|
||||
onLoadStartKeyChange: (newKey: number) => void;
|
||||
resourceId: string;
|
||||
|
||||
igraphConfigUiData: ViewModels.IGraphConfigUiData;
|
||||
igraphConfig: IGraphConfig;
|
||||
setIConfigUiData?: (data: string[]) => void;
|
||||
}
|
||||
|
||||
interface IGraphExplorerProps {
|
||||
isChanged: boolean;
|
||||
}
|
||||
|
||||
interface IGraphExplorerStates {
|
||||
isChangedState: boolean;
|
||||
}
|
||||
|
||||
export interface GraphExplorerAdapter
|
||||
extends ReactAdapter,
|
||||
React.Component<IGraphExplorerProps, IGraphExplorerStates> {}
|
||||
export class GraphExplorerAdapter implements ReactAdapter {
|
||||
public params: Parameter;
|
||||
public parameters = {};
|
||||
public isNewVertexDisabled: boolean;
|
||||
|
||||
public constructor(params: Parameter, props?: IGraphExplorerProps) {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public renderComponent(): JSX.Element {
|
||||
return (
|
||||
<GraphExplorer
|
||||
onIsNewVertexDisabledChange={this.params.onIsNewVertexDisabledChange}
|
||||
onGraphAccessorCreated={this.params.onGraphAccessorCreated}
|
||||
onIsFilterQueryLoadingChange={this.params.onIsFilterQueryLoading}
|
||||
onIsValidQueryChange={this.params.onIsValidQuery}
|
||||
onIsPropertyEditing={this.params.onIsPropertyEditing}
|
||||
onIsGraphDisplayed={this.params.onIsGraphDisplayed}
|
||||
onResetDefaultGraphConfigValues={this.params.onResetDefaultGraphConfigValues}
|
||||
collectionPartitionKeyProperty={this.params.collectionPartitionKeyProperty}
|
||||
graphBackendEndpoint={this.params.graphBackendEndpoint}
|
||||
databaseId={this.params.databaseId}
|
||||
collectionId={this.params.collectionId}
|
||||
masterKey={this.params.masterKey}
|
||||
onLoadStartKey={this.params.onLoadStartKey}
|
||||
onLoadStartKeyChange={this.params.onLoadStartKeyChange}
|
||||
resourceId={this.params.resourceId}
|
||||
igraphConfigUiData={this.params.igraphConfigUiData}
|
||||
igraphConfig={this.params.igraphConfig}
|
||||
setIConfigUiData={this.params.setIConfigUiData}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import * as sinon from "sinon";
|
||||
import { GraphData, GremlinEdge, GremlinVertex } from "./GraphData";
|
||||
import { GraphExplorer } from "./GraphExplorer";
|
||||
import * as GraphUtil from "./GraphUtil";
|
||||
import { GraphData, GremlinVertex, GremlinEdge } from "./GraphData";
|
||||
import * as sinon from "sinon";
|
||||
import { GraphExplorer } from "./GraphExplorer";
|
||||
window.$ = window.jQuery = require("jquery");
|
||||
|
||||
const OUT_E_MATCHER = "g\\.V\\(.*\\).outE\\(\\).*\\.as\\('e'\\).inV\\(\\)\\.as\\('v'\\)\\.select\\('e', *'v'\\)";
|
||||
const IN_E_MATCHER = "g\\.V\\(.*\\).inE\\(\\).*\\.as\\('e'\\).outV\\(\\)\\.as\\('v'\\)\\.select\\('e', *'v'\\)";
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
* 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 CancelIcon from "../../../../images/cancel.svg";
|
||||
import CheckIcon from "../../../../images/check.svg";
|
||||
import DeleteIcon from "../../../../images/delete.svg";
|
||||
import EditIcon from "../../../../images/edit.svg";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { AccessibleElement } from "../../Controls/AccessibleElement/AccessibleElement";
|
||||
import { CollapsiblePanel } from "../../Controls/CollapsiblePanel/CollapsiblePanel";
|
||||
import { Item } from "../../Controls/InputTypeahead/InputTypeaheadComponent";
|
||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/ConsoleData";
|
||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||
import * as EditorNeighbors from "./EditorNeighborsComponent";
|
||||
import { EditorNodePropertiesComponent } from "./EditorNodePropertiesComponent";
|
||||
import {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -3,71 +3,100 @@
|
||||
* If the component signals a change through the callback passed in the properties, it must render the React component when appropriate
|
||||
* and update any knockout observables passed from the parent.
|
||||
*/
|
||||
import { CommandBar as FluentCommandBar, ICommandBarItemProps } from "@fluentui/react";
|
||||
import { CommandBar, ICommandBarItemProps } from "@fluentui/react";
|
||||
import * as ko from "knockout";
|
||||
import * as React from "react";
|
||||
import create, { UseStore } from "zustand";
|
||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
||||
import { StyleConstants } from "../../../Common/Constants";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
import Explorer from "../../Explorer";
|
||||
import { useSelectedNode } from "../../useSelectedNode";
|
||||
import * as CommandBarComponentButtonFactory from "./CommandBarComponentButtonFactory";
|
||||
import * as CommandBarUtil from "./CommandBarUtil";
|
||||
|
||||
interface Props {
|
||||
container: Explorer;
|
||||
export class CommandBarComponentAdapter implements ReactAdapter {
|
||||
public parameters: ko.Observable<number>;
|
||||
public container: Explorer;
|
||||
private tabsButtons: CommandButtonComponentProps[];
|
||||
private isNotebookTabActive: ko.Computed<boolean>;
|
||||
|
||||
constructor(container: Explorer) {
|
||||
this.container = container;
|
||||
this.tabsButtons = [];
|
||||
this.isNotebookTabActive = ko.computed(
|
||||
() => container.tabsManager.activeTab()?.tabKind === ViewModels.CollectionTabKind.NotebookV2
|
||||
);
|
||||
|
||||
// These are the parameters watched by the react binding that will trigger a renderComponent() if one of the ko mutates
|
||||
const toWatch = [
|
||||
container.isDatabaseNodeOrNoneSelected,
|
||||
container.isDatabaseNodeSelected,
|
||||
container.isNoneSelected,
|
||||
container.isResourceTokenCollectionNodeSelected,
|
||||
container.isHostedDataExplorerEnabled,
|
||||
container.isSynapseLinkUpdating,
|
||||
userContext?.databaseAccount,
|
||||
this.isNotebookTabActive,
|
||||
container.isServerlessEnabled,
|
||||
];
|
||||
|
||||
ko.computed(() => ko.toJSON(toWatch)).subscribe(() => this.triggerRender());
|
||||
this.parameters = ko.observable(Date.now());
|
||||
}
|
||||
|
||||
public onUpdateTabsButtons(buttons: CommandButtonComponentProps[]): void {
|
||||
this.tabsButtons = buttons;
|
||||
this.triggerRender();
|
||||
}
|
||||
|
||||
public renderComponent(): JSX.Element {
|
||||
const backgroundColor = StyleConstants.BaseLight;
|
||||
|
||||
const staticButtons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(this.container);
|
||||
const contextButtons = (this.tabsButtons || []).concat(
|
||||
CommandBarComponentButtonFactory.createContextCommandBarButtons(this.container)
|
||||
);
|
||||
const controlButtons = CommandBarComponentButtonFactory.createControlCommandBarButtons(this.container);
|
||||
|
||||
const uiFabricStaticButtons = CommandBarUtil.convertButton(staticButtons, backgroundColor);
|
||||
if (this.tabsButtons && this.tabsButtons.length > 0) {
|
||||
uiFabricStaticButtons.forEach((btn: ICommandBarItemProps) => (btn.iconOnly = true));
|
||||
}
|
||||
|
||||
const uiFabricTabsButtons: ICommandBarItemProps[] = CommandBarUtil.convertButton(contextButtons, backgroundColor);
|
||||
|
||||
if (uiFabricTabsButtons.length > 0) {
|
||||
uiFabricStaticButtons.push(CommandBarUtil.createDivider("commandBarDivider"));
|
||||
}
|
||||
|
||||
const uiFabricControlButtons = CommandBarUtil.convertButton(controlButtons, backgroundColor);
|
||||
uiFabricControlButtons.forEach((btn: ICommandBarItemProps) => (btn.iconOnly = true));
|
||||
|
||||
if (this.isNotebookTabActive()) {
|
||||
uiFabricControlButtons.unshift(
|
||||
CommandBarUtil.createMemoryTracker("memoryTracker", this.container.memoryUsageInfo)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="commandBarContainer">
|
||||
<CommandBar
|
||||
ariaLabel="Use left and right arrow keys to navigate between commands"
|
||||
items={uiFabricStaticButtons.concat(uiFabricTabsButtons)}
|
||||
farItems={uiFabricControlButtons}
|
||||
styles={{
|
||||
root: { backgroundColor: backgroundColor },
|
||||
}}
|
||||
overflowButtonProps={{ ariaLabel: "More commands" }}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
private triggerRender() {
|
||||
window.requestAnimationFrame(() => this.parameters(Date.now()));
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommandBarStore {
|
||||
contextButtons: CommandButtonComponentProps[];
|
||||
setContextButtons: (contextButtons: CommandButtonComponentProps[]) => void;
|
||||
}
|
||||
|
||||
export const useCommandBar: UseStore<CommandBarStore> = create((set) => ({
|
||||
contextButtons: [],
|
||||
setContextButtons: (contextButtons: CommandButtonComponentProps[]) => set((state) => ({ ...state, contextButtons })),
|
||||
}));
|
||||
|
||||
export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
||||
const selectedNodeState = useSelectedNode();
|
||||
const buttons = useCommandBar((state) => state.contextButtons);
|
||||
const backgroundColor = StyleConstants.BaseLight;
|
||||
|
||||
const staticButtons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(container, selectedNodeState);
|
||||
const contextButtons = (buttons || []).concat(
|
||||
CommandBarComponentButtonFactory.createContextCommandBarButtons(container, selectedNodeState)
|
||||
);
|
||||
const controlButtons = CommandBarComponentButtonFactory.createControlCommandBarButtons(container);
|
||||
|
||||
const uiFabricStaticButtons = CommandBarUtil.convertButton(staticButtons, backgroundColor);
|
||||
if (buttons && buttons.length > 0) {
|
||||
uiFabricStaticButtons.forEach((btn: ICommandBarItemProps) => (btn.iconOnly = true));
|
||||
}
|
||||
|
||||
const uiFabricTabsButtons: ICommandBarItemProps[] = CommandBarUtil.convertButton(contextButtons, backgroundColor);
|
||||
|
||||
if (uiFabricTabsButtons.length > 0) {
|
||||
uiFabricStaticButtons.push(CommandBarUtil.createDivider("commandBarDivider"));
|
||||
}
|
||||
|
||||
const uiFabricControlButtons = CommandBarUtil.convertButton(controlButtons, backgroundColor);
|
||||
uiFabricControlButtons.forEach((btn: ICommandBarItemProps) => (btn.iconOnly = true));
|
||||
|
||||
if (container.tabsManager.activeTab()?.tabKind === ViewModels.CollectionTabKind.NotebookV2) {
|
||||
uiFabricControlButtons.unshift(CommandBarUtil.createMemoryTracker("memoryTracker", container.memoryUsageInfo));
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="commandBarContainer">
|
||||
<FluentCommandBar
|
||||
ariaLabel="Use left and right arrow keys to navigate between commands"
|
||||
items={uiFabricStaticButtons.concat(uiFabricTabsButtons)}
|
||||
farItems={uiFabricControlButtons}
|
||||
styles={{
|
||||
root: { backgroundColor: backgroundColor },
|
||||
}}
|
||||
overflowButtonProps={{ ariaLabel: "More commands" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
import * as ko from "knockout";
|
||||
import { AuthType } from "../../../AuthType";
|
||||
import { DatabaseAccount } from "../../../Contracts/DataModels";
|
||||
import { CollectionBase } from "../../../Contracts/ViewModels";
|
||||
import { GitHubOAuthService } from "../../../GitHub/GitHubOAuthService";
|
||||
import { updateUserContext } from "../../../UserContext";
|
||||
import Explorer from "../../Explorer";
|
||||
import NotebookManager from "../../Notebook/NotebookManager";
|
||||
import { useSelectedNode } from "../../useSelectedNode";
|
||||
import * as CommandBarComponentButtonFactory from "./CommandBarComponentButtonFactory";
|
||||
|
||||
describe("CommandBarComponentButtonFactory tests", () => {
|
||||
let mockExplorer: Explorer;
|
||||
|
||||
afterEach(() => useSelectedNode.getState().setSelectedNode(undefined));
|
||||
|
||||
describe("Enable Azure Synapse Link Button", () => {
|
||||
const enableAzureSynapseLinkBtnLabel = "Enable Azure Synapse Link";
|
||||
const selectedNodeState = useSelectedNode.getState();
|
||||
|
||||
beforeAll(() => {
|
||||
mockExplorer = {} as Explorer;
|
||||
@@ -28,12 +23,17 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||
|
||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
||||
mockExplorer.isRunningOnNationalCloud = () => false;
|
||||
});
|
||||
|
||||
it("Account is not serverless - button should be visible", () => {
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const enableAzureSynapseLinkBtn = buttons.find(
|
||||
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel
|
||||
);
|
||||
@@ -41,14 +41,9 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
});
|
||||
|
||||
it("Account is serverless - button should be hidden", () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
capabilities: [{ name: "EnableServerless" }],
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const enableAzureSynapseLinkBtn = buttons.find(
|
||||
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel
|
||||
);
|
||||
@@ -58,12 +53,10 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
|
||||
describe("Enable notebook button", () => {
|
||||
const enableNotebookBtnLabel = "Enable Notebooks (Preview)";
|
||||
const selectedNodeState = useSelectedNode.getState();
|
||||
|
||||
beforeAll(() => {
|
||||
mockExplorer = {} as Explorer;
|
||||
updateUserContext({
|
||||
portalEnv: "prod",
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
capabilities: [{ name: "EnableTable" }],
|
||||
@@ -71,19 +64,18 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||
});
|
||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||
|
||||
afterEach(() => {
|
||||
updateUserContext({
|
||||
portalEnv: "prod",
|
||||
});
|
||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
||||
});
|
||||
|
||||
it("Notebooks is already enabled - button should be hidden", () => {
|
||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
||||
mockExplorer.isRunningOnNationalCloud = ko.observable(false);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
||||
expect(enableNotebookBtn).toBeUndefined();
|
||||
});
|
||||
@@ -91,11 +83,9 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
it("Account is running on one of the national clouds - button should be hidden", () => {
|
||||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
||||
updateUserContext({
|
||||
portalEnv: "mooncake",
|
||||
});
|
||||
mockExplorer.isRunningOnNationalCloud = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
||||
expect(enableNotebookBtn).toBeUndefined();
|
||||
});
|
||||
@@ -103,8 +93,9 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
it("Notebooks is not enabled but is available - button should be shown and enabled", () => {
|
||||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
||||
mockExplorer.isRunningOnNationalCloud = ko.observable(false);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
||||
expect(enableNotebookBtn).toBeDefined();
|
||||
expect(enableNotebookBtn.disabled).toBe(false);
|
||||
@@ -114,8 +105,9 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
it("Notebooks is not enabled and is unavailable - button should be shown and disabled", () => {
|
||||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
||||
mockExplorer.isRunningOnNationalCloud = ko.observable(false);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const enableNotebookBtn = buttons.find((button) => button.commandButtonLabel === enableNotebookBtnLabel);
|
||||
expect(enableNotebookBtn).toBeDefined();
|
||||
expect(enableNotebookBtn.disabled).toBe(true);
|
||||
@@ -127,7 +119,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
|
||||
describe("Open Mongo Shell button", () => {
|
||||
const openMongoShellBtnLabel = "Open Mongo Shell";
|
||||
const selectedNodeState = useSelectedNode.getState();
|
||||
|
||||
beforeAll(() => {
|
||||
mockExplorer = {} as Explorer;
|
||||
@@ -139,6 +130,9 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||
|
||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
||||
mockExplorer.isShellEnabled = ko.observable(true);
|
||||
});
|
||||
|
||||
@@ -154,7 +148,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
});
|
||||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
||||
|
||||
mockExplorer.isRunningOnNationalCloud = ko.observable(false);
|
||||
mockExplorer.isShellEnabled = ko.observable(true);
|
||||
});
|
||||
|
||||
@@ -162,23 +156,21 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
updateUserContext({
|
||||
apiType: "SQL",
|
||||
});
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||
expect(openMongoShellBtn).toBeUndefined();
|
||||
});
|
||||
|
||||
it("Running on a national cloud - button should be hidden", () => {
|
||||
updateUserContext({
|
||||
portalEnv: "mooncake",
|
||||
});
|
||||
mockExplorer.isRunningOnNationalCloud = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||
expect(openMongoShellBtn).toBeUndefined();
|
||||
});
|
||||
|
||||
it("Notebooks is not enabled and is unavailable - button should be hidden", () => {
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||
expect(openMongoShellBtn).toBeUndefined();
|
||||
});
|
||||
@@ -186,7 +178,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
it("Notebooks is not enabled and is available - button should be hidden", () => {
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||
expect(openMongoShellBtn).toBeUndefined();
|
||||
});
|
||||
@@ -194,7 +186,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
it("Notebooks is enabled and is unavailable - button should be shown and enabled", () => {
|
||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||
expect(openMongoShellBtn).toBeDefined();
|
||||
expect(openMongoShellBtn.disabled).toBe(false);
|
||||
@@ -205,7 +197,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||
expect(openMongoShellBtn).toBeDefined();
|
||||
expect(openMongoShellBtn.disabled).toBe(false);
|
||||
@@ -217,7 +209,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
||||
mockExplorer.isShellEnabled = ko.observable(false);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||
expect(openMongoShellBtn).toBeUndefined();
|
||||
});
|
||||
@@ -225,7 +217,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
|
||||
describe("Open Cassandra Shell button", () => {
|
||||
const openCassandraShellBtnLabel = "Open Cassandra Shell";
|
||||
const selectedNodeState = useSelectedNode.getState();
|
||||
|
||||
beforeAll(() => {
|
||||
mockExplorer = {} as Explorer;
|
||||
@@ -237,6 +228,9 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||
|
||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -249,6 +243,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
});
|
||||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
||||
mockExplorer.isRunningOnNationalCloud = ko.observable(false);
|
||||
});
|
||||
|
||||
it("Cassandra Api not available - button should be hidden", () => {
|
||||
@@ -260,23 +255,21 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
console.log(mockExplorer);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||
expect(openCassandraShellBtn).toBeUndefined();
|
||||
});
|
||||
|
||||
it("Running on a national cloud - button should be hidden", () => {
|
||||
updateUserContext({
|
||||
portalEnv: "mooncake",
|
||||
});
|
||||
mockExplorer.isRunningOnNationalCloud = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||
expect(openCassandraShellBtn).toBeUndefined();
|
||||
});
|
||||
|
||||
it("Notebooks is not enabled and is unavailable - button should be shown and disabled", () => {
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||
expect(openCassandraShellBtn).toBeUndefined();
|
||||
});
|
||||
@@ -284,7 +277,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
it("Notebooks is not enabled and is available - button should be shown and enabled", () => {
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||
expect(openCassandraShellBtn).toBeUndefined();
|
||||
});
|
||||
@@ -292,7 +285,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
it("Notebooks is enabled and is unavailable - button should be shown and enabled", () => {
|
||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||
expect(openCassandraShellBtn).toBeDefined();
|
||||
expect(openCassandraShellBtn.disabled).toBe(false);
|
||||
@@ -303,7 +296,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||
expect(openCassandraShellBtn).toBeDefined();
|
||||
expect(openCassandraShellBtn.disabled).toBe(false);
|
||||
@@ -314,7 +307,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
describe("GitHub buttons", () => {
|
||||
const connectToGitHubBtnLabel = "Connect to GitHub";
|
||||
const manageGitHubSettingsBtnLabel = "Manage GitHub settings";
|
||||
const selectedNodeState = useSelectedNode.getState();
|
||||
|
||||
beforeAll(() => {
|
||||
mockExplorer = {} as Explorer;
|
||||
@@ -327,10 +319,12 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
});
|
||||
|
||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
||||
|
||||
mockExplorer.isRunningOnNationalCloud = ko.observable(false);
|
||||
mockExplorer.notebookManager = new NotebookManager();
|
||||
mockExplorer.notebookManager.gitHubOAuthService = new GitHubOAuthService(undefined);
|
||||
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -344,7 +338,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
it("Notebooks is enabled and GitHubOAuthService is not logged in - connect to github button should be visible", () => {
|
||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const connectToGitHubBtn = buttons.find((button) => button.commandButtonLabel === connectToGitHubBtnLabel);
|
||||
expect(connectToGitHubBtn).toBeDefined();
|
||||
});
|
||||
@@ -353,7 +347,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
mockExplorer.isNotebookEnabled = ko.observable(true);
|
||||
mockExplorer.notebookManager.gitHubOAuthService.isLoggedIn = jest.fn().mockReturnValue(true);
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const manageGitHubSettingsBtn = buttons.find(
|
||||
(button) => button.commandButtonLabel === manageGitHubSettingsBtnLabel
|
||||
);
|
||||
@@ -361,7 +355,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
});
|
||||
|
||||
it("Notebooks is not enabled - connect to github and manage github settings buttons should be hidden", () => {
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
|
||||
const connectToGitHubBtn = buttons.find((button) => button.commandButtonLabel === connectToGitHubBtnLabel);
|
||||
expect(connectToGitHubBtn).toBeUndefined();
|
||||
@@ -374,13 +368,11 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
});
|
||||
|
||||
describe("Resource token", () => {
|
||||
const mockCollection = { id: ko.observable("test") } as CollectionBase;
|
||||
useSelectedNode.getState().setSelectedNode(mockCollection);
|
||||
const selectedNodeState = useSelectedNode.getState();
|
||||
beforeAll(() => {
|
||||
mockExplorer = {} as Explorer;
|
||||
mockExplorer.resourceTokenCollection = ko.observable(mockCollection);
|
||||
|
||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||
mockExplorer.isResourceTokenCollectionNodeSelected = ko.computed(() => true);
|
||||
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
||||
updateUserContext({
|
||||
authType: AuthType.ResourceToken,
|
||||
});
|
||||
@@ -392,7 +384,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
kind: "DocumentDB",
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
expect(buttons.length).toBe(2);
|
||||
expect(buttons[0].commandButtonLabel).toBe("New SQL Query");
|
||||
expect(buttons[0].disabled).toBe(false);
|
||||
|
||||
@@ -1,46 +1,37 @@
|
||||
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 { isServerlessAccount } from "../../../Utils/CapabilityUtils";
|
||||
import { isRunningOnNationalCloud } from "../../../Utils/CloudUtils";
|
||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
import Explorer from "../../Explorer";
|
||||
import { OpenFullScreen } from "../../OpenFullScreen";
|
||||
import { LoadQueryPane } from "../../Panes/LoadQueryPane/LoadQueryPane";
|
||||
import { SettingsPane } from "../../Panes/SettingsPane/SettingsPane";
|
||||
import { SelectedNodeState } from "../../useSelectedNode";
|
||||
|
||||
let counter = 0;
|
||||
|
||||
export function createStaticCommandBarButtons(
|
||||
container: Explorer,
|
||||
selectedNodeState: SelectedNodeState
|
||||
): CommandButtonComponentProps[] {
|
||||
export function createStaticCommandBarButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||
if (userContext.authType === AuthType.ResourceToken) {
|
||||
return createStaticCommandBarButtonsForResourceToken(container, selectedNodeState);
|
||||
return createStaticCommandBarButtonsForResourceToken(container);
|
||||
}
|
||||
|
||||
const newCollectionBtn = createNewCollectionGroup(container);
|
||||
@@ -74,36 +65,35 @@ export function createStaticCommandBarButtons(
|
||||
buttons.push(createOpenTerminalButton(container));
|
||||
|
||||
buttons.push(createNotebookWorkspaceResetButton(container));
|
||||
|
||||
buttons.push(createDivider());
|
||||
buttons.push(createOpenPostgreSQLTerminalButton(container));
|
||||
|
||||
if (userContext.apiType === "Mongo" &&
|
||||
container.isShellEnabled() &&
|
||||
selectedNodeState.isDatabaseNodeOrNoneSelected()){
|
||||
if (
|
||||
(userContext.apiType === "Mongo" && container.isShellEnabled() && container.isDatabaseNodeOrNoneSelected()) ||
|
||||
userContext.apiType === "Cassandra"
|
||||
) {
|
||||
buttons.push(createDivider());
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
buttons.push(createOpenCassandraTerminalButton(container));
|
||||
} else {
|
||||
buttons.push(createOpenMongoTerminalButton(container));
|
||||
}
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
buttons.push(createOpenCassandraTerminalButton(container));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!isRunningOnNationalCloud()) {
|
||||
if (!container.isRunningOnNationalCloud()) {
|
||||
buttons.push(createEnableNotebooksButton(container));
|
||||
}
|
||||
}
|
||||
|
||||
if (!selectedNodeState.isDatabaseNodeOrNoneSelected()) {
|
||||
if (!container.isDatabaseNodeOrNoneSelected()) {
|
||||
const isQuerySupported = userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
||||
|
||||
if (isQuerySupported) {
|
||||
buttons.push(createDivider());
|
||||
const newSqlQueryBtn = createNewSQLQueryButton(selectedNodeState);
|
||||
const newSqlQueryBtn = createNewSQLQueryButton(container);
|
||||
buttons.push(newSqlQueryBtn);
|
||||
}
|
||||
|
||||
if (isQuerySupported && selectedNodeState.findSelectedCollection()) {
|
||||
if (isQuerySupported && container.selectedNode() && container.findSelectedCollection()) {
|
||||
const openQueryBtn = createOpenQueryButton(container);
|
||||
openQueryBtn.children = [createOpenQueryButton(container), createOpenQueryFromDiskButton()];
|
||||
openQueryBtn.children = [createOpenQueryButton(container), createOpenQueryFromDiskButton(container)];
|
||||
buttons.push(openQueryBtn);
|
||||
}
|
||||
|
||||
@@ -113,16 +103,16 @@ export function createStaticCommandBarButtons(
|
||||
iconSrc: AddStoredProcedureIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection);
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||
disabled: container.isDatabaseNodeOrNoneSelected(),
|
||||
};
|
||||
|
||||
newStoredProcedureBtn.children = createScriptCommandButtons(selectedNodeState);
|
||||
newStoredProcedureBtn.children = createScriptCommandButtons(container);
|
||||
buttons.push(newStoredProcedureBtn);
|
||||
}
|
||||
}
|
||||
@@ -130,19 +120,16 @@ export function createStaticCommandBarButtons(
|
||||
return buttons;
|
||||
}
|
||||
|
||||
export function createContextCommandBarButtons(
|
||||
container: Explorer,
|
||||
selectedNodeState: SelectedNodeState
|
||||
): CommandButtonComponentProps[] {
|
||||
export function createContextCommandBarButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||
const buttons: CommandButtonComponentProps[] = [];
|
||||
|
||||
if (!selectedNodeState.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo") {
|
||||
if (!container.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo") {
|
||||
const label = container.isShellEnabled() ? "Open Mongo Shell" : "New Shell";
|
||||
const newMongoShellBtn: CommandButtonComponentProps = {
|
||||
iconSrc: HostedTerminalIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
if (container.isShellEnabled()) {
|
||||
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
||||
} else {
|
||||
@@ -152,7 +139,7 @@ export function createContextCommandBarButtons(
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo",
|
||||
disabled: container.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo",
|
||||
};
|
||||
buttons.push(newMongoShellBtn);
|
||||
}
|
||||
@@ -165,7 +152,7 @@ export function createControlCommandBarButtons(container: Explorer): CommandButt
|
||||
{
|
||||
iconSrc: SettingsIcon,
|
||||
iconAlt: "Settings",
|
||||
onCommandClick: () => useSidePanel.getState().openSidePanel("Settings", <SettingsPane />),
|
||||
onCommandClick: () => container.openSettingPane(),
|
||||
commandButtonLabel: undefined,
|
||||
ariaLabel: "Settings",
|
||||
tooltipText: "Settings",
|
||||
@@ -174,22 +161,19 @@ export function createControlCommandBarButtons(container: Explorer): CommandButt
|
||||
},
|
||||
];
|
||||
|
||||
const showOpenFullScreen =
|
||||
configContext.platform === Platform.Portal && !isRunningOnNationalCloud() && userContext.apiType !== "Gremlin";
|
||||
|
||||
if (showOpenFullScreen) {
|
||||
if (container.isHostedDataExplorerEnabled()) {
|
||||
const label = "Open Full Screen";
|
||||
const fullScreenButton: CommandButtonComponentProps = {
|
||||
iconSrc: OpenInTabIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
useSidePanel.getState().openSidePanel("Open Full Screen", <OpenFullScreen />);
|
||||
container.openSidePanel("Open Full Screen", <OpenFullScreen />);
|
||||
},
|
||||
commandButtonLabel: undefined,
|
||||
ariaLabel: label,
|
||||
tooltipText: label,
|
||||
hasPopup: false,
|
||||
disabled: !showOpenFullScreen,
|
||||
disabled: !container.isHostedDataExplorerEnabled(),
|
||||
className: "OpenFullScreen",
|
||||
};
|
||||
buttons.push(fullScreenButton);
|
||||
@@ -248,7 +232,7 @@ function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonCo
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (isServerlessAccount()) {
|
||||
if (container.isServerlessEnabled()) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -287,20 +271,20 @@ function createNewDatabase(container: Explorer): CommandButtonComponentProps {
|
||||
};
|
||||
}
|
||||
|
||||
function createNewSQLQueryButton(selectedNodeState: SelectedNodeState): CommandButtonComponentProps {
|
||||
function createNewSQLQueryButton(container: Explorer): CommandButtonComponentProps {
|
||||
if (userContext.apiType === "SQL" || userContext.apiType === "Gremlin") {
|
||||
const label = "New SQL Query";
|
||||
return {
|
||||
iconSrc: AddSqlQueryIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection);
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||
disabled: container.isDatabaseNodeOrNoneSelected(),
|
||||
};
|
||||
} else if (userContext.apiType === "Mongo") {
|
||||
const label = "New Query";
|
||||
@@ -308,24 +292,23 @@ function createNewSQLQueryButton(selectedNodeState: SelectedNodeState): CommandB
|
||||
iconSrc: AddSqlQueryIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection);
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||
disabled: container.isDatabaseNodeOrNoneSelected(),
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function createScriptCommandButtons(selectedNodeState: SelectedNodeState): CommandButtonComponentProps[] {
|
||||
export function createScriptCommandButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||
const buttons: CommandButtonComponentProps[] = [];
|
||||
|
||||
const shouldEnableScriptsCommands: boolean =
|
||||
!selectedNodeState.isDatabaseNodeOrNoneSelected() && areScriptsSupported();
|
||||
const shouldEnableScriptsCommands: boolean = !container.isDatabaseNodeOrNoneSelected() && areScriptsSupported();
|
||||
|
||||
if (shouldEnableScriptsCommands) {
|
||||
const label = "New Stored Procedure";
|
||||
@@ -333,13 +316,13 @@ export function createScriptCommandButtons(selectedNodeState: SelectedNodeState)
|
||||
iconSrc: AddStoredProcedureIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection);
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||
disabled: container.isDatabaseNodeOrNoneSelected(),
|
||||
};
|
||||
buttons.push(newStoredProcedureBtn);
|
||||
}
|
||||
@@ -350,13 +333,13 @@ export function createScriptCommandButtons(selectedNodeState: SelectedNodeState)
|
||||
iconSrc: AddUdfIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection);
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||
disabled: container.isDatabaseNodeOrNoneSelected(),
|
||||
};
|
||||
buttons.push(newUserDefinedFunctionBtn);
|
||||
}
|
||||
@@ -367,13 +350,13 @@ export function createScriptCommandButtons(selectedNodeState: SelectedNodeState)
|
||||
iconSrc: AddTriggerIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection);
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
disabled: selectedNodeState.isDatabaseNodeOrNoneSelected(),
|
||||
disabled: container.isDatabaseNodeOrNoneSelected(),
|
||||
};
|
||||
buttons.push(newTriggerBtn);
|
||||
}
|
||||
@@ -420,12 +403,12 @@ function createOpenQueryButton(container: Explorer): CommandButtonComponentProps
|
||||
};
|
||||
}
|
||||
|
||||
function createOpenQueryFromDiskButton(): CommandButtonComponentProps {
|
||||
function createOpenQueryFromDiskButton(container: Explorer): CommandButtonComponentProps {
|
||||
const label = "Open Query From Disk";
|
||||
return {
|
||||
iconSrc: OpenQueryFromDiskIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => useSidePanel.getState().openSidePanel("Load Query", <LoadQueryPane />),
|
||||
onCommandClick: () => container.openLoadQueryPanel(),
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
@@ -467,19 +450,6 @@ function createOpenTerminalButton(container: Explorer): CommandButtonComponentPr
|
||||
};
|
||||
}
|
||||
|
||||
function createOpenPostgreSQLTerminalButton(container: Explorer): CommandButtonComponentProps {
|
||||
const label = "Open PostgreSQL Shell";
|
||||
return {
|
||||
iconSrc: HostedTerminalIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => container.openNotebookTerminal(ViewModels.TerminalKind.PostgreSQL),
|
||||
commandButtonLabel: label,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
ariaLabel: label,
|
||||
};
|
||||
}
|
||||
|
||||
function createOpenMongoTerminalButton(container: Explorer): CommandButtonComponentProps {
|
||||
const label = "Open Mongo Shell";
|
||||
const tooltip =
|
||||
@@ -559,25 +529,19 @@ function createManageGitHubAccountButton(container: Explorer): CommandButtonComp
|
||||
};
|
||||
}
|
||||
|
||||
function createStaticCommandBarButtonsForResourceToken(
|
||||
container: Explorer,
|
||||
selectedNodeState: SelectedNodeState
|
||||
): CommandButtonComponentProps[] {
|
||||
const newSqlQueryBtn = createNewSQLQueryButton(selectedNodeState);
|
||||
function createStaticCommandBarButtonsForResourceToken(container: Explorer): CommandButtonComponentProps[] {
|
||||
const newSqlQueryBtn = createNewSQLQueryButton(container);
|
||||
const openQueryBtn = createOpenQueryButton(container);
|
||||
|
||||
const isResourceTokenCollectionNodeSelected: boolean =
|
||||
container.resourceTokenCollection() &&
|
||||
container.resourceTokenCollection().id() === selectedNodeState.selectedNode?.id();
|
||||
newSqlQueryBtn.disabled = !isResourceTokenCollectionNodeSelected;
|
||||
newSqlQueryBtn.disabled = !container.isResourceTokenCollectionNodeSelected();
|
||||
newSqlQueryBtn.onCommandClick = () => {
|
||||
const resourceTokenCollection: ViewModels.CollectionBase = container.resourceTokenCollection();
|
||||
resourceTokenCollection && resourceTokenCollection.onNewQueryClick(resourceTokenCollection, undefined);
|
||||
};
|
||||
|
||||
openQueryBtn.disabled = !isResourceTokenCollectionNodeSelected;
|
||||
openQueryBtn.disabled = !container.isResourceTokenCollectionNodeSelected();
|
||||
if (!openQueryBtn.disabled) {
|
||||
openQueryBtn.children = [createOpenQueryButton(container), createOpenQueryFromDiskButton()];
|
||||
openQueryBtn.children = [createOpenQueryButton(container), createOpenQueryFromDiskButton(container)];
|
||||
}
|
||||
|
||||
return [newSqlQueryBtn, openQueryBtn];
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
31
src/Explorer/Menus/NavBar/ControlBarComponent.tsx
Normal file
31
src/Explorer/Menus/NavBar/ControlBarComponent.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* React component for control bar
|
||||
*/
|
||||
|
||||
import * as React from "react";
|
||||
import {
|
||||
CommandButtonComponent,
|
||||
CommandButtonComponentProps,
|
||||
} from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
|
||||
export interface ControlBarComponentProps {
|
||||
buttons: CommandButtonComponentProps[];
|
||||
}
|
||||
|
||||
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}`);
|
||||
});
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
if (!this.props.buttons || this.props.buttons.length < 1) {
|
||||
return <React.Fragment />;
|
||||
}
|
||||
|
||||
return <React.Fragment>{ControlBarComponent.renderButtons(this.props.buttons)}</React.Fragment>;
|
||||
}
|
||||
}
|
||||
28
src/Explorer/Menus/NavBar/ControlBarComponentAdapter.tsx
Normal file
28
src/Explorer/Menus/NavBar/ControlBarComponentAdapter.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* This adapter is responsible to render the React component
|
||||
* If the component signals a change through the callback passed in the properties, it must render the React component when appropriate
|
||||
* and update any knockout observables passed from the parent.
|
||||
*/
|
||||
|
||||
import * as ko from "knockout";
|
||||
import * as React from "react";
|
||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
||||
import { ControlBarComponent } from "./ControlBarComponent";
|
||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
|
||||
export class ControlBarComponentAdapter implements ReactAdapter {
|
||||
public parameters: ko.Observable<number>;
|
||||
|
||||
constructor(private buttons: ko.ObservableArray<CommandButtonComponentProps>) {
|
||||
this.buttons.subscribe(() => this.forceRender());
|
||||
this.parameters = ko.observable<number>(Date.now());
|
||||
}
|
||||
|
||||
public renderComponent(): JSX.Element {
|
||||
return <ControlBarComponent buttons={this.buttons()} />;
|
||||
}
|
||||
|
||||
public forceRender(): void {
|
||||
window.requestAnimationFrame(() => this.parameters(Date.now()));
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/**
|
||||
* Interface for the data/content that will be recorded
|
||||
*/
|
||||
|
||||
export interface ConsoleData {
|
||||
type: ConsoleDataType;
|
||||
date: string;
|
||||
message: string;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export enum ConsoleDataType {
|
||||
Info = 0,
|
||||
Error = 1,
|
||||
InProgress = 2,
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { ConsoleDataType } from "./ConsoleData";
|
||||
import { NotificationConsoleComponent, NotificationConsoleComponentProps } from "./NotificationConsoleComponent";
|
||||
import {
|
||||
ConsoleDataType,
|
||||
NotificationConsoleComponent,
|
||||
NotificationConsoleComponentProps,
|
||||
} from "./NotificationConsoleComponent";
|
||||
|
||||
describe("NotificationConsoleComponent", () => {
|
||||
const createBlankProps = (): NotificationConsoleComponentProps => {
|
||||
|
||||
@@ -3,21 +3,38 @@
|
||||
*/
|
||||
|
||||
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";
|
||||
import { ConsoleData, ConsoleDataType } from "./ConsoleData";
|
||||
|
||||
/**
|
||||
* Log levels
|
||||
*/
|
||||
export enum ConsoleDataType {
|
||||
Info = 0,
|
||||
Error = 1,
|
||||
InProgress = 2,
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for the data/content that will be recorded
|
||||
*/
|
||||
export interface ConsoleData {
|
||||
type: ConsoleDataType;
|
||||
date: string;
|
||||
message: string;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export interface NotificationConsoleComponentProps {
|
||||
isConsoleExpanded: boolean;
|
||||
@@ -89,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">
|
||||
@@ -162,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>
|
||||
@@ -304,22 +323,3 @@ const PrPreview = (props: { pr: string }) => {
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const NotificationConsole: React.FC = () => {
|
||||
const setIsExpanded = useNotificationConsole((state) => state.setIsExpanded);
|
||||
const isExpanded = useNotificationConsole((state) => state.isExpanded);
|
||||
const consoleData = useNotificationConsole((state) => state.consoleData);
|
||||
const inProgressConsoleDataIdToBeDeleted = useNotificationConsole(
|
||||
(state) => state.inProgressConsoleDataIdToBeDeleted
|
||||
);
|
||||
// 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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
makeStateRecord,
|
||||
makeTransformsRecord,
|
||||
} from "@nteract/core";
|
||||
import { configOption, defineConfigOption } from "@nteract/mythic-configuration";
|
||||
import { configOption, createConfigCollection, defineConfigOption } from "@nteract/mythic-configuration";
|
||||
import { Media } from "@nteract/outputs";
|
||||
import TransformVDOM from "@nteract/transform-vdom";
|
||||
import * as Immutable from "immutable";
|
||||
@@ -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, {
|
||||
@@ -242,27 +242,22 @@ export class NotebookClientV2 {
|
||||
);
|
||||
|
||||
// Additional configuration
|
||||
this.store.dispatch(configOption("editorType").action(params.cellEditorType ?? "codemirror"));
|
||||
this.store.dispatch(configOption("editorType").action(params.cellEditorType ?? "monaco"));
|
||||
this.store.dispatch(
|
||||
configOption("autoSaveInterval").action(params.autoSaveInterval ?? Constants.Notebook.autoSaveIntervalMs)
|
||||
);
|
||||
this.store.dispatch(configOption("codeMirror.lineNumbers").action(true));
|
||||
|
||||
const readOnlyConfigOption = configOption("codeMirror.readOnly");
|
||||
const readOnlyValue = params.isReadOnly ? "nocursor" : undefined;
|
||||
if (!readOnlyConfigOption) {
|
||||
defineConfigOption({
|
||||
label: "Read-only",
|
||||
key: "codeMirror.readOnly",
|
||||
values: [
|
||||
{ label: "Read-Only", value: "nocursor" },
|
||||
{ label: "Not read-only", value: undefined },
|
||||
],
|
||||
defaultValue: readOnlyValue,
|
||||
});
|
||||
} else {
|
||||
this.store.dispatch(readOnlyConfigOption.action(readOnlyValue));
|
||||
}
|
||||
createConfigCollection({
|
||||
key: "monaco",
|
||||
});
|
||||
defineConfigOption({
|
||||
label: "Show Line numbers",
|
||||
key: "monaco.lineNumbers",
|
||||
values: [
|
||||
{ label: "Yes", value: true },
|
||||
{ label: "No", value: false },
|
||||
],
|
||||
defaultValue: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
.notebookComponentContainer {
|
||||
text-transform: none;
|
||||
line-height: 1.28581;
|
||||
letter-spacing: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: #182026;
|
||||
text-transform:none;
|
||||
line-height:1.28581;
|
||||
letter-spacing:0;
|
||||
font-size:14px;
|
||||
font-weight:400;
|
||||
color:#182026;
|
||||
height: 100%;
|
||||
|
||||
.hotKeys {
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
KernelRef,
|
||||
RemoteKernelProps,
|
||||
selectors,
|
||||
ServerConfig as JupyterServerConfig
|
||||
ServerConfig as JupyterServerConfig,
|
||||
} from "@nteract/core";
|
||||
import { Channels, childOf, createMessage, JupyterMessage, message, ofMessageType } from "@nteract/messaging";
|
||||
import { RecordOf } from "immutable";
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
switchMap,
|
||||
take,
|
||||
tap,
|
||||
timeout
|
||||
timeout,
|
||||
} from "rxjs/operators";
|
||||
import { webSocket } from "rxjs/webSocket";
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
@@ -107,7 +107,7 @@ const formWebSocketURL = (serverConfig: NotebookServiceConfig, kernelId: string,
|
||||
const q = params.toString();
|
||||
const suffix = q !== "" ? `?${q}` : "";
|
||||
|
||||
const url = (serverConfig.endpoint.slice(0, -1) || "") + `api/kernels/${kernelId}/channels${suffix}`;
|
||||
const url = (serverConfig.endpoint || "") + `api/kernels/${kernelId}/channels${suffix}`;
|
||||
|
||||
return url.replace(/^http(s)?/, "ws$1");
|
||||
};
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -53,7 +53,7 @@ export class NotebookContainerClient {
|
||||
|
||||
const { notebookServerEndpoint, authToken } = this.getNotebookServerConfig();
|
||||
try {
|
||||
const response = await fetch(`${notebookServerEndpoint}api/metrics/memory`, {
|
||||
const response = await fetch(`${notebookServerEndpoint}/api/metrics/memory`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: authToken,
|
||||
@@ -130,23 +130,19 @@ export class NotebookContainerClient {
|
||||
}
|
||||
|
||||
private async recreateNotebookWorkspaceAsync(): Promise<void> {
|
||||
const explorer = window.dataExplorer;
|
||||
const { databaseAccount } = userContext;
|
||||
if (!databaseAccount?.id) {
|
||||
throw new Error("DataExplorer not initialized");
|
||||
}
|
||||
/*
|
||||
|
||||
const notebookWorkspaceManager = explorer.notebookWorkspaceManager;
|
||||
try {
|
||||
await destroy(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name, "default");
|
||||
await createOrUpdate(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
userContext.databaseAccount.name,
|
||||
"default"
|
||||
);
|
||||
await notebookWorkspaceManager.deleteNotebookWorkspaceAsync(databaseAccount?.id, "default");
|
||||
await notebookWorkspaceManager.createNotebookWorkspaceAsync(databaseAccount?.id, "default");
|
||||
} catch (error) {
|
||||
Logger.logError(getErrorMessage(error), "NotebookContainerClient/recreateNotebookWorkspaceAsync");
|
||||
return Promise.reject(error);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export class NotebookContentClient {
|
||||
private notebookServerInfo: ko.Observable<DataModels.NotebookWorkspaceConnectionInfo>,
|
||||
public notebookBasePath: ko.Observable<string>,
|
||||
private contentProvider: IContentProvider
|
||||
) { }
|
||||
) {}
|
||||
|
||||
/**
|
||||
* This updates the item and points all the children's parent to this item
|
||||
@@ -25,9 +25,6 @@ export class NotebookContentClient {
|
||||
});
|
||||
}
|
||||
|
||||
private sleep = (milliseconds: number) => {
|
||||
return new Promise(resolve => setTimeout(resolve, milliseconds))
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param parent parent folder
|
||||
@@ -38,7 +35,6 @@ export class NotebookContentClient {
|
||||
}
|
||||
|
||||
const type = "notebook";
|
||||
|
||||
return this.contentProvider
|
||||
.create<"notebook">(this.getServerConfig(), parent.path, { type })
|
||||
.toPromise()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user