mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-07 19:46:53 +00:00
Compare commits
10 Commits
dependabot
...
users/v-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
863fdb4f7e | ||
|
|
260c99e15c | ||
|
|
c1c12019da | ||
|
|
4e5358185f | ||
|
|
b4bc93ac03 | ||
|
|
c61788198f | ||
|
|
379395567c | ||
|
|
c2d2ff3dee | ||
|
|
c9bd0b6c7f | ||
|
|
8ce7aaa728 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -85,6 +85,8 @@ jobs:
|
||||
path: .cache
|
||||
key: ${{ runner.os }}-build-cache
|
||||
- run: npm run pack:prod
|
||||
env:
|
||||
NODE_OPTIONS: '--max-old-space-size=4096'
|
||||
- run: cp -r ./Contracts ./dist/contracts
|
||||
- run: cp -r ./configs ./dist/configs
|
||||
- uses: actions/upload-artifact@v2
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
@DataExplorerFont: wf_segoe-ui_normal, "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
|
||||
@SemiboldFont: "Segoe UI Semibold", "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
|
||||
@GrayScale: "grayscale()";
|
||||
@NoColor: "brightness(0) saturate(100%)";
|
||||
|
||||
@xSmallFontSize: 4px;
|
||||
@smallFontSize: 8px;
|
||||
@@ -147,14 +148,41 @@
|
||||
// CommandBar
|
||||
@CommandBarButtonHeight: 40px;
|
||||
|
||||
/**********************************************************************************
|
||||
Portal Consts
|
||||
/**********************************************************************************/
|
||||
|
||||
@PortalAccentMediumHigh: #0058ad;
|
||||
@PortalAccentMedium: #004e87;
|
||||
@PortalAccentLight: #eef7ff;
|
||||
@PortalAccentAccentExtra: #ddf0ff;
|
||||
|
||||
/**********************************************************************************
|
||||
Fabric Consts
|
||||
/**********************************************************************************/
|
||||
|
||||
@FabricFont: "Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif;
|
||||
|
||||
@FabricBoxBorderRadius: 8px;
|
||||
@FabricBoxBorderShadow: 0 0 2px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.14);
|
||||
@FabricBoxMargin: 4px 3px 4px 3px;
|
||||
|
||||
@FabricAccentMediumHigh: #0c695a;
|
||||
@FabricAccentMedium: #117865;
|
||||
@FabricAccentLight: #f5f5f5;
|
||||
@FabricAccentExtra: #ebebeb;
|
||||
|
||||
@FabricButtonBorderRadius: 4px;
|
||||
|
||||
|
||||
/**********************************************************************************
|
||||
Common Flex Property
|
||||
/**********************************************************************************/
|
||||
|
||||
.flex-display(@display: flex) {
|
||||
display: ~"-webkit-@{display}";
|
||||
display: ~"-ms-@{display}box"; // IE10 uses -ms-flexbox
|
||||
display: ~"-ms-@{display}"; // IE11
|
||||
display:~"-webkit-@{display}";
|
||||
display:~"-ms-@{display}box"; // IE10 uses -ms-flexbox
|
||||
display:~"-ms-@{display}"; // IE11
|
||||
display: @display;
|
||||
}
|
||||
|
||||
@@ -168,13 +196,15 @@
|
||||
High contrast mode active
|
||||
**************************************************************************************/
|
||||
|
||||
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
|
||||
@media all and (-ms-high-contrast: none),
|
||||
(-ms-high-contrast: active) {
|
||||
|
||||
.selectedRadio,
|
||||
.selectedRadio:hover,
|
||||
.selectedRadio:active,
|
||||
.selectedRadio.dirty,
|
||||
.tab [type="radio"]:checked ~ label,
|
||||
.tab [type="radio"]:checked ~ label:hover {
|
||||
.tab [type="radio"]:checked~label,
|
||||
.tab [type="radio"]:checked~label:hover {
|
||||
-ms-high-contrast-adjust: none;
|
||||
-webkit-text-fill-color: HighlightText;
|
||||
color: HighlightText;
|
||||
@@ -183,6 +213,7 @@
|
||||
}
|
||||
|
||||
.queryMetricsSummaryTuple {
|
||||
|
||||
th,
|
||||
td {
|
||||
&:nth-child(2) {
|
||||
@@ -302,4 +333,4 @@
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: @InfoPointerColor transparent;
|
||||
}
|
||||
}
|
||||
@@ -2646,6 +2646,11 @@ a:link {
|
||||
width: @ActiveTabWidth;
|
||||
}
|
||||
|
||||
.nav-tabs > li.active > .tabNavContentContainer > .tab_Content > .tabNavText {
|
||||
font-weight: bolder;
|
||||
border-bottom: 2px solid rgba(0,120,212,1);
|
||||
}
|
||||
|
||||
.nav-tabs > li.active:focus > .tabNavContentContainer {
|
||||
.focus();
|
||||
}
|
||||
|
||||
211
less/documentDBFabric.less
Normal file
211
less/documentDBFabric.less
Normal file
@@ -0,0 +1,211 @@
|
||||
@import "./Common/Constants";
|
||||
|
||||
html {
|
||||
font-family: @FabricFont;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: @FabricFont;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
a {
|
||||
color: @FabricAccentMedium;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover,
|
||||
a:focus {
|
||||
color: @FabricAccentMediumHigh;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#divExplorer {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.resourceTreeAndTabs {
|
||||
border-radius: @FabricBoxBorderRadius;
|
||||
box-shadow: @FabricBoxBorderShadow;
|
||||
margin: @FabricBoxMargin;
|
||||
margin-top: 4px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.tabsManagerContainer {
|
||||
background-color: #fafafa
|
||||
}
|
||||
|
||||
.nav-tabs-margin {
|
||||
padding-top: 8px;
|
||||
background-color: #fafafa
|
||||
}
|
||||
|
||||
.commandBarContainer {
|
||||
background-color: #ffffff;
|
||||
border-bottom: none;
|
||||
border-radius: @FabricBoxBorderRadius;
|
||||
box-shadow: @FabricBoxBorderShadow;
|
||||
margin: @FabricBoxMargin;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.dividerContainer {
|
||||
padding: @SmallSpace 0px @SmallSpace 0px;
|
||||
.flex-display();
|
||||
|
||||
span {
|
||||
border-left: @ButtonBorderWidth solid @BaseMedium;
|
||||
margin: 0 10px 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.nav-tabs>li>.tabNavContentContainer>.tab_Content:hover {
|
||||
border-bottom: 2px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.nav-tabs>li.active>.tabNavContentContainer>.tab_Content,
|
||||
.nav-tabs>li.active>.tabNavContentContainer>.tab_Content:hover {
|
||||
border-bottom: 2px solid @FabricAccentMedium;
|
||||
}
|
||||
|
||||
.nav-tabs>li.active>.tabNavContentContainer>.tab_Content>.tabNavText {
|
||||
border-bottom: 0px none transparent;
|
||||
}
|
||||
|
||||
.tabNavContentContainer {
|
||||
padding: @SmallSpace 0px @SmallSpace 0px;
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.tab_Content {
|
||||
border-right: 0px none transparent;
|
||||
margin: 0px @SmallSpace 0px @SmallSpace;
|
||||
width: calc(@TabsWidth - (@SmallSpace * 2));
|
||||
padding-bottom: @SmallSpace;
|
||||
|
||||
.statusIconContainer {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.tabIconSection {
|
||||
.cancelButton {
|
||||
padding: 0px 0px 0px @SmallSpace;
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.resourceTree {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.accordion {
|
||||
.accordionItemContainer {
|
||||
.accordionItemHeader {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.treeComponent {
|
||||
.nodeItem {
|
||||
&:focus {
|
||||
outline: 2px @FabricAccentMedium;
|
||||
}
|
||||
|
||||
.treeNodeHeader {
|
||||
padding: 5px 5px;
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
background-color: @FabricAccentLight;
|
||||
|
||||
.treeMenuEllipsis {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.showingMenu {
|
||||
background-color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.selected {
|
||||
&>.treeNodeHeader {
|
||||
background-color: @FabricAccentExtra;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.dataExplorerErrorConsoleContainer {
|
||||
border-radius: @FabricBoxBorderRadius;
|
||||
box-shadow: @FabricBoxBorderShadow;
|
||||
margin: @FabricBoxMargin;
|
||||
width: auto;
|
||||
align-self: auto;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.filterbtnstyle {
|
||||
background: #fff;
|
||||
color: #000;
|
||||
border: solid 1px #d1d1d1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.filterbtnstyle:hover {
|
||||
background: @FabricAccentLight;
|
||||
color: #000;
|
||||
border: solid 1px #d1d1d1;
|
||||
}
|
||||
|
||||
.filterbtnstyle:active {
|
||||
background: @FabricAccentLight;
|
||||
color: #000;
|
||||
border: solid 1px #d1d1d1;
|
||||
}
|
||||
|
||||
.filterbtnstyle:focus {
|
||||
background: #fff;
|
||||
color: #000;
|
||||
border: solid 1px #d1d1d1;
|
||||
}
|
||||
|
||||
|
||||
.gridRowSelected .tabdocumentsGridElement:hover {
|
||||
background-color: @FabricAccentLight !important;
|
||||
}
|
||||
|
||||
|
||||
.refreshcol {
|
||||
filter: brightness(0) saturate(100%);
|
||||
}
|
||||
|
||||
.refreshcol1 {
|
||||
filter: brightness(0) saturate(100%);
|
||||
}
|
||||
|
||||
.fileImportImg img {
|
||||
filter: brightness(0) saturate(100%);
|
||||
}
|
||||
96
package-lock.json
generated
96
package-lock.json
generated
@@ -100,31 +100,22 @@
|
||||
}
|
||||
},
|
||||
"@azure/core-rest-pipeline": {
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.12.1.tgz",
|
||||
"integrity": "sha512-SsyWQ+T5MFQRX+M8H/66AlaI6HyCbQStGfFngx2fuiW+vKI2DkhtOvbYodPyf9fOe/ARLWWc3ohX54lQ5Kmaog==",
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.8.1.tgz",
|
||||
"integrity": "sha512-R/XpxZcDgGbnneEifnsAcjLoR2NCmlDxKDmzd8oi5jx5YEnPE6gsxHQWAk2+uY55Ka717x/fdctyoCYKnumrqw==",
|
||||
"requires": {
|
||||
"@azure/abort-controller": "^1.0.0",
|
||||
"@azure/core-auth": "^1.4.0",
|
||||
"@azure/core-auth": "^1.3.0",
|
||||
"@azure/core-tracing": "^1.0.1",
|
||||
"@azure/core-util": "^1.3.0",
|
||||
"@azure/core-util": "^1.0.0",
|
||||
"@azure/logger": "^1.0.0",
|
||||
"form-data": "^4.0.0",
|
||||
"http-proxy-agent": "^5.0.0",
|
||||
"http-proxy-agent": "^4.0.1",
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"tslib": "^2.2.0"
|
||||
"tslib": "^2.2.0",
|
||||
"uuid": "^8.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@azure/core-auth": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.5.0.tgz",
|
||||
"integrity": "sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw==",
|
||||
"requires": {
|
||||
"@azure/abort-controller": "^1.0.0",
|
||||
"@azure/core-util": "^1.1.0",
|
||||
"tslib": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"@azure/core-tracing": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz",
|
||||
@@ -133,11 +124,6 @@
|
||||
"tslib": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"@tootallnate/once": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
||||
"integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="
|
||||
},
|
||||
"form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
@@ -148,20 +134,15 @@
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"http-proxy-agent": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
|
||||
"integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
|
||||
"requires": {
|
||||
"@tootallnate/once": "2",
|
||||
"agent-base": "6",
|
||||
"debug": "4"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -183,30 +164,27 @@
|
||||
}
|
||||
},
|
||||
"@azure/core-util": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.4.0.tgz",
|
||||
"integrity": "sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig==",
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.0.0.tgz",
|
||||
"integrity": "sha512-yWshY9cdPthlebnb3Zuz/j0Lv4kjU6u7PR5sW7A9FF7EX+0irMRJAtyTq5TPiDHJfjH8gTSlnIYFj9m7Ed76IQ==",
|
||||
"requires": {
|
||||
"@azure/abort-controller": "^1.0.0",
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@azure/cosmos": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.0.0.tgz",
|
||||
"integrity": "sha512-/Z27p1+FTkmjmm8jk90zi/HrczPHw2t8WecFnsnTe4xGocWl0Z4clP0YlLUTJPhRLWYa5upwD9rMvKJkS1f1kg==",
|
||||
"version": "3.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-3.16.2.tgz",
|
||||
"integrity": "sha512-sceY5LWj0BHGj8PSyaVCfDRQLVZyoCfIY78kyIROJVEw0k+p9XFs8fhpykN8JklkCftL0WlaVY+X25SQwnhZsw==",
|
||||
"requires": {
|
||||
"@azure/abort-controller": "^1.0.0",
|
||||
"@azure/core-auth": "^1.3.0",
|
||||
"@azure/core-rest-pipeline": "^1.2.0",
|
||||
"@azure/core-tracing": "^1.0.0",
|
||||
"debug": "^4.1.1",
|
||||
"fast-json-stable-stringify": "^2.1.0",
|
||||
"jsbi": "^3.1.3",
|
||||
@@ -227,9 +205,9 @@
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
|
||||
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
|
||||
},
|
||||
"universal-user-agent": {
|
||||
"version": "6.0.0",
|
||||
@@ -6074,8 +6052,7 @@
|
||||
"@tootallnate/once": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
||||
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw=="
|
||||
},
|
||||
"@types/anymatch": {
|
||||
"version": "1.3.1",
|
||||
@@ -13339,7 +13316,6 @@
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
|
||||
"integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@tootallnate/once": "1",
|
||||
"agent-base": "6",
|
||||
@@ -18862,9 +18838,9 @@
|
||||
}
|
||||
},
|
||||
"jsbi": {
|
||||
"version": "3.2.5",
|
||||
"resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.2.5.tgz",
|
||||
"integrity": "sha512-aBE4n43IPvjaddScbvWRA2YlTzKEynHzu7MqOyTipdHucf/VxS63ViCjxYRg86M8Rxwbt/GfzHl1kKERkt45fQ=="
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.1.4.tgz",
|
||||
"integrity": "sha512-52QRRFSsi9impURE8ZUbzAMCLjPm4THO7H2fcuIvaaeFTbSysvkodbQQXIVsNgq/ypDbq6dJiuGKL0vZ/i9hUg=="
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.1",
|
||||
@@ -19996,9 +19972,9 @@
|
||||
}
|
||||
},
|
||||
"node-abort-controller": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
|
||||
"integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ=="
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.0.1.tgz",
|
||||
"integrity": "sha512-/ujIVxthRs+7q6hsdjHMaj8hRG9NuWmwrz+JdRwZ14jdFoKSkm+vDsCbF9PLpnSqjaWQJuTmVtcWHNLr+vrOFw=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.1",
|
||||
@@ -21457,7 +21433,7 @@
|
||||
"priorityqueuejs": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/priorityqueuejs/-/priorityqueuejs-1.0.0.tgz",
|
||||
"integrity": "sha512-lg++21mreCEOuGWTbO5DnJKAdxfjrdN0S9ysoW9SzdSJvbkWpkaDdpG/cdsPCsEnoLUwmd9m3WcZhngW7yKA2g=="
|
||||
"integrity": "sha1-LuTyPCVgkT4IwHzlzN1t498sWvg="
|
||||
},
|
||||
"prismjs": {
|
||||
"version": "1.23.0",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@azure/arm-cosmosdb": "9.1.0",
|
||||
"@azure/cosmos": "4.0.0",
|
||||
"@azure/cosmos": "3.16.2",
|
||||
"@azure/cosmos-language-service": "0.0.5",
|
||||
"@azure/identity": "1.2.1",
|
||||
"@azure/ms-rest-nodeauth": "3.0.7",
|
||||
|
||||
@@ -365,9 +365,6 @@ export const EmulatorMasterKey =
|
||||
//[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Well known public masterKey for emulator")]
|
||||
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
|
||||
|
||||
// A variable @MyVariable defined in Constants.less is accessible as StyleConstants.MyVariable
|
||||
export const StyleConstants = require("less-vars-loader!../../less/Common/Constants.less");
|
||||
|
||||
export class Notebook {
|
||||
public static readonly defaultBasePath = "./notebooks";
|
||||
public static readonly heartbeatDelayMs = 60000;
|
||||
|
||||
@@ -5,8 +5,10 @@ import refreshImg from "../../images/refresh-cosmos.svg";
|
||||
import { AuthType } from "../AuthType";
|
||||
import Explorer from "../Explorer/Explorer";
|
||||
import { ResourceTokenTree } from "../Explorer/Tree/ResourceTokenTree";
|
||||
import { ResourceTree2 } from "../Explorer/Tree2/ResourceTree";
|
||||
import { userContext } from "../UserContext";
|
||||
import { getApiShortDisplayName } from "../Utils/APITypeUtils";
|
||||
import { Platform, configContext } from "./../ConfigContext";
|
||||
import { NormalizedEventKey } from "./Constants";
|
||||
|
||||
export interface ResourceTreeContainerProps {
|
||||
@@ -76,10 +78,10 @@ export const ResourceTreeContainer: FunctionComponent<ResourceTreeContainerProps
|
||||
<ResourceTokenTree />
|
||||
) : userContext.features.enableKoResourceTree ? (
|
||||
<div style={{ overflowY: "auto" }} data-bind="react:resourceTree" />
|
||||
) : configContext.platform === Platform.Fabric ? (
|
||||
<ResourceTree2 container={container} />
|
||||
) : (
|
||||
<ResourceTree container={container} />
|
||||
// Uncomment the following line to use the fluent ui tree
|
||||
// <ResourceTree2 container={container} />
|
||||
)}
|
||||
</div>
|
||||
{/* Collections Window - End */}
|
||||
|
||||
18
src/Common/StyleConstants.ts
Normal file
18
src/Common/StyleConstants.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Platform, configContext } from "../ConfigContext";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
export const StyleConstants = require("less-vars-loader!../../less/Common/Constants.less");
|
||||
|
||||
export function updateStyles(): void {
|
||||
if (configContext.platform === Platform.Fabric) {
|
||||
StyleConstants.AccentMediumHigh = StyleConstants.FabricAccentMediumHigh;
|
||||
StyleConstants.AccentMedium = StyleConstants.FabricAccentMedium;
|
||||
StyleConstants.AccentLight = StyleConstants.FabricAccentLight;
|
||||
StyleConstants.AccentAccentExtra = StyleConstants.FabricAccentMediumHigh;
|
||||
} else {
|
||||
StyleConstants.AccentMediumHigh = StyleConstants.PortalAccentMediumHigh;
|
||||
StyleConstants.AccentMedium = StyleConstants.PortalAccentMedium;
|
||||
StyleConstants.AccentLight = StyleConstants.PortalAccentLight;
|
||||
StyleConstants.AccentAccentExtra = StyleConstants.PortalAccentMediumHigh;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ export enum Platform {
|
||||
Portal = "Portal",
|
||||
Hosted = "Hosted",
|
||||
Emulator = "Emulator",
|
||||
Fabric = "Fabric",
|
||||
}
|
||||
|
||||
export interface ConfigContext {
|
||||
@@ -187,6 +188,7 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
|
||||
console.error(`Invalid platform query parameter: ${platform}`);
|
||||
break;
|
||||
case Platform.Portal:
|
||||
case Platform.Fabric:
|
||||
case Platform.Hosted:
|
||||
case Platform.Emulator:
|
||||
updateConfigContext({ platform });
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import dayjs from "dayjs";
|
||||
import * as Plotly from "plotly.js-cartesian-dist-min";
|
||||
import { StyleConstants } from "../../Common/Constants";
|
||||
import { sendCachedDataMessage, sendReadyMessage } from "../../Common/MessageHandler";
|
||||
import { StyleConstants } from "../../Common/StyleConstants";
|
||||
import { MessageTypes } from "../../Contracts/ExplorerContracts";
|
||||
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
|
||||
import "./Heatmap.less";
|
||||
|
||||
@@ -18,6 +18,7 @@ import * as ViewModels from "../Contracts/ViewModels";
|
||||
import { userContext } from "../UserContext";
|
||||
import { getCollectionName, getDatabaseName } from "../Utils/APITypeUtils";
|
||||
import { useSidePanel } from "../hooks/useSidePanel";
|
||||
import { Platform, configContext } from "./../ConfigContext";
|
||||
import { TreeNodeMenuItem } from "./Controls/TreeComponent/TreeComponent";
|
||||
import Explorer from "./Explorer";
|
||||
import { useNotebook } from "./Notebook/useNotebook";
|
||||
@@ -99,7 +100,10 @@ export const createCollectionContextMenuButton = (
|
||||
});
|
||||
}
|
||||
|
||||
if (userContext.apiType === "SQL" || userContext.apiType === "Gremlin") {
|
||||
if (
|
||||
configContext.platform !== Platform.Fabric &&
|
||||
(userContext.apiType === "SQL" || userContext.apiType === "Gremlin")
|
||||
) {
|
||||
items.push({
|
||||
iconSrc: AddStoredProcedureIcon,
|
||||
onClick: () => {
|
||||
|
||||
@@ -23,9 +23,9 @@ 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";
|
||||
import { QueriesClient } from "../../../Common/QueriesClient";
|
||||
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||
import * as DataModels from "../../../Contracts/DataModels";
|
||||
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
|
||||
@@ -23,7 +23,8 @@ import {
|
||||
Text,
|
||||
} from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import { StyleConstants, Urls } from "../../../Common/Constants";
|
||||
import { Urls } from "../../../Common/Constants";
|
||||
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||
import { hoursInAMonth } from "../../../Shared/Constants";
|
||||
import {
|
||||
computeRUUsagePriceHourly,
|
||||
|
||||
@@ -18,6 +18,7 @@ import LoadingIndicator_3Squares from "../../../../images/LoadingIndicator_3Squa
|
||||
import TriangleDownIcon from "../../../../images/Triangle-down.svg";
|
||||
import TriangleRightIcon from "../../../../images/Triangle-right.svg";
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
|
||||
@@ -237,7 +238,7 @@ export class TreeNodeComponent extends React.Component<TreeNodeComponentProps, T
|
||||
private renderContextMenuButton(node: TreeNode): JSX.Element {
|
||||
const menuItemLabel = "More";
|
||||
const buttonStyles: Partial<IButtonStyles> = {
|
||||
rootFocused: { outline: `1px dashed ${Constants.StyleConstants.FocusColor}` },
|
||||
rootFocused: { outline: `1px dashed ${StyleConstants.FocusColor}` },
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Link } from "@fluentui/react/lib/Link";
|
||||
import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
|
||||
import { sendMessage } from "Common/MessageHandler";
|
||||
import { Platform } from "ConfigContext";
|
||||
import { Platform, configContext } from "ConfigContext";
|
||||
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||
import { IGalleryItem } from "Juno/JunoClient";
|
||||
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointValidation";
|
||||
@@ -1343,9 +1343,10 @@ export default class Explorer {
|
||||
|
||||
// TODO: remove reference to isNotebookEnabled and isNotebooksEnabledForAccount
|
||||
const isNotebookEnabled =
|
||||
userContext.features.notebooksDownBanner ||
|
||||
useNotebook.getState().isPhoenixNotebooks ||
|
||||
useNotebook.getState().isPhoenixFeatures;
|
||||
configContext.platform !== Platform.Fabric &&
|
||||
(userContext.features.notebooksDownBanner ||
|
||||
useNotebook.getState().isPhoenixNotebooks ||
|
||||
useNotebook.getState().isPhoenixFeatures);
|
||||
useNotebook.getState().setIsNotebookEnabled(isNotebookEnabled);
|
||||
useNotebook
|
||||
.getState()
|
||||
|
||||
@@ -8,7 +8,9 @@ import { useNotebook } from "Explorer/Notebook/useNotebook";
|
||||
import { userContext } from "UserContext";
|
||||
import * as React from "react";
|
||||
import create, { UseStore } from "zustand";
|
||||
import { ConnectionStatusType, PoolIdType, StyleConstants } from "../../../Common/Constants";
|
||||
import { ConnectionStatusType, PoolIdType } from "../../../Common/Constants";
|
||||
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||
import { Platform, configContext } from "../../../ConfigContext";
|
||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
import Explorer from "../../Explorer";
|
||||
import { useSelectedNode } from "../../useSelectedNode";
|
||||
@@ -84,15 +86,27 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
||||
);
|
||||
}
|
||||
|
||||
const rootStyle =
|
||||
configContext.platform === Platform.Fabric
|
||||
? {
|
||||
root: {
|
||||
backgroundColor: "transparent",
|
||||
padding: "0px 14px 0px 14px",
|
||||
},
|
||||
}
|
||||
: {
|
||||
root: {
|
||||
backgroundColor: backgroundColor,
|
||||
},
|
||||
};
|
||||
|
||||
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 },
|
||||
}}
|
||||
styles={rootStyle}
|
||||
overflowButtonProps={{ ariaLabel: "More commands" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -54,7 +54,11 @@ export function createStaticCommandBarButtons(
|
||||
const buttons: CommandButtonComponentProps[] = [];
|
||||
|
||||
buttons.push(newCollectionBtn);
|
||||
if (userContext.apiType !== "Tables" && userContext.apiType !== "Cassandra") {
|
||||
if (
|
||||
configContext.platform !== Platform.Fabric &&
|
||||
userContext.apiType !== "Tables" &&
|
||||
userContext.apiType !== "Cassandra"
|
||||
) {
|
||||
const addSynapseLink = createOpenSynapseLinkDialogButton(container);
|
||||
|
||||
if (addSynapseLink) {
|
||||
@@ -257,7 +261,9 @@ export function createDivider(): CommandButtonComponentProps {
|
||||
}
|
||||
|
||||
function areScriptsSupported(): boolean {
|
||||
return userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
||||
return (
|
||||
configContext.platform !== Platform.Fabric && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin")
|
||||
);
|
||||
}
|
||||
|
||||
function createNewCollectionGroup(container: Explorer): CommandButtonComponentProps {
|
||||
|
||||
@@ -9,7 +9,9 @@ import {
|
||||
import * as React from "react";
|
||||
import _ from "underscore";
|
||||
import ChevronDownIcon from "../../../../images/Chevron_down.svg";
|
||||
import { PoolIdType, StyleConstants } from "../../../Common/Constants";
|
||||
import { PoolIdType } from "../../../Common/Constants";
|
||||
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||
import { configContext, Platform } from "../../../ConfigContext";
|
||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
@@ -24,11 +26,14 @@ import { MemoryTracker } from "./MemoryTrackerComponent";
|
||||
export const convertButton = (btns: CommandButtonComponentProps[], backgroundColor: string): ICommandBarItemProps[] => {
|
||||
const buttonHeightPx = StyleConstants.CommandBarButtonHeight;
|
||||
|
||||
const hoverColor =
|
||||
configContext.platform == Platform.Fabric ? StyleConstants.FabricAccentLight : StyleConstants.AccentLight;
|
||||
|
||||
const getFilter = (isDisabled: boolean): string => {
|
||||
if (isDisabled) {
|
||||
return StyleConstants.GrayScale;
|
||||
}
|
||||
return undefined;
|
||||
return configContext.platform == Platform.Fabric ? StyleConstants.NoColor : undefined;
|
||||
};
|
||||
|
||||
return btns
|
||||
@@ -68,6 +73,7 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
|
||||
height: buttonHeightPx,
|
||||
paddingRight: 0,
|
||||
paddingLeft: 0,
|
||||
borderRadius: configContext.platform == Platform.Fabric ? StyleConstants.FabricButtonBorderRadius : "0px",
|
||||
minWidth: 24,
|
||||
marginLeft: isSplit ? 0 : 5,
|
||||
marginRight: isSplit ? 0 : 5,
|
||||
@@ -79,17 +85,17 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
|
||||
splitButtonMenuButton: {
|
||||
backgroundColor: backgroundColor,
|
||||
selectors: {
|
||||
":hover": { backgroundColor: StyleConstants.AccentLight },
|
||||
":hover": { backgroundColor: hoverColor },
|
||||
},
|
||||
width: 16,
|
||||
},
|
||||
label: { fontSize: StyleConstants.mediumFontSize },
|
||||
rootHovered: { backgroundColor: StyleConstants.AccentLight },
|
||||
rootPressed: { backgroundColor: StyleConstants.AccentLight },
|
||||
rootHovered: { backgroundColor: hoverColor },
|
||||
rootPressed: { backgroundColor: hoverColor },
|
||||
splitButtonMenuButtonExpanded: {
|
||||
backgroundColor: StyleConstants.AccentExtra,
|
||||
selectors: {
|
||||
":hover": { backgroundColor: StyleConstants.AccentLight },
|
||||
":hover": { backgroundColor: hoverColor },
|
||||
},
|
||||
},
|
||||
splitButtonDivider: {
|
||||
@@ -120,7 +126,7 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
|
||||
// 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-link:hover": { backgroundColor: hoverColor },
|
||||
".ms-ContextualMenu-icon": { width: 16, height: 16 },
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ import distanceInWordsToNow from "date-fns/distance_in_words_to_now";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import styled from "styled-components";
|
||||
import { StyleConstants } from "../../../Common/Constants";
|
||||
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||
|
||||
interface Props {
|
||||
lastSaved?: Date | null;
|
||||
|
||||
@@ -11,8 +11,9 @@ import {
|
||||
Stack,
|
||||
Text,
|
||||
} from "@fluentui/react";
|
||||
import { QueryCopilotSampleDatabaseId, StyleConstants } from "Common/Constants";
|
||||
import { QueryCopilotSampleDatabaseId } from "Common/Constants";
|
||||
import { handleError } from "Common/ErrorHandlingUtils";
|
||||
import { StyleConstants } from "Common/StyleConstants";
|
||||
import { createCollection } from "Common/dataAccess/createCollection";
|
||||
import * as DataModels from "Contracts/DataModels";
|
||||
import { ContainerSampleGenerator } from "Explorer/DataSamples/ContainerSampleGenerator";
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { Text } from "@fluentui/react";
|
||||
import { shallow } from "enzyme";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import React from "react";
|
||||
import { ExplanationButton } from "./ExplanationButton";
|
||||
|
||||
describe("Explanation Button", () => {
|
||||
const initialStoreState = useQueryCopilot.getState();
|
||||
beforeEach(() => {
|
||||
useQueryCopilot.setState(initialStoreState, true);
|
||||
useQueryCopilot.getState().showExplanationBubble = true;
|
||||
useQueryCopilot.getState().shouldIncludeInMessages = false;
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it("should render explanation bubble with generated comments", () => {
|
||||
useQueryCopilot.getState().shouldIncludeInMessages = true;
|
||||
|
||||
const wrapper = shallow(<ExplanationButton />);
|
||||
|
||||
expect(wrapper.find("Stack")).toHaveLength(1);
|
||||
expect(wrapper.find("Text")).toHaveLength(1);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should render 'Explain this query' link", () => {
|
||||
useQueryCopilot.getState().shouldIncludeInMessages = true;
|
||||
const mockSetChatMessages = jest.fn();
|
||||
const mockSetIsGeneratingExplanation = jest.fn();
|
||||
const mockSetShouldIncludeInMessages = jest.fn();
|
||||
const mockSetShowExplanationBubble = jest.fn();
|
||||
useQueryCopilot.getState().setChatMessages = mockSetChatMessages;
|
||||
useQueryCopilot.getState().setIsGeneratingExplanation = mockSetIsGeneratingExplanation;
|
||||
useQueryCopilot.getState().setShouldIncludeInMessages = mockSetShouldIncludeInMessages;
|
||||
useQueryCopilot.getState().setShowExplanationBubble = mockSetShowExplanationBubble;
|
||||
|
||||
const wrapper = shallow(<ExplanationButton />);
|
||||
|
||||
const textElement = wrapper.find(Text);
|
||||
textElement.simulate("click");
|
||||
|
||||
expect(mockSetChatMessages).toHaveBeenCalledWith([
|
||||
...initialStoreState.chatMessages,
|
||||
{ source: 0, message: "Explain this query to me" },
|
||||
]);
|
||||
expect(mockSetIsGeneratingExplanation).toHaveBeenCalledWith(true);
|
||||
expect(mockSetShouldIncludeInMessages).toHaveBeenCalledWith(true);
|
||||
expect(mockSetShowExplanationBubble).toHaveBeenCalledWith(false);
|
||||
|
||||
jest.advanceTimersByTime(3000);
|
||||
|
||||
expect(mockSetIsGeneratingExplanation).toHaveBeenCalledWith(false);
|
||||
expect(mockSetChatMessages).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should render nothing when conditions are not met", () => {
|
||||
useQueryCopilot.getState().showExplanationBubble = false;
|
||||
|
||||
const wrapper = shallow(<ExplanationButton />);
|
||||
|
||||
expect(wrapper.isEmptyRender()).toBe(true);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -2,12 +2,13 @@ import { Stack, Text } from "@fluentui/react";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import React from "react";
|
||||
|
||||
export const ExplanationBubble: React.FC = (): JSX.Element => {
|
||||
export const ExplanationButton: React.FC = (): JSX.Element => {
|
||||
const {
|
||||
showExplanationBubble,
|
||||
isGeneratingQuery,
|
||||
chatMessages,
|
||||
setChatMessages,
|
||||
generatedQuery,
|
||||
generatedQueryComments,
|
||||
isGeneratingExplanation,
|
||||
setIsGeneratingExplanation,
|
||||
@@ -24,7 +25,7 @@ export const ExplanationBubble: React.FC = (): JSX.Element => {
|
||||
setTimeout(() => {
|
||||
if (useQueryCopilot.getState().shouldIncludeInMessages) {
|
||||
setIsGeneratingExplanation(false);
|
||||
setChatMessages([...chatMessages, { source: 2, message: generatedQueryComments }]);
|
||||
setChatMessages([...chatMessages, { source: 2, message: generatedQueryComments, sqlQuery: generatedQuery }]);
|
||||
}
|
||||
}, 3000);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Explanation Button should render explanation bubble with generated comments 1`] = `
|
||||
<Stack
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"display": "flex",
|
||||
"margin": "5px",
|
||||
"padding": "5px 5px 5px 50px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"border": "1.5px solid #B0BEFF",
|
||||
"borderRadius": "4px",
|
||||
"cursor": "pointer",
|
||||
"marginBottom": "5px",
|
||||
"padding": "2px",
|
||||
"width": "100%",
|
||||
}
|
||||
}
|
||||
>
|
||||
Explain this query to me
|
||||
</Text>
|
||||
</Stack>
|
||||
`;
|
||||
|
||||
exports[`Explanation Button should render nothing when conditions are not met 1`] = `""`;
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Stack, Text } from "@fluentui/react";
|
||||
import { CopilotMessage } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
|
||||
import { FeedbackButtons } from "Explorer/QueryCopilot/V2/Bubbles/Output/Buttons/Feedback/FeedbackButtons";
|
||||
import React from "react";
|
||||
|
||||
export const ExplanationBubble = ({ copilotMessage }: { copilotMessage: CopilotMessage }): JSX.Element => {
|
||||
return (
|
||||
<Stack
|
||||
horizontalAlign="start"
|
||||
verticalAlign="start"
|
||||
tokens={{ padding: 8, childrenGap: 8 }}
|
||||
style={{
|
||||
backgroundColor: "white",
|
||||
borderRadius: "8px",
|
||||
margin: "5px 10px",
|
||||
textAlign: "start",
|
||||
}}
|
||||
>
|
||||
<Text>{copilotMessage.message}</Text>
|
||||
<FeedbackButtons sqlQuery={copilotMessage.sqlQuery} />
|
||||
<Text style={{ fontWeight: 400, fontSize: "10px", lineHeight: "14px" }}>
|
||||
AI-generated content may be incorrect
|
||||
</Text>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -1,69 +1,17 @@
|
||||
import { Text } from "@fluentui/react";
|
||||
import { CopilotMessage } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
|
||||
import { ExplanationBubble } from "Explorer/QueryCopilot/V2/Bubbles/Explanation/ExplainationBubble";
|
||||
import { shallow } from "enzyme";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import React from "react";
|
||||
import { ExplanationBubble } from "./ExplanationBubble";
|
||||
|
||||
describe("Explanation Bubble", () => {
|
||||
const initialStoreState = useQueryCopilot.getState();
|
||||
beforeEach(() => {
|
||||
useQueryCopilot.setState(initialStoreState, true);
|
||||
useQueryCopilot.getState().showExplanationBubble = true;
|
||||
useQueryCopilot.getState().shouldIncludeInMessages = false;
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
describe("Explanation Bubble snapshot tests", () => {
|
||||
it("should render", () => {
|
||||
const mockCopilotMessage: CopilotMessage = {
|
||||
source: 2,
|
||||
message: "Mock message",
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
const wrapper = shallow(<ExplanationBubble copilotMessage={mockCopilotMessage} />);
|
||||
|
||||
it("should render explanation bubble with generated comments", () => {
|
||||
useQueryCopilot.getState().shouldIncludeInMessages = true;
|
||||
|
||||
const wrapper = shallow(<ExplanationBubble />);
|
||||
|
||||
expect(wrapper.find("Stack")).toHaveLength(1);
|
||||
expect(wrapper.find("Text")).toHaveLength(1);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should render 'Explain this query' link", () => {
|
||||
useQueryCopilot.getState().shouldIncludeInMessages = true;
|
||||
const mockSetChatMessages = jest.fn();
|
||||
const mockSetIsGeneratingExplanation = jest.fn();
|
||||
const mockSetShouldIncludeInMessages = jest.fn();
|
||||
const mockSetShowExplanationBubble = jest.fn();
|
||||
useQueryCopilot.getState().setChatMessages = mockSetChatMessages;
|
||||
useQueryCopilot.getState().setIsGeneratingExplanation = mockSetIsGeneratingExplanation;
|
||||
useQueryCopilot.getState().setShouldIncludeInMessages = mockSetShouldIncludeInMessages;
|
||||
useQueryCopilot.getState().setShowExplanationBubble = mockSetShowExplanationBubble;
|
||||
|
||||
const wrapper = shallow(<ExplanationBubble />);
|
||||
|
||||
const textElement = wrapper.find(Text);
|
||||
textElement.simulate("click");
|
||||
|
||||
expect(mockSetChatMessages).toHaveBeenCalledWith([
|
||||
...initialStoreState.chatMessages,
|
||||
{ source: 0, message: "Explain this query to me" },
|
||||
]);
|
||||
expect(mockSetIsGeneratingExplanation).toHaveBeenCalledWith(true);
|
||||
expect(mockSetShouldIncludeInMessages).toHaveBeenCalledWith(true);
|
||||
expect(mockSetShowExplanationBubble).toHaveBeenCalledWith(false);
|
||||
|
||||
jest.advanceTimersByTime(3000);
|
||||
|
||||
expect(mockSetIsGeneratingExplanation).toHaveBeenCalledWith(false);
|
||||
expect(mockSetChatMessages).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should render nothing when conditions are not met", () => {
|
||||
useQueryCopilot.getState().showExplanationBubble = false;
|
||||
|
||||
const wrapper = shallow(<ExplanationBubble />);
|
||||
|
||||
expect(wrapper.isEmptyRender()).toBe(true);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,32 +1,38 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Explanation Bubble should render explanation bubble with generated comments 1`] = `
|
||||
exports[`Explanation Bubble snapshot tests should render 1`] = `
|
||||
<Stack
|
||||
horizontalAlign="start"
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"display": "flex",
|
||||
"margin": "5px",
|
||||
"padding": "5px 5px 5px 50px",
|
||||
"backgroundColor": "white",
|
||||
"borderRadius": "8px",
|
||||
"margin": "5px 10px",
|
||||
"textAlign": "start",
|
||||
}
|
||||
}
|
||||
tokens={
|
||||
Object {
|
||||
"childrenGap": 8,
|
||||
"padding": 8,
|
||||
}
|
||||
}
|
||||
verticalAlign="start"
|
||||
>
|
||||
<Text>
|
||||
Mock message
|
||||
</Text>
|
||||
<FeedbackButtons />
|
||||
<Text
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"border": "1.5px solid #B0BEFF",
|
||||
"borderRadius": "4px",
|
||||
"cursor": "pointer",
|
||||
"marginBottom": "5px",
|
||||
"padding": "2px",
|
||||
"width": "100%",
|
||||
"fontSize": "10px",
|
||||
"fontWeight": 400,
|
||||
"lineHeight": "14px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Explain this query to me
|
||||
AI-generated content may be incorrect
|
||||
</Text>
|
||||
</Stack>
|
||||
`;
|
||||
|
||||
exports[`Explanation Bubble should render nothing when conditions are not met 1`] = `""`;
|
||||
|
||||
@@ -2,18 +2,31 @@ import { Stack, Text } from "@fluentui/react";
|
||||
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
|
||||
import { CopilotMessage } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
|
||||
import { OutputBubbleButtons } from "Explorer/QueryCopilot/V2/Bubbles/Output/Buttons/OutputBubbleButtons";
|
||||
import { userContext } from "UserContext";
|
||||
import React, { useState } from "react";
|
||||
|
||||
export const OutputBubble = ({ copilotMessage }: { copilotMessage: CopilotMessage }): JSX.Element => {
|
||||
const [windowHeight, setWindowHeight] = useState<string>();
|
||||
const textHeightWithPadding = 16;
|
||||
|
||||
const calculateQueryWindowHeight = (): string => {
|
||||
const calculatedHeight = document.getElementById("outputBubble")?.clientHeight * (3 / 5);
|
||||
return `${calculatedHeight}px`;
|
||||
const outputWidth = document.getElementById("outputBubble")?.clientWidth;
|
||||
const responseLength = copilotMessage.sqlQuery.length;
|
||||
|
||||
if (outputWidth > responseLength) {
|
||||
return `${textHeightWithPadding * 3}px`;
|
||||
} else {
|
||||
const neededLines = Math.ceil(responseLength / outputWidth);
|
||||
return `${neededLines * textHeightWithPadding}px`;
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
setWindowHeight(calculateQueryWindowHeight());
|
||||
if (userContext.features.copilotChatFixedMonacoEditorHeight) {
|
||||
setWindowHeight(`${textHeightWithPadding * 5}px`);
|
||||
} else {
|
||||
setWindowHeight(calculateQueryWindowHeight());
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,39 +1,81 @@
|
||||
import { IconButton, Image, Stack, Text } from "@fluentui/react";
|
||||
import { DefaultButton, IconButton, Image, Modal, PrimaryButton, Stack, Text } from "@fluentui/react";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import React from "react";
|
||||
import CopilotIcon from "../../../../../images/CopilotSidebarLogo.svg";
|
||||
|
||||
export const Header: React.FC = (): JSX.Element => {
|
||||
const { setShowCopilotSidebar } = useQueryCopilot();
|
||||
const { setShowCopilotSidebar, chatMessages, setChatMessages, setShowExplanationBubble } = useQueryCopilot();
|
||||
const [showDeleteHistoryModal, setShowDeleteHistoryModal] = React.useState(false);
|
||||
|
||||
const getDeleteHistoryModal = () => {
|
||||
return (
|
||||
<Modal isOpen={showDeleteHistoryModal} styles={{ main: { minHeight: "122px", minWidth: "480px" } }}>
|
||||
<Stack style={{ padding: "16px 24px", height: "auto" }}>
|
||||
<Text style={{ height: 24, fontSize: "18px" }}>
|
||||
<b>Delete chat history?</b>
|
||||
</Text>
|
||||
<Text style={{ marginTop: 10, marginBottom: 20 }}>
|
||||
This action will clear all chat history. Are you sure you want to continue?
|
||||
</Text>
|
||||
<Stack horizontal tokens={{ childrenGap: 10 }} horizontalAlign="start">
|
||||
<PrimaryButton
|
||||
style={{ padding: "0px 20px", height: 24 }}
|
||||
onClick={() => {
|
||||
setChatMessages([]);
|
||||
setShowExplanationBubble(false);
|
||||
setShowDeleteHistoryModal(false);
|
||||
}}
|
||||
>
|
||||
Continue
|
||||
</PrimaryButton>
|
||||
<DefaultButton style={{ padding: "0px 20px", height: 24 }} onClick={() => setShowDeleteHistoryModal(false)}>
|
||||
Close
|
||||
</DefaultButton>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack
|
||||
style={{ margin: "15px 0px 0px 0px", padding: "5px", display: "flex", justifyContent: "space-between" }}
|
||||
horizontal
|
||||
verticalAlign="center"
|
||||
>
|
||||
<Stack horizontal verticalAlign="center">
|
||||
<Image src={CopilotIcon} />
|
||||
<Text style={{ marginLeft: "5px", fontWeight: "bold" }}>Copilot</Text>
|
||||
<Text
|
||||
style={{
|
||||
background: "#f0f0f0",
|
||||
fontSize: "10px",
|
||||
padding: "2px 4px",
|
||||
marginLeft: "5px",
|
||||
borderRadius: "8px",
|
||||
}}
|
||||
>
|
||||
Preview
|
||||
</Text>
|
||||
<>
|
||||
<Stack
|
||||
style={{ margin: "15px 0px 0px 0px", padding: "5px", display: "flex", justifyContent: "space-between" }}
|
||||
horizontal
|
||||
verticalAlign="center"
|
||||
>
|
||||
<Stack horizontal verticalAlign="center">
|
||||
<Image src={CopilotIcon} />
|
||||
<Text style={{ marginLeft: "5px", fontWeight: "bold" }}>Copilot</Text>
|
||||
<Text
|
||||
style={{
|
||||
background: "#f0f0f0",
|
||||
fontSize: "10px",
|
||||
padding: "2px 4px",
|
||||
marginLeft: "5px",
|
||||
borderRadius: "8px",
|
||||
}}
|
||||
>
|
||||
Preview
|
||||
</Text>
|
||||
</Stack>
|
||||
<IconButton
|
||||
onClick={() => setShowDeleteHistoryModal(true)}
|
||||
iconProps={{ iconName: "History" }}
|
||||
title="Delete history"
|
||||
ariaLabel="Delete history"
|
||||
style={{ color: "#424242", verticalAlign: "middle" }}
|
||||
disabled={chatMessages.length === 0}
|
||||
/>
|
||||
<IconButton
|
||||
onClick={() => setShowCopilotSidebar(false)}
|
||||
iconProps={{ iconName: "Cancel" }}
|
||||
title="Exit"
|
||||
ariaLabel="Exit"
|
||||
style={{ color: "#424242", verticalAlign: "middle" }}
|
||||
/>
|
||||
</Stack>
|
||||
<IconButton
|
||||
onClick={() => setShowCopilotSidebar(false)}
|
||||
iconProps={{ iconName: "Cancel" }}
|
||||
title="Exit"
|
||||
ariaLabel="Exit"
|
||||
style={{ color: "#424242", verticalAlign: "middle" }}
|
||||
/>
|
||||
</Stack>
|
||||
{getDeleteHistoryModal()}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,64 +1,158 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Header snapshot test should close on button click 1`] = `
|
||||
<Stack
|
||||
horizontal={true}
|
||||
style={
|
||||
Object {
|
||||
"display": "flex",
|
||||
"justifyContent": "space-between",
|
||||
"margin": "15px 0px 0px 0px",
|
||||
"padding": "5px",
|
||||
}
|
||||
}
|
||||
verticalAlign="center"
|
||||
>
|
||||
<Fragment>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
verticalAlign="center"
|
||||
>
|
||||
<Image
|
||||
src={Object {}}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontWeight": "bold",
|
||||
"marginLeft": "5px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Copilot
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"background": "#f0f0f0",
|
||||
"borderRadius": "8px",
|
||||
"fontSize": "10px",
|
||||
"marginLeft": "5px",
|
||||
"padding": "2px 4px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Preview
|
||||
</Text>
|
||||
</Stack>
|
||||
<CustomizedIconButton
|
||||
ariaLabel="Exit"
|
||||
iconProps={
|
||||
Object {
|
||||
"iconName": "Cancel",
|
||||
}
|
||||
}
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#424242",
|
||||
"verticalAlign": "middle",
|
||||
"display": "flex",
|
||||
"justifyContent": "space-between",
|
||||
"margin": "15px 0px 0px 0px",
|
||||
"padding": "5px",
|
||||
}
|
||||
}
|
||||
title="Exit"
|
||||
/>
|
||||
</Stack>
|
||||
verticalAlign="center"
|
||||
>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
verticalAlign="center"
|
||||
>
|
||||
<Image
|
||||
src={Object {}}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontWeight": "bold",
|
||||
"marginLeft": "5px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Copilot
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"background": "#f0f0f0",
|
||||
"borderRadius": "8px",
|
||||
"fontSize": "10px",
|
||||
"marginLeft": "5px",
|
||||
"padding": "2px 4px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Preview
|
||||
</Text>
|
||||
</Stack>
|
||||
<CustomizedIconButton
|
||||
ariaLabel="Delete history"
|
||||
disabled={true}
|
||||
iconProps={
|
||||
Object {
|
||||
"iconName": "History",
|
||||
}
|
||||
}
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#424242",
|
||||
"verticalAlign": "middle",
|
||||
}
|
||||
}
|
||||
title="Delete history"
|
||||
/>
|
||||
<CustomizedIconButton
|
||||
ariaLabel="Exit"
|
||||
iconProps={
|
||||
Object {
|
||||
"iconName": "Cancel",
|
||||
}
|
||||
}
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#424242",
|
||||
"verticalAlign": "middle",
|
||||
}
|
||||
}
|
||||
title="Exit"
|
||||
/>
|
||||
</Stack>
|
||||
<Modal
|
||||
isOpen={true}
|
||||
styles={
|
||||
Object {
|
||||
"main": Object {
|
||||
"minHeight": "122px",
|
||||
"minWidth": "480px",
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
<Stack
|
||||
style={
|
||||
Object {
|
||||
"height": "auto",
|
||||
"padding": "16px 24px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "18px",
|
||||
"height": 24,
|
||||
}
|
||||
}
|
||||
>
|
||||
<b>
|
||||
Delete chat history?
|
||||
</b>
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"marginBottom": 20,
|
||||
"marginTop": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
This action will clear all chat history. Are you sure you want to continue?
|
||||
</Text>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
horizontalAlign="start"
|
||||
tokens={
|
||||
Object {
|
||||
"childrenGap": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
<CustomizedPrimaryButton
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"height": 24,
|
||||
"padding": "0px 20px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Continue
|
||||
</CustomizedPrimaryButton>
|
||||
<CustomizedDefaultButton
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"height": 24,
|
||||
"padding": "0px 20px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Close
|
||||
</CustomizedDefaultButton>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Modal>
|
||||
</Fragment>
|
||||
`;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Stack } from "@fluentui/react";
|
||||
import { QueryCopilotProps } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
|
||||
import { ExplanationBubble } from "Explorer/QueryCopilot/V2/Bubbles/Explanation/ExplanationBubble";
|
||||
import { ExplanationButton } from "Explorer/QueryCopilot/V2/Bubbles/Explanation/Button/ExplanationButton";
|
||||
import { ExplanationBubble } from "Explorer/QueryCopilot/V2/Bubbles/Explanation/ExplainationBubble";
|
||||
import { OutputBubble } from "Explorer/QueryCopilot/V2/Bubbles/Output/OutputBubble";
|
||||
import { RetrievingBubble } from "Explorer/QueryCopilot/V2/Bubbles/Retriveing/RetrievingBubble";
|
||||
import { SampleBubble } from "Explorer/QueryCopilot/V2/Bubbles/Sample/SampleBubble";
|
||||
@@ -42,27 +43,34 @@ export const QueryCopilotSidebar: React.FC<QueryCopilotProps> = ({ explorer }: Q
|
||||
}}
|
||||
>
|
||||
<WelcomeBubble />
|
||||
{chatMessages.map((message, index) =>
|
||||
message.source === 0 || message.source === 2 ? (
|
||||
<Stack
|
||||
key={index}
|
||||
horizontalAlign="center"
|
||||
tokens={{ padding: 8, childrenGap: 8 }}
|
||||
style={{
|
||||
backgroundColor: message.source === 0 ? "#E0E7FF" : "white",
|
||||
borderRadius: "8px",
|
||||
margin: "5px 10px",
|
||||
textAlign: "start",
|
||||
}}
|
||||
>
|
||||
{message.message}
|
||||
</Stack>
|
||||
) : (
|
||||
<OutputBubble key={index} copilotMessage={message} />
|
||||
)
|
||||
)}
|
||||
{chatMessages.map((message, index) => {
|
||||
switch (message.source) {
|
||||
case 0:
|
||||
return (
|
||||
<Stack
|
||||
key={index}
|
||||
horizontalAlign="center"
|
||||
tokens={{ padding: 8, childrenGap: 8 }}
|
||||
style={{
|
||||
backgroundColor: "#E0E7FF",
|
||||
borderRadius: "8px",
|
||||
margin: "5px 10px",
|
||||
textAlign: "start",
|
||||
}}
|
||||
>
|
||||
{message.message}
|
||||
</Stack>
|
||||
);
|
||||
case 1:
|
||||
return <OutputBubble key={index} copilotMessage={message} />;
|
||||
case 2:
|
||||
return <ExplanationBubble key={index} copilotMessage={message} />;
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
})}
|
||||
<RetrievingBubble />
|
||||
<ExplanationBubble />
|
||||
<ExplanationButton />
|
||||
|
||||
{chatMessages.length === 0 && !isGeneratingQuery && <SampleBubble />}
|
||||
</Stack>
|
||||
|
||||
@@ -138,12 +138,7 @@ function TabNav({ tab, active, tabKind }: { tab?: Tab; active: boolean; tabKind?
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
<span
|
||||
className="tabNavText"
|
||||
style={active ? { fontWeight: "bolder", borderBottom: "2px solid rgba(0,120,212,1)" } : {}}
|
||||
>
|
||||
{useObservable(tab?.tabTitle || getReactTabTitle())}
|
||||
</span>
|
||||
<span className="tabNavText">{useObservable(tab?.tabTitle || getReactTabTitle())}</span>
|
||||
{tabKind !== ReactTabKind.Home && (
|
||||
<span className="tabIconSection">
|
||||
<CloseButton tab={tab} active={active} hovering={hovering} tabKind={tabKind} />
|
||||
|
||||
@@ -37,6 +37,7 @@ import QueryTablesTab from "../Tabs/QueryTablesTab";
|
||||
import { CollectionSettingsTabV2 } from "../Tabs/SettingsTabV2";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
import { Platform, configContext } from "./../../ConfigContext";
|
||||
import ConflictId from "./ConflictId";
|
||||
import DocumentId from "./DocumentId";
|
||||
import StoredProcedure from "./StoredProcedure";
|
||||
@@ -205,7 +206,8 @@ export default class Collection implements ViewModels.Collection {
|
||||
.map((node) => <Trigger>node);
|
||||
});
|
||||
|
||||
const showScriptsMenus: boolean = userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
||||
const showScriptsMenus: boolean =
|
||||
configContext.platform != Platform.Fabric && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin");
|
||||
this.showStoredProcedures = ko.observable<boolean>(showScriptsMenus);
|
||||
this.showTriggers = ko.observable<boolean>(showScriptsMenus);
|
||||
this.showUserDefinedFunctions = ko.observable<boolean>(showScriptsMenus);
|
||||
|
||||
@@ -39,6 +39,7 @@ import { GitHubReposPanel } from "../Panes/GitHubReposPanel/GitHubReposPanel";
|
||||
import TabsBase from "../Tabs/TabsBase";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
import { Platform, configContext } from "./../../ConfigContext";
|
||||
import StoredProcedure from "./StoredProcedure";
|
||||
import Trigger from "./Trigger";
|
||||
import UserDefinedFunction from "./UserDefinedFunction";
|
||||
@@ -69,7 +70,8 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
|
||||
shallow
|
||||
);
|
||||
const { activeTab, refreshActiveTab } = useTabs();
|
||||
const showScriptNodes = userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
||||
const showScriptNodes =
|
||||
configContext.platform !== Platform.Fabric && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin");
|
||||
const pseudoDirPath = "PsuedoDir";
|
||||
|
||||
const buildGalleryCallout = (): JSX.Element => {
|
||||
|
||||
@@ -40,6 +40,7 @@ import { GitHubReposPanel } from "../Panes/GitHubReposPanel/GitHubReposPanel";
|
||||
import TabsBase from "../Tabs/TabsBase";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
import { Platform, configContext } from "./../../ConfigContext";
|
||||
import StoredProcedure from "./StoredProcedure";
|
||||
import Trigger from "./Trigger";
|
||||
import UserDefinedFunction from "./UserDefinedFunction";
|
||||
@@ -249,7 +250,9 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
||||
* @param container
|
||||
*/
|
||||
private static showScriptNodes(container: Explorer): boolean {
|
||||
return userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
||||
return (
|
||||
configContext.platform !== Platform.Fabric && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin")
|
||||
);
|
||||
}
|
||||
|
||||
private buildCollectionNode(database: ViewModels.Database, collection: ViewModels.Collection): TreeNode {
|
||||
|
||||
@@ -89,8 +89,8 @@ export const ResourceTree2: React.FC<ResourceTreeProps> = ({ container }: Resour
|
||||
aria-label="CosmosDB resources"
|
||||
openItems={openItems}
|
||||
onOpenChange={handleOpenChange}
|
||||
size="small"
|
||||
style={{ height: "100%" }}
|
||||
size="medium"
|
||||
style={{ height: "100%", width: "290px" }}
|
||||
>
|
||||
{[dataNodeTree].map((node) => (
|
||||
<TreeNode2Component
|
||||
|
||||
@@ -17,6 +17,7 @@ import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
|
||||
import { mostRecentActivity } from "../MostRecentActivity/MostRecentActivity";
|
||||
import { useNotebook } from "../Notebook/useNotebook";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
import { Platform, configContext } from "./../../ConfigContext";
|
||||
|
||||
export const buildCollectionNode = (
|
||||
database: ViewModels.Database,
|
||||
@@ -25,6 +26,46 @@ export const buildCollectionNode = (
|
||||
container: Explorer,
|
||||
refreshActiveTab: (comparator: (tab: TabsBase) => boolean) => void
|
||||
): TreeNode2 => {
|
||||
let children: TreeNode2[];
|
||||
|
||||
// Flat Tree for Fabric
|
||||
if (configContext.platform !== Platform.Fabric) {
|
||||
children = buildCollectionNodeChildren(database, collection, isNotebookEnabled, container, refreshActiveTab);
|
||||
}
|
||||
|
||||
return {
|
||||
label: collection.id(),
|
||||
iconSrc: CollectionIcon,
|
||||
children: children,
|
||||
className: "collectionHeader",
|
||||
contextMenu: ResourceTreeContextMenuButtonFactory.createCollectionContextMenuButton(container, collection),
|
||||
onClick: () => {
|
||||
useSelectedNode.getState().setSelectedNode(collection);
|
||||
collection.openTab();
|
||||
// push to most recent
|
||||
mostRecentActivity.collectionWasOpened(userContext.databaseAccount?.id, collection);
|
||||
},
|
||||
onExpanded: () => {
|
||||
// Rewritten version of expandCollapseCollection
|
||||
useSelectedNode.getState().setSelectedNode(collection);
|
||||
useCommandBar.getState().setContextButtons([]);
|
||||
refreshActiveTab(
|
||||
(tab: TabsBase) =>
|
||||
tab.collection?.id() === collection.id() && tab.collection.databaseId === collection.databaseId
|
||||
);
|
||||
},
|
||||
isSelected: () => useSelectedNode.getState().isDataNodeSelected(collection.databaseId, collection.id()),
|
||||
onContextMenuOpen: () => useSelectedNode.getState().setSelectedNode(collection),
|
||||
};
|
||||
};
|
||||
|
||||
const buildCollectionNodeChildren = (
|
||||
database: ViewModels.Database,
|
||||
collection: ViewModels.Collection,
|
||||
isNotebookEnabled: boolean,
|
||||
container: Explorer,
|
||||
refreshActiveTab: (comparator: (tab: TabsBase) => boolean) => void
|
||||
): TreeNode2[] => {
|
||||
const showScriptNodes = userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
||||
const children: TreeNode2[] = [];
|
||||
children.push({
|
||||
@@ -110,27 +151,7 @@ export const buildCollectionNode = (
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
label: collection.id(),
|
||||
iconSrc: CollectionIcon,
|
||||
children: children,
|
||||
className: "collectionHeader",
|
||||
contextMenu: ResourceTreeContextMenuButtonFactory.createCollectionContextMenuButton(container, collection),
|
||||
onClick: () => {
|
||||
useSelectedNode.getState().setSelectedNode(collection);
|
||||
},
|
||||
onExpanded: () => {
|
||||
// Rewritten version of expandCollapseCollection
|
||||
useSelectedNode.getState().setSelectedNode(collection);
|
||||
useCommandBar.getState().setContextButtons([]);
|
||||
refreshActiveTab(
|
||||
(tab: TabsBase) =>
|
||||
tab.collection?.id() === collection.id() && tab.collection.databaseId === collection.databaseId
|
||||
);
|
||||
},
|
||||
isSelected: () => useSelectedNode.getState().isDataNodeSelected(collection.databaseId, collection.id()),
|
||||
onContextMenuOpen: () => useSelectedNode.getState().setSelectedNode(collection),
|
||||
};
|
||||
return children;
|
||||
};
|
||||
|
||||
const buildStoredProcedureNode = (
|
||||
|
||||
@@ -9,6 +9,7 @@ import * as ResourceTreeContextMenuButtonFactory from "../ContextMenuButtonFacto
|
||||
import Explorer from "../Explorer";
|
||||
import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
import { Platform, configContext } from "./../../ConfigContext";
|
||||
|
||||
export const useDatabaseTreeNodes = (container: Explorer, isNotebookEnabled: boolean): TreeNode2[] => {
|
||||
const databases = useDatabases((state) => state.databases);
|
||||
@@ -35,7 +36,7 @@ export const useDatabaseTreeNodes = (container: Explorer, isNotebookEnabled: boo
|
||||
onContextMenuOpen: () => useSelectedNode.getState().setSelectedNode(database),
|
||||
};
|
||||
|
||||
if (database.isDatabaseShared()) {
|
||||
if (database.isDatabaseShared() && configContext.platform !== Platform.Fabric) {
|
||||
databaseNode.children.push({
|
||||
id: database.isSampleDB ? "sampleScaleSettings" : "",
|
||||
label: "Scale",
|
||||
|
||||
23
src/Main.tsx
23
src/Main.tsx
@@ -1,5 +1,5 @@
|
||||
// CSS Dependencies
|
||||
import { initializeIcons } from "@fluentui/react";
|
||||
import { initializeIcons, loadTheme } from "@fluentui/react";
|
||||
import { QuickstartCarousel } from "Explorer/Quickstart/QuickstartCarousel";
|
||||
import { MongoQuickstartTutorial } from "Explorer/Quickstart/Tutorials/MongoQuickstartTutorial";
|
||||
import { SQLQuickstartTutorial } from "Explorer/Quickstart/Tutorials/SQLQuickstartTutorial";
|
||||
@@ -26,6 +26,8 @@ import "../less/TableStyles/CustomizeColumns.less";
|
||||
import "../less/TableStyles/EntityEditor.less";
|
||||
import "../less/TableStyles/fulldatatables.less";
|
||||
import "../less/TableStyles/queryBuilder.less";
|
||||
import * as StyleConstants from "./Common/StyleConstants";
|
||||
import { configContext, Platform } from "ConfigContext";
|
||||
import "../less/documentDB.less";
|
||||
import "../less/forms.less";
|
||||
import "../less/infobox.less";
|
||||
@@ -57,6 +59,7 @@ import "./Libs/jquery";
|
||||
import "./Shared/appInsights";
|
||||
import { useConfig } from "./hooks/useConfig";
|
||||
import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer";
|
||||
import { appThemeFabric } from "./Platform/Fabric/FabricTheme";
|
||||
|
||||
initializeIcons();
|
||||
|
||||
@@ -67,6 +70,10 @@ const App: React.FunctionComponent = () => {
|
||||
const shouldShowModal = useQueryCopilot((state) => state.showFeedbackModal);
|
||||
|
||||
const config = useConfig();
|
||||
if (config?.platform === Platform.Fabric) {
|
||||
loadTheme(appThemeFabric);
|
||||
}
|
||||
StyleConstants.updateStyles();
|
||||
const explorer = useKnockoutExplorer(config?.platform);
|
||||
|
||||
const toggleLeftPaneExpanded = () => {
|
||||
@@ -84,6 +91,7 @@ const App: React.FunctionComponent = () => {
|
||||
|
||||
return (
|
||||
<div className="flexContainer" aria-hidden="false">
|
||||
<LoadFabricOverrides />
|
||||
<div id="divExplorer" className="flexContainer hideOverflows">
|
||||
<div id="freeTierTeachingBubble"> </div>
|
||||
{/* Main Command Bar - Start */}
|
||||
@@ -135,6 +143,19 @@ const App: React.FunctionComponent = () => {
|
||||
|
||||
ReactDOM.render(<App />, document.body);
|
||||
|
||||
function LoadFabricOverrides(): JSX.Element {
|
||||
if (configContext.platform === Platform.Fabric) {
|
||||
const FabricStyle = React.lazy(() => import("./Platform/Fabric/FabricPlatform"));
|
||||
return (
|
||||
<React.Suspense fallback={<div></div>}>
|
||||
<FabricStyle />
|
||||
</React.Suspense>
|
||||
);
|
||||
} else {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
|
||||
function LoadingExplorer(): JSX.Element {
|
||||
return (
|
||||
<div className="splashLoaderContainer">
|
||||
|
||||
7
src/Platform/Fabric/FabricPlatform.tsx
Normal file
7
src/Platform/Fabric/FabricPlatform.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import React from "react";
|
||||
import "../../../less/documentDBFabric.less";
|
||||
// This is a dummy export, allowing us to conditionally import documentDBFabric.less
|
||||
// by lazy-importing this in Main.tsx (see LoadFabricOverrides() there)
|
||||
export default function InitFabric() {
|
||||
return <></>;
|
||||
}
|
||||
208
src/Platform/Fabric/FabricTheme.tsx
Normal file
208
src/Platform/Fabric/FabricTheme.tsx
Normal file
@@ -0,0 +1,208 @@
|
||||
import { Theme, createTheme } from "@fluentui/react";
|
||||
|
||||
export const appThemeFabric: Theme = createTheme({
|
||||
palette: {
|
||||
/**
|
||||
* Color code for themeDarker.
|
||||
*/
|
||||
themeDarker: "#033f38",
|
||||
/**
|
||||
* Color code for themeDark.
|
||||
*/
|
||||
themeDark: "#0a5c50",
|
||||
/**
|
||||
* Color code for themeDarkAlt.
|
||||
*/
|
||||
themeDarkAlt: "#0c695a",
|
||||
/**
|
||||
* Color code for themePrimary.
|
||||
*/
|
||||
themePrimary: "#117865",
|
||||
/**
|
||||
* Color code for themeSecondary.
|
||||
*/
|
||||
themeSecondary: "#1f937e",
|
||||
/**
|
||||
* Color code for themeTertiary.
|
||||
*/
|
||||
themeTertiary: "#52c7aa",
|
||||
/**
|
||||
* Color code for themeLight.
|
||||
*/
|
||||
themeLight: "#9ee0cb",
|
||||
/**
|
||||
* Color code for themeLighter.
|
||||
*/
|
||||
themeLighter: "#c0ecdd",
|
||||
/**
|
||||
* Color code for themeLighterAlt.
|
||||
*/
|
||||
themeLighterAlt: "#e3f7ef",
|
||||
/**
|
||||
* Color code for the strongest color, which is black in the default theme.
|
||||
* This is a very light color in inverted themes.
|
||||
*/
|
||||
black: "#000000",
|
||||
/**
|
||||
* Color code for blackTranslucent40.
|
||||
*/
|
||||
blackTranslucent40: "rgba(0, 0, 0, 0.4)",
|
||||
/**
|
||||
* Color code for neutralDark.
|
||||
*/
|
||||
neutralDark: "#141414",
|
||||
/**
|
||||
* Color code for neutralPrimary.
|
||||
*/
|
||||
neutralPrimary: "#242424",
|
||||
/**
|
||||
* Color code for neutralPrimaryAlt.
|
||||
*/
|
||||
neutralPrimaryAlt: "#383838",
|
||||
/**
|
||||
* Color code for neutralSecondary.
|
||||
*/
|
||||
neutralSecondary: "#5c5c5c",
|
||||
/**
|
||||
* Color code for neutralSecondaryAlt.
|
||||
*/
|
||||
neutralSecondaryAlt: "#858585",
|
||||
/**
|
||||
* Color code for neutralTertiary.
|
||||
*/
|
||||
neutralTertiary: "#9e9e9e",
|
||||
/**
|
||||
* Color code for neutralTertiaryAlt.
|
||||
*/
|
||||
neutralTertiaryAlt: "#c7c7c7",
|
||||
/**
|
||||
* Color code for neutralQuaternary.
|
||||
*/
|
||||
neutralQuaternary: "#d1d1d1",
|
||||
/**
|
||||
* Color code for neutralQuaternaryAlt.
|
||||
*/
|
||||
neutralQuaternaryAlt: "#e0e0e0",
|
||||
/**
|
||||
* Color code for neutralLight.
|
||||
*/
|
||||
neutralLight: "#ebebeb",
|
||||
/**
|
||||
* Color code for neutralLighter.
|
||||
*/
|
||||
neutralLighter: "#f5f5f5",
|
||||
/**
|
||||
* Color code for neutralLighterAlt.
|
||||
*/
|
||||
neutralLighterAlt: "#fafafa",
|
||||
/**
|
||||
* Color code for the accent.
|
||||
*/
|
||||
accent: "#117865",
|
||||
/**
|
||||
* Color code for the softest color, which is white in the default theme. This is a very dark color in dark themes.
|
||||
* This is the page background.
|
||||
*/
|
||||
white: "#ffffff",
|
||||
/**
|
||||
* Color code for whiteTranslucent40
|
||||
*/
|
||||
whiteTranslucent40: "rgba(255, 255, 255, 0.4)",
|
||||
/**
|
||||
* Color code for yellowDark.
|
||||
*/
|
||||
yellowDark: "#d39300",
|
||||
/**
|
||||
* Color code for yellow.
|
||||
*/
|
||||
yellow: "#fde300",
|
||||
/**
|
||||
* Color code for yellowLight.
|
||||
*/
|
||||
yellowLight: "#fef7b2",
|
||||
/**
|
||||
* Color code for orange.
|
||||
*/
|
||||
orange: "#f7630c",
|
||||
/**
|
||||
* Color code for orangeLight.
|
||||
*/
|
||||
orangeLight: "#f98845",
|
||||
/**
|
||||
* Color code for orangeLighter.
|
||||
*/
|
||||
orangeLighter: "#fdcfb4",
|
||||
/**
|
||||
* Color code for redDark.
|
||||
*/
|
||||
redDark: "#750b1c",
|
||||
/**
|
||||
* Color code for red.
|
||||
*/
|
||||
red: "#d13438",
|
||||
/**
|
||||
* Color code for magentaDark.
|
||||
*/
|
||||
magentaDark: "#6b0043",
|
||||
/**
|
||||
* Color code for magenta.
|
||||
*/
|
||||
magenta: "#bf0077",
|
||||
/**
|
||||
* Color code for magentaLight.
|
||||
*/
|
||||
magentaLight: "#d957a8",
|
||||
/**
|
||||
* Color code for purpleDark.
|
||||
*/
|
||||
purpleDark: "#401b6c",
|
||||
/**
|
||||
* Color code for purple.
|
||||
*/
|
||||
purple: "#5c2e91",
|
||||
/**
|
||||
* Color code for purpleLight.
|
||||
*/
|
||||
purpleLight: "#c6b1de",
|
||||
/**
|
||||
* Color code for blueDark.
|
||||
*/
|
||||
blueDark: "#003966",
|
||||
/**
|
||||
* Color code for blueMid.
|
||||
*/
|
||||
blueMid: "#004e8c",
|
||||
/**
|
||||
* Color code for blue.
|
||||
*/
|
||||
blue: "#0078d4",
|
||||
/**
|
||||
* Color code for blueLight.
|
||||
*/
|
||||
blueLight: "#3a96dd",
|
||||
/**
|
||||
* Color code for tealDark.
|
||||
*/
|
||||
tealDark: "#006666",
|
||||
/**
|
||||
* Color code for teal.
|
||||
*/
|
||||
teal: "#038387",
|
||||
/**
|
||||
* Color code for tealLight.
|
||||
*/
|
||||
tealLight: "#00b7c3",
|
||||
/**
|
||||
* Color code for greenDark.
|
||||
*/
|
||||
greenDark: "#0b6a0b",
|
||||
/**
|
||||
* Color code for green.
|
||||
*/
|
||||
green: "#107c10",
|
||||
/**
|
||||
* Color code for greenLight.
|
||||
*/
|
||||
greenLight: "#13a10e",
|
||||
},
|
||||
});
|
||||
@@ -4,7 +4,7 @@
|
||||
import { DefaultButton, IButtonStyles, IContextualMenuItem } from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import { FunctionComponent, useEffect, useState } from "react";
|
||||
import { StyleConstants } from "../../../Common/Constants";
|
||||
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||
import { DatabaseAccount } from "../../../Contracts/DataModels";
|
||||
import { useDatabaseAccounts } from "../../../hooks/useDatabaseAccounts";
|
||||
import { useSubscriptions } from "../../../hooks/useSubscriptions";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useBoolean } from "@fluentui/react-hooks";
|
||||
import * as React from "react";
|
||||
import ErrorImage from "../../../../images/error.svg";
|
||||
import ConnectImage from "../../../../images/HdeConnectCosmosDB.svg";
|
||||
import ErrorImage from "../../../../images/error.svg";
|
||||
import { AuthType } from "../../../AuthType";
|
||||
import { HttpHeaders } from "../../../Common/Constants";
|
||||
import { configContext } from "../../../ConfigContext";
|
||||
@@ -16,6 +16,19 @@ interface Props {
|
||||
setAuthType: (authType: AuthType) => void;
|
||||
}
|
||||
|
||||
export const fetchEncryptedToken = async (connectionString: string): Promise<string> => {
|
||||
const headers = new Headers();
|
||||
headers.append(HttpHeaders.connectionString, connectionString);
|
||||
const url = configContext.BACKEND_ENDPOINT + "/api/guest/tokens/generateToken";
|
||||
const response = await fetch(url, { headers, method: "POST" });
|
||||
if (!response.ok) {
|
||||
throw response;
|
||||
}
|
||||
// This API has a quirk where it must be parsed twice
|
||||
const result: GenerateTokenResponse = JSON.parse(await response.json());
|
||||
return decodeURIComponent(result.readWrite || result.read);
|
||||
};
|
||||
|
||||
export const ConnectExplorer: React.FunctionComponent<Props> = ({
|
||||
setEncryptedToken,
|
||||
login,
|
||||
@@ -44,16 +57,8 @@ export const ConnectExplorer: React.FunctionComponent<Props> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const headers = new Headers();
|
||||
headers.append(HttpHeaders.connectionString, connectionString);
|
||||
const url = configContext.BACKEND_ENDPOINT + "/api/guest/tokens/generateToken";
|
||||
const response = await fetch(url, { headers, method: "POST" });
|
||||
if (!response.ok) {
|
||||
throw response;
|
||||
}
|
||||
// This API has a quirk where it must be parsed twice
|
||||
const result: GenerateTokenResponse = JSON.parse(await response.json());
|
||||
setEncryptedToken(decodeURIComponent(result.readWrite || result.read));
|
||||
const encryptedToken = await fetchEncryptedToken(connectionString);
|
||||
setEncryptedToken(encryptedToken);
|
||||
setAuthType(AuthType.ConnectionString);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -40,6 +40,7 @@ export type Features = {
|
||||
readonly copilotVersion?: string;
|
||||
readonly disableCopilotPhoenixGateaway: boolean;
|
||||
readonly enableCopilotFullSchema: boolean;
|
||||
readonly copilotChatFixedMonacoEditorHeight: boolean;
|
||||
|
||||
// can be set via both flight and feature flag
|
||||
autoscaleDefault: boolean;
|
||||
@@ -112,6 +113,7 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
|
||||
copilotVersion: get("copilotversion") ?? "v1.0",
|
||||
disableCopilotPhoenixGateaway: "true" === get("disablecopilotphoenixgateaway"),
|
||||
enableCopilotFullSchema: "true" === get("enablecopilotfullschema", "true"),
|
||||
copilotChatFixedMonacoEditorHeight: "true" === get("copilotchatfixedmonacoeditorheight"),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IButtonStyles, ICommandBarStyles, ISeparatorStyles, IStackTokens } from "@fluentui/react";
|
||||
import { StyleConstants } from "../Common/Constants";
|
||||
import { StyleConstants } from "../Common/StyleConstants";
|
||||
|
||||
export const commandBarItemStyles: IButtonStyles = { root: { paddingLeft: 20 } };
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Platform, configContext } from "./../ConfigContext";
|
||||
|
||||
export const getDataExplorerWindow = (currentWindow: Window): Window | undefined => {
|
||||
// Data explorer is always loaded in an iframe, so traverse the parents until we hit the top and return the first child window.
|
||||
try {
|
||||
@@ -5,7 +7,11 @@ export const getDataExplorerWindow = (currentWindow: Window): Window | undefined
|
||||
if (currentWindow.parent === currentWindow) {
|
||||
return undefined;
|
||||
}
|
||||
if (currentWindow.parent === currentWindow.top) {
|
||||
if (configContext.platform === Platform.Fabric && currentWindow.parent.parent === currentWindow.top) {
|
||||
// in Fabric data explorer is inside an extension iframe, so we have two parent iframes
|
||||
return currentWindow;
|
||||
}
|
||||
if (configContext.platform !== Platform.Fabric && currentWindow.parent === currentWindow.top) {
|
||||
return currentWindow;
|
||||
}
|
||||
currentWindow = currentWindow.parent;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { createUri } from "Common/UrlUtility";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import { fetchEncryptedToken } from "Platform/Hosted/Components/ConnectExplorer";
|
||||
import { getNetworkSettingsWarningMessage } from "Utils/NetworkUtility";
|
||||
import { fetchAccessData } from "hooks/usePortalAccessToken";
|
||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||
import { useEffect, useState } from "react";
|
||||
import { AuthType } from "../AuthType";
|
||||
@@ -60,6 +62,26 @@ export function useKnockoutExplorer(platform: Platform): Explorer {
|
||||
} else if (platform === Platform.Portal) {
|
||||
const explorer = await configurePortal();
|
||||
setExplorer(explorer);
|
||||
} else if (platform === Platform.Fabric) {
|
||||
// TODO For now, retrieve info from session storage. Replace with info injected into Data Explorer
|
||||
const connectionString = sessionStorage.getItem("connectionString");
|
||||
if (!connectionString) {
|
||||
console.error("No connection string found in session storage");
|
||||
return;
|
||||
}
|
||||
const encryptedToken = await fetchEncryptedToken(connectionString);
|
||||
// TODO Duplicated from useTokenMetadata
|
||||
const encryptedTokenMetadata = await fetchAccessData(encryptedToken);
|
||||
|
||||
const win = (window as unknown) as HostedExplorerChildFrame;
|
||||
win.hostedConfig = {
|
||||
authType: AuthType.EncryptedToken,
|
||||
encryptedToken,
|
||||
encryptedTokenMetadata,
|
||||
};
|
||||
|
||||
const explorer = await configureHosted();
|
||||
setExplorer(explorer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<clear />
|
||||
<add name="X-Xss-Protection" value="1; mode=block" />
|
||||
<add name="X-Content-Type-Options" value="nosniff" />
|
||||
<add name="Content-Security-Policy" value="frame-ancestors 'self' portal.azure.com *.portal.azure.com portal.azure.us portal.azure.cn portal.microsoftazure.de df.onecloud.azure-test.net" />
|
||||
<add name="Content-Security-Policy" value="frame-ancestors 'self' portal.azure.com *.portal.azure.com portal.azure.us portal.azure.cn portal.microsoftazure.de df.onecloud.azure-test.net *.fabric.microsoft.com *.powerbi.com pbi-rdb-edog.analysis-df.windows.net" />
|
||||
</customHeaders>
|
||||
<redirectHeaders>
|
||||
<clear />
|
||||
|
||||
Reference in New Issue
Block a user