Migrate resource tree for resource token to react (#956)

This commit is contained in:
victor-meng 2021-07-22 17:11:19 -07:00 committed by GitHub
parent dc21032d69
commit fecac5625a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 143 additions and 266 deletions

View File

@ -191,5 +191,4 @@ src/Explorer/Notebook/NotebookRenderer/decorators/kbd-shortcuts/index.tsx
src/Explorer/Notebook/temp/inputs/connected-editors/codemirror.tsx src/Explorer/Notebook/temp/inputs/connected-editors/codemirror.tsx
src/Explorer/Tree/ResourceTreeAdapter.tsx src/Explorer/Tree/ResourceTreeAdapter.tsx
__mocks__/monaco-editor.ts __mocks__/monaco-editor.ts
src/Explorer/Tree/ResourceTreeAdapterForResourceToken.test.tsx
src/Explorer/Tree/ResourceTree.tsx src/Explorer/Tree/ResourceTree.tsx

View File

@ -1,272 +1,270 @@
@import "./Common/Constants"; @import "./Common/Constants";
.resourceTree { .resourceTree {
height: 100%;
flex: 0 0 auto;
.main {
height: 100%; height: 100%;
flex: 0 0 auto; }
.main {
height: 100%;
}
} }
.resourceTreeScroll { .resourceTreeScroll {
height: 100%; height: 100%;
display: flex; display: flex;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
padding-right: 10px; padding-right: 10px;
} }
.userSelectNone { .userSelectNone {
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
} }
.treeHovermargin { .treeHovermargin {
margin-left: 16px; margin-left: 16px;
} }
.highlight { .highlight {
padding: @SmallSpace 2px; padding: @SmallSpace 2px;
outline: 0; outline: 0;
&:hover { &:hover {
.hover(); .hover();
} }
&:active { &:active {
.active(); .active();
} }
&:focus { &:focus {
.focus(); .focus();
} }
} }
.contextmenushowing { .contextmenushowing {
background-color: #EEE; background-color: #eee;
} }
.collectionstree { .collectionstree {
width: 100%; width: 100%;
margin-top: @DefaultSpace; margin-top: @DefaultSpace;
.databaseList {
list-style-type: none;
padding-left: 0px;
.databaseList { .collectionList {
list-style-type: none; padding-left: (2 * @MediumSpace);
padding-left: 0px;
.collectionList {
padding-left:(2 * @MediumSpace);
}
.collectionChildList {
padding-left: @LargeSpace;
}
.databaseDocuments {
padding-left: (5 * @MediumSpace);
}
} }
.collectionChildList {
padding-left: @LargeSpace;
}
.databaseDocuments {
padding-left: (5 * @MediumSpace);
}
}
} }
.pointerCursor { .pointerCursor {
cursor: pointer; cursor: pointer;
} }
.menuEllipsis { .menuEllipsis {
padding-right: 6px; padding-right: 6px;
font-weight: bold; font-weight: bold;
font-size: 18px; font-size: 18px;
position: relative; position: relative;
top: -5px; top: -5px;
left: 0px; left: 0px;
float: right; float: right;
display: none; display: none;
padding-left: 6px!important; padding-left: 6px !important;
line-height: @TreeLineHeight; line-height: @TreeLineHeight;
} }
.databaseMenu { .databaseMenu {
.flex-display(); .flex-display();
} }
.databaseMenu:hover .menuEllipsis, .databaseMenu:hover .menuEllipsis,
.databaseMenu:focus .menuEllipsis { .databaseMenu:focus .menuEllipsis {
display: block; display: block;
} }
.databaseCollChildTextOverflow { .databaseCollChildTextOverflow {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
flex: 1; flex: 1;
} }
.collectionMenu { .collectionMenu {
.flex-display(); .flex-display();
} }
.collectionMenu:hover .menuEllipsis, .collectionMenu:hover .menuEllipsis,
.collectionMenu:focus .menuEllipsis { .collectionMenu:focus .menuEllipsis {
display: block; display: block;
} }
.documentsMenu:hover .menuEllipsis, .documentsMenu:hover .menuEllipsis,
.documentsMenu:focus .menuEllipsis { .documentsMenu:focus .menuEllipsis {
display: block; display: block;
} }
.treeChildMenu { .treeChildMenu {
display: flex; display: flex;
} }
.storedProcedureMenu:hover .menuEllipsis, .storedProcedureMenu:hover .menuEllipsis,
.storedProcedureMenu:focus .menuEllipsis { .storedProcedureMenu:focus .menuEllipsis {
display: block; display: block;
} }
.childMenu { .childMenu {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
padding-left: (6 * @MediumSpace); padding-left: (6 * @MediumSpace);
width: 100%; width: 100%;
} }
.storedChildMenu:hover .menuEllipsis, .storedChildMenu:hover .menuEllipsis,
.storedChildMenu:focus .menuEllipsis { .storedChildMenu:focus .menuEllipsis {
display: block; display: block;
} }
.contextmenu6 { .contextmenu6 {
top: -29px; top: -29px;
} }
.userDefinedMenu:hover .contextmenu6 { .userDefinedMenu:hover .contextmenu6 {
display: block; display: block;
} }
.userDefinedchildMenu:hover .menuEllipsis, .userDefinedchildMenu:hover .menuEllipsis,
.userDefinedchildMenu:focus .menuEllipsis { .userDefinedchildMenu:focus .menuEllipsis {
display: block; display: block;
} }
.triggersMenu:hover .menuEllipsis, .triggersMenu:hover .menuEllipsis,
.triggersMenu:focus .menuEllipsis { .triggersMenu:focus .menuEllipsis {
display: block; display: block;
} }
.triggersChildMenu:hover .menuEllipsis, .triggersChildMenu:hover .menuEllipsis,
.triggersChildMenu:focus .menuEllipsis { .triggersChildMenu:focus .menuEllipsis {
display: block; display: block;
} }
.databaseId { .databaseId {
font-size: 14px; font-size: 14px;
} }
.storedUdfTriggerMenu { .storedUdfTriggerMenu {
padding-left: 0px; padding-left: 0px;
} }
.collectionstree img { .collectionstree img {
width: 16px; width: 16px;
height: 16px; height: 16px;
vertical-align: text-top; vertical-align: text-top;
} }
img.collectionsTreeCollapseExpand { img.collectionsTreeCollapseExpand {
width: 10px; width: 10px;
height: 10px; height: 10px;
vertical-align: middle; vertical-align: middle;
margin-bottom: 5px; margin-bottom: 5px;
} }
.collapsed::before { .collapsed::before {
content: "\23F5"; content: "\23F5";
margin-left: 0px; margin-left: 0px;
font-size: 15px; font-size: 15px;
} }
.expanded::before { .expanded::before {
content: '\23F7'; content: "\23F7";
margin-left: 0px; margin-left: 0px;
font-size: 15px; font-size: 15px;
} }
.collectionMenuChildren { .collectionMenuChildren {
padding-left: 42px; padding-left: 42px;
} }
.main-nav { .main-nav {
width: 100vh; width: 100vh;
height: 40px; height: 40px;
background: white; background: white;
transform-origin: left top; transform-origin: left top;
-webkit-transform-origin: left top; -webkit-transform-origin: left top;
-ms-transform-origin: left top; -ms-transform-origin: left top;
transform: rotate(-90deg) translateX(-100%); transform: rotate(-90deg) translateX(-100%);
-webkit-transform: rotate(-90deg) translateX(-100%); -webkit-transform: rotate(-90deg) translateX(-100%);
-ms-transform: rotate(-90deg) translateX(-100%); -ms-transform: rotate(-90deg) translateX(-100%);
border-bottom: 1px solid #CCC; border-bottom: 1px solid #ccc;
} }
.main-nav-img { .main-nav-img {
width: 16px; width: 16px;
height: 16px; height: 16px;
margin: -32px 0 0 0; margin: -32px 0 0 0;
transform: rotate(-90deg) translateX(-100%); transform: rotate(-90deg) translateX(-100%);
-webkit-transform: rotate(-90deg) translateX(-100%); -webkit-transform: rotate(-90deg) translateX(-100%);
-ms-transform: rotate(-90deg) translateX(-100%); -ms-transform: rotate(-90deg) translateX(-100%);
} }
.main-nav-img.main-nav-sub-img { .main-nav-img.main-nav-sub-img {
width: 16px; width: 16px;
height: 16px; height: 16px;
margin: 0px 0px 0 0; margin: 0px 0px 0 0;
transform: rotate(180deg) translateX(0%); transform: rotate(180deg) translateX(0%);
-webkit-transform: rotate(180deg) translateX(0%); -webkit-transform: rotate(180deg) translateX(0%);
-ms-transform: rotate(180deg) translateX(0%); -ms-transform: rotate(180deg) translateX(0%);
position: absolute; position: absolute;
right: -8px; right: -8px;
top: 16px; top: 16px;
} }
ul.nav { ul.nav {
margin: 0 auto; margin: 0 auto;
margin-top: 0px; margin-top: 0px;
margin-left: 0px; margin-left: 0px;
} }
.mini ul.nav li { .mini ul.nav li {
float: right; float: right;
line-height: 25px; line-height: 25px;
height: auto; height: auto;
margin-top: 3px; margin-top: 3px;
} }
.spancolchildstyle { .spancolchildstyle {
padding: 4px; padding: 4px;
} }
.contextmenubutton { .contextmenubutton {
float: right; float: right;
display: none; display: none;
} }
.highlight:hover>.contextmenubutton { .highlight:hover > .contextmenubutton {
display: unset; display: unset;
} }
.highlight:hover>.contextmenubutton::after { .highlight:hover > .contextmenubutton::after {
content: "\2026"; content: "\2026";
font-size: 12px; font-size: 12px;
} }
.showEllipsis { .showEllipsis {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
} }

View File

@ -3,6 +3,7 @@ import arrowLeftImg from "../../images/imgarrowlefticon.svg";
import refreshImg from "../../images/refresh-cosmos.svg"; import refreshImg from "../../images/refresh-cosmos.svg";
import { AuthType } from "../AuthType"; import { AuthType } from "../AuthType";
import Explorer from "../Explorer/Explorer"; import Explorer from "../Explorer/Explorer";
import { ResourceTokenTree } from "../Explorer/Tree/ResourceTokenTree";
import { ResourceTree } from "../Explorer/Tree/ResourceTree"; import { ResourceTree } from "../Explorer/Tree/ResourceTree";
import { userContext } from "../UserContext"; import { userContext } from "../UserContext";
@ -52,7 +53,7 @@ export const ResourceTreeContainer: FunctionComponent<ResourceTreeContainerProps
</div> </div>
</div> </div>
{userContext.authType === AuthType.ResourceToken ? ( {userContext.authType === AuthType.ResourceToken ? (
<div style={{ overflowY: "auto" }} data-bind="react:resourceTreeForResourceToken" /> <ResourceTokenTree />
) : userContext.features.enableKOResourceTree ? ( ) : userContext.features.enableKOResourceTree ? (
<div style={{ overflowY: "auto" }} data-bind="react:resourceTree" /> <div style={{ overflowY: "auto" }} data-bind="react:resourceTree" />
) : ( ) : (

View File

@ -44,10 +44,6 @@ exports[`SettingsComponent renders 1`] = `
"copyNotebook": [Function], "copyNotebook": [Function],
"parameters": [Function], "parameters": [Function],
}, },
"resourceTreeForResourceToken": ResourceTreeAdapterForResourceToken {
"container": [Circular],
"parameters": [Function],
},
}, },
"databaseId": "test", "databaseId": "test",
"defaultTtl": [Function], "defaultTtl": [Function],
@ -115,10 +111,6 @@ exports[`SettingsComponent renders 1`] = `
"copyNotebook": [Function], "copyNotebook": [Function],
"parameters": [Function], "parameters": [Function],
}, },
"resourceTreeForResourceToken": ResourceTreeAdapterForResourceToken {
"container": [Circular],
"parameters": [Function],
},
}, },
"databaseId": "test", "databaseId": "test",
"defaultTtl": [Function], "defaultTtl": [Function],

View File

@ -59,7 +59,6 @@ import TerminalTab from "./Tabs/TerminalTab";
import Database from "./Tree/Database"; import Database from "./Tree/Database";
import ResourceTokenCollection from "./Tree/ResourceTokenCollection"; import ResourceTokenCollection from "./Tree/ResourceTokenCollection";
import { ResourceTreeAdapter } from "./Tree/ResourceTreeAdapter"; import { ResourceTreeAdapter } from "./Tree/ResourceTreeAdapter";
import { ResourceTreeAdapterForResourceToken } from "./Tree/ResourceTreeAdapterForResourceToken";
import StoredProcedure from "./Tree/StoredProcedure"; import StoredProcedure from "./Tree/StoredProcedure";
import { useDatabases } from "./useDatabases"; import { useDatabases } from "./useDatabases";
import { useSelectedNode } from "./useSelectedNode"; import { useSelectedNode } from "./useSelectedNode";
@ -74,9 +73,6 @@ export default class Explorer {
// Resource Tree // Resource Tree
private resourceTree: ResourceTreeAdapter; private resourceTree: ResourceTreeAdapter;
// Resource Token
public resourceTreeForResourceToken: ResourceTreeAdapterForResourceToken;
// Tabs // Tabs
public isTabsContentExpanded: ko.Observable<boolean>; public isTabsContentExpanded: ko.Observable<boolean>;
@ -186,7 +182,6 @@ export default class Explorer {
); );
this.resourceTree = new ResourceTreeAdapter(this); this.resourceTree = new ResourceTreeAdapter(this);
this.resourceTreeForResourceToken = new ResourceTreeAdapterForResourceToken(this);
// Override notebook server parameters from URL parameters // Override notebook server parameters from URL parameters
if (userContext.features.notebookServerUrl && userContext.features.notebookServerToken) { if (userContext.features.notebookServerUrl && userContext.features.notebookServerToken) {

View File

@ -33,10 +33,6 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
"copyNotebook": [Function], "copyNotebook": [Function],
"parameters": [Function], "parameters": [Function],
}, },
"resourceTreeForResourceToken": ResourceTreeAdapterForResourceToken {
"container": [Circular],
"parameters": [Function],
},
}, },
"getRepo": [Function], "getRepo": [Function],
"pinRepo": [Function], "pinRepo": [Function],

View File

@ -23,10 +23,6 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"copyNotebook": [Function], "copyNotebook": [Function],
"parameters": [Function], "parameters": [Function],
}, },
"resourceTreeForResourceToken": ResourceTreeAdapterForResourceToken {
"container": [Circular],
"parameters": [Function],
},
} }
} }
inProgressMessage="Creating directory " inProgressMessage="Creating directory "

View File

@ -1,45 +1,18 @@
import * as ko from "knockout"; import React from "react";
import * as React from "react";
import CollectionIcon from "../../../images/tree-collection.svg"; import CollectionIcon from "../../../images/tree-collection.svg";
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
import * as ViewModels from "../../Contracts/ViewModels"; import * as ViewModels from "../../Contracts/ViewModels";
import { useTabs } from "../../hooks/useTabs"; import { useTabs } from "../../hooks/useTabs";
import { userContext } from "../../UserContext"; import { userContext } from "../../UserContext";
import { TreeComponent, TreeNode } from "../Controls/TreeComponent/TreeComponent"; import { TreeComponent, TreeNode } from "../Controls/TreeComponent/TreeComponent";
import Explorer from "../Explorer";
import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter"; import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
import { mostRecentActivity } from "../MostRecentActivity/MostRecentActivity"; import { mostRecentActivity } from "../MostRecentActivity/MostRecentActivity";
import { NotebookContentItem } from "../Notebook/NotebookContentItem";
import { useDatabases } from "../useDatabases"; import { useDatabases } from "../useDatabases";
import { useSelectedNode } from "../useSelectedNode"; import { useSelectedNode } from "../useSelectedNode";
export class ResourceTreeAdapterForResourceToken implements ReactAdapter { export const ResourceTokenTree: React.FC = (): JSX.Element => {
public parameters: ko.Observable<number>; const collection = useDatabases((state) => state.resourceTokenCollection);
public myNotebooksContentRoot: NotebookContentItem;
public constructor(private container: Explorer) { const buildCollectionNode = (): TreeNode => {
this.parameters = ko.observable(Date.now());
useDatabases.subscribe(
() => this.triggerRender(),
(state) => state.resourceTokenCollection
);
useSelectedNode.subscribe(() => this.triggerRender());
useTabs.subscribe(
() => this.triggerRender(),
(state) => state.activeTab
);
this.triggerRender();
}
public renderComponent(): JSX.Element {
const dataRootNode = this.buildCollectionNode();
return <TreeComponent className="dataResourceTree" rootNode={dataRootNode} />;
}
public buildCollectionNode(): TreeNode {
const collection: ViewModels.CollectionBase = useDatabases.getState().resourceTokenCollection;
if (!collection) { if (!collection) {
return { return {
label: undefined, label: undefined,
@ -86,9 +59,7 @@ export class ResourceTreeAdapterForResourceToken implements ReactAdapter {
isExpanded: true, isExpanded: true,
children: [collectionNode], children: [collectionNode],
}; };
} };
public triggerRender() { return <TreeComponent className="dataResourceTree" rootNode={buildCollectionNode()} />;
window.requestAnimationFrame(() => this.parameters(Date.now())); };
}
}

View File

@ -1,35 +0,0 @@
import { shallow } from "enzyme";
import React from "react";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
import { TreeComponent, TreeComponentProps, TreeNode } from "../Controls/TreeComponent/TreeComponent";
import Explorer from "../Explorer";
import { useDatabases } from "../useDatabases";
import ResourceTokenCollection from "./ResourceTokenCollection";
import { ResourceTreeAdapterForResourceToken } from "./ResourceTreeAdapterForResourceToken";
describe("Resource tree for resource token", () => {
const mockContainer = {} as Explorer;
const resourceTree = new ResourceTreeAdapterForResourceToken(mockContainer);
const mockCollection = {
_rid: "fakeRid",
_self: "fakeSelf",
id: "fakeId",
} as DataModels.Collection;
const mockResourceTokenCollection: ViewModels.CollectionBase = new ResourceTokenCollection(
mockContainer,
"fakeDatabaseId",
mockCollection
);
useDatabases.setState({ resourceTokenCollection: mockResourceTokenCollection });
it("should render", () => {
const rootNode: TreeNode = resourceTree.buildCollectionNode();
const props: TreeComponentProps = {
rootNode,
className: "dataResourceTree",
};
const wrapper = shallow(<TreeComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -1,36 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Resource tree for resource token should render 1`] = `
<div
className="treeComponent dataResourceTree"
role="tree"
>
<TreeNodeComponent
generation={0}
node={
Object {
"children": Array [
Object {
"children": Array [
Object {
"isSelected": [Function],
"label": "Items",
"onClick": [Function],
},
],
"className": "collectionHeader",
"iconSrc": "",
"isExpanded": true,
"isSelected": [Function],
"label": "fakeId",
"onClick": [Function],
},
],
"isExpanded": true,
"label": undefined,
}
}
paddingLeft={0}
/>
</div>
`;