mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-22 03:04:07 +00:00
Compare commits
31 Commits
upload_doc
...
2262594
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
311e517836 | ||
|
|
04092c9da5 | ||
|
|
7b82a8ff81 | ||
|
|
832964f1b9 | ||
|
|
1e5042c2ec | ||
|
|
bc9f617537 | ||
|
|
288dc675d4 | ||
|
|
c727d82cdd | ||
|
|
ec9d56e55f | ||
|
|
4766b69799 | ||
|
|
19cf52353c | ||
|
|
fd50580ff7 | ||
|
|
140313e5e3 | ||
|
|
8f228260a3 | ||
|
|
045d038a93 | ||
|
|
42e11d5160 | ||
|
|
10037d844e | ||
|
|
13434715b3 | ||
|
|
676434cac5 | ||
|
|
3d5580865a | ||
|
|
7375cc717c | ||
|
|
de3e56bb99 | ||
|
|
53cd78452b | ||
|
|
fb6eb635c1 | ||
|
|
fb6c0caca6 | ||
|
|
e9f3c64239 | ||
|
|
bb6ee5deec | ||
|
|
5796b28045 | ||
|
|
9635d14599 | ||
|
|
c58d5765c2 | ||
|
|
708f4316b4 |
@@ -28,4 +28,4 @@
|
|||||||
.clickDisabled {
|
.clickDisabled {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
package-lock.json
generated
20
package-lock.json
generated
@@ -164,6 +164,7 @@
|
|||||||
"jest": "26.6.3",
|
"jest": "26.6.3",
|
||||||
"jest-canvas-mock": "2.3.1",
|
"jest-canvas-mock": "2.3.1",
|
||||||
"jest-playwright-preset": "1.5.1",
|
"jest-playwright-preset": "1.5.1",
|
||||||
|
"jest-react-hooks-shallow": "1.5.1",
|
||||||
"jest-trx-results-processor": "0.0.7",
|
"jest-trx-results-processor": "0.0.7",
|
||||||
"less": "3.8.1",
|
"less": "3.8.1",
|
||||||
"less-loader": "4.1.0",
|
"less-loader": "4.1.0",
|
||||||
@@ -19578,6 +19579,16 @@
|
|||||||
"node": ">=8.9.0"
|
"node": ">=8.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jest-react-hooks-shallow": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/jest-react-hooks-shallow/-/jest-react-hooks-shallow-1.5.1.tgz",
|
||||||
|
"integrity": "sha1-1SI4EGG7nf9WcfhLBT0LPxy+N/k=",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"react": "^16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jest-regex-util": {
|
"node_modules/jest-regex-util": {
|
||||||
"version": "24.9.0",
|
"version": "24.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz",
|
||||||
@@ -48608,6 +48619,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jest-react-hooks-shallow": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://msazure.pkgs.visualstudio.com/_packaging/AzurePortal/npm/registry/jest-react-hooks-shallow/-/jest-react-hooks-shallow-1.5.1.tgz",
|
||||||
|
"integrity": "sha1-1SI4EGG7nf9WcfhLBT0LPxy+N/k=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"react": "^16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"jest-regex-util": {
|
"jest-regex-util": {
|
||||||
"version": "24.9.0",
|
"version": "24.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz",
|
||||||
|
|||||||
@@ -160,6 +160,7 @@
|
|||||||
"jest": "26.6.3",
|
"jest": "26.6.3",
|
||||||
"jest-canvas-mock": "2.3.1",
|
"jest-canvas-mock": "2.3.1",
|
||||||
"jest-playwright-preset": "1.5.1",
|
"jest-playwright-preset": "1.5.1",
|
||||||
|
"jest-react-hooks-shallow": "1.5.1",
|
||||||
"jest-trx-results-processor": "0.0.7",
|
"jest-trx-results-processor": "0.0.7",
|
||||||
"less": "3.8.1",
|
"less": "3.8.1",
|
||||||
"less-loader": "4.1.0",
|
"less-loader": "4.1.0",
|
||||||
@@ -232,4 +233,4 @@
|
|||||||
"prettier": {
|
"prettier": {
|
||||||
"printWidth": 120
|
"printWidth": 120
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -435,24 +435,89 @@ export const QueryCopilotSampleContainerId = "SampleContainer";
|
|||||||
export const QueryCopilotSampleContainerSchema = {
|
export const QueryCopilotSampleContainerSchema = {
|
||||||
product: {
|
product: {
|
||||||
sampleData: {
|
sampleData: {
|
||||||
id: "de6fadec-0384-43c8-93ea-16c0170b5460",
|
id: "c415e70f-9bf5-4cda-aebe-a290cb8b94c2",
|
||||||
name: "Premium Phone Mini (Red)",
|
name: "Amazing Phone 3000 (Black)",
|
||||||
price: 652.04,
|
price: 223.33,
|
||||||
category: "Electronics",
|
category: "Electronics",
|
||||||
description:
|
description:
|
||||||
"This Premium Phone Mini (Red) is designed by the company under agreement with the FCC, so we'd give it a pass in either direction, but no one should be using this handset without a compatible handset. All in all, this phone is one of the most affordable Android handsets out there at $100. Check them out.\n\n9. HTC One M9 4",
|
"This Amazing Phone 3000 (Black) is made of black metal! It has a very well made aluminum body and it feels very comfortable. We loved the sound that comes out of it! Also, the design of the phone was a little loose at first because I was using the camera and felt uncomfortable wearing it. The phone is actually made slightly smaller than these photos! This is due to the addition of a 3.3mm filter",
|
||||||
stock: 74,
|
stock: 84,
|
||||||
countryOfOrigin: "Mexico",
|
countryOfOrigin: "USA",
|
||||||
firstAvailable: "2018-07-07 17:42:28",
|
firstAvailable: "2018-09-07 19:41:44",
|
||||||
priceHistory: [592.81],
|
priceHistory: [238.68, 234.7, 221.49, 205.88, 220.15],
|
||||||
customerRatings: [
|
customerRatings: [
|
||||||
{ username: "dannyhowell", stars: 1, date: "2022-03-12 17:01:23", verifiedUser: true },
|
{
|
||||||
{ username: "lindsay26", stars: 1, date: "2022-12-29 07:18:20", verifiedUser: false },
|
username: "steven66",
|
||||||
{ username: "smithmiguel", stars: 3, date: "2022-09-08 11:43:27", verifiedUser: false },
|
firstName: "Carol",
|
||||||
{ username: "julie07", stars: 3, date: "2021-03-14 23:54:10", verifiedUser: true },
|
gender: "female",
|
||||||
{ username: "kelly93", stars: 3, date: "2022-11-05 12:45:43", verifiedUser: false },
|
lastName: "Shelton",
|
||||||
{ username: "katherinereynolds", stars: 2, date: "2022-09-14 11:49:36", verifiedUser: false },
|
age: "25-35",
|
||||||
{ username: "chandlerkenneth", stars: 1, date: "2022-01-14 12:14:43", verifiedUser: true },
|
area: "suburban",
|
||||||
|
address: "261 Collins Burgs Apt. 332\nNorth Taylor, NM 32268",
|
||||||
|
stars: 5,
|
||||||
|
date: "2021-04-22 13:42:14",
|
||||||
|
verifiedUser: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
username: "khudson",
|
||||||
|
firstName: "Ronald",
|
||||||
|
gender: "male",
|
||||||
|
lastName: "Webb",
|
||||||
|
age: "18-24",
|
||||||
|
area: "suburban",
|
||||||
|
address: "9912 Parker Court Apt. 068\nNorth Austin, HI 76225",
|
||||||
|
stars: 5,
|
||||||
|
date: "2021-02-07 07:00:22",
|
||||||
|
verifiedUser: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
username: "lfrancis",
|
||||||
|
firstName: "Brady",
|
||||||
|
gender: "male",
|
||||||
|
lastName: "Wright",
|
||||||
|
age: "35-45",
|
||||||
|
area: "urban",
|
||||||
|
address: "PSC 5437, Box 3159\nAPO AA 26385",
|
||||||
|
stars: 2,
|
||||||
|
date: "2022-02-23 21:40:10",
|
||||||
|
verifiedUser: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
username: "nicolemartinez",
|
||||||
|
firstName: "Megan",
|
||||||
|
gender: "female",
|
||||||
|
lastName: "Tran",
|
||||||
|
age: "18-24",
|
||||||
|
area: "rural",
|
||||||
|
address: "7445 Salazar Brooks\nNew Sarah, PW 18097",
|
||||||
|
stars: 4,
|
||||||
|
date: "2021-09-01 22:21:40",
|
||||||
|
verifiedUser: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
username: "uguzman",
|
||||||
|
firstName: "Deanna",
|
||||||
|
gender: "female",
|
||||||
|
lastName: "Campbell",
|
||||||
|
age: "18-24",
|
||||||
|
area: "urban",
|
||||||
|
address: "41104 Moreno Fort Suite 872\nPort Michaelbury, AK 48712",
|
||||||
|
stars: 1,
|
||||||
|
date: "2022-03-07 02:23:14",
|
||||||
|
verifiedUser: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
username: "rebeccahunt",
|
||||||
|
firstName: "Jared",
|
||||||
|
gender: "male",
|
||||||
|
lastName: "Lopez",
|
||||||
|
age: "18-24",
|
||||||
|
area: "rural",
|
||||||
|
address: "392 Morgan Village Apt. 785\nGreenshire, CT 05921",
|
||||||
|
stars: 5,
|
||||||
|
date: "2021-04-17 04:17:49",
|
||||||
|
verifiedUser: false,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
rareProperty: true,
|
rareProperty: true,
|
||||||
},
|
},
|
||||||
@@ -494,6 +559,24 @@ export const QueryCopilotSampleContainerSchema = {
|
|||||||
username: {
|
username: {
|
||||||
type: "string",
|
type: "string",
|
||||||
},
|
},
|
||||||
|
firstName: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
gender: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
lastName: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
age: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
area: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
address: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
stars: {
|
stars: {
|
||||||
type: "number",
|
type: "number",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export class AccordionComponent extends React.Component<AccordionComponentProps>
|
|||||||
export interface AccordionItemComponentProps {
|
export interface AccordionItemComponentProps {
|
||||||
title: string;
|
title: string;
|
||||||
isExpanded?: boolean;
|
isExpanded?: boolean;
|
||||||
|
containerStyles?: React.CSSProperties;
|
||||||
styles?: React.CSSProperties;
|
styles?: React.CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,9 +55,9 @@ export class AccordionItemComponent extends React.Component<AccordionItemCompone
|
|||||||
}
|
}
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
const { styles } = this.props;
|
const { containerStyles, styles } = this.props;
|
||||||
return (
|
return (
|
||||||
<div className="accordionItemContainer">
|
<div className="accordionItemContainer" style={{ ...containerStyles }}>
|
||||||
<div className="accordionItemHeader" onClick={this.onHeaderClick} onKeyPress={this.onHeaderKeyPress}>
|
<div className="accordionItemHeader" onClick={this.onHeaderClick} onKeyPress={this.onHeaderKeyPress}>
|
||||||
{this.renderCollapseExpandIcon()}
|
{this.renderCollapseExpandIcon()}
|
||||||
{this.props.title}
|
{this.props.title}
|
||||||
|
|||||||
@@ -31,6 +31,13 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
|||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
this.createEditor(this.configureEditor.bind(this));
|
this.createEditor(this.configureEditor.bind(this));
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const suggestionWidget = this.editor?.getDomNode()?.querySelector(".suggest-widget") as HTMLElement;
|
||||||
|
if (suggestionWidget) {
|
||||||
|
suggestionWidget.style.display = "none";
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidUpdate(previous: EditorReactProps) {
|
public componentDidUpdate(previous: EditorReactProps) {
|
||||||
|
|||||||
@@ -1291,7 +1291,7 @@ export default class Explorer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sampleDataResourceTokenCollection = new ResourceTokenCollection(this, databaseId, collection);
|
const sampleDataResourceTokenCollection = new ResourceTokenCollection(this, databaseId, collection, true);
|
||||||
useDatabases.setState({ sampleDataResourceTokenCollection });
|
useDatabases.setState({ sampleDataResourceTokenCollection });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -268,7 +268,6 @@ function createNewCollectionGroup(container: Explorer): CommandButtonComponentPr
|
|||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
hasPopup: true,
|
hasPopup: true,
|
||||||
id: "createNewContainerCommandButton",
|
id: "createNewContainerCommandButton",
|
||||||
disabled: useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,7 +313,6 @@ function createNewDatabase(container: Explorer): CommandButtonComponentProps {
|
|||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
hasPopup: true,
|
hasPopup: true,
|
||||||
disabled: useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,7 +491,7 @@ function createOpenTerminalButton(container: Explorer): CommandButtonComponentPr
|
|||||||
onCommandClick: () => container.openNotebookTerminal(ViewModels.TerminalKind.Default),
|
onCommandClick: () => container.openNotebookTerminal(ViewModels.TerminalKind.Default),
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
hasPopup: false,
|
hasPopup: false,
|
||||||
disabled: false,
|
disabled: useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { Checkbox, Dropdown, IDropdownOption, Link, Stack, Text, TextField } fro
|
|||||||
import * as Constants from "Common/Constants";
|
import * as Constants from "Common/Constants";
|
||||||
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
||||||
import { InfoTooltip } from "Common/Tooltip/InfoTooltip";
|
import { InfoTooltip } from "Common/Tooltip/InfoTooltip";
|
||||||
import { useSidePanel } from "hooks/useSidePanel";
|
|
||||||
import React, { FunctionComponent, useState } from "react";
|
|
||||||
import * as SharedConstants from "Shared/Constants";
|
import * as SharedConstants from "Shared/Constants";
|
||||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import { isServerlessAccount } from "Utils/CapabilityUtils";
|
import { isServerlessAccount } from "Utils/CapabilityUtils";
|
||||||
|
import { useSidePanel } from "hooks/useSidePanel";
|
||||||
|
import React, { FunctionComponent, useState } from "react";
|
||||||
import { ThroughputInput } from "../../Controls/ThroughputInput/ThroughputInput";
|
import { ThroughputInput } from "../../Controls/ThroughputInput/ThroughputInput";
|
||||||
import Explorer from "../../Explorer";
|
import Explorer from "../../Explorer";
|
||||||
import { CassandraAPIDataClient } from "../../Tables/TableDataClient";
|
import { CassandraAPIDataClient } from "../../Tables/TableDataClient";
|
||||||
@@ -291,7 +291,7 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
|
|||||||
styles={getTextFieldStyles({ fontSize: 12, width: 150 })}
|
styles={getTextFieldStyles({ fontSize: 12, width: 150 })}
|
||||||
aria-required="true"
|
aria-required="true"
|
||||||
required={true}
|
required={true}
|
||||||
ariaLabel="addCollection-tableId"
|
ariaLabel="addCollection-table Id Create table"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
pattern="[^/?#\\]*[^/?# \\]"
|
pattern="[^/?#\\]*[^/?# \\]"
|
||||||
title="May not end with space nor contain characters '\' '/' '#' '?'"
|
title="May not end with space nor contain characters '\' '/' '#' '?'"
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { Text, TextField } from "@fluentui/react";
|
import { Text, TextField } from "@fluentui/react";
|
||||||
import { Areas } from "Common/Constants";
|
import { Areas } from "Common/Constants";
|
||||||
import { deleteCollection } from "Common/dataAccess/deleteCollection";
|
|
||||||
import DeleteFeedback from "Common/DeleteFeedback";
|
import DeleteFeedback from "Common/DeleteFeedback";
|
||||||
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
||||||
|
import { deleteCollection } from "Common/dataAccess/deleteCollection";
|
||||||
import { Collection } from "Contracts/ViewModels";
|
import { Collection } from "Contracts/ViewModels";
|
||||||
import { useSidePanel } from "hooks/useSidePanel";
|
|
||||||
import { useTabs } from "hooks/useTabs";
|
|
||||||
import React, { FunctionComponent, useState } from "react";
|
|
||||||
import { DefaultExperienceUtility } from "Shared/DefaultExperienceUtility";
|
import { DefaultExperienceUtility } from "Shared/DefaultExperienceUtility";
|
||||||
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 { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import { getCollectionName } from "Utils/APITypeUtils";
|
import { getCollectionName } from "Utils/APITypeUtils";
|
||||||
import * as NotificationConsoleUtils from "Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "Utils/NotificationConsoleUtils";
|
||||||
|
import { useSidePanel } from "hooks/useSidePanel";
|
||||||
|
import { useTabs } from "hooks/useTabs";
|
||||||
|
import React, { FunctionComponent, useState } from "react";
|
||||||
import { useDatabases } from "../../useDatabases";
|
import { useDatabases } from "../../useDatabases";
|
||||||
import { useSelectedNode } from "../../useSelectedNode";
|
import { useSelectedNode } from "../../useSelectedNode";
|
||||||
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
||||||
@@ -126,6 +126,7 @@ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectio
|
|||||||
setInputCollectionName(newInput);
|
setInputCollectionName(newInput);
|
||||||
}}
|
}}
|
||||||
ariaLabel={confirmContainer}
|
ariaLabel={confirmContainer}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{shouldRecordFeedback() && (
|
{shouldRecordFeedback() && (
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
|
|||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
id="confirmCollectionId"
|
id="confirmCollectionId"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
|
required={true}
|
||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"fieldGroup": Object {
|
"fieldGroup": Object {
|
||||||
@@ -59,6 +60,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
|
|||||||
deferredValidationTime={200}
|
deferredValidationTime={200}
|
||||||
id="confirmCollectionId"
|
id="confirmCollectionId"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
|
required={true}
|
||||||
resizable={true}
|
resizable={true}
|
||||||
styles={[Function]}
|
styles={[Function]}
|
||||||
theme={
|
theme={
|
||||||
@@ -338,7 +340,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
|
|||||||
value=""
|
value=""
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ms-TextField root-55"
|
className="ms-TextField is-required root-55"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ms-TextField-wrapper"
|
className="ms-TextField-wrapper"
|
||||||
@@ -356,6 +358,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
|
|||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
onFocus={[Function]}
|
onFocus={[Function]}
|
||||||
onInput={[Function]}
|
onInput={[Function]}
|
||||||
|
required={true}
|
||||||
type="text"
|
type="text"
|
||||||
value=""
|
value=""
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
|
|||||||
setDatabaseInput(newInput);
|
setDatabaseInput(newInput);
|
||||||
}}
|
}}
|
||||||
ariaLabel={confirmDatabase}
|
ariaLabel={confirmDatabase}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{isLastNonEmptyDatabase() && (
|
{isLastNonEmptyDatabase() && (
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { IPanelProps, IRenderFunction, Panel, PanelType } from "@fluentui/react";
|
import { IPanelProps, IRenderFunction, Panel, PanelType } from "@fluentui/react";
|
||||||
|
import { useSelectedNode } from "Explorer/useSelectedNode";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useNotificationConsole } from "../../hooks/useNotificationConsole";
|
import { useNotificationConsole } from "../../hooks/useNotificationConsole";
|
||||||
import { useSidePanel } from "../../hooks/useSidePanel";
|
import { useSidePanel } from "../../hooks/useSidePanel";
|
||||||
@@ -77,6 +78,20 @@ export class PanelContainerComponent extends React.Component<PanelContainerProps
|
|||||||
}
|
}
|
||||||
|
|
||||||
private onDissmiss = (ev?: KeyboardEvent | React.SyntheticEvent<HTMLElement>): void => {
|
private onDissmiss = (ev?: KeyboardEvent | React.SyntheticEvent<HTMLElement>): void => {
|
||||||
|
const collection = useSelectedNode.getState().findSelectedCollection();
|
||||||
|
if (collection) {
|
||||||
|
const targetElementDataTest: string | undefined = collection.id();
|
||||||
|
const targetElement: HTMLElement | null = document.querySelector(`[data-test="${targetElementDataTest}"]`);
|
||||||
|
if (targetElement) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const moreButton: HTMLElement | null = targetElement.querySelector('[name="More"]');
|
||||||
|
if (moreButton) {
|
||||||
|
moreButton.focus();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
clearTimeout;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (ev && (ev.target as HTMLElement).id === "notificationConsoleHeader") {
|
if (ev && (ev.target as HTMLElement).id === "notificationConsoleHeader") {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -371,6 +371,7 @@ exports[`Delete Database Confirmation Pane Should call delete database 1`] = `
|
|||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
id="confirmDatabaseId"
|
id="confirmDatabaseId"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
|
required={true}
|
||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"fieldGroup": Object {
|
"fieldGroup": Object {
|
||||||
@@ -385,6 +386,7 @@ exports[`Delete Database Confirmation Pane Should call delete database 1`] = `
|
|||||||
deferredValidationTime={200}
|
deferredValidationTime={200}
|
||||||
id="confirmDatabaseId"
|
id="confirmDatabaseId"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
|
required={true}
|
||||||
resizable={true}
|
resizable={true}
|
||||||
styles={[Function]}
|
styles={[Function]}
|
||||||
theme={
|
theme={
|
||||||
@@ -663,7 +665,7 @@ exports[`Delete Database Confirmation Pane Should call delete database 1`] = `
|
|||||||
validateOnLoad={true}
|
validateOnLoad={true}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ms-TextField root-58"
|
className="ms-TextField is-required root-58"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ms-TextField-wrapper"
|
className="ms-TextField-wrapper"
|
||||||
@@ -681,6 +683,7 @@ exports[`Delete Database Confirmation Pane Should call delete database 1`] = `
|
|||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
onFocus={[Function]}
|
onFocus={[Function]}
|
||||||
onInput={[Function]}
|
onInput={[Function]}
|
||||||
|
required={true}
|
||||||
type="text"
|
type="text"
|
||||||
value=""
|
value=""
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
import { Checkbox, ChoiceGroup, DefaultButton, IconButton, PrimaryButton, TextField } from "@fluentui/react";
|
||||||
|
import { QueryCopilotFeedbackModal } from "Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal";
|
||||||
|
import { submitFeedback } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
||||||
|
import { getUserEmail } from "Utils/UserUtils";
|
||||||
|
import { shallow } from "enzyme";
|
||||||
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
jest.mock("Utils/UserUtils");
|
||||||
|
(getUserEmail as jest.Mock).mockResolvedValue("test@email.com");
|
||||||
|
|
||||||
|
jest.mock("Explorer/QueryCopilot/QueryCopilotUtilities");
|
||||||
|
submitFeedback as jest.Mock;
|
||||||
|
|
||||||
|
describe("Query Copilot Feedback Modal snapshot test", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
it("shoud render and match snapshot", () => {
|
||||||
|
useQueryCopilot.getState().openFeedbackModal("test query", false, "test prompt");
|
||||||
|
|
||||||
|
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
||||||
|
|
||||||
|
expect(wrapper.props().isOpen).toBeTruthy();
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should close on cancel click", () => {
|
||||||
|
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
||||||
|
|
||||||
|
const cancelButton = wrapper.find(IconButton);
|
||||||
|
cancelButton.simulate("click");
|
||||||
|
wrapper.setProps({});
|
||||||
|
|
||||||
|
expect(wrapper.props().isOpen).toBeFalsy();
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should get user unput", () => {
|
||||||
|
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
||||||
|
const testUserInput = "test user input";
|
||||||
|
|
||||||
|
const userInput = wrapper.find(TextField).first();
|
||||||
|
userInput.simulate("change", {}, testUserInput);
|
||||||
|
|
||||||
|
expect(wrapper.find(TextField).first().props().value).toEqual(testUserInput);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should record user contact choice no", () => {
|
||||||
|
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
||||||
|
const contactAllowed = wrapper.find(ChoiceGroup);
|
||||||
|
|
||||||
|
contactAllowed.simulate("change", {}, { key: "no" });
|
||||||
|
|
||||||
|
expect(getUserEmail).toHaveBeenCalledTimes(3);
|
||||||
|
expect(wrapper.find(ChoiceGroup).props().selectedKey).toEqual("no");
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should record user contact choice yes", () => {
|
||||||
|
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
||||||
|
const contactAllowed = wrapper.find(ChoiceGroup);
|
||||||
|
|
||||||
|
contactAllowed.simulate("change", {}, { key: "yes" });
|
||||||
|
|
||||||
|
expect(getUserEmail).toHaveBeenCalledTimes(4);
|
||||||
|
expect(wrapper.find(ChoiceGroup).props().selectedKey).toEqual("yes");
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not render dont show again button", () => {
|
||||||
|
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
||||||
|
|
||||||
|
const dontShowAgain = wrapper.find(Checkbox);
|
||||||
|
|
||||||
|
expect(dontShowAgain).toHaveLength(0);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render dont show again button and check it ", () => {
|
||||||
|
useQueryCopilot.getState().openFeedbackModal("test query", true, "test prompt");
|
||||||
|
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
||||||
|
|
||||||
|
const dontShowAgain = wrapper.find(Checkbox);
|
||||||
|
dontShowAgain.simulate("change", {}, true);
|
||||||
|
|
||||||
|
expect(wrapper.find(Checkbox)).toHaveLength(1);
|
||||||
|
expect(wrapper.find(Checkbox).first().props().checked).toBeTruthy();
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should cancel submission", () => {
|
||||||
|
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
||||||
|
|
||||||
|
const cancelButton = wrapper.find(DefaultButton);
|
||||||
|
cancelButton.simulate("click");
|
||||||
|
wrapper.setProps({});
|
||||||
|
|
||||||
|
expect(wrapper.props().isOpen).toBeFalsy();
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should submit submission", () => {
|
||||||
|
const wrapper = shallow(<QueryCopilotFeedbackModal />);
|
||||||
|
|
||||||
|
const submitButton = wrapper.find(PrimaryButton);
|
||||||
|
submitButton.simulate("click");
|
||||||
|
wrapper.setProps({});
|
||||||
|
|
||||||
|
expect(submitFeedback).toHaveBeenCalledTimes(1);
|
||||||
|
expect(submitFeedback).toHaveBeenCalledWith({
|
||||||
|
likeQuery: false,
|
||||||
|
generatedQuery: "",
|
||||||
|
userPrompt: "",
|
||||||
|
description: "",
|
||||||
|
contact: getUserEmail(),
|
||||||
|
});
|
||||||
|
expect(wrapper.props().isOpen).toBeFalsy();
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
import { submitFeedback } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
import { submitFeedback } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { getUserEmail } from "../../Utils/UserUtils";
|
import { getUserEmail } from "../../../Utils/UserUtils";
|
||||||
|
|
||||||
export const QueryCopilotFeedbackModal: React.FC = (): JSX.Element => {
|
export const QueryCopilotFeedbackModal: React.FC = (): JSX.Element => {
|
||||||
const {
|
const {
|
||||||
@@ -28,6 +28,7 @@ export const QueryCopilotFeedbackModal: React.FC = (): JSX.Element => {
|
|||||||
const [description, setDescription] = React.useState<string>("");
|
const [description, setDescription] = React.useState<string>("");
|
||||||
const [doNotShowAgainChecked, setDoNotShowAgainChecked] = React.useState<boolean>(false);
|
const [doNotShowAgainChecked, setDoNotShowAgainChecked] = React.useState<boolean>(false);
|
||||||
const [contact, setContact] = React.useState<string>(getUserEmail());
|
const [contact, setContact] = React.useState<string>(getUserEmail());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={showFeedbackModal}>
|
<Modal isOpen={showFeedbackModal}>
|
||||||
<Stack style={{ padding: 24 }}>
|
<Stack style={{ padding: 24 }}>
|
||||||
32
src/Explorer/QueryCopilot/Modal/WelcomeModal.test.tsx
Normal file
32
src/Explorer/QueryCopilot/Modal/WelcomeModal.test.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { shallow } from "enzyme";
|
||||||
|
import { withHooks } from "jest-react-hooks-shallow";
|
||||||
|
import React from "react";
|
||||||
|
import { WelcomeModal } from "./WelcomeModal";
|
||||||
|
|
||||||
|
describe("Query Copilot Welcome Modal snapshot test", () => {
|
||||||
|
it("should render when isOpen is true", () => {
|
||||||
|
withHooks(() => {
|
||||||
|
const spy = jest.spyOn(localStorage, "setItem");
|
||||||
|
spy.mockClear();
|
||||||
|
const wrapper = shallow(<WelcomeModal visible={true} />);
|
||||||
|
|
||||||
|
expect(wrapper.props().children.props.isOpen).toBeTruthy();
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(spy).toHaveBeenLastCalledWith("hideWelcomeModal", "true");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("should not render when isOpen is false", () => {
|
||||||
|
withHooks(() => {
|
||||||
|
const spy = jest.spyOn(localStorage, "setItem");
|
||||||
|
spy.mockClear();
|
||||||
|
const wrapper = shallow(<WelcomeModal visible={false} />);
|
||||||
|
|
||||||
|
expect(wrapper.props().children.props.isOpen).toBeFalsy();
|
||||||
|
|
||||||
|
expect(spy).not.toHaveBeenCalled();
|
||||||
|
expect(spy.mock.instances.length).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,196 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is true 1`] = `
|
||||||
|
<Fragment>
|
||||||
|
<Modal
|
||||||
|
isBlocking={false}
|
||||||
|
isOpen={true}
|
||||||
|
onDismiss={[Function]}
|
||||||
|
>
|
||||||
|
<Stack
|
||||||
|
className="modalContentPadding"
|
||||||
|
>
|
||||||
|
<Stack
|
||||||
|
horizontal={true}
|
||||||
|
>
|
||||||
|
<Stack
|
||||||
|
grow={4}
|
||||||
|
horizontal={true}
|
||||||
|
horizontalAlign="end"
|
||||||
|
>
|
||||||
|
<StackItem>
|
||||||
|
<Image
|
||||||
|
src=""
|
||||||
|
/>
|
||||||
|
</StackItem>
|
||||||
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
className="exitPadding"
|
||||||
|
grow={1}
|
||||||
|
horizontal={true}
|
||||||
|
horizontalAlign="end"
|
||||||
|
verticalAlign="start"
|
||||||
|
>
|
||||||
|
<StackItem
|
||||||
|
className="previewMargin"
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
className="preview"
|
||||||
|
>
|
||||||
|
Preview
|
||||||
|
</Text>
|
||||||
|
</StackItem>
|
||||||
|
<StackItem>
|
||||||
|
<CustomizedIconButton
|
||||||
|
ariaLabel="Exit"
|
||||||
|
className="exitIcon"
|
||||||
|
iconProps={
|
||||||
|
Object {
|
||||||
|
"iconName": "Cancel",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onClick={[Function]}
|
||||||
|
title="Exit"
|
||||||
|
/>
|
||||||
|
</StackItem>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
horizontalAlign="center"
|
||||||
|
>
|
||||||
|
<StackItem
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
className="title bold"
|
||||||
|
>
|
||||||
|
Welcome to Copilot in CosmosDB
|
||||||
|
</Text>
|
||||||
|
</StackItem>
|
||||||
|
<StackItem
|
||||||
|
align="center"
|
||||||
|
className="text"
|
||||||
|
>
|
||||||
|
<Stack
|
||||||
|
horizontal={true}
|
||||||
|
>
|
||||||
|
<StackItem
|
||||||
|
align="start"
|
||||||
|
className="imageTextPadding"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src=""
|
||||||
|
/>
|
||||||
|
</StackItem>
|
||||||
|
<StackItem
|
||||||
|
align="start"
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
className="bold"
|
||||||
|
>
|
||||||
|
Let copilot do the work for you
|
||||||
|
<br />
|
||||||
|
</Text>
|
||||||
|
</StackItem>
|
||||||
|
</Stack>
|
||||||
|
<Text>
|
||||||
|
Ask Copilot to generate a query by describing the query in your words.
|
||||||
|
<br />
|
||||||
|
<StyledLinkBase
|
||||||
|
href=""
|
||||||
|
>
|
||||||
|
Learn more
|
||||||
|
</StyledLinkBase>
|
||||||
|
</Text>
|
||||||
|
</StackItem>
|
||||||
|
<StackItem
|
||||||
|
align="center"
|
||||||
|
className="text"
|
||||||
|
>
|
||||||
|
<Stack
|
||||||
|
horizontal={true}
|
||||||
|
>
|
||||||
|
<StackItem
|
||||||
|
align="start"
|
||||||
|
className="imageTextPadding"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src=""
|
||||||
|
/>
|
||||||
|
</StackItem>
|
||||||
|
<StackItem
|
||||||
|
align="start"
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
className="bold"
|
||||||
|
>
|
||||||
|
Use your judgement
|
||||||
|
<br />
|
||||||
|
</Text>
|
||||||
|
</StackItem>
|
||||||
|
</Stack>
|
||||||
|
<Text>
|
||||||
|
AI-generated content can have mistakes. Make sure it’s accurate and appropriate before using it.
|
||||||
|
<br />
|
||||||
|
<StyledLinkBase
|
||||||
|
href=""
|
||||||
|
>
|
||||||
|
Read preview terms
|
||||||
|
</StyledLinkBase>
|
||||||
|
</Text>
|
||||||
|
</StackItem>
|
||||||
|
<StackItem
|
||||||
|
align="center"
|
||||||
|
className="text"
|
||||||
|
>
|
||||||
|
<Stack
|
||||||
|
horizontal={true}
|
||||||
|
>
|
||||||
|
<StackItem
|
||||||
|
align="start"
|
||||||
|
className="imageTextPadding"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src=""
|
||||||
|
/>
|
||||||
|
</StackItem>
|
||||||
|
<StackItem
|
||||||
|
align="start"
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
className="bold"
|
||||||
|
>
|
||||||
|
Copilot currently works only a sample database
|
||||||
|
<br />
|
||||||
|
</Text>
|
||||||
|
</StackItem>
|
||||||
|
</Stack>
|
||||||
|
<Text>
|
||||||
|
Copilot is setup on a sample database we have configured for you at no cost
|
||||||
|
<br />
|
||||||
|
<StyledLinkBase
|
||||||
|
href=""
|
||||||
|
>
|
||||||
|
Learn more
|
||||||
|
</StyledLinkBase>
|
||||||
|
</Text>
|
||||||
|
</StackItem>
|
||||||
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
className="buttonPadding"
|
||||||
|
>
|
||||||
|
<StackItem
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<CustomizedPrimaryButton
|
||||||
|
className="tryButton"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
Try Copilot
|
||||||
|
</CustomizedPrimaryButton>
|
||||||
|
</StackItem>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Modal>
|
||||||
|
</Fragment>
|
||||||
|
`;
|
||||||
@@ -1,11 +1,48 @@
|
|||||||
|
import { IconButton } from "@fluentui/react";
|
||||||
import { shallow } from "enzyme";
|
import { shallow } from "enzyme";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { any } from "underscore";
|
|
||||||
import { CopyPopup } from "./CopyPopup";
|
import { CopyPopup } from "./CopyPopup";
|
||||||
|
|
||||||
describe("Copy Popup snapshot test", () => {
|
describe("Copy Popup snapshot test", () => {
|
||||||
|
const setShowCopyPopupMock = jest.fn();
|
||||||
it("should render when showCopyPopup is true", () => {
|
it("should render when showCopyPopup is true", () => {
|
||||||
const wrapper = shallow(<CopyPopup showCopyPopup={true} setShowCopyPopup={() => any} />);
|
const wrapper = shallow(<CopyPopup showCopyPopup={true} setShowCopyPopup={setShowCopyPopupMock} />);
|
||||||
|
expect(wrapper.exists()).toBe(true);
|
||||||
|
expect(wrapper.prop("setShowCopyPopup")).toBeUndefined();
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should render when showCopyPopup is false", () => {
|
||||||
|
const wrapper = shallow(<CopyPopup showCopyPopup={false} setShowCopyPopup={setShowCopyPopupMock} />);
|
||||||
|
expect(wrapper.prop("showCopyPopup")).toBeFalsy();
|
||||||
|
expect(wrapper.prop("setShowCopyPopup")).toBeUndefined();
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call setShowCopyPopup(false) when close button is clicked", () => {
|
||||||
|
const wrapper = shallow(<CopyPopup showCopyPopup={true} setShowCopyPopup={setShowCopyPopupMock} />);
|
||||||
|
|
||||||
|
const closeButton = wrapper.find(IconButton);
|
||||||
|
closeButton.props().onClick?.({} as React.MouseEvent<HTMLButtonElement, MouseEvent>);
|
||||||
|
|
||||||
|
expect(setShowCopyPopupMock).toHaveBeenCalledWith(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have the correct inline styles", () => {
|
||||||
|
const wrapper = shallow(<CopyPopup showCopyPopup={true} setShowCopyPopup={setShowCopyPopupMock} />);
|
||||||
|
|
||||||
|
const stackStyle = wrapper.find("Stack").first().props().style;
|
||||||
|
|
||||||
|
expect(stackStyle).toEqual({
|
||||||
|
position: "fixed",
|
||||||
|
width: 345,
|
||||||
|
height: 66,
|
||||||
|
padding: 10,
|
||||||
|
gap: 5,
|
||||||
|
top: 75,
|
||||||
|
right: 20,
|
||||||
|
background: "#FFFFFF",
|
||||||
|
boxShadow: "0 2px 6px rgba(0, 0, 0, 0.16)",
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,19 +1,113 @@
|
|||||||
import { shallow } from "enzyme";
|
import { mount, shallow } from "enzyme";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { any } from "underscore";
|
|
||||||
import { DeletePopup } from "./DeletePopup";
|
import { DeletePopup } from "./DeletePopup";
|
||||||
|
|
||||||
describe("Delete Popup snapshot test", () => {
|
describe("Delete Popup snapshot test", () => {
|
||||||
|
const setShowDeletePopupMock = jest.fn();
|
||||||
|
const setQueryMock = jest.fn();
|
||||||
|
const clearFeedbackMock = jest.fn();
|
||||||
|
const showFeedbackBarMock = jest.fn();
|
||||||
|
|
||||||
it("should render when showDeletePopup is true", () => {
|
it("should render when showDeletePopup is true", () => {
|
||||||
const wrapper = shallow(
|
const wrapper = shallow(
|
||||||
<DeletePopup
|
<DeletePopup
|
||||||
showDeletePopup={true}
|
showDeletePopup={true}
|
||||||
setShowDeletePopup={() => any}
|
setShowDeletePopup={setShowDeletePopupMock}
|
||||||
setQuery={() => any}
|
setQuery={setQueryMock}
|
||||||
clearFeedback={() => any}
|
clearFeedback={clearFeedbackMock}
|
||||||
showFeedbackBar={() => any}
|
showFeedbackBar={showFeedbackBarMock}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
expect(wrapper.find("Modal").prop("isOpen")).toBeTruthy();
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should not render when showDeletePopup is false", () => {
|
||||||
|
const wrapper = shallow(
|
||||||
|
<DeletePopup
|
||||||
|
showDeletePopup={false}
|
||||||
|
setShowDeletePopup={setShowDeletePopupMock}
|
||||||
|
setQuery={setQueryMock}
|
||||||
|
clearFeedback={clearFeedbackMock}
|
||||||
|
showFeedbackBar={showFeedbackBarMock}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(wrapper.props().children.props.showDeletePopup).toBeFalsy();
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call setQuery with an empty string and setShowDeletePopup(false) when delete button is clicked", () => {
|
||||||
|
const wrapper = mount(
|
||||||
|
<DeletePopup
|
||||||
|
showDeletePopup={true}
|
||||||
|
setShowDeletePopup={setShowDeletePopupMock}
|
||||||
|
setQuery={setQueryMock}
|
||||||
|
clearFeedback={clearFeedbackMock}
|
||||||
|
showFeedbackBar={showFeedbackBarMock}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
wrapper.find("PrimaryButton").simulate("click");
|
||||||
|
|
||||||
|
expect(setQueryMock).toHaveBeenCalledWith("");
|
||||||
|
expect(setShowDeletePopupMock).toHaveBeenCalledWith(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call setShowDeletePopup(false) when close button is clicked", () => {
|
||||||
|
const setShowDeletePopupMock = jest.fn();
|
||||||
|
const wrapper = mount(
|
||||||
|
<DeletePopup
|
||||||
|
showDeletePopup={true}
|
||||||
|
setShowDeletePopup={setShowDeletePopupMock}
|
||||||
|
setQuery={setQueryMock}
|
||||||
|
clearFeedback={clearFeedbackMock}
|
||||||
|
showFeedbackBar={showFeedbackBarMock}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
wrapper.find("DefaultButton").at(1).simulate("click");
|
||||||
|
|
||||||
|
expect(setShowDeletePopupMock).toHaveBeenCalledWith(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render the appropriate text content", () => {
|
||||||
|
const wrapper = shallow(
|
||||||
|
<DeletePopup
|
||||||
|
showDeletePopup={true}
|
||||||
|
setShowDeletePopup={setShowDeletePopupMock}
|
||||||
|
setQuery={setQueryMock}
|
||||||
|
clearFeedback={clearFeedbackMock}
|
||||||
|
showFeedbackBar={showFeedbackBarMock}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const textContent = wrapper
|
||||||
|
.find("Text")
|
||||||
|
.map((text, index) => <React.Fragment key={index}>{text.props().children}</React.Fragment>);
|
||||||
|
|
||||||
|
expect(textContent).toEqual([
|
||||||
|
<React.Fragment key={0}>
|
||||||
|
<b>Delete code?</b>
|
||||||
|
</React.Fragment>,
|
||||||
|
<React.Fragment key={1}>
|
||||||
|
This will clear the query from the query builder pane along with all comments and also reset the prompt pane
|
||||||
|
</React.Fragment>,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have the correct inline style", () => {
|
||||||
|
const wrapper = shallow(
|
||||||
|
<DeletePopup
|
||||||
|
showDeletePopup={true}
|
||||||
|
setShowDeletePopup={setShowDeletePopupMock}
|
||||||
|
setQuery={setQueryMock}
|
||||||
|
clearFeedback={clearFeedbackMock}
|
||||||
|
showFeedbackBar={showFeedbackBarMock}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const stackStyle = wrapper.find("Stack[style]").props().style;
|
||||||
|
|
||||||
|
expect(stackStyle).toEqual({ padding: "16px 24px", height: "auto" });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Copy Popup snapshot test should render when showCopyPopup is false 1`] = `<Fragment />`;
|
||||||
|
|
||||||
exports[`Copy Popup snapshot test should render when showCopyPopup is true 1`] = `
|
exports[`Copy Popup snapshot test should render when showCopyPopup is true 1`] = `
|
||||||
<Stack
|
<Stack
|
||||||
style={
|
style={
|
||||||
|
|||||||
@@ -1,5 +1,83 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Delete Popup snapshot test should not render when showDeletePopup is false 1`] = `
|
||||||
|
<Modal
|
||||||
|
isOpen={false}
|
||||||
|
styles={
|
||||||
|
Object {
|
||||||
|
"main": Object {
|
||||||
|
"minHeight": "122px",
|
||||||
|
"minWidth": "880px",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Stack
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"height": "auto",
|
||||||
|
"padding": "16px 24px",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"fontSize": "18px",
|
||||||
|
"height": 24,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<b>
|
||||||
|
Delete code?
|
||||||
|
</b>
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"marginBottom": 20,
|
||||||
|
"marginTop": 10,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
This will clear the query from the query builder pane along with all comments and also reset the prompt pane
|
||||||
|
</Text>
|
||||||
|
<Stack
|
||||||
|
horizontal={true}
|
||||||
|
horizontalAlign="start"
|
||||||
|
tokens={
|
||||||
|
Object {
|
||||||
|
"childrenGap": 10,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<CustomizedPrimaryButton
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"height": 24,
|
||||||
|
"padding": "0px 20px",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</CustomizedPrimaryButton>
|
||||||
|
<CustomizedDefaultButton
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"height": 24,
|
||||||
|
"padding": "0px 20px",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</CustomizedDefaultButton>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Modal>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`Delete Popup snapshot test should render when showDeletePopup is true 1`] = `
|
exports[`Delete Popup snapshot test should render when showDeletePopup is true 1`] = `
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
import { FeedOptions, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
import { FeedOptions } from "@azure/cosmos";
|
||||||
import {
|
import {
|
||||||
Callout,
|
Callout,
|
||||||
CommandBarButton,
|
CommandBarButton,
|
||||||
@@ -17,16 +17,10 @@ import {
|
|||||||
TextField,
|
TextField,
|
||||||
} from "@fluentui/react";
|
} from "@fluentui/react";
|
||||||
import { useBoolean } from "@fluentui/react-hooks";
|
import { useBoolean } from "@fluentui/react-hooks";
|
||||||
import {
|
import { QueryCopilotSampleContainerId, QueryCopilotSampleContainerSchema } from "Common/Constants";
|
||||||
QueryCopilotSampleContainerId,
|
|
||||||
QueryCopilotSampleContainerSchema,
|
|
||||||
QueryCopilotSampleDatabaseId,
|
|
||||||
} from "Common/Constants";
|
|
||||||
import { getErrorMessage, handleError } from "Common/ErrorHandlingUtils";
|
import { getErrorMessage, handleError } from "Common/ErrorHandlingUtils";
|
||||||
import { shouldEnableCrossPartitionKey } from "Common/HeadersUtility";
|
import { shouldEnableCrossPartitionKey } from "Common/HeadersUtility";
|
||||||
import { MinimalQueryIterator } from "Common/IteratorUtilities";
|
import { MinimalQueryIterator } from "Common/IteratorUtilities";
|
||||||
import { sampleDataClient } from "Common/SampleDataClient";
|
|
||||||
import { getCommonQueryOptions } from "Common/dataAccess/queryDocuments";
|
|
||||||
import { queryDocumentsPage } from "Common/dataAccess/queryDocumentsPage";
|
import { queryDocumentsPage } from "Common/dataAccess/queryDocumentsPage";
|
||||||
import { QueryResults } from "Contracts/ViewModels";
|
import { QueryResults } from "Contracts/ViewModels";
|
||||||
import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||||
@@ -37,7 +31,7 @@ import { SaveQueryPane } from "Explorer/Panes/SaveQueryPane/SaveQueryPane";
|
|||||||
import { WelcomeModal } from "Explorer/QueryCopilot/Modal/WelcomeModal";
|
import { WelcomeModal } from "Explorer/QueryCopilot/Modal/WelcomeModal";
|
||||||
import { CopyPopup } from "Explorer/QueryCopilot/Popup/CopyPopup";
|
import { CopyPopup } from "Explorer/QueryCopilot/Popup/CopyPopup";
|
||||||
import { DeletePopup } from "Explorer/QueryCopilot/Popup/DeletePopup";
|
import { DeletePopup } from "Explorer/QueryCopilot/Popup/DeletePopup";
|
||||||
import { submitFeedback } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
import { querySampleDocuments, submitFeedback } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
||||||
import { SamplePrompts, SamplePromptsProps } from "Explorer/QueryCopilot/SamplePrompts/SamplePrompts";
|
import { SamplePrompts, SamplePromptsProps } from "Explorer/QueryCopilot/SamplePrompts/SamplePrompts";
|
||||||
import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection";
|
import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
@@ -50,7 +44,6 @@ import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
|
|||||||
import HintIcon from "../../../images/Hint.svg";
|
import HintIcon from "../../../images/Hint.svg";
|
||||||
import CopilotIcon from "../../../images/QueryCopilotNewLogo.svg";
|
import CopilotIcon from "../../../images/QueryCopilotNewLogo.svg";
|
||||||
import RecentIcon from "../../../images/Recent.svg";
|
import RecentIcon from "../../../images/Recent.svg";
|
||||||
import SamplePromptsIcon from "../../../images/SamplePromptsIcon.svg";
|
|
||||||
import XErrorMessage from "../../../images/X-errorMessage.svg";
|
import XErrorMessage from "../../../images/X-errorMessage.svg";
|
||||||
import SaveQueryIcon from "../../../images/save-cosmos.svg";
|
import SaveQueryIcon from "../../../images/save-cosmos.svg";
|
||||||
import { useTabs } from "../../hooks/useTabs";
|
import { useTabs } from "../../hooks/useTabs";
|
||||||
@@ -128,12 +121,12 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const cachedHistoriesString = localStorage.getItem(`${userContext.databaseAccount?.id}-queryCopilotHistories`);
|
const cachedHistoriesString = localStorage.getItem(`${userContext.databaseAccount?.id}-queryCopilotHistories`);
|
||||||
const cachedHistories = cachedHistoriesString?.split(",");
|
const cachedHistories = cachedHistoriesString?.split("|");
|
||||||
const [histories, setHistories] = useState<string[]>(cachedHistories || []);
|
const [histories, setHistories] = useState<string[]>(cachedHistories || []);
|
||||||
const suggestedPrompts: SuggestedPrompt[] = [
|
const suggestedPrompts: SuggestedPrompt[] = [
|
||||||
{ id: 1, text: "Give me all customers whose names start with C" },
|
{ id: 1, text: 'Show all products that have the word "ultra" in the name or description' },
|
||||||
{ id: 2, text: "Show me all customers" },
|
{ id: 2, text: "What are all of the possible categories for the products, and their counts?" },
|
||||||
{ id: 3, text: "Show me all customers who bought a bike in 2019" },
|
{ id: 3, text: 'Show me all products that have been reviewed by someone with a username that contains "bob"' },
|
||||||
];
|
];
|
||||||
const [filteredHistories, setFilteredHistories] = useState<string[]>(histories);
|
const [filteredHistories, setFilteredHistories] = useState<string[]>(histories);
|
||||||
const [filteredSuggestedPrompts, setFilteredSuggestedPrompts] = useState<SuggestedPrompt[]>(suggestedPrompts);
|
const [filteredSuggestedPrompts, setFilteredSuggestedPrompts] = useState<SuggestedPrompt[]>(suggestedPrompts);
|
||||||
@@ -155,9 +148,16 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateHistories = (): void => {
|
const updateHistories = (): void => {
|
||||||
const newHistories = histories.length < 3 ? [userPrompt, ...histories] : [userPrompt, histories[1], histories[2]];
|
const formattedUserPrompt = userPrompt.replace(/\s+/g, " ").trim();
|
||||||
|
const existingHistories = histories.map((history) => history.replace(/\s+/g, " ").trim());
|
||||||
|
|
||||||
|
const updatedHistories = existingHistories.filter(
|
||||||
|
(history) => history.toLowerCase() !== formattedUserPrompt.toLowerCase()
|
||||||
|
);
|
||||||
|
const newHistories = [formattedUserPrompt, ...updatedHistories.slice(0, 2)];
|
||||||
|
|
||||||
setHistories(newHistories);
|
setHistories(newHistories);
|
||||||
localStorage.setItem(`${userContext.databaseAccount.id}-queryCopilotHistories`, newHistories.join(","));
|
localStorage.setItem(`${userContext.databaseAccount.id}-queryCopilotHistories`, newHistories.join("|"));
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateSQLQuery = async (): Promise<void> => {
|
const generateSQLQuery = async (): Promise<void> => {
|
||||||
@@ -179,14 +179,21 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const generateSQLQueryResponse: GenerateSQLQueryResponse = await response?.json();
|
const generateSQLQueryResponse: GenerateSQLQueryResponse = await response?.json();
|
||||||
if (generateSQLQueryResponse?.sql) {
|
if (response.ok) {
|
||||||
let query = `-- **Prompt:** ${userPrompt}\r\n`;
|
if (generateSQLQueryResponse?.sql) {
|
||||||
if (generateSQLQueryResponse.explanation) {
|
let query = `-- **Prompt:** ${userPrompt}\r\n`;
|
||||||
query += `-- **Explanation of query:** ${generateSQLQueryResponse.explanation}\r\n`;
|
if (generateSQLQueryResponse.explanation) {
|
||||||
|
query += `-- **Explanation of query:** ${generateSQLQueryResponse.explanation}\r\n`;
|
||||||
|
}
|
||||||
|
query += generateSQLQueryResponse.sql;
|
||||||
|
setQuery(query);
|
||||||
|
setGeneratedQuery(generateSQLQueryResponse.sql);
|
||||||
|
setShowErrorMessageBar(false);
|
||||||
}
|
}
|
||||||
query += generateSQLQueryResponse.sql;
|
} else {
|
||||||
setQuery(query);
|
handleError(JSON.stringify(generateSQLQueryResponse), "copilotInternalServerError");
|
||||||
setGeneratedQuery(generateSQLQueryResponse.sql);
|
useTabs.getState().setIsQueryErrorThrown(true);
|
||||||
|
setShowErrorMessageBar(true);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, "executeNaturalLanguageQuery");
|
handleError(error, "executeNaturalLanguageQuery");
|
||||||
@@ -200,14 +207,6 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const querySampleDocuments = (query: string, options: FeedOptions): QueryIterator<ItemDefinition & Resource> => {
|
|
||||||
options = getCommonQueryOptions(options);
|
|
||||||
return sampleDataClient()
|
|
||||||
.database(QueryCopilotSampleDatabaseId)
|
|
||||||
.container(QueryCopilotSampleContainerId)
|
|
||||||
.items.query(query, options);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onExecuteQueryClick = async (): Promise<void> => {
|
const onExecuteQueryClick = async (): Promise<void> => {
|
||||||
const queryToExecute = selectedQuery || query;
|
const queryToExecute = selectedQuery || query;
|
||||||
const queryIterator = querySampleDocuments(queryToExecute, {
|
const queryIterator = querySampleDocuments(queryToExecute, {
|
||||||
@@ -233,6 +232,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
|
|
||||||
setQueryResults(queryResults);
|
setQueryResults(queryResults);
|
||||||
setErrorMessage("");
|
setErrorMessage("");
|
||||||
|
setShowErrorMessageBar(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = getErrorMessage(error);
|
const errorMessage = getErrorMessage(error);
|
||||||
setErrorMessage(errorMessage);
|
setErrorMessage(errorMessage);
|
||||||
@@ -254,6 +254,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
commandButtonLabel: executeQueryBtnLabel,
|
commandButtonLabel: executeQueryBtnLabel,
|
||||||
ariaLabel: executeQueryBtnLabel,
|
ariaLabel: executeQueryBtnLabel,
|
||||||
hasPopup: false,
|
hasPopup: false,
|
||||||
|
disabled: query?.trim() === "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveQueryBtn = {
|
const saveQueryBtn = {
|
||||||
@@ -264,18 +265,20 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
commandButtonLabel: "Save Query",
|
commandButtonLabel: "Save Query",
|
||||||
ariaLabel: "Save Query",
|
ariaLabel: "Save Query",
|
||||||
hasPopup: false,
|
hasPopup: false,
|
||||||
|
disabled: query?.trim() === "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const samplePromptsBtn = {
|
// Sample Prompts temporary disabled due current design
|
||||||
iconSrc: SamplePromptsIcon,
|
// const samplePromptsBtn = {
|
||||||
iconAlt: "Sample Prompts",
|
// iconSrc: SamplePromptsIcon,
|
||||||
onCommandClick: () => setIsSamplePromptsOpen(true),
|
// iconAlt: "Sample Prompts",
|
||||||
commandButtonLabel: "Sample Prompts",
|
// onCommandClick: () => setIsSamplePromptsOpen(true),
|
||||||
ariaLabel: "Sample Prompts",
|
// commandButtonLabel: "Sample Prompts",
|
||||||
hasPopup: false,
|
// ariaLabel: "Sample Prompts",
|
||||||
};
|
// hasPopup: false,
|
||||||
|
// };
|
||||||
|
|
||||||
return [executeQueryBtn, saveQueryBtn, samplePromptsBtn];
|
return [executeQueryBtn, saveQueryBtn];
|
||||||
};
|
};
|
||||||
const showTeachingBubble = (): void => {
|
const showTeachingBubble = (): void => {
|
||||||
if (!inputEdited.current) {
|
if (!inputEdited.current) {
|
||||||
@@ -307,269 +310,283 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack className="tab-pane" style={{ padding: 24, width: "100%", height: "100%" }}>
|
<Stack className="tab-pane" style={{ padding: 24, width: "100%" }}>
|
||||||
<Stack horizontal verticalAlign="center">
|
<div style={{ overflowY: "auto", height: "100%" }}>
|
||||||
<Image src={CopilotIcon} />
|
<Stack horizontal verticalAlign="center">
|
||||||
<Text style={{ marginLeft: 8, fontWeight: 600, fontSize: 16 }}>Copilot</Text>
|
<Image src={CopilotIcon} />
|
||||||
</Stack>
|
<Text style={{ marginLeft: 8, fontWeight: 600, fontSize: 16 }}>Copilot</Text>
|
||||||
<Stack horizontal verticalAlign="center" style={{ marginTop: 16, width: "100%", position: "relative" }}>
|
</Stack>
|
||||||
<TextField
|
<Stack horizontal verticalAlign="center" style={{ marginTop: 16, width: "100%", position: "relative" }}>
|
||||||
id="naturalLanguageInput"
|
<TextField
|
||||||
value={userPrompt}
|
id="naturalLanguageInput"
|
||||||
onChange={handleUserPromptChange}
|
value={userPrompt}
|
||||||
onClick={() => {
|
onChange={handleUserPromptChange}
|
||||||
inputEdited.current = true;
|
onClick={() => {
|
||||||
setShowSamplePrompts(true);
|
inputEdited.current = true;
|
||||||
}}
|
setShowSamplePrompts(true);
|
||||||
style={{ lineHeight: 30 }}
|
}}
|
||||||
styles={{ root: { width: "95%" } }}
|
style={{ lineHeight: 30 }}
|
||||||
disabled={isGeneratingQuery}
|
styles={{ root: { width: "95%" } }}
|
||||||
autoComplete="off"
|
disabled={isGeneratingQuery}
|
||||||
/>
|
autoComplete="off"
|
||||||
{copilotTeachingBubbleVisible && (
|
/>
|
||||||
<TeachingBubble
|
{copilotTeachingBubbleVisible && (
|
||||||
calloutProps={{ directionalHint: DirectionalHint.bottomCenter }}
|
<TeachingBubble
|
||||||
target="#naturalLanguageInput"
|
calloutProps={{ directionalHint: DirectionalHint.bottomCenter }}
|
||||||
hasCloseButton={true}
|
target="#naturalLanguageInput"
|
||||||
closeButtonAriaLabel="Close"
|
hasCloseButton={true}
|
||||||
onDismiss={toggleCopilotTeachingBubbleVisible}
|
closeButtonAriaLabel="Close"
|
||||||
hasSmallHeadline={true}
|
onDismiss={toggleCopilotTeachingBubbleVisible}
|
||||||
headline="Write a prompt"
|
hasSmallHeadline={true}
|
||||||
>
|
headline="Write a prompt"
|
||||||
Write a prompt here and Copilot will generate the query for you. You can also choose from our{" "}
|
|
||||||
<Link
|
|
||||||
onClick={() => {
|
|
||||||
setShowSamplePrompts(true);
|
|
||||||
toggleCopilotTeachingBubbleVisible();
|
|
||||||
}}
|
|
||||||
style={{ color: "white", fontWeight: 600 }}
|
|
||||||
>
|
>
|
||||||
sample prompts
|
Write a prompt here and Copilot will generate the query for you. You can also choose from our{" "}
|
||||||
</Link>{" "}
|
<Link
|
||||||
or write your own query
|
onClick={() => {
|
||||||
</TeachingBubble>
|
setShowSamplePrompts(true);
|
||||||
)}
|
toggleCopilotTeachingBubbleVisible();
|
||||||
<IconButton
|
|
||||||
iconProps={{ iconName: "Send" }}
|
|
||||||
disabled={isGeneratingQuery || !userPrompt.trim()}
|
|
||||||
style={{ marginLeft: 8 }}
|
|
||||||
onClick={() => {
|
|
||||||
updateHistories();
|
|
||||||
generateSQLQuery();
|
|
||||||
resetButtonState();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{isGeneratingQuery && <Spinner style={{ marginLeft: 8 }} />}
|
|
||||||
{showSamplePrompts && (
|
|
||||||
<Callout
|
|
||||||
styles={{ root: { minWidth: 400 } }}
|
|
||||||
target="#naturalLanguageInput"
|
|
||||||
isBeakVisible={false}
|
|
||||||
onDismiss={() => setShowSamplePrompts(false)}
|
|
||||||
directionalHintFixed={true}
|
|
||||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
|
||||||
alignTargetEdge={true}
|
|
||||||
gapSpace={4}
|
|
||||||
>
|
|
||||||
<Stack>
|
|
||||||
{filteredHistories?.length > 0 && (
|
|
||||||
<Stack>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: 600,
|
|
||||||
color: "#0078D4",
|
|
||||||
marginLeft: 16,
|
|
||||||
padding: "4px 0",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Recent
|
|
||||||
</Text>
|
|
||||||
{filteredHistories.map((history, i) => (
|
|
||||||
<DefaultButton
|
|
||||||
key={i}
|
|
||||||
onClick={() => {
|
|
||||||
setUserPrompt(history);
|
|
||||||
setShowSamplePrompts(false);
|
|
||||||
}}
|
|
||||||
onRenderIcon={() => <Image src={RecentIcon} />}
|
|
||||||
styles={promptStyles}
|
|
||||||
>
|
|
||||||
{history}
|
|
||||||
</DefaultButton>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: 600,
|
|
||||||
color: "#0078D4",
|
|
||||||
marginLeft: 16,
|
|
||||||
padding: "4px 0",
|
|
||||||
}}
|
}}
|
||||||
|
style={{ color: "white", fontWeight: 600 }}
|
||||||
>
|
>
|
||||||
Suggested Prompts
|
sample prompts
|
||||||
</Text>
|
</Link>{" "}
|
||||||
{filteredSuggestedPrompts.map((prompt) => (
|
or write your own query
|
||||||
<DefaultButton
|
</TeachingBubble>
|
||||||
key={prompt.id}
|
|
||||||
onClick={() => {
|
|
||||||
setUserPrompt(prompt.text);
|
|
||||||
setShowSamplePrompts(false);
|
|
||||||
}}
|
|
||||||
onRenderIcon={() => <Image src={HintIcon} />}
|
|
||||||
styles={promptStyles}
|
|
||||||
>
|
|
||||||
{prompt.text}
|
|
||||||
</DefaultButton>
|
|
||||||
))}
|
|
||||||
<Separator
|
|
||||||
styles={{
|
|
||||||
root: {
|
|
||||||
selectors: { "::before": { background: "#E1DFDD" } },
|
|
||||||
padding: 0,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
fontSize: 14,
|
|
||||||
marginLeft: 16,
|
|
||||||
padding: "4px 0",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Learn about{" "}
|
|
||||||
<Link target="_blank" href="">
|
|
||||||
writing effective prompts
|
|
||||||
</Link>
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
</Callout>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<Text style={{ marginTop: 8, marginBottom: 24, fontSize: 12 }}>
|
|
||||||
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.{" "}
|
|
||||||
<Link href="" target="_blank">
|
|
||||||
Read preview terms
|
|
||||||
</Link>
|
|
||||||
{showErrorMessageBar && (
|
|
||||||
<Stack style={{ backgroundColor: "#FEF0F1", padding: "4px 8px" }} horizontal verticalAlign="center">
|
|
||||||
<Image src={XErrorMessage} style={{ marginRight: "8px" }} />
|
|
||||||
<Text style={{ fontSize: 12 }}>
|
|
||||||
We ran into an error and were not able to execute query. Please try again after sometime
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
{showFeedbackBar && (
|
|
||||||
<Stack style={{ backgroundColor: "#FFF8F0", padding: "2px 8px" }} horizontal verticalAlign="center">
|
|
||||||
<Text style={{ fontWeight: 600, fontSize: 12 }}>Provide feedback on the query generated</Text>
|
|
||||||
{showCallout && !hideFeedbackModalForLikedQueries && (
|
|
||||||
<Callout
|
|
||||||
style={{ padding: 8 }}
|
|
||||||
target="#likeBtn"
|
|
||||||
onDismiss={() => {
|
|
||||||
setShowCallout(false);
|
|
||||||
submitFeedback({ generatedQuery, likeQuery, description: "", userPrompt: userPrompt });
|
|
||||||
}}
|
|
||||||
directionalHint={DirectionalHint.topCenter}
|
|
||||||
>
|
|
||||||
<Text>
|
|
||||||
Thank you. Need to give{" "}
|
|
||||||
<Link
|
|
||||||
onClick={() => {
|
|
||||||
setShowCallout(false);
|
|
||||||
useQueryCopilot.getState().openFeedbackModal(generatedQuery, true, userPrompt);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
more feedback?
|
|
||||||
</Link>
|
|
||||||
</Text>
|
|
||||||
</Callout>
|
|
||||||
)}
|
)}
|
||||||
<IconButton
|
<IconButton
|
||||||
id="likeBtn"
|
iconProps={{ iconName: "Send" }}
|
||||||
style={{ marginLeft: 20 }}
|
disabled={isGeneratingQuery || !userPrompt.trim()}
|
||||||
iconProps={{ iconName: likeQuery === true ? "LikeSolid" : "Like" }}
|
style={{ marginLeft: 8 }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowCallout(!likeQuery);
|
updateHistories();
|
||||||
setLikeQuery(!likeQuery);
|
generateSQLQuery();
|
||||||
if (dislikeQuery) {
|
resetButtonState();
|
||||||
setDislikeQuery(!dislikeQuery);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
{isGeneratingQuery && <Spinner style={{ marginLeft: 8 }} />}
|
||||||
style={{ margin: "0 10px" }}
|
{showSamplePrompts && (
|
||||||
iconProps={{ iconName: dislikeQuery === true ? "DislikeSolid" : "Dislike" }}
|
<Callout
|
||||||
onClick={() => {
|
styles={{ root: { minWidth: 400 } }}
|
||||||
if (!dislikeQuery) {
|
target="#naturalLanguageInput"
|
||||||
useQueryCopilot.getState().openFeedbackModal(generatedQuery, false, userPrompt);
|
isBeakVisible={false}
|
||||||
setLikeQuery(false);
|
onDismiss={() => setShowSamplePrompts(false)}
|
||||||
}
|
directionalHintFixed={true}
|
||||||
setDislikeQuery(!dislikeQuery);
|
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||||
setShowCallout(false);
|
alignTargetEdge={true}
|
||||||
}}
|
gapSpace={4}
|
||||||
/>
|
>
|
||||||
<Separator vertical style={{ color: "#EDEBE9" }} />
|
<Stack>
|
||||||
<CommandBarButton
|
{filteredHistories?.length > 0 && (
|
||||||
onClick={copyGeneratedCode}
|
<Stack>
|
||||||
iconProps={{ iconName: "Copy" }}
|
<Text
|
||||||
style={{ margin: "0 10px", backgroundColor: "#FFF8F0", transition: "background-color 0.3s ease" }}
|
style={{
|
||||||
>
|
width: "100%",
|
||||||
Copy code
|
fontSize: 14,
|
||||||
</CommandBarButton>
|
fontWeight: 600,
|
||||||
<CommandBarButton
|
color: "#0078D4",
|
||||||
onClick={() => {
|
marginLeft: 16,
|
||||||
setShowDeletePopup(true);
|
padding: "4px 0",
|
||||||
}}
|
}}
|
||||||
iconProps={{ iconName: "Delete" }}
|
>
|
||||||
style={{ margin: "0 10px", backgroundColor: "#FFF8F0", transition: "background-color 0.3s ease" }}
|
Recent
|
||||||
>
|
</Text>
|
||||||
Delete code
|
{filteredHistories.map((history, i) => (
|
||||||
</CommandBarButton>
|
<DefaultButton
|
||||||
|
key={i}
|
||||||
|
onClick={() => {
|
||||||
|
setUserPrompt(history);
|
||||||
|
setShowSamplePrompts(false);
|
||||||
|
}}
|
||||||
|
onRenderIcon={() => <Image src={RecentIcon} />}
|
||||||
|
styles={promptStyles}
|
||||||
|
>
|
||||||
|
{history}
|
||||||
|
</DefaultButton>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{filteredSuggestedPrompts?.length > 0 && (
|
||||||
|
<Stack>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: 600,
|
||||||
|
color: "#0078D4",
|
||||||
|
marginLeft: 16,
|
||||||
|
padding: "4px 0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Suggested Prompts
|
||||||
|
</Text>
|
||||||
|
{filteredSuggestedPrompts.map((prompt) => (
|
||||||
|
<DefaultButton
|
||||||
|
key={prompt.id}
|
||||||
|
onClick={() => {
|
||||||
|
setUserPrompt(prompt.text);
|
||||||
|
setShowSamplePrompts(false);
|
||||||
|
}}
|
||||||
|
onRenderIcon={() => <Image src={HintIcon} />}
|
||||||
|
styles={promptStyles}
|
||||||
|
>
|
||||||
|
{prompt.text}
|
||||||
|
</DefaultButton>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{(filteredHistories?.length > 0 || filteredSuggestedPrompts?.length > 0) && (
|
||||||
|
<Stack>
|
||||||
|
<Separator
|
||||||
|
styles={{
|
||||||
|
root: {
|
||||||
|
selectors: { "::before": { background: "#E1DFDD" } },
|
||||||
|
padding: 0,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
fontSize: 14,
|
||||||
|
marginLeft: 16,
|
||||||
|
padding: "4px 0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Learn about{" "}
|
||||||
|
<Link target="_blank" href="">
|
||||||
|
writing effective prompts
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Callout>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
|
||||||
|
|
||||||
<Stack className="tabPaneContentContainer">
|
<Stack style={{ marginTop: 8, marginBottom: 24 }}>
|
||||||
<SplitterLayout vertical={true} primaryIndex={0} primaryMinSize={100} secondaryMinSize={200}>
|
<Text style={{ fontSize: 12 }}>
|
||||||
<EditorReact
|
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.{" "}
|
||||||
language={"sql"}
|
<Link href="" target="_blank">
|
||||||
content={query}
|
Read preview terms
|
||||||
isReadOnly={false}
|
</Link>
|
||||||
ariaLabel={"Editing Query"}
|
{showErrorMessageBar && (
|
||||||
lineNumbers={"on"}
|
<Stack style={{ backgroundColor: "#FEF0F1", padding: "4px 8px" }} horizontal verticalAlign="center">
|
||||||
onContentChanged={(newQuery: string) => setQuery(newQuery)}
|
<Image src={XErrorMessage} style={{ marginRight: "8px" }} />
|
||||||
onContentSelected={(selectedQuery: string) => setSelectedQuery(selectedQuery)}
|
<Text style={{ fontSize: 12 }}>
|
||||||
|
We ran into an error and were not able to execute query. Please try again after sometime
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{showFeedbackBar && (
|
||||||
|
<Stack style={{ backgroundColor: "#FFF8F0", padding: "2px 8px" }} horizontal verticalAlign="center">
|
||||||
|
<Text style={{ fontWeight: 600, fontSize: 12 }}>Provide feedback on the query generated</Text>
|
||||||
|
{showCallout && !hideFeedbackModalForLikedQueries && (
|
||||||
|
<Callout
|
||||||
|
style={{ padding: 8 }}
|
||||||
|
target="#likeBtn"
|
||||||
|
onDismiss={() => {
|
||||||
|
setShowCallout(false);
|
||||||
|
submitFeedback({ generatedQuery, likeQuery, description: "", userPrompt: userPrompt });
|
||||||
|
}}
|
||||||
|
directionalHint={DirectionalHint.topCenter}
|
||||||
|
>
|
||||||
|
<Text>
|
||||||
|
Thank you. Need to give{" "}
|
||||||
|
<Link
|
||||||
|
onClick={() => {
|
||||||
|
setShowCallout(false);
|
||||||
|
useQueryCopilot.getState().openFeedbackModal(generatedQuery, true, userPrompt);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
more feedback?
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
</Callout>
|
||||||
|
)}
|
||||||
|
<IconButton
|
||||||
|
id="likeBtn"
|
||||||
|
style={{ marginLeft: 20 }}
|
||||||
|
iconProps={{ iconName: likeQuery === true ? "LikeSolid" : "Like" }}
|
||||||
|
onClick={() => {
|
||||||
|
setShowCallout(!likeQuery);
|
||||||
|
setLikeQuery(!likeQuery);
|
||||||
|
if (dislikeQuery) {
|
||||||
|
setDislikeQuery(!dislikeQuery);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
style={{ margin: "0 10px" }}
|
||||||
|
iconProps={{ iconName: dislikeQuery === true ? "DislikeSolid" : "Dislike" }}
|
||||||
|
onClick={() => {
|
||||||
|
if (!dislikeQuery) {
|
||||||
|
useQueryCopilot.getState().openFeedbackModal(generatedQuery, false, userPrompt);
|
||||||
|
setLikeQuery(false);
|
||||||
|
}
|
||||||
|
setDislikeQuery(!dislikeQuery);
|
||||||
|
setShowCallout(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Separator vertical style={{ color: "#EDEBE9" }} />
|
||||||
|
<CommandBarButton
|
||||||
|
onClick={copyGeneratedCode}
|
||||||
|
iconProps={{ iconName: "Copy" }}
|
||||||
|
style={{ margin: "0 10px", backgroundColor: "#FFF8F0", transition: "background-color 0.3s ease" }}
|
||||||
|
>
|
||||||
|
Copy code
|
||||||
|
</CommandBarButton>
|
||||||
|
<CommandBarButton
|
||||||
|
onClick={() => {
|
||||||
|
setShowDeletePopup(true);
|
||||||
|
}}
|
||||||
|
iconProps={{ iconName: "Delete" }}
|
||||||
|
style={{ margin: "0 10px", backgroundColor: "#FFF8F0", transition: "background-color 0.3s ease" }}
|
||||||
|
>
|
||||||
|
Delete code
|
||||||
|
</CommandBarButton>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Stack className="tabPaneContentContainer">
|
||||||
|
<SplitterLayout vertical={true} primaryIndex={0} primaryMinSize={100} secondaryMinSize={200}>
|
||||||
|
<EditorReact
|
||||||
|
language={"sql"}
|
||||||
|
content={query}
|
||||||
|
isReadOnly={false}
|
||||||
|
ariaLabel={"Editing Query"}
|
||||||
|
lineNumbers={"on"}
|
||||||
|
onContentChanged={(newQuery: string) => setQuery(newQuery)}
|
||||||
|
onContentSelected={(selectedQuery: string) => setSelectedQuery(selectedQuery)}
|
||||||
|
/>
|
||||||
|
<QueryResultSection
|
||||||
|
isMongoDB={false}
|
||||||
|
queryEditorContent={selectedQuery || query}
|
||||||
|
error={errorMessage}
|
||||||
|
queryResults={queryResults}
|
||||||
|
isExecuting={isExecuting}
|
||||||
|
executeQueryDocumentsPage={(firstItemIndex: number) =>
|
||||||
|
queryDocumentsPerPage(firstItemIndex, queryIterator)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SplitterLayout>
|
||||||
|
</Stack>
|
||||||
|
<WelcomeModal visible={localStorage.getItem("hideWelcomeModal") !== "true"} />
|
||||||
|
{isSamplePromptsOpen && <SamplePrompts sampleProps={sampleProps} />}
|
||||||
|
{query !== "" && query.trim().length !== 0 && (
|
||||||
|
<DeletePopup
|
||||||
|
showDeletePopup={showDeletePopup}
|
||||||
|
setShowDeletePopup={setShowDeletePopup}
|
||||||
|
setQuery={setQuery}
|
||||||
|
clearFeedback={resetButtonState}
|
||||||
|
showFeedbackBar={setShowFeedbackBar}
|
||||||
/>
|
/>
|
||||||
<QueryResultSection
|
)}
|
||||||
isMongoDB={false}
|
<CopyPopup showCopyPopup={showCopyPopup} setShowCopyPopup={setshowCopyPopup} />
|
||||||
queryEditorContent={selectedQuery || query}
|
</div>
|
||||||
error={errorMessage}
|
|
||||||
queryResults={queryResults}
|
|
||||||
isExecuting={isExecuting}
|
|
||||||
executeQueryDocumentsPage={(firstItemIndex: number) => queryDocumentsPerPage(firstItemIndex, queryIterator)}
|
|
||||||
/>
|
|
||||||
</SplitterLayout>
|
|
||||||
</Stack>
|
|
||||||
<WelcomeModal visible={localStorage.getItem("hideWelcomeModal") !== "true"} />
|
|
||||||
{isSamplePromptsOpen && <SamplePrompts sampleProps={sampleProps} />}
|
|
||||||
{query !== "" && query.trim().length !== 0 && (
|
|
||||||
<DeletePopup
|
|
||||||
showDeletePopup={showDeletePopup}
|
|
||||||
setShowDeletePopup={setShowDeletePopup}
|
|
||||||
setQuery={setQuery}
|
|
||||||
clearFeedback={resetButtonState}
|
|
||||||
showFeedbackBar={setShowFeedbackBar}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<CopyPopup showCopyPopup={showCopyPopup} setShowCopyPopup={setshowCopyPopup} />
|
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
import { QueryCopilotSampleContainerSchema } from "Common/Constants";
|
import { FeedOptions, Item, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||||
|
import {
|
||||||
|
QueryCopilotSampleContainerId,
|
||||||
|
QueryCopilotSampleContainerSchema,
|
||||||
|
QueryCopilotSampleDatabaseId,
|
||||||
|
} from "Common/Constants";
|
||||||
import { handleError } from "Common/ErrorHandlingUtils";
|
import { handleError } from "Common/ErrorHandlingUtils";
|
||||||
|
import { sampleDataClient } from "Common/SampleDataClient";
|
||||||
|
import { getPartitionKeyValue } from "Common/dataAccess/getPartitionKeyValue";
|
||||||
|
import { getCommonQueryOptions } from "Common/dataAccess/queryDocuments";
|
||||||
|
import DocumentId from "Explorer/Tree/DocumentId";
|
||||||
|
import { logConsoleProgress } from "Utils/NotificationConsoleUtils";
|
||||||
|
|
||||||
interface FeedbackParams {
|
interface FeedbackParams {
|
||||||
likeQuery: boolean;
|
likeQuery: boolean;
|
||||||
@@ -21,16 +31,41 @@ export const submitFeedback = async (params: FeedbackParams): Promise<void> => {
|
|||||||
contact: contact || "",
|
contact: contact || "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch("https://copilotorchestrater.azurewebsites.net/feedback", {
|
await fetch("https://copilotorchestrater.azurewebsites.net/feedback", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(response);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, "copilotSubmitFeedback");
|
handleError(error, "copilotSubmitFeedback");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const querySampleDocuments = (query: string, options: FeedOptions): QueryIterator<ItemDefinition & Resource> => {
|
||||||
|
options = getCommonQueryOptions(options);
|
||||||
|
return sampleDataClient()
|
||||||
|
.database(QueryCopilotSampleDatabaseId)
|
||||||
|
.container(QueryCopilotSampleContainerId)
|
||||||
|
.items.query(query, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const readSampleDocument = async (documentId: DocumentId): Promise<Item> => {
|
||||||
|
const clearMessage = logConsoleProgress(`Reading item ${documentId.id()}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await sampleDataClient()
|
||||||
|
.database(QueryCopilotSampleDatabaseId)
|
||||||
|
.container(QueryCopilotSampleContainerId)
|
||||||
|
.item(documentId.id(), getPartitionKeyValue(documentId))
|
||||||
|
.read();
|
||||||
|
|
||||||
|
return response?.resource;
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, "ReadDocument", `Failed to read item ${documentId.id()}`);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
clearMessage();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,26 +1,91 @@
|
|||||||
|
import { DefaultButton, IconButton } from "@fluentui/react";
|
||||||
import { shallow } from "enzyme";
|
import { shallow } from "enzyme";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { SamplePrompts, SamplePromptsProps } from "./SamplePrompts";
|
import { SamplePrompts, SamplePromptsProps } from "./SamplePrompts";
|
||||||
|
|
||||||
describe("Sample Prompts snapshot test", () => {
|
describe("Sample Prompts snapshot test", () => {
|
||||||
it("should render properly if isSamplePromptsOpen is true", () => {
|
const setTextBoxMock = jest.fn();
|
||||||
const sampleProps: SamplePromptsProps = {
|
const setIsSamplePromptsOpenMock = jest.fn();
|
||||||
isSamplePromptsOpen: true,
|
const sampleProps: SamplePromptsProps = {
|
||||||
setIsSamplePromptsOpen: () => undefined,
|
isSamplePromptsOpen: true,
|
||||||
setTextBox: () => undefined,
|
setIsSamplePromptsOpen: setIsSamplePromptsOpenMock,
|
||||||
};
|
setTextBox: setTextBoxMock,
|
||||||
|
};
|
||||||
|
beforeEach(() => jest.clearAllMocks());
|
||||||
|
|
||||||
|
it("should render properly if isSamplePromptsOpen is true", () => {
|
||||||
const wrapper = shallow(<SamplePrompts sampleProps={sampleProps} />);
|
const wrapper = shallow(<SamplePrompts sampleProps={sampleProps} />);
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render properly if isSamplePromptsOpen is false", () => {
|
it("should render properly if isSamplePromptsOpen is false", () => {
|
||||||
const sampleProps: SamplePromptsProps = {
|
sampleProps.isSamplePromptsOpen = false;
|
||||||
isSamplePromptsOpen: false,
|
|
||||||
setIsSamplePromptsOpen: () => undefined,
|
|
||||||
setTextBox: () => undefined,
|
|
||||||
};
|
|
||||||
const wrapper = shallow(<SamplePrompts sampleProps={sampleProps} />);
|
const wrapper = shallow(<SamplePrompts sampleProps={sampleProps} />);
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should call setTextBox and setIsSamplePromptsOpen(false) when a button is clicked", () => {
|
||||||
|
const wrapper = shallow(<SamplePrompts sampleProps={sampleProps} />);
|
||||||
|
|
||||||
|
wrapper.find(DefaultButton).at(0).simulate("click");
|
||||||
|
expect(setTextBoxMock).toHaveBeenCalledWith("Show me products less than 100 dolars");
|
||||||
|
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||||
|
|
||||||
|
wrapper.find(DefaultButton).at(3).simulate("click");
|
||||||
|
expect(setTextBoxMock).toHaveBeenCalledWith(
|
||||||
|
"Write a query to return all records in this table created in the last thirty days"
|
||||||
|
);
|
||||||
|
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call setIsSamplePromptsOpen(false) when the close button is clicked", () => {
|
||||||
|
const wrapper = shallow(<SamplePrompts sampleProps={sampleProps} />);
|
||||||
|
|
||||||
|
wrapper.find(IconButton).first().simulate("click");
|
||||||
|
|
||||||
|
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call setTextBox and setIsSamplePromptsOpen(false) when a simple prompt button is clicked", () => {
|
||||||
|
const wrapper = shallow(<SamplePrompts sampleProps={sampleProps} />);
|
||||||
|
|
||||||
|
wrapper.find(DefaultButton).at(0).simulate("click");
|
||||||
|
expect(setTextBoxMock).toHaveBeenCalledWith("Show me products less than 100 dolars");
|
||||||
|
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||||
|
|
||||||
|
wrapper.find(DefaultButton).at(1).simulate("click");
|
||||||
|
expect(setTextBoxMock).toHaveBeenCalledWith("Show schema");
|
||||||
|
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call setTextBox and setIsSamplePromptsOpen(false) when an intermediate prompt button is clicked", () => {
|
||||||
|
const wrapper = shallow(<SamplePrompts sampleProps={sampleProps} />);
|
||||||
|
|
||||||
|
wrapper.find(DefaultButton).at(2).simulate("click");
|
||||||
|
expect(setTextBoxMock).toHaveBeenCalledWith(
|
||||||
|
"Show items with a description that contains a number between 0 and 99 inclusive."
|
||||||
|
);
|
||||||
|
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||||
|
|
||||||
|
wrapper.find(DefaultButton).at(3).simulate("click");
|
||||||
|
expect(setTextBoxMock).toHaveBeenCalledWith(
|
||||||
|
"Write a query to return all records in this table created in the last thirty days"
|
||||||
|
);
|
||||||
|
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call setTextBox and setIsSamplePromptsOpen(false) when a complex prompt button is clicked", () => {
|
||||||
|
const wrapper = shallow(<SamplePrompts sampleProps={sampleProps} />);
|
||||||
|
|
||||||
|
wrapper.find(DefaultButton).at(4).simulate("click");
|
||||||
|
expect(setTextBoxMock).toHaveBeenCalledWith("Show all the products that customer Bob has reviewed.");
|
||||||
|
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||||
|
|
||||||
|
wrapper.find(DefaultButton).at(5).simulate("click");
|
||||||
|
expect(setTextBoxMock).toHaveBeenCalledWith("Which computers are more than 300 dollars and less than 400 dollars?");
|
||||||
|
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,133 +5,148 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
|
|||||||
className="tab-pane"
|
className="tab-pane"
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"height": "100%",
|
|
||||||
"padding": 24,
|
"padding": 24,
|
||||||
"width": "100%",
|
"width": "100%",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Stack
|
<div
|
||||||
horizontal={true}
|
|
||||||
verticalAlign="center"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
src=""
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"fontSize": 16,
|
|
||||||
"fontWeight": 600,
|
|
||||||
"marginLeft": 8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Copilot
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
<Stack
|
|
||||||
horizontal={true}
|
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"marginTop": 16,
|
"height": "100%",
|
||||||
"position": "relative",
|
"overflowY": "auto",
|
||||||
"width": "100%",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
verticalAlign="center"
|
|
||||||
>
|
|
||||||
<StyledTextFieldBase
|
|
||||||
autoComplete="off"
|
|
||||||
disabled={false}
|
|
||||||
id="naturalLanguageInput"
|
|
||||||
onChange={[Function]}
|
|
||||||
onClick={[Function]}
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"lineHeight": 30,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
styles={
|
|
||||||
Object {
|
|
||||||
"root": Object {
|
|
||||||
"width": "95%",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
value="Write a query to return all records in this table"
|
|
||||||
/>
|
|
||||||
<CustomizedIconButton
|
|
||||||
disabled={false}
|
|
||||||
iconProps={
|
|
||||||
Object {
|
|
||||||
"iconName": "Send",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onClick={[Function]}
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"marginLeft": 8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
<Text
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"fontSize": 12,
|
|
||||||
"marginBottom": 24,
|
|
||||||
"marginTop": 8,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.
|
<Stack
|
||||||
|
horizontal={true}
|
||||||
<StyledLinkBase
|
verticalAlign="center"
|
||||||
href=""
|
|
||||||
target="_blank"
|
|
||||||
>
|
>
|
||||||
Read preview terms
|
<Image
|
||||||
</StyledLinkBase>
|
src=""
|
||||||
</Text>
|
/>
|
||||||
<Stack
|
<Text
|
||||||
className="tabPaneContentContainer"
|
style={
|
||||||
>
|
Object {
|
||||||
<t
|
"fontSize": 16,
|
||||||
customClassName=""
|
"fontWeight": 600,
|
||||||
onDragEnd={null}
|
"marginLeft": 8,
|
||||||
onDragStart={null}
|
}
|
||||||
onSecondaryPaneSizeChange={null}
|
}
|
||||||
percentage={false}
|
>
|
||||||
primaryIndex={0}
|
Copilot
|
||||||
primaryMinSize={100}
|
</Text>
|
||||||
secondaryMinSize={200}
|
</Stack>
|
||||||
vertical={true}
|
<Stack
|
||||||
|
horizontal={true}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"marginTop": 16,
|
||||||
|
"position": "relative",
|
||||||
|
"width": "100%",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
verticalAlign="center"
|
||||||
>
|
>
|
||||||
<EditorReact
|
<StyledTextFieldBase
|
||||||
ariaLabel="Editing Query"
|
autoComplete="off"
|
||||||
content=""
|
disabled={false}
|
||||||
isReadOnly={false}
|
id="naturalLanguageInput"
|
||||||
language="sql"
|
onChange={[Function]}
|
||||||
lineNumbers="on"
|
onClick={[Function]}
|
||||||
onContentChanged={[Function]}
|
style={
|
||||||
onContentSelected={[Function]}
|
Object {
|
||||||
|
"lineHeight": 30,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
styles={
|
||||||
|
Object {
|
||||||
|
"root": Object {
|
||||||
|
"width": "95%",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value="Write a query to return all records in this table"
|
||||||
/>
|
/>
|
||||||
<QueryResultSection
|
<CustomizedIconButton
|
||||||
error=""
|
disabled={false}
|
||||||
executeQueryDocumentsPage={[Function]}
|
iconProps={
|
||||||
isExecuting={false}
|
Object {
|
||||||
isMongoDB={false}
|
"iconName": "Send",
|
||||||
queryEditorContent=""
|
}
|
||||||
|
}
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"marginLeft": 8,
|
||||||
|
}
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</t>
|
</Stack>
|
||||||
</Stack>
|
<Stack
|
||||||
<WelcomeModal
|
style={
|
||||||
visible={true}
|
Object {
|
||||||
/>
|
"marginBottom": 24,
|
||||||
<CopyPopup
|
"marginTop": 8,
|
||||||
setShowCopyPopup={[Function]}
|
}
|
||||||
showCopyPopup={false}
|
}
|
||||||
/>
|
>
|
||||||
|
<Text
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"fontSize": 12,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.
|
||||||
|
|
||||||
|
<StyledLinkBase
|
||||||
|
href=""
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Read preview terms
|
||||||
|
</StyledLinkBase>
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
className="tabPaneContentContainer"
|
||||||
|
>
|
||||||
|
<t
|
||||||
|
customClassName=""
|
||||||
|
onDragEnd={null}
|
||||||
|
onDragStart={null}
|
||||||
|
onSecondaryPaneSizeChange={null}
|
||||||
|
percentage={false}
|
||||||
|
primaryIndex={0}
|
||||||
|
primaryMinSize={100}
|
||||||
|
secondaryMinSize={200}
|
||||||
|
vertical={true}
|
||||||
|
>
|
||||||
|
<EditorReact
|
||||||
|
ariaLabel="Editing Query"
|
||||||
|
content=""
|
||||||
|
isReadOnly={false}
|
||||||
|
language="sql"
|
||||||
|
lineNumbers="on"
|
||||||
|
onContentChanged={[Function]}
|
||||||
|
onContentSelected={[Function]}
|
||||||
|
/>
|
||||||
|
<QueryResultSection
|
||||||
|
error=""
|
||||||
|
executeQueryDocumentsPage={[Function]}
|
||||||
|
isExecuting={false}
|
||||||
|
isMongoDB={false}
|
||||||
|
queryEditorContent=""
|
||||||
|
/>
|
||||||
|
</t>
|
||||||
|
</Stack>
|
||||||
|
<WelcomeModal
|
||||||
|
visible={true}
|
||||||
|
/>
|
||||||
|
<CopyPopup
|
||||||
|
setShowCopyPopup={[Function]}
|
||||||
|
showCopyPopup={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -581,7 +581,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
>
|
>
|
||||||
{item.title}
|
{item.title}
|
||||||
</Link>
|
</Link>
|
||||||
<Image src={LinkIcon} alt=" " />
|
<Image src={LinkIcon} alt={item.title} />
|
||||||
</Stack>
|
</Stack>
|
||||||
<Text>{item.description}</Text>
|
<Text>{item.description}</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -600,7 +600,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
<li key={`${item.title}${item.description}${index}`}>
|
<li key={`${item.title}${item.description}${index}`}>
|
||||||
<Stack style={{ marginBottom: 26 }}>
|
<Stack style={{ marginBottom: 26 }}>
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
<Image style={{ marginRight: 8 }} src={item.iconSrc} />
|
<Image style={{ marginRight: 8 }} src={item.iconSrc} alt={item.title} />
|
||||||
<Link style={{ fontSize: 14 }} onClick={item.onClick} title={item.info}>
|
<Link style={{ fontSize: 14 }} onClick={item.onClick} title={item.info}>
|
||||||
{item.title}
|
{item.title}
|
||||||
</Link>
|
</Link>
|
||||||
@@ -720,7 +720,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
>
|
>
|
||||||
{item.title}
|
{item.title}
|
||||||
</Link>
|
</Link>
|
||||||
<Image src={LinkIcon} />
|
<Image src={LinkIcon} alt={item.title} />
|
||||||
</Stack>
|
</Stack>
|
||||||
<Text>{item.description}</Text>
|
<Text>{item.description}</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { extractPartitionKey, ItemDefinition, PartitionKeyDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
import { extractPartitionKey, ItemDefinition, PartitionKeyDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||||
|
import { querySampleDocuments, readSampleDocument } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import Q from "q";
|
import Q from "q";
|
||||||
import DeleteDocumentIcon from "../../../images/DeleteDocument.svg";
|
import DeleteDocumentIcon from "../../../images/DeleteDocument.svg";
|
||||||
@@ -7,7 +8,12 @@ import NewDocumentIcon from "../../../images/NewDocument.svg";
|
|||||||
import SaveIcon from "../../../images/save-cosmos.svg";
|
import SaveIcon from "../../../images/save-cosmos.svg";
|
||||||
import UploadIcon from "../../../images/Upload_16x16.svg";
|
import UploadIcon from "../../../images/Upload_16x16.svg";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import { DocumentsGridMetrics, KeyCodes } from "../../Common/Constants";
|
import {
|
||||||
|
DocumentsGridMetrics,
|
||||||
|
KeyCodes,
|
||||||
|
QueryCopilotSampleContainerId,
|
||||||
|
QueryCopilotSampleDatabaseId,
|
||||||
|
} from "../../Common/Constants";
|
||||||
import { createDocument } from "../../Common/dataAccess/createDocument";
|
import { createDocument } from "../../Common/dataAccess/createDocument";
|
||||||
import { deleteDocument } from "../../Common/dataAccess/deleteDocument";
|
import { deleteDocument } from "../../Common/dataAccess/deleteDocument";
|
||||||
import { queryDocuments } from "../../Common/dataAccess/queryDocuments";
|
import { queryDocuments } from "../../Common/dataAccess/queryDocuments";
|
||||||
@@ -71,6 +77,7 @@ export default class DocumentsTab extends TabsBase {
|
|||||||
|
|
||||||
private _documentsIterator: QueryIterator<ItemDefinition & Resource>;
|
private _documentsIterator: QueryIterator<ItemDefinition & Resource>;
|
||||||
private _resourceTokenPartitionKey: string;
|
private _resourceTokenPartitionKey: string;
|
||||||
|
private _isQueryCopilotSampleContainer: boolean;
|
||||||
|
|
||||||
constructor(options: ViewModels.DocumentsTabOptions) {
|
constructor(options: ViewModels.DocumentsTabOptions) {
|
||||||
super(options);
|
super(options);
|
||||||
@@ -317,6 +324,9 @@ export default class DocumentsTab extends TabsBase {
|
|||||||
this.selectedDocumentContent.subscribe((newContent: string) => this._onEditorContentChange(newContent));
|
this.selectedDocumentContent.subscribe((newContent: string) => this._onEditorContentChange(newContent));
|
||||||
|
|
||||||
this.showPartitionKey = this._shouldShowPartitionKey();
|
this.showPartitionKey = this._shouldShowPartitionKey();
|
||||||
|
this._isQueryCopilotSampleContainer =
|
||||||
|
this.collection?.databaseId === QueryCopilotSampleDatabaseId &&
|
||||||
|
this.collection?.id() === QueryCopilotSampleContainerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _shouldShowPartitionKey(): boolean {
|
private _shouldShowPartitionKey(): boolean {
|
||||||
@@ -678,7 +688,6 @@ export default class DocumentsTab extends TabsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public createIterator(): QueryIterator<ItemDefinition & Resource> {
|
public createIterator(): QueryIterator<ItemDefinition & Resource> {
|
||||||
let filters = this.lastFilterContents();
|
|
||||||
const filter: string = this.filterContent().trim();
|
const filter: string = this.filterContent().trim();
|
||||||
const query: string = this.buildQuery(filter);
|
const query: string = this.buildQuery(filter);
|
||||||
let options: any = {};
|
let options: any = {};
|
||||||
@@ -688,12 +697,16 @@ export default class DocumentsTab extends TabsBase {
|
|||||||
options.partitionKey = this._resourceTokenPartitionKey;
|
options.partitionKey = this._resourceTokenPartitionKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
return queryDocuments(this.collection.databaseId, this.collection.id(), query, options);
|
return this._isQueryCopilotSampleContainer
|
||||||
|
? querySampleDocuments(query, options)
|
||||||
|
: queryDocuments(this.collection.databaseId, this.collection.id(), query, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async selectDocument(documentId: DocumentId): Promise<void> {
|
public async selectDocument(documentId: DocumentId): Promise<void> {
|
||||||
this.selectedDocumentId(documentId);
|
this.selectedDocumentId(documentId);
|
||||||
const content = await readDocument(this.collection, documentId);
|
const content = await (this._isQueryCopilotSampleContainer
|
||||||
|
? readSampleDocument(documentId)
|
||||||
|
: readDocument(this.collection, documentId));
|
||||||
this.initDocumentEditor(documentId, content);
|
this.initDocumentEditor(documentId, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -810,7 +823,7 @@ export default class DocumentsTab extends TabsBase {
|
|||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
hasPopup: false,
|
hasPopup: false,
|
||||||
disabled: !this.newDocumentButton.enabled(),
|
disabled: !this.newDocumentButton.enabled() || useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||||
id: "mongoNewDocumentBtn",
|
id: "mongoNewDocumentBtn",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -824,7 +837,8 @@ export default class DocumentsTab extends TabsBase {
|
|||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
hasPopup: false,
|
hasPopup: false,
|
||||||
disabled: !this.saveNewDocumentButton.enabled(),
|
disabled:
|
||||||
|
!this.saveNewDocumentButton.enabled() || useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -837,7 +851,9 @@ export default class DocumentsTab extends TabsBase {
|
|||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
hasPopup: false,
|
hasPopup: false,
|
||||||
disabled: !this.discardNewDocumentChangesButton.enabled(),
|
disabled:
|
||||||
|
!this.discardNewDocumentChangesButton.enabled() ||
|
||||||
|
useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -850,7 +866,8 @@ export default class DocumentsTab extends TabsBase {
|
|||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
hasPopup: false,
|
hasPopup: false,
|
||||||
disabled: !this.saveExistingDocumentButton.enabled(),
|
disabled:
|
||||||
|
!this.saveExistingDocumentButton.enabled() || useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -863,7 +880,9 @@ export default class DocumentsTab extends TabsBase {
|
|||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
hasPopup: false,
|
hasPopup: false,
|
||||||
disabled: !this.discardExisitingDocumentChangesButton.enabled(),
|
disabled:
|
||||||
|
!this.discardExisitingDocumentChangesButton.enabled() ||
|
||||||
|
useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -876,7 +895,9 @@ export default class DocumentsTab extends TabsBase {
|
|||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
hasPopup: false,
|
hasPopup: false,
|
||||||
disabled: !this.deleteExisitingDocumentButton.enabled(),
|
disabled:
|
||||||
|
!this.deleteExisitingDocumentButton.enabled() ||
|
||||||
|
useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -920,7 +941,9 @@ export default class DocumentsTab extends TabsBase {
|
|||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
hasPopup: true,
|
hasPopup: true,
|
||||||
disabled: useSelectedNode.getState().isDatabaseNodeOrNoneSelected(),
|
disabled:
|
||||||
|
useSelectedNode.getState().isDatabaseNodeOrNoneSelected() ||
|
||||||
|
useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
|
|||||||
function TabNav({ tab, active, tabKind }: { tab?: Tab; active: boolean; tabKind?: ReactTabKind }) {
|
function TabNav({ tab, active, tabKind }: { tab?: Tab; active: boolean; tabKind?: ReactTabKind }) {
|
||||||
const [hovering, setHovering] = useState(false);
|
const [hovering, setHovering] = useState(false);
|
||||||
const focusTab = useRef<HTMLLIElement>() as MutableRefObject<HTMLLIElement>;
|
const focusTab = useRef<HTMLLIElement>() as MutableRefObject<HTMLLIElement>;
|
||||||
const tabId = tab ? tab.tabId : "connect";
|
const tabId = tab ? tab.tabId : "";
|
||||||
|
|
||||||
const getReactTabTitle = (): ko.Observable<string> => {
|
const getReactTabTitle = (): ko.Observable<string> => {
|
||||||
if (tabKind === ReactTabKind.QueryCopilot) {
|
if (tabKind === ReactTabKind.QueryCopilot) {
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import * as ko from "knockout";
|
|||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { useTabs } from "../../hooks/useTabs";
|
|
||||||
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 { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
|
import { useTabs } from "../../hooks/useTabs";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import DocumentsTab from "../Tabs/DocumentsTab";
|
import DocumentsTab from "../Tabs/DocumentsTab";
|
||||||
import { NewQueryTab } from "../Tabs/QueryTab/QueryTab";
|
import { NewQueryTab } from "../Tabs/QueryTab/QueryTab";
|
||||||
@@ -28,8 +28,9 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
|||||||
public children: ko.ObservableArray<ViewModels.TreeNode>;
|
public children: ko.ObservableArray<ViewModels.TreeNode>;
|
||||||
public selectedSubnodeKind: ko.Observable<ViewModels.CollectionTabKind>;
|
public selectedSubnodeKind: ko.Observable<ViewModels.CollectionTabKind>;
|
||||||
public isCollectionExpanded: ko.Observable<boolean>;
|
public isCollectionExpanded: ko.Observable<boolean>;
|
||||||
|
public isSampleCollection?: boolean;
|
||||||
|
|
||||||
constructor(container: Explorer, databaseId: string, data: DataModels.Collection) {
|
constructor(container: Explorer, databaseId: string, data: DataModels.Collection, isSampleCollection?: boolean) {
|
||||||
this.nodeKind = "Collection";
|
this.nodeKind = "Collection";
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.databaseId = databaseId;
|
this.databaseId = databaseId;
|
||||||
@@ -42,6 +43,7 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
|||||||
this.children = ko.observableArray<ViewModels.TreeNode>([]);
|
this.children = ko.observableArray<ViewModels.TreeNode>([]);
|
||||||
this.selectedSubnodeKind = ko.observable<ViewModels.CollectionTabKind>();
|
this.selectedSubnodeKind = ko.observable<ViewModels.CollectionTabKind>();
|
||||||
this.isCollectionExpanded = ko.observable<boolean>(true);
|
this.isCollectionExpanded = ko.observable<boolean>(true);
|
||||||
|
this.isSampleCollection = isSampleCollection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public expandCollection(): void {
|
public expandCollection(): void {
|
||||||
@@ -139,7 +141,7 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
|||||||
|
|
||||||
documentsTab = new DocumentsTab({
|
documentsTab = new DocumentsTab({
|
||||||
partitionKey: this.partitionKey,
|
partitionKey: this.partitionKey,
|
||||||
resourceTokenPartitionKey: userContext.parsedResourceToken.partitionKey,
|
resourceTokenPartitionKey: userContext.parsedResourceToken?.partitionKey,
|
||||||
documentIds: ko.observableArray<DocumentId>([]),
|
documentIds: ko.observableArray<DocumentId>([]),
|
||||||
tabKind: ViewModels.CollectionTabKind.Documents,
|
tabKind: ViewModels.CollectionTabKind.Documents,
|
||||||
title: "Items",
|
title: "Items",
|
||||||
|
|||||||
@@ -790,14 +790,10 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
|
|||||||
{!isNotebookEnabled && isSampleDataEnabled && (
|
{!isNotebookEnabled && isSampleDataEnabled && (
|
||||||
<>
|
<>
|
||||||
<AccordionComponent>
|
<AccordionComponent>
|
||||||
<AccordionItemComponent
|
<AccordionItemComponent title={"MY DATA"} isExpanded={!gitHubNotebooksContentRoot}>
|
||||||
title={"MY DATA"}
|
|
||||||
isExpanded={!gitHubNotebooksContentRoot}
|
|
||||||
styles={{ maxHeight: 230 }}
|
|
||||||
>
|
|
||||||
<TreeComponent className="dataResourceTree" rootNode={dataRootNode} />
|
<TreeComponent className="dataResourceTree" rootNode={dataRootNode} />
|
||||||
</AccordionItemComponent>
|
</AccordionItemComponent>
|
||||||
<AccordionItemComponent title={"SAMPLE DATA"}>
|
<AccordionItemComponent title={"SAMPLE DATA"} containerStyles={{ display: "table" }}>
|
||||||
<SampleDataTree sampleDataResourceTokenCollection={sampleDataResourceTokenCollection} />
|
<SampleDataTree sampleDataResourceTokenCollection={sampleDataResourceTokenCollection} />
|
||||||
</AccordionItemComponent>
|
</AccordionItemComponent>
|
||||||
</AccordionComponent>
|
</AccordionComponent>
|
||||||
@@ -808,14 +804,10 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
|
|||||||
{isNotebookEnabled && isSampleDataEnabled && (
|
{isNotebookEnabled && isSampleDataEnabled && (
|
||||||
<>
|
<>
|
||||||
<AccordionComponent>
|
<AccordionComponent>
|
||||||
<AccordionItemComponent
|
<AccordionItemComponent title={"MY DATA"} isExpanded={!gitHubNotebooksContentRoot}>
|
||||||
title={"MY DATA"}
|
|
||||||
isExpanded={!gitHubNotebooksContentRoot}
|
|
||||||
styles={{ maxHeight: 130 }}
|
|
||||||
>
|
|
||||||
<TreeComponent className="dataResourceTree" rootNode={dataRootNode} />
|
<TreeComponent className="dataResourceTree" rootNode={dataRootNode} />
|
||||||
</AccordionItemComponent>
|
</AccordionItemComponent>
|
||||||
<AccordionItemComponent title={"SAMPLE DATA"}>
|
<AccordionItemComponent title={"SAMPLE DATA"} containerStyles={{ display: "table" }}>
|
||||||
<SampleDataTree sampleDataResourceTokenCollection={sampleDataResourceTokenCollection} />
|
<SampleDataTree sampleDataResourceTokenCollection={sampleDataResourceTokenCollection} />
|
||||||
</AccordionItemComponent>
|
</AccordionItemComponent>
|
||||||
<AccordionItemComponent title={"NOTEBOOKS"}>
|
<AccordionItemComponent title={"NOTEBOOKS"}>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdap
|
|||||||
import TabsBase from "Explorer/Tabs/TabsBase";
|
import TabsBase from "Explorer/Tabs/TabsBase";
|
||||||
import { useSelectedNode } from "Explorer/useSelectedNode";
|
import { useSelectedNode } from "Explorer/useSelectedNode";
|
||||||
import { useTabs } from "hooks/useTabs";
|
import { useTabs } from "hooks/useTabs";
|
||||||
import React, { useEffect, useState } from "react";
|
import React from "react";
|
||||||
import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg";
|
import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg";
|
||||||
import CollectionIcon from "../../../images/tree-collection.svg";
|
import CollectionIcon from "../../../images/tree-collection.svg";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
@@ -14,51 +14,64 @@ export const SampleDataTree = ({
|
|||||||
}: {
|
}: {
|
||||||
sampleDataResourceTokenCollection: ViewModels.CollectionBase;
|
sampleDataResourceTokenCollection: ViewModels.CollectionBase;
|
||||||
}): JSX.Element => {
|
}): JSX.Element => {
|
||||||
const [root, setRoot] = useState<TreeNode | undefined>(undefined);
|
const buildSampleDataTree = (): TreeNode => {
|
||||||
|
const updatedSampleTree: TreeNode = {
|
||||||
useEffect(() => {
|
label: sampleDataResourceTokenCollection.databaseId,
|
||||||
if (sampleDataResourceTokenCollection) {
|
isExpanded: true,
|
||||||
const updatedSampleTree: TreeNode = {
|
iconSrc: CosmosDBIcon,
|
||||||
label: sampleDataResourceTokenCollection.databaseId,
|
className: "databaseHeader",
|
||||||
isExpanded: false,
|
children: [
|
||||||
iconSrc: CosmosDBIcon,
|
{
|
||||||
className: "databaseHeader",
|
label: sampleDataResourceTokenCollection.id(),
|
||||||
children: [
|
iconSrc: CollectionIcon,
|
||||||
{
|
isExpanded: false,
|
||||||
label: sampleDataResourceTokenCollection.id(),
|
className: "collectionHeader",
|
||||||
iconSrc: CollectionIcon,
|
contextMenu: ResourceTreeContextMenuButtonFactory.createSampleCollectionContextMenuButton(),
|
||||||
isExpanded: false,
|
onClick: () => {
|
||||||
className: "dataResourceTree",
|
useSelectedNode.getState().setSelectedNode(sampleDataResourceTokenCollection);
|
||||||
contextMenu: ResourceTreeContextMenuButtonFactory.createSampleCollectionContextMenuButton(),
|
useCommandBar.getState().setContextButtons([]);
|
||||||
onClick: () => {
|
useTabs
|
||||||
// Rewritten version of expandCollapseCollection
|
.getState()
|
||||||
useSelectedNode.getState().setSelectedNode(sampleDataResourceTokenCollection);
|
.refreshActiveTab(
|
||||||
useCommandBar.getState().setContextButtons([]);
|
|
||||||
useTabs().refreshActiveTab(
|
|
||||||
(tab: TabsBase) =>
|
(tab: TabsBase) =>
|
||||||
tab.collection?.id() === sampleDataResourceTokenCollection.id() &&
|
tab.collection?.id() === sampleDataResourceTokenCollection.id() &&
|
||||||
tab.collection.databaseId === sampleDataResourceTokenCollection.databaseId
|
tab.collection.databaseId === sampleDataResourceTokenCollection.databaseId
|
||||||
);
|
);
|
||||||
},
|
|
||||||
isSelected: () =>
|
|
||||||
useSelectedNode
|
|
||||||
.getState()
|
|
||||||
.isDataNodeSelected(
|
|
||||||
sampleDataResourceTokenCollection.databaseId,
|
|
||||||
sampleDataResourceTokenCollection.id()
|
|
||||||
),
|
|
||||||
onContextMenuOpen: () => useSelectedNode.getState().setSelectedNode(sampleDataResourceTokenCollection),
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: "Items",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
isSelected: () =>
|
||||||
};
|
useSelectedNode
|
||||||
setRoot(updatedSampleTree);
|
.getState()
|
||||||
}
|
.isDataNodeSelected(sampleDataResourceTokenCollection.databaseId, sampleDataResourceTokenCollection.id()),
|
||||||
}, [sampleDataResourceTokenCollection]);
|
onContextMenuOpen: () => useSelectedNode.getState().setSelectedNode(sampleDataResourceTokenCollection),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: "Items",
|
||||||
|
onClick: () => sampleDataResourceTokenCollection.onDocumentDBDocumentsClick(),
|
||||||
|
isSelected: () =>
|
||||||
|
useSelectedNode
|
||||||
|
.getState()
|
||||||
|
.isDataNodeSelected(
|
||||||
|
sampleDataResourceTokenCollection.databaseId,
|
||||||
|
sampleDataResourceTokenCollection.id(),
|
||||||
|
[ViewModels.CollectionTabKind.Documents]
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
return <TreeComponent className="dataResourceTree" rootNode={root || { label: "Sample data not initialized." }} />;
|
return {
|
||||||
|
label: undefined,
|
||||||
|
isExpanded: true,
|
||||||
|
children: [updatedSampleTree],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TreeComponent
|
||||||
|
className="dataResourceTree"
|
||||||
|
rootNode={sampleDataResourceTokenCollection ? buildSampleDataTree() : { label: "Sample data not initialized." }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -66,11 +66,12 @@ export const useSelectedNode: UseStore<SelectedNodeState> = create((set, get) =>
|
|||||||
return useNotebook.getState().connectionInfo?.status === ConnectionStatusType.Connected;
|
return useNotebook.getState().connectionInfo?.status === ConnectionStatusType.Connected;
|
||||||
},
|
},
|
||||||
isQueryCopilotCollectionSelected: (): boolean => {
|
isQueryCopilotCollectionSelected: (): boolean => {
|
||||||
const selectedNode = get().selectedNode;
|
const selectedNode = get().selectedNode as ViewModels.CollectionBase;
|
||||||
if (
|
if (
|
||||||
selectedNode &&
|
selectedNode &&
|
||||||
|
selectedNode.isSampleCollection &&
|
||||||
selectedNode.id() === QueryCopilotSampleContainerId &&
|
selectedNode.id() === QueryCopilotSampleContainerId &&
|
||||||
(selectedNode as ViewModels.Collection)?.databaseId === QueryCopilotSampleDatabaseId
|
selectedNode.databaseId === QueryCopilotSampleDatabaseId
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import "../externals/jquery.typeahead.min.css";
|
|||||||
import "../externals/jquery.typeahead.min.js";
|
import "../externals/jquery.typeahead.min.js";
|
||||||
// Image Dependencies
|
// Image Dependencies
|
||||||
import { QueryCopilotCarousel } from "Explorer/QueryCopilot/CopilotCarousel";
|
import { QueryCopilotCarousel } from "Explorer/QueryCopilot/CopilotCarousel";
|
||||||
import { QueryCopilotFeedbackModal } from "Explorer/QueryCopilot/QueryCopilotFeedbackModal";
|
import { QueryCopilotFeedbackModal } from "Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
import "../images/CosmosDB_rgb_ui_lighttheme.ico";
|
import "../images/CosmosDB_rgb_ui_lighttheme.ico";
|
||||||
import hdeConnectImage from "../images/HdeConnectCosmosDB.svg";
|
import hdeConnectImage from "../images/HdeConnectCosmosDB.svg";
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
import { initializeIcons } from "@fluentui/react";
|
||||||
import { configure } from "enzyme";
|
import { configure } from "enzyme";
|
||||||
import Adapter from "enzyme-adapter-react-16";
|
import Adapter from "enzyme-adapter-react-16";
|
||||||
import "jest-canvas-mock";
|
import "jest-canvas-mock";
|
||||||
import { initializeIcons } from "@fluentui/react";
|
import enableHooks from "jest-react-hooks-shallow";
|
||||||
import { TextDecoder, TextEncoder } from "util";
|
import { TextDecoder, TextEncoder } from "util";
|
||||||
configure({ adapter: new Adapter() });
|
configure({ adapter: new Adapter() });
|
||||||
initializeIcons();
|
initializeIcons();
|
||||||
@@ -10,6 +11,30 @@ if (typeof window.URL.createObjectURL === "undefined") {
|
|||||||
Object.defineProperty(window.URL, "createObjectURL", { value: () => {} });
|
Object.defineProperty(window.URL, "createObjectURL", { value: () => {} });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enableHooks(jest, { dontMockByDefault: true });
|
||||||
|
|
||||||
|
const localStorageMock = (function () {
|
||||||
|
let store: { [key: string]: string } = {};
|
||||||
|
return {
|
||||||
|
getItem: function (key: string) {
|
||||||
|
return store[key] || null;
|
||||||
|
},
|
||||||
|
setItem: function (key: string, value: string) {
|
||||||
|
store[key] = value.toString();
|
||||||
|
},
|
||||||
|
removeItem: function (key: string) {
|
||||||
|
delete store[key];
|
||||||
|
},
|
||||||
|
clear: function () {
|
||||||
|
store = {};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
Object.defineProperty(window, "localStorage", {
|
||||||
|
value: localStorageMock,
|
||||||
|
});
|
||||||
|
|
||||||
// TODO Remove when jquery and documentdbclient SDK are removed
|
// TODO Remove when jquery and documentdbclient SDK are removed
|
||||||
(<any>window).$ = (<any>window).jQuery = require("jquery");
|
(<any>window).$ = (<any>window).jQuery = require("jquery");
|
||||||
(<any>global).$ = (<any>global).$.jQuery = require("jquery");
|
(<any>global).$ = (<any>global).$.jQuery = require("jquery");
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ test("Cassandra keyspace and table CRUD", async () => {
|
|||||||
await explorer.click('[data-test="New Table"]');
|
await explorer.click('[data-test="New Table"]');
|
||||||
await explorer.click('[aria-label="Keyspace id"]');
|
await explorer.click('[aria-label="Keyspace id"]');
|
||||||
await explorer.fill('[aria-label="Keyspace id"]', keyspaceId);
|
await explorer.fill('[aria-label="Keyspace id"]', keyspaceId);
|
||||||
await explorer.click('[aria-label="addCollection-tableId"]');
|
await explorer.click('[aria-label="addCollection-table Id Create table"]');
|
||||||
await explorer.fill('[aria-label="addCollection-tableId"]', tableId);
|
await explorer.fill('[aria-label="addCollection-table Id Create table"]', tableId);
|
||||||
await explorer.click("#sidePanelOkButton");
|
await explorer.click("#sidePanelOkButton");
|
||||||
await explorer.click(`.nodeItem >> text=${keyspaceId}`);
|
await explorer.click(`.nodeItem >> text=${keyspaceId}`);
|
||||||
await explorer.click(`[data-test="${tableId}"] [aria-label="More"]`);
|
await explorer.click(`[data-test="${tableId}"] [aria-label="More"]`);
|
||||||
|
|||||||
@@ -163,4 +163,4 @@
|
|||||||
"src/Terminal/**/*",
|
"src/Terminal/**/*",
|
||||||
"src/Utils/arm/**/*"
|
"src/Utils/arm/**/*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user