mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-08 01:37:54 +00:00
Split dashboard metric bar runtime owners
This commit is contained in:
parent
5117ed0abc
commit
ce9974d1cf
9 changed files with 266 additions and 87 deletions
|
|
@ -39,33 +39,38 @@ regression protection.
|
|||
17. `frontend-modern/src/components/Dashboard/StackedMemoryBar.tsx`
|
||||
18. `frontend-modern/src/components/Dashboard/stackedMemoryBarModel.ts`
|
||||
19. `frontend-modern/src/components/Dashboard/useStackedMemoryBarState.ts`
|
||||
20. `frontend-modern/src/components/Dashboard/DiskList.tsx`
|
||||
21. `frontend-modern/src/components/Dashboard/diskListModel.ts`
|
||||
22. `frontend-modern/src/components/Dashboard/useDiskListState.ts`
|
||||
23. `frontend-modern/src/components/Dashboard/GuestRow.tsx`
|
||||
24. `frontend-modern/src/components/Dashboard/guestRowModel.tsx`
|
||||
25. `frontend-modern/src/components/Dashboard/useGuestRowState.ts`
|
||||
26. `frontend-modern/src/components/Dashboard/GuestDrawer.tsx`
|
||||
27. `frontend-modern/src/components/Dashboard/guestDrawerModel.ts`
|
||||
28. `frontend-modern/src/components/Dashboard/useGuestDrawerState.ts`
|
||||
29. `frontend-modern/src/components/Dashboard/workloadSelectors.ts`
|
||||
30. `frontend-modern/src/components/Infrastructure/UnifiedResourceTable.tsx`
|
||||
31. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts`
|
||||
32. `frontend-modern/src/components/Infrastructure/infrastructureSelectors.ts`
|
||||
33. `frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts`
|
||||
34. `frontend-modern/src/components/Dashboard/__tests__/Dashboard.performance.contract.test.tsx`
|
||||
35. `frontend-modern/src/components/Dashboard/__tests__/DashboardFilter.test.tsx`
|
||||
36. `frontend-modern/src/components/Dashboard/__tests__/useDashboardFilterState.test.ts`
|
||||
37. `frontend-modern/src/components/Dashboard/ThresholdSlider.test.tsx`
|
||||
38. `frontend-modern/src/components/Dashboard/__tests__/useThresholdSliderState.test.ts`
|
||||
39. `frontend-modern/src/components/Dashboard/__tests__/StackedDiskBar.test.tsx`
|
||||
40. `frontend-modern/src/components/Dashboard/__tests__/useStackedDiskBarState.test.tsx`
|
||||
41. `frontend-modern/src/components/Dashboard/StackedMemoryBar.test.tsx`
|
||||
42. `frontend-modern/src/components/Dashboard/__tests__/useStackedMemoryBarState.test.tsx`
|
||||
43. `frontend-modern/src/components/Dashboard/__tests__/DiskList.test.tsx`
|
||||
44. `frontend-modern/src/components/Dashboard/__tests__/GuestRow.test.tsx`
|
||||
45. `frontend-modern/src/components/Dashboard/GuestDrawer.test.tsx`
|
||||
46. `frontend-modern/src/components/Infrastructure/__tests__/UnifiedResourceTable.performance.contract.test.tsx`
|
||||
20. `frontend-modern/src/components/Dashboard/MetricBar.tsx`
|
||||
21. `frontend-modern/src/components/Dashboard/metricBarModel.ts`
|
||||
22. `frontend-modern/src/components/Dashboard/useMetricBarState.ts`
|
||||
23. `frontend-modern/src/components/Dashboard/DiskList.tsx`
|
||||
24. `frontend-modern/src/components/Dashboard/diskListModel.ts`
|
||||
25. `frontend-modern/src/components/Dashboard/useDiskListState.ts`
|
||||
26. `frontend-modern/src/components/Dashboard/GuestRow.tsx`
|
||||
27. `frontend-modern/src/components/Dashboard/guestRowModel.tsx`
|
||||
28. `frontend-modern/src/components/Dashboard/useGuestRowState.ts`
|
||||
29. `frontend-modern/src/components/Dashboard/GuestDrawer.tsx`
|
||||
30. `frontend-modern/src/components/Dashboard/guestDrawerModel.ts`
|
||||
31. `frontend-modern/src/components/Dashboard/useGuestDrawerState.ts`
|
||||
32. `frontend-modern/src/components/Dashboard/workloadSelectors.ts`
|
||||
33. `frontend-modern/src/components/Infrastructure/UnifiedResourceTable.tsx`
|
||||
34. `frontend-modern/src/components/Infrastructure/useUnifiedResourceTableState.ts`
|
||||
35. `frontend-modern/src/components/Infrastructure/infrastructureSelectors.ts`
|
||||
36. `frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts`
|
||||
37. `frontend-modern/src/components/Dashboard/__tests__/Dashboard.performance.contract.test.tsx`
|
||||
38. `frontend-modern/src/components/Dashboard/__tests__/DashboardFilter.test.tsx`
|
||||
39. `frontend-modern/src/components/Dashboard/__tests__/useDashboardFilterState.test.ts`
|
||||
40. `frontend-modern/src/components/Dashboard/MetricBar.test.tsx`
|
||||
41. `frontend-modern/src/components/Dashboard/__tests__/useMetricBarState.test.tsx`
|
||||
42. `frontend-modern/src/components/Dashboard/ThresholdSlider.test.tsx`
|
||||
43. `frontend-modern/src/components/Dashboard/__tests__/useThresholdSliderState.test.ts`
|
||||
44. `frontend-modern/src/components/Dashboard/__tests__/StackedDiskBar.test.tsx`
|
||||
45. `frontend-modern/src/components/Dashboard/__tests__/useStackedDiskBarState.test.tsx`
|
||||
46. `frontend-modern/src/components/Dashboard/StackedMemoryBar.test.tsx`
|
||||
47. `frontend-modern/src/components/Dashboard/__tests__/useStackedMemoryBarState.test.tsx`
|
||||
48. `frontend-modern/src/components/Dashboard/__tests__/DiskList.test.tsx`
|
||||
49. `frontend-modern/src/components/Dashboard/__tests__/GuestRow.test.tsx`
|
||||
50. `frontend-modern/src/components/Dashboard/GuestDrawer.test.tsx`
|
||||
51. `frontend-modern/src/components/Infrastructure/__tests__/UnifiedResourceTable.performance.contract.test.tsx`
|
||||
|
||||
## Shared Boundaries
|
||||
|
||||
|
|
@ -92,6 +97,7 @@ regression protection.
|
|||
13. Extend threshold-slider value-position math, title/label derivation, and drag scroll-lock runtime through `frontend-modern/src/components/Dashboard/thresholdSliderModel.ts` and `frontend-modern/src/components/Dashboard/useThresholdSliderState.ts` rather than rebuilding slider-local state and pointer lifecycle inside `frontend-modern/src/components/Dashboard/ThresholdSlider.tsx`
|
||||
14. Extend stacked disk-bar capacity math, segment/tooltip derivation, and resize-observer runtime through `frontend-modern/src/components/Dashboard/stackedDiskBarModel.ts` and `frontend-modern/src/components/Dashboard/useStackedDiskBarState.ts` rather than rebuilding disk-bar-local state, mode branching, and tooltip shaping inside `frontend-modern/src/components/Dashboard/StackedDiskBar.tsx`
|
||||
15. Extend stacked memory-bar capacity math, balloon/swap derivation, and resize-observer runtime through `frontend-modern/src/components/Dashboard/stackedMemoryBarModel.ts` and `frontend-modern/src/components/Dashboard/useStackedMemoryBarState.ts` rather than rebuilding memory-bar-local state, tooltip shaping, and label-fit logic inside `frontend-modern/src/components/Dashboard/StackedMemoryBar.tsx`
|
||||
16. Extend metric-bar width, label-fit logic, and resize-observer runtime through `frontend-modern/src/components/Dashboard/metricBarModel.ts` and `frontend-modern/src/components/Dashboard/useMetricBarState.ts` rather than rebuilding metric-local state and threshold mapping inside `frontend-modern/src/components/Dashboard/MetricBar.tsx`
|
||||
|
||||
## Forbidden Paths
|
||||
|
||||
|
|
@ -183,6 +189,14 @@ resize-observer plus tooltip lifecycle live in
|
|||
Future memory-bar runtime changes must extend through those owners instead of
|
||||
reintroducing mixed resize state, balloon branching, and tooltip shaping into
|
||||
the shell.
|
||||
The dashboard metric bar now follows that same pattern: the shell stays in
|
||||
`frontend-modern/src/components/Dashboard/MetricBar.tsx`, while width,
|
||||
show-label, sublabel-fit, and threshold-color derivation live in
|
||||
`frontend-modern/src/components/Dashboard/metricBarModel.ts` and
|
||||
resize-observer lifecycle lives in
|
||||
`frontend-modern/src/components/Dashboard/useMetricBarState.ts`. Future
|
||||
metric-bar runtime changes must extend through those owners instead of
|
||||
reintroducing mixed resize state and label-fit logic into the shell.
|
||||
|
||||
The unified resource table hot path is now also governed as explicit
|
||||
performance-owned runtime, with shared ownership against the unified-resource
|
||||
|
|
|
|||
|
|
@ -2526,6 +2526,8 @@
|
|||
"frontend-modern/src/components/Dashboard/guestDrawerModel.ts",
|
||||
"frontend-modern/src/components/Dashboard/GuestRow.tsx",
|
||||
"frontend-modern/src/components/Dashboard/guestRowModel.tsx",
|
||||
"frontend-modern/src/components/Dashboard/MetricBar.tsx",
|
||||
"frontend-modern/src/components/Dashboard/metricBarModel.ts",
|
||||
"frontend-modern/src/components/Dashboard/StackedDiskBar.tsx",
|
||||
"frontend-modern/src/components/Dashboard/stackedDiskBarModel.ts",
|
||||
"frontend-modern/src/components/Dashboard/StackedMemoryBar.tsx",
|
||||
|
|
@ -2537,6 +2539,7 @@
|
|||
"frontend-modern/src/components/Dashboard/useDiskListState.ts",
|
||||
"frontend-modern/src/components/Dashboard/useGuestDrawerState.ts",
|
||||
"frontend-modern/src/components/Dashboard/useGuestRowState.ts",
|
||||
"frontend-modern/src/components/Dashboard/useMetricBarState.ts",
|
||||
"frontend-modern/src/components/Dashboard/useStackedDiskBarState.ts",
|
||||
"frontend-modern/src/components/Dashboard/useStackedMemoryBarState.ts",
|
||||
"frontend-modern/src/components/Dashboard/useThresholdSliderState.ts",
|
||||
|
|
@ -2558,9 +2561,11 @@
|
|||
"frontend-modern/src/components/Dashboard/__tests__/DiskList.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/StackedDiskBar.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/useDashboardFilterState.test.ts",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/useMetricBarState.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/useStackedDiskBarState.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/useStackedMemoryBarState.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/useThresholdSliderState.test.ts",
|
||||
"frontend-modern/src/components/Dashboard/MetricBar.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/StackedMemoryBar.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/ThresholdSlider.test.tsx",
|
||||
"internal/api/router_bench_test.go",
|
||||
|
|
@ -2601,6 +2606,8 @@
|
|||
"frontend-modern/src/components/Dashboard/guestDrawerModel.ts",
|
||||
"frontend-modern/src/components/Dashboard/GuestRow.tsx",
|
||||
"frontend-modern/src/components/Dashboard/guestRowModel.tsx",
|
||||
"frontend-modern/src/components/Dashboard/MetricBar.tsx",
|
||||
"frontend-modern/src/components/Dashboard/metricBarModel.ts",
|
||||
"frontend-modern/src/components/Dashboard/StackedDiskBar.tsx",
|
||||
"frontend-modern/src/components/Dashboard/stackedDiskBarModel.ts",
|
||||
"frontend-modern/src/components/Dashboard/StackedMemoryBar.tsx",
|
||||
|
|
@ -2612,6 +2619,7 @@
|
|||
"frontend-modern/src/components/Dashboard/useDiskListState.ts",
|
||||
"frontend-modern/src/components/Dashboard/useGuestDrawerState.ts",
|
||||
"frontend-modern/src/components/Dashboard/useGuestRowState.ts",
|
||||
"frontend-modern/src/components/Dashboard/useMetricBarState.ts",
|
||||
"frontend-modern/src/components/Dashboard/useStackedDiskBarState.ts",
|
||||
"frontend-modern/src/components/Dashboard/useStackedMemoryBarState.ts",
|
||||
"frontend-modern/src/components/Dashboard/useThresholdSliderState.ts",
|
||||
|
|
@ -2630,11 +2638,13 @@
|
|||
"frontend-modern/src/components/Dashboard/__tests__/GuestRow.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/StackedDiskBar.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/useDashboardFilterState.test.ts",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/useMetricBarState.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/useStackedDiskBarState.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/useStackedMemoryBarState.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/useThresholdSliderState.test.ts",
|
||||
"frontend-modern/src/components/Dashboard/__tests__/workloadSelectors.test.ts",
|
||||
"frontend-modern/src/components/Dashboard/GuestDrawer.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/MetricBar.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/StackedMemoryBar.test.tsx",
|
||||
"frontend-modern/src/components/Dashboard/ThresholdSlider.test.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/__tests__/UnifiedResourceTable.performance.contract.test.tsx"
|
||||
|
|
|
|||
|
|
@ -1,74 +1,27 @@
|
|||
import { Show, createMemo, createSignal, onMount, onCleanup } from 'solid-js';
|
||||
import { Show } from 'solid-js';
|
||||
import { ProgressBar } from '@/components/shared/ProgressBar';
|
||||
import { estimateTextWidth } from '@/utils/format';
|
||||
import { getMetricColorClass } from '@/utils/metricThresholds';
|
||||
import type { MetricType } from '@/utils/metricThresholds';
|
||||
|
||||
interface MetricBarProps {
|
||||
value: number;
|
||||
label: string;
|
||||
sublabel?: string;
|
||||
showLabel?: boolean;
|
||||
type?: 'cpu' | 'memory' | 'disk' | 'generic';
|
||||
resourceId?: string;
|
||||
class?: string;
|
||||
}
|
||||
import { type MetricBarProps } from './metricBarModel';
|
||||
import { useMetricBarState } from './useMetricBarState';
|
||||
|
||||
export function MetricBar(props: MetricBarProps) {
|
||||
const width = createMemo(() => Math.min(props.value, 100));
|
||||
|
||||
// Track container width
|
||||
const [containerWidth, setContainerWidth] = createSignal(100);
|
||||
let containerRef: HTMLDivElement | undefined;
|
||||
|
||||
// Set up ResizeObserver to track container width changes
|
||||
onMount(() => {
|
||||
if (!containerRef) return;
|
||||
|
||||
setContainerWidth(containerRef.offsetWidth);
|
||||
|
||||
const observer = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
setContainerWidth(entry.contentRect.width);
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(containerRef);
|
||||
|
||||
onCleanup(() => observer.disconnect());
|
||||
});
|
||||
|
||||
// Determine if sublabel fits based on estimated text width
|
||||
const showSublabel = createMemo(() => {
|
||||
if (props.showLabel === false) return false;
|
||||
if (!props.sublabel) return false;
|
||||
const fullText = `${props.label} (${props.sublabel})`;
|
||||
const estimatedWidth = estimateTextWidth(fullText);
|
||||
return containerWidth() >= estimatedWidth;
|
||||
});
|
||||
|
||||
const showLabel = createMemo(() => props.showLabel !== false && props.label.trim().length > 0);
|
||||
|
||||
// Get color class from centralized thresholds
|
||||
const progressColorClass = createMemo(() => {
|
||||
const metric = props.type || 'cpu';
|
||||
// 'generic' falls back to cpu thresholds
|
||||
const metricType: MetricType = metric === 'generic' ? 'cpu' : metric;
|
||||
return getMetricColorClass(props.value, metricType);
|
||||
});
|
||||
const state = useMetricBarState(props);
|
||||
const presentation = state.presentation;
|
||||
|
||||
return (
|
||||
<div ref={containerRef} class="metric-text w-full h-4 flex items-center justify-center min-w-0">
|
||||
<div
|
||||
ref={state.setContainerRef}
|
||||
class="metric-text w-full h-4 flex items-center justify-center min-w-0"
|
||||
>
|
||||
<ProgressBar
|
||||
value={width()}
|
||||
value={presentation().width}
|
||||
class={`h-full ${props.class || ''}`}
|
||||
fillClass={progressColorClass()}
|
||||
fillClass={presentation().progressColorClass}
|
||||
label={
|
||||
<Show when={showLabel()}>
|
||||
<Show when={presentation().showLabel}>
|
||||
<span class="absolute inset-0 flex items-center justify-center text-[10px] font-semibold text-base-content leading-none min-w-0 overflow-hidden">
|
||||
<span class="max-w-full min-w-0 whitespace-nowrap overflow-hidden text-ellipsis px-0.5 text-center">
|
||||
<span>{props.label}</span>
|
||||
<Show when={showSublabel()}>
|
||||
<Show when={presentation().showSublabel}>
|
||||
<span class="metric-sublabel font-normal text-muted"> ({props.sublabel})</span>
|
||||
</Show>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ import stackedMemoryBarStateSource from '../useStackedMemoryBarState.ts?raw';
|
|||
import diskListSource from '../DiskList.tsx?raw';
|
||||
import diskListModelSource from '../diskListModel.ts?raw';
|
||||
import diskListStateSource from '../useDiskListState.ts?raw';
|
||||
import metricBarSource from '../MetricBar.tsx?raw';
|
||||
import metricBarModelSource from '../metricBarModel.ts?raw';
|
||||
import metricBarStateSource from '../useMetricBarState.ts?raw';
|
||||
import guestDrawerSource from '../GuestDrawer.tsx?raw';
|
||||
import guestDrawerModelSource from '../guestDrawerModel.ts?raw';
|
||||
import guestRowSource from '../GuestRow.tsx?raw';
|
||||
|
|
@ -542,6 +545,17 @@ describe('Dashboard performance contract', () => {
|
|||
expect(stackedMemoryBarModelSource).toContain('tooltipRows');
|
||||
});
|
||||
|
||||
it('keeps metric bar runtime and derivations in canonical owners', () => {
|
||||
expect(metricBarSource).toContain('useMetricBarState');
|
||||
expect(metricBarSource).not.toContain('const [containerWidth, setContainerWidth] =');
|
||||
expect(metricBarSource).not.toContain('const progressColorClass = createMemo(() => {');
|
||||
expect(metricBarSource).not.toContain('const showSublabel = createMemo(() => {');
|
||||
expect(metricBarStateSource).toContain('new ResizeObserver');
|
||||
expect(metricBarModelSource).toContain('export function buildMetricBarPresentation');
|
||||
expect(metricBarModelSource).toContain('estimateTextWidth');
|
||||
expect(metricBarModelSource).toContain('getMetricColorClass');
|
||||
});
|
||||
|
||||
it('keeps guest row contract and hot-path state in canonical row owners', () => {
|
||||
expect(guestRowSource).toContain('useGuestRowState');
|
||||
expect(guestRowSource).not.toContain('export const GUEST_COLUMNS');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { cleanup, render } from '@solidjs/testing-library';
|
||||
import { useMetricBarState } from '@/components/Dashboard/useMetricBarState';
|
||||
|
||||
class MockResizeObserver {
|
||||
callback: ResizeObserverCallback;
|
||||
disconnect = vi.fn();
|
||||
observe = vi.fn();
|
||||
|
||||
constructor(callback: ResizeObserverCallback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
trigger(width: number) {
|
||||
this.callback(
|
||||
[
|
||||
{
|
||||
contentRect: { width } as DOMRectReadOnly,
|
||||
} as ResizeObserverEntry,
|
||||
],
|
||||
this as unknown as ResizeObserver,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const originalResizeObserver = globalThis.ResizeObserver;
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
globalThis.ResizeObserver = originalResizeObserver;
|
||||
});
|
||||
|
||||
describe('useMetricBarState', () => {
|
||||
it('centralizes metric bar derivations and resize observer cleanup', () => {
|
||||
const observers: MockResizeObserver[] = [];
|
||||
globalThis.ResizeObserver = class extends MockResizeObserver {
|
||||
constructor(callback: ResizeObserverCallback) {
|
||||
super(callback);
|
||||
observers.push(this);
|
||||
}
|
||||
} as unknown as typeof ResizeObserver;
|
||||
|
||||
let captured: ReturnType<typeof useMetricBarState> | undefined;
|
||||
|
||||
const Harness = () => {
|
||||
captured = useMetricBarState({
|
||||
value: 75,
|
||||
label: 'Memory',
|
||||
sublabel: '6 GB / 8 GB',
|
||||
type: 'memory',
|
||||
});
|
||||
return <div ref={captured.setContainerRef} />;
|
||||
};
|
||||
|
||||
const { unmount } = render(() => <Harness />);
|
||||
|
||||
expect(captured).toBeDefined();
|
||||
expect(observers).toHaveLength(1);
|
||||
expect(observers[0].observe).toHaveBeenCalled();
|
||||
expect(captured!.presentation().progressColorClass).toContain('warning');
|
||||
|
||||
observers[0].trigger(320);
|
||||
expect(captured!.presentation().showSublabel).toBe(true);
|
||||
|
||||
unmount();
|
||||
expect(observers[0].disconnect).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
42
frontend-modern/src/components/Dashboard/metricBarModel.ts
Normal file
42
frontend-modern/src/components/Dashboard/metricBarModel.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { estimateTextWidth } from '@/utils/format';
|
||||
import { getMetricColorClass } from '@/utils/metricThresholds';
|
||||
import type { MetricType } from '@/utils/metricThresholds';
|
||||
|
||||
export interface MetricBarProps {
|
||||
value: number;
|
||||
label: string;
|
||||
sublabel?: string;
|
||||
showLabel?: boolean;
|
||||
type?: 'cpu' | 'memory' | 'disk' | 'generic';
|
||||
resourceId?: string;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
export interface MetricBarPresentation {
|
||||
progressColorClass: string;
|
||||
showLabel: boolean;
|
||||
showSublabel: boolean;
|
||||
width: number;
|
||||
}
|
||||
|
||||
export function buildMetricBarPresentation(
|
||||
props: MetricBarProps,
|
||||
containerWidth: number,
|
||||
): MetricBarPresentation {
|
||||
const width = Math.min(props.value, 100);
|
||||
const showLabel = props.showLabel !== false && props.label.trim().length > 0;
|
||||
const showSublabel =
|
||||
showLabel &&
|
||||
Boolean(props.sublabel) &&
|
||||
containerWidth >= estimateTextWidth(`${props.label} (${props.sublabel})`);
|
||||
const metric = props.type || 'cpu';
|
||||
const metricType: MetricType = metric === 'generic' ? 'cpu' : metric;
|
||||
|
||||
return {
|
||||
progressColorClass: getMetricColorClass(props.value, metricType),
|
||||
showLabel,
|
||||
showSublabel,
|
||||
width,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import { createMemo, createSignal, onCleanup, onMount } from 'solid-js';
|
||||
import { buildMetricBarPresentation, type MetricBarProps } from './metricBarModel';
|
||||
|
||||
export function useMetricBarState(props: MetricBarProps) {
|
||||
const [containerWidth, setContainerWidth] = createSignal(100);
|
||||
let containerRef: HTMLDivElement | undefined;
|
||||
let resizeObserver: ResizeObserver | undefined;
|
||||
|
||||
const presentation = createMemo(() => buildMetricBarPresentation(props, containerWidth()));
|
||||
|
||||
onMount(() => {
|
||||
if (!containerRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
setContainerWidth(containerRef.offsetWidth);
|
||||
|
||||
resizeObserver = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
setContainerWidth(entry.contentRect.width);
|
||||
}
|
||||
});
|
||||
resizeObserver.observe(containerRef);
|
||||
});
|
||||
|
||||
onCleanup(() => {
|
||||
resizeObserver?.disconnect();
|
||||
});
|
||||
|
||||
return {
|
||||
presentation,
|
||||
setContainerRef: (element: HTMLDivElement) => {
|
||||
containerRef = element;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +112,9 @@ import guestRowStateSource from '@/components/Dashboard/useGuestRowState.ts?raw'
|
|||
import dashboardDiskListSource from '@/components/Dashboard/DiskList.tsx?raw';
|
||||
import dashboardDiskListModelSource from '@/components/Dashboard/diskListModel.ts?raw';
|
||||
import dashboardDiskListStateSource from '@/components/Dashboard/useDiskListState.ts?raw';
|
||||
import metricBarSource from '@/components/Dashboard/MetricBar.tsx?raw';
|
||||
import metricBarModelSource from '@/components/Dashboard/metricBarModel.ts?raw';
|
||||
import metricBarStateSource from '@/components/Dashboard/useMetricBarState.ts?raw';
|
||||
import guestDrawerSource from '@/components/Dashboard/GuestDrawer.tsx?raw';
|
||||
import guestDrawerModelSource from '@/components/Dashboard/guestDrawerModel.ts?raw';
|
||||
import guestDrawerStateSource from '@/components/Dashboard/useGuestDrawerState.ts?raw';
|
||||
|
|
@ -548,6 +551,13 @@ describe('frontend resource type boundaries', () => {
|
|||
'export function buildStackedMemoryBarPresentation',
|
||||
);
|
||||
expect(stackedMemoryBarModelSource).toContain('const MEMORY_COLORS');
|
||||
expect(metricBarSource).toContain('useMetricBarState');
|
||||
expect(metricBarSource).not.toContain('const [containerWidth, setContainerWidth] =');
|
||||
expect(metricBarSource).not.toContain('const progressColorClass = createMemo(() => {');
|
||||
expect(metricBarSource).not.toContain('const showSublabel = createMemo(() => {');
|
||||
expect(metricBarStateSource).toContain('new ResizeObserver');
|
||||
expect(metricBarModelSource).toContain('export function buildMetricBarPresentation');
|
||||
expect(metricBarModelSource).toContain('estimateTextWidth');
|
||||
expect(workloadsSummarySource).toContain('normalizeOrgScope(getOrgID())');
|
||||
expect(workloadsSummarySource).not.toContain("const DEFAULT_ORG_SCOPE = 'default'");
|
||||
expect(workloadsSummarySource).not.toContain('const normalizeOrgScope =');
|
||||
|
|
|
|||
|
|
@ -1981,6 +1981,36 @@ class SubsystemLookupTest(unittest.TestCase):
|
|||
"dashboard-workload-hot-path",
|
||||
)
|
||||
|
||||
def test_lookup_paths_assigns_dashboard_metric_bar_runtime_to_performance_and_scalability(self) -> None:
|
||||
result = lookup_paths(
|
||||
[
|
||||
"frontend-modern/src/components/Dashboard/MetricBar.tsx",
|
||||
"frontend-modern/src/components/Dashboard/metricBarModel.ts",
|
||||
"frontend-modern/src/components/Dashboard/useMetricBarState.ts",
|
||||
]
|
||||
)
|
||||
self.assertEqual(result["unowned_runtime_files"], [])
|
||||
self.assertEqual(
|
||||
{item["subsystem"] for item in result["impacted_subsystems"]},
|
||||
{"performance-and-scalability"},
|
||||
)
|
||||
for file_entry in result["files"]:
|
||||
self.assertEqual(file_entry["classification"], "runtime")
|
||||
self.assertEqual(
|
||||
{match["subsystem"] for match in file_entry["matches"]},
|
||||
{"performance-and-scalability"},
|
||||
)
|
||||
match = file_entry["matches"][0]
|
||||
self.assertEqual(
|
||||
match["contract"],
|
||||
"docs/release-control/v6/internal/subsystems/performance-and-scalability.md",
|
||||
)
|
||||
self.assertEqual(match["lane_context"]["lane_id"], "L10")
|
||||
self.assertEqual(
|
||||
match["verification_requirement"]["id"],
|
||||
"dashboard-workload-hot-path",
|
||||
)
|
||||
|
||||
def test_lookup_paths_assigns_settings_page_shell_to_frontend_primitives(self) -> None:
|
||||
result = lookup_paths(["frontend-modern/src/components/Settings/SettingsPageShell.tsx"])
|
||||
self.assertEqual(result["unowned_runtime_files"], [])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue