[Query Copilot v2] Implementing output bubble (#1587)

* Implementing output bubble

* Fix lint

* Run prettier
This commit is contained in:
v-darkora
2023-08-29 08:56:53 +02:00
committed by GitHub
parent f7370fd341
commit 6a8e87f45f
47 changed files with 1798 additions and 123 deletions

View File

@@ -13,8 +13,14 @@ export interface EditorReactProps {
ariaLabel: string; // Sets what will be read to the user to define the control
onContentSelected?: (selectedContent: string) => void; // Called when text is selected
onContentChanged?: (newContent: string) => void; // Called when text is changed
lineNumbers?: monaco.editor.IEditorOptions["lineNumbers"];
theme?: string; // Monaco editor theme
wordWrap?: monaco.editor.IEditorOptions["wordWrap"];
lineNumbers?: monaco.editor.IEditorOptions["lineNumbers"];
lineNumbersMinChars?: monaco.editor.IEditorOptions["lineNumbersMinChars"];
lineDecorationsWidth?: monaco.editor.IEditorOptions["lineDecorationsWidth"];
minimap?: monaco.editor.IEditorOptions["minimap"];
scrollBeyondLastLine?: monaco.editor.IEditorOptions["scrollBeyondLastLine"];
monacoContainerStyles?: React.CSSProperties;
}
export class EditorReact extends React.Component<EditorReactProps, EditorReactStates> {
@@ -54,7 +60,11 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
return (
<React.Fragment>
{!this.state.showEditor && <Spinner size={SpinnerSize.large} className="spinner" />}
<div className="jsonEditor" ref={(elt: HTMLElement) => this.setRef(elt)} />
<div
className="jsonEditor"
style={this.props.monacoContainerStyles}
ref={(elt: HTMLElement) => this.setRef(elt)}
/>
</React.Fragment>
);
}
@@ -84,14 +94,19 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
*/
private async createEditor(createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) {
const options: monaco.editor.IEditorConstructionOptions = {
value: this.props.content,
language: this.props.language,
value: this.props.content,
readOnly: this.props.isReadOnly,
lineNumbers: this.props.lineNumbers || "off",
fontSize: 12,
ariaLabel: this.props.ariaLabel,
theme: this.props.theme,
fontSize: 12,
automaticLayout: true,
theme: this.props.theme,
wordWrap: this.props.wordWrap || "off",
lineNumbers: this.props.lineNumbers || "off",
lineNumbersMinChars: this.props.lineNumbersMinChars,
lineDecorationsWidth: this.props.lineDecorationsWidth,
minimap: this.props.minimap,
scrollBeyondLastLine: this.props.scrollBeyondLastLine,
};
this.rootNode.innerHTML = "";

View File

@@ -55,7 +55,7 @@ exports[`TreeNodeComponent does not render children by default 1`] = `
className="expandCollapseIcon"
onKeyPress={[Function]}
role="button"
src=""
src={Object {}}
tabIndex={0}
/>
<span
@@ -158,7 +158,7 @@ exports[`TreeNodeComponent renders a simple node (sorted children, expanded) 1`]
className="expandCollapseIcon"
onKeyPress={[Function]}
role="button"
src=""
src={Object {}}
tabIndex={0}
/>
<span
@@ -309,7 +309,7 @@ exports[`TreeNodeComponent renders loading icon 1`] = `
className="expandCollapseIcon"
onKeyPress={[Function]}
role="button"
src=""
src={Object {}}
tabIndex={0}
/>
<span
@@ -383,7 +383,7 @@ exports[`TreeNodeComponent renders sorted children, expanded, leaves and parents
className="expandCollapseIcon"
onKeyPress={[Function]}
role="button"
src=""
src={Object {}}
tabIndex={0}
/>
<span
@@ -553,7 +553,7 @@ exports[`TreeNodeComponent renders unsorted children by default 1`] = `
className="expandCollapseIcon"
onKeyPress={[Function]}
role="button"
src=""
src={Object {}}
tabIndex={0}
/>
<span

View File

@@ -128,7 +128,7 @@ exports[`<EditorNodePropertiesComponent /> renders component 1`] = `
>
<img
alt="Delete"
src=""
src={Object {}}
/>
</AccessibleElement>
</td>
@@ -185,7 +185,7 @@ exports[`<EditorNodePropertiesComponent /> renders component 1`] = `
>
<img
alt="Delete"
src=""
src={Object {}}
/>
</AccessibleElement>
</td>
@@ -203,7 +203,7 @@ exports[`<EditorNodePropertiesComponent /> renders component 1`] = `
>
<img
alt="Add"
src=""
src={Object {}}
/>
Add Property
</AccessibleElement>
@@ -317,7 +317,7 @@ exports[`<EditorNodePropertiesComponent /> renders proper unicode 1`] = `
>
<img
alt="Delete"
src=""
src={Object {}}
/>
</AccessibleElement>
</td>
@@ -379,7 +379,7 @@ exports[`<EditorNodePropertiesComponent /> renders proper unicode 1`] = `
>
<img
alt="Delete"
src=""
src={Object {}}
/>
</AccessibleElement>
</td>
@@ -397,7 +397,7 @@ exports[`<EditorNodePropertiesComponent /> renders proper unicode 1`] = `
>
<img
alt="Add"
src=""
src={Object {}}
/>
Add Property
</AccessibleElement>

View File

@@ -22,7 +22,7 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
>
<img
alt="in progress items"
src=""
src={Object {}}
/>
<span
className="numInProgress"
@@ -35,7 +35,7 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
>
<img
alt="error items"
src=""
src={Object {}}
/>
<span
className="numErroredItems"
@@ -48,7 +48,7 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
>
<img
alt="info items"
src=""
src={Object {}}
/>
<span
className="numInfoItems"
@@ -150,7 +150,7 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
>
<img
alt="clear notifications image"
src=""
src={Object {}}
/>
Clear Notifications
</span>
@@ -185,7 +185,7 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
>
<img
alt="in progress items"
src=""
src={Object {}}
/>
<span
className="numInProgress"
@@ -198,7 +198,7 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
>
<img
alt="error items"
src=""
src={Object {}}
/>
<span
className="numErroredItems"
@@ -211,7 +211,7 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
>
<img
alt="info items"
src=""
src={Object {}}
/>
<span
className="numInfoItems"
@@ -315,7 +315,7 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
>
<img
alt="clear notifications image"
src=""
src={Object {}}
/>
Clear Notifications
</span>
@@ -330,7 +330,7 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
<img
alt="info"
className="infoIcon"
src=""
src={Object {}}
/>
<span
className="date"

View File

@@ -4340,7 +4340,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
id="deleteparam"
onClick={[Function]}
role="button"
src=""
src={Object {}}
width={20}
>
<ImageBase
@@ -4350,7 +4350,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
id="deleteparam"
onClick={[Function]}
role="button"
src=""
src={Object {}}
styles={[Function]}
theme={
Object {
@@ -4640,12 +4640,12 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
alt="Delete param"
className="ms-Image-image ms-Image-image--portrait is-notLoaded is-fadeIn image-87"
id="deleteparam"
key="fabricImage"
key="fabricImage[object Object]"
onClick={[Function]}
onError={[Function]}
onLoad={[Function]}
role="button"
src=""
src={Object {}}
/>
</div>
</ImageBase>
@@ -4661,7 +4661,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
id="addparam"
onClick={[Function]}
role="button"
src=""
src={Object {}}
width={20}
>
<ImageBase
@@ -4671,7 +4671,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
id="addparam"
onClick={[Function]}
role="button"
src=""
src={Object {}}
styles={[Function]}
theme={
Object {
@@ -4961,12 +4961,12 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
alt="Add param"
className="ms-Image-image ms-Image-image--portrait is-notLoaded is-fadeIn image-87"
id="addparam"
key="fabricImage"
key="fabricImage[object Object]"
onClick={[Function]}
onError={[Function]}
onLoad={[Function]}
role="button"
src=""
src={Object {}}
/>
</div>
</ImageBase>
@@ -4989,13 +4989,13 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
alt="Add param"
height={30}
key=".0:$.0"
src=""
src={Object {}}
width={20}
>
<ImageBase
alt="Add param"
height={30}
src=""
src={Object {}}
styles={[Function]}
theme={
Object {
@@ -5284,10 +5284,10 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
<img
alt="Add param"
className="ms-Image-image ms-Image-image--portrait is-notLoaded is-fadeIn image-87"
key="fabricImage"
key="fabricImage[object Object]"
onError={[Function]}
onLoad={[Function]}
src=""
src={Object {}}
/>
</div>
</ImageBase>

View File

@@ -39,7 +39,7 @@ exports[`Load Query Pane should render Default properly 1`] = `
className="fileIcon"
height={20}
imageFit={4}
src=""
src={Object {}}
width={20}
/>
<input

View File

@@ -44,13 +44,13 @@ exports[`Excute Add Table Entity Pane should render Default properly 1`] = `
alt="Add Property"
height={30}
key=".0:$.0"
src=""
src={Object {}}
width={16}
>
<ImageBase
alt="Add Property"
height={30}
src=""
src={Object {}}
styles={[Function]}
theme={
Object {
@@ -339,10 +339,10 @@ exports[`Excute Add Table Entity Pane should render Default properly 1`] = `
<img
alt="Add Property"
className="ms-Image-image ms-Image-image--portrait is-notLoaded is-fadeIn image-55"
key="fabricImage"
key="fabricImage[object Object]"
onError={[Function]}
onLoad={[Function]}
src=""
src={Object {}}
/>
</div>
</ImageBase>

View File

@@ -39,13 +39,13 @@ exports[`Excute Edit Table Entity Pane should render Default properly 1`] = `
alt="Add Entity"
height={30}
key=".0:$.0"
src=""
src={Object {}}
width={16}
>
<ImageBase
alt="Add Entity"
height={30}
src=""
src={Object {}}
styles={[Function]}
theme={
Object {
@@ -334,10 +334,10 @@ exports[`Excute Edit Table Entity Pane should render Default properly 1`] = `
<img
alt="Add Entity"
className="ms-Image-image ms-Image-image--portrait is-notLoaded is-fadeIn image-55"
key="fabricImage"
key="fabricImage[object Object]"
onError={[Function]}
onLoad={[Function]}
src=""
src={Object {}}
/>
</div>
</ImageBase>

View File

@@ -20,7 +20,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
>
<StackItem>
<Image
src=""
src={Object {}}
/>
</StackItem>
</Stack>
@@ -84,7 +84,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
className="imageTextPadding"
>
<Image
src=""
src={Object {}}
/>
</StackItem>
<StackItem
@@ -120,7 +120,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
className="imageTextPadding"
>
<Image
src=""
src={Object {}}
/>
</StackItem>
<StackItem
@@ -156,7 +156,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
className="imageTextPadding"
>
<Image
src=""
src={Object {}}
/>
</StackItem>
<StackItem

View File

@@ -40,7 +40,7 @@ exports[`Copy Popup snapshot test should render when showCopyPopup is true 1`] =
verticalAlign="center"
>
<Image
src=""
src={Object {}}
style={
Object {
"height": 15,

View File

@@ -87,7 +87,7 @@ exports[`Sample Prompts snapshot test should render properly if isSamplePromptsO
verticalAlign="center"
>
<Image
src=""
src={Object {}}
style={
Object {
"height": 25,
@@ -191,7 +191,7 @@ exports[`Sample Prompts snapshot test should render properly if isSamplePromptsO
verticalAlign="center"
>
<Image
src=""
src={Object {}}
style={
Object {
"height": 25,
@@ -295,7 +295,7 @@ exports[`Sample Prompts snapshot test should render properly if isSamplePromptsO
verticalAlign="center"
>
<Image
src=""
src={Object {}}
style={
Object {
"height": 25,
@@ -477,7 +477,7 @@ exports[`Sample Prompts snapshot test should render properly if isSamplePromptsO
verticalAlign="center"
>
<Image
src=""
src={Object {}}
style={
Object {
"height": 25,
@@ -581,7 +581,7 @@ exports[`Sample Prompts snapshot test should render properly if isSamplePromptsO
verticalAlign="center"
>
<Image
src=""
src={Object {}}
style={
Object {
"height": 25,
@@ -685,7 +685,7 @@ exports[`Sample Prompts snapshot test should render properly if isSamplePromptsO
verticalAlign="center"
>
<Image
src=""
src={Object {}}
style={
Object {
"height": 25,

View File

@@ -0,0 +1,21 @@
import { IconButton } from "@fluentui/react";
import { CopyButton } from "Explorer/QueryCopilot/V2/Bubbles/Output/Buttons/Copy/CopyButton";
import { shallow } from "enzyme";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import React from "react";
document.execCommand = jest.fn();
describe("Copy button snapshot tests", () => {
it("should render and click copy", async () => {
const testInput = "test input query";
useQueryCopilot.getState().setGeneratedQuery(testInput);
const wrapper = shallow(<CopyButton />);
const button = wrapper.find(IconButton).first();
button.simulate("click", {});
expect(document.execCommand).toHaveBeenCalledWith("copy");
expect(wrapper).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,23 @@
import { IconButton } from "@fluentui/react";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import React from "react";
import CopilotCopy from "../../../../../../../../images/CopilotCopy.svg";
export const CopyButton: React.FC = (): JSX.Element => {
const copyGeneratedCode = (): void => {
const queryElement = document.createElement("textarea");
queryElement.value = useQueryCopilot.getState().generatedQuery;
document.body.appendChild(queryElement);
queryElement.select();
document.execCommand("copy");
document.body.removeChild(queryElement);
};
return (
<IconButton
iconProps={{ imageProps: { src: CopilotCopy } }}
ariaLabel="Copy"
onClick={copyGeneratedCode}
></IconButton>
);
};

View File

@@ -0,0 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Copy button snapshot tests should render and click copy 1`] = `
<CustomizedIconButton
ariaLabel="Copy"
iconProps={
Object {
"imageProps": Object {
"src": Object {},
},
}
}
onClick={[Function]}
/>
`;

View File

@@ -0,0 +1,205 @@
import { Callout, IconButton, Link } from "@fluentui/react";
import { useId } from "@fluentui/react-hooks";
import { FeedbackButtons } from "Explorer/QueryCopilot/V2/Bubbles/Output/Buttons/Feedback/FeedbackButtons";
import { shallow } from "enzyme";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import React from "react";
import LikeHover from "../../../../../../../../images/CopilotLikeHover.svg";
import LikePressed from "../../../../../../../../images/CopilotLikePressed.svg";
import LikeRest from "../../../../../../../../images/CopilotLikeRest.svg";
useId as jest.Mock;
jest.mock("../../../../../../../../images/CopilotLikeHover.svg", () => "LikeHover");
jest.mock("../../../../../../../../images/CopilotLikePressed.svg", () => "LikePressed");
jest.mock("../../../../../../../../images/CopilotLikeRest.svg", () => "LikeRest");
beforeEach(() => {
jest.resetAllMocks();
});
describe("Feedback buttons snapshot tests", () => {
it("should click like and show callout", () => {
const wrapper = shallow(<FeedbackButtons />);
let likeButton = wrapper.find(IconButton).first();
const dislikeButton = wrapper.find(IconButton).last();
likeButton.simulate("click");
likeButton = wrapper.find(IconButton).first();
const callout = wrapper.find(Callout).first();
expect(likeButton.props().iconProps.imageProps.src).toEqual(LikePressed);
expect(dislikeButton.props().iconProps.imageProps.src).toEqual(LikeRest);
expect(callout.exists()).toBeTruthy();
expect(wrapper).toMatchSnapshot();
});
it("should click like and dismiss callout", () => {
const wrapper = shallow(<FeedbackButtons />);
const likeButton = wrapper.find(IconButton).first();
likeButton.simulate("click");
let callout = wrapper.find(Callout).first();
callout.simulate("dismiss");
callout = wrapper.find(Callout).first();
expect(callout.exists()).toBeFalsy();
expect(wrapper).toMatchSnapshot();
});
it("should click like and submit feedback", () => {
const spy = jest.spyOn(useQueryCopilot.getState(), "openFeedbackModal");
const wrapper = shallow(<FeedbackButtons />);
const likeButton = wrapper.find(IconButton).first();
likeButton.simulate("click");
const link = wrapper.find(Link).first();
link.simulate("click");
expect(spy).toHaveBeenNthCalledWith(1, "", true, "");
expect(wrapper).toMatchSnapshot();
});
it("should hover over like", () => {
const wrapper = shallow(<FeedbackButtons />);
let likeButton = wrapper.find(IconButton).first();
likeButton.simulate("mouseover");
likeButton = wrapper.find(IconButton).first();
expect(likeButton.props().iconProps.imageProps.src).toEqual(LikeHover);
expect(wrapper).toMatchSnapshot();
});
it("should hover over rest like and leave", () => {
const wrapper = shallow(<FeedbackButtons />);
let likeButton = wrapper.find(IconButton).first();
likeButton.simulate("mouseover");
likeButton.simulate("mouseleave");
likeButton = wrapper.find(IconButton).first();
expect(likeButton.props().iconProps.imageProps.src).toEqual(LikeRest);
expect(wrapper).toMatchSnapshot();
});
it("should hover over pressed like and leave", () => {
const wrapper = shallow(<FeedbackButtons />);
let likeButton = wrapper.find(IconButton).first();
likeButton.simulate("click");
likeButton = wrapper.find(IconButton).first();
likeButton.simulate("mouseover");
likeButton.simulate("mouseleave");
expect(likeButton.props().iconProps.imageProps.src).toEqual(LikePressed);
expect(wrapper).toMatchSnapshot();
});
it("should hover over like and click", () => {
const wrapper = shallow(<FeedbackButtons />);
let likeButton = wrapper.find(IconButton).first();
likeButton.simulate("mouseover");
likeButton.simulate("click");
likeButton = wrapper.find(IconButton).first();
expect(likeButton.props().iconProps.imageProps.src).toEqual(LikePressed);
expect(wrapper).toMatchSnapshot();
});
it("should dobule click on like", () => {
const wrapper = shallow(<FeedbackButtons />);
let likeButton = wrapper.find(IconButton).first();
likeButton.simulate("click");
likeButton = wrapper.find(IconButton).first();
expect(likeButton.props().iconProps.imageProps.src).toEqual(LikePressed);
likeButton.simulate("click");
likeButton = wrapper.find(IconButton).first();
expect(likeButton.props().iconProps.imageProps.src).toEqual(LikeRest);
expect(wrapper).toMatchSnapshot();
});
it("should click dislike and show popup", () => {
const spy = jest.spyOn(useQueryCopilot.getState(), "openFeedbackModal");
const wrapper = shallow(<FeedbackButtons />);
const likeButton = wrapper.find(IconButton).first();
let dislikeButton = wrapper.find(IconButton).last();
dislikeButton.simulate("click");
const callout = wrapper.find(Callout).first();
dislikeButton = wrapper.find(IconButton).last();
expect(likeButton.props().iconProps.imageProps.src).toEqual(LikeRest);
expect(dislikeButton.props().iconProps.imageProps.src).toEqual(LikePressed);
expect(spy).toHaveBeenNthCalledWith(1, "", false, "");
expect(callout.exists()).toBeFalsy();
expect(wrapper).toMatchSnapshot();
});
it("should hover over dislike", () => {
const wrapper = shallow(<FeedbackButtons />);
let dislikeButton = wrapper.find(IconButton).last();
dislikeButton.simulate("mouseover");
dislikeButton = wrapper.find(IconButton).last();
expect(dislikeButton.props().iconProps.imageProps.src).toEqual(LikeHover);
expect(wrapper).toMatchSnapshot();
});
it("should hover over rest dislike and leave", () => {
const wrapper = shallow(<FeedbackButtons />);
let dislikeButton = wrapper.find(IconButton).last();
dislikeButton.simulate("mouseover");
dislikeButton.simulate("mouseleave");
dislikeButton = wrapper.find(IconButton).last();
expect(dislikeButton.props().iconProps.imageProps.src).toEqual(LikeRest);
expect(wrapper).toMatchSnapshot();
});
it("should hover over pressed dislike and leave", () => {
const wrapper = shallow(<FeedbackButtons />);
let dislikeButton = wrapper.find(IconButton).last();
dislikeButton.simulate("click");
dislikeButton = wrapper.find(IconButton).last();
expect(dislikeButton.props().iconProps.imageProps.src).toEqual(LikePressed);
dislikeButton.simulate("mouseover");
dislikeButton.simulate("mouseleave");
dislikeButton = wrapper.find(IconButton).last();
expect(dislikeButton.props().iconProps.imageProps.src).toEqual(LikePressed);
expect(wrapper).toMatchSnapshot();
});
it("should hover over dislike and click", () => {
const wrapper = shallow(<FeedbackButtons />);
let dislikeButton = wrapper.find(IconButton).last();
dislikeButton.simulate("mouseover");
dislikeButton.simulate("click");
dislikeButton = wrapper.find(IconButton).last();
expect(dislikeButton.props().iconProps.imageProps.src).toEqual(LikePressed);
expect(wrapper).toMatchSnapshot();
});
it("should dobule click on dislike", () => {
const wrapper = shallow(<FeedbackButtons />);
let dislikeButton = wrapper.find(IconButton).last();
dislikeButton.simulate("click");
dislikeButton = wrapper.find(IconButton).last();
expect(dislikeButton.props().iconProps.imageProps.src).toEqual(LikePressed);
dislikeButton.simulate("click");
dislikeButton = wrapper.find(IconButton).last();
expect(dislikeButton.props().iconProps.imageProps.src).toEqual(LikeRest);
expect(wrapper).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,93 @@
import { Callout, DirectionalHint, IconButton, Link, Stack, Text } from "@fluentui/react";
import { useId } from "@fluentui/react-hooks";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import React, { useState } from "react";
import LikeHover from "../../../../../../../../images/CopilotLikeHover.svg";
import LikePressed from "../../../../../../../../images/CopilotLikePressed.svg";
import LikeRest from "../../../../../../../../images/CopilotLikeRest.svg";
export const FeedbackButtons: React.FC = (): JSX.Element => {
const { generatedQuery, userPrompt } = useQueryCopilot();
const [likeQuery, setLikeQuery] = useState<boolean>(false);
const [dislikeQuery, setDislikeQuery] = useState<boolean>(false);
const [likeImageLink, setLikeImageLink] = useState<string>(LikeRest);
const [dislikeImageLink, setDislikeImageLink] = useState<string>(LikeRest);
const [calloutVisible, setCalloutVisible] = useState<boolean>(false);
const likeBtnId = useId("likeBtn");
const dislikeBtnId = useId("dislikeBtn");
return (
<Stack horizontal>
{calloutVisible && (
<Callout
target={`#${likeBtnId}`}
onDismiss={() => setCalloutVisible(false)}
directionalHint={DirectionalHint.topCenter}
role="dialog"
style={{ padding: "5px 12px 5px 12px", borderRadius: "4px" }}
styles={{ beakCurtain: { borderRadius: "4px" }, root: { borderRadius: "4px" } }}
>
<Text>
{" "}
<Text>
Thank you. Need to give{" "}
<Link
onClick={() => {
setCalloutVisible(false);
useQueryCopilot.getState().openFeedbackModal(generatedQuery, true, userPrompt);
}}
>
more feedback?
</Link>
</Text>
</Text>
</Callout>
)}
<IconButton
id={likeBtnId}
iconProps={{
imageProps: { src: likeImageLink },
style: { minHeight: "18px" },
}}
onClick={() => {
if (likeQuery) {
setLikeQuery(false);
setLikeImageLink(LikeRest);
setCalloutVisible(false);
} else {
setLikeQuery(true);
setDislikeQuery(false);
setLikeImageLink(LikePressed);
setDislikeImageLink(LikeRest);
setCalloutVisible(true);
}
}}
onMouseOver={() => setLikeImageLink(LikeHover)}
onMouseLeave={() => setLikeImageLink(likeQuery ? LikePressed : LikeRest)}
/>
<IconButton
id={dislikeBtnId}
iconProps={{
imageProps: { src: dislikeImageLink },
style: { minHeight: "18px", transform: "rotate(180deg)" },
}}
onClick={() => {
if (dislikeQuery) {
setDislikeQuery(false);
setDislikeImageLink(LikeRest);
} else {
setDislikeQuery(true);
setLikeQuery(false);
setDislikeImageLink(LikePressed);
setLikeImageLink(LikeRest);
useQueryCopilot.getState().openFeedbackModal(generatedQuery, false, userPrompt);
}
}}
onMouseOver={() => setDislikeImageLink(LikeHover)}
onMouseLeave={() => setDislikeImageLink(dislikeQuery ? LikePressed : LikeRest)}
/>
</Stack>
);
};

View File

@@ -0,0 +1,666 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Feedback buttons snapshot tests should click dislike and show popup 1`] = `
<Stack
horizontal={true}
>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
},
}
}
id="likeBtn16"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikePressed",
},
"style": Object {
"minHeight": "18px",
"transform": "rotate(180deg)",
},
}
}
id="dislikeBtn17"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
</Stack>
`;
exports[`Feedback buttons snapshot tests should click like and dismiss callout 1`] = `
<Stack
horizontal={true}
>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikePressed",
},
"style": Object {
"minHeight": "18px",
},
}
}
id="likeBtn2"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
"transform": "rotate(180deg)",
},
}
}
id="dislikeBtn3"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
</Stack>
`;
exports[`Feedback buttons snapshot tests should click like and show callout 1`] = `
<Stack
horizontal={true}
>
<Callout
directionalHint={1}
onDismiss={[Function]}
role="dialog"
style={
Object {
"borderRadius": "4px",
"padding": "5px 12px 5px 12px",
}
}
styles={
Object {
"beakCurtain": Object {
"borderRadius": "4px",
},
"root": Object {
"borderRadius": "4px",
},
}
}
target="#likeBtn0"
>
<Text>
<Text>
Thank you. Need to give
<StyledLinkBase
onClick={[Function]}
>
more feedback?
</StyledLinkBase>
</Text>
</Text>
</Callout>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikePressed",
},
"style": Object {
"minHeight": "18px",
},
}
}
id="likeBtn0"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
"transform": "rotate(180deg)",
},
}
}
id="dislikeBtn1"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
</Stack>
`;
exports[`Feedback buttons snapshot tests should click like and submit feedback 1`] = `
<Stack
horizontal={true}
>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikePressed",
},
"style": Object {
"minHeight": "18px",
},
}
}
id="likeBtn4"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
"transform": "rotate(180deg)",
},
}
}
id="dislikeBtn5"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
</Stack>
`;
exports[`Feedback buttons snapshot tests should dobule click on dislike 1`] = `
<Stack
horizontal={true}
>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
},
}
}
id="likeBtn26"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
"transform": "rotate(180deg)",
},
}
}
id="dislikeBtn27"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
</Stack>
`;
exports[`Feedback buttons snapshot tests should dobule click on like 1`] = `
<Stack
horizontal={true}
>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
},
}
}
id="likeBtn14"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
"transform": "rotate(180deg)",
},
}
}
id="dislikeBtn15"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
</Stack>
`;
exports[`Feedback buttons snapshot tests should hover over dislike 1`] = `
<Stack
horizontal={true}
>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
},
}
}
id="likeBtn18"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeHover",
},
"style": Object {
"minHeight": "18px",
"transform": "rotate(180deg)",
},
}
}
id="dislikeBtn19"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
</Stack>
`;
exports[`Feedback buttons snapshot tests should hover over dislike and click 1`] = `
<Stack
horizontal={true}
>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
},
}
}
id="likeBtn24"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikePressed",
},
"style": Object {
"minHeight": "18px",
"transform": "rotate(180deg)",
},
}
}
id="dislikeBtn25"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
</Stack>
`;
exports[`Feedback buttons snapshot tests should hover over like 1`] = `
<Stack
horizontal={true}
>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeHover",
},
"style": Object {
"minHeight": "18px",
},
}
}
id="likeBtn6"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
"transform": "rotate(180deg)",
},
}
}
id="dislikeBtn7"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
</Stack>
`;
exports[`Feedback buttons snapshot tests should hover over like and click 1`] = `
<Stack
horizontal={true}
>
<Callout
directionalHint={1}
onDismiss={[Function]}
role="dialog"
style={
Object {
"borderRadius": "4px",
"padding": "5px 12px 5px 12px",
}
}
styles={
Object {
"beakCurtain": Object {
"borderRadius": "4px",
},
"root": Object {
"borderRadius": "4px",
},
}
}
target="#likeBtn12"
>
<Text>
<Text>
Thank you. Need to give
<StyledLinkBase
onClick={[Function]}
>
more feedback?
</StyledLinkBase>
</Text>
</Text>
</Callout>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikePressed",
},
"style": Object {
"minHeight": "18px",
},
}
}
id="likeBtn12"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
"transform": "rotate(180deg)",
},
}
}
id="dislikeBtn13"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
</Stack>
`;
exports[`Feedback buttons snapshot tests should hover over pressed dislike and leave 1`] = `
<Stack
horizontal={true}
>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
},
}
}
id="likeBtn22"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikePressed",
},
"style": Object {
"minHeight": "18px",
"transform": "rotate(180deg)",
},
}
}
id="dislikeBtn23"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
</Stack>
`;
exports[`Feedback buttons snapshot tests should hover over pressed like and leave 1`] = `
<Stack
horizontal={true}
>
<Callout
directionalHint={1}
onDismiss={[Function]}
role="dialog"
style={
Object {
"borderRadius": "4px",
"padding": "5px 12px 5px 12px",
}
}
styles={
Object {
"beakCurtain": Object {
"borderRadius": "4px",
},
"root": Object {
"borderRadius": "4px",
},
}
}
target="#likeBtn10"
>
<Text>
<Text>
Thank you. Need to give
<StyledLinkBase
onClick={[Function]}
>
more feedback?
</StyledLinkBase>
</Text>
</Text>
</Callout>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikePressed",
},
"style": Object {
"minHeight": "18px",
},
}
}
id="likeBtn10"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
"transform": "rotate(180deg)",
},
}
}
id="dislikeBtn11"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
</Stack>
`;
exports[`Feedback buttons snapshot tests should hover over rest dislike and leave 1`] = `
<Stack
horizontal={true}
>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
},
}
}
id="likeBtn20"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
"transform": "rotate(180deg)",
},
}
}
id="dislikeBtn21"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
</Stack>
`;
exports[`Feedback buttons snapshot tests should hover over rest like and leave 1`] = `
<Stack
horizontal={true}
>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
},
}
}
id="likeBtn8"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
<CustomizedIconButton
iconProps={
Object {
"imageProps": Object {
"src": "LikeRest",
},
"style": Object {
"minHeight": "18px",
"transform": "rotate(180deg)",
},
}
}
id="dislikeBtn9"
onClick={[Function]}
onMouseLeave={[Function]}
onMouseOver={[Function]}
/>
</Stack>
`;

View File

@@ -0,0 +1,19 @@
import { ActionButton } from "@fluentui/react";
import { InsertButton } from "Explorer/QueryCopilot/V2/Bubbles/Output/Buttons/Insert/InsertButton";
import { shallow } from "enzyme";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import React from "react";
describe("Insert button snapshot tests", () => {
it("should click and update state", () => {
const testQuery = "test query";
useQueryCopilot.getState().setGeneratedQuery(testQuery);
const wrapper = shallow(<InsertButton />);
const button = wrapper.find(ActionButton).first();
button.simulate("click");
expect(useQueryCopilot.getState().query).toEqual(testQuery);
expect(wrapper).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,16 @@
import { ActionButton } from "@fluentui/react";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import React from "react";
import CopilotInsert from "../../../../../../../../images/CopilotInsert.svg";
export const InsertButton: React.FC = (): JSX.Element => {
return (
<ActionButton
iconProps={{ imageProps: { src: CopilotInsert } }}
style={{ borderRadius: "4px", borderWidth: "1px", borderColor: "#D1D1D1", height: "24px", paddingBottom: "2px" }}
onClick={() => useQueryCopilot.getState().setQuery(useQueryCopilot.getState().generatedQuery)}
>
Insert
</ActionButton>
);
};

View File

@@ -0,0 +1,25 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Insert button snapshot tests should click and update state 1`] = `
<CustomizedActionButton
iconProps={
Object {
"imageProps": Object {
"src": Object {},
},
}
}
onClick={[Function]}
style={
Object {
"borderColor": "#D1D1D1",
"borderRadius": "4px",
"borderWidth": "1px",
"height": "24px",
"paddingBottom": "2px",
}
}
>
Insert
</CustomizedActionButton>
`;

