Redesign resource tree (#1865)

* start redesign work

* add left padding to all tree nodes

* fiddling with padding

* align tab bar line with first item in resource tree

* final touch ups

* fix a strange password manager autofill prompt

* add keyboard shortcuts

* revert testing change

* nudge messagebar to layout row height

* tidy up

* switch to Allotment to stop ResizeObserver issues with monaco

* refmt and fix lints

* fabric touch-ups

* update snapshots

* remove explicit react-icons dependency

* reinstall packages

* remove background from FluentProvider

* fix alignment of message bar

* undo temporary workaround

* restore refresh button

* fix e2e tests and reformat

* fix compiler error

* remove uiw/react-split

* uncomment selection change on expand
This commit is contained in:
Ashley Stanton-Nurse 2024-08-01 10:02:36 -07:00 committed by GitHub
parent 3d1f280378
commit 31773ee73b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 1551 additions and 2067 deletions

3
.npmrc
View File

@ -1 +1,4 @@
save-exact=true
# Ignore peer dependency conflicts
force=true # TODO: Remove this when we update to React 17 or higher!

View File

@ -168,7 +168,7 @@
@FabricBoxBorderRadius: 8px;
@FabricBoxBorderShadow: rgba(0, 0, 0, 0.133) 0px 1.6px 3.6px 0px, rgba(0, 0, 0, 0.11) 0px 0.3px 0.9px 0px;
@FabricBoxMargin: 4px 3px 4px 3px;
@FabricBoxMargin: 4px 8px 4px 8px;
@FabricAccentMediumHigh: #0c695a;
@FabricAccentMedium: #117865;

View File

@ -1906,7 +1906,7 @@ input::-webkit-calendar-picker-indicator::after {
}
.nav-tabs-margin {
padding-top: 8px;
padding-top: 5px;
background-color: #f2f2f2;
}
@ -2074,14 +2074,6 @@ a:link {
display: inline;
}
.resourceTreeAndTabs {
display: flex;
flex: 1 1 auto;
overflow-x: clip;
overflow-y: auto;
height: 100%;
}
.collectiontitle {
font-size: 14px;
text-transform: uppercase;
@ -2325,11 +2317,6 @@ td a:hover {
outline: 1px dotted;
}
#content.active .tabdocuments .scrollable {
height: 100%;
overflow-y: auto;
}
.table-fixed thead {
width: 97%;
padding-left: 18px;
@ -2365,10 +2352,8 @@ a:link {
.tabsManagerContainer {
height: 100%;
flex-grow: 1;
display: flex;
flex-direction: column;
min-height: 300px;
display: grid;
grid-template-rows: 36px 36px 1fr;
min-width: 0; // This prevents it to grow past the parent's width if its content is too wide
}
@ -2580,18 +2565,6 @@ a:link {
cursor: pointer;
}
.documentsTab {
.documentsTable {
.documentsTableCell {
border-left: 1px solid @BaseMedium;
height: 100%;
}
.documentsTableHeader {
border-bottom: 1px solid @BaseMedium;
}
}
}
.querydropdown {
border: 1px solid @BaseMedium;
font-style: normal;
@ -2637,6 +2610,7 @@ a:link {
}
.tabPanesContainer {
grid-row: span 2; // Fill the remaining space
display: flex;
height: 100%;
overflow: hidden;

View File

@ -38,7 +38,7 @@ a:focus {
}
.nav-tabs-margin {
padding-top: 8px;
padding-top: 5px;
background-color: #ffffff
}

View File

@ -3,19 +3,6 @@
.dataResourceTree {
margin-left: @MediumSpace;
overflow: auto;
.databaseHeader {
padding: 1px;
font-size: 14px;
}
.collectionHeader {
font-size: 12px;
}
.loadMoreHeader {
color: RGB(5, 99, 193);
}
}
.notebookResourceTree {

55
package-lock.json generated
View File

@ -51,8 +51,8 @@
"@types/lodash": "4.14.171",
"@types/mkdirp": "1.0.1",
"@types/node-fetch": "2.5.7",
"@uiw/react-split": "5.9.3",
"@xmldom/xmldom": "0.7.13",
"allotment": "1.20.2",
"applicationinsights": "1.8.0",
"bootstrap": "3.4.1",
"canvas": "2.11.2",
@ -13063,17 +13063,6 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@uiw/react-split": {
"version": "5.9.3",
"license": "MIT",
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@ungap/url-search-params": {
"version": "0.2.2",
"license": "ISC"
@ -13496,6 +13485,28 @@
"ajv": "^6.9.1"
}
},
"node_modules/allotment": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/allotment/-/allotment-1.20.2.tgz",
"integrity": "sha512-TaCuHfYNcsJS9EPk04M7TlG5Rl3vbAdHeAyrTE9D5vbpzV+wxnRoUrulDbfnzaQcPIZKpHJNixDOoZNuzliKEA==",
"dependencies": {
"classnames": "^2.3.0",
"eventemitter3": "^5.0.0",
"lodash.clamp": "^4.0.0",
"lodash.debounce": "^4.0.0",
"lodash.isequal": "^4.5.0",
"use-resize-observer": "^9.0.0"
},
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0"
}
},
"node_modules/allotment/node_modules/eventemitter3": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
},
"node_modules/anser": {
"version": "1.4.10",
"license": "MIT"
@ -27962,6 +27973,11 @@
"version": "4.3.0",
"license": "MIT"
},
"node_modules/lodash.clamp": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/lodash.clamp/-/lodash.clamp-4.0.3.tgz",
"integrity": "sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg=="
},
"node_modules/lodash.clonedeep": {
"version": "4.5.0",
"license": "MIT"
@ -28012,7 +28028,8 @@
},
"node_modules/lodash.isequal": {
"version": "4.5.0",
"dev": true,
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
"license": "MIT"
},
"node_modules/lodash.isinteger": {
@ -35658,6 +35675,18 @@
"react-dom": ">=16.8.0 <19.0.0"
}
},
"node_modules/use-resize-observer": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz",
"integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==",
"dependencies": {
"@juggle/resize-observer": "^3.3.1"
},
"peerDependencies": {
"react": "16.8.0 - 18",
"react-dom": "16.8.0 - 18"
}
},
"node_modules/use-sync-external-store": {
"version": "1.2.0",
"license": "MIT",

View File

@ -46,8 +46,8 @@
"@types/lodash": "4.14.171",
"@types/mkdirp": "1.0.1",
"@types/node-fetch": "2.5.7",
"@uiw/react-split": "5.9.3",
"@xmldom/xmldom": "0.7.13",
"allotment": "1.20.2",
"applicationinsights": "1.8.0",
"bootstrap": "3.4.1",
"canvas": "2.11.2",
@ -98,8 +98,8 @@
"react-redux": "7.1.3",
"react-splitter-layout": "4.0.0",
"react-string-format": "1.0.1",
"react-youtube": "9.0.1",
"react-window": "1.8.10",
"react-youtube": "9.0.1",
"reflect-metadata": "0.1.13",
"rx-jupyter": "5.5.12",
"sanitize-html": "2.3.3",

View File

@ -1,11 +0,0 @@
diff --git a/node_modules/@uiw/react-split/cjs/index.d.ts b/node_modules/@uiw/react-split/cjs/index.d.ts
index 644bcc3..f794760 100644
--- a/node_modules/@uiw/react-split/cjs/index.d.ts
+++ b/node_modules/@uiw/react-split/cjs/index.d.ts
@@ -56,5 +56,5 @@ export default class Split extends React.Component<SplitProps, SplitState> {
onMouseDown(paneNumber: number, env: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
onDragging(env: Event): void;
onDragEnd(): void;
- render(): import("react/jsx-runtime").JSX.Element;
+ render(): JSX.Element;
}

View File

@ -1,55 +0,0 @@
import React, { FunctionComponent, MutableRefObject, useEffect, useRef } from "react";
import arrowLeftImg from "../../images/imgarrowlefticon.svg";
import { getApiShortDisplayName } from "../Utils/APITypeUtils";
import { NormalizedEventKey } from "./Constants";
export interface CollapsedResourceTreeProps {
toggleLeftPaneExpanded: () => void;
isLeftPaneExpanded: boolean;
}
export const CollapsedResourceTree: FunctionComponent<CollapsedResourceTreeProps> = ({
toggleLeftPaneExpanded,
isLeftPaneExpanded,
}: CollapsedResourceTreeProps): JSX.Element => {
const focusButton = useRef<HTMLLIElement>() as MutableRefObject<HTMLLIElement>;
useEffect(() => {
if (focusButton.current) {
focusButton.current.focus();
}
});
const onKeyPressToggleLeftPaneExpanded = (event: React.KeyboardEvent) => {
if (event.key === NormalizedEventKey.Space || event.key === NormalizedEventKey.Enter) {
toggleLeftPaneExpanded();
event.stopPropagation();
}
};
return (
<div id="mini" className={!isLeftPaneExpanded ? "mini toggle-mini" : "hiddenMain"}>
<div className="main-nav nav">
<ul className="nav">
<li
className="resourceTreeCollapse"
id="collapseToggleLeftPaneButton"
role="button"
tabIndex={0}
aria-label={getApiShortDisplayName() + `Expand tree`}
onClick={toggleLeftPaneExpanded}
onKeyPress={onKeyPressToggleLeftPaneExpanded}
ref={focusButton}
>
<span className="leftarrowCollapsed">
<img className="arrowCollapsed" src={arrowLeftImg} alt="Expand" />
</span>
<span className="collectionCollapsed">
<span>{getApiShortDisplayName()}</span>
</span>
</li>
</ul>
</div>
</div>
);
};

View File

@ -1,82 +0,0 @@
import React, { FunctionComponent, MutableRefObject, useEffect, useRef } from "react";
import arrowLeftImg from "../../images/imgarrowlefticon.svg";
import refreshImg from "../../images/refresh-cosmos.svg";
import Explorer from "../Explorer/Explorer";
import { ResourceTree } from "../Explorer/Tree/ResourceTree";
import { userContext } from "../UserContext";
import { getApiShortDisplayName } from "../Utils/APITypeUtils";
import { NormalizedEventKey } from "./Constants";
export interface ResourceTreeContainerProps {
toggleLeftPaneExpanded: () => void;
isLeftPaneExpanded: boolean;
container: Explorer;
}
export const ResourceTreeContainer: FunctionComponent<ResourceTreeContainerProps> = ({
toggleLeftPaneExpanded,
isLeftPaneExpanded,
container,
}: ResourceTreeContainerProps): JSX.Element => {
const focusButton = useRef<HTMLLIElement>() as MutableRefObject<HTMLLIElement>;
useEffect(() => {
if (isLeftPaneExpanded) {
if (focusButton.current) {
focusButton.current.focus();
}
}
});
const onKeyPressToggleLeftPaneExpanded = (event: React.KeyboardEvent) => {
if (event.key === NormalizedEventKey.Space || event.key === NormalizedEventKey.Enter) {
toggleLeftPaneExpanded();
event.stopPropagation();
}
};
return (
<div id="main" className={isLeftPaneExpanded ? "main" : "hiddenMain"}>
{/* Collections Window - - Start */}
<div id="mainslide" className="flexContainer">
{/* Collections Window Title/Command Bar - Start */}
<div className="collectiontitle">
<div className="coltitle">
<span className="titlepadcol">{getApiShortDisplayName()}</span>
<div className="float-right">
<span
className="padimgcolrefresh"
data-test="refreshTree"
role="button"
data-bind="click: onRefreshResourcesClick, clickBubble: false, event: { keypress: onRefreshDatabasesKeyPress }"
tabIndex={0}
aria-label={getApiShortDisplayName() + `Refresh tree`}
title="Refresh tree"
>
<img className="refreshcol" src={refreshImg} alt="Refresh Tree" />
</span>
<span
className="padimgcolrefresh1"
id="expandToggleLeftPaneButton"
role="button"
onClick={toggleLeftPaneExpanded}
onKeyPress={onKeyPressToggleLeftPaneExpanded}
tabIndex={0}
aria-label={getApiShortDisplayName() + `Collapse Tree`}
title="Collapse Tree"
ref={focusButton}
>
<img className="refreshcol1" src={arrowLeftImg} alt="Hide" />
</span>
</div>
</div>
</div>
{userContext.features.enableKoResourceTree ? (
<div style={{ overflowY: "auto" }} data-bind="react:resourceTree" />
) : (
<ResourceTree container={container} />
)}
</div>
{/* Collections Window - End */}
</div>
);
};

View File

@ -0,0 +1,65 @@
import { makeStyles, shorthands, treeItemLevelToken } from "@fluentui/react-components";
import { cosmosShorthands, tokens } from "Explorer/Theme/ThemeUtil";
export type TreeStyleName = keyof ReturnType<typeof useTreeStyles>;
const treeIconWidth = "--cosmos-Tree--iconWidth" as const;
const leafNodeSpacing = "--cosmos-Tree--leafNodeSpacing" as const;
const actionButtonBackground = "--cosmos-Tree--actionButtonBackground" as const;
export const useTreeStyles = makeStyles({
treeContainer: {
height: "100%",
...shorthands.overflow("auto"),
},
tree: {
width: "fit-content",
minWidth: "100%",
rowGap: "0px",
paddingTop: "0px",
[treeIconWidth]: "20px",
[leafNodeSpacing]: "24px",
},
nodeIcon: {
width: `var(${treeIconWidth})`,
height: `var(${treeIconWidth})`,
},
treeItem: {},
nodeLabel: {},
treeItemLayout: {
fontSize: tokens.fontSizeBase300,
height: tokens.layoutRowHeight,
...cosmosShorthands.borderBottom(),
paddingLeft: `calc(var(${treeItemLevelToken}, 1) * ${tokens.spacingHorizontalXXL})`,
// Some sneaky CSS variables stuff to change the background color of the action button on hover.
[actionButtonBackground]: tokens.colorNeutralBackground1,
"&:hover": {
[actionButtonBackground]: tokens.colorNeutralBackground1Hover,
},
},
actionsButtonContainer: {
position: "sticky",
right: 0,
},
actionsButton: {
backgroundColor: `var(${actionButtonBackground})`,
},
treeItemLayoutNoIcon: {
// Pad the text out by the level, the width of the icon, AND the usual spacing between the icon and the level.
// It would be nice to see if we can use Grid layout or something here, but that would require overriding a lot of the existing Tree component behavior.
paddingLeft: `calc((var(${treeItemLevelToken}, 1) * ${tokens.spacingHorizontalXXL}) + var(${leafNodeSpacing}))`,
},
selectedItem: {
backgroundColor: tokens.colorNeutralBackground1Selected,
},
databaseNode: {
fontWeight: tokens.fontWeightSemibold,
},
collectionNode: {
fontWeight: tokens.fontWeightSemibold,
},
loadMoreNode: {
color: tokens.colorBrandForegroundLink,
},
});

View File

@ -1,4 +1,4 @@
import { TreeItem, TreeItemLayout, tokens } from "@fluentui/react-components";
import { TreeItem, TreeItemLayout } from "@fluentui/react-components";
import PromiseSource from "Utils/PromiseSource";
import { mount, shallow } from "enzyme";
import React from "react";
@ -9,7 +9,7 @@ function generateTestNode(id: string, additionalProps?: Partial<TreeNode>): Tree
const node: TreeNode = {
id,
label: `${id}Label`,
className: `${id}Class`,
className: "nodeIcon",
iconSrc: `${id}Icon`,
onClick: jest.fn().mockName(`${id}Click`),
...additionalProps,
@ -20,7 +20,7 @@ function generateTestNode(id: string, additionalProps?: Partial<TreeNode>): Tree
describe("TreeNodeComponent", () => {
it("renders a single node", () => {
const node = generateTestNode("root");
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
expect(component).toMatchSnapshot();
// The "click" handler is actually attached to onOpenChange, with a type of "Click".
@ -45,7 +45,7 @@ describe("TreeNodeComponent", () => {
},
],
});
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
expect(component).toMatchSnapshot();
});
@ -54,7 +54,7 @@ describe("TreeNodeComponent", () => {
const node = generateTestNode("root", {
onExpanded: () => loading.promise,
});
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
act(() => {
component
@ -72,10 +72,7 @@ describe("TreeNodeComponent", () => {
const node = generateTestNode("root", {
isSelected: () => true,
});
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
expect(component.find(TreeItemLayout).props().style?.backgroundColor).toStrictEqual(
tokens.colorNeutralBackground1Selected,
);
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
expect(component).toMatchSnapshot();
});
@ -89,10 +86,7 @@ describe("TreeNodeComponent", () => {
generateTestNode("child2"),
],
});
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
expect(component.find(TreeItemLayout).props().style?.backgroundColor).toStrictEqual(
tokens.colorNeutralBackground1Selected,
);
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
expect(component).toMatchSnapshot();
});
@ -111,7 +105,7 @@ describe("TreeNodeComponent", () => {
generateTestNode("child2"),
],
});
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
expect(component.find(TreeItemLayout).props().style?.backgroundColor).toBeUndefined();
expect(component).toMatchSnapshot();
});
@ -120,7 +114,7 @@ describe("TreeNodeComponent", () => {
const node = generateTestNode("root", {
iconSrc: "the-icon.svg",
});
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
expect(component).toMatchSnapshot();
});
@ -134,7 +128,7 @@ describe("TreeNodeComponent", () => {
generateTestNode("child2"),
],
});
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
expect(component).toMatchSnapshot();
});
@ -148,7 +142,7 @@ describe("TreeNodeComponent", () => {
generateTestNode("child2"),
],
});
const component = shallow(<TreeNodeComponent node={node} treeNodeId={node.id} />);
const component = shallow(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
expect(component).toMatchSnapshot();
});
@ -175,7 +169,7 @@ describe("TreeNodeComponent", () => {
}),
],
});
const component = mount(<TreeNodeComponent node={node} treeNodeId={node.id} />);
const component = mount(<TreeNodeComponent openItems={[]} node={node} treeNodeId={node.id} />);
// Find and expand the child3Expanding node
const expandingChild = component.find(TreeItem).filterWhere((n) => n.props().value === "root/child3ExpandingLabel");

View File

