[Query Copilot] Resource tree styling and New query button (#1519)

* Styling implemented related with the work item

* Sample container New query button implementation

* Fixing related with the not rendering Sample Data

* Fix race condition when rendering sample data resource tree

* Remove export keyword for updateContextForSampleData

* Copilot New Query should open Copilot tab

* showing buttons in sample command bar

---------

Co-authored-by: Predrag Klepic <v-prklepic@microsoft.com>
Co-authored-by: Victor Meng <vimeng@microsoft.com>
This commit is contained in:
Predrag Klepic 2023-07-10 09:14:14 +02:00 committed by GitHub
parent 0eaa5d004b
commit b6d576b7b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 70 additions and 14 deletions

View File

@ -1,3 +1,4 @@
import { ReactTabKind, useTabs } from "hooks/useTabs";
import React from "react"; import React from "react";
import AddCollectionIcon from "../../images/AddCollection.svg"; import AddCollectionIcon from "../../images/AddCollection.svg";
import AddSqlQueryIcon from "../../images/AddSqlQuery_16x16.svg"; import AddSqlQueryIcon from "../../images/AddSqlQuery_16x16.svg";
@ -140,6 +141,21 @@ export const createCollectionContextMenuButton = (
return items; return items;
}; };
export const createSampleCollectionContextMenuButton = (
selectedCollection: ViewModels.CollectionBase
): TreeNodeMenuItem[] => {
const items: TreeNodeMenuItem[] = [];
if (userContext.apiType === "SQL") {
items.push({
iconSrc: AddSqlQueryIcon,
onClick: () => selectedCollection && useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot),
label: "New SQL Query",
});
}
return items;
};
export const createStoreProcedureContextMenuItems = ( export const createStoreProcedureContextMenuItems = (
container: Explorer, container: Explorer,
storedProcedure: StoredProcedure storedProcedure: StoredProcedure

View File

@ -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;
styles?: React.CSSProperties;
} }
interface AccordionItemComponentState { interface AccordionItemComponentState {
@ -53,6 +54,7 @@ export class AccordionItemComponent extends React.Component<AccordionItemCompone
} }
public render(): JSX.Element { public render(): JSX.Element {
const { styles } = this.props;
return ( return (
<div className="accordionItemContainer"> <div className="accordionItemContainer">
<div className="accordionItemHeader" onClick={this.onHeaderClick} onKeyPress={this.onHeaderKeyPress}> <div className="accordionItemHeader" onClick={this.onHeaderClick} onKeyPress={this.onHeaderKeyPress}>
@ -60,7 +62,11 @@ export class AccordionItemComponent extends React.Component<AccordionItemCompone
{this.props.title} {this.props.title}
</div> </div>
<div className="accordionItemContent"> <div className="accordionItemContent">
<AnimateHeight duration={AccordionItemComponent.durationMS} height={this.state.isExpanded ? "auto" : 0}> <AnimateHeight
style={{ ...styles }}
duration={AccordionItemComponent.durationMS}
height={this.state.isExpanded ? "auto" : 0}
>
{this.props.children} {this.props.children}
</AnimateHeight> </AnimateHeight>
</div> </div>

View File

@ -790,7 +790,11 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
{!isNotebookEnabled && isSampleDataEnabled && ( {!isNotebookEnabled && isSampleDataEnabled && (
<> <>
<AccordionComponent> <AccordionComponent>
<AccordionItemComponent title={"MY DATA"} isExpanded={!gitHubNotebooksContentRoot}> <AccordionItemComponent
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"}>
@ -804,7 +808,11 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
{isNotebookEnabled && isSampleDataEnabled && ( {isNotebookEnabled && isSampleDataEnabled && (
<> <>
<AccordionComponent> <AccordionComponent>
<AccordionItemComponent title={"MY DATA"} isExpanded={!gitHubNotebooksContentRoot}> <AccordionItemComponent
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"}>

View File

@ -1,7 +1,12 @@
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
import TabsBase from "Explorer/Tabs/TabsBase";
import { useSelectedNode } from "Explorer/useSelectedNode";
import { useTabs } from "hooks/useTabs";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } 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";
import * as ResourceTreeContextMenuButtonFactory from "../ContextMenuButtonFactory";
import { TreeComponent, TreeNode } from "../Controls/TreeComponent/TreeComponent"; import { TreeComponent, TreeNode } from "../Controls/TreeComponent/TreeComponent";
export const SampleDataTree = ({ export const SampleDataTree = ({
@ -15,14 +20,36 @@ export const SampleDataTree = ({
if (sampleDataResourceTokenCollection) { if (sampleDataResourceTokenCollection) {
const updatedSampleTree: TreeNode = { const updatedSampleTree: TreeNode = {
label: sampleDataResourceTokenCollection.databaseId, label: sampleDataResourceTokenCollection.databaseId,
isExpanded: true, isExpanded: false,
iconSrc: CosmosDBIcon, iconSrc: CosmosDBIcon,
className: "databaseHeader",
children: [ children: [
{ {
label: sampleDataResourceTokenCollection.id(), label: sampleDataResourceTokenCollection.id(),
iconSrc: CollectionIcon, iconSrc: CollectionIcon,
isExpanded: true, isExpanded: false,
className: "collectionHeader", className: "dataResourceTree",
contextMenu: ResourceTreeContextMenuButtonFactory.createSampleCollectionContextMenuButton(
sampleDataResourceTokenCollection
),
onClick: () => {
// Rewritten version of expandCollapseCollection
useSelectedNode.getState().setSelectedNode(sampleDataResourceTokenCollection);
useCommandBar.getState().setContextButtons([]);
useTabs().refreshActiveTab(
(tab: TabsBase) =>
tab.collection?.id() === sampleDataResourceTokenCollection.id() &&
tab.collection.databaseId === sampleDataResourceTokenCollection.databaseId
);
},
isSelected: () =>
useSelectedNode
.getState()
.isDataNodeSelected(
sampleDataResourceTokenCollection.databaseId,
sampleDataResourceTokenCollection.id()
),
onContextMenuOpen: () => useSelectedNode.getState().setSelectedNode(sampleDataResourceTokenCollection),
children: [ children: [
{ {
label: "Items", label: "Items",
@ -35,7 +62,5 @@ export const SampleDataTree = ({
} }
}, [sampleDataResourceTokenCollection]); }, [sampleDataResourceTokenCollection]);
return ( return <TreeComponent className="dataResourceTree" rootNode={root || { label: "Sample data not initialized." }} />;
<TreeComponent className="sampleDataResourceTree" rootNode={root || { label: "Sample data not initialized." }} />
);
}; };

View File

@ -62,10 +62,6 @@ export function useKnockoutExplorer(platform: Platform): Explorer {
setExplorer(explorer); setExplorer(explorer);
} }
} }
if (userContext.features.enableCopilot) {
await updateContextForSampleData();
}
}; };
effect(); effect();
}, [platform]); }, [platform]);
@ -73,6 +69,9 @@ export function useKnockoutExplorer(platform: Platform): Explorer {
useEffect(() => { useEffect(() => {
if (explorer) { if (explorer) {
applyExplorerBindings(explorer); applyExplorerBindings(explorer);
if (userContext.features.enableCopilot) {
updateContextForSampleData(explorer);
}
} }
}, [explorer]); }, [explorer]);
@ -415,7 +414,7 @@ interface PortalMessage {
inputs?: DataExplorerInputsFrame; inputs?: DataExplorerInputsFrame;
} }
async function updateContextForSampleData(): Promise<void> { async function updateContextForSampleData(explorer: Explorer): Promise<void> {
if (!userContext.features.enableCopilot) { if (!userContext.features.enableCopilot) {
return; return;
} }
@ -435,6 +434,8 @@ async function updateContextForSampleData(): Promise<void> {
const data: SampledataconnectionResponse = await response.json(); const data: SampledataconnectionResponse = await response.json();
const sampleDataConnectionInfo = parseResourceTokenConnectionString(data.connectionString); const sampleDataConnectionInfo = parseResourceTokenConnectionString(data.connectionString);
updateUserContext({ sampleDataConnectionInfo }); updateUserContext({ sampleDataConnectionInfo });
await explorer.refreshSampleData();
} }
interface SampledataconnectionResponse { interface SampledataconnectionResponse {