Move delete collection confirmation pane to react (#417)
This commit is contained in:
parent
b217d4be1b
commit
4210e0752b
|
@ -87,7 +87,7 @@ src/Explorer/DataSamples/ContainerSampleGenerator.test.ts
|
||||||
src/Explorer/DataSamples/ContainerSampleGenerator.ts
|
src/Explorer/DataSamples/ContainerSampleGenerator.ts
|
||||||
src/Explorer/DataSamples/DataSamplesUtil.test.ts
|
src/Explorer/DataSamples/DataSamplesUtil.test.ts
|
||||||
src/Explorer/DataSamples/DataSamplesUtil.ts
|
src/Explorer/DataSamples/DataSamplesUtil.ts
|
||||||
src/Explorer/Explorer.ts
|
src/Explorer/Explorer.tsx
|
||||||
src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.test.ts
|
src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.test.ts
|
||||||
src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.ts
|
src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.ts
|
||||||
src/Explorer/Graph/GraphExplorerComponent/D3ForceGraph.test.ts
|
src/Explorer/Graph/GraphExplorerComponent/D3ForceGraph.test.ts
|
||||||
|
|
|
@ -59,6 +59,11 @@
|
||||||
|
|
||||||
@GalleryBackgroundColor: #fdfdfd;
|
@GalleryBackgroundColor: #fdfdfd;
|
||||||
|
|
||||||
|
//Icons
|
||||||
|
@InfoIconColor: #0072c6;
|
||||||
|
@WarningIconColor: #db7500;
|
||||||
|
@ErrorIconColor: #b91f26;
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
METRICS
|
METRICS
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
|
@ -1523,6 +1523,21 @@ p {
|
||||||
.tooltipVisible();
|
.tooltipVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inputTooltip {
|
||||||
|
.inputTooltip();
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputTooltip .inputTooltipText {
|
||||||
|
top: -68px;
|
||||||
|
.inputTooltipText();
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputTooltip .inputTooltipText::after {
|
||||||
|
border-width: @MediumSpace @MediumSpace 0 @MediumSpace;
|
||||||
|
top: 55px;
|
||||||
|
.inputTooltipTextAfter();
|
||||||
|
}
|
||||||
|
|
||||||
.infoTooltip a {
|
.infoTooltip a {
|
||||||
color: @AccentHigh;
|
color: @AccentHigh;
|
||||||
}
|
}
|
||||||
|
@ -3028,3 +3043,45 @@ settings-pane {
|
||||||
.collapsibleSection :hover {
|
.collapsibleSection :hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.messageBarInfoIcon {
|
||||||
|
color: @InfoIconColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageBarWarningIcon {
|
||||||
|
color: @WarningIconColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.freeTierInfoBanner {
|
||||||
|
background-color: @BaseLow;
|
||||||
|
display: inline-flex;
|
||||||
|
padding: @DefaultSpace;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.freeTierInfoIcon img {
|
||||||
|
height: 28px;
|
||||||
|
width: 28px;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.freeTierInfoMessage {
|
||||||
|
margin: auto 0;
|
||||||
|
padding-left: @MediumSpace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.freeTierInlineWarning {
|
||||||
|
display: inline-flex;
|
||||||
|
padding: 8px 8px 8px 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.freeTierWarningIcon img {
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.freeTierWarningMessage {
|
||||||
|
margin: auto 0;
|
||||||
|
padding-left: @SmallSpace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -120,6 +120,7 @@ export class Features {
|
||||||
public static readonly enableSDKoperations = "enablesdkoperations";
|
public static readonly enableSDKoperations = "enablesdkoperations";
|
||||||
public static readonly showMinRUSurvey = "showminrusurvey";
|
public static readonly showMinRUSurvey = "showminrusurvey";
|
||||||
public static readonly selfServeType = "selfservetype";
|
public static readonly selfServeType = "selfservetype";
|
||||||
|
public static readonly enableKOPanel = "enablekopanel";
|
||||||
}
|
}
|
||||||
|
|
||||||
// flight names returned from the portal are always lowercase
|
// flight names returned from the portal are always lowercase
|
||||||
|
|
|
@ -137,7 +137,6 @@ export interface Collection extends CollectionBase {
|
||||||
openTab(): void;
|
openTab(): void;
|
||||||
|
|
||||||
onSettingsClick: () => Promise<void>;
|
onSettingsClick: () => Promise<void>;
|
||||||
onDeleteCollectionContextMenuClick(source: Collection, event: MouseEvent): void;
|
|
||||||
|
|
||||||
onNewGraphClick(): void;
|
onNewGraphClick(): void;
|
||||||
onNewMongoQueryClick(source: any, event: MouseEvent, queryText?: string): void;
|
onNewMongoQueryClick(source: any, event: MouseEvent, queryText?: string): void;
|
||||||
|
|
|
@ -112,10 +112,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
||||||
|
|
||||||
items.push({
|
items.push({
|
||||||
iconSrc: DeleteCollectionIcon,
|
iconSrc: DeleteCollectionIcon,
|
||||||
onClick: () => {
|
onClick: () => container.openDeleteCollectionConfirmationPane(),
|
||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
|
||||||
selectedCollection && selectedCollection.onDeleteCollectionContextMenuClick(selectedCollection, null);
|
|
||||||
},
|
|
||||||
label: container.deleteCollectionText(),
|
label: container.deleteCollectionText(),
|
||||||
styleClass: "deleteCollectionMenuItem",
|
styleClass: "deleteCollectionMenuItem",
|
||||||
});
|
});
|
||||||
|
|
|
@ -804,6 +804,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||||
},
|
},
|
||||||
"clickHostedAccountSwitch": [Function],
|
"clickHostedAccountSwitch": [Function],
|
||||||
"clickHostedDirectorySwitch": [Function],
|
"clickHostedDirectorySwitch": [Function],
|
||||||
|
"closeSidePanel": undefined,
|
||||||
"collapsedResourceTreeWidth": 36,
|
"collapsedResourceTreeWidth": 36,
|
||||||
"collectionCreationDefaults": Object {
|
"collectionCreationDefaults": Object {
|
||||||
"storage": "100",
|
"storage": "100",
|
||||||
|
@ -1021,6 +1022,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"onSwitchToConnectionString": [Function],
|
"onSwitchToConnectionString": [Function],
|
||||||
"onToggleKeyDown": [Function],
|
"onToggleKeyDown": [Function],
|
||||||
|
"openSidePanel": undefined,
|
||||||
"provideFeedbackEmail": [Function],
|
"provideFeedbackEmail": [Function],
|
||||||
"queriesClient": QueriesClient {
|
"queriesClient": QueriesClient {
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
|
@ -2083,6 +2085,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||||
},
|
},
|
||||||
"clickHostedAccountSwitch": [Function],
|
"clickHostedAccountSwitch": [Function],
|
||||||
"clickHostedDirectorySwitch": [Function],
|
"clickHostedDirectorySwitch": [Function],
|
||||||
|
"closeSidePanel": undefined,
|
||||||
"collapsedResourceTreeWidth": 36,
|
"collapsedResourceTreeWidth": 36,
|
||||||
"collectionCreationDefaults": Object {
|
"collectionCreationDefaults": Object {
|
||||||
"storage": "100",
|
"storage": "100",
|
||||||
|
@ -2300,6 +2303,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"onSwitchToConnectionString": [Function],
|
"onSwitchToConnectionString": [Function],
|
||||||
"onToggleKeyDown": [Function],
|
"onToggleKeyDown": [Function],
|
||||||
|
"openSidePanel": undefined,
|
||||||
"provideFeedbackEmail": [Function],
|
"provideFeedbackEmail": [Function],
|
||||||
"queriesClient": QueriesClient {
|
"queriesClient": QueriesClient {
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
|
@ -3375,6 +3379,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||||
},
|
},
|
||||||
"clickHostedAccountSwitch": [Function],
|
"clickHostedAccountSwitch": [Function],
|
||||||
"clickHostedDirectorySwitch": [Function],
|
"clickHostedDirectorySwitch": [Function],
|
||||||
|
"closeSidePanel": undefined,
|
||||||
"collapsedResourceTreeWidth": 36,
|
"collapsedResourceTreeWidth": 36,
|
||||||
"collectionCreationDefaults": Object {
|
"collectionCreationDefaults": Object {
|
||||||
"storage": "100",
|
"storage": "100",
|
||||||
|
@ -3592,6 +3597,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"onSwitchToConnectionString": [Function],
|
"onSwitchToConnectionString": [Function],
|
||||||
"onToggleKeyDown": [Function],
|
"onToggleKeyDown": [Function],
|
||||||
|
"openSidePanel": undefined,
|
||||||
"provideFeedbackEmail": [Function],
|
"provideFeedbackEmail": [Function],
|
||||||
"queriesClient": QueriesClient {
|
"queriesClient": QueriesClient {
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
|
@ -4654,6 +4660,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||||
},
|
},
|
||||||
"clickHostedAccountSwitch": [Function],
|
"clickHostedAccountSwitch": [Function],
|
||||||
"clickHostedDirectorySwitch": [Function],
|
"clickHostedDirectorySwitch": [Function],
|
||||||
|
"closeSidePanel": undefined,
|
||||||
"collapsedResourceTreeWidth": 36,
|
"collapsedResourceTreeWidth": 36,
|
||||||
"collectionCreationDefaults": Object {
|
"collectionCreationDefaults": Object {
|
||||||
"storage": "100",
|
"storage": "100",
|
||||||
|
@ -4871,6 +4878,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"onSwitchToConnectionString": [Function],
|
"onSwitchToConnectionString": [Function],
|
||||||
"onToggleKeyDown": [Function],
|
"onToggleKeyDown": [Function],
|
||||||
|
"openSidePanel": undefined,
|
||||||
"provideFeedbackEmail": [Function],
|
"provideFeedbackEmail": [Function],
|
||||||
"queriesClient": QueriesClient {
|
"queriesClient": QueriesClient {
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import React from "react";
|
||||||
import * as ComponentRegisterer from "./ComponentRegisterer";
|
import * as ComponentRegisterer from "./ComponentRegisterer";
|
||||||
import * as Constants from "../Common/Constants";
|
import * as Constants from "../Common/Constants";
|
||||||
import * as DataModels from "../Contracts/DataModels";
|
import * as DataModels from "../Contracts/DataModels";
|
||||||
|
@ -47,7 +48,6 @@ import { ExplorerMetrics } from "../Common/Constants";
|
||||||
import { ExplorerSettings } from "../Shared/ExplorerSettings";
|
import { ExplorerSettings } from "../Shared/ExplorerSettings";
|
||||||
import { FileSystemUtil } from "./Notebook/FileSystemUtil";
|
import { FileSystemUtil } from "./Notebook/FileSystemUtil";
|
||||||
import { handleOpenAction } from "./OpenActions";
|
import { handleOpenAction } from "./OpenActions";
|
||||||
import { isInvalidParentFrameOrigin } from "../Utils/MessageValidation";
|
|
||||||
import { IGalleryItem } from "../Juno/JunoClient";
|
import { IGalleryItem } from "../Juno/JunoClient";
|
||||||
import { LoadQueryPane } from "./Panes/LoadQueryPane";
|
import { LoadQueryPane } from "./Panes/LoadQueryPane";
|
||||||
import * as Logger from "../Common/Logger";
|
import * as Logger from "../Common/Logger";
|
||||||
|
@ -92,6 +92,7 @@ import { SelfServeLoadingComponentAdapter } from "../SelfServe/SelfServeLoadingC
|
||||||
import { SelfServeType } from "../SelfServe/SelfServeUtils";
|
import { SelfServeType } from "../SelfServe/SelfServeUtils";
|
||||||
import { SelfServeComponentAdapter } from "../SelfServe/SelfServeComponentAdapter";
|
import { SelfServeComponentAdapter } from "../SelfServe/SelfServeComponentAdapter";
|
||||||
import { GalleryTab } from "./Controls/NotebookGallery/GalleryViewerComponent";
|
import { GalleryTab } from "./Controls/NotebookGallery/GalleryViewerComponent";
|
||||||
|
import { DeleteCollectionConfirmationPanel } from "./Panes/DeleteCollectionConfirmationPanel";
|
||||||
|
|
||||||
BindingHandlersRegisterer.registerBindingHandlers();
|
BindingHandlersRegisterer.registerBindingHandlers();
|
||||||
// Hold a reference to ComponentRegisterer to prevent transpiler to ignore import
|
// Hold a reference to ComponentRegisterer to prevent transpiler to ignore import
|
||||||
|
@ -111,6 +112,8 @@ export interface ExplorerParams {
|
||||||
setIsNotificationConsoleExpanded: (isExpanded: boolean) => void;
|
setIsNotificationConsoleExpanded: (isExpanded: boolean) => void;
|
||||||
setNotificationConsoleData: (consoleData: ConsoleData) => void;
|
setNotificationConsoleData: (consoleData: ConsoleData) => void;
|
||||||
setInProgressConsoleDataIdToBeDeleted: (id: string) => void;
|
setInProgressConsoleDataIdToBeDeleted: (id: string) => void;
|
||||||
|
openSidePanel: (headerText: string, panelContent: JSX.Element) => void;
|
||||||
|
closeSidePanel: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Explorer {
|
export default class Explorer {
|
||||||
|
@ -158,6 +161,8 @@ export default class Explorer {
|
||||||
|
|
||||||
// Panes
|
// Panes
|
||||||
public contextPanes: ContextualPaneBase[];
|
public contextPanes: ContextualPaneBase[];
|
||||||
|
private openSidePanel: (headerText: string, panelContent: JSX.Element) => void;
|
||||||
|
private closeSidePanel: () => void;
|
||||||
|
|
||||||
// Resource Tree
|
// Resource Tree
|
||||||
public databases: ko.ObservableArray<ViewModels.Database>;
|
public databases: ko.ObservableArray<ViewModels.Database>;
|
||||||
|
@ -279,6 +284,8 @@ export default class Explorer {
|
||||||
this.setIsNotificationConsoleExpanded = params?.setIsNotificationConsoleExpanded;
|
this.setIsNotificationConsoleExpanded = params?.setIsNotificationConsoleExpanded;
|
||||||
this.setNotificationConsoleData = params?.setNotificationConsoleData;
|
this.setNotificationConsoleData = params?.setNotificationConsoleData;
|
||||||
this.setInProgressConsoleDataIdToBeDeleted = params?.setInProgressConsoleDataIdToBeDeleted;
|
this.setInProgressConsoleDataIdToBeDeleted = params?.setInProgressConsoleDataIdToBeDeleted;
|
||||||
|
this.openSidePanel = params?.openSidePanel;
|
||||||
|
this.closeSidePanel = params?.closeSidePanel;
|
||||||
|
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, {
|
||||||
dataExplorerArea: Constants.Areas.ResourceTree,
|
dataExplorerArea: Constants.Areas.ResourceTree,
|
||||||
|
@ -3039,4 +3046,17 @@ export default class Explorer {
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public openDeleteCollectionConfirmationPane(): void {
|
||||||
|
this.isFeatureEnabled(Constants.Features.enableKOPanel)
|
||||||
|
? this.deleteCollectionConfirmationPane.open()
|
||||||
|
: this.openSidePanel(
|
||||||
|
"Delete Collection",
|
||||||
|
<DeleteCollectionConfirmationPanel
|
||||||
|
explorer={this}
|
||||||
|
closePanel={() => this.closeSidePanel()}
|
||||||
|
openNotificationConsole={() => this.expandConsole()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -114,6 +114,7 @@ export class NotificationConsoleComponent extends React.Component<
|
||||||
<div className="notificationConsoleContainer">
|
<div className="notificationConsoleContainer">
|
||||||
<div
|
<div
|
||||||
className="notificationConsoleHeader"
|
className="notificationConsoleHeader"
|
||||||
|
id="notificationConsoleHeader"
|
||||||
ref={this.setElememntRef}
|
ref={this.setElememntRef}
|
||||||
onClick={(event: React.MouseEvent<HTMLDivElement>) => this.expandCollapseConsole()}
|
onClick={(event: React.MouseEvent<HTMLDivElement>) => this.expandCollapseConsole()}
|
||||||
onKeyDown={(event: React.KeyboardEvent<HTMLDivElement>) => this.onExpandCollapseKeyPress(event)}
|
onKeyDown={(event: React.KeyboardEvent<HTMLDivElement>) => this.onExpandCollapseKeyPress(event)}
|
||||||
|
|
|
@ -6,6 +6,7 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="notificationConsoleHeader"
|
className="notificationConsoleHeader"
|
||||||
|
id="notificationConsoleHeader"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
onKeyDown={[Function]}
|
onKeyDown={[Function]}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
|
@ -169,6 +170,7 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="notificationConsoleHeader"
|
className="notificationConsoleHeader"
|
||||||
|
id="notificationConsoleHeader"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
onKeyDown={[Function]}
|
onKeyDown={[Function]}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
|
|
|
@ -1,142 +0,0 @@
|
||||||
jest.mock("../../Common/dataAccess/deleteCollection");
|
|
||||||
import * as ko from "knockout";
|
|
||||||
import * as sinon from "sinon";
|
|
||||||
import Q from "q";
|
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
|
||||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
|
||||||
import DeleteCollectionConfirmationPane from "./DeleteCollectionConfirmationPane";
|
|
||||||
import DeleteFeedback from "../../Common/DeleteFeedback";
|
|
||||||
import Explorer from "../Explorer";
|
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
|
||||||
import { TreeNode } from "../../Contracts/ViewModels";
|
|
||||||
import { deleteCollection } from "../../Common/dataAccess/deleteCollection";
|
|
||||||
|
|
||||||
describe("Delete Collection Confirmation Pane", () => {
|
|
||||||
describe("Explorer.isLastCollection()", () => {
|
|
||||||
let explorer: Explorer;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
explorer = new Explorer();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should be true if 1 database and 1 collection", () => {
|
|
||||||
let database = {} as ViewModels.Database;
|
|
||||||
database.collections = ko.observableArray<ViewModels.Collection>([{} as ViewModels.Collection]);
|
|
||||||
explorer.databases = ko.observableArray<ViewModels.Database>([database]);
|
|
||||||
expect(explorer.isLastCollection()).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should be false if if 1 database and 2 collection", () => {
|
|
||||||
let database = {} as ViewModels.Database;
|
|
||||||
database.collections = ko.observableArray<ViewModels.Collection>([
|
|
||||||
{} as ViewModels.Collection,
|
|
||||||
{} as ViewModels.Collection,
|
|
||||||
]);
|
|
||||||
explorer.databases = ko.observableArray<ViewModels.Database>([database]);
|
|
||||||
expect(explorer.isLastCollection()).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should be false if 2 database and 1 collection each", () => {
|
|
||||||
let database = {} as ViewModels.Database;
|
|
||||||
database.collections = ko.observableArray<ViewModels.Collection>([{} as ViewModels.Collection]);
|
|
||||||
let database2 = {} as ViewModels.Database;
|
|
||||||
database2.collections = ko.observableArray<ViewModels.Collection>([{} as ViewModels.Collection]);
|
|
||||||
explorer.databases = ko.observableArray<ViewModels.Database>([database, database2]);
|
|
||||||
expect(explorer.isLastCollection()).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should be false if 0 databases", () => {
|
|
||||||
let database = {} as ViewModels.Database;
|
|
||||||
explorer.databases = ko.observableArray<ViewModels.Database>();
|
|
||||||
database.collections = ko.observableArray<ViewModels.Collection>();
|
|
||||||
expect(explorer.isLastCollection()).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("shouldRecordFeedback()", () => {
|
|
||||||
it("should return true if last collection and database does not have shared throughput else false", () => {
|
|
||||||
let fakeExplorer = new Explorer();
|
|
||||||
fakeExplorer.refreshAllDatabases = () => Q.resolve();
|
|
||||||
|
|
||||||
let pane = new DeleteCollectionConfirmationPane({
|
|
||||||
id: "deletecollectionconfirmationpane",
|
|
||||||
visible: ko.observable<boolean>(false),
|
|
||||||
container: fakeExplorer,
|
|
||||||
});
|
|
||||||
|
|
||||||
fakeExplorer.isLastCollection = () => true;
|
|
||||||
fakeExplorer.isSelectedDatabaseShared = () => false;
|
|
||||||
expect(pane.shouldRecordFeedback()).toBe(true);
|
|
||||||
|
|
||||||
fakeExplorer.isLastCollection = () => true;
|
|
||||||
fakeExplorer.isSelectedDatabaseShared = () => true;
|
|
||||||
expect(pane.shouldRecordFeedback()).toBe(false);
|
|
||||||
|
|
||||||
fakeExplorer.isLastCollection = () => false;
|
|
||||||
fakeExplorer.isSelectedDatabaseShared = () => false;
|
|
||||||
expect(pane.shouldRecordFeedback()).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("submit()", () => {
|
|
||||||
let telemetryProcessorSpy: sinon.SinonSpy;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
(deleteCollection as jest.Mock).mockResolvedValue(undefined);
|
|
||||||
telemetryProcessorSpy = sinon.spy(TelemetryProcessor, "trace");
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
telemetryProcessorSpy.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("it should log feedback if last collection and database is not shared", () => {
|
|
||||||
let selectedCollectionId = "testCol";
|
|
||||||
let fakeExplorer = {} as Explorer;
|
|
||||||
fakeExplorer.findSelectedCollection = () => {
|
|
||||||
return {
|
|
||||||
id: ko.observable<string>(selectedCollectionId),
|
|
||||||
rid: "test",
|
|
||||||
} as ViewModels.Collection;
|
|
||||||
};
|
|
||||||
fakeExplorer.selectedCollectionId = ko.computed<string>(() => selectedCollectionId);
|
|
||||||
fakeExplorer.isSelectedDatabaseShared = () => false;
|
|
||||||
const SubscriptionId = "testId";
|
|
||||||
const AccountName = "testAccount";
|
|
||||||
fakeExplorer.databaseAccount = ko.observable<DataModels.DatabaseAccount>({
|
|
||||||
id: SubscriptionId,
|
|
||||||
name: AccountName,
|
|
||||||
} as DataModels.DatabaseAccount);
|
|
||||||
|
|
||||||
fakeExplorer.defaultExperience = ko.observable<string>("DocumentDB");
|
|
||||||
fakeExplorer.isPreferredApiCassandra = ko.computed(() => {
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
fakeExplorer.selectedNode = ko.observable<TreeNode>();
|
|
||||||
fakeExplorer.isLastCollection = () => true;
|
|
||||||
fakeExplorer.isSelectedDatabaseShared = () => false;
|
|
||||||
fakeExplorer.refreshAllDatabases = () => Q.resolve();
|
|
||||||
|
|
||||||
let pane = new DeleteCollectionConfirmationPane({
|
|
||||||
id: "deletecollectionconfirmationpane",
|
|
||||||
visible: ko.observable<boolean>(false),
|
|
||||||
container: fakeExplorer as any,
|
|
||||||
});
|
|
||||||
pane.collectionIdConfirmation = ko.observable<string>(selectedCollectionId);
|
|
||||||
const Feedback = "my feedback";
|
|
||||||
pane.containerDeleteFeedback(Feedback);
|
|
||||||
|
|
||||||
return pane.submit().then(() => {
|
|
||||||
expect(telemetryProcessorSpy.called).toBe(true);
|
|
||||||
let deleteFeedback = new DeleteFeedback(SubscriptionId, AccountName, DataModels.ApiKind.SQL, Feedback);
|
|
||||||
expect(
|
|
||||||
telemetryProcessorSpy.calledWith(Action.DeleteCollection, ActionModifiers.Mark, {
|
|
||||||
message: JSON.stringify(deleteFeedback, Object.getOwnPropertyNames(deleteFeedback)),
|
|
||||||
})
|
|
||||||
).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
jest.mock("../../Common/dataAccess/deleteCollection");
|
||||||
|
jest.mock("../../Shared/Telemetry/TelemetryProcessor");
|
||||||
|
import * as ko from "knockout";
|
||||||
|
import { ApiKind, DatabaseAccount } from "../../Contracts/DataModels";
|
||||||
|
import { Collection, Database } from "../../Contracts/ViewModels";
|
||||||
|
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
|
import { mount, ReactWrapper, shallow } from "enzyme";
|
||||||
|
import React from "react";
|
||||||
|
import DeleteFeedback from "../../Common/DeleteFeedback";
|
||||||
|
import Explorer from "../Explorer";
|
||||||
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
import { TreeNode } from "../../Contracts/ViewModels";
|
||||||
|
import { deleteCollection } from "../../Common/dataAccess/deleteCollection";
|
||||||
|
import { DeleteCollectionConfirmationPanel } from "./DeleteCollectionConfirmationPanel";
|
||||||
|
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||||
|
import { updateUserContext } from "../../UserContext";
|
||||||
|
|
||||||
|
describe("Delete Collection Confirmation Pane", () => {
|
||||||
|
describe("Explorer.isLastCollection()", () => {
|
||||||
|
let explorer: Explorer;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
explorer = new Explorer();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be true if 1 database and 1 collection", () => {
|
||||||
|
const database = {} as Database;
|
||||||
|
database.collections = ko.observableArray<Collection>([{} as Collection]);
|
||||||
|
explorer.databases = ko.observableArray<Database>([database]);
|
||||||
|
expect(explorer.isLastCollection()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be false if if 1 database and 2 collection", () => {
|
||||||
|
const database = {} as Database;
|
||||||
|
database.collections = ko.observableArray<Collection>([{} as Collection, {} as Collection]);
|
||||||
|
explorer.databases = ko.observableArray<Database>([database]);
|
||||||
|
expect(explorer.isLastCollection()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be false if 2 database and 1 collection each", () => {
|
||||||
|
const database = {} as Database;
|
||||||
|
database.collections = ko.observableArray<Collection>([{} as Collection]);
|
||||||
|
const database2 = {} as Database;
|
||||||
|
database2.collections = ko.observableArray<Collection>([{} as Collection]);
|
||||||
|
explorer.databases = ko.observableArray<Database>([database, database2]);
|
||||||
|
expect(explorer.isLastCollection()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be false if 0 databases", () => {
|
||||||
|
const database = {} as Database;
|
||||||
|
explorer.databases = ko.observableArray<Database>();
|
||||||
|
database.collections = ko.observableArray<Collection>();
|
||||||
|
expect(explorer.isLastCollection()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("shouldRecordFeedback()", () => {
|
||||||
|
it("should return true if last collection and database does not have shared throughput else false", () => {
|
||||||
|
const fakeExplorer = new Explorer();
|
||||||
|
fakeExplorer.refreshAllDatabases = () => undefined;
|
||||||
|
fakeExplorer.isLastCollection = () => true;
|
||||||
|
fakeExplorer.isSelectedDatabaseShared = () => false;
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
explorer: fakeExplorer,
|
||||||
|
closePanel: (): void => undefined,
|
||||||
|
openNotificationConsole: (): void => undefined,
|
||||||
|
};
|
||||||
|
const wrapper = shallow(<DeleteCollectionConfirmationPanel {...props} />);
|
||||||
|
expect(wrapper.exists(".deleteCollectionFeedback")).toBe(true);
|
||||||
|
|
||||||
|
props.explorer.isLastCollection = () => true;
|
||||||
|
props.explorer.isSelectedDatabaseShared = () => true;
|
||||||
|
wrapper.setProps(props);
|
||||||
|
expect(wrapper.exists(".deleteCollectionFeedback")).toBe(false);
|
||||||
|
|
||||||
|
props.explorer.isLastCollection = () => false;
|
||||||
|
props.explorer.isSelectedDatabaseShared = () => false;
|
||||||
|
wrapper.setProps(props);
|
||||||
|
expect(wrapper.exists(".deleteCollectionFeedback")).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("submit()", () => {
|
||||||
|
let wrapper: ReactWrapper;
|
||||||
|
const selectedCollectionId = "testCol";
|
||||||
|
const databaseId = "testDatabase";
|
||||||
|
const fakeExplorer = {} as Explorer;
|
||||||
|
fakeExplorer.findSelectedCollection = () => {
|
||||||
|
return {
|
||||||
|
id: ko.observable<string>(selectedCollectionId),
|
||||||
|
databaseId,
|
||||||
|
rid: "test",
|
||||||
|
} as Collection;
|
||||||
|
};
|
||||||
|
fakeExplorer.selectedCollectionId = ko.computed<string>(() => selectedCollectionId);
|
||||||
|
fakeExplorer.selectedNode = ko.observable<TreeNode>();
|
||||||
|
fakeExplorer.refreshAllDatabases = () => undefined;
|
||||||
|
fakeExplorer.isLastCollection = () => true;
|
||||||
|
fakeExplorer.isSelectedDatabaseShared = () => false;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
updateUserContext({
|
||||||
|
databaseAccount: {
|
||||||
|
name: "testDatabaseAccountName",
|
||||||
|
properties: {
|
||||||
|
cassandraEndpoint: "testEndpoint",
|
||||||
|
},
|
||||||
|
id: "testDatabaseAccountId",
|
||||||
|
} as DatabaseAccount,
|
||||||
|
defaultExperience: DefaultAccountExperienceType.DocumentDB,
|
||||||
|
});
|
||||||
|
(deleteCollection as jest.Mock).mockResolvedValue(undefined);
|
||||||
|
(TelemetryProcessor.trace as jest.Mock).mockReturnValue(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const props = {
|
||||||
|
explorer: fakeExplorer,
|
||||||
|
closePanel: (): void => undefined,
|
||||||
|
openNotificationConsole: (): void => undefined,
|
||||||
|
};
|
||||||
|
wrapper = mount(<DeleteCollectionConfirmationPanel {...props} />);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call delete collection", () => {
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
|
||||||
|
expect(wrapper.exists("#confirmCollectionId")).toBe(true);
|
||||||
|
wrapper
|
||||||
|
.find("#confirmCollectionId")
|
||||||
|
.hostNodes()
|
||||||
|
.simulate("change", { target: { value: selectedCollectionId } });
|
||||||
|
|
||||||
|
expect(wrapper.exists("#sidePanelOkButton")).toBe(true);
|
||||||
|
wrapper.find("#sidePanelOkButton").hostNodes().simulate("click");
|
||||||
|
expect(deleteCollection).toHaveBeenCalledWith(databaseId, selectedCollectionId);
|
||||||
|
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should record feedback", async () => {
|
||||||
|
expect(wrapper.exists("#confirmCollectionId")).toBe(true);
|
||||||
|
wrapper
|
||||||
|
.find("#confirmCollectionId")
|
||||||
|
.hostNodes()
|
||||||
|
.simulate("change", { target: { value: selectedCollectionId } });
|
||||||
|
|
||||||
|
expect(wrapper.exists("#deleteCollectionFeedbackInput")).toBe(true);
|
||||||
|
const feedbackText = "Test delete collection feedback text";
|
||||||
|
wrapper
|
||||||
|
.find("#deleteCollectionFeedbackInput")
|
||||||
|
.hostNodes()
|
||||||
|
.simulate("change", { target: { value: feedbackText } });
|
||||||
|
|
||||||
|
expect(wrapper.exists("#sidePanelOkButton")).toBe(true);
|
||||||
|
wrapper.find("#sidePanelOkButton").hostNodes().simulate("click");
|
||||||
|
expect(deleteCollection).toHaveBeenCalledWith(databaseId, selectedCollectionId);
|
||||||
|
|
||||||
|
const deleteFeedback = new DeleteFeedback(
|
||||||
|
"testDatabaseAccountId",
|
||||||
|
"testDatabaseAccountName",
|
||||||
|
ApiKind.SQL,
|
||||||
|
feedbackText
|
||||||
|
);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
|
expect(TelemetryProcessor.trace).toHaveBeenCalledWith(Action.DeleteCollection, ActionModifiers.Mark, {
|
||||||
|
message: JSON.stringify(deleteFeedback, Object.getOwnPropertyNames(deleteFeedback)),
|
||||||
|
});
|
||||||
|
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,186 @@
|
||||||
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
import * as React from "react";
|
||||||
|
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
|
import { PanelFooterComponent } from "./PanelFooterComponent";
|
||||||
|
import { Collection } from "../../Contracts/ViewModels";
|
||||||
|
import { Text, TextField } from "office-ui-fabric-react";
|
||||||
|
import { userContext } from "../../UserContext";
|
||||||
|
import { Areas } from "../../Common/Constants";
|
||||||
|
import { deleteCollection } from "../../Common/dataAccess/deleteCollection";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||||
|
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
|
||||||
|
import { PanelErrorComponent, PanelErrorProps } from "./PanelErrorComponent";
|
||||||
|
import DeleteFeedback from "../../Common/DeleteFeedback";
|
||||||
|
import Explorer from "../Explorer";
|
||||||
|
import LoadingIndicator_3Squares from "../../../images/LoadingIndicator_3Squares.gif";
|
||||||
|
|
||||||
|
export interface DeleteCollectionConfirmationPanelProps {
|
||||||
|
explorer: Explorer;
|
||||||
|
closePanel: () => void;
|
||||||
|
openNotificationConsole: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeleteCollectionConfirmationPanelState {
|
||||||
|
formError: string;
|
||||||
|
isExecuting: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DeleteCollectionConfirmationPanel extends React.Component<
|
||||||
|
DeleteCollectionConfirmationPanelProps,
|
||||||
|
DeleteCollectionConfirmationPanelState
|
||||||
|
> {
|
||||||
|
private inputCollectionName: string;
|
||||||
|
private deleteCollectionFeedback: string;
|
||||||
|
|
||||||
|
constructor(props: DeleteCollectionConfirmationPanelProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
formError: "",
|
||||||
|
isExecuting: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div className="panelContentContainer">
|
||||||
|
<PanelErrorComponent {...this.getPanelErrorProps()} />
|
||||||
|
<div className="panelMainContent">
|
||||||
|
<div className="confirmDeleteInput">
|
||||||
|
<span className="mandatoryStar">* </span>
|
||||||
|
<Text variant="small">Confirm by typing the collection id</Text>
|
||||||
|
<TextField
|
||||||
|
id="confirmCollectionId"
|
||||||
|
autoFocus
|
||||||
|
styles={{ fieldGroup: { width: 300 } }}
|
||||||
|
onChange={(event, newInput?: string) => {
|
||||||
|
this.inputCollectionName = newInput;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{this.shouldRecordFeedback() && (
|
||||||
|
<div className="deleteCollectionFeedback">
|
||||||
|
<Text variant="small" block>
|
||||||
|
Help us improve Azure Cosmos DB!
|
||||||
|
</Text>
|
||||||
|
<Text variant="small" block>
|
||||||
|
What is the reason why you are deleting this container?
|
||||||
|
</Text>
|
||||||
|
<TextField
|
||||||
|
id="deleteCollectionFeedbackInput"
|
||||||
|
styles={{ fieldGroup: { width: 300 } }}
|
||||||
|
multiline
|
||||||
|
rows={3}
|
||||||
|
onChange={(event, newInput?: string) => {
|
||||||
|
this.deleteCollectionFeedback = newInput;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<PanelFooterComponent buttonLabel="OK" onOKButtonClicked={() => this.submit()} />
|
||||||
|
<div className="dataExplorerLoaderContainer dataExplorerPaneLoaderContainer" hidden={!this.state.isExecuting}>
|
||||||
|
<img className="dataExplorerLoader" src={LoadingIndicator_3Squares} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPanelErrorProps(): PanelErrorProps {
|
||||||
|
if (this.state.formError) {
|
||||||
|
return {
|
||||||
|
isWarning: false,
|
||||||
|
message: this.state.formError,
|
||||||
|
showErrorDetails: true,
|
||||||
|
openNotificationConsole: this.props.openNotificationConsole,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isWarning: true,
|
||||||
|
showErrorDetails: false,
|
||||||
|
message:
|
||||||
|
"Warning! The action you are about to take cannot be undone. Continuing will permanently delete this resource and all of its children resources.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private shouldRecordFeedback(): boolean {
|
||||||
|
return this.props.explorer.isLastCollection() && !this.props.explorer.isSelectedDatabaseShared();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async submit(): Promise<void> {
|
||||||
|
const collection = this.props.explorer.findSelectedCollection();
|
||||||
|
|
||||||
|
if (!collection || this.inputCollectionName !== collection.id()) {
|
||||||
|
const errorMessage = "Input collection name does not match the selected collection";
|
||||||
|
this.setState({ formError: errorMessage });
|
||||||
|
NotificationConsoleUtils.logConsoleError(`Error while deleting collection ${collection.id()}: ${errorMessage}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ formError: "", isExecuting: true });
|
||||||
|
|
||||||
|
const startKey: number = TelemetryProcessor.traceStart(Action.DeleteCollection, {
|
||||||
|
databaseAccountName: userContext.databaseAccount?.name,
|
||||||
|
defaultExperience: userContext.defaultExperience,
|
||||||
|
collectionId: collection.id(),
|
||||||
|
dataExplorerArea: Areas.ContextualPane,
|
||||||
|
paneTitle: "Delete Collection",
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await deleteCollection(collection.databaseId, collection.id());
|
||||||
|
|
||||||
|
this.setState({ isExecuting: false });
|
||||||
|
this.props.explorer.selectedNode(collection.database);
|
||||||
|
this.props.explorer.tabsManager?.closeTabsByComparator(
|
||||||
|
(tab) => tab.node?.id() === collection.id() && (tab.node as Collection).databaseId === collection.databaseId
|
||||||
|
);
|
||||||
|
this.props.explorer.refreshAllDatabases();
|
||||||
|
|
||||||
|
TelemetryProcessor.traceSuccess(
|
||||||
|
Action.DeleteCollection,
|
||||||
|
{
|
||||||
|
databaseAccountName: userContext.databaseAccount?.name,
|
||||||
|
defaultExperience: userContext.defaultExperience,
|
||||||
|
collectionId: collection.id(),
|
||||||
|
dataExplorerArea: Areas.ContextualPane,
|
||||||
|
paneTitle: "Delete Collection",
|
||||||
|
},
|
||||||
|
startKey
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.shouldRecordFeedback()) {
|
||||||
|
const deleteFeedback = new DeleteFeedback(
|
||||||
|
userContext.databaseAccount?.id,
|
||||||
|
userContext.databaseAccount?.name,
|
||||||
|
DefaultExperienceUtility.getApiKindFromDefaultExperience(userContext.defaultExperience),
|
||||||
|
this.deleteCollectionFeedback
|
||||||
|
);
|
||||||
|
|
||||||
|
TelemetryProcessor.trace(Action.DeleteCollection, ActionModifiers.Mark, {
|
||||||
|
message: JSON.stringify(deleteFeedback, Object.getOwnPropertyNames(deleteFeedback)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.closePanel();
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = getErrorMessage(error);
|
||||||
|
this.setState({ formError: errorMessage, isExecuting: false });
|
||||||
|
TelemetryProcessor.traceFailure(
|
||||||
|
Action.DeleteCollection,
|
||||||
|
{
|
||||||
|
databaseAccountName: userContext.databaseAccount?.name,
|
||||||
|
defaultExperience: userContext.defaultExperience,
|
||||||
|
collectionId: collection.id(),
|
||||||
|
dataExplorerArea: Areas.ContextualPane,
|
||||||
|
paneTitle: "Delete Collection",
|
||||||
|
error: errorMessage,
|
||||||
|
errorStack: getErrorStack(error),
|
||||||
|
},
|
||||||
|
startKey
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
@import "../../../less/Common/Constants";
|
||||||
|
|
||||||
|
.panelContentContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.panelMainContent {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.panelHeader {
|
||||||
|
color: @BaseDark;
|
||||||
|
font-size: @largeFontSize;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panelWarningErrorContainer {
|
||||||
|
background-color: @BaseLow;
|
||||||
|
padding: @DefaultSpace;
|
||||||
|
display: inline-flex;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
.panelWarningIcon {
|
||||||
|
font-size: @WarningErrorIconSize;
|
||||||
|
width: @WarningErrorIconSize;
|
||||||
|
margin: auto 0 auto @SmallSpace;
|
||||||
|
color: @WarningIconColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panelErrorIcon {
|
||||||
|
font-size: @WarningErrorIconSize;
|
||||||
|
width: @WarningErrorIconSize;
|
||||||
|
margin: auto 0 auto @SmallSpace;
|
||||||
|
color: @ErrorIconColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panelWarningErrorDetailsLinkContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-left: @MediumSpace;
|
||||||
|
|
||||||
|
.paneErrorLink {
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: @mediumFontSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.panelFooter button {
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleteCollectionFeedback {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
import React from "react";
|
||||||
|
import { shallow } from "enzyme";
|
||||||
|
import { PanelContainerComponent, PanelContainerProps } from "./PanelContainerComponent";
|
||||||
|
|
||||||
|
describe("PaneContainerComponent test", () => {
|
||||||
|
it("should render with panel content and header", () => {
|
||||||
|
const panelContainerProps: PanelContainerProps = {
|
||||||
|
headerText: "test",
|
||||||
|
panelContent: <div></div>,
|
||||||
|
isOpen: true,
|
||||||
|
isConsoleExpanded: false,
|
||||||
|
closePanel: undefined,
|
||||||
|
};
|
||||||
|
const wrapper = shallow(<PanelContainerComponent {...panelContainerProps} />);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render nothing if content is undefined", () => {
|
||||||
|
const panelContainerProps: PanelContainerProps = {
|
||||||
|
headerText: "test",
|
||||||
|
panelContent: undefined,
|
||||||
|
isOpen: true,
|
||||||
|
isConsoleExpanded: false,
|
||||||
|
closePanel: undefined,
|
||||||
|
};
|
||||||
|
const wrapper = shallow(<PanelContainerComponent {...panelContainerProps} />);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be resize if notification console is expanded", () => {
|
||||||
|
const panelContainerProps: PanelContainerProps = {
|
||||||
|
headerText: "test",
|
||||||
|
panelContent: <div></div>,
|
||||||
|
isOpen: true,
|
||||||
|
isConsoleExpanded: true,
|
||||||
|
closePanel: undefined,
|
||||||
|
};
|
||||||
|
const wrapper = shallow(<PanelContainerComponent {...panelContainerProps} />);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,58 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { Panel, PanelType } from "office-ui-fabric-react";
|
||||||
|
|
||||||
|
export interface PanelContainerProps {
|
||||||
|
headerText: string;
|
||||||
|
panelContent: JSX.Element;
|
||||||
|
isConsoleExpanded: boolean;
|
||||||
|
isOpen: boolean;
|
||||||
|
closePanel: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PanelContainerComponent extends React.Component<PanelContainerProps> {
|
||||||
|
private static readonly consoleHeaderHeight = 32;
|
||||||
|
private static readonly consoleContentHeight = 220;
|
||||||
|
|
||||||
|
render(): JSX.Element {
|
||||||
|
if (!this.props.panelContent) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Panel
|
||||||
|
headerText={this.props.headerText}
|
||||||
|
isOpen={this.props.isOpen}
|
||||||
|
onDismiss={this.onDissmiss}
|
||||||
|
isLightDismiss
|
||||||
|
type={PanelType.custom}
|
||||||
|
closeButtonAriaLabel="Close"
|
||||||
|
customWidth="440px"
|
||||||
|
headerClassName="panelHeader"
|
||||||
|
styles={{
|
||||||
|
navigation: { borderBottom: "1px solid #cccccc" },
|
||||||
|
content: { padding: "24px 34px 20px 34px", height: "100%" },
|
||||||
|
scrollableContent: { height: "100%" },
|
||||||
|
}}
|
||||||
|
style={{ height: this.getPanelHeight() }}
|
||||||
|
>
|
||||||
|
{this.props.panelContent}
|
||||||
|
</Panel>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onDissmiss = (ev?: React.SyntheticEvent<HTMLElement>): void => {
|
||||||
|
if ((ev.target as HTMLElement).id === "notificationConsoleHeader") {
|
||||||
|
ev.preventDefault();
|
||||||
|
} else {
|
||||||
|
this.props.closePanel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private getPanelHeight = (): string => {
|
||||||
|
const consoleHeight = this.props.isConsoleExpanded
|
||||||
|
? PanelContainerComponent.consoleContentHeight + PanelContainerComponent.consoleHeaderHeight
|
||||||
|
: PanelContainerComponent.consoleHeaderHeight;
|
||||||
|
const panelHeight = window.innerHeight - consoleHeight;
|
||||||
|
return panelHeight + "px";
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Icon, Text } from "office-ui-fabric-react";
|
||||||
|
|
||||||
|
export interface PanelErrorProps {
|
||||||
|
message: string;
|
||||||
|
isWarning: boolean;
|
||||||
|
showErrorDetails: boolean;
|
||||||
|
openNotificationConsole?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PanelErrorComponent: React.FunctionComponent<PanelErrorProps> = (props: PanelErrorProps): JSX.Element => (
|
||||||
|
<div className="panelWarningErrorContainer">
|
||||||
|
{props.isWarning ? (
|
||||||
|
<Icon iconName="WarningSolid" className="panelWarningIcon" />
|
||||||
|
) : (
|
||||||
|
<Icon iconName="StatusErrorFull" className="panelErrorIcon" />
|
||||||
|
)}
|
||||||
|
<span className="panelWarningErrorDetailsLinkContainer">
|
||||||
|
<Text className="panelWarningErrorMessage" variant="small">
|
||||||
|
{props.message}
|
||||||
|
</Text>
|
||||||
|
{props.showErrorDetails && (
|
||||||
|
<a className="paneErrorLink" role="link" onClick={props.openNotificationConsole}>
|
||||||
|
More details
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
|
@ -0,0 +1,15 @@
|
||||||
|
import React from "react";
|
||||||
|
import { PrimaryButton } from "office-ui-fabric-react";
|
||||||
|
|
||||||
|
export interface PanelFooterProps {
|
||||||
|
buttonLabel: string;
|
||||||
|
onOKButtonClicked: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PanelFooterComponent: React.FunctionComponent<PanelFooterProps> = (
|
||||||
|
props: PanelFooterProps
|
||||||
|
): JSX.Element => (
|
||||||
|
<div className="panelFooter">
|
||||||
|
<PrimaryButton id="sidePanelOkButton" text={props.buttonLabel} onClick={() => props.onOKButtonClicked()} />
|
||||||
|
</div>
|
||||||
|
);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,71 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`PaneContainerComponent test should be resize if notification console is expanded 1`] = `
|
||||||
|
<StyledPanelBase
|
||||||
|
closeButtonAriaLabel="Close"
|
||||||
|
customWidth="440px"
|
||||||
|
headerClassName="panelHeader"
|
||||||
|
headerText="test"
|
||||||
|
isLightDismiss={true}
|
||||||
|
isOpen={true}
|
||||||
|
onDismiss={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"height": "516px",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
styles={
|
||||||
|
Object {
|
||||||
|
"content": Object {
|
||||||
|
"height": "100%",
|
||||||
|
"padding": "24px 34px 20px 34px",
|
||||||
|
},
|
||||||
|
"navigation": Object {
|
||||||
|
"borderBottom": "1px solid #cccccc",
|
||||||
|
},
|
||||||
|
"scrollableContent": Object {
|
||||||
|
"height": "100%",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type={7}
|
||||||
|
>
|
||||||
|
<div />
|
||||||
|
</StyledPanelBase>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`PaneContainerComponent test should render nothing if content is undefined 1`] = `<Fragment />`;
|
||||||
|
|
||||||
|
exports[`PaneContainerComponent test should render with panel content and header 1`] = `
|
||||||
|
<StyledPanelBase
|
||||||
|
closeButtonAriaLabel="Close"
|
||||||
|
customWidth="440px"
|
||||||
|
headerClassName="panelHeader"
|
||||||
|
headerText="test"
|
||||||
|
isLightDismiss={true}
|
||||||
|
isOpen={true}
|
||||||
|
onDismiss={[Function]}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"height": "736px",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
styles={
|
||||||
|
Object {
|
||||||
|
"content": Object {
|
||||||
|
"height": "100%",
|
||||||
|
"padding": "24px 34px 20px 34px",
|
||||||
|
},
|
||||||
|
"navigation": Object {
|
||||||
|
"borderBottom": "1px solid #cccccc",
|
||||||
|
},
|
||||||
|
"scrollableContent": Object {
|
||||||
|
"height": "100%",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type={7}
|
||||||
|
>
|
||||||
|
<div />
|
||||||
|
</StyledPanelBase>
|
||||||
|
`;
|
|
@ -19,6 +19,7 @@ import { createDocument } from "../../Common/dataAccess/createDocument";
|
||||||
import { deleteDocument } from "../../Common/dataAccess/deleteDocument";
|
import { deleteDocument } from "../../Common/dataAccess/deleteDocument";
|
||||||
import { queryDocuments } from "../../Common/dataAccess/queryDocuments";
|
import { queryDocuments } from "../../Common/dataAccess/queryDocuments";
|
||||||
import { updateDocument } from "../../Common/dataAccess/updateDocument";
|
import { updateDocument } from "../../Common/dataAccess/updateDocument";
|
||||||
|
import { userContext } from "../../UserContext";
|
||||||
|
|
||||||
export interface CassandraTableKeys {
|
export interface CassandraTableKeys {
|
||||||
partitionKeys: CassandraTableKey[];
|
partitionKeys: CassandraTableKey[];
|
||||||
|
@ -345,7 +346,7 @@ export class CassandraAPIDataClient extends TableDataClient {
|
||||||
ConsoleDataType.InProgress,
|
ConsoleDataType.InProgress,
|
||||||
`Creating a new keyspace with query ${createKeyspaceQuery}`
|
`Creating a new keyspace with query ${createKeyspaceQuery}`
|
||||||
);
|
);
|
||||||
this.createOrDeleteQuery(cassandraEndpoint, resourceId, createKeyspaceQuery, explorer)
|
this.createOrDeleteQuery(cassandraEndpoint, resourceId, createKeyspaceQuery)
|
||||||
.then(
|
.then(
|
||||||
(data: any) => {
|
(data: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
|
@ -391,7 +392,7 @@ export class CassandraAPIDataClient extends TableDataClient {
|
||||||
ConsoleDataType.InProgress,
|
ConsoleDataType.InProgress,
|
||||||
`Creating a new table with query ${createTableQuery}`
|
`Creating a new table with query ${createTableQuery}`
|
||||||
);
|
);
|
||||||
this.createOrDeleteQuery(cassandraEndpoint, resourceId, createTableQuery, explorer)
|
this.createOrDeleteQuery(cassandraEndpoint, resourceId, createTableQuery)
|
||||||
.then(
|
.then(
|
||||||
(data: any) => {
|
(data: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
|
@ -516,12 +517,7 @@ export class CassandraAPIDataClient extends TableDataClient {
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createOrDeleteQuery(
|
private createOrDeleteQuery(cassandraEndpoint: string, resourceId: string, query: string): Q.Promise<any> {
|
||||||
cassandraEndpoint: string,
|
|
||||||
resourceId: string,
|
|
||||||
query: string,
|
|
||||||
explorer: Explorer
|
|
||||||
): Q.Promise<any> {
|
|
||||||
const deferred = Q.defer();
|
const deferred = Q.defer();
|
||||||
const authType = window.authType;
|
const authType = window.authType;
|
||||||
const apiEndpoint: string =
|
const apiEndpoint: string =
|
||||||
|
@ -531,7 +527,7 @@ export class CassandraAPIDataClient extends TableDataClient {
|
||||||
$.ajax(`${configContext.BACKEND_ENDPOINT}/${apiEndpoint}`, {
|
$.ajax(`${configContext.BACKEND_ENDPOINT}/${apiEndpoint}`, {
|
||||||
type: "POST",
|
type: "POST",
|
||||||
data: {
|
data: {
|
||||||
accountName: explorer.databaseAccount() && explorer.databaseAccount().name,
|
accountName: userContext.databaseAccount?.name,
|
||||||
cassandraEndpoint: this.trimCassandraEndpoint(cassandraEndpoint),
|
cassandraEndpoint: this.trimCassandraEndpoint(cassandraEndpoint),
|
||||||
resourceId: resourceId,
|
resourceId: resourceId,
|
||||||
query: query,
|
query: query,
|
||||||
|
|
|
@ -973,10 +973,6 @@ export default class Collection implements ViewModels.Collection {
|
||||||
this.uploadFiles(event.originalEvent.dataTransfer.files);
|
this.uploadFiles(event.originalEvent.dataTransfer.files);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onDeleteCollectionContextMenuClick(source: ViewModels.Collection, event: MouseEvent | KeyboardEvent) {
|
|
||||||
this.container.deleteCollectionConfirmationPane.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
public uploadFiles = (fileList: FileList): Promise<UploadDetails> => {
|
public uploadFiles = (fileList: FileList): Promise<UploadDetails> => {
|
||||||
// TODO: right now web worker is not working with AAD flow. Use main thread for upload for now until we have backend upload capability
|
// TODO: right now web worker is not working with AAD flow. Use main thread for upload for now until we have backend upload capability
|
||||||
if (configContext.platform === Platform.Hosted && window.authType === AuthType.AAD) {
|
if (configContext.platform === Platform.Hosted && window.authType === AuthType.AAD) {
|
||||||
|
|
15
src/Main.tsx
15
src/Main.tsx
|
@ -14,6 +14,7 @@ import "./Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.less";
|
||||||
import "./Explorer/Controls/DynamicList/DynamicListComponent.less";
|
import "./Explorer/Controls/DynamicList/DynamicListComponent.less";
|
||||||
import "./Explorer/Controls/JsonEditor/JsonEditorComponent.less";
|
import "./Explorer/Controls/JsonEditor/JsonEditorComponent.less";
|
||||||
import "./Explorer/Graph/GraphExplorerComponent/graphExplorer.less";
|
import "./Explorer/Graph/GraphExplorerComponent/graphExplorer.less";
|
||||||
|
import "./Explorer/Panes/PanelComponent.less";
|
||||||
import "../less/TableStyles/queryBuilder.less";
|
import "../less/TableStyles/queryBuilder.less";
|
||||||
import "../externals/jquery.dataTables.min.css";
|
import "../externals/jquery.dataTables.min.css";
|
||||||
import "../less/TableStyles/fulldatatables.less";
|
import "../less/TableStyles/fulldatatables.less";
|
||||||
|
@ -64,7 +65,9 @@ import arrowLeftImg from "../images/imgarrowlefticon.svg";
|
||||||
import { KOCommentEnd, KOCommentIfStart } from "./koComment";
|
import { KOCommentEnd, KOCommentIfStart } from "./koComment";
|
||||||
import { useConfig } from "./hooks/useConfig";
|
import { useConfig } from "./hooks/useConfig";
|
||||||
import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer";
|
import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer";
|
||||||
|
import { useSidePanel } from "./hooks/useSidePanel";
|
||||||
import { NotificationConsoleComponent } from "./Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
import { NotificationConsoleComponent } from "./Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
|
import { PanelContainerComponent } from "./Explorer/Panes/PanelContainerComponent";
|
||||||
|
|
||||||
initializeIcons();
|
initializeIcons();
|
||||||
|
|
||||||
|
@ -73,10 +76,15 @@ const App: React.FunctionComponent = () => {
|
||||||
const [notificationConsoleData, setNotificationConsoleData] = useState(undefined);
|
const [notificationConsoleData, setNotificationConsoleData] = useState(undefined);
|
||||||
//TODO: Refactor so we don't need to pass the id to remove a console data
|
//TODO: Refactor so we don't need to pass the id to remove a console data
|
||||||
const [inProgressConsoleDataIdToBeDeleted, setInProgressConsoleDataIdToBeDeleted] = useState("");
|
const [inProgressConsoleDataIdToBeDeleted, setInProgressConsoleDataIdToBeDeleted] = useState("");
|
||||||
|
|
||||||
|
const { isPanelOpen, panelContent, headerText, openSidePanel, closeSidePanel } = useSidePanel();
|
||||||
|
|
||||||
const explorerParams: ExplorerParams = {
|
const explorerParams: ExplorerParams = {
|
||||||
setIsNotificationConsoleExpanded,
|
setIsNotificationConsoleExpanded,
|
||||||
setNotificationConsoleData,
|
setNotificationConsoleData,
|
||||||
setInProgressConsoleDataIdToBeDeleted,
|
setInProgressConsoleDataIdToBeDeleted,
|
||||||
|
openSidePanel,
|
||||||
|
closeSidePanel,
|
||||||
};
|
};
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
useKnockoutExplorer(config, explorerParams);
|
useKnockoutExplorer(config, explorerParams);
|
||||||
|
@ -309,6 +317,13 @@ const App: React.FunctionComponent = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Global loader - End */}
|
{/* Global loader - End */}
|
||||||
|
<PanelContainerComponent
|
||||||
|
isOpen={isPanelOpen}
|
||||||
|
panelContent={panelContent}
|
||||||
|
headerText={headerText}
|
||||||
|
closePanel={closeSidePanel}
|
||||||
|
isConsoleExpanded={isNotificationConsoleExpanded}
|
||||||
|
/>
|
||||||
<div data-bind="react:uploadItemsPaneAdapter" />
|
<div data-bind="react:uploadItemsPaneAdapter" />
|
||||||
<div data-bind='component: { name: "add-database-pane", params: {data: addDatabasePane} }' />
|
<div data-bind='component: { name: "add-database-pane", params: {data: addDatabasePane} }' />
|
||||||
<div data-bind='component: { name: "add-collection-pane", params: { data: addCollectionPane} }' />
|
<div data-bind='component: { name: "add-collection-pane", params: { data: addCollectionPane} }' />
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
export interface SidePanelHooks {
|
||||||
|
isPanelOpen: boolean;
|
||||||
|
panelContent: JSX.Element;
|
||||||
|
headerText: string;
|
||||||
|
openSidePanel: (headerText: string, panelContent: JSX.Element) => void;
|
||||||
|
closeSidePanel: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSidePanel = (): SidePanelHooks => {
|
||||||
|
const [isPanelOpen, setIsPanelOpen] = useState<boolean>(false);
|
||||||
|
const [panelContent, setPanelContent] = useState<JSX.Element>();
|
||||||
|
const [headerText, setHeaderText] = useState<string>();
|
||||||
|
|
||||||
|
const openSidePanel = (headerText: string, panelContent: JSX.Element): void => {
|
||||||
|
setHeaderText(headerText);
|
||||||
|
setPanelContent(panelContent);
|
||||||
|
setIsPanelOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeSidePanel = (): void => {
|
||||||
|
setHeaderText("");
|
||||||
|
setPanelContent(undefined);
|
||||||
|
setIsPanelOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { isPanelOpen, panelContent, headerText, openSidePanel, closeSidePanel };
|
||||||
|
};
|
|
@ -82,12 +82,12 @@ describe("Collection Add and Delete Cassandra spec", () => {
|
||||||
await frame.click('span[class="treeComponentMenuItemLabel deleteCollectionMenuItemLabel"]');
|
await frame.click('span[class="treeComponentMenuItemLabel deleteCollectionMenuItemLabel"]');
|
||||||
|
|
||||||
// confirm delete container
|
// confirm delete container
|
||||||
await frame.waitFor('input[data-test="confirmCollectionId"]', { visible: true });
|
await frame.waitFor('input[id="confirmCollectionId"]', { visible: true });
|
||||||
await frame.type('input[data-test="confirmCollectionId"]', textId);
|
await frame.type('input[id="confirmCollectionId"]', textId);
|
||||||
|
|
||||||
// click delete
|
// click delete
|
||||||
await frame.waitFor('input[data-test="deleteCollection"]', { visible: true });
|
await frame.waitFor('button[id="sidePanelOkButton"]', { visible: true });
|
||||||
await frame.click('input[data-test="deleteCollection"]');
|
await frame.click('button[id="sidePanelOkButton"]');
|
||||||
await frame.waitFor(LOADING_STATE_DELAY);
|
await frame.waitFor(LOADING_STATE_DELAY);
|
||||||
await frame.waitForSelector('div[class="splashScreen"] > div[class="title"]', { visible: true });
|
await frame.waitForSelector('div[class="splashScreen"] > div[class="title"]', { visible: true });
|
||||||
|
|
||||||
|
|
|
@ -98,12 +98,12 @@ describe("Collection Add and Delete Mongo spec", () => {
|
||||||
await frame.click('span[class="treeComponentMenuItemLabel deleteCollectionMenuItemLabel"]');
|
await frame.click('span[class="treeComponentMenuItemLabel deleteCollectionMenuItemLabel"]');
|
||||||
|
|
||||||
// confirm delete container
|
// confirm delete container
|
||||||
await frame.waitFor('input[data-test="confirmCollectionId"]', { visible: true });
|
await frame.waitFor('input[id="confirmCollectionId"]', { visible: true });
|
||||||
await frame.type('input[data-test="confirmCollectionId"]', textId);
|
await frame.type('input[id="confirmCollectionId"]', textId);
|
||||||
|
|
||||||
// click delete
|
// click delete
|
||||||
await frame.waitFor('input[data-test="deleteCollection"]', { visible: true });
|
await frame.waitFor('button[id="sidePanelOkButton"]', { visible: true });
|
||||||
await frame.click('input[data-test="deleteCollection"]');
|
await frame.click('button[id="sidePanelOkButton"]');
|
||||||
await frame.waitFor(LOADING_STATE_DELAY);
|
await frame.waitFor(LOADING_STATE_DELAY);
|
||||||
await frame.waitForSelector('div[class="splashScreen"] > div[class="title"]', { visible: true });
|
await frame.waitForSelector('div[class="splashScreen"] > div[class="title"]', { visible: true });
|
||||||
|
|
||||||
|
|
|
@ -101,12 +101,12 @@ describe("Collection Add and Delete SQL spec", () => {
|
||||||
await frame.click('span[class="treeComponentMenuItemLabel deleteCollectionMenuItemLabel"]');
|
await frame.click('span[class="treeComponentMenuItemLabel deleteCollectionMenuItemLabel"]');
|
||||||
|
|
||||||
// confirm delete container
|
// confirm delete container
|
||||||
await frame.waitFor('input[data-test="confirmCollectionId"]', { visible: true });
|
await frame.waitFor('input[id="confirmCollectionId"]', { visible: true });
|
||||||
await frame.type('input[data-test="confirmCollectionId"]', textId);
|
await frame.type('input[id="confirmCollectionId"]', textId);
|
||||||
|
|
||||||
// click delete
|
// click delete
|
||||||
await frame.waitFor('input[data-test="deleteCollection"]', { visible: true });
|
await frame.waitFor('button[id="sidePanelOkButton"]', { visible: true });
|
||||||
await frame.click('input[data-test="deleteCollection"]');
|
await frame.click('button[id="sidePanelOkButton"]');
|
||||||
await frame.waitFor(LOADING_STATE_DELAY);
|
await frame.waitFor(LOADING_STATE_DELAY);
|
||||||
await frame.waitForSelector('div[class="splashScreen"] > div[class="title"]', { visible: true });
|
await frame.waitForSelector('div[class="splashScreen"] > div[class="title"]', { visible: true });
|
||||||
|
|
||||||
|
|
|
@ -68,12 +68,12 @@ describe("Collection Add and Delete Tables spec", () => {
|
||||||
await frame.click('span[class="treeComponentMenuItemLabel deleteCollectionMenuItemLabel"]');
|
await frame.click('span[class="treeComponentMenuItemLabel deleteCollectionMenuItemLabel"]');
|
||||||
|
|
||||||
// confirm delete container
|
// confirm delete container
|
||||||
await frame.waitFor('input[data-test="confirmCollectionId"]', { visible: true });
|
await frame.waitFor('input[id="confirmCollectionId"]', { visible: true });
|
||||||
await frame.type('input[data-test="confirmCollectionId"]', textId);
|
await frame.type('input[id="confirmCollectionId"]', textId);
|
||||||
|
|
||||||
// click delete
|
// click delete
|
||||||
await frame.waitFor('input[data-test="deleteCollection"]', { visible: true });
|
await frame.waitFor('button[id="sidePanelOkButton"]', { visible: true });
|
||||||
await frame.click('input[data-test="deleteCollection"]');
|
await frame.click('button[id="sidePanelOkButton"]');
|
||||||
await frame.waitFor(LOADING_STATE_DELAY);
|
await frame.waitFor(LOADING_STATE_DELAY);
|
||||||
await frame.waitForSelector('div[class="splashScreen"] > div[class="title"]', { visible: true });
|
await frame.waitForSelector('div[class="splashScreen"] > div[class="title"]', { visible: true });
|
||||||
await frame.waitFor(LOADING_STATE_DELAY);
|
await frame.waitFor(LOADING_STATE_DELAY);
|
||||||
|
|
Loading…
Reference in New Issue