mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2024-11-25 23:16:56 +00:00
Move notification console to react (#400)
This commit is contained in:
parent
59ec18cd9b
commit
bd4d8da065
@ -958,7 +958,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"isMongoIndexingEnabled": [Function],
|
"isMongoIndexingEnabled": [Function],
|
||||||
"isNotebookEnabled": [Function],
|
"isNotebookEnabled": [Function],
|
||||||
"isNotebooksEnabledForAccount": [Function],
|
"isNotebooksEnabledForAccount": [Function],
|
||||||
"isNotificationConsoleExpanded": [Function],
|
|
||||||
"isPreferredApiCassandra": [Function],
|
"isPreferredApiCassandra": [Function],
|
||||||
"isPreferredApiDocumentDB": [Function],
|
"isPreferredApiDocumentDB": [Function],
|
||||||
"isPreferredApiGraph": [Function],
|
"isPreferredApiGraph": [Function],
|
||||||
@ -1018,12 +1017,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"nonSystemDatabases": [Function],
|
"nonSystemDatabases": [Function],
|
||||||
"notebookBasePath": [Function],
|
"notebookBasePath": [Function],
|
||||||
"notebookServerInfo": [Function],
|
"notebookServerInfo": [Function],
|
||||||
"notificationConsoleComponentAdapter": NotificationConsoleComponentAdapter {
|
|
||||||
"consoleData": [Function],
|
|
||||||
"container": [Circular],
|
|
||||||
"parameters": [Function],
|
|
||||||
},
|
|
||||||
"notificationConsoleData": [Function],
|
|
||||||
"onRefreshDatabasesKeyPress": [Function],
|
"onRefreshDatabasesKeyPress": [Function],
|
||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"onSwitchToConnectionString": [Function],
|
"onSwitchToConnectionString": [Function],
|
||||||
@ -1129,6 +1122,9 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"selfServeType": [Function],
|
"selfServeType": [Function],
|
||||||
"serverId": [Function],
|
"serverId": [Function],
|
||||||
|
"setInProgressConsoleDataIdToBeDeleted": undefined,
|
||||||
|
"setIsNotificationConsoleExpanded": undefined,
|
||||||
|
"setNotificationConsoleData": undefined,
|
||||||
"settingsPane": SettingsPane {
|
"settingsPane": SettingsPane {
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"crossPartitionQueryEnabled": [Function],
|
"crossPartitionQueryEnabled": [Function],
|
||||||
@ -2241,7 +2237,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"isMongoIndexingEnabled": [Function],
|
"isMongoIndexingEnabled": [Function],
|
||||||
"isNotebookEnabled": [Function],
|
"isNotebookEnabled": [Function],
|
||||||
"isNotebooksEnabledForAccount": [Function],
|
"isNotebooksEnabledForAccount": [Function],
|
||||||
"isNotificationConsoleExpanded": [Function],
|
|
||||||
"isPreferredApiCassandra": [Function],
|
"isPreferredApiCassandra": [Function],
|
||||||
"isPreferredApiDocumentDB": [Function],
|
"isPreferredApiDocumentDB": [Function],
|
||||||
"isPreferredApiGraph": [Function],
|
"isPreferredApiGraph": [Function],
|
||||||
@ -2301,12 +2296,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"nonSystemDatabases": [Function],
|
"nonSystemDatabases": [Function],
|
||||||
"notebookBasePath": [Function],
|
"notebookBasePath": [Function],
|
||||||
"notebookServerInfo": [Function],
|
"notebookServerInfo": [Function],
|
||||||
"notificationConsoleComponentAdapter": NotificationConsoleComponentAdapter {
|
|
||||||
"consoleData": [Function],
|
|
||||||
"container": [Circular],
|
|
||||||
"parameters": [Function],
|
|
||||||
},
|
|
||||||
"notificationConsoleData": [Function],
|
|
||||||
"onRefreshDatabasesKeyPress": [Function],
|
"onRefreshDatabasesKeyPress": [Function],
|
||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"onSwitchToConnectionString": [Function],
|
"onSwitchToConnectionString": [Function],
|
||||||
@ -2412,6 +2401,9 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"selfServeType": [Function],
|
"selfServeType": [Function],
|
||||||
"serverId": [Function],
|
"serverId": [Function],
|
||||||
|
"setInProgressConsoleDataIdToBeDeleted": undefined,
|
||||||
|
"setIsNotificationConsoleExpanded": undefined,
|
||||||
|
"setNotificationConsoleData": undefined,
|
||||||
"settingsPane": SettingsPane {
|
"settingsPane": SettingsPane {
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"crossPartitionQueryEnabled": [Function],
|
"crossPartitionQueryEnabled": [Function],
|
||||||
@ -3537,7 +3529,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"isMongoIndexingEnabled": [Function],
|
"isMongoIndexingEnabled": [Function],
|
||||||
"isNotebookEnabled": [Function],
|
"isNotebookEnabled": [Function],
|
||||||
"isNotebooksEnabledForAccount": [Function],
|
"isNotebooksEnabledForAccount": [Function],
|
||||||
"isNotificationConsoleExpanded": [Function],
|
|
||||||
"isPreferredApiCassandra": [Function],
|
"isPreferredApiCassandra": [Function],
|
||||||
"isPreferredApiDocumentDB": [Function],
|
"isPreferredApiDocumentDB": [Function],
|
||||||
"isPreferredApiGraph": [Function],
|
"isPreferredApiGraph": [Function],
|
||||||
@ -3597,12 +3588,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"nonSystemDatabases": [Function],
|
"nonSystemDatabases": [Function],
|
||||||
"notebookBasePath": [Function],
|
"notebookBasePath": [Function],
|
||||||
"notebookServerInfo": [Function],
|
"notebookServerInfo": [Function],
|
||||||
"notificationConsoleComponentAdapter": NotificationConsoleComponentAdapter {
|
|
||||||
"consoleData": [Function],
|
|
||||||
"container": [Circular],
|
|
||||||
"parameters": [Function],
|
|
||||||
},
|
|
||||||
"notificationConsoleData": [Function],
|
|
||||||
"onRefreshDatabasesKeyPress": [Function],
|
"onRefreshDatabasesKeyPress": [Function],
|
||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"onSwitchToConnectionString": [Function],
|
"onSwitchToConnectionString": [Function],
|
||||||
@ -3708,6 +3693,9 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"selfServeType": [Function],
|
"selfServeType": [Function],
|
||||||
"serverId": [Function],
|
"serverId": [Function],
|
||||||
|
"setInProgressConsoleDataIdToBeDeleted": undefined,
|
||||||
|
"setIsNotificationConsoleExpanded": undefined,
|
||||||
|
"setNotificationConsoleData": undefined,
|
||||||
"settingsPane": SettingsPane {
|
"settingsPane": SettingsPane {
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"crossPartitionQueryEnabled": [Function],
|
"crossPartitionQueryEnabled": [Function],
|
||||||
@ -4820,7 +4808,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"isMongoIndexingEnabled": [Function],
|
"isMongoIndexingEnabled": [Function],
|
||||||
"isNotebookEnabled": [Function],
|
"isNotebookEnabled": [Function],
|
||||||
"isNotebooksEnabledForAccount": [Function],
|
"isNotebooksEnabledForAccount": [Function],
|
||||||
"isNotificationConsoleExpanded": [Function],
|
|
||||||
"isPreferredApiCassandra": [Function],
|
"isPreferredApiCassandra": [Function],
|
||||||
"isPreferredApiDocumentDB": [Function],
|
"isPreferredApiDocumentDB": [Function],
|
||||||
"isPreferredApiGraph": [Function],
|
"isPreferredApiGraph": [Function],
|
||||||
@ -4880,12 +4867,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"nonSystemDatabases": [Function],
|
"nonSystemDatabases": [Function],
|
||||||
"notebookBasePath": [Function],
|
"notebookBasePath": [Function],
|
||||||
"notebookServerInfo": [Function],
|
"notebookServerInfo": [Function],
|
||||||
"notificationConsoleComponentAdapter": NotificationConsoleComponentAdapter {
|
|
||||||
"consoleData": [Function],
|
|
||||||
"container": [Circular],
|
|
||||||
"parameters": [Function],
|
|
||||||
},
|
|
||||||
"notificationConsoleData": [Function],
|
|
||||||
"onRefreshDatabasesKeyPress": [Function],
|
"onRefreshDatabasesKeyPress": [Function],
|
||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"onSwitchToConnectionString": [Function],
|
"onSwitchToConnectionString": [Function],
|
||||||
@ -4991,6 +4972,9 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"selfServeType": [Function],
|
"selfServeType": [Function],
|
||||||
"serverId": [Function],
|
"serverId": [Function],
|
||||||
|
"setInProgressConsoleDataIdToBeDeleted": undefined,
|
||||||
|
"setIsNotificationConsoleExpanded": undefined,
|
||||||
|
"setNotificationConsoleData": undefined,
|
||||||
"settingsPane": SettingsPane {
|
"settingsPane": SettingsPane {
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"crossPartitionQueryEnabled": [Function],
|
"crossPartitionQueryEnabled": [Function],
|
||||||
|
@ -55,7 +55,6 @@ import { sendMessage, sendCachedDataMessage, handleCachedDataMessage } from "../
|
|||||||
import { NotebookContentItem, NotebookContentItemType } from "./Notebook/NotebookContentItem";
|
import { NotebookContentItem, NotebookContentItemType } from "./Notebook/NotebookContentItem";
|
||||||
import { NotebookUtil } from "./Notebook/NotebookUtil";
|
import { NotebookUtil } from "./Notebook/NotebookUtil";
|
||||||
import { NotebookWorkspaceManager } from "../NotebookWorkspaceManager/NotebookWorkspaceManager";
|
import { NotebookWorkspaceManager } from "../NotebookWorkspaceManager/NotebookWorkspaceManager";
|
||||||
import { NotificationConsoleComponentAdapter } from "./Menus/NotificationConsole/NotificationConsoleComponentAdapter";
|
|
||||||
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
||||||
import { QueriesClient } from "../Common/QueriesClient";
|
import { QueriesClient } from "../Common/QueriesClient";
|
||||||
import { QuerySelectPane } from "./Panes/Tables/QuerySelectPane";
|
import { QuerySelectPane } from "./Panes/Tables/QuerySelectPane";
|
||||||
@ -107,6 +106,12 @@ interface AdHocAccessData {
|
|||||||
readUrl: string;
|
readUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ExplorerParams {
|
||||||
|
setIsNotificationConsoleExpanded: (isExpanded: boolean) => void;
|
||||||
|
setNotificationConsoleData: (consoleData: ConsoleData) => void;
|
||||||
|
setInProgressConsoleDataIdToBeDeleted: (id: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
export default class Explorer {
|
export default class Explorer {
|
||||||
public flight: ko.Observable<string> = ko.observable<string>(
|
public flight: ko.Observable<string> = ko.observable<string>(
|
||||||
SharedConstants.CollectionCreation.DefaultAddCollectionDefaultFlight
|
SharedConstants.CollectionCreation.DefaultAddCollectionDefaultFlight
|
||||||
@ -146,8 +151,9 @@ export default class Explorer {
|
|||||||
public mostRecentActivity: MostRecentActivity.MostRecentActivity;
|
public mostRecentActivity: MostRecentActivity.MostRecentActivity;
|
||||||
|
|
||||||
// Notification Console
|
// Notification Console
|
||||||
public notificationConsoleData: ko.ObservableArray<ConsoleData>;
|
private setIsNotificationConsoleExpanded: (isExpanded: boolean) => void;
|
||||||
public isNotificationConsoleExpanded: ko.Observable<boolean>;
|
private setNotificationConsoleData: (consoleData: ConsoleData) => void;
|
||||||
|
private setInProgressConsoleDataIdToBeDeleted: (id: string) => void;
|
||||||
|
|
||||||
// Panes
|
// Panes
|
||||||
public contextPanes: ContextualPaneBase[];
|
public contextPanes: ContextualPaneBase[];
|
||||||
@ -260,7 +266,6 @@ export default class Explorer {
|
|||||||
// React adapters
|
// React adapters
|
||||||
private commandBarComponentAdapter: CommandBarComponentAdapter;
|
private commandBarComponentAdapter: CommandBarComponentAdapter;
|
||||||
private splashScreenAdapter: SplashScreenComponentAdapter;
|
private splashScreenAdapter: SplashScreenComponentAdapter;
|
||||||
private notificationConsoleComponentAdapter: NotificationConsoleComponentAdapter;
|
|
||||||
private dialogComponentAdapter: DialogComponentAdapter;
|
private dialogComponentAdapter: DialogComponentAdapter;
|
||||||
private _dialogProps: ko.Observable<DialogProps>;
|
private _dialogProps: ko.Observable<DialogProps>;
|
||||||
private addSynapseLinkDialog: DialogComponentAdapter;
|
private addSynapseLinkDialog: DialogComponentAdapter;
|
||||||
@ -269,7 +274,11 @@ export default class Explorer {
|
|||||||
|
|
||||||
private static readonly MaxNbDatabasesToAutoExpand = 5;
|
private static readonly MaxNbDatabasesToAutoExpand = 5;
|
||||||
|
|
||||||
constructor() {
|
constructor(params?: ExplorerParams) {
|
||||||
|
this.setIsNotificationConsoleExpanded = params?.setIsNotificationConsoleExpanded;
|
||||||
|
this.setNotificationConsoleData = params?.setNotificationConsoleData;
|
||||||
|
this.setInProgressConsoleDataIdToBeDeleted = params?.setInProgressConsoleDataIdToBeDeleted;
|
||||||
|
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, {
|
||||||
dataExplorerArea: Constants.Areas.ResourceTree,
|
dataExplorerArea: Constants.Areas.ResourceTree,
|
||||||
});
|
});
|
||||||
@ -430,7 +439,6 @@ export default class Explorer {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.isSchemaEnabled = ko.computed<boolean>(() => this.isFeatureEnabled(Constants.Features.enableSchema));
|
this.isSchemaEnabled = ko.computed<boolean>(() => this.isFeatureEnabled(Constants.Features.enableSchema));
|
||||||
this.isNotificationConsoleExpanded = ko.observable<boolean>(false);
|
|
||||||
|
|
||||||
this.isAutoscaleDefaultEnabled = ko.observable<boolean>(false);
|
this.isAutoscaleDefaultEnabled = ko.observable<boolean>(false);
|
||||||
|
|
||||||
@ -478,7 +486,6 @@ export default class Explorer {
|
|||||||
bounds: splitterBounds,
|
bounds: splitterBounds,
|
||||||
direction: SplitterDirection.Vertical,
|
direction: SplitterDirection.Vertical,
|
||||||
});
|
});
|
||||||
this.notificationConsoleData = ko.observableArray<ConsoleData>([]);
|
|
||||||
this.defaultExperience = ko.observable<string>();
|
this.defaultExperience = ko.observable<string>();
|
||||||
this.databaseAccount.subscribe((databaseAccount) => {
|
this.databaseAccount.subscribe((databaseAccount) => {
|
||||||
const defaultExperience: string = DefaultExperienceUtility.getDefaultExperienceFromDatabaseAccount(
|
const defaultExperience: string = DefaultExperienceUtility.getDefaultExperienceFromDatabaseAccount(
|
||||||
@ -892,7 +899,6 @@ export default class Explorer {
|
|||||||
|
|
||||||
this.commandBarComponentAdapter = new CommandBarComponentAdapter(this);
|
this.commandBarComponentAdapter = new CommandBarComponentAdapter(this);
|
||||||
this.selfServeLoadingComponentAdapter = new SelfServeLoadingComponentAdapter();
|
this.selfServeLoadingComponentAdapter = new SelfServeLoadingComponentAdapter();
|
||||||
this.notificationConsoleComponentAdapter = new NotificationConsoleComponentAdapter(this);
|
|
||||||
|
|
||||||
this._initSettings();
|
this._initSettings();
|
||||||
|
|
||||||
@ -1349,23 +1355,19 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public logConsoleData(consoleData: ConsoleData): void {
|
public logConsoleData(consoleData: ConsoleData): void {
|
||||||
this.notificationConsoleData.splice(0, 0, consoleData);
|
this.setNotificationConsoleData(consoleData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteInProgressConsoleDataWithId(id: string): void {
|
public deleteInProgressConsoleDataWithId(id: string): void {
|
||||||
const updatedConsoleData = _.reject(
|
this.setInProgressConsoleDataIdToBeDeleted(id);
|
||||||
this.notificationConsoleData(),
|
|
||||||
(data: ConsoleData) => data.type === ConsoleDataType.InProgress && data.id === id
|
|
||||||
);
|
|
||||||
this.notificationConsoleData(updatedConsoleData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public expandConsole(): void {
|
public expandConsole(): void {
|
||||||
this.isNotificationConsoleExpanded(true);
|
this.setIsNotificationConsoleExpanded(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public collapseConsole(): void {
|
public collapseConsole(): void {
|
||||||
this.isNotificationConsoleExpanded(false);
|
this.setIsNotificationConsoleExpanded(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggleLeftPaneExpanded() {
|
public toggleLeftPaneExpanded() {
|
||||||
|
@ -2,7 +2,6 @@ import React from "react";
|
|||||||
import { shallow } from "enzyme";
|
import { shallow } from "enzyme";
|
||||||
import {
|
import {
|
||||||
NotificationConsoleComponentProps,
|
NotificationConsoleComponentProps,
|
||||||
ConsoleData,
|
|
||||||
NotificationConsoleComponent,
|
NotificationConsoleComponent,
|
||||||
ConsoleDataType,
|
ConsoleDataType,
|
||||||
} from "./NotificationConsoleComponent";
|
} from "./NotificationConsoleComponent";
|
||||||
@ -10,38 +9,40 @@ import {
|
|||||||
describe("NotificationConsoleComponent", () => {
|
describe("NotificationConsoleComponent", () => {
|
||||||
const createBlankProps = (): NotificationConsoleComponentProps => {
|
const createBlankProps = (): NotificationConsoleComponentProps => {
|
||||||
return {
|
return {
|
||||||
consoleData: [],
|
consoleData: undefined,
|
||||||
isConsoleExpanded: true,
|
isConsoleExpanded: false,
|
||||||
onConsoleDataChange: (consoleData: ConsoleData[]) => {},
|
inProgressConsoleDataIdToBeDeleted: "",
|
||||||
onConsoleExpandedChange: (isExpanded: boolean) => {},
|
setIsConsoleExpanded: (isExpanded: boolean): void => {},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
it("renders the console (expanded)", () => {
|
it("renders the console", () => {
|
||||||
const props = createBlankProps();
|
const props = createBlankProps();
|
||||||
props.consoleData.push({
|
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
|
||||||
|
props.consoleData = {
|
||||||
type: ConsoleDataType.Info,
|
type: ConsoleDataType.Info,
|
||||||
date: "date",
|
date: "date",
|
||||||
message: "message",
|
message: "message",
|
||||||
});
|
};
|
||||||
|
wrapper.setProps(props);
|
||||||
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows proper progress count", () => {
|
it("shows proper progress count", () => {
|
||||||
const count = 100;
|
const count = 100;
|
||||||
const props = createBlankProps();
|
const props = createBlankProps();
|
||||||
|
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
||||||
|
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
props.consoleData.push({
|
props.consoleData = {
|
||||||
type: ConsoleDataType.InProgress,
|
type: ConsoleDataType.InProgress,
|
||||||
date: "date",
|
date: "date" + i,
|
||||||
message: "message",
|
message: "message",
|
||||||
});
|
};
|
||||||
|
wrapper.setProps(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
|
||||||
expect(wrapper.find(".notificationConsoleHeader .numInProgress").text()).toEqual(count.toString());
|
expect(wrapper.find(".notificationConsoleHeader .numInProgress").text()).toEqual(count.toString());
|
||||||
expect(wrapper.find(".notificationConsoleHeader .numErroredItems").text()).toEqual("0");
|
expect(wrapper.find(".notificationConsoleHeader .numErroredItems").text()).toEqual("0");
|
||||||
expect(wrapper.find(".notificationConsoleHeader .numInfoItems").text()).toEqual("0");
|
expect(wrapper.find(".notificationConsoleHeader .numInfoItems").text()).toEqual("0");
|
||||||
@ -50,16 +51,17 @@ describe("NotificationConsoleComponent", () => {
|
|||||||
it("shows proper error count", () => {
|
it("shows proper error count", () => {
|
||||||
const count = 100;
|
const count = 100;
|
||||||
const props = createBlankProps();
|
const props = createBlankProps();
|
||||||
|
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
||||||
|
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
props.consoleData.push({
|
props.consoleData = {
|
||||||
type: ConsoleDataType.Error,
|
type: ConsoleDataType.Error,
|
||||||
date: "date",
|
date: "date" + i,
|
||||||
message: "message",
|
message: "message",
|
||||||
});
|
};
|
||||||
|
wrapper.setProps(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
|
||||||
expect(wrapper.find(".notificationConsoleHeader .numInProgress").text()).toEqual("0");
|
expect(wrapper.find(".notificationConsoleHeader .numInProgress").text()).toEqual("0");
|
||||||
expect(wrapper.find(".notificationConsoleHeader .numErroredItems").text()).toEqual(count.toString());
|
expect(wrapper.find(".notificationConsoleHeader .numErroredItems").text()).toEqual(count.toString());
|
||||||
expect(wrapper.find(".notificationConsoleHeader .numInfoItems").text()).toEqual("0");
|
expect(wrapper.find(".notificationConsoleHeader .numInfoItems").text()).toEqual("0");
|
||||||
@ -68,31 +70,34 @@ describe("NotificationConsoleComponent", () => {
|
|||||||
it("shows proper info count", () => {
|
it("shows proper info count", () => {
|
||||||
const count = 100;
|
const count = 100;
|
||||||
const props = createBlankProps();
|
const props = createBlankProps();
|
||||||
|
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
||||||
|
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
props.consoleData.push({
|
props.consoleData = {
|
||||||
type: ConsoleDataType.Info,
|
type: ConsoleDataType.Info,
|
||||||
date: "date",
|
date: "date" + i,
|
||||||
message: "message",
|
message: "message",
|
||||||
});
|
};
|
||||||
|
wrapper.setProps(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
|
||||||
expect(wrapper.find(".notificationConsoleHeader .numInProgress").text()).toEqual("0");
|
expect(wrapper.find(".notificationConsoleHeader .numInProgress").text()).toEqual("0");
|
||||||
expect(wrapper.find(".notificationConsoleHeader .numErroredItems").text()).toEqual("0");
|
expect(wrapper.find(".notificationConsoleHeader .numErroredItems").text()).toEqual("0");
|
||||||
expect(wrapper.find(".notificationConsoleHeader .numInfoItems").text()).toEqual(count.toString());
|
expect(wrapper.find(".notificationConsoleHeader .numInfoItems").text()).toEqual(count.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
const testRenderNotification = (date: string, msg: string, type: ConsoleDataType, iconClassName: string) => {
|
const testRenderNotification = (date: string, message: string, type: ConsoleDataType, iconClassName: string) => {
|
||||||
const props = createBlankProps();
|
const props = createBlankProps();
|
||||||
props.consoleData.push({
|
|
||||||
date: date,
|
|
||||||
message: msg,
|
|
||||||
type: type,
|
|
||||||
});
|
|
||||||
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
||||||
|
|
||||||
|
props.consoleData = {
|
||||||
|
type,
|
||||||
|
date,
|
||||||
|
message,
|
||||||
|
};
|
||||||
|
wrapper.setProps(props);
|
||||||
expect(wrapper.find(".notificationConsoleData .date").text()).toEqual(date);
|
expect(wrapper.find(".notificationConsoleData .date").text()).toEqual(date);
|
||||||
expect(wrapper.find(".notificationConsoleData .message").text()).toEqual(msg);
|
expect(wrapper.find(".notificationConsoleData .message").text()).toEqual(message);
|
||||||
expect(wrapper.exists(`.notificationConsoleData .${iconClassName}`));
|
expect(wrapper.exists(`.notificationConsoleData .${iconClassName}`));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -110,55 +115,78 @@ describe("NotificationConsoleComponent", () => {
|
|||||||
|
|
||||||
it("clears notifications", () => {
|
it("clears notifications", () => {
|
||||||
const props = createBlankProps();
|
const props = createBlankProps();
|
||||||
props.consoleData.push({
|
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
||||||
|
|
||||||
|
props.consoleData = {
|
||||||
type: ConsoleDataType.InProgress,
|
type: ConsoleDataType.InProgress,
|
||||||
date: "date",
|
date: "date",
|
||||||
message: "message1",
|
message: "message1",
|
||||||
});
|
};
|
||||||
props.consoleData.push({
|
wrapper.setProps(props);
|
||||||
|
|
||||||
|
props.consoleData = {
|
||||||
type: ConsoleDataType.Error,
|
type: ConsoleDataType.Error,
|
||||||
date: "date",
|
date: "date",
|
||||||
message: "message2",
|
message: "message2",
|
||||||
});
|
};
|
||||||
props.consoleData.push({
|
wrapper.setProps(props);
|
||||||
|
|
||||||
|
props.consoleData = {
|
||||||
type: ConsoleDataType.Info,
|
type: ConsoleDataType.Info,
|
||||||
date: "date",
|
date: "date",
|
||||||
message: "message3",
|
message: "message3",
|
||||||
});
|
};
|
||||||
|
wrapper.setProps(props);
|
||||||
|
|
||||||
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
|
||||||
wrapper.find(".clearNotificationsButton").simulate("click");
|
wrapper.find(".clearNotificationsButton").simulate("click");
|
||||||
|
|
||||||
expect(!wrapper.exists(".notificationConsoleData"));
|
expect(!wrapper.exists(".notificationConsoleData"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("collapses and hide content", () => {
|
it("collapses and hide content", () => {
|
||||||
const props = createBlankProps();
|
const props = createBlankProps();
|
||||||
props.consoleData.push({
|
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
||||||
|
|
||||||
|
props.consoleData = {
|
||||||
|
type: ConsoleDataType.Info,
|
||||||
date: "date",
|
date: "date",
|
||||||
message: "message",
|
message: "message",
|
||||||
type: ConsoleDataType.Info,
|
};
|
||||||
});
|
|
||||||
props.isConsoleExpanded = true;
|
props.isConsoleExpanded = true;
|
||||||
|
wrapper.setProps(props);
|
||||||
|
|
||||||
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
|
||||||
wrapper.find(".notificationConsoleHeader").simulate("click");
|
wrapper.find(".notificationConsoleHeader").simulate("click");
|
||||||
expect(!wrapper.exists(".notificationConsoleContent"));
|
expect(!wrapper.exists(".notificationConsoleContent"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("display latest data in header", () => {
|
it("display latest data in header", () => {
|
||||||
const latestData = "latest data";
|
const latestData = "latest data";
|
||||||
const props1 = createBlankProps();
|
const props = createBlankProps();
|
||||||
const props2 = createBlankProps();
|
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
||||||
props2.consoleData.push({
|
|
||||||
|
props.consoleData = {
|
||||||
|
type: ConsoleDataType.Info,
|
||||||
date: "date",
|
date: "date",
|
||||||
message: latestData,
|
message: latestData,
|
||||||
type: ConsoleDataType.Info,
|
};
|
||||||
});
|
props.isConsoleExpanded = true;
|
||||||
props2.isConsoleExpanded = true;
|
wrapper.setProps(props);
|
||||||
|
|
||||||
const wrapper = shallow(<NotificationConsoleComponent {...props1} />);
|
|
||||||
wrapper.setProps(props2);
|
|
||||||
expect(wrapper.find(".headerStatusEllipsis").text()).toEqual(latestData);
|
expect(wrapper.find(".headerStatusEllipsis").text()).toEqual(latestData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("delete in progress message", () => {
|
||||||
|
const props = createBlankProps();
|
||||||
|
props.consoleData = {
|
||||||
|
type: ConsoleDataType.InProgress,
|
||||||
|
date: "date",
|
||||||
|
message: "message",
|
||||||
|
id: "1",
|
||||||
|
};
|
||||||
|
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
||||||
|
expect(wrapper.find(".notificationConsoleHeader .numInProgress").text()).toEqual("1");
|
||||||
|
|
||||||
|
props.inProgressConsoleDataIdToBeDeleted = "1";
|
||||||
|
wrapper.setProps(props);
|
||||||
|
expect(wrapper.find(".notificationConsoleHeader .numInProgress").text()).toEqual("0");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -37,15 +37,15 @@ export interface ConsoleData {
|
|||||||
|
|
||||||
export interface NotificationConsoleComponentProps {
|
export interface NotificationConsoleComponentProps {
|
||||||
isConsoleExpanded: boolean;
|
isConsoleExpanded: boolean;
|
||||||
onConsoleExpandedChange: (isExpanded: boolean) => void;
|
consoleData: ConsoleData;
|
||||||
consoleData: ConsoleData[];
|
inProgressConsoleDataIdToBeDeleted: string;
|
||||||
onConsoleDataChange: (consoleData: ConsoleData[]) => void;
|
setIsConsoleExpanded: (isExpanded: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NotificationConsoleComponentState {
|
interface NotificationConsoleComponentState {
|
||||||
headerStatus: string;
|
headerStatus: string;
|
||||||
selectedFilter: string;
|
selectedFilter: string;
|
||||||
isExpanded: boolean;
|
allConsoleData: ConsoleData[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NotificationConsoleComponent extends React.Component<
|
export class NotificationConsoleComponent extends React.Component<
|
||||||
@ -60,28 +60,28 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
{ key: "Error", text: "Error" },
|
{ key: "Error", text: "Error" },
|
||||||
];
|
];
|
||||||
private headerTimeoutId?: number;
|
private headerTimeoutId?: number;
|
||||||
private prevHeaderStatus: string | null;
|
private prevHeaderStatus: string;
|
||||||
private consoleHeaderElement?: HTMLElement;
|
private consoleHeaderElement?: HTMLElement;
|
||||||
|
|
||||||
constructor(props: NotificationConsoleComponentProps) {
|
constructor(props: NotificationConsoleComponentProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
headerStatus: "",
|
headerStatus: undefined,
|
||||||
selectedFilter: NotificationConsoleComponent.FilterOptions[0].key || "",
|
selectedFilter: NotificationConsoleComponent.FilterOptions[0].key,
|
||||||
isExpanded: props.isConsoleExpanded,
|
allConsoleData: props.consoleData ? [props.consoleData] : [],
|
||||||
};
|
};
|
||||||
this.prevHeaderStatus = null;
|
this.prevHeaderStatus = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidUpdate(
|
public componentDidUpdate(
|
||||||
prevProps: NotificationConsoleComponentProps,
|
prevProps: NotificationConsoleComponentProps,
|
||||||
prevState: NotificationConsoleComponentState
|
prevState: NotificationConsoleComponentState
|
||||||
) {
|
) {
|
||||||
const currentHeaderStatus = NotificationConsoleComponent.extractHeaderStatus(this.props);
|
const currentHeaderStatus = NotificationConsoleComponent.extractHeaderStatus(this.props.consoleData);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.prevHeaderStatus !== currentHeaderStatus &&
|
this.prevHeaderStatus !== currentHeaderStatus &&
|
||||||
currentHeaderStatus !== null &&
|
currentHeaderStatus !== undefined &&
|
||||||
prevState.headerStatus !== currentHeaderStatus
|
prevState.headerStatus !== currentHeaderStatus
|
||||||
) {
|
) {
|
||||||
this.setHeaderStatus(currentHeaderStatus);
|
this.setHeaderStatus(currentHeaderStatus);
|
||||||
@ -92,10 +92,8 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
// updates: currentHeaderStatus -> "" -> currentHeaderStatus -> "" etc.
|
// updates: currentHeaderStatus -> "" -> currentHeaderStatus -> "" etc.
|
||||||
this.prevHeaderStatus = currentHeaderStatus;
|
this.prevHeaderStatus = currentHeaderStatus;
|
||||||
|
|
||||||
if (prevProps.isConsoleExpanded !== this.props.isConsoleExpanded) {
|
if (this.props.consoleData || this.props.inProgressConsoleDataIdToBeDeleted) {
|
||||||
// Sync state and props
|
this.updateConsoleData(prevProps);
|
||||||
// TODO react anti-pattern: remove isExpanded from state which duplicates prop's isConsoleExpanded
|
|
||||||
this.setState({ isExpanded: this.props.isConsoleExpanded });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,12 +102,14 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
};
|
};
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
const numInProgress = this.props.consoleData.filter((data: ConsoleData) => data.type === ConsoleDataType.InProgress)
|
const numInProgress = this.state.allConsoleData.filter(
|
||||||
|
(data: ConsoleData) => data.type === ConsoleDataType.InProgress
|
||||||
|
).length;
|
||||||
|
const numErroredItems = this.state.allConsoleData.filter((data: ConsoleData) => data.type === ConsoleDataType.Error)
|
||||||
.length;
|
.length;
|
||||||
const numErroredItems = this.props.consoleData.filter((data: ConsoleData) => data.type === ConsoleDataType.Error)
|
const numInfoItems = this.state.allConsoleData.filter((data: ConsoleData) => data.type === ConsoleDataType.Info)
|
||||||
.length;
|
|
||||||
const numInfoItems = this.props.consoleData.filter((data: ConsoleData) => data.type === ConsoleDataType.Info)
|
|
||||||
.length;
|
.length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="notificationConsoleContainer">
|
<div className="notificationConsoleContainer">
|
||||||
<div
|
<div
|
||||||
@ -143,18 +143,18 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
className="expandCollapseButton"
|
className="expandCollapseButton"
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
aria-label={"console button" + (this.state.isExpanded ? " collapsed" : " expanded")}
|
aria-label={"console button" + (this.props.isConsoleExpanded ? " collapsed" : " expanded")}
|
||||||
aria-expanded={!this.state.isExpanded}
|
aria-expanded={!this.props.isConsoleExpanded}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={this.state.isExpanded ? ChevronDownIcon : ChevronUpIcon}
|
src={this.props.isConsoleExpanded ? ChevronDownIcon : ChevronUpIcon}
|
||||||
alt={this.state.isExpanded ? "ChevronDownIcon" : "ChevronUpIcon"}
|
alt={this.props.isConsoleExpanded ? "ChevronDownIcon" : "ChevronUpIcon"}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<AnimateHeight
|
<AnimateHeight
|
||||||
duration={NotificationConsoleComponent.transitionDurationMs}
|
duration={NotificationConsoleComponent.transitionDurationMs}
|
||||||
height={this.state.isExpanded ? "auto" : 0}
|
height={this.props.isConsoleExpanded ? "auto" : 0}
|
||||||
onAnimationEnd={this.onConsoleWasExpanded}
|
onAnimationEnd={this.onConsoleWasExpanded}
|
||||||
>
|
>
|
||||||
<div className="notificationConsoleContents">
|
<div className="notificationConsoleContents">
|
||||||
@ -189,7 +189,7 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
private expandCollapseConsole() {
|
private expandCollapseConsole() {
|
||||||
this.setState({ isExpanded: !this.state.isExpanded });
|
this.props.setIsConsoleExpanded(!this.props.isConsoleExpanded);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onExpandCollapseKeyPress = (event: React.KeyboardEvent<HTMLDivElement>): void => {
|
private onExpandCollapseKeyPress = (event: React.KeyboardEvent<HTMLDivElement>): void => {
|
||||||
@ -209,7 +209,7 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
};
|
};
|
||||||
|
|
||||||
private clearNotifications(): void {
|
private clearNotifications(): void {
|
||||||
this.props.onConsoleDataChange([]);
|
this.setState({ allConsoleData: [] });
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderAllFilteredConsoleData(rowData: ConsoleData[]): JSX.Element[] {
|
private renderAllFilteredConsoleData(rowData: ConsoleData[]): JSX.Element[] {
|
||||||
@ -229,12 +229,9 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
};
|
};
|
||||||
|
|
||||||
private getFilteredConsoleData(): ConsoleData[] {
|
private getFilteredConsoleData(): ConsoleData[] {
|
||||||
let filterType: ConsoleDataType | null = null;
|
let filterType: ConsoleDataType;
|
||||||
|
|
||||||
switch (this.state.selectedFilter) {
|
switch (this.state.selectedFilter) {
|
||||||
case "All":
|
|
||||||
filterType = null;
|
|
||||||
break;
|
|
||||||
case "In Progress":
|
case "In Progress":
|
||||||
filterType = ConsoleDataType.InProgress;
|
filterType = ConsoleDataType.InProgress;
|
||||||
break;
|
break;
|
||||||
@ -245,12 +242,12 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
filterType = ConsoleDataType.Error;
|
filterType = ConsoleDataType.Error;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
filterType = null;
|
filterType = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return filterType == null
|
return filterType
|
||||||
? this.props.consoleData
|
? this.state.allConsoleData.filter((data: ConsoleData) => data.type === filterType)
|
||||||
: this.props.consoleData.filter((data: ConsoleData) => data.type === filterType);
|
: this.state.allConsoleData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private setHeaderStatus(statusMessage: string): void {
|
private setHeaderStatus(statusMessage: string): void {
|
||||||
@ -266,18 +263,43 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static extractHeaderStatus(props: NotificationConsoleComponentProps) {
|
private static extractHeaderStatus(consoleData: ConsoleData) {
|
||||||
if (props.consoleData && props.consoleData.length > 0) {
|
return consoleData?.message.split(":\n")[0];
|
||||||
return props.consoleData[0].message.split(":\n")[0];
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onConsoleWasExpanded = (): void => {
|
private onConsoleWasExpanded = (): void => {
|
||||||
this.props.onConsoleExpandedChange(this.state.isExpanded);
|
if (this.props.isConsoleExpanded && this.consoleHeaderElement) {
|
||||||
if (this.state.isExpanded && this.consoleHeaderElement) {
|
|
||||||
this.consoleHeaderElement.focus();
|
this.consoleHeaderElement.focus();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private updateConsoleData = (prevProps: NotificationConsoleComponentProps): void => {
|
||||||
|
if (!this.areConsoleDataEqual(this.props.consoleData, prevProps.consoleData)) {
|
||||||
|
this.setState({ allConsoleData: [this.props.consoleData, ...this.state.allConsoleData] });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.props.inProgressConsoleDataIdToBeDeleted &&
|
||||||
|
prevProps.inProgressConsoleDataIdToBeDeleted !== this.props.inProgressConsoleDataIdToBeDeleted
|
||||||
|
) {
|
||||||
|
const allConsoleData = this.state.allConsoleData.filter(
|
||||||
|
(data: ConsoleData) =>
|
||||||
|
!(data.type === ConsoleDataType.InProgress && data.id === this.props.inProgressConsoleDataIdToBeDeleted)
|
||||||
|
);
|
||||||
|
this.setState({ allConsoleData });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private areConsoleDataEqual = (currentData: ConsoleData, prevData: ConsoleData): boolean => {
|
||||||
|
if (!currentData || !prevData) {
|
||||||
|
return !currentData && !prevData;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
currentData.date === prevData.date &&
|
||||||
|
currentData.message === prevData.message &&
|
||||||
|
currentData.type === prevData.type &&
|
||||||
|
currentData.id === prevData.id
|
||||||
|
);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
import * as ko from "knockout";
|
|
||||||
import * as React from "react";
|
|
||||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import { NotificationConsoleComponent } from "./NotificationConsoleComponent";
|
|
||||||
import { ConsoleData } from "./NotificationConsoleComponent";
|
|
||||||
import Explorer from "../../Explorer";
|
|
||||||
|
|
||||||
export class NotificationConsoleComponentAdapter implements ReactAdapter {
|
|
||||||
public parameters: ko.Observable<number>;
|
|
||||||
public container: Explorer;
|
|
||||||
private consoleData: ko.ObservableArray<ConsoleData>;
|
|
||||||
|
|
||||||
constructor(container: Explorer) {
|
|
||||||
this.container = container;
|
|
||||||
|
|
||||||
this.consoleData = container.notificationConsoleData;
|
|
||||||
this.consoleData.subscribe((newValue: ConsoleData[]) => this.triggerRender());
|
|
||||||
container.isNotificationConsoleExpanded.subscribe(() => this.triggerRender());
|
|
||||||
this.parameters = ko.observable(Date.now());
|
|
||||||
}
|
|
||||||
|
|
||||||
private onConsoleExpandedChange(isExpanded: boolean): void {
|
|
||||||
isExpanded ? this.container.expandConsole() : this.container.collapseConsole();
|
|
||||||
this.triggerRender();
|
|
||||||
}
|
|
||||||
|
|
||||||
private onConsoleDataChange(consoleData: ConsoleData[]): void {
|
|
||||||
this.consoleData(consoleData);
|
|
||||||
this.triggerRender();
|
|
||||||
}
|
|
||||||
|
|
||||||
public renderComponent(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<NotificationConsoleComponent
|
|
||||||
isConsoleExpanded={this.container.isNotificationConsoleExpanded()}
|
|
||||||
onConsoleExpandedChange={this.onConsoleExpandedChange.bind(this)}
|
|
||||||
consoleData={this.consoleData()}
|
|
||||||
onConsoleDataChange={this.onConsoleDataChange.bind(this)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private triggerRender() {
|
|
||||||
window.requestAnimationFrame(() => this.parameters(Date.now()));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,169 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`NotificationConsoleComponent renders the console (expanded) 1`] = `
|
exports[`NotificationConsoleComponent renders the console 1`] = `
|
||||||
|
<div
|
||||||
|
className="notificationConsoleContainer"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="notificationConsoleHeader"
|
||||||
|
onClick={[Function]}
|
||||||
|
onKeyDown={[Function]}
|
||||||
|
tabIndex={0}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="statusBar"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="dataTypeIcons"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="notificationConsoleHeaderIconWithData"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="in progress items"
|
||||||
|
src=""
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className="numInProgress"
|
||||||
|
>
|
||||||
|
0
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className="notificationConsoleHeaderIconWithData"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="error items"
|
||||||
|
src=""
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className="numErroredItems"
|
||||||
|
>
|
||||||
|
0
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className="notificationConsoleHeaderIconWithData"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="info items"
|
||||||
|
src=""
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className="numInfoItems"
|
||||||
|
>
|
||||||
|
0
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className="consoleSplitter"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className="headerStatus"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="headerStatusEllipsis"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
aria-expanded={true}
|
||||||
|
aria-label="console button expanded"
|
||||||
|
className="expandCollapseButton"
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="ChevronUpIcon"
|
||||||
|
src=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<AnimateHeight
|
||||||
|
animateOpacity={false}
|
||||||
|
animationStateClasses={
|
||||||
|
Object {
|
||||||
|
"animating": "rah-animating",
|
||||||
|
"animatingDown": "rah-animating--down",
|
||||||
|
"animatingToHeightAuto": "rah-animating--to-height-auto",
|
||||||
|
"animatingToHeightSpecific": "rah-animating--to-height-specific",
|
||||||
|
"animatingToHeightZero": "rah-animating--to-height-zero",
|
||||||
|
"animatingUp": "rah-animating--up",
|
||||||
|
"static": "rah-static",
|
||||||
|
"staticHeightAuto": "rah-static--height-auto",
|
||||||
|
"staticHeightSpecific": "rah-static--height-specific",
|
||||||
|
"staticHeightZero": "rah-static--height-zero",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
applyInlineTransitions={true}
|
||||||
|
delay={0}
|
||||||
|
duration={200}
|
||||||
|
easing="ease"
|
||||||
|
height={0}
|
||||||
|
onAnimationEnd={[Function]}
|
||||||
|
style={Object {}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="notificationConsoleContents"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="notificationConsoleControls"
|
||||||
|
>
|
||||||
|
<StyledWithResponsiveMode
|
||||||
|
aria-label="All"
|
||||||
|
aria-labelledby="consoleFilterLabel"
|
||||||
|
label="Filter:"
|
||||||
|
onChange={[Function]}
|
||||||
|
options={
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"key": "All",
|
||||||
|
"text": "All",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"key": "In Progress",
|
||||||
|
"text": "In progress",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"key": "Info",
|
||||||
|
"text": "Info",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"key": "Error",
|
||||||
|
"text": "Error",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
role="combobox"
|
||||||
|
selectedKey="All"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className="consoleSplitter"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className="clearNotificationsButton"
|
||||||
|
onClick={[Function]}
|
||||||
|
onKeyDown={[Function]}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="clear notifications image"
|
||||||
|
src=""
|
||||||
|
/>
|
||||||
|
Clear Notifications
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="notificationConsoleData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</AnimateHeight>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`NotificationConsoleComponent renders the console 2`] = `
|
||||||
<div
|
<div
|
||||||
className="notificationConsoleContainer"
|
className="notificationConsoleContainer"
|
||||||
>
|
>
|
||||||
@ -64,18 +227,20 @@ exports[`NotificationConsoleComponent renders the console (expanded) 1`] = `
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="headerStatusEllipsis"
|
className="headerStatusEllipsis"
|
||||||
/>
|
>
|
||||||
|
message
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-expanded={false}
|
aria-expanded={true}
|
||||||
aria-label="console button collapsed"
|
aria-label="console button expanded"
|
||||||
className="expandCollapseButton"
|
className="expandCollapseButton"
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="ChevronDownIcon"
|
alt="ChevronUpIcon"
|
||||||
src=""
|
src=""
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -100,7 +265,7 @@ exports[`NotificationConsoleComponent renders the console (expanded) 1`] = `
|
|||||||
delay={0}
|
delay={0}
|
||||||
duration={200}
|
duration={200}
|
||||||
easing="ease"
|
easing="ease"
|
||||||
height="auto"
|
height={0}
|
||||||
onAnimationEnd={[Function]}
|
onAnimationEnd={[Function]}
|
||||||
style={Object {}}
|
style={Object {}}
|
||||||
>
|
>
|
||||||
|
@ -29,10 +29,6 @@ export abstract class ContextualPaneBase extends WaitsForTemplateViewModel {
|
|||||||
this.title = ko.observable<string>();
|
this.title = ko.observable<string>();
|
||||||
this.formErrorsDetails = ko.observable<string>();
|
this.formErrorsDetails = ko.observable<string>();
|
||||||
this.isExecuting = ko.observable<boolean>(false);
|
this.isExecuting = ko.observable<boolean>(false);
|
||||||
this.container.isNotificationConsoleExpanded.subscribe((isExpanded: boolean) => {
|
|
||||||
this.resizePane();
|
|
||||||
});
|
|
||||||
this.container.isNotificationConsoleExpanded.extend({ rateLimit: 10 });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public cancel() {
|
public cancel() {
|
||||||
|
@ -57,7 +57,6 @@ describe("Delete Collection Confirmation Pane", () => {
|
|||||||
describe("shouldRecordFeedback()", () => {
|
describe("shouldRecordFeedback()", () => {
|
||||||
it("should return true if last collection and database does not have shared throughput else false", () => {
|
it("should return true if last collection and database does not have shared throughput else false", () => {
|
||||||
let fakeExplorer = new Explorer();
|
let fakeExplorer = new Explorer();
|
||||||
fakeExplorer.isNotificationConsoleExpanded = ko.observable<boolean>(false);
|
|
||||||
fakeExplorer.refreshAllDatabases = () => Q.resolve();
|
fakeExplorer.refreshAllDatabases = () => Q.resolve();
|
||||||
|
|
||||||
let pane = new DeleteCollectionConfirmationPane({
|
let pane = new DeleteCollectionConfirmationPane({
|
||||||
@ -101,7 +100,6 @@ describe("Delete Collection Confirmation Pane", () => {
|
|||||||
rid: "test",
|
rid: "test",
|
||||||
} as ViewModels.Collection;
|
} as ViewModels.Collection;
|
||||||
};
|
};
|
||||||
fakeExplorer.isNotificationConsoleExpanded = ko.observable<boolean>(false);
|
|
||||||
fakeExplorer.selectedCollectionId = ko.computed<string>(() => selectedCollectionId);
|
fakeExplorer.selectedCollectionId = ko.computed<string>(() => selectedCollectionId);
|
||||||
fakeExplorer.isSelectedDatabaseShared = () => false;
|
fakeExplorer.isSelectedDatabaseShared = () => false;
|
||||||
const SubscriptionId = "testId";
|
const SubscriptionId = "testId";
|
||||||
|
@ -55,7 +55,6 @@ describe("Delete Database Confirmation Pane", () => {
|
|||||||
describe("shouldRecordFeedback()", () => {
|
describe("shouldRecordFeedback()", () => {
|
||||||
it("should return true if last non empty database or is last database that has shared throughput, else false", () => {
|
it("should return true if last non empty database or is last database that has shared throughput, else false", () => {
|
||||||
let fakeExplorer = {} as Explorer;
|
let fakeExplorer = {} as Explorer;
|
||||||
fakeExplorer.isNotificationConsoleExpanded = ko.observable<boolean>(false);
|
|
||||||
|
|
||||||
let pane = new DeleteDatabaseConfirmationPane({
|
let pane = new DeleteDatabaseConfirmationPane({
|
||||||
id: "deletedatabaseconfirmationpane",
|
id: "deletedatabaseconfirmationpane",
|
||||||
@ -92,7 +91,6 @@ describe("Delete Database Confirmation Pane", () => {
|
|||||||
} as ViewModels.Database;
|
} as ViewModels.Database;
|
||||||
};
|
};
|
||||||
fakeExplorer.refreshAllDatabases = () => Q.resolve();
|
fakeExplorer.refreshAllDatabases = () => Q.resolve();
|
||||||
fakeExplorer.isNotificationConsoleExpanded = ko.observable<boolean>(false);
|
|
||||||
fakeExplorer.selectedDatabaseId = ko.computed<string>(() => selectedDatabaseId);
|
fakeExplorer.selectedDatabaseId = ko.computed<string>(() => selectedDatabaseId);
|
||||||
fakeExplorer.isSelectedDatabaseShared = () => false;
|
fakeExplorer.isSelectedDatabaseShared = () => false;
|
||||||
const SubscriptionId = "testId";
|
const SubscriptionId = "testId";
|
||||||
|
@ -34,13 +34,6 @@ export class GenericRightPaneComponent extends React.Component<GenericRightPaneP
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
|
||||||
this.notificationConsoleSubscription = this.props.container.isNotificationConsoleExpanded.subscribe(() => {
|
|
||||||
this.setState({ panelHeight: this.getPanelHeight() });
|
|
||||||
});
|
|
||||||
this.props.container.isNotificationConsoleExpanded.extend({ rateLimit: 10 });
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
this.notificationConsoleSubscription && this.notificationConsoleSubscription.dispose();
|
this.notificationConsoleSubscription && this.notificationConsoleSubscription.dispose();
|
||||||
}
|
}
|
||||||
|
@ -240,10 +240,7 @@ function updateTableScrollableRegionHeight(): void {
|
|||||||
var dataTablesScrollBodyPosY = $(tabElement).find(Constants.htmlSelectors.dataTableScrollBodySelector).offset().top;
|
var dataTablesScrollBodyPosY = $(tabElement).find(Constants.htmlSelectors.dataTableScrollBodySelector).offset().top;
|
||||||
var dataTablesInfoElem = $(tabElement).find(".dataTables_info");
|
var dataTablesInfoElem = $(tabElement).find(".dataTables_info");
|
||||||
var dataTablesPaginateElem = $(tabElement).find(".dataTables_paginate");
|
var dataTablesPaginateElem = $(tabElement).find(".dataTables_paginate");
|
||||||
const explorer = window.dataExplorer;
|
const notificationConsoleHeight = 32; /** Header height **/
|
||||||
const notificationConsoleHeight = explorer.isNotificationConsoleExpanded()
|
|
||||||
? 252 /** 32px(header) + 220px(content height) **/
|
|
||||||
: 32; /** Header height **/
|
|
||||||
|
|
||||||
var scrollHeight =
|
var scrollHeight =
|
||||||
bodyHeight -
|
bodyHeight -
|
||||||
|
23
src/Main.tsx
23
src/Main.tsx
@ -54,7 +54,8 @@ import "./Libs/is-integer-polyfill";
|
|||||||
import "url-polyfill/url-polyfill.min";
|
import "url-polyfill/url-polyfill.min";
|
||||||
|
|
||||||
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
|
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
|
||||||
import React from "react";
|
import { ExplorerParams } from "./Explorer/Explorer";
|
||||||
|
import React, { useState } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import copyImage from "../images/Copy.svg";
|
import copyImage from "../images/Copy.svg";
|
||||||
import hdeConnectImage from "../images/HdeConnectCosmosDB.svg";
|
import hdeConnectImage from "../images/HdeConnectCosmosDB.svg";
|
||||||
@ -63,12 +64,22 @@ 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 { NotificationConsoleComponent } from "./Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
|
|
||||||
initializeIcons();
|
initializeIcons();
|
||||||
|
|
||||||
const App: React.FunctionComponent = () => {
|
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 explorerParams: ExplorerParams = {
|
||||||
|
setIsNotificationConsoleExpanded,
|
||||||
|
setNotificationConsoleData,
|
||||||
|
setInProgressConsoleDataIdToBeDeleted,
|
||||||
|
};
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
useKnockoutExplorer(config);
|
useKnockoutExplorer(config, explorerParams);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flexContainer">
|
<div className="flexContainer">
|
||||||
@ -270,9 +281,15 @@ const App: React.FunctionComponent = () => {
|
|||||||
role="contentinfo"
|
role="contentinfo"
|
||||||
aria-label="Notification console"
|
aria-label="Notification console"
|
||||||
id="explorerNotificationConsole"
|
id="explorerNotificationConsole"
|
||||||
data-bind="react: notificationConsoleComponentAdapter"
|
>
|
||||||
|
<NotificationConsoleComponent
|
||||||
|
isConsoleExpanded={isNotificationConsoleExpanded}
|
||||||
|
consoleData={notificationConsoleData}
|
||||||
|
inProgressConsoleDataIdToBeDeleted={inProgressConsoleDataIdToBeDeleted}
|
||||||
|
setIsConsoleExpanded={setIsNotificationConsoleExpanded}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{/* Global loader - Start */}
|
{/* Global loader - Start */}
|
||||||
|
|
||||||
<div className="splashLoaderContainer" data-bind="visible: isRefreshingExplorer">
|
<div className="splashLoaderContainer" data-bind="visible: isRefreshingExplorer">
|
||||||
|
@ -19,14 +19,13 @@ export function logConsoleMessage(type: ConsoleDataType, message: string, id?: s
|
|||||||
if (!id) {
|
if (!id) {
|
||||||
id = _.uniqueId();
|
id = _.uniqueId();
|
||||||
}
|
}
|
||||||
dataExplorer.logConsoleData({ type: type, date: formattedDate, message: message, id: id });
|
dataExplorer.logConsoleData({ type, date: formattedDate, message, id });
|
||||||
}
|
}
|
||||||
return id || "";
|
return id || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearInProgressMessageWithId(id: string): void {
|
export function clearInProgressMessageWithId(id: string): void {
|
||||||
const dataExplorer = _global.dataExplorer;
|
_global.dataExplorer?.deleteInProgressConsoleDataWithId(id);
|
||||||
dataExplorer && dataExplorer.deleteInProgressConsoleDataWithId(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logConsoleProgress(message: string): () => void {
|
export function logConsoleProgress(message: string): () => void {
|
||||||
|
@ -7,7 +7,7 @@ import { configContext, ConfigContext, Platform } from "../ConfigContext";
|
|||||||
import { ActionType, DataExplorerAction } from "../Contracts/ActionContracts";
|
import { ActionType, DataExplorerAction } from "../Contracts/ActionContracts";
|
||||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||||
import { DataExplorerInputsFrame } from "../Contracts/ViewModels";
|
import { DataExplorerInputsFrame } from "../Contracts/ViewModels";
|
||||||
import Explorer from "../Explorer/Explorer";
|
import Explorer, { ExplorerParams } from "../Explorer/Explorer";
|
||||||
import {
|
import {
|
||||||
AAD,
|
AAD,
|
||||||
ConnectionString,
|
ConnectionString,
|
||||||
@ -34,8 +34,8 @@ import { isInvalidParentFrameOrigin } from "../Utils/MessageValidation";
|
|||||||
// Pleas tread carefully :)
|
// Pleas tread carefully :)
|
||||||
let explorer: Explorer;
|
let explorer: Explorer;
|
||||||
|
|
||||||
export function useKnockoutExplorer(config: ConfigContext): Explorer {
|
export function useKnockoutExplorer(config: ConfigContext, explorerParams: ExplorerParams): Explorer {
|
||||||
explorer = explorer || new Explorer();
|
explorer = explorer || new Explorer(explorerParams);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const effect = async () => {
|
const effect = async () => {
|
||||||
if (config) {
|
if (config) {
|
||||||
|
Loading…
Reference in New Issue
Block a user