@principal-ai/principal-view-react 0.7.10 → 0.7.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect } from 'react';
1
+ import React, { useState } from 'react';
2
2
  import type { Meta, StoryObj } from '@storybook/react';
3
3
  import { GraphRenderer } from '../components/GraphRenderer';
4
4
  import { TestEventPanel } from '../components/TestEventPanel';
@@ -183,159 +183,37 @@ const testExecutionCanvas: ExtendedCanvas = {
183
183
  };
184
184
 
185
185
  // ============================================================================
186
- // Convert Test Spans to Graph Events
187
- // ============================================================================
188
-
189
- function convertSpansToEvents(spans: typeof testSpans): GraphEvent[] {
190
- const events: GraphEvent[] = [];
191
- let time = 0;
192
-
193
- spans.forEach((testSpan) => {
194
- // Pulse test suite node at start of each test
195
- events.push({
196
- timestamp: time,
197
- category: 'node',
198
- operation: 'animate',
199
- payload: {
200
- nodeId: 'test-suite',
201
- animation: { type: 'pulse', duration: 500 },
202
- },
203
- });
204
- time += 600;
205
-
206
- // Animate through events in the span
207
- testSpan.events.forEach((event) => {
208
- const eventName = event.name;
209
-
210
- // Determine which phase based on event name
211
- let nodeId = '';
212
- let edgeId = '';
213
-
214
- if (eventName.startsWith('setup.')) {
215
- nodeId = 'setup-phase';
216
- edgeId = 'suite-to-setup';
217
- } else if (eventName.startsWith('execution.')) {
218
- nodeId = 'execution-phase';
219
- edgeId = 'setup-to-execution';
220
- } else if (eventName.startsWith('assertion.')) {
221
- nodeId = 'assertion-phase';
222
- edgeId = 'execution-to-assertion';
223
- }
224
-
225
- // Animate edge when phase starts
226
- if (eventName.endsWith('.started') && edgeId) {
227
- events.push({
228
- timestamp: time,
229
- category: 'edge',
230
- operation: 'animate',
231
- payload: {
232
- edgeId,
233
- animation: { type: 'particle', duration: 500 },
234
- },
235
- });
236
- time += 600;
237
- }
238
-
239
- // Pulse node
240
- if (nodeId) {
241
- events.push({
242
- timestamp: time,
243
- category: 'node',
244
- operation: 'animate',
245
- payload: {
246
- nodeId,
247
- animation: { type: 'pulse', duration: 600 },
248
- },
249
- });
250
- time += 700;
251
- }
252
- });
253
-
254
- // Animate to result
255
- events.push({
256
- timestamp: time,
257
- category: 'edge',
258
- operation: 'animate',
259
- payload: {
260
- edgeId: 'assertion-to-result',
261
- animation: { type: 'particle', duration: 500 },
262
- },
263
- });
264
- time += 600;
265
-
266
- events.push({
267
- timestamp: time,
268
- category: 'node',
269
- operation: 'animate',
270
- payload: {
271
- nodeId: 'test-result',
272
- animation: { type: 'pulse', duration: 800 },
273
- },
274
- });
275
- time += 1200; // Pause between tests
276
- });
277
-
278
- return events;
279
- }
280
-
281
- // ============================================================================
282
- // Animated Story
186
+ // Interactive Story (No Animation)
283
187
  // ============================================================================
284
188
 
285
189
  const AnimatedTestExecution = () => {
286
- const [events, setEvents] = useState<GraphEvent[]>([]);
287
- const [currentSpanIndex, setCurrentSpanIndex] = useState(0);
288
- const [currentEventIndex, setCurrentEventIndex] = useState(0);
190
+ const [events] = useState<GraphEvent[]>([]);
191
+ const [currentSpanIndex] = useState(0);
192
+ // Show all events by default - set to a large number
193
+ const [currentEventIndex] = useState(999);
289
194
  const [highlightedPhase, setHighlightedPhase] = useState<string | undefined>();
290
195
 
291
- useEffect(() => {
292
- const graphEvents = convertSpansToEvents(testSpans);
293
- const timers: NodeJS.Timeout[] = [];
294
-
295
- let spanIndex = 0;
296
- let eventIndex = 0;
297
- let eventsPerTest = testSpans[0].events.length * 2 + 2; // ~2 graph events per span event + suite + result
298
-
299
- graphEvents.forEach((event, index) => {
300
- const timer = setTimeout(() => {
301
- setEvents((prev) => [...prev, event]);
302
-
303
- // Track which span and event we're on
304
- spanIndex = Math.floor(index / eventsPerTest);
305
- eventIndex = Math.floor((index % eventsPerTest) / 2);
306
-
307
- setCurrentSpanIndex(Math.min(spanIndex, testSpans.length - 1));
308
- setCurrentEventIndex(eventIndex);
309
- }, event.timestamp);
310
- timers.push(timer);
311
- });
312
-
313
- // Reset animation
314
- const resetTimer = setTimeout(() => {
315
- setEvents([]);
316
- setCurrentSpanIndex(0);
317
- setCurrentEventIndex(0);
318
- }, graphEvents[graphEvents.length - 1].timestamp + 2000);
319
-
320
- return () => {
321
- timers.forEach(clearTimeout);
322
- clearTimeout(resetTimer);
323
- };
324
- }, []);
325
-
326
- // Map node IDs to phase names
327
- const getPhaseFromNodeId = (nodeId: string): string | undefined => {
328
- if (nodeId === 'setup-phase') return 'setup';
329
- if (nodeId === 'execution-phase') return 'execution';
330
- if (nodeId === 'assertion-phase') return 'assertion';
331
- return undefined;
332
- };
196
+ // Extract spans and logs from test data
197
+ const testData = testSpans as any;
198
+ const spans = Array.isArray(testData) ? testData : testData.spans || testData;
199
+ const logs = testData.logs || [];
333
200
 
334
201
  return (
335
- <div style={{ display: 'flex', width: '100%', height: '100%' }}>
336
- {/* Graph Visualization - Left Side */}
202
+ <div style={{ display: 'flex', width: '100vw', height: '100vh' }}>
203
+ {/* Event Panel - Left Side */}
204
+ <div style={{ flex: '0 0 50%', height: '100%', borderRight: `1px solid #333`, overflow: 'hidden' }}>
205
+ <TestEventPanel
206
+ spans={spans}
207
+ logs={logs}
208
+ currentSpanIndex={currentSpanIndex}
209
+ currentEventIndex={currentEventIndex}
210
+ highlightedPhase={highlightedPhase}
211
+ />
212
+ </div>
213
+
214
+ {/* Graph Visualization - Right Side */}
337
215
  <div
338
- style={{ flex: '0 0 60%', height: '100%', position: 'relative' }}
216
+ style={{ flex: '0 0 50%', height: '100%', position: 'relative' }}
339
217
  onMouseLeave={() => setHighlightedPhase(undefined)}
340
218
  >
341
219
  <div
@@ -351,28 +229,17 @@ const AnimatedTestExecution = () => {
351
229
  >
352
230
  <GraphRenderer
353
231
  canvas={testExecutionCanvas}
354
- showMinimap={true}
355
232
  showControls={true}
356
233
  events={events}
357
234
  />
358
235
  </div>
359
236
  </div>
360
-
361
- {/* Event Panel - Right Side */}
362
- <div style={{ flex: '0 0 40%', height: '100%', borderLeft: '1px solid #333' }}>
363
- <TestEventPanel
364
- spans={testSpans as any}
365
- currentSpanIndex={currentSpanIndex}
366
- currentEventIndex={currentEventIndex}
367
- highlightedPhase={highlightedPhase}
368
- />
369
- </div>
370
237
  </div>
371
238
  );
372
239
  };
373
240
 
374
241
  /**
375
- * Animated visualization of real test execution data using the "wide event" pattern.
242
+ * Interactive visualization of real test execution data using the "wide event" pattern.
376
243
  *
377
244
  * This demonstrates the key concept from loggingsucks.com:
378
245
  * - ONE comprehensive span per test (not multiple child spans)
@@ -383,7 +250,7 @@ const AnimatedTestExecution = () => {
383
250
  * **Interaction:**
384
251
  * - Hover over graph nodes (Setup, Execution, Assertion) to highlight related events
385
252
  * - Watch the code journey: blue = test file, green = code under test
386
- * - See how context builds up through events as the animation plays
253
+ * - All events are shown immediately for easy review
387
254
  */
388
255
  export const Animated: Story = {
389
256
  render: () => <AnimatedTestExecution />,
@@ -395,7 +262,6 @@ export const Animated: Story = {
395
262
  export const StaticView: Story = {
396
263
  args: {
397
264
  canvas: testExecutionCanvas,
398
- showMinimap: true,
399
265
  showControls: true,
400
266
  },
401
267
  };
@@ -403,18 +269,25 @@ export const StaticView: Story = {
403
269
  /**
404
270
  * Event panel component showing test execution narrative with file/line information.
405
271
  *
406
- * Shows how events accumulate context as tests execute, with automatic file/line
407
- * capture from stack traces and manual override for code under test.
272
+ * Shows how events and logs are interleaved in chronological order, with automatic
273
+ * file/line capture from stack traces and severity-based color coding for logs.
408
274
  */
409
275
  export const EventPanelOnly: StoryObj = {
410
- render: () => (
411
- <div style={{ width: '600px', height: '100vh' }}>
412
- <TestEventPanel
413
- spans={testSpans as any}
414
- currentSpanIndex={0}
415
- currentEventIndex={5} // Show all events
416
- highlightedPhase={undefined}
417
- />
418
- </div>
419
- ),
276
+ render: () => {
277
+ const testData = testSpans as any;
278
+ const spans = Array.isArray(testData) ? testData : testData.spans || testData;
279
+ const logs = testData.logs || [];
280
+
281
+ return (
282
+ <div style={{ width: '600px', height: '100vh' }}>
283
+ <TestEventPanel
284
+ spans={spans}
285
+ logs={logs}
286
+ currentSpanIndex={0}
287
+ currentEventIndex={999} // Show all events
288
+ highlightedPhase={undefined}
289
+ />
290
+ </div>
291
+ );
292
+ },
420
293
  };
@@ -52,7 +52,6 @@ type Story = StoryObj<typeof meta>;
52
52
  export const ExecutionFlow: Story = {
53
53
  args: {
54
54
  canvas: executionCanvas as ExtendedCanvas,
55
- showMinimap: true,
56
55
  showControls: true,
57
56
  },
58
57
  };
@@ -68,7 +67,7 @@ export const ExecutionFlow: Story = {
68
67
  *
69
68
  * All events in this panel passed schema validation.
70
69
  */
71
- export const ValidatedEvents: StoryObj = {
70
+ export const ValidatedEvents: Story = {
72
71
  render: () => (
73
72
  <div style={{ width: '800px', height: '100vh' }}>
74
73
  <TestEventPanel
@@ -93,14 +92,13 @@ export const ValidatedEvents: StoryObj = {
93
92
  *
94
93
  * This ensures production code emits events that match the architecture.
95
94
  */
96
- export const FlowWithValidation: StoryObj = {
95
+ export const FlowWithValidation: Story = {
97
96
  render: () => (
98
97
  <div style={{ display: 'flex', width: '100%', height: '100%' }}>
99
98
  {/* Graph Visualization - Left Side */}
100
99
  <div style={{ flex: '0 0 60%', height: '100%', position: 'relative' }}>
101
100
  <GraphRenderer
102
101
  canvas={executionCanvas as ExtendedCanvas}
103
- showMinimap={true}
104
102
  showControls={true}
105
103
  />
106
104
  </div>
@@ -136,7 +134,7 @@ export const FlowWithValidation: StoryObj = {
136
134
  * - Event descriptions
137
135
  * - Field schemas with types and requirements
138
136
  */
139
- export const CanvasSchema: StoryObj = {
137
+ export const CanvasSchema: Story = {
140
138
  render: () => (
141
139
  <div
142
140
  style={{