Filtering DBs and option for pin(fav)(#2301)

* implemented search bar

* formatting corrected

* added pin(fav) and sorting in local in sidebar tree of DE

* reverted changes

* fixed lint and formatting issues

* fixed lint and formatting issues

* theme toggle button is disabled if in portal

* fixed lint error

* added link on disabled theme toggle button

* updated the variable for pin icon

* removed en-us from url

---------

Co-authored-by: nishthaAhujaa <nishtha17354@iiittd.ac.in>
Co-authored-by: sakshigupta12feb <sakshigupta12feb1@gmail.com>
Co-authored-by: Sakshi Gupta <sakshig@microsoft.com>
This commit is contained in:
Nishtha Ahuja
2026-03-16 20:23:44 +05:30
committed by GitHub
parent 454a02bc53
commit 8cce0a4802
19 changed files with 1493 additions and 1080 deletions
+70 -23
View File
@@ -1,5 +1,12 @@
import { Tree, TreeItemValue, TreeOpenChangeData, TreeOpenChangeEvent } from "@fluentui/react-components";
import { Home16Regular } from "@fluentui/react-icons";
import {
Button,
Input,
Tree,
TreeItemValue,
TreeOpenChangeData,
TreeOpenChangeEvent,
} from "@fluentui/react-components";
import { ArrowSortDown20Regular, ArrowSortUp20Regular, Home16Regular, Search20Regular } from "@fluentui/react-icons";
import { AuthType } from "AuthType";
import { useTreeStyles } from "Explorer/Controls/TreeComponent/Styles";
import { TreeNode, TreeNodeComponent } from "Explorer/Controls/TreeComponent/TreeNodeComponent";
@@ -55,6 +62,11 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ explorer }: Resource
sampleDataResourceTokenCollection: state.sampleDataResourceTokenCollection,
}));
const databasesFetchedSuccessfully = useDatabases((state) => state.databasesFetchedSuccessfully);
const searchText = useDatabases((state) => state.searchText);
const setSearchText = useDatabases((state) => state.setSearchText);
const sortOrder = useDatabases((state) => state.sortOrder);
const setSortOrder = useDatabases((state) => state.setSortOrder);
const pinnedDatabaseIds = useDatabases((state) => state.pinnedDatabaseIds);
const { isCopilotEnabled, isCopilotSampleDBEnabled } = useQueryCopilot((state) => ({
isCopilotEnabled: state.copilotEnabled,
isCopilotSampleDBEnabled: state.copilotSampleDBEnabled,
@@ -63,8 +75,24 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ explorer }: Resource
const databaseTreeNodes = useMemo(() => {
return userContext.authType === AuthType.ResourceToken
? createResourceTokenTreeNodes(resourceTokenCollection)
: createDatabaseTreeNodes(explorer, isNotebookEnabled, databases, refreshActiveTab);
}, [resourceTokenCollection, databases, isNotebookEnabled, refreshActiveTab]);
: createDatabaseTreeNodes(
explorer,
isNotebookEnabled,
databases,
refreshActiveTab,
searchText,
sortOrder,
pinnedDatabaseIds,
);
}, [
resourceTokenCollection,
databases,
isNotebookEnabled,
refreshActiveTab,
searchText,
sortOrder,
pinnedDatabaseIds,
]);
const isSampleDataEnabled =
isCopilotEnabled &&
@@ -114,46 +142,65 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ explorer }: Resource
} else {
return [...headerNodes, ...databaseTreeNodes];
}
// headerNodes is intentionally excluded — it depends only on isFabricMirrored() which is stable.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [databaseTreeNodes, sampleDataNodes]);
// Track complete DatabaseLoad scenario (start, tree rendered, interactive)
useDatabaseLoadScenario(databaseTreeNodes, databasesFetchedSuccessfully);
useEffect(() => {
// Compute open items based on node.isExpanded
const updateOpenItems = (node: TreeNode, parentNodeId: string): void => {
// This will look for ANY expanded node, event if its parent node isn't expanded
// and add it to the openItems list
const expandedIds: TreeItemValue[] = [];
const collectExpandedIds = (node: TreeNode, parentNodeId: string | undefined): void => {
const globalId = parentNodeId === undefined ? node.label : `${parentNodeId}/${node.label}`;
if (node.isExpanded) {
let found = false;
for (const id of openItems) {
if (id === globalId) {
found = true;
break;
}
}
if (!found) {
setOpenItems((prevOpenItems) => [...prevOpenItems, globalId]);
}
expandedIds.push(globalId);
}
if (node.children) {
for (const child of node.children) {
updateOpenItems(child, globalId);
collectExpandedIds(child, globalId);
}
}
};
rootNodes.forEach((n) => updateOpenItems(n, undefined));
}, [rootNodes, openItems, setOpenItems]);
rootNodes.forEach((n) => collectExpandedIds(n, undefined));
if (expandedIds.length > 0) {
setOpenItems((prevOpenItems) => {
const prevSet = new Set(prevOpenItems);
const newIds = expandedIds.filter((id) => !prevSet.has(id));
return newIds.length > 0 ? [...prevOpenItems, ...newIds] : prevOpenItems;
});
}
}, [rootNodes]);
const handleOpenChange = (event: TreeOpenChangeEvent, data: TreeOpenChangeData) =>
setOpenItems(Array.from(data.openItems));
const toggleSortOrder = () => {
setSortOrder(sortOrder === "az" ? "za" : "az");
};
return (
<div className={treeStyles.treeContainer}>
{userContext.authType !== AuthType.ResourceToken && databases.length > 0 && (
<div style={{ padding: "8px", display: "flex", gap: "4px", alignItems: "center" }}>
<Input
placeholder="Search databases only"
value={searchText}
onChange={(_, data) => setSearchText(data?.value || "")}
size="small"
contentBefore={<Search20Regular />}
style={{ flex: 1 }}
/>
<Button
appearance="subtle"
size="small"
icon={sortOrder === "az" ? <ArrowSortDown20Regular /> : <ArrowSortUp20Regular />}
onClick={toggleSortOrder}
/>
</div>
)}
<Tree
aria-label="CosmosDB resources"
openItems={openItems}