mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
feat: enhance MetricCharts and ChartGroup with metric info handling and modal display; update UptimeBarTooltip styles and Tooltip theme
This commit is contained in:
@@ -3,8 +3,10 @@ import OneUptimeDate from "Common/Types/Date";
|
||||
import XAxisType from "Common/UI/Components/Charts/Types/XAxis/XAxisType";
|
||||
import ChartGroup, {
|
||||
Chart,
|
||||
ChartMetricInfo,
|
||||
ChartType,
|
||||
} from "Common/UI/Components/Charts/ChartGroup/ChartGroup";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import AggregatedResult from "Common/Types/BaseDatabase/AggregatedResult";
|
||||
import { XAxisAggregateType } from "Common/UI/Components/Charts/Types/XAxis/XAxis";
|
||||
import MetricsAggregationType from "Common/Types/Metrics/MetricsAggregationType";
|
||||
@@ -201,6 +203,35 @@ const MetricCharts: FunctionComponent<ComponentProps> = (
|
||||
});
|
||||
}
|
||||
|
||||
// Build metric info for the info icon modal
|
||||
const metricAttributes: Dictionary<string> = {};
|
||||
const filterAttributes:
|
||||
| Dictionary<string | boolean | number>
|
||||
| undefined = queryConfig.metricQueryData.filterData.attributes as
|
||||
| Dictionary<string | boolean | number>
|
||||
| undefined;
|
||||
|
||||
if (filterAttributes) {
|
||||
for (const key of Object.keys(filterAttributes)) {
|
||||
metricAttributes[key] = String(filterAttributes[key]);
|
||||
}
|
||||
}
|
||||
|
||||
const metricInfo: ChartMetricInfo = {
|
||||
metricName:
|
||||
queryConfig.metricQueryData.filterData.metricName?.toString() || "",
|
||||
aggregationType:
|
||||
queryConfig.metricQueryData.filterData.aggegationType?.toString() ||
|
||||
"",
|
||||
attributes:
|
||||
Object.keys(metricAttributes).length > 0
|
||||
? metricAttributes
|
||||
: undefined,
|
||||
groupByAttribute:
|
||||
queryConfig.metricQueryData.filterData.groupByAttribute,
|
||||
unit,
|
||||
};
|
||||
|
||||
const chart: Chart = {
|
||||
id: index.toString(),
|
||||
type: chartType,
|
||||
@@ -209,6 +240,7 @@ const MetricCharts: FunctionComponent<ComponentProps> = (
|
||||
queryConfig.metricQueryData.filterData.metricName?.toString() ||
|
||||
"",
|
||||
description: queryConfig.metricAliasData?.description || "",
|
||||
metricInfo,
|
||||
props: {
|
||||
data: chartSeries,
|
||||
xAxis: {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Text from "../../../../Types/Text";
|
||||
import Dictionary from "../../../../Types/Dictionary";
|
||||
import LineChart, { ComponentProps as LineChartProps } from "../Line/LineChart";
|
||||
import BarChartElement, {
|
||||
ComponentProps as BarChartProps,
|
||||
@@ -6,7 +7,10 @@ import BarChartElement, {
|
||||
import AreaChartElement, {
|
||||
ComponentProps as AreaChartProps,
|
||||
} from "../Area/AreaChart";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import Icon, { SizeProp } from "../../Icon/Icon";
|
||||
import IconProp from "../../../../Types/Icon/IconProp";
|
||||
import Modal, { ModalWidth } from "../../Modal/Modal";
|
||||
import React, { FunctionComponent, ReactElement, useState } from "react";
|
||||
|
||||
export enum ChartType {
|
||||
LINE = "line",
|
||||
@@ -14,12 +18,21 @@ export enum ChartType {
|
||||
AREA = "area",
|
||||
}
|
||||
|
||||
export interface ChartMetricInfo {
|
||||
metricName: string;
|
||||
aggregationType: string;
|
||||
attributes?: Dictionary<string> | undefined;
|
||||
groupByAttribute?: string | undefined;
|
||||
unit?: string | undefined;
|
||||
}
|
||||
|
||||
export interface Chart {
|
||||
id: string;
|
||||
title: string;
|
||||
description?: string | undefined;
|
||||
type: ChartType;
|
||||
props: LineChartProps | BarChartProps | AreaChartProps;
|
||||
metricInfo?: ChartMetricInfo | undefined;
|
||||
}
|
||||
|
||||
export interface ComponentProps {
|
||||
@@ -33,6 +46,8 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
const syncId: string = Text.generateRandomText(10);
|
||||
const [metricInfoModalChart, setMetricInfoModalChart] =
|
||||
useState<ChartMetricInfo | null>(null);
|
||||
|
||||
const isLastChart: (index: number) => boolean = (index: number): boolean => {
|
||||
return index === props.charts.length - 1;
|
||||
@@ -77,33 +92,157 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
||||
}
|
||||
};
|
||||
|
||||
type GetInfoIconFunction = (chart: Chart) => ReactElement;
|
||||
|
||||
const getInfoIcon: GetInfoIconFunction = (chart: Chart): ReactElement => {
|
||||
if (!chart.metricInfo) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="ml-2 inline-flex items-center text-gray-400 hover:text-gray-600 transition-colors"
|
||||
title="View metric details"
|
||||
onClick={() => {
|
||||
setMetricInfoModalChart(chart.metricInfo || null);
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon={IconProp.InformationCircle}
|
||||
size={SizeProp.Small}
|
||||
className="h-4 w-4"
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
const renderMetricInfoModal: () => ReactElement = (): ReactElement => {
|
||||
if (!metricInfoModalChart) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const attributes: Dictionary<string> =
|
||||
metricInfoModalChart.attributes || {};
|
||||
const attributeKeys: Array<string> = Object.keys(attributes);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="Metric Details"
|
||||
onClose={() => {
|
||||
setMetricInfoModalChart(null);
|
||||
}}
|
||||
onSubmit={() => {
|
||||
setMetricInfoModalChart(null);
|
||||
}}
|
||||
submitButtonText="Close"
|
||||
modalWidth={ModalWidth.Normal}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<div className="rounded-lg border border-gray-200 bg-gray-50 p-4">
|
||||
<table className="w-full text-sm">
|
||||
<tbody>
|
||||
<tr className="border-b border-gray-200">
|
||||
<td className="py-2.5 pr-4 font-medium text-gray-500 whitespace-nowrap">
|
||||
Metric Name
|
||||
</td>
|
||||
<td className="py-2.5 text-gray-900 font-mono text-xs">
|
||||
{metricInfoModalChart.metricName}
|
||||
</td>
|
||||
</tr>
|
||||
<tr className="border-b border-gray-200">
|
||||
<td className="py-2.5 pr-4 font-medium text-gray-500 whitespace-nowrap">
|
||||
Aggregation
|
||||
</td>
|
||||
<td className="py-2.5 text-gray-900">
|
||||
{metricInfoModalChart.aggregationType}
|
||||
</td>
|
||||
</tr>
|
||||
{metricInfoModalChart.unit && (
|
||||
<tr className="border-b border-gray-200">
|
||||
<td className="py-2.5 pr-4 font-medium text-gray-500 whitespace-nowrap">
|
||||
Unit
|
||||
</td>
|
||||
<td className="py-2.5 text-gray-900">
|
||||
{metricInfoModalChart.unit}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{metricInfoModalChart.groupByAttribute && (
|
||||
<tr className="border-b border-gray-200">
|
||||
<td className="py-2.5 pr-4 font-medium text-gray-500 whitespace-nowrap">
|
||||
Grouped By
|
||||
</td>
|
||||
<td className="py-2.5 text-gray-900 font-mono text-xs">
|
||||
{metricInfoModalChart.groupByAttribute}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{attributeKeys.length > 0 && (
|
||||
<tr>
|
||||
<td className="py-2.5 pr-4 font-medium text-gray-500 whitespace-nowrap align-top">
|
||||
Attributes
|
||||
</td>
|
||||
<td className="py-2.5">
|
||||
<div className="space-y-1.5">
|
||||
{attributeKeys.map((key: string) => {
|
||||
return (
|
||||
<div key={key} className="flex items-center gap-2">
|
||||
<span className="inline-flex items-center rounded bg-gray-200 px-2 py-0.5 text-xs font-mono text-gray-700">
|
||||
{key}
|
||||
</span>
|
||||
<span className="text-gray-400">=</span>
|
||||
<span className="text-xs text-gray-900 font-mono">
|
||||
{attributes[key]}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
// When hideCard is true, render charts in a clean vertical stack with dividers
|
||||
if (props.hideCard) {
|
||||
return (
|
||||
<div className="space-y-0">
|
||||
{props.charts.map((chart: Chart, index: number) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`${!isLastChart(index) ? "border-b border-gray-100" : ""} ${props.chartCssClass || ""}`}
|
||||
>
|
||||
<div className="px-1 pt-5 pb-4">
|
||||
<div className="mb-1">
|
||||
<h3 className="text-sm font-semibold text-gray-700 tracking-tight">
|
||||
{chart.title}
|
||||
</h3>
|
||||
{chart.description && (
|
||||
<p className="mt-0.5 text-xs text-gray-400 hidden md:block">
|
||||
{chart.description}
|
||||
</p>
|
||||
)}
|
||||
<>
|
||||
{renderMetricInfoModal()}
|
||||
<div className="space-y-0">
|
||||
{props.charts.map((chart: Chart, index: number) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`${!isLastChart(index) ? "border-b border-gray-100" : ""} ${props.chartCssClass || ""}`}
|
||||
>
|
||||
<div className="px-1 pt-5 pb-4">
|
||||
<div className="mb-1">
|
||||
<div className="flex items-center">
|
||||
<h3 className="text-sm font-semibold text-gray-700 tracking-tight">
|
||||
{chart.title}
|
||||
</h3>
|
||||
{getInfoIcon(chart)}
|
||||
</div>
|
||||
{chart.description && (
|
||||
<p className="mt-0.5 text-xs text-gray-400 hidden md:block">
|
||||
{chart.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{getChartContent(chart, index)}
|
||||
</div>
|
||||
{getChartContent(chart, index)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -112,35 +251,41 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
||||
props.charts.length > 1 ? "lg:grid-cols-2" : "lg:grid-cols-1";
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`grid grid-cols-1 ${gridCols} gap-4 space-y-4 lg:space-y-0`}
|
||||
>
|
||||
{props.charts.map((chart: Chart, index: number) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`p-5 rounded-lg border border-gray-200 bg-white shadow-sm ${props.chartCssClass || ""}`}
|
||||
>
|
||||
<h2
|
||||
data-testid="card-details-heading"
|
||||
id="card-details-heading"
|
||||
className="text-base font-semibold leading-6 text-gray-900"
|
||||
<>
|
||||
{renderMetricInfoModal()}
|
||||
<div
|
||||
className={`grid grid-cols-1 ${gridCols} gap-4 space-y-4 lg:space-y-0`}
|
||||
>
|
||||
{props.charts.map((chart: Chart, index: number) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`p-5 rounded-lg border border-gray-200 bg-white shadow-sm ${props.chartCssClass || ""}`}
|
||||
>
|
||||
{chart.title}
|
||||
</h2>
|
||||
{chart.description && (
|
||||
<p
|
||||
data-testid="card-description"
|
||||
className="mt-0.5 text-sm text-gray-500 w-full hidden md:block"
|
||||
>
|
||||
{chart.description}
|
||||
</p>
|
||||
)}
|
||||
{getChartContent(chart, index)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<h2
|
||||
data-testid="card-details-heading"
|
||||
id="card-details-heading"
|
||||
className="text-base font-semibold leading-6 text-gray-900"
|
||||
>
|
||||
{chart.title}
|
||||
</h2>
|
||||
{getInfoIcon(chart)}
|
||||
</div>
|
||||
{chart.description && (
|
||||
<p
|
||||
data-testid="card-description"
|
||||
className="mt-0.5 text-sm text-gray-500 w-full hidden md:block"
|
||||
>
|
||||
{chart.description}
|
||||
</p>
|
||||
)}
|
||||
{getChartContent(chart, index)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -26,50 +26,76 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
|
||||
const uptimeColor: string =
|
||||
props.uptimePercent >= 99.9
|
||||
? "#22c55e"
|
||||
? "#16a34a"
|
||||
: props.uptimePercent >= 99
|
||||
? "#eab308"
|
||||
: "#ef4444";
|
||||
? "#ca8a04"
|
||||
: "#dc2626";
|
||||
|
||||
const uptimeBgColor: string =
|
||||
props.uptimePercent >= 99.9
|
||||
? "#f0fdf4"
|
||||
: props.uptimePercent >= 99
|
||||
? "#fefce8"
|
||||
: "#fef2f2";
|
||||
|
||||
const uptimeTrackColor: string =
|
||||
props.uptimePercent >= 99.9
|
||||
? "#dcfce7"
|
||||
: props.uptimePercent >= 99
|
||||
? "#fef9c3"
|
||||
: "#fee2e2";
|
||||
|
||||
return (
|
||||
<div style={{ minWidth: "240px", maxWidth: "320px", padding: "4px" }}>
|
||||
{/* Header */}
|
||||
<div style={{ minWidth: "260px", maxWidth: "340px" }}>
|
||||
{/* Date header */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
marginBottom: "10px",
|
||||
paddingBottom: "8px",
|
||||
marginBottom: "8px",
|
||||
borderBottom: "1px solid #f3f4f6",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
<div
|
||||
style={{
|
||||
fontWeight: 600,
|
||||
fontSize: "13px",
|
||||
color: "#f3f4f6",
|
||||
color: "#111827",
|
||||
}}
|
||||
>
|
||||
{dateStr}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Uptime bar */}
|
||||
{/* Uptime card */}
|
||||
{props.hasEvents && (
|
||||
<div style={{ marginBottom: "10px" }}>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: uptimeBgColor,
|
||||
borderRadius: "8px",
|
||||
padding: "10px 12px",
|
||||
marginBottom: "10px",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
marginBottom: "4px",
|
||||
alignItems: "baseline",
|
||||
marginBottom: "6px",
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: "11px", color: "#9ca3af" }}>Uptime</span>
|
||||
<span
|
||||
style={{ fontSize: "11px", color: "#6b7280", fontWeight: 500 }}
|
||||
>
|
||||
Uptime
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "13px",
|
||||
fontWeight: 600,
|
||||
fontSize: "18px",
|
||||
fontWeight: 700,
|
||||
color: uptimeColor,
|
||||
fontVariantNumeric: "tabular-nums",
|
||||
lineHeight: 1,
|
||||
}}
|
||||
>
|
||||
{props.uptimePercent.toFixed(2)}%
|
||||
@@ -78,9 +104,9 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "4px",
|
||||
backgroundColor: "#374151",
|
||||
borderRadius: "2px",
|
||||
height: "6px",
|
||||
backgroundColor: uptimeTrackColor,
|
||||
borderRadius: "3px",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
@@ -89,8 +115,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
width: `${Math.min(props.uptimePercent, 100)}%`,
|
||||
height: "100%",
|
||||
backgroundColor: uptimeColor,
|
||||
borderRadius: "2px",
|
||||
transition: "width 0.3s ease",
|
||||
borderRadius: "3px",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@@ -100,19 +125,38 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
{!props.hasEvents && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "#6b7280",
|
||||
backgroundColor: "#f9fafb",
|
||||
borderRadius: "8px",
|
||||
padding: "12px",
|
||||
textAlign: "center",
|
||||
padding: "6px 0",
|
||||
marginBottom: "4px",
|
||||
}}
|
||||
>
|
||||
No data available for this day
|
||||
<div style={{ fontSize: "12px", color: "#9ca3af", fontWeight: 500 }}>
|
||||
No data available for this day
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Status breakdown */}
|
||||
{props.statusDurations.length > 0 && (
|
||||
<div style={{ marginBottom: props.incidents.length > 0 ? "8px" : "0" }}>
|
||||
<div
|
||||
style={{
|
||||
marginBottom: props.incidents.length > 0 ? "10px" : "0",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "10px",
|
||||
color: "#9ca3af",
|
||||
textTransform: "uppercase",
|
||||
letterSpacing: "0.06em",
|
||||
fontWeight: 600,
|
||||
marginBottom: "4px",
|
||||
}}
|
||||
>
|
||||
Status Breakdown
|
||||
</div>
|
||||
{props.statusDurations.map(
|
||||
(status: StatusDuration, index: number) => {
|
||||
return (
|
||||
@@ -122,11 +166,15 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
padding: "3px 0",
|
||||
padding: "4px 0",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{ display: "flex", alignItems: "center", gap: "6px" }}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
@@ -136,17 +184,24 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
backgroundColor: status.color.toString(),
|
||||
display: "inline-block",
|
||||
flexShrink: 0,
|
||||
boxShadow: `0 0 0 2px ${status.color.toString()}30`,
|
||||
}}
|
||||
/>
|
||||
<span style={{ fontSize: "11px", color: "#d1d5db" }}>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "#374151",
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
{status.label}
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "11px",
|
||||
color: status.isDowntime ? "#fbbf24" : "#9ca3af",
|
||||
fontWeight: status.isDowntime ? 500 : 400,
|
||||
fontSize: "12px",
|
||||
color: status.isDowntime ? "#dc2626" : "#6b7280",
|
||||
fontWeight: status.isDowntime ? 600 : 400,
|
||||
fontVariantNumeric: "tabular-nums",
|
||||
}}
|
||||
>
|
||||
@@ -165,22 +220,42 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
{props.incidents.length > 0 && (
|
||||
<div
|
||||
style={{
|
||||
borderTop: "1px solid #374151",
|
||||
paddingTop: "8px",
|
||||
borderTop: "1px solid #f3f4f6",
|
||||
paddingTop: "10px",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "11px",
|
||||
color: "#9ca3af",
|
||||
marginBottom: "6px",
|
||||
textTransform: "uppercase",
|
||||
letterSpacing: "0.05em",
|
||||
fontWeight: 500,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
marginBottom: "8px",
|
||||
}}
|
||||
>
|
||||
{props.incidents.length} Incident
|
||||
{props.incidents.length !== 1 ? "s" : ""}
|
||||
<div
|
||||
style={{
|
||||
fontSize: "10px",
|
||||
color: "#9ca3af",
|
||||
textTransform: "uppercase",
|
||||
letterSpacing: "0.06em",
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
Incidents
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "10px",
|
||||
fontWeight: 600,
|
||||
color: "#dc2626",
|
||||
backgroundColor: "#fef2f2",
|
||||
padding: "1px 8px",
|
||||
borderRadius: "9999px",
|
||||
lineHeight: "1.6",
|
||||
}}
|
||||
>
|
||||
{props.incidents.length}
|
||||
</div>
|
||||
</div>
|
||||
{props.incidents.slice(0, 3).map(
|
||||
(incident: UptimeBarTooltipIncident) => {
|
||||
@@ -188,19 +263,20 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
<div
|
||||
key={incident.id}
|
||||
style={{
|
||||
backgroundColor: "rgba(255,255,255,0.05)",
|
||||
borderRadius: "6px",
|
||||
padding: "6px 8px",
|
||||
marginBottom: "4px",
|
||||
backgroundColor: "#f9fafb",
|
||||
border: "1px solid #f3f4f6",
|
||||
borderRadius: "8px",
|
||||
padding: "8px 10px",
|
||||
marginBottom: "6px",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "#e5e7eb",
|
||||
fontWeight: 500,
|
||||
marginBottom: "3px",
|
||||
lineHeight: "1.3",
|
||||
color: "#111827",
|
||||
fontWeight: 600,
|
||||
marginBottom: "4px",
|
||||
lineHeight: "1.4",
|
||||
}}
|
||||
>
|
||||
{incident.title}
|
||||
@@ -217,13 +293,14 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
<span
|
||||
style={{
|
||||
fontSize: "10px",
|
||||
fontWeight: 500,
|
||||
fontWeight: 600,
|
||||
color: incident.incidentSeverity.color.toString(),
|
||||
backgroundColor:
|
||||
incident.incidentSeverity.color.toString() + "20",
|
||||
padding: "1px 6px",
|
||||
incident.incidentSeverity.color.toString() + "15",
|
||||
border: `1px solid ${incident.incidentSeverity.color.toString()}30`,
|
||||
padding: "1px 8px",
|
||||
borderRadius: "9999px",
|
||||
lineHeight: "1.5",
|
||||
lineHeight: "1.6",
|
||||
}}
|
||||
>
|
||||
{incident.incidentSeverity.name}
|
||||
@@ -233,15 +310,16 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
<span
|
||||
style={{
|
||||
fontSize: "10px",
|
||||
fontWeight: 500,
|
||||
fontWeight: 600,
|
||||
color:
|
||||
incident.currentIncidentState.color.toString(),
|
||||
backgroundColor:
|
||||
incident.currentIncidentState.color.toString() +
|
||||
"20",
|
||||
padding: "1px 6px",
|
||||
"15",
|
||||
border: `1px solid ${incident.currentIncidentState.color.toString()}30`,
|
||||
padding: "1px 8px",
|
||||
borderRadius: "9999px",
|
||||
lineHeight: "1.5",
|
||||
lineHeight: "1.6",
|
||||
}}
|
||||
>
|
||||
{incident.currentIncidentState.name}
|
||||
@@ -256,23 +334,27 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
<div
|
||||
style={{
|
||||
fontSize: "11px",
|
||||
color: "#6b7280",
|
||||
color: "#9ca3af",
|
||||
textAlign: "center",
|
||||
paddingTop: "2px",
|
||||
padding: "2px 0",
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
+{props.incidents.length - 3} more
|
||||
+{props.incidents.length - 3} more incident
|
||||
{props.incidents.length - 3 !== 1 ? "s" : ""}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
fontSize: "10px",
|
||||
color: "#6b7280",
|
||||
color: "#9ca3af",
|
||||
textAlign: "center",
|
||||
marginTop: "6px",
|
||||
marginTop: "8px",
|
||||
fontWeight: 500,
|
||||
letterSpacing: "0.02em",
|
||||
}}
|
||||
>
|
||||
Click bar for full details
|
||||
Click bar to view details
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Tippy from "@tippyjs/react";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import "tippy.js/dist/tippy.css";
|
||||
import "tippy.js/themes/light-border.css";
|
||||
|
||||
export interface ComponentProps {
|
||||
text?: string | undefined;
|
||||
@@ -21,6 +22,9 @@ const Tooltip: FunctionComponent<ComponentProps> = (
|
||||
<span>{props.text}</span>
|
||||
);
|
||||
|
||||
const themeProps: { theme: string } | Record<string, never> =
|
||||
props.richContent ? { theme: "light-border" } : {};
|
||||
|
||||
return (
|
||||
<Tippy
|
||||
key={Math.random()}
|
||||
@@ -29,8 +33,9 @@ const Tooltip: FunctionComponent<ComponentProps> = (
|
||||
trigger="mouseenter focus"
|
||||
hideOnClick={false}
|
||||
maxWidth={props.richContent ? 380 : 350}
|
||||
delay={[100, 0]}
|
||||
delay={[80, 0]}
|
||||
duration={[150, 100]}
|
||||
{...themeProps}
|
||||
aria={{
|
||||
content: "describedby",
|
||||
expanded: "auto",
|
||||
|
||||
Reference in New Issue
Block a user