Skip to content

Commit 8da7ae2

Browse files
Merge pull request #1355 from digma-ai/feature/insight-tooltip
Add Insight metrics tooltip
1 parent e9f7165 commit 8da7ae2

File tree

12 files changed

+175
-30
lines changed

12 files changed

+175
-30
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import * as s from "./styles";
2+
import type { GradientBackgroundProps } from "./types";
3+
4+
export const GradientProgressBar = ({
5+
label,
6+
value,
7+
valueLabel
8+
}: GradientBackgroundProps) => (
9+
<s.Container>
10+
<s.ProgressBarContainer>
11+
<s.Background />
12+
<s.Circle value={value} />
13+
</s.ProgressBarContainer>
14+
<s.Legend>
15+
<s.Label>{label}</s.Label>
16+
<s.ValueLabel>{valueLabel}</s.ValueLabel>
17+
</s.Legend>
18+
</s.Container>
19+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import styled from "styled-components";
2+
import { footnoteRegularTypography } from "../../../../../../../../../../common/App/typographies";
3+
4+
export const Container = styled.div`
5+
display: flex;
6+
flex-direction: column;
7+
gap: 8px;
8+
`;
9+
10+
export const ProgressBarContainer = styled.div`
11+
position: relative;
12+
`;
13+
14+
export const Background = styled.div`
15+
border-radius: 10px;
16+
opacity: 0.7;
17+
background: linear-gradient(90deg, #6ebd9c 0%, #da802d 50.5%, #da2d5f 100%);
18+
height: 4px;
19+
width: 100%;
20+
`;
21+
22+
const CIRCLE_RADIUS = 5; // pixels
23+
24+
export const Circle = styled.div<{ value: number }>`
25+
position: absolute;
26+
border-radius: 50%;
27+
border: 2px solid ${({ theme }) => theme.colors.v3.surface.primary};
28+
background: ${({ theme }) => theme.colors.v3.icon.primary};
29+
width: ${CIRCLE_RADIUS * 2}px;
30+
height: ${CIRCLE_RADIUS * 2}px;
31+
top: 0;
32+
bottom: 0;
33+
margin: auto;
34+
left: ${({ value }) => value - CIRCLE_RADIUS / 2}px;
35+
`;
36+
37+
export const Legend = styled.div`
38+
${footnoteRegularTypography}
39+
40+
display: flex;
41+
justify-content: space-between;
42+
align-items: center;
43+
`;
44+
45+
export const Label = styled.span`
46+
color: ${({ theme }) => theme.colors.v3.text.tertiary};
47+
`;
48+
49+
export const ValueLabel = styled.span`
50+
font-weight: 700;
51+
color: ${({ theme }) => theme.colors.v3.text.primary};
52+
`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface GradientBackgroundProps {
2+
value: number;
3+
label: string;
4+
valueLabel: string;
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { isNumber } from "../../../../../../../../../../../typeGuards/isNumber";
2+
import { intersperse } from "../../../../../../../../../../../utils/intersperse";
3+
import { roundTo } from "../../../../../../../../../../../utils/roundTo";
4+
import { getValueLabel } from "../getValueLabel";
5+
import { GradientProgressBar } from "../GradientProgressBar";
6+
import * as s from "./styles";
7+
import type { InsightIconTooltipProps, Metric } from "./types";
8+
9+
export const InsightIconTooltip = ({
10+
severity,
11+
impact,
12+
criticality
13+
}: InsightIconTooltipProps) => {
14+
const metrics = [
15+
{ label: "Severity", value: severity },
16+
{ label: "Impact", value: impact },
17+
{ label: "Criticality", value: criticality }
18+
].filter((metric) => isNumber(metric.value)) as Metric[];
19+
20+
return (
21+
<s.TitleContainer>
22+
{intersperse(
23+
metrics.map(({ label, value }) => (
24+
<GradientProgressBar
25+
key={label}
26+
label={label}
27+
value={roundTo(value * 100, 0)}
28+
valueLabel={getValueLabel(value)}
29+
/>
30+
)),
31+
(i) => (
32+
<s.Divider key={`separator-${i}`} />
33+
)
34+
)}
35+
</s.TitleContainer>
36+
);
37+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import styled from "styled-components";
2+
3+
export const TitleContainer = styled.div`
4+
display: flex;
5+
flex-direction: column;
6+
gap: 8px;
7+
width: 180px;
8+
padding: 8px;
9+
`;
10+
11+
export const Divider = styled.div`
12+
height: 1px;
13+
background: ${({ theme }) => theme.colors.v3.stroke.tertiary};
14+
width: 100%;
15+
`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export interface InsightIconTooltipProps {
2+
severity?: number;
3+
impact?: number;
4+
criticality: number;
5+
}
6+
7+
export interface Metric {
8+
label: string;
9+
value: number;
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { ValueLabel } from "./types";
2+
3+
export const getValueLabel = (value: number): ValueLabel => {
4+
if (value < 0.2) {
5+
return "Low";
6+
}
7+
8+
if (value < 0.6) {
9+
return "Medium";
10+
}
11+
12+
return "High";
13+
};

src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/InsightHeader/InsightIcon/index.tsx

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,32 @@
1-
import type { InsightTypeInfo } from "../../../../../../../../../../utils/getInsightTypeInfo";
2-
import { roundTo } from "../../../../../../../../../../utils/roundTo";
31
import { Tag } from "../../../../../../../../../common/v3/Tag";
42
import type { TagType } from "../../../../../../../../../common/v3/Tag/types";
3+
import { getValueLabel } from "./getValueLabel";
4+
import { InsightIconTooltip } from "./InsightIconTooltip";
55
import * as s from "./styles";
6-
import type { InsightIconProps } from "./types";
6+
import type { InsightIconProps, ValueLabel } from "./types";
77

8-
export const getTagType = (criticality: number): TagType => {
9-
if (criticality < 0.2) {
10-
return "lowSeverity";
8+
const getTagType = (valueLabel: ValueLabel): TagType => {
9+
switch (valueLabel) {
10+
case "Low":
11+
return "lowSeverity";
12+
case "Medium":
13+
return "mediumSeverity";
14+
case "High":
15+
return "highSeverity";
1116
}
12-
13-
if (criticality < 0.6) {
14-
return "mediumSeverity";
15-
}
16-
17-
return "highSeverity";
18-
};
19-
20-
export const getTagTitle = (
21-
insightTypeInfo: InsightTypeInfo | undefined,
22-
criticality: number
23-
) => {
24-
const title = `${
25-
insightTypeInfo ? `${insightTypeInfo.label}\n` : ""
26-
}Criticality: ${roundTo(criticality * 100, 0)}%`;
27-
28-
return <s.TagTitle>{title}</s.TagTitle>;
2917
};
3018

3119
export const InsightIcon = ({
3220
insightTypeInfo,
21+
severity,
22+
impact,
3323
criticality
3424
}: InsightIconProps) => {
35-
const tagTitle = getTagTitle(insightTypeInfo, criticality);
36-
const tagType = getTagType(criticality);
25+
const tagType = getTagType(getValueLabel(criticality));
26+
3727
return (
3828
<Tag
39-
title={tagTitle}
29+
title={<InsightIconTooltip {...{ severity, impact, criticality }} />}
4030
type={tagType}
4131
content={
4232
<s.InsightIconContainer>
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import styled from "styled-components";
22

3-
export const TagTitle = styled.span`
4-
white-space: pre;
5-
`;
6-
73
export const InsightIconContainer = styled.div`
84
display: flex;
95
`;

src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/InsightHeader/InsightIcon/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,9 @@ import type { InsightTypeInfo } from "../../../../../../../../../../utils/getIns
22

33
export interface InsightIconProps {
44
insightTypeInfo: InsightTypeInfo;
5+
severity?: number;
6+
impact?: number;
57
criticality: number;
68
}
9+
10+
export type ValueLabel = "Low" | "Medium" | "High";

0 commit comments

Comments
 (0)