View File

@@ -0,0 +1,11 @@
import { MoreButton } from "Explorer/QueryCopilot/V2/Bubbles/Output/Buttons/More/MoreButton";
import { shallow } from "enzyme";
import React from "react";
describe("More button snapshot tests", () => {
it("should render", () => {
const wrapper = shallow(<MoreButton />);
expect(wrapper).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,41 @@
import { DirectionalHint, IContextualMenuProps, IconButton } from "@fluentui/react";
import React from "react";
import ExplainIcon from "../../../../../../../../images/CopilotExplain.svg";
import OptimizeIcon from "../../../../../../../../images/CopilotOptimize.svg";
import RegenerateIcon from "../../../../../../../../images/CopilotRegenerate.svg";
import SimplifyIcon from "../../../../../../../../images/CopilotSimplify.svg";
export const MoreButton: React.FC = (): JSX.Element => {
const menuProps: IContextualMenuProps = {
items: [
{
key: "regenerate",
text: "Regenerate code",
iconProps: { imageProps: { src: RegenerateIcon } },
},
{
key: "explain",
text: "Explain code",
iconProps: { imageProps: { src: ExplainIcon } },
},
{
key: "optimize",
text: "Optimize",
iconProps: { imageProps: { src: OptimizeIcon } },
},
{
key: "simplify",
text: "Simplify",
iconProps: { imageProps: { src: SimplifyIcon } },
},
],
directionalHint: DirectionalHint.topRightEdge,
calloutProps: {
styles: { calloutMain: { borderRadius: "4px" }, root: { borderRadius: "4px" } },
},
};
return (
<IconButton iconProps={{ iconName: "More" }} menuProps={menuProps} menuIconProps={{ hidden: true }}></IconButton>
);
};

View File

@@ -0,0 +1,69 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`More button snapshot tests should render 1`] = `
<CustomizedIconButton
iconProps={
Object {
"iconName": "More",
}
}
menuIconProps={
Object {
"hidden": true,
}
}
menuProps={
Object {
"calloutProps": Object {
"styles": Object {
"calloutMain": Object {
"borderRadius": "4px",
},
"root": Object {
"borderRadius": "4px",
},
},
},
"directionalHint": 2,
"items": Array [
Object {
"iconProps": Object {
"imageProps": Object {
"src": Object {},
},
},
"key": "regenerate",
"text": "Regenerate code",
},
Object {
"iconProps": Object {
"imageProps": Object {
"src": Object {},
},
},
"key": "explain",
"text": "Explain code",
},
Object {
"iconProps": Object {
"imageProps": Object {
"src": Object {},
},
},
"key": "optimize",
"text": "Optimize",
},
Object {
"iconProps": Object {
"imageProps": Object {
"src": Object {},
},
},
"key": "simplify",
"text": "Simplify",
},
],
}
}
/>
`;

View File

@@ -0,0 +1,11 @@
import { OutputBubbleButtons } from "Explorer/QueryCopilot/V2/Bubbles/Output/Buttons/OutputBubbleButtons";
import { shallow } from "enzyme";
import React from "react";
describe("Output Bubble Buttons snapshot tests", () => {
it("should render", () => {
const wrapper = shallow(<OutputBubbleButtons />);
expect(wrapper).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,25 @@
import { Stack } from "@fluentui/react";
import { CopyButton } from "Explorer/QueryCopilot/V2/Bubbles/Output/Buttons/Copy/CopyButton";
import { FeedbackButtons } from "Explorer/QueryCopilot/V2/Bubbles/Output/Buttons/Feedback/FeedbackButtons";
import { InsertButton } from "Explorer/QueryCopilot/V2/Bubbles/Output/Buttons/Insert/InsertButton";
import { MoreButton } from "Explorer/QueryCopilot/V2/Bubbles/Output/Buttons/More/MoreButton";
import React from "react";
export const OutputBubbleButtons: React.FC = (): JSX.Element => {
return (
<Stack horizontal>
<Stack.Item style={{ paddingTop: "5px" }}>
<InsertButton />
</Stack.Item>
<Stack.Item>
<CopyButton />
</Stack.Item>
<Stack.Item>
<FeedbackButtons />
</Stack.Item>
<Stack.Item>
<MoreButton />
</Stack.Item>
</Stack>
);
};

View File

@@ -0,0 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Output Bubble Buttons snapshot tests should render 1`] = `
<Stack
horizontal={true}
>
<StackItem
style={
Object {
"paddingTop": "5px",
}
}
>
<InsertButton />
</StackItem>
<StackItem>
<CopyButton />
</StackItem>
<StackItem>
<FeedbackButtons />
</StackItem>
<StackItem>
<MoreButton />
</StackItem>
</Stack>
`;

View File

@@ -0,0 +1,21 @@
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
import { OutputBubble } from "Explorer/QueryCopilot/V2/Bubbles/Output/OutputBubble";
import { shallow } from "enzyme";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import { withHooks } from "jest-react-hooks-shallow";
import React from "react";
describe("Output Bubble snapshot tests", () => {
it("should render and update height", () => {
withHooks(() => {
useQueryCopilot.getState().setGeneratedQuery("test query");
useQueryCopilot.getState().setGeneratedQueryComments("test comments");
const wrapper = shallow(<OutputBubble />);
const editor = wrapper.find(EditorReact).first();
expect(editor.props().monacoContainerStyles).not.toHaveProperty("height", undefined);
expect(wrapper).toMatchSnapshot();
});
});
});

View File

@@ -0,0 +1,60 @@
import { Stack, Text } from "@fluentui/react";
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
import { OutputBubbleButtons } from "Explorer/QueryCopilot/V2/Bubbles/Output/Buttons/OutputBubbleButtons";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import React, { useState } from "react";
export const OutputBubble: React.FC = (): JSX.Element => {
const [windowHeight, setWindowHeight] = useState<string>();
const calculateQueryWindowHeight = (): string => {
const calculatedHeight = document.getElementById("outputBubble")?.clientHeight * (3 / 5);
return `${calculatedHeight}px`;
};
React.useEffect(() => {
setWindowHeight(calculateQueryWindowHeight());
}, []);
return (
<Stack
id="outputBubble"
style={{
display: "flex",
alignItems: "center",
padding: "10px",
margin: "10px",
backgroundColor: "white",
borderRadius: "8px",
}}
tokens={{ padding: 8, childrenGap: 8 }}
>
<Stack.Item style={{ alignSelf: "flex-start", paddingLeft: "2px" }}>
{useQueryCopilot.getState()?.generatedQueryComments}
</Stack.Item>
<Stack.Item style={{ alignSelf: "stretch", flexGrow: 4 }}>
<EditorReact
language={"sql"}
content={useQueryCopilot.getState()?.generatedQuery}
isReadOnly={true}
ariaLabel={"AI Response"}
wordWrap="on"
lineNumbers="on"
lineNumbersMinChars={2}
lineDecorationsWidth={0}
minimap={{ enabled: false }}
scrollBeyondLastLine={false}
monacoContainerStyles={{ height: windowHeight, borderRadius: "4px" }}
/>
</Stack.Item>
<Stack.Item style={{ alignSelf: "flex-start" }}>
<OutputBubbleButtons />
</Stack.Item>
<Stack.Item>
<Text style={{ fontWeight: 400, fontSize: "10px", lineHeight: "14px" }}>
AI-generated content may be incorrect
</Text>
</Stack.Item>
</Stack>
);
};

View File

@@ -0,0 +1,87 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Output Bubble snapshot tests should render and update height 1`] = `
<Stack
id="outputBubble"
style={
Object {
"alignItems": "center",
"backgroundColor": "white",
"borderRadius": "8px",
"display": "flex",
"margin": "10px",
"padding": "10px",
}
}
tokens={
Object {
"childrenGap": 8,
"padding": 8,
}
}
>
<StackItem
style={
Object {
"alignSelf": "flex-start",
"paddingLeft": "2px",
}
}
>
test comments
</StackItem>
<StackItem
style={
Object {
"alignSelf": "stretch",
"flexGrow": 4,
}
}
>
<EditorReact
ariaLabel="AI Response"
content="test query"
isReadOnly={true}
language="sql"
lineDecorationsWidth={0}
lineNumbers="on"
lineNumbersMinChars={2}
minimap={
Object {
"enabled": false,
}
}
monacoContainerStyles={
Object {
"borderRadius": "4px",
"height": "NaNpx",
}
}
scrollBeyondLastLine={false}
wordWrap="on"
/>
</StackItem>
<StackItem
style={
Object {
"alignSelf": "flex-start",
}
}
>
<OutputBubbleButtons />
</StackItem>
<StackItem>
<Text
style={
Object {
"fontSize": "10px",
"fontWeight": 400,
"lineHeight": "14px",
}
}
>
AI-generated content may be incorrect
</Text>
</StackItem>
</Stack>
`;

View File

@@ -19,7 +19,7 @@ exports[`Footer snapshot test should not pass if no text 1`] = `
<Stack>
<Image
onClick={[Function]}
src=""
src={Object {}}
styles={
Object {
"label": Object {
@@ -104,7 +104,7 @@ exports[`Footer snapshot test should not pass text with non enter key 1`] = `
<Stack>
<Image
onClick={[Function]}
src=""
src={Object {}}
styles={
Object {
"label": Object {
@@ -189,7 +189,7 @@ exports[`Footer snapshot test should open sample prompts on button click 1`] = `
<Stack>
<Image
onClick={[Function]}
src=""
src={Object {}}
styles={
Object {
"label": Object {
@@ -274,7 +274,7 @@ exports[`Footer snapshot test should pass text with enter key 1`] = `
<Stack>
<Image
onClick={[Function]}
src=""
src={Object {}}
styles={
Object {
"label": Object {
@@ -359,7 +359,7 @@ exports[`Footer snapshot test should pass text with icon button 1`] = `
<Stack>
<Image
onClick={[Function]}
src=""
src={Object {}}
styles={
Object {
"label": Object {
@@ -444,7 +444,7 @@ exports[`Footer snapshot test should update user input 1`] = `
<Stack>
<Image
onClick={[Function]}
src=""
src={Object {}}
styles={
Object {
"label": Object {

View File

@@ -18,7 +18,7 @@ exports[`Header snapshot test should close on button click 1`] = `
verticalAlign="center"
>
<Image
src=""
src={Object {}}
/>
<Text
style={

View File

@@ -1,5 +1,6 @@
import { Stack } from "@fluentui/react";
import { QueryCopilotProps } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
import { OutputBubble } from "Explorer/QueryCopilot/V2/Bubbles/Output/OutputBubble";
import { RetrievingBubble } from "Explorer/QueryCopilot/V2/Bubbles/Retriveing/RetrievingBubble";
import { SampleBubble } from "Explorer/QueryCopilot/V2/Bubbles/Sample/SampleBubble";
import { WelcomeBubble } from "Explorer/QueryCopilot/V2/Bubbles/Welcome/WelcomeBubble";
@@ -10,13 +11,7 @@ import React from "react";
import { WelcomeSidebarModal } from "../Modal/WelcomeSidebarModal";
export const QueryCopilotSidebar: React.FC<QueryCopilotProps> = ({ explorer }: QueryCopilotProps): JSX.Element => {
const {
setWasCopilotUsed,
showCopilotSidebar,
chatMessages,
showWelcomeSidebar,
isGeneratingQuery,
} = useQueryCopilot();
const { setWasCopilotUsed, showCopilotSidebar, chatMessages, isGeneratingQuery } = useQueryCopilot();
React.useEffect(() => {
if (showCopilotSidebar) {
@@ -27,58 +22,36 @@ export const QueryCopilotSidebar: React.FC<QueryCopilotProps> = ({ explorer }: Q
return (
<Stack style={{ width: "100%", height: "100%", backgroundColor: "#FAFAFA" }}>
<Header />
{showWelcomeSidebar ? (
<WelcomeSidebarModal />
) : (
<>
<WelcomeSidebarModal />
<Stack
style={{
flexGrow: 1,
display: "flex",
flexDirection: "column",
overflowY: "auto",
}}
>
<WelcomeBubble />
{chatMessages.map((message, index) => (
<Stack
key={index}
horizontalAlign="center"
tokens={{ padding: 8, childrenGap: 8 }}
style={{
flexGrow: 1,
display: "flex",
flexDirection: "column",
overflowY: "auto",
backgroundColor: "#E0E7FF",
borderRadius: "8px",
margin: "5px 10px",
textAlign: "start",
}}
>
<WelcomeBubble />
{chatMessages.map((message, index) =>
message.source === 0 ? (
<Stack
key={index}
horizontalAlign="center"
tokens={{ padding: 8, childrenGap: 8 }}
style={{
backgroundColor: "#E0E7FF",
borderRadius: "8px",
margin: "5px 10px",
textAlign: "start",
}}
>
{message.message}
</Stack>
) : (
<Stack
key={index}
horizontalAlign="center"
tokens={{ padding: 8, childrenGap: 8 }}
style={{
backgroundColor: "white",
borderRadius: "8px",
margin: "5px 10px",
textAlign: "start",
}}
>
{message.message}
</Stack>
)
)}
<RetrievingBubble />
{chatMessages.length === 0 && !isGeneratingQuery && <SampleBubble />}
{message}
</Stack>
<Footer explorer={explorer} />
</>
)}
))}
<OutputBubble />
<RetrievingBubble />
{chatMessages.length === 0 && !isGeneratingQuery && <SampleBubble />}
</Stack>
<Footer explorer={explorer} />
</Stack>
);
};

View File

@@ -12,6 +12,51 @@ exports[`Query Copilot Sidebar snapshot test should render and not set copilot u
>
<Header />
<WelcomeSidebarModal />
<Stack
style={
Object {
"display": "flex",
"flexDirection": "column",
"flexGrow": 1,
"overflowY": "auto",
}
}
>
<WelcomeBubble />
<OutputBubble />
<RetrievingBubble />
<SampleBubble />
</Stack>
<Footer
explorer={
Explorer {
"_isInitializingNotebooks": false,
"_resetNotebookWorkspace": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isTabsContentExpanded": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"phoenixClient": PhoenixClient {
"armResourceId": undefined,
"retryOptions": Object {
"maxTimeout": 5000,
"minTimeout": 5000,
"retries": 3,
},
},
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {
"container": [Circular],
},
"refreshNotebookList": [Function],
"resourceTree": ResourceTreeAdapter {
"container": [Circular],
"copyNotebook": [Function],
"parameters": [Function],
},
}
}
/>
</Stack>
`;
@@ -27,6 +72,51 @@ exports[`Query Copilot Sidebar snapshot test should render and set copilot used
>
<Header />
<WelcomeSidebarModal />
<Stack
style={
Object {
"display": "flex",
"flexDirection": "column",
"flexGrow": 1,
"overflowY": "auto",
}
}
>
<WelcomeBubble />
<OutputBubble />
<RetrievingBubble />
<SampleBubble />
</Stack>
<Footer
explorer={
Explorer {
"_isInitializingNotebooks": false,
"_resetNotebookWorkspace": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isTabsContentExpanded": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"phoenixClient": PhoenixClient {
"armResourceId": undefined,
"retryOptions": Object {
"maxTimeout": 5000,
"minTimeout": 5000,
"retries": 3,
},
},
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {
"container": [Circular],
},
"refreshNotebookList": [Function],
"resourceTree": ResourceTreeAdapter {
"container": [Circular],
"copyNotebook": [Function],
"parameters": [Function],
},
}
}
/>
</Stack>
`;
@@ -42,6 +132,51 @@ exports[`Query Copilot Sidebar snapshot test should render samples without messa
>
<Header />
<WelcomeSidebarModal />
<Stack
style={
Object {
"display": "flex",
"flexDirection": "column",
"flexGrow": 1,
"overflowY": "auto",
}
}
>
<WelcomeBubble />
<OutputBubble />
<RetrievingBubble />
<SampleBubble />
</Stack>
<Footer
explorer={
Explorer {
"_isInitializingNotebooks": false,
"_resetNotebookWorkspace": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isTabsContentExpanded": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"phoenixClient": PhoenixClient {
"armResourceId": undefined,
"retryOptions": Object {
"maxTimeout": 5000,
"minTimeout": 5000,
"retries": 3,
},
},
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {
"container": [Circular],
},
"refreshNotebookList": [Function],
"resourceTree": ResourceTreeAdapter {
"container": [Circular],
"copyNotebook": [Function],
"parameters": [Function],
},
}
}
/>
</Stack>
`;
@@ -57,5 +192,69 @@ exports[`Query Copilot Sidebar snapshot test should render with chat messages 1`
>
<Header />
<WelcomeSidebarModal />
<Stack
style={
Object {
"display": "flex",
"flexDirection": "column",
"flexGrow": 1,
"overflowY": "auto",
}
}
>
<WelcomeBubble />
<Stack
horizontalAlign="center"
key="0"
style={
Object {
"backgroundColor": "#E0E7FF",
"borderRadius": "8px",
"margin": "5px 10px",
"textAlign": "start",
}
}
tokens={
Object {
"childrenGap": 8,
"padding": 8,
}
}
>
<Component />
</Stack>
<OutputBubble />
<RetrievingBubble />
</Stack>
<Footer
explorer={
Explorer {
"_isInitializingNotebooks": false,
"_resetNotebookWorkspace": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isTabsContentExpanded": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"phoenixClient": PhoenixClient {
"armResourceId": undefined,
"retryOptions": Object {
"maxTimeout": 5000,
"minTimeout": 5000,
"retries": 3,
},
},
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {
"container": [Circular],
},
"refreshNotebookList": [Function],
"resourceTree": ResourceTreeAdapter {
"container": [Circular],
"copyNotebook": [Function],
"parameters": [Function],
},
}
}
/>
</Stack>
`;

View File

@@ -74,7 +74,7 @@ exports[`Query Copilot Carousel snapshot test should render when isOpen is true
To generate queries , just describe the query you want and copilot will generate the query for you.Watch this video to learn more about how to use copilot.
</Text>
<Image
src=""
src={Object {}}
style={
Object {
"margin": "16px auto",

View File

@@ -23,7 +23,7 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
verticalAlign="center"
>
<Image
src=""
src={Object {}}
/>
<Text
style={

View File

@@ -76,6 +76,7 @@ interface IQueryTabStates {
isExecutionError: boolean;
isExecuting: boolean;
showCopilotSidebar: boolean;
queryCopilotGeneratedQuery: string;
}
export default class QueryTabComponent extends React.Component<IQueryTabComponentProps, IQueryTabStates> {
@@ -101,6 +102,7 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
isExecutionError: this.props.isExecutionError,
isExecuting: false,
showCopilotSidebar: useQueryCopilot.getState().showCopilotSidebar,
queryCopilotGeneratedQuery: useQueryCopilot.getState().query,
};
this.isCloseClicked = false;
this.splitterId = this.props.tabId + "_splitter";
@@ -334,6 +336,7 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
public onChangeContent(newContent: string): void {
this.setState({
sqlQueryEditorContent: newContent,
queryCopilotGeneratedQuery: "",
});
if (this.isPreferredApiMongoDB) {
if (newContent.length > 0) {
@@ -365,6 +368,14 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
useCommandBar.getState().setContextButtons(this.getTabsButtons());
}
public setEditorContent(): string {
if (this.state.queryCopilotGeneratedQuery) {
return this.state.queryCopilotGeneratedQuery;
}
return this.state.sqlQueryEditorContent;
}
private unsubscribeCopilotSidebar: () => void;
componentDidMount(): void {
@@ -372,6 +383,9 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
if (this.state.showCopilotSidebar !== state.showCopilotSidebar) {
this.setState({ showCopilotSidebar: state.showCopilotSidebar });
}
if (this.state.queryCopilotGeneratedQuery !== state.query) {
this.setState({ queryCopilotGeneratedQuery: state.query });
}
});
useCommandBar.getState().setContextButtons(this.getTabsButtons());
@@ -393,7 +407,7 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
<div className="queryEditor" style={{ height: "100%" }}>
<EditorReact
language={"sql"}
content={this.state.sqlQueryEditorContent}
content={this.setEditorContent()}
isReadOnly={false}
ariaLabel={"Editing Query"}
lineNumbers={"on"}