All files / memory-monitor/src/components/Visualizations TrendIndicator.tsx

58.82% Statements 10/17
58.33% Branches 7/12
40% Functions 2/5
66.66% Lines 10/15

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162                                                            1x                                                                   28x                                       1x                                                 28x 28x 28x   28x 27x                   1x                                                                       1x  
import React from "react";
import clsx from "clsx";
import { TREND_COLORS } from "../../constants";
import type { Trend } from "../../types";
import styles from "./TrendIndicator.module.scss";
 
export interface TrendIndicatorProps {
  /** Current trend direction */
  trend: Trend;
  /** Leak probability (0-100) */
  leakProbability?: number;
  /** Number of history samples */
  sampleCount?: number;
  /** Compact display mode */
  compact?: boolean;
  /** Custom class name */
  className?: string;
}
 
/**
 * Icon props with optional style
 */
interface IconProps {
  className?: string;
  style?: React.CSSProperties;
}
 
/**
 * Trend arrow icons
 */
const TrendIcons: Record<Trend, React.FC<IconProps>> = {
  increasing: ({ className, style }) => (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      strokeWidth="2.5"
      strokeLinecap="round"
      strokeLinejoin="round"
      className={className}
      style={style}
    >
      <path d="M7 17L17 7" />
      <path d="M7 7h10v10" />
    </svg>
  ),
  decreasing: ({ className, style }) => (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      strokeWidth="2.5"
      strokeLinecap="round"
      strokeLinejoin="round"
      className={className}
      style={style}
    >
      <path d="M7 7L17 17" />
      <path d="M17 7v10H7" />
    </svg>
  ),
  stable: ({ className, style }) => (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      strokeWidth="2.5"
      strokeLinecap="round"
      strokeLinejoin="round"
      className={className}
      style={style}
    >
      <path d="M5 12h14" />
      <path d="M12 5l7 7-7 7" />
    </svg>
  ),
};
 
/**
 * Trend labels
 */
const trendLabels: Record<Trend, string> = {
  increasing: "Increasing",
  decreasing: "Decreasing",
  stable: "Stable",
};
 
/**
 * Get leak probability badge level
 */
function getLeakLevel(probability: number): 'high' | 'medium' | 'low' {
  if (probability >= 70) return 'high';
  if (probability >= 40) return 'medium';
  return 'low';
}
 
/**
 * Trend indicator badge component
 */
export function TrendIndicator({
  trend,
  leakProbability,
  sampleCount,
  compact = false,
  className,
}: TrendIndicatorProps) {
  const Icon = TrendIcons[trend];
  const label = trendLabels[trend];
  const color = TREND_COLORS[trend];
 
  if (compact) {
    return (
      <div className={clsx(styles.compactContainer, className)}>
        <Icon className={styles.compactIcon} style={{ color }} />
        <span className={clsx(styles.compactLabel, styles[trend])}>
          {label}
        </span>
      </div>
    );
  }
 
  return (
    <div className={clsx(styles.container, className)}>
      <div className={styles.header}>
        <div className={styles.trendInfo}>
          <div
            className={styles.iconWrapper}
            style={{ backgroundColor: `${color}20` }}
          >
            <Icon className={styles.icon} style={{ color }} />
          </div>
          <div className={styles.trendTextContainer}>
            <div className={styles.trendLabel}>Memory Trend</div>
            <div className={clsx(styles.trendValue, styles[trend])}>
              {label}
            </div>
          </div>
        </div>
 
        {/* Leak probability badge - only show if probability is meaningful (>= 30%) */}
        {leakProbability !== undefined && leakProbability >= 30 && (
          <div className={clsx(styles.leakBadge, styles[getLeakLevel(leakProbability)])}>
            {leakProbability.toFixed(0)}% risk
          </div>
        )}
      </div>
 
      {/* Sample count */}
      {sampleCount !== undefined && (
        <div className={styles.sampleCount}>
          Based on {sampleCount} samples
        </div>
      )}
    </div>
  );
}
 
TrendIndicator.displayName = "TrendIndicator";