test: Created OutputInfoBar with color-coded stats, wired Use This butt…
- "app/src/components/OutputInfoBar.tsx" - "app/src/components/__tests__/OutputInfoBar.test.tsx" - "app/src/views/ImportConvert.tsx" - "app/src/App.css" GSD-Task: S01/T04
This commit is contained in:
parent
fc63195d68
commit
a97629c390
9 changed files with 377 additions and 22 deletions
|
|
@ -16,3 +16,4 @@
|
|||
{"cmd":"complete-task","params":{"milestoneId":"M002","sliceId":"S01","taskId":"T01"},"ts":"2026-03-26T05:05:22.658Z","actor":"agent","hash":"59aebe24d8f53b7a","session_id":"49f8e0fe-34a0-4608-b519-eca93850ed7c"}
|
||||
{"cmd":"complete-task","params":{"milestoneId":"M002","sliceId":"S01","taskId":"T02"},"ts":"2026-03-26T05:07:29.861Z","actor":"agent","hash":"a3980272c7b74afa","session_id":"49f8e0fe-34a0-4608-b519-eca93850ed7c"}
|
||||
{"cmd":"complete-task","params":{"milestoneId":"M002","sliceId":"S01","taskId":"T03"},"ts":"2026-03-26T05:15:38.849Z","actor":"agent","hash":"51de22a58ca5b075","session_id":"49f8e0fe-34a0-4608-b519-eca93850ed7c"}
|
||||
{"cmd":"complete-task","params":{"milestoneId":"M002","sliceId":"S01","taskId":"T04"},"ts":"2026-03-26T05:17:44.460Z","actor":"agent","hash":"fd1cf932b3152ba6","session_id":"49f8e0fe-34a0-4608-b519-eca93850ed7c"}
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@
|
|||
- Estimate: 1h30m
|
||||
- Files: app/src/hooks/useDebouncedTrace.ts, app/src/components/ParameterSliders.tsx, app/src/components/SvgPreview.tsx, app/src/views/ImportConvert.tsx, app/src/hooks/__tests__/useDebouncedTrace.test.ts
|
||||
- Verify: cd app && npx vitest run --reporter=verbose 2>&1 | tail -20 && npx tsc --noEmit 2>&1 | tail -10
|
||||
- [ ] **T04: Add output info bar, Use This flow, error states, and verify full integration** — Complete the slice by adding the output info bar with color-coded stats, the 'Use This' button that advances to View 2, error/empty states, and a full-stack integration verification.
|
||||
- [x] **T04: Created OutputInfoBar with color-coded stats, wired Use This button with disabled state, added 7 component tests — 23/23 tests pass, zero TS errors** — Complete the slice by adding the output info bar with color-coded stats, the 'Use This' button that advances to View 2, error/empty states, and a full-stack integration verification.
|
||||
|
||||
## Steps
|
||||
|
||||
|
|
|
|||
16
.gsd/milestones/M002/slices/S01/tasks/T03-VERIFY.json
Normal file
16
.gsd/milestones/M002/slices/S01/tasks/T03-VERIFY.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"taskId": "T03",
|
||||
"unitId": "M002/S01/T03",
|
||||
"timestamp": 1774502143859,
|
||||
"passed": true,
|
||||
"discoverySource": "task-plan",
|
||||
"checks": [
|
||||
{
|
||||
"command": "cd app",
|
||||
"exitCode": 0,
|
||||
"durationMs": 3,
|
||||
"verdict": "pass"
|
||||
}
|
||||
]
|
||||
}
|
||||
81
.gsd/milestones/M002/slices/S01/tasks/T04-SUMMARY.md
Normal file
81
.gsd/milestones/M002/slices/S01/tasks/T04-SUMMARY.md
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
---
|
||||
id: T04
|
||||
parent: S01
|
||||
milestone: M002
|
||||
provides: []
|
||||
requires: []
|
||||
affects: []
|
||||
key_files: ["app/src/components/OutputInfoBar.tsx", "app/src/components/__tests__/OutputInfoBar.test.tsx", "app/src/views/ImportConvert.tsx", "app/src/App.css"]
|
||||
key_decisions: ["Use This button always visible but disabled when no SVG output — avoids layout shift vs conditional render"]
|
||||
patterns_established: []
|
||||
drill_down_paths: []
|
||||
observability_surfaces: []
|
||||
duration: ""
|
||||
verification_result: "npx tsc --noEmit: zero errors. npx vitest run --reporter=verbose: 23/23 tests pass across 3 test files (9 API client, 7 OutputInfoBar, 7 useDebouncedTrace)."
|
||||
completed_at: 2026-03-26T05:17:44.406Z
|
||||
blocker_discovered: false
|
||||
---
|
||||
|
||||
# T04: Created OutputInfoBar with color-coded stats, wired Use This button with disabled state, added 7 component tests — 23/23 tests pass, zero TS errors
|
||||
|
||||
> Created OutputInfoBar with color-coded stats, wired Use This button with disabled state, added 7 component tests — 23/23 tests pass, zero TS errors
|
||||
|
||||
## What Happened
|
||||
---
|
||||
id: T04
|
||||
parent: S01
|
||||
milestone: M002
|
||||
key_files:
|
||||
- app/src/components/OutputInfoBar.tsx
|
||||
- app/src/components/__tests__/OutputInfoBar.test.tsx
|
||||
- app/src/views/ImportConvert.tsx
|
||||
- app/src/App.css
|
||||
key_decisions:
|
||||
- Use This button always visible but disabled when no SVG output — avoids layout shift vs conditional render
|
||||
duration: ""
|
||||
verification_result: passed
|
||||
completed_at: 2026-03-26T05:17:44.415Z
|
||||
blocker_discovered: false
|
||||
---
|
||||
|
||||
# T04: Created OutputInfoBar with color-coded stats, wired Use This button with disabled state, added 7 component tests — 23/23 tests pass, zero TS errors
|
||||
|
||||
**Created OutputInfoBar with color-coded stats, wired Use This button with disabled state, added 7 component tests — 23/23 tests pass, zero TS errors**
|
||||
|
||||
## What Happened
|
||||
|
||||
Built the OutputInfoBar component displaying trace metadata (Path Count, Total Nodes, Open Paths, Processing Time) with color-coded indicators: green for normal values, yellow when node_count_total > 5000, red when open_paths > 0. Null metadata shows a muted placeholder. Warnings render below the stats. Updated ImportConvert to render OutputInfoBar below the preview and changed the Use This button to always-visible with disabled state (disabled when no SVG output or loading). Added CSS for the info bar. Wrote 7 tests covering all color coding thresholds and edge cases. Verified empty/error/loading states from T03's SvgPreview remain correct.
|
||||
|
||||
## Verification
|
||||
|
||||
npx tsc --noEmit: zero errors. npx vitest run --reporter=verbose: 23/23 tests pass across 3 test files (9 API client, 7 OutputInfoBar, 7 useDebouncedTrace).
|
||||
|
||||
## Verification Evidence
|
||||
|
||||
| # | Command | Exit Code | Verdict | Duration |
|
||||
|---|---------|-----------|---------|----------|
|
||||
| 1 | `cd app && npx tsc --noEmit` | 0 | ✅ pass | 6400ms |
|
||||
| 2 | `cd app && npx vitest run --reporter=verbose` | 0 | ✅ pass | 2500ms |
|
||||
|
||||
|
||||
## Deviations
|
||||
|
||||
Use This button changed from conditionally rendered to always-visible with disabled attribute — better UX (no layout shift). Empty/error/loading states already complete from T03.
|
||||
|
||||
## Known Issues
|
||||
|
||||
None.
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `app/src/components/OutputInfoBar.tsx`
|
||||
- `app/src/components/__tests__/OutputInfoBar.test.tsx`
|
||||
- `app/src/views/ImportConvert.tsx`
|
||||
- `app/src/App.css`
|
||||
|
||||
|
||||
## Deviations
|
||||
Use This button changed from conditionally rendered to always-visible with disabled attribute — better UX (no layout shift). Empty/error/loading states already complete from T03.
|
||||
|
||||
## Known Issues
|
||||
None.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"version": 1,
|
||||
"exported_at": "2026-03-26T05:15:38.848Z",
|
||||
"exported_at": "2026-03-26T05:17:44.459Z",
|
||||
"milestones": [
|
||||
{
|
||||
"id": "M001",
|
||||
|
|
@ -990,19 +990,26 @@
|
|||
"milestone_id": "M002",
|
||||
"slice_id": "S01",
|
||||
"id": "T04",
|
||||
"title": "Add output info bar, Use This flow, error states, and verify full integration",
|
||||
"status": "pending",
|
||||
"one_liner": "",
|
||||
"narrative": "",
|
||||
"verification_result": "",
|
||||
"title": "Created OutputInfoBar with color-coded stats, wired Use This button with disabled state, added 7 component tests — 23/23 tests pass, zero TS errors",
|
||||
"status": "complete",
|
||||
"one_liner": "Created OutputInfoBar with color-coded stats, wired Use This button with disabled state, added 7 component tests — 23/23 tests pass, zero TS errors",
|
||||
"narrative": "Built the OutputInfoBar component displaying trace metadata (Path Count, Total Nodes, Open Paths, Processing Time) with color-coded indicators: green for normal values, yellow when node_count_total > 5000, red when open_paths > 0. Null metadata shows a muted placeholder. Warnings render below the stats. Updated ImportConvert to render OutputInfoBar below the preview and changed the Use This button to always-visible with disabled state (disabled when no SVG output or loading). Added CSS for the info bar. Wrote 7 tests covering all color coding thresholds and edge cases. Verified empty/error/loading states from T03's SvgPreview remain correct.",
|
||||
"verification_result": "npx tsc --noEmit: zero errors. npx vitest run --reporter=verbose: 23/23 tests pass across 3 test files (9 API client, 7 OutputInfoBar, 7 useDebouncedTrace).",
|
||||
"duration": "",
|
||||
"completed_at": null,
|
||||
"completed_at": "2026-03-26T05:17:44.406Z",
|
||||
"blocker_discovered": false,
|
||||
"deviations": "",
|
||||
"known_issues": "",
|
||||
"key_files": [],
|
||||
"key_decisions": [],
|
||||
"full_summary_md": "",
|
||||
"deviations": "Use This button changed from conditionally rendered to always-visible with disabled attribute — better UX (no layout shift). Empty/error/loading states already complete from T03.",
|
||||
"known_issues": "None.",
|
||||
"key_files": [
|
||||
"app/src/components/OutputInfoBar.tsx",
|
||||
"app/src/components/__tests__/OutputInfoBar.test.tsx",
|
||||
"app/src/views/ImportConvert.tsx",
|
||||
"app/src/App.css"
|
||||
],
|
||||
"key_decisions": [
|
||||
"Use This button always visible but disabled when no SVG output — avoids layout shift vs conditional render"
|
||||
],
|
||||
"full_summary_md": "---\nid: T04\nparent: S01\nmilestone: M002\nkey_files:\n - app/src/components/OutputInfoBar.tsx\n - app/src/components/__tests__/OutputInfoBar.test.tsx\n - app/src/views/ImportConvert.tsx\n - app/src/App.css\nkey_decisions:\n - Use This button always visible but disabled when no SVG output — avoids layout shift vs conditional render\nduration: \"\"\nverification_result: passed\ncompleted_at: 2026-03-26T05:17:44.415Z\nblocker_discovered: false\n---\n\n# T04: Created OutputInfoBar with color-coded stats, wired Use This button with disabled state, added 7 component tests — 23/23 tests pass, zero TS errors\n\n**Created OutputInfoBar with color-coded stats, wired Use This button with disabled state, added 7 component tests — 23/23 tests pass, zero TS errors**\n\n## What Happened\n\nBuilt the OutputInfoBar component displaying trace metadata (Path Count, Total Nodes, Open Paths, Processing Time) with color-coded indicators: green for normal values, yellow when node_count_total > 5000, red when open_paths > 0. Null metadata shows a muted placeholder. Warnings render below the stats. Updated ImportConvert to render OutputInfoBar below the preview and changed the Use This button to always-visible with disabled state (disabled when no SVG output or loading). Added CSS for the info bar. Wrote 7 tests covering all color coding thresholds and edge cases. Verified empty/error/loading states from T03's SvgPreview remain correct.\n\n## Verification\n\nnpx tsc --noEmit: zero errors. npx vitest run --reporter=verbose: 23/23 tests pass across 3 test files (9 API client, 7 OutputInfoBar, 7 useDebouncedTrace).\n\n## Verification Evidence\n\n| # | Command | Exit Code | Verdict | Duration |\n|---|---------|-----------|---------|----------|\n| 1 | `cd app && npx tsc --noEmit` | 0 | ✅ pass | 6400ms |\n| 2 | `cd app && npx vitest run --reporter=verbose` | 0 | ✅ pass | 2500ms |\n\n\n## Deviations\n\nUse This button changed from conditionally rendered to always-visible with disabled attribute — better UX (no layout shift). Empty/error/loading states already complete from T03.\n\n## Known Issues\n\nNone.\n\n## Files Created/Modified\n\n- `app/src/components/OutputInfoBar.tsx`\n- `app/src/components/__tests__/OutputInfoBar.test.tsx`\n- `app/src/views/ImportConvert.tsx`\n- `app/src/App.css`\n",
|
||||
"description": "Complete the slice by adding the output info bar with color-coded stats, the 'Use This' button that advances to View 2, error/empty states, and a full-stack integration verification.\n\n## Steps\n\n1. Create `app/src/components/OutputInfoBar.tsx`:\n - Accepts `metadata: TraceMetadata | null`\n - When metadata is null: render nothing or a muted placeholder\n - Render stats: Path Count, Total Nodes, Open Paths, Processing Time\n - Color coding: green for normal values, yellow when node_count_total > 5000, red when open_paths > 0\n - Show warnings from metadata.warnings array if any\n2. Add 'Use This' button to `ImportConvert.tsx`:\n - Disabled when no SVG output is available\n - On click: call `onUseThis(svgOutput, metadata)` prop, which App.tsx uses to transition view state to 'canvas'\n - Pass the SVG string forward (stored in App.tsx state for S02 to consume)\n3. Add empty/error states throughout:\n - No file selected: show upload prompt in preview area\n - API error: show error message in preview area with status code\n - Loading: show spinner overlay on preview\n4. Write `app/src/components/__tests__/OutputInfoBar.test.tsx` — test color coding logic:\n - Normal metadata → green indicators\n - High node count → yellow indicator\n - Open paths > 0 → red indicator\n - Null metadata → no crash, placeholder shown\n5. Final verification: ensure `npx tsc --noEmit` passes with zero errors, `npx vitest run` passes all tests, and all components are wired together end-to-end.",
|
||||
"estimate": "45m",
|
||||
"files": [
|
||||
|
|
@ -1312,6 +1319,28 @@
|
|||
"verdict": "✅ pass",
|
||||
"duration_ms": 2800,
|
||||
"created_at": "2026-03-26T05:15:38.801Z"
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"task_id": "T04",
|
||||
"slice_id": "S01",
|
||||
"milestone_id": "M002",
|
||||
"command": "cd app && npx tsc --noEmit",
|
||||
"exit_code": 0,
|
||||
"verdict": "✅ pass",
|
||||
"duration_ms": 6400,
|
||||
"created_at": "2026-03-26T05:17:44.406Z"
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"task_id": "T04",
|
||||
"slice_id": "S01",
|
||||
"milestone_id": "M002",
|
||||
"command": "cd app && npx vitest run --reporter=verbose",
|
||||
"exit_code": 0,
|
||||
"verdict": "✅ pass",
|
||||
"duration_ms": 2500,
|
||||
"created_at": "2026-03-26T05:17:44.406Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -324,6 +324,77 @@
|
|||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Output Info Bar */
|
||||
.output-info-bar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 10px 12px;
|
||||
background: var(--code-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.output-info-bar--empty {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.output-info-placeholder {
|
||||
color: var(--text);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.output-info-stats {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.output-stat {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
.output-stat-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.output-stat-value {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.output-stat--green .output-stat-value {
|
||||
color: #27ae60;
|
||||
}
|
||||
|
||||
.output-stat--yellow .output-stat-value {
|
||||
color: #f39c12;
|
||||
}
|
||||
|
||||
.output-stat--red .output-stat-value {
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
.output-info-warnings {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.output-info-warning {
|
||||
font-size: 12px;
|
||||
color: #e67e22;
|
||||
}
|
||||
|
||||
/* Placeholder views */
|
||||
.placeholder-view {
|
||||
display: flex;
|
||||
|
|
|
|||
68
app/src/components/OutputInfoBar.tsx
Normal file
68
app/src/components/OutputInfoBar.tsx
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import type { TraceMetadata } from '../types/engine';
|
||||
|
||||
interface OutputInfoBarProps {
|
||||
metadata: TraceMetadata | null;
|
||||
}
|
||||
|
||||
type StatColor = 'green' | 'yellow' | 'red';
|
||||
|
||||
function getNodeCountColor(count: number): StatColor {
|
||||
if (count > 5000) return 'yellow';
|
||||
return 'green';
|
||||
}
|
||||
|
||||
function getOpenPathsColor(count: number): StatColor {
|
||||
if (count > 0) return 'red';
|
||||
return 'green';
|
||||
}
|
||||
|
||||
export default function OutputInfoBar({ metadata }: OutputInfoBarProps) {
|
||||
if (!metadata) {
|
||||
return (
|
||||
<div className="output-info-bar output-info-bar--empty">
|
||||
<span className="output-info-placeholder">
|
||||
Trace an image to see output stats
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const nodeColor = getNodeCountColor(metadata.node_count_total);
|
||||
const openPathsColor = getOpenPathsColor(metadata.open_paths);
|
||||
|
||||
return (
|
||||
<div className="output-info-bar">
|
||||
<div className="output-info-stats">
|
||||
<span className="output-stat output-stat--green" data-testid="stat-paths">
|
||||
<span className="output-stat-label">Paths</span>
|
||||
<span className="output-stat-value">{metadata.path_count}</span>
|
||||
</span>
|
||||
<span
|
||||
className={`output-stat output-stat--${nodeColor}`}
|
||||
data-testid="stat-nodes"
|
||||
>
|
||||
<span className="output-stat-label">Nodes</span>
|
||||
<span className="output-stat-value">{metadata.node_count_total.toLocaleString()}</span>
|
||||
</span>
|
||||
<span
|
||||
className={`output-stat output-stat--${openPathsColor}`}
|
||||
data-testid="stat-open-paths"
|
||||
>
|
||||
<span className="output-stat-label">Open Paths</span>
|
||||
<span className="output-stat-value">{metadata.open_paths}</span>
|
||||
</span>
|
||||
<span className="output-stat output-stat--green" data-testid="stat-time">
|
||||
<span className="output-stat-label">Time</span>
|
||||
<span className="output-stat-value">{metadata.processing_ms}ms</span>
|
||||
</span>
|
||||
</div>
|
||||
{metadata.warnings.length > 0 && (
|
||||
<div className="output-info-warnings" data-testid="warnings">
|
||||
{metadata.warnings.map((w, i) => (
|
||||
<span key={i} className="output-info-warning">⚠ {w}</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
88
app/src/components/__tests__/OutputInfoBar.test.tsx
Normal file
88
app/src/components/__tests__/OutputInfoBar.test.tsx
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import OutputInfoBar from '../OutputInfoBar';
|
||||
import type { TraceMetadata } from '../../types/engine';
|
||||
|
||||
function makeMetadata(overrides: Partial<TraceMetadata> = {}): TraceMetadata {
|
||||
return {
|
||||
format: 'svg',
|
||||
path_count: 42,
|
||||
node_count_total: 1200,
|
||||
open_paths: 0,
|
||||
island_count: 3,
|
||||
warnings: [],
|
||||
processing_ms: 150,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe('OutputInfoBar', () => {
|
||||
it('shows placeholder when metadata is null', () => {
|
||||
const { container } = render(<OutputInfoBar metadata={null} />);
|
||||
expect(container.querySelector('.output-info-bar--empty')).toBeTruthy();
|
||||
expect(screen.getByText(/trace an image/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders stats with green indicators for normal metadata', () => {
|
||||
const meta = makeMetadata();
|
||||
render(<OutputInfoBar metadata={meta} />);
|
||||
|
||||
const pathsStat = screen.getByTestId('stat-paths');
|
||||
expect(pathsStat).toHaveClass('output-stat--green');
|
||||
expect(pathsStat).toHaveTextContent('42');
|
||||
|
||||
const nodesStat = screen.getByTestId('stat-nodes');
|
||||
expect(nodesStat).toHaveClass('output-stat--green');
|
||||
expect(nodesStat).toHaveTextContent('1,200');
|
||||
|
||||
const openStat = screen.getByTestId('stat-open-paths');
|
||||
expect(openStat).toHaveClass('output-stat--green');
|
||||
expect(openStat).toHaveTextContent('0');
|
||||
|
||||
const timeStat = screen.getByTestId('stat-time');
|
||||
expect(timeStat).toHaveClass('output-stat--green');
|
||||
expect(timeStat).toHaveTextContent('150ms');
|
||||
});
|
||||
|
||||
it('shows yellow indicator when node_count_total > 5000', () => {
|
||||
const meta = makeMetadata({ node_count_total: 8500 });
|
||||
render(<OutputInfoBar metadata={meta} />);
|
||||
|
||||
const nodesStat = screen.getByTestId('stat-nodes');
|
||||
expect(nodesStat).toHaveClass('output-stat--yellow');
|
||||
expect(nodesStat).toHaveTextContent('8,500');
|
||||
});
|
||||
|
||||
it('shows red indicator when open_paths > 0', () => {
|
||||
const meta = makeMetadata({ open_paths: 3 });
|
||||
render(<OutputInfoBar metadata={meta} />);
|
||||
|
||||
const openStat = screen.getByTestId('stat-open-paths');
|
||||
expect(openStat).toHaveClass('output-stat--red');
|
||||
expect(openStat).toHaveTextContent('3');
|
||||
});
|
||||
|
||||
it('shows both yellow and red indicators simultaneously', () => {
|
||||
const meta = makeMetadata({ node_count_total: 6000, open_paths: 5 });
|
||||
render(<OutputInfoBar metadata={meta} />);
|
||||
|
||||
expect(screen.getByTestId('stat-nodes')).toHaveClass('output-stat--yellow');
|
||||
expect(screen.getByTestId('stat-open-paths')).toHaveClass('output-stat--red');
|
||||
});
|
||||
|
||||
it('displays warnings when present', () => {
|
||||
const meta = makeMetadata({ warnings: ['Too many paths', 'Complex geometry'] });
|
||||
render(<OutputInfoBar metadata={meta} />);
|
||||
|
||||
const warnings = screen.getByTestId('warnings');
|
||||
expect(warnings).toHaveTextContent('Too many paths');
|
||||
expect(warnings).toHaveTextContent('Complex geometry');
|
||||
});
|
||||
|
||||
it('does not render warnings container when no warnings', () => {
|
||||
const meta = makeMetadata({ warnings: [] });
|
||||
render(<OutputInfoBar metadata={meta} />);
|
||||
|
||||
expect(screen.queryByTestId('warnings')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
@ -4,6 +4,7 @@ import FileUpload from '../components/FileUpload';
|
|||
import PresetSelector from '../components/PresetSelector';
|
||||
import ParameterSliders from '../components/ParameterSliders';
|
||||
import SvgPreview from '../components/SvgPreview';
|
||||
import OutputInfoBar from '../components/OutputInfoBar';
|
||||
import { useDebouncedTrace } from '../hooks/useDebouncedTrace';
|
||||
import styles from './ImportConvert.module.css';
|
||||
|
||||
|
|
@ -93,15 +94,14 @@ export default function ImportConvert({ onUseThis }: ImportConvertProps) {
|
|||
params={currentParams}
|
||||
onChange={handleParamsChange}
|
||||
/>
|
||||
{svgOutput && (
|
||||
<button
|
||||
type="button"
|
||||
className="use-this-btn"
|
||||
onClick={handleUseThis}
|
||||
>
|
||||
Use This →
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className="use-this-btn"
|
||||
disabled={!svgOutput || isLoading}
|
||||
onClick={handleUseThis}
|
||||
>
|
||||
Use This →
|
||||
</button>
|
||||
</div>
|
||||
<div className={styles.rightPanel}>
|
||||
<SvgPreview
|
||||
|
|
@ -110,6 +110,7 @@ export default function ImportConvert({ onUseThis }: ImportConvertProps) {
|
|||
error={error}
|
||||
metadata={metadata}
|
||||
/>
|
||||
<OutputInfoBar metadata={metadata} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue