mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-10-13 15:28:05 +01:00
MSRC fixes for testExplorer and HeatMap (#2183)
* MSRC fixes for testExplorer and HeatMap * MSRC fixes for testExplorer and HeatMap
This commit is contained in:
parent
f8231600d6
commit
e0edaf405c
@ -23,8 +23,6 @@ src/Common/MongoUtility.ts
|
|||||||
src/Common/NotificationsClientBase.ts
|
src/Common/NotificationsClientBase.ts
|
||||||
src/Common/QueriesClient.ts
|
src/Common/QueriesClient.ts
|
||||||
src/Common/Splitter.ts
|
src/Common/Splitter.ts
|
||||||
src/Controls/Heatmap/Heatmap.test.ts
|
|
||||||
src/Controls/Heatmap/Heatmap.ts
|
|
||||||
src/Definitions/datatables.d.ts
|
src/Definitions/datatables.d.ts
|
||||||
src/Definitions/gif.d.ts
|
src/Definitions/gif.d.ts
|
||||||
src/Definitions/globals.d.ts
|
src/Definitions/globals.d.ts
|
||||||
|
@ -76,9 +76,6 @@ let configContext: Readonly<ConfigContext> = {
|
|||||||
`^https:\\/\\/cosmos-db-dataexplorer-germanycentral\\.azurewebsites\\.de$`,
|
`^https:\\/\\/cosmos-db-dataexplorer-germanycentral\\.azurewebsites\\.de$`,
|
||||||
`^https:\\/\\/.*\\.fabric\\.microsoft\\.com$`,
|
`^https:\\/\\/.*\\.fabric\\.microsoft\\.com$`,
|
||||||
`^https:\\/\\/.*\\.powerbi\\.com$`,
|
`^https:\\/\\/.*\\.powerbi\\.com$`,
|
||||||
`^https:\\/\\/.*\\.analysis-df\\.net$`,
|
|
||||||
`^https:\\/\\/.*\\.analysis-df\\.windows\\.net$`,
|
|
||||||
`^https:\\/\\/.*\\.azure-test\\.net$`,
|
|
||||||
`^https:\\/\\/dataexplorer-preview\\.azurewebsites\\.net$`,
|
`^https:\\/\\/dataexplorer-preview\\.azurewebsites\\.net$`,
|
||||||
], // Webpack injects this at build time
|
], // Webpack injects this at build time
|
||||||
gitSha: process.env.GIT_SHA,
|
gitSha: process.env.GIT_SHA,
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html class="no-js" lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="icon" href="data:," />
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="heatmap"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,55 +0,0 @@
|
|||||||
@import "../../../less/Common/Constants";
|
|
||||||
html {
|
|
||||||
font-family: @DataExplorerFont;
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
border: 0px;
|
|
||||||
overflow: hidden;
|
|
||||||
position: fixed;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: @DataExplorerFont;
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
border: 0px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
#heatmap {
|
|
||||||
.dark-theme {
|
|
||||||
color: @BaseLight;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chartTitle {
|
|
||||||
position: absolute;
|
|
||||||
top: 5px;
|
|
||||||
left: 3px;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.noDataMessage {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 10000;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
opacity: 0.97;
|
|
||||||
div {
|
|
||||||
border-color: rgba(204, 204, 204, 0.8);
|
|
||||||
box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.12);
|
|
||||||
padding: 15px 10px;
|
|
||||||
width: calc(55% - 40px);
|
|
||||||
font-size: 13px;
|
|
||||||
text-align: center;
|
|
||||||
border-width: 1px;
|
|
||||||
border-style: solid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,143 +0,0 @@
|
|||||||
import dayjs from "dayjs";
|
|
||||||
import { handleMessage, Heatmap, isDarkTheme } from "./Heatmap";
|
|
||||||
import { PortalTheme } from "./HeatmapDatatypes";
|
|
||||||
|
|
||||||
describe("The Heatmap Control", () => {
|
|
||||||
const dataPoints = {
|
|
||||||
"1": {
|
|
||||||
"2019-06-19T00:59:10Z": {
|
|
||||||
"Normalized Throughput": 0.35,
|
|
||||||
},
|
|
||||||
"2019-06-19T00:48:10Z": {
|
|
||||||
"Normalized Throughput": 0.25,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const chartCaptions = {
|
|
||||||
chartTitle: "chart title",
|
|
||||||
yAxisTitle: "YAxisTitle",
|
|
||||||
tooltipText: "Tooltip text",
|
|
||||||
timeWindow: 123456789,
|
|
||||||
};
|
|
||||||
|
|
||||||
let heatmap: Heatmap;
|
|
||||||
const theme: PortalTheme = 1;
|
|
||||||
const divElement = `<div id="${Heatmap.elementId}"></div>`;
|
|
||||||
|
|
||||||
describe("drawHeatmap rendering", () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
heatmap = new Heatmap(dataPoints, chartCaptions, theme);
|
|
||||||
document.body.innerHTML = divElement;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
document.body.innerHTML = ``;
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call _getChartSettings when drawHeatmap is invoked", () => {
|
|
||||||
const _getChartSettings = jest.spyOn(heatmap, "_getChartSettings");
|
|
||||||
heatmap.drawHeatmap();
|
|
||||||
expect(_getChartSettings).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call _getLayoutSettings when drawHeatmap is invoked", () => {
|
|
||||||
const _getLayoutSettings = jest.spyOn(heatmap, "_getLayoutSettings");
|
|
||||||
heatmap.drawHeatmap();
|
|
||||||
expect(_getLayoutSettings).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call _getChartDisplaySettings when drawHeatmap is invoked", () => {
|
|
||||||
const _getChartDisplaySettings = jest.spyOn(heatmap, "_getChartDisplaySettings");
|
|
||||||
heatmap.drawHeatmap();
|
|
||||||
expect(_getChartDisplaySettings).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("drawHeatmap should render a Heatmap inside the div element", () => {
|
|
||||||
heatmap.drawHeatmap();
|
|
||||||
expect(document.body.innerHTML).not.toEqual(divElement);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("generateMatrixFromMap", () => {
|
|
||||||
it("should massage input data to match output expected", () => {
|
|
||||||
expect(heatmap.generateMatrixFromMap(dataPoints).yAxisPoints).toEqual(["1"]);
|
|
||||||
expect(heatmap.generateMatrixFromMap(dataPoints).dataPoints).toEqual([[0.25, 0.35]]);
|
|
||||||
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints.length).toEqual(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should output the date format to ISO8601 string format", () => {
|
|
||||||
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints[0].slice(10, 11)).toEqual("T");
|
|
||||||
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints[0].slice(-1)).toEqual("Z");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should convert the time to the user's local time", () => {
|
|
||||||
if (dayjs().utcOffset()) {
|
|
||||||
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).not.toEqual([
|
|
||||||
"2019-06-19T00:48:10Z",
|
|
||||||
"2019-06-19T00:59:10Z",
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).toEqual([
|
|
||||||
"2019-06-19T00:48:10Z",
|
|
||||||
"2019-06-19T00:59:10Z",
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("isDarkTheme", () => {
|
|
||||||
it("isDarkTheme should return the correct result", () => {
|
|
||||||
expect(isDarkTheme(PortalTheme.dark)).toEqual(true);
|
|
||||||
expect(isDarkTheme(PortalTheme.azure)).not.toEqual(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("iframe rendering when there is no data", () => {
|
|
||||||
afterEach(() => {
|
|
||||||
document.body.innerHTML = ``;
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should show a no data message with a dark theme", () => {
|
|
||||||
const data = {
|
|
||||||
data: {
|
|
||||||
signature: "pcIframe",
|
|
||||||
data: {
|
|
||||||
chartData: {},
|
|
||||||
chartSettings: {},
|
|
||||||
theme: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
origin: "http://localhost",
|
|
||||||
};
|
|
||||||
|
|
||||||
const divElement = `<div id="${Heatmap.elementId}"></div>`;
|
|
||||||
document.body.innerHTML = divElement;
|
|
||||||
|
|
||||||
handleMessage(data as MessageEvent);
|
|
||||||
expect(document.body.innerHTML).toContain("dark-theme");
|
|
||||||
expect(document.body.innerHTML).toContain("noDataMessage");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should show a no data message with a white theme", () => {
|
|
||||||
const data = {
|
|
||||||
data: {
|
|
||||||
signature: "pcIframe",
|
|
||||||
data: {
|
|
||||||
chartData: {},
|
|
||||||
chartSettings: {},
|
|
||||||
theme: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
origin: "http://localhost",
|
|
||||||
};
|
|
||||||
|
|
||||||
const divElement = `<div id="${Heatmap.elementId}"></div>`;
|
|
||||||
document.body.innerHTML = divElement;
|
|
||||||
|
|
||||||
handleMessage(data as MessageEvent);
|
|
||||||
expect(document.body.innerHTML).not.toContain("dark-theme");
|
|
||||||
expect(document.body.innerHTML).toContain("noDataMessage");
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,272 +0,0 @@
|
|||||||
import dayjs from "dayjs";
|
|
||||||
import * as Plotly from "plotly.js-cartesian-dist-min";
|
|
||||||
import { sendCachedDataMessage, sendReadyMessage } from "../../Common/MessageHandler";
|
|
||||||
import { StyleConstants } from "../../Common/StyleConstants";
|
|
||||||
import { MessageTypes } from "../../Contracts/ExplorerContracts";
|
|
||||||
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
|
|
||||||
import "./Heatmap.less";
|
|
||||||
import {
|
|
||||||
ChartSettings,
|
|
||||||
DataPayload,
|
|
||||||
DisplaySettings,
|
|
||||||
FontSettings,
|
|
||||||
HeatmapCaptions,
|
|
||||||
HeatmapData,
|
|
||||||
LayoutSettings,
|
|
||||||
PartitionTimeStampToData,
|
|
||||||
PortalTheme,
|
|
||||||
} from "./HeatmapDatatypes";
|
|
||||||
|
|
||||||
export class Heatmap {
|
|
||||||
public static readonly elementId: string = "heatmap";
|
|
||||||
|
|
||||||
private _chartData: HeatmapData;
|
|
||||||
private _heatmapCaptions: HeatmapCaptions;
|
|
||||||
private _theme: PortalTheme;
|
|
||||||
private _defaultFontColor: string;
|
|
||||||
|
|
||||||
constructor(data: DataPayload, heatmapCaptions: HeatmapCaptions, theme: PortalTheme) {
|
|
||||||
this._theme = theme;
|
|
||||||
this._defaultFontColor = StyleConstants.BaseDark;
|
|
||||||
this._setThemeColorForChart();
|
|
||||||
this._chartData = this.generateMatrixFromMap(data);
|
|
||||||
this._heatmapCaptions = heatmapCaptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _setThemeColorForChart() {
|
|
||||||
if (isDarkTheme(this._theme)) {
|
|
||||||
this._defaultFontColor = StyleConstants.BaseLight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getFontStyles(size: number = StyleConstants.MediumFontSize, color = "#838383"): FontSettings {
|
|
||||||
return {
|
|
||||||
family: StyleConstants.DataExplorerFont,
|
|
||||||
size,
|
|
||||||
color,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public generateMatrixFromMap(data: DataPayload): HeatmapData {
|
|
||||||
// all keys in data payload, sorted...
|
|
||||||
const rows: string[] = Object.keys(data).sort((a: string, b: string) => {
|
|
||||||
if (parseInt(a) < parseInt(b)) {
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
if (parseInt(a) > parseInt(b)) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const output: HeatmapData = {
|
|
||||||
yAxisPoints: [],
|
|
||||||
dataPoints: [],
|
|
||||||
xAxisPoints: Object.keys(data[rows[0]]).sort((a: string, b: string) => {
|
|
||||||
if (a < b) {
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
if (a > b) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
// go thru all rows and create 2d matrix for heatmap...
|
|
||||||
for (let i = 0; i < rows.length; i++) {
|
|
||||||
output.yAxisPoints.push(rows[i]);
|
|
||||||
const dataPoints: number[] = [];
|
|
||||||
for (let a = 0; a < output.xAxisPoints.length; a++) {
|
|
||||||
const row: PartitionTimeStampToData = data[rows[i]];
|
|
||||||
dataPoints.push(row[output.xAxisPoints[a]]["Normalized Throughput"]);
|
|
||||||
}
|
|
||||||
output.dataPoints.push(dataPoints);
|
|
||||||
}
|
|
||||||
for (let a = 0; a < output.xAxisPoints.length; a++) {
|
|
||||||
const dateTime = output.xAxisPoints[a];
|
|
||||||
// convert to local users timezone...
|
|
||||||
const day = dayjs(new Date(dateTime)).format("YYYY-MM-DD");
|
|
||||||
const hour = dayjs(new Date(dateTime)).format("HH:mm:ss");
|
|
||||||
// coerce to ISOString format since that is what plotly wants...
|
|
||||||
output.xAxisPoints[a] = `${day}T${hour}Z`;
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
// public for testing purposes
|
|
||||||
public _getChartSettings(): ChartSettings[] {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
z: this._chartData.dataPoints,
|
|
||||||
type: "heatmap",
|
|
||||||
zmin: 0,
|
|
||||||
zmid: 50,
|
|
||||||
zmax: 100,
|
|
||||||
colorscale: [
|
|
||||||
[0.0, "#1FD338"],
|
|
||||||
[0.1, "#1CAD2F"],
|
|
||||||
[0.2, "#50A527"],
|
|
||||||
[0.3, "#719F21"],
|
|
||||||
[0.4, "#95991B"],
|
|
||||||
[0.5, "#CE8F11"],
|
|
||||||
[0.6, "#E27F0F"],
|
|
||||||
[0.7, "#E46612"],
|
|
||||||
[0.8, "#E64914"],
|
|
||||||
[0.9, "#B80016"],
|
|
||||||
[1.0, "#B80016"],
|
|
||||||
],
|
|
||||||
name: "",
|
|
||||||
hovertemplate: this._heatmapCaptions.tooltipText,
|
|
||||||
colorbar: {
|
|
||||||
thickness: 15,
|
|
||||||
outlinewidth: 0,
|
|
||||||
tickcolor: StyleConstants.BaseDark,
|
|
||||||
tickfont: this._getFontStyles(10, this._defaultFontColor),
|
|
||||||
},
|
|
||||||
y: this._chartData.yAxisPoints,
|
|
||||||
x: this._chartData.xAxisPoints,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// public for testing purposes
|
|
||||||
public _getLayoutSettings(): LayoutSettings {
|
|
||||||
return {
|
|
||||||
margin: {
|
|
||||||
l: 40,
|
|
||||||
r: 10,
|
|
||||||
b: 35,
|
|
||||||
t: 30,
|
|
||||||
pad: 0,
|
|
||||||
},
|
|
||||||
paper_bgcolor: "transparent",
|
|
||||||
plot_bgcolor: "transparent",
|
|
||||||
width: 462,
|
|
||||||
height: 240,
|
|
||||||
yaxis: {
|
|
||||||
title: this._heatmapCaptions.yAxisTitle,
|
|
||||||
titlefont: this._getFontStyles(11),
|
|
||||||
autorange: true,
|
|
||||||
showgrid: false,
|
|
||||||
zeroline: false,
|
|
||||||
showline: false,
|
|
||||||
autotick: true,
|
|
||||||
fixedrange: true,
|
|
||||||
ticks: "",
|
|
||||||
showticklabels: false,
|
|
||||||
},
|
|
||||||
xaxis: {
|
|
||||||
fixedrange: true,
|
|
||||||
title: "*White area in heatmap indicates there is no available data",
|
|
||||||
titlefont: this._getFontStyles(11),
|
|
||||||
autorange: true,
|
|
||||||
showgrid: false,
|
|
||||||
zeroline: false,
|
|
||||||
showline: false,
|
|
||||||
autotick: true,
|
|
||||||
tickformat: this._heatmapCaptions.timeWindow > 7 ? "%I:%M %p" : "%b %e",
|
|
||||||
showticklabels: true,
|
|
||||||
tickfont: this._getFontStyles(10),
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
text: this._heatmapCaptions.chartTitle,
|
|
||||||
x: 0.01,
|
|
||||||
font: this._getFontStyles(13, this._defaultFontColor),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// public for testing purposes
|
|
||||||
public _getChartDisplaySettings(): DisplaySettings {
|
|
||||||
return {
|
|
||||||
/* heatmap can be fully responsive however the min-height needed in that case is greater than the iframe portal height, hence explicit width + height have been set in _getLayoutSettings
|
|
||||||
responsive: true,*/
|
|
||||||
displayModeBar: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public drawHeatmap(): void {
|
|
||||||
// todo - create random elementId generator so multiple heatmaps can be created - ticket # 431469
|
|
||||||
Plotly.plot(
|
|
||||||
Heatmap.elementId,
|
|
||||||
this._getChartSettings(),
|
|
||||||
this._getLayoutSettings(),
|
|
||||||
this._getChartDisplaySettings(),
|
|
||||||
);
|
|
||||||
const plotDiv: any = document.getElementById(Heatmap.elementId);
|
|
||||||
plotDiv.on("plotly_click", (data: any) => {
|
|
||||||
let timeSelected: string = data.points[0].x;
|
|
||||||
timeSelected = timeSelected.replace(" ", "T");
|
|
||||||
timeSelected = `${timeSelected}Z`;
|
|
||||||
let xAxisIndex = 0;
|
|
||||||
for (let i = 0; i < this._chartData.xAxisPoints.length; i++) {
|
|
||||||
if (this._chartData.xAxisPoints[i] === timeSelected) {
|
|
||||||
xAxisIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const output = [];
|
|
||||||
for (let i = 0; i < this._chartData.dataPoints.length; i++) {
|
|
||||||
output.push(this._chartData.dataPoints[i][xAxisIndex]);
|
|
||||||
}
|
|
||||||
sendCachedDataMessage(MessageTypes.LogInfo, output);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isDarkTheme(theme: PortalTheme) {
|
|
||||||
return theme === PortalTheme.dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function handleMessage(event: MessageEvent) {
|
|
||||||
if (isInvalidParentFrameOrigin(event)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof event.data !== "object" || event.data["signature"] !== "pcIframe") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
typeof event.data.data !== "object" ||
|
|
||||||
!("chartData" in event.data.data) ||
|
|
||||||
!("chartSettings" in event.data.data)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Plotly.purge(Heatmap.elementId);
|
|
||||||
|
|
||||||
document.getElementById(Heatmap.elementId)!.innerHTML = "";
|
|
||||||
const data = event.data.data;
|
|
||||||
const chartData: DataPayload = data.chartData;
|
|
||||||
const chartSettings: HeatmapCaptions = data.chartSettings;
|
|
||||||
const chartTheme: PortalTheme = data.theme;
|
|
||||||
if (Object.keys(chartData).length) {
|
|
||||||
new Heatmap(chartData, chartSettings, chartTheme).drawHeatmap();
|
|
||||||
} else {
|
|
||||||
const chartTitleElement = document.createElement("div");
|
|
||||||
chartTitleElement.innerHTML = data.chartSettings.chartTitle;
|
|
||||||
chartTitleElement.classList.add("chartTitle");
|
|
||||||
|
|
||||||
const noDataMessageElement = document.createElement("div");
|
|
||||||
noDataMessageElement.classList.add("noDataMessage");
|
|
||||||
const noDataMessageContent = document.createElement("div");
|
|
||||||
noDataMessageContent.innerHTML = data.errorMessage;
|
|
||||||
|
|
||||||
noDataMessageElement.appendChild(noDataMessageContent);
|
|
||||||
|
|
||||||
if (isDarkTheme(chartTheme)) {
|
|
||||||
chartTitleElement.classList.add("dark-theme");
|
|
||||||
noDataMessageElement.classList.add("dark-theme");
|
|
||||||
noDataMessageContent.classList.add("dark-theme");
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById(Heatmap.elementId)!.appendChild(chartTitleElement);
|
|
||||||
document.getElementById(Heatmap.elementId)!.appendChild(noDataMessageElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("message", handleMessage, false);
|
|
||||||
sendReadyMessage();
|
|
@ -1,106 +0,0 @@
|
|||||||
type dataPoint = string | number;
|
|
||||||
|
|
||||||
export interface DataPayload {
|
|
||||||
[id: string]: PartitionTimeStampToData;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum PortalTheme {
|
|
||||||
blue = 1,
|
|
||||||
azure,
|
|
||||||
light,
|
|
||||||
dark,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HeatmapData {
|
|
||||||
yAxisPoints: string[];
|
|
||||||
xAxisPoints: string[];
|
|
||||||
dataPoints: dataPoint[][];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HeatmapCaptions {
|
|
||||||
chartTitle: string;
|
|
||||||
yAxisTitle: string;
|
|
||||||
tooltipText: string;
|
|
||||||
timeWindow: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FontSettings {
|
|
||||||
family: string;
|
|
||||||
size: number;
|
|
||||||
color: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LayoutSettings {
|
|
||||||
paper_bgcolor?: string;
|
|
||||||
plot_bgcolor?: string;
|
|
||||||
margin?: {
|
|
||||||
l: number;
|
|
||||||
r: number;
|
|
||||||
b: number;
|
|
||||||
t: number;
|
|
||||||
pad: number;
|
|
||||||
};
|
|
||||||
width?: number;
|
|
||||||
height?: number;
|
|
||||||
yaxis?: {
|
|
||||||
fixedrange: boolean;
|
|
||||||
title: HeatmapCaptions["yAxisTitle"];
|
|
||||||
titlefont: FontSettings;
|
|
||||||
autorange: boolean;
|
|
||||||
showgrid: boolean;
|
|
||||||
zeroline: boolean;
|
|
||||||
showline: boolean;
|
|
||||||
autotick: boolean;
|
|
||||||
ticks: "";
|
|
||||||
showticklabels: boolean;
|
|
||||||
};
|
|
||||||
xaxis?: {
|
|
||||||
fixedrange: boolean;
|
|
||||||
title: string;
|
|
||||||
titlefont: FontSettings;
|
|
||||||
autorange: boolean;
|
|
||||||
showgrid: boolean;
|
|
||||||
zeroline: boolean;
|
|
||||||
showline: boolean;
|
|
||||||
autotick: boolean;
|
|
||||||
showticklabels: boolean;
|
|
||||||
tickformat: string;
|
|
||||||
tickfont: FontSettings;
|
|
||||||
};
|
|
||||||
title?: {
|
|
||||||
text: HeatmapCaptions["chartTitle"];
|
|
||||||
x: number;
|
|
||||||
font?: FontSettings;
|
|
||||||
};
|
|
||||||
font?: FontSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChartSettings {
|
|
||||||
z: HeatmapData["dataPoints"];
|
|
||||||
type: "heatmap";
|
|
||||||
zmin: number;
|
|
||||||
zmid: number;
|
|
||||||
zmax: number;
|
|
||||||
colorscale: [number, string][];
|
|
||||||
name: string;
|
|
||||||
hovertemplate: HeatmapCaptions["tooltipText"];
|
|
||||||
colorbar: {
|
|
||||||
thickness: number;
|
|
||||||
outlinewidth: number;
|
|
||||||
tickcolor: string;
|
|
||||||
tickfont: FontSettings;
|
|
||||||
};
|
|
||||||
y: HeatmapData["yAxisPoints"];
|
|
||||||
x: HeatmapData["xAxisPoints"];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DisplaySettings {
|
|
||||||
displayModeBar: boolean;
|
|
||||||
responsive?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PartitionTimeStampToData {
|
|
||||||
[timeSeriesDates: string]: {
|
|
||||||
[NormalizedThroughput: string]: number;
|
|
||||||
};
|
|
||||||
}
|
|
@ -89,7 +89,7 @@ const initTestExplorer = async (): Promise<void> => {
|
|||||||
iframe.setAttribute("data-test", "DataExplorerFrame");
|
iframe.setAttribute("data-test", "DataExplorerFrame");
|
||||||
iframe.classList.add("iframe");
|
iframe.classList.add("iframe");
|
||||||
iframe.title = "explorer";
|
iframe.title = "explorer";
|
||||||
iframe.src = iframeSrc;
|
iframe.src = iframeSrc; // CodeQL [SM03712] Not used in production, only for testing purposes
|
||||||
document.body.appendChild(iframe);
|
document.body.appendChild(iframe);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -106,23 +106,21 @@ module.exports = function (_env = {}, argv = {}) {
|
|||||||
typescriptRule.use[0].options.compilerOptions = { target: "ES2018" };
|
typescriptRule.use[0].options.compilerOptions = { target: "ES2018" };
|
||||||
}
|
}
|
||||||
|
|
||||||
const plugins = [
|
const entry = {
|
||||||
new CleanWebpackPlugin(),
|
main: "./src/Main.tsx",
|
||||||
new webpack.ProvidePlugin({
|
index: "./src/Index.tsx",
|
||||||
process: "process/browser",
|
quickstart: "./src/quickstart.ts",
|
||||||
Buffer: ["buffer", "Buffer"],
|
hostedExplorer: "./src/HostedExplorer.tsx",
|
||||||
}),
|
terminal: "./src/Terminal/index.ts",
|
||||||
new CreateFileWebpack({
|
cellOutputViewer: "./src/CellOutputViewer/CellOutputViewer.tsx",
|
||||||
path: "./dist",
|
notebookViewer: "./src/NotebookViewer/NotebookViewer.tsx",
|
||||||
fileName: "version.txt",
|
galleryViewer: "./src/GalleryViewer/GalleryViewer.tsx",
|
||||||
content: `${gitSha.trim()} ${new Date().toUTCString()}`,
|
selfServe: "./src/SelfServe/SelfServe.tsx",
|
||||||
}),
|
connectToGitHub: "./src/GitHub/GitHubConnector.ts",
|
||||||
// TODO Enable when @nteract once removed
|
...(mode !== "production" && { testExplorer: "./test/testExplorer/TestExplorer.ts" }),
|
||||||
// ./node_modules/@nteract/markdown/node_modules/@nteract/presentational-components/lib/index.js line 63 breaks this with physical file Icon.js referred to as icon.js
|
};
|
||||||
// new CaseSensitivePathsPlugin(),
|
|
||||||
new MiniCssExtractPlugin({
|
const htmlWebpackPlugins = [
|
||||||
filename: "[name].[contenthash].css",
|
|
||||||
}),
|
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
filename: "explorer.html",
|
filename: "explorer.html",
|
||||||
template: "src/explorer.html",
|
template: "src/explorer.html",
|
||||||
@ -148,16 +146,6 @@ module.exports = function (_env = {}, argv = {}) {
|
|||||||
template: "src/hostedExplorer.html",
|
template: "src/hostedExplorer.html",
|
||||||
chunks: ["hostedExplorer"],
|
chunks: ["hostedExplorer"],
|
||||||
}),
|
}),
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
filename: "testExplorer.html",
|
|
||||||
template: "test/testExplorer/testExplorer.html",
|
|
||||||
chunks: ["testExplorer"],
|
|
||||||
}),
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
filename: "Heatmap.html",
|
|
||||||
template: "src/Controls/Heatmap/Heatmap.html",
|
|
||||||
chunks: ["heatmap"],
|
|
||||||
}),
|
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
filename: "cellOutputViewer.html",
|
filename: "cellOutputViewer.html",
|
||||||
template: "src/CellOutputViewer/cellOutputViewer.html",
|
template: "src/CellOutputViewer/cellOutputViewer.html",
|
||||||
@ -183,6 +171,35 @@ module.exports = function (_env = {}, argv = {}) {
|
|||||||
template: "src/SelfServe/selfServe.html",
|
template: "src/SelfServe/selfServe.html",
|
||||||
chunks: ["selfServe"],
|
chunks: ["selfServe"],
|
||||||
}),
|
}),
|
||||||
|
...(mode !== "production"
|
||||||
|
? [
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
filename: "testExplorer.html",
|
||||||
|
template: "test/testExplorer/testExplorer.html",
|
||||||
|
chunks: ["testExplorer"],
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
];
|
||||||
|
|
||||||
|
const plugins = [
|
||||||
|
new CleanWebpackPlugin(),
|
||||||
|
new webpack.ProvidePlugin({
|
||||||
|
process: "process/browser",
|
||||||
|
Buffer: ["buffer", "Buffer"],
|
||||||
|
}),
|
||||||
|
new CreateFileWebpack({
|
||||||
|
path: "./dist",
|
||||||
|
fileName: "version.txt",
|
||||||
|
content: `${gitSha.trim()} ${new Date().toUTCString()}`,
|
||||||
|
}),
|
||||||
|
// TODO Enable when @nteract once removed
|
||||||
|
// ./node_modules/@nteract/markdown/node_modules/@nteract/presentational-components/lib/index.js line 63 breaks this with physical file Icon.js referred to as icon.js
|
||||||
|
// new CaseSensitivePathsPlugin(),
|
||||||
|
new MiniCssExtractPlugin({
|
||||||
|
filename: "[name].[contenthash].css",
|
||||||
|
}),
|
||||||
|
...htmlWebpackPlugins,
|
||||||
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/cellOutputViewer/]),
|
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/cellOutputViewer/]),
|
||||||
new HTMLInlineCSSWebpackPlugin({
|
new HTMLInlineCSSWebpackPlugin({
|
||||||
filter: (fileName) => fileName.includes("cellOutputViewer"),
|
filter: (fileName) => fileName.includes("cellOutputViewer"),
|
||||||
@ -205,20 +222,7 @@ module.exports = function (_env = {}, argv = {}) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
mode: mode,
|
mode: mode,
|
||||||
entry: {
|
entry: entry,
|
||||||
main: "./src/Main.tsx",
|
|
||||||
index: "./src/Index.tsx",
|
|
||||||
quickstart: "./src/quickstart.ts",
|
|
||||||
hostedExplorer: "./src/HostedExplorer.tsx",
|
|
||||||
testExplorer: "./test/testExplorer/TestExplorer.ts",
|
|
||||||
heatmap: "./src/Controls/Heatmap/Heatmap.ts",
|
|
||||||
terminal: "./src/Terminal/index.ts",
|
|
||||||
cellOutputViewer: "./src/CellOutputViewer/CellOutputViewer.tsx",
|
|
||||||
notebookViewer: "./src/NotebookViewer/NotebookViewer.tsx",
|
|
||||||
galleryViewer: "./src/GalleryViewer/GalleryViewer.tsx",
|
|
||||||
selfServe: "./src/SelfServe/SelfServe.tsx",
|
|
||||||
connectToGitHub: "./src/GitHub/GitHubConnector.ts",
|
|
||||||
},
|
|
||||||
output: {
|
output: {
|
||||||
chunkFilename: "[name].[chunkhash:6].js",
|
chunkFilename: "[name].[chunkhash:6].js",
|
||||||
filename: "[name].[chunkhash:6].js",
|
filename: "[name].[chunkhash:6].js",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user