Display user story description as multiline in specs pages
- GherkinHighlighter: Render user story lines (En tant que, Je peux, Afin de) in a violet card at the top of feature details - FeatureView: Remove duplicate description display (now in GherkinHighlighter) - SpecsPage: Display feature cards in single column layout with multiline user story formatting Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -82,14 +82,9 @@ export function FeatureView({ feature, onBack, onSelectScreen, onSelectStory }:
|
||||
)}
|
||||
</div>
|
||||
|
||||
<h1 className="text-2xl font-semibold mb-2">
|
||||
<h1 className="text-2xl font-semibold">
|
||||
{feature.name.replace(/^US-\d+\s*/, '')}
|
||||
</h1>
|
||||
{feature.description && (
|
||||
<p className="text-muted-foreground max-w-3xl">
|
||||
{feature.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="px-4 sm:px-8 py-6">
|
||||
|
||||
@@ -121,8 +121,8 @@ export function GherkinHighlighter({ content, scenarioResults = [] }: GherkinHig
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Scenario/Background Blocks */}
|
||||
{blocks.filter(b => b.type !== 'header').map((block, blockIndex) => (
|
||||
{/* All Blocks including header */}
|
||||
{blocks.map((block, blockIndex) => (
|
||||
<BlockRenderer
|
||||
key={blockIndex}
|
||||
block={block}
|
||||
@@ -206,8 +206,31 @@ interface BlockRendererProps {
|
||||
|
||||
function BlockRenderer({ block, isCollapsed, onToggle, showDefinitions }: BlockRendererProps) {
|
||||
if (block.type === 'header') {
|
||||
// Header is now handled separately in the main component
|
||||
return null;
|
||||
// Extract user story lines (En tant que, Je peux/Je veux, Afin de)
|
||||
const userStoryLines = block.lines.filter(line => {
|
||||
const trimmed = line.trim();
|
||||
return trimmed.startsWith('En tant qu') ||
|
||||
trimmed.startsWith('Je peux') ||
|
||||
trimmed.startsWith('Je veux') ||
|
||||
trimmed.startsWith('Et ') ||
|
||||
trimmed.startsWith('Afin ');
|
||||
});
|
||||
|
||||
if (userStoryLines.length === 0) return null;
|
||||
|
||||
return (
|
||||
<Card className="border-l-4 border-l-violet-500 bg-violet-50/50 dark:bg-violet-950/20">
|
||||
<CardContent className="p-3">
|
||||
<div className="space-y-0.5">
|
||||
{userStoryLines.map((line, index) => (
|
||||
<div key={index} className="text-sm text-foreground">
|
||||
{line.trim()}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
const restLines = block.lines.slice(1);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { categoryLabels, categoryColors, priorityLabels, priorityColors, getStor
|
||||
import { getTestStatus, getTestSummary } from '../../data/testResults';
|
||||
import { FeatureView } from './FeatureView';
|
||||
import { FeatureFilter } from './FeatureFilter';
|
||||
import { Card, CardHeader, CardTitle, CardContent, CardDescription } from '../ui/card';
|
||||
import { Card, CardHeader, CardTitle, CardContent } from '../ui/card';
|
||||
import { Button } from '../ui/button';
|
||||
import { ArrowLeft, FileText, Monitor, CheckCircle2, XCircle, AlertCircle, ExternalLink } from 'lucide-react';
|
||||
import type { ParsedFeature } from '../../types/gherkin';
|
||||
@@ -146,7 +146,7 @@ export function SpecsPage({ selectedFeatureId, onBack, onSelectScreen, onSelectS
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 sm:gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<div className="flex flex-col gap-3 sm:gap-4">
|
||||
{features.map(feature => (
|
||||
<FeatureCard
|
||||
key={feature.id}
|
||||
@@ -168,6 +168,15 @@ export function SpecsPage({ selectedFeatureId, onBack, onSelectScreen, onSelectS
|
||||
);
|
||||
}
|
||||
|
||||
// Split user story description into separate lines
|
||||
function formatUserStory(description: string): string[] {
|
||||
// Split on user story keywords while keeping the keywords
|
||||
return description
|
||||
.split(/(?=En tant qu|Je peux|Je veux|Et |Afin d)/)
|
||||
.map(s => s.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
interface FeatureCardProps {
|
||||
feature: ParsedFeature;
|
||||
onClick: () => void;
|
||||
@@ -230,9 +239,13 @@ function FeatureCard({ feature, onClick }: FeatureCardProps) {
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{feature.description && (
|
||||
<CardDescription className="line-clamp-2 text-sm mb-3">
|
||||
{feature.description}
|
||||
</CardDescription>
|
||||
<div className="text-sm text-muted-foreground mb-3 space-y-1">
|
||||
{formatUserStory(feature.description).map((line, i) => (
|
||||
<div key={i} className="leading-snug">
|
||||
{line}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center gap-4 text-xs text-muted-foreground">
|
||||
<span className="flex items-center gap-1">
|
||||
|
||||
Reference in New Issue
Block a user