From 481ff9e7feae4b135d22bc113550f1b5c0ad8d07 Mon Sep 17 00:00:00 2001 From: Steve Faulkner Date: Thu, 27 May 2021 16:07:07 -0500 Subject: [PATCH] Migrate SidePanel state to Zustand (#799) Co-authored-by: hardiknai-techm --- .eslintignore | 3 - .../SettingsComponent.test.tsx.snap | 12 - src/Explorer/Explorer.tsx | 215 ++++++------------ .../CommandBarComponentButtonFactory.tsx | 8 +- .../NotificationConsoleComponent.tsx | 21 ++ src/Explorer/Notebook/NotebookManager.tsx | 60 +++-- src/Explorer/Panes/AddCollectionPanel.tsx | 7 +- .../AddDatabasePanel/AddDatabasePanel.tsx | 10 +- .../AddDatabasePanel.test.tsx.snap | 1 - .../BrowseQueriesPane/BrowseQueriesPane.tsx | 6 +- .../CassandraAddCollectionPane.tsx | 7 +- .../CassandraAddCollectionPane.test.tsx.snap | 1 - src/Explorer/Panes/ContextualPaneBase.ts | 126 ---------- .../CopyNotebookPane/CopyNotebookPane.tsx | 7 +- .../DeleteCollectionConfirmationPane.tsx | 7 +- ...teCollectionConfirmationPane.test.tsx.snap | 1 - .../Panes/DeleteDatabaseConfirmationPanel.tsx | 9 +- .../ExecuteSprocParamsPane.tsx | 9 +- .../ExecuteSprocParamsPane.test.tsx.snap | 1 - .../GitHubReposPanel/GitHubReposPanel.tsx | 6 +- .../GitHubReposPanel.test.tsx.snap | 3 - .../GraphStylingPanel/GraphStylingPanel.tsx | 7 +- .../Panes/LoadQueryPane/LoadQueryPane.tsx | 11 +- .../__snapshots__/LoadQueryPane.test.tsx.snap | 1 - .../NewVertexPanel/NewVertexPanel.test.tsx | 18 +- .../Panes/NewVertexPanel/NewVertexPanel.tsx | 11 +- .../NewVertexPanel.test.tsx.snap | 1 - .../Panes/PanelContainerComponent.test.tsx | 3 - .../Panes/PanelContainerComponent.tsx | 26 ++- .../Panes/PanelInfoErrorComponent.tsx | 7 +- .../PublishNotebookPane.tsx | 9 +- .../Panes/RightPaneForm/RightPaneForm.tsx | 11 +- .../Panes/SaveQueryPane/SaveQueryPane.tsx | 11 +- .../__snapshots__/SaveQueryPane.test.tsx.snap | 1 - .../Panes/SettingsPane/SettingsPane.test.tsx | 9 +- .../Panes/SettingsPane/SettingsPane.tsx | 15 +- .../__snapshots__/SettingsPane.test.tsx.snap | 2 - .../SetupNotebooksPanel.tsx | 23 +- .../Panes/StringInputPane/StringInputPane.tsx | 1 - .../StringInputPane.test.tsx.snap | 4 - .../Panes/Tables/AddTableEntityPanel.test.tsx | 7 +- .../Panes/Tables/AddTableEntityPanel.tsx | 20 +- .../Tables/EditTableEntityPanel.test.tsx | 7 +- .../Panes/Tables/EditTableEntityPanel.tsx | 19 +- .../TableQuerySelectPanel.tsx | 11 +- .../TableQuerySelectPanel.test.tsx.snap | 1 - .../AddTableEntityPanel.test.tsx.snap | 4 +- .../EditTableEntityPanel.test.tsx.snap | 4 +- .../Panes/UploadFilePane/UploadFilePane.tsx | 13 +- .../UploadItemsPane/UploadItemsPane.test.tsx | 1 - .../Panes/UploadItemsPane/UploadItemsPane.tsx | 2 - .../UploadItemsPane.test.tsx.snap | 1 - ...eteDatabaseConfirmationPanel.test.tsx.snap | 4 - src/Explorer/Tabs/GraphTab.tsx | 14 +- src/Explorer/Tabs/QueryTab.ts | 3 +- src/Explorer/Tabs/StoredProcedureTab.ts | 3 +- src/Explorer/Tabs/TabsBase.ts | 5 +- src/Main.tsx | 22 +- src/hooks/useNotificationConsole.ts | 14 ++ src/hooks/useSidePanel.ts | 40 +--- 60 files changed, 288 insertions(+), 598 deletions(-) delete mode 100644 src/Explorer/Panes/ContextualPaneBase.ts create mode 100644 src/hooks/useNotificationConsole.ts diff --git a/.eslintignore b/.eslintignore index f723666ed..02b4d7eb7 100644 --- a/.eslintignore +++ b/.eslintignore @@ -111,9 +111,6 @@ src/Explorer/OpenActionsStubs.ts src/Explorer/Panes/AddDatabasePane.ts src/Explorer/Panes/AddDatabasePane.test.ts src/Explorer/Panes/BrowseQueriesPane.ts -src/Explorer/Panes/ContextualPaneBase.ts -# src/Explorer/Panes/GraphStylingPane.ts -# src/Explorer/Panes/NewVertexPane.ts src/Explorer/Panes/RenewAdHocAccessPane.ts src/Explorer/Panes/SetupNotebooksPane.ts src/Explorer/Panes/SwitchDirectoryPane.ts diff --git a/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap b/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap index 81ba20a46..7020c7bf1 100644 --- a/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap +++ b/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap @@ -31,7 +31,6 @@ exports[`SettingsComponent renders 1`] = ` "_isInitializingNotebooks": false, "_resetNotebookWorkspace": [Function], "canSaveQueries": [Function], - "closeSidePanel": undefined, "collapsedResourceTreeWidth": 36, "commandBarComponentAdapter": CommandBarComponentAdapter { "container": [Circular], @@ -56,7 +55,6 @@ exports[`SettingsComponent renders 1`] = ` "notebookServerInfo": [Function], "onRefreshDatabasesKeyPress": [Function], "onRefreshResourcesClick": [Function], - "openSidePanel": undefined, "provideFeedbackEmail": [Function], "queriesClient": QueriesClient { "container": [Circular], @@ -81,7 +79,6 @@ exports[`SettingsComponent renders 1`] = ` "selectedDatabaseId": [Function], "selectedNode": [Function], "setInProgressConsoleDataIdToBeDeleted": undefined, - "setIsNotificationConsoleExpanded": undefined, "setNotificationConsoleData": undefined, "sparkClusterConnectionInfo": [Function], "splitter": Splitter { @@ -124,7 +121,6 @@ exports[`SettingsComponent renders 1`] = ` "_isInitializingNotebooks": false, "_resetNotebookWorkspace": [Function], "canSaveQueries": [Function], - "closeSidePanel": undefined, "collapsedResourceTreeWidth": 36, "commandBarComponentAdapter": CommandBarComponentAdapter { "container": [Circular], @@ -149,7 +145,6 @@ exports[`SettingsComponent renders 1`] = ` "notebookServerInfo": [Function], "onRefreshDatabasesKeyPress": [Function], "onRefreshResourcesClick": [Function], - "openSidePanel": undefined, "provideFeedbackEmail": [Function], "queriesClient": QueriesClient { "container": [Circular], @@ -174,7 +169,6 @@ exports[`SettingsComponent renders 1`] = ` "selectedDatabaseId": [Function], "selectedNode": [Function], "setInProgressConsoleDataIdToBeDeleted": undefined, - "setIsNotificationConsoleExpanded": undefined, "setNotificationConsoleData": undefined, "sparkClusterConnectionInfo": [Function], "splitter": Splitter { @@ -230,7 +224,6 @@ exports[`SettingsComponent renders 1`] = ` "_isInitializingNotebooks": false, "_resetNotebookWorkspace": [Function], "canSaveQueries": [Function], - "closeSidePanel": undefined, "collapsedResourceTreeWidth": 36, "commandBarComponentAdapter": CommandBarComponentAdapter { "container": [Circular], @@ -255,7 +248,6 @@ exports[`SettingsComponent renders 1`] = ` "notebookServerInfo": [Function], "onRefreshDatabasesKeyPress": [Function], "onRefreshResourcesClick": [Function], - "openSidePanel": undefined, "provideFeedbackEmail": [Function], "queriesClient": QueriesClient { "container": [Circular], @@ -280,7 +272,6 @@ exports[`SettingsComponent renders 1`] = ` "selectedDatabaseId": [Function], "selectedNode": [Function], "setInProgressConsoleDataIdToBeDeleted": undefined, - "setIsNotificationConsoleExpanded": undefined, "setNotificationConsoleData": undefined, "sparkClusterConnectionInfo": [Function], "splitter": Splitter { @@ -323,7 +314,6 @@ exports[`SettingsComponent renders 1`] = ` "_isInitializingNotebooks": false, "_resetNotebookWorkspace": [Function], "canSaveQueries": [Function], - "closeSidePanel": undefined, "collapsedResourceTreeWidth": 36, "commandBarComponentAdapter": CommandBarComponentAdapter { "container": [Circular], @@ -348,7 +338,6 @@ exports[`SettingsComponent renders 1`] = ` "notebookServerInfo": [Function], "onRefreshDatabasesKeyPress": [Function], "onRefreshResourcesClick": [Function], - "openSidePanel": undefined, "provideFeedbackEmail": [Function], "queriesClient": QueriesClient { "container": [Circular], @@ -373,7 +362,6 @@ exports[`SettingsComponent renders 1`] = ` "selectedDatabaseId": [Function], "selectedNode": [Function], "setInProgressConsoleDataIdToBeDeleted": undefined, - "setIsNotificationConsoleExpanded": undefined, "setNotificationConsoleData": undefined, "sparkClusterConnectionInfo": [Function], "splitter": Splitter { diff --git a/src/Explorer/Explorer.tsx b/src/Explorer/Explorer.tsx index 540d848d0..73c530283 100644 --- a/src/Explorer/Explorer.tsx +++ b/src/Explorer/Explorer.tsx @@ -18,6 +18,7 @@ import { configContext, Platform } from "../ConfigContext"; import * as DataModels from "../Contracts/DataModels"; import * as ViewModels from "../Contracts/ViewModels"; import { GitHubOAuthService } from "../GitHub/GitHubOAuthService"; +import { useSidePanel } from "../hooks/useSidePanel"; import { IGalleryItem, JunoClient } from "../Juno/JunoClient"; import { NotebookWorkspaceManager } from "../NotebookWorkspaceManager/NotebookWorkspaceManager"; import { RouteHandler } from "../RouteHandlers/RouteHandler"; @@ -53,7 +54,6 @@ import { DeleteCollectionConfirmationPane } from "./Panes/DeleteCollectionConfir import { DeleteDatabaseConfirmationPanel } from "./Panes/DeleteDatabaseConfirmationPanel"; import { ExecuteSprocParamsPane } from "./Panes/ExecuteSprocParamsPane/ExecuteSprocParamsPane"; import { GitHubReposPanel } from "./Panes/GitHubReposPanel/GitHubReposPanel"; -import { LoadQueryPane } from "./Panes/LoadQueryPane/LoadQueryPane"; import { SaveQueryPane } from "./Panes/SaveQueryPane/SaveQueryPane"; import { SettingsPane } from "./Panes/SettingsPane/SettingsPane"; import { SetupNoteBooksPanel } from "./Panes/SetupNotebooksPanel/SetupNotebooksPanel"; @@ -81,11 +81,8 @@ BindingHandlersRegisterer.registerBindingHandlers(); var tmp = ComponentRegisterer; export interface ExplorerParams { - setIsNotificationConsoleExpanded: (isExpanded: boolean) => void; setNotificationConsoleData: (consoleData: ConsoleData) => void; setInProgressConsoleDataIdToBeDeleted: (id: string) => void; - openSidePanel: (headerText: string, panelContent: JSX.Element, onClose?: () => void) => void; - closeSidePanel: () => void; tabsManager: TabsManager; } @@ -100,15 +97,9 @@ export default class Explorer { public tableDataClient: TableDataClient; public splitter: Splitter; - // Notification Console - private setIsNotificationConsoleExpanded: (isExpanded: boolean) => void; private setNotificationConsoleData: (consoleData: ConsoleData) => void; private setInProgressConsoleDataIdToBeDeleted: (id: string) => void; - // Panes - public openSidePanel: (headerText: string, panelContent: JSX.Element, onClose?: () => void) => void; - public closeSidePanel: () => void; - // Resource Tree public databases: ko.ObservableArray; public selectedDatabaseId: ko.Computed; @@ -159,11 +150,8 @@ export default class Explorer { private static readonly MaxNbDatabasesToAutoExpand = 5; constructor(params?: ExplorerParams) { - this.setIsNotificationConsoleExpanded = params?.setIsNotificationConsoleExpanded; this.setNotificationConsoleData = params?.setNotificationConsoleData; this.setInProgressConsoleDataIdToBeDeleted = params?.setInProgressConsoleDataIdToBeDeleted; - this.openSidePanel = params?.openSidePanel; - this.closeSidePanel = params?.closeSidePanel; const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, { dataExplorerArea: Constants.Areas.ResourceTree, @@ -483,14 +471,6 @@ export default class Explorer { this.setInProgressConsoleDataIdToBeDeleted(id); } - public expandConsole(): void { - this.setIsNotificationConsoleExpanded(true); - } - - public collapseConsole(): void { - this.setIsNotificationConsoleExpanded(false); - } - public refreshDatabaseForResourceToken(): Q.Promise { const databaseId = this.resourceTokenDatabaseId(); const collectionId = this.resourceTokenCollectionId(); @@ -1136,12 +1116,12 @@ export default class Explorer { if (openedNotebookTabs.length > 0) { this.showOkModalDialog("Unable to rename file", "This file is being edited. Please close the tab and try again."); } else { - this.openSidePanel( + useSidePanel.getState().openSidePanel( "Rename Notebook", { - this.closeSidePanel(); + useSidePanel.getState().closeSidePanel(); this.resourceTree.triggerRender(); }} inputLabel="Enter new notebook name" @@ -1167,12 +1147,12 @@ export default class Explorer { throw new Error(error); } - this.openSidePanel( + useSidePanel.getState().openSidePanel( "Create new directory", { - this.closeSidePanel(); + useSidePanel.getState().closeSidePanel(); this.resourceTree.triggerRender(); }} errorMessage="Could not create directory " @@ -1561,160 +1541,115 @@ export default class Explorer { return false; }); } - public openDeleteCollectionConfirmationPane(): void { - this.openSidePanel( - "Delete " + getCollectionName(), - - ); + useSidePanel + .getState() + .openSidePanel("Delete " + getCollectionName(), ); } public openDeleteDatabaseConfirmationPane(): void { - this.openSidePanel( - "Delete " + getDatabaseName(), - this.expandConsole()} - closePanel={this.closeSidePanel} - selectedDatabase={this.findSelectedDatabase()} - /> - ); + useSidePanel + .getState() + .openSidePanel( + "Delete " + getDatabaseName(), + + ); } - public openUploadItemsPanePane(): void { - this.openSidePanel("Upload " + getUploadName(), ); + useSidePanel.getState().openSidePanel("Upload " + getUploadName(), ); } - - public openSettingPane(): void { - this.openSidePanel( - "Setting", - this.expandConsole()} closePanel={this.closeSidePanel} /> - ); - } - public openExecuteSprocParamsPanel(storedProcedure: StoredProcedure): void { - this.openSidePanel( - "Input parameters", - this.expandConsole()} - closePanel={() => this.closeSidePanel()} - /> - ); + useSidePanel + .getState() + .openSidePanel("Input parameters", ); } public async openAddCollectionPanel(databaseId?: string): Promise { await this.loadDatabaseOffers(); - this.openSidePanel( - "New " + getCollectionName(), - this.closeSidePanel()} - openNotificationConsole={() => this.expandConsole()} - databaseId={databaseId} - /> - ); + useSidePanel + .getState() + .openSidePanel("New " + getCollectionName(), ); } public openAddDatabasePane(): void { - this.openSidePanel( - "New " + getDatabaseName(), - this.expandConsole()} - closePanel={this.closeSidePanel} - /> - ); + useSidePanel.getState().openSidePanel("New " + getDatabaseName(), ); } public openBrowseQueriesPanel(): void { - this.openSidePanel("Open Saved Queries", ); - } - - public openLoadQueryPanel(): void { - this.openSidePanel("Load Query", this.closeSidePanel()} />); + useSidePanel.getState().openSidePanel("Open Saved Queries", ); } public openSaveQueryPanel(): void { - this.openSidePanel("Save Query", this.closeSidePanel()} />); + useSidePanel.getState().openSidePanel("Save Query", ); } public openUploadFilePanel(parent?: NotebookContentItem): void { parent = parent || this.resourceTree.myNotebooksContentRoot; - this.openSidePanel( - "Upload file to notebook server", - this.expandConsole()} - closePanel={this.closeSidePanel} - uploadFile={(name: string, content: string) => this.uploadFile(name, content, parent)} - /> - ); + useSidePanel + .getState() + .openSidePanel( + "Upload file to notebook server", + this.uploadFile(name, content, parent)} /> + ); } public openCassandraAddCollectionPane(): void { - this.openSidePanel( - "Add Table", - this.closeSidePanel()} - cassandraApiClient={new CassandraAPIDataClient()} - /> - ); + useSidePanel + .getState() + .openSidePanel( + "Add Table", + + ); } public openGitHubReposPanel(header: string, junoClient?: JunoClient): void { - this.openSidePanel( - header, - this.expandConsole()} - /> - ); + useSidePanel + .getState() + .openSidePanel( + header, + + ); } public openAddTableEntityPanel(queryTablesTab: QueryTablesTab, tableEntityListViewModel: TableListViewModal): void { - this.openSidePanel( - "Add Table Entity", - - ); + useSidePanel + .getState() + .openSidePanel( + "Add Table Entity", + + ); } public openSetupNotebooksPanel(title: string, description: string): void { - this.openSidePanel( - title, - this.expandConsole()} - panelTitle={title} - panelDescription={description} - /> - ); + useSidePanel + .getState() + .openSidePanel(title, ); } public openEditTableEntityPanel(queryTablesTab: QueryTablesTab, tableEntityListViewModel: TableListViewModal): void { - this.openSidePanel( - "Edit Table Entity", - - ); + useSidePanel + .getState() + .openSidePanel( + "Edit Table Entity", + + ); } public openTableSelectQueryPanel(queryViewModal: QueryViewModel): void { - this.openSidePanel( - "Select Column", - - ); + useSidePanel.getState().openSidePanel("Select Column", ); + } + public openSettingPane(): void { + useSidePanel.getState().openSidePanel("Settings", ); } } diff --git a/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx b/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx index 736dcef56..7b185fdba 100644 --- a/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx +++ b/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx @@ -21,11 +21,13 @@ import { AuthType } from "../../../AuthType"; import * as Constants from "../../../Common/Constants"; import { configContext, Platform } from "../../../ConfigContext"; import * as ViewModels from "../../../Contracts/ViewModels"; +import { useSidePanel } from "../../../hooks/useSidePanel"; import { userContext } from "../../../UserContext"; import { getCollectionName, getDatabaseName } from "../../../Utils/APITypeUtils"; import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent"; import Explorer from "../../Explorer"; import { OpenFullScreen } from "../../OpenFullScreen"; +import { LoadQueryPane } from "../../Panes/LoadQueryPane/LoadQueryPane"; let counter = 0; @@ -152,7 +154,7 @@ export function createControlCommandBarButtons(container: Explorer): CommandButt { iconSrc: SettingsIcon, iconAlt: "Settings", - onCommandClick: () => container.openSettingPane(), + onCommandClick: container.openSettingPane, commandButtonLabel: undefined, ariaLabel: "Settings", tooltipText: "Settings", @@ -167,7 +169,7 @@ export function createControlCommandBarButtons(container: Explorer): CommandButt iconSrc: OpenInTabIcon, iconAlt: label, onCommandClick: () => { - container.openSidePanel("Open Full Screen", ); + useSidePanel.getState().openSidePanel("Open Full Screen", ); }, commandButtonLabel: undefined, ariaLabel: label, @@ -408,7 +410,7 @@ function createOpenQueryFromDiskButton(container: Explorer): CommandButtonCompon return { iconSrc: OpenQueryFromDiskIcon, iconAlt: label, - onCommandClick: () => container.openLoadQueryPanel(), + onCommandClick: () => useSidePanel.getState().openSidePanel("Load Query", ), commandButtonLabel: label, ariaLabel: label, hasPopup: true, diff --git a/src/Explorer/Menus/NotificationConsole/NotificationConsoleComponent.tsx b/src/Explorer/Menus/NotificationConsole/NotificationConsoleComponent.tsx index f2c427c2b..218bd4893 100644 --- a/src/Explorer/Menus/NotificationConsole/NotificationConsoleComponent.tsx +++ b/src/Explorer/Menus/NotificationConsole/NotificationConsoleComponent.tsx @@ -15,6 +15,7 @@ import LoadingIcon from "../../../../images/loading.svg"; import ChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png"; import ChevronUpIcon from "../../../../images/QueryBuilder/CollapseChevronUp_16x.png"; import { ClientDefaults, KeyCodes } from "../../../Common/Constants"; +import { useNotificationConsole } from "../../../hooks/useNotificationConsole"; import { userContext } from "../../../UserContext"; /** @@ -321,3 +322,23 @@ const PrPreview = (props: { pr: string }) => { ); }; + +export const NotificationConsole: React.FC< + Pick +> = ({ + consoleData, + inProgressConsoleDataIdToBeDeleted, +}: Pick) => { + const setIsExpanded = useNotificationConsole((state) => state.setIsExpanded); + const isExpanded = useNotificationConsole((state) => state.isExpanded); + // TODO Refactor NotificationConsoleComponent into a functional component and remove this wrapper + // This component only exists so we can use hooks and pass them down to a non-functional component + return ( + + ); +}; diff --git a/src/Explorer/Notebook/NotebookManager.tsx b/src/Explorer/Notebook/NotebookManager.tsx index a0f09e3ea..d2fb3bcb0 100644 --- a/src/Explorer/Notebook/NotebookManager.tsx +++ b/src/Explorer/Notebook/NotebookManager.tsx @@ -14,13 +14,13 @@ import { MemoryUsageInfo } from "../../Contracts/DataModels"; import { GitHubClient } from "../../GitHub/GitHubClient"; import { GitHubContentProvider } from "../../GitHub/GitHubContentProvider"; import { GitHubOAuthService } from "../../GitHub/GitHubOAuthService"; +import { useSidePanel } from "../../hooks/useSidePanel"; import { JunoClient } from "../../Juno/JunoClient"; import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import { userContext } from "../../UserContext"; import { getFullName } from "../../Utils/UserUtils"; import Explorer from "../Explorer"; -import { ContextualPaneBase } from "../Panes/ContextualPaneBase"; import { CopyNotebookPane } from "../Panes/CopyNotebookPane/CopyNotebookPane"; import { PublishNotebookPane } from "../Panes/PublishNotebookPane/PublishNotebookPane"; import { ResourceTreeAdapter } from "../Tree/ResourceTreeAdapter"; @@ -56,8 +56,6 @@ export default class NotebookManager { public gitHubOAuthService: GitHubOAuthService; public gitHubClient: GitHubClient; - public gitHubReposPane: ContextualPaneBase; - public initialize(params: NotebookManagerOptions): void { this.params = params; this.junoClient = new JunoClient(); @@ -98,7 +96,7 @@ export default class NotebookManager { this.gitHubOAuthService.getTokenObservable().subscribe((token) => { this.gitHubClient.setToken(token?.access_token); if (this?.gitHubOAuthService.isLoggedIn()) { - this.params.container.closeSidePanel(); + useSidePanel.getState().closeSidePanel(); this.params.container.openGitHubReposPanel("Manager GitHub settings", this.junoClient); } @@ -127,37 +125,37 @@ export default class NotebookManager { onTakeSnapshot: (request: SnapshotRequest) => void, onClosePanel: () => void ): Promise { - const explorer = this.params.container; - explorer.openSidePanel( - "Publish Notebook", - , - onClosePanel - ); + useSidePanel + .getState() + .openSidePanel( + "Publish Notebook", + , + onClosePanel + ); } public openCopyNotebookPane(name: string, content: string): void { const { container } = this.params; - container.openSidePanel( - "Copy Notebook", - - ); + useSidePanel + .getState() + .openSidePanel( + "Copy Notebook", + + ); } // Octokit's error handler uses any diff --git a/src/Explorer/Panes/AddCollectionPanel.tsx b/src/Explorer/Panes/AddCollectionPanel.tsx index e6b332546..aa98e4113 100644 --- a/src/Explorer/Panes/AddCollectionPanel.tsx +++ b/src/Explorer/Panes/AddCollectionPanel.tsx @@ -20,6 +20,7 @@ import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils" import { configContext, Platform } from "../../ConfigContext"; import * as DataModels from "../../Contracts/DataModels"; import { SubscriptionType } from "../../Contracts/SubscriptionType"; +import { useSidePanel } from "../../hooks/useSidePanel"; import { CollectionCreation } from "../../Shared/Constants"; import { Action } from "../../Shared/Telemetry/TelemetryConstants"; import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; @@ -36,8 +37,6 @@ import { PanelLoadingScreen } from "./PanelLoadingScreen"; export interface AddCollectionPanelProps { explorer: Explorer; - closePanel: () => void; - openNotificationConsole: () => void; databaseId?: string; } @@ -133,7 +132,6 @@ export class AddCollectionPanel extends React.Component )} @@ -142,7 +140,6 @@ export class AddCollectionPanel extends React.Component @@ -1062,7 +1059,7 @@ export class AddCollectionPanel extends React.Component void; - openNotificationConsole: () => void; } export const AddDatabasePanel: FunctionComponent = ({ explorer: container, - closePanel, - openNotificationConsole, }: AddDatabasePaneProps) => { + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); let throughput: number; let isAutoscaleSelected: boolean; let isCostAcknowledged: boolean; @@ -114,7 +112,7 @@ export const AddDatabasePanel: FunctionComponent = ({ const _onCreateDatabaseSuccess = (offerThroughput: number, startKey: number): void => { setIsExecuting(false); - closePanel(); + closeSidePanel(); container.refreshAllDatabases(); const addDatabasePaneSuccessMessage = { ...addDatabasePaneMessage, @@ -163,7 +161,6 @@ export const AddDatabasePanel: FunctionComponent = ({ ); const props: RightPaneFormProps = { - expandConsole: openNotificationConsole, formError: formErrors, isExecuting, submitButtonText: "OK", @@ -177,7 +174,6 @@ export const AddDatabasePanel: FunctionComponent = ({ message={getUpsellMessage(userContext.portalEnv, true, container.isFirstResourceCreated(), true)} messageType="info" showErrorDetails={false} - openNotificationConsole={openNotificationConsole} link={Constants.Urls.freeTierInformation} linkText="Learn more" /> diff --git a/src/Explorer/Panes/AddDatabasePanel/__snapshots__/AddDatabasePanel.test.tsx.snap b/src/Explorer/Panes/AddDatabasePanel/__snapshots__/AddDatabasePanel.test.tsx.snap index 2c5343436..75e11242d 100644 --- a/src/Explorer/Panes/AddDatabasePanel/__snapshots__/AddDatabasePanel.test.tsx.snap +++ b/src/Explorer/Panes/AddDatabasePanel/__snapshots__/AddDatabasePanel.test.tsx.snap @@ -2,7 +2,6 @@ exports[`AddDatabasePane Pane should render Default properly 1`] = ` void; } export const BrowseQueriesPane: FunctionComponent = ({ explorer, - closePanel, }: BrowseQueriesPaneProps): JSX.Element => { + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const loadSavedQuery = (savedQuery: Query): void => { const selectedCollection: Collection = explorer && explorer.findSelectedCollection(); if (!selectedCollection) { @@ -43,7 +43,7 @@ export const BrowseQueriesPane: FunctionComponent = ({ queryName: savedQuery.queryName, paneTitle: "Open Saved Queries", }); - closePanel(); + closeSidePanel(); }; const props: QueriesGridComponentProps = { diff --git a/src/Explorer/Panes/CassandraAddCollectionPane/CassandraAddCollectionPane.tsx b/src/Explorer/Panes/CassandraAddCollectionPane/CassandraAddCollectionPane.tsx index 03fb49ebe..bef852942 100644 --- a/src/Explorer/Panes/CassandraAddCollectionPane/CassandraAddCollectionPane.tsx +++ b/src/Explorer/Panes/CassandraAddCollectionPane/CassandraAddCollectionPane.tsx @@ -6,6 +6,7 @@ import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUti import { InfoTooltip } from "../../../Common/Tooltip/InfoTooltip"; import * as DataModels from "../../../Contracts/DataModels"; import * as ViewModels from "../../../Contracts/ViewModels"; +import { useSidePanel } from "../../../hooks/useSidePanel"; import * as AddCollectionUtility from "../../../Shared/AddCollectionUtility"; import * as SharedConstants from "../../../Shared/Constants"; import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants"; @@ -19,15 +20,14 @@ import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneFor export interface CassandraAddCollectionPaneProps { explorer: Explorer; - closePanel: () => void; cassandraApiClient: CassandraAPIDataClient; } export const CassandraAddCollectionPane: FunctionComponent = ({ explorer: container, - closePanel, cassandraApiClient, }: CassandraAddCollectionPaneProps) => { + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const throughputDefaults = userContext.collectionCreationDefaults.throughput; const [createTableQuery, setCreateTableQuery] = useState("CREATE TABLE "); const [keyspaceId, setKeyspaceId] = useState(""); @@ -241,7 +241,7 @@ export const CassandraAddCollectionPane: FunctionComponent container.expandConsole(), formError: formErrors, isExecuting, submitButtonText: "Apply", diff --git a/src/Explorer/Panes/CassandraAddCollectionPane/__snapshots__/CassandraAddCollectionPane.test.tsx.snap b/src/Explorer/Panes/CassandraAddCollectionPane/__snapshots__/CassandraAddCollectionPane.test.tsx.snap index 19e1b1e10..d3f41614e 100644 --- a/src/Explorer/Panes/CassandraAddCollectionPane/__snapshots__/CassandraAddCollectionPane.test.tsx.snap +++ b/src/Explorer/Panes/CassandraAddCollectionPane/__snapshots__/CassandraAddCollectionPane.test.tsx.snap @@ -2,7 +2,6 @@ exports[`CassandraAddCollectionPane Pane should render Default properly 1`] = ` ; - public formErrorsDetails: ko.Observable; - public formErrors: ko.Observable; - public title: ko.Observable; - public visible: ko.Observable; - public isExecuting: ko.Observable; - - constructor(options: ViewModels.PaneOptions) { - super(); - this.id = options.id; - this.container = options.container; - this.visible = options.visible || ko.observable(false); - this.firstFieldHasFocus = ko.observable(false); - this.formErrors = ko.observable(); - this.title = ko.observable(); - this.formErrorsDetails = ko.observable(); - this.isExecuting = ko.observable(false); - } - - public cancel() { - this.close(); - this.container.isAccountReady() && - TelemetryProcessor.trace(Action.ContextualPane, ActionModifiers.Close, { - dataExplorerArea: Constants.Areas.ContextualPane, - paneTitle: this.title(), - }); - } - - public close() { - this.visible(false); - this.isExecuting(false); - this.resetData(); - this.resetFocus(); - } - - public open() { - this.initalFocusedElement = document.activeElement as HTMLElement; - this.visible(true); - this.firstFieldHasFocus(true); - this.resizePane(); - this.container.isAccountReady() && - TelemetryProcessor.trace(Action.ContextualPane, ActionModifiers.Open, { - dataExplorerArea: Constants.Areas.ContextualPane, - paneTitle: this.title(), - }); - } - - public resetData() { - this.firstFieldHasFocus(false); - this.formErrors(null); - this.formErrorsDetails(null); - } - - public showErrorDetails() { - this.container.expandConsole(); - } - - public submit() { - this.close(); - event.stopPropagation(); - this.container.isAccountReady() && - TelemetryProcessor.trace(Action.ContextualPane, ActionModifiers.Submit, { - dataExplorerArea: Constants.Areas.ContextualPane, - paneTitle: this.title(), - }); - } - - public onCloseKeyPress(source: any, event: KeyboardEvent): boolean { - if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) { - this.close(); - event.stopPropagation(); - return false; - } - - return true; - } - - public onPaneKeyDown(source: any, event: KeyboardEvent): boolean { - if (event.keyCode === KeyCodes.Escape) { - this.close(); - event.stopPropagation(); - return false; - } - - return true; - } - - public onSubmitKeyPress(source: any, event: KeyboardEvent): boolean { - if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) { - this.submit(); - event.stopPropagation(); - return false; - } - - return true; - } - - private resizePane(): void { - const paneElement: HTMLElement = document.getElementById(this.id); - const notificationConsoleElement: HTMLElement = document.getElementById("explorerNotificationConsole"); - const newPaneElementHeight = window.innerHeight - $(notificationConsoleElement).height(); - - $(paneElement).height(newPaneElementHeight); - } - - private resetFocus(): void { - if (this.initalFocusedElement) { - this.initalFocusedElement.focus(); - this.initalFocusedElement = undefined; - } - } -} diff --git a/src/Explorer/Panes/CopyNotebookPane/CopyNotebookPane.tsx b/src/Explorer/Panes/CopyNotebookPane/CopyNotebookPane.tsx index 8026401b6..3a982da82 100644 --- a/src/Explorer/Panes/CopyNotebookPane/CopyNotebookPane.tsx +++ b/src/Explorer/Panes/CopyNotebookPane/CopyNotebookPane.tsx @@ -3,6 +3,7 @@ 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 { useSidePanel } from "../../../hooks/useSidePanel"; import { IPinnedRepo, JunoClient } from "../../../Juno/JunoClient"; import * as GitHubUtils from "../../../Utils/GitHubUtils"; import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils"; @@ -26,7 +27,6 @@ export interface CopyNotebookPanelProps { container: Explorer; junoClient: JunoClient; gitHubOAuthService: GitHubOAuthService; - closePanel: () => void; } export const CopyNotebookPane: FunctionComponent = ({ @@ -35,8 +35,8 @@ export const CopyNotebookPane: FunctionComponent = ({ container, junoClient, gitHubOAuthService, - closePanel, }: CopyNotebookPanelProps) => { + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const [isExecuting, setIsExecuting] = useState(); const [formError, setFormError] = useState(""); const [pinnedRepos, setPinnedRepos] = useState(); @@ -84,7 +84,7 @@ export const CopyNotebookPane: FunctionComponent = ({ } NotificationConsoleUtils.logConsoleInfo(`Successfully copied ${name} to ${destination}`); - closePanel(); + closeSidePanel(); } catch (error) { const errorMessage = getErrorMessage(error); setFormError(`Failed to copy ${name} to ${destination}`); @@ -130,7 +130,6 @@ export const CopyNotebookPane: FunctionComponent = ({ isExecuting: isExecuting, submitButtonText: "OK", onSubmit: () => submit(), - expandConsole: () => container.expandConsole(), }; const copyNotebookPaneProps: CopyNotebookPaneProps = { diff --git a/src/Explorer/Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane.tsx b/src/Explorer/Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane.tsx index 5fe8c76f5..8fad674bd 100644 --- a/src/Explorer/Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane.tsx +++ b/src/Explorer/Panes/DeleteCollectionConfirmationPane/DeleteCollectionConfirmationPane.tsx @@ -5,6 +5,7 @@ import { deleteCollection } from "../../../Common/dataAccess/deleteCollection"; import DeleteFeedback from "../../../Common/DeleteFeedback"; import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils"; import { Collection } from "../../../Contracts/ViewModels"; +import { useSidePanel } from "../../../hooks/useSidePanel"; import { DefaultExperienceUtility } from "../../../Shared/DefaultExperienceUtility"; import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants"; import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; @@ -15,13 +16,12 @@ import Explorer from "../../Explorer"; import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm"; export interface DeleteCollectionConfirmationPaneProps { explorer: Explorer; - closePanel: () => void; } export const DeleteCollectionConfirmationPane: FunctionComponent = ({ explorer, - closePanel, }: DeleteCollectionConfirmationPaneProps) => { + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const [deleteCollectionFeedback, setDeleteCollectionFeedback] = useState(""); const [inputCollectionName, setInputCollectionName] = useState(""); const [formError, setFormError] = useState(""); @@ -79,7 +79,7 @@ export const DeleteCollectionConfirmationPane: FunctionComponent explorer.expandConsole(), }; return ( diff --git a/src/Explorer/Panes/DeleteCollectionConfirmationPane/__snapshots__/DeleteCollectionConfirmationPane.test.tsx.snap b/src/Explorer/Panes/DeleteCollectionConfirmationPane/__snapshots__/DeleteCollectionConfirmationPane.test.tsx.snap index 0157faed6..5032adad2 100644 --- a/src/Explorer/Panes/DeleteCollectionConfirmationPane/__snapshots__/DeleteCollectionConfirmationPane.test.tsx.snap +++ b/src/Explorer/Panes/DeleteCollectionConfirmationPane/__snapshots__/DeleteCollectionConfirmationPane.test.tsx.snap @@ -16,7 +16,6 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect } > void; - openNotificationConsole: () => void; selectedDatabase: Database; } export const DeleteDatabaseConfirmationPanel: FunctionComponent = ({ explorer, - openNotificationConsole, - closePanel, selectedDatabase, }: DeleteDatabaseConfirmationPanelProps): JSX.Element => { + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false); const [formError, setFormError] = useState(""); @@ -51,7 +49,7 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent tab.node?.id() === selectedDatabase.id()); explorer.selectedNode(undefined); @@ -111,7 +109,6 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent submit(), - expandConsole: openNotificationConsole, }; const errorProps: PanelInfoErrorProps = { diff --git a/src/Explorer/Panes/ExecuteSprocParamsPane/ExecuteSprocParamsPane.tsx b/src/Explorer/Panes/ExecuteSprocParamsPane/ExecuteSprocParamsPane.tsx index 5940304c1..eacbe9faa 100644 --- a/src/Explorer/Panes/ExecuteSprocParamsPane/ExecuteSprocParamsPane.tsx +++ b/src/Explorer/Panes/ExecuteSprocParamsPane/ExecuteSprocParamsPane.tsx @@ -2,15 +2,14 @@ import { IDropdownOption, IImageProps, Image, Stack, Text } from "@fluentui/reac import { useBoolean } from "@fluentui/react-hooks"; import React, { FunctionComponent, useState } from "react"; import AddPropertyIcon from "../../../../images/Add-property.svg"; +import { useSidePanel } from "../../../hooks/useSidePanel"; import { logConsoleError } from "../../../Utils/NotificationConsoleUtils"; import StoredProcedure from "../../Tree/StoredProcedure"; import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm"; import { InputParameter } from "./InputParameter"; interface ExecuteSprocParamsPaneProps { - expandConsole: () => void; storedProcedure: StoredProcedure; - closePanel: () => void; } const imageProps: IImageProps = { @@ -24,10 +23,9 @@ interface UnwrappedExecuteSprocParam { } export const ExecuteSprocParamsPane: FunctionComponent = ({ - expandConsole, storedProcedure, - closePanel, }: ExecuteSprocParamsPaneProps): JSX.Element => { + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false); const [paramKeyValues, setParamKeyValues] = useState([{ key: "string", text: "" }]); const [partitionValue, setPartitionValue] = useState(); // Defaulting to undefined here is important. It is not the same partition key as "" @@ -76,7 +74,7 @@ export const ExecuteSprocParamsPane: FunctionComponent { @@ -114,7 +112,6 @@ export const ExecuteSprocParamsPane: FunctionComponent void; gitHubClientProp: GitHubClient; junoClientProp: JunoClient; - openNotificationConsole: () => void; } interface IGitHubReposPanelState { @@ -91,7 +90,7 @@ export class GitHubReposPanel extends React.Component this.setup(true), onOkClick: (): Promise => this.submit(), - onCancelClick: (): void => this.props.closePanel(), + onCancelClick: (): void => useSidePanel.getState().closeSidePanel(), }, }; this.gitHubClient = this.props.gitHubClientProp; @@ -439,7 +438,6 @@ export class GitHubReposPanel extends React.Component )}
diff --git a/src/Explorer/Panes/GitHubReposPanel/__snapshots__/GitHubReposPanel.test.tsx.snap b/src/Explorer/Panes/GitHubReposPanel/__snapshots__/GitHubReposPanel.test.tsx.snap index 8a69edf54..200772d5a 100644 --- a/src/Explorer/Panes/GitHubReposPanel/__snapshots__/GitHubReposPanel.test.tsx.snap +++ b/src/Explorer/Panes/GitHubReposPanel/__snapshots__/GitHubReposPanel.test.tsx.snap @@ -20,7 +20,6 @@ exports[`GitHub Repos Panel should render Default properly 1`] = ` "_isInitializingNotebooks": false, "_resetNotebookWorkspace": [Function], "canSaveQueries": [Function], - "closeSidePanel": undefined, "collapsedResourceTreeWidth": 36, "commandBarComponentAdapter": CommandBarComponentAdapter { "container": [Circular], @@ -45,7 +44,6 @@ exports[`GitHub Repos Panel should render Default properly 1`] = ` "notebookServerInfo": [Function], "onRefreshDatabasesKeyPress": [Function], "onRefreshResourcesClick": [Function], - "openSidePanel": undefined, "provideFeedbackEmail": [Function], "queriesClient": QueriesClient { "container": [Circular], @@ -70,7 +68,6 @@ exports[`GitHub Repos Panel should render Default properly 1`] = ` "selectedDatabaseId": [Function], "selectedNode": [Function], "setInProgressConsoleDataIdToBeDeleted": undefined, - "setIsNotificationConsoleExpanded": undefined, "setNotificationConsoleData": undefined, "sparkClusterConnectionInfo": [Function], "splitter": Splitter { diff --git a/src/Explorer/Panes/GraphStylingPanel/GraphStylingPanel.tsx b/src/Explorer/Panes/GraphStylingPanel/GraphStylingPanel.tsx index 42e7dca60..9fc520693 100644 --- a/src/Explorer/Panes/GraphStylingPanel/GraphStylingPanel.tsx +++ b/src/Explorer/Panes/GraphStylingPanel/GraphStylingPanel.tsx @@ -1,25 +1,26 @@ import React, { FunctionComponent } from "react"; import * as ViewModels from "../../../Contracts/ViewModels"; +import { useSidePanel } from "../../../hooks/useSidePanel"; import { GraphStyleComponent } from "../../Graph/GraphStyleComponent/GraphStyleComponent"; import { IGraphConfig } from "../../Tabs/GraphTab"; import { PanelFooterComponent } from "../PanelFooterComponent"; interface GraphStylingProps { - closePanel: () => void; igraphConfigUiData: ViewModels.IGraphConfigUiData; igraphConfig: IGraphConfig; getValues: (igraphConfig?: IGraphConfig) => void; } export const GraphStylingPanel: FunctionComponent = ({ - closePanel, igraphConfigUiData, igraphConfig, getValues, }: GraphStylingProps): JSX.Element => { + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); + const buttonLabel = "Ok"; const submit = () => { - closePanel(); + closeSidePanel(); }; return ( diff --git a/src/Explorer/Panes/LoadQueryPane/LoadQueryPane.tsx b/src/Explorer/Panes/LoadQueryPane/LoadQueryPane.tsx index e4ed45640..2539d42b1 100644 --- a/src/Explorer/Panes/LoadQueryPane/LoadQueryPane.tsx +++ b/src/Explorer/Panes/LoadQueryPane/LoadQueryPane.tsx @@ -4,6 +4,7 @@ import React, { FunctionComponent, useState } from "react"; import folderIcon from "../../../../images/folder_16x16.svg"; import { logError } from "../../../Common/Logger"; import { Collection } from "../../../Contracts/ViewModels"; +import { useSidePanel } from "../../../hooks/useSidePanel"; import { userContext } from "../../../UserContext"; import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../../Utils/NotificationConsoleUtils"; import Explorer from "../../Explorer"; @@ -12,13 +13,10 @@ import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneFor interface LoadQueryPaneProps { explorer: Explorer; - closePanel: () => void; } -export const LoadQueryPane: FunctionComponent = ({ - explorer, - closePanel, -}: LoadQueryPaneProps): JSX.Element => { +export const LoadQueryPane: FunctionComponent = ({ explorer }: LoadQueryPaneProps): JSX.Element => { + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false); const [formError, setFormError] = useState(""); const [selectedFileName, setSelectedFileName] = useState(""); @@ -51,7 +49,7 @@ export const LoadQueryPane: FunctionComponent = ({ try { await loadQueryFromFile(file); logConsoleInfo(`Successfully loaded query from file ${file.name}`); - closePanel(); + closeSidePanel(); setLoadingFalse(); } catch (error) { setLoadingFalse(); @@ -89,7 +87,6 @@ export const LoadQueryPane: FunctionComponent = ({ isExecuting: isLoading, submitButtonText: "Load", onSubmit: () => submit(), - expandConsole: () => explorer.expandConsole(), }; return ( diff --git a/src/Explorer/Panes/LoadQueryPane/__snapshots__/LoadQueryPane.test.tsx.snap b/src/Explorer/Panes/LoadQueryPane/__snapshots__/LoadQueryPane.test.tsx.snap index 341dbde29..ee120f01a 100644 --- a/src/Explorer/Panes/LoadQueryPane/__snapshots__/LoadQueryPane.test.tsx.snap +++ b/src/Explorer/Panes/LoadQueryPane/__snapshots__/LoadQueryPane.test.tsx.snap @@ -2,7 +2,6 @@ exports[`Load Query Pane should render Default properly 1`] = ` { it("should call form submit method", () => { const onSubmitSpy = jest.fn(); - const newWrapper = mount( - undefined} - onSubmit={onSubmitSpy} - /> - ); + const newWrapper = mount(); //eslint-disable-next-line newWrapper.find("form").simulate("submit", { preventDefault: () => {} }); @@ -61,14 +54,7 @@ describe("New Vertex Panel", () => { const result = onSubmitSpy(fakeNewVertexData, onErrorSpy, onSuccessSpy); - const newWrapper = mount( - undefined} - onSubmit={onSubmitSpy} - /> - ); + const newWrapper = mount(); //eslint-disable-next-line newWrapper.find("form").simulate("submit", { preventDefault: () => {} }); diff --git a/src/Explorer/Panes/NewVertexPanel/NewVertexPanel.tsx b/src/Explorer/Panes/NewVertexPanel/NewVertexPanel.tsx index 2ebe1f59b..48b901547 100644 --- a/src/Explorer/Panes/NewVertexPanel/NewVertexPanel.tsx +++ b/src/Explorer/Panes/NewVertexPanel/NewVertexPanel.tsx @@ -1,21 +1,17 @@ import { useBoolean } from "@fluentui/react-hooks"; import React, { FunctionComponent, useState } from "react"; import * as ViewModels from "../../../Contracts/ViewModels"; -import Explorer from "../../Explorer"; +import { useSidePanel } from "../../../hooks/useSidePanel"; import { NewVertexComponent } from "../../Graph/NewVertexComponent/NewVertexComponent"; import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm"; export interface INewVertexPanelProps { - explorer: Explorer; partitionKeyPropertyProp: string; onSubmit: (result: ViewModels.NewVertexData, onError: (errorMsg: string) => void, onSuccess: () => void) => void; - openNotificationConsole: () => void; } export const NewVertexPanel: FunctionComponent = ({ - explorer, partitionKeyPropertyProp, onSubmit, - openNotificationConsole, }: INewVertexPanelProps): JSX.Element => { let newVertexDataValue: ViewModels.NewVertexData; const [errorMessage, setErrorMessage] = useState(""); @@ -33,10 +29,10 @@ export const NewVertexPanel: FunctionComponent = ({ setErrorMessage(errorMsg); setLoadingFalse(); }; - + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const onSuccess = () => { setLoadingFalse(); - explorer.closeSidePanel(); + closeSidePanel(); }; const onChange = (newVertexData: ViewModels.NewVertexData) => { @@ -47,7 +43,6 @@ export const NewVertexPanel: FunctionComponent = ({ isExecuting: isLoading, submitButtonText: "OK", onSubmit: () => submit(), - expandConsole: openNotificationConsole, }; return ( diff --git a/src/Explorer/Panes/NewVertexPanel/__snapshots__/NewVertexPanel.test.tsx.snap b/src/Explorer/Panes/NewVertexPanel/__snapshots__/NewVertexPanel.test.tsx.snap index d90dfe68d..a59aa2fca 100644 --- a/src/Explorer/Panes/NewVertexPanel/__snapshots__/NewVertexPanel.test.tsx.snap +++ b/src/Explorer/Panes/NewVertexPanel/__snapshots__/NewVertexPanel.test.tsx.snap @@ -2,7 +2,6 @@ exports[`New Vertex Panel should render default property 1`] = ` { panelContent:
, isOpen: true, isConsoleExpanded: false, - closePanel: undefined, }; const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); @@ -21,7 +20,6 @@ describe("PaneContainerComponent test", () => { panelContent: undefined, isOpen: true, isConsoleExpanded: false, - closePanel: undefined, }; const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); @@ -33,7 +31,6 @@ describe("PaneContainerComponent test", () => { panelContent:
, isOpen: true, isConsoleExpanded: true, - closePanel: undefined, }; const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); diff --git a/src/Explorer/Panes/PanelContainerComponent.tsx b/src/Explorer/Panes/PanelContainerComponent.tsx index 1e2ecfd9a..f489f7114 100644 --- a/src/Explorer/Panes/PanelContainerComponent.tsx +++ b/src/Explorer/Panes/PanelContainerComponent.tsx @@ -1,12 +1,13 @@ import { IPanelProps, IRenderFunction, Panel, PanelType } from "@fluentui/react"; import * as React from "react"; +import { useNotificationConsole } from "../../hooks/useNotificationConsole"; +import { useSidePanel } from "../../hooks/useSidePanel"; export interface PanelContainerProps { headerText: string; panelContent: JSX.Element; isConsoleExpanded: boolean; isOpen: boolean; - closePanel: () => void; panelWidth?: string; onRenderNavigationContent?: IRenderFunction; } @@ -69,7 +70,7 @@ export class PanelContainerComponent extends React.Component { + const isConsoleExpanded = useNotificationConsole((state) => state.isExpanded); + const { isOpen, panelContent, headerText } = useSidePanel((state) => { + return { + isOpen: state.isOpen, + panelContent: state.panelContent, + headerText: state.headerText, + }; + }); + // TODO Refactor PanelContainerComponent into a functional component and remove this wrapper + // This component only exists so we can use hooks and pass them down to a non-functional component + return ( + + ); +}; diff --git a/src/Explorer/Panes/PanelInfoErrorComponent.tsx b/src/Explorer/Panes/PanelInfoErrorComponent.tsx index f124a3fb1..be8a8b303 100644 --- a/src/Explorer/Panes/PanelInfoErrorComponent.tsx +++ b/src/Explorer/Panes/PanelInfoErrorComponent.tsx @@ -1,5 +1,6 @@ import { Icon, Link, Stack, Text } from "@fluentui/react"; import React from "react"; +import { useNotificationConsole } from "../../hooks/useNotificationConsole"; export interface PanelInfoErrorProps { message: string; @@ -7,7 +8,6 @@ export interface PanelInfoErrorProps { showErrorDetails: boolean; link?: string; linkText?: string; - openNotificationConsole?: () => void; formError?: boolean; } @@ -17,8 +17,9 @@ export const PanelInfoErrorComponent: React.FunctionComponent { + const expandConsole = useNotificationConsole((state) => state.expandConsole); + let icon: JSX.Element = ; if (messageType === "error") { icon = ; @@ -41,7 +42,7 @@ export const PanelInfoErrorComponent: React.FunctionComponent {showErrorDetails && ( - + More details )} diff --git a/src/Explorer/Panes/PublishNotebookPane/PublishNotebookPane.tsx b/src/Explorer/Panes/PublishNotebookPane/PublishNotebookPane.tsx index c457944d2..6843e8328 100644 --- a/src/Explorer/Panes/PublishNotebookPane/PublishNotebookPane.tsx +++ b/src/Explorer/Panes/PublishNotebookPane/PublishNotebookPane.tsx @@ -3,6 +3,7 @@ import React, { FunctionComponent, useEffect, useState } from "react"; import { HttpStatusCodes } from "../../../Common/Constants"; import { getErrorMessage, getErrorStack, handleError } from "../../../Common/ErrorHandlingUtils"; import { useNotebookSnapshotStore } from "../../../hooks/useNotebookSnapshotStore"; +import { useSidePanel } from "../../../hooks/useSidePanel"; import { JunoClient } from "../../../Juno/JunoClient"; import { Action } from "../../../Shared/Telemetry/TelemetryConstants"; import { traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor"; @@ -17,8 +18,6 @@ import { PublishNotebookPaneComponent, PublishNotebookPaneProps } from "./Publis export interface PublishNotebookPaneAProps { explorer: Explorer; - closePanel: () => void; - openNotificationConsole: () => void; junoClient: JunoClient; name: string; author: string; @@ -29,13 +28,14 @@ export interface PublishNotebookPaneAProps { export const PublishNotebookPane: FunctionComponent = ({ explorer: container, junoClient, - closePanel, name, author, notebookContent, notebookContentRef, onTakeSnapshot, }: PublishNotebookPaneAProps): JSX.Element => { + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); + const [isCodeOfConductAccepted, setIsCodeOfConductAccepted] = useState(false); const [content, setContent] = useState(""); const [formError, setFormError] = useState(""); @@ -152,7 +152,7 @@ export const PublishNotebookPane: FunctionComponent = clearPublishingMessage(); setIsExecuting(false); } - closePanel(); + closeSidePanel(); }; const createFormError = (formError: string, formErrorDetail: string, area: string): void => { @@ -171,7 +171,6 @@ export const PublishNotebookPane: FunctionComponent = isExecuting: isExecuting, submitButtonText: "Publish", onSubmit: () => submit(), - expandConsole: () => container.expandConsole(), isSubmitButtonHidden: !isCodeOfConductAccepted, }; diff --git a/src/Explorer/Panes/RightPaneForm/RightPaneForm.tsx b/src/Explorer/Panes/RightPaneForm/RightPaneForm.tsx index 7f8e14134..26c356a0f 100644 --- a/src/Explorer/Panes/RightPaneForm/RightPaneForm.tsx +++ b/src/Explorer/Panes/RightPaneForm/RightPaneForm.tsx @@ -4,7 +4,6 @@ import { PanelInfoErrorComponent } from "../PanelInfoErrorComponent"; import { PanelLoadingScreen } from "../PanelLoadingScreen"; export interface RightPaneFormProps { - expandConsole: () => void; formError: string; isExecuting: boolean; onSubmit: () => void; @@ -14,7 +13,6 @@ export interface RightPaneFormProps { } export const RightPaneForm: FunctionComponent = ({ - expandConsole, formError, isExecuting, onSubmit, @@ -30,14 +28,7 @@ export const RightPaneForm: FunctionComponent = ({ return ( <>
- {formError && ( - - )} + {formError && } {children} {!isSubmitButtonHidden && } diff --git a/src/Explorer/Panes/SaveQueryPane/SaveQueryPane.tsx b/src/Explorer/Panes/SaveQueryPane/SaveQueryPane.tsx index cd7b351bf..bea696d06 100644 --- a/src/Explorer/Panes/SaveQueryPane/SaveQueryPane.tsx +++ b/src/Explorer/Panes/SaveQueryPane/SaveQueryPane.tsx @@ -4,6 +4,7 @@ import React, { FunctionComponent, useState } from "react"; import { Areas, SavedQueries } from "../../../Common/Constants"; import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils"; import { Query } from "../../../Contracts/DataModels"; +import { useSidePanel } from "../../../hooks/useSidePanel"; import { Action } from "../../../Shared/Telemetry/TelemetryConstants"; import { traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor"; import { logConsoleError } from "../../../Utils/NotificationConsoleUtils"; @@ -13,13 +14,10 @@ import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneFor interface SaveQueryPaneProps { explorer: Explorer; - closePanel: () => void; } -export const SaveQueryPane: FunctionComponent = ({ - explorer, - closePanel, -}: SaveQueryPaneProps): JSX.Element => { +export const SaveQueryPane: FunctionComponent = ({ explorer }: SaveQueryPaneProps): JSX.Element => { + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false); const [formError, setFormError] = useState(""); const [queryName, setQueryName] = useState(""); @@ -71,7 +69,7 @@ export const SaveQueryPane: FunctionComponent = ({ }, startKey ); - closePanel(); + closeSidePanel(); } catch (error) { setLoadingFalse(); const errorMessage = getErrorMessage(error); @@ -128,7 +126,6 @@ export const SaveQueryPane: FunctionComponent = ({ }; const props: RightPaneFormProps = { - expandConsole: () => explorer.expandConsole(), formError: formError, isExecuting: isLoading, submitButtonText: canSaveQueries() ? "Save" : "Complete setup", diff --git a/src/Explorer/Panes/SaveQueryPane/__snapshots__/SaveQueryPane.test.tsx.snap b/src/Explorer/Panes/SaveQueryPane/__snapshots__/SaveQueryPane.test.tsx.snap index 3c4244eda..21bb861f6 100644 --- a/src/Explorer/Panes/SaveQueryPane/__snapshots__/SaveQueryPane.test.tsx.snap +++ b/src/Explorer/Panes/SaveQueryPane/__snapshots__/SaveQueryPane.test.tsx.snap @@ -2,7 +2,6 @@ exports[`Save Query Pane should render Default properly 1`] = ` undefined, - closePanel: (): void => undefined, -}; + describe("Settings Pane", () => { it("should render Default properly", () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); @@ -21,7 +18,7 @@ describe("Settings Pane", () => { }, } as DatabaseAccount, }); - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); }); diff --git a/src/Explorer/Panes/SettingsPane/SettingsPane.tsx b/src/Explorer/Panes/SettingsPane/SettingsPane.tsx index 23bbc0a2a..bec1200f7 100644 --- a/src/Explorer/Panes/SettingsPane/SettingsPane.tsx +++ b/src/Explorer/Panes/SettingsPane/SettingsPane.tsx @@ -3,21 +3,15 @@ import React, { FunctionComponent, MouseEvent, useState } from "react"; import * as Constants from "../../../Common/Constants"; import { InfoTooltip } from "../../../Common/Tooltip/InfoTooltip"; import { configContext } from "../../../ConfigContext"; +import { useSidePanel } from "../../../hooks/useSidePanel"; import { LocalStorageUtility, StorageKey } from "../../../Shared/StorageUtility"; import * as StringUtility from "../../../Shared/StringUtility"; import { userContext } from "../../../UserContext"; import { logConsoleInfo } from "../../../Utils/NotificationConsoleUtils"; import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm"; -export interface SettingsPaneProps { - expandConsole: () => void; - closePanel: () => void; -} - -export const SettingsPane: FunctionComponent = ({ - expandConsole, - closePanel, -}: SettingsPaneProps) => { +export const SettingsPane: FunctionComponent = () => { + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const [isExecuting, setIsExecuting] = useState(false); const [pageOption, setPageOption] = useState( LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage) === Constants.Queries.unlimitedItemsPerPage @@ -88,7 +82,7 @@ export const SettingsPane: FunctionComponent = ({ logConsoleInfo( `Updated query setting to ${LocalStorageUtility.getEntryString(StorageKey.SetPartitionKeyUndefined)}` ); - closePanel(); + closeSidePanel(); e.preventDefault(); }; @@ -101,7 +95,6 @@ export const SettingsPane: FunctionComponent = ({ }; const genericPaneProps: RightPaneFormProps = { - expandConsole, formError: "", isExecuting, submitButtonText: "Apply", diff --git a/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap b/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap index 0af71bf15..fdf0642f7 100644 --- a/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap +++ b/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap @@ -2,7 +2,6 @@ exports[`Settings Pane should render Default properly 1`] = ` void; - openNotificationConsole: () => void; panelTitle: string; panelDescription: string; } export const SetupNoteBooksPanel: FunctionComponent = ({ explorer, - closePanel, - openNotificationConsole, panelTitle, panelDescription, }: SetupNoteBooksPanelProps): JSX.Element => { - const title = panelTitle; + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); + const description = panelDescription; const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false); const [errorMessage, setErrorMessage] = useState(""); @@ -51,7 +49,7 @@ export const SetupNoteBooksPanel: FunctionComponent = const startKey: number = TelemetryProcessor.traceStart(Action.CreateNotebookWorkspace, { dataExplorerArea: Areas.ContextualPane, - paneTitle: title, + paneTitle: panelTitle, }); const clear = NotificationConsoleUtils.logConsoleProgress("Creating a new default notebook workspace"); @@ -64,13 +62,13 @@ export const SetupNoteBooksPanel: FunctionComponent = ); explorer.isAccountReady.valueHasMutated(); // re-trigger init notebooks - closePanel(); + closeSidePanel(); TelemetryProcessor.traceSuccess( Action.CreateNotebookWorkspace, { dataExplorerArea: Areas.ContextualPane, - paneTitle: title, + paneTitle: panelTitle, }, startKey ); @@ -81,7 +79,7 @@ export const SetupNoteBooksPanel: FunctionComponent = Action.CreateNotebookWorkspace, { dataExplorerArea: Areas.ContextualPane, - paneTitle: title, + paneTitle: panelTitle, error: errorMessage, errorStack: getErrorStack(error), }, @@ -99,12 +97,7 @@ export const SetupNoteBooksPanel: FunctionComponent = return (
{errorMessage && ( - + )}
diff --git a/src/Explorer/Panes/StringInputPane/StringInputPane.tsx b/src/Explorer/Panes/StringInputPane/StringInputPane.tsx index bbd095e61..c449b6cb1 100644 --- a/src/Explorer/Panes/StringInputPane/StringInputPane.tsx +++ b/src/Explorer/Panes/StringInputPane/StringInputPane.tsx @@ -89,7 +89,6 @@ export const StringInputPane: FunctionComponent = ({ isExecuting: isExecuting, submitButtonText: submitButtonLabel, onSubmit: submit, - expandConsole: () => container.expandConsole(), }; return ( diff --git a/src/Explorer/Panes/StringInputPane/__snapshots__/StringInputPane.test.tsx.snap b/src/Explorer/Panes/StringInputPane/__snapshots__/StringInputPane.test.tsx.snap index 2faa53f5d..760e170ee 100644 --- a/src/Explorer/Panes/StringInputPane/__snapshots__/StringInputPane.test.tsx.snap +++ b/src/Explorer/Panes/StringInputPane/__snapshots__/StringInputPane.test.tsx.snap @@ -10,7 +10,6 @@ exports[`StringInput Pane should render Create new directory properly 1`] = ` "_isInitializingNotebooks": false, "_resetNotebookWorkspace": [Function], "canSaveQueries": [Function], - "closeSidePanel": undefined, "collapsedResourceTreeWidth": 36, "commandBarComponentAdapter": CommandBarComponentAdapter { "container": [Circular], @@ -35,7 +34,6 @@ exports[`StringInput Pane should render Create new directory properly 1`] = ` "notebookServerInfo": [Function], "onRefreshDatabasesKeyPress": [Function], "onRefreshResourcesClick": [Function], - "openSidePanel": undefined, "provideFeedbackEmail": [Function], "queriesClient": QueriesClient { "container": [Circular], @@ -60,7 +58,6 @@ exports[`StringInput Pane should render Create new directory properly 1`] = ` "selectedDatabaseId": [Function], "selectedNode": [Function], "setInProgressConsoleDataIdToBeDeleted": undefined, - "setIsNotificationConsoleExpanded": undefined, "setNotificationConsoleData": undefined, "sparkClusterConnectionInfo": [Function], "splitter": Splitter { @@ -97,7 +94,6 @@ exports[`StringInput Pane should render Create new directory properly 1`] = ` successMessage="Created directory " > { - const fakeExplorer = {} as Explorer; const fakeQueryTablesTab = {} as QueryTablesTab; const fakeTableEntityListViewModel = {} as TableListViewModal; const fakeCassandraApiClient = {} as CassandraAPIDataClient; fakeTableEntityListViewModel.items = ko.observableArray(); fakeTableEntityListViewModel.headers = []; const props = { - explorer: fakeExplorer, - closePanel: (): void => undefined, + tableDataClient: new TablesAPIDataClient(), queryTablesTab: fakeQueryTablesTab, tableEntityListViewModel: fakeTableEntityListViewModel, cassandraApiClient: fakeCassandraApiClient, diff --git a/src/Explorer/Panes/Tables/AddTableEntityPanel.tsx b/src/Explorer/Panes/Tables/AddTableEntityPanel.tsx index 4bf8a0273..5107bc6da 100644 --- a/src/Explorer/Panes/Tables/AddTableEntityPanel.tsx +++ b/src/Explorer/Panes/Tables/AddTableEntityPanel.tsx @@ -5,13 +5,13 @@ import * as _ from "underscore"; import AddPropertyIcon from "../../../../images/Add-property.svg"; import RevertBackIcon from "../../../../images/RevertBack.svg"; import { TableEntity } from "../../../Common/TableEntity"; +import { useSidePanel } from "../../../hooks/useSidePanel"; import { userContext } from "../../../UserContext"; -import Explorer from "../../Explorer"; import * as TableConstants from "../../Tables/Constants"; import * as DataTableUtilities from "../../Tables/DataTable/DataTableUtilities"; import TableEntityListViewModel from "../../Tables/DataTable/TableEntityListViewModel"; import * as Entities from "../../Tables/Entities"; -import { CassandraAPIDataClient, CassandraTableKey } from "../../Tables/TableDataClient"; +import { CassandraAPIDataClient, CassandraTableKey, TableDataClient } from "../../Tables/TableDataClient"; import * as TableEntityProcessor from "../../Tables/TableEntityProcessor"; import * as Utilities from "../../Tables/Utilities"; import QueryTablesTab from "../../Tabs/QueryTablesTab"; @@ -37,8 +37,7 @@ import { } from "./Validators/EntityTableHelper"; interface AddTableEntityPanelProps { - explorer: Explorer; - closePanel: () => void; + tableDataClient: TableDataClient; queryTablesTab: QueryTablesTab; tableEntityListViewModel: TableEntityListViewModel; cassandraApiClient: CassandraAPIDataClient; @@ -57,12 +56,12 @@ interface EntityRowType { } export const AddTableEntityPanel: FunctionComponent = ({ - explorer, - closePanel, + tableDataClient, queryTablesTab, tableEntityListViewModel, cassandraApiClient, }: AddTableEntityPanelProps): JSX.Element => { + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const [entities, setEntities] = useState([]); const [selectedRow, setSelectedRow] = useState(0); const [entityAttributeValue, setEntityAttributeValue] = useState(""); @@ -106,15 +105,12 @@ export const AddTableEntityPanel: FunctionComponent = event.preventDefault(); const entity: Entities.ITableEntity = entityFromAttributes(entities); - const newEntity: Entities.ITableEntity = await explorer.tableDataClient.createDocument( - queryTablesTab.collection, - entity - ); + const newEntity: Entities.ITableEntity = await tableDataClient.createDocument(queryTablesTab.collection, entity); await tableEntityListViewModel.addEntityToCache(newEntity); if (!tryInsertNewHeaders(tableEntityListViewModel, newEntity)) { tableEntityListViewModel.redrawTableThrottled(); } - closePanel(); + closeSidePanel(); }; const tryInsertNewHeaders = (viewModel: TableEntityListViewModel, newEntity: Entities.ITableEntity): boolean => { @@ -296,7 +292,6 @@ export const AddTableEntityPanel: FunctionComponent = }} /> } - closePanel={() => closePanel()} isConsoleExpanded={false} /> ); @@ -308,7 +303,6 @@ export const AddTableEntityPanel: FunctionComponent = panelWidth="700px" isOpen={true} panelContent={renderPanelContent()} - closePanel={() => closePanel()} isConsoleExpanded={false} /> ); diff --git a/src/Explorer/Panes/Tables/EditTableEntityPanel.test.tsx b/src/Explorer/Panes/Tables/EditTableEntityPanel.test.tsx index fa5a26885..82aa0a211 100644 --- a/src/Explorer/Panes/Tables/EditTableEntityPanel.test.tsx +++ b/src/Explorer/Panes/Tables/EditTableEntityPanel.test.tsx @@ -1,15 +1,13 @@ import { mount } from "enzyme"; import * as ko from "knockout"; import React from "react"; -import Explorer from "../../Explorer"; import TableListViewModal from "../../Tables/DataTable/TableEntityListViewModel"; import * as Entities from "../../Tables/Entities"; -import { CassandraAPIDataClient } from "../../Tables/TableDataClient"; +import { CassandraAPIDataClient, TablesAPIDataClient } from "../../Tables/TableDataClient"; import QueryTablesTab from "../../Tabs/QueryTablesTab"; import { EditTableEntityPanel } from "./EditTableEntityPanel"; describe("Excute Edit Table Entity Pane", () => { - const fakeExplorer = {} as Explorer; const fakeQueryTablesTab = {} as QueryTablesTab; const fakeTableEntityListViewModel = {} as TableListViewModal; fakeTableEntityListViewModel.items = ko.observableArray(); @@ -18,8 +16,7 @@ describe("Excute Edit Table Entity Pane", () => { fakeTableEntityListViewModel.selected = ko.observableArray([{}]); const props = { - explorer: fakeExplorer, - closePanel: (): void => undefined, + tableDataClient: new TablesAPIDataClient(), queryTablesTab: fakeQueryTablesTab, tableEntityListViewModel: fakeTableEntityListViewModel, cassandraApiClient: fakeCassandraApiClient, diff --git a/src/Explorer/Panes/Tables/EditTableEntityPanel.tsx b/src/Explorer/Panes/Tables/EditTableEntityPanel.tsx index 27715979a..f71a1a829 100644 --- a/src/Explorer/Panes/Tables/EditTableEntityPanel.tsx +++ b/src/Explorer/Panes/Tables/EditTableEntityPanel.tsx @@ -5,13 +5,13 @@ import * as _ from "underscore"; import AddPropertyIcon from "../../../../images/Add-property.svg"; import RevertBackIcon from "../../../../images/RevertBack.svg"; import { TableEntity } from "../../../Common/TableEntity"; +import { useSidePanel } from "../../../hooks/useSidePanel"; import { userContext } from "../../../UserContext"; -import Explorer from "../../Explorer"; import * as TableConstants from "../../Tables/Constants"; import * as DataTableUtilities from "../../Tables/DataTable/DataTableUtilities"; import TableEntityListViewModel from "../../Tables/DataTable/TableEntityListViewModel"; import * as Entities from "../../Tables/Entities"; -import { CassandraAPIDataClient } from "../../Tables/TableDataClient"; +import { CassandraAPIDataClient, TableDataClient } from "../../Tables/TableDataClient"; import * as TableEntityProcessor from "../../Tables/TableEntityProcessor"; import QueryTablesTab from "../../Tabs/QueryTablesTab"; import { PanelContainerComponent } from "../PanelContainerComponent"; @@ -34,8 +34,7 @@ import { } from "./Validators/EntityTableHelper"; interface EditTableEntityPanelProps { - explorer: Explorer; - closePanel: () => void; + tableDataClient: TableDataClient; queryTablesTab: QueryTablesTab; tableEntityListViewModel: TableEntityListViewModel; cassandraApiClient: CassandraAPIDataClient; @@ -55,12 +54,12 @@ interface EntityRowType { } export const EditTableEntityPanel: FunctionComponent = ({ - explorer, - closePanel, + tableDataClient, queryTablesTab, tableEntityListViewModel, cassandraApiClient, }: EditTableEntityPanelProps): JSX.Element => { + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const [entities, setEntities] = useState([]); const [selectedRow, setSelectedRow] = useState(0); const [entityAttributeValue, setEntityAttributeValue] = useState(""); @@ -197,9 +196,9 @@ export const EditTableEntityPanel: FunctionComponent } event.preventDefault(); const entity: Entities.ITableEntity = entityFromAttributes(entities); - const tableDataClient = userContext.apiType === "Cassandra" ? cassandraApiClient : explorer.tableDataClient; + const newTableDataClient = userContext.apiType === "Cassandra" ? cassandraApiClient : tableDataClient; const originalDocumentData = userContext.apiType === "Cassandra" ? originalDocument[0] : originalDocument; - const newEntity: Entities.ITableEntity = await tableDataClient.updateDocument( + const newEntity: Entities.ITableEntity = await newTableDataClient.updateDocument( queryTablesTab.collection, originalDocumentData, entity @@ -210,7 +209,7 @@ export const EditTableEntityPanel: FunctionComponent } tableEntityListViewModel.selected.removeAll(); tableEntityListViewModel.selected.push(newEntity); - closePanel(); + closeSidePanel(); }; const tryInsertNewHeaders = (viewModel: TableEntityListViewModel, newEntity: Entities.ITableEntity): boolean => { @@ -391,7 +390,6 @@ export const EditTableEntityPanel: FunctionComponent }} /> } - closePanel={() => closePanel()} isConsoleExpanded={false} /> ); @@ -403,7 +401,6 @@ export const EditTableEntityPanel: FunctionComponent panelWidth="700px" isOpen={true} panelContent={renderPanelContent()} - closePanel={() => closePanel()} isConsoleExpanded={false} /> ); diff --git a/src/Explorer/Panes/Tables/TableQuerySelectPanel/TableQuerySelectPanel.tsx b/src/Explorer/Panes/Tables/TableQuerySelectPanel/TableQuerySelectPanel.tsx index 72ee0d0bf..dbc3c5238 100644 --- a/src/Explorer/Panes/Tables/TableQuerySelectPanel/TableQuerySelectPanel.tsx +++ b/src/Explorer/Panes/Tables/TableQuerySelectPanel/TableQuerySelectPanel.tsx @@ -1,14 +1,12 @@ import { Checkbox, Text } from "@fluentui/react"; import React, { FunctionComponent, useEffect, useState } from "react"; +import { useSidePanel } from "../../../../hooks/useSidePanel"; import { userContext } from "../../../../UserContext"; -import Explorer from "../../../Explorer"; import * as Constants from "../../../Tables/Constants"; import QueryViewModel from "../../../Tables/QueryBuilder/QueryViewModel"; import { RightPaneForm, RightPaneFormProps } from "../../RightPaneForm/RightPaneForm"; interface TableQuerySelectPanelProps { - explorer: Explorer; - closePanel: () => void; queryViewModel: QueryViewModel; } @@ -19,10 +17,10 @@ interface ISelectColumn { } export const TableQuerySelectPanel: FunctionComponent = ({ - explorer, - closePanel, queryViewModel, }: TableQuerySelectPanelProps): JSX.Element => { + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); + const [columnOptions, setColumnOptions] = useState([ { columnName: "", selected: true, editable: false }, ]); @@ -31,7 +29,7 @@ export const TableQuerySelectPanel: FunctionComponent { queryViewModel.selectText(getParameters()); queryViewModel.getSelectMessage(); - closePanel(); + closeSidePanel(); }; const props: RightPaneFormProps = { @@ -39,7 +37,6 @@ export const TableQuerySelectPanel: FunctionComponent explorer.expandConsole(), }; const handleClick = (isChecked: boolean, selectedColumn: string): void => { diff --git a/src/Explorer/Panes/Tables/TableQuerySelectPanel/__snapshots__/TableQuerySelectPanel.test.tsx.snap b/src/Explorer/Panes/Tables/TableQuerySelectPanel/__snapshots__/TableQuerySelectPanel.test.tsx.snap index 260bfd7d2..d83b5a138 100644 --- a/src/Explorer/Panes/Tables/TableQuerySelectPanel/__snapshots__/TableQuerySelectPanel.test.tsx.snap +++ b/src/Explorer/Panes/Tables/TableQuerySelectPanel/__snapshots__/TableQuerySelectPanel.test.tsx.snap @@ -11,7 +11,6 @@ exports[`Table query select Panel should render Default properly 1`] = ` } > void; - closePanel: () => void; uploadFile: (name: string, content: string) => Promise; } -export const UploadFilePane: FunctionComponent = ({ - expandConsole, - closePanel, - uploadFile, -}: UploadFilePanelProps) => { +export const UploadFilePane: FunctionComponent = ({ uploadFile }: UploadFilePanelProps) => { + const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const extensions: string = undefined; //ex. ".ipynb" const errorMessage = "Could not upload file"; const inProgressMessage = "Uploading file to notebook server"; @@ -42,7 +38,7 @@ export const UploadFilePane: FunctionComponent = ({ .then( () => { logConsoleInfo(`${successMessage} ${file.name}`); - closePanel(); + closeSidePanel(); }, (error: string) => { setFormErrors(errorMessage); @@ -79,7 +75,6 @@ export const UploadFilePane: FunctionComponent = ({ }; const props: RightPaneFormProps = { - expandConsole, formError: formErrors, isExecuting: isExecuting, submitButtonText: "Upload", diff --git a/src/Explorer/Panes/UploadItemsPane/UploadItemsPane.test.tsx b/src/Explorer/Panes/UploadItemsPane/UploadItemsPane.test.tsx index e259b6ba5..2074573c7 100644 --- a/src/Explorer/Panes/UploadItemsPane/UploadItemsPane.test.tsx +++ b/src/Explorer/Panes/UploadItemsPane/UploadItemsPane.test.tsx @@ -4,7 +4,6 @@ import Explorer from "../../Explorer"; import { UploadItemsPane } from "./UploadItemsPane"; const props = { explorer: new Explorer(), - closePanel: (): void => undefined, }; describe("Upload Items Pane", () => { it("should render Default properly", () => { diff --git a/src/Explorer/Panes/UploadItemsPane/UploadItemsPane.tsx b/src/Explorer/Panes/UploadItemsPane/UploadItemsPane.tsx index 285801434..4ae3d656f 100644 --- a/src/Explorer/Panes/UploadItemsPane/UploadItemsPane.tsx +++ b/src/Explorer/Panes/UploadItemsPane/UploadItemsPane.tsx @@ -26,7 +26,6 @@ export const UploadItemsPane: FunctionComponent = ({ explo } const selectedCollection = explorer.findSelectedCollection(); - setIsExecuting(true); selectedCollection @@ -51,7 +50,6 @@ export const UploadItemsPane: FunctionComponent = ({ explo }; const props: RightPaneFormProps = { - expandConsole: () => explorer.expandConsole(), formError, isExecuting: isExecuting, submitButtonText: "Upload", diff --git a/src/Explorer/Panes/UploadItemsPane/__snapshots__/UploadItemsPane.test.tsx.snap b/src/Explorer/Panes/UploadItemsPane/__snapshots__/UploadItemsPane.test.tsx.snap index 5c34e367c..f4f8696dc 100644 --- a/src/Explorer/Panes/UploadItemsPane/__snapshots__/UploadItemsPane.test.tsx.snap +++ b/src/Explorer/Panes/UploadItemsPane/__snapshots__/UploadItemsPane.test.tsx.snap @@ -2,7 +2,6 @@ exports[`Upload Items Pane should render Default properly 1`] = ` ; private isValidQuery: ko.Observable; private collectionPartitionKeyProperty: string; - private contextualPane: ContextualPaneBase; public graphExplorer: GraphExplorer; public options: GraphTabOptions; constructor(options: GraphTabOptions) { @@ -159,12 +156,10 @@ export default class GraphTab extends TabsBase { /* Command bar */ private showNewVertexEditor(): void { - this.collection.container.openSidePanel( + useSidePanel.getState().openSidePanel( "New Vertex", this.collection.container.expandConsole()} onSubmit={( result: ViewModels.NewVertexData, onError: (errorMessage: string) => void, @@ -173,7 +168,7 @@ export default class GraphTab extends TabsBase { this.graphAccessor.addVertex(result).then( () => { onSuccess(); - this.contextualPane.cancel(); + useSidePanel.getState().closeSidePanel(); }, (error: GraphExplorerError) => { onError(error.title); @@ -184,10 +179,9 @@ export default class GraphTab extends TabsBase { ); } public openStyling(): void { - this.collection.container.openSidePanel( + useSidePanel.getState().openSidePanel( "Graph Style", { diff --git a/src/Explorer/Tabs/QueryTab.ts b/src/Explorer/Tabs/QueryTab.ts index bf85f257c..50c258e8c 100644 --- a/src/Explorer/Tabs/QueryTab.ts +++ b/src/Explorer/Tabs/QueryTab.ts @@ -10,6 +10,7 @@ import { MinimalQueryIterator } from "../../Common/IteratorUtilities"; import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter"; import * as DataModels from "../../Contracts/DataModels"; import * as ViewModels from "../../Contracts/ViewModels"; +import { useNotificationConsole } from "../../hooks/useNotificationConsole"; import { Action } from "../../Shared/Telemetry/TelemetryConstants"; import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import { userContext } from "../../UserContext"; @@ -198,7 +199,7 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem } public onErrorDetailsClick = (src: any, event: MouseEvent): boolean => { - this.collection && this.collection.container.expandConsole(); + useNotificationConsole.getState().expandConsole(); return false; }; diff --git a/src/Explorer/Tabs/StoredProcedureTab.ts b/src/Explorer/Tabs/StoredProcedureTab.ts index 01ff59c11..1b3de5502 100644 --- a/src/Explorer/Tabs/StoredProcedureTab.ts +++ b/src/Explorer/Tabs/StoredProcedureTab.ts @@ -9,6 +9,7 @@ import { updateStoredProcedure } from "../../Common/dataAccess/updateStoredProce import editable from "../../Common/EditableUtility"; import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"; import * as ViewModels from "../../Contracts/ViewModels"; +import { useNotificationConsole } from "../../hooks/useNotificationConsole"; import { Action } from "../../Shared/Telemetry/TelemetryConstants"; import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent"; @@ -136,7 +137,7 @@ export default class StoredProcedureTab extends ScriptTabBase { } public onErrorDetailsClick = (src: any, event: MouseEvent): boolean => { - this.collection && this.collection.container.expandConsole(); + useNotificationConsole.getState().expandConsole(); return false; }; diff --git a/src/Explorer/Tabs/TabsBase.ts b/src/Explorer/Tabs/TabsBase.ts index dfd92a651..1f0dbc3bb 100644 --- a/src/Explorer/Tabs/TabsBase.ts +++ b/src/Explorer/Tabs/TabsBase.ts @@ -3,6 +3,7 @@ import * as Constants from "../../Common/Constants"; import * as ThemeUtility from "../../Common/ThemeUtility"; import * as DataModels from "../../Contracts/DataModels"; import * as ViewModels from "../../Contracts/ViewModels"; +import { useNotificationConsole } from "../../hooks/useNotificationConsole"; import { RouteHandler } from "../../RouteHandlers/RouteHandler"; import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; @@ -122,8 +123,8 @@ export default class TabsBase extends WaitsForTemplateViewModel { } public onErrorDetailsClick = (src: any, event: MouseEvent): boolean => { - this.collection?.container?.expandConsole(); - this.database?.container?.expandConsole(); + useNotificationConsole.getState().expandConsole(); + useNotificationConsole.getState().expandConsole(); return false; }; diff --git a/src/Main.tsx b/src/Main.tsx index 90c43cb3f..d8244ec2b 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -39,16 +39,15 @@ import "./Explorer/Graph/GraphExplorerComponent/graphExplorer.less"; import "./Explorer/Menus/CommandBar/CommandBarComponent.less"; import "./Explorer/Menus/CommandBar/MemoryTrackerComponent.less"; import "./Explorer/Menus/NotificationConsole/NotificationConsole.less"; -import { NotificationConsoleComponent } from "./Explorer/Menus/NotificationConsole/NotificationConsoleComponent"; +import { NotificationConsole } from "./Explorer/Menus/NotificationConsole/NotificationConsoleComponent"; import "./Explorer/Panes/PanelComponent.less"; -import { PanelContainerComponent } from "./Explorer/Panes/PanelContainerComponent"; +import { SidePanel } from "./Explorer/Panes/PanelContainerComponent"; import { SplashScreen } from "./Explorer/SplashScreen/SplashScreen"; import "./Explorer/SplashScreen/SplashScreen.less"; import "./Explorer/Tabs/QueryTab.less"; import { Tabs } from "./Explorer/Tabs/Tabs"; import { useConfig } from "./hooks/useConfig"; import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer"; -import { useSidePanel } from "./hooks/useSidePanel"; import { useTabs } from "./hooks/useTabs"; import "./Libs/jquery"; import "./Shared/appInsights"; @@ -56,21 +55,16 @@ import "./Shared/appInsights"; initializeIcons(); const App: React.FunctionComponent = () => { - const [isNotificationConsoleExpanded, setIsNotificationConsoleExpanded] = useState(false); const [notificationConsoleData, setNotificationConsoleData] = useState(undefined); //TODO: Refactor so we don't need to pass the id to remove a console data const [inProgressConsoleDataIdToBeDeleted, setInProgressConsoleDataIdToBeDeleted] = useState(""); const [isLeftPaneExpanded, setIsLeftPaneExpanded] = useState(true); - const { isPanelOpen, panelContent, headerText, openSidePanel, closeSidePanel } = useSidePanel(); const { tabs, activeTab, tabsManager } = useTabs(); const explorerParams: ExplorerParams = { - setIsNotificationConsoleExpanded, setNotificationConsoleData, setInProgressConsoleDataIdToBeDeleted, - openSidePanel, - closeSidePanel, tabsManager, }; @@ -125,21 +119,13 @@ const App: React.FunctionComponent = () => { aria-label="Notification console" id="explorerNotificationConsole" > -
- +
); diff --git a/src/hooks/useNotificationConsole.ts b/src/hooks/useNotificationConsole.ts new file mode 100644 index 000000000..8b293db99 --- /dev/null +++ b/src/hooks/useNotificationConsole.ts @@ -0,0 +1,14 @@ +import create, { UseStore } from "zustand"; + +export interface NotificationConsoleState { + isExpanded: boolean; + expandConsole: () => void; + // TODO Remove this method. Add a `closeConsole` method instead + setIsExpanded: (isExpanded: boolean) => void; +} + +export const useNotificationConsole: UseStore = create((set) => ({ + isExpanded: false, + expandConsole: () => set((state) => ({ ...state, isExpanded: true })), + setIsExpanded: (isExpanded) => set((state) => ({ ...state, isExpanded })), +})); diff --git a/src/hooks/useSidePanel.ts b/src/hooks/useSidePanel.ts index e5d93c84b..26fce5954 100644 --- a/src/hooks/useSidePanel.ts +++ b/src/hooks/useSidePanel.ts @@ -1,35 +1,15 @@ -import { useState } from "react"; +import create, { UseStore } from "zustand"; -export interface SidePanelHooks { - isPanelOpen: boolean; - panelContent: JSX.Element; - headerText: string; +export interface SidePanelState { + isOpen: boolean; + panelContent?: JSX.Element; + headerText?: string; openSidePanel: (headerText: string, panelContent: JSX.Element, onClose?: () => void) => void; closeSidePanel: () => void; } -export const useSidePanel = (): SidePanelHooks => { - const [isPanelOpen, setIsPanelOpen] = useState(false); - const [panelContent, setPanelContent] = useState(); - const [headerText, setHeaderText] = useState(); - const [onCloseCallback, setOnCloseCallback] = useState<{ callback: () => void }>(); - - const openSidePanel = (headerText: string, panelContent: JSX.Element, onClose?: () => void): void => { - setHeaderText(headerText); - setPanelContent(panelContent); - setIsPanelOpen(true); - !!onClose && setOnCloseCallback({ callback: onClose }); - }; - - const closeSidePanel = (): void => { - setHeaderText(""); - setPanelContent(undefined); - setIsPanelOpen(false); - if (onCloseCallback) { - onCloseCallback.callback(); - setOnCloseCallback(undefined); - } - }; - - return { isPanelOpen, panelContent, headerText, openSidePanel, closeSidePanel }; -}; +export const useSidePanel: UseStore = create((set) => ({ + isOpen: false, + openSidePanel: (headerText, panelContent) => set((state) => ({ ...state, headerText, panelContent, isOpen: true })), + closeSidePanel: () => set((state) => ({ ...state, isOpen: false })), +}));