@ -11,11 +11,13 @@ import {
Tree,
TreeItem,
TreeItemLayout,
TreeItemValue,
TreeOpenChangeData,
TreeOpenChangeEvent,
mergeClasses,
} from "@fluentui/react-components";
import { MoreHorizontal20Regular } from "@fluentui/react-icons";
import { tokens } from "@fluentui/react-theme";
import { ChevronDown20Regular, ChevronRight20Regular, MoreHorizontal20Regular } from "@fluentui/react-icons";
import { TreeStyleName, useTreeStyles } from "Explorer/Controls/TreeComponent/Styles";
import * as React from "react";
import { useCallback } from "react";
@ -32,15 +34,14 @@ export interface TreeNode {
id?: string;
children?: TreeNode[];
contextMenu?: TreeNodeMenuItem[];
iconSrc?: string;
iconSrc?: string | JSX.Element;
isExpanded?: boolean;
className?: string;
className?: TreeStyleName;
isAlphaSorted?: boolean;
// data?: any; // Piece of data corresponding to this node
timestamp?: number;
isLeavesParentsSeparate?: boolean; // Display parents together first, then leaves
isLoading?: boolean;
isScrollable?: boolean;
isSelected?: () => boolean;
onClick?: () => void; // Only if a leaf, other click will expand/collapse
onExpanded?: () => Promise<void>;
@ -52,6 +53,7 @@ export interface TreeNodeComponentProps {
node: TreeNode;
className?: string;
treeNodeId: string;
openItems: TreeItemValue[];
}
/** Function that returns true if any descendant (at any depth) of this node is selected. */
@ -66,13 +68,13 @@ function isAnyDescendantSelected(node: TreeNode): boolean {
);
}
const getTreeIcon = (iconSrc: string): JSX.Element => <img src={iconSrc} alt="" style={{ width: 16, height: 16 }} />;
export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
node,
treeNodeId,
openItems,
}: TreeNodeComponentProps): JSX.Element => {
const [isLoading, setIsLoading] = React.useState<boolean>(false);
const treeStyles = useTreeStyles();
const getSortedChildren = (treeNode: TreeNode): TreeNode[] => {
if (!treeNode || !treeNode.children) {
@ -145,45 +147,72 @@ export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
</MenuItem>
));
// We use the expandIcon slot to hold the node icon too.
// We only show a node icon for leaf nodes, even if a branch node has an iconSrc.
const expandIcon = isLoading ? (
<Spinner size="extra-tiny" />
) : !isBranch ? (
typeof node.iconSrc === "string" ? (
<img src={node.iconSrc} className={treeStyles.nodeIcon} alt="" />
) : (
node.iconSrc
)
) : openItems.includes(treeNodeId) ? (
<ChevronDown20Regular />
) : (
<ChevronRight20Regular />
);
const treeItem = (
<TreeItem
data-test={`TreeNodeContainer:${treeNodeId}`}
value={treeNodeId}
itemType={isBranch ? "branch" : "leaf"}
onOpenChange={onOpenChange}
className={treeStyles.treeItem}
>
<TreeItemLayout
className={node.className}
className={mergeClasses(
treeStyles.treeItemLayout,
expandIcon ? undefined : treeStyles.treeItemLayoutNoIcon,
shouldShowAsSelected && treeStyles.selectedItem,
node.className && treeStyles[node.className],
)}
data-test={`TreeNode:${treeNodeId}`}
actions={
contextMenuItems.length > 0 && (
<Menu onOpenChange={onMenuOpenChange}>
<MenuTrigger disableButtonEnhancement>
<Button
aria-label="More options"
data-test="TreeNode/ContextMenuTrigger"
appearance="subtle"
icon={<MoreHorizontal20Regular />}
/>
</MenuTrigger>
<MenuPopover data-test={`TreeNode/ContextMenu:${treeNodeId}`}>
<MenuList>{contextMenuItems}</MenuList>
</MenuPopover>
</Menu>
)
contextMenuItems.length > 0 && {
className: treeStyles.actionsButtonContainer,
children: (
<Menu onOpenChange={onMenuOpenChange}>
<MenuTrigger disableButtonEnhancement>
<Button
aria-label="More options"
className={mergeClasses(treeStyles.actionsButton, shouldShowAsSelected && treeStyles.selectedItem)}
data-test="TreeNode/ContextMenuTrigger"
appearance="subtle"
icon={<MoreHorizontal20Regular />}
/>
</MenuTrigger>
<MenuPopover data-test={`TreeNode/ContextMenu:${treeNodeId}`}>
<MenuList>{contextMenuItems}</MenuList>
</MenuPopover>
</Menu>
),
}
}
expandIcon={isLoading ? <Spinner size="extra-tiny" /> : undefined}
iconBefore={node.iconSrc && getTreeIcon(node.iconSrc)}
style={{
backgroundColor: shouldShowAsSelected ? tokens.colorNeutralBackground1Selected : undefined,
}}
expandIcon={expandIcon}
>
{node.label}
<span className={treeStyles.nodeLabel}>{node.label}</span>
</TreeItemLayout>
{!node.isLoading && node.children?.length > 0 && (
<Tree style={{ overflow: node.isScrollable ? "auto" : undefined }}>
<Tree className={treeStyles.tree}>
{getSortedChildren(node).map((childNode: TreeNode) => (
<TreeNodeComponent key={childNode.label} node={childNode} treeNodeId={`${treeNodeId}/${childNode.label}`} />
<TreeNodeComponent
openItems={openItems}
key={childNode.label}
node={childNode}
treeNodeId={`${treeNodeId}/${childNode.label}`}
/>
))}
</Tree>
)}

View File

@ -393,16 +393,16 @@ export default class Explorer {
return true;
};
public onRefreshResourcesClick = (): void => {
public onRefreshResourcesClick = async (): Promise<void> => {
if (configContext.platform === Platform.Fabric) {
scheduleRefreshDatabaseResourceToken(true).then(() => this.refreshAllDatabases());
return;
}
userContext.authType === AuthType.ResourceToken
await (userContext.authType === AuthType.ResourceToken
? this.refreshDatabaseForResourceToken()
: this.refreshAllDatabases();
this.refreshNotebookList();
: this.refreshAllDatabases());
await this.refreshNotebookList();
};
// Facade

View File

@ -1,9 +1,6 @@
import { KeyboardAction } from "KeyboardShortcuts";
import { ReactTabKind, useTabs } from "hooks/useTabs";
import * as React from "react";
import { useEffect, useState } from "react";
import AddCollectionIcon from "../../../../images/AddCollection.svg";
import AddDatabaseIcon from "../../../../images/AddDatabase.svg";
import AddSqlQueryIcon from "../../../../images/AddSqlQuery_16x16.svg";
import AddStoredProcedureIcon from "../../../../images/AddStoredProcedure.svg";
import AddTriggerIcon from "../../../../images/AddTrigger.svg";
@ -11,7 +8,6 @@ import AddUdfIcon from "../../../../images/AddUdf.svg";
import BrowseQueriesIcon from "../../../../images/BrowseQuery.svg";
import EntraIDIcon from "../../../../images/EntraID.svg";
import FeedbackIcon from "../../../../images/Feedback-Command.svg";
import HomeIcon from "../../../../images/Home_16.svg";
import HostedTerminalIcon from "../../../../images/Hosted-Terminal.svg";
import OpenQueryFromDiskIcon from "../../../../images/OpenQueryFromDisk.svg";
import OpenInTabIcon from "../../../../images/open-in-tab.svg";
@ -22,14 +18,12 @@ import * as Constants from "../../../Common/Constants";
import { Platform, configContext } from "../../../ConfigContext";
import * as ViewModels from "../../../Contracts/ViewModels";
import { userContext } from "../../../UserContext";
import { getCollectionName, getDatabaseName } from "../../../Utils/APITypeUtils";
import { isRunningOnNationalCloud } from "../../../Utils/CloudUtils";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
import Explorer from "../../Explorer";
import { useNotebook } from "../../Notebook/useNotebook";
import { OpenFullScreen } from "../../OpenFullScreen";
import { AddDatabasePanel } from "../../Panes/AddDatabasePanel/AddDatabasePanel";
import { BrowseQueriesPane } from "../../Panes/BrowseQueriesPane/BrowseQueriesPane";
import { LoadQueryPane } from "../../Panes/LoadQueryPane/LoadQueryPane";
import { SettingsPane, useDataPlaneRbac } from "../../Panes/SettingsPane/SettingsPane";
@ -55,42 +49,31 @@ export function createStaticCommandBarButtons(
}
};
if (configContext.platform !== Platform.Fabric) {
const homeBtn = createHomeButton();
buttons.push(homeBtn);
const newCollectionBtn = createNewCollectionGroup(container);
newCollectionBtn.keyboardAction = KeyboardAction.NEW_COLLECTION; // Just for the root button, not the child version we create below.
buttons.push(newCollectionBtn);
if (userContext.apiType !== "Tables" && userContext.apiType !== "Cassandra") {
const addSynapseLink = createOpenSynapseLinkDialogButton(container);
if (addSynapseLink) {
addDivider();
buttons.push(addSynapseLink);
}
if (
configContext.platform !== Platform.Fabric &&
userContext.apiType !== "Tables" &&
userContext.apiType !== "Cassandra"
) {
const addSynapseLink = createOpenSynapseLinkDialogButton(container);
if (addSynapseLink) {
addDivider();
buttons.push(addSynapseLink);
}
}
if (userContext.apiType === "SQL") {
const [loginButtonProps, setLoginButtonProps] = useState<CommandButtonComponentProps | undefined>(undefined);
const dataPlaneRbacEnabled = useDataPlaneRbac((state) => state.dataPlaneRbacEnabled);
const aadTokenUpdated = useDataPlaneRbac((state) => state.aadTokenUpdated);
if (userContext.apiType === "SQL") {
const [loginButtonProps, setLoginButtonProps] = useState<CommandButtonComponentProps | undefined>(undefined);
const dataPlaneRbacEnabled = useDataPlaneRbac((state) => state.dataPlaneRbacEnabled);
const aadTokenUpdated = useDataPlaneRbac((state) => state.aadTokenUpdated);
useEffect(() => {
const buttonProps = createLoginForEntraIDButton(container);
setLoginButtonProps(buttonProps);
}, [dataPlaneRbacEnabled, aadTokenUpdated, container]);
useEffect(() => {
const buttonProps = createLoginForEntraIDButton(container);
setLoginButtonProps(buttonProps);
}, [dataPlaneRbacEnabled, aadTokenUpdated, container]);
if (loginButtonProps) {
addDivider();
buttons.push(loginButtonProps);
}
}
if (userContext.apiType !== "Tables") {
newCollectionBtn.children = [createNewCollectionGroup(container)];
const newDatabaseBtn = createNewDatabase(container);
newCollectionBtn.children.push(newDatabaseBtn);
if (loginButtonProps) {
addDivider();
buttons.push(loginButtonProps);
}
}
@ -260,31 +243,6 @@ function areScriptsSupported(): boolean {
);
}
function createNewCollectionGroup(container: Explorer): CommandButtonComponentProps {
const label = `New ${getCollectionName()}`;
return {
iconSrc: AddCollectionIcon,
iconAlt: label,
onCommandClick: () => container.onNewCollectionClicked(),
commandButtonLabel: label,
ariaLabel: label,
hasPopup: true,
id: "createNewContainerCommandButton",
};
}
function createHomeButton(): CommandButtonComponentProps {
const label = "Home";
return {
iconSrc: HomeIcon,
iconAlt: label,
onCommandClick: () => useTabs.getState().openAndActivateReactTab(ReactTabKind.Home),
commandButtonLabel: label,
hasPopup: false,
ariaLabel: label,
};
}
function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonComponentProps {
if (configContext.platform === Platform.Emulator) {
return undefined;
@ -337,25 +295,6 @@ function createLoginForEntraIDButton(container: Explorer): CommandButtonComponen
};
}
function createNewDatabase(container: Explorer): CommandButtonComponentProps {
const label = "New " + getDatabaseName();
return {
iconSrc: AddDatabaseIcon,
iconAlt: label,
keyboardAction: KeyboardAction.NEW_DATABASE,
onCommandClick: async () => {
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
if (throughputCap && throughputCap !== -1) {
await useDatabases.getState().loadAllOffers();
}
useSidePanel.getState().openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={container} />);
},
commandButtonLabel: label,
ariaLabel: label,
hasPopup: true,
};
}
function createNewSQLQueryButton(selectedNodeState: SelectedNodeState): CommandButtonComponentProps {
if (userContext.apiType === "SQL" || userContext.apiType === "Gremlin") {
const label = "New SQL Query";

View File

@ -212,6 +212,9 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
onChange={handleonChangeDBId}
autoFocus
styles={getTextFieldStyles()}
// We've seen password managers prompt to autofill this field, which is not desired.
data-lpignore={true}
data-1p-ignore={true}
/>
{!isServerlessAccount() && (

View File

@ -35,6 +35,8 @@ exports[`AddDatabasePane Pane should render Default properly 1`] = `
aria-required="true"
autoComplete="off"
autoFocus={true}
data-1p-ignore={true}
data-lpignore={true}
id="database-id"
onChange={[Function]}
pattern="[^/?#\\\\]*[^/?# \\\\]"

312
src/Explorer/Sidebar.tsx Normal file
View File

@ -0,0 +1,312 @@
import {
Button,
Menu,
MenuButtonProps,
MenuItem,
MenuList,
MenuPopover,
MenuTrigger,
SplitButton,
makeStyles,
mergeClasses,
shorthands,
} from "@fluentui/react-components";
import { Add16Regular, ArrowSync12Regular, ChevronLeft12Regular, ChevronRight12Regular } from "@fluentui/react-icons";
import { Platform, configContext } from "ConfigContext";
import Explorer from "Explorer/Explorer";
import { AddDatabasePanel } from "Explorer/Panes/AddDatabasePanel/AddDatabasePanel";
import { Tabs } from "Explorer/Tabs/Tabs";
import { CosmosFluentProvider, cosmosShorthands, tokens } from "Explorer/Theme/ThemeUtil";
import { ResourceTree } from "Explorer/Tree/ResourceTree";
import { useDatabases } from "Explorer/useDatabases";
import { KeyboardAction, KeyboardActionGroup, KeyboardActionHandler, useKeyboardActionGroup } from "KeyboardShortcuts";
import { userContext } from "UserContext";
import { getCollectionName, getDatabaseName } from "Utils/APITypeUtils";
import { Allotment, AllotmentHandle } from "allotment";
import { useSidePanel } from "hooks/useSidePanel";
import { debounce } from "lodash";
import React, { useCallback, useEffect, useMemo, useRef } from "react";
const useSidebarStyles = makeStyles({
sidebar: {
height: "100%",
},
sidebarContainer: {
height: "100%",
backgroundColor: tokens.colorNeutralBackground1,
},
expandedContent: {
display: "grid",
height: "100%",
gridTemplateRows: `calc(${tokens.layoutRowHeight} * 2) 1fr`,
},
floatingControlsContainer: {
position: "relative",
zIndex: 1000,
width: "100%",
},
floatingControls: {
display: "flex",
flexDirection: "row",
position: "absolute",
right: 0,
},
floatingControlButton: {
...shorthands.border("none"),
backgroundColor: "transparent",
},
globalCommandsContainer: {
display: "grid",
alignItems: "center",
justifyItems: "center",
width: "100%",
...cosmosShorthands.borderBottom(),
},
loadingProgressBar: {
// Float above the content
position: "absolute",
width: "100%",
height: "2px",
zIndex: 2000,
backgroundColor: tokens.colorCompoundBrandBackground,
animationIterationCount: "infinite",
animationDuration: "3s",
animationName: {
"0%": {
opacity: ".2", // matches indeterminate bar width
},
"50%": {
opacity: "1",
},
"100%": {
opacity: ".2",
},
},
},
});
interface GlobalCommandsProps {
explorer: Explorer;
}
interface GlobalCommand {
id: string;
label: string;
icon: JSX.Element;
onClick: () => void;
keyboardAction?: KeyboardAction;
}
const GlobalCommands: React.FC<GlobalCommandsProps> = ({ explorer }) => {
const styles = useSidebarStyles();
const actions = useMemo<GlobalCommand[]>(() => {
if (
configContext.platform === Platform.Fabric ||
userContext.apiType === "Postgres" ||
userContext.apiType === "VCoreMongo"
) {
// No Global Commands for these API types.
// In fact, no sidebar for Postgres or VCoreMongo at all, but just in case, we check here anyway.
return [];
}
const actions: GlobalCommand[] = [
{
id: "new_collection",
label: `New ${getCollectionName()}`,
icon: <Add16Regular />,
onClick: () => explorer.onNewCollectionClicked(),
keyboardAction: KeyboardAction.NEW_COLLECTION,
},
];
if (userContext.apiType !== "Tables") {
actions.push({
id: "new_database",
label: `New ${getDatabaseName()}`,
icon: <Add16Regular />,
onClick: async () => {
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
if (throughputCap && throughputCap !== -1) {
await useDatabases.getState().loadAllOffers();
}
useSidePanel.getState().openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={explorer} />);
},
keyboardAction: KeyboardAction.NEW_DATABASE,
});
}
return actions;
}, [explorer]);
const primaryAction = useMemo(() => (actions.length > 0 ? actions[0] : undefined), [actions]);
const onPrimaryActionClick = useCallback(() => primaryAction?.onClick(), [primaryAction]);
const setKeyboardActions = useKeyboardActionGroup(KeyboardActionGroup.GLOBAL_COMMANDS);
useEffect(() => {
setKeyboardActions(
actions.reduce(
(acc, action) => {
if (action.keyboardAction) {
acc[action.keyboardAction] = action.onClick;
}
return acc;
},
{} as Record<KeyboardAction, KeyboardActionHandler>,
),
);
}, [actions, setKeyboardActions]);
if (!primaryAction) {
return null;
}
return (
<div className={styles.globalCommandsContainer} data-test="GlobalCommands">
{actions.length === 1 ? (
<Button icon={primaryAction.icon} onClick={onPrimaryActionClick}>
{primaryAction.label}
</Button>
) : (
<Menu positioning="below-end">
<MenuTrigger disableButtonEnhancement>
{(triggerProps: MenuButtonProps) => (
<SplitButton
menuButton={{ ...triggerProps, "aria-label": "More commands" }}
primaryActionButton={{ onClick: onPrimaryActionClick }}
icon={primaryAction.icon}
>
{primaryAction.label}
</SplitButton>
)}
</MenuTrigger>
<MenuPopover>
<MenuList>
{actions.map((action) => (
<MenuItem key={action.id} icon={action.icon} onClick={action.onClick}>
{action.label}
</MenuItem>
))}
</MenuList>
</MenuPopover>
</Menu>
)}
</div>
);
};
interface SidebarProps {
explorer: Explorer;
}
const CollapseThreshold = 50;
export const SidebarContainer: React.FC<SidebarProps> = ({ explorer }) => {
const styles = useSidebarStyles();
const [expanded, setExpanded] = React.useState(true);
const [loading, setLoading] = React.useState(false);
const [expandedSize, setExpandedSize] = React.useState(300);
const hasSidebar = userContext.apiType !== "Postgres" && userContext.apiType !== "VCoreMongo";
const allotment = useRef<AllotmentHandle>(null);
const expand = useCallback(() => {
if (!expanded) {
allotment.current.resize([expandedSize, Infinity]);
setExpanded(true);
}
}, [expanded, expandedSize, setExpanded]);
const collapse = useCallback(() => {
if (expanded) {
allotment.current.resize([24, Infinity]);
setExpanded(false);
}
}, [expanded, setExpanded]);
const onChange = debounce((sizes: number[]) => {
if (expanded && sizes[0] <= CollapseThreshold) {
collapse();
} else if (!expanded && sizes[0] > CollapseThreshold) {
expand();
}
}, 10);
const onDragEnd = useCallback(
(sizes: number[]) => {
if (expanded) {
// Remember the last size we had when expanded
setExpandedSize(sizes[0]);
} else {
allotment.current.resize([24, Infinity]);
}
},
[expanded, setExpandedSize],
);
const onRefreshClick = useCallback(async () => {
setLoading(true);
await explorer.onRefreshResourcesClick();
setLoading(false);
}, [setLoading]);
return (
<Allotment ref={allotment} onChange={onChange} onDragEnd={onDragEnd} className="resourceTreeAndTabs">
{/* Collections Tree - Start */}
{hasSidebar && (
// When collapsed, we force the pane to 24 pixels wide and make it non-resizable.
<Allotment.Pane minSize={24} preferredSize={300}>
<CosmosFluentProvider className={mergeClasses(styles.sidebar)}>
<div className={styles.sidebarContainer}>
{loading && (
// The Fluent UI progress bar has some issues in reduced-motion environments so we use a simple CSS animation here.
// https://github.com/microsoft/fluentui/issues/29076
<div className={styles.loadingProgressBar} title="Refreshing tree..." />
)}
{expanded ? (
<>
<div className={styles.floatingControlsContainer}>
<div className={styles.floatingControls}>
<button
type="button"
className={styles.floatingControlButton}
disabled={loading}
title="Refresh"
onClick={onRefreshClick}
>
<ArrowSync12Regular />
</button>
<button
type="button"
className={styles.floatingControlButton}
title="Collapse sidebar"
onClick={() => collapse()}
>
<ChevronLeft12Regular />
</button>
</div>
</div>
<div className={styles.expandedContent}>
<GlobalCommands explorer={explorer} />
<ResourceTree explorer={explorer} />
</div>
</>
) : (
<button
type="button"
className={styles.floatingControlButton}
title="Expand sidebar"
onClick={() => expand()}
>
<ChevronRight12Regular />
</button>
)}
</div>
</CosmosFluentProvider>
</Allotment.Pane>
)}
<Allotment.Pane minSize={800}>
<Tabs explorer={explorer} />
</Allotment.Pane>
</Allotment>
);
};

View File

@ -1,7 +1,6 @@
import { Item, ItemDefinition, PartitionKey, PartitionKeyDefinition, QueryIterator, Resource } from "@azure/cosmos";
import { Button, FluentProvider, Input, TableRowId } from "@fluentui/react-components";
import { Button, Input, TableRowId, makeStyles, shorthands } from "@fluentui/react-components";
import { ArrowClockwise16Filled, Dismiss16Filled } from "@fluentui/react-icons";
import Split from "@uiw/react-split";
import { KeyCodes, QueryCopilotSampleContainerId, QueryCopilotSampleDatabaseId } from "Common/Constants";
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
import MongoUtility from "Common/MongoUtility";
@ -21,7 +20,7 @@ import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
import Explorer from "Explorer/Explorer";
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
import { querySampleDocuments, readSampleDocument } from "Explorer/QueryCopilot/QueryCopilotUtilities";
import { getPlatformTheme } from "Explorer/Theme/ThemeUtil";
import { CosmosFluentProvider, LayoutConstants, cosmosShorthands, tokens } from "Explorer/Theme/ThemeUtil";
import { useSelectedNode } from "Explorer/useSelectedNode";
import { KeyboardAction, KeyboardActionGroup, useKeyboardActionGroup } from "KeyboardShortcuts";
import { QueryConstants } from "Shared/Constants";
@ -29,9 +28,9 @@ import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
import { Action } from "Shared/Telemetry/TelemetryConstants";
import { userContext } from "UserContext";
import { logConsoleError } from "Utils/NotificationConsoleUtils";
import { Allotment } from "allotment";
import React, { KeyboardEventHandler, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { format } from "react-string-format";
import { CSSProperties } from "styled-components";
import DeleteDocumentIcon from "../../../../images/DeleteDocument.svg";
import NewDocumentIcon from "../../../../images/NewDocument.svg";
import UploadIcon from "../../../../images/Upload_16x16.svg";
@ -51,6 +50,61 @@ import ObjectId from "../../Tree/ObjectId";
import TabsBase from "../TabsBase";
import { DocumentsTableComponent, DocumentsTableComponentItem } from "./DocumentsTableComponent";
const loadMoreHeight = LayoutConstants.rowHeight;
export const useDocumentsTabStyles = makeStyles({
container: {
height: "100%",
},
filterRow: {
minHeight: tokens.layoutRowHeight,
fontSize: tokens.fontSizeBase200,
display: "flex",
columnGap: tokens.spacingHorizontalS,
paddingLeft: tokens.spacingHorizontalS,
paddingRight: tokens.spacingHorizontalL,
alignItems: "center",
...cosmosShorthands.borderBottom(),
},
filterInput: {
flexGrow: 1,
},
appliedFilter: {
flexGrow: 1,
},
tableContainer: {
marginRight: tokens.spacingHorizontalXXXL,
},
tableRow: {
height: tokens.layoutRowHeight,
},
tableCell: {
...cosmosShorthands.borderLeft(),
},
loadMore: {
...cosmosShorthands.borderTop(),
display: "grid",
alignItems: "center",
justifyItems: "center",
width: "100%",
height: `${loadMoreHeight}px`,
textAlign: "center",
":focus": {
...shorthands.outline("1px", "dotted"),
},
},
floatingControlsContainer: {
position: "relative",
},
floatingControls: {
position: "absolute",
top: "6px",
right: 0,
float: "right",
backgroundColor: "white",
zIndex: 1,
},
});
export class DocumentsTabV2 extends TabsBase {
public partitionKey: DataModels.PartitionKey;
private documentIds: DocumentId[];
@ -95,10 +149,6 @@ const RESET_INDEX = -1;
// Auto-select first row. Define as constant to show where first row is selected
export const INITIAL_SELECTED_ROW_INDEX = 0;
const filterButtonStyle: CSSProperties = {
marginLeft: 8,
};
// From TabsBase.renderObjectForEditor()
let renderObjectForEditor = (
value: unknown,
@ -458,6 +508,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
const [documentIds, setDocumentIds] = useState<DocumentId[]>([]);
const [isExecuting, setIsExecuting] = useState<boolean>(false);
const filterInput = useRef<HTMLInputElement>(null);
const styles = useDocumentsTabStyles();
// Query
const [documentsIterator, setDocumentsIterator] = useState<{
@ -1296,12 +1347,12 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
if (!tableContainerRef.current) {
return undefined;
}
const resizeObserver = new ResizeObserver(() =>
const resizeObserver = new ResizeObserver(() => {
setTableContainerSizePx({
height: tableContainerRef.current.offsetHeight,
height: tableContainerRef.current.offsetHeight - loadMoreHeight,
width: tableContainerRef.current.offsetWidth,
}),
);
});
});
resizeObserver.observe(tableContainerRef.current);
return () => resizeObserver.disconnect(); // clean up
}, []);
@ -1680,146 +1731,130 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
);
return (
<FluentProvider theme={getPlatformTheme(configContext.platform)} style={{ height: "100%" }}>
<div className="tab-pane active documentsTab" role="tabpanel" style={{ display: "flex" }}>
<CosmosFluentProvider className={styles.container}>
<div className="tab-pane active" role="tabpanel" style={{ display: "flex" }}>
{isFilterCreated && (
<div className="filterdivs">
<>
{!isFilterExpanded && !isPreferredApiMongoDB && (
<div className="filterDocCollapsed">
<span className="selectQuery">SELECT * FROM c</span>
<span className="appliedQuery">{appliedFilter}</span>
<Button appearance="primary" onClick={onShowFilterClick} style={filterButtonStyle}>
<div className={styles.filterRow}>
<span>SELECT * FROM c</span>
<span className={styles.appliedFilter}>{appliedFilter}</span>
<Button appearance="primary" size="small" onClick={onShowFilterClick}>
Edit Filter
</Button>
</div>
)}
{!isFilterExpanded && isPreferredApiMongoDB && (
<div className="filterDocCollapsed">
{appliedFilter.length > 0 && <span className="selectQuery">Filter :</span>}
<div className={styles.filterRow}>
{appliedFilter.length > 0 && <span>Filter :</span>}
{!(appliedFilter.length > 0) && <span className="noFilterApplied">No filter applied</span>}
<span className="appliedQuery">{appliedFilter}</span>
<Button style={filterButtonStyle} appearance="primary" onClick={onShowFilterClick}>
<span className={styles.appliedFilter}>{appliedFilter}</span>
<Button appearance="primary" size="small" onClick={onShowFilterClick}>
Edit Filter
</Button>
</div>
)}
{isFilterExpanded && (
<div className="filterDocExpanded">
<div>
<div className="editFilterContainer">
{!isPreferredApiMongoDB && <span className="filterspan"> SELECT * FROM c </span>}
<Input
id="filterInput"
ref={filterInput}
type="text"
list="filtersList"
className={`${filterContent.length === 0 ? "placeholderVisible" : ""}`}
style={{ width: "100%" }}
title="Type a query predicate or choose one from the list."
placeholder={
isPreferredApiMongoDB
? "Type a query predicate (e.g., {´a´:´foo´}), or choose one from the drop down list, or leave empty to query all documents."
: "Type a query predicate (e.g., WHERE c.id=´1´), or choose one from the drop down list, or leave empty to query all documents."
}
value={filterContent}
autoFocus={true}
onKeyDown={onFilterKeyDown}
onChange={(e) => setFilterContent(e.target.value)}
onBlur={() => setIsFilterFocused(false)}
/>
<div className={styles.filterRow}>
{!isPreferredApiMongoDB && <span> SELECT * FROM c </span>}
<Input
id="filterInput"
ref={filterInput}
type="text"
size="small"
list="filtersList"
className={styles.filterInput}
title="Type a query predicate or choose one from the list."
placeholder={
isPreferredApiMongoDB
? "Type a query predicate (e.g., {´a´:´foo´}), or choose one from the drop down list, or leave empty to query all documents."
: "Type a query predicate (e.g., WHERE c.id=´1´), or choose one from the drop down list, or leave empty to query all documents."
}
value={filterContent}
autoFocus={true}
onKeyDown={onFilterKeyDown}
onChange={(e) => setFilterContent(e.target.value)}
onBlur={() => setIsFilterFocused(false)}
/>
<datalist id="filtersList">
{lastFilterContents.map((filter) => (
<option key={filter} value={filter} />
))}
</datalist>
<datalist id="filtersList">
{lastFilterContents.map((filter) => (
<option key={filter} value={filter} />
))}
</datalist>
<span className="filterbuttonpad">
<Button
appearance="primary"
style={filterButtonStyle}
onClick={() => refreshDocumentsGrid(true)}
disabled={!applyFilterButton.enabled}
aria-label="Apply filter"
tabIndex={0}
>
Apply Filter
</Button>
</span>
<span className="filterbuttonpad">
{!isPreferredApiMongoDB && isExecuting && (
<Button
style={filterButtonStyle}
appearance="primary"
aria-label="Cancel Query"
onClick={() => queryAbortController.abort()}
tabIndex={0}
>
Cancel Query
</Button>
)}
</span>
<Button
appearance="primary"
size="small"
onClick={() => refreshDocumentsGrid(true)}
disabled={!applyFilterButton.enabled}
aria-label="Apply filter"
tabIndex={0}
>
Apply Filter
</Button>
{!isPreferredApiMongoDB && isExecuting && (
<Button
appearance="primary"
size="small"
aria-label="Cancel Query"
onClick={() => queryAbortController.abort()}
tabIndex={0}
>
Cancel Query
</Button>
)}
<Button
aria-label="close filter"
tabIndex={0}
onClick={onHideFilterClick}
onKeyDown={onCloseButtonKeyDown}
appearance="transparent"
size="small"
icon={<Dismiss16Filled />}
/>
</div>
)}
</>
)}
{/* <Split> doesn't like to be a flex child */}
<div style={{ overflow: "hidden", height: "100%" }}>
<Allotment>
<Allotment.Pane preferredSize="35%" minSize={175}>
<div style={{ height: "100%", width: "100%", overflow: "hidden" }} ref={tableContainerRef}>
<div className={styles.floatingControlsContainer}>
<div className={styles.floatingControls}>
<Button
aria-label="close filter"
tabIndex={0}
onClick={onHideFilterClick}
onKeyDown={onCloseButtonKeyDown}
appearance="transparent"
icon={<Dismiss16Filled />}
aria-label="Refresh"
size="small"
icon={<ArrowClockwise16Filled />}
style={{
color: StyleConstants.AccentMedium,
}}
onClick={() => refreshDocumentsGrid(false)}
onKeyDown={onRefreshKeyInput}
/>
</div>
</div>
</div>
)}
</div>
)}
{/* <Split> doesn't like to be a flex child */}
<div style={{ overflow: "hidden", height: "100%" }}>
<Split>
<div
style={{ minWidth: 120, width: "35%", overflow: "hidden", position: "relative" }}
ref={tableContainerRef}
>
<Button
appearance="transparent"
aria-label="Refresh"
size="small"
icon={<ArrowClockwise16Filled />}
style={{
position: "absolute",
top: 6,
right: 0,
float: "right",
backgroundColor: "white",
zIndex: 1,
color: StyleConstants.AccentMedium,
}}
onClick={() => refreshDocumentsGrid(false)}
onKeyDown={onRefreshKeyInput}
/>
<div
style={
{
height: "100%",
width: "calc(100% - 50px)",
} /* Fix to make table not resize beyond parent's width */
}
>
<DocumentsTableComponent
items={tableItems}
onItemClicked={(index) => onDocumentClicked(index, documentIds)}
onSelectedRowsChange={onSelectedRowsChange}
selectedRows={selectedRows}
size={tableContainerSizePx}
columnHeaders={columnHeaders}
isSelectionDisabled={
(partitionKey.systemKey && !isPreferredApiMongoDB) ||
(configContext.platform === Platform.Fabric && userContext.fabricContext?.isReadOnly)
}
/>
<div className={styles.tableContainer}>
<DocumentsTableComponent
items={tableItems}
onItemClicked={(index) => onDocumentClicked(index, documentIds)}
onSelectedRowsChange={onSelectedRowsChange}
selectedRows={selectedRows}
size={tableContainerSizePx}
columnHeaders={columnHeaders}
isSelectionDisabled={
(partitionKey.systemKey && !isPreferredApiMongoDB) ||
(configContext.platform === Platform.Fabric && userContext.fabricContext?.isReadOnly)
}
/>
</div>
{tableItems.length > 0 && (
<a
className="loadMore"
className={styles.loadMore}
role="button"
tabIndex={0}
onClick={() => loadNextPage(documentsIterator.iterator, false)}
@ -1829,26 +1864,28 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
</a>
)}
</div>
</div>
<div style={{ minWidth: "20%", width: "100%" }}>
{isTabActive && selectedDocumentContent && selectedRows.size <= 1 && (
<EditorReact
language={"json"}
content={selectedDocumentContent}
isReadOnly={false}
ariaLabel={"Document editor"}
lineNumbers={"on"}
theme={"_theme"}
onContentChanged={_onEditorContentChange}
/>
)}
{selectedRows.size > 1 && (
<span style={{ margin: 10 }}>Number of selected documents: {selectedRows.size}</span>
)}
</div>
</Split>
</Allotment.Pane>
<Allotment.Pane preferredSize="65%" minSize={300}>
<div style={{ height: "100%", width: "100%" }}>
{isTabActive && selectedDocumentContent && selectedRows.size <= 1 && (
<EditorReact
language={"json"}
content={selectedDocumentContent}
isReadOnly={false}
ariaLabel={"Document editor"}
lineNumbers={"on"}
theme={"_theme"}
onContentChanged={_onEditorContentChange}
/>
)}
{selectedRows.size > 1 && (
<span style={{ margin: 10 }}>Number of selected documents: {selectedRows.size}</span>
)}
</div>
</Allotment.Pane>
</Allotment>
</div>
</div>
</FluentProvider>
</CosmosFluentProvider>
);
};

View File

@ -23,8 +23,9 @@ import {
useTableSelection,
} from "@fluentui/react-components";
import { NormalizedEventKey } from "Common/Constants";
import { INITIAL_SELECTED_ROW_INDEX } from "Explorer/Tabs/DocumentsTabV2/DocumentsTabV2";
import { INITIAL_SELECTED_ROW_INDEX, useDocumentsTabStyles } from "Explorer/Tabs/DocumentsTabV2/DocumentsTabV2";
import { selectionHelper } from "Explorer/Tabs/DocumentsTabV2/SelectionHelper";
import { LayoutConstants } from "Explorer/Theme/ThemeUtil";
import { isEnvironmentCtrlPressed, isEnvironmentShiftPressed } from "Utils/KeyboardUtils";
import React, { useCallback, useMemo } from "react";
import { FixedSizeList as List, ListChildComponentProps } from "react-window";
@ -67,6 +68,8 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
columnHeaders,
isSelectionDisabled,
}: IDocumentsTableComponentProps) => {
const styles = useDocumentsTabStyles();
const initialSizingOptions: TableColumnSizingOptions = {
id: {
idealWidth: 280,
@ -168,7 +171,12 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
return (
<TableRow
aria-rowindex={index + 2}
style={{ ...style, cursor: "pointer", userSelect: "none" }}
className={styles.tableRow}
style={{
...style,
cursor: "pointer",
userSelect: "none",
}}
key={item.id}
aria-selected={selected}
appearance={appearance}
@ -187,7 +195,7 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
{columns.map((column) => (
<TableCell
key={column.columnId}
className="documentsTableCell"
className={styles.tableCell}
// When clicking on a cell with shift/ctrl key, onKeyDown is called instead of onClick.
onClick={(e: React.MouseEvent<Element, MouseEvent>) => onTableCellClicked(e, index)}
onKeyPress={(e: React.KeyboardEvent<Element>) => onIdClicked(e, index)}
@ -258,15 +266,15 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
role: "grid",
...columnSizing.getTableProps(),
...keyboardNavAttr,
size: "extra-small",
size: "small",
ref: tableRef,
...style,
};
return (
<Table className="documentsTable" noNativeElements {...tableProps}>
<TableHeader className="documentsTableHeader">
<TableRow style={{ width: size ? size.width - 15 : "100%" }}>
<Table noNativeElements {...tableProps}>
<TableHeader>
<TableRow className={styles.tableRow} style={{ width: size ? size.width - 15 : "100%" }}>
{!isSelectionDisabled && (
<TableSelectionCell
checked={allRowsSelected ? true : someRowsSelected ? "mixed" : false}
@ -279,7 +287,7 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
<Menu openOnContext key={column.columnId}>
<MenuTrigger>
<TableHeaderCell
className="documentsTableCell"
className={styles.tableCell}
key={column.columnId}
{...columnSizing.getTableHeaderCellProps(column.columnId)}
>
@ -299,9 +307,9 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
</TableHeader>
<TableBody>
<List
height={size !== undefined ? size.height - 32 /* table header */ - 21 /* load more */ : 0}
height={size !== undefined ? size.height - LayoutConstants.rowHeight /* table header */ : 0}
itemCount={items.length}
itemSize={30}
itemSize={LayoutConstants.rowHeight}
width={size ? size.width : 0}
itemData={rows}
style={{ overflowY: "scroll" }}

View File

@ -1,455 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Documents tab (noSql API) when rendered should render the page 1`] = `
<FluentProvider
style={
{
"height": "100%",
}
}
theme={
{
"borderRadiusCircular": "10000px",
"borderRadiusLarge": "6px",
"borderRadiusMedium": "4px",
"borderRadiusNone": "0",
"borderRadiusSmall": "2px",
"borderRadiusXLarge": "8px",
"colorBackgroundOverlay": "rgba(0, 0, 0, 0.4)",
"colorBrandBackground": "#117865",
"colorBrandBackground2": "#e3f7ef",
"colorBrandBackground2Hover": "#c0ecdd",
"colorBrandBackground2Pressed": "#78d3b9",
"colorBrandBackground3Static": "#0a5c50",
"colorBrandBackground4Static": "#033f38",
"colorBrandBackgroundHover": "#0c695a",
"colorBrandBackgroundInverted": "#ffffff",
"colorBrandBackgroundInvertedHover": "#e3f7ef",
"colorBrandBackgroundInvertedPressed": "#9ee0cb",
"colorBrandBackgroundInvertedSelected": "#c0ecdd",
"colorBrandBackgroundPressed": "#033f38",
"colorBrandBackgroundSelected": "#0a5c50",
"colorBrandBackgroundStatic": "#117865",
"colorBrandForeground1": "#117865",
"colorBrandForeground2": "#0c695a",
"colorBrandForeground2Hover": "#0a5c50",
"colorBrandForeground2Pressed": "#01322E",
"colorBrandForegroundInverted": "#2aaC94",
"colorBrandForegroundInvertedHover": "#3abb9f",
"colorBrandForegroundInvertedPressed": "#2aaC94",
"colorBrandForegroundLink": "#0c695a",
"colorBrandForegroundLinkHover": "#0a5c50",
"colorBrandForegroundLinkPressed": "#033f38",
"colorBrandForegroundLinkSelected": "#0c695a",
"colorBrandForegroundOnLight": "#117865",
"colorBrandForegroundOnLightHover": "#0c695a",
"colorBrandForegroundOnLightPressed": "#054d43",
"colorBrandForegroundOnLightSelected": "#0a5c50",
"colorBrandShadowAmbient": "rgba(0,0,0,0.30)",
"colorBrandShadowKey": "rgba(0,0,0,0.25)",
"colorBrandStroke1": "#117865",
"colorBrandStroke2": "#9ee0cb",
"colorBrandStroke2Contrast": "#9ee0cb",
"colorBrandStroke2Hover": "#52c7aa",
"colorBrandStroke2Pressed": "#117865",
"colorCompoundBrandBackground": "#117865",
"colorCompoundBrandBackgroundHover": "#0c695a",
"colorCompoundBrandBackgroundPressed": "#0a5c50",
"colorCompoundBrandForeground1": "#117865",
"colorCompoundBrandForeground1Hover": "#0c695a",
"colorCompoundBrandForeground1Pressed": "#0a5c50",
"colorCompoundBrandStroke": "#117865",
"colorCompoundBrandStrokeHover": "#0c695a",
"colorCompoundBrandStrokePressed": "#0a5c50",
"colorNeutralBackground1": "#ffffff",
"colorNeutralBackground1Hover": "#f5f5f5",
"colorNeutralBackground1Pressed": "#e0e0e0",
"colorNeutralBackground1Selected": "#ebebeb",
"colorNeutralBackground2": "#fafafa",
"colorNeutralBackground2Hover": "#f0f0f0",
"colorNeutralBackground2Pressed": "#dbdbdb",
"colorNeutralBackground2Selected": "#e6e6e6",
"colorNeutralBackground3": "#f5f5f5",
"colorNeutralBackground3Hover": "#ebebeb",
"colorNeutralBackground3Pressed": "#d6d6d6",
"colorNeutralBackground3Selected": "#e0e0e0",
"colorNeutralBackground4": "#f0f0f0",
"colorNeutralBackground4Hover": "#fafafa",
"colorNeutralBackground4Pressed": "#f5f5f5",
"colorNeutralBackground4Selected": "#ffffff",
"colorNeutralBackground5": "#ebebeb",
"colorNeutralBackground5Hover": "#f5f5f5",
"colorNeutralBackground5Pressed": "#f0f0f0",
"colorNeutralBackground5Selected": "#fafafa",
"colorNeutralBackground6": "#e6e6e6",
"colorNeutralBackgroundAlpha": "rgba(255, 255, 255, 0.5)",
"colorNeutralBackgroundAlpha2": "rgba(255, 255, 255, 0.8)",
"colorNeutralBackgroundDisabled": "#f0f0f0",
"colorNeutralBackgroundInverted": "#292929",
"colorNeutralBackgroundInvertedDisabled": "rgba(255, 255, 255, 0.1)",
"colorNeutralBackgroundStatic": "#333333",
"colorNeutralCardBackground": "#fafafa",
"colorNeutralCardBackgroundDisabled": "#f0f0f0",
"colorNeutralCardBackgroundHover": "#ffffff",
"colorNeutralCardBackgroundPressed": "#f5f5f5",
"colorNeutralCardBackgroundSelected": "#ebebeb",
"colorNeutralForeground1": "#242424",
"colorNeutralForeground1Hover": "#242424",
"colorNeutralForeground1Pressed": "#242424",
"colorNeutralForeground1Selected": "#242424",
"colorNeutralForeground1Static": "#242424",
"colorNeutralForeground2": "#424242",
"colorNeutralForeground2BrandHover": "#117865",
"colorNeutralForeground2BrandPressed": "#0c695a",
"colorNeutralForeground2BrandSelected": "#117865",
"colorNeutralForeground2Hover": "#242424",
"colorNeutralForeground2Link": "#424242",
"colorNeutralForeground2LinkHover": "#242424",
"colorNeutralForeground2LinkPressed": "#242424",
"colorNeutralForeground2LinkSelected": "#242424",
"colorNeutralForeground2Pressed": "#242424",
"colorNeutralForeground2Selected": "#242424",
"colorNeutralForeground3": "#616161",
"colorNeutralForeground3BrandHover": "#117865",
"colorNeutralForeground3BrandPressed": "#0c695a",
"colorNeutralForeground3BrandSelected": "#117865",
"colorNeutralForeground3Hover": "#424242",
"colorNeutralForeground3Pressed": "#424242",
"colorNeutralForeground3Selected": "#424242",
"colorNeutralForeground4": "#707070",
"colorNeutralForegroundDisabled": "#bdbdbd",
"colorNeutralForegroundInverted": "#ffffff",
"colorNeutralForegroundInverted2": "#ffffff",
"colorNeutralForegroundInvertedDisabled": "rgba(255, 255, 255, 0.4)",
"colorNeutralForegroundInvertedHover": "#ffffff",
"colorNeutralForegroundInvertedLink": "#ffffff",
"colorNeutralForegroundInvertedLinkHover": "#ffffff",
"colorNeutralForegroundInvertedLinkPressed": "#ffffff",
"colorNeutralForegroundInvertedLinkSelected": "#ffffff",
"colorNeutralForegroundInvertedPressed": "#ffffff",
"colorNeutralForegroundInvertedSelected": "#ffffff",
"colorNeutralForegroundOnBrand": "#ffffff",
"colorNeutralForegroundStaticInverted": "#ffffff",
"colorNeutralShadowAmbient": "rgba(0,0,0,0.12)",
"colorNeutralShadowAmbientDarker": "rgba(0,0,0,0.20)",
"colorNeutralShadowAmbientLighter": "rgba(0,0,0,0.06)",
"colorNeutralShadowKey": "rgba(0,0,0,0.14)",
"colorNeutralShadowKeyDarker": "rgba(0,0,0,0.24)",
"colorNeutralShadowKeyLighter": "rgba(0,0,0,0.07)",
"colorNeutralStencil1": "#e6e6e6",
"colorNeutralStencil1Alpha": "rgba(0, 0, 0, 0.1)",
"colorNeutralStencil2": "#fafafa",
"colorNeutralStencil2Alpha": "rgba(0, 0, 0, 0.05)",
"colorNeutralStroke1": "#d1d1d1",
"colorNeutralStroke1Hover": "#c7c7c7",
"colorNeutralStroke1Pressed": "#b3b3b3",
"colorNeutralStroke1Selected": "#bdbdbd",
"colorNeutralStroke2": "#e0e0e0",
"colorNeutralStroke3": "#f0f0f0",
"colorNeutralStrokeAccessible": "#616161",
"colorNeutralStrokeAccessibleHover": "#575757",
"colorNeutralStrokeAccessiblePressed": "#4d4d4d",
"colorNeutralStrokeAccessibleSelected": "#117865",
"colorNeutralStrokeAlpha": "rgba(0, 0, 0, 0.05)",
"colorNeutralStrokeAlpha2": "rgba(255, 255, 255, 0.2)",
"colorNeutralStrokeDisabled": "#e0e0e0",
"colorNeutralStrokeInvertedDisabled": "rgba(255, 255, 255, 0.4)",
"colorNeutralStrokeOnBrand": "#ffffff",
"colorNeutralStrokeOnBrand2": "#ffffff",
"colorNeutralStrokeOnBrand2Hover": "#ffffff",
"colorNeutralStrokeOnBrand2Pressed": "#ffffff",
"colorNeutralStrokeOnBrand2Selected": "#ffffff",
"colorNeutralStrokeSubtle": "#e0e0e0",
"colorPaletteAnchorBackground2": "#bcc3c7",
"colorPaletteAnchorBorderActive": "#394146",
"colorPaletteAnchorForeground2": "#202427",
"colorPaletteBeigeBackground2": "#d7d4d4",
"colorPaletteBeigeBorderActive": "#7a7574",
"colorPaletteBeigeForeground2": "#444241",
"colorPaletteBerryBackground1": "#fdf5fc",
"colorPaletteBerryBackground2": "#edbbe7",
"colorPaletteBerryBackground3": "#c239b3",
"colorPaletteBerryBorder1": "#edbbe7",
"colorPaletteBerryBorder2": "#c239b3",
"colorPaletteBerryBorderActive": "#c239b3",
"colorPaletteBerryForeground1": "#af33a1",
"colorPaletteBerryForeground2": "#6d2064",
"colorPaletteBerryForeground3": "#c239b3",
"colorPaletteBlueBackground2": "#a9d3f2",
"colorPaletteBlueBorderActive": "#0078d4",
"colorPaletteBlueForeground2": "#004377",
"colorPaletteBrassBackground2": "#e0cea2",
"colorPaletteBrassBorderActive": "#986f0b",
"colorPaletteBrassForeground2": "#553e06",
"colorPaletteBrownBackground2": "#ddc3b0",
"colorPaletteBrownBorderActive": "#8e562e",
"colorPaletteBrownForeground2": "#50301a",
"colorPaletteCornflowerBackground2": "#c8d1fa",
"colorPaletteCornflowerBorderActive": "#4f6bed",
"colorPaletteCornflowerForeground2": "#2c3c85",
"colorPaletteCranberryBackground2": "#eeacb2",
"colorPaletteCranberryBorderActive": "#c50f1f",
"colorPaletteCranberryForeground2": "#6e0811",
"colorPaletteDarkGreenBackground2": "#9ad29a",
"colorPaletteDarkGreenBorderActive": "#0b6a0b",
"colorPaletteDarkGreenForeground2": "#063b06",
"colorPaletteDarkOrangeBackground1": "#fdf6f3",
"colorPaletteDarkOrangeBackground2": "#f4bfab",
"colorPaletteDarkOrangeBackground3": "#da3b01",
"colorPaletteDarkOrangeBorder1": "#f4bfab",
"colorPaletteDarkOrangeBorder2": "#da3b01",
"colorPaletteDarkOrangeBorderActive": "#da3b01",
"colorPaletteDarkOrangeForeground1": "#c43501",
"colorPaletteDarkOrangeForeground2": "#7a2101",
"colorPaletteDarkOrangeForeground3": "#da3b01",
"colorPaletteDarkRedBackground2": "#d69ca5",
"colorPaletteDarkRedBorderActive": "#750b1c",
"colorPaletteDarkRedForeground2": "#420610",
"colorPaletteForestBackground2": "#bdd99b",
"colorPaletteForestBorderActive": "#498205",
"colorPaletteForestForeground2": "#294903",
"colorPaletteGoldBackground2": "#ecdfa5",
"colorPaletteGoldBorderActive": "#c19c00",
"colorPaletteGoldForeground2": "#6c5700",
"colorPaletteGrapeBackground2": "#d9a7e0",
"colorPaletteGrapeBorderActive": "#881798",
"colorPaletteGrapeForeground2": "#4c0d55",
"colorPaletteGreenBackground1": "#f1faf1",
"colorPaletteGreenBackground2": "#9fd89f",
"colorPaletteGreenBackground3": "#107c10",
"colorPaletteGreenBorder1": "#9fd89f",
"colorPaletteGreenBorder2": "#107c10",
"colorPaletteGreenBorderActive": "#107c10",
"colorPaletteGreenForeground1": "#0e700e",
"colorPaletteGreenForeground2": "#094509",
"colorPaletteGreenForeground3": "#107c10",
"colorPaletteGreenForegroundInverted": "#359b35",
"colorPaletteLavenderBackground2": "#d2ccf8",
"colorPaletteLavenderBorderActive": "#7160e8",
"colorPaletteLavenderForeground2": "#3f3682",
"colorPaletteLightGreenBackground1": "#f2fbf2",
"colorPaletteLightGreenBackground2": "#a7e3a5",
"colorPaletteLightGreenBackground3": "#13a10e",
"colorPaletteLightGreenBorder1": "#a7e3a5",
"colorPaletteLightGreenBorder2": "#13a10e",
"colorPaletteLightGreenBorderActive": "#13a10e",
"colorPaletteLightGreenForeground1": "#11910d",
"colorPaletteLightGreenForeground2": "#0b5a08",
"colorPaletteLightGreenForeground3": "#13a10e",
"colorPaletteLightTealBackground2": "#a6e9ed",
"colorPaletteLightTealBorderActive": "#00b7c3",
"colorPaletteLightTealForeground2": "#00666d",
"colorPaletteLilacBackground2": "#e6bfed",
"colorPaletteLilacBorderActive": "#b146c2",
"colorPaletteLilacForeground2": "#63276d",
"colorPaletteMagentaBackground2": "#eca5d1",
"colorPaletteMagentaBorderActive": "#bf0077",
"colorPaletteMagentaForeground2": "#6b0043",
"colorPaletteMarigoldBackground1": "#fefbf4",
"colorPaletteMarigoldBackground2": "#f9e2ae",
"colorPaletteMarigoldBackground3": "#eaa300",
"colorPaletteMarigoldBorder1": "#f9e2ae",
"colorPaletteMarigoldBorder2": "#eaa300",
"colorPaletteMarigoldBorderActive": "#eaa300",
"colorPaletteMarigoldForeground1": "#d39300",
"colorPaletteMarigoldForeground2": "#835b00",
"colorPaletteMarigoldForeground3": "#eaa300",
"colorPaletteMinkBackground2": "#cecccb",
"colorPaletteMinkBorderActive": "#5d5a58",
"colorPaletteMinkForeground2": "#343231",
"colorPaletteNavyBackground2": "#a3b2e8",
"colorPaletteNavyBorderActive": "#0027b4",
"colorPaletteNavyForeground2": "#001665",
"colorPalettePeachBackground2": "#ffddb3",
"colorPalettePeachBorderActive": "#ff8c00",
"colorPalettePeachForeground2": "#8f4e00",
"colorPalettePinkBackground2": "#f7c0e3",
"colorPalettePinkBorderActive": "#e43ba6",
"colorPalettePinkForeground2": "#80215d",
"colorPalettePlatinumBackground2": "#cdd6d8",
"colorPalettePlatinumBorderActive": "#69797e",
"colorPalettePlatinumForeground2": "#3b4447",
"colorPalettePlumBackground2": "#d696c0",
"colorPalettePlumBorderActive": "#77004d",
"colorPalettePlumForeground2": "#43002b",
"colorPalettePumpkinBackground2": "#efc4ad",
"colorPalettePumpkinBorderActive": "#ca5010",
"colorPalettePumpkinForeground2": "#712d09",
"colorPalettePurpleBackground2": "#c6b1de",
"colorPalettePurpleBorderActive": "#5c2e91",
"colorPalettePurpleForeground2": "#341a51",
"colorPaletteRedBackground1": "#fdf6f6",
"colorPaletteRedBackground2": "#f1bbbc",
"colorPaletteRedBackground3": "#d13438",
"colorPaletteRedBorder1": "#f1bbbc",
"colorPaletteRedBorder2": "#d13438",
"colorPaletteRedBorderActive": "#d13438",
"colorPaletteRedForeground1": "#bc2f32",
"colorPaletteRedForeground2": "#751d1f",
"colorPaletteRedForeground3": "#d13438",
"colorPaletteRedForegroundInverted": "#dc5e62",
"colorPaletteRoyalBlueBackground2": "#9abfdc",
"colorPaletteRoyalBlueBorderActive": "#004e8c",
"colorPaletteRoyalBlueForeground2": "#002c4e",
"colorPaletteSeafoamBackground2": "#a8f0cd",
"colorPaletteSeafoamBorderActive": "#00cc6a",
"colorPaletteSeafoamForeground2": "#00723b",
"colorPaletteSteelBackground2": "#94c8d4",
"colorPaletteSteelBorderActive": "#005b70",
"colorPaletteSteelForeground2": "#00333f",
"colorPaletteTealBackground2": "#9bd9db",
"colorPaletteTealBorderActive": "#038387",
"colorPaletteTealForeground2": "#02494c",
"colorPaletteYellowBackground1": "#fffef5",
"colorPaletteYellowBackground2": "#fef7b2",
"colorPaletteYellowBackground3": "#fde300",
"colorPaletteYellowBorder1": "#fef7b2",
"colorPaletteYellowBorder2": "#fde300",
"colorPaletteYellowBorderActive": "#fde300",
"colorPaletteYellowForeground1": "#817400",
"colorPaletteYellowForeground2": "#817400",
"colorPaletteYellowForeground3": "#fde300",
"colorPaletteYellowForegroundInverted": "#fef7b2",
"colorScrollbarOverlay": "rgba(0, 0, 0, 0.5)",
"colorStatusDangerBackground1": "#fdf3f4",
"colorStatusDangerBackground2": "#eeacb2",
"colorStatusDangerBackground3": "#c50f1f",
"colorStatusDangerBackground3Hover": "#b10e1c",
"colorStatusDangerBackground3Pressed": "#960b18",
"colorStatusDangerBorder1": "#eeacb2",
"colorStatusDangerBorder2": "#c50f1f",
"colorStatusDangerBorderActive": "#c50f1f",
"colorStatusDangerForeground1": "#b10e1c",
"colorStatusDangerForeground2": "#6e0811",
"colorStatusDangerForeground3": "#c50f1f",
"colorStatusDangerForegroundInverted": "#dc626d",
"colorStatusSuccessBackground1": "#f1faf1",
"colorStatusSuccessBackground2": "#9fd89f",
"colorStatusSuccessBackground3": "#107c10",
"colorStatusSuccessBorder1": "#9fd89f",
"colorStatusSuccessBorder2": "#107c10",
"colorStatusSuccessBorderActive": "#107c10",
"colorStatusSuccessForeground1": "#0e700e",
"colorStatusSuccessForeground2": "#094509",
"colorStatusSuccessForeground3": "#107c10",
"colorStatusSuccessForegroundInverted": "#54b054",
"colorStatusWarningBackground1": "#fff9f5",
"colorStatusWarningBackground2": "#fdcfb4",
"colorStatusWarningBackground3": "#f7630c",
"colorStatusWarningBorder1": "#fdcfb4",
"colorStatusWarningBorder2": "#bc4b09",
"colorStatusWarningBorderActive": "#f7630c",
"colorStatusWarningForeground1": "#bc4b09",
"colorStatusWarningForeground2": "#8a3707",
"colorStatusWarningForeground3": "#bc4b09",
"colorStatusWarningForegroundInverted": "#faa06b",
"colorStrokeFocus1": "#ffffff",
"colorStrokeFocus2": "#000000",
"colorSubtleBackground": "transparent",
"colorSubtleBackgroundHover": "#f5f5f5",
"colorSubtleBackgroundInverted": "transparent",
"colorSubtleBackgroundInvertedHover": "rgba(0, 0, 0, 0.1)",
"colorSubtleBackgroundInvertedPressed": "rgba(0, 0, 0, 0.3)",
"colorSubtleBackgroundInvertedSelected": "rgba(0, 0, 0, 0.2)",
"colorSubtleBackgroundLightAlphaHover": "rgba(255, 255, 255, 0.7)",
"colorSubtleBackgroundLightAlphaPressed": "rgba(255, 255, 255, 0.5)",
"colorSubtleBackgroundLightAlphaSelected": "transparent",
"colorSubtleBackgroundPressed": "#e0e0e0",
"colorSubtleBackgroundSelected": "#ebebeb",
"colorTransparentBackground": "transparent",
"colorTransparentBackgroundHover": "transparent",
"colorTransparentBackgroundPressed": "transparent",
"colorTransparentBackgroundSelected": "transparent",
"colorTransparentStroke": "transparent",
"colorTransparentStrokeDisabled": "transparent",
"colorTransparentStrokeInteractive": "transparent",
"curveAccelerateMax": "cubic-bezier(0.9,0.1,1,0.2)",
"curveAccelerateMid": "cubic-bezier(1,0,1,1)",
"curveAccelerateMin": "cubic-bezier(0.8,0,0.78,1)",
"curveDecelerateMax": "cubic-bezier(0.1,0.9,0.2,1)",
"curveDecelerateMid": "cubic-bezier(0,0,0,1)",
"curveDecelerateMin": "cubic-bezier(0.33,0,0.1,1)",
"curveEasyEase": "cubic-bezier(0.33,0,0.67,1)",
"curveEasyEaseMax": "cubic-bezier(0.8,0,0.2,1)",
"curveLinear": "cubic-bezier(0,0,1,1)",
"durationFast": "150ms",
"durationFaster": "100ms",
"durationGentle": "250ms",
"durationNormal": "200ms",
"durationSlow": "300ms",
"durationSlower": "400ms",
"durationUltraFast": "50ms",
"durationUltraSlow": "500ms",
"fontFamilyBase": "'Segoe UI', 'Segoe UI Web (West European)', -apple-system, BlinkMacSystemFont, Roboto, 'Helvetica Neue', sans-serif",
"fontFamilyMonospace": "Consolas, 'Courier New', Courier, monospace",
"fontFamilyNumeric": "Bahnschrift, 'Segoe UI', 'Segoe UI Web (West European)', -apple-system, BlinkMacSystemFont, Roboto, 'Helvetica Neue', sans-serif",
"fontSizeBase100": "10px",
"fontSizeBase200": "12px",
"fontSizeBase300": "14px",
"fontSizeBase400": "16px",
"fontSizeBase500": "20px",
"fontSizeBase600": "24px",
"fontSizeHero1000": "68px",
"fontSizeHero700": "28px",
"fontSizeHero800": "32px",
"fontSizeHero900": "40px",
"fontWeightBold": 700,
"fontWeightMedium": 500,
"fontWeightRegular": 400,
"fontWeightSemibold": 600,
"lineHeightBase100": "14px",
"lineHeightBase200": "16px",
"lineHeightBase300": "20px",
"lineHeightBase400": "22px",
"lineHeightBase500": "28px",
"lineHeightBase600": "32px",
"lineHeightHero1000": "92px",
"lineHeightHero700": "36px",
"lineHeightHero800": "40px",
"lineHeightHero900": "52px",
"shadow16": "0 0 2px rgba(0,0,0,0.12), 0 8px 16px rgba(0,0,0,0.14)",
"shadow16Brand": "0 0 2px rgba(0,0,0,0.30), 0 8px 16px rgba(0,0,0,0.25)",
"shadow2": "0 0 2px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.14)",
"shadow28": "0 0 8px rgba(0,0,0,0.12), 0 14px 28px rgba(0,0,0,0.14)",
"shadow28Brand": "0 0 8px rgba(0,0,0,0.30), 0 14px 28px rgba(0,0,0,0.25)",
"shadow2Brand": "0 0 2px rgba(0,0,0,0.30), 0 1px 2px rgba(0,0,0,0.25)",
"shadow4": "0 0 2px rgba(0,0,0,0.12), 0 2px 4px rgba(0,0,0,0.14)",
"shadow4Brand": "0 0 2px rgba(0,0,0,0.30), 0 2px 4px rgba(0,0,0,0.25)",
"shadow64": "0 0 8px rgba(0,0,0,0.12), 0 32px 64px rgba(0,0,0,0.14)",
"shadow64Brand": "0 0 8px rgba(0,0,0,0.30), 0 32px 64px rgba(0,0,0,0.25)",
"shadow8": "0 0 2px rgba(0,0,0,0.12), 0 4px 8px rgba(0,0,0,0.14)",
"shadow8Brand": "0 0 2px rgba(0,0,0,0.30), 0 4px 8px rgba(0,0,0,0.25)",
"spacingHorizontalL": "16px",
"spacingHorizontalM": "12px",
"spacingHorizontalMNudge": "10px",
"spacingHorizontalNone": "0",
"spacingHorizontalS": "8px",
"spacingHorizontalSNudge": "6px",
"spacingHorizontalXL": "20px",
"spacingHorizontalXS": "4px",
"spacingHorizontalXXL": "24px",
"spacingHorizontalXXS": "2px",
"spacingHorizontalXXXL": "32px",
"spacingVerticalL": "16px",
"spacingVerticalM": "12px",
"spacingVerticalMNudge": "10px",
"spacingVerticalNone": "0",
"spacingVerticalS": "8px",
"spacingVerticalSNudge": "6px",
"spacingVerticalXL": "20px",
"spacingVerticalXS": "4px",
"spacingVerticalXXL": "24px",
"spacingVerticalXXS": "2px",
"spacingVerticalXXXL": "32px",
"strokeWidthThick": "2px",
"strokeWidthThicker": "3px",
"strokeWidthThickest": "4px",
"strokeWidthThin": "1px",
}
}
<CosmosFluentProvider
className="___1wp8swp_0000000 f1l02sjl"
>
<div
className="tab-pane active documentsTab"
className="tab-pane active"
role="tabpanel"
style={
{
@ -458,31 +14,21 @@ exports[`Documents tab (noSql API) when rendered should render the page 1`] = `
}
>
<div
className="filterdivs"
className="___11ktxfv_0000000 f1o614cb fy9rknc f22iagw fsnqrgy f1f5gg8d fjodcmx f122n59 f1f09k3d fg706s2 frpde29"
>
<div
className="filterDocCollapsed"
<span>
SELECT * FROM c
</span>
<span
className="___r7kt3y0_0000000 fqerorx"
/>
<Button
appearance="primary"
onClick={[Function]}
size="small"
>
<span
className="selectQuery"
>
SELECT * FROM c
</span>
<span
className="appliedQuery"
/>
<Button
appearance="primary"
onClick={[Function]}
style={
{
"marginLeft": 8,
}
}
>
Edit Filter
</Button>
</div>
Edit Filter
</Button>
</div>
<div
style={
@ -492,77 +38,79 @@ exports[`Documents tab (noSql API) when rendered should render the page 1`] = `
}
}
>
<Split
mode="horizontal"
prefixCls="w-split"
visiable={true}
>
<div
style={
{
"minWidth": 120,
"overflow": "hidden",
"position": "relative",
"width": "35%",
}
}
<Allotment>
<Allotment.Pane
minSize={175}
preferredSize="35%"
>
<Button
appearance="transparent"
aria-label="Refresh"
icon={<ArrowClockwise16Filled />}
onClick={[Function]}
onKeyDown={[Function]}
size="small"
style={
{
"backgroundColor": "white",
"color": undefined,
"float": "right",
"position": "absolute",
"right": 0,
"top": 6,
"zIndex": 1,
}
}
/>
<div
style={
{
"height": "100%",
"width": "calc(100% - 50px)",
"overflow": "hidden",
"width": "100%",
}
}
>
<DocumentsTableComponent
columnHeaders={
{
"idHeader": "id",
"partitionKeyHeaders": [],
<div
className="___77lcry0_0000000 f10pi13n"
>
<div
className="___1rwkz4r_0000000 f1euv43f f1l8gmrm f1e31b4d f150nix6 fy6ml6n f19g0ac"
>
<Button
appearance="transparent"
aria-label="Refresh"
icon={<ArrowClockwise16Filled />}
onClick={[Function]}
onKeyDown={[Function]}
size="small"
style={
{
"color": undefined,
}
}
/>
</div>
</div>
<div
className="___9o87uj0_0000000 ffefeo0"
>
<DocumentsTableComponent
columnHeaders={
{
"idHeader": "id",
"partitionKeyHeaders": [],
}
}
}
isSelectionDisabled={true}
items={[]}
onItemClicked={[Function]}
onSelectedRowsChange={[Function]}
selectedRows={
Set {
0,
isSelectionDisabled={true}
items={[]}
onItemClicked={[Function]}
onSelectedRowsChange={[Function]}
selectedRows={
Set {
0,
}
}
}
/>
/>
</div>
</div>
</div>
<div
style={
{
"minWidth": "20%",
"width": "100%",
</Allotment.Pane>
<Allotment.Pane
minSize={300}
preferredSize="65%"
>
<div
style={
{
"height": "100%",
"width": "100%",
}
}
}
/>
</Split>
/>
</Allotment.Pane>
</Allotment>
</div>
</div>
</FluentProvider>
</CosmosFluentProvider>
`;

View File

@ -39,11 +39,10 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
>
<Table
aria-label="Filtered documents table"
className="documentsTable"
data-tabster="{"mover":{"cyclic":false,"direction":3,"memorizeCurrent":true}}"
noNativeElements={true}
role="grid"
size="extra-small"
size="small"
style={
{
"minWidth": "fit-content",
@ -52,7 +51,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
>
<div
aria-label="Filtered documents table"
className="fui-Table documentsTable ___1kbqy50_18rlg51 fgkb47j fhovq9v ftgm304"
className="fui-Table ___1kbqy50_18rlg51 fgkb47j fhovq9v ftgm304"
data-tabster="{"mover":{"cyclic":false,"direction":3,"memorizeCurrent":true}}"
role="grid"
style={
@ -61,14 +60,13 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
}
}
>
<TableHeader
className="documentsTableHeader"
>
<TableHeader>
<div
className="fui-TableHeader documentsTableHeader ___oeyxrt0_1baslyg ftgm304"
className="fui-TableHeader ___oeyxrt0_1baslyg ftgm304"
role="rowgroup"
>
<TableRow
className="___1flxklq_0000000 f1i800kq"
style={
{
"width": -15,
@ -76,7 +74,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
}
>
<div
className="fui-TableRow ___fjp17h0_1kcw78z f19n0e5 f1ewtqcl f1jazu75 f1xeqee6 f1dxfoyt f2krc9w fy9rknc f22iagw f122n59"
className="fui-TableRow ___1ah7syw_1i43eqh f19n0e5 f1ewtqcl f1jazu75 f1xeqee6 f1dxfoyt f2krc9w f1facbz3 f22iagw f122n59 f1i800kq"
role="row"
style={
{
@ -109,7 +107,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
"triggerId": "menu17",
"triggerRef": {
"current": <div
class="fui-TableHeaderCell documentsTableCell ___g3gp0b0_n32kis0 figsok6 f3gpkru f14ym4q2 f1euou18 f10pi13n f22iagw f1izfyrr f10tiqix"
class="fui-TableHeaderCell ___tygr690_1tp3egy figsok6 f3gpkru f14ym4q2 f1euou18 f10pi13n f22iagw f1izfyrr f10tiqix finvdd3 fjik90z fw35ms5"
data-tabster="{"restorer":{"type":1}}"
id="menu17"
role="columnheader"
@ -155,7 +153,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
role="separator"
/>
}
className="documentsTableCell"
className="___16q6g07_0000000 finvdd3 fjik90z fw35ms5"
data-tabster="{"restorer":{"type":1}}"
id="menu17"
key="id"
@ -172,7 +170,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
}
>
<div
className="fui-TableHeaderCell documentsTableCell ___g3gp0b0_n32kis0 figsok6 f3gpkru f14ym4q2 f1euou18 f10pi13n f22iagw f1izfyrr f10tiqix"
className="fui-TableHeaderCell ___tygr690_1tp3egy figsok6 f3gpkru f14ym4q2 f1euou18 f10pi13n f22iagw f1izfyrr f10tiqix finvdd3 fjik90z fw35ms5"
data-tabster="{"restorer":{"type":1}}"
id="menu17"
onContextMenu={[Function]}
@ -256,7 +254,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
"triggerId": "menu18",
"triggerRef": {
"current": <div
class="fui-TableHeaderCell documentsTableCell ___g3gp0b0_n32kis0 figsok6 f3gpkru f14ym4q2 f1euou18 f10pi13n f22iagw f1izfyrr f10tiqix"
class="fui-TableHeaderCell ___tygr690_1tp3egy figsok6 f3gpkru f14ym4q2 f1euou18 f10pi13n f22iagw f1izfyrr f10tiqix finvdd3 fjik90z fw35ms5"
data-tabster="{"restorer":{"type":1}}"
id="menu18"
role="columnheader"
@ -282,7 +280,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
>
<TableHeaderCell
aside={null}
className="documentsTableCell"
className="___16q6g07_0000000 finvdd3 fjik90z fw35ms5"
data-tabster="{"restorer":{"type":1}}"
id="menu18"
key="partitionKey"
@ -299,7 +297,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
}
>
<div
className="fui-TableHeaderCell documentsTableCell ___g3gp0b0_n32kis0 figsok6 f3gpkru f14ym4q2 f1euou18 f10pi13n f22iagw f1izfyrr f10tiqix"
className="fui-TableHeaderCell ___tygr690_1tp3egy figsok6 f3gpkru f14ym4q2 f1euou18 f10pi13n f22iagw f1izfyrr f10tiqix finvdd3 fjik90z fw35ms5"
data-tabster="{"restorer":{"type":1}}"
id="menu18"
onContextMenu={[Function]}
@ -344,7 +342,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
>
<List
direction="ltr"
height={-53}
height={-36}
itemCount={3}
itemData={
[
@ -383,7 +381,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
},
]
}
itemSize={30}
itemSize={36}
layout="vertical"
overscanCount={2}
style={
@ -400,7 +398,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
{
"WebkitOverflowScrolling": "touch",
"direction": "ltr",
"height": -53,
"height": -36,
"overflow": "auto",
"overflowY": "scroll",
"position": "relative",
@ -412,7 +410,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
<div
style={
{
"height": 90,
"height": 108,
"pointerEvents": undefined,
"width": "100%",
}
@ -460,7 +458,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
key="0"
style={
{
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
@ -473,11 +471,12 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
appearance="none"
aria-rowindex={2}
aria-selected={false}
className="___1flxklq_0000000 f1i800kq"
key="1"
style={
{
"cursor": "pointer",
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
@ -490,12 +489,12 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
<div
aria-rowindex={2}
aria-selected={false}
className="fui-TableRow ___1krbsl3_1c5rp6c f19n0e5 f1ewtqcl f1jazu75 f1xeqee6 f1dxfoyt f2krc9w f1wfn5kd f1g4hkjv f15ngxrw fjbbrdp f1t94bn6 feu1g3u f1uorfem fw60kww f4xjyn1 ff1wgvm fiob0tu f1j6scgf f1x4h75k fy9rknc f22iagw f122n59"
className="fui-TableRow ___1h2me2v_1yag1rz f19n0e5 f1ewtqcl f1jazu75 f1xeqee6 f1dxfoyt f2krc9w f1wfn5kd f1g4hkjv f15ngxrw fjbbrdp f1t94bn6 feu1g3u f1uorfem fw60kww f4xjyn1 ff1wgvm fiob0tu f1j6scgf f1x4h75k f1facbz3 f22iagw f122n59 f1i800kq"
role="row"
style={
{
"cursor": "pointer",
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
@ -506,7 +505,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
}
>
<TableCell
className="documentsTableCell"
className="___16q6g07_0000000 finvdd3 fjik90z fw35ms5"
key="id"
onClick={[Function]}
onKeyPress={[Function]}
@ -520,7 +519,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
tabIndex={0}
>
<div
className="fui-TableCell documentsTableCell ___4rzx8z0_j3d5kl0 f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr f1pha7fy"
className="fui-TableCell ___h9qj4u0_1sv9uyx f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr fcep9tg finvdd3 fjik90z fw35ms5"
onClick={[Function]}
onKeyPress={[Function]}
role="cell"
@ -555,7 +554,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
</div>
</TableCell>
<TableCell
className="documentsTableCell"
className="___16q6g07_0000000 finvdd3 fjik90z fw35ms5"
key="partitionKey"
onClick={[Function]}
onKeyPress={[Function]}
@ -569,7 +568,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
tabIndex={-1}
>
<div
className="fui-TableCell documentsTableCell ___4rzx8z0_j3d5kl0 f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr f1pha7fy"
className="fui-TableCell ___h9qj4u0_1sv9uyx f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr fcep9tg finvdd3 fjik90z fw35ms5"
onClick={[Function]}
onKeyPress={[Function]}
role="cell"
@ -648,11 +647,11 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
key="1"
style={
{
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 30,
"top": 36,
"width": "100%",
}
}
@ -661,15 +660,16 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
appearance="none"
aria-rowindex={3}
aria-selected={false}
className="___1flxklq_0000000 f1i800kq"
key="2"
style={
{
"cursor": "pointer",
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 30,
"top": 36,
"userSelect": "none",
"width": "100%",
}
@ -678,23 +678,23 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
<div
aria-rowindex={3}
aria-selected={false}
className="fui-TableRow ___1krbsl3_1c5rp6c f19n0e5 f1ewtqcl f1jazu75 f1xeqee6 f1dxfoyt f2krc9w f1wfn5kd f1g4hkjv f15ngxrw fjbbrdp f1t94bn6 feu1g3u f1uorfem fw60kww f4xjyn1 ff1wgvm fiob0tu f1j6scgf f1x4h75k fy9rknc f22iagw f122n59"
className="fui-TableRow ___1h2me2v_1yag1rz f19n0e5 f1ewtqcl f1jazu75 f1xeqee6 f1dxfoyt f2krc9w f1wfn5kd f1g4hkjv f15ngxrw fjbbrdp f1t94bn6 feu1g3u f1uorfem fw60kww f4xjyn1 ff1wgvm fiob0tu f1j6scgf f1x4h75k f1facbz3 f22iagw f122n59 f1i800kq"
role="row"
style={
{
"cursor": "pointer",
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 30,
"top": 36,
"userSelect": "none",
"width": "100%",
}
}
>
<TableCell
className="documentsTableCell"
className="___16q6g07_0000000 finvdd3 fjik90z fw35ms5"
key="id"
onClick={[Function]}
onKeyPress={[Function]}
@ -708,7 +708,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
tabIndex={0}
>
<div
className="fui-TableCell documentsTableCell ___4rzx8z0_j3d5kl0 f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr f1pha7fy"
className="fui-TableCell ___h9qj4u0_1sv9uyx f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr fcep9tg finvdd3 fjik90z fw35ms5"
onClick={[Function]}
onKeyPress={[Function]}
role="cell"
@ -743,7 +743,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
</div>
</TableCell>
<TableCell
className="documentsTableCell"
className="___16q6g07_0000000 finvdd3 fjik90z fw35ms5"
key="partitionKey"
onClick={[Function]}
onKeyPress={[Function]}
@ -757,7 +757,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
tabIndex={-1}
>
<div
className="fui-TableCell documentsTableCell ___4rzx8z0_j3d5kl0 f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr f1pha7fy"
className="fui-TableCell ___h9qj4u0_1sv9uyx f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr fcep9tg finvdd3 fjik90z fw35ms5"
onClick={[Function]}
onKeyPress={[Function]}
role="cell"
@ -836,11 +836,11 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
key="2"
style={
{
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 60,
"top": 72,
"width": "100%",
}
}
@ -849,15 +849,16 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
appearance="none"
aria-rowindex={4}
aria-selected={false}
className="___1flxklq_0000000 f1i800kq"
key="3"
style={
{
"cursor": "pointer",
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 60,
"top": 72,
"userSelect": "none",
"width": "100%",
}
@ -866,23 +867,23 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
<div
aria-rowindex={4}
aria-selected={false}
className="fui-TableRow ___1krbsl3_1c5rp6c f19n0e5 f1ewtqcl f1jazu75 f1xeqee6 f1dxfoyt f2krc9w f1wfn5kd f1g4hkjv f15ngxrw fjbbrdp f1t94bn6 feu1g3u f1uorfem fw60kww f4xjyn1 ff1wgvm fiob0tu f1j6scgf f1x4h75k fy9rknc f22iagw f122n59"
className="fui-TableRow ___1h2me2v_1yag1rz f19n0e5 f1ewtqcl f1jazu75 f1xeqee6 f1dxfoyt f2krc9w f1wfn5kd f1g4hkjv f15ngxrw fjbbrdp f1t94bn6 feu1g3u f1uorfem fw60kww f4xjyn1 ff1wgvm fiob0tu f1j6scgf f1x4h75k f1facbz3 f22iagw f122n59 f1i800kq"
role="row"
style={
{
"cursor": "pointer",
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 60,
"top": 72,
"userSelect": "none",
"width": "100%",
}
}
>
<TableCell
className="documentsTableCell"
className="___16q6g07_0000000 finvdd3 fjik90z fw35ms5"
key="id"
onClick={[Function]}
onKeyPress={[Function]}
@ -896,7 +897,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
tabIndex={0}
>
<div
className="fui-TableCell documentsTableCell ___4rzx8z0_j3d5kl0 f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr f1pha7fy"
className="fui-TableCell ___h9qj4u0_1sv9uyx f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr fcep9tg finvdd3 fjik90z fw35ms5"
onClick={[Function]}
onKeyPress={[Function]}
role="cell"
@ -931,7 +932,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
</div>
</TableCell>
<TableCell
className="documentsTableCell"
className="___16q6g07_0000000 finvdd3 fjik90z fw35ms5"
key="partitionKey"
onClick={[Function]}
onKeyPress={[Function]}
@ -945,7 +946,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
tabIndex={-1}
>
<div
className="fui-TableCell documentsTableCell ___4rzx8z0_j3d5kl0 f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr f1pha7fy"
className="fui-TableCell ___h9qj4u0_1sv9uyx f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr fcep9tg finvdd3 fjik90z fw35ms5"
onClick={[Function]}
onKeyPress={[Function]}
role="cell"
@ -1031,11 +1032,10 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
>
<Table
aria-label="Filtered documents table"
className="documentsTable"
data-tabster="{"mover":{"cyclic":false,"direction":3,"memorizeCurrent":true}}"
noNativeElements={true}
role="grid"
size="extra-small"
size="small"
style={
{
"minWidth": "fit-content",
@ -1044,7 +1044,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
>
<div
aria-label="Filtered documents table"
className="fui-Table documentsTable ___1kbqy50_18rlg51 fgkb47j fhovq9v ftgm304"
className="fui-Table ___1kbqy50_18rlg51 fgkb47j fhovq9v ftgm304"
data-tabster="{"mover":{"cyclic":false,"direction":3,"memorizeCurrent":true}}"
role="grid"
style={
@ -1053,14 +1053,13 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
}
}
>
<TableHeader
className="documentsTableHeader"
>
<TableHeader>
<div
className="fui-TableHeader documentsTableHeader ___oeyxrt0_1baslyg ftgm304"
className="fui-TableHeader ___oeyxrt0_1baslyg ftgm304"
role="rowgroup"
>
<TableRow
className="___1flxklq_0000000 f1i800kq"
style={
{
"width": -15,
@ -1068,7 +1067,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
}
>
<div
className="fui-TableRow ___fjp17h0_1kcw78z f19n0e5 f1ewtqcl f1jazu75 f1xeqee6 f1dxfoyt f2krc9w fy9rknc f22iagw f122n59"
className="fui-TableRow ___1ah7syw_1i43eqh f19n0e5 f1ewtqcl f1jazu75 f1xeqee6 f1dxfoyt f2krc9w f1facbz3 f22iagw f122n59 f1i800kq"
role="row"
style={
{
@ -1141,7 +1140,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
"triggerId": "menu3",
"triggerRef": {
"current": <div
class="fui-TableHeaderCell documentsTableCell ___g3gp0b0_n32kis0 figsok6 f3gpkru f14ym4q2 f1euou18 f10pi13n f22iagw f1izfyrr f10tiqix"
class="fui-TableHeaderCell ___tygr690_1tp3egy figsok6 f3gpkru f14ym4q2 f1euou18 f10pi13n f22iagw f1izfyrr f10tiqix finvdd3 fjik90z fw35ms5"
data-tabster="{"restorer":{"type":1}}"
id="menu3"
role="columnheader"
@ -1187,7 +1186,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
role="separator"
/>
}
className="documentsTableCell"
className="___16q6g07_0000000 finvdd3 fjik90z fw35ms5"
data-tabster="{"restorer":{"type":1}}"
id="menu3"
key="id"
@ -1204,7 +1203,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
}
>
<div
className="fui-TableHeaderCell documentsTableCell ___g3gp0b0_n32kis0 figsok6 f3gpkru f14ym4q2 f1euou18 f10pi13n f22iagw f1izfyrr f10tiqix"
className="fui-TableHeaderCell ___tygr690_1tp3egy figsok6 f3gpkru f14ym4q2 f1euou18 f10pi13n f22iagw f1izfyrr f10tiqix finvdd3 fjik90z fw35ms5"
data-tabster="{"restorer":{"type":1}}"
id="menu3"
onContextMenu={[Function]}
@ -1288,7 +1287,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
"triggerId": "menu4",
"triggerRef": {
"current": <div
class="fui-TableHeaderCell documentsTableCell ___g3gp0b0_n32kis0 figsok6 f3gpkru f14ym4q2 f1euou18 f10pi13n f22iagw f1izfyrr f10tiqix"
class="fui-TableHeaderCell ___tygr690_1tp3egy figsok6 f3gpkru f14ym4q2 f1euou18 f10pi13n f22iagw f1izfyrr f10tiqix finvdd3 fjik90z fw35ms5"
data-tabster="{"restorer":{"type":1}}"
id="menu4"
role="columnheader"
@ -1314,7 +1313,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
>
<TableHeaderCell
aside={null}
className="documentsTableCell"
className="___16q6g07_0000000 finvdd3 fjik90z fw35ms5"
data-tabster="{"restorer":{"type":1}}"
id="menu4"
key="partitionKey"
@ -1331,7 +1330,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
}
>
<div
className="fui-TableHeaderCell documentsTableCell ___g3gp0b0_n32kis0 figsok6 f3gpkru f14ym4q2 f1euou18 f10pi13n f22iagw f1izfyrr f10tiqix"
className="fui-TableHeaderCell ___tygr690_1tp3egy figsok6 f3gpkru f14ym4q2 f1euou18 f10pi13n f22iagw f1izfyrr f10tiqix finvdd3 fjik90z fw35ms5"
data-tabster="{"restorer":{"type":1}}"
id="menu4"
onContextMenu={[Function]}
@ -1376,7 +1375,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
>
<List
direction="ltr"
height={-53}
height={-36}
itemCount={3}
itemData={
[
@ -1415,7 +1414,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
},
]
}
itemSize={30}
itemSize={36}
layout="vertical"
overscanCount={2}
style={
@ -1432,7 +1431,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
{
"WebkitOverflowScrolling": "touch",
"direction": "ltr",
"height": -53,
"height": -36,
"overflow": "auto",
"overflowY": "scroll",
"position": "relative",
@ -1444,7 +1443,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
<div
style={
{
"height": 90,
"height": 108,
"pointerEvents": undefined,
"width": "100%",
}
@ -1492,7 +1491,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
key="0"
style={
{
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
@ -1505,11 +1504,12 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
appearance="none"
aria-rowindex={2}
aria-selected={false}
className="___1flxklq_0000000 f1i800kq"
key="1"
style={
{
"cursor": "pointer",
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
@ -1522,12 +1522,12 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
<div
aria-rowindex={2}
aria-selected={false}
className="fui-TableRow ___1krbsl3_1c5rp6c f19n0e5 f1ewtqcl f1jazu75 f1xeqee6 f1dxfoyt f2krc9w f1wfn5kd f1g4hkjv f15ngxrw fjbbrdp f1t94bn6 feu1g3u f1uorfem fw60kww f4xjyn1 ff1wgvm fiob0tu f1j6scgf f1x4h75k fy9rknc f22iagw f122n59"
className="fui-TableRow ___1h2me2v_1yag1rz f19n0e5 f1ewtqcl f1jazu75 f1xeqee6 f1dxfoyt f2krc9w f1wfn5kd f1g4hkjv f15ngxrw fjbbrdp f1t94bn6 feu1g3u f1uorfem fw60kww f4xjyn1 ff1wgvm fiob0tu f1j6scgf f1x4h75k f1facbz3 f22iagw f122n59 f1i800kq"
role="row"
style={
{
"cursor": "pointer",
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
@ -1578,7 +1578,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
</div>
</TableSelectionCell>
<TableCell
className="documentsTableCell"
className="___16q6g07_0000000 finvdd3 fjik90z fw35ms5"
key="id"
onClick={[Function]}
onKeyPress={[Function]}
@ -1592,7 +1592,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
tabIndex={0}
>
<div
className="fui-TableCell documentsTableCell ___4rzx8z0_j3d5kl0 f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr f1pha7fy"
className="fui-TableCell ___h9qj4u0_1sv9uyx f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr fcep9tg finvdd3 fjik90z fw35ms5"
onClick={[Function]}
onKeyPress={[Function]}
role="cell"
@ -1627,7 +1627,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
</div>
</TableCell>
<TableCell
className="documentsTableCell"
className="___16q6g07_0000000 finvdd3 fjik90z fw35ms5"
key="partitionKey"
onClick={[Function]}
onKeyPress={[Function]}
@ -1641,7 +1641,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
tabIndex={-1}
>
<div
className="fui-TableCell documentsTableCell ___4rzx8z0_j3d5kl0 f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr f1pha7fy"
className="fui-TableCell ___h9qj4u0_1sv9uyx f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr fcep9tg finvdd3 fjik90z fw35ms5"
onClick={[Function]}
onKeyPress={[Function]}
role="cell"
@ -1720,11 +1720,11 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
key="1"
style={
{
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 30,
"top": 36,
"width": "100%",
}
}
@ -1733,15 +1733,16 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
appearance="none"
aria-rowindex={3}
aria-selected={false}
className="___1flxklq_0000000 f1i800kq"
key="2"
style={
{
"cursor": "pointer",
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 30,
"top": 36,
"userSelect": "none",
"width": "100%",
}
@ -1750,16 +1751,16 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
<div
aria-rowindex={3}
aria-selected={false}
className="fui-TableRow ___1krbsl3_1c5rp6c f19n0e5 f1ewtqcl f1jazu75 f1xeqee6 f1dxfoyt f2krc9w f1wfn5kd f1g4hkjv f15ngxrw fjbbrdp f1t94bn6 feu1g3u f1uorfem fw60kww f4xjyn1 ff1wgvm fiob0tu f1j6scgf f1x4h75k fy9rknc f22iagw f122n59"
className="fui-TableRow ___1h2me2v_1yag1rz f19n0e5 f1ewtqcl f1jazu75 f1xeqee6 f1dxfoyt f2krc9w f1wfn5kd f1g4hkjv f15ngxrw fjbbrdp f1t94bn6 feu1g3u f1uorfem fw60kww f4xjyn1 ff1wgvm fiob0tu f1j6scgf f1x4h75k f1facbz3 f22iagw f122n59 f1i800kq"
role="row"
style={
{
"cursor": "pointer",
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 30,
"top": 36,
"userSelect": "none",
"width": "100%",
}
@ -1806,7 +1807,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
</div>
</TableSelectionCell>
<TableCell
className="documentsTableCell"
className="___16q6g07_0000000 finvdd3 fjik90z fw35ms5"
key="id"
onClick={[Function]}
onKeyPress={[Function]}
@ -1820,7 +1821,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
tabIndex={0}
>
<div
className="fui-TableCell documentsTableCell ___4rzx8z0_j3d5kl0 f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr f1pha7fy"
className="fui-TableCell ___h9qj4u0_1sv9uyx f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr fcep9tg finvdd3 fjik90z fw35ms5"
onClick={[Function]}
onKeyPress={[Function]}
role="cell"
@ -1855,7 +1856,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
</div>
</TableCell>
<TableCell
className="documentsTableCell"
className="___16q6g07_0000000 finvdd3 fjik90z fw35ms5"
key="partitionKey"
onClick={[Function]}
onKeyPress={[Function]}
@ -1869,7 +1870,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
tabIndex={-1}
>
<div
className="fui-TableCell documentsTableCell ___4rzx8z0_j3d5kl0 f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr f1pha7fy"
className="fui-TableCell ___h9qj4u0_1sv9uyx f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr fcep9tg finvdd3 fjik90z fw35ms5"
onClick={[Function]}
onKeyPress={[Function]}
role="cell"
@ -1948,11 +1949,11 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
key="2"
style={
{
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 60,
"top": 72,
"width": "100%",
}
}
@ -1961,15 +1962,16 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
appearance="none"
aria-rowindex={4}
aria-selected={false}
className="___1flxklq_0000000 f1i800kq"
key="3"
style={
{
"cursor": "pointer",
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 60,
"top": 72,
"userSelect": "none",
"width": "100%",
}
@ -1978,16 +1980,16 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
<div
aria-rowindex={4}
aria-selected={false}
className="fui-TableRow ___1krbsl3_1c5rp6c f19n0e5 f1ewtqcl f1jazu75 f1xeqee6 f1dxfoyt f2krc9w f1wfn5kd f1g4hkjv f15ngxrw fjbbrdp f1t94bn6 feu1g3u f1uorfem fw60kww f4xjyn1 ff1wgvm fiob0tu f1j6scgf f1x4h75k fy9rknc f22iagw f122n59"
className="fui-TableRow ___1h2me2v_1yag1rz f19n0e5 f1ewtqcl f1jazu75 f1xeqee6 f1dxfoyt f2krc9w f1wfn5kd f1g4hkjv f15ngxrw fjbbrdp f1t94bn6 feu1g3u f1uorfem fw60kww f4xjyn1 ff1wgvm fiob0tu f1j6scgf f1x4h75k f1facbz3 f22iagw f122n59 f1i800kq"
role="row"
style={
{
"cursor": "pointer",
"height": 30,
"height": 36,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 60,
"top": 72,
"userSelect": "none",
"width": "100%",
}
@ -2034,7 +2036,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
</div>
</TableSelectionCell>
<TableCell
className="documentsTableCell"
className="___16q6g07_0000000 finvdd3 fjik90z fw35ms5"
key="id"
onClick={[Function]}
onKeyPress={[Function]}
@ -2048,7 +2050,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
tabIndex={0}
>
<div
className="fui-TableCell documentsTableCell ___4rzx8z0_j3d5kl0 f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr f1pha7fy"
className="fui-TableCell ___h9qj4u0_1sv9uyx f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr fcep9tg finvdd3 fjik90z fw35ms5"
onClick={[Function]}
onKeyPress={[Function]}
role="cell"
@ -2083,7 +2085,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
</div>
</TableCell>
<TableCell
className="documentsTableCell"
className="___16q6g07_0000000 finvdd3 fjik90z fw35ms5"
key="partitionKey"
onClick={[Function]}
onKeyPress={[Function]}
@ -2097,7 +2099,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
tabIndex={-1}
>
<div
className="fui-TableCell documentsTableCell ___4rzx8z0_j3d5kl0 f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr f1pha7fy"
className="fui-TableCell ___h9qj4u0_1sv9uyx f10pi13n f3gpkru f1dxfoyt f2krc9w f22iagw f10tiqix f122n59 f1izfyrr fcep9tg finvdd3 fjik90z fw35ms5"
onClick={[Function]}
onKeyPress={[Function]}
role="cell"

View File

@ -1,4 +1,4 @@
import { Link, MessageBar, MessageBarButton, MessageBarType } from "@fluentui/react";
import { IMessageBarStyles, Link, MessageBar, MessageBarButton, MessageBarType } from "@fluentui/react";
import { CassandraProxyEndpoints, MongoProxyEndpoints } from "Common/Constants";
import { sendMessage } from "Common/MessageHandler";
import { Platform, configContext, updateConfigContext } from "ConfigContext";
@ -14,6 +14,7 @@ import { PostgresConnectTab } from "Explorer/Tabs/PostgresConnectTab";
import { QuickstartTab } from "Explorer/Tabs/QuickstartTab";
import { VcoreMongoConnectTab } from "Explorer/Tabs/VCoreMongoConnectTab";
import { VcoreMongoQuickstartTab } from "Explorer/Tabs/VCoreMongoQuickstartTab";
import { LayoutConstants } from "Explorer/Theme/ThemeUtil";
import { KeyboardAction, KeyboardActionGroup, useKeyboardActionGroup } from "KeyboardShortcuts";
import { hasRUThresholdBeenConfigured } from "Shared/StorageUtility";
import { userContext } from "UserContext";
@ -53,11 +54,19 @@ export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
});
}, [setKeyboardHandlers]);
const defaultMessageBarStyles: IMessageBarStyles = {
root: {
height: `${LayoutConstants.rowHeight}px`,
overflow: "auto",
},
};
return (
<div className="tabsManagerContainer">
{networkSettingsWarning && (
<MessageBar
messageBarType={MessageBarType.warning}
styles={defaultMessageBarStyles}
actions={
<MessageBarButton
onClick={() =>
@ -84,6 +93,7 @@ export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
setShowRUThresholdMessageBar(false);
}}
styles={{
...defaultMessageBarStyles,
innerText: {
fontWeight: "bold",
},
@ -103,6 +113,7 @@ export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
{showMongoAndCassandraProxiesNetworkSettingsWarningState && (
<MessageBar
messageBarType={MessageBarType.warning}
styles={defaultMessageBarStyles}
onDismiss={() => {
setShowMongoAndCassandraProxiesNetworkSettingsWarningState(false);
}}
@ -111,23 +122,21 @@ export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
re-enable "Allow access from Azure Portal" on the Networking blade for your account.`}
</MessageBar>
)}
<div id="content" className="flexContainer hideOverflows">
<div className="nav-tabs-margin">
<ul className="nav nav-tabs level navTabHeight" id="navTabs" role="tablist">
{openedReactTabs.map((tab) => (
<TabNav key={ReactTabKind[tab]} active={activeReactTab === tab} tabKind={tab} />
))}
{openedTabs.map((tab) => (
<TabNav key={tab.tabId} tab={tab} active={activeTab === tab} />
))}
</ul>
</div>
<div className="tabPanesContainer">
{activeReactTab !== undefined && getReactTabContent(activeReactTab, explorer)}
{openedTabs.map((tab) => (
<TabPane key={tab.tabId} tab={tab} active={activeTab === tab} />
<div className="nav-tabs-margin">
<ul className="nav nav-tabs level navTabHeight" id="navTabs" role="tablist">
{openedReactTabs.map((tab) => (
<TabNav key={ReactTabKind[tab]} active={activeReactTab === tab} tabKind={tab} />
))}
</div>
{openedTabs.map((tab) => (
<TabNav key={tab.tabId} tab={tab} active={activeTab === tab} />
))}
</ul>
</div>
<div className="tabPanesContainer">
{activeReactTab !== undefined && getReactTabContent(activeReactTab, explorer)}
{openedTabs.map((tab) => (
<TabPane key={tab.tabId} tab={tab} active={activeTab === tab} />
))}
</div>
</div>
);

View File

@ -1,31 +0,0 @@
import { BrandVariants, Theme, createLightTheme } from "@fluentui/react-components";
import { Platform } from "ConfigContext";
import { appThemeFabricTealBrandRamp } from "../../Platform/Fabric/FabricTheme";
// These are the theme colors for Fluent UI 9 React components
const appThemePortalBrandRamp: BrandVariants = {
10: "#020305",
20: "#111723",
30: "#16263D",
40: "#193253",
50: "#1B3F6A",
60: "#1B4C82",
70: "#18599B",
80: "#1267B4",
90: "#3174C2",
100: "#4F82C8",
110: "#6790CF",
120: "#7D9ED5",
130: "#92ACDC",
140: "#A6BAE2",
150: "#BAC9E9",
160: "#CDD8EF",
};
export function getPlatformTheme(platform: Platform): Theme {
if (platform === Platform.Fabric) {
return createLightTheme(appThemeFabricTealBrandRamp);
} else {
return createLightTheme(appThemePortalBrandRamp);
}
}

View File

@ -0,0 +1,95 @@
import {
BrandVariants,
FluentProvider,
Theme,
createLightTheme,
makeStyles,
mergeClasses,
shorthands,
themeToTokensObject,
webLightTheme,
} from "@fluentui/react-components";
import { Platform, configContext } from "ConfigContext";
import React, { PropsWithChildren } from "react";
import { appThemeFabricTealBrandRamp } from "../../Platform/Fabric/FabricTheme";
export const LayoutConstants = {
rowHeight: 36,
};
export type CosmosFluentProviderProps = PropsWithChildren<{
className?: string;
}>;
const useDefaultRootStyles = makeStyles({
fluentProvider: {
// By default, a FluentProvider has a solid background.
// The styles for a FluentProvider are _copied_ to any Portals (https://react.fluentui.dev/?path=/docs/components-portal-portal--default)
// created by components inside the FluentProvider, such as when rendering popup-up menus.
// However, we often stretch our FluentProviders to full height using a `height: 100%` style.
// When we do that, the Portal will also stretch to full height, but it will have a solid background and block out the entire document behind it.
backgroundColor: "transparent",
},
});
export const CosmosFluentProvider: React.FC<CosmosFluentProviderProps> = ({ children, className }) => {
const styles = useDefaultRootStyles();
return (
<FluentProvider
theme={getPlatformTheme(configContext.platform)}
className={mergeClasses(styles.fluentProvider, className)}
>
{children}
</FluentProvider>
);
};
// These are the theme colors for Fluent UI 9 React components
const appThemePortalBrandRamp: BrandVariants = {
10: "#020305",
20: "#111723",
30: "#16263D",
40: "#193253",
50: "#1B3F6A",
60: "#1B4C82",
70: "#18599B",
80: "#1267B4",
90: "#3174C2",
100: "#4F82C8",
110: "#6790CF",
120: "#7D9ED5",
130: "#92ACDC",
140: "#A6BAE2",
150: "#BAC9E9",
160: "#CDD8EF",
};
const cosmosThemeElements = {
layoutRowHeight: `${LayoutConstants.rowHeight}px`,
sidebarMinimumWidth: "200px",
sidebarInitialWidth: "300px",
};
export type CosmosTheme = Theme & typeof cosmosThemeElements;
export const tokens = themeToTokensObject({ ...webLightTheme, ...cosmosThemeElements });
export const cosmosShorthands = {
border: () => shorthands.border("1px", "solid", tokens.colorNeutralStroke2),
borderBottom: () => shorthands.borderBottom("1px", "solid", tokens.colorNeutralStroke2),
borderRight: () => shorthands.borderRight("1px", "solid", tokens.colorNeutralStroke2),
borderTop: () => shorthands.borderTop("1px", "solid", tokens.colorNeutralStroke2),
borderLeft: () => shorthands.borderLeft("1px", "solid", tokens.colorNeutralStroke2),
};
export function getPlatformTheme(platform: Platform): CosmosTheme {
const baseTheme =
platform === Platform.Fabric
? createLightTheme(appThemeFabricTealBrandRamp)
: createLightTheme(appThemePortalBrandRamp);
return {
...baseTheme,
...cosmosThemeElements,
};
}

View File

@ -3,15 +3,13 @@ import React from "react";
import * as _ from "underscore";
import { AuthType } from "../../AuthType";
import * as Constants from "../../Common/Constants";
import { readCollections, readCollectionsWithPagination } from "../../Common/dataAccess/readCollections";
import { readDatabaseOffer } from "../../Common/dataAccess/readDatabaseOffer";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
import * as Logger from "../../Common/Logger";
import { fetchPortalNotifications } from "../../Common/PortalNotifications";
import { readCollections, readCollectionsWithPagination } from "../../Common/dataAccess/readCollections";
import { readDatabaseOffer } from "../../Common/dataAccess/readDatabaseOffer";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
import { useSidePanel } from "../../hooks/useSidePanel";
import { useTabs } from "../../hooks/useTabs";
import { IJunoResponse, JunoClient } from "../../Juno/JunoClient";
import * as StorageUtility from "../../Shared/StorageUtility";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
@ -20,6 +18,8 @@ import { userContext } from "../../UserContext";
import { getCollectionName } from "../../Utils/APITypeUtils";
import { isServerlessAccount } from "../../Utils/CapabilityUtils";
import { logConsoleError } from "../../Utils/NotificationConsoleUtils";
import { useSidePanel } from "../../hooks/useSidePanel";
import { useTabs } from "../../hooks/useTabs";
import Explorer from "../Explorer";
import { AddCollectionPanel } from "../Panes/AddCollectionPanel";
import { DatabaseSettingsTabV2 } from "../Tabs/SettingsTabV2";
@ -52,6 +52,7 @@ export default class Database implements ViewModels.Database {
this.collections = ko.observableArray<Collection>();
this.collections.subscribe(() => useDatabases.getState().updateDatabase(this));
this.isDatabaseExpanded = ko.observable<boolean>(false);
this.isDatabaseExpanded.subscribe(() => useDatabases.getState().updateDatabase(this));
this.selectedSubnodeKind = ko.observable<ViewModels.CollectionTabKind>();
this.isDatabaseShared = ko.pureComputed(() => {
return this.offer && !!this.offer();

View File

@ -1,14 +1,8 @@
import {
BrandVariants,
FluentProvider,
Theme,
Tree,
TreeItemValue,
TreeOpenChangeData,
TreeOpenChangeEvent,
createLightTheme,
} from "@fluentui/react-components";
import { Tree, TreeItemValue, TreeOpenChangeData, TreeOpenChangeEvent } from "@fluentui/react-components";
import { Home16Regular } from "@fluentui/react-icons";
import { AuthType } from "AuthType";
import { Platform, configContext } from "ConfigContext";
import { useTreeStyles } from "Explorer/Controls/TreeComponent/Styles";
import { TreeNode, TreeNodeComponent } from "Explorer/Controls/TreeComponent/TreeNodeComponent";
import {
createDatabaseTreeNodes,
@ -16,9 +10,10 @@ import {
createSampleDataTreeNodes,
} from "Explorer/Tree/treeNodeUtil";
import { useDatabases } from "Explorer/useDatabases";
import { useSelectedNode } from "Explorer/useSelectedNode";
import { userContext } from "UserContext";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import { useTabs } from "hooks/useTabs";
import { ReactTabKind, useTabs } from "hooks/useTabs";
import * as React from "react";
import { useEffect, useMemo } from "react";
import shallow from "zustand/shallow";
@ -29,32 +24,9 @@ export const MyNotebooksTitle = "My Notebooks";
export const GitHubReposTitle = "GitHub repos";
interface ResourceTreeProps {
container: Explorer;
explorer: Explorer;
}
const cosmosdb: BrandVariants = {
10: "#020305",
20: "#111723",
30: "#16263D",
40: "#193253",
50: "#1B3F6A",
60: "#1B4C82",
70: "#18599B",
80: "#1267B4",
90: "#3174C2",
100: "#4F82C8",
110: "#6790CF",
120: "#7D9ED5",
130: "#92ACDC",
140: "#A6BAE2",
150: "#BAC9E9",
160: "#CDD8EF",
};
const lightTheme: Theme = {
...createLightTheme(cosmosdb),
};
export const DATA_TREE_LABEL = "DATA";
export const MY_DATA_TREE_LABEL = "MY DATA";
export const SAMPLE_DATA_TREE_LABEL = "SAMPLE DATA";
@ -62,8 +34,9 @@ export const SAMPLE_DATA_TREE_LABEL = "SAMPLE DATA";
/**
* Top-level tree that has no label, but contains all subtrees
*/
export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: ResourceTreeProps): JSX.Element => {
const [openItems, setOpenItems] = React.useState<Iterable<TreeItemValue>>([DATA_TREE_LABEL]);
export const ResourceTree: React.FC<ResourceTreeProps> = ({ explorer }: ResourceTreeProps): JSX.Element => {
const [openItems, setOpenItems] = React.useState<TreeItemValue[]>([]);
const treeStyles = useTreeStyles();
const { isNotebookEnabled } = useNotebook(
(state) => ({
@ -85,10 +58,11 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
isCopilotSampleDBEnabled: state.copilotSampleDBEnabled,
}));
const databaseTreeNodes =
userContext.authType === AuthType.ResourceToken
const databaseTreeNodes = useMemo(() => {
return userContext.authType === AuthType.ResourceToken
? createResourceTokenTreeNodes(resourceTokenCollection)
: createDatabaseTreeNodes(container, isNotebookEnabled, databases, refreshActiveTab);
: createDatabaseTreeNodes(explorer, isNotebookEnabled, databases, refreshActiveTab);
}, [resourceTokenCollection, databases, isNotebookEnabled, refreshActiveTab]);
const isSampleDataEnabled =
isCopilotEnabled &&
@ -102,33 +76,42 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
: [];
}, [isSampleDataEnabled, sampleDataResourceTokenCollection]);
const headerNodes: TreeNode[] =
configContext.platform === Platform.Fabric
? []
: [
{
id: "home",
iconSrc: <Home16Regular />,
label: "Home",
isSelected: () =>
useSelectedNode.getState().selectedNode === undefined &&
useTabs.getState().activeReactTab === ReactTabKind.Home,
onClick: () => {
useSelectedNode.getState().setSelectedNode(undefined);
useTabs.getState().openAndActivateReactTab(ReactTabKind.Home);
},
},
];
const rootNodes: TreeNode[] = useMemo(() => {
if (sampleDataNodes.length > 0) {
return [
...headerNodes,
{
id: "data",
label: MY_DATA_TREE_LABEL,
className: "accordionItemHeader",
children: databaseTreeNodes,
isScrollable: true,
},
{
id: "sampleData",
label: SAMPLE_DATA_TREE_LABEL,
className: "accordionItemHeader",
children: sampleDataNodes,
},
];
} else {
return [
{
id: "data",
label: DATA_TREE_LABEL,
className: "accordionItemHeader",
children: databaseTreeNodes,
isScrollable: true,
},
];
return [...headerNodes, ...databaseTreeNodes];
}
}, [databaseTreeNodes, sampleDataNodes]);
@ -162,23 +145,28 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
rootNodes.forEach((n) => updateOpenItems(n, undefined));
}, [rootNodes, openItems, setOpenItems]);
const handleOpenChange = (event: TreeOpenChangeEvent, data: TreeOpenChangeData) => setOpenItems(data.openItems);
const handleOpenChange = (event: TreeOpenChangeEvent, data: TreeOpenChangeData) =>
setOpenItems(Array.from(data.openItems));
return (
<>
<FluentProvider theme={lightTheme} style={{ overflow: "auto" }}>
<Tree
aria-label="CosmosDB resources"
openItems={openItems}
onOpenChange={handleOpenChange}
size="small"
style={{ height: "100%", minWidth: "290px" }}
>
{rootNodes.map((node) => (
<TreeNodeComponent key={node.label} className="dataResourceTree" node={node} treeNodeId={node.label} />
))}
</Tree>
</FluentProvider>
</>
<div className={treeStyles.treeContainer}>
<Tree
aria-label="CosmosDB resources"
openItems={openItems}
className={treeStyles.tree}
onOpenChange={handleOpenChange}
size="medium"
>
{rootNodes.map((node) => (
<TreeNodeComponent
key={node.label}
openItems={openItems}
className="dataResourceTree"
node={node}
treeNodeId={node.label}
/>
))}
</Tree>
</div>
);
};

View File

@ -21,7 +21,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"onClick": [Function],
},
],
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -30,7 +30,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "standardCollection",
@ -61,7 +60,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"onClick": [Function],
},
],
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -70,7 +69,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "conflictsCollection",
@ -80,7 +78,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"onExpanded": [Function],
},
],
"className": "databaseHeader",
"className": "databaseNode",
"contextMenu": [
{
"iconSrc": {},
@ -94,7 +92,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "standardDb",
@ -127,7 +124,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"onClick": [Function],
},
],
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -136,7 +133,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "sampleItemsCollection",
@ -146,7 +142,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"onExpanded": [Function],
},
],
"className": "databaseHeader",
"className": "databaseNode",
"contextMenu": [
{
"iconSrc": {},
@ -160,7 +156,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "sharedDatabase",
@ -242,7 +237,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"onClick": [Function],
},
],
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -251,7 +246,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "schemaCollection",
@ -261,12 +255,12 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"onExpanded": [Function],
},
{
"className": "loadMoreHeader",
"className": "loadMoreNode",
"label": "load more",
"onClick": [Function],
},
],
"className": "databaseHeader",
"className": "databaseNode",
"contextMenu": [
{
"iconSrc": {},
@ -280,7 +274,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "giganticDatabase",
@ -333,7 +326,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"onClick": [Function],
},
],
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -352,7 +345,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "standardCollection",
@ -404,7 +396,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"onClick": [Function],
},
],
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -423,7 +415,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "conflictsCollection",
@ -433,7 +424,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"onExpanded": [Function],
},
],
"className": "databaseHeader",
"className": "databaseNode",
"contextMenu": [
{
"iconSrc": {},
@ -447,7 +438,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "standardDb",
@ -501,7 +491,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"onClick": [Function],
},
],
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -520,7 +510,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "sampleItemsCollection",
@ -530,7 +519,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"onExpanded": [Function],
},
],
"className": "databaseHeader",
"className": "databaseNode",
"contextMenu": [
{
"iconSrc": {},
@ -544,7 +533,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "sharedDatabase",
@ -647,7 +635,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"onClick": [Function],
},
],
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -666,7 +654,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "schemaCollection",
@ -676,12 +663,12 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"onExpanded": [Function],
},
{
"className": "loadMoreHeader",
"className": "loadMoreNode",
"label": "load more",
"onClick": [Function],
},
],
"className": "databaseHeader",
"className": "databaseNode",
"contextMenu": [
{
"iconSrc": {},
@ -695,7 +682,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "giganticDatabase",
@ -712,7 +698,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"children": [
{
"children": undefined,
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -720,7 +706,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onClick": [Function],
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "standardCollection",
@ -731,7 +716,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
},
{
"children": undefined,
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -739,7 +724,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onClick": [Function],
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "conflictsCollection",
@ -749,7 +733,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onExpanded": [Function],
},
],
"className": "databaseHeader",
"className": "databaseNode",
"contextMenu": [
{
"iconSrc": {},
@ -763,7 +747,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "standardDb",
@ -775,7 +758,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"children": [
{
"children": undefined,
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -783,7 +766,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onClick": [Function],
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "sampleItemsCollection",
@ -793,7 +775,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onExpanded": [Function],
},
],
"className": "databaseHeader",
"className": "databaseNode",
"contextMenu": [
{
"iconSrc": {},
@ -807,7 +789,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "sharedDatabase",
@ -819,7 +800,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"children": [
{
"children": undefined,
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -827,7 +808,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onClick": [Function],
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "schemaCollection",
@ -837,12 +817,12 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onExpanded": [Function],
},
{
"className": "loadMoreHeader",
"className": "loadMoreNode",
"label": "load more",
"onClick": [Function],
},
],
"className": "databaseHeader",
"className": "databaseNode",
"contextMenu": [
{
"iconSrc": {},
@ -856,7 +836,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "giganticDatabase",
@ -968,7 +947,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onExpanded": [Function],
},
],
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -997,7 +976,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "standardCollection",
@ -1069,7 +1047,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onClick": [Function],
},
],
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -1098,7 +1076,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "conflictsCollection",
@ -1108,7 +1085,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onExpanded": [Function],
},
],
"className": "databaseHeader",
"className": "databaseNode",
"contextMenu": [
{
"iconSrc": {},
@ -1122,7 +1099,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "standardDb",
@ -1196,7 +1172,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onExpanded": [Function],
},
],
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -1225,7 +1201,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "sampleItemsCollection",
@ -1235,7 +1210,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onExpanded": [Function],
},
],
"className": "databaseHeader",
"className": "databaseNode",
"contextMenu": [
{
"iconSrc": {},
@ -1249,7 +1224,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "sharedDatabase",
@ -1372,7 +1346,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onExpanded": [Function],
},
],
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -1401,7 +1375,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "schemaCollection",
@ -1411,12 +1384,12 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onExpanded": [Function],
},
{
"className": "loadMoreHeader",
"className": "loadMoreNode",
"label": "load more",
"onClick": [Function],
},
],
"className": "databaseHeader",
"className": "databaseNode",
"contextMenu": [
{
"iconSrc": {},
@ -1430,7 +1403,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "giganticDatabase",
@ -1542,7 +1514,7 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"onExpanded": [Function],
},
],
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -1571,7 +1543,6 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "standardCollection",
@ -1638,7 +1609,7 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"onExpanded": [Function],
},
],
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -1667,7 +1638,6 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "conflictsCollection",
@ -1677,7 +1647,7 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"onExpanded": [Function],
},
],
"className": "databaseHeader",
"className": "databaseNode",
"contextMenu": [
{
"iconSrc": {},
@ -1691,7 +1661,6 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "standardDb",
@ -1765,7 +1734,7 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"onExpanded": [Function],
},
],
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -1794,7 +1763,6 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "sampleItemsCollection",
@ -1804,7 +1772,7 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"onExpanded": [Function],
},
],
"className": "databaseHeader",
"className": "databaseNode",
"contextMenu": [
{
"iconSrc": {},
@ -1818,7 +1786,6 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "sharedDatabase",
@ -1941,7 +1908,7 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"onExpanded": [Function],
},
],
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -1970,7 +1937,6 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "schemaCollection",
@ -1980,12 +1946,12 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"onExpanded": [Function],
},
{
"className": "loadMoreHeader",
"className": "loadMoreNode",
"label": "load more",
"onClick": [Function],
},
],
"className": "databaseHeader",
"className": "databaseNode",
"contextMenu": [
{
"iconSrc": {},
@ -1999,7 +1965,6 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": {},
"isExpanded": true,
"isSelected": [Function],
"label": "giganticDatabase",
@ -2020,8 +1985,7 @@ exports[`createResourceTokenTreeNodes creates the expected tree nodes 1`] = `
"onClick": [Function],
},
],
"className": "collectionHeader",
"iconSrc": {},
"className": "collectionNode",
"isExpanded": true,
"isSelected": [Function],
"label": "testCollection",
@ -2049,7 +2013,7 @@ exports[`createSampleDataTreeNodes creates the expected tree nodes 1`] = `
"onClick": [Function],
},
],
"className": "collectionHeader",
"className": "collectionNode",
"contextMenu": [
{
"iconSrc": {},
@ -2057,7 +2021,6 @@ exports[`createSampleDataTreeNodes creates the expected tree nodes 1`] = `
"onClick": [Function],
},
],
"iconSrc": {},
"isExpanded": false,
"isSelected": [Function],
"label": "testCollection",
@ -2065,8 +2028,7 @@ exports[`createSampleDataTreeNodes creates the expected tree nodes 1`] = `
"onContextMenuOpen": [Function],
},
],
"className": "databaseHeader",
"iconSrc": {},
"className": "databaseNode",
"isExpanded": false,
"label": "testDatabase",
},

View File

@ -7,8 +7,6 @@ import { useDatabases } from "Explorer/useDatabases";
import { getItemName } from "Utils/APITypeUtils";
import { isServerlessAccount } from "Utils/CapabilityUtils";
import { useTabs } from "hooks/useTabs";
import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg";
import CollectionIcon from "../../../images/tree-collection.svg";
import { isPublicInternetAccessAllowed } from "../../Common/DatabaseAccountUtility";
import { Platform, configContext } from "../../ConfigContext";
import * as DataModels from "../../Contracts/DataModels";
@ -31,14 +29,12 @@ export const createSampleDataTreeNodes = (sampleDataResourceTokenCollection: Vie
const updatedSampleTree: TreeNode = {
label: sampleDataResourceTokenCollection.databaseId,
isExpanded: false,
iconSrc: CosmosDBIcon,
className: "databaseHeader",
className: "databaseNode",
children: [
{
label: sampleDataResourceTokenCollection.id(),
iconSrc: CollectionIcon,
isExpanded: false,
className: "collectionHeader",
className: "collectionNode",
contextMenu: ResourceTreeContextMenuButtonFactory.createSampleCollectionContextMenuButton(),
onClick: () => {
useSelectedNode.getState().setSelectedNode(sampleDataResourceTokenCollection);
@ -105,10 +101,9 @@ export const createResourceTokenTreeNodes = (collection: ViewModels.CollectionBa
const collectionNode: TreeNode = {
label: collection.id(),
iconSrc: CollectionIcon,
isExpanded: true,
children,
className: "collectionHeader",
className: "collectionNode",
onClick: () => {
// Rewritten version of expandCollapseCollection
useSelectedNode.getState().setSelectedNode(collection);
@ -158,7 +153,7 @@ export const createDatabaseTreeNodes = (
if (database.collectionsContinuationToken) {
const loadMoreNode: TreeNode = {
label: "load more",
className: "loadMoreHeader",
className: "loadMoreNode",
onClick: async () => {
await database.loadCollections();
useDatabases.getState().updateDatabase(database);
@ -170,8 +165,7 @@ export const createDatabaseTreeNodes = (
const databaseNode: TreeNode = {
label: database.id(),
iconSrc: CosmosDBIcon,
className: "databaseHeader",
className: "databaseNode",
children: [],
isSelected: () => useSelectedNode.getState().isDataNodeSelected(database.id()),
contextMenu: ResourceTreeContextMenuButtonFactory.createDatabaseContextMenu(container, database.id()),
@ -222,9 +216,8 @@ export const buildCollectionNode = (
const collectionNode: TreeNode = {
label: collection.id(),
iconSrc: CollectionIcon,
children: children,
className: "collectionHeader",
className: "collectionNode",
contextMenu: ResourceTreeContextMenuButtonFactory.createCollectionContextMenuButton(container, collection),
onClick: () => {
useSelectedNode.getState().setSelectedNode(collection);

View File

@ -20,7 +20,7 @@ export enum KeyboardActionGroup {
/** Keyboard actions related to tab navigation. */
TABS = "TABS",
/** Keyboard actions managed by the global command bar. */
/** Keyboard actions managed by the command bar. */
COMMAND_BAR = "COMMAND_BAR",
/**
@ -28,6 +28,9 @@ export enum KeyboardActionGroup {
* This group is automatically cleared when the active tab changes.
*/
ACTIVE_TAB = "ACTIVE_TAB",
/** Keyboard actions managed by the global commands section, in the top-left corner. */
GLOBAL_COMMANDS = "GLOBAL_COMMANDS",
}
/**

View File

@ -6,10 +6,9 @@ import { initializeIcons, loadTheme } from "@fluentui/react";
import { QuickstartCarousel } from "Explorer/Quickstart/QuickstartCarousel";
import { MongoQuickstartTutorial } from "Explorer/Quickstart/Tutorials/MongoQuickstartTutorial";
import { SQLQuickstartTutorial } from "Explorer/Quickstart/Tutorials/SQLQuickstartTutorial";
import { userContext } from "UserContext";
import "bootstrap/dist/css/bootstrap.css";
import { useCarousel } from "hooks/useCarousel";
import React, { useState } from "react";
import React from "react";
import ReactDOM from "react-dom";
import "../externals/jquery-ui.min.css";
import "../externals/jquery-ui.min.js";
@ -21,7 +20,9 @@ import "../externals/jquery.typeahead.min.js";
// Image Dependencies
import { Platform } from "ConfigContext";
import { QueryCopilotCarousel } from "Explorer/QueryCopilot/CopilotCarousel";
import { SidebarContainer } from "Explorer/Sidebar";
import { KeyboardShortcutRoot } from "KeyboardShortcuts";
import "allotment/dist/style.css";
import "../images/CosmosDB_rgb_ui_lighttheme.ico";
import hdeConnectImage from "../images/HdeConnectCosmosDB.svg";
import "../images/favicon.ico";
@ -36,8 +37,6 @@ import "../less/menus.less";
import "../less/messagebox.less";
import "../less/resourceTree.less";
import "../less/tree.less";
import { CollapsedResourceTree } from "./Common/CollapsedResourceTree";
import { ResourceTreeContainer } from "./Common/ResourceTreeContainer";
import * as StyleConstants from "./Common/StyleConstants";
import "./Explorer/Controls/Accordion/AccordionComponent.less";
import "./Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.less";
@ -56,7 +55,6 @@ import { NotificationConsole } from "./Explorer/Menus/NotificationConsole/Notifi
import "./Explorer/Panes/PanelComponent.less";
import { SidePanel } from "./Explorer/Panes/PanelContainerComponent";
import "./Explorer/SplashScreen/SplashScreen.less";
import { Tabs } from "./Explorer/Tabs/Tabs";
import "./Libs/jquery";
import { appThemeFabric } from "./Platform/Fabric/FabricTheme";
import "./Shared/appInsights";
@ -66,7 +64,6 @@ import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer";
initializeIcons();
const App: React.FunctionComponent = () => {
const [isLeftPaneExpanded, setIsLeftPaneExpanded] = useState<boolean>(true);
const isCarouselOpen = useCarousel((state) => state.shouldOpen);
const isCopilotCarouselOpen = useCarousel((state) => state.showCopilotCarousel);
@ -78,15 +75,6 @@ const App: React.FunctionComponent = () => {
StyleConstants.updateStyles();
const explorer = useKnockoutExplorer(config?.platform);
const toggleLeftPaneExpanded = () => {
setIsLeftPaneExpanded(!isLeftPaneExpanded);
if (isLeftPaneExpanded) {
document.getElementById("expandToggleLeftPaneButton").focus();
} else {
document.getElementById("collapseToggleLeftPaneButton").focus();
}
};
if (!explorer) {
return <LoadingExplorer />;
}
@ -99,29 +87,7 @@ const App: React.FunctionComponent = () => {
{/* Main Command Bar - Start */}
<CommandBar container={explorer} />
{/* Collections Tree and Tabs - Begin */}
<div className="resourceTreeAndTabs">
{/* Collections Tree - Start */}
{userContext.apiType !== "Postgres" && userContext.apiType !== "VCoreMongo" && (
<div id="resourcetree" data-test="resourceTreeId" className="resourceTree">
<div className="collectionsTreeWithSplitter">
{/* Collections Tree Expanded - Start */}
<ResourceTreeContainer
container={explorer}
toggleLeftPaneExpanded={toggleLeftPaneExpanded}
isLeftPaneExpanded={isLeftPaneExpanded}
/>
{/* Collections Tree Expanded - End */}
{/* Collections Tree Collapsed - Start */}
<CollapsedResourceTree
toggleLeftPaneExpanded={toggleLeftPaneExpanded}
isLeftPaneExpanded={isLeftPaneExpanded}
/>
{/* Collections Tree Collapsed - End */}
</div>
</div>
)}
<Tabs explorer={explorer} />
</div>
<SidebarContainer explorer={explorer} />
{/* Collections Tree and Tabs - End */}
<div
className="dataExplorerErrorConsoleContainer"

View File

@ -1,4 +1,5 @@
import { clamp } from "@fluentui/react";
import { useSelectedNode } from "Explorer/useSelectedNode";
import create, { UseStore } from "zustand";
import * as ViewModels from "../Contracts/ViewModels";
import { CollectionTabKind } from "../Contracts/ViewModels";
@ -74,7 +75,11 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
set((state) => ({ openedTabs: [...state.openedTabs, tab], activeTab: tab, activeReactTab: undefined }));
tab.onActivate();
},
activateReactTab: (tabKind: ReactTabKind): void => set({ activeTab: undefined, activeReactTab: tabKind }),
activateReactTab: (tabKind: ReactTabKind): void => {
// Clear the selected node when switching to a react tab.
useSelectedNode.getState().setSelectedNode(undefined);
set({ activeTab: undefined, activeReactTab: tabKind });
},
updateTab: (tab: TabsBase) => {
if (get().activeTab?.tabId === tab.tabId) {
set({ activeTab: tab });

View File

@ -8,7 +8,7 @@ test("Cassandra keyspace and table CRUD", async ({ page }) => {
const explorer = await DataExplorer.open(page, TestAccount.Cassandra);
await explorer.commandBarButton("New Table").click();
await explorer.globalCommandButton("New Table").click();
await explorer.whilePanelOpen("Add Table", async (panel, okButton) => {
await panel.getByPlaceholder("Type a new keyspace id").fill(keyspaceId);
await panel.getByPlaceholder("Enter table Id").fill(tableId);
@ -16,9 +16,9 @@ test("Cassandra keyspace and table CRUD", async ({ page }) => {
await okButton.click();
});
const keyspaceNode = explorer.treeNode(`DATA/${keyspaceId}`);
const keyspaceNode = explorer.treeNode(keyspaceId);
await keyspaceNode.expand();
const tableNode = explorer.treeNode(`DATA/${keyspaceId}/${tableId}`);
const tableNode = explorer.treeNode(`${keyspaceId}/${tableId}`);
await tableNode.openContextMenu();
await tableNode.contextMenuItem("Delete Table").click();

View File

@ -63,11 +63,21 @@ export async function getTestExplorerUrl(accountType: TestAccount, iframeSrc?: s
// We can't retrieve AZ CLI credentials from the browser so we get them here.
const token = await getAzureCLICredentialsToken();
const accountName = getAccountName(accountType);
const baseUrl = `https://localhost:1234/testExplorer.html?accountName=${accountName}&resourceGroup=${resourceGroupName}&subscriptionId=${subscriptionId}&token=${token}`;
const params = new URLSearchParams();
params.set("accountName", accountName);
params.set("resourceGroup", resourceGroupName);
params.set("subscriptionId", subscriptionId);
params.set("token", token);
// There seem to be occasional CORS issues with calling the copilot APIs (/api/tokens/sampledataconnection/v2, for example)
// For now, since we don't test copilot, we can disable the copilot APIs by setting the feature flag to false.
params.set("feature.enableCopilot", "false");
if (iframeSrc) {
return `${baseUrl}&iframeSrc=${iframeSrc}`;
params.set("iframeSrc", iframeSrc);
}
return baseUrl;
return `https://localhost:1234/testExplorer.html?${params.toString()}`;
}
/** Helper class that provides locator methods for TreeNode elements, on top of a Locator */
@ -106,18 +116,30 @@ class TreeNode {
export class DataExplorer {
constructor(public frame: Frame) {}
/** Select the primary global command button.
*
* There's only a single "primary" button, but we still require you to pass the label to confirm you're selecting the right button.
*/
globalCommandButton(label: string): Locator {
return this.frame.getByTestId("GlobalCommands").getByText(label);
}
/** Select the command bar button with the specified label */
commandBarButton(label: string): Locator {
return this.frame.getByTestId(`CommandBar/Button:${label}`).and(this.frame.locator("css=button"));
}
/** Select the side panel with the specified title */
panel(title: string): Locator {
return this.frame.getByTestId(`Panel:${title}`);
}
/** Select the tree node with the specified id */
treeNode(id: string): TreeNode {
return new TreeNode(this.frame.getByTestId(`TreeNode:${id}`), this.frame, id);
}
/** Waits for the panel with the specified title to be open, then runs the provided callback. After the callback completes, waits for the panel to close. */
async whilePanelOpen(title: string, action: (panel: Locator, okButton: Locator) => Promise<void>): Promise<void> {
const panel = this.panel(title);
await panel.waitFor();
@ -126,6 +148,7 @@ export class DataExplorer {
await panel.waitFor({ state: "detached" });
}
/** Waits for the Data Explorer app to load */
static async waitForExplorer(page: Page) {
const iframeElement = await page.getByTestId("DataExplorerFrame").elementHandle();
if (iframeElement === null) {
@ -143,6 +166,7 @@ export class DataExplorer {
return new DataExplorer(explorerFrame);
}
/** Opens the Data Explorer app using the specified test account (and optionally, the provided IFRAME src url). */
static async open(page: Page, testAccount: TestAccount, iframeSrc?: string): Promise<DataExplorer> {
const url = await getTestExplorerUrl(testAccount, iframeSrc);
await page.goto(url);

View File

@ -9,7 +9,7 @@ test("Gremlin graph CRUD", async ({ page }) => {
const explorer = await DataExplorer.open(page, TestAccount.Gremlin);
// Create new database and graph
await explorer.commandBarButton("New Graph").click();
await explorer.globalCommandButton("New Graph").click();
await explorer.whilePanelOpen("New Graph", async (panel, okButton) => {
await panel.getByPlaceholder("Type a new database id").fill(databaseId);
await panel.getByRole("textbox", { name: "Graph id, Example Graph1" }).fill(graphId);
@ -18,9 +18,9 @@ test("Gremlin graph CRUD", async ({ page }) => {
await okButton.click();
});
const databaseNode = explorer.treeNode(`DATA/${databaseId}`);
const databaseNode = explorer.treeNode(databaseId);
await databaseNode.expand();
const graphNode = explorer.treeNode(`DATA/${databaseId}/${graphId}`);
const graphNode = explorer.treeNode(`${databaseId}/${graphId}`);
await graphNode.openContextMenu();
await graphNode.contextMenuItem("Delete Graph").click();

View File

@ -14,7 +14,7 @@ import { DataExplorer, TestAccount, generateDatabaseNameWithTimestamp, generateU
const explorer = await DataExplorer.open(page, accountType);
await explorer.commandBarButton("New Collection").click();
await explorer.globalCommandButton("New Collection").click();
await explorer.whilePanelOpen("New Collection", async (panel, okButton) => {
await panel.getByPlaceholder("Type a new database id").fill(databaseId);
await panel.getByRole("textbox", { name: "Collection id, Example Collection1" }).fill(collectionId);
@ -23,9 +23,9 @@ import { DataExplorer, TestAccount, generateDatabaseNameWithTimestamp, generateU
await okButton.click();
});
const databaseNode = explorer.treeNode(`DATA/${databaseId}`);
const databaseNode = explorer.treeNode(databaseId);
await databaseNode.expand();
const collectionNode = explorer.treeNode(`DATA/${databaseId}/${collectionId}`);
const collectionNode = explorer.treeNode(`${databaseId}/${collectionId}`);
await collectionNode.openContextMenu();
await collectionNode.contextMenuItem("Delete Collection").click();

View File

@ -8,7 +8,7 @@ test("SQL database and container CRUD", async ({ page }) => {
const explorer = await DataExplorer.open(page, TestAccount.SQL);
await explorer.commandBarButton("New Container").click();
await explorer.globalCommandButton("New Container").click();
await explorer.whilePanelOpen("New Container", async (panel, okButton) => {
await panel.getByPlaceholder("Type a new database id").fill(databaseId);
await panel.getByRole("textbox", { name: "Container id, Example Container1" }).fill(containerId);
@ -17,9 +17,9 @@ test("SQL database and container CRUD", async ({ page }) => {
await okButton.click();
});
const databaseNode = explorer.treeNode(`DATA/${databaseId}`);
const databaseNode = explorer.treeNode(databaseId);
await databaseNode.expand();
const containerNode = explorer.treeNode(`DATA/${databaseId}/${containerId}`);
const containerNode = explorer.treeNode(`${databaseId}/${containerId}`);
await containerNode.openContextMenu();
await containerNode.contextMenuItem("Delete Container").click();

View File

@ -47,7 +47,7 @@ test("SQL account using Resource token", async ({ page }) => {
const explorer = await DataExplorer.waitForExplorer(page);
const collectionNode = explorer.treeNode(`DATA/${collectionId}`);
const collectionNode = explorer.treeNode(`${collectionId}`);
await collectionNode.element.waitFor();
await expect(collectionNode.element).toBeAttached();

View File

@ -7,17 +7,17 @@ test("Tables CRUD", async ({ page }) => {
const explorer = await DataExplorer.open(page, TestAccount.Tables);
await explorer.commandBarButton("New Table").click();
await explorer.globalCommandButton("New Table").click();
await explorer.whilePanelOpen("New Table", async (panel, okButton) => {
await panel.getByRole("textbox", { name: "Table id, Example Table1" }).fill(tableId);
await panel.getByLabel("Table Max RU/s").fill("1000");
await okButton.click();
});
const databaseNode = explorer.treeNode("DATA/TablesDB");
const databaseNode = explorer.treeNode("TablesDB");
await databaseNode.expand();
const tableNode = explorer.treeNode(`DATA/TablesDB/${tableId}`);
const tableNode = explorer.treeNode(`TablesDB/${tableId}`);
await expect(tableNode.element).toBeAttached();
await tableNode.openContextMenu();

View File

@ -11,7 +11,6 @@
"./src/AuthType.ts",
"./src/Bindings/ReactBindingHandler.ts",
"./src/Common/ArrayHashMap.ts",
"./src/Common/CollapsedResourceTree.tsx",
"./src/Common/Constants.ts",
"./src/Common/DeleteFeedback.ts",
"./src/Common/DocumentUtility.ts",