mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-25 20:01:45 +00:00
Compare commits
76 Commits
release/mo
...
ashleyst/r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf8359c548 | ||
|
|
b03925abab | ||
|
|
53d3413c62 | ||
|
|
7128133874 | ||
|
|
053dc9d76b | ||
|
|
23b2e59560 | ||
|
|
869d81dfbc | ||
|
|
42a1c6c319 | ||
|
|
9f1cc4cd5c | ||
|
|
78154bd976 | ||
|
|
91649d2f52 | ||
|
|
d7647b2ecf | ||
|
|
2c7e788358 | ||
|
|
fdbbbd7378 | ||
|
|
82bdeff158 | ||
|
|
825a5d5257 | ||
|
|
d75553a94d | ||
|
|
50c47a82d6 | ||
|
|
2c2f0c8d7b | ||
|
|
cfc8196c4b | ||
|
|
87024f4bf4 | ||
|
|
fe9730206e | ||
|
|
7e95f5d8c8 | ||
|
|
1be221e106 | ||
|
|
8e7a3db67e | ||
|
|
07c0ead523 | ||
|
|
4296b5ae02 | ||
|
|
e8a5658799 | ||
|
|
b4973e8367 | ||
|
|
4b207f3fa6 | ||
|
|
c5b7f599b3 | ||
|
|
6aeac542b1 | ||
|
|
0d22d4ab4d | ||
|
|
0658448b54 | ||
|
|
833d677d20 | ||
|
|
038142c180 | ||
|
|
94d3fcb30f | ||
|
|
d3722f2c99 | ||
|
|
5a5e155205 | ||
|
|
2226169a71 | ||
|
|
6f35fb5526 | ||
|
|
805a4ae168 | ||
|
|
cc89691da3 | ||
|
|
24860a6842 | ||
|
|
bf6b362610 | ||
|
|
baca7922b4 | ||
|
|
b59ba20ed0 | ||
|
|
7f55de7aa2 | ||
|
|
62c76cc264 | ||
|
|
99d95a4cec | ||
|
|
647cca09b3 | ||
|
|
2c5f4e9666 | ||
|
|
58ae64193f | ||
|
|
806a0657df | ||
|
|
bc479fb808 | ||
|
|
31773ee73b | ||
|
|
3d1f280378 | ||
|
|
2ef036ee94 | ||
|
|
77c758714d | ||
|
|
bcd8b7229f | ||
|
|
0a1d16de1b | ||
|
|
1e6c40eabf | ||
|
|
70d1dc6f74 | ||
|
|
d07d2c7c0d | ||
|
|
7a1aa89cd1 | ||
|
|
e67c3f6774 | ||
|
|
bd334a118a | ||
|
|
5871c1e2d0 | ||
|
|
81dccbe5be | ||
|
|
49c3d0f0cb | ||
|
|
375bb5f567 | ||
|
|
e9f83a8efd | ||
|
|
093ddba2db | ||
|
|
dfe79b20f5 | ||
|
|
1021e9c969 | ||
|
|
c30a9681fe |
16
.devcontainer/Dockerfile
Normal file
16
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
FROM mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm
|
||||||
|
|
||||||
|
# Install pre-reqs for gyp, and 'canvas' npm module
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y \
|
||||||
|
make \
|
||||||
|
gcc \
|
||||||
|
g++ \
|
||||||
|
python3-minimal \
|
||||||
|
libcairo2-dev \
|
||||||
|
libpango1.0-dev \
|
||||||
|
&& \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Install node-gyp to build native modules
|
||||||
|
RUN npm install -g node-gyp
|
||||||
32
.devcontainer/devcontainer.json
Normal file
32
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||||
|
// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
|
||||||
|
{
|
||||||
|
"name": "Azure Cosmos DB Explorer",
|
||||||
|
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||||
|
"build": {
|
||||||
|
"dockerfile": "Dockerfile"
|
||||||
|
},
|
||||||
|
"onCreateCommand": ".devcontainer/oncreate",
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers/features/azure-cli:1": {
|
||||||
|
"version": "latest"
|
||||||
|
},
|
||||||
|
"ghcr.io/devcontainers/features/github-cli:1": {
|
||||||
|
"installDirectlyFromGitHubRelease": true,
|
||||||
|
"version": "latest"
|
||||||
|
},
|
||||||
|
"ghcr.io/devcontainers/features/sshd:1": {
|
||||||
|
"version": "latest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||||
|
// "features": {},
|
||||||
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
// "forwardPorts": [],
|
||||||
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
|
// "postCreateCommand": "yarn install",
|
||||||
|
// Configure tool-specific properties.
|
||||||
|
// "customizations": {},
|
||||||
|
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||||
|
// "remoteUser": "root"
|
||||||
|
}
|
||||||
4
.devcontainer/oncreate
Executable file
4
.devcontainer/oncreate
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Install packages once, to prime the node_modules directory.
|
||||||
|
npm ci
|
||||||
5
.npmrc
5
.npmrc
@@ -1 +1,4 @@
|
|||||||
save-exact=true
|
save-exact=true
|
||||||
|
|
||||||
|
# Ignore peer dependency conflicts
|
||||||
|
force=true # TODO: Remove this when we update to React 17 or higher!
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# Why?
|
|
||||||
|
|
||||||
This adds a mock module for `canvas`. Nteract has a ignored require and undeclared dependency on this module. `cavnas` is a server side node module and is not used in browser side code for nteract.
|
|
||||||
|
|
||||||
Installing it locally (`npm install canvas`) will resolve the problem, but it is a native module so it is flaky depending on the system, node version, processor arch, etc. This module provides a simpler, more robust solution.
|
|
||||||
|
|
||||||
Remove this workaround if [this bug](https://github.com/nteract/any-vega/issues/2) ever gets resolved
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
module.exports = {}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "canvas",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC"
|
|
||||||
}
|
|
||||||
9
images/EntraID.svg
Normal file
9
images/EntraID.svg
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="uuid-f8d4d392-7c12-4bd9-baff-66fbf7814b91" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||||
|
<path d="m3.802,14.032c.388.242,1.033.511,1.715.511.621,0,1.198-.18,1.676-.487,0,0,.001,0,.002-.001l1.805-1.128v4.073c-.286,0-.574-.078-.824-.234l-4.374-2.734Z" fill="#225086"/>
|
||||||
|
<path d="m7.853,1.507L.353,9.967c-.579.654-.428,1.642.323,2.111,0,0,2.776,1.735,3.126,1.954.388.242,1.033.511,1.715.511.621,0,1.198-.18,1.676-.487,0,0,.001,0,.002-.001l1.805-1.128-4.364-2.728,4.365-4.924V1s0,0,0,0c-.424,0-.847.169-1.147.507Z" fill="#6df"/>
|
||||||
|
<polygon points="4.636 10.199 4.688 10.231 9 12.927 9.001 12.927 9.001 12.927 9.001 5.276 9 5.275 4.636 10.199" fill="#cbf8ff"/>
|
||||||
|
<path d="m17.324,12.078c.751-.469.902-1.457.323-2.111l-4.921-5.551c-.397-.185-.842-.291-1.313-.291-.925,0-1.752.399-2.302,1.026l-.109.123h0s4.364,4.924,4.364,4.924h0s0,0,0,0l-4.365,2.728v4.073c.287,0,.573-.078.823-.234l7.5-4.688Z" fill="#074793"/>
|
||||||
|
<path d="m9.001,1v4.275s.109-.123.109-.123c.55-.627,1.377-1.026,2.302-1.026.472,0,.916.107,1.313.291l-2.579-2.909c-.299-.338-.723-.507-1.146-.507Z" fill="#0294e4"/>
|
||||||
|
<polygon points="13.365 10.199 13.365 10.199 13.365 10.199 9.001 5.276 9.001 12.926 13.365 10.199" fill="#96bcc2"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -80,6 +80,7 @@ module.exports = {
|
|||||||
"d3-quadtree": "<rootDir>/node_modules/d3-quadtree/dist/d3-quadtree.min.js",
|
"d3-quadtree": "<rootDir>/node_modules/d3-quadtree/dist/d3-quadtree.min.js",
|
||||||
"d3-scale-chromatic": "<rootDir>/node_modules/d3-scale-chromatic/dist/d3-scale-chromatic.min.js",
|
"d3-scale-chromatic": "<rootDir>/node_modules/d3-scale-chromatic/dist/d3-scale-chromatic.min.js",
|
||||||
"d3-zoom": "<rootDir>/node_modules/d3-zoom/dist/d3-zoom.min.js",
|
"d3-zoom": "<rootDir>/node_modules/d3-zoom/dist/d3-zoom.min.js",
|
||||||
|
uuid: require.resolve("uuid"), // Force module uuid to resolve with the CJS entry point, because Jest does not support package.json.exports. See https://github.com/uuidjs/uuid/issues/451
|
||||||
},
|
},
|
||||||
|
|
||||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||||
@@ -133,7 +134,7 @@ module.exports = {
|
|||||||
snapshotSerializers: ["enzyme-to-json/serializer"],
|
snapshotSerializers: ["enzyme-to-json/serializer"],
|
||||||
|
|
||||||
// The test environment that will be used for testing
|
// The test environment that will be used for testing
|
||||||
// testEnvironment: "jest-environment-jsdom",
|
testEnvironment: "jsdom",
|
||||||
modulePaths: ["node_modules", "<rootDir>/src"],
|
modulePaths: ["node_modules", "<rootDir>/src"],
|
||||||
|
|
||||||
// Options that will be passed to the testEnvironment
|
// Options that will be passed to the testEnvironment
|
||||||
@@ -157,7 +158,7 @@ module.exports = {
|
|||||||
// testResultsProcessor: "./trxProcessor.js",
|
// testResultsProcessor: "./trxProcessor.js",
|
||||||
|
|
||||||
// This option allows use of a custom test runner
|
// This option allows use of a custom test runner
|
||||||
// testRunner: "jasmine2",
|
testRunner: "jest-circus/runner",
|
||||||
|
|
||||||
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
|
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
|
||||||
// testURL: "http://localhost",
|
// testURL: "http://localhost",
|
||||||
@@ -167,13 +168,13 @@ module.exports = {
|
|||||||
|
|
||||||
// A map from regular expressions to paths to transformers
|
// A map from regular expressions to paths to transformers
|
||||||
transform: {
|
transform: {
|
||||||
"^.+\\.html?$": "html-loader-jest",
|
"^.+\\.html?$": "jest-html-loader",
|
||||||
"^.+\\.[t|j]sx?$": "babel-jest",
|
"^.+\\.[t|j]sx?$": "babel-jest",
|
||||||
"^.+\\.svg$": "<rootDir>/svgTransform.js",
|
"^.+\\.svg$": "<rootDir>/jest/svgTransform.js",
|
||||||
},
|
},
|
||||||
|
|
||||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||||
transformIgnorePatterns: ["/node_modules/", "/externals/"],
|
transformIgnorePatterns: ["/node_modules/(?!@fluentui/react-icons)", "/externals/"],
|
||||||
|
|
||||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||||
// unmockedModulePathPatterns: undefined,
|
// unmockedModulePathPatterns: undefined,
|
||||||
@@ -186,4 +187,7 @@ module.exports = {
|
|||||||
|
|
||||||
// Whether to use watchman for file crawling
|
// Whether to use watchman for file crawling
|
||||||
// watchman: true,
|
// watchman: true,
|
||||||
|
|
||||||
|
// TODO: toMatchInlineSnapshot() does not work with prettier 3. Remove when fixed: https://github.com/jestjs/jest/issues/14305
|
||||||
|
prettierPath: null,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -168,7 +168,7 @@
|
|||||||
|
|
||||||
@FabricBoxBorderRadius: 8px;
|
@FabricBoxBorderRadius: 8px;
|
||||||
@FabricBoxBorderShadow: rgba(0, 0, 0, 0.133) 0px 1.6px 3.6px 0px, rgba(0, 0, 0, 0.11) 0px 0.3px 0.9px 0px;
|
@FabricBoxBorderShadow: rgba(0, 0, 0, 0.133) 0px 1.6px 3.6px 0px, rgba(0, 0, 0, 0.11) 0px 0.3px 0.9px 0px;
|
||||||
@FabricBoxMargin: 4px 3px 4px 3px;
|
@FabricBoxMargin: 4px 8px 4px 8px;
|
||||||
|
|
||||||
@FabricAccentMediumHigh: #0c695a;
|
@FabricAccentMediumHigh: #0c695a;
|
||||||
@FabricAccentMedium: #117865;
|
@FabricAccentMedium: #117865;
|
||||||
|
|||||||
@@ -1906,8 +1906,14 @@ input::-webkit-calendar-picker-indicator::after {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.nav-tabs-margin {
|
.nav-tabs-margin {
|
||||||
padding-top: 8px;
|
height: 32px;
|
||||||
background-color: #f2f2f2;
|
background-color: #f2f2f2;
|
||||||
|
|
||||||
|
.nav-tabs {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.navTabHeight {
|
.navTabHeight {
|
||||||
@@ -2074,14 +2080,6 @@ a:link {
|
|||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.resourceTreeAndTabs {
|
|
||||||
display: flex;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
overflow-x: clip;
|
|
||||||
overflow-y: auto;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.collectiontitle {
|
.collectiontitle {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
@@ -2325,11 +2323,6 @@ td a:hover {
|
|||||||
outline: 1px dotted;
|
outline: 1px dotted;
|
||||||
}
|
}
|
||||||
|
|
||||||
#content.active .tabdocuments .scrollable {
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-fixed thead {
|
.table-fixed thead {
|
||||||
width: 97%;
|
width: 97%;
|
||||||
padding-left: 18px;
|
padding-left: 18px;
|
||||||
@@ -2365,10 +2358,9 @@ a:link {
|
|||||||
|
|
||||||
.tabsManagerContainer {
|
.tabsManagerContainer {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
flex-grow: 1;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 300px;
|
min-width: 0; // This prevents it to grow past the parent's width if its content is too wide
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs {
|
.tabs {
|
||||||
@@ -2579,18 +2571,6 @@ a:link {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.documentsTab {
|
|
||||||
.documentsTable {
|
|
||||||
.documentsTableCell {
|
|
||||||
border-left: 1px solid @BaseMedium;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.documentsTableHeader {
|
|
||||||
border-bottom: 1px solid @BaseMedium;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.querydropdown {
|
.querydropdown {
|
||||||
border: 1px solid @BaseMedium;
|
border: 1px solid @BaseMedium;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@@ -2637,7 +2617,8 @@ a:link {
|
|||||||
|
|
||||||
.tabPanesContainer {
|
.tabPanesContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100%;
|
flex-grow: 1;
|
||||||
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ a:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.nav-tabs-margin {
|
.nav-tabs-margin {
|
||||||
padding-top: 8px;
|
padding-top: 5px;
|
||||||
background-color: #ffffff
|
background-color: #ffffff
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,19 +3,6 @@
|
|||||||
.dataResourceTree {
|
.dataResourceTree {
|
||||||
margin-left: @MediumSpace;
|
margin-left: @MediumSpace;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
.databaseHeader {
|
|
||||||
padding: 1px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.collectionHeader {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loadMoreHeader {
|
|
||||||
color: RGB(5, 99, 193);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.notebookResourceTree {
|
.notebookResourceTree {
|
||||||
|
|||||||
19929
package-lock.json
generated
19929
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
52
package.json
52
package.json
@@ -12,14 +12,14 @@
|
|||||||
"@azure/msal-browser": "2.14.2",
|
"@azure/msal-browser": "2.14.2",
|
||||||
"@babel/plugin-proposal-class-properties": "7.12.1",
|
"@babel/plugin-proposal-class-properties": "7.12.1",
|
||||||
"@babel/plugin-proposal-decorators": "7.12.12",
|
"@babel/plugin-proposal-decorators": "7.12.12",
|
||||||
"@fluentui/react": "8.112.1",
|
"@fluentui/react": "8.119.0",
|
||||||
"@fluentui/react-components": "9.34.0",
|
"@fluentui/react-components": "9.54.2",
|
||||||
"@jupyterlab/services": "6.0.2",
|
"@jupyterlab/services": "6.0.2",
|
||||||
"@jupyterlab/terminal": "3.0.3",
|
"@jupyterlab/terminal": "3.0.3",
|
||||||
"@microsoft/applicationinsights-web": "2.6.1",
|
"@microsoft/applicationinsights-web": "2.6.1",
|
||||||
"@nteract/commutable": "7.5.1",
|
"@nteract/commutable": "7.5.1",
|
||||||
"@nteract/connected-components": "6.8.2",
|
"@nteract/connected-components": "6.8.2",
|
||||||
"@nteract/core": "15.1.0",
|
"@nteract/core": "15.1.9",
|
||||||
"@nteract/data-explorer": "8.0.3",
|
"@nteract/data-explorer": "8.0.3",
|
||||||
"@nteract/directory-listing": "2.0.6",
|
"@nteract/directory-listing": "2.0.6",
|
||||||
"@nteract/dropdown-menu": "1.0.1",
|
"@nteract/dropdown-menu": "1.0.1",
|
||||||
@@ -42,15 +42,15 @@
|
|||||||
"@nteract/transform-vega": "7.0.6",
|
"@nteract/transform-vega": "7.0.6",
|
||||||
"@octokit/rest": "17.9.2",
|
"@octokit/rest": "17.9.2",
|
||||||
"@phosphor/widgets": "1.9.3",
|
"@phosphor/widgets": "1.9.3",
|
||||||
"@testing-library/jest-dom": "5.11.9",
|
"@testing-library/jest-dom": "6.4.6",
|
||||||
"@types/lodash": "4.14.171",
|
"@types/lodash": "4.14.171",
|
||||||
"@types/mkdirp": "1.0.1",
|
"@types/mkdirp": "1.0.1",
|
||||||
"@types/node-fetch": "2.5.7",
|
"@types/node-fetch": "2.5.7",
|
||||||
"@uiw/react-split": "5.9.3",
|
|
||||||
"@xmldom/xmldom": "0.7.13",
|
"@xmldom/xmldom": "0.7.13",
|
||||||
|
"allotment": "1.20.2",
|
||||||
"applicationinsights": "1.8.0",
|
"applicationinsights": "1.8.0",
|
||||||
"bootstrap": "3.4.1",
|
"bootstrap": "3.4.1",
|
||||||
"canvas": "file:./canvas",
|
"canvas": "2.11.2",
|
||||||
"clean-webpack-plugin": "4.0.0",
|
"clean-webpack-plugin": "4.0.0",
|
||||||
"clipboard-copy": "4.0.1",
|
"clipboard-copy": "4.0.1",
|
||||||
"copy-webpack-plugin": "11.0.0",
|
"copy-webpack-plugin": "11.0.0",
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
"eslint-plugin-react": "7.33.2",
|
"eslint-plugin-react": "7.33.2",
|
||||||
"hasher": "1.2.0",
|
"hasher": "1.2.0",
|
||||||
"html2canvas": "1.0.0-rc.5",
|
"html2canvas": "1.0.0-rc.5",
|
||||||
"i18next": "19.8.4",
|
"i18next": "23.11.5",
|
||||||
"i18next-browser-languagedetector": "6.0.1",
|
"i18next-browser-languagedetector": "6.0.1",
|
||||||
"i18next-http-backend": "1.0.23",
|
"i18next-http-backend": "1.0.23",
|
||||||
"iframe-resizer-react": "1.1.0",
|
"iframe-resizer-react": "1.1.0",
|
||||||
@@ -93,13 +93,13 @@
|
|||||||
"react-dnd-html5-backend": "14.0.0",
|
"react-dnd-html5-backend": "14.0.0",
|
||||||
"react-dom": "16.14.0",
|
"react-dom": "16.14.0",
|
||||||
"react-hotkeys": "2.0.0",
|
"react-hotkeys": "2.0.0",
|
||||||
"react-i18next": "11.8.5",
|
"react-i18next": "14.1.2",
|
||||||
"react-notification-system": "0.2.17",
|
"react-notification-system": "0.2.17",
|
||||||
"react-redux": "7.1.3",
|
"react-redux": "7.1.3",
|
||||||
"react-splitter-layout": "4.0.0",
|
"react-splitter-layout": "4.0.0",
|
||||||
"react-string-format": "1.0.1",
|
"react-string-format": "1.0.1",
|
||||||
"react-youtube": "9.0.1",
|
|
||||||
"react-window": "1.8.10",
|
"react-window": "1.8.10",
|
||||||
|
"react-youtube": "9.0.1",
|
||||||
"reflect-metadata": "0.1.13",
|
"reflect-metadata": "0.1.13",
|
||||||
"rx-jupyter": "5.5.12",
|
"rx-jupyter": "5.5.12",
|
||||||
"sanitize-html": "2.3.3",
|
"sanitize-html": "2.3.3",
|
||||||
@@ -113,10 +113,10 @@
|
|||||||
"zustand": "3.5.0"
|
"zustand": "3.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.9.0",
|
"@babel/core": "7.24.7",
|
||||||
"@babel/preset-env": "7.9.0",
|
"@babel/preset-env": "7.24.7",
|
||||||
"@babel/preset-react": "7.9.4",
|
"@babel/preset-react": "7.24.7",
|
||||||
"@babel/preset-typescript": "7.9.0",
|
"@babel/preset-typescript": "7.24.7",
|
||||||
"@playwright/test": "1.44.0",
|
"@playwright/test": "1.44.0",
|
||||||
"@testing-library/react": "11.2.3",
|
"@testing-library/react": "11.2.3",
|
||||||
"@types/applicationinsights-js": "1.0.7",
|
"@types/applicationinsights-js": "1.0.7",
|
||||||
@@ -129,13 +129,13 @@
|
|||||||
"@types/enzyme": "3.10.12",
|
"@types/enzyme": "3.10.12",
|
||||||
"@types/enzyme-adapter-react-16": "1.0.9",
|
"@types/enzyme-adapter-react-16": "1.0.9",
|
||||||
"@types/hasher": "0.0.31",
|
"@types/hasher": "0.0.31",
|
||||||
"@types/jest": "26.0.20",
|
"@types/jest": "29.5.12",
|
||||||
"@types/jquery": "3.5.29",
|
"@types/jquery": "3.5.29",
|
||||||
"@types/node": "12.11.1",
|
"@types/node": "12.11.1",
|
||||||
"@types/post-robot": "10.0.1",
|
"@types/post-robot": "10.0.1",
|
||||||
"@types/q": "1.5.1",
|
"@types/q": "1.5.1",
|
||||||
"@types/react": "17.0.3",
|
"@types/react": "17.0.44",
|
||||||
"@types/react-dom": "17.0.3",
|
"@types/react-dom": "17.0.15",
|
||||||
"@types/react-notification-system": "0.2.39",
|
"@types/react-notification-system": "0.2.39",
|
||||||
"@types/react-redux": "7.1.7",
|
"@types/react-redux": "7.1.7",
|
||||||
"@types/react-splitter-layout": "3.0.1",
|
"@types/react-splitter-layout": "3.0.1",
|
||||||
@@ -148,7 +148,7 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "6.7.4",
|
"@typescript-eslint/eslint-plugin": "6.7.4",
|
||||||
"@typescript-eslint/parser": "6.7.4",
|
"@typescript-eslint/parser": "6.7.4",
|
||||||
"@webpack-cli/serve": "2.0.5",
|
"@webpack-cli/serve": "2.0.5",
|
||||||
"babel-jest": "24.9.0",
|
"babel-jest": "29.7.0",
|
||||||
"babel-loader": "8.1.0",
|
"babel-loader": "8.1.0",
|
||||||
"buffer": "5.1.0",
|
"buffer": "5.1.0",
|
||||||
"case-sensitive-paths-webpack-plugin": "2.4.0",
|
"case-sensitive-paths-webpack-plugin": "2.4.0",
|
||||||
@@ -165,13 +165,15 @@
|
|||||||
"fast-glob": "3.2.5",
|
"fast-glob": "3.2.5",
|
||||||
"fs-extra": "7.0.0",
|
"fs-extra": "7.0.0",
|
||||||
"html-inline-css-webpack-plugin": "1.11.2",
|
"html-inline-css-webpack-plugin": "1.11.2",
|
||||||
"html-loader": "0.5.5",
|
"html-loader": "5.0.0",
|
||||||
"html-loader-jest": "0.2.1",
|
|
||||||
"html-webpack-plugin": "5.5.3",
|
"html-webpack-plugin": "5.5.3",
|
||||||
"jest": "26.6.3",
|
"jest": "29.7.0",
|
||||||
"jest-canvas-mock": "2.3.1",
|
"jest-canvas-mock": "2.5.2",
|
||||||
|
"jest-circus": "29.7.0",
|
||||||
|
"jest-html-loader": "1.0.0",
|
||||||
"jest-react-hooks-shallow": "1.5.1",
|
"jest-react-hooks-shallow": "1.5.1",
|
||||||
"jest-trx-results-processor": "0.0.7",
|
"jest-trx-results-processor": "3.0.2",
|
||||||
|
"jest-environment-jsdom": "29.7.0",
|
||||||
"less": "3.8.1",
|
"less": "3.8.1",
|
||||||
"less-loader": "11.1.3",
|
"less-loader": "11.1.3",
|
||||||
"less-vars-loader": "1.1.0",
|
"less-vars-loader": "1.1.0",
|
||||||
@@ -187,8 +189,8 @@
|
|||||||
"sinon": "3.2.1",
|
"sinon": "3.2.1",
|
||||||
"style-loader": "0.23.0",
|
"style-loader": "0.23.0",
|
||||||
"ts-loader": "9.2.4",
|
"ts-loader": "9.2.4",
|
||||||
"typedoc": "0.22.15",
|
"typedoc": "0.26.2",
|
||||||
"typescript": "4.3.5",
|
"typescript": "4.9.5",
|
||||||
"url-loader": "4.1.1",
|
"url-loader": "4.1.1",
|
||||||
"wait-on": "4.0.2",
|
"wait-on": "4.0.2",
|
||||||
"webpack": "5.88.2",
|
"webpack": "5.88.2",
|
||||||
@@ -245,4 +247,4 @@
|
|||||||
"printWidth": 120,
|
"printWidth": 120,
|
||||||
"endOfLine": "auto"
|
"endOfLine": "auto"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
13
patches/@phosphor+virtualdom+1.2.0.patch
Normal file
13
patches/@phosphor+virtualdom+1.2.0.patch
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
diff --git a/node_modules/@phosphor/virtualdom/lib/index.d.ts b/node_modules/@phosphor/virtualdom/lib/index.d.ts
|
||||||
|
index 95682b9..73e2daa 100644
|
||||||
|
--- a/node_modules/@phosphor/virtualdom/lib/index.d.ts
|
||||||
|
+++ b/node_modules/@phosphor/virtualdom/lib/index.d.ts
|
||||||
|
@@ -58,7 +58,7 @@ export declare type ElementEventMap = {
|
||||||
|
ondrop: DragEvent;
|
||||||
|
ondurationchange: Event;
|
||||||
|
onemptied: Event;
|
||||||
|
- onended: MediaStreamErrorEvent;
|
||||||
|
+ onended: ErrorEvent;
|
||||||
|
onerror: ErrorEvent;
|
||||||
|
onfocus: FocusEvent;
|
||||||
|
oninput: Event;
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
diff --git a/node_modules/@uiw/react-split/cjs/index.d.ts b/node_modules/@uiw/react-split/cjs/index.d.ts
|
|
||||||
index 644bcc3..f794760 100644
|
|
||||||
--- a/node_modules/@uiw/react-split/cjs/index.d.ts
|
|
||||||
+++ b/node_modules/@uiw/react-split/cjs/index.d.ts
|
|
||||||
@@ -56,5 +56,5 @@ export default class Split extends React.Component<SplitProps, SplitState> {
|
|
||||||
onMouseDown(paneNumber: number, env: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
|
|
||||||
onDragging(env: Event): void;
|
|
||||||
onDragEnd(): void;
|
|
||||||
- render(): import("react/jsx-runtime").JSX.Element;
|
|
||||||
+ render(): JSX.Element;
|
|
||||||
}
|
|
||||||
@@ -1,51 +1,58 @@
|
|||||||
import { defineConfig, devices } from '@playwright/test';
|
import { defineConfig, devices } from "@playwright/test";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See https://playwright.dev/docs/test-configuration.
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
*/
|
*/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
testDir: 'test',
|
testDir: "test",
|
||||||
fullyParallel: true,
|
fullyParallel: true,
|
||||||
forbidOnly: !!process.env.CI,
|
forbidOnly: !!process.env.CI,
|
||||||
retries: process.env.CI ? 3 : 0,
|
retries: process.env.CI ? 3 : 0,
|
||||||
workers: process.env.CI ? 1 : undefined,
|
workers: process.env.CI ? 1 : undefined,
|
||||||
reporter: process.env.CI ? 'blob' : 'html',
|
reporter: process.env.CI ? "blob" : "html",
|
||||||
timeout: 10 * 60 * 1000,
|
timeout: 10 * 60 * 1000,
|
||||||
use: {
|
use: {
|
||||||
actionTimeout: 5 * 60 * 1000,
|
trace: "off",
|
||||||
trace: 'off',
|
video: "off",
|
||||||
video: 'off',
|
screenshot: "on",
|
||||||
screenshot: 'on',
|
testIdAttribute: "data-test",
|
||||||
testIdAttribute: 'data-test',
|
|
||||||
contextOptions: {
|
contextOptions: {
|
||||||
ignoreHTTPSErrors: true,
|
ignoreHTTPSErrors: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
expect: {
|
expect: {
|
||||||
timeout: 5 * 60 * 1000,
|
// Many of our expectations take a little longer than the default 5 seconds.
|
||||||
|
timeout: 15 * 1000,
|
||||||
},
|
},
|
||||||
|
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: 'chromium',
|
name: "chromium",
|
||||||
use: { ...devices['Desktop Chrome'] },
|
use: { ...devices["Desktop Chrome"] },
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'firefox',
|
name: "firefox",
|
||||||
use: { ...devices['Desktop Firefox'] },
|
use: { ...devices["Desktop Firefox"] },
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'webkit',
|
name: "webkit",
|
||||||
use: { ...devices['Desktop Safari'] },
|
use: { ...devices["Desktop Safari"] },
|
||||||
|
},
|
||||||
|
/* Test against branded browsers. */
|
||||||
|
{
|
||||||
|
name: "Google Chrome",
|
||||||
|
use: { ...devices["Desktop Chrome"], channel: "chrome" }, // or 'chrome-beta'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Microsoft Edge",
|
||||||
|
use: { ...devices["Desktop Edge"], channel: "msedge" }, // or 'msedge-dev'
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
webServer: {
|
webServer: {
|
||||||
command: 'npm run start',
|
command: "npm run start",
|
||||||
url: 'https://127.0.0.1:1234/_ready',
|
url: "https://127.0.0.1:1234/_ready",
|
||||||
timeout: 120 * 1000,
|
timeout: 120 * 1000,
|
||||||
ignoreHTTPSErrors: true,
|
ignoreHTTPSErrors: true,
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: !process.env.CI,
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
import React, { FunctionComponent, MutableRefObject, useEffect, useRef } from "react";
|
|
||||||
import arrowLeftImg from "../../images/imgarrowlefticon.svg";
|
|
||||||
import { getApiShortDisplayName } from "../Utils/APITypeUtils";
|
|
||||||
import { NormalizedEventKey } from "./Constants";
|
|
||||||
|
|
||||||
export interface CollapsedResourceTreeProps {
|
|
||||||
toggleLeftPaneExpanded: () => void;
|
|
||||||
isLeftPaneExpanded: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CollapsedResourceTree: FunctionComponent<CollapsedResourceTreeProps> = ({
|
|
||||||
toggleLeftPaneExpanded,
|
|
||||||
isLeftPaneExpanded,
|
|
||||||
}: CollapsedResourceTreeProps): JSX.Element => {
|
|
||||||
const focusButton = useRef<HTMLLIElement>() as MutableRefObject<HTMLLIElement>;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (focusButton.current) {
|
|
||||||
focusButton.current.focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const onKeyPressToggleLeftPaneExpanded = (event: React.KeyboardEvent) => {
|
|
||||||
if (event.key === NormalizedEventKey.Space || event.key === NormalizedEventKey.Enter) {
|
|
||||||
toggleLeftPaneExpanded();
|
|
||||||
event.stopPropagation();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="mini" className={!isLeftPaneExpanded ? "mini toggle-mini" : "hiddenMain"}>
|
|
||||||
<div className="main-nav nav">
|
|
||||||
<ul className="nav">
|
|
||||||
<li
|
|
||||||
className="resourceTreeCollapse"
|
|
||||||
id="collapseToggleLeftPaneButton"
|
|
||||||
role="button"
|
|
||||||
tabIndex={0}
|
|
||||||
aria-label={getApiShortDisplayName() + `Expand tree`}
|
|
||||||
onClick={toggleLeftPaneExpanded}
|
|
||||||
onKeyPress={onKeyPressToggleLeftPaneExpanded}
|
|
||||||
ref={focusButton}
|
|
||||||
>
|
|
||||||
<span className="leftarrowCollapsed">
|
|
||||||
<img className="arrowCollapsed" src={arrowLeftImg} alt="Expand" />
|
|
||||||
</span>
|
|
||||||
<span className="collectionCollapsed">
|
|
||||||
<span>{getApiShortDisplayName()}</span>
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -134,6 +134,9 @@ export class BackendApi {
|
|||||||
public static readonly GenerateToken: string = "GenerateToken";
|
public static readonly GenerateToken: string = "GenerateToken";
|
||||||
public static readonly PortalSettings: string = "PortalSettings";
|
public static readonly PortalSettings: string = "PortalSettings";
|
||||||
public static readonly AccountRestrictions: string = "AccountRestrictions";
|
public static readonly AccountRestrictions: string = "AccountRestrictions";
|
||||||
|
public static readonly RuntimeProxy: string = "RuntimeProxy";
|
||||||
|
public static readonly DisallowedLocations: string = "DisallowedLocations";
|
||||||
|
public static readonly SampleData: string = "SampleData";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PortalBackendEndpoints {
|
export class PortalBackendEndpoints {
|
||||||
@@ -183,6 +186,12 @@ export class CassandraProxyAPIs {
|
|||||||
public static readonly connectionStringSchemaApi: string = "api/connectionstring/cassandra/schema";
|
public static readonly connectionStringSchemaApi: string = "api/connectionstring/cassandra/schema";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class AadEndpoints {
|
||||||
|
public static readonly Prod: string = "https://login.microsoftonline.com/";
|
||||||
|
public static readonly Fairfax: string = "https://login.microsoftonline.us/";
|
||||||
|
public static readonly Mooncake: string = "https://login.partner.microsoftonline.cn/";
|
||||||
|
}
|
||||||
|
|
||||||
export class Queries {
|
export class Queries {
|
||||||
public static CustomPageOption: string = "custom";
|
public static CustomPageOption: string = "custom";
|
||||||
public static UnlimitedPageOption: string = "unlimited";
|
public static UnlimitedPageOption: string = "unlimited";
|
||||||
@@ -197,6 +206,12 @@ export class Queries {
|
|||||||
public static readonly DefaultMaxWaitTimeInSeconds = 30;
|
public static readonly DefaultMaxWaitTimeInSeconds = 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class RBACOptions {
|
||||||
|
public static setAutomaticRBACOption: string = "Automatic";
|
||||||
|
public static setTrueRBACOption: string = "True";
|
||||||
|
public static setFalseRBACOption: string = "False";
|
||||||
|
}
|
||||||
|
|
||||||
export class SavedQueries {
|
export class SavedQueries {
|
||||||
public static readonly CollectionName: string = "___Query";
|
public static readonly CollectionName: string = "___Query";
|
||||||
public static readonly DatabaseName: string = "___Cosmos";
|
public static readonly DatabaseName: string = "___Cosmos";
|
||||||
@@ -278,6 +293,7 @@ export class HttpStatusCodes {
|
|||||||
public static readonly Accepted: number = 202;
|
public static readonly Accepted: number = 202;
|
||||||
public static readonly NoContent: number = 204;
|
public static readonly NoContent: number = 204;
|
||||||
public static readonly NotModified: number = 304;
|
public static readonly NotModified: number = 304;
|
||||||
|
public static readonly BadRequest: number = 400;
|
||||||
public static readonly Unauthorized: number = 401;
|
public static readonly Unauthorized: number = 401;
|
||||||
public static readonly Forbidden: number = 403;
|
public static readonly Forbidden: number = 403;
|
||||||
public static readonly NotFound: number = 404;
|
public static readonly NotFound: number = 404;
|
||||||
@@ -489,7 +505,7 @@ export class PriorityLevel {
|
|||||||
public static readonly Default = "low";
|
public static readonly Default = "low";
|
||||||
}
|
}
|
||||||
|
|
||||||
export const QueryCopilotSampleDatabaseId = "CopilotSampleDb";
|
export const QueryCopilotSampleDatabaseId = "CopilotSampleDB";
|
||||||
export const QueryCopilotSampleContainerId = "SampleContainer";
|
export const QueryCopilotSampleContainerId = "SampleContainer";
|
||||||
|
|
||||||
export const QueryCopilotSampleContainerSchema = {
|
export const QueryCopilotSampleContainerSchema = {
|
||||||
|
|||||||
@@ -1,47 +1,6 @@
|
|||||||
import { ResourceType } from "@azure/cosmos";
|
|
||||||
import { Platform, resetConfigContext, updateConfigContext } from "../ConfigContext";
|
import { Platform, resetConfigContext, updateConfigContext } from "../ConfigContext";
|
||||||
import { updateUserContext } from "../UserContext";
|
import { updateUserContext } from "../UserContext";
|
||||||
import { endpoint, getTokenFromAuthService, requestPlugin, tokenProvider } from "./CosmosClient";
|
import { endpoint, getTokenFromAuthService, requestPlugin } from "./CosmosClient";
|
||||||
|
|
||||||
describe("tokenProvider", () => {
|
|
||||||
const options = {
|
|
||||||
verb: "GET" as any,
|
|
||||||
path: "/",
|
|
||||||
resourceId: "",
|
|
||||||
resourceType: "dbs" as ResourceType,
|
|
||||||
headers: {},
|
|
||||||
getAuthorizationTokenUsingMasterKey: () => "",
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
updateConfigContext({
|
|
||||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
|
|
||||||
});
|
|
||||||
window.fetch = jest.fn().mockImplementation(() => {
|
|
||||||
return {
|
|
||||||
json: () => "{}",
|
|
||||||
headers: new Map(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
jest.restoreAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("calls the auth token service if no master key is set", async () => {
|
|
||||||
await tokenProvider(options);
|
|
||||||
expect((window.fetch as any).mock.calls.length).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not call the auth service if a master key is set", async () => {
|
|
||||||
updateUserContext({
|
|
||||||
masterKey: "foo",
|
|
||||||
});
|
|
||||||
await tokenProvider(options);
|
|
||||||
expect((window.fetch as any).mock.calls.length).toBe(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("getTokenFromAuthService", () => {
|
describe("getTokenFromAuthService", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ import { getAuthorizationTokenUsingResourceTokens } from "Common/getAuthorizatio
|
|||||||
import { AuthorizationToken } from "Contracts/FabricMessageTypes";
|
import { AuthorizationToken } from "Contracts/FabricMessageTypes";
|
||||||
import { checkDatabaseResourceTokensValidity } from "Platform/Fabric/FabricUtil";
|
import { checkDatabaseResourceTokensValidity } from "Platform/Fabric/FabricUtil";
|
||||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||||
|
import { useNewPortalBackendEndpoint } from "Utils/EndpointUtils";
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
import { PriorityLevel } from "../Common/Constants";
|
import { BackendApi, PriorityLevel } from "../Common/Constants";
|
||||||
|
import * as Logger from "../Common/Logger";
|
||||||
import { Platform, configContext } from "../ConfigContext";
|
import { Platform, configContext } from "../ConfigContext";
|
||||||
import { userContext } from "../UserContext";
|
import { userContext } from "../UserContext";
|
||||||
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
||||||
@@ -17,7 +19,18 @@ const _global = typeof self === "undefined" ? window : self;
|
|||||||
export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
|
export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
|
||||||
const { verb, resourceId, resourceType, headers } = requestInfo;
|
const { verb, resourceId, resourceType, headers } = requestInfo;
|
||||||
|
|
||||||
if (userContext.features.enableAadDataPlane && userContext.aadToken) {
|
const dataPlaneRBACOptionEnabled = userContext.dataPlaneRbacEnabled && userContext.apiType === "SQL";
|
||||||
|
if (userContext.features.enableAadDataPlane || dataPlaneRBACOptionEnabled) {
|
||||||
|
Logger.logInfo(
|
||||||
|
`AAD Data Plane Feature flag set to ${userContext.features.enableAadDataPlane} for account with disable local auth ${userContext.databaseAccount.properties.disableLocalAuth} `,
|
||||||
|
"Explorer/tokenProvider",
|
||||||
|
);
|
||||||
|
if (!userContext.aadToken) {
|
||||||
|
logConsoleError(
|
||||||
|
`AAD token does not exist. Please click on "Login for Entra ID" button prior to performing Entra ID RBAC operations`,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const AUTH_PREFIX = `type=aad&ver=1.0&sig=`;
|
const AUTH_PREFIX = `type=aad&ver=1.0&sig=`;
|
||||||
const authorizationToken = `${AUTH_PREFIX}${userContext.aadToken}`;
|
const authorizationToken = `${AUTH_PREFIX}${userContext.aadToken}`;
|
||||||
return authorizationToken;
|
return authorizationToken;
|
||||||
@@ -71,8 +84,15 @@ export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (userContext.masterKey) {
|
if (userContext.masterKey) {
|
||||||
|
Logger.logInfo(`Master Key exists`, "Explorer/tokenProvider");
|
||||||
// TODO This SDK method mutates the headers object. Find a better one or fix the SDK.
|
// TODO This SDK method mutates the headers object. Find a better one or fix the SDK.
|
||||||
await Cosmos.setAuthorizationTokenHeaderUsingMasterKey(verb, resourceId, resourceType, headers, EmulatorMasterKey);
|
await Cosmos.setAuthorizationTokenHeaderUsingMasterKey(
|
||||||
|
verb,
|
||||||
|
resourceId,
|
||||||
|
resourceType,
|
||||||
|
headers,
|
||||||
|
userContext.masterKey,
|
||||||
|
);
|
||||||
return decodeURIComponent(headers.authorization);
|
return decodeURIComponent(headers.authorization);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +124,37 @@ export async function getTokenFromAuthService(
|
|||||||
verb: string,
|
verb: string,
|
||||||
resourceType: string,
|
resourceType: string,
|
||||||
resourceId?: string,
|
resourceId?: string,
|
||||||
|
): Promise<AuthorizationToken> {
|
||||||
|
if (!useNewPortalBackendEndpoint(BackendApi.RuntimeProxy)) {
|
||||||
|
return getTokenFromAuthService_ToBeDeprecated(verb, resourceType, resourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const host: string = configContext.PORTAL_BACKEND_ENDPOINT;
|
||||||
|
const response: Response = await _global.fetch(host + "/api/connectionstring/runtimeproxy/authorizationtokens", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json",
|
||||||
|
"x-ms-encrypted-auth-token": userContext.accessToken,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
verb,
|
||||||
|
resourceType,
|
||||||
|
resourceId,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const result: AuthorizationToken = await response.json();
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
logConsoleError(`Failed to get authorization headers for ${resourceType}: ${getErrorMessage(error)}`);
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getTokenFromAuthService_ToBeDeprecated(
|
||||||
|
verb: string,
|
||||||
|
resourceType: string,
|
||||||
|
resourceId?: string,
|
||||||
): Promise<AuthorizationToken> {
|
): Promise<AuthorizationToken> {
|
||||||
try {
|
try {
|
||||||
const host = configContext.BACKEND_ENDPOINT;
|
const host = configContext.BACKEND_ENDPOINT;
|
||||||
@@ -137,8 +188,11 @@ enum SDKSupportedCapabilities {
|
|||||||
let _client: Cosmos.CosmosClient;
|
let _client: Cosmos.CosmosClient;
|
||||||
|
|
||||||
export function client(): Cosmos.CosmosClient {
|
export function client(): Cosmos.CosmosClient {
|
||||||
if (_client) return _client;
|
if (_client) {
|
||||||
|
if (!userContext.hasDataPlaneRbacSettingChanged) {
|
||||||
|
return _client;
|
||||||
|
}
|
||||||
|
}
|
||||||
let _defaultHeaders: Cosmos.CosmosHeaders = {};
|
let _defaultHeaders: Cosmos.CosmosHeaders = {};
|
||||||
_defaultHeaders["x-ms-cosmos-sdk-supportedcapabilities"] =
|
_defaultHeaders["x-ms-cosmos-sdk-supportedcapabilities"] =
|
||||||
SDKSupportedCapabilities.None | SDKSupportedCapabilities.PartitionMerge;
|
SDKSupportedCapabilities.None | SDKSupportedCapabilities.PartitionMerge;
|
||||||
@@ -157,7 +211,7 @@ export function client(): Cosmos.CosmosClient {
|
|||||||
|
|
||||||
const options: Cosmos.CosmosClientOptions = {
|
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
|
endpoint: endpoint() || "https://cosmos.azure.com", // CosmosClient gets upset if we pass a bad URL. This should never actually get called
|
||||||
key: userContext.masterKey,
|
key: userContext.dataPlaneRbacEnabled ? "" : userContext.masterKey,
|
||||||
tokenProvider,
|
tokenProvider,
|
||||||
userAgentSuffix: "Azure Portal",
|
userAgentSuffix: "Azure Portal",
|
||||||
defaultHeaders: _defaultHeaders,
|
defaultHeaders: _defaultHeaders,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { PortalBackendEndpoints } from "Common/Constants";
|
||||||
|
import { updateConfigContext } from "ConfigContext";
|
||||||
import * as EnvironmentUtility from "./EnvironmentUtility";
|
import * as EnvironmentUtility from "./EnvironmentUtility";
|
||||||
|
|
||||||
describe("Environment Utility Test", () => {
|
describe("Environment Utility Test", () => {
|
||||||
@@ -11,4 +13,18 @@ describe("Environment Utility Test", () => {
|
|||||||
const expectedResult = "test/";
|
const expectedResult = "test/";
|
||||||
expect(EnvironmentUtility.normalizeArmEndpoint(uri)).toEqual(expectedResult);
|
expect(EnvironmentUtility.normalizeArmEndpoint(uri)).toEqual(expectedResult);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Detect environment is Mpac", () => {
|
||||||
|
updateConfigContext({
|
||||||
|
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Mpac,
|
||||||
|
});
|
||||||
|
expect(EnvironmentUtility.getEnvironment()).toBe(EnvironmentUtility.Environment.Mpac);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Detect environment is Development", () => {
|
||||||
|
updateConfigContext({
|
||||||
|
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Development,
|
||||||
|
});
|
||||||
|
expect(EnvironmentUtility.getEnvironment()).toBe(EnvironmentUtility.Environment.Development);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,29 @@
|
|||||||
|
import { PortalBackendEndpoints } from "Common/Constants";
|
||||||
|
import { configContext } from "ConfigContext";
|
||||||
|
|
||||||
export function normalizeArmEndpoint(uri: string): string {
|
export function normalizeArmEndpoint(uri: string): string {
|
||||||
if (uri && uri.slice(-1) !== "/") {
|
if (uri && uri.slice(-1) !== "/") {
|
||||||
return `${uri}/`;
|
return `${uri}/`;
|
||||||
}
|
}
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum Environment {
|
||||||
|
Development = "Development",
|
||||||
|
Mpac = "MPAC",
|
||||||
|
Prod = "Prod",
|
||||||
|
Fairfax = "Fairfax",
|
||||||
|
Mooncake = "Mooncake",
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getEnvironment = (): Environment => {
|
||||||
|
const environmentMap: { [key: string]: Environment } = {
|
||||||
|
[PortalBackendEndpoints.Development]: Environment.Development,
|
||||||
|
[PortalBackendEndpoints.Mpac]: Environment.Mpac,
|
||||||
|
[PortalBackendEndpoints.Prod]: Environment.Prod,
|
||||||
|
[PortalBackendEndpoints.Fairfax]: Environment.Fairfax,
|
||||||
|
[PortalBackendEndpoints.Mooncake]: Environment.Mooncake,
|
||||||
|
};
|
||||||
|
|
||||||
|
return environmentMap[configContext.PORTAL_BACKEND_ENDPOINT];
|
||||||
|
};
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ const replaceKnownError = (errorMessage: string): string => {
|
|||||||
return "Partition key paths must contain only valid characters and not contain a trailing slash or wildcard character.";
|
return "Partition key paths must contain only valid characters and not contain a trailing slash or wildcard character.";
|
||||||
} else if (
|
} else if (
|
||||||
errorMessage?.indexOf("The user aborted a request") >= 0 ||
|
errorMessage?.indexOf("The user aborted a request") >= 0 ||
|
||||||
errorMessage?.indexOf("The operation was aborted") >= 0
|
errorMessage?.indexOf("The operation was aborted") >= 0 ||
|
||||||
|
errorMessage === "signal is aborted without reason"
|
||||||
) {
|
) {
|
||||||
return "User aborted query.";
|
return "User aborted query.";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -550,6 +550,49 @@ export function deleteDocument_ToBeDeprecated(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function deleteDocuments(
|
||||||
|
databaseId: string,
|
||||||
|
collection: Collection,
|
||||||
|
documentIds: DocumentId[],
|
||||||
|
): Promise<{
|
||||||
|
deletedCount: number;
|
||||||
|
isAcknowledged: boolean;
|
||||||
|
}> {
|
||||||
|
const { databaseAccount } = userContext;
|
||||||
|
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
||||||
|
|
||||||
|
const rids = documentIds.map((documentId) => documentId.id());
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
databaseID: databaseId,
|
||||||
|
collectionID: collection.id(),
|
||||||
|
resourceUrl: `${resourceEndpoint}`,
|
||||||
|
resourceIDs: rids,
|
||||||
|
subscriptionID: userContext.subscriptionId,
|
||||||
|
resourceGroup: userContext.resourceGroup,
|
||||||
|
databaseAccountName: databaseAccount.name,
|
||||||
|
};
|
||||||
|
const endpoint = getFeatureEndpointOrDefault("bulkdelete");
|
||||||
|
|
||||||
|
return window
|
||||||
|
.fetch(`${endpoint}/bulkdelete`, {
|
||||||
|
method: "DELETE",
|
||||||
|
body: JSON.stringify(params),
|
||||||
|
headers: {
|
||||||
|
...defaultHeaders,
|
||||||
|
...authHeaders(),
|
||||||
|
[HttpHeaders.contentType]: ContentType.applicationJson,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(async (response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
const result = await response.json();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return await errorHandling(response, "deleting documents", params);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function createMongoCollectionWithProxy(
|
export function createMongoCollectionWithProxy(
|
||||||
params: DataModels.CreateCollectionParams,
|
params: DataModels.CreateCollectionParams,
|
||||||
): Promise<DataModels.Collection> {
|
): Promise<DataModels.Collection> {
|
||||||
@@ -678,22 +721,21 @@ export function useMongoProxyEndpoint(api: string): boolean {
|
|||||||
MongoProxyEndpoints.Mpac,
|
MongoProxyEndpoints.Mpac,
|
||||||
MongoProxyEndpoints.Prod,
|
MongoProxyEndpoints.Prod,
|
||||||
MongoProxyEndpoints.Fairfax,
|
MongoProxyEndpoints.Fairfax,
|
||||||
|
MongoProxyEndpoints.Mooncake,
|
||||||
];
|
];
|
||||||
let canAccessMongoProxy: boolean = userContext.databaseAccount.properties.publicNetworkAccess === "Enabled";
|
|
||||||
if (
|
|
||||||
configContext.MONGO_PROXY_ENDPOINT !== MongoProxyEndpoints.Local &&
|
|
||||||
userContext.databaseAccount.properties.ipRules?.length > 0
|
|
||||||
) {
|
|
||||||
canAccessMongoProxy = canAccessMongoProxy && configContext.MONGO_PROXY_OUTBOUND_IPS_ALLOWLISTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
canAccessMongoProxy &&
|
|
||||||
configContext.NEW_MONGO_APIS?.includes(api) &&
|
configContext.NEW_MONGO_APIS?.includes(api) &&
|
||||||
activeMongoProxyEndpoints.includes(configContext.MONGO_PROXY_ENDPOINT)
|
activeMongoProxyEndpoints.includes(configContext.MONGO_PROXY_ENDPOINT)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ThrottlingError extends Error {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: This function throws most of the time except on Forbidden which is a bit strange
|
// TODO: This function throws most of the time except on Forbidden which is a bit strange
|
||||||
// It causes problems for TypeScript understanding the types
|
// It causes problems for TypeScript understanding the types
|
||||||
async function errorHandling(response: Response, action: string, params: unknown): Promise<void> {
|
async function errorHandling(response: Response, action: string, params: unknown): Promise<void> {
|
||||||
@@ -703,6 +745,14 @@ async function errorHandling(response: Response, action: string, params: unknown
|
|||||||
if (response.status === HttpStatusCodes.Forbidden) {
|
if (response.status === HttpStatusCodes.Forbidden) {
|
||||||
sendMessage({ type: MessageTypes.ForbiddenError, reason: errorMessage });
|
sendMessage({ type: MessageTypes.ForbiddenError, reason: errorMessage });
|
||||||
return;
|
return;
|
||||||
|
} else if (
|
||||||
|
response.status === HttpStatusCodes.BadRequest &&
|
||||||
|
errorMessage.includes("Error=16500") &&
|
||||||
|
errorMessage.includes("RetryAfterMs=")
|
||||||
|
) {
|
||||||
|
// If throttling is happening, Cosmos DB will return a 400 with a body of:
|
||||||
|
// A write operation resulted in an error. Error=16500, RetryAfterMs=4, Details='Batch write error.
|
||||||
|
throw new ThrottlingError(errorMessage);
|
||||||
}
|
}
|
||||||
throw new Error(errorMessage);
|
throw new Error(errorMessage);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
import { configContext, Platform } from "../ConfigContext";
|
|
||||||
import * as DataModels from "../Contracts/DataModels";
|
|
||||||
import * as ViewModels from "../Contracts/ViewModels";
|
|
||||||
import { userContext } from "../UserContext";
|
|
||||||
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
|
|
||||||
|
|
||||||
const notificationsPath = () => {
|
|
||||||
switch (configContext.platform) {
|
|
||||||
case Platform.Hosted:
|
|
||||||
return "/api/guest/notifications";
|
|
||||||
case Platform.Portal:
|
|
||||||
return "/api/notifications";
|
|
||||||
default:
|
|
||||||
throw new Error(`Unknown platform: ${configContext.platform}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchPortalNotifications = async (): Promise<DataModels.Notification[]> => {
|
|
||||||
if (configContext.platform === Platform.Emulator || configContext.platform === Platform.Hosted) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const { databaseAccount, resourceGroup, subscriptionId } = userContext;
|
|
||||||
const url = `${configContext.BACKEND_ENDPOINT}${notificationsPath()}?accountName=${
|
|
||||||
databaseAccount.name
|
|
||||||
}&subscriptionId=${subscriptionId}&resourceGroup=${resourceGroup}`;
|
|
||||||
const authorizationHeader: ViewModels.AuthorizationTokenHeaderMetadata = getAuthorizationHeader();
|
|
||||||
const headers = { [authorizationHeader.header]: authorizationHeader.token };
|
|
||||||
|
|
||||||
const response = await window.fetch(url, {
|
|
||||||
headers,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(await response.text());
|
|
||||||
}
|
|
||||||
|
|
||||||
return (await response.json()) as DataModels.Notification[];
|
|
||||||
};
|
|
||||||
94
src/Common/QueryError.test.ts
Normal file
94
src/Common/QueryError.test.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import QueryError, { QueryErrorLocation, QueryErrorSeverity } from "Common/QueryError";
|
||||||
|
|
||||||
|
describe("QueryError.tryParse", () => {
|
||||||
|
const testErrorLocationResolver = ({ start, end }: { start: number; end: number }) =>
|
||||||
|
new QueryErrorLocation(
|
||||||
|
{ offset: start, lineNumber: start, column: start },
|
||||||
|
{ offset: end, lineNumber: end, column: end },
|
||||||
|
);
|
||||||
|
|
||||||
|
it("handles a string error", () => {
|
||||||
|
const error = "error";
|
||||||
|
const result = QueryError.tryParse(error, testErrorLocationResolver);
|
||||||
|
expect(result).toEqual([new QueryError("error", QueryErrorSeverity.Error)]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles an error object", () => {
|
||||||
|
const error = {
|
||||||
|
message: "error",
|
||||||
|
severity: "Warning",
|
||||||
|
location: { start: 0, end: 1 },
|
||||||
|
code: "code",
|
||||||
|
};
|
||||||
|
const result = QueryError.tryParse(error, testErrorLocationResolver);
|
||||||
|
expect(result).toEqual([
|
||||||
|
new QueryError(
|
||||||
|
"error",
|
||||||
|
QueryErrorSeverity.Warning,
|
||||||
|
"code",
|
||||||
|
new QueryErrorLocation({ offset: 0, lineNumber: 0, column: 0 }, { offset: 1, lineNumber: 1, column: 1 }),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles a JSON message without syntax errors", () => {
|
||||||
|
const innerError = {
|
||||||
|
code: "BadRequest",
|
||||||
|
message: "Your query is bad, and you should feel bad",
|
||||||
|
};
|
||||||
|
const message = JSON.stringify(innerError);
|
||||||
|
const outerError = {
|
||||||
|
code: "BadRequest",
|
||||||
|
message,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = QueryError.tryParse(outerError, testErrorLocationResolver);
|
||||||
|
expect(result).toEqual([
|
||||||
|
new QueryError("Your query is bad, and you should feel bad", QueryErrorSeverity.Error, "BadRequest"),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Imitate the value coming from the backend, which has the syntax errors serialized as JSON in the message.
|
||||||
|
it("handles single-nested error", () => {
|
||||||
|
const errors = [
|
||||||
|
{
|
||||||
|
message: "error1",
|
||||||
|
severity: "Warning",
|
||||||
|
location: { start: 0, end: 1 },
|
||||||
|
code: "code1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: "error2",
|
||||||
|
severity: "Error",
|
||||||
|
location: { start: 2, end: 3 },
|
||||||
|
code: "code2",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const innerError = {
|
||||||
|
code: "BadRequest",
|
||||||
|
message: "Your query is bad, and you should feel bad",
|
||||||
|
errors,
|
||||||
|
};
|
||||||
|
const message = JSON.stringify(innerError);
|
||||||
|
const outerError = {
|
||||||
|
code: "BadRequest",
|
||||||
|
message,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = QueryError.tryParse(outerError, testErrorLocationResolver);
|
||||||
|
expect(result).toEqual([
|
||||||
|
new QueryError(
|
||||||
|
"error1",
|
||||||
|
QueryErrorSeverity.Warning,
|
||||||
|
"code1",
|
||||||
|
new QueryErrorLocation({ offset: 0, lineNumber: 0, column: 0 }, { offset: 1, lineNumber: 1, column: 1 }),
|
||||||
|
),
|
||||||
|
new QueryError(
|
||||||
|
"error2",
|
||||||
|
QueryErrorSeverity.Error,
|
||||||
|
"code2",
|
||||||
|
new QueryErrorLocation({ offset: 2, lineNumber: 2, column: 2 }, { offset: 3, lineNumber: 3, column: 3 }),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
247
src/Common/QueryError.ts
Normal file
247
src/Common/QueryError.ts
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
import { monaco } from "Explorer/LazyMonaco";
|
||||||
|
import { getRUThreshold, ruThresholdEnabled } from "Shared/StorageUtility";
|
||||||
|
|
||||||
|
export enum QueryErrorSeverity {
|
||||||
|
Error = "Error",
|
||||||
|
Warning = "Warning",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class QueryErrorLocation {
|
||||||
|
constructor(
|
||||||
|
public start: ErrorPosition,
|
||||||
|
public end: ErrorPosition,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ErrorPosition {
|
||||||
|
constructor(
|
||||||
|
public offset: number,
|
||||||
|
public lineNumber?: number,
|
||||||
|
public column?: number,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maps severities to numbers for sorting.
|
||||||
|
const severityMap: Record<QueryErrorSeverity, number> = {
|
||||||
|
Error: 1,
|
||||||
|
Warning: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function compareSeverity(left: QueryErrorSeverity, right: QueryErrorSeverity): number {
|
||||||
|
return severityMap[left] - severityMap[right];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMonacoErrorLocationResolver(
|
||||||
|
editor: monaco.editor.IStandaloneCodeEditor,
|
||||||
|
selection?: monaco.Selection,
|
||||||
|
): (location: { start: number; end: number }) => QueryErrorLocation {
|
||||||
|
return ({ start, end }) => {
|
||||||
|
// Start and end are absolute offsets (character index) in the document.
|
||||||
|
// But we need line numbers and columns for the monaco editor.
|
||||||
|
// To get those, we use the editor's model to convert the offsets to positions.
|
||||||
|
const model = editor.getModel();
|
||||||
|
if (!model) {
|
||||||
|
return new QueryErrorLocation(new ErrorPosition(start), new ErrorPosition(end));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the error was found in a selection, adjust the start and end positions to be relative to the document.
|
||||||
|
if (selection) {
|
||||||
|
// Get the character index of the start of the selection.
|
||||||
|
const selectionStartOffset = model.getOffsetAt(selection.getStartPosition());
|
||||||
|
|
||||||
|
// Adjust the start and end positions to be relative to the document.
|
||||||
|
start = selectionStartOffset + start;
|
||||||
|
end = selectionStartOffset + end;
|
||||||
|
|
||||||
|
// Now, when we resolve the positions, they will be relative to the document and appear in the correct location.
|
||||||
|
}
|
||||||
|
|
||||||
|
const startPos = model.getPositionAt(start);
|
||||||
|
const endPos = model.getPositionAt(end);
|
||||||
|
return new QueryErrorLocation(
|
||||||
|
new ErrorPosition(start, startPos.lineNumber, startPos.column),
|
||||||
|
new ErrorPosition(end, endPos.lineNumber, endPos.column),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createMonacoMarkersForQueryErrors = (errors: QueryError[]) => {
|
||||||
|
if (!errors) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors
|
||||||
|
.map((error): monaco.editor.IMarkerData => {
|
||||||
|
// Validate that we have what we need to make a marker
|
||||||
|
if (
|
||||||
|
error.location === undefined ||
|
||||||
|
error.location.start === undefined ||
|
||||||
|
error.location.end === undefined ||
|
||||||
|
error.location.start.lineNumber === undefined ||
|
||||||
|
error.location.end.lineNumber === undefined ||
|
||||||
|
error.location.start.column === undefined ||
|
||||||
|
error.location.end.column === undefined
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: error.message,
|
||||||
|
severity: error.getMonacoSeverity(),
|
||||||
|
startLineNumber: error.location.start.lineNumber,
|
||||||
|
startColumn: error.location.start.column,
|
||||||
|
endLineNumber: error.location.end.lineNumber,
|
||||||
|
endColumn: error.location.end.column,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter((marker) => !!marker);
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface ErrorEnrichment {
|
||||||
|
title?: string;
|
||||||
|
message: string;
|
||||||
|
learnMoreUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const REPLACEMENT_MESSAGES: Record<string, (original: string) => string> = {
|
||||||
|
OPERATION_RU_LIMIT_EXCEEDED: (original) => {
|
||||||
|
if (ruThresholdEnabled()) {
|
||||||
|
const threshold = getRUThreshold();
|
||||||
|
return `Query exceeded the Request Unit (RU) limit of ${threshold} RUs. You can change this limit in Data Explorer settings.`;
|
||||||
|
}
|
||||||
|
return original;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const HELP_LINKS: Record<string, string> = {
|
||||||
|
OPERATION_RU_LIMIT_EXCEEDED:
|
||||||
|
"https://learn.microsoft.com/en-us/azure/cosmos-db/data-explorer#configure-request-unit-threshold",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class QueryError {
|
||||||
|
message: string;
|
||||||
|
helpLink?: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
message: string,
|
||||||
|
public severity: QueryErrorSeverity,
|
||||||
|
public code?: string,
|
||||||
|
public location?: QueryErrorLocation,
|
||||||
|
helpLink?: string,
|
||||||
|
) {
|
||||||
|
// Automatically replace the message with a more Data Explorer-specific message if we have for this error code.
|
||||||
|
this.message = REPLACEMENT_MESSAGES[code] ? REPLACEMENT_MESSAGES[code](message) : message;
|
||||||
|
|
||||||
|
// Automatically set the help link if we have one for this error code.
|
||||||
|
this.helpLink = helpLink ?? HELP_LINKS[code];
|
||||||
|
}
|
||||||
|
|
||||||
|
getMonacoSeverity(): monaco.MarkerSeverity {
|
||||||
|
// It's very difficult to use the monaco.MarkerSeverity enum from here, so we'll just use the numbers directly.
|
||||||
|
// See: https://microsoft.github.io/monaco-editor/typedoc/enums/MarkerSeverity.html
|
||||||
|
switch (this.severity) {
|
||||||
|
case QueryErrorSeverity.Error:
|
||||||
|
return 8;
|
||||||
|
case QueryErrorSeverity.Warning:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
return 2; // Info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Attempts to parse a query error from a string or object.
|
||||||
|
*
|
||||||
|
* @param error The error to parse.
|
||||||
|
* @returns An array of query errors if the error could be parsed, or null otherwise.
|
||||||
|
*/
|
||||||
|
static tryParse(
|
||||||
|
error: unknown,
|
||||||
|
locationResolver?: (location: { start: number; end: number }) => QueryErrorLocation,
|
||||||
|
): QueryError[] {
|
||||||
|
locationResolver =
|
||||||
|
locationResolver ||
|
||||||
|
(({ start, end }) => new QueryErrorLocation(new ErrorPosition(start), new ErrorPosition(end)));
|
||||||
|
const errors = QueryError.tryParseObject(error, locationResolver);
|
||||||
|
if (errors !== null) {
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
const errorMessage = error as string;
|
||||||
|
|
||||||
|
// Map some well known messages to richer errors
|
||||||
|
const knownError = knownErrors[errorMessage];
|
||||||
|
if (knownError) {
|
||||||
|
return [knownError];
|
||||||
|
} else {
|
||||||
|
return [new QueryError(errorMessage, QueryErrorSeverity.Error)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static read(
|
||||||
|
error: unknown,
|
||||||
|
locationResolver: (location: { start: number; end: number }) => QueryErrorLocation,
|
||||||
|
): QueryError | null {
|
||||||
|
if (typeof error !== "object" || error === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = "message" in error && typeof error.message === "string" ? error.message : undefined;
|
||||||
|
if (!message) {
|
||||||
|
return null; // Invalid error (no message).
|
||||||
|
}
|
||||||
|
|
||||||
|
const severity =
|
||||||
|
"severity" in error && typeof error.severity === "string"
|
||||||
|
? (error.severity as QueryErrorSeverity)
|
||||||
|
: QueryErrorSeverity.Error;
|
||||||
|
const location =
|
||||||
|
"location" in error && typeof error.location === "object"
|
||||||
|
? locationResolver(error.location as { start: number; end: number })
|
||||||
|
: undefined;
|
||||||
|
const code = "code" in error && typeof error.code === "string" ? error.code : undefined;
|
||||||
|
return new QueryError(message, severity, code, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static tryParseObject(
|
||||||
|
error: unknown,
|
||||||
|
locationResolver: (location: { start: number; end: number }) => QueryErrorLocation,
|
||||||
|
): QueryError[] | null {
|
||||||
|
let message: string | undefined;
|
||||||
|
if (typeof error === "object" && "message" in error && typeof error.message === "string") {
|
||||||
|
message = error.message;
|
||||||
|
} else {
|
||||||
|
// Unsupported error format.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign to a new variable because of a TypeScript flow typing quirk, see below.
|
||||||
|
if (message.startsWith("Message: ")) {
|
||||||
|
// Reassigning this to 'error' restores the original type of 'error', which is 'unknown'.
|
||||||
|
// So we use a separate variable to avoid this.
|
||||||
|
message = message.substring("Message: ".length);
|
||||||
|
}
|
||||||
|
|
||||||
|
const lines = message.split("\n");
|
||||||
|
message = lines[0].trim();
|
||||||
|
|
||||||
|
let parsed: unknown;
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(message);
|
||||||
|
} catch (e) {
|
||||||
|
// The message doesn't contain a nested error.
|
||||||
|
return [QueryError.read(error, locationResolver)];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof parsed === "object") {
|
||||||
|
if ("errors" in parsed && Array.isArray(parsed.errors)) {
|
||||||
|
return parsed.errors.map((e) => QueryError.read(e, locationResolver)).filter((e) => e !== null);
|
||||||
|
}
|
||||||
|
return [QueryError.read(parsed, locationResolver)];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const knownErrors: Record<string, QueryError> = {
|
||||||
|
"User aborted query.": new QueryError("User aborted query.", QueryErrorSeverity.Warning),
|
||||||
|
};
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
import React, { FunctionComponent, MutableRefObject, useEffect, useRef } from "react";
|
|
||||||
import arrowLeftImg from "../../images/imgarrowlefticon.svg";
|
|
||||||
import refreshImg from "../../images/refresh-cosmos.svg";
|
|
||||||
import Explorer from "../Explorer/Explorer";
|
|
||||||
import { ResourceTree } from "../Explorer/Tree/ResourceTree";
|
|
||||||
import { userContext } from "../UserContext";
|
|
||||||
import { getApiShortDisplayName } from "../Utils/APITypeUtils";
|
|
||||||
import { NormalizedEventKey } from "./Constants";
|
|
||||||
|
|
||||||
export interface ResourceTreeContainerProps {
|
|
||||||
toggleLeftPaneExpanded: () => void;
|
|
||||||
isLeftPaneExpanded: boolean;
|
|
||||||
container: Explorer;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ResourceTreeContainer: FunctionComponent<ResourceTreeContainerProps> = ({
|
|
||||||
toggleLeftPaneExpanded,
|
|
||||||
isLeftPaneExpanded,
|
|
||||||
container,
|
|
||||||
}: ResourceTreeContainerProps): JSX.Element => {
|
|
||||||
const focusButton = useRef<HTMLLIElement>() as MutableRefObject<HTMLLIElement>;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isLeftPaneExpanded) {
|
|
||||||
if (focusButton.current) {
|
|
||||||
focusButton.current.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const onKeyPressToggleLeftPaneExpanded = (event: React.KeyboardEvent) => {
|
|
||||||
if (event.key === NormalizedEventKey.Space || event.key === NormalizedEventKey.Enter) {
|
|
||||||
toggleLeftPaneExpanded();
|
|
||||||
event.stopPropagation();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<div id="main" className={isLeftPaneExpanded ? "main" : "hiddenMain"}>
|
|
||||||
{/* Collections Window - - Start */}
|
|
||||||
<div id="mainslide" className="flexContainer">
|
|
||||||
{/* Collections Window Title/Command Bar - Start */}
|
|
||||||
<div className="collectiontitle">
|
|
||||||
<div className="coltitle">
|
|
||||||
<span className="titlepadcol">{getApiShortDisplayName()}</span>
|
|
||||||
<div className="float-right">
|
|
||||||
<span
|
|
||||||
className="padimgcolrefresh"
|
|
||||||
data-test="refreshTree"
|
|
||||||
role="button"
|
|
||||||
data-bind="click: onRefreshResourcesClick, clickBubble: false, event: { keypress: onRefreshDatabasesKeyPress }"
|
|
||||||
tabIndex={0}
|
|
||||||
aria-label={getApiShortDisplayName() + `Refresh tree`}
|
|
||||||
title="Refresh tree"
|
|
||||||
>
|
|
||||||
<img className="refreshcol" src={refreshImg} alt="Refresh Tree" />
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
className="padimgcolrefresh1"
|
|
||||||
id="expandToggleLeftPaneButton"
|
|
||||||
role="button"
|
|
||||||
onClick={toggleLeftPaneExpanded}
|
|
||||||
onKeyPress={onKeyPressToggleLeftPaneExpanded}
|
|
||||||
tabIndex={0}
|
|
||||||
aria-label={getApiShortDisplayName() + `Collapse Tree`}
|
|
||||||
title="Collapse Tree"
|
|
||||||
ref={focusButton}
|
|
||||||
>
|
|
||||||
<img className="refreshcol1" src={arrowLeftImg} alt="Hide" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{userContext.features.enableKoResourceTree ? (
|
|
||||||
<div style={{ overflowY: "auto" }} data-bind="react:resourceTree" />
|
|
||||||
) : (
|
|
||||||
<ResourceTree container={container} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{/* Collections Window - End */}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -3,11 +3,12 @@ import * as React from "react";
|
|||||||
|
|
||||||
export interface TooltipProps {
|
export interface TooltipProps {
|
||||||
children: string;
|
children: string;
|
||||||
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InfoTooltip: React.FunctionComponent<TooltipProps> = ({ children }: TooltipProps) => {
|
export const InfoTooltip: React.FunctionComponent<TooltipProps> = ({ children, className }: TooltipProps) => {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span className={className}>
|
||||||
<TooltipHost content={children}>
|
<TooltipHost content={children}>
|
||||||
<Icon iconName="Info" ariaLabel={children} className="panelInfoIcon" tabIndex={0} />
|
<Icon iconName="Info" ariaLabel={children} className="panelInfoIcon" tabIndex={0} />
|
||||||
</TooltipHost>
|
</TooltipHost>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`requestPlugin Emulator builds a url for emulator proxy via webpack 1`] = `
|
exports[`requestPlugin Emulator builds a url for emulator proxy via webpack 1`] = `
|
||||||
Object {
|
{
|
||||||
"endpoint": "http://localhost/proxy",
|
"endpoint": "http://localhost/proxy",
|
||||||
"headers": Object {
|
"headers": {
|
||||||
"x-ms-proxy-target": "http://localhost",
|
"x-ms-proxy-target": "http://localhost",
|
||||||
},
|
},
|
||||||
"path": "/dbs/foo",
|
"path": "/dbs/foo",
|
||||||
@@ -11,9 +11,9 @@ Object {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`requestPlugin Hosted builds a proxy URL in development 1`] = `
|
exports[`requestPlugin Hosted builds a proxy URL in development 1`] = `
|
||||||
Object {
|
{
|
||||||
"endpoint": "http://localhost/proxy",
|
"endpoint": "http://localhost/proxy",
|
||||||
"headers": Object {
|
"headers": {
|
||||||
"x-ms-proxy-target": "baz",
|
"x-ms-proxy-target": "baz",
|
||||||
},
|
},
|
||||||
"path": "/dbs/foo",
|
"path": "/dbs/foo",
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`nextPage returns results for the next page 1`] = `
|
exports[`nextPage returns results for the next page 1`] = `
|
||||||
Object {
|
{
|
||||||
"activityId": "foo",
|
"activityId": "foo",
|
||||||
"documents": Array [],
|
"documents": [],
|
||||||
"firstItemIndex": 11,
|
"firstItemIndex": 11,
|
||||||
"hasMoreResults": false,
|
"hasMoreResults": false,
|
||||||
"headers": Object {},
|
"headers": {},
|
||||||
"itemCount": 0,
|
"itemCount": 0,
|
||||||
"lastItemIndex": 10,
|
"lastItemIndex": 10,
|
||||||
"requestCharge": 1,
|
"requestCharge": 1,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`getCommonQueryOptions builds the correct default options objects 1`] = `
|
exports[`getCommonQueryOptions builds the correct default options objects 1`] = `
|
||||||
Object {
|
{
|
||||||
"disableNonStreamingOrderByQuery": true,
|
"disableNonStreamingOrderByQuery": true,
|
||||||
"enableScanInQuery": true,
|
"enableScanInQuery": true,
|
||||||
"forceQueryPlan": true,
|
"forceQueryPlan": true,
|
||||||
@@ -12,7 +12,7 @@ Object {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`getCommonQueryOptions reads from localStorage 1`] = `
|
exports[`getCommonQueryOptions reads from localStorage 1`] = `
|
||||||
Object {
|
{
|
||||||
"disableNonStreamingOrderByQuery": true,
|
"disableNonStreamingOrderByQuery": true,
|
||||||
"enableScanInQuery": true,
|
"enableScanInQuery": true,
|
||||||
"forceQueryPlan": true,
|
"forceQueryPlan": true,
|
||||||
|
|||||||
@@ -269,6 +269,7 @@ const createCollectionWithSDK = async (params: DataModels.CreateCollectionParams
|
|||||||
indexingPolicy: params.indexingPolicy || undefined,
|
indexingPolicy: params.indexingPolicy || undefined,
|
||||||
uniqueKeyPolicy: params.uniqueKeyPolicy || undefined,
|
uniqueKeyPolicy: params.uniqueKeyPolicy || undefined,
|
||||||
analyticalStorageTtl: params.analyticalStorageTtl,
|
analyticalStorageTtl: params.analyticalStorageTtl,
|
||||||
|
vectorEmbeddingPolicy: params.vectorEmbeddingPolicy,
|
||||||
} as ContainerRequest; // TODO: remove cast when https://github.com/Azure/azure-cosmos-js/issues/423 is fixed
|
} as ContainerRequest; // TODO: remove cast when https://github.com/Azure/azure-cosmos-js/issues/423 is fixed
|
||||||
const collectionOptions: RequestOptions = {};
|
const collectionOptions: RequestOptions = {};
|
||||||
const createDatabaseBody: DatabaseRequest = { id: params.databaseId };
|
const createDatabaseBody: DatabaseRequest = { id: params.databaseId };
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import * as DataModels from "../../Contracts/DataModels";
|
|||||||
import { useDatabases } from "../../Explorer/useDatabases";
|
import { useDatabases } from "../../Explorer/useDatabases";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
import { getDatabaseName } from "../../Utils/APITypeUtils";
|
import { getDatabaseName } from "../../Utils/APITypeUtils";
|
||||||
|
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
import { createUpdateCassandraKeyspace } from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
|
import { createUpdateCassandraKeyspace } from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
|
||||||
import { createUpdateGremlinDatabase } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
|
import { createUpdateGremlinDatabase } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
|
||||||
import { createUpdateMongoDBDatabase } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
|
import { createUpdateMongoDBDatabase } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
|
||||||
@@ -15,7 +16,6 @@ import {
|
|||||||
MongoDBDatabaseCreateUpdateParameters,
|
MongoDBDatabaseCreateUpdateParameters,
|
||||||
SqlDatabaseCreateUpdateParameters,
|
SqlDatabaseCreateUpdateParameters,
|
||||||
} from "../../Utils/arm/generatedClients/cosmos/types";
|
} from "../../Utils/arm/generatedClients/cosmos/types";
|
||||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
|
||||||
import { client } from "../CosmosClient";
|
import { client } from "../CosmosClient";
|
||||||
import { handleError } from "../ErrorHandlingUtils";
|
import { handleError } from "../ErrorHandlingUtils";
|
||||||
|
|
||||||
@@ -152,8 +152,18 @@ async function createDatabaseWithSDK(params: DataModels.CreateDatabaseParams): P
|
|||||||
createBody.throughput = params.offerThroughput;
|
createBody.throughput = params.offerThroughput;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let response: DatabaseResponse;
|
||||||
const response: DatabaseResponse = await client().databases.create(createBody);
|
try {
|
||||||
|
response = await client().databases.create(createBody);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.message.includes("Shared throughput database creation is not supported for serverless accounts")) {
|
||||||
|
createBody.maxThroughput = undefined;
|
||||||
|
createBody.throughput = undefined;
|
||||||
|
response = await client().databases.create(createBody);
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
return response.resource;
|
return response.resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,14 +26,23 @@ export const deleteDocument = async (collection: CollectionBase, documentId: Doc
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface IBulkDeleteResult {
|
||||||
|
documentId: DocumentId;
|
||||||
|
requestCharge: number;
|
||||||
|
statusCode: number;
|
||||||
|
retryAfterMilliseconds?: number;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bulk delete documents
|
* Bulk delete documents
|
||||||
* @param collection
|
* @param collection
|
||||||
* @param documentId
|
* @param documentId
|
||||||
* @returns array of ids that were successfully deleted
|
* @returns array of results and status codes
|
||||||
*/
|
*/
|
||||||
export const deleteDocuments = async (collection: CollectionBase, documentIds: DocumentId[]): Promise<DocumentId[]> => {
|
export const deleteDocuments = async (
|
||||||
const nbDocuments = documentIds.length;
|
collection: CollectionBase,
|
||||||
|
documentIds: DocumentId[],
|
||||||
|
): Promise<IBulkDeleteResult[]> => {
|
||||||
const clearMessage = logConsoleProgress(`Deleting ${documentIds.length} ${getEntityName(true)}`);
|
const clearMessage = logConsoleProgress(`Deleting ${documentIds.length} ${getEntityName(true)}`);
|
||||||
try {
|
try {
|
||||||
const v2Container = await client().database(collection.databaseId).container(collection.id());
|
const v2Container = await client().database(collection.databaseId).container(collection.id());
|
||||||
@@ -56,18 +65,17 @@ export const deleteDocuments = async (collection: CollectionBase, documentIds: D
|
|||||||
operationType: BulkOperationType.Delete,
|
operationType: BulkOperationType.Delete,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const promise = v2Container.items.bulk(operations).then((bulkResult) => {
|
const promise = v2Container.items.bulk(operations).then((bulkResults) => {
|
||||||
return documentIdsChunk.filter((_, index) => bulkResult[index].statusCode === 204);
|
return bulkResults.map((bulkResult, index) => {
|
||||||
|
const documentId = documentIdsChunk[index];
|
||||||
|
return { ...bulkResult, documentId };
|
||||||
|
});
|
||||||
});
|
});
|
||||||
promiseArray.push(promise);
|
promiseArray.push(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
const allResult = await Promise.all(promiseArray);
|
const allResult = await Promise.all(promiseArray);
|
||||||
const flatAllResult = Array.prototype.concat.apply([], allResult);
|
const flatAllResult = Array.prototype.concat.apply([], allResult);
|
||||||
logConsoleInfo(
|
|
||||||
`Successfully deleted ${getEntityName(flatAllResult.length > 1)}: ${flatAllResult.length} out of ${nbDocuments}`,
|
|
||||||
);
|
|
||||||
// TODO: handle case result.length != nbDocuments
|
|
||||||
return flatAllResult;
|
return flatAllResult;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(
|
handleError(
|
||||||
|
|||||||
@@ -49,14 +49,12 @@ export interface ConfigContext {
|
|||||||
ARCADIA_ENDPOINT: string;
|
ARCADIA_ENDPOINT: string;
|
||||||
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: string;
|
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: string;
|
||||||
BACKEND_ENDPOINT?: string;
|
BACKEND_ENDPOINT?: string;
|
||||||
PORTAL_BACKEND_ENDPOINT?: string;
|
PORTAL_BACKEND_ENDPOINT: string;
|
||||||
NEW_BACKEND_APIS?: BackendApi[];
|
NEW_BACKEND_APIS?: BackendApi[];
|
||||||
MONGO_BACKEND_ENDPOINT?: string;
|
MONGO_BACKEND_ENDPOINT?: string;
|
||||||
MONGO_PROXY_ENDPOINT?: string;
|
MONGO_PROXY_ENDPOINT: string;
|
||||||
MONGO_PROXY_OUTBOUND_IPS_ALLOWLISTED?: boolean;
|
|
||||||
NEW_MONGO_APIS?: string[];
|
NEW_MONGO_APIS?: string[];
|
||||||
CASSANDRA_PROXY_ENDPOINT?: string;
|
CASSANDRA_PROXY_ENDPOINT: string;
|
||||||
CASSANDRA_PROXY_OUTBOUND_IPS_ALLOWLISTED: boolean;
|
|
||||||
NEW_CASSANDRA_APIS?: string[];
|
NEW_CASSANDRA_APIS?: string[];
|
||||||
PROXY_PATH?: string;
|
PROXY_PATH?: string;
|
||||||
JUNO_ENDPOINT: string;
|
JUNO_ENDPOINT: string;
|
||||||
@@ -87,7 +85,7 @@ let configContext: Readonly<ConfigContext> = {
|
|||||||
`^https:\\/\\/.*\\.analysis-df\\.net$`,
|
`^https:\\/\\/.*\\.analysis-df\\.net$`,
|
||||||
`^https:\\/\\/.*\\.analysis-df\\.windows\\.net$`,
|
`^https:\\/\\/.*\\.analysis-df\\.windows\\.net$`,
|
||||||
`^https:\\/\\/.*\\.azure-test\\.net$`,
|
`^https:\\/\\/.*\\.azure-test\\.net$`,
|
||||||
`^https:\\/\\/cosmos-explorer-preview\\.azurewebsites\\.net`,
|
`^https:\\/\\/cosmos-explorer-preview\\.azurewebsites\\.net$`,
|
||||||
], // Webpack injects this at build time
|
], // Webpack injects this at build time
|
||||||
gitSha: process.env.GIT_SHA,
|
gitSha: process.env.GIT_SHA,
|
||||||
hostedExplorerURL: "https://cosmos.azure.com/",
|
hostedExplorerURL: "https://cosmos.azure.com/",
|
||||||
@@ -109,19 +107,18 @@ let configContext: Readonly<ConfigContext> = {
|
|||||||
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Prod,
|
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Prod,
|
||||||
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
||||||
NEW_MONGO_APIS: [
|
NEW_MONGO_APIS: [
|
||||||
// "resourcelist",
|
"resourcelist",
|
||||||
// "queryDocuments",
|
"queryDocuments",
|
||||||
// "createDocument",
|
"createDocument",
|
||||||
// "readDocument",
|
"readDocument",
|
||||||
// "updateDocument",
|
"updateDocument",
|
||||||
// "deleteDocument",
|
"deleteDocument",
|
||||||
// "createCollectionWithProxy",
|
"createCollectionWithProxy",
|
||||||
// "legacyMongoShell",
|
"legacyMongoShell",
|
||||||
|
// "bulkdelete",
|
||||||
],
|
],
|
||||||
MONGO_PROXY_OUTBOUND_IPS_ALLOWLISTED: false,
|
|
||||||
CASSANDRA_PROXY_ENDPOINT: CassandraProxyEndpoints.Prod,
|
CASSANDRA_PROXY_ENDPOINT: CassandraProxyEndpoints.Prod,
|
||||||
NEW_CASSANDRA_APIS: ["postQuery", "createOrDelete", "getKeys", "getSchema"],
|
NEW_CASSANDRA_APIS: ["postQuery", "createOrDelete", "getKeys", "getSchema"],
|
||||||
CASSANDRA_PROXY_OUTBOUND_IPS_ALLOWLISTED: false,
|
|
||||||
isTerminalEnabled: false,
|
isTerminalEnabled: false,
|
||||||
isPhoenixEnabled: false,
|
isPhoenixEnabled: false,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -98,7 +98,6 @@ export interface Database extends TreeNode {
|
|||||||
openAddCollection(database: Database, event: MouseEvent): void;
|
openAddCollection(database: Database, event: MouseEvent): void;
|
||||||
onSettingsClick: () => void;
|
onSettingsClick: () => void;
|
||||||
loadOffer(): Promise<void>;
|
loadOffer(): Promise<void>;
|
||||||
getPendingThroughputSplitNotification(): Promise<DataModels.Notification>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CollectionBase extends TreeNode {
|
export interface CollectionBase extends TreeNode {
|
||||||
@@ -191,8 +190,6 @@ export interface Collection extends CollectionBase {
|
|||||||
onDragOver(source: Collection, event: { originalEvent: DragEvent }): void;
|
onDragOver(source: Collection, event: { originalEvent: DragEvent }): void;
|
||||||
onDrop(source: Collection, event: { originalEvent: DragEvent }): void;
|
onDrop(source: Collection, event: { originalEvent: DragEvent }): void;
|
||||||
uploadFiles(fileList: FileList): Promise<{ data: UploadDetailsRecord[] }>;
|
uploadFiles(fileList: FileList): Promise<{ data: UploadDetailsRecord[] }>;
|
||||||
|
|
||||||
getPendingThroughputSplitNotification(): Promise<DataModels.Notification>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -36,21 +36,21 @@ describe("The Heatmap Control", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should call _getChartSettings when drawHeatmap is invoked", () => {
|
it("should call _getChartSettings when drawHeatmap is invoked", () => {
|
||||||
const _getChartSettings = spyOn<any>(heatmap, "_getChartSettings");
|
const _getChartSettings = jest.spyOn(heatmap, "_getChartSettings");
|
||||||
heatmap.drawHeatmap();
|
heatmap.drawHeatmap();
|
||||||
expect(_getChartSettings.calls.any()).toBe(true);
|
expect(_getChartSettings).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should call _getLayoutSettings when drawHeatmap is invoked", () => {
|
it("should call _getLayoutSettings when drawHeatmap is invoked", () => {
|
||||||
const _getLayoutSettings = spyOn<any>(heatmap, "_getLayoutSettings");
|
const _getLayoutSettings = jest.spyOn(heatmap, "_getLayoutSettings");
|
||||||
heatmap.drawHeatmap();
|
heatmap.drawHeatmap();
|
||||||
expect(_getLayoutSettings.calls.any()).toBe(true);
|
expect(_getLayoutSettings).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should call _getChartDisplaySettings when drawHeatmap is invoked", () => {
|
it("should call _getChartDisplaySettings when drawHeatmap is invoked", () => {
|
||||||
const _getChartDisplaySettings = spyOn<any>(heatmap, "_getChartDisplaySettings");
|
const _getChartDisplaySettings = jest.spyOn(heatmap, "_getChartDisplaySettings");
|
||||||
heatmap.drawHeatmap();
|
heatmap.drawHeatmap();
|
||||||
expect(_getChartDisplaySettings.calls.any()).toBe(true);
|
expect(_getChartDisplaySettings).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("drawHeatmap should render a Heatmap inside the div element", () => {
|
it("drawHeatmap should render a Heatmap inside the div element", () => {
|
||||||
|
|||||||
@@ -96,7 +96,8 @@ export class Heatmap {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getChartSettings(): ChartSettings[] {
|
// public for testing purposes
|
||||||
|
public _getChartSettings(): ChartSettings[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
z: this._chartData.dataPoints,
|
z: this._chartData.dataPoints,
|
||||||
@@ -131,7 +132,8 @@ export class Heatmap {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getLayoutSettings(): LayoutSettings {
|
// public for testing purposes
|
||||||
|
public _getLayoutSettings(): LayoutSettings {
|
||||||
return {
|
return {
|
||||||
margin: {
|
margin: {
|
||||||
l: 40,
|
l: 40,
|
||||||
@@ -177,7 +179,8 @@ export class Heatmap {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getChartDisplaySettings(): DisplaySettings {
|
// public for testing purposes
|
||||||
|
public _getChartDisplaySettings(): DisplaySettings {
|
||||||
return {
|
return {
|
||||||
/* heatmap can be fully responsive however the min-height needed in that case is greater than the iframe portal height, hence explicit width + height have been set in _getLayoutSettings
|
/* heatmap can be fully responsive however the min-height needed in that case is greater than the iframe portal height, hence explicit width + height have been set in _getLayoutSettings
|
||||||
responsive: true,*/
|
responsive: true,*/
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ export interface DatabaseContextMenuButtonParams {
|
|||||||
* New resource tree (in ReactJS)
|
* New resource tree (in ReactJS)
|
||||||
*/
|
*/
|
||||||
export const createDatabaseContextMenu = (container: Explorer, databaseId: string): TreeNodeMenuItem[] => {
|
export const createDatabaseContextMenu = (container: Explorer, databaseId: string): TreeNodeMenuItem[] => {
|
||||||
|
if (configContext.platform === Platform.Fabric && userContext.fabricContext?.isReadOnly) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const items: TreeNodeMenuItem[] = [
|
const items: TreeNodeMenuItem[] = [
|
||||||
{
|
{
|
||||||
iconSrc: AddCollectionIcon,
|
iconSrc: AddCollectionIcon,
|
||||||
@@ -100,6 +104,16 @@ export const createCollectionContextMenuButton = (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (useNotebook.getState().isShellEnabled && userContext.apiType === "Cassandra") {
|
||||||
|
items.push({
|
||||||
|
iconSrc: HostedTerminalIcon,
|
||||||
|
onClick: () => {
|
||||||
|
container.openNotebookTerminal(ViewModels.TerminalKind.Cassandra);
|
||||||
|
},
|
||||||
|
label: "Open Cassandra Shell",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
configContext.platform !== Platform.Fabric &&
|
configContext.platform !== Platform.Fabric &&
|
||||||
(userContext.apiType === "SQL" || userContext.apiType === "Gremlin")
|
(userContext.apiType === "SQL" || userContext.apiType === "Gremlin")
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ exports[`CollapsibleSectionComponent renders 1`] = `
|
|||||||
role="button"
|
role="button"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,14 @@
|
|||||||
/**
|
/**
|
||||||
* React component for Command button component.
|
* React component for Command button component.
|
||||||
*/
|
*/
|
||||||
|
import Explorer from "Explorer/Explorer";
|
||||||
import { KeyboardAction } from "KeyboardShortcuts";
|
import { KeyboardAction } from "KeyboardShortcuts";
|
||||||
import * as React from "react";
|
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";
|
|
||||||
import * as StringUtils from "../../../Utils/StringUtils";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options for this component
|
* Options for this component
|
||||||
*/
|
*/
|
||||||
export interface CommandButtonComponentProps {
|
export interface CommandButtonComponentProps {
|
||||||
/**
|
|
||||||
* font icon name for the button
|
|
||||||
*/
|
|
||||||
iconName?: string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* image source for the button icon
|
* image source for the button icon
|
||||||
*/
|
*/
|
||||||
@@ -31,7 +22,7 @@ export interface CommandButtonComponentProps {
|
|||||||
/**
|
/**
|
||||||
* Click handler for command button click
|
* Click handler for command button click
|
||||||
*/
|
*/
|
||||||
onCommandClick?: (e: React.SyntheticEvent | KeyboardEvent) => void;
|
onCommandClick?: (e: React.SyntheticEvent | KeyboardEvent, container: Explorer) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Label for the button
|
* Label for the button
|
||||||
@@ -120,157 +111,3 @@ export interface CommandButtonComponentProps {
|
|||||||
*/
|
*/
|
||||||
keyboardAction?: KeyboardAction;
|
keyboardAction?: KeyboardAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommandButtonComponent extends React.Component<CommandButtonComponentProps> {
|
|
||||||
private dropdownElt: HTMLElement;
|
|
||||||
private expandButtonElt: HTMLElement;
|
|
||||||
|
|
||||||
public componentDidUpdate(): void {
|
|
||||||
if (!this.dropdownElt || !this.expandButtonElt) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$(this.dropdownElt).offset({ left: $(this.expandButtonElt).offset().left });
|
|
||||||
}
|
|
||||||
|
|
||||||
private onKeyPress(event: React.KeyboardEvent): boolean {
|
|
||||||
if (event.keyCode === KeyCodes.Space || event.keyCode === KeyCodes.Enter) {
|
|
||||||
this.commandClickCallback && this.commandClickCallback(event);
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private onLauncherKeyDown(event: React.KeyboardEvent<HTMLDivElement>): boolean {
|
|
||||||
if (event.keyCode === KeyCodes.DownArrow) {
|
|
||||||
$(this.dropdownElt).hide();
|
|
||||||
$(this.dropdownElt).show().focus();
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (event.keyCode === KeyCodes.UpArrow) {
|
|
||||||
$(this.dropdownElt).hide();
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getCommandButtonId(): string {
|
|
||||||
if (this.props.id) {
|
|
||||||
return this.props.id;
|
|
||||||
} else {
|
|
||||||
return `commandButton-${StringUtils.stripSpacesFromString(this.props.commandButtonLabel)}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static renderButton(options: CommandButtonComponentProps, key?: string): JSX.Element {
|
|
||||||
return <CommandButtonComponent key={key} {...options} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
private commandClickCallback(e: React.SyntheticEvent): void {
|
|
||||||
if (this.props.disabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Query component's parent, not document
|
|
||||||
const el = document.querySelector(".commandDropdownContainer") as HTMLElement;
|
|
||||||
if (el) {
|
|
||||||
el.style.display = "none";
|
|
||||||
}
|
|
||||||
this.props.onCommandClick(e);
|
|
||||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
|
||||||
commandButtonClicked: this.props.commandButtonLabel,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private renderChildren(): JSX.Element {
|
|
||||||
if (!this.props.children || this.props.children.length < 1) {
|
|
||||||
return <React.Fragment />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="commandExpand"
|
|
||||||
tabIndex={0}
|
|
||||||
ref={(ref: HTMLElement) => {
|
|
||||||
this.expandButtonElt = ref;
|
|
||||||
}}
|
|
||||||
onKeyDown={(e: React.KeyboardEvent<HTMLDivElement>) => this.onLauncherKeyDown(e)}
|
|
||||||
>
|
|
||||||
<div className="commandDropdownLauncher">
|
|
||||||
<span className="partialSplitter" />
|
|
||||||
<span className="expandDropdown">
|
|
||||||
<img src={CollapseChevronDownIcon} />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="commandDropdownContainer"
|
|
||||||
ref={(ref: HTMLElement) => {
|
|
||||||
this.dropdownElt = ref;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="commandDropdown">
|
|
||||||
{this.props.children.map((c: CommandButtonComponentProps, index: number): JSX.Element => {
|
|
||||||
return CommandButtonComponent.renderButton(c, `${index}`);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static renderLabel(
|
|
||||||
props: CommandButtonComponentProps,
|
|
||||||
key?: string,
|
|
||||||
refct?: (input: HTMLElement) => void,
|
|
||||||
): JSX.Element {
|
|
||||||
if (!props.commandButtonLabel) {
|
|
||||||
return <React.Fragment />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span className="commandLabel" key={key} ref={refct}>
|
|
||||||
{props.commandButtonLabel}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): JSX.Element {
|
|
||||||
let mainClassName = "commandButtonComponent";
|
|
||||||
if (this.props.disabled) {
|
|
||||||
mainClassName += " commandDisabled";
|
|
||||||
}
|
|
||||||
if (this.props.isSelected) {
|
|
||||||
mainClassName += " selectedButton";
|
|
||||||
}
|
|
||||||
|
|
||||||
let contentClassName = "commandContent";
|
|
||||||
if (this.props.children && this.props.children.length > 0) {
|
|
||||||
contentClassName += " hasHiddenItems";
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="commandButtonReact">
|
|
||||||
<span
|
|
||||||
className={mainClassName}
|
|
||||||
role="menuitem"
|
|
||||||
tabIndex={this.props.tabIndex}
|
|
||||||
onKeyPress={(e: React.KeyboardEvent<HTMLSpanElement>) => this.onKeyPress(e)}
|
|
||||||
title={this.props.tooltipText}
|
|
||||||
id={this.getCommandButtonId()}
|
|
||||||
aria-disabled={this.props.disabled}
|
|
||||||
aria-haspopup={this.props.hasPopup}
|
|
||||||
aria-label={this.props.ariaLabel}
|
|
||||||
onClick={(e: React.MouseEvent<HTMLSpanElement>) => this.commandClickCallback(e)}
|
|
||||||
>
|
|
||||||
<div className={contentClassName}>
|
|
||||||
<img className="commandIcon" src={this.props.iconSrc} alt={this.props.iconAlt} />
|
|
||||||
{CommandButtonComponent.renderLabel(this.props)}
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
{this.props.children && this.renderChildren()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export interface DialogState {
|
|||||||
textFieldProps?: TextFieldProps,
|
textFieldProps?: TextFieldProps,
|
||||||
primaryButtonDisabled?: boolean,
|
primaryButtonDisabled?: boolean,
|
||||||
) => void;
|
) => void;
|
||||||
showOkModalDialog: (title: string, subText: string) => void;
|
showOkModalDialog: (title: string, subText: string, linkProps?: LinkProps) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useDialog: UseStore<DialogState> = create((set, get) => ({
|
export const useDialog: UseStore<DialogState> = create((set, get) => ({
|
||||||
@@ -83,7 +83,7 @@ export const useDialog: UseStore<DialogState> = create((set, get) => ({
|
|||||||
textFieldProps,
|
textFieldProps,
|
||||||
primaryButtonDisabled,
|
primaryButtonDisabled,
|
||||||
}),
|
}),
|
||||||
showOkModalDialog: (title: string, subText: string): void =>
|
showOkModalDialog: (title: string, subText: string, linkProps?: LinkProps): void =>
|
||||||
get().openDialog({
|
get().openDialog({
|
||||||
isModal: true,
|
isModal: true,
|
||||||
title,
|
title,
|
||||||
@@ -94,6 +94,7 @@ export const useDialog: UseStore<DialogState> = create((set, get) => ({
|
|||||||
get().closeDialog();
|
get().closeDialog();
|
||||||
},
|
},
|
||||||
onSecondaryButtonClick: undefined,
|
onSecondaryButtonClick: undefined,
|
||||||
|
linkProps,
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,37 @@ import * as React from "react";
|
|||||||
import { loadMonaco, monaco } from "../../LazyMonaco";
|
import { loadMonaco, monaco } from "../../LazyMonaco";
|
||||||
// import "./EditorReact.less";
|
// import "./EditorReact.less";
|
||||||
|
|
||||||
|
// In development, add a function to window to allow us to get the editor instance for a given element
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const win = window as any;
|
||||||
|
win._monaco_getEditorForElement =
|
||||||
|
win._monaco_getEditorForElement ||
|
||||||
|
((element: HTMLElement) => {
|
||||||
|
const editorId = element.dataset["monacoEditorId"];
|
||||||
|
if (!editorId || !win.__monaco_editors || typeof win.__monaco_editors !== "object") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return win.__monaco_editors[editorId];
|
||||||
|
});
|
||||||
|
|
||||||
|
win._monaco_getEditorContentForElement =
|
||||||
|
win._monaco_getEditorContentForElement ||
|
||||||
|
((element: HTMLElement) => {
|
||||||
|
const editor = win._monaco_getEditorForElement(element);
|
||||||
|
return editor ? editor.getValue() : null;
|
||||||
|
});
|
||||||
|
|
||||||
|
win._monaco_setEditorContentForElement =
|
||||||
|
win._monaco_setEditorContentForElement ||
|
||||||
|
((element: HTMLElement, text: string) => {
|
||||||
|
const editor = win._monaco_getEditorForElement(element);
|
||||||
|
if (editor) {
|
||||||
|
editor.setValue(text);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
interface EditorReactStates {
|
interface EditorReactStates {
|
||||||
showEditor: boolean;
|
showEditor: boolean;
|
||||||
}
|
}
|
||||||
@@ -11,7 +42,7 @@ export interface EditorReactProps {
|
|||||||
content: string;
|
content: string;
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
ariaLabel: string; // Sets what will be read to the user to define the control
|
ariaLabel: string; // Sets what will be read to the user to define the control
|
||||||
onContentSelected?: (selectedContent: string) => void; // Called when text is selected
|
onContentSelected?: (selectedContent: string, selection: monaco.Selection) => void; // Called when text is selected
|
||||||
onContentChanged?: (newContent: string) => void; // Called when text is changed
|
onContentChanged?: (newContent: string) => void; // Called when text is changed
|
||||||
theme?: string; // Monaco editor theme
|
theme?: string; // Monaco editor theme
|
||||||
wordWrap?: monaco.editor.IEditorOptions["wordWrap"];
|
wordWrap?: monaco.editor.IEditorOptions["wordWrap"];
|
||||||
@@ -24,12 +55,34 @@ export interface EditorReactProps {
|
|||||||
monacoContainerStyles?: React.CSSProperties;
|
monacoContainerStyles?: React.CSSProperties;
|
||||||
className?: string;
|
className?: string;
|
||||||
spinnerClassName?: string;
|
spinnerClassName?: string;
|
||||||
|
|
||||||
|
modelMarkers?: monaco.editor.IMarkerData[];
|
||||||
|
enableWordWrapContextMenuItem?: boolean; // Enable/Disable "Word Wrap" context menu item
|
||||||
|
onWordWrapChanged?: (wordWrap: "on" | "off") => void; // Called when word wrap is changed
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EditorReact extends React.Component<EditorReactProps, EditorReactStates> {
|
export class EditorReact extends React.Component<EditorReactProps, EditorReactStates> {
|
||||||
|
private static readonly VIEWING_OPTIONS_GROUP_ID = "viewingoptions"; // Group ID for the context menu group
|
||||||
private rootNode: HTMLElement;
|
private rootNode: HTMLElement;
|
||||||
private editor: monaco.editor.IStandaloneCodeEditor;
|
public editor: monaco.editor.IStandaloneCodeEditor;
|
||||||
private selectionListener: monaco.IDisposable;
|
private selectionListener: monaco.IDisposable;
|
||||||
|
monacoApi: {
|
||||||
|
default: typeof monaco;
|
||||||
|
Emitter: typeof monaco.Emitter;
|
||||||
|
MarkerTag: typeof monaco.MarkerTag;
|
||||||
|
MarkerSeverity: typeof monaco.MarkerSeverity;
|
||||||
|
CancellationTokenSource: typeof monaco.CancellationTokenSource;
|
||||||
|
Uri: typeof monaco.Uri;
|
||||||
|
KeyCode: typeof monaco.KeyCode;
|
||||||
|
KeyMod: typeof monaco.KeyMod;
|
||||||
|
Position: typeof monaco.Position;
|
||||||
|
Range: typeof monaco.Range;
|
||||||
|
Selection: typeof monaco.Selection;
|
||||||
|
SelectionDirection: typeof monaco.SelectionDirection;
|
||||||
|
Token: typeof monaco.Token;
|
||||||
|
editor: typeof monaco.editor;
|
||||||
|
languages: typeof monaco.languages;
|
||||||
|
};
|
||||||
|
|
||||||
public constructor(props: EditorReactProps) {
|
public constructor(props: EditorReactProps) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -58,7 +111,7 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
|||||||
|
|
||||||
if (this.props.content !== existingContent) {
|
if (this.props.content !== existingContent) {
|
||||||
if (this.props.isReadOnly) {
|
if (this.props.isReadOnly) {
|
||||||
this.editor.setValue(this.props.content);
|
this.editor.setValue(this.props.content || ""); // Monaco throws an error if you set the value to undefined.
|
||||||
} else {
|
} else {
|
||||||
this.editor.pushUndoStop();
|
this.editor.pushUndoStop();
|
||||||
this.editor.executeEdits("", [
|
this.editor.executeEdits("", [
|
||||||
@@ -69,6 +122,8 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.monacoApi.editor.setModelMarkers(this.editor.getModel(), "owner", this.props.modelMarkers || []);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
@@ -82,6 +137,7 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
|||||||
<Spinner size={SpinnerSize.large} className={this.props.spinnerClassName || "spinner"} />
|
<Spinner size={SpinnerSize.large} className={this.props.spinnerClassName || "spinner"} />
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
|
data-test="EditorReact/Host/Unloaded"
|
||||||
className={this.props.className || "jsonEditor"}
|
className={this.props.className || "jsonEditor"}
|
||||||
style={this.props.monacoContainerStyles}
|
style={this.props.monacoContainerStyles}
|
||||||
ref={(elt: HTMLElement) => this.setRef(elt)}
|
ref={(elt: HTMLElement) => this.setRef(elt)}
|
||||||
@@ -92,6 +148,18 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
|||||||
|
|
||||||
protected configureEditor(editor: monaco.editor.IStandaloneCodeEditor) {
|
protected configureEditor(editor: monaco.editor.IStandaloneCodeEditor) {
|
||||||
this.editor = editor;
|
this.editor = editor;
|
||||||
|
this.rootNode.dataset["test"] = "EditorReact/Host/Loaded";
|
||||||
|
|
||||||
|
// In development, we want to be able to access the editor instance from the console
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
this.rootNode.dataset["monacoEditorId"] = this.editor.getId();
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const win = window as any;
|
||||||
|
|
||||||
|
win["__monaco_editors"] = win["__monaco_editors"] || {};
|
||||||
|
win["__monaco_editors"][this.editor.getId()] = this.editor;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.props.isReadOnly && this.props.onContentChanged) {
|
if (!this.props.isReadOnly && this.props.onContentChanged) {
|
||||||
// Hooking the model's onDidChangeContent event because of some event ordering issues.
|
// Hooking the model's onDidChangeContent event because of some event ordering issues.
|
||||||
// If a single user input causes BOTH the editor content to change AND the cursor selection to change (which is likely),
|
// If a single user input causes BOTH the editor content to change AND the cursor selection to change (which is likely),
|
||||||
@@ -109,10 +177,27 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
|||||||
this.selectionListener = this.editor.onDidChangeCursorSelection(
|
this.selectionListener = this.editor.onDidChangeCursorSelection(
|
||||||
(event: monaco.editor.ICursorSelectionChangedEvent) => {
|
(event: monaco.editor.ICursorSelectionChangedEvent) => {
|
||||||
const selectedContent: string = this.editor.getModel().getValueInRange(event.selection);
|
const selectedContent: string = this.editor.getModel().getValueInRange(event.selection);
|
||||||
this.props.onContentSelected(selectedContent);
|
this.props.onContentSelected(selectedContent, event.selection);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.props.enableWordWrapContextMenuItem) {
|
||||||
|
editor.addAction({
|
||||||
|
// An unique identifier of the contributed action.
|
||||||
|
id: "wordwrap",
|
||||||
|
label: "Toggle Word Wrap",
|
||||||
|
contextMenuGroupId: EditorReact.VIEWING_OPTIONS_GROUP_ID,
|
||||||
|
contextMenuOrder: 1,
|
||||||
|
// Method that will be executed when the action is triggered.
|
||||||
|
// @param editor The editor instance is passed in as a convenience
|
||||||
|
run: (ed) => {
|
||||||
|
const newOption = ed.getOption(this.monacoApi.editor.EditorOption.wordWrap) === "on" ? "off" : "on";
|
||||||
|
ed.updateOptions({ wordWrap: newOption });
|
||||||
|
this.props.onWordWrapChanged(newOption);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -133,12 +218,14 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
|||||||
lineDecorationsWidth: this.props.lineDecorationsWidth,
|
lineDecorationsWidth: this.props.lineDecorationsWidth,
|
||||||
minimap: this.props.minimap,
|
minimap: this.props.minimap,
|
||||||
scrollBeyondLastLine: this.props.scrollBeyondLastLine,
|
scrollBeyondLastLine: this.props.scrollBeyondLastLine,
|
||||||
|
fixedOverflowWidgets: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.rootNode.innerHTML = "";
|
this.rootNode.innerHTML = "";
|
||||||
const monaco = await loadMonaco();
|
this.monacoApi = await loadMonaco();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
createCallback(monaco?.editor?.create(this.rootNode, options));
|
createCallback(this.monacoApi.editor.create(this.rootNode, options));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// This could happen if the parent node suddenly disappears during create()
|
// This could happen if the parent node suddenly disappears during create()
|
||||||
console.error("Unable to create EditorReact", error);
|
console.error("Unable to create EditorReact", error);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ exports[`Feature panel renders all flags 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
className="options"
|
className="options"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ exports[`Feature panel renders all flags 1`] = `
|
|||||||
horizontal={true}
|
horizontal={true}
|
||||||
horizontalAlign="space-between"
|
horizontalAlign="space-between"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ exports[`Feature panel renders all flags 1`] = `
|
|||||||
horizontal={true}
|
horizontal={true}
|
||||||
horizontalAlign="start"
|
horizontalAlign="start"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,16 +61,16 @@ exports[`Feature panel renders all flags 1`] = `
|
|||||||
label="Base Url"
|
label="Base Url"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
options={
|
options={
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"key": "https://localhost:1234/explorer.html",
|
"key": "https://localhost:1234/explorer.html",
|
||||||
"text": "localhost:1234",
|
"text": "localhost:1234",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "https://cosmos.azure.com/explorer.html",
|
"key": "https://cosmos.azure.com/explorer.html",
|
||||||
"text": "cosmos.azure.com",
|
"text": "cosmos.azure.com",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "https://portal.azure.com",
|
"key": "https://portal.azure.com",
|
||||||
"text": "portal",
|
"text": "portal",
|
||||||
},
|
},
|
||||||
@@ -78,8 +78,8 @@ exports[`Feature panel renders all flags 1`] = `
|
|||||||
}
|
}
|
||||||
selectedKey="https://localhost:1234/explorer.html"
|
selectedKey="https://localhost:1234/explorer.html"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"dropdown": Object {
|
"dropdown": {
|
||||||
"width": 200,
|
"width": 200,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -89,20 +89,20 @@ exports[`Feature panel renders all flags 1`] = `
|
|||||||
label="Platform"
|
label="Platform"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
options={
|
options={
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"key": "Hosted",
|
"key": "Hosted",
|
||||||
"text": "Hosted",
|
"text": "Hosted",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "Portal",
|
"key": "Portal",
|
||||||
"text": "Portal",
|
"text": "Portal",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "Emulator",
|
"key": "Emulator",
|
||||||
"text": "Emulator",
|
"text": "Emulator",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "",
|
"key": "",
|
||||||
"text": "None",
|
"text": "None",
|
||||||
},
|
},
|
||||||
@@ -110,8 +110,8 @@ exports[`Feature panel renders all flags 1`] = `
|
|||||||
}
|
}
|
||||||
selectedKey="Hosted"
|
selectedKey="Hosted"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"dropdown": Object {
|
"dropdown": {
|
||||||
"width": 200,
|
"width": 200,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -208,7 +208,7 @@ exports[`Feature panel renders all flags 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -222,8 +222,8 @@ exports[`Feature panel renders all flags 1`] = `
|
|||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
placeholder="https://notebookserver"
|
placeholder="https://notebookserver"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"fieldGroup": Object {
|
"fieldGroup": {
|
||||||
"width": 300,
|
"width": 300,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -235,8 +235,8 @@ exports[`Feature panel renders all flags 1`] = `
|
|||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
placeholder=""
|
placeholder=""
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"fieldGroup": Object {
|
"fieldGroup": {
|
||||||
"width": 300,
|
"width": 300,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -248,8 +248,8 @@ exports[`Feature panel renders all flags 1`] = `
|
|||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
placeholder=""
|
placeholder=""
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"fieldGroup": Object {
|
"fieldGroup": {
|
||||||
"width": 300,
|
"width": 300,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -265,8 +265,8 @@ exports[`Feature panel renders all flags 1`] = `
|
|||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
placeholder=""
|
placeholder=""
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"fieldGroup": Object {
|
"fieldGroup": {
|
||||||
"width": 300,
|
"width": 300,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -279,8 +279,8 @@ exports[`Feature panel renders all flags 1`] = `
|
|||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
placeholder="https://localhost:1234/explorer.html"
|
placeholder="https://localhost:1234/explorer.html"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"fieldGroup": Object {
|
"fieldGroup": {
|
||||||
"width": 300,
|
"width": 300,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -292,8 +292,8 @@ exports[`Feature panel renders all flags 1`] = `
|
|||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
placeholder=""
|
placeholder=""
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"fieldGroup": Object {
|
"fieldGroup": {
|
||||||
"width": 300,
|
"width": 300,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
37
src/Explorer/Controls/IndeterminateProgressBar.tsx
Normal file
37
src/Explorer/Controls/IndeterminateProgressBar.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { ProgressBar, makeStyles } from "@fluentui/react-components";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const useStyles = makeStyles({
|
||||||
|
indeterminateProgressBarRoot: {
|
||||||
|
"@media screen and (prefers-reduced-motion: reduce)": {
|
||||||
|
animationIterationCount: "infinite",
|
||||||
|
animationDuration: "3s",
|
||||||
|
animationName: {
|
||||||
|
"0%": {
|
||||||
|
opacity: ".2", // matches indeterminate bar width
|
||||||
|
},
|
||||||
|
"50%": {
|
||||||
|
opacity: "1",
|
||||||
|
},
|
||||||
|
"100%": {
|
||||||
|
opacity: ".2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
indeterminateProgressBarBar: {
|
||||||
|
"@media screen and (prefers-reduced-motion: reduce)": {
|
||||||
|
maxWidth: "100%",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const IndeterminateProgressBar: React.FC = () => {
|
||||||
|
const styles = useStyles();
|
||||||
|
return (
|
||||||
|
<ProgressBar
|
||||||
|
bar={{ className: styles.indeterminateProgressBarBar }}
|
||||||
|
className={styles.indeterminateProgressBarRoot}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
68
src/Explorer/Controls/MessageBanner.tsx
Normal file
68
src/Explorer/Controls/MessageBanner.tsx
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { Button, MessageBar, MessageBarActions, MessageBarBody } from "@fluentui/react-components";
|
||||||
|
import { DismissRegular } from "@fluentui/react-icons";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
|
||||||
|
export enum MessageBannerState {
|
||||||
|
/** The banner should be visible if the triggering conditions are met. */
|
||||||
|
Allowed = "allowed",
|
||||||
|
|
||||||
|
/** The banner has been dismissed by the user and will not be shown until the component is recreated, even if the visibility condition is true. */
|
||||||
|
Dismissed = "dismissed",
|
||||||
|
|
||||||
|
/** The banner has been supressed by the user and will not be shown at all, even if the visibility condition is true. */
|
||||||
|
Suppressed = "suppressed",
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MessageBannerProps = {
|
||||||
|
/** A CSS class for the root MessageBar component */
|
||||||
|
className: string;
|
||||||
|
|
||||||
|
/** A unique ID for the message that will be used to store it's dismiss/suppress state across sessions. */
|
||||||
|
messageId: string;
|
||||||
|
|
||||||
|
/** The current visibility state for the banner IGNORING the user's dimiss/suppress preference
|
||||||
|
*
|
||||||
|
* If this value is true but the user has dismissed the banner, the banner will NOT be shown.
|
||||||
|
*/
|
||||||
|
visible: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** A component that shows a message banner which can be dismissed by the user.
|
||||||
|
*
|
||||||
|
* In the future, this can also support persisting the dismissed state in local storage without requiring changes to all the components that use it.
|
||||||
|
*
|
||||||
|
* A message banner can be in three "states":
|
||||||
|
* - Allowed: The banner should be visible if the triggering conditions are met.
|
||||||
|
* - Dismissed: The banner has been dismissed by the user and will not be shown until the component is recreated, even if the visibility condition is true.
|
||||||
|
* - Suppressed: The banner has been supressed by the user and will not be shown at all, even if the visibility condition is true.
|
||||||
|
*
|
||||||
|
* The "Dismissed" state represents the user clicking the "x" in the banner to dismiss it.
|
||||||
|
* The "Suppressed" state represents the user clicking "Don't show this again".
|
||||||
|
*/
|
||||||
|
export const MessageBanner: React.FC<MessageBannerProps> = ({ visible, className, children }) => {
|
||||||
|
const [state, setState] = useState<MessageBannerState>(MessageBannerState.Allowed);
|
||||||
|
|
||||||
|
if (state !== MessageBannerState.Allowed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!visible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MessageBar className={className}>
|
||||||
|
<MessageBarBody>{children}</MessageBarBody>
|
||||||
|
<MessageBarActions
|
||||||
|
containerAction={
|
||||||
|
<Button
|
||||||
|
aria-label="dismiss"
|
||||||
|
appearance="transparent"
|
||||||
|
icon={<DismissRegular />}
|
||||||
|
onClick={() => setState(MessageBannerState.Dismissed)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
></MessageBarActions>
|
||||||
|
</MessageBar>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -4,8 +4,8 @@ exports[`GalleryCardComponent renders 1`] = `
|
|||||||
<StyledDocumentCardBase
|
<StyledDocumentCardBase
|
||||||
aria-label="name"
|
aria-label="name"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"display": "inline-block",
|
"display": "inline-block",
|
||||||
"marginRight": 20,
|
"marginRight": 20,
|
||||||
"width": 256,
|
"width": 256,
|
||||||
@@ -16,8 +16,8 @@ exports[`GalleryCardComponent renders 1`] = `
|
|||||||
<StyledDocumentCardActivityBase
|
<StyledDocumentCardActivityBase
|
||||||
activity="Invalid Date"
|
activity="Invalid Date"
|
||||||
people={
|
people={
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"name": "author",
|
"name": "author",
|
||||||
"profileImageSrc": false,
|
"profileImageSrc": false,
|
||||||
},
|
},
|
||||||
@@ -26,8 +26,8 @@ exports[`GalleryCardComponent renders 1`] = `
|
|||||||
/>
|
/>
|
||||||
<StyledDocumentCardPreviewBase
|
<StyledDocumentCardPreviewBase
|
||||||
previewImages={
|
previewImages={
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"height": 144,
|
"height": 144,
|
||||||
"imageFit": 2,
|
"imageFit": 2,
|
||||||
"previewImageSrc": "thumbnailUrl",
|
"previewImageSrc": "thumbnailUrl",
|
||||||
@@ -40,8 +40,8 @@ exports[`GalleryCardComponent renders 1`] = `
|
|||||||
<Text
|
<Text
|
||||||
nowrap={true}
|
nowrap={true}
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"height": 18,
|
"height": 18,
|
||||||
"padding": "2px 16px",
|
"padding": "2px 16px",
|
||||||
},
|
},
|
||||||
@@ -69,15 +69,15 @@ exports[`GalleryCardComponent renders 1`] = `
|
|||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"padding": "8px 16px",
|
"padding": "8px 16px",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"color": "#605E5C",
|
"color": "#605E5C",
|
||||||
"paddingRight": 8,
|
"paddingRight": 8,
|
||||||
},
|
},
|
||||||
@@ -88,8 +88,8 @@ exports[`GalleryCardComponent renders 1`] = `
|
|||||||
<Icon
|
<Icon
|
||||||
iconName="RedEye"
|
iconName="RedEye"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"verticalAlign": "middle",
|
"verticalAlign": "middle",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -100,8 +100,8 @@ exports[`GalleryCardComponent renders 1`] = `
|
|||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"color": "#605E5C",
|
"color": "#605E5C",
|
||||||
"paddingRight": 8,
|
"paddingRight": 8,
|
||||||
},
|
},
|
||||||
@@ -112,8 +112,8 @@ exports[`GalleryCardComponent renders 1`] = `
|
|||||||
<Icon
|
<Icon
|
||||||
iconName="Download"
|
iconName="Download"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"verticalAlign": "middle",
|
"verticalAlign": "middle",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -124,8 +124,8 @@ exports[`GalleryCardComponent renders 1`] = `
|
|||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"color": "#605E5C",
|
"color": "#605E5C",
|
||||||
"paddingRight": 8,
|
"paddingRight": 8,
|
||||||
},
|
},
|
||||||
@@ -136,8 +136,8 @@ exports[`GalleryCardComponent renders 1`] = `
|
|||||||
<Icon
|
<Icon
|
||||||
iconName="Heart"
|
iconName="Heart"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"verticalAlign": "middle",
|
"verticalAlign": "middle",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -151,8 +151,8 @@ exports[`GalleryCardComponent renders 1`] = `
|
|||||||
<StyledDocumentCardDetailsBase>
|
<StyledDocumentCardDetailsBase>
|
||||||
<Separator
|
<Separator
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"height": 1,
|
"height": 1,
|
||||||
"padding": 0,
|
"padding": 0,
|
||||||
},
|
},
|
||||||
@@ -161,22 +161,22 @@ exports[`GalleryCardComponent renders 1`] = `
|
|||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"padding": "0px 16px",
|
"padding": "0px 16px",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<StyledTooltipHostBase
|
<StyledTooltipHostBase
|
||||||
calloutProps={
|
calloutProps={
|
||||||
Object {
|
{
|
||||||
"gapSpace": 0,
|
"gapSpace": 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
content="Favorite"
|
content="Favorite"
|
||||||
id="TooltipHost-IconButton-Heart"
|
id="TooltipHost-IconButton-Heart"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"display": "inline-block",
|
"display": "inline-block",
|
||||||
"float": "left",
|
"float": "left",
|
||||||
},
|
},
|
||||||
@@ -186,7 +186,7 @@ exports[`GalleryCardComponent renders 1`] = `
|
|||||||
<CustomizedIconButton
|
<CustomizedIconButton
|
||||||
ariaLabel="Favorite"
|
ariaLabel="Favorite"
|
||||||
iconProps={
|
iconProps={
|
||||||
Object {
|
{
|
||||||
"iconName": "Heart",
|
"iconName": "Heart",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,15 +196,15 @@ exports[`GalleryCardComponent renders 1`] = `
|
|||||||
</StyledTooltipHostBase>
|
</StyledTooltipHostBase>
|
||||||
<StyledTooltipHostBase
|
<StyledTooltipHostBase
|
||||||
calloutProps={
|
calloutProps={
|
||||||
Object {
|
{
|
||||||
"gapSpace": 0,
|
"gapSpace": 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
content="Download"
|
content="Download"
|
||||||
id="TooltipHost-IconButton-Download"
|
id="TooltipHost-IconButton-Download"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"display": "inline-block",
|
"display": "inline-block",
|
||||||
"float": "left",
|
"float": "left",
|
||||||
},
|
},
|
||||||
@@ -214,7 +214,7 @@ exports[`GalleryCardComponent renders 1`] = `
|
|||||||
<CustomizedIconButton
|
<CustomizedIconButton
|
||||||
ariaLabel="Download"
|
ariaLabel="Download"
|
||||||
iconProps={
|
iconProps={
|
||||||
Object {
|
{
|
||||||
"iconName": "Download",
|
"iconName": "Download",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,15 +224,15 @@ exports[`GalleryCardComponent renders 1`] = `
|
|||||||
</StyledTooltipHostBase>
|
</StyledTooltipHostBase>
|
||||||
<StyledTooltipHostBase
|
<StyledTooltipHostBase
|
||||||
calloutProps={
|
calloutProps={
|
||||||
Object {
|
{
|
||||||
"gapSpace": 0,
|
"gapSpace": 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
content="Remove"
|
content="Remove"
|
||||||
id="TooltipHost-IconButton-Delete"
|
id="TooltipHost-IconButton-Delete"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"display": "inline-block",
|
"display": "inline-block",
|
||||||
"float": "right",
|
"float": "right",
|
||||||
},
|
},
|
||||||
@@ -242,7 +242,7 @@ exports[`GalleryCardComponent renders 1`] = `
|
|||||||
<CustomizedIconButton
|
<CustomizedIconButton
|
||||||
ariaLabel="Remove"
|
ariaLabel="Remove"
|
||||||
iconProps={
|
iconProps={
|
||||||
Object {
|
{
|
||||||
"iconName": "Delete",
|
"iconName": "Delete",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
exports[`CodeOfConduct renders 1`] = `
|
exports[`CodeOfConduct renders 1`] = `
|
||||||
<Stack
|
<Stack
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 20,
|
"childrenGap": 20,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ exports[`CodeOfConduct renders 1`] = `
|
|||||||
<StackItem>
|
<StackItem>
|
||||||
<Text
|
<Text
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"fontSize": "20px",
|
"fontSize": "20px",
|
||||||
"fontWeight": 500,
|
"fontWeight": 500,
|
||||||
}
|
}
|
||||||
@@ -41,12 +41,12 @@ exports[`CodeOfConduct renders 1`] = `
|
|||||||
label="I have read and accept the code of conduct."
|
label="I have read and accept the code of conduct."
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"label": Object {
|
"label": {
|
||||||
"margin": 0,
|
"margin": 0,
|
||||||
"padding": "2 0 2 0",
|
"padding": "2 0 2 0",
|
||||||
},
|
},
|
||||||
"text": Object {
|
"text": {
|
||||||
"fontSize": 12,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ exports[`InfoComponent renders 1`] = `
|
|||||||
<StyledHoverCardBase
|
<StyledHoverCardBase
|
||||||
instantOpenOnClick={true}
|
instantOpenOnClick={true}
|
||||||
plainCardProps={
|
plainCardProps={
|
||||||
Object {
|
{
|
||||||
"onRenderPlainCard": [Function],
|
"onRenderPlainCard": [Function],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,8 +18,8 @@ exports[`InfoComponent renders 1`] = `
|
|||||||
className="infoIconMain"
|
className="infoIconMain"
|
||||||
iconName="Help"
|
iconName="Help"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"verticalAlign": "middle",
|
"verticalAlign": "middle",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ exports[`GalleryViewerComponent renders 1`] = `
|
|||||||
itemKey="OfficialSamples"
|
itemKey="OfficialSamples"
|
||||||
key="OfficialSamples"
|
key="OfficialSamples"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"marginTop": 20,
|
"marginTop": 20,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Stack
|
<Stack
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ exports[`GalleryViewerComponent renders 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 20,
|
"childrenGap": 20,
|
||||||
"padding": 10,
|
"padding": 10,
|
||||||
}
|
}
|
||||||
@@ -50,8 +50,8 @@ exports[`GalleryViewerComponent renders 1`] = `
|
|||||||
</StackItem>
|
</StackItem>
|
||||||
<StackItem
|
<StackItem
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"minWidth": 200,
|
"minWidth": 200,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -60,20 +60,20 @@ exports[`GalleryViewerComponent renders 1`] = `
|
|||||||
<Dropdown
|
<Dropdown
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
options={
|
options={
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"key": 0,
|
"key": 0,
|
||||||
"text": "Most viewed",
|
"text": "Most viewed",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": 1,
|
"key": 1,
|
||||||
"text": "Most downloaded",
|
"text": "Most downloaded",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": 3,
|
"key": 3,
|
||||||
"text": "Most recent",
|
"text": "Most recent",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": 2,
|
"key": 2,
|
||||||
"text": "Most favorited",
|
"text": "Most favorited",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
exports[`NotebookMetadataComponent renders liked notebook 1`] = `
|
exports[`NotebookMetadataComponent renders liked notebook 1`] = `
|
||||||
<Stack
|
<Stack
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ exports[`NotebookMetadataComponent renders liked notebook 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 30,
|
"childrenGap": 30,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ exports[`NotebookMetadataComponent renders liked notebook 1`] = `
|
|||||||
<Text>
|
<Text>
|
||||||
<CustomizedIconButton
|
<CustomizedIconButton
|
||||||
iconProps={
|
iconProps={
|
||||||
Object {
|
{
|
||||||
"iconName": "HeartFill",
|
"iconName": "HeartFill",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,7 @@ exports[`NotebookMetadataComponent renders liked notebook 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,8 +96,8 @@ exports[`NotebookMetadataComponent renders liked notebook 1`] = `
|
|||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -115,7 +115,7 @@ exports[`NotebookMetadataComponent renders liked notebook 1`] = `
|
|||||||
exports[`NotebookMetadataComponent renders un-liked notebook 1`] = `
|
exports[`NotebookMetadataComponent renders un-liked notebook 1`] = `
|
||||||
<Stack
|
<Stack
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,7 +123,7 @@ exports[`NotebookMetadataComponent renders un-liked notebook 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 30,
|
"childrenGap": 30,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,7 +141,7 @@ exports[`NotebookMetadataComponent renders un-liked notebook 1`] = `
|
|||||||
<Text>
|
<Text>
|
||||||
<CustomizedIconButton
|
<CustomizedIconButton
|
||||||
iconProps={
|
iconProps={
|
||||||
Object {
|
{
|
||||||
"iconName": "Heart",
|
"iconName": "Heart",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,7 +165,7 @@ exports[`NotebookMetadataComponent renders un-liked notebook 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,8 +208,8 @@ exports[`NotebookMetadataComponent renders un-liked notebook 1`] = `
|
|||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
79
src/Explorer/Controls/ProgressModalDialog.tsx
Normal file
79
src/Explorer/Controls/ProgressModalDialog.tsx
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogBody,
|
||||||
|
DialogContent,
|
||||||
|
DialogSurface,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
Field,
|
||||||
|
ProgressBar,
|
||||||
|
} from "@fluentui/react-components";
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
interface ProgressModalDialogProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
maxValue: number;
|
||||||
|
value: number;
|
||||||
|
dismissText: string;
|
||||||
|
onDismiss: () => void;
|
||||||
|
onCancel?: () => void;
|
||||||
|
/* mode drives the state of the action buttons
|
||||||
|
* inProgress: Show cancel button
|
||||||
|
* completed: Show close button
|
||||||
|
* aborting: Show cancel button, but disabled
|
||||||
|
* aborted: Show close button
|
||||||
|
*/
|
||||||
|
mode?: "inProgress" | "completed" | "aborting" | "aborted";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React component that renders a modal dialog with a progress bar.
|
||||||
|
*/
|
||||||
|
export const ProgressModalDialog: React.FC<ProgressModalDialogProps> = ({
|
||||||
|
isOpen,
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
maxValue,
|
||||||
|
value,
|
||||||
|
dismissText,
|
||||||
|
onCancel,
|
||||||
|
onDismiss,
|
||||||
|
children,
|
||||||
|
mode = "completed",
|
||||||
|
}) => (
|
||||||
|
<Dialog
|
||||||
|
open={isOpen}
|
||||||
|
onOpenChange={(event, data) => {
|
||||||
|
if (!data.open) {
|
||||||
|
onDismiss();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogSurface>
|
||||||
|
<DialogBody>
|
||||||
|
<DialogTitle>{title}</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<Field validationMessage={message} validationState="none">
|
||||||
|
<ProgressBar max={maxValue} value={value} />
|
||||||
|
</Field>
|
||||||
|
{children}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
{mode === "inProgress" || mode === "aborting" ? (
|
||||||
|
<Button appearance="secondary" onClick={onCancel} disabled={mode === "aborting"}>
|
||||||
|
{dismissText}
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<DialogTrigger disableButtonEnhancement>
|
||||||
|
<Button appearance="primary">Close</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
)}
|
||||||
|
</DialogActions>
|
||||||
|
</DialogBody>
|
||||||
|
</DialogSurface>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
@@ -134,7 +134,6 @@ describe("SettingsComponent", () => {
|
|||||||
readSettings: undefined,
|
readSettings: undefined,
|
||||||
onSettingsClick: undefined,
|
onSettingsClick: undefined,
|
||||||
loadOffer: undefined,
|
loadOffer: undefined,
|
||||||
getPendingThroughputSplitNotification: undefined,
|
|
||||||
} as ViewModels.Database;
|
} as ViewModels.Database;
|
||||||
newCollection.getDatabase = () => newDatabase;
|
newCollection.getDatabase = () => newDatabase;
|
||||||
newCollection.offer = ko.observable(undefined);
|
newCollection.offer = ko.observable(undefined);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
ContainerVectorPolicyComponent,
|
ContainerVectorPolicyComponent,
|
||||||
ContainerVectorPolicyComponentProps,
|
ContainerVectorPolicyComponentProps,
|
||||||
} from "Explorer/Controls/Settings/SettingsSubComponents/ContainerVectorPolicyComponent";
|
} from "Explorer/Controls/Settings/SettingsSubComponents/ContainerVectorPolicyComponent";
|
||||||
|
import { useCommandBar } from "Explorer/Menus/CommandBar/useCommandBar";
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
import { useDatabases } from "Explorer/useDatabases";
|
||||||
import { isVectorSearchEnabled } from "Utils/CapabilityUtils";
|
import { isVectorSearchEnabled } from "Utils/CapabilityUtils";
|
||||||
import { isRunningOnPublicCloud } from "Utils/CloudUtils";
|
import { isRunningOnPublicCloud } from "Utils/CloudUtils";
|
||||||
@@ -32,7 +33,6 @@ import {
|
|||||||
PartitionKeyComponent,
|
PartitionKeyComponent,
|
||||||
PartitionKeyComponentProps,
|
PartitionKeyComponentProps,
|
||||||
} from "../../Controls/Settings/SettingsSubComponents/PartitionKeyComponent";
|
} from "../../Controls/Settings/SettingsSubComponents/PartitionKeyComponent";
|
||||||
import { useCommandBar } from "../../Menus/CommandBar/CommandBarComponentAdapter";
|
|
||||||
import { SettingsTabV2 } from "../../Tabs/SettingsTabV2";
|
import { SettingsTabV2 } from "../../Tabs/SettingsTabV2";
|
||||||
import "./SettingsComponent.less";
|
import "./SettingsComponent.less";
|
||||||
import { mongoIndexingPolicyAADError } from "./SettingsRenderUtils";
|
import { mongoIndexingPolicyAADError } from "./SettingsRenderUtils";
|
||||||
@@ -130,7 +130,6 @@ export interface SettingsComponentState {
|
|||||||
conflictResolutionPolicyProcedureBaseline: string;
|
conflictResolutionPolicyProcedureBaseline: string;
|
||||||
isConflictResolutionDirty: boolean;
|
isConflictResolutionDirty: boolean;
|
||||||
|
|
||||||
initialNotification: DataModels.Notification;
|
|
||||||
selectedTab: SettingsV2TabTypes;
|
selectedTab: SettingsV2TabTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +228,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
conflictResolutionPolicyProcedureBaseline: undefined,
|
conflictResolutionPolicyProcedureBaseline: undefined,
|
||||||
isConflictResolutionDirty: false,
|
isConflictResolutionDirty: false,
|
||||||
|
|
||||||
initialNotification: undefined,
|
|
||||||
selectedTab: SettingsV2TabTypes.ScaleTab,
|
selectedTab: SettingsV2TabTypes.ScaleTab,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1052,7 +1050,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
onMaxAutoPilotThroughputChange: this.onMaxAutoPilotThroughputChange,
|
onMaxAutoPilotThroughputChange: this.onMaxAutoPilotThroughputChange,
|
||||||
onScaleSaveableChange: this.onScaleSaveableChange,
|
onScaleSaveableChange: this.onScaleSaveableChange,
|
||||||
onScaleDiscardableChange: this.onScaleDiscardableChange,
|
onScaleDiscardableChange: this.onScaleDiscardableChange,
|
||||||
initialNotification: this.props.settingsTab.pendingNotification(),
|
|
||||||
throughputError: this.state.throughputError,
|
throughputError: this.state.throughputError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ exports[`IndexingPolicyRefreshComponent renders 1`] = `
|
|||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"color": "windowtext",
|
"color": "windowtext",
|
||||||
"fontSize": 14,
|
"fontSize": 14,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
exports[`AddMongoIndexComponent renders 1`] = `
|
exports[`AddMongoIndexComponent renders 1`] = `
|
||||||
<Stack
|
<Stack
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 5,
|
"childrenGap": 5,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ exports[`AddMongoIndexComponent renders 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 20,
|
"childrenGap": 20,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,8 +21,8 @@ exports[`AddMongoIndexComponent renders 1`] = `
|
|||||||
componentRef={[Function]}
|
componentRef={[Function]}
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"paddingLeft": 10,
|
"paddingLeft": 10,
|
||||||
"width": 210,
|
"width": 210,
|
||||||
},
|
},
|
||||||
@@ -34,12 +34,12 @@ exports[`AddMongoIndexComponent renders 1`] = `
|
|||||||
ariaLabel="Index Type 1"
|
ariaLabel="Index Type 1"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
options={
|
options={
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"key": "Single",
|
"key": "Single",
|
||||||
"text": "Single Field",
|
"text": "Single Field",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "Wildcard",
|
"key": "Wildcard",
|
||||||
"text": "Wildcard",
|
"text": "Wildcard",
|
||||||
},
|
},
|
||||||
@@ -48,8 +48,8 @@ exports[`AddMongoIndexComponent renders 1`] = `
|
|||||||
placeholder="Select an index type"
|
placeholder="Select an index type"
|
||||||
selectedKey="Single"
|
selectedKey="Single"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"dropdown": Object {
|
"dropdown": {
|
||||||
"paddingleft": 10,
|
"paddingleft": 10,
|
||||||
"width": 202,
|
"width": 202,
|
||||||
},
|
},
|
||||||
@@ -60,7 +60,7 @@ exports[`AddMongoIndexComponent renders 1`] = `
|
|||||||
ariaLabel="Undo Button 1"
|
ariaLabel="Undo Button 1"
|
||||||
disabled={false}
|
disabled={false}
|
||||||
iconProps={
|
iconProps={
|
||||||
Object {
|
{
|
||||||
"iconName": "Undo",
|
"iconName": "Undo",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,8 +70,8 @@ exports[`AddMongoIndexComponent renders 1`] = `
|
|||||||
<StyledMessageBar
|
<StyledMessageBar
|
||||||
messageBarType={1}
|
messageBarType={1}
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"marginLeft": 10,
|
"marginLeft": 10,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ exports[`MongoIndexingPolicyComponent error shown for collection with compound i
|
|||||||
exports[`MongoIndexingPolicyComponent renders 1`] = `
|
exports[`MongoIndexingPolicyComponent renders 1`] = `
|
||||||
<Stack
|
<Stack
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 20,
|
"childrenGap": 20,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,14 +29,14 @@ exports[`MongoIndexingPolicyComponent renders 1`] = `
|
|||||||
</Text>
|
</Text>
|
||||||
<Stack
|
<Stack
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"width": 600,
|
"width": 600,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 5,
|
"childrenGap": 5,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,8 +47,8 @@ exports[`MongoIndexingPolicyComponent renders 1`] = `
|
|||||||
>
|
>
|
||||||
<StyledWithViewportComponent
|
<StyledWithViewportComponent
|
||||||
columns={
|
columns={
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"fieldName": "definition",
|
"fieldName": "definition",
|
||||||
"isResizable": true,
|
"isResizable": true,
|
||||||
"key": "definition",
|
"key": "definition",
|
||||||
@@ -56,7 +56,7 @@ exports[`MongoIndexingPolicyComponent renders 1`] = `
|
|||||||
"minWidth": 100,
|
"minWidth": 100,
|
||||||
"name": "Definition",
|
"name": "Definition",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"fieldName": "type",
|
"fieldName": "type",
|
||||||
"isResizable": true,
|
"isResizable": true,
|
||||||
"key": "type",
|
"key": "type",
|
||||||
@@ -64,7 +64,7 @@ exports[`MongoIndexingPolicyComponent renders 1`] = `
|
|||||||
"minWidth": 100,
|
"minWidth": 100,
|
||||||
"name": "Type",
|
"name": "Type",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"fieldName": "actionButton",
|
"fieldName": "actionButton",
|
||||||
"isResizable": true,
|
"isResizable": true,
|
||||||
"key": "actionButton",
|
"key": "actionButton",
|
||||||
@@ -75,15 +75,15 @@ exports[`MongoIndexingPolicyComponent renders 1`] = `
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
disableSelectionZone={true}
|
disableSelectionZone={true}
|
||||||
items={Array []}
|
items={[]}
|
||||||
layoutMode={1}
|
layoutMode={1}
|
||||||
onRenderRow={[Function]}
|
onRenderRow={[Function]}
|
||||||
selectionMode={0}
|
selectionMode={0}
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"selectors": Object {
|
"selectors": {
|
||||||
".ms-FocusZone": Object {
|
".ms-FocusZone": {
|
||||||
"paddingTop": 0,
|
"paddingTop": 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -93,14 +93,14 @@ exports[`MongoIndexingPolicyComponent renders 1`] = `
|
|||||||
/>
|
/>
|
||||||
<Stack
|
<Stack
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"width": 600,
|
"width": 600,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,11 +117,11 @@ exports[`MongoIndexingPolicyComponent renders 1`] = `
|
|||||||
</Stack>
|
</Stack>
|
||||||
<Separator
|
<Separator
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Array [
|
"root": [
|
||||||
Object {
|
{
|
||||||
"selectors": Object {
|
"selectors": {
|
||||||
"::before": Object {
|
"::before": {
|
||||||
"background": undefined,
|
"background": undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -132,8 +132,8 @@ exports[`MongoIndexingPolicyComponent renders 1`] = `
|
|||||||
/>
|
/>
|
||||||
<Stack
|
<Stack
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"width": 600,
|
"width": 600,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,10 @@
|
|||||||
import { shallow } from "enzyme";
|
|
||||||
import ko from "knockout";
|
|
||||||
import React from "react";
|
|
||||||
import * as Constants from "../../../../Common/Constants";
|
import * as Constants from "../../../../Common/Constants";
|
||||||
import * as DataModels from "../../../../Contracts/DataModels";
|
|
||||||
import { updateUserContext } from "../../../../UserContext";
|
import { updateUserContext } from "../../../../UserContext";
|
||||||
import Explorer from "../../../Explorer";
|
import Explorer from "../../../Explorer";
|
||||||
import { throughputUnit } from "../SettingsRenderUtils";
|
|
||||||
import { collection } from "../TestUtils";
|
import { collection } from "../TestUtils";
|
||||||
import { ScaleComponent, ScaleComponentProps } from "./ScaleComponent";
|
import { ScaleComponent, ScaleComponentProps } from "./ScaleComponent";
|
||||||
import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents/ThroughputInputAutoPilotV3Component";
|
|
||||||
|
|
||||||
describe("ScaleComponent", () => {
|
describe("ScaleComponent", () => {
|
||||||
const targetThroughput = 6000;
|
|
||||||
|
|
||||||
const baseProps: ScaleComponentProps = {
|
const baseProps: ScaleComponentProps = {
|
||||||
collection: collection,
|
collection: collection,
|
||||||
database: undefined,
|
database: undefined,
|
||||||
@@ -36,39 +28,8 @@ describe("ScaleComponent", () => {
|
|||||||
onScaleDiscardableChange: () => {
|
onScaleDiscardableChange: () => {
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
initialNotification: {
|
|
||||||
description: `Throughput update for ${targetThroughput} ${throughputUnit}`,
|
|
||||||
} as DataModels.Notification,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
it("renders with correct initial notification", () => {
|
|
||||||
let wrapper = shallow(<ScaleComponent {...baseProps} />);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
expect(wrapper.exists(ThroughputInputAutoPilotV3Component)).toEqual(true);
|
|
||||||
expect(wrapper.exists("#throughputApplyLongDelayMessage")).toEqual(true);
|
|
||||||
expect(wrapper.exists("#throughputApplyShortDelayMessage")).toEqual(false);
|
|
||||||
expect(wrapper.find("#throughputApplyLongDelayMessage").html()).toContain(targetThroughput);
|
|
||||||
|
|
||||||
const newCollection = { ...collection };
|
|
||||||
const maxThroughput = 5000;
|
|
||||||
newCollection.offer = ko.observable({
|
|
||||||
manualThroughput: undefined,
|
|
||||||
autoscaleMaxThroughput: maxThroughput,
|
|
||||||
minimumThroughput: 400,
|
|
||||||
id: "offer",
|
|
||||||
offerReplacePending: true,
|
|
||||||
});
|
|
||||||
const newProps = {
|
|
||||||
...baseProps,
|
|
||||||
initialNotification: undefined as DataModels.Notification,
|
|
||||||
collection: newCollection,
|
|
||||||
};
|
|
||||||
wrapper = shallow(<ScaleComponent {...newProps} />);
|
|
||||||
expect(wrapper.exists("#throughputApplyShortDelayMessage")).toEqual(true);
|
|
||||||
expect(wrapper.exists("#throughputApplyLongDelayMessage")).toEqual(false);
|
|
||||||
expect(wrapper.find("#throughputApplyShortDelayMessage").html()).toContain(maxThroughput);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("autoScale disabled", () => {
|
it("autoScale disabled", () => {
|
||||||
const scaleComponent = new ScaleComponent(baseProps);
|
const scaleComponent = new ScaleComponent(baseProps);
|
||||||
expect(scaleComponent.isAutoScaleEnabled()).toEqual(false);
|
expect(scaleComponent.isAutoScaleEnabled()).toEqual(false);
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import * as AutoPilotUtils from "../../../../Utils/AutoPilotUtils";
|
|||||||
import { isRunningOnNationalCloud } from "../../../../Utils/CloudUtils";
|
import { isRunningOnNationalCloud } from "../../../../Utils/CloudUtils";
|
||||||
import {
|
import {
|
||||||
getTextFieldStyles,
|
getTextFieldStyles,
|
||||||
getThroughputApplyLongDelayMessage,
|
|
||||||
getThroughputApplyShortDelayMessage,
|
getThroughputApplyShortDelayMessage,
|
||||||
subComponentStackProps,
|
subComponentStackProps,
|
||||||
throughputUnit,
|
throughputUnit,
|
||||||
@@ -34,7 +33,6 @@ export interface ScaleComponentProps {
|
|||||||
onMaxAutoPilotThroughputChange: (newThroughput: number) => void;
|
onMaxAutoPilotThroughputChange: (newThroughput: number) => void;
|
||||||
onScaleSaveableChange: (isScaleSaveable: boolean) => void;
|
onScaleSaveableChange: (isScaleSaveable: boolean) => void;
|
||||||
onScaleDiscardableChange: (isScaleDiscardable: boolean) => void;
|
onScaleDiscardableChange: (isScaleDiscardable: boolean) => void;
|
||||||
initialNotification: DataModels.Notification;
|
|
||||||
throughputError?: string;
|
throughputError?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,10 +100,6 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public getInitialNotificationElement = (): JSX.Element => {
|
public getInitialNotificationElement = (): JSX.Element => {
|
||||||
if (this.props.initialNotification) {
|
|
||||||
return this.getLongDelayMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.offer?.offerReplacePending) {
|
if (this.offer?.offerReplacePending) {
|
||||||
const throughput = this.offer.manualThroughput || this.offer.autoscaleMaxThroughput;
|
const throughput = this.offer.manualThroughput || this.offer.autoscaleMaxThroughput;
|
||||||
return getThroughputApplyShortDelayMessage(
|
return getThroughputApplyShortDelayMessage(
|
||||||
@@ -120,26 +114,6 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
public getLongDelayMessage = (): JSX.Element => {
|
|
||||||
const matches: string[] = this.props.initialNotification?.description.match(
|
|
||||||
`Throughput update for (.*) ${throughputUnit}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
const throughput = this.props.throughputBaseline;
|
|
||||||
const targetThroughput: number = matches.length > 1 && Number(matches[1]);
|
|
||||||
if (targetThroughput) {
|
|
||||||
return getThroughputApplyLongDelayMessage(
|
|
||||||
this.props.wasAutopilotOriginallySet,
|
|
||||||
throughput,
|
|
||||||
throughputUnit,
|
|
||||||
this.databaseId,
|
|
||||||
this.collectionId,
|
|
||||||
targetThroughput,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return <></>;
|
|
||||||
};
|
|
||||||
|
|
||||||
private getThroughputInputComponent = (): JSX.Element => (
|
private getThroughputInputComponent = (): JSX.Element => (
|
||||||
<ThroughputInputAutoPilotV3Component
|
<ThroughputInputAutoPilotV3Component
|
||||||
databaseAccount={userContext?.databaseAccount}
|
databaseAccount={userContext?.databaseAccount}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -3,14 +3,14 @@
|
|||||||
exports[`ComputedPropertiesComponent renders 1`] = `
|
exports[`ComputedPropertiesComponent renders 1`] = `
|
||||||
<Stack
|
<Stack
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 5,
|
"childrenGap": 5,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"marginBottom": "10px",
|
"marginBottom": "10px",
|
||||||
"marginLeft": "30px",
|
"marginLeft": "30px",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
exports[`ConflictResolutionComponent Path text field displayed 1`] = `
|
exports[`ConflictResolutionComponent Path text field displayed 1`] = `
|
||||||
<Stack
|
<Stack
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 20,
|
"childrenGap": 20,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,12 +12,12 @@ exports[`ConflictResolutionComponent Path text field displayed 1`] = `
|
|||||||
label="Mode"
|
label="Mode"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
options={
|
options={
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"key": "LastWriterWins",
|
"key": "LastWriterWins",
|
||||||
"text": "Last Write Wins (default)",
|
"text": "Last Write Wins (default)",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "Custom",
|
"key": "Custom",
|
||||||
"text": "Merge Procedure (custom)",
|
"text": "Merge Procedure (custom)",
|
||||||
},
|
},
|
||||||
@@ -25,19 +25,19 @@ exports[`ConflictResolutionComponent Path text field displayed 1`] = `
|
|||||||
}
|
}
|
||||||
selectedKey="LastWriterWins"
|
selectedKey="LastWriterWins"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"flexContainer": Array [
|
"flexContainer": [
|
||||||
Object {
|
{
|
||||||
"columnGap": "default",
|
"columnGap": "default",
|
||||||
"display": "default",
|
"display": "default",
|
||||||
"selectors": Object {
|
"selectors": {
|
||||||
".ms-ChoiceField-field.is-checked::after": Object {
|
".ms-ChoiceField-field.is-checked::after": {
|
||||||
"borderColor": undefined,
|
"borderColor": undefined,
|
||||||
},
|
},
|
||||||
".ms-ChoiceField-field.is-checked::before": Object {
|
".ms-ChoiceField-field.is-checked::before": {
|
||||||
"borderColor": undefined,
|
"borderColor": undefined,
|
||||||
},
|
},
|
||||||
".ms-ChoiceField-wrapper label": Object {
|
".ms-ChoiceField-wrapper label": {
|
||||||
"fontFamily": undefined,
|
"fontFamily": undefined,
|
||||||
"fontSize": 14,
|
"fontSize": 14,
|
||||||
"padding": "2px 5px",
|
"padding": "2px 5px",
|
||||||
@@ -55,12 +55,12 @@ exports[`ConflictResolutionComponent Path text field displayed 1`] = `
|
|||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
onRenderLabel={[Function]}
|
onRenderLabel={[Function]}
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"fieldGroup": Object {
|
"fieldGroup": {
|
||||||
"borderColor": "",
|
"borderColor": "",
|
||||||
"height": 25,
|
"height": 25,
|
||||||
"selectors": Object {
|
"selectors": {
|
||||||
":disabled": Object {
|
":disabled": {
|
||||||
"backgroundColor": undefined,
|
"backgroundColor": undefined,
|
||||||
"borderColor": undefined,
|
"borderColor": undefined,
|
||||||
},
|
},
|
||||||
@@ -77,7 +77,7 @@ exports[`ConflictResolutionComponent Path text field displayed 1`] = `
|
|||||||
exports[`ConflictResolutionComponent Sproc text field displayed 1`] = `
|
exports[`ConflictResolutionComponent Sproc text field displayed 1`] = `
|
||||||
<Stack
|
<Stack
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 20,
|
"childrenGap": 20,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,12 +86,12 @@ exports[`ConflictResolutionComponent Sproc text field displayed 1`] = `
|
|||||||
label="Mode"
|
label="Mode"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
options={
|
options={
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"key": "LastWriterWins",
|
"key": "LastWriterWins",
|
||||||
"text": "Last Write Wins (default)",
|
"text": "Last Write Wins (default)",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "Custom",
|
"key": "Custom",
|
||||||
"text": "Merge Procedure (custom)",
|
"text": "Merge Procedure (custom)",
|
||||||
},
|
},
|
||||||
@@ -99,19 +99,19 @@ exports[`ConflictResolutionComponent Sproc text field displayed 1`] = `
|
|||||||
}
|
}
|
||||||
selectedKey="Custom"
|
selectedKey="Custom"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"flexContainer": Array [
|
"flexContainer": [
|
||||||
Object {
|
{
|
||||||
"columnGap": "default",
|
"columnGap": "default",
|
||||||
"display": "default",
|
"display": "default",
|
||||||
"selectors": Object {
|
"selectors": {
|
||||||
".ms-ChoiceField-field.is-checked::after": Object {
|
".ms-ChoiceField-field.is-checked::after": {
|
||||||
"borderColor": "",
|
"borderColor": "",
|
||||||
},
|
},
|
||||||
".ms-ChoiceField-field.is-checked::before": Object {
|
".ms-ChoiceField-field.is-checked::before": {
|
||||||
"borderColor": "",
|
"borderColor": "",
|
||||||
},
|
},
|
||||||
".ms-ChoiceField-wrapper label": Object {
|
".ms-ChoiceField-wrapper label": {
|
||||||
"fontFamily": undefined,
|
"fontFamily": undefined,
|
||||||
"fontSize": 14,
|
"fontSize": 14,
|
||||||
"padding": "2px 5px",
|
"padding": "2px 5px",
|
||||||
@@ -129,12 +129,12 @@ exports[`ConflictResolutionComponent Sproc text field displayed 1`] = `
|
|||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
onRenderLabel={[Function]}
|
onRenderLabel={[Function]}
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"fieldGroup": Object {
|
"fieldGroup": {
|
||||||
"borderColor": "",
|
"borderColor": "",
|
||||||
"height": 25,
|
"height": 25,
|
||||||
"selectors": Object {
|
"selectors": {
|
||||||
":disabled": Object {
|
":disabled": {
|
||||||
"backgroundColor": undefined,
|
"backgroundColor": undefined,
|
||||||
"borderColor": undefined,
|
"borderColor": undefined,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
exports[`IndexingPolicyComponent renders 1`] = `
|
exports[`IndexingPolicyComponent renders 1`] = `
|
||||||
<Stack
|
<Stack
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 5,
|
"childrenGap": 5,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`ScaleComponent renders with correct initial notification 1`] = `
|
|
||||||
<Stack
|
|
||||||
tokens={
|
|
||||||
Object {
|
|
||||||
"childrenGap": 20,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<StyledMessageBar
|
|
||||||
messageBarType={5}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
id="throughputApplyLongDelayMessage"
|
|
||||||
styles={
|
|
||||||
Object {
|
|
||||||
"root": Object {
|
|
||||||
"color": "windowtext",
|
|
||||||
"fontSize": 14,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
A request to increase the throughput is currently in progress. This operation will take 1-3 business days to complete. View the latest status in Notifications.
|
|
||||||
<br />
|
|
||||||
Database: test, Container: test
|
|
||||||
, Current autoscale throughput: 100 - 1000 RU/s, Target autoscale throughput: 600 - 6000 RU/s
|
|
||||||
</Text>
|
|
||||||
</StyledMessageBar>
|
|
||||||
<Stack
|
|
||||||
tokens={
|
|
||||||
Object {
|
|
||||||
"childrenGap": 20,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ThroughputInputAutoPilotV3Component
|
|
||||||
canExceedMaximumValue={true}
|
|
||||||
collectionName="test"
|
|
||||||
databaseName="test"
|
|
||||||
isAutoPilotSelected={false}
|
|
||||||
isEmulator={false}
|
|
||||||
isEnabled={true}
|
|
||||||
isFixed={false}
|
|
||||||
label="Throughput (6,000 - unlimited RU/s)"
|
|
||||||
maxAutoPilotThroughput={4000}
|
|
||||||
maxAutoPilotThroughputBaseline={4000}
|
|
||||||
maximum={1000000}
|
|
||||||
minimum={6000}
|
|
||||||
onAutoPilotSelected={[Function]}
|
|
||||||
onMaxAutoPilotThroughputChange={[Function]}
|
|
||||||
onScaleDiscardableChange={[Function]}
|
|
||||||
onScaleSaveableChange={[Function]}
|
|
||||||
onThroughputChange={[Function]}
|
|
||||||
spendAckChecked={false}
|
|
||||||
throughput={1000}
|
|
||||||
throughputBaseline={1000}
|
|
||||||
usageSizeInKB={100}
|
|
||||||
wasAutopilotOriginallySet={true}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
`;
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@ exports[`ToolTipLabelComponent renders 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 6,
|
"childrenGap": 6,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ exports[`ToolTipLabelComponent renders 1`] = `
|
|||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ exports[`ToolTipLabelComponent renders 1`] = `
|
|||||||
</Text>
|
</Text>
|
||||||
<StyledTooltipHostBase
|
<StyledTooltipHostBase
|
||||||
calloutProps={
|
calloutProps={
|
||||||
Object {
|
{
|
||||||
"gapSpace": 0,
|
"gapSpace": 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,8 +33,8 @@ exports[`ToolTipLabelComponent renders 1`] = `
|
|||||||
}
|
}
|
||||||
directionalHint={12}
|
directionalHint={12}
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"display": "inline-block",
|
"display": "inline-block",
|
||||||
"float": "right",
|
"float": "right",
|
||||||
},
|
},
|
||||||
@@ -45,8 +45,8 @@ exports[`ToolTipLabelComponent renders 1`] = `
|
|||||||
ariaLabel="Info"
|
ariaLabel="Info"
|
||||||
iconName="Info"
|
iconName="Info"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"marginBottom": -3,
|
"marginBottom": -3,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ describe("SettingsUtils", () => {
|
|||||||
readSettings: undefined,
|
readSettings: undefined,
|
||||||
onSettingsClick: undefined,
|
onSettingsClick: undefined,
|
||||||
loadOffer: undefined,
|
loadOffer: undefined,
|
||||||
getPendingThroughputSplitNotification: undefined,
|
|
||||||
} as ViewModels.Database;
|
} as ViewModels.Database;
|
||||||
};
|
};
|
||||||
newCollection.offer(undefined);
|
newCollection.offer(undefined);
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
itemKey="ScaleTab"
|
itemKey="ScaleTab"
|
||||||
key="ScaleTab"
|
key="ScaleTab"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"marginTop": 20,
|
"marginTop": 20,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ScaleComponent
|
<ScaleComponent
|
||||||
collection={
|
collection={
|
||||||
Object {
|
{
|
||||||
"analyticalStorageTtl": [Function],
|
"analyticalStorageTtl": [Function],
|
||||||
"changeFeedPolicy": [Function],
|
"changeFeedPolicy": [Function],
|
||||||
"computedProperties": [Function],
|
"computedProperties": [Function],
|
||||||
@@ -36,7 +36,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"phoenixClient": PhoenixClient {
|
"phoenixClient": PhoenixClient {
|
||||||
"armResourceId": undefined,
|
"armResourceId": undefined,
|
||||||
"retryOptions": Object {
|
"retryOptions": {
|
||||||
"maxTimeout": 5000,
|
"maxTimeout": 5000,
|
||||||
"minTimeout": 5000,
|
"minTimeout": 5000,
|
||||||
"retries": 3,
|
"retries": 3,
|
||||||
@@ -60,16 +60,16 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"id": [Function],
|
"id": [Function],
|
||||||
"indexingPolicy": [Function],
|
"indexingPolicy": [Function],
|
||||||
"offer": [Function],
|
"offer": [Function],
|
||||||
"partitionKey": Object {
|
"partitionKey": {
|
||||||
"kind": "hash",
|
"kind": "hash",
|
||||||
"paths": Array [],
|
"paths": [],
|
||||||
"version": 2,
|
"version": 2,
|
||||||
},
|
},
|
||||||
"partitionKeyProperties": Array [
|
"partitionKeyProperties": [
|
||||||
"partitionKey",
|
"partitionKey",
|
||||||
],
|
],
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
"uniqueKeyPolicy": Object {},
|
"uniqueKeyPolicy": {},
|
||||||
"usageSizeInKB": [Function],
|
"usageSizeInKB": [Function],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
itemKey="SubSettingsTab"
|
itemKey="SubSettingsTab"
|
||||||
key="SubSettingsTab"
|
key="SubSettingsTab"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"marginTop": 20,
|
"marginTop": 20,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
changeFeedPolicyBaseline="Off"
|
changeFeedPolicyBaseline="Off"
|
||||||
changeFeedPolicyVisible={false}
|
changeFeedPolicyVisible={false}
|
||||||
collection={
|
collection={
|
||||||
Object {
|
{
|
||||||
"analyticalStorageTtl": [Function],
|
"analyticalStorageTtl": [Function],
|
||||||
"changeFeedPolicy": [Function],
|
"changeFeedPolicy": [Function],
|
||||||
"computedProperties": [Function],
|
"computedProperties": [Function],
|
||||||
@@ -113,7 +113,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"phoenixClient": PhoenixClient {
|
"phoenixClient": PhoenixClient {
|
||||||
"armResourceId": undefined,
|
"armResourceId": undefined,
|
||||||
"retryOptions": Object {
|
"retryOptions": {
|
||||||
"maxTimeout": 5000,
|
"maxTimeout": 5000,
|
||||||
"minTimeout": 5000,
|
"minTimeout": 5000,
|
||||||
"retries": 3,
|
"retries": 3,
|
||||||
@@ -137,16 +137,16 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"id": [Function],
|
"id": [Function],
|
||||||
"indexingPolicy": [Function],
|
"indexingPolicy": [Function],
|
||||||
"offer": [Function],
|
"offer": [Function],
|
||||||
"partitionKey": Object {
|
"partitionKey": {
|
||||||
"kind": "hash",
|
"kind": "hash",
|
||||||
"paths": Array [],
|
"paths": [],
|
||||||
"version": 2,
|
"version": 2,
|
||||||
},
|
},
|
||||||
"partitionKeyProperties": Array [
|
"partitionKeyProperties": [
|
||||||
"partitionKey",
|
"partitionKey",
|
||||||
],
|
],
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
"uniqueKeyPolicy": Object {},
|
"uniqueKeyPolicy": {},
|
||||||
"usageSizeInKB": [Function],
|
"usageSizeInKB": [Function],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,25 +174,25 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
itemKey="IndexingPolicyTab"
|
itemKey="IndexingPolicyTab"
|
||||||
key="IndexingPolicyTab"
|
key="IndexingPolicyTab"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"marginTop": 20,
|
"marginTop": 20,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<IndexingPolicyComponent
|
<IndexingPolicyComponent
|
||||||
indexingPolicyContent={
|
indexingPolicyContent={
|
||||||
Object {
|
{
|
||||||
"automatic": true,
|
"automatic": true,
|
||||||
"excludedPaths": Array [],
|
"excludedPaths": [],
|
||||||
"includedPaths": Array [],
|
"includedPaths": [],
|
||||||
"indexingMode": "consistent",
|
"indexingMode": "consistent",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
indexingPolicyContentBaseline={
|
indexingPolicyContentBaseline={
|
||||||
Object {
|
{
|
||||||
"automatic": true,
|
"automatic": true,
|
||||||
"excludedPaths": Array [],
|
"excludedPaths": [],
|
||||||
"includedPaths": Array [],
|
"includedPaths": [],
|
||||||
"indexingMode": "consistent",
|
"indexingMode": "consistent",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,14 +210,14 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
itemKey="PartitionKeyTab"
|
itemKey="PartitionKeyTab"
|
||||||
key="PartitionKeyTab"
|
key="PartitionKeyTab"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"marginTop": 20,
|
"marginTop": 20,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<PartitionKeyComponent
|
<PartitionKeyComponent
|
||||||
collection={
|
collection={
|
||||||
Object {
|
{
|
||||||
"analyticalStorageTtl": [Function],
|
"analyticalStorageTtl": [Function],
|
||||||
"changeFeedPolicy": [Function],
|
"changeFeedPolicy": [Function],
|
||||||
"computedProperties": [Function],
|
"computedProperties": [Function],
|
||||||
@@ -230,7 +230,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"phoenixClient": PhoenixClient {
|
"phoenixClient": PhoenixClient {
|
||||||
"armResourceId": undefined,
|
"armResourceId": undefined,
|
||||||
"retryOptions": Object {
|
"retryOptions": {
|
||||||
"maxTimeout": 5000,
|
"maxTimeout": 5000,
|
||||||
"minTimeout": 5000,
|
"minTimeout": 5000,
|
||||||
"retries": 3,
|
"retries": 3,
|
||||||
@@ -254,16 +254,16 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"id": [Function],
|
"id": [Function],
|
||||||
"indexingPolicy": [Function],
|
"indexingPolicy": [Function],
|
||||||
"offer": [Function],
|
"offer": [Function],
|
||||||
"partitionKey": Object {
|
"partitionKey": {
|
||||||
"kind": "hash",
|
"kind": "hash",
|
||||||
"paths": Array [],
|
"paths": [],
|
||||||
"version": 2,
|
"version": 2,
|
||||||
},
|
},
|
||||||
"partitionKeyProperties": Array [
|
"partitionKeyProperties": [
|
||||||
"partitionKey",
|
"partitionKey",
|
||||||
],
|
],
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
"uniqueKeyPolicy": Object {},
|
"uniqueKeyPolicy": {},
|
||||||
"usageSizeInKB": [Function],
|
"usageSizeInKB": [Function],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -276,7 +276,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"phoenixClient": PhoenixClient {
|
"phoenixClient": PhoenixClient {
|
||||||
"armResourceId": undefined,
|
"armResourceId": undefined,
|
||||||
"retryOptions": Object {
|
"retryOptions": {
|
||||||
"maxTimeout": 5000,
|
"maxTimeout": 5000,
|
||||||
"minTimeout": 5000,
|
"minTimeout": 5000,
|
||||||
"retries": 3,
|
"retries": 3,
|
||||||
@@ -301,23 +301,23 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
itemKey="ComputedPropertiesTab"
|
itemKey="ComputedPropertiesTab"
|
||||||
key="ComputedPropertiesTab"
|
key="ComputedPropertiesTab"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"marginTop": 20,
|
"marginTop": 20,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ComputedPropertiesComponent
|
<ComputedPropertiesComponent
|
||||||
computedPropertiesContent={
|
computedPropertiesContent={
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"name": "queryName",
|
"name": "queryName",
|
||||||
"query": "query",
|
"query": "query",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
computedPropertiesContentBaseline={
|
computedPropertiesContentBaseline={
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"name": "queryName",
|
"name": "queryName",
|
||||||
"query": "query",
|
"query": "query",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
<Stack>
|
<Stack>
|
||||||
<Text
|
<Text
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
"marginTop": 15,
|
"marginTop": 15,
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
id="throughputSpendElement"
|
id="throughputSpendElement"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"marginTop": 5,
|
"marginTop": 5,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
</Stack>
|
</Stack>
|
||||||
<Text
|
<Text
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"marginTop": 15,
|
"marginTop": 15,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,8 +63,8 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
<Text
|
<Text
|
||||||
id="manualToAutoscaleDisclaimerElement"
|
id="manualToAutoscaleDisclaimerElement"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"color": "windowtext",
|
"color": "windowtext",
|
||||||
"fontSize": 14,
|
"fontSize": 14,
|
||||||
},
|
},
|
||||||
@@ -81,8 +81,8 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"color": "windowtext",
|
"color": "windowtext",
|
||||||
"fontSize": 14,
|
"fontSize": 14,
|
||||||
},
|
},
|
||||||
@@ -102,8 +102,8 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
<Text
|
<Text
|
||||||
id="updateThroughputDelayedApplyWarningMessage"
|
id="updateThroughputDelayedApplyWarningMessage"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"color": "windowtext",
|
"color": "windowtext",
|
||||||
"fontSize": 14,
|
"fontSize": 14,
|
||||||
},
|
},
|
||||||
@@ -114,8 +114,8 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"color": "windowtext",
|
"color": "windowtext",
|
||||||
"fontSize": 14,
|
"fontSize": 14,
|
||||||
},
|
},
|
||||||
@@ -134,8 +134,8 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
<Text
|
<Text
|
||||||
id="throughputApplyShortDelayMessage"
|
id="throughputApplyShortDelayMessage"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"color": "windowtext",
|
"color": "windowtext",
|
||||||
"fontSize": 14,
|
"fontSize": 14,
|
||||||
},
|
},
|
||||||
@@ -150,8 +150,8 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
<Text
|
<Text
|
||||||
id="throughputApplyLongDelayMessage"
|
id="throughputApplyLongDelayMessage"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"color": "windowtext",
|
"color": "windowtext",
|
||||||
"fontSize": 14,
|
"fontSize": 14,
|
||||||
},
|
},
|
||||||
@@ -165,8 +165,8 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"color": "windowtext",
|
"color": "windowtext",
|
||||||
"fontSize": 14,
|
"fontSize": 14,
|
||||||
},
|
},
|
||||||
@@ -179,8 +179,8 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"color": "windowtext",
|
"color": "windowtext",
|
||||||
"fontSize": 14,
|
"fontSize": 14,
|
||||||
},
|
},
|
||||||
@@ -191,8 +191,8 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"color": "windowtext",
|
"color": "windowtext",
|
||||||
"fontSize": 14,
|
"fontSize": 14,
|
||||||
},
|
},
|
||||||
@@ -211,8 +211,8 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"color": "windowtext",
|
"color": "windowtext",
|
||||||
"fontSize": 14,
|
"fontSize": 14,
|
||||||
},
|
},
|
||||||
@@ -247,15 +247,15 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 5,
|
"childrenGap": 5,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"color": "windowtext",
|
"color": "windowtext",
|
||||||
"fontSize": 14,
|
"fontSize": 14,
|
||||||
},
|
},
|
||||||
@@ -270,8 +270,8 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
</Stack>
|
</Stack>
|
||||||
<Text
|
<Text
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"color": "windowtext",
|
"color": "windowtext",
|
||||||
"fontSize": 14,
|
"fontSize": 14,
|
||||||
},
|
},
|
||||||
@@ -287,8 +287,8 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"color": "windowtext",
|
"color": "windowtext",
|
||||||
"fontSize": 14,
|
"fontSize": 14,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import {
|
|||||||
TextField,
|
TextField,
|
||||||
Toggle,
|
Toggle,
|
||||||
} from "@fluentui/react";
|
} from "@fluentui/react";
|
||||||
import { TFunction } from "i18next";
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {
|
import {
|
||||||
ChoiceItem,
|
ChoiceItem,
|
||||||
@@ -100,7 +99,7 @@ export interface SmartUiComponentProps {
|
|||||||
onInputChange: (input: AnyDisplay, newValue: InputType) => void;
|
onInputChange: (input: AnyDisplay, newValue: InputType) => void;
|
||||||
onError: (hasError: boolean) => void;
|
onError: (hasError: boolean) => void;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
getTranslation: TFunction;
|
getTranslation: (messageKey: string, namespace?: string) => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SmartUiComponentState {
|
interface SmartUiComponentState {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ exports[`SmartUiComponent disable all inputs 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ exports[`SmartUiComponent disable all inputs 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ exports[`SmartUiComponent disable all inputs 1`] = `
|
|||||||
aria-labelledby="description-label"
|
aria-labelledby="description-label"
|
||||||
id="description-text-display"
|
id="description-text-display"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"whiteSpace": "pre-line",
|
"whiteSpace": "pre-line",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,7 @@ exports[`SmartUiComponent disable all inputs 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,14 +70,14 @@ exports[`SmartUiComponent disable all inputs 1`] = `
|
|||||||
</StyledLabelBase>
|
</StyledLabelBase>
|
||||||
<Stack
|
<Stack
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"width": 400,
|
"width": 400,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 2,
|
"childrenGap": 2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,7 +107,7 @@ exports[`SmartUiComponent disable all inputs 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,11 +133,11 @@ exports[`SmartUiComponent disable all inputs 1`] = `
|
|||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
step={10}
|
step={10}
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"width": 400,
|
"width": 400,
|
||||||
},
|
},
|
||||||
"valueLabel": Object {
|
"valueLabel": {
|
||||||
"color": "#393939",
|
"color": "#393939",
|
||||||
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
|
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
|
||||||
"fontSize": 12,
|
"fontSize": 12,
|
||||||
@@ -157,7 +157,7 @@ exports[`SmartUiComponent disable all inputs 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -178,7 +178,7 @@ exports[`SmartUiComponent disable all inputs 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,8 +199,8 @@ exports[`SmartUiComponent disable all inputs 1`] = `
|
|||||||
id="containerId-textField-input"
|
id="containerId-textField-input"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"width": 400,
|
"width": 400,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -219,7 +219,7 @@ exports[`SmartUiComponent disable all inputs 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -243,8 +243,8 @@ exports[`SmartUiComponent disable all inputs 1`] = `
|
|||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
onText="Enabled"
|
onText="Enabled"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"width": 400,
|
"width": 400,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -261,7 +261,7 @@ exports[`SmartUiComponent disable all inputs 1`] = `
|
|||||||
<Stack
|
<Stack
|
||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,16 +282,16 @@ exports[`SmartUiComponent disable all inputs 1`] = `
|
|||||||
id="database-dropdown-input"
|
id="database-dropdown-input"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
options={
|
options={
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"key": "db1",
|
"key": "db1",
|
||||||
"text": "Database 1",
|
"text": "Database 1",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "db2",
|
"key": "db2",
|
||||||
"text": "Database 2",
|
"text": "Database 2",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "db3",
|
"key": "db3",
|
||||||
"text": "Database 3",
|
"text": "Database 3",
|
||||||
},
|
},
|
||||||
@@ -299,13 +299,13 @@ exports[`SmartUiComponent disable all inputs 1`] = `
|
|||||||
}
|
}
|
||||||
selectedKey="db2"
|
selectedKey="db2"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"dropdown": Object {
|
"dropdown": {
|
||||||
"color": "#393939",
|
"color": "#393939",
|
||||||
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
|
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
|
||||||
"fontSize": 12,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
"root": Object {
|
"root": {
|
||||||
"width": 400,
|
"width": 400,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -323,7 +323,7 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
|
|||||||
<Stack
|
<Stack
|
||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -335,7 +335,7 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
|
|||||||
<Stack
|
<Stack
|
||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,7 +347,7 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
|
|||||||
aria-labelledby="description-label"
|
aria-labelledby="description-label"
|
||||||
id="description-text-display"
|
id="description-text-display"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"whiteSpace": "pre-line",
|
"whiteSpace": "pre-line",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -372,7 +372,7 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
|
|||||||
<Stack
|
<Stack
|
||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -389,14 +389,14 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
|
|||||||
</StyledLabelBase>
|
</StyledLabelBase>
|
||||||
<Stack
|
<Stack
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"width": 400,
|
"width": 400,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 2,
|
"childrenGap": 2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -425,7 +425,7 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
|
|||||||
<Stack
|
<Stack
|
||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -450,11 +450,11 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
|
|||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
step={10}
|
step={10}
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"width": 400,
|
"width": 400,
|
||||||
},
|
},
|
||||||
"valueLabel": Object {
|
"valueLabel": {
|
||||||
"color": "#393939",
|
"color": "#393939",
|
||||||
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
|
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
|
||||||
"fontSize": 12,
|
"fontSize": 12,
|
||||||
@@ -474,7 +474,7 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
|
|||||||
<Stack
|
<Stack
|
||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -495,7 +495,7 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
|
|||||||
<Stack
|
<Stack
|
||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -515,8 +515,8 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
|
|||||||
id="containerId-textField-input"
|
id="containerId-textField-input"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"width": 400,
|
"width": 400,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -535,7 +535,7 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
|
|||||||
<Stack
|
<Stack
|
||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -558,8 +558,8 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
|
|||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
onText="Enabled"
|
onText="Enabled"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"root": Object {
|
"root": {
|
||||||
"width": 400,
|
"width": 400,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -576,7 +576,7 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
|
|||||||
<Stack
|
<Stack
|
||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
{
|
||||||
"childrenGap": 10,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -596,16 +596,16 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
|
|||||||
id="database-dropdown-input"
|
id="database-dropdown-input"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
options={
|
options={
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"key": "db1",
|
"key": "db1",
|
||||||
"text": "Database 1",
|
"text": "Database 1",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "db2",
|
"key": "db2",
|
||||||
"text": "Database 2",
|
"text": "Database 2",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "db3",
|
"key": "db3",
|
||||||
"text": "Database 3",
|
"text": "Database 3",
|
||||||
},
|
},
|
||||||
@@ -613,13 +613,13 @@ exports[`SmartUiComponent should render and honor input's hidden, disabled state
|
|||||||
}
|
}
|
||||||
selectedKey="db2"
|
selectedKey="db2"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"dropdown": Object {
|
"dropdown": {
|
||||||
"color": "#393939",
|
"color": "#393939",
|
||||||
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
|
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
|
||||||
"fontSize": 12,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
"root": Object {
|
"root": {
|
||||||
"width": 400,
|
"width": 400,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,16 @@
|
|||||||
@import "../../../../less/Common/Constants";
|
@import "../../../../less/Common/Constants";
|
||||||
|
|
||||||
.tabComponentContainer {
|
.tabComponentContainer {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
.flex-display();
|
.flex-display();
|
||||||
.flex-direction();
|
.flex-direction();
|
||||||
|
|
||||||
.tabSwitch {
|
.tabSwitch {
|
||||||
margin-left: @LargeSpace;
|
margin-left: @LargeSpace;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
.tab {
|
.tab {
|
||||||
margin-right: @MediumSpace;
|
margin-right: @MediumSpace;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.toggleSwitch {
|
|
||||||
.toggleSwitch();
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectedToggle {
|
|
||||||
.selectedToggle();
|
|
||||||
}
|
|
||||||
|
|
||||||
.unselectedToggle {
|
|
||||||
.unselectedToggle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { AccessibleElement } from "../../Controls/AccessibleElement/AccessibleElement";
|
import { Pivot, PivotItem } from "@fluentui/react";
|
||||||
import "./TabComponent.less";
|
import "./TabComponent.less";
|
||||||
|
|
||||||
export interface TabContent {
|
export interface TabContent {
|
||||||
@@ -35,58 +35,36 @@ export class TabComponent extends React.Component<TabComponentProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private setActiveTab(index: number): void {
|
private setActiveTab(index: number): void {
|
||||||
this.setState({ activeTabIndex: index });
|
|
||||||
this.props.onTabIndexChange(index);
|
this.props.onTabIndexChange(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderTabTitles(): JSX.Element[] {
|
|
||||||
return this.props.tabs.map((tab: Tab, index: number) => {
|
|
||||||
if (!tab.isVisible()) {
|
|
||||||
return <React.Fragment key={index} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
let className = "toggleSwitch";
|
|
||||||
let ariaselected;
|
|
||||||
if (index === this.props.currentTabIndex) {
|
|
||||||
className += " selectedToggle";
|
|
||||||
ariaselected = true;
|
|
||||||
} else {
|
|
||||||
className += " unselectedToggle";
|
|
||||||
ariaselected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="tab" key={index}>
|
|
||||||
<AccessibleElement
|
|
||||||
as="span"
|
|
||||||
className={className}
|
|
||||||
role="tab"
|
|
||||||
onActivated={() => this.setActiveTab(index)}
|
|
||||||
aria-label={`Select tab: ${tab.title}`}
|
|
||||||
aria-selected={ariaselected}
|
|
||||||
>
|
|
||||||
{tab.title}
|
|
||||||
</AccessibleElement>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
const currentTabContent = this.props.tabs[this.props.currentTabIndex].content;
|
const { tabs, currentTabIndex, hideHeader } = this.props;
|
||||||
|
const currentTabContent = tabs[currentTabIndex].content;
|
||||||
let className = "tabComponentContent";
|
let className = "tabComponentContent";
|
||||||
if (currentTabContent.className) {
|
if (currentTabContent.className) {
|
||||||
className += ` ${currentTabContent.className}`;
|
className += ` ${currentTabContent.className}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tabComponentContainer">
|
<div className="tabComponentContainer">
|
||||||
{!this.props.hideHeader && (
|
<div className="tabs tabSwitch">
|
||||||
<div className="tabs tabSwitch" role="tablist">
|
{!hideHeader && (
|
||||||
{this.renderTabTitles()}
|
<Pivot
|
||||||
</div>
|
aria-label="Tab navigation"
|
||||||
)}
|
selectedKey={currentTabIndex.toString()}
|
||||||
<div className={className}>{currentTabContent.render()}</div>
|
linkSize="normal"
|
||||||
|
onLinkClick={(item) => this.setActiveTab(parseInt(item?.props.itemKey || ""))}
|
||||||
|
>
|
||||||
|
{tabs.map((tab: Tab, index: number) => {
|
||||||
|
if (!tab.isVisible()) {
|
||||||
|
return null; // Skip rendering invisible tabs
|
||||||
|
}
|
||||||
|
return <PivotItem key={index} headerText={tab.title} itemKey={index.toString()} />;
|
||||||
|
})}
|
||||||
|
</Pivot>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={className}>{tabs[currentTabIndex].content.render()}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
aria-label="Throughput header"
|
aria-label="Throughput header"
|
||||||
key=".0:$.$.1"
|
key=".0:$.$.1"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
"lineHeight": "20px",
|
"lineHeight": "20px",
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
aria-label="Throughput header"
|
aria-label="Throughput header"
|
||||||
className="css-110"
|
className="css-110"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
"lineHeight": "20px",
|
"lineHeight": "20px",
|
||||||
}
|
}
|
||||||
@@ -62,9 +62,9 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
delay={1}
|
delay={1}
|
||||||
styles={[Function]}
|
styles={[Function]}
|
||||||
theme={
|
theme={
|
||||||
Object {
|
{
|
||||||
"disableGlobalClassNames": false,
|
"disableGlobalClassNames": false,
|
||||||
"effects": Object {
|
"effects": {
|
||||||
"elevation16": "0 6.4px 14.4px 0 rgba(0, 0, 0, 0.132), 0 1.2px 3.6px 0 rgba(0, 0, 0, 0.108)",
|
"elevation16": "0 6.4px 14.4px 0 rgba(0, 0, 0, 0.132), 0 1.2px 3.6px 0 rgba(0, 0, 0, 0.108)",
|
||||||
"elevation4": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
|
"elevation4": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
|
||||||
"elevation64": "0 25.6px 57.6px 0 rgba(0, 0, 0, 0.22), 0 4.8px 14.4px 0 rgba(0, 0, 0, 0.18)",
|
"elevation64": "0 25.6px 57.6px 0 rgba(0, 0, 0, 0.22), 0 4.8px 14.4px 0 rgba(0, 0, 0, 0.18)",
|
||||||
@@ -73,92 +73,92 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"roundedCorner4": "4px",
|
"roundedCorner4": "4px",
|
||||||
"roundedCorner6": "6px",
|
"roundedCorner6": "6px",
|
||||||
},
|
},
|
||||||
"fonts": Object {
|
"fonts": {
|
||||||
"large": Object {
|
"large": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "18px",
|
"fontSize": "18px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"medium": Object {
|
"medium": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "14px",
|
"fontSize": "14px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"mediumPlus": Object {
|
"mediumPlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "16px",
|
"fontSize": "16px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"mega": Object {
|
"mega": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "68px",
|
"fontSize": "68px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"small": Object {
|
"small": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "12px",
|
"fontSize": "12px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"smallPlus": Object {
|
"smallPlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "12px",
|
"fontSize": "12px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"superLarge": Object {
|
"superLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "42px",
|
"fontSize": "42px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"tiny": Object {
|
"tiny": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "10px",
|
"fontSize": "10px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"xLarge": Object {
|
"xLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "20px",
|
"fontSize": "20px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xLargePlus": Object {
|
"xLargePlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "24px",
|
"fontSize": "24px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xSmall": Object {
|
"xSmall": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "10px",
|
"fontSize": "10px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"xxLarge": Object {
|
"xxLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "28px",
|
"fontSize": "28px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xxLargePlus": Object {
|
"xxLargePlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
@@ -167,7 +167,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"isInverted": false,
|
"isInverted": false,
|
||||||
"palette": Object {
|
"palette": {
|
||||||
"accent": "#0078d4",
|
"accent": "#0078d4",
|
||||||
"black": "#000000",
|
"black": "#000000",
|
||||||
"blackTranslucent40": "rgba(0,0,0,.4)",
|
"blackTranslucent40": "rgba(0,0,0,.4)",
|
||||||
@@ -220,7 +220,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"yellowLight": "#fff100",
|
"yellowLight": "#fff100",
|
||||||
},
|
},
|
||||||
"rtl": undefined,
|
"rtl": undefined,
|
||||||
"semanticColors": Object {
|
"semanticColors": {
|
||||||
"accentButtonBackground": "#0078d4",
|
"accentButtonBackground": "#0078d4",
|
||||||
"accentButtonText": "#ffffff",
|
"accentButtonText": "#ffffff",
|
||||||
"actionLink": "#323130",
|
"actionLink": "#323130",
|
||||||
@@ -325,7 +325,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"warningIcon": "#797775",
|
"warningIcon": "#797775",
|
||||||
"warningText": "#323130",
|
"warningText": "#323130",
|
||||||
},
|
},
|
||||||
"spacing": Object {
|
"spacing": {
|
||||||
"l1": "20px",
|
"l1": "20px",
|
||||||
"l2": "32px",
|
"l2": "32px",
|
||||||
"m": "16px",
|
"m": "16px",
|
||||||
@@ -357,9 +357,9 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
styles={[Function]}
|
styles={[Function]}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
theme={
|
theme={
|
||||||
Object {
|
{
|
||||||
"disableGlobalClassNames": false,
|
"disableGlobalClassNames": false,
|
||||||
"effects": Object {
|
"effects": {
|
||||||
"elevation16": "0 6.4px 14.4px 0 rgba(0, 0, 0, 0.132), 0 1.2px 3.6px 0 rgba(0, 0, 0, 0.108)",
|
"elevation16": "0 6.4px 14.4px 0 rgba(0, 0, 0, 0.132), 0 1.2px 3.6px 0 rgba(0, 0, 0, 0.108)",
|
||||||
"elevation4": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
|
"elevation4": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
|
||||||
"elevation64": "0 25.6px 57.6px 0 rgba(0, 0, 0, 0.22), 0 4.8px 14.4px 0 rgba(0, 0, 0, 0.18)",
|
"elevation64": "0 25.6px 57.6px 0 rgba(0, 0, 0, 0.22), 0 4.8px 14.4px 0 rgba(0, 0, 0, 0.18)",
|
||||||
@@ -368,92 +368,92 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"roundedCorner4": "4px",
|
"roundedCorner4": "4px",
|
||||||
"roundedCorner6": "6px",
|
"roundedCorner6": "6px",
|
||||||
},
|
},
|
||||||
"fonts": Object {
|
"fonts": {
|
||||||
"large": Object {
|
"large": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "18px",
|
"fontSize": "18px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"medium": Object {
|
"medium": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "14px",
|
"fontSize": "14px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"mediumPlus": Object {
|
"mediumPlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "16px",
|
"fontSize": "16px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"mega": Object {
|
"mega": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "68px",
|
"fontSize": "68px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"small": Object {
|
"small": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "12px",
|
"fontSize": "12px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"smallPlus": Object {
|
"smallPlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "12px",
|
"fontSize": "12px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"superLarge": Object {
|
"superLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "42px",
|
"fontSize": "42px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"tiny": Object {
|
"tiny": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "10px",
|
"fontSize": "10px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"xLarge": Object {
|
"xLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "20px",
|
"fontSize": "20px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xLargePlus": Object {
|
"xLargePlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "24px",
|
"fontSize": "24px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xSmall": Object {
|
"xSmall": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "10px",
|
"fontSize": "10px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"xxLarge": Object {
|
"xxLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "28px",
|
"fontSize": "28px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xxLargePlus": Object {
|
"xxLargePlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
@@ -462,7 +462,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"isInverted": false,
|
"isInverted": false,
|
||||||
"palette": Object {
|
"palette": {
|
||||||
"accent": "#0078d4",
|
"accent": "#0078d4",
|
||||||
"black": "#000000",
|
"black": "#000000",
|
||||||
"blackTranslucent40": "rgba(0,0,0,.4)",
|
"blackTranslucent40": "rgba(0,0,0,.4)",
|
||||||
@@ -515,7 +515,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"yellowLight": "#fff100",
|
"yellowLight": "#fff100",
|
||||||
},
|
},
|
||||||
"rtl": undefined,
|
"rtl": undefined,
|
||||||
"semanticColors": Object {
|
"semanticColors": {
|
||||||
"accentButtonBackground": "#0078d4",
|
"accentButtonBackground": "#0078d4",
|
||||||
"accentButtonText": "#ffffff",
|
"accentButtonText": "#ffffff",
|
||||||
"actionLink": "#323130",
|
"actionLink": "#323130",
|
||||||
@@ -620,7 +620,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"warningIcon": "#797775",
|
"warningIcon": "#797775",
|
||||||
"warningText": "#323130",
|
"warningText": "#323130",
|
||||||
},
|
},
|
||||||
"spacing": Object {
|
"spacing": {
|
||||||
"l1": "20px",
|
"l1": "20px",
|
||||||
"l2": "32px",
|
"l2": "32px",
|
||||||
"m": "16px",
|
"m": "16px",
|
||||||
@@ -645,7 +645,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
hidden={true}
|
hidden={true}
|
||||||
id="tooltip0"
|
id="tooltip0"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"border": 0,
|
"border": 0,
|
||||||
"height": 1,
|
"height": 1,
|
||||||
"margin": -1,
|
"margin": -1,
|
||||||
@@ -744,9 +744,9 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
styles={[Function]}
|
styles={[Function]}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
theme={
|
theme={
|
||||||
Object {
|
{
|
||||||
"disableGlobalClassNames": false,
|
"disableGlobalClassNames": false,
|
||||||
"effects": Object {
|
"effects": {
|
||||||
"elevation16": "0 6.4px 14.4px 0 rgba(0, 0, 0, 0.132), 0 1.2px 3.6px 0 rgba(0, 0, 0, 0.108)",
|
"elevation16": "0 6.4px 14.4px 0 rgba(0, 0, 0, 0.132), 0 1.2px 3.6px 0 rgba(0, 0, 0, 0.108)",
|
||||||
"elevation4": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
|
"elevation4": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
|
||||||
"elevation64": "0 25.6px 57.6px 0 rgba(0, 0, 0, 0.22), 0 4.8px 14.4px 0 rgba(0, 0, 0, 0.18)",
|
"elevation64": "0 25.6px 57.6px 0 rgba(0, 0, 0, 0.22), 0 4.8px 14.4px 0 rgba(0, 0, 0, 0.18)",
|
||||||
@@ -755,92 +755,92 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"roundedCorner4": "4px",
|
"roundedCorner4": "4px",
|
||||||
"roundedCorner6": "6px",
|
"roundedCorner6": "6px",
|
||||||
},
|
},
|
||||||
"fonts": Object {
|
"fonts": {
|
||||||
"large": Object {
|
"large": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "18px",
|
"fontSize": "18px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"medium": Object {
|
"medium": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "14px",
|
"fontSize": "14px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"mediumPlus": Object {
|
"mediumPlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "16px",
|
"fontSize": "16px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"mega": Object {
|
"mega": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "68px",
|
"fontSize": "68px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"small": Object {
|
"small": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "12px",
|
"fontSize": "12px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"smallPlus": Object {
|
"smallPlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "12px",
|
"fontSize": "12px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"superLarge": Object {
|
"superLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "42px",
|
"fontSize": "42px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"tiny": Object {
|
"tiny": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "10px",
|
"fontSize": "10px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"xLarge": Object {
|
"xLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "20px",
|
"fontSize": "20px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xLargePlus": Object {
|
"xLargePlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "24px",
|
"fontSize": "24px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xSmall": Object {
|
"xSmall": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "10px",
|
"fontSize": "10px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"xxLarge": Object {
|
"xxLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "28px",
|
"fontSize": "28px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xxLargePlus": Object {
|
"xxLargePlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
@@ -849,7 +849,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"isInverted": false,
|
"isInverted": false,
|
||||||
"palette": Object {
|
"palette": {
|
||||||
"accent": "#0078d4",
|
"accent": "#0078d4",
|
||||||
"black": "#000000",
|
"black": "#000000",
|
||||||
"blackTranslucent40": "rgba(0,0,0,.4)",
|
"blackTranslucent40": "rgba(0,0,0,.4)",
|
||||||
@@ -902,7 +902,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"yellowLight": "#fff100",
|
"yellowLight": "#fff100",
|
||||||
},
|
},
|
||||||
"rtl": undefined,
|
"rtl": undefined,
|
||||||
"semanticColors": Object {
|
"semanticColors": {
|
||||||
"accentButtonBackground": "#0078d4",
|
"accentButtonBackground": "#0078d4",
|
||||||
"accentButtonText": "#ffffff",
|
"accentButtonText": "#ffffff",
|
||||||
"actionLink": "#323130",
|
"actionLink": "#323130",
|
||||||
@@ -1007,7 +1007,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"warningIcon": "#797775",
|
"warningIcon": "#797775",
|
||||||
"warningText": "#323130",
|
"warningText": "#323130",
|
||||||
},
|
},
|
||||||
"spacing": Object {
|
"spacing": {
|
||||||
"l1": "20px",
|
"l1": "20px",
|
||||||
"l2": "32px",
|
"l2": "32px",
|
||||||
"m": "16px",
|
"m": "16px",
|
||||||
@@ -1042,7 +1042,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
aria-label="maxRUDescription"
|
aria-label="maxRUDescription"
|
||||||
key=".0:$.$.0"
|
key=".0:$.$.0"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
"lineHeight": "20px",
|
"lineHeight": "20px",
|
||||||
}
|
}
|
||||||
@@ -1053,7 +1053,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
aria-label="maxRUDescription"
|
aria-label="maxRUDescription"
|
||||||
className="css-110"
|
className="css-110"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
"lineHeight": "20px",
|
"lineHeight": "20px",
|
||||||
}
|
}
|
||||||
@@ -1075,9 +1075,9 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
delay={1}
|
delay={1}
|
||||||
styles={[Function]}
|
styles={[Function]}
|
||||||
theme={
|
theme={
|
||||||
Object {
|
{
|
||||||
"disableGlobalClassNames": false,
|
"disableGlobalClassNames": false,
|
||||||
"effects": Object {
|
"effects": {
|
||||||
"elevation16": "0 6.4px 14.4px 0 rgba(0, 0, 0, 0.132), 0 1.2px 3.6px 0 rgba(0, 0, 0, 0.108)",
|
"elevation16": "0 6.4px 14.4px 0 rgba(0, 0, 0, 0.132), 0 1.2px 3.6px 0 rgba(0, 0, 0, 0.108)",
|
||||||
"elevation4": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
|
"elevation4": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
|
||||||
"elevation64": "0 25.6px 57.6px 0 rgba(0, 0, 0, 0.22), 0 4.8px 14.4px 0 rgba(0, 0, 0, 0.18)",
|
"elevation64": "0 25.6px 57.6px 0 rgba(0, 0, 0, 0.22), 0 4.8px 14.4px 0 rgba(0, 0, 0, 0.18)",
|
||||||
@@ -1086,92 +1086,92 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"roundedCorner4": "4px",
|
"roundedCorner4": "4px",
|
||||||
"roundedCorner6": "6px",
|
"roundedCorner6": "6px",
|
||||||
},
|
},
|
||||||
"fonts": Object {
|
"fonts": {
|
||||||
"large": Object {
|
"large": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "18px",
|
"fontSize": "18px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"medium": Object {
|
"medium": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "14px",
|
"fontSize": "14px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"mediumPlus": Object {
|
"mediumPlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "16px",
|
"fontSize": "16px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"mega": Object {
|
"mega": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "68px",
|
"fontSize": "68px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"small": Object {
|
"small": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "12px",
|
"fontSize": "12px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"smallPlus": Object {
|
"smallPlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "12px",
|
"fontSize": "12px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"superLarge": Object {
|
"superLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "42px",
|
"fontSize": "42px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"tiny": Object {
|
"tiny": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "10px",
|
"fontSize": "10px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"xLarge": Object {
|
"xLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "20px",
|
"fontSize": "20px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xLargePlus": Object {
|
"xLargePlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "24px",
|
"fontSize": "24px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xSmall": Object {
|
"xSmall": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "10px",
|
"fontSize": "10px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"xxLarge": Object {
|
"xxLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "28px",
|
"fontSize": "28px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xxLargePlus": Object {
|
"xxLargePlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
@@ -1180,7 +1180,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"isInverted": false,
|
"isInverted": false,
|
||||||
"palette": Object {
|
"palette": {
|
||||||
"accent": "#0078d4",
|
"accent": "#0078d4",
|
||||||
"black": "#000000",
|
"black": "#000000",
|
||||||
"blackTranslucent40": "rgba(0,0,0,.4)",
|
"blackTranslucent40": "rgba(0,0,0,.4)",
|
||||||
@@ -1233,7 +1233,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"yellowLight": "#fff100",
|
"yellowLight": "#fff100",
|
||||||
},
|
},
|
||||||
"rtl": undefined,
|
"rtl": undefined,
|
||||||
"semanticColors": Object {
|
"semanticColors": {
|
||||||
"accentButtonBackground": "#0078d4",
|
"accentButtonBackground": "#0078d4",
|
||||||
"accentButtonText": "#ffffff",
|
"accentButtonText": "#ffffff",
|
||||||
"actionLink": "#323130",
|
"actionLink": "#323130",
|
||||||
@@ -1338,7 +1338,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"warningIcon": "#797775",
|
"warningIcon": "#797775",
|
||||||
"warningText": "#323130",
|
"warningText": "#323130",
|
||||||
},
|
},
|
||||||
"spacing": Object {
|
"spacing": {
|
||||||
"l1": "20px",
|
"l1": "20px",
|
||||||
"l2": "32px",
|
"l2": "32px",
|
||||||
"m": "16px",
|
"m": "16px",
|
||||||
@@ -1370,9 +1370,9 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
styles={[Function]}
|
styles={[Function]}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
theme={
|
theme={
|
||||||
Object {
|
{
|
||||||
"disableGlobalClassNames": false,
|
"disableGlobalClassNames": false,
|
||||||
"effects": Object {
|
"effects": {
|
||||||
"elevation16": "0 6.4px 14.4px 0 rgba(0, 0, 0, 0.132), 0 1.2px 3.6px 0 rgba(0, 0, 0, 0.108)",
|
"elevation16": "0 6.4px 14.4px 0 rgba(0, 0, 0, 0.132), 0 1.2px 3.6px 0 rgba(0, 0, 0, 0.108)",
|
||||||
"elevation4": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
|
"elevation4": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
|
||||||
"elevation64": "0 25.6px 57.6px 0 rgba(0, 0, 0, 0.22), 0 4.8px 14.4px 0 rgba(0, 0, 0, 0.18)",
|
"elevation64": "0 25.6px 57.6px 0 rgba(0, 0, 0, 0.22), 0 4.8px 14.4px 0 rgba(0, 0, 0, 0.18)",
|
||||||
@@ -1381,92 +1381,92 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"roundedCorner4": "4px",
|
"roundedCorner4": "4px",
|
||||||
"roundedCorner6": "6px",
|
"roundedCorner6": "6px",
|
||||||
},
|
},
|
||||||
"fonts": Object {
|
"fonts": {
|
||||||
"large": Object {
|
"large": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "18px",
|
"fontSize": "18px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"medium": Object {
|
"medium": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "14px",
|
"fontSize": "14px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"mediumPlus": Object {
|
"mediumPlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "16px",
|
"fontSize": "16px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"mega": Object {
|
"mega": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "68px",
|
"fontSize": "68px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"small": Object {
|
"small": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "12px",
|
"fontSize": "12px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"smallPlus": Object {
|
"smallPlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "12px",
|
"fontSize": "12px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"superLarge": Object {
|
"superLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "42px",
|
"fontSize": "42px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"tiny": Object {
|
"tiny": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "10px",
|
"fontSize": "10px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"xLarge": Object {
|
"xLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "20px",
|
"fontSize": "20px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xLargePlus": Object {
|
"xLargePlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "24px",
|
"fontSize": "24px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xSmall": Object {
|
"xSmall": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "10px",
|
"fontSize": "10px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"xxLarge": Object {
|
"xxLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "28px",
|
"fontSize": "28px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xxLargePlus": Object {
|
"xxLargePlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
@@ -1475,7 +1475,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"isInverted": false,
|
"isInverted": false,
|
||||||
"palette": Object {
|
"palette": {
|
||||||
"accent": "#0078d4",
|
"accent": "#0078d4",
|
||||||
"black": "#000000",
|
"black": "#000000",
|
||||||
"blackTranslucent40": "rgba(0,0,0,.4)",
|
"blackTranslucent40": "rgba(0,0,0,.4)",
|
||||||
@@ -1528,7 +1528,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"yellowLight": "#fff100",
|
"yellowLight": "#fff100",
|
||||||
},
|
},
|
||||||
"rtl": undefined,
|
"rtl": undefined,
|
||||||
"semanticColors": Object {
|
"semanticColors": {
|
||||||
"accentButtonBackground": "#0078d4",
|
"accentButtonBackground": "#0078d4",
|
||||||
"accentButtonText": "#ffffff",
|
"accentButtonText": "#ffffff",
|
||||||
"actionLink": "#323130",
|
"actionLink": "#323130",
|
||||||
@@ -1633,7 +1633,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"warningIcon": "#797775",
|
"warningIcon": "#797775",
|
||||||
"warningText": "#323130",
|
"warningText": "#323130",
|
||||||
},
|
},
|
||||||
"spacing": Object {
|
"spacing": {
|
||||||
"l1": "20px",
|
"l1": "20px",
|
||||||
"l2": "32px",
|
"l2": "32px",
|
||||||
"m": "16px",
|
"m": "16px",
|
||||||
@@ -1658,7 +1658,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
hidden={true}
|
hidden={true}
|
||||||
id="tooltip1"
|
id="tooltip1"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"border": 0,
|
"border": 0,
|
||||||
"height": 1,
|
"height": 1,
|
||||||
"margin": -1,
|
"margin": -1,
|
||||||
@@ -1690,11 +1690,11 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
required={true}
|
required={true}
|
||||||
step={1000}
|
step={1000}
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"field": Object {
|
"field": {
|
||||||
"fontSize": 12,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
"fieldGroup": Object {
|
"fieldGroup": {
|
||||||
"height": 27,
|
"height": 27,
|
||||||
"width": 300,
|
"width": 300,
|
||||||
},
|
},
|
||||||
@@ -1716,9 +1716,9 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
step={1000}
|
step={1000}
|
||||||
styles={[Function]}
|
styles={[Function]}
|
||||||
theme={
|
theme={
|
||||||
Object {
|
{
|
||||||
"disableGlobalClassNames": false,
|
"disableGlobalClassNames": false,
|
||||||
"effects": Object {
|
"effects": {
|
||||||
"elevation16": "0 6.4px 14.4px 0 rgba(0, 0, 0, 0.132), 0 1.2px 3.6px 0 rgba(0, 0, 0, 0.108)",
|
"elevation16": "0 6.4px 14.4px 0 rgba(0, 0, 0, 0.132), 0 1.2px 3.6px 0 rgba(0, 0, 0, 0.108)",
|
||||||
"elevation4": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
|
"elevation4": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
|
||||||
"elevation64": "0 25.6px 57.6px 0 rgba(0, 0, 0, 0.22), 0 4.8px 14.4px 0 rgba(0, 0, 0, 0.18)",
|
"elevation64": "0 25.6px 57.6px 0 rgba(0, 0, 0, 0.22), 0 4.8px 14.4px 0 rgba(0, 0, 0, 0.18)",
|
||||||
@@ -1727,92 +1727,92 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"roundedCorner4": "4px",
|
"roundedCorner4": "4px",
|
||||||
"roundedCorner6": "6px",
|
"roundedCorner6": "6px",
|
||||||
},
|
},
|
||||||
"fonts": Object {
|
"fonts": {
|
||||||
"large": Object {
|
"large": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "18px",
|
"fontSize": "18px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"medium": Object {
|
"medium": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "14px",
|
"fontSize": "14px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"mediumPlus": Object {
|
"mediumPlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "16px",
|
"fontSize": "16px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"mega": Object {
|
"mega": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "68px",
|
"fontSize": "68px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"small": Object {
|
"small": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "12px",
|
"fontSize": "12px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"smallPlus": Object {
|
"smallPlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "12px",
|
"fontSize": "12px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"superLarge": Object {
|
"superLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "42px",
|
"fontSize": "42px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"tiny": Object {
|
"tiny": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "10px",
|
"fontSize": "10px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"xLarge": Object {
|
"xLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "20px",
|
"fontSize": "20px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xLargePlus": Object {
|
"xLargePlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "24px",
|
"fontSize": "24px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xSmall": Object {
|
"xSmall": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "10px",
|
"fontSize": "10px",
|
||||||
"fontWeight": 400,
|
"fontWeight": 400,
|
||||||
},
|
},
|
||||||
"xxLarge": Object {
|
"xxLarge": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
"fontSize": "28px",
|
"fontSize": "28px",
|
||||||
"fontWeight": 600,
|
"fontWeight": 600,
|
||||||
},
|
},
|
||||||
"xxLargePlus": Object {
|
"xxLargePlus": {
|
||||||
"MozOsxFontSmoothing": "grayscale",
|
"MozOsxFontSmoothing": "grayscale",
|
||||||
"WebkitFontSmoothing": "antialiased",
|
"WebkitFontSmoothing": "antialiased",
|
||||||
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
"fontFamily": "'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif",
|
||||||
@@ -1821,7 +1821,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"isInverted": false,
|
"isInverted": false,
|
||||||
"palette": Object {
|
"palette": {
|
||||||
"accent": "#0078d4",
|
"accent": "#0078d4",
|
||||||
"black": "#000000",
|
"black": "#000000",
|
||||||
"blackTranslucent40": "rgba(0,0,0,.4)",
|
"blackTranslucent40": "rgba(0,0,0,.4)",
|
||||||
@@ -1874,7 +1874,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"yellowLight": "#fff100",
|
"yellowLight": "#fff100",
|
||||||
},
|
},
|
||||||
"rtl": undefined,
|
"rtl": undefined,
|
||||||
"semanticColors": Object {
|
"semanticColors": {
|
||||||
"accentButtonBackground": "#0078d4",
|
"accentButtonBackground": "#0078d4",
|
||||||
"accentButtonText": "#ffffff",
|
"accentButtonText": "#ffffff",
|
||||||
"actionLink": "#323130",
|
"actionLink": "#323130",
|
||||||
@@ -1979,7 +1979,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
"warningIcon": "#797775",
|
"warningIcon": "#797775",
|
||||||
"warningText": "#323130",
|
"warningText": "#323130",
|
||||||
},
|
},
|
||||||
"spacing": Object {
|
"spacing": {
|
||||||
"l1": "20px",
|
"l1": "20px",
|
||||||
"l2": "32px",
|
"l2": "32px",
|
||||||
"m": "16px",
|
"m": "16px",
|
||||||
|
|||||||
66
src/Explorer/Controls/TreeComponent/Styles.ts
Normal file
66
src/Explorer/Controls/TreeComponent/Styles.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { makeStyles, shorthands, treeItemLevelToken } from "@fluentui/react-components";
|
||||||
|
import { cosmosShorthands, tokens } from "Explorer/Theme/ThemeUtil";
|
||||||
|
|
||||||
|
export type TreeStyleName = keyof ReturnType<typeof useTreeStyles>;
|
||||||
|
|
||||||
|
const treeIconWidth = "--cosmos-Tree--iconWidth" as const;
|
||||||
|
const leafNodeSpacing = "--cosmos-Tree--leafNodeSpacing" as const;
|
||||||
|
const actionButtonBackground = "--cosmos-Tree--actionButtonBackground" as const;
|
||||||
|
|
||||||
|
export const useTreeStyles = makeStyles({
|
||||||
|
treeContainer: {
|
||||||
|
height: "100%",
|
||||||
|
...shorthands.overflow("auto"),
|
||||||
|
},
|
||||||
|
tree: {
|
||||||
|
width: "fit-content",
|
||||||
|
minWidth: "100%",
|
||||||
|
rowGap: "0px",
|
||||||
|
paddingTop: "0px",
|
||||||
|
[treeIconWidth]: "16px",
|
||||||
|
[leafNodeSpacing]: "24px",
|
||||||
|
},
|
||||||
|
nodeIcon: {
|
||||||
|
width: `var(${treeIconWidth})`,
|
||||||
|
height: `var(${treeIconWidth})`,
|
||||||
|
},
|
||||||
|
treeItem: {},
|
||||||
|
nodeLabel: {
|
||||||
|
whiteSpace: "nowrap", // Don't wrap text, there will be a scrollbar.
|
||||||
|
},
|
||||||
|
treeItemLayout: {
|
||||||
|
fontSize: tokens.fontSizeBase300,
|
||||||
|
height: tokens.layoutRowHeight,
|
||||||
|
...cosmosShorthands.borderBottom(),
|
||||||
|
|
||||||
|
// Some sneaky CSS variables stuff to change the background color of the action button on hover.
|
||||||
|
[actionButtonBackground]: tokens.colorNeutralBackground1,
|
||||||
|
"&:hover": {
|
||||||
|
[actionButtonBackground]: tokens.colorNeutralBackground1Hover,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actionsButtonContainer: {
|
||||||
|
position: "sticky",
|
||||||
|
right: 0,
|
||||||
|
},
|
||||||
|
actionsButton: {
|
||||||
|
backgroundColor: `var(${actionButtonBackground})`,
|
||||||
|
},
|
||||||
|
treeItemLayoutNoIcon: {
|
||||||
|
// Pad the text out by the level, the width of the icon, AND the usual spacing between the icon and the level.
|
||||||
|
// It would be nice to see if we can use Grid layout or something here, but that would require overriding a lot of the existing Tree component behavior.
|
||||||
|
paddingLeft: `calc((var(${treeItemLevelToken}, 1) * ${tokens.spacingHorizontalXXL}) + var(${leafNodeSpacing}))`,
|
||||||
|
},
|
||||||
|
selectedItem: {
|
||||||
|
backgroundColor: tokens.colorNeutralBackground1Selected,
|
||||||
|
},
|
||||||
|
databaseNode: {
|
||||||
|
fontWeight: tokens.fontWeightSemibold,
|
||||||
|
},
|
||||||
|
collectionNode: {
|
||||||
|
fontWeight: tokens.fontWeightSemibold,
|
||||||
|
},
|
||||||
|
loadMoreNode: {
|
||||||
|
color: tokens.colorBrandForegroundLink,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { TreeItem, TreeItemLayout, tokens } from "@fluentui/react-components";
|
import { TreeItem, TreeItemLayout } from "@fluentui/react-components";
|
||||||
import PromiseSource from "Utils/PromiseSource";
|
import PromiseSource from "Utils/PromiseSource";
|
||||||
import { mount, shallow } from "enzyme";
|
import { mount, shallow } from "enzyme";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
@@ -9,7 +9,7 @@ function generateTestNode(id: string, additionalProps?: Partial<TreeNode>): Tree
|
|||||||
const node: TreeNode = {
|
const node: TreeNode = {
|
||||||
id,
|
id,
|
||||||
label: `${id}Label`,
|
label: `${id}Label`,
|
||||||
className: `${id}Class`,
|
className: "nodeIcon",
|
||||||
iconSrc: `${id}Icon`,
|
iconSrc: `${id}Icon`,
|
||||||
onClick: jest.fn().mockName(`${id}Click`),
|
onClick: jest.fn().mockName(`${id}Click`),
|
||||||
...additionalProps,
|
...additionalProps,
|
||||||
@@ -20,7 +20,7 @@ function generateTestNode(id: string, additionalProps?: Partial<TreeNode>): Tree
|
|||||||
describe("TreeNodeComponent", () => {
|
describe("TreeNodeComponent", () => {
|
||||||
it("renders a single node", () => {
|
it("renders a single node", () => {
|
||||||
const node = generateTestNode("root");
|
const node = generateTestNode("root");
|
||||||
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
|
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
|
||||||
expect(component).toMatchSnapshot();
|
expect(component).toMatchSnapshot();
|
||||||
|
|
||||||
// The "click" handler is actually attached to onOpenChange, with a type of "Click".
|
// The "click" handler is actually attached to onOpenChange, with a type of "Click".
|
||||||
@@ -45,7 +45,7 @@ describe("TreeNodeComponent", () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
|
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
|
||||||
expect(component).toMatchSnapshot();
|
expect(component).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ describe("TreeNodeComponent", () => {
|
|||||||
const node = generateTestNode("root", {
|
const node = generateTestNode("root", {
|
||||||
onExpanded: () => loading.promise,
|
onExpanded: () => loading.promise,
|
||||||
});
|
});
|
||||||
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
|
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
component
|
component
|
||||||
@@ -72,10 +72,7 @@ describe("TreeNodeComponent", () => {
|
|||||||
const node = generateTestNode("root", {
|
const node = generateTestNode("root", {
|
||||||
isSelected: () => true,
|
isSelected: () => true,
|
||||||
});
|
});
|
||||||
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
|
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
|
||||||
expect(component.find(TreeItemLayout).props().style?.backgroundColor).toStrictEqual(
|
|
||||||
tokens.colorNeutralBackground1Selected,
|
|
||||||
);
|
|
||||||
expect(component).toMatchSnapshot();
|
expect(component).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -89,10 +86,7 @@ describe("TreeNodeComponent", () => {
|
|||||||
generateTestNode("child2"),
|
generateTestNode("child2"),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
|
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
|
||||||
expect(component.find(TreeItemLayout).props().style?.backgroundColor).toStrictEqual(
|
|
||||||
tokens.colorNeutralBackground1Selected,
|
|
||||||
);
|
|
||||||
expect(component).toMatchSnapshot();
|
expect(component).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -111,7 +105,7 @@ describe("TreeNodeComponent", () => {
|
|||||||
generateTestNode("child2"),
|
generateTestNode("child2"),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
|
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
|
||||||
expect(component.find(TreeItemLayout).props().style?.backgroundColor).toBeUndefined();
|
expect(component.find(TreeItemLayout).props().style?.backgroundColor).toBeUndefined();
|
||||||
expect(component).toMatchSnapshot();
|
expect(component).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
@@ -120,7 +114,7 @@ describe("TreeNodeComponent", () => {
|
|||||||
const node = generateTestNode("root", {
|
const node = generateTestNode("root", {
|
||||||
iconSrc: "the-icon.svg",
|
iconSrc: "the-icon.svg",
|
||||||
});
|
});
|
||||||
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
|
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
|
||||||
expect(component).toMatchSnapshot();
|
expect(component).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -134,7 +128,7 @@ describe("TreeNodeComponent", () => {
|
|||||||
generateTestNode("child2"),
|
generateTestNode("child2"),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
|
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
|
||||||
expect(component).toMatchSnapshot();
|
expect(component).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -148,7 +142,7 @@ describe("TreeNodeComponent", () => {
|
|||||||
generateTestNode("child2"),
|
generateTestNode("child2"),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
|
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
|
||||||
expect(component).toMatchSnapshot();
|
expect(component).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -175,7 +169,7 @@ describe("TreeNodeComponent", () => {
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const component = mount(<TreeNodeComponent node={node} treeNodeId={node.id} />);
|
const component = mount(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
|
||||||
|
|
||||||
// Find and expand the child3Expanding node
|
// Find and expand the child3Expanding node
|
||||||
const expandingChild = component.find(TreeItem).filterWhere((n) => n.props().value === "root/child3ExpandingLabel");
|
const expandingChild = component.find(TreeItem).filterWhere((n) => n.props().value === "root/child3ExpandingLabel");
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ import {
|
|||||||
Tree,
|
Tree,
|
||||||
TreeItem,
|
TreeItem,
|
||||||
TreeItemLayout,
|
TreeItemLayout,
|
||||||
|
TreeItemValue,
|
||||||
TreeOpenChangeData,
|
TreeOpenChangeData,
|
||||||
TreeOpenChangeEvent,
|
TreeOpenChangeEvent,
|
||||||
|
mergeClasses,
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
import { MoreHorizontal20Regular } from "@fluentui/react-icons";
|
import { ChevronDown20Regular, ChevronRight20Regular, MoreHorizontal20Regular } from "@fluentui/react-icons";
|
||||||
import { tokens } from "@fluentui/react-theme";
|
import { TreeStyleName, useTreeStyles } from "Explorer/Controls/TreeComponent/Styles";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
|
|
||||||
@@ -32,15 +34,14 @@ export interface TreeNode {
|
|||||||
id?: string;
|
id?: string;
|
||||||
children?: TreeNode[];
|
children?: TreeNode[];
|
||||||
contextMenu?: TreeNodeMenuItem[];
|
contextMenu?: TreeNodeMenuItem[];
|
||||||
iconSrc?: string;
|
iconSrc?: string | JSX.Element;
|
||||||
isExpanded?: boolean;
|
isExpanded?: boolean;
|
||||||
className?: string;
|
className?: TreeStyleName;
|
||||||
isAlphaSorted?: boolean;
|
isAlphaSorted?: boolean;
|
||||||
// data?: any; // Piece of data corresponding to this node
|
// data?: any; // Piece of data corresponding to this node
|
||||||
timestamp?: number;
|
timestamp?: number;
|
||||||
isLeavesParentsSeparate?: boolean; // Display parents together first, then leaves
|
isLeavesParentsSeparate?: boolean; // Display parents together first, then leaves
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
isScrollable?: boolean;
|
|
||||||
isSelected?: () => boolean;
|
isSelected?: () => boolean;
|
||||||
onClick?: () => void; // Only if a leaf, other click will expand/collapse
|
onClick?: () => void; // Only if a leaf, other click will expand/collapse
|
||||||
onExpanded?: () => Promise<void>;
|
onExpanded?: () => Promise<void>;
|
||||||
@@ -52,6 +53,7 @@ export interface TreeNodeComponentProps {
|
|||||||
node: TreeNode;
|
node: TreeNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
treeNodeId: string;
|
treeNodeId: string;
|
||||||
|
openItems: TreeItemValue[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Function that returns true if any descendant (at any depth) of this node is selected. */
|
/** Function that returns true if any descendant (at any depth) of this node is selected. */
|
||||||
@@ -66,13 +68,13 @@ function isAnyDescendantSelected(node: TreeNode): boolean {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTreeIcon = (iconSrc: string): JSX.Element => <img src={iconSrc} alt="" style={{ width: 16, height: 16 }} />;
|
|
||||||
|
|
||||||
export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
|
export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
|
||||||
node,
|
node,
|
||||||
treeNodeId,
|
treeNodeId,
|
||||||
|
openItems,
|
||||||
}: TreeNodeComponentProps): JSX.Element => {
|
}: TreeNodeComponentProps): JSX.Element => {
|
||||||
const [isLoading, setIsLoading] = React.useState<boolean>(false);
|
const [isLoading, setIsLoading] = React.useState<boolean>(false);
|
||||||
|
const treeStyles = useTreeStyles();
|
||||||
|
|
||||||
const getSortedChildren = (treeNode: TreeNode): TreeNode[] => {
|
const getSortedChildren = (treeNode: TreeNode): TreeNode[] => {
|
||||||
if (!treeNode || !treeNode.children) {
|
if (!treeNode || !treeNode.children) {
|
||||||
@@ -105,7 +107,7 @@ export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
|
|||||||
|
|
||||||
const onOpenChange = useCallback(
|
const onOpenChange = useCallback(
|
||||||
(_: TreeOpenChangeEvent, data: TreeOpenChangeData) => {
|
(_: TreeOpenChangeEvent, data: TreeOpenChangeData) => {
|
||||||
if (data.type === "Click" && !isBranch && node.onClick) {
|
if (data.type === "Click" && node.onClick) {
|
||||||
node.onClick();
|
node.onClick();
|
||||||
}
|
}
|
||||||
if (!node.isExpanded && data.open && node.onExpanded) {
|
if (!node.isExpanded && data.open && node.onExpanded) {
|
||||||
@@ -117,7 +119,7 @@ export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
|
|||||||
node.onCollapsed?.();
|
node.onCollapsed?.();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[isBranch, node, setIsLoading],
|
[node, setIsLoading],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onMenuOpenChange = useCallback(
|
const onMenuOpenChange = useCallback(
|
||||||
@@ -145,45 +147,73 @@ export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// We use the expandIcon slot to hold the node icon too.
|
||||||
|
// We only show a node icon for leaf nodes, even if a branch node has an iconSrc.
|
||||||
|
const treeIcon =
|
||||||
|
node.iconSrc === undefined ? undefined : typeof node.iconSrc === "string" ? (
|
||||||
|
<img src={node.iconSrc} className={treeStyles.nodeIcon} alt="" />
|
||||||
|
) : (
|
||||||
|
node.iconSrc
|
||||||
|
);
|
||||||
|
|
||||||
|
const expandIcon = isLoading ? (
|
||||||
|
<Spinner size="extra-tiny" />
|
||||||
|
) : !isBranch ? undefined : openItems.includes(treeNodeId) ? (
|
||||||
|
<ChevronDown20Regular data-test="TreeNode/CollapseIcon" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight20Regular data-text="TreeNode/ExpandIcon" />
|
||||||
|
);
|
||||||
|
|
||||||
const treeItem = (
|
const treeItem = (
|
||||||
<TreeItem
|
<TreeItem
|
||||||
data-test={`TreeNodeContainer:${treeNodeId}`}
|
data-test={`TreeNodeContainer:${treeNodeId}`}
|
||||||
value={treeNodeId}
|
value={treeNodeId}
|
||||||
itemType={isBranch ? "branch" : "leaf"}
|
itemType={isBranch ? "branch" : "leaf"}
|
||||||
onOpenChange={onOpenChange}
|
onOpenChange={onOpenChange}
|
||||||
|
className={treeStyles.treeItem}
|
||||||
>
|
>
|
||||||
<TreeItemLayout
|
<TreeItemLayout
|
||||||
className={node.className}
|
className={mergeClasses(
|
||||||
|
treeStyles.treeItemLayout,
|
||||||
|
shouldShowAsSelected && treeStyles.selectedItem,
|
||||||
|
node.className && treeStyles[node.className],
|
||||||
|
)}
|
||||||
data-test={`TreeNode:${treeNodeId}`}
|
data-test={`TreeNode:${treeNodeId}`}
|
||||||
actions={
|
actions={
|
||||||
contextMenuItems.length > 0 && (
|
contextMenuItems.length > 0 && {
|
||||||
<Menu onOpenChange={onMenuOpenChange}>
|
className: treeStyles.actionsButtonContainer,
|
||||||
<MenuTrigger disableButtonEnhancement>
|
children: (
|
||||||
<Button
|
<Menu onOpenChange={onMenuOpenChange}>
|
||||||
aria-label="More options"
|
<MenuTrigger disableButtonEnhancement>
|
||||||
data-test="TreeNode/ContextMenuTrigger"
|
<Button
|
||||||
appearance="subtle"
|
aria-label="More options"
|
||||||
icon={<MoreHorizontal20Regular />}
|
className={mergeClasses(treeStyles.actionsButton, shouldShowAsSelected && treeStyles.selectedItem)}
|
||||||
/>
|
data-test="TreeNode/ContextMenuTrigger"
|
||||||
</MenuTrigger>
|
appearance="subtle"
|
||||||
<MenuPopover data-test={`TreeNode/ContextMenu:${treeNodeId}`}>
|
icon={<MoreHorizontal20Regular />}
|
||||||
<MenuList>{contextMenuItems}</MenuList>
|
/>
|
||||||
</MenuPopover>
|
</MenuTrigger>
|
||||||
</Menu>
|
<MenuPopover data-test={`TreeNode/ContextMenu:${treeNodeId}`}>
|
||||||
)
|
<MenuList>{contextMenuItems}</MenuList>
|
||||||
|
</MenuPopover>
|
||||||
|
</Menu>
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
expandIcon={isLoading ? <Spinner size="extra-tiny" /> : undefined}
|
iconBefore={treeIcon}
|
||||||
iconBefore={node.iconSrc && getTreeIcon(node.iconSrc)}
|
expandIcon={expandIcon}
|
||||||
style={{
|
|
||||||
backgroundColor: shouldShowAsSelected ? tokens.colorNeutralBackground1Selected : undefined,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{node.label}
|
<span className={treeStyles.nodeLabel}>{node.label}</span>
|
||||||
</TreeItemLayout>
|
</TreeItemLayout>
|
||||||
{!node.isLoading && node.children?.length > 0 && (
|
{!node.isLoading && node.children?.length > 0 && (
|
||||||
<Tree style={{ overflow: node.isScrollable ? "auto" : undefined }}>
|
<Tree data-test={`Tree:${treeNodeId}`} className={treeStyles.tree}>
|
||||||
{getSortedChildren(node).map((childNode: TreeNode) => (
|
{getSortedChildren(node).map((childNode: TreeNode) => (
|
||||||
<TreeNodeComponent key={childNode.label} node={childNode} treeNodeId={`${treeNodeId}/${childNode.label}`} />
|
<TreeNodeComponent
|
||||||
|
openItems={openItems}
|
||||||
|
key={childNode.label}
|
||||||
|
node={childNode}
|
||||||
|
treeNodeId={`${treeNodeId}/${childNode.label}`}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</Tree>
|
</Tree>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -8,20 +8,20 @@ exports[`LegacyTreeComponent renders a simple tree 1`] = `
|
|||||||
<LegacyTreeNodeComponent
|
<LegacyTreeNodeComponent
|
||||||
generation={0}
|
generation={0}
|
||||||
node={
|
node={
|
||||||
Object {
|
{
|
||||||
"children": Array [
|
"children": [
|
||||||
Object {
|
{
|
||||||
"children": Array [
|
"children": [
|
||||||
Object {
|
{
|
||||||
"label": "ZgrandChild11",
|
"label": "ZgrandChild11",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"label": "AgrandChild12",
|
"label": "AgrandChild12",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"label": "Bchild1",
|
"label": "Bchild1",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"label": "2child2",
|
"label": "2child2",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -45,7 +45,7 @@ exports[`LegacyTreeNodeComponent does not render children by default 1`] = `
|
|||||||
className="treeNodeHeader "
|
className="treeNodeHeader "
|
||||||
data-test="Tree/TreeNode/Header:label"
|
data-test="Tree/TreeNode/Header:label"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"paddingLeft": 9,
|
"paddingLeft": 9,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ exports[`LegacyTreeNodeComponent does not render children by default 1`] = `
|
|||||||
className="expandCollapseIcon"
|
className="expandCollapseIcon"
|
||||||
onKeyPress={[Function]}
|
onKeyPress={[Function]}
|
||||||
role="button"
|
role="button"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
@@ -78,7 +78,7 @@ exports[`LegacyTreeNodeComponent does not render children by default 1`] = `
|
|||||||
<AnimateHeight
|
<AnimateHeight
|
||||||
animateOpacity={false}
|
animateOpacity={false}
|
||||||
animationStateClasses={
|
animationStateClasses={
|
||||||
Object {
|
{
|
||||||
"animating": "rah-animating",
|
"animating": "rah-animating",
|
||||||
"animatingDown": "rah-animating--down",
|
"animatingDown": "rah-animating--down",
|
||||||
"animatingToHeightAuto": "rah-animating--to-height-auto",
|
"animatingToHeightAuto": "rah-animating--to-height-auto",
|
||||||
@@ -96,7 +96,7 @@ exports[`LegacyTreeNodeComponent does not render children by default 1`] = `
|
|||||||
duration={200}
|
duration={200}
|
||||||
easing="ease"
|
easing="ease"
|
||||||
height={0}
|
height={0}
|
||||||
style={Object {}}
|
style={{}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="nodeChildren"
|
className="nodeChildren"
|
||||||
@@ -107,12 +107,12 @@ exports[`LegacyTreeNodeComponent does not render children by default 1`] = `
|
|||||||
generation={3}
|
generation={3}
|
||||||
key="Bchild1-3-undefined"
|
key="Bchild1-3-undefined"
|
||||||
node={
|
node={
|
||||||
Object {
|
{
|
||||||
"children": Array [
|
"children": [
|
||||||
Object {
|
{
|
||||||
"label": "ZgrandChild11",
|
"label": "ZgrandChild11",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"label": "AgrandChild12",
|
"label": "AgrandChild12",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -125,7 +125,7 @@ exports[`LegacyTreeNodeComponent does not render children by default 1`] = `
|
|||||||
generation={3}
|
generation={3}
|
||||||
key="2child2-3-undefined"
|
key="2child2-3-undefined"
|
||||||
node={
|
node={
|
||||||
Object {
|
{
|
||||||
"label": "2child2",
|
"label": "2child2",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,7 +149,7 @@ exports[`LegacyTreeNodeComponent renders a simple node (sorted children, expande
|
|||||||
className="treeNodeHeader "
|
className="treeNodeHeader "
|
||||||
data-test="Tree/TreeNode/Header:label"
|
data-test="Tree/TreeNode/Header:label"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"paddingLeft": 23,
|
"paddingLeft": 23,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,7 +160,7 @@ exports[`LegacyTreeNodeComponent renders a simple node (sorted children, expande
|
|||||||
className="expandCollapseIcon"
|
className="expandCollapseIcon"
|
||||||
onKeyPress={[Function]}
|
onKeyPress={[Function]}
|
||||||
role="button"
|
role="button"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
@@ -177,10 +177,10 @@ exports[`LegacyTreeNodeComponent renders a simple node (sorted children, expande
|
|||||||
ariaLabel="More options"
|
ariaLabel="More options"
|
||||||
className="treeMenuEllipsis"
|
className="treeMenuEllipsis"
|
||||||
menuIconProps={
|
menuIconProps={
|
||||||
Object {
|
{
|
||||||
"iconName": "More",
|
"iconName": "More",
|
||||||
"styles": Object {
|
"styles": {
|
||||||
"root": Object {
|
"root": {
|
||||||
"fontSize": "18px",
|
"fontSize": "18px",
|
||||||
"fontWeight": "bold",
|
"fontWeight": "bold",
|
||||||
},
|
},
|
||||||
@@ -188,13 +188,13 @@ exports[`LegacyTreeNodeComponent renders a simple node (sorted children, expande
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
menuProps={
|
menuProps={
|
||||||
Object {
|
{
|
||||||
"contextualMenuItemAs": [Function],
|
"contextualMenuItemAs": [Function],
|
||||||
"coverTarget": true,
|
"coverTarget": true,
|
||||||
"directionalHint": 3,
|
"directionalHint": 3,
|
||||||
"isBeakVisible": false,
|
"isBeakVisible": false,
|
||||||
"items": Array [
|
"items": [
|
||||||
Object {
|
{
|
||||||
"className": undefined,
|
"className": undefined,
|
||||||
"disabled": true,
|
"disabled": true,
|
||||||
"key": "menuLabel",
|
"key": "menuLabel",
|
||||||
@@ -209,8 +209,8 @@ exports[`LegacyTreeNodeComponent renders a simple node (sorted children, expande
|
|||||||
}
|
}
|
||||||
name="More"
|
name="More"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"rootFocused": Object {
|
"rootFocused": {
|
||||||
"outline": "1px dashed undefined",
|
"outline": "1px dashed undefined",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -231,7 +231,7 @@ exports[`LegacyTreeNodeComponent renders a simple node (sorted children, expande
|
|||||||
<AnimateHeight
|
<AnimateHeight
|
||||||
animateOpacity={false}
|
animateOpacity={false}
|
||||||
animationStateClasses={
|
animationStateClasses={
|
||||||
Object {
|
{
|
||||||
"animating": "rah-animating",
|
"animating": "rah-animating",
|
||||||
"animatingDown": "rah-animating--down",
|
"animatingDown": "rah-animating--down",
|
||||||
"animatingToHeightAuto": "rah-animating--to-height-auto",
|
"animatingToHeightAuto": "rah-animating--to-height-auto",
|
||||||
@@ -249,7 +249,7 @@ exports[`LegacyTreeNodeComponent renders a simple node (sorted children, expande
|
|||||||
duration={200}
|
duration={200}
|
||||||
easing="ease"
|
easing="ease"
|
||||||
height="auto"
|
height="auto"
|
||||||
style={Object {}}
|
style={{}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="nodeChildren"
|
className="nodeChildren"
|
||||||
@@ -260,7 +260,7 @@ exports[`LegacyTreeNodeComponent renders a simple node (sorted children, expande
|
|||||||
generation={13}
|
generation={13}
|
||||||
key="2child2-13-undefined"
|
key="2child2-13-undefined"
|
||||||
node={
|
node={
|
||||||
Object {
|
{
|
||||||
"label": "2child2",
|
"label": "2child2",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -270,12 +270,12 @@ exports[`LegacyTreeNodeComponent renders a simple node (sorted children, expande
|
|||||||
generation={13}
|
generation={13}
|
||||||
key="Bchild1-13-undefined"
|
key="Bchild1-13-undefined"
|
||||||
node={
|
node={
|
||||||
Object {
|
{
|
||||||
"children": Array [
|
"children": [
|
||||||
Object {
|
{
|
||||||
"label": "ZgrandChild11",
|
"label": "ZgrandChild11",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"label": "AgrandChild12",
|
"label": "AgrandChild12",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -301,7 +301,7 @@ exports[`LegacyTreeNodeComponent renders loading icon 1`] = `
|
|||||||
className="treeNodeHeader "
|
className="treeNodeHeader "
|
||||||
data-test="Tree/TreeNode/Header:label"
|
data-test="Tree/TreeNode/Header:label"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"paddingLeft": 9,
|
"paddingLeft": 9,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -312,7 +312,7 @@ exports[`LegacyTreeNodeComponent renders loading icon 1`] = `
|
|||||||
className="expandCollapseIcon"
|
className="expandCollapseIcon"
|
||||||
onKeyPress={[Function]}
|
onKeyPress={[Function]}
|
||||||
role="button"
|
role="button"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
@@ -334,7 +334,7 @@ exports[`LegacyTreeNodeComponent renders loading icon 1`] = `
|
|||||||
<AnimateHeight
|
<AnimateHeight
|
||||||
animateOpacity={false}
|
animateOpacity={false}
|
||||||
animationStateClasses={
|
animationStateClasses={
|
||||||
Object {
|
{
|
||||||
"animating": "rah-animating",
|
"animating": "rah-animating",
|
||||||
"animatingDown": "rah-animating--down",
|
"animatingDown": "rah-animating--down",
|
||||||
"animatingToHeightAuto": "rah-animating--to-height-auto",
|
"animatingToHeightAuto": "rah-animating--to-height-auto",
|
||||||
@@ -352,7 +352,7 @@ exports[`LegacyTreeNodeComponent renders loading icon 1`] = `
|
|||||||
duration={200}
|
duration={200}
|
||||||
easing="ease"
|
easing="ease"
|
||||||
height="auto"
|
height="auto"
|
||||||
style={Object {}}
|
style={{}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="nodeChildren"
|
className="nodeChildren"
|
||||||
@@ -376,7 +376,7 @@ exports[`LegacyTreeNodeComponent renders sorted children, expanded, leaves and p
|
|||||||
className="treeNodeHeader "
|
className="treeNodeHeader "
|
||||||
data-test="Tree/TreeNode/Header:label"
|
data-test="Tree/TreeNode/Header:label"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"paddingLeft": 23,
|
"paddingLeft": 23,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -387,7 +387,7 @@ exports[`LegacyTreeNodeComponent renders sorted children, expanded, leaves and p
|
|||||||
className="expandCollapseIcon"
|
className="expandCollapseIcon"
|
||||||
onKeyPress={[Function]}
|
onKeyPress={[Function]}
|
||||||
role="button"
|
role="button"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
@@ -404,10 +404,10 @@ exports[`LegacyTreeNodeComponent renders sorted children, expanded, leaves and p
|
|||||||
ariaLabel="More options"
|
ariaLabel="More options"
|
||||||
className="treeMenuEllipsis"
|
className="treeMenuEllipsis"
|
||||||
menuIconProps={
|
menuIconProps={
|
||||||
Object {
|
{
|
||||||
"iconName": "More",
|
"iconName": "More",
|
||||||
"styles": Object {
|
"styles": {
|
||||||
"root": Object {
|
"root": {
|
||||||
"fontSize": "18px",
|
"fontSize": "18px",
|
||||||
"fontWeight": "bold",
|
"fontWeight": "bold",
|
||||||
},
|
},
|
||||||
@@ -415,20 +415,20 @@ exports[`LegacyTreeNodeComponent renders sorted children, expanded, leaves and p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
menuProps={
|
menuProps={
|
||||||
Object {
|
{
|
||||||
"contextualMenuItemAs": [Function],
|
"contextualMenuItemAs": [Function],
|
||||||
"coverTarget": true,
|
"coverTarget": true,
|
||||||
"directionalHint": 3,
|
"directionalHint": 3,
|
||||||
"isBeakVisible": false,
|
"isBeakVisible": false,
|
||||||
"items": Array [],
|
"items": [],
|
||||||
"onMenuDismissed": [Function],
|
"onMenuDismissed": [Function],
|
||||||
"onMenuOpened": [Function],
|
"onMenuOpened": [Function],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
name="More"
|
name="More"
|
||||||
styles={
|
styles={
|
||||||
Object {
|
{
|
||||||
"rootFocused": Object {
|
"rootFocused": {
|
||||||
"outline": "1px dashed undefined",
|
"outline": "1px dashed undefined",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -449,7 +449,7 @@ exports[`LegacyTreeNodeComponent renders sorted children, expanded, leaves and p
|
|||||||
<AnimateHeight
|
<AnimateHeight
|
||||||
animateOpacity={false}
|
animateOpacity={false}
|
||||||
animationStateClasses={
|
animationStateClasses={
|
||||||
Object {
|
{
|
||||||
"animating": "rah-animating",
|
"animating": "rah-animating",
|
||||||
"animatingDown": "rah-animating--down",
|
"animatingDown": "rah-animating--down",
|
||||||
"animatingToHeightAuto": "rah-animating--to-height-auto",
|
"animatingToHeightAuto": "rah-animating--to-height-auto",
|
||||||
@@ -467,7 +467,7 @@ exports[`LegacyTreeNodeComponent renders sorted children, expanded, leaves and p
|
|||||||
duration={200}
|
duration={200}
|
||||||
easing="ease"
|
easing="ease"
|
||||||
height="auto"
|
height="auto"
|
||||||
style={Object {}}
|
style={{}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="nodeChildren"
|
className="nodeChildren"
|
||||||
@@ -478,12 +478,12 @@ exports[`LegacyTreeNodeComponent renders sorted children, expanded, leaves and p
|
|||||||
generation={13}
|
generation={13}
|
||||||
key="bchild-13-undefined"
|
key="bchild-13-undefined"
|
||||||
node={
|
node={
|
||||||
Object {
|
{
|
||||||
"children": Array [
|
"children": [
|
||||||
Object {
|
{
|
||||||
"label": "ZgrandChild11",
|
"label": "ZgrandChild11",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"label": "AgrandChild12",
|
"label": "AgrandChild12",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -496,12 +496,12 @@ exports[`LegacyTreeNodeComponent renders sorted children, expanded, leaves and p
|
|||||||
generation={13}
|
generation={13}
|
||||||
key="dchild-13-undefined"
|
key="dchild-13-undefined"
|
||||||
node={
|
node={
|
||||||
Object {
|
{
|
||||||
"children": Array [
|
"children": [
|
||||||
Object {
|
{
|
||||||
"label": "ZgrandChild11",
|
"label": "ZgrandChild11",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"label": "AgrandChild12",
|
"label": "AgrandChild12",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -514,7 +514,7 @@ exports[`LegacyTreeNodeComponent renders sorted children, expanded, leaves and p
|
|||||||
generation={13}
|
generation={13}
|
||||||
key="aChild-13-undefined"
|
key="aChild-13-undefined"
|
||||||
node={
|
node={
|
||||||
Object {
|
{
|
||||||
"label": "aChild",
|
"label": "aChild",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -524,7 +524,7 @@ exports[`LegacyTreeNodeComponent renders sorted children, expanded, leaves and p
|
|||||||
generation={13}
|
generation={13}
|
||||||
key="cchild-13-undefined"
|
key="cchild-13-undefined"
|
||||||
node={
|
node={
|
||||||
Object {
|
{
|
||||||
"label": "cchild",
|
"label": "cchild",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -547,7 +547,7 @@ exports[`LegacyTreeNodeComponent renders unsorted children by default 1`] = `
|
|||||||
className="treeNodeHeader "
|
className="treeNodeHeader "
|
||||||
data-test="Tree/TreeNode/Header:label"
|
data-test="Tree/TreeNode/Header:label"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"paddingLeft": 9,
|
"paddingLeft": 9,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -558,7 +558,7 @@ exports[`LegacyTreeNodeComponent renders unsorted children by default 1`] = `
|
|||||||
className="expandCollapseIcon"
|
className="expandCollapseIcon"
|
||||||
onKeyPress={[Function]}
|
onKeyPress={[Function]}
|
||||||
role="button"
|
role="button"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
@@ -580,7 +580,7 @@ exports[`LegacyTreeNodeComponent renders unsorted children by default 1`] = `
|
|||||||
<AnimateHeight
|
<AnimateHeight
|
||||||
animateOpacity={false}
|
animateOpacity={false}
|
||||||
animationStateClasses={
|
animationStateClasses={
|
||||||
Object {
|
{
|
||||||
"animating": "rah-animating",
|
"animating": "rah-animating",
|
||||||
"animatingDown": "rah-animating--down",
|
"animatingDown": "rah-animating--down",
|
||||||
"animatingToHeightAuto": "rah-animating--to-height-auto",
|
"animatingToHeightAuto": "rah-animating--to-height-auto",
|
||||||
@@ -598,7 +598,7 @@ exports[`LegacyTreeNodeComponent renders unsorted children by default 1`] = `
|
|||||||
duration={200}
|
duration={200}
|
||||||
easing="ease"
|
easing="ease"
|
||||||
height="auto"
|
height="auto"
|
||||||
style={Object {}}
|
style={{}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="nodeChildren"
|
className="nodeChildren"
|
||||||
@@ -609,12 +609,12 @@ exports[`LegacyTreeNodeComponent renders unsorted children by default 1`] = `
|
|||||||
generation={3}
|
generation={3}
|
||||||
key="Bchild1-3-undefined"
|
key="Bchild1-3-undefined"
|
||||||
node={
|
node={
|
||||||
Object {
|
{
|
||||||
"children": Array [
|
"children": [
|
||||||
Object {
|
{
|
||||||
"label": "ZgrandChild11",
|
"label": "ZgrandChild11",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"label": "AgrandChild12",
|
"label": "AgrandChild12",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -627,7 +627,7 @@ exports[`LegacyTreeNodeComponent renders unsorted children by default 1`] = `
|
|||||||
generation={3}
|
generation={3}
|
||||||
key="2child2-3-undefined"
|
key="2child2-3-undefined"
|
||||||
node={
|
node={
|
||||||
Object {
|
{
|
||||||
"label": "2child2",
|
"label": "2child2",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,19 @@
|
|||||||
|
import * as msal from "@azure/msal-browser";
|
||||||
import { Link } from "@fluentui/react/lib/Link";
|
import { Link } from "@fluentui/react/lib/Link";
|
||||||
import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
|
import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
|
||||||
|
import { Environment, getEnvironment } from "Common/EnvironmentUtility";
|
||||||
import { sendMessage } from "Common/MessageHandler";
|
import { sendMessage } from "Common/MessageHandler";
|
||||||
import { Platform, configContext } from "ConfigContext";
|
import { Platform, configContext } from "ConfigContext";
|
||||||
import { MessageTypes } from "Contracts/ExplorerContracts";
|
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||||
|
import { useCommandBar } from "Explorer/Menus/CommandBar/useCommandBar";
|
||||||
|
import { useDataPlaneRbac } from "Explorer/Panes/SettingsPane/SettingsPane";
|
||||||
import { getCopilotEnabled, isCopilotFeatureRegistered } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
import { getCopilotEnabled, isCopilotFeatureRegistered } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
||||||
import { IGalleryItem } from "Juno/JunoClient";
|
import { IGalleryItem } from "Juno/JunoClient";
|
||||||
import { scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil";
|
import { scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil";
|
||||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||||
|
import { acquireTokenWithMsal, getMsalInstance } from "Utils/AuthorizationUtils";
|
||||||
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
|
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
|
||||||
|
import { update } from "Utils/arm/generatedClients/cosmos/databaseAccounts";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
@@ -30,20 +36,18 @@ import { PhoenixClient } from "../Phoenix/PhoenixClient";
|
|||||||
import * as ExplorerSettings from "../Shared/ExplorerSettings";
|
import * as ExplorerSettings from "../Shared/ExplorerSettings";
|
||||||
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { isAccountNewerThanThresholdInMs, userContext } from "../UserContext";
|
import { isAccountNewerThanThresholdInMs, updateUserContext, userContext } from "../UserContext";
|
||||||
import { getCollectionName, getUploadName } from "../Utils/APITypeUtils";
|
import { getCollectionName, getUploadName } from "../Utils/APITypeUtils";
|
||||||
import { stringToBlob } from "../Utils/BlobUtils";
|
import { stringToBlob } from "../Utils/BlobUtils";
|
||||||
import { isCapabilityEnabled } from "../Utils/CapabilityUtils";
|
import { isCapabilityEnabled } from "../Utils/CapabilityUtils";
|
||||||
import { fromContentUri, toRawContentUri } from "../Utils/GitHubUtils";
|
import { fromContentUri, toRawContentUri } from "../Utils/GitHubUtils";
|
||||||
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
||||||
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../Utils/NotificationConsoleUtils";
|
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../Utils/NotificationConsoleUtils";
|
||||||
import { update } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
|
|
||||||
import { useSidePanel } from "../hooks/useSidePanel";
|
import { useSidePanel } from "../hooks/useSidePanel";
|
||||||
import { useTabs } from "../hooks/useTabs";
|
import { useTabs } from "../hooks/useTabs";
|
||||||
import "./ComponentRegisterer";
|
import "./ComponentRegisterer";
|
||||||
import { DialogProps, useDialog } from "./Controls/Dialog";
|
import { DialogProps, useDialog } from "./Controls/Dialog";
|
||||||
import { GalleryTab as GalleryTabKind } from "./Controls/NotebookGallery/GalleryViewerComponent";
|
import { GalleryTab as GalleryTabKind } from "./Controls/NotebookGallery/GalleryViewerComponent";
|
||||||
import { useCommandBar } from "./Menus/CommandBar/CommandBarComponentAdapter";
|
|
||||||
import * as FileSystemUtil from "./Notebook/FileSystemUtil";
|
import * as FileSystemUtil from "./Notebook/FileSystemUtil";
|
||||||
import { SnapshotRequest } from "./Notebook/NotebookComponent/types";
|
import { SnapshotRequest } from "./Notebook/NotebookComponent/types";
|
||||||
import { NotebookContentItem, NotebookContentItemType } from "./Notebook/NotebookContentItem";
|
import { NotebookContentItem, NotebookContentItemType } from "./Notebook/NotebookContentItem";
|
||||||
@@ -251,12 +255,48 @@ export default class Explorer {
|
|||||||
};
|
};
|
||||||
useDialog.getState().openDialog(addSynapseLinkDialogProps);
|
useDialog.getState().openDialog(addSynapseLinkDialogProps);
|
||||||
TelemetryProcessor.traceStart(Action.EnableAzureSynapseLink);
|
TelemetryProcessor.traceStart(Action.EnableAzureSynapseLink);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: return result
|
public async openLoginForEntraIDPopUp(): Promise<void> {
|
||||||
|
if (userContext.databaseAccount.properties?.documentEndpoint) {
|
||||||
|
const hrefEndpoint = new URL(userContext.databaseAccount.properties.documentEndpoint).href.replace(
|
||||||
|
/\/$/,
|
||||||
|
"/.default",
|
||||||
|
);
|
||||||
|
const msalInstance = await getMsalInstance();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await msalInstance.loginPopup({
|
||||||
|
redirectUri: configContext.msalRedirectURI,
|
||||||
|
scopes: [],
|
||||||
|
});
|
||||||
|
localStorage.setItem("cachedTenantId", response.tenantId);
|
||||||
|
const cachedAccount = msalInstance.getAllAccounts()?.[0];
|
||||||
|
msalInstance.setActiveAccount(cachedAccount);
|
||||||
|
const aadToken = await acquireTokenWithMsal(msalInstance, {
|
||||||
|
forceRefresh: true,
|
||||||
|
scopes: [hrefEndpoint],
|
||||||
|
authority: `${configContext.AAD_ENDPOINT}${localStorage.getItem("cachedTenantId")}`,
|
||||||
|
});
|
||||||
|
updateUserContext({ aadToken: aadToken });
|
||||||
|
useDataPlaneRbac.setState({ aadTokenUpdated: true });
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof msal.AuthError && error.errorCode === msal.BrowserAuthErrorMessage.popUpWindowError.code) {
|
||||||
|
logConsoleError(
|
||||||
|
"We were unable to establish authorization for this account, due to pop-ups being disabled in the browser.\nPlease enable pop-ups for this site and try again",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const errorJson = JSON.stringify(error);
|
||||||
|
logConsoleError(
|
||||||
|
`Failed to perform authorization for this account, due to the following error: \n${errorJson}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public openNPSSurveyDialog(): void {
|
public openNPSSurveyDialog(): void {
|
||||||
if (!Platform.Portal) {
|
if (!Platform.Portal || !["Postgres", "SQL", "Mongo"].includes(userContext.apiType)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,16 +394,16 @@ export default class Explorer {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
public onRefreshResourcesClick = (): void => {
|
public onRefreshResourcesClick = async (): Promise<void> => {
|
||||||
if (configContext.platform === Platform.Fabric) {
|
if (configContext.platform === Platform.Fabric) {
|
||||||
scheduleRefreshDatabaseResourceToken(true).then(() => this.refreshAllDatabases());
|
scheduleRefreshDatabaseResourceToken(true).then(() => this.refreshAllDatabases());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
userContext.authType === AuthType.ResourceToken
|
await (userContext.authType === AuthType.ResourceToken
|
||||||
? this.refreshDatabaseForResourceToken()
|
? this.refreshDatabaseForResourceToken()
|
||||||
: this.refreshAllDatabases();
|
: this.refreshAllDatabases());
|
||||||
this.refreshNotebookList();
|
await this.refreshNotebookList();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Facade
|
// Facade
|
||||||
@@ -1080,7 +1120,7 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public openUploadItemsPanePane(): void {
|
public openUploadItemsPane(): void {
|
||||||
useSidePanel.getState().openSidePanel("Upload " + getUploadName(), <UploadItemsPane />);
|
useSidePanel.getState().openSidePanel("Upload " + getUploadName(), <UploadItemsPane />);
|
||||||
}
|
}
|
||||||
public openExecuteSprocParamsPanel(storedProcedure: StoredProcedure): void {
|
public openExecuteSprocParamsPanel(storedProcedure: StoredProcedure): void {
|
||||||
@@ -1139,7 +1179,11 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async configureCopilot(): Promise<void> {
|
public async configureCopilot(): Promise<void> {
|
||||||
if (userContext.apiType !== "SQL" || !userContext.subscriptionId) {
|
if (
|
||||||
|
userContext.apiType !== "SQL" ||
|
||||||
|
!userContext.subscriptionId ||
|
||||||
|
![Environment.Development, Environment.Mpac, Environment.Prod].includes(getEnvironment())
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const copilotEnabledPromise = getCopilotEnabled();
|
const copilotEnabledPromise = getCopilotEnabled();
|
||||||
|
|||||||
@@ -737,17 +737,17 @@ export class D3ForceGraph implements GraphRenderer {
|
|||||||
.on("dblclick", function (this: Element, _: MouseEvent, d: D3Node) {
|
.on("dblclick", function (this: Element, _: MouseEvent, d: D3Node) {
|
||||||
// https://stackoverflow.com/a/41945742 ('this' implicitly has type 'any' because it does not have a type annotation)
|
// https://stackoverflow.com/a/41945742 ('this' implicitly has type 'any' because it does not have a type annotation)
|
||||||
// this is the <g> element
|
// this is the <g> element
|
||||||
self.onNodeClicked(this.parentNode, d);
|
self.onNodeClicked(this.parentNode as BaseType, d);
|
||||||
})
|
})
|
||||||
.on("click", function (this: Element, _: MouseEvent, d: D3Node) {
|
.on("click", function (this: Element, _: MouseEvent, d: D3Node) {
|
||||||
// this is the <g> element
|
// this is the <g> element
|
||||||
self.onNodeClicked(this.parentNode, d);
|
self.onNodeClicked(this.parentNode as BaseType, d);
|
||||||
})
|
})
|
||||||
.on("keypress", function (this: Element, event: KeyboardEvent, d: D3Node) {
|
.on("keypress", function (this: Element, event: KeyboardEvent, d: D3Node) {
|
||||||
if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) {
|
if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
// this is the <g> element
|
// this is the <g> element
|
||||||
self.onNodeClicked(this.parentNode, d);
|
self.onNodeClicked(this.parentNode as BaseType, d);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var nodeSize = this.igraphConfig.nodeSize;
|
var nodeSize = this.igraphConfig.nodeSize;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { GraphData, GremlinVertex, GremlinEdge } from "./GraphData";
|
import { GraphData, GremlinEdge, GremlinVertex } from "./GraphData";
|
||||||
|
|
||||||
describe("Graph Data", () => {
|
describe("Graph Data", () => {
|
||||||
it("should set only one node as root", () => {
|
it("should set only one node as root", () => {
|
||||||
@@ -54,12 +54,12 @@ describe("Graph Data", () => {
|
|||||||
// in edge
|
// in edge
|
||||||
graphData.removeEdge("e1", false);
|
graphData.removeEdge("e1", false);
|
||||||
expect(graphData.edges.length).toBe(1);
|
expect(graphData.edges.length).toBe(1);
|
||||||
expect(graphData).not.toContain(jasmine.objectContaining({ id: "e1" }));
|
expect(graphData).not.toContainEqual({ id: "e1" });
|
||||||
|
|
||||||
// out edge
|
// out edge
|
||||||
graphData.removeEdge("e2", false);
|
graphData.removeEdge("e2", false);
|
||||||
expect(graphData.edges.length).toBe(0);
|
expect(graphData.edges.length).toBe(0);
|
||||||
expect(graphData).not.toContain(jasmine.objectContaining({ id: "e2" }));
|
expect(graphData).not.toContainEqual({ id: "e2" });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should get string node property", () => {
|
it("should get string node property", () => {
|
||||||
|
|||||||
@@ -342,7 +342,7 @@ describe("GraphExplorer", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("Load Graph button", () => {
|
describe("Load Graph button", () => {
|
||||||
beforeEach(async (done) => {
|
beforeEach((done) => {
|
||||||
const backendResponses: BackendResponses = {};
|
const backendResponses: BackendResponses = {};
|
||||||
backendResponses["g.V()"] = backendResponses["g.V('1')"] = {
|
backendResponses["g.V()"] = backendResponses["g.V('1')"] = {
|
||||||
response: [{ id: "1", type: "vertex" }],
|
response: [{ id: "1", type: "vertex" }],
|
||||||
@@ -632,24 +632,15 @@ describe("GraphExplorer", () => {
|
|||||||
|
|
||||||
it("should display RU consumption", () => {
|
it("should display RU consumption", () => {
|
||||||
// Find link for query stats
|
// Find link for query stats
|
||||||
const links = wrapper.find(".toggleSwitch");
|
const queryStatsTab = wrapper.find(`button[name="${GraphExplorer.QUERY_STATS_BUTTON_LABEL}"]`);
|
||||||
|
queryStatsTab.simulate("click");
|
||||||
|
const values = wrapper.find(".queryMetricsSummary td");
|
||||||
let isRUDisplayed = false;
|
let isRUDisplayed = false;
|
||||||
for (let i = 0; i < links.length; i++) {
|
values.forEach((value) => {
|
||||||
const link = links.at(i);
|
if (Number(value.text()) === gVRU) {
|
||||||
if (link.text() === GraphExplorer.QUERY_STATS_BUTTON_LABEL) {
|
isRUDisplayed = true;
|
||||||
link.simulate("click");
|
|
||||||
|
|
||||||
const values = wrapper.find(".queryMetricsSummary td");
|
|
||||||
for (let j = 0; j < values.length; j++) {
|
|
||||||
if (Number(values.at(j).text()) === gVRU) {
|
|
||||||
isRUDisplayed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
expect(isRUDisplayed).toBe(true);
|
expect(isRUDisplayed).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -348,8 +348,9 @@ export class NodePropertiesComponent extends React.Component<
|
|||||||
as="span"
|
as="span"
|
||||||
onActivated={this.setIsDeleteConfirm.bind(this, true)}
|
onActivated={this.setIsDeleteConfirm.bind(this, true)}
|
||||||
aria-label="Delete this vertex"
|
aria-label="Delete this vertex"
|
||||||
|
role="button"
|
||||||
>
|
>
|
||||||
<img src={DeleteIcon} alt="Delete" role="button" />
|
<img src={DeleteIcon} alt="Delete" aria-label="hidden" />
|
||||||
</AccessibleElement>
|
</AccessibleElement>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -405,8 +406,9 @@ export class NodePropertiesComponent extends React.Component<
|
|||||||
as="span"
|
as="span"
|
||||||
aria-label="Edit properties"
|
aria-label="Edit properties"
|
||||||
onActivated={expandClickHandler}
|
onActivated={expandClickHandler}
|
||||||
|
role="button"
|
||||||
>
|
>
|
||||||
<img src={EditIcon} alt="Edit" role="button" />
|
<img src={EditIcon} alt="Edit" aria-label="hidden" />
|
||||||
</AccessibleElement>
|
</AccessibleElement>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ exports[`<EditorNodePropertiesComponent /> renders component 1`] = `
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="Delete"
|
alt="Delete"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
/>
|
/>
|
||||||
</AccessibleElement>
|
</AccessibleElement>
|
||||||
</td>
|
</td>
|
||||||
@@ -185,7 +185,7 @@ exports[`<EditorNodePropertiesComponent /> renders component 1`] = `
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="Delete"
|
alt="Delete"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
/>
|
/>
|
||||||
</AccessibleElement>
|
</AccessibleElement>
|
||||||
</td>
|
</td>
|
||||||
@@ -203,7 +203,7 @@ exports[`<EditorNodePropertiesComponent /> renders component 1`] = `
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="Add"
|
alt="Add"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
/>
|
/>
|
||||||
Add Property
|
Add Property
|
||||||
</AccessibleElement>
|
</AccessibleElement>
|
||||||
@@ -317,7 +317,7 @@ exports[`<EditorNodePropertiesComponent /> renders proper unicode 1`] = `
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="Delete"
|
alt="Delete"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
/>
|
/>
|
||||||
</AccessibleElement>
|
</AccessibleElement>
|
||||||
</td>
|
</td>
|
||||||
@@ -379,7 +379,7 @@ exports[`<EditorNodePropertiesComponent /> renders proper unicode 1`] = `
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="Delete"
|
alt="Delete"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
/>
|
/>
|
||||||
</AccessibleElement>
|
</AccessibleElement>
|
||||||
</td>
|
</td>
|
||||||
@@ -397,7 +397,7 @@ exports[`<EditorNodePropertiesComponent /> renders proper unicode 1`] = `
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="Add"
|
alt="Add"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
/>
|
/>
|
||||||
Add Property
|
Add Property
|
||||||
</AccessibleElement>
|
</AccessibleElement>
|
||||||
|
|||||||
@@ -4,83 +4,51 @@
|
|||||||
* and update any knockout observables passed from the parent.
|
* and update any knockout observables passed from the parent.
|
||||||
*/
|
*/
|
||||||
import { CommandBar as FluentCommandBar, ICommandBarItemProps } from "@fluentui/react";
|
import { CommandBar as FluentCommandBar, ICommandBarItemProps } from "@fluentui/react";
|
||||||
|
import {
|
||||||
|
createPlatformButtons,
|
||||||
|
createStaticCommandBarButtons,
|
||||||
|
} from "Explorer/Menus/CommandBar/CommandBarComponentButtonFactory";
|
||||||
|
import { useCommandBar } from "Explorer/Menus/CommandBar/useCommandBar";
|
||||||
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
||||||
import { KeyboardActionGroup, useKeyboardActionGroup } from "KeyboardShortcuts";
|
import { KeyboardActionGroup, useKeyboardActionGroup } from "KeyboardShortcuts";
|
||||||
import { userContext } from "UserContext";
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import create, { UseStore } from "zustand";
|
|
||||||
import { ConnectionStatusType, PoolIdType } from "../../../Common/Constants";
|
import { ConnectionStatusType, PoolIdType } from "../../../Common/Constants";
|
||||||
import { StyleConstants } from "../../../Common/StyleConstants";
|
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||||
import { Platform, configContext } from "../../../ConfigContext";
|
import { Platform, configContext } from "../../../ConfigContext";
|
||||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
|
||||||
import Explorer from "../../Explorer";
|
import Explorer from "../../Explorer";
|
||||||
import { useSelectedNode } from "../../useSelectedNode";
|
import { useSelectedNode } from "../../useSelectedNode";
|
||||||
import * as CommandBarComponentButtonFactory from "./CommandBarComponentButtonFactory";
|
|
||||||
import * as CommandBarUtil from "./CommandBarUtil";
|
import * as CommandBarUtil from "./CommandBarUtil";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
container: Explorer;
|
container: Explorer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommandBarStore {
|
|
||||||
contextButtons: CommandButtonComponentProps[];
|
|
||||||
setContextButtons: (contextButtons: CommandButtonComponentProps[]) => void;
|
|
||||||
isHidden: boolean;
|
|
||||||
setIsHidden: (isHidden: boolean) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useCommandBar: UseStore<CommandBarStore> = create((set) => ({
|
|
||||||
contextButtons: [],
|
|
||||||
setContextButtons: (contextButtons: CommandButtonComponentProps[]) => set((state) => ({ ...state, contextButtons })),
|
|
||||||
isHidden: false,
|
|
||||||
setIsHidden: (isHidden: boolean) => set((state) => ({ ...state, isHidden })),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
||||||
const selectedNodeState = useSelectedNode();
|
const selectedNodeState = useSelectedNode();
|
||||||
const buttons = useCommandBar((state) => state.contextButtons);
|
const contextButtons = useCommandBar((state) => state.contextButtons);
|
||||||
const isHidden = useCommandBar((state) => state.isHidden);
|
const isHidden = useCommandBar((state) => state.isHidden);
|
||||||
const backgroundColor = StyleConstants.BaseLight;
|
const backgroundColor = StyleConstants.BaseLight;
|
||||||
const setKeyboardHandlers = useKeyboardActionGroup(KeyboardActionGroup.COMMAND_BAR);
|
const setKeyboardHandlers = useKeyboardActionGroup(KeyboardActionGroup.COMMAND_BAR);
|
||||||
|
const staticButtons = createStaticCommandBarButtons(selectedNodeState);
|
||||||
|
const platformButtons = createPlatformButtons();
|
||||||
|
|
||||||
if (userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo") {
|
const uiFabricStaticButtons = CommandBarUtil.convertButton(staticButtons, backgroundColor, container);
|
||||||
const buttons =
|
if (contextButtons?.length > 0) {
|
||||||
userContext.apiType === "Postgres"
|
|
||||||
? CommandBarComponentButtonFactory.createPostgreButtons(container)
|
|
||||||
: CommandBarComponentButtonFactory.createVCoreMongoButtons(container);
|
|
||||||
return (
|
|
||||||
<div className="commandBarContainer" style={{ display: isHidden ? "none" : "initial" }}>
|
|
||||||
<FluentCommandBar
|
|
||||||
ariaLabel="Use left and right arrow keys to navigate between commands"
|
|
||||||
items={CommandBarUtil.convertButton(buttons, backgroundColor)}
|
|
||||||
styles={{
|
|
||||||
root: { backgroundColor: backgroundColor },
|
|
||||||
}}
|
|
||||||
overflowButtonProps={{ ariaLabel: "More commands" }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
uiFabricStaticButtons.forEach((btn: ICommandBarItemProps) => (btn.iconOnly = true));
|
||||||
}
|
}
|
||||||
|
|
||||||
const uiFabricTabsButtons: ICommandBarItemProps[] = CommandBarUtil.convertButton(contextButtons, backgroundColor);
|
const uiFabricTabsButtons: ICommandBarItemProps[] = CommandBarUtil.convertButton(
|
||||||
|
contextButtons || [],
|
||||||
|
backgroundColor,
|
||||||
|
container,
|
||||||
|
);
|
||||||
|
|
||||||
if (uiFabricTabsButtons.length > 0) {
|
if (uiFabricTabsButtons.length > 0) {
|
||||||
uiFabricStaticButtons.push(CommandBarUtil.createDivider("commandBarDivider"));
|
uiFabricStaticButtons.push(CommandBarUtil.createDivider("commandBarDivider"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const uiFabricControlButtons = CommandBarUtil.convertButton(controlButtons, backgroundColor);
|
const uiFabricPlatformButtons = CommandBarUtil.convertButton(platformButtons || [], backgroundColor, container);
|
||||||
uiFabricControlButtons.forEach((btn: ICommandBarItemProps) => (btn.iconOnly = true));
|
uiFabricPlatformButtons.forEach((btn: ICommandBarItemProps) => (btn.iconOnly = true));
|
||||||
|
|
||||||
const connectionInfo = useNotebook((state) => state.connectionInfo);
|
const connectionInfo = useNotebook((state) => state.connectionInfo);
|
||||||
|
|
||||||
@@ -88,7 +56,7 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
|||||||
(useNotebook.getState().isPhoenixNotebooks || useNotebook.getState().isPhoenixFeatures) &&
|
(useNotebook.getState().isPhoenixNotebooks || useNotebook.getState().isPhoenixFeatures) &&
|
||||||
connectionInfo?.status !== ConnectionStatusType.Connect
|
connectionInfo?.status !== ConnectionStatusType.Connect
|
||||||
) {
|
) {
|
||||||
uiFabricControlButtons.unshift(
|
uiFabricPlatformButtons.unshift(
|
||||||
CommandBarUtil.createConnectionStatus(container, PoolIdType.DefaultPoolId, "connectionStatus"),
|
CommandBarUtil.createConnectionStatus(container, PoolIdType.DefaultPoolId, "connectionStatus"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -107,8 +75,8 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const allButtons = staticButtons.concat(contextButtons).concat(controlButtons);
|
const allButtons = staticButtons.concat(contextButtons).concat(platformButtons);
|
||||||
const keyboardHandlers = CommandBarUtil.createKeyboardHandlers(allButtons);
|
const keyboardHandlers = CommandBarUtil.createKeyboardHandlers(allButtons, container);
|
||||||
setKeyboardHandlers(keyboardHandlers);
|
setKeyboardHandlers(keyboardHandlers);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -116,7 +84,7 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
|||||||
<FluentCommandBar
|
<FluentCommandBar
|
||||||
ariaLabel="Use left and right arrow keys to navigate between commands"
|
ariaLabel="Use left and right arrow keys to navigate between commands"
|
||||||
items={uiFabricStaticButtons.concat(uiFabricTabsButtons)}
|
items={uiFabricStaticButtons.concat(uiFabricTabsButtons)}
|
||||||
farItems={uiFabricControlButtons}
|
farItems={uiFabricPlatformButtons}
|
||||||
styles={rootStyle}
|
styles={rootStyle}
|
||||||
overflowButtonProps={{ ariaLabel: "More commands" }}
|
overflowButtonProps={{ ariaLabel: "More commands" }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3,15 +3,12 @@ import { AuthType } from "../../../AuthType";
|
|||||||
import { DatabaseAccount } from "../../../Contracts/DataModels";
|
import { DatabaseAccount } from "../../../Contracts/DataModels";
|
||||||
import { CollectionBase } from "../../../Contracts/ViewModels";
|
import { CollectionBase } from "../../../Contracts/ViewModels";
|
||||||
import { updateUserContext } from "../../../UserContext";
|
import { updateUserContext } from "../../../UserContext";
|
||||||
import Explorer from "../../Explorer";
|
|
||||||
import { useNotebook } from "../../Notebook/useNotebook";
|
import { useNotebook } from "../../Notebook/useNotebook";
|
||||||
import { useDatabases } from "../../useDatabases";
|
import { useDatabases } from "../../useDatabases";
|
||||||
import { useSelectedNode } from "../../useSelectedNode";
|
import { useSelectedNode } from "../../useSelectedNode";
|
||||||
import * as CommandBarComponentButtonFactory from "./CommandBarComponentButtonFactory";
|
import * as CommandBarComponentButtonFactory from "./CommandBarComponentButtonFactory";
|
||||||
|
|
||||||
describe("CommandBarComponentButtonFactory tests", () => {
|
describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
let mockExplorer: Explorer;
|
|
||||||
|
|
||||||
afterEach(() => useSelectedNode.getState().setSelectedNode(undefined));
|
afterEach(() => useSelectedNode.getState().setSelectedNode(undefined));
|
||||||
|
|
||||||
describe("Enable Azure Synapse Link Button", () => {
|
describe("Enable Azure Synapse Link Button", () => {
|
||||||
@@ -19,7 +16,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
const selectedNodeState = useSelectedNode.getState();
|
const selectedNodeState = useSelectedNode.getState();
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
mockExplorer = {} as Explorer;
|
|
||||||
updateUserContext({
|
updateUserContext({
|
||||||
databaseAccount: {
|
databaseAccount: {
|
||||||
properties: {
|
properties: {
|
||||||
@@ -30,7 +26,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Button should be visible", () => {
|
it("Button should be visible", () => {
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(selectedNodeState);
|
||||||
const enableAzureSynapseLinkBtn = buttons.find(
|
const enableAzureSynapseLinkBtn = buttons.find(
|
||||||
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel,
|
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel,
|
||||||
);
|
);
|
||||||
@@ -46,7 +42,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
} as DatabaseAccount,
|
} as DatabaseAccount,
|
||||||
});
|
});
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(selectedNodeState);
|
||||||
const enableAzureSynapseLinkBtn = buttons.find(
|
const enableAzureSynapseLinkBtn = buttons.find(
|
||||||
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel,
|
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel,
|
||||||
);
|
);
|
||||||
@@ -62,7 +58,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
} as DatabaseAccount,
|
} as DatabaseAccount,
|
||||||
});
|
});
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(selectedNodeState);
|
||||||
const enableAzureSynapseLinkBtn = buttons.find(
|
const enableAzureSynapseLinkBtn = buttons.find(
|
||||||
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel,
|
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel,
|
||||||
);
|
);
|
||||||
@@ -75,7 +71,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
const selectedNodeState = useSelectedNode.getState();
|
const selectedNodeState = useSelectedNode.getState();
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
mockExplorer = {} as Explorer;
|
|
||||||
updateUserContext({
|
updateUserContext({
|
||||||
databaseAccount: {
|
databaseAccount: {
|
||||||
properties: {
|
properties: {
|
||||||
@@ -108,7 +103,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
},
|
},
|
||||||
} as DatabaseAccount,
|
} as DatabaseAccount,
|
||||||
});
|
});
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(selectedNodeState);
|
||||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||||
expect(openCassandraShellBtn).toBeUndefined();
|
expect(openCassandraShellBtn).toBeUndefined();
|
||||||
});
|
});
|
||||||
@@ -118,13 +113,13 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
portalEnv: "mooncake",
|
portalEnv: "mooncake",
|
||||||
});
|
});
|
||||||
|
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(selectedNodeState);
|
||||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||||
expect(openCassandraShellBtn).toBeUndefined();
|
expect(openCassandraShellBtn).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is not enabled and is unavailable - button should be shown and disabled", () => {
|
it("Notebooks is not enabled and is unavailable - button should be shown and disabled", () => {
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(selectedNodeState);
|
||||||
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
const openCassandraShellBtn = buttons.find((button) => button.commandButtonLabel === openCassandraShellBtnLabel);
|
||||||
expect(openCassandraShellBtn).toBeUndefined();
|
expect(openCassandraShellBtn).toBeUndefined();
|
||||||
});
|
});
|
||||||
@@ -134,12 +129,8 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
const openPostgresShellButtonLabel = "Open PSQL shell";
|
const openPostgresShellButtonLabel = "Open PSQL shell";
|
||||||
const openVCoreMongoShellButtonLabel = "Open MongoDB (vCore) shell";
|
const openVCoreMongoShellButtonLabel = "Open MongoDB (vCore) shell";
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
mockExplorer = {} as Explorer;
|
|
||||||
});
|
|
||||||
|
|
||||||
it("creates Postgres shell button", () => {
|
it("creates Postgres shell button", () => {
|
||||||
const buttons = CommandBarComponentButtonFactory.createPostgreButtons(mockExplorer);
|
const buttons = CommandBarComponentButtonFactory.createPostgreButtons();
|
||||||
const openPostgresShellButton = buttons.find(
|
const openPostgresShellButton = buttons.find(
|
||||||
(button) => button.commandButtonLabel === openPostgresShellButtonLabel,
|
(button) => button.commandButtonLabel === openPostgresShellButtonLabel,
|
||||||
);
|
);
|
||||||
@@ -147,7 +138,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("creates vCore Mongo shell button", () => {
|
it("creates vCore Mongo shell button", () => {
|
||||||
const buttons = CommandBarComponentButtonFactory.createVCoreMongoButtons(mockExplorer);
|
const buttons = CommandBarComponentButtonFactory.createVCoreMongoButtons();
|
||||||
const openVCoreMongoShellButton = buttons.find(
|
const openVCoreMongoShellButton = buttons.find(
|
||||||
(button) => button.commandButtonLabel === openVCoreMongoShellButtonLabel,
|
(button) => button.commandButtonLabel === openVCoreMongoShellButtonLabel,
|
||||||
);
|
);
|
||||||
@@ -162,8 +153,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
const selectedNodeState = useSelectedNode.getState();
|
const selectedNodeState = useSelectedNode.getState();
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
mockExplorer = {} as Explorer;
|
|
||||||
|
|
||||||
updateUserContext({
|
updateUserContext({
|
||||||
authType: AuthType.ResourceToken,
|
authType: AuthType.ResourceToken,
|
||||||
});
|
});
|
||||||
@@ -175,7 +164,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
kind: "DocumentDB",
|
kind: "DocumentDB",
|
||||||
} as DatabaseAccount,
|
} as DatabaseAccount,
|
||||||
});
|
});
|
||||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(selectedNodeState);
|
||||||
expect(buttons.length).toBe(2);
|
expect(buttons.length).toBe(2);
|
||||||
expect(buttons[0].commandButtonLabel).toBe("New SQL Query");
|
expect(buttons[0].commandButtonLabel).toBe("New SQL Query");
|
||||||
expect(buttons[0].disabled).toBe(false);
|
expect(buttons[0].disabled).toBe(false);
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
import { KeyboardAction } from "KeyboardShortcuts";
|
import { KeyboardAction } from "KeyboardShortcuts";
|
||||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import AddCollectionIcon from "../../../../images/AddCollection.svg";
|
import { useEffect, useState } from "react";
|
||||||
import AddDatabaseIcon from "../../../../images/AddDatabase.svg";
|
|
||||||
import AddSqlQueryIcon from "../../../../images/AddSqlQuery_16x16.svg";
|
import AddSqlQueryIcon from "../../../../images/AddSqlQuery_16x16.svg";
|
||||||
import AddStoredProcedureIcon from "../../../../images/AddStoredProcedure.svg";
|
import AddStoredProcedureIcon from "../../../../images/AddStoredProcedure.svg";
|
||||||
import AddTriggerIcon from "../../../../images/AddTrigger.svg";
|
import AddTriggerIcon from "../../../../images/AddTrigger.svg";
|
||||||
import AddUdfIcon from "../../../../images/AddUdf.svg";
|
import AddUdfIcon from "../../../../images/AddUdf.svg";
|
||||||
import BrowseQueriesIcon from "../../../../images/BrowseQuery.svg";
|
import BrowseQueriesIcon from "../../../../images/BrowseQuery.svg";
|
||||||
|
import EntraIDIcon from "../../../../images/EntraID.svg";
|
||||||
import FeedbackIcon from "../../../../images/Feedback-Command.svg";
|
import FeedbackIcon from "../../../../images/Feedback-Command.svg";
|
||||||
import HomeIcon from "../../../../images/Home_16.svg";
|
|
||||||
import HostedTerminalIcon from "../../../../images/Hosted-Terminal.svg";
|
import HostedTerminalIcon from "../../../../images/Hosted-Terminal.svg";
|
||||||
import OpenQueryFromDiskIcon from "../../../../images/OpenQueryFromDisk.svg";
|
import OpenQueryFromDiskIcon from "../../../../images/OpenQueryFromDisk.svg";
|
||||||
import OpenInTabIcon from "../../../../images/open-in-tab.svg";
|
import OpenInTabIcon from "../../../../images/open-in-tab.svg";
|
||||||
@@ -20,59 +18,62 @@ import * as Constants from "../../../Common/Constants";
|
|||||||
import { Platform, configContext } from "../../../ConfigContext";
|
import { Platform, configContext } from "../../../ConfigContext";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
import { userContext } from "../../../UserContext";
|
import { userContext } from "../../../UserContext";
|
||||||
import { getCollectionName, getDatabaseName } from "../../../Utils/APITypeUtils";
|
|
||||||
import { isRunningOnNationalCloud } from "../../../Utils/CloudUtils";
|
import { isRunningOnNationalCloud } from "../../../Utils/CloudUtils";
|
||||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
import Explorer from "../../Explorer";
|
|
||||||
import { useNotebook } from "../../Notebook/useNotebook";
|
import { useNotebook } from "../../Notebook/useNotebook";
|
||||||
import { OpenFullScreen } from "../../OpenFullScreen";
|
import { OpenFullScreen } from "../../OpenFullScreen";
|
||||||
import { AddDatabasePanel } from "../../Panes/AddDatabasePanel/AddDatabasePanel";
|
|
||||||
import { BrowseQueriesPane } from "../../Panes/BrowseQueriesPane/BrowseQueriesPane";
|
import { BrowseQueriesPane } from "../../Panes/BrowseQueriesPane/BrowseQueriesPane";
|
||||||
import { LoadQueryPane } from "../../Panes/LoadQueryPane/LoadQueryPane";
|
import { LoadQueryPane } from "../../Panes/LoadQueryPane/LoadQueryPane";
|
||||||
import { SettingsPane } from "../../Panes/SettingsPane/SettingsPane";
|
import { SettingsPane, useDataPlaneRbac } from "../../Panes/SettingsPane/SettingsPane";
|
||||||
import { useDatabases } from "../../useDatabases";
|
import { useDatabases } from "../../useDatabases";
|
||||||
import { SelectedNodeState, useSelectedNode } from "../../useSelectedNode";
|
import { SelectedNodeState, useSelectedNode } from "../../useSelectedNode";
|
||||||
|
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
|
|
||||||
export function createStaticCommandBarButtons(
|
export function createStaticCommandBarButtons(selectedNodeState: SelectedNodeState): CommandButtonComponentProps[] {
|
||||||
container: Explorer,
|
if (userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo") {
|
||||||
selectedNodeState: SelectedNodeState,
|
return userContext.apiType === "Postgres" ? createPostgreButtons() : createVCoreMongoButtons();
|
||||||
): CommandButtonComponentProps[] {
|
}
|
||||||
|
|
||||||
if (userContext.authType === AuthType.ResourceToken) {
|
if (userContext.authType === AuthType.ResourceToken) {
|
||||||
return createStaticCommandBarButtonsForResourceToken(container, selectedNodeState);
|
return createStaticCommandBarButtonsForResourceToken(selectedNodeState);
|
||||||
}
|
}
|
||||||
|
|
||||||
const buttons: CommandButtonComponentProps[] = [];
|
const buttons: CommandButtonComponentProps[] = [];
|
||||||
|
|
||||||
// Avoid starting with a divider
|
// Avoid starting with a divider
|
||||||
const addDivider = () => {
|
const addDivider = () => {
|
||||||
if (buttons.length > 0) {
|
if (buttons.length > 0 && !buttons[buttons.length - 1].isDivider) {
|
||||||
buttons.push(createDivider());
|
buttons.push(createDivider());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (configContext.platform !== Platform.Fabric) {
|
if (
|
||||||
const homeBtn = createHomeButton();
|
configContext.platform !== Platform.Fabric &&
|
||||||
buttons.push(homeBtn);
|
userContext.apiType !== "Tables" &&
|
||||||
|
userContext.apiType !== "Cassandra"
|
||||||
const newCollectionBtn = createNewCollectionGroup(container);
|
) {
|
||||||
newCollectionBtn.keyboardAction = KeyboardAction.NEW_COLLECTION; // Just for the root button, not the child version we create below.
|
const addSynapseLink = createOpenSynapseLinkDialogButton();
|
||||||
buttons.push(newCollectionBtn);
|
if (addSynapseLink) {
|
||||||
if (userContext.apiType !== "Tables" && userContext.apiType !== "Cassandra") {
|
addDivider();
|
||||||
const addSynapseLink = createOpenSynapseLinkDialogButton(container);
|
buttons.push(addSynapseLink);
|
||||||
|
|
||||||
if (addSynapseLink) {
|
|
||||||
addDivider();
|
|
||||||
buttons.push(addSynapseLink);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (userContext.apiType !== "Tables") {
|
if (userContext.apiType === "SQL") {
|
||||||
newCollectionBtn.children = [createNewCollectionGroup(container)];
|
const [loginButtonProps, setLoginButtonProps] = useState<CommandButtonComponentProps | undefined>(undefined);
|
||||||
const newDatabaseBtn = createNewDatabase(container);
|
const dataPlaneRbacEnabled = useDataPlaneRbac((state) => state.dataPlaneRbacEnabled);
|
||||||
newCollectionBtn.children.push(newDatabaseBtn);
|
const aadTokenUpdated = useDataPlaneRbac((state) => state.aadTokenUpdated);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const buttonProps = createLoginForEntraIDButton();
|
||||||
|
setLoginButtonProps(buttonProps);
|
||||||
|
}, [dataPlaneRbacEnabled, aadTokenUpdated]);
|
||||||
|
|
||||||
|
if (loginButtonProps) {
|
||||||
|
addDivider();
|
||||||
|
buttons.push(loginButtonProps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,8 +87,8 @@ export function createStaticCommandBarButtons(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isQuerySupported && selectedNodeState.findSelectedCollection() && configContext.platform !== Platform.Fabric) {
|
if (isQuerySupported && selectedNodeState.findSelectedCollection() && configContext.platform !== Platform.Fabric) {
|
||||||
const openQueryBtn = createOpenQueryButton(container);
|
const openQueryBtn = createOpenQueryButton();
|
||||||
openQueryBtn.children = [createOpenQueryButton(container), createOpenQueryFromDiskButton()];
|
openQueryBtn.children = [createOpenQueryButton(), createOpenQueryFromDiskButton()];
|
||||||
buttons.push(openQueryBtn);
|
buttons.push(openQueryBtn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +103,7 @@ export function createStaticCommandBarButtons(
|
|||||||
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection);
|
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection);
|
||||||
},
|
},
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
|
tooltipText: userContext.features.commandBarV2 ? "New..." : label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
hasPopup: true,
|
hasPopup: true,
|
||||||
disabled:
|
disabled:
|
||||||
@@ -114,21 +116,12 @@ export function createStaticCommandBarButtons(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return buttons;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createContextCommandBarButtons(
|
|
||||||
container: Explorer,
|
|
||||||
selectedNodeState: SelectedNodeState,
|
|
||||||
): CommandButtonComponentProps[] {
|
|
||||||
const buttons: CommandButtonComponentProps[] = [];
|
|
||||||
|
|
||||||
if (!selectedNodeState.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo") {
|
if (!selectedNodeState.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo") {
|
||||||
const label = useNotebook.getState().isShellEnabled ? "Open Mongo Shell" : "New Shell";
|
const label = useNotebook.getState().isShellEnabled ? "Open Mongo Shell" : "New Shell";
|
||||||
const newMongoShellBtn: CommandButtonComponentProps = {
|
const newMongoShellBtn: CommandButtonComponentProps = {
|
||||||
iconSrc: HostedTerminalIcon,
|
iconSrc: HostedTerminalIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => {
|
onCommandClick: (_, container) => {
|
||||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||||
if (useNotebook.getState().isShellEnabled) {
|
if (useNotebook.getState().isShellEnabled) {
|
||||||
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
||||||
@@ -140,29 +133,47 @@ export function createContextCommandBarButtons(
|
|||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
hasPopup: true,
|
hasPopup: true,
|
||||||
};
|
};
|
||||||
|
addDivider();
|
||||||
buttons.push(newMongoShellBtn);
|
buttons.push(newMongoShellBtn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
useNotebook.getState().isShellEnabled &&
|
||||||
|
!selectedNodeState.isDatabaseNodeOrNoneSelected() &&
|
||||||
|
userContext.apiType === "Cassandra"
|
||||||
|
) {
|
||||||
|
const label: string = "Open Cassandra Shell";
|
||||||
|
const newCassandraShellButton: CommandButtonComponentProps = {
|
||||||
|
iconSrc: HostedTerminalIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: (_, container) => {
|
||||||
|
container.openNotebookTerminal(ViewModels.TerminalKind.Cassandra);
|
||||||
|
},
|
||||||
|
commandButtonLabel: label,
|
||||||
|
ariaLabel: label,
|
||||||
|
hasPopup: true,
|
||||||
|
};
|
||||||
|
addDivider();
|
||||||
|
buttons.push(newCassandraShellButton);
|
||||||
|
}
|
||||||
|
|
||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createControlCommandBarButtons(container: Explorer): CommandButtonComponentProps[] {
|
export function createPlatformButtons(): CommandButtonComponentProps[] {
|
||||||
const buttons: CommandButtonComponentProps[] =
|
const buttons: CommandButtonComponentProps[] = [
|
||||||
configContext.platform === Platform.Fabric && userContext.fabricContext?.isReadOnly
|
{
|
||||||
? []
|
iconSrc: SettingsIcon,
|
||||||
: [
|
iconAlt: "Settings",
|
||||||
{
|
onCommandClick: (_, container) =>
|
||||||
iconSrc: SettingsIcon,
|
useSidePanel.getState().openSidePanel("Settings", <SettingsPane explorer={container} />),
|
||||||
iconAlt: "Settings",
|
commandButtonLabel: undefined,
|
||||||
onCommandClick: () =>
|
ariaLabel: "Settings",
|
||||||
useSidePanel.getState().openSidePanel("Settings", <SettingsPane explorer={container} />),
|
tooltipText: "Settings",
|
||||||
commandButtonLabel: undefined,
|
hasPopup: true,
|
||||||
ariaLabel: "Settings",
|
disabled: false,
|
||||||
tooltipText: "Settings",
|
},
|
||||||
hasPopup: true,
|
];
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const showOpenFullScreen =
|
const showOpenFullScreen =
|
||||||
configContext.platform === Platform.Portal && !isRunningOnNationalCloud() && userContext.apiType !== "Gremlin";
|
configContext.platform === Platform.Portal && !isRunningOnNationalCloud() && userContext.apiType !== "Gremlin";
|
||||||
@@ -191,7 +202,7 @@ export function createControlCommandBarButtons(container: Explorer): CommandButt
|
|||||||
const feedbackButtonOptions: CommandButtonComponentProps = {
|
const feedbackButtonOptions: CommandButtonComponentProps = {
|
||||||
iconSrc: FeedbackIcon,
|
iconSrc: FeedbackIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => container.openCESCVAFeedbackBlade(),
|
onCommandClick: (_, container) => container.openCESCVAFeedbackBlade(),
|
||||||
commandButtonLabel: undefined,
|
commandButtonLabel: undefined,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
tooltipText: label,
|
tooltipText: label,
|
||||||
@@ -223,32 +234,7 @@ function areScriptsSupported(): boolean {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNewCollectionGroup(container: Explorer): CommandButtonComponentProps {
|
function createOpenSynapseLinkDialogButton(): CommandButtonComponentProps {
|
||||||
const label = `New ${getCollectionName()}`;
|
|
||||||
return {
|
|
||||||
iconSrc: AddCollectionIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => container.onNewCollectionClicked(),
|
|
||||||
commandButtonLabel: label,
|
|
||||||
ariaLabel: label,
|
|
||||||
hasPopup: true,
|
|
||||||
id: "createNewContainerCommandButton",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createHomeButton(): CommandButtonComponentProps {
|
|
||||||
const label = "Home";
|
|
||||||
return {
|
|
||||||
iconSrc: HomeIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => useTabs.getState().openAndActivateReactTab(ReactTabKind.Home),
|
|
||||||
commandButtonLabel: label,
|
|
||||||
hasPopup: false,
|
|
||||||
ariaLabel: label,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonComponentProps {
|
|
||||||
if (configContext.platform === Platform.Emulator) {
|
if (configContext.platform === Platform.Emulator) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -266,7 +252,7 @@ function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonCo
|
|||||||
return {
|
return {
|
||||||
iconSrc: SynapseIcon,
|
iconSrc: SynapseIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => container.openEnableSynapseLinkDialog(),
|
onCommandClick: (_, container) => container.openEnableSynapseLinkDialog(),
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
hasPopup: false,
|
hasPopup: false,
|
||||||
disabled:
|
disabled:
|
||||||
@@ -275,22 +261,28 @@ function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonCo
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNewDatabase(container: Explorer): CommandButtonComponentProps {
|
function createLoginForEntraIDButton(): CommandButtonComponentProps {
|
||||||
const label = "New " + getDatabaseName();
|
if (configContext.platform !== Platform.Portal) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCommandClick: CommandButtonComponentProps["onCommandClick"] = async (_, container) => {
|
||||||
|
await container.openLoginForEntraIDPopUp();
|
||||||
|
useDataPlaneRbac.setState({ dataPlaneRbacEnabled: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!userContext.dataPlaneRbacEnabled || userContext.aadToken) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const label = "Login for Entra ID RBAC";
|
||||||
return {
|
return {
|
||||||
iconSrc: AddDatabaseIcon,
|
iconSrc: EntraIDIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
keyboardAction: KeyboardAction.NEW_DATABASE,
|
onCommandClick: handleCommandClick,
|
||||||
onCommandClick: async () => {
|
|
||||||
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
|
|
||||||
if (throughputCap && throughputCap !== -1) {
|
|
||||||
await useDatabases.getState().loadAllOffers();
|
|
||||||
}
|
|
||||||
useSidePanel.getState().openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={container} />);
|
|
||||||
},
|
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
|
||||||
hasPopup: true,
|
hasPopup: true,
|
||||||
|
ariaLabel: label,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,13 +393,14 @@ export function createScriptCommandButtons(selectedNodeState: SelectedNodeState)
|
|||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createOpenQueryButton(container: Explorer): CommandButtonComponentProps {
|
function createOpenQueryButton(): CommandButtonComponentProps {
|
||||||
const label = "Open Query";
|
const label = "Open Query";
|
||||||
return {
|
return {
|
||||||
iconSrc: BrowseQueriesIcon,
|
iconSrc: BrowseQueriesIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
|
tooltipText: userContext.features.commandBarV2 ? "Open Query..." : "Open Query",
|
||||||
keyboardAction: KeyboardAction.OPEN_QUERY,
|
keyboardAction: KeyboardAction.OPEN_QUERY,
|
||||||
onCommandClick: () =>
|
onCommandClick: (_, container) =>
|
||||||
useSidePanel.getState().openSidePanel("Open Saved Queries", <BrowseQueriesPane explorer={container} />),
|
useSidePanel.getState().openSidePanel("Open Saved Queries", <BrowseQueriesPane explorer={container} />),
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
@@ -430,10 +423,7 @@ function createOpenQueryFromDiskButton(): CommandButtonComponentProps {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createOpenTerminalButtonByKind(
|
function createOpenTerminalButtonByKind(terminalKind: ViewModels.TerminalKind): CommandButtonComponentProps {
|
||||||
container: Explorer,
|
|
||||||
terminalKind: ViewModels.TerminalKind,
|
|
||||||
): CommandButtonComponentProps {
|
|
||||||
const terminalFriendlyName = (): string => {
|
const terminalFriendlyName = (): string => {
|
||||||
switch (terminalKind) {
|
switch (terminalKind) {
|
||||||
case ViewModels.TerminalKind.Cassandra:
|
case ViewModels.TerminalKind.Cassandra:
|
||||||
@@ -456,7 +446,7 @@ function createOpenTerminalButtonByKind(
|
|||||||
return {
|
return {
|
||||||
iconSrc: HostedTerminalIcon,
|
iconSrc: HostedTerminalIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => {
|
onCommandClick: (_, container) => {
|
||||||
if (useNotebook.getState().isNotebookEnabled) {
|
if (useNotebook.getState().isNotebookEnabled) {
|
||||||
container.openNotebookTerminal(terminalKind);
|
container.openNotebookTerminal(terminalKind);
|
||||||
}
|
}
|
||||||
@@ -470,11 +460,10 @@ function createOpenTerminalButtonByKind(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createStaticCommandBarButtonsForResourceToken(
|
function createStaticCommandBarButtonsForResourceToken(
|
||||||
container: Explorer,
|
|
||||||
selectedNodeState: SelectedNodeState,
|
selectedNodeState: SelectedNodeState,
|
||||||
): CommandButtonComponentProps[] {
|
): CommandButtonComponentProps[] {
|
||||||
const newSqlQueryBtn = createNewSQLQueryButton(selectedNodeState);
|
const newSqlQueryBtn = createNewSQLQueryButton(selectedNodeState);
|
||||||
const openQueryBtn = createOpenQueryButton(container);
|
const openQueryBtn = createOpenQueryButton();
|
||||||
|
|
||||||
const resourceTokenCollection: ViewModels.CollectionBase = useDatabases.getState().resourceTokenCollection;
|
const resourceTokenCollection: ViewModels.CollectionBase = useDatabases.getState().resourceTokenCollection;
|
||||||
const isResourceTokenCollectionNodeSelected: boolean =
|
const isResourceTokenCollectionNodeSelected: boolean =
|
||||||
@@ -487,20 +476,20 @@ function createStaticCommandBarButtonsForResourceToken(
|
|||||||
|
|
||||||
openQueryBtn.disabled = !isResourceTokenCollectionNodeSelected;
|
openQueryBtn.disabled = !isResourceTokenCollectionNodeSelected;
|
||||||
if (!openQueryBtn.disabled) {
|
if (!openQueryBtn.disabled) {
|
||||||
openQueryBtn.children = [createOpenQueryButton(container), createOpenQueryFromDiskButton()];
|
openQueryBtn.children = [createOpenQueryButton(), createOpenQueryFromDiskButton()];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [newSqlQueryBtn, openQueryBtn];
|
return [newSqlQueryBtn, openQueryBtn];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createPostgreButtons(container: Explorer): CommandButtonComponentProps[] {
|
export function createPostgreButtons(): CommandButtonComponentProps[] {
|
||||||
const openPostgreShellBtn = createOpenTerminalButtonByKind(container, ViewModels.TerminalKind.Postgres);
|
const openPostgreShellBtn = createOpenTerminalButtonByKind(ViewModels.TerminalKind.Postgres);
|
||||||
|
|
||||||
return [openPostgreShellBtn];
|
return [openPostgreShellBtn];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createVCoreMongoButtons(container: Explorer): CommandButtonComponentProps[] {
|
export function createVCoreMongoButtons(): CommandButtonComponentProps[] {
|
||||||
const openVCoreMongoTerminalButton = createOpenTerminalButtonByKind(container, ViewModels.TerminalKind.VCoreMongo);
|
const openVCoreMongoTerminalButton = createOpenTerminalButtonByKind(ViewModels.TerminalKind.VCoreMongo);
|
||||||
|
|
||||||
return [openVCoreMongoTerminalButton];
|
return [openVCoreMongoTerminalButton];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { ICommandBarItemProps } from "@fluentui/react";
|
import { ICommandBarItemProps } from "@fluentui/react";
|
||||||
|
import Explorer from "Explorer/Explorer";
|
||||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
import * as CommandBarUtil from "./CommandBarUtil";
|
import * as CommandBarUtil from "./CommandBarUtil";
|
||||||
|
|
||||||
describe("CommandBarUtil tests", () => {
|
describe("CommandBarUtil tests", () => {
|
||||||
|
const mockExplorer = {} as Explorer;
|
||||||
const createButton = (): CommandButtonComponentProps => {
|
const createButton = (): CommandButtonComponentProps => {
|
||||||
return {
|
return {
|
||||||
iconSrc: "icon",
|
iconSrc: "icon",
|
||||||
@@ -22,7 +24,7 @@ describe("CommandBarUtil tests", () => {
|
|||||||
const btn = createButton();
|
const btn = createButton();
|
||||||
const backgroundColor = "backgroundColor";
|
const backgroundColor = "backgroundColor";
|
||||||
|
|
||||||
const converteds = CommandBarUtil.convertButton([btn], backgroundColor);
|
const converteds = CommandBarUtil.convertButton([btn], backgroundColor, mockExplorer);
|
||||||
expect(converteds.length).toBe(1);
|
expect(converteds.length).toBe(1);
|
||||||
const converted = converteds[0];
|
const converted = converteds[0];
|
||||||
expect(converted.split).toBe(undefined);
|
expect(converted.split).toBe(undefined);
|
||||||
@@ -46,7 +48,7 @@ describe("CommandBarUtil tests", () => {
|
|||||||
btn.children.push(child);
|
btn.children.push(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
const converteds = CommandBarUtil.convertButton([btn], "backgroundColor");
|
const converteds = CommandBarUtil.convertButton([btn], "backgroundColor", mockExplorer);
|
||||||
expect(converteds.length).toBe(1);
|
expect(converteds.length).toBe(1);
|
||||||
const converted = converteds[0];
|
const converted = converteds[0];
|
||||||
expect(converted.split).toBe(true);
|
expect(converted.split).toBe(true);
|
||||||
@@ -62,7 +64,7 @@ describe("CommandBarUtil tests", () => {
|
|||||||
btns.push(createButton());
|
btns.push(createButton());
|
||||||
}
|
}
|
||||||
|
|
||||||
const converteds = CommandBarUtil.convertButton(btns, "backgroundColor");
|
const converteds = CommandBarUtil.convertButton(btns, "backgroundColor", mockExplorer);
|
||||||
const uniqueKeys = converteds
|
const uniqueKeys = converteds
|
||||||
.map((btn: ICommandBarItemProps) => btn.key)
|
.map((btn: ICommandBarItemProps) => btn.key)
|
||||||
.filter((value: string, index: number, self: string[]) => self.indexOf(value) === index);
|
.filter((value: string, index: number, self: string[]) => self.indexOf(value) === index);
|
||||||
@@ -74,10 +76,10 @@ describe("CommandBarUtil tests", () => {
|
|||||||
const backgroundColor = "backgroundColor";
|
const backgroundColor = "backgroundColor";
|
||||||
|
|
||||||
btn.commandButtonLabel = undefined;
|
btn.commandButtonLabel = undefined;
|
||||||
let converted = CommandBarUtil.convertButton([btn], backgroundColor)[0];
|
let converted = CommandBarUtil.convertButton([btn], backgroundColor, mockExplorer)[0];
|
||||||
expect(converted.text).toEqual(btn.tooltipText);
|
expect(converted.text).toEqual(btn.tooltipText);
|
||||||
|
|
||||||
converted = CommandBarUtil.convertButton([btn], backgroundColor)[0];
|
converted = CommandBarUtil.convertButton([btn], backgroundColor, mockExplorer)[0];
|
||||||
delete btn.commandButtonLabel;
|
delete btn.commandButtonLabel;
|
||||||
expect(converted.text).toEqual(btn.tooltipText);
|
expect(converted.text).toEqual(btn.tooltipText);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,7 +25,11 @@ import { MemoryTracker } from "./MemoryTrackerComponent";
|
|||||||
* Convert our NavbarButtonConfig to UI Fabric buttons
|
* Convert our NavbarButtonConfig to UI Fabric buttons
|
||||||
* @param btns
|
* @param btns
|
||||||
*/
|
*/
|
||||||
export const convertButton = (btns: CommandButtonComponentProps[], backgroundColor: string): ICommandBarItemProps[] => {
|
export const convertButton = (
|
||||||
|
btns: CommandButtonComponentProps[],
|
||||||
|
backgroundColor: string,
|
||||||
|
container: Explorer,
|
||||||
|
): ICommandBarItemProps[] => {
|
||||||
const buttonHeightPx =
|
const buttonHeightPx =
|
||||||
configContext.platform == Platform.Fabric
|
configContext.platform == Platform.Fabric
|
||||||
? StyleConstants.FabricCommandBarButtonHeight
|
? StyleConstants.FabricCommandBarButtonHeight
|
||||||
@@ -54,15 +58,14 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
|
|||||||
iconProps: {
|
iconProps: {
|
||||||
style: {
|
style: {
|
||||||
width: StyleConstants.CommandBarIconWidth, // 16
|
width: StyleConstants.CommandBarIconWidth, // 16
|
||||||
alignSelf: btn.iconName ? "baseline" : undefined,
|
alignSelf: undefined,
|
||||||
filter: getFilter(btn.disabled),
|
filter: getFilter(btn.disabled),
|
||||||
},
|
},
|
||||||
imageProps: btn.iconSrc ? { src: btn.iconSrc, alt: btn.iconAlt } : undefined,
|
imageProps: btn.iconSrc ? { src: btn.iconSrc, alt: btn.iconAlt } : undefined,
|
||||||
iconName: btn.iconName,
|
|
||||||
},
|
},
|
||||||
onClick: btn.onCommandClick
|
onClick: btn.onCommandClick
|
||||||
? (ev?: React.MouseEvent<HTMLElement, MouseEvent> | React.KeyboardEvent<HTMLElement>) => {
|
? (ev?: React.MouseEvent<HTMLElement, MouseEvent> | React.KeyboardEvent<HTMLElement>) => {
|
||||||
btn.onCommandClick(ev);
|
btn.onCommandClick(ev, container);
|
||||||
let copilotEnabled = false;
|
let copilotEnabled = false;
|
||||||
if (useQueryCopilot.getState().copilotEnabled && useQueryCopilot.getState().copilotUserDBEnabled) {
|
if (useQueryCopilot.getState().copilotEnabled && useQueryCopilot.getState().copilotUserDBEnabled) {
|
||||||
copilotEnabled = useQueryCopilot.getState().copilotEnabledforExecution;
|
copilotEnabled = useQueryCopilot.getState().copilotEnabledforExecution;
|
||||||
@@ -135,7 +138,7 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
|
|||||||
result.split = true;
|
result.split = true;
|
||||||
|
|
||||||
result.subMenuProps = {
|
result.subMenuProps = {
|
||||||
items: convertButton(btn.children, backgroundColor),
|
items: convertButton(btn.children, backgroundColor, container),
|
||||||
styles: {
|
styles: {
|
||||||
list: {
|
list: {
|
||||||
// TODO Figure out how to do it the proper way with subComponentStyles.
|
// TODO Figure out how to do it the proper way with subComponentStyles.
|
||||||
@@ -186,7 +189,7 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
|
|||||||
option?: IDropdownOption,
|
option?: IDropdownOption,
|
||||||
index?: number,
|
index?: number,
|
||||||
): void => {
|
): void => {
|
||||||
btn.children[index].onCommandClick(event);
|
btn.children[index].onCommandClick(event, container);
|
||||||
TelemetryProcessor.trace(Action.ClickCommandBarButton, ActionModifiers.Mark, { label: option.text });
|
TelemetryProcessor.trace(Action.ClickCommandBarButton, ActionModifiers.Mark, { label: option.text });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -237,14 +240,17 @@ export const createConnectionStatus = (container: Explorer, poolId: PoolIdType,
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function createKeyboardHandlers(allButtons: CommandButtonComponentProps[]): KeyboardHandlerMap {
|
export function createKeyboardHandlers(
|
||||||
|
allButtons: CommandButtonComponentProps[],
|
||||||
|
container: Explorer,
|
||||||
|
): KeyboardHandlerMap {
|
||||||
const handlers: KeyboardHandlerMap = {};
|
const handlers: KeyboardHandlerMap = {};
|
||||||
|
|
||||||
function createHandlers(buttons: CommandButtonComponentProps[]) {
|
function createHandlers(buttons: CommandButtonComponentProps[]) {
|
||||||
buttons.forEach((button) => {
|
buttons.forEach((button) => {
|
||||||
if (!button.disabled && button.keyboardAction) {
|
if (!button.disabled && button.keyboardAction) {
|
||||||
handlers[button.keyboardAction] = (e) => {
|
handlers[button.keyboardAction] = (e) => {
|
||||||
button.onCommandClick(e);
|
button.onCommandClick(e, container);
|
||||||
|
|
||||||
// If the handler is bound, it means the button is visible and enabled, so we should prevent the default action
|
// If the handler is bound, it means the button is visible and enabled, so we should prevent the default action
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
16
src/Explorer/Menus/CommandBar/useCommandBar.ts
Normal file
16
src/Explorer/Menus/CommandBar/useCommandBar.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import create, { UseStore } from "zustand";
|
||||||
|
|
||||||
|
export interface CommandBarStore {
|
||||||
|
contextButtons: CommandButtonComponentProps[];
|
||||||
|
setContextButtons: (contextButtons: CommandButtonComponentProps[]) => void;
|
||||||
|
isHidden: boolean;
|
||||||
|
setIsHidden: (isHidden: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useCommandBar: UseStore<CommandBarStore> = create((set) => ({
|
||||||
|
contextButtons: [],
|
||||||
|
setContextButtons: (contextButtons: CommandButtonComponentProps[]) => set((state) => ({ ...state, contextButtons })),
|
||||||
|
isHidden: false,
|
||||||
|
setIsHidden: (isHidden: boolean) => set((state) => ({ ...state, isHidden })),
|
||||||
|
}));
|
||||||
159
src/Explorer/Menus/CommandBarV2/CommandBarV2.tsx
Normal file
159
src/Explorer/Menus/CommandBarV2/CommandBarV2.tsx
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
import {
|
||||||
|
makeStyles,
|
||||||
|
Menu,
|
||||||
|
MenuButton,
|
||||||
|
MenuItem,
|
||||||
|
MenuList,
|
||||||
|
MenuPopover,
|
||||||
|
MenuTrigger,
|
||||||
|
Toolbar,
|
||||||
|
ToolbarButton,
|
||||||
|
ToolbarDivider,
|
||||||
|
ToolbarGroup,
|
||||||
|
Tooltip,
|
||||||
|
} from "@fluentui/react-components";
|
||||||
|
import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import Explorer from "Explorer/Explorer";
|
||||||
|
import {
|
||||||
|
createPlatformButtons,
|
||||||
|
createStaticCommandBarButtons,
|
||||||
|
} from "Explorer/Menus/CommandBar/CommandBarComponentButtonFactory";
|
||||||
|
import { createKeyboardHandlers } from "Explorer/Menus/CommandBar/CommandBarUtil";
|
||||||
|
import { useCommandBar } from "Explorer/Menus/CommandBar/useCommandBar";
|
||||||
|
import { CosmosFluentProvider, cosmosShorthands, tokens } from "Explorer/Theme/ThemeUtil";
|
||||||
|
import { useSelectedNode } from "Explorer/useSelectedNode";
|
||||||
|
import { KeyboardActionGroup, useKeyboardActionGroup } from "KeyboardShortcuts";
|
||||||
|
import React, { MouseEventHandler } from "react";
|
||||||
|
|
||||||
|
const useToolbarStyles = makeStyles({
|
||||||
|
toolbar: {
|
||||||
|
height: tokens.layoutRowHeight,
|
||||||
|
justifyContent: "space-between", // Ensures that the two toolbar groups are at opposite ends of the toolbar
|
||||||
|
...cosmosShorthands.borderBottom(),
|
||||||
|
},
|
||||||
|
toolbarGroup: {
|
||||||
|
display: "flex",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export interface CommandBarV2Props {
|
||||||
|
explorer: Explorer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CommandBarV2: React.FC<CommandBarV2Props> = ({ explorer }: CommandBarV2Props) => {
|
||||||
|
const styles = useToolbarStyles();
|
||||||
|
const selectedNodeState = useSelectedNode();
|
||||||
|
const contextButtons = useCommandBar((state) => state.contextButtons);
|
||||||
|
const isHidden = useCommandBar((state) => state.isHidden);
|
||||||
|
const setKeyboardHandlers = useKeyboardActionGroup(KeyboardActionGroup.COMMAND_BAR);
|
||||||
|
const staticButtons = createStaticCommandBarButtons(selectedNodeState);
|
||||||
|
const platformButtons = createPlatformButtons();
|
||||||
|
|
||||||
|
if (isHidden) {
|
||||||
|
setKeyboardHandlers({});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const allButtons = staticButtons.concat(contextButtons).concat(platformButtons);
|
||||||
|
const keyboardHandlers = createKeyboardHandlers(allButtons, explorer);
|
||||||
|
setKeyboardHandlers(keyboardHandlers);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CosmosFluentProvider>
|
||||||
|
<Toolbar className={styles.toolbar}>
|
||||||
|
<ToolbarGroup role="presentation" className={styles.toolbarGroup}>
|
||||||
|
{staticButtons.map((button, index) =>
|
||||||
|
renderButton(explorer, button, `static-${index}`, contextButtons?.length > 0),
|
||||||
|
)}
|
||||||
|
{staticButtons.length > 0 && contextButtons?.length > 0 && <ToolbarDivider />}
|
||||||
|
{contextButtons.map((button, index) => renderButton(explorer, button, `context-${index}`, false))}
|
||||||
|
</ToolbarGroup>
|
||||||
|
<ToolbarGroup role="presentation">
|
||||||
|
{platformButtons.map((button, index) => renderButton(explorer, button, `platform-${index}`, true))}
|
||||||
|
</ToolbarGroup>
|
||||||
|
</Toolbar>
|
||||||
|
</CosmosFluentProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// This allows us to migrate individual buttons over to using a JSX.Element for the icon, without requiring us to change them all at once.
|
||||||
|
function renderIcon(iconSrcOrElement: string | JSX.Element, alt?: string): JSX.Element {
|
||||||
|
if (typeof iconSrcOrElement === "string") {
|
||||||
|
return <img src={iconSrcOrElement} alt={alt} />;
|
||||||
|
}
|
||||||
|
return iconSrcOrElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderButton(
|
||||||
|
explorer: Explorer,
|
||||||
|
btn: CommandButtonComponentProps,
|
||||||
|
key: string,
|
||||||
|
iconOnly: boolean,
|
||||||
|
): JSX.Element {
|
||||||
|
if (btn.isDivider) {
|
||||||
|
return <ToolbarDivider key={key} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasChildren = !!btn.children && btn.children.length > 0;
|
||||||
|
const label = btn.commandButtonLabel || btn.tooltipText;
|
||||||
|
const tooltip = btn.tooltipText || (iconOnly ? label : undefined);
|
||||||
|
const onClick: MouseEventHandler | undefined =
|
||||||
|
btn.onCommandClick && !hasChildren ? (e) => btn.onCommandClick(e, explorer) : undefined;
|
||||||
|
|
||||||
|
// We don't know which element will be the top-level element, so just slap a key on all of 'em
|
||||||
|
|
||||||
|
let button = hasChildren ? (
|
||||||
|
<MenuButton key={key} appearance="subtle" aria-label={btn.ariaLabel} icon={renderIcon(btn.iconSrc, btn.iconAlt)}>
|
||||||
|
{!iconOnly && label}
|
||||||
|
</MenuButton>
|
||||||
|
) : (
|
||||||
|
<ToolbarButton key={key} aria-label={btn.ariaLabel} onClick={onClick} icon={renderIcon(btn.iconSrc, btn.iconAlt)}>
|
||||||
|
{!iconOnly && label}
|
||||||
|
</ToolbarButton>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (tooltip) {
|
||||||
|
button = (
|
||||||
|
<Tooltip key={key} content={tooltip} relationship="description" withArrow>
|
||||||
|
{button}
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasChildren) {
|
||||||
|
button = (
|
||||||
|
<Menu key={key}>
|
||||||
|
<MenuTrigger disableButtonEnhancement>{button}</MenuTrigger>
|
||||||
|
<MenuPopover>
|
||||||
|
<MenuList>{btn.children.map((child, index) => renderMenuItem(explorer, child, index.toString()))}</MenuList>
|
||||||
|
</MenuPopover>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMenuItem(explorer: Explorer, btn: CommandButtonComponentProps, key: string): JSX.Element {
|
||||||
|
const hasChildren = !!btn.children && btn.children.length > 0;
|
||||||
|
const onClick: MouseEventHandler | undefined = btn.onCommandClick
|
||||||
|
? (e) => btn.onCommandClick(e, explorer)
|
||||||
|
: undefined;
|
||||||
|
const item = (
|
||||||
|
<MenuItem key={key} onClick={onClick} icon={renderIcon(btn.iconSrc, btn.iconAlt)}>
|
||||||
|
{btn.commandButtonLabel || btn.tooltipText}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasChildren) {
|
||||||
|
return (
|
||||||
|
<Menu>
|
||||||
|
<MenuTrigger disableButtonEnhancement>{item}</MenuTrigger>
|
||||||
|
<MenuPopover>
|
||||||
|
<MenuList>{btn.children.map((child, index) => renderMenuItem(explorer, child, index.toString()))}</MenuList>
|
||||||
|
</MenuPopover>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
@@ -131,6 +131,7 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="expandCollapseButton"
|
className="expandCollapseButton"
|
||||||
|
data-test="NotificationConsole/ExpandCollapseButton"
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
aria-label={"console button" + (this.props.isConsoleExpanded ? " expanded" : " collapsed")}
|
aria-label={"console button" + (this.props.isConsoleExpanded ? " expanded" : " collapsed")}
|
||||||
@@ -147,7 +148,7 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
height={this.props.isConsoleExpanded ? "auto" : 0}
|
height={this.props.isConsoleExpanded ? "auto" : 0}
|
||||||
onAnimationEnd={this.onConsoleWasExpanded}
|
onAnimationEnd={this.onConsoleWasExpanded}
|
||||||
>
|
>
|
||||||
<div className="notificationConsoleContents">
|
<div data-test="NotificationConsole/Contents" className="notificationConsoleContents">
|
||||||
<div className="notificationConsoleControls">
|
<div className="notificationConsoleControls">
|
||||||
<Dropdown
|
<Dropdown
|
||||||
label="Filter:"
|
label="Filter:"
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="in progress items"
|
alt="in progress items"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
className="numInProgress"
|
className="numInProgress"
|
||||||
@@ -35,7 +35,7 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="error items"
|
alt="error items"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
className="numErroredItems"
|
className="numErroredItems"
|
||||||
@@ -48,7 +48,7 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="info items"
|
alt="info items"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
className="numInfoItems"
|
className="numInfoItems"
|
||||||
@@ -74,6 +74,7 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
|
|||||||
aria-expanded={true}
|
aria-expanded={true}
|
||||||
aria-label="console button collapsed"
|
aria-label="console button collapsed"
|
||||||
className="expandCollapseButton"
|
className="expandCollapseButton"
|
||||||
|
data-test="NotificationConsole/ExpandCollapseButton"
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
@@ -86,7 +87,7 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
|
|||||||
<AnimateHeight
|
<AnimateHeight
|
||||||
animateOpacity={false}
|
animateOpacity={false}
|
||||||
animationStateClasses={
|
animationStateClasses={
|
||||||
Object {
|
{
|
||||||
"animating": "rah-animating",
|
"animating": "rah-animating",
|
||||||
"animatingDown": "rah-animating--down",
|
"animatingDown": "rah-animating--down",
|
||||||
"animatingToHeightAuto": "rah-animating--to-height-auto",
|
"animatingToHeightAuto": "rah-animating--to-height-auto",
|
||||||
@@ -105,10 +106,11 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
|
|||||||
easing="ease"
|
easing="ease"
|
||||||
height={0}
|
height={0}
|
||||||
onAnimationEnd={[Function]}
|
onAnimationEnd={[Function]}
|
||||||
style={Object {}}
|
style={{}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="notificationConsoleContents"
|
className="notificationConsoleContents"
|
||||||
|
data-test="NotificationConsole/Contents"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="notificationConsoleControls"
|
className="notificationConsoleControls"
|
||||||
@@ -117,20 +119,20 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
|
|||||||
label="Filter:"
|
label="Filter:"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
options={
|
options={
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"key": "All",
|
"key": "All",
|
||||||
"text": "All",
|
"text": "All",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "In Progress",
|
"key": "In Progress",
|
||||||
"text": "In progress",
|
"text": "In progress",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "Info",
|
"key": "Info",
|
||||||
"text": "Info",
|
"text": "Info",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "Error",
|
"key": "Error",
|
||||||
"text": "Error",
|
"text": "Error",
|
||||||
},
|
},
|
||||||
@@ -147,7 +149,7 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
|
|||||||
onKeyDown={[Function]}
|
onKeyDown={[Function]}
|
||||||
role="button"
|
role="button"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"border": "1px solid black",
|
"border": "1px solid black",
|
||||||
"borderRadius": "2px",
|
"borderRadius": "2px",
|
||||||
}
|
}
|
||||||
@@ -156,7 +158,7 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="clear notifications image"
|
alt="clear notifications image"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
/>
|
/>
|
||||||
Clear Notifications
|
Clear Notifications
|
||||||
</span>
|
</span>
|
||||||
@@ -191,7 +193,7 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="in progress items"
|
alt="in progress items"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
className="numInProgress"
|
className="numInProgress"
|
||||||
@@ -204,7 +206,7 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="error items"
|
alt="error items"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
className="numErroredItems"
|
className="numErroredItems"
|
||||||
@@ -217,7 +219,7 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="info items"
|
alt="info items"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
className="numInfoItems"
|
className="numInfoItems"
|
||||||
@@ -245,6 +247,7 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
|
|||||||
aria-expanded={true}
|
aria-expanded={true}
|
||||||
aria-label="console button collapsed"
|
aria-label="console button collapsed"
|
||||||
className="expandCollapseButton"
|
className="expandCollapseButton"
|
||||||
|
data-test="NotificationConsole/ExpandCollapseButton"
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
@@ -257,7 +260,7 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
|
|||||||
<AnimateHeight
|
<AnimateHeight
|
||||||
animateOpacity={false}
|
animateOpacity={false}
|
||||||
animationStateClasses={
|
animationStateClasses={
|
||||||
Object {
|
{
|
||||||
"animating": "rah-animating",
|
"animating": "rah-animating",
|
||||||
"animatingDown": "rah-animating--down",
|
"animatingDown": "rah-animating--down",
|
||||||
"animatingToHeightAuto": "rah-animating--to-height-auto",
|
"animatingToHeightAuto": "rah-animating--to-height-auto",
|
||||||
@@ -276,10 +279,11 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
|
|||||||
easing="ease"
|
easing="ease"
|
||||||
height={0}
|
height={0}
|
||||||
onAnimationEnd={[Function]}
|
onAnimationEnd={[Function]}
|
||||||
style={Object {}}
|
style={{}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="notificationConsoleContents"
|
className="notificationConsoleContents"
|
||||||
|
data-test="NotificationConsole/Contents"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="notificationConsoleControls"
|
className="notificationConsoleControls"
|
||||||
@@ -288,20 +292,20 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
|
|||||||
label="Filter:"
|
label="Filter:"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
options={
|
options={
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"key": "All",
|
"key": "All",
|
||||||
"text": "All",
|
"text": "All",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "In Progress",
|
"key": "In Progress",
|
||||||
"text": "In progress",
|
"text": "In progress",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "Info",
|
"key": "Info",
|
||||||
"text": "Info",
|
"text": "Info",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"key": "Error",
|
"key": "Error",
|
||||||
"text": "Error",
|
"text": "Error",
|
||||||
},
|
},
|
||||||
@@ -318,7 +322,7 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
|
|||||||
onKeyDown={[Function]}
|
onKeyDown={[Function]}
|
||||||
role="button"
|
role="button"
|
||||||
style={
|
style={
|
||||||
Object {
|
{
|
||||||
"border": "1px solid black",
|
"border": "1px solid black",
|
||||||
"borderRadius": "2px",
|
"borderRadius": "2px",
|
||||||
}
|
}
|
||||||
@@ -327,7 +331,7 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="clear notifications image"
|
alt="clear notifications image"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
/>
|
/>
|
||||||
Clear Notifications
|
Clear Notifications
|
||||||
</span>
|
</span>
|
||||||
@@ -342,7 +346,7 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
|
|||||||
<img
|
<img
|
||||||
alt="info"
|
alt="info"
|
||||||
className="infoIcon"
|
className="infoIcon"
|
||||||
src={Object {}}
|
src={{}}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
className="date"
|
className="date"
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
|
import { clear, collectionWasOpened, getItems, Type } from "Explorer/MostRecentActivity/MostRecentActivity";
|
||||||
import { observable } from "knockout";
|
import { observable } from "knockout";
|
||||||
import { mostRecentActivity } from "./MostRecentActivity";
|
|
||||||
|
|
||||||
describe("MostRecentActivity", () => {
|
describe("MostRecentActivity", () => {
|
||||||
const accountId = "some account";
|
const accountName = "some account";
|
||||||
|
|
||||||
beforeEach(() => mostRecentActivity.clear(accountId));
|
beforeEach(() => clear(accountName));
|
||||||
|
|
||||||
it("Has no items at first", () => {
|
it("Has no items at first", () => {
|
||||||
expect(mostRecentActivity.getItems(accountId)).toStrictEqual([]);
|
expect(getItems(accountName)).toStrictEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Can record collections being opened", () => {
|
it("Can record collections being opened", () => {
|
||||||
@@ -18,9 +18,9 @@ describe("MostRecentActivity", () => {
|
|||||||
databaseId,
|
databaseId,
|
||||||
};
|
};
|
||||||
|
|
||||||
mostRecentActivity.collectionWasOpened(accountId, collection);
|
collectionWasOpened(accountName, collection);
|
||||||
|
|
||||||
const activity = mostRecentActivity.getItems(accountId);
|
const activity = getItems(accountName);
|
||||||
expect(activity).toEqual([
|
expect(activity).toEqual([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
collectionId,
|
collectionId,
|
||||||
@@ -29,58 +29,24 @@ describe("MostRecentActivity", () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Can record notebooks being opened", () => {
|
it("Does not store duplicate entries", () => {
|
||||||
const name = "some notebook";
|
const collectionId = "some collection";
|
||||||
const path = "some path";
|
const databaseId = "some database";
|
||||||
const notebook = { name, path };
|
const collection = {
|
||||||
|
id: observable(collectionId),
|
||||||
|
databaseId,
|
||||||
|
};
|
||||||
|
|
||||||
mostRecentActivity.notebookWasItemOpened(accountId, notebook);
|
collectionWasOpened(accountName, collection);
|
||||||
|
collectionWasOpened(accountName, collection);
|
||||||
|
|
||||||
const activity = mostRecentActivity.getItems(accountId);
|
const activity = getItems(accountName);
|
||||||
expect(activity).toEqual([expect.objectContaining(notebook)]);
|
expect(activity).toEqual([
|
||||||
});
|
expect.objectContaining({
|
||||||
|
type: Type.OpenCollection,
|
||||||
it("Filters out duplicates", () => {
|
collectionId,
|
||||||
const name = "some notebook";
|
databaseId,
|
||||||
const path = "some path";
|
}),
|
||||||
const notebook = { name, path };
|
]);
|
||||||
const sameNotebook = { name, path };
|
|
||||||
|
|
||||||
mostRecentActivity.notebookWasItemOpened(accountId, notebook);
|
|
||||||
mostRecentActivity.notebookWasItemOpened(accountId, sameNotebook);
|
|
||||||
|
|
||||||
const activity = mostRecentActivity.getItems(accountId);
|
|
||||||
expect(activity.length).toEqual(1);
|
|
||||||
expect(activity).toEqual([expect.objectContaining(notebook)]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Allows for multiple accounts", () => {
|
|
||||||
const name = "some notebook";
|
|
||||||
const path = "some path";
|
|
||||||
const notebook = { name, path };
|
|
||||||
|
|
||||||
const anotherNotebook = { name: "Another " + name, path };
|
|
||||||
const anotherAccountId = "Another " + accountId;
|
|
||||||
|
|
||||||
mostRecentActivity.notebookWasItemOpened(accountId, notebook);
|
|
||||||
mostRecentActivity.notebookWasItemOpened(anotherAccountId, anotherNotebook);
|
|
||||||
|
|
||||||
expect(mostRecentActivity.getItems(accountId)).toEqual([expect.objectContaining(notebook)]);
|
|
||||||
expect(mostRecentActivity.getItems(anotherAccountId)).toEqual([expect.objectContaining(anotherNotebook)]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Can store multiple distinct elements, in FIFO order", () => {
|
|
||||||
const name = "some notebook";
|
|
||||||
const path = "some path";
|
|
||||||
const first = { name, path };
|
|
||||||
const second = { name: "Another " + name, path };
|
|
||||||
const third = { name, path: "Another " + path };
|
|
||||||
|
|
||||||
mostRecentActivity.notebookWasItemOpened(accountId, first);
|
|
||||||
mostRecentActivity.notebookWasItemOpened(accountId, second);
|
|
||||||
mostRecentActivity.notebookWasItemOpened(accountId, third);
|
|
||||||
|
|
||||||
const activity = mostRecentActivity.getItems(accountId);
|
|
||||||
expect(activity).toEqual([third, second, first].map(expect.objectContaining));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
import { AppStateComponentNames, deleteState, loadState, saveState } from "Shared/AppStatePersistenceUtility";
|
||||||
import { CollectionBase } from "../../Contracts/ViewModels";
|
import { CollectionBase } from "../../Contracts/ViewModels";
|
||||||
import { StorageKey, LocalStorageUtility } from "../../Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
|
||||||
import { NotebookContentItem } from "../Notebook/NotebookContentItem";
|
|
||||||
|
|
||||||
export enum Type {
|
export enum Type {
|
||||||
OpenCollection,
|
OpenCollection = "OpenCollection",
|
||||||
OpenNotebook,
|
OpenNotebook = "OpenNotebook",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OpenNotebookItem {
|
export interface OpenNotebookItem {
|
||||||
@@ -21,158 +21,174 @@ export interface OpenCollectionItem {
|
|||||||
|
|
||||||
type Item = OpenNotebookItem | OpenCollectionItem;
|
type Item = OpenNotebookItem | OpenCollectionItem;
|
||||||
|
|
||||||
// Update schemaVersion if you are going to change this interface
|
const itemsMaxNumber: number = 5;
|
||||||
interface StoredData {
|
|
||||||
schemaVersion: string;
|
|
||||||
itemsMap: { [accountId: string]: Item[] }; // FIFO
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores most recent activity
|
* Migrate old data to new AppState
|
||||||
*/
|
*/
|
||||||
class MostRecentActivity {
|
const migrateOldData = () => {
|
||||||
private static readonly schemaVersion: string = "2";
|
if (LocalStorageUtility.hasItem(StorageKey.MostRecentActivity)) {
|
||||||
private static itemsMaxNumber: number = 5;
|
const oldDataSchemaVersion: string = "2";
|
||||||
private storedData: StoredData;
|
const rawData = LocalStorageUtility.getEntryString(StorageKey.MostRecentActivity);
|
||||||
constructor() {
|
if (rawData) {
|
||||||
// Retrieve from local storage
|
const oldData = JSON.parse(rawData);
|
||||||
if (LocalStorageUtility.hasItem(StorageKey.MostRecentActivity)) {
|
if (oldData.schemaVersion === oldDataSchemaVersion) {
|
||||||
const rawData = LocalStorageUtility.getEntryString(StorageKey.MostRecentActivity);
|
const itemsMap: Record<string, Item[]> = oldData.itemsMap;
|
||||||
|
Object.keys(itemsMap).forEach((accountId: string) => {
|
||||||
if (!rawData) {
|
const accountName = accountId.split("/").pop();
|
||||||
this.storedData = MostRecentActivity.createEmptyData();
|
if (accountName) {
|
||||||
} else {
|
saveState(
|
||||||
try {
|
{
|
||||||
this.storedData = JSON.parse(rawData);
|
componentName: AppStateComponentNames.MostRecentActivity,
|
||||||
} catch (e) {
|
globalAccountName: accountName,
|
||||||
console.error("Unable to parse stored most recent activity. Use empty data:", rawData);
|
},
|
||||||
this.storedData = MostRecentActivity.createEmptyData();
|
itemsMap[accountId].map((item) => {
|
||||||
}
|
if ((item.type as unknown as number) === 0) {
|
||||||
|
item.type = Type.OpenCollection;
|
||||||
// If version doesn't match or schema broke, nuke it!
|
} else if ((item.type as unknown as number) === 1) {
|
||||||
if (
|
item.type = Type.OpenNotebook;
|
||||||
!this.storedData.hasOwnProperty("schemaVersion") ||
|
}
|
||||||
this.storedData["schemaVersion"] !== MostRecentActivity.schemaVersion
|
return item;
|
||||||
) {
|
}),
|
||||||
LocalStorageUtility.removeEntry(StorageKey.MostRecentActivity);
|
);
|
||||||
this.storedData = MostRecentActivity.createEmptyData();
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this.storedData = MostRecentActivity.createEmptyData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let p in this.storedData.itemsMap) {
|
// Remove old data
|
||||||
this.cleanupItems(p);
|
LocalStorageUtility.removeEntry(StorageKey.MostRecentActivity);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addItem = (accountName: string, newItem: Item): void => {
|
||||||
|
// When debugging, accountId is "undefined": most recent activity cannot be saved by account. Uncomment to disable.
|
||||||
|
// if (!accountId) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
let items =
|
||||||
|
(loadState({
|
||||||
|
componentName: AppStateComponentNames.MostRecentActivity,
|
||||||
|
globalAccountName: accountName,
|
||||||
|
}) as Item[]) || [];
|
||||||
|
|
||||||
|
// Remove duplicate
|
||||||
|
items = removeDuplicate(newItem, items);
|
||||||
|
|
||||||
|
items.unshift(newItem);
|
||||||
|
items = cleanupItems(items, accountName);
|
||||||
|
saveState(
|
||||||
|
{
|
||||||
|
componentName: AppStateComponentNames.MostRecentActivity,
|
||||||
|
globalAccountName: accountName,
|
||||||
|
},
|
||||||
|
items,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getItems = (accountName: string): Item[] => {
|
||||||
|
if (!accountName) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
(loadState({
|
||||||
|
componentName: AppStateComponentNames.MostRecentActivity,
|
||||||
|
globalAccountName: accountName,
|
||||||
|
}) as Item[]) || []
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const collectionWasOpened = (
|
||||||
|
accountName: string,
|
||||||
|
{ id, databaseId }: Pick<CollectionBase, "id" | "databaseId">,
|
||||||
|
) => {
|
||||||
|
if (accountName === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const collectionId = id();
|
||||||
|
addItem(accountName, {
|
||||||
|
type: Type.OpenCollection,
|
||||||
|
databaseId,
|
||||||
|
collectionId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clear = (accountName: string): void => {
|
||||||
|
if (!accountName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteState({
|
||||||
|
componentName: AppStateComponentNames.MostRecentActivity,
|
||||||
|
globalAccountName: accountName,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sort object by key
|
||||||
|
const sortObjectKeys = (unordered: Record<string, unknown>): Record<string, unknown> => {
|
||||||
|
return Object.keys(unordered)
|
||||||
|
.sort()
|
||||||
|
.reduce((obj: Record<string, unknown>, key: string) => {
|
||||||
|
obj[key] = unordered[key];
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find items by doing strict comparison and remove from array if duplicate is found.
|
||||||
|
* Modifies the array.
|
||||||
|
* @param item
|
||||||
|
* @param itemsArray
|
||||||
|
* @returns new array
|
||||||
|
*/
|
||||||
|
const removeDuplicate = (item: Item, itemsArray: Item[]): Item[] => {
|
||||||
|
if (!itemsArray) {
|
||||||
|
return itemsArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: Item[] = [...itemsArray];
|
||||||
|
|
||||||
|
let index = -1;
|
||||||
|
for (let i = 0; i < result.length; i++) {
|
||||||
|
const currentItem = result[i];
|
||||||
|
|
||||||
|
if (
|
||||||
|
JSON.stringify(sortObjectKeys(currentItem as unknown as Record<string, unknown>)) ===
|
||||||
|
JSON.stringify(sortObjectKeys(item as unknown as Record<string, unknown>))
|
||||||
|
) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
this.saveToLocalStorage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createEmptyData(): StoredData {
|
if (index !== -1) {
|
||||||
return {
|
result.splice(index, 1);
|
||||||
schemaVersion: MostRecentActivity.schemaVersion,
|
|
||||||
itemsMap: {},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static isEmpty(object: any) {
|
return result;
|
||||||
return Object.keys(object).length === 0 && object.constructor === Object;
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove unknown types
|
||||||
|
* Limit items to max number
|
||||||
|
* Modifies the array.
|
||||||
|
*/
|
||||||
|
const cleanupItems = (items: Item[], accountName: string): Item[] => {
|
||||||
|
if (accountName === undefined) {
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private saveToLocalStorage() {
|
const itemsArray = items.filter((item) => item.type in Type).slice(0, itemsMaxNumber);
|
||||||
if (MostRecentActivity.isEmpty(this.storedData.itemsMap)) {
|
if (itemsArray.length === 0) {
|
||||||
if (LocalStorageUtility.hasItem(StorageKey.MostRecentActivity)) {
|
deleteState({
|
||||||
LocalStorageUtility.removeEntry(StorageKey.MostRecentActivity);
|
componentName: AppStateComponentNames.MostRecentActivity,
|
||||||
}
|
globalAccountName: accountName,
|
||||||
// Don't save if empty
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalStorageUtility.setEntryString(StorageKey.MostRecentActivity, JSON.stringify(this.storedData));
|
|
||||||
}
|
|
||||||
|
|
||||||
private addItem(accountId: string, newItem: Item): void {
|
|
||||||
// When debugging, accountId is "undefined": most recent activity cannot be saved by account. Uncomment to disable.
|
|
||||||
// if (!accountId) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Remove duplicate
|
|
||||||
MostRecentActivity.removeDuplicate(newItem, this.storedData.itemsMap[accountId]);
|
|
||||||
|
|
||||||
this.storedData.itemsMap[accountId] = this.storedData.itemsMap[accountId] || [];
|
|
||||||
this.storedData.itemsMap[accountId].unshift(newItem);
|
|
||||||
this.cleanupItems(accountId);
|
|
||||||
this.saveToLocalStorage();
|
|
||||||
}
|
|
||||||
|
|
||||||
public getItems(accountId: string): Item[] {
|
|
||||||
return this.storedData.itemsMap[accountId] || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public collectionWasOpened(accountId: string, { id, databaseId }: Pick<CollectionBase, "id" | "databaseId">) {
|
|
||||||
const collectionId = id();
|
|
||||||
this.addItem(accountId, {
|
|
||||||
type: Type.OpenCollection,
|
|
||||||
databaseId,
|
|
||||||
collectionId,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return itemsArray;
|
||||||
|
};
|
||||||
|
|
||||||
public notebookWasItemOpened(accountId: string, { name, path }: Pick<NotebookContentItem, "name" | "path">) {
|
migrateOldData();
|
||||||
this.addItem(accountId, {
|
|
||||||
type: Type.OpenNotebook,
|
|
||||||
name,
|
|
||||||
path,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public clear(accountId: string): void {
|
|
||||||
delete this.storedData.itemsMap[accountId];
|
|
||||||
this.saveToLocalStorage();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find items by doing strict comparison and remove from array if duplicate is found
|
|
||||||
* @param item
|
|
||||||
*/
|
|
||||||
private static removeDuplicate(item: Item, itemsArray: Item[]): void {
|
|
||||||
if (!itemsArray) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let index = -1;
|
|
||||||
for (let i = 0; i < itemsArray.length; i++) {
|
|
||||||
const currentItem = itemsArray[i];
|
|
||||||
if (JSON.stringify(currentItem) === JSON.stringify(item)) {
|
|
||||||
index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index !== -1) {
|
|
||||||
itemsArray.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove unknown types
|
|
||||||
* Limit items to max number
|
|
||||||
*/
|
|
||||||
private cleanupItems(accountId: string): void {
|
|
||||||
if (!this.storedData.itemsMap.hasOwnProperty(accountId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemsArray = this.storedData.itemsMap[accountId]
|
|
||||||
.filter((item) => item.type in Type)
|
|
||||||
.slice(0, MostRecentActivity.itemsMaxNumber);
|
|
||||||
if (itemsArray.length === 0) {
|
|
||||||
delete this.storedData.itemsMap[accountId];
|
|
||||||
} else {
|
|
||||||
this.storedData.itemsMap[accountId] = itemsArray;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const mostRecentActivity = new MostRecentActivity();
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user