Migrate new graph vertex panel to react (#702)

Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>
This commit is contained in:
vaidankarswapnil
2021-04-22 20:17:59 +05:30
committed by GitHub
parent 9d411c57b0
commit c6766dd69e
26 changed files with 663 additions and 814 deletions

View File

@@ -1,60 +0,0 @@
<div data-bind="visible: visible, event: { keydown: onPaneKeyDown }">
<div class="contextual-pane-out" data-bind="click: cancel, clickBubble: false"></div>
<div class="contextual-pane" data-bind="attr: { id: id }">
<!-- New Vertex form - Start -->
<div class="contextual-pane-in">
<form class="paneContentContainer" data-bind="submit: submit">
<!-- New Vertex header - Start -->
<div class="firstdivbg headerline">
<span role="heading" aria-level="2">New Vertex</span>
<div
class="closeImg"
role="button"
aria-label="Close pane"
data-bind="click: cancel, event: { keypress: onCloseKeyPress }"
tabindex="0"
>
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
</div>
</div>
<!-- New Vertex header - End -->
<!-- New Vertex errors - Start -->
<div
aria-live="assertive"
class="warningErrorContainer"
data-bind="visible: formErrors() && formErrors() !== ''"
>
<div class="warningErrorContent">
<span><img class="paneErrorIcon" src="/error_red.svg" alt="Error" /></span>
<span class="warningErrorDetailsLinkContainer">
<span class="formErrors" data-bind="text: formErrors, attr: { title: formErrors }"></span>
<a
class="errorLink"
role="link"
data-bind="visible: formErrorsDetails() && formErrorsDetails() !== '' , click: showErrorDetails, event: { keypress: onMoreDetailsKeyPress }"
tabindex="0"
>
More details
</a>
</span>
</div>
</div>
<!-- New Vertex errors - End -->
<!-- New Vertex inputs - Start -->
<div class="paneMainContent">
<new-vertex-form
class="newvertexContainer"
params="{ newVertexData: tempVertexData, firstFieldHasFocus: firstFieldHasFocus, partitionKeyProperty: partitionKeyProperty }"
></new-vertex-form>
</div>
<div class="paneFooter">
<div class="leftpanel-okbut"><input type="submit" value="OK" class="btncreatecoll1" /></div>
</div>
<!-- New Vertex inputs - End -->
</form>
</div>
<!-- New Vertex form - End -->
</div>
</div>

View File

@@ -1,10 +0,0 @@
@import "../../../less/Common/Constants";
.newvertexContainer {
height:100%;
overflow-y: auto;
overflow-x: hidden;
white-space: nowrap;
.flex-display();
.flex-direction();
}

View File

@@ -1,65 +0,0 @@
import * as ko from "knockout";
import * as ViewModels from "../../Contracts/ViewModels";
import { ContextualPaneBase } from "./ContextualPaneBase";
import { KeyCodes } from "../../Common/Constants";
import Explorer from "../Explorer";
export default class NewVertexPane extends ContextualPaneBase {
public container: Explorer;
public visible: ko.Observable<boolean>;
public formErrors: ko.Observable<string>;
public formErrorsDetails: ko.Observable<string>;
// Graph style stuff
public tempVertexData: ko.Observable<ViewModels.NewVertexData>; // vertex data being edited
private onSubmitCreateCallback: (newVertexData: ViewModels.NewVertexData) => void;
private partitionKeyProperty: ko.Observable<string>;
constructor(options: ViewModels.PaneOptions) {
super(options);
this.tempVertexData = ko.observable<ViewModels.NewVertexData>(null);
this.partitionKeyProperty = ko.observable(null);
this.resetData();
}
public submit() {
// Commit edited changes
if (this.onSubmitCreateCallback != null) {
this.onSubmitCreateCallback(this.tempVertexData());
}
// this.close();
}
public resetData() {
super.resetData();
this.onSubmitCreateCallback = null;
this.tempVertexData({
label: "",
properties: <ViewModels.InputProperty[]>[],
});
this.partitionKeyProperty(null);
}
public subscribeOnSubmitCreate(callback: (newVertexData: ViewModels.NewVertexData) => void): void {
this.onSubmitCreateCallback = callback;
}
public setPartitionKeyProperty(pKeyProp: string): void {
this.partitionKeyProperty(pKeyProp);
}
public onMoreDetailsKeyPress = (source: any, event: KeyboardEvent): boolean => {
if (event.keyCode === KeyCodes.Space || event.keyCode === KeyCodes.Enter) {
this.showErrorDetails();
return false;
}
return true;
};
public buildString = (prefix: string, index: number): string => {
return `${prefix}${index}`;
};
}

View File

@@ -0,0 +1,10 @@
@import "../../../../less/Common/Constants";
.newvertexContainer {
height: 100%;
overflow-y: auto;
overflow-x: hidden;
white-space: nowrap;
.flex-display();
.flex-direction();
}

View File

@@ -0,0 +1,78 @@
import { shallow, ShallowWrapper } from "enzyme";
import React from "react";
import * as ViewModels from "../../../Contracts/ViewModels";
import Explorer from "../../Explorer";
import { NewVertexPanel } from "./NewVertexPanel";
describe("New Vertex Panel", () => {
let fakeExplorer: Explorer;
let wrapper: ShallowWrapper;
beforeEach(() => {
fakeExplorer = new Explorer();
});
it("should render default property", () => {
const props = {
explorer: fakeExplorer,
partitionKeyPropertyProp: "",
onSubmit: (): void => undefined,
openNotificationConsole: (): void => undefined,
};
wrapper = shallow(<NewVertexPanel {...props} />);
expect(wrapper).toMatchSnapshot();
});
it("should render button in footer", () => {
const button = wrapper.find("PrimaryButton").first();
expect(button).toBeDefined();
});
it("should render form", () => {
const form = wrapper.find("form").first();
expect(form).toBeDefined();
});
it("should call form submit method", () => {
const onSubmitSpy = jest.fn();
const newWrapper = shallow(
<NewVertexPanel
explorer={fakeExplorer}
partitionKeyPropertyProp={undefined}
openNotificationConsole={(): void => undefined}
onSubmit={onSubmitSpy}
/>
);
//eslint-disable-next-line
newWrapper.find("form").simulate("submit", { preventDefault: () => {} });
expect(onSubmitSpy).toHaveBeenCalled();
});
it("should call error and success scenario method", () => {
const onSubmitSpy = jest.fn();
const onErrorSpy = jest.fn();
const onSuccessSpy = jest.fn();
const fakeNewVertexData: ViewModels.NewVertexData = {
label: "",
properties: [],
};
const result = onSubmitSpy(fakeNewVertexData, onErrorSpy, onSuccessSpy);
const newWrapper = shallow(
<NewVertexPanel
explorer={fakeExplorer}
partitionKeyPropertyProp={undefined}
openNotificationConsole={(): void => undefined}
onSubmit={onSubmitSpy}
/>
);
//eslint-disable-next-line
newWrapper.find("form").simulate("submit", { preventDefault: () => {} });
expect(result).toBeUndefined();
expect(onSubmitSpy).toHaveBeenCalledWith(fakeNewVertexData, onErrorSpy, onSuccessSpy);
});
});

View File

@@ -0,0 +1,74 @@
import { useBoolean } from "@uifabric/react-hooks";
import React, { FunctionComponent, useState } from "react";
import * as ViewModels from "../../../Contracts/ViewModels";
import Explorer from "../../Explorer";
import { NewVertexComponent } from "../../Graph/NewVertexComponent/NewVertexComponent";
import { PanelFooterComponent } from "../PanelFooterComponent";
import { PanelInfoErrorComponent } from "../PanelInfoErrorComponent";
import { PanelLoadingScreen } from "../PanelLoadingScreen";
export interface INewVertexPanelProps {
explorer: Explorer;
partitionKeyPropertyProp: string;
onSubmit: (result: ViewModels.NewVertexData, onError: (errorMsg: string) => void, onSuccess: () => void) => void;
openNotificationConsole: () => void;
}
export const NewVertexPanel: FunctionComponent<INewVertexPanelProps> = ({
explorer,
partitionKeyPropertyProp,
onSubmit,
openNotificationConsole,
}: INewVertexPanelProps): JSX.Element => {
let newVertexDataValue: ViewModels.NewVertexData;
const [errorMessage, setErrorMessage] = useState<string>("");
const [showErrorDetails, setShowErrorDetails] = useState<boolean>(false);
const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false);
const buttonLabel = "OK";
const submit = (event: React.MouseEvent<HTMLFormElement>) => {
event.preventDefault();
setErrorMessage(undefined);
setShowErrorDetails(false);
if (onSubmit !== undefined) {
setLoadingTrue();
onSubmit(newVertexDataValue, onError, onSuccess);
}
};
const onError = (errorMsg: string) => {
setErrorMessage(errorMsg);
setShowErrorDetails(true);
setLoadingFalse();
};
const onSuccess = () => {
setLoadingFalse();
explorer.closeSidePanel();
};
const onChange = (newVertexData: ViewModels.NewVertexData) => {
newVertexDataValue = newVertexData;
};
return (
<form className="panelFormWrapper" onSubmit={(event: React.MouseEvent<HTMLFormElement>) => submit(event)}>
{errorMessage && (
<PanelInfoErrorComponent
message={errorMessage}
messageType="error"
showErrorDetails={showErrorDetails}
openNotificationConsole={openNotificationConsole}
/>
)}
<div className="panelMainContent">
<NewVertexComponent
newVertexDataProp={newVertexDataValue}
partitionKeyPropertyProp={partitionKeyPropertyProp}
onChangeProp={onChange}
/>
</div>
<PanelFooterComponent buttonLabel={buttonLabel} />
{isLoading && <PanelLoadingScreen />}
</form>
);
};

View File

@@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`New Vertex Panel should render default property 1`] = `
<form
className="panelFormWrapper"
onSubmit={[Function]}
>
<div
className="panelMainContent"
>
<NewVertexComponent
onChangeProp={[Function]}
partitionKeyPropertyProp=""
/>
</div>
<PanelFooterComponent
buttonLabel="OK"
/>
</form>
`;

View File

@@ -2,7 +2,6 @@ import AddCollectionPaneTemplate from "./AddCollectionPane.html";
import AddDatabasePaneTemplate from "./AddDatabasePane.html";
import CassandraAddCollectionPaneTemplate from "./CassandraAddCollectionPane.html";
import GitHubReposPaneTemplate from "./GitHubReposPane.html";
import GraphNewVertexPaneTemplate from "./GraphNewVertexPane.html";
import GraphStylingPaneTemplate from "./GraphStylingPane.html";
import StringInputPaneTemplate from "./StringInputPane.html";
import TableAddEntityPaneTemplate from "./Tables/TableAddEntityPane.html";
@@ -32,15 +31,6 @@ export class AddCollectionPaneComponent {
}
}
export class GraphNewVertexPaneComponent {
constructor() {
return {
viewModel: PaneComponent,
template: GraphNewVertexPaneTemplate,
};
}
}
export class GraphStylingPaneComponent {
constructor() {
return {

View File

@@ -187,22 +187,6 @@ exports[`Settings Pane should render Default properly 1`] = `
"title": [Function],
"visible": [Function],
},
NewVertexPane {
"buildString": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"id": "newvertexpane",
"isExecuting": [Function],
"isTemplateReady": [Function],
"onMoreDetailsKeyPress": [Function],
"onSubmitCreateCallback": null,
"partitionKeyProperty": [Function],
"tempVertexData": [Function],
"title": [Function],
"visible": [Function],
},
CassandraAddCollectionPane {
"autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function],
@@ -547,22 +531,6 @@ exports[`Settings Pane should render Default properly 1`] = `
"isSynapseLinkUpdating": [Function],
"isTabsContentExpanded": [Function],
"memoryUsageInfo": [Function],
"newVertexPane": NewVertexPane {
"buildString": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"id": "newvertexpane",
"isExecuting": [Function],
"isTemplateReady": [Function],
"onMoreDetailsKeyPress": [Function],
"onSubmitCreateCallback": null,
"partitionKeyProperty": [Function],
"tempVertexData": [Function],
"title": [Function],
"visible": [Function],
},
"notebookBasePath": [Function],
"notebookServerInfo": [Function],
"onRefreshDatabasesKeyPress": [Function],
@@ -970,22 +938,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
"title": [Function],
"visible": [Function],
},
NewVertexPane {
"buildString": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"id": "newvertexpane",
"isExecuting": [Function],
"isTemplateReady": [Function],
"onMoreDetailsKeyPress": [Function],
"onSubmitCreateCallback": null,
"partitionKeyProperty": [Function],
"tempVertexData": [Function],
"title": [Function],
"visible": [Function],
},
CassandraAddCollectionPane {
"autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function],
@@ -1330,22 +1282,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
"isSynapseLinkUpdating": [Function],
"isTabsContentExpanded": [Function],
"memoryUsageInfo": [Function],
"newVertexPane": NewVertexPane {
"buildString": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"id": "newvertexpane",
"isExecuting": [Function],
"isTemplateReady": [Function],
"onMoreDetailsKeyPress": [Function],
"onSubmitCreateCallback": null,
"partitionKeyProperty": [Function],
"tempVertexData": [Function],
"title": [Function],
"visible": [Function],
},
"notebookBasePath": [Function],
"notebookServerInfo": [Function],
"onRefreshDatabasesKeyPress": [Function],

View File

@@ -187,22 +187,6 @@ exports[`Upload Items Pane should render Default properly 1`] = `
"title": [Function],
"visible": [Function],
},
NewVertexPane {
"buildString": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"id": "newvertexpane",
"isExecuting": [Function],
"isTemplateReady": [Function],
"onMoreDetailsKeyPress": [Function],
"onSubmitCreateCallback": null,
"partitionKeyProperty": [Function],
"tempVertexData": [Function],
"title": [Function],
"visible": [Function],
},
CassandraAddCollectionPane {
"autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function],
@@ -547,22 +531,6 @@ exports[`Upload Items Pane should render Default properly 1`] = `
"isSynapseLinkUpdating": [Function],
"isTabsContentExpanded": [Function],
"memoryUsageInfo": [Function],
"newVertexPane": NewVertexPane {
"buildString": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"id": "newvertexpane",
"isExecuting": [Function],
"isTemplateReady": [Function],
"onMoreDetailsKeyPress": [Function],
"onSubmitCreateCallback": null,
"partitionKeyProperty": [Function],
"tempVertexData": [Function],
"title": [Function],
"visible": [Function],
},
"notebookBasePath": [Function],
"notebookServerInfo": [Function],
"onRefreshDatabasesKeyPress": [Function],

View File

@@ -188,22 +188,6 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
"title": [Function],
"visible": [Function],
},
NewVertexPane {
"buildString": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"id": "newvertexpane",
"isExecuting": [Function],
"isTemplateReady": [Function],
"onMoreDetailsKeyPress": [Function],
"onSubmitCreateCallback": null,
"partitionKeyProperty": [Function],
"tempVertexData": [Function],
"title": [Function],
"visible": [Function],
},
CassandraAddCollectionPane {
"autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function],
@@ -551,22 +535,6 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
"isSynapseLinkUpdating": [Function],
"isTabsContentExpanded": [Function],
"memoryUsageInfo": [Function],
"newVertexPane": NewVertexPane {
"buildString": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"id": "newvertexpane",
"isExecuting": [Function],
"isTemplateReady": [Function],
"onMoreDetailsKeyPress": [Function],
"onSubmitCreateCallback": null,
"partitionKeyProperty": [Function],
"tempVertexData": [Function],
"title": [Function],
"visible": [Function],
},
"notebookBasePath": [Function],
"notebookServerInfo": [Function],
"onRefreshDatabasesKeyPress": [Function],