From ff58eb3724b633e072050d02af2daf5b96afe412 Mon Sep 17 00:00:00 2001 From: Hardikkumar Nai <80053762+hardiknai-techm@users.noreply.github.com> Date: Thu, 22 Apr 2021 00:39:19 +0530 Subject: [PATCH] Migrate Copy Notebook Pane to React (#640) Co-authored-by: Steve Faulkner --- src/Explorer/Controls/Dialog.tsx | 130 ++++++------ .../SettingsComponent.test.tsx.snap | 4 - src/Explorer/Explorer.tsx | 9 +- ...NotebookManager.ts => NotebookManager.tsx} | 63 +++--- src/Explorer/Panes/CopyNotebookPane.tsx | 197 ------------------ .../CopyNotebookPane/CopyNotebookPane.tsx | 156 ++++++++++++++ .../CopyNotebookPaneComponent.tsx | 89 ++++---- .../__snapshots__/SettingsPane.test.tsx.snap | 2 - .../UploadItemsPane.test.tsx.snap | 1 - ...eteDatabaseConfirmationPanel.test.tsx.snap | 1 - src/Main.tsx | 3 - 11 files changed, 305 insertions(+), 350 deletions(-) rename src/Explorer/Notebook/{NotebookManager.ts => NotebookManager.tsx} (92%) delete mode 100644 src/Explorer/Panes/CopyNotebookPane.tsx create mode 100644 src/Explorer/Panes/CopyNotebookPane/CopyNotebookPane.tsx rename src/Explorer/Panes/{ => CopyNotebookPane}/CopyNotebookPaneComponent.tsx (52%) diff --git a/src/Explorer/Controls/Dialog.tsx b/src/Explorer/Controls/Dialog.tsx index 3b69e45be..ffb19b31d 100644 --- a/src/Explorer/Controls/Dialog.tsx +++ b/src/Explorer/Controls/Dialog.tsx @@ -1,8 +1,3 @@ -import * as React from "react"; -import { Dialog as FluentDialog, DialogType, DialogFooter, IDialogProps } from "office-ui-fabric-react/lib/Dialog"; -import { IButtonProps, PrimaryButton, DefaultButton } from "office-ui-fabric-react/lib/Button"; -import { ITextFieldProps, TextField } from "office-ui-fabric-react/lib/TextField"; -import { Link } from "office-ui-fabric-react/lib/Link"; import { ChoiceGroup, FontIcon, @@ -10,6 +5,11 @@ import { IProgressIndicatorProps, ProgressIndicator, } from "office-ui-fabric-react"; +import { DefaultButton, IButtonProps, PrimaryButton } from "office-ui-fabric-react/lib/Button"; +import { Dialog as FluentDialog, DialogFooter, DialogType, IDialogProps } from "office-ui-fabric-react/lib/Dialog"; +import { Link } from "office-ui-fabric-react/lib/Link"; +import { ITextFieldProps, TextField } from "office-ui-fabric-react/lib/TextField"; +import React, { FunctionComponent } from "react"; export interface TextFieldProps extends ITextFieldProps { label: string; @@ -50,61 +50,69 @@ const DIALOG_TITLE_FONT_SIZE = "17px"; const DIALOG_TITLE_FONT_WEIGHT = 400; const DIALOG_SUBTEXT_FONT_SIZE = "15px"; -export class Dialog extends React.Component { - constructor(props: DialogProps) { - super(props); - } - - public render(): JSX.Element { - const dialogProps: IDialogProps = { - hidden: !this.props.visible, - dialogContentProps: { - type: this.props.type || DialogType.normal, - title: this.props.title, - subText: this.props.subText, - styles: { - title: { fontSize: DIALOG_TITLE_FONT_SIZE, fontWeight: DIALOG_TITLE_FONT_WEIGHT }, - subText: { fontSize: DIALOG_SUBTEXT_FONT_SIZE }, - }, - showCloseButton: this.props.showCloseButton || false, - onDismiss: this.props.onDismiss, +export const Dialog: FunctionComponent = ({ + title, + subText, + isModal, + visible, + choiceGroupProps, + textFieldProps, + linkProps, + progressIndicatorProps, + primaryButtonText, + secondaryButtonText, + onPrimaryButtonClick, + onSecondaryButtonClick, + primaryButtonDisabled, + type, + showCloseButton, + onDismiss, +}: DialogProps) => { + const dialogProps: IDialogProps = { + hidden: !visible, + dialogContentProps: { + type: type || DialogType.normal, + title, + subText, + styles: { + title: { fontSize: DIALOG_TITLE_FONT_SIZE, fontWeight: DIALOG_TITLE_FONT_WEIGHT }, + subText: { fontSize: DIALOG_SUBTEXT_FONT_SIZE }, }, - modalProps: { isBlocking: this.props.isModal, isDarkOverlay: false }, - minWidth: DIALOG_MIN_WIDTH, - maxWidth: DIALOG_MAX_WIDTH, - }; - const choiceGroupProps: IChoiceGroupProps = this.props.choiceGroupProps; - const textFieldProps: ITextFieldProps = this.props.textFieldProps; - const linkProps: LinkProps = this.props.linkProps; - const progressIndicatorProps: IProgressIndicatorProps = this.props.progressIndicatorProps; - const primaryButtonProps: IButtonProps = { - text: this.props.primaryButtonText, - disabled: this.props.primaryButtonDisabled || false, - onClick: this.props.onPrimaryButtonClick, - }; - const secondaryButtonProps: IButtonProps = - this.props.secondaryButtonText && this.props.onSecondaryButtonClick - ? { - text: this.props.secondaryButtonText, - onClick: this.props.onSecondaryButtonClick, - } - : undefined; + showCloseButton: showCloseButton || false, + onDismiss, + }, + modalProps: { isBlocking: isModal, isDarkOverlay: false }, + minWidth: DIALOG_MIN_WIDTH, + maxWidth: DIALOG_MAX_WIDTH, + }; - return ( - - {choiceGroupProps && } - {textFieldProps && } - {linkProps && ( - - {linkProps.linkText} - - )} - {progressIndicatorProps && } - - - {secondaryButtonProps && } - - - ); - } -} + const primaryButtonProps: IButtonProps = { + text: primaryButtonText, + disabled: primaryButtonDisabled || false, + onClick: onPrimaryButtonClick, + }; + const secondaryButtonProps: IButtonProps = + secondaryButtonText && onSecondaryButtonClick + ? { + text: secondaryButtonText, + onClick: onSecondaryButtonClick, + } + : undefined; + + return ( + + {choiceGroupProps && } + {textFieldProps && } + {linkProps && ( + + {linkProps.linkText} + + )} + {progressIndicatorProps && } + + + {secondaryButtonProps && } + + + ); +}; diff --git a/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap b/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap index c9c5fab96..893360470 100644 --- a/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap +++ b/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap @@ -552,7 +552,6 @@ exports[`SettingsComponent renders 1`] = ` "hasStorageAnalyticsAfecFeature": [Function], "isAccountReady": [Function], "isAutoscaleDefaultEnabled": [Function], - "isCopyNotebookPaneEnabled": [Function], "isEnableMongoCapabilityPresent": [Function], "isFixedCollectionWithSharedThroughputSupported": [Function], "isGitHubPaneEnabled": [Function], @@ -1214,7 +1213,6 @@ exports[`SettingsComponent renders 1`] = ` "hasStorageAnalyticsAfecFeature": [Function], "isAccountReady": [Function], "isAutoscaleDefaultEnabled": [Function], - "isCopyNotebookPaneEnabled": [Function], "isEnableMongoCapabilityPresent": [Function], "isFixedCollectionWithSharedThroughputSupported": [Function], "isGitHubPaneEnabled": [Function], @@ -1889,7 +1887,6 @@ exports[`SettingsComponent renders 1`] = ` "hasStorageAnalyticsAfecFeature": [Function], "isAccountReady": [Function], "isAutoscaleDefaultEnabled": [Function], - "isCopyNotebookPaneEnabled": [Function], "isEnableMongoCapabilityPresent": [Function], "isFixedCollectionWithSharedThroughputSupported": [Function], "isGitHubPaneEnabled": [Function], @@ -2551,7 +2548,6 @@ exports[`SettingsComponent renders 1`] = ` "hasStorageAnalyticsAfecFeature": [Function], "isAccountReady": [Function], "isAutoscaleDefaultEnabled": [Function], - "isCopyNotebookPaneEnabled": [Function], "isEnableMongoCapabilityPresent": [Function], "isFixedCollectionWithSharedThroughputSupported": [Function], "isGitHubPaneEnabled": [Function], diff --git a/src/Explorer/Explorer.tsx b/src/Explorer/Explorer.tsx index e79d99c99..42e3f2fbf 100644 --- a/src/Explorer/Explorer.tsx +++ b/src/Explorer/Explorer.tsx @@ -186,12 +186,10 @@ export default class Explorer { public stringInputPane: StringInputPane; public gitHubReposPane: ContextualPaneBase; public publishNotebookPaneAdapter: ReactAdapter; - public copyNotebookPaneAdapter: ReactAdapter; // features public isGitHubPaneEnabled: ko.Observable; public isPublishNotebookPaneEnabled: ko.Observable; - public isCopyNotebookPaneEnabled: ko.Observable; public isHostedDataExplorerEnabled: ko.Computed; public isRightPanelV2Enabled: ko.Computed; public isMongoIndexingEnabled: ko.Observable; @@ -341,7 +339,6 @@ export default class Explorer { this.isGitHubPaneEnabled = ko.observable(false); this.isMongoIndexingEnabled = ko.observable(false); this.isPublishNotebookPaneEnabled = ko.observable(false); - this.isCopyNotebookPaneEnabled = ko.observable(false); this.canExceedMaximumValue = ko.computed(() => userContext.features.canExceedMaximumValue); @@ -1457,11 +1454,7 @@ export default class Explorer { } public copyNotebook(name: string, content: string): void { - if (this.notebookManager) { - this.notebookManager.openCopyNotebookPane(name, content); - this.copyNotebookPaneAdapter = this.notebookManager.copyNotebookPaneAdapter; - this.isCopyNotebookPaneEnabled(true); - } + this.notebookManager?.openCopyNotebookPane(name, content); } public showOkModalDialog(title: string, msg: string): void { diff --git a/src/Explorer/Notebook/NotebookManager.ts b/src/Explorer/Notebook/NotebookManager.tsx similarity index 92% rename from src/Explorer/Notebook/NotebookManager.ts rename to src/Explorer/Notebook/NotebookManager.tsx index b59c3377b..42f41baf5 100644 --- a/src/Explorer/Notebook/NotebookManager.ts +++ b/src/Explorer/Notebook/NotebookManager.tsx @@ -2,30 +2,31 @@ * Contains all notebook related stuff meant to be dynamically loaded by explorer */ -import { JunoClient } from "../../Juno/JunoClient"; -import { GitHubOAuthService } from "../../GitHub/GitHubOAuthService"; -import { GitHubClient } from "../../GitHub/GitHubClient"; -import * as Logger from "../../Common/Logger"; -import { HttpStatusCodes, Areas } from "../../Common/Constants"; -import { GitHubReposPane } from "../Panes/GitHubReposPane"; -import ko from "knockout"; -import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; -import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; -import { IContentProvider } from "@nteract/core"; -import { NotebookContentProvider } from "./NotebookComponent/NotebookContentProvider"; -import { GitHubContentProvider } from "../../GitHub/GitHubContentProvider"; -import { contents } from "rx-jupyter"; -import { NotebookContainerClient } from "./NotebookContainerClient"; -import { MemoryUsageInfo } from "../../Contracts/DataModels"; -import { NotebookContentClient } from "./NotebookContentClient"; -import { ResourceTreeAdapter } from "../Tree/ResourceTreeAdapter"; -import { PublishNotebookPaneAdapter } from "../Panes/PublishNotebookPaneAdapter"; -import { getFullName } from "../../Utils/UserUtils"; import { ImmutableNotebook } from "@nteract/commutable"; +import { IContentProvider } from "@nteract/core"; +import ko from "knockout"; +import React from "react"; +import { contents } from "rx-jupyter"; +import { Areas, HttpStatusCodes } from "../../Common/Constants"; +import { getErrorMessage } from "../../Common/ErrorHandlingUtils"; +import * as Logger from "../../Common/Logger"; +import { MemoryUsageInfo } from "../../Contracts/DataModels"; +import { GitHubClient } from "../../GitHub/GitHubClient"; +import { GitHubContentProvider } from "../../GitHub/GitHubContentProvider"; +import { GitHubOAuthService } from "../../GitHub/GitHubOAuthService"; +import { JunoClient } from "../../Juno/JunoClient"; +import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; +import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; +import { getFullName } from "../../Utils/UserUtils"; import Explorer from "../Explorer"; import { ContextualPaneBase } from "../Panes/ContextualPaneBase"; -import { CopyNotebookPaneAdapter } from "../Panes/CopyNotebookPane"; -import { getErrorMessage } from "../../Common/ErrorHandlingUtils"; +import { CopyNotebookPane } from "../Panes/CopyNotebookPane/CopyNotebookPane"; +import { GitHubReposPane } from "../Panes/GitHubReposPane"; +import { PublishNotebookPaneAdapter } from "../Panes/PublishNotebookPaneAdapter"; +import { ResourceTreeAdapter } from "../Tree/ResourceTreeAdapter"; +import { NotebookContentProvider } from "./NotebookComponent/NotebookContentProvider"; +import { NotebookContainerClient } from "./NotebookContainerClient"; +import { NotebookContentClient } from "./NotebookContentClient"; export interface NotebookManagerOptions { container: Explorer; @@ -49,7 +50,6 @@ export default class NotebookManager { public gitHubReposPane: ContextualPaneBase; public publishNotebookPaneAdapter: PublishNotebookPaneAdapter; - public copyNotebookPaneAdapter: CopyNotebookPaneAdapter; public initialize(params: NotebookManagerOptions): void { this.params = params; @@ -89,12 +89,6 @@ export default class NotebookManager { this.publishNotebookPaneAdapter = new PublishNotebookPaneAdapter(this.params.container, this.junoClient); - this.copyNotebookPaneAdapter = new CopyNotebookPaneAdapter( - this.params.container, - this.junoClient, - this.gitHubOAuthService - ); - this.gitHubOAuthService.getTokenObservable().subscribe((token) => { this.gitHubClient.setToken(token?.access_token); @@ -129,7 +123,18 @@ export default class NotebookManager { } public openCopyNotebookPane(name: string, content: string): void { - this.copyNotebookPaneAdapter.open(name, content); + const { container } = this.params; + container.openSidePanel( + "Copy Notebook", + + ); } // Octokit's error handler uses any diff --git a/src/Explorer/Panes/CopyNotebookPane.tsx b/src/Explorer/Panes/CopyNotebookPane.tsx deleted file mode 100644 index a5b429aa7..000000000 --- a/src/Explorer/Panes/CopyNotebookPane.tsx +++ /dev/null @@ -1,197 +0,0 @@ -import ko from "knockout"; -import { IDropdownOption } from "office-ui-fabric-react"; -import * as React from "react"; -import { ReactAdapter } from "../../Bindings/ReactBindingHandler"; -import { HttpStatusCodes } from "../../Common/Constants"; -import { getErrorMessage, handleError } from "../../Common/ErrorHandlingUtils"; -import { GitHubOAuthService } from "../../GitHub/GitHubOAuthService"; -import { IPinnedRepo, JunoClient } from "../../Juno/JunoClient"; -import * as GitHubUtils from "../../Utils/GitHubUtils"; -import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils"; -import Explorer from "../Explorer"; -import { NotebookContentItem, NotebookContentItemType } from "../Notebook/NotebookContentItem"; -import { ResourceTreeAdapter } from "../Tree/ResourceTreeAdapter"; -import { CopyNotebookPaneComponent, CopyNotebookPaneProps } from "./CopyNotebookPaneComponent"; -import { - GenericRightPaneComponent, - GenericRightPaneProps, -} from "./GenericRightPaneComponent/GenericRightPaneComponent"; - -interface Location { - type: "MyNotebooks" | "GitHub"; - - // GitHub - owner?: string; - repo?: string; - branch?: string; -} - -export class CopyNotebookPaneAdapter implements ReactAdapter { - private static readonly BranchNameWhiteSpace = " "; - - parameters: ko.Observable; - private isOpened: boolean; - private isExecuting: boolean; - private formError: string; - private formErrorDetail: string; - private name: string; - private content: string; - private pinnedRepos: IPinnedRepo[]; - private selectedLocation: Location; - - constructor( - private container: Explorer, - private junoClient: JunoClient, - private gitHubOAuthService: GitHubOAuthService - ) { - this.parameters = ko.observable(Date.now()); - this.reset(); - this.triggerRender(); - } - - public renderComponent(): JSX.Element { - if (!this.isOpened) { - return undefined; - } - - const genericPaneProps: GenericRightPaneProps = { - container: this.container, - formError: this.formError, - formErrorDetail: this.formErrorDetail, - id: "copynotebookpane", - isExecuting: this.isExecuting, - title: "Copy notebook", - submitButtonText: "OK", - onClose: () => this.close(), - onSubmit: () => this.submit(), - }; - - const copyNotebookPaneProps: CopyNotebookPaneProps = { - name: this.name, - pinnedRepos: this.pinnedRepos, - onDropDownChange: this.onDropDownChange, - }; - - return ( - - - - ); - } - - public triggerRender(): void { - window.requestAnimationFrame(() => this.parameters(Date.now())); - } - - public async open(name: string, content: string): Promise { - this.name = name; - this.content = content; - - this.isOpened = true; - this.triggerRender(); - - if (this.gitHubOAuthService.isLoggedIn()) { - const response = await this.junoClient.getPinnedRepos(this.gitHubOAuthService.getTokenObservable()()?.scope); - if (response.status !== HttpStatusCodes.OK && response.status !== HttpStatusCodes.NoContent) { - handleError(`Received HTTP ${response.status} when fetching pinned repos`, "CopyNotebookPaneAdapter/submit"); - } - - if (response.data?.length > 0) { - this.pinnedRepos = response.data; - this.triggerRender(); - } - } - } - - public close(): void { - this.reset(); - this.triggerRender(); - } - - public async submit(): Promise { - let destination: string = this.selectedLocation?.type; - let clearMessage: () => void; - this.isExecuting = true; - this.triggerRender(); - - try { - if (!this.selectedLocation) { - throw new Error(`No location selected`); - } - - if (this.selectedLocation.type === "GitHub") { - destination = `${destination} - ${GitHubUtils.toRepoFullName( - this.selectedLocation.owner, - this.selectedLocation.repo - )} - ${this.selectedLocation.branch}`; - } - - clearMessage = NotificationConsoleUtils.logConsoleProgress(`Copying ${this.name} to ${destination}`); - - const notebookContentItem = await this.copyNotebook(this.selectedLocation); - if (!notebookContentItem) { - throw new Error(`Failed to upload ${this.name}`); - } - - NotificationConsoleUtils.logConsoleInfo(`Successfully copied ${this.name} to ${destination}`); - } catch (error) { - const errorMessage = getErrorMessage(error); - this.formError = `Failed to copy ${this.name} to ${destination}`; - this.formErrorDetail = `${errorMessage}`; - handleError(errorMessage, "CopyNotebookPaneAdapter/submit", this.formError); - return; - } finally { - clearMessage && clearMessage(); - this.isExecuting = false; - this.triggerRender(); - } - - this.close(); - } - - private copyNotebook = async (location: Location): Promise => { - let parent: NotebookContentItem; - switch (location.type) { - case "MyNotebooks": - parent = { - name: ResourceTreeAdapter.MyNotebooksTitle, - path: this.container.getNotebookBasePath(), - type: NotebookContentItemType.Directory, - }; - break; - - case "GitHub": - parent = { - name: ResourceTreeAdapter.GitHubReposTitle, - path: GitHubUtils.toContentUri( - this.selectedLocation.owner, - this.selectedLocation.repo, - this.selectedLocation.branch, - "" - ), - type: NotebookContentItemType.Directory, - }; - break; - - default: - throw new Error(`Unsupported location type ${location.type}`); - } - - return this.container.uploadFile(this.name, this.content, parent); - }; - - private onDropDownChange = (_: React.FormEvent, option?: IDropdownOption): void => { - this.selectedLocation = option?.data; - }; - - private reset = (): void => { - this.isOpened = false; - this.isExecuting = false; - this.formError = undefined; - this.formErrorDetail = undefined; - this.name = undefined; - this.content = undefined; - this.pinnedRepos = undefined; - this.selectedLocation = undefined; - }; -} diff --git a/src/Explorer/Panes/CopyNotebookPane/CopyNotebookPane.tsx b/src/Explorer/Panes/CopyNotebookPane/CopyNotebookPane.tsx new file mode 100644 index 000000000..3f4c281aa --- /dev/null +++ b/src/Explorer/Panes/CopyNotebookPane/CopyNotebookPane.tsx @@ -0,0 +1,156 @@ +import { IDropdownOption } from "office-ui-fabric-react"; +import React, { FormEvent, FunctionComponent, useEffect, useState } from "react"; +import { HttpStatusCodes } from "../../../Common/Constants"; +import { getErrorMessage, handleError } from "../../../Common/ErrorHandlingUtils"; +import { GitHubOAuthService } from "../../../GitHub/GitHubOAuthService"; +import { IPinnedRepo, JunoClient } from "../../../Juno/JunoClient"; +import * as GitHubUtils from "../../../Utils/GitHubUtils"; +import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils"; +import Explorer from "../../Explorer"; +import { NotebookContentItem, NotebookContentItemType } from "../../Notebook/NotebookContentItem"; +import { ResourceTreeAdapter } from "../../Tree/ResourceTreeAdapter"; +import { + GenericRightPaneComponent, + GenericRightPaneProps, +} from "../GenericRightPaneComponent/GenericRightPaneComponent"; +import { CopyNotebookPaneComponent, CopyNotebookPaneProps } from "./CopyNotebookPaneComponent"; + +interface Location { + type: "MyNotebooks" | "GitHub"; + + // GitHub + owner?: string; + repo?: string; + branch?: string; +} +export interface CopyNotebookPanelProps { + name: string; + content: string; + container: Explorer; + junoClient: JunoClient; + gitHubOAuthService: GitHubOAuthService; + closePanel: () => void; +} + +export const CopyNotebookPane: FunctionComponent = ({ + name, + content, + container, + junoClient, + gitHubOAuthService, + closePanel, +}: CopyNotebookPanelProps) => { + const [isExecuting, setIsExecuting] = useState(); + const [formError, setFormError] = useState(""); + const [formErrorDetail, setFormErrorDetail] = useState(""); + const [pinnedRepos, setPinnedRepos] = useState(); + const [selectedLocation, setSelectedLocation] = useState(); + + useEffect(() => { + open(); + }, []); + + const open = async (): Promise => { + if (gitHubOAuthService.isLoggedIn()) { + const response = await junoClient.getPinnedRepos(gitHubOAuthService.getTokenObservable()()?.scope); + if (response.status !== HttpStatusCodes.OK && response.status !== HttpStatusCodes.NoContent) { + handleError(`Received HTTP ${response.status} when fetching pinned repos`, "CopyNotebookPaneAdapter/submit"); + } + + if (response.data?.length > 0) { + setPinnedRepos(response.data); + } + } + }; + + const submit = async (): Promise => { + let destination: string = selectedLocation?.type; + let clearMessage: () => void; + setIsExecuting(true); + + try { + if (!selectedLocation) { + throw new Error(`No location selected`); + } + + if (selectedLocation.type === "GitHub") { + destination = `${destination} - ${GitHubUtils.toRepoFullName( + selectedLocation.owner, + selectedLocation.repo + )} - ${selectedLocation.branch}`; + } + + clearMessage = NotificationConsoleUtils.logConsoleProgress(`Copying ${name} to ${destination}`); + + const notebookContentItem = await copyNotebook(selectedLocation); + if (!notebookContentItem) { + throw new Error(`Failed to upload ${name}`); + } + + NotificationConsoleUtils.logConsoleInfo(`Successfully copied ${name} to ${destination}`); + closePanel(); + } catch (error) { + const errorMessage = getErrorMessage(error); + setFormError(`Failed to copy ${name} to ${destination}`); + setFormErrorDetail(`${errorMessage}`); + handleError(errorMessage, "CopyNotebookPaneAdapter/submit", formError); + } finally { + clearMessage && clearMessage(); + setIsExecuting(false); + } + }; + + const copyNotebook = async (location: Location): Promise => { + let parent: NotebookContentItem; + switch (location.type) { + case "MyNotebooks": + parent = { + name: ResourceTreeAdapter.MyNotebooksTitle, + path: container.getNotebookBasePath(), + type: NotebookContentItemType.Directory, + }; + break; + + case "GitHub": + parent = { + name: ResourceTreeAdapter.GitHubReposTitle, + path: GitHubUtils.toContentUri(selectedLocation.owner, selectedLocation.repo, selectedLocation.branch, ""), + type: NotebookContentItemType.Directory, + }; + break; + + default: + throw new Error(`Unsupported location type ${location.type}`); + } + + return container.uploadFile(name, content, parent); + }; + + const onDropDownChange = (_: FormEvent, option?: IDropdownOption): void => { + setSelectedLocation(option?.data); + }; + + const genericPaneProps: GenericRightPaneProps = { + container, + formError, + formErrorDetail, + id: "copynotebookpane", + isExecuting: isExecuting, + title: "Copy notebook", + submitButtonText: "OK", + onClose: closePanel, + onSubmit: () => submit(), + }; + + const copyNotebookPaneProps: CopyNotebookPaneProps = { + name, + pinnedRepos, + onDropDownChange: onDropDownChange, + }; + + return ( + + + + ); +}; diff --git a/src/Explorer/Panes/CopyNotebookPaneComponent.tsx b/src/Explorer/Panes/CopyNotebookPane/CopyNotebookPaneComponent.tsx similarity index 52% rename from src/Explorer/Panes/CopyNotebookPaneComponent.tsx rename to src/Explorer/Panes/CopyNotebookPane/CopyNotebookPaneComponent.tsx index 7ae30ccfe..915c0bd30 100644 --- a/src/Explorer/Panes/CopyNotebookPaneComponent.tsx +++ b/src/Explorer/Panes/CopyNotebookPane/CopyNotebookPaneComponent.tsx @@ -1,18 +1,18 @@ -import * as GitHubUtils from "../../Utils/GitHubUtils"; -import * as React from "react"; -import { IPinnedRepo } from "../../Juno/JunoClient"; -import { ResourceTreeAdapter } from "../Tree/ResourceTreeAdapter"; import { - Stack, - Label, - Text, Dropdown, - IDropdownProps, IDropdownOption, - SelectableOptionMenuItemType, + IDropdownProps, IRenderFunction, ISelectableOption, + Label, + SelectableOptionMenuItemType, + Stack, + Text, } from "office-ui-fabric-react"; +import React, { FormEvent, FunctionComponent } from "react"; +import { IPinnedRepo } from "../../../Juno/JunoClient"; +import * as GitHubUtils from "../../../Utils/GitHubUtils"; +import { ResourceTreeAdapter } from "../../Tree/ResourceTreeAdapter"; interface Location { type: "MyNotebooks" | "GitHub"; @@ -26,46 +26,25 @@ interface Location { export interface CopyNotebookPaneProps { name: string; pinnedRepos: IPinnedRepo[]; - onDropDownChange: (_: React.FormEvent, option?: IDropdownOption) => void; + onDropDownChange: (_: FormEvent, option?: IDropdownOption) => void; } -export class CopyNotebookPaneComponent extends React.Component { - private static readonly BranchNameWhiteSpace = " "; +export const CopyNotebookPaneComponent: FunctionComponent = ({ + name, + pinnedRepos, + onDropDownChange, +}: CopyNotebookPaneProps) => { + const BranchNameWhiteSpace = " "; - public render(): JSX.Element { - const dropDownProps: IDropdownProps = { - label: "Location", - ariaLabel: "Location", - placeholder: "Select an option", - onRenderTitle: this.onRenderDropDownTitle, - onRenderOption: this.onRenderDropDownOption, - options: this.getDropDownOptions(), - onChange: this.props.onDropDownChange, - }; - - return ( -
- - - - {this.props.name} - - - - -
- ); - } - - private onRenderDropDownTitle: IRenderFunction = (options: IDropdownOption[]): JSX.Element => { + const onRenderDropDownTitle: IRenderFunction = (options: IDropdownOption[]): JSX.Element => { return {options.length && options[0].title}; }; - private onRenderDropDownOption: IRenderFunction = (option: ISelectableOption): JSX.Element => { + const onRenderDropDownOption: IRenderFunction = (option: ISelectableOption): JSX.Element => { return {option.text}; }; - private getDropDownOptions = (): IDropdownOption[] => { + const getDropDownOptions = (): IDropdownOption[] => { const options: IDropdownOption[] = []; options.push({ @@ -77,7 +56,7 @@ export class CopyNotebookPaneComponent extends React.Component 0) { + if (pinnedRepos && pinnedRepos.length > 0) { options.push({ key: "GitHub-Header-Divider", text: undefined, @@ -90,7 +69,7 @@ export class CopyNotebookPaneComponent extends React.Component { + pinnedRepos.forEach((pinnedRepo) => { const repoFullName = GitHubUtils.toRepoFullName(pinnedRepo.owner, pinnedRepo.name); options.push({ key: `GitHub-Repo-${repoFullName}`, @@ -101,7 +80,7 @@ export class CopyNotebookPaneComponent extends React.Component options.push({ key: `GitHub-Repo-${repoFullName}-${branch.name}`, - text: `${CopyNotebookPaneComponent.BranchNameWhiteSpace}${branch.name}`, + text: `${BranchNameWhiteSpace}${branch.name}`, title: `${repoFullName} - ${branch.name}`, data: { type: "GitHub", @@ -116,4 +95,26 @@ export class CopyNotebookPaneComponent extends React.Component + + + + {name} + + + + + + ); +}; diff --git a/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap b/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap index 4df3f4a52..456e08b1c 100644 --- a/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap +++ b/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap @@ -528,7 +528,6 @@ exports[`Settings Pane should render Default properly 1`] = ` "hasStorageAnalyticsAfecFeature": [Function], "isAccountReady": [Function], "isAutoscaleDefaultEnabled": [Function], - "isCopyNotebookPaneEnabled": [Function], "isEnableMongoCapabilityPresent": [Function], "isFixedCollectionWithSharedThroughputSupported": [Function], "isGitHubPaneEnabled": [Function], @@ -1313,7 +1312,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = ` "hasStorageAnalyticsAfecFeature": [Function], "isAccountReady": [Function], "isAutoscaleDefaultEnabled": [Function], - "isCopyNotebookPaneEnabled": [Function], "isEnableMongoCapabilityPresent": [Function], "isFixedCollectionWithSharedThroughputSupported": [Function], "isGitHubPaneEnabled": [Function], diff --git a/src/Explorer/Panes/UploadItemsPane/__snapshots__/UploadItemsPane.test.tsx.snap b/src/Explorer/Panes/UploadItemsPane/__snapshots__/UploadItemsPane.test.tsx.snap index 9619eadf7..03397b4a8 100644 --- a/src/Explorer/Panes/UploadItemsPane/__snapshots__/UploadItemsPane.test.tsx.snap +++ b/src/Explorer/Panes/UploadItemsPane/__snapshots__/UploadItemsPane.test.tsx.snap @@ -528,7 +528,6 @@ exports[`Upload Items Pane should render Default properly 1`] = ` "hasStorageAnalyticsAfecFeature": [Function], "isAccountReady": [Function], "isAutoscaleDefaultEnabled": [Function], - "isCopyNotebookPaneEnabled": [Function], "isEnableMongoCapabilityPresent": [Function], "isFixedCollectionWithSharedThroughputSupported": [Function], "isGitHubPaneEnabled": [Function], diff --git a/src/Explorer/Panes/__snapshots__/DeleteDatabaseConfirmationPanel.test.tsx.snap b/src/Explorer/Panes/__snapshots__/DeleteDatabaseConfirmationPanel.test.tsx.snap index 572882ac5..ca0674169 100644 --- a/src/Explorer/Panes/__snapshots__/DeleteDatabaseConfirmationPanel.test.tsx.snap +++ b/src/Explorer/Panes/__snapshots__/DeleteDatabaseConfirmationPanel.test.tsx.snap @@ -529,7 +529,6 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database "hasStorageAnalyticsAfecFeature": [Function], "isAccountReady": [Function], "isAutoscaleDefaultEnabled": [Function], - "isCopyNotebookPaneEnabled": [Function], "isEnableMongoCapabilityPresent": [Function], "isFixedCollectionWithSharedThroughputSupported": [Function], "isGitHubPaneEnabled": [Function], diff --git a/src/Main.tsx b/src/Main.tsx index 48b223c82..5875afc57 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -243,9 +243,6 @@ const App: React.FunctionComponent = () => {
- -
- {showDialog && }
);