[Query Copilot V2] Explanation bubble added buttons (#1609)
* Fixing naming convention * Additional implementation for Explanation * Added snaps * Removing snapshots * re-updated snapshots --------- Co-authored-by: Predrag Klepic <v-prklepic@microsoft.com>
This commit is contained in:
parent
c1c12019da
commit
260c99e15c
|
@ -0,0 +1,69 @@
|
||||||
|
import { Text } from "@fluentui/react";
|
||||||
|
import { shallow } from "enzyme";
|
||||||
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
|
import React from "react";
|
||||||
|
import { ExplanationButton } from "./ExplanationButton";
|
||||||
|
|
||||||
|
describe("Explanation Button", () => {
|
||||||
|
const initialStoreState = useQueryCopilot.getState();
|
||||||
|
beforeEach(() => {
|
||||||
|
useQueryCopilot.setState(initialStoreState, true);
|
||||||
|
useQueryCopilot.getState().showExplanationBubble = true;
|
||||||
|
useQueryCopilot.getState().shouldIncludeInMessages = false;
|
||||||
|
jest.useFakeTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render explanation bubble with generated comments", () => {
|
||||||
|
useQueryCopilot.getState().shouldIncludeInMessages = true;
|
||||||
|
|
||||||
|
const wrapper = shallow(<ExplanationButton />);
|
||||||
|
|
||||||
|
expect(wrapper.find("Stack")).toHaveLength(1);
|
||||||
|
expect(wrapper.find("Text")).toHaveLength(1);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render 'Explain this query' link", () => {
|
||||||
|
useQueryCopilot.getState().shouldIncludeInMessages = true;
|
||||||
|
const mockSetChatMessages = jest.fn();
|
||||||
|
const mockSetIsGeneratingExplanation = jest.fn();
|
||||||
|
const mockSetShouldIncludeInMessages = jest.fn();
|
||||||
|
const mockSetShowExplanationBubble = jest.fn();
|
||||||
|
useQueryCopilot.getState().setChatMessages = mockSetChatMessages;
|
||||||
|
useQueryCopilot.getState().setIsGeneratingExplanation = mockSetIsGeneratingExplanation;
|
||||||
|
useQueryCopilot.getState().setShouldIncludeInMessages = mockSetShouldIncludeInMessages;
|
||||||
|
useQueryCopilot.getState().setShowExplanationBubble = mockSetShowExplanationBubble;
|
||||||
|
|
||||||
|
const wrapper = shallow(<ExplanationButton />);
|
||||||
|
|
||||||
|
const textElement = wrapper.find(Text);
|
||||||
|
textElement.simulate("click");
|
||||||
|
|
||||||
|
expect(mockSetChatMessages).toHaveBeenCalledWith([
|
||||||
|
...initialStoreState.chatMessages,
|
||||||
|
{ source: 0, message: "Explain this query to me" },
|
||||||
|
]);
|
||||||
|
expect(mockSetIsGeneratingExplanation).toHaveBeenCalledWith(true);
|
||||||
|
expect(mockSetShouldIncludeInMessages).toHaveBeenCalledWith(true);
|
||||||
|
expect(mockSetShowExplanationBubble).toHaveBeenCalledWith(false);
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(3000);
|
||||||
|
|
||||||
|
expect(mockSetIsGeneratingExplanation).toHaveBeenCalledWith(false);
|
||||||
|
expect(mockSetChatMessages).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render nothing when conditions are not met", () => {
|
||||||
|
useQueryCopilot.getState().showExplanationBubble = false;
|
||||||
|
|
||||||
|
const wrapper = shallow(<ExplanationButton />);
|
||||||
|
|
||||||
|
expect(wrapper.isEmptyRender()).toBe(true);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
|
@ -2,12 +2,13 @@ import { Stack, Text } from "@fluentui/react";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export const ExplanationBubble: React.FC = (): JSX.Element => {
|
export const ExplanationButton: React.FC = (): JSX.Element => {
|
||||||
const {
|
const {
|
||||||
showExplanationBubble,
|
showExplanationBubble,
|
||||||
isGeneratingQuery,
|
isGeneratingQuery,
|
||||||
chatMessages,
|
chatMessages,
|
||||||
setChatMessages,
|
setChatMessages,
|
||||||
|
generatedQuery,
|
||||||
generatedQueryComments,
|
generatedQueryComments,
|
||||||
isGeneratingExplanation,
|
isGeneratingExplanation,
|
||||||
setIsGeneratingExplanation,
|
setIsGeneratingExplanation,
|
||||||
|
@ -24,7 +25,7 @@ export const ExplanationBubble: React.FC = (): JSX.Element => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (useQueryCopilot.getState().shouldIncludeInMessages) {
|
if (useQueryCopilot.getState().shouldIncludeInMessages) {
|
||||||
setIsGeneratingExplanation(false);
|
setIsGeneratingExplanation(false);
|
||||||
setChatMessages([...chatMessages, { source: 2, message: generatedQueryComments }]);
|
setChatMessages([...chatMessages, { source: 2, message: generatedQueryComments, sqlQuery: generatedQuery }]);
|
||||||
}
|
}
|
||||||
}, 3000);
|
}, 3000);
|
||||||
};
|
};
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Explanation Button should render explanation bubble with generated comments 1`] = `
|
||||||
|
<Stack
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"alignItems": "center",
|
||||||
|
"display": "flex",
|
||||||
|
"margin": "5px",
|
||||||
|
"padding": "5px 5px 5px 50px",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
onClick={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"border": "1.5px solid #B0BEFF",
|
||||||
|
"borderRadius": "4px",
|
||||||
|
"cursor": "pointer",
|
||||||
|
"marginBottom": "5px",
|
||||||
|
"padding": "2px",
|
||||||
|
"width": "100%",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Explain this query to me
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Explanation Button should render nothing when conditions are not met 1`] = `""`;
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { Stack, Text } from "@fluentui/react";
|
||||||
|
import { CopilotMessage } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
|
||||||
|
import { FeedbackButtons } from "Explorer/QueryCopilot/V2/Bubbles/Output/Buttons/Feedback/FeedbackButtons";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export const ExplanationBubble = ({ copilotMessage }: { copilotMessage: CopilotMessage }): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
horizontalAlign="start"
|
||||||
|
verticalAlign="start"
|
||||||
|
tokens={{ padding: 8, childrenGap: 8 }}
|
||||||
|
style={{
|
||||||
|
backgroundColor: "white",
|
||||||
|
borderRadius: "8px",
|
||||||
|
margin: "5px 10px",
|
||||||
|
textAlign: "start",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text>{copilotMessage.message}</Text>
|
||||||
|
<FeedbackButtons sqlQuery={copilotMessage.sqlQuery} />
|
||||||
|
<Text style={{ fontWeight: 400, fontSize: "10px", lineHeight: "14px" }}>
|
||||||
|
AI-generated content may be incorrect
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,69 +1,17 @@
|
||||||
import { Text } from "@fluentui/react";
|
import { CopilotMessage } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
|
||||||
|
import { ExplanationBubble } from "Explorer/QueryCopilot/V2/Bubbles/Explanation/ExplainationBubble";
|
||||||
import { shallow } from "enzyme";
|
import { shallow } from "enzyme";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { ExplanationBubble } from "./ExplanationBubble";
|
|
||||||
|
|
||||||
describe("Explanation Bubble", () => {
|
describe("Explanation Bubble snapshot tests", () => {
|
||||||
const initialStoreState = useQueryCopilot.getState();
|
it("should render", () => {
|
||||||
beforeEach(() => {
|
const mockCopilotMessage: CopilotMessage = {
|
||||||
useQueryCopilot.setState(initialStoreState, true);
|
source: 2,
|
||||||
useQueryCopilot.getState().showExplanationBubble = true;
|
message: "Mock message",
|
||||||
useQueryCopilot.getState().shouldIncludeInMessages = false;
|
};
|
||||||
jest.useFakeTimers();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
const wrapper = shallow(<ExplanationBubble copilotMessage={mockCopilotMessage} />);
|
||||||
jest.clearAllMocks();
|
|
||||||
jest.useRealTimers();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render explanation bubble with generated comments", () => {
|
|
||||||
useQueryCopilot.getState().shouldIncludeInMessages = true;
|
|
||||||
|
|
||||||
const wrapper = shallow(<ExplanationBubble />);
|
|
||||||
|
|
||||||
expect(wrapper.find("Stack")).toHaveLength(1);
|
|
||||||
expect(wrapper.find("Text")).toHaveLength(1);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render 'Explain this query' link", () => {
|
|
||||||
useQueryCopilot.getState().shouldIncludeInMessages = true;
|
|
||||||
const mockSetChatMessages = jest.fn();
|
|
||||||
const mockSetIsGeneratingExplanation = jest.fn();
|
|
||||||
const mockSetShouldIncludeInMessages = jest.fn();
|
|
||||||
const mockSetShowExplanationBubble = jest.fn();
|
|
||||||
useQueryCopilot.getState().setChatMessages = mockSetChatMessages;
|
|
||||||
useQueryCopilot.getState().setIsGeneratingExplanation = mockSetIsGeneratingExplanation;
|
|
||||||
useQueryCopilot.getState().setShouldIncludeInMessages = mockSetShouldIncludeInMessages;
|
|
||||||
useQueryCopilot.getState().setShowExplanationBubble = mockSetShowExplanationBubble;
|
|
||||||
|
|
||||||
const wrapper = shallow(<ExplanationBubble />);
|
|
||||||
|
|
||||||
const textElement = wrapper.find(Text);
|
|
||||||
textElement.simulate("click");
|
|
||||||
|
|
||||||
expect(mockSetChatMessages).toHaveBeenCalledWith([
|
|
||||||
...initialStoreState.chatMessages,
|
|
||||||
{ source: 0, message: "Explain this query to me" },
|
|
||||||
]);
|
|
||||||
expect(mockSetIsGeneratingExplanation).toHaveBeenCalledWith(true);
|
|
||||||
expect(mockSetShouldIncludeInMessages).toHaveBeenCalledWith(true);
|
|
||||||
expect(mockSetShowExplanationBubble).toHaveBeenCalledWith(false);
|
|
||||||
|
|
||||||
jest.advanceTimersByTime(3000);
|
|
||||||
|
|
||||||
expect(mockSetIsGeneratingExplanation).toHaveBeenCalledWith(false);
|
|
||||||
expect(mockSetChatMessages).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render nothing when conditions are not met", () => {
|
|
||||||
useQueryCopilot.getState().showExplanationBubble = false;
|
|
||||||
|
|
||||||
const wrapper = shallow(<ExplanationBubble />);
|
|
||||||
|
|
||||||
expect(wrapper.isEmptyRender()).toBe(true);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,32 +1,38 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`Explanation Bubble should render explanation bubble with generated comments 1`] = `
|
exports[`Explanation Bubble snapshot tests should render 1`] = `
|
||||||
<Stack
|
<Stack
|
||||||
|
horizontalAlign="start"
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"alignItems": "center",
|
"backgroundColor": "white",
|
||||||
"display": "flex",
|
"borderRadius": "8px",
|
||||||
"margin": "5px",
|
"margin": "5px 10px",
|
||||||
"padding": "5px 5px 5px 50px",
|
"textAlign": "start",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tokens={
|
||||||
|
Object {
|
||||||
|
"childrenGap": 8,
|
||||||
|
"padding": 8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
verticalAlign="start"
|
||||||
>
|
>
|
||||||
|
<Text>
|
||||||
|
Mock message
|
||||||
|
</Text>
|
||||||
|
<FeedbackButtons />
|
||||||
<Text
|
<Text
|
||||||
onClick={[Function]}
|
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"border": "1.5px solid #B0BEFF",
|
"fontSize": "10px",
|
||||||
"borderRadius": "4px",
|
"fontWeight": 400,
|
||||||
"cursor": "pointer",
|
"lineHeight": "14px",
|
||||||
"marginBottom": "5px",
|
|
||||||
"padding": "2px",
|
|
||||||
"width": "100%",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Explain this query to me
|
AI-generated content may be incorrect
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Explanation Bubble should render nothing when conditions are not met 1`] = `""`;
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Stack } from "@fluentui/react";
|
import { Stack } from "@fluentui/react";
|
||||||
import { QueryCopilotProps } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
|
import { QueryCopilotProps } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
|
||||||
import { ExplanationBubble } from "Explorer/QueryCopilot/V2/Bubbles/Explanation/ExplanationBubble";
|
import { ExplanationButton } from "Explorer/QueryCopilot/V2/Bubbles/Explanation/Button/ExplanationButton";
|
||||||
|
import { ExplanationBubble } from "Explorer/QueryCopilot/V2/Bubbles/Explanation/ExplainationBubble";
|
||||||
import { OutputBubble } from "Explorer/QueryCopilot/V2/Bubbles/Output/OutputBubble";
|
import { OutputBubble } from "Explorer/QueryCopilot/V2/Bubbles/Output/OutputBubble";
|
||||||
import { RetrievingBubble } from "Explorer/QueryCopilot/V2/Bubbles/Retriveing/RetrievingBubble";
|
import { RetrievingBubble } from "Explorer/QueryCopilot/V2/Bubbles/Retriveing/RetrievingBubble";
|
||||||
import { SampleBubble } from "Explorer/QueryCopilot/V2/Bubbles/Sample/SampleBubble";
|
import { SampleBubble } from "Explorer/QueryCopilot/V2/Bubbles/Sample/SampleBubble";
|
||||||
|
@ -42,27 +43,34 @@ export const QueryCopilotSidebar: React.FC<QueryCopilotProps> = ({ explorer }: Q
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<WelcomeBubble />
|
<WelcomeBubble />
|
||||||
{chatMessages.map((message, index) =>
|
{chatMessages.map((message, index) => {
|
||||||
message.source === 0 || message.source === 2 ? (
|
switch (message.source) {
|
||||||
<Stack
|
case 0:
|
||||||
key={index}
|
return (
|
||||||
horizontalAlign="center"
|
<Stack
|
||||||
tokens={{ padding: 8, childrenGap: 8 }}
|
key={index}
|
||||||
style={{
|
horizontalAlign="center"
|
||||||
backgroundColor: message.source === 0 ? "#E0E7FF" : "white",
|
tokens={{ padding: 8, childrenGap: 8 }}
|
||||||
borderRadius: "8px",
|
style={{
|
||||||
margin: "5px 10px",
|
backgroundColor: "#E0E7FF",
|
||||||
textAlign: "start",
|
borderRadius: "8px",
|
||||||
}}
|
margin: "5px 10px",
|
||||||
>
|
textAlign: "start",
|
||||||
{message.message}
|
}}
|
||||||
</Stack>
|
>
|
||||||
) : (
|
{message.message}
|
||||||
<OutputBubble key={index} copilotMessage={message} />
|
</Stack>
|
||||||
)
|
);
|
||||||
)}
|
case 1:
|
||||||
|
return <OutputBubble key={index} copilotMessage={message} />;
|
||||||
|
case 2:
|
||||||
|
return <ExplanationBubble key={index} copilotMessage={message} />;
|
||||||
|
default:
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
})}
|
||||||
<RetrievingBubble />
|
<RetrievingBubble />
|
||||||
<ExplanationBubble />
|
<ExplanationButton />
|
||||||
|
|
||||||
{chatMessages.length === 0 && !isGeneratingQuery && <SampleBubble />}
|
{chatMessages.length === 0 && !isGeneratingQuery && <SampleBubble />}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
Loading…
Reference in New Issue