mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-20 17:30:46 +00:00
Initial Move from Azure DevOps to GitHub
This commit is contained in:
160
src/Explorer/Menus/NotificationConsole/NotificationConsole.less
Normal file
160
src/Explorer/Menus/NotificationConsole/NotificationConsole.less
Normal file
@@ -0,0 +1,160 @@
|
||||
@import "../../../../less/Common/Constants";
|
||||
|
||||
@ConsoleHeaderHeight: 32px;
|
||||
@ConsoleContentsPaneHeight: 220px;
|
||||
@ConsoleStatusMaxWidth: 672px;
|
||||
|
||||
@ConsoleIconSize: 12px;
|
||||
@ExpandCollapseIconSize: 20px;
|
||||
|
||||
.notificationConsoleContainer {
|
||||
width: 100%;
|
||||
.flex-display();
|
||||
.flex-direction();
|
||||
|
||||
img {
|
||||
width: @ConsoleIconSize;
|
||||
height: @ConsoleIconSize;
|
||||
}
|
||||
|
||||
.notificationConsoleHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
height: @ConsoleHeaderHeight;
|
||||
width: 100%;
|
||||
background-color: @NotificationLow;
|
||||
border-top: @ButtonBorderWidth @BaseMedium solid;
|
||||
cursor: pointer;
|
||||
flex-shrink:0;
|
||||
|
||||
&:hover {
|
||||
background-color:@NotificationHigh;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color:@NotificationHigh;
|
||||
}
|
||||
|
||||
.statusBar {
|
||||
.dataTypeIcons {
|
||||
cursor: pointer;
|
||||
margin: 0px @DefaultSpace 0px @MediumSpace;
|
||||
padding-left: @DefaultSpace;
|
||||
|
||||
.notificationConsoleHeaderIconWithData{
|
||||
&:not(:last-child) {
|
||||
padding-right: @LargeSpace;
|
||||
}
|
||||
|
||||
img {
|
||||
margin-bottom: @SmallSpace;
|
||||
margin-right: @DefaultSpace;
|
||||
}
|
||||
|
||||
.numInProgress, .numErroredItems, .numInfoItems {
|
||||
padding-left: 2px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.consoleSplitter {
|
||||
border-left: 1px solid @BaseMedium;
|
||||
margin-right: @LargeSpace;
|
||||
padding: 0px 0px 2px;
|
||||
}
|
||||
|
||||
.headerStatus {
|
||||
display: inline-flex;
|
||||
|
||||
.headerStatusEllipsis {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
max-width: @ConsoleStatusMaxWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.expandCollapseButton {
|
||||
cursor: pointer;
|
||||
padding-right: 5px;
|
||||
|
||||
img {
|
||||
width: @ExpandCollapseIconSize;
|
||||
height: @ExpandCollapseIconSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notificationConsoleContents {
|
||||
width: 100%;
|
||||
height: @ConsoleContentsPaneHeight;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: @BaseLow;
|
||||
|
||||
.notificationConsoleControls {
|
||||
padding: @MediumSpace;
|
||||
margin-left:@DefaultSpace;
|
||||
|
||||
#consoleFilterLabel {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.consoleSplitter {
|
||||
border-left: 1px solid @BaseMedium;
|
||||
margin: @MediumSpace;
|
||||
}
|
||||
|
||||
.clearNotificationsButton {
|
||||
cursor: pointer;
|
||||
padding:@SmallSpace;
|
||||
border:@ButtonBorderWidth solid @BaseLow;
|
||||
|
||||
&:hover {
|
||||
background-color:@BaseMediumLow;
|
||||
}
|
||||
|
||||
&:active {
|
||||
border: @ButtonBorderWidth dashed @AccentMedium;
|
||||
background-color: @AccentMediumLow;
|
||||
}
|
||||
|
||||
img{
|
||||
margin-bottom:@SmallSpace;
|
||||
margin-right:2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notificationConsoleData {
|
||||
overflow-y: auto;
|
||||
overflow-x:hidden;
|
||||
margin-left:@LargeSpace;
|
||||
|
||||
.rowData {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: @SmallSpace;
|
||||
|
||||
img {
|
||||
margin-top:@SmallSpace;
|
||||
}
|
||||
|
||||
.date {
|
||||
margin: 0px @LargeSpace;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.message {
|
||||
flex-grow: 1;
|
||||
white-space:pre-wrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
import React from "react";
|
||||
import { shallow } from "enzyme";
|
||||
import {
|
||||
NotificationConsoleComponentProps,
|
||||
ConsoleData,
|
||||
NotificationConsoleComponent,
|
||||
ConsoleDataType
|
||||
} from "./NotificationConsoleComponent";
|
||||
|
||||
describe("NotificationConsoleComponent", () => {
|
||||
const createBlankProps = (): NotificationConsoleComponentProps => {
|
||||
return {
|
||||
consoleData: [],
|
||||
isConsoleExpanded: true,
|
||||
onConsoleDataChange: (consoleData: ConsoleData[]) => {},
|
||||
onConsoleExpandedChange: (isExpanded: boolean) => {}
|
||||
};
|
||||
};
|
||||
|
||||
it("renders the console (expanded)", () => {
|
||||
const props = createBlankProps();
|
||||
props.consoleData.push({
|
||||
type: ConsoleDataType.Info,
|
||||
date: "date",
|
||||
message: "message"
|
||||
});
|
||||
|
||||
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("shows proper progress count", () => {
|
||||
const count = 100;
|
||||
const props = createBlankProps();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
props.consoleData.push({
|
||||
type: ConsoleDataType.InProgress,
|
||||
date: "date",
|
||||
message: "message"
|
||||
});
|
||||
}
|
||||
|
||||
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
||||
expect(wrapper.find(".notificationConsoleHeader .numInProgress").text()).toEqual(count.toString());
|
||||
expect(wrapper.find(".notificationConsoleHeader .numErroredItems").text()).toEqual("0");
|
||||
expect(wrapper.find(".notificationConsoleHeader .numInfoItems").text()).toEqual("0");
|
||||
});
|
||||
|
||||
it("shows proper error count", () => {
|
||||
const count = 100;
|
||||
const props = createBlankProps();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
props.consoleData.push({
|
||||
type: ConsoleDataType.Error,
|
||||
date: "date",
|
||||
message: "message"
|
||||
});
|
||||
}
|
||||
|
||||
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
||||
expect(wrapper.find(".notificationConsoleHeader .numInProgress").text()).toEqual("0");
|
||||
expect(wrapper.find(".notificationConsoleHeader .numErroredItems").text()).toEqual(count.toString());
|
||||
expect(wrapper.find(".notificationConsoleHeader .numInfoItems").text()).toEqual("0");
|
||||
});
|
||||
|
||||
it("shows proper info count", () => {
|
||||
const count = 100;
|
||||
const props = createBlankProps();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
props.consoleData.push({
|
||||
type: ConsoleDataType.Info,
|
||||
date: "date",
|
||||
message: "message"
|
||||
});
|
||||
}
|
||||
|
||||
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
||||
expect(wrapper.find(".notificationConsoleHeader .numInProgress").text()).toEqual("0");
|
||||
expect(wrapper.find(".notificationConsoleHeader .numErroredItems").text()).toEqual("0");
|
||||
expect(wrapper.find(".notificationConsoleHeader .numInfoItems").text()).toEqual(count.toString());
|
||||
});
|
||||
|
||||
const testRenderNotification = (date: string, msg: string, type: ConsoleDataType, iconClassName: string) => {
|
||||
const props = createBlankProps();
|
||||
props.consoleData.push({
|
||||
date: date,
|
||||
message: msg,
|
||||
type: type
|
||||
});
|
||||
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
||||
expect(wrapper.find(".notificationConsoleData .date").text()).toEqual(date);
|
||||
expect(wrapper.find(".notificationConsoleData .message").text()).toEqual(msg);
|
||||
expect(wrapper.exists(`.notificationConsoleData .${iconClassName}`));
|
||||
};
|
||||
|
||||
it("renders progress notifications", () => {
|
||||
testRenderNotification("date", "message", ConsoleDataType.InProgress, "loaderIcon");
|
||||
});
|
||||
|
||||
it("renders error notifications", () => {
|
||||
testRenderNotification("date", "message", ConsoleDataType.Error, "errorIcon");
|
||||
});
|
||||
|
||||
it("renders info notifications", () => {
|
||||
testRenderNotification("date", "message", ConsoleDataType.Info, "infoIcon");
|
||||
});
|
||||
|
||||
it("clears notifications", () => {
|
||||
const props = createBlankProps();
|
||||
props.consoleData.push({
|
||||
type: ConsoleDataType.InProgress,
|
||||
date: "date",
|
||||
message: "message1"
|
||||
});
|
||||
props.consoleData.push({
|
||||
type: ConsoleDataType.Error,
|
||||
date: "date",
|
||||
message: "message2"
|
||||
});
|
||||
props.consoleData.push({
|
||||
type: ConsoleDataType.Info,
|
||||
date: "date",
|
||||
message: "message3"
|
||||
});
|
||||
|
||||
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
||||
wrapper.find(".clearNotificationsButton").simulate("click");
|
||||
|
||||
expect(!wrapper.exists(".notificationConsoleData"));
|
||||
});
|
||||
|
||||
it("collapses and hide content", () => {
|
||||
const props = createBlankProps();
|
||||
props.consoleData.push({
|
||||
date: "date",
|
||||
message: "message",
|
||||
type: ConsoleDataType.Info
|
||||
});
|
||||
props.isConsoleExpanded = true;
|
||||
|
||||
const wrapper = shallow(<NotificationConsoleComponent {...props} />);
|
||||
wrapper.find(".notificationConsoleHeader").simulate("click");
|
||||
expect(!wrapper.exists(".notificationConsoleContent"));
|
||||
});
|
||||
|
||||
it("display latest data in header", () => {
|
||||
const latestData = "latest data";
|
||||
const props1 = createBlankProps();
|
||||
const props2 = createBlankProps();
|
||||
props2.consoleData.push({
|
||||
date: "date",
|
||||
message: latestData,
|
||||
type: ConsoleDataType.Info
|
||||
});
|
||||
props2.isConsoleExpanded = true;
|
||||
|
||||
const wrapper = shallow(<NotificationConsoleComponent {...props1} />);
|
||||
wrapper.setProps(props2);
|
||||
expect(wrapper.find(".headerStatusEllipsis").text()).toEqual(latestData);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,273 @@
|
||||
/**
|
||||
* React component for control bar
|
||||
*/
|
||||
|
||||
import * as React from "react";
|
||||
import { ClientDefaults, KeyCodes } from "../../../Common/Constants";
|
||||
import AnimateHeight from "react-animate-height";
|
||||
|
||||
import LoadingIcon from "../../../../images/loading.svg";
|
||||
import ErrorBlackIcon from "../../../../images/error_black.svg";
|
||||
import infoBubbleIcon from "../../../../images/info-bubble-9x9.svg";
|
||||
import InfoIcon from "../../../../images/info_color.svg";
|
||||
import ErrorRedIcon from "../../../../images/error_red.svg";
|
||||
import LoaderIcon from "../../../../images/circular_loader_black_16x16.gif";
|
||||
import ClearIcon from "../../../../images/Clear.svg";
|
||||
import ChevronUpIcon from "../../../../images/QueryBuilder/CollapseChevronUp_16x.png";
|
||||
import ChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png";
|
||||
|
||||
/**
|
||||
* Log levels
|
||||
*/
|
||||
export enum ConsoleDataType {
|
||||
Info = 0,
|
||||
Error = 1,
|
||||
InProgress = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for the data/content that will be recorded
|
||||
*/
|
||||
export interface ConsoleData {
|
||||
type: ConsoleDataType;
|
||||
date: string;
|
||||
message: string;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export interface NotificationConsoleComponentProps {
|
||||
isConsoleExpanded: boolean;
|
||||
onConsoleExpandedChange: (isExpanded: boolean) => void;
|
||||
consoleData: ConsoleData[];
|
||||
onConsoleDataChange: (consoleData: ConsoleData[]) => void;
|
||||
}
|
||||
|
||||
interface NotificationConsoleComponentState {
|
||||
headerStatus: string;
|
||||
selectedFilter: string;
|
||||
isExpanded: boolean;
|
||||
}
|
||||
|
||||
export class NotificationConsoleComponent extends React.Component<
|
||||
NotificationConsoleComponentProps,
|
||||
NotificationConsoleComponentState
|
||||
> {
|
||||
private static readonly transitionDurationMs = 200;
|
||||
private static readonly FilterOptions = ["All", "In Progress", "Info", "Error"];
|
||||
private headerTimeoutId: number;
|
||||
private prevHeaderStatus: string;
|
||||
private consoleHeaderElement: HTMLElement;
|
||||
|
||||
constructor(props: NotificationConsoleComponentProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
headerStatus: "",
|
||||
selectedFilter: NotificationConsoleComponent.FilterOptions[0],
|
||||
isExpanded: props.isConsoleExpanded
|
||||
};
|
||||
this.prevHeaderStatus = null;
|
||||
}
|
||||
|
||||
public componentDidUpdate(
|
||||
prevProps: NotificationConsoleComponentProps,
|
||||
prevState: NotificationConsoleComponentState
|
||||
) {
|
||||
const currentHeaderStatus = NotificationConsoleComponent.extractHeaderStatus(this.props);
|
||||
|
||||
if (
|
||||
this.prevHeaderStatus !== currentHeaderStatus &&
|
||||
currentHeaderStatus !== null &&
|
||||
prevState.headerStatus !== currentHeaderStatus
|
||||
) {
|
||||
this.setHeaderStatus(currentHeaderStatus);
|
||||
}
|
||||
|
||||
// Call setHeaderStatus() only to clear HeaderStatus or update status to a different value.
|
||||
// Cache previous headerStatus externally. Otherwise, simply comparing with previous state/props will cause circular
|
||||
// updates: currentHeaderStatus -> "" -> currentHeaderStatus -> "" etc.
|
||||
this.prevHeaderStatus = currentHeaderStatus;
|
||||
|
||||
if (prevProps.isConsoleExpanded !== this.props.isConsoleExpanded) {
|
||||
// Sync state and props
|
||||
// TODO react anti-pattern: remove isExpanded from state which duplicates prop's isConsoleExpanded
|
||||
this.setState({ isExpanded: this.props.isConsoleExpanded });
|
||||
}
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
const numInProgress = this.props.consoleData.filter((data: ConsoleData) => data.type === ConsoleDataType.InProgress)
|
||||
.length;
|
||||
const numErroredItems = this.props.consoleData.filter((data: ConsoleData) => data.type === ConsoleDataType.Error)
|
||||
.length;
|
||||
const numInfoItems = this.props.consoleData.filter((data: ConsoleData) => data.type === ConsoleDataType.Info)
|
||||
.length;
|
||||
return (
|
||||
<div className="notificationConsoleContainer">
|
||||
<div
|
||||
className="notificationConsoleHeader"
|
||||
ref={(element: HTMLElement) => (this.consoleHeaderElement = element)}
|
||||
onClick={(event: React.MouseEvent<HTMLDivElement>) => this.expandCollapseConsole()}
|
||||
onKeyDown={(event: React.KeyboardEvent<HTMLDivElement>) => this.onExpandCollapseKeyPress(event)}
|
||||
tabIndex={0}
|
||||
>
|
||||
<div className="statusBar">
|
||||
<span className="dataTypeIcons">
|
||||
<span className="notificationConsoleHeaderIconWithData">
|
||||
<img src={LoadingIcon} alt="in progress items" />
|
||||
<span className="numInProgress">{numInProgress}</span>
|
||||
</span>
|
||||
<span className="notificationConsoleHeaderIconWithData">
|
||||
<img src={ErrorBlackIcon} alt="error items" />
|
||||
<span className="numErroredItems">{numErroredItems}</span>
|
||||
</span>
|
||||
<span className="notificationConsoleHeaderIconWithData">
|
||||
<img src={infoBubbleIcon} alt="info items" />
|
||||
<span className="numInfoItems">{numInfoItems}</span>
|
||||
</span>
|
||||
</span>
|
||||
<span className="consoleSplitter" />
|
||||
<span className="headerStatus">
|
||||
<span className="headerStatusEllipsis">{this.state.headerStatus}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="expandCollapseButton" role="button" tabIndex={0}>
|
||||
<img
|
||||
src={this.state.isExpanded ? ChevronDownIcon : ChevronUpIcon}
|
||||
alt={this.state.isExpanded ? "collapse console" : "expand console"}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<AnimateHeight
|
||||
duration={NotificationConsoleComponent.transitionDurationMs}
|
||||
height={this.state.isExpanded ? "auto" : 0}
|
||||
onAnimationEnd={this.onConsoleWasExpanded}
|
||||
>
|
||||
<div className="notificationConsoleContents">
|
||||
<div className="notificationConsoleControls">
|
||||
<label id="consoleFilterLabel">Filter</label>
|
||||
<select
|
||||
aria-labelledby="consoleFilterLabel"
|
||||
role="combobox"
|
||||
aria-label={this.state.selectedFilter}
|
||||
value={this.state.selectedFilter}
|
||||
onChange={this.onFilterSelected.bind(this)}
|
||||
>
|
||||
{NotificationConsoleComponent.FilterOptions.map((value: string) => (
|
||||
<option value={value} key={value}>
|
||||
{value}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<span className="consoleSplitter" />
|
||||
<span
|
||||
className="clearNotificationsButton"
|
||||
onClick={() => this.clearNotifications()}
|
||||
role="button"
|
||||
onKeyDown={(event: React.KeyboardEvent<HTMLSpanElement>) => this.onClearNotificationsKeyPress(event)}
|
||||
tabIndex={0}
|
||||
>
|
||||
<img src={ClearIcon} alt="clear notifications image" />
|
||||
Clear Notifications
|
||||
</span>
|
||||
</div>
|
||||
<div className="notificationConsoleData">
|
||||
{this.renderAllFilteredConsoleData(this.getFilteredConsoleData())}
|
||||
</div>
|
||||
</div>
|
||||
</AnimateHeight>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
private expandCollapseConsole() {
|
||||
this.setState({ isExpanded: !this.state.isExpanded });
|
||||
}
|
||||
|
||||
private onExpandCollapseKeyPress = (event: React.KeyboardEvent<HTMLDivElement>): void => {
|
||||
if (event.keyCode === KeyCodes.Space || event.keyCode === KeyCodes.Enter) {
|
||||
this.expandCollapseConsole();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
private onClearNotificationsKeyPress = (event: React.KeyboardEvent<HTMLSpanElement>): void => {
|
||||
if (event.keyCode === KeyCodes.Space || event.keyCode === KeyCodes.Enter) {
|
||||
this.clearNotifications();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
private clearNotifications(): void {
|
||||
this.props.onConsoleDataChange([]);
|
||||
}
|
||||
|
||||
private renderAllFilteredConsoleData(rowData: ConsoleData[]): JSX.Element[] {
|
||||
return rowData.map((item: ConsoleData, index: number) => (
|
||||
<div className="rowData" key={index}>
|
||||
{item.type === ConsoleDataType.Info && <img className="infoIcon" src={InfoIcon} alt="info" />}
|
||||
{item.type === ConsoleDataType.Error && <img className="errorIcon" src={ErrorRedIcon} alt="error" />}
|
||||
{item.type === ConsoleDataType.InProgress && <img className="loaderIcon" src={LoaderIcon} alt="in progress" />}
|
||||
<span className="date">{item.date}</span>
|
||||
<span className="message">{item.message}</span>
|
||||
</div>
|
||||
));
|
||||
}
|
||||
|
||||
private onFilterSelected(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
this.setState({ selectedFilter: event.target.value });
|
||||
}
|
||||
|
||||
private getFilteredConsoleData(): ConsoleData[] {
|
||||
let filterType: ConsoleDataType = null;
|
||||
|
||||
switch (this.state.selectedFilter) {
|
||||
case "All":
|
||||
filterType = null;
|
||||
break;
|
||||
case "In Progress":
|
||||
filterType = ConsoleDataType.InProgress;
|
||||
break;
|
||||
case "Info":
|
||||
filterType = ConsoleDataType.Info;
|
||||
break;
|
||||
case "Error":
|
||||
filterType = ConsoleDataType.Error;
|
||||
break;
|
||||
default:
|
||||
filterType = null;
|
||||
}
|
||||
|
||||
return filterType == null
|
||||
? this.props.consoleData
|
||||
: this.props.consoleData.filter((data: ConsoleData) => data.type === filterType);
|
||||
}
|
||||
|
||||
private setHeaderStatus(statusMessage: string): void {
|
||||
if (this.state.headerStatus === statusMessage) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headerTimeoutId && clearTimeout(this.headerTimeoutId);
|
||||
this.setState({ headerStatus: statusMessage });
|
||||
this.headerTimeoutId = window.setTimeout(
|
||||
() => this.setState({ headerStatus: "" }),
|
||||
ClientDefaults.errorNotificationTimeoutMs
|
||||
);
|
||||
}
|
||||
|
||||
private static extractHeaderStatus(props: NotificationConsoleComponentProps) {
|
||||
if (props.consoleData && props.consoleData.length > 0) {
|
||||
return props.consoleData[0].message.split(":\n")[0];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private onConsoleWasExpanded = (): void => {
|
||||
this.props.onConsoleExpandedChange(this.state.isExpanded);
|
||||
if (this.state.isExpanded) {
|
||||
this.consoleHeaderElement.focus();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
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";
|
||||
|
||||
export class NotificationConsoleComponentAdapter implements ReactAdapter {
|
||||
public parameters: ko.Observable<number>;
|
||||
public container: ViewModels.Explorer;
|
||||
private consoleData: ko.ObservableArray<ConsoleData>;
|
||||
|
||||
constructor(container: ViewModels.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()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`NotificationConsoleComponent renders the console (expanded) 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"
|
||||
>
|
||||
1
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
className="consoleSplitter"
|
||||
/>
|
||||
<span
|
||||
className="headerStatus"
|
||||
>
|
||||
<span
|
||||
className="headerStatusEllipsis"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="expandCollapseButton"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<img
|
||||
alt="collapse console"
|
||||
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="auto"
|
||||
onAnimationEnd={[Function]}
|
||||
style={Object {}}
|
||||
>
|
||||
<div
|
||||
className="notificationConsoleContents"
|
||||
>
|
||||
<div
|
||||
className="notificationConsoleControls"
|
||||
>
|
||||
<label
|
||||
id="consoleFilterLabel"
|
||||
>
|
||||
Filter
|
||||
</label>
|
||||
<select
|
||||
aria-label="All"
|
||||
aria-labelledby="consoleFilterLabel"
|
||||
onChange={[Function]}
|
||||
role="combobox"
|
||||
value="All"
|
||||
>
|
||||
<option
|
||||
key="All"
|
||||
value="All"
|
||||
>
|
||||
All
|
||||
</option>
|
||||
<option
|
||||
key="In Progress"
|
||||
value="In Progress"
|
||||
>
|
||||
In Progress
|
||||
</option>
|
||||
<option
|
||||
key="Info"
|
||||
value="Info"
|
||||
>
|
||||
Info
|
||||
</option>
|
||||
<option
|
||||
key="Error"
|
||||
value="Error"
|
||||
>
|
||||
Error
|
||||
</option>
|
||||
</select>
|
||||
<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
|
||||
className="rowData"
|
||||
key="0"
|
||||
>
|
||||
<img
|
||||
alt="info"
|
||||
className="infoIcon"
|
||||
src=""
|
||||
/>
|
||||
<span
|
||||
className="date"
|
||||
>
|
||||
date
|
||||
</span>
|
||||
<span
|
||||
className="message"
|
||||
>
|
||||
message
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AnimateHeight>
|
||||
</div>
|
||||
`;
|
||||
Reference in New Issue
Block a user