@reqord/web 0.2.0 → 0.3.0
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.
- package/package.json +2 -2
- package/src/__tests__/components/dashboard/critical-path-display.test.tsx +61 -0
- package/src/__tests__/components/dashboard/progress-bar.test.tsx +63 -0
- package/src/__tests__/components/dashboard/project-health.test.tsx +21 -7
- package/src/__tests__/components/dashboard/status-card.test.tsx +86 -0
- package/src/__tests__/components/dashboard/warning-alert.test.tsx +6 -6
- package/src/__tests__/components/feedback/feedback-filters-improved.test.tsx +33 -0
- package/src/__tests__/components/graph/drilldown-breadcrumb.test.tsx +12 -0
- package/src/__tests__/components/graph/edge-styles.test.ts +6 -6
- package/src/__tests__/components/graph/issue-node.test.tsx +25 -6
- package/src/__tests__/components/graph/requirement-node.test.tsx +45 -0
- package/src/__tests__/components/graph/specification-node.test.tsx +27 -14
- package/src/__tests__/components/requirement/requirement-table.test.tsx +165 -0
- package/src/__tests__/components/specification/specification-table.test.tsx +189 -0
- package/src/__tests__/components/ui/badge.test.tsx +98 -0
- package/src/__tests__/components/ui/button.test.tsx +98 -0
- package/src/__tests__/components/ui/card.test.tsx +58 -0
- package/src/__tests__/components/ui/nav.test.tsx +91 -0
- package/src/__tests__/components/ui/tabs.test.tsx +53 -0
- package/src/__tests__/lib/drilldown-graph-data.test.ts +45 -3
- package/src/app/dashboard/page.tsx +29 -21
- package/src/app/globals.css +46 -0
- package/src/app/layout.tsx +4 -1
- package/src/app/requirements/loading.tsx +30 -5
- package/src/app/specifications/loading.tsx +29 -5
- package/src/components/dashboard/critical-path-display.tsx +30 -15
- package/src/components/dashboard/progress-bar.tsx +2 -4
- package/src/components/dashboard/project-health.tsx +9 -10
- package/src/components/dashboard/status-card.tsx +20 -9
- package/src/components/dashboard/warning-alert.tsx +57 -5
- package/src/components/feedback/feedback-filters.tsx +41 -12
- package/src/components/graph/drilldown-breadcrumb.tsx +1 -1
- package/src/components/graph/drilldown-graph.tsx +3 -1
- package/src/components/graph/edge-styles.ts +3 -3
- package/src/components/graph/issue-node.tsx +7 -7
- package/src/components/graph/multi-level-graph.tsx +2 -2
- package/src/components/graph/requirement-node.tsx +5 -5
- package/src/components/graph/specification-node.tsx +12 -9
- package/src/components/requirement/requirement-table.tsx +62 -18
- package/src/components/specification/specification-table.tsx +59 -17
- package/src/components/ui/badge.tsx +4 -4
- package/src/components/ui/button.tsx +43 -0
- package/src/components/ui/card.tsx +25 -0
- package/src/components/ui/nav.tsx +35 -35
- package/src/components/ui/tabs.tsx +2 -0
- package/src/lib/drilldown-graph-data.ts +23 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reqord/web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Web dashboard for Reqord",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"repository": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"react-dom": "^19.0.0",
|
|
38
38
|
"react-markdown": "^10.1.0",
|
|
39
39
|
"remark-gfm": "^4.0.1",
|
|
40
|
-
"@reqord/shared": "0.
|
|
40
|
+
"@reqord/shared": "0.3.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@tailwindcss/postcss": "^4.1.0",
|
|
@@ -126,4 +126,65 @@ describe("CriticalPathDisplay", () => {
|
|
|
126
126
|
);
|
|
127
127
|
expect(listItems).toHaveLength(0);
|
|
128
128
|
});
|
|
129
|
+
|
|
130
|
+
it("renders issue number as GitHub link", () => {
|
|
131
|
+
const items: CriticalPathItem[] = [
|
|
132
|
+
{
|
|
133
|
+
issueNumber: 42,
|
|
134
|
+
title: "Linked task",
|
|
135
|
+
url: "https://github.com/repo/issues/42",
|
|
136
|
+
priority: "high",
|
|
137
|
+
status: "open",
|
|
138
|
+
estimatedHours: 4,
|
|
139
|
+
specId: "spec-000001",
|
|
140
|
+
},
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
render(<CriticalPathDisplay items={items} />);
|
|
144
|
+
|
|
145
|
+
const link = screen.getByRole("link", { name: "#42" });
|
|
146
|
+
expect(link).toBeInTheDocument();
|
|
147
|
+
expect(link).toHaveAttribute(
|
|
148
|
+
"href",
|
|
149
|
+
"https://github.com/repo/issues/42"
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("applies red badge to P1 priority items", () => {
|
|
154
|
+
const items: CriticalPathItem[] = [
|
|
155
|
+
{
|
|
156
|
+
issueNumber: 100,
|
|
157
|
+
title: "High priority task",
|
|
158
|
+
url: "https://github.com/repo/issues/100",
|
|
159
|
+
priority: "P1",
|
|
160
|
+
status: "open",
|
|
161
|
+
estimatedHours: 8,
|
|
162
|
+
specId: "spec-000001",
|
|
163
|
+
},
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
render(<CriticalPathDisplay items={items} />);
|
|
167
|
+
|
|
168
|
+
const badge = screen.getByText("P1");
|
|
169
|
+
expect(badge).toHaveClass("bg-red-100");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("applies orange badge to P2 priority items", () => {
|
|
173
|
+
const items: CriticalPathItem[] = [
|
|
174
|
+
{
|
|
175
|
+
issueNumber: 101,
|
|
176
|
+
title: "Medium priority task",
|
|
177
|
+
url: "https://github.com/repo/issues/101",
|
|
178
|
+
priority: "P2",
|
|
179
|
+
status: "open",
|
|
180
|
+
estimatedHours: 8,
|
|
181
|
+
specId: "spec-000002",
|
|
182
|
+
},
|
|
183
|
+
];
|
|
184
|
+
|
|
185
|
+
render(<CriticalPathDisplay items={items} />);
|
|
186
|
+
|
|
187
|
+
const badge = screen.getByText("P2");
|
|
188
|
+
expect(badge).toHaveClass("bg-orange-100");
|
|
189
|
+
});
|
|
129
190
|
});
|
|
@@ -84,4 +84,67 @@ describe("ProgressBar", () => {
|
|
|
84
84
|
const progressBar = screen.getByTestId("progress-bar-fill");
|
|
85
85
|
expect(progressBar).toHaveClass("bg-green-500");
|
|
86
86
|
});
|
|
87
|
+
|
|
88
|
+
it("uses h-3 height for the track", () => {
|
|
89
|
+
render(
|
|
90
|
+
<ProgressBar
|
|
91
|
+
label="Test"
|
|
92
|
+
current={5}
|
|
93
|
+
total={10}
|
|
94
|
+
percentage={50}
|
|
95
|
+
color="blue"
|
|
96
|
+
/>
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const progressBar = screen.getByTestId("progress-bar-fill");
|
|
100
|
+
const track = progressBar.parentElement;
|
|
101
|
+
expect(track).toHaveClass("h-3");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("uses gray-100 background for the track", () => {
|
|
105
|
+
render(
|
|
106
|
+
<ProgressBar
|
|
107
|
+
label="Test"
|
|
108
|
+
current={5}
|
|
109
|
+
total={10}
|
|
110
|
+
percentage={50}
|
|
111
|
+
color="blue"
|
|
112
|
+
/>
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const progressBar = screen.getByTestId("progress-bar-fill");
|
|
116
|
+
const track = progressBar.parentElement;
|
|
117
|
+
expect(track).toHaveClass("bg-gray-100");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("applies rounded-full to the fill bar", () => {
|
|
121
|
+
render(
|
|
122
|
+
<ProgressBar
|
|
123
|
+
label="Test"
|
|
124
|
+
current={5}
|
|
125
|
+
total={10}
|
|
126
|
+
percentage={50}
|
|
127
|
+
color="blue"
|
|
128
|
+
/>
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const progressBar = screen.getByTestId("progress-bar-fill");
|
|
132
|
+
expect(progressBar).toHaveClass("rounded-full");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("applies duration-500 ease-out transition to the fill bar", () => {
|
|
136
|
+
render(
|
|
137
|
+
<ProgressBar
|
|
138
|
+
label="Test"
|
|
139
|
+
current={5}
|
|
140
|
+
total={10}
|
|
141
|
+
percentage={50}
|
|
142
|
+
color="blue"
|
|
143
|
+
/>
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const progressBar = screen.getByTestId("progress-bar-fill");
|
|
147
|
+
expect(progressBar).toHaveClass("duration-500");
|
|
148
|
+
expect(progressBar).toHaveClass("ease-out");
|
|
149
|
+
});
|
|
87
150
|
});
|
|
@@ -10,13 +10,13 @@ describe("ProjectHealth", () => {
|
|
|
10
10
|
cleanup();
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
it("renders score with
|
|
13
|
+
it("renders score with emerald color when score >= 80", () => {
|
|
14
14
|
render(<ProjectHealth score={85} />);
|
|
15
15
|
|
|
16
16
|
const scoreElement = screen.getByTestId("health-score");
|
|
17
17
|
expect(scoreElement).toBeInTheDocument();
|
|
18
18
|
expect(scoreElement).toHaveTextContent("85");
|
|
19
|
-
expect(scoreElement).toHaveClass("text-
|
|
19
|
+
expect(scoreElement).toHaveClass("text-emerald-600");
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
it("renders score with yellow color when score >= 50 and < 80", () => {
|
|
@@ -24,7 +24,7 @@ describe("ProjectHealth", () => {
|
|
|
24
24
|
|
|
25
25
|
const scoreElement = screen.getByTestId("health-score");
|
|
26
26
|
expect(scoreElement).toHaveTextContent("65");
|
|
27
|
-
expect(scoreElement).toHaveClass("text-yellow-
|
|
27
|
+
expect(scoreElement).toHaveClass("text-yellow-600");
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
it("renders score with red color when score < 50", () => {
|
|
@@ -32,21 +32,21 @@ describe("ProjectHealth", () => {
|
|
|
32
32
|
|
|
33
33
|
const scoreElement = screen.getByTestId("health-score");
|
|
34
34
|
expect(scoreElement).toHaveTextContent("30");
|
|
35
|
-
expect(scoreElement).toHaveClass("text-red-
|
|
35
|
+
expect(scoreElement).toHaveClass("text-red-600");
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
it("renders score at boundary (score = 80) as
|
|
38
|
+
it("renders score at boundary (score = 80) as emerald", () => {
|
|
39
39
|
render(<ProjectHealth score={80} />);
|
|
40
40
|
|
|
41
41
|
const scoreElement = screen.getByTestId("health-score");
|
|
42
|
-
expect(scoreElement).toHaveClass("text-
|
|
42
|
+
expect(scoreElement).toHaveClass("text-emerald-600");
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
it("renders score at boundary (score = 50) as yellow", () => {
|
|
46
46
|
render(<ProjectHealth score={50} />);
|
|
47
47
|
|
|
48
48
|
const scoreElement = screen.getByTestId("health-score");
|
|
49
|
-
expect(scoreElement).toHaveClass("text-yellow-
|
|
49
|
+
expect(scoreElement).toHaveClass("text-yellow-600");
|
|
50
50
|
});
|
|
51
51
|
|
|
52
52
|
it("displays '/ 100' label", () => {
|
|
@@ -54,4 +54,18 @@ describe("ProjectHealth", () => {
|
|
|
54
54
|
|
|
55
55
|
expect(screen.getByText("/ 100")).toBeInTheDocument();
|
|
56
56
|
});
|
|
57
|
+
|
|
58
|
+
it("renders with warm background and accent bar", () => {
|
|
59
|
+
render(<ProjectHealth score={75} />);
|
|
60
|
+
|
|
61
|
+
const container = screen.getByTestId("health-score").closest("div[class*='bg-warm']");
|
|
62
|
+
expect(container).not.toBeNull();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("rounds fractional score for display", () => {
|
|
66
|
+
render(<ProjectHealth score={84.6} />);
|
|
67
|
+
|
|
68
|
+
const scoreElement = screen.getByTestId("health-score");
|
|
69
|
+
expect(scoreElement).toHaveTextContent("85");
|
|
70
|
+
});
|
|
57
71
|
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
4
|
+
import { render, screen, cleanup } from "@testing-library/react";
|
|
5
|
+
import "@testing-library/jest-dom/vitest";
|
|
6
|
+
import { StatusCard } from "../../../components/dashboard/status-card";
|
|
7
|
+
import type { StatusBreakdown } from "../../../lib/dashboard-data";
|
|
8
|
+
|
|
9
|
+
describe("StatusCard", () => {
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
cleanup();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("renders title and total count", () => {
|
|
15
|
+
const breakdown: StatusBreakdown = { draft: 5, approved: 3 };
|
|
16
|
+
render(<StatusCard title="Requirements" total={8} breakdown={breakdown} />);
|
|
17
|
+
|
|
18
|
+
expect(screen.getByText("Requirements")).toBeInTheDocument();
|
|
19
|
+
expect(screen.getByText("8")).toBeInTheDocument();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("renders breakdown badges for each status", () => {
|
|
23
|
+
const breakdown: StatusBreakdown = { draft: 5, approved: 3 };
|
|
24
|
+
render(<StatusCard title="Requirements" total={8} breakdown={breakdown} />);
|
|
25
|
+
|
|
26
|
+
expect(screen.getByText("draft: 5")).toBeInTheDocument();
|
|
27
|
+
expect(screen.getByText("approved: 3")).toBeInTheDocument();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("applies rounded-xl class to the card container", () => {
|
|
31
|
+
const breakdown: StatusBreakdown = { draft: 2 };
|
|
32
|
+
const { container } = render(
|
|
33
|
+
<StatusCard title="Test" total={2} breakdown={breakdown} />
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const card = container.firstChild as HTMLElement;
|
|
37
|
+
expect(card).toHaveClass("rounded-xl");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("applies draft badge styling to draft status", () => {
|
|
41
|
+
const breakdown: StatusBreakdown = { draft: 3 };
|
|
42
|
+
render(<StatusCard title="Test" total={3} breakdown={breakdown} />);
|
|
43
|
+
|
|
44
|
+
const draftBadge = screen.getByText("draft: 3");
|
|
45
|
+
expect(draftBadge).toHaveClass("bg-gray-100");
|
|
46
|
+
expect(draftBadge).toHaveClass("text-gray-600");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("applies approved badge styling to approved status", () => {
|
|
50
|
+
const breakdown: StatusBreakdown = { approved: 2 };
|
|
51
|
+
render(<StatusCard title="Test" total={2} breakdown={breakdown} />);
|
|
52
|
+
|
|
53
|
+
const approvedBadge = screen.getByText("approved: 2");
|
|
54
|
+
expect(approvedBadge).toHaveClass("bg-blue-50");
|
|
55
|
+
expect(approvedBadge).toHaveClass("text-blue-700");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("applies implemented badge styling to implemented status", () => {
|
|
59
|
+
const breakdown: StatusBreakdown = { implemented: 4 };
|
|
60
|
+
render(<StatusCard title="Test" total={4} breakdown={breakdown} />);
|
|
61
|
+
|
|
62
|
+
const implementedBadge = screen.getByText("implemented: 4");
|
|
63
|
+
expect(implementedBadge).toHaveClass("bg-emerald-50");
|
|
64
|
+
expect(implementedBadge).toHaveClass("text-emerald-700");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("applies deprecated badge styling to deprecated status", () => {
|
|
68
|
+
const breakdown: StatusBreakdown = { deprecated: 1 };
|
|
69
|
+
render(<StatusCard title="Test" total={1} breakdown={breakdown} />);
|
|
70
|
+
|
|
71
|
+
const deprecatedBadge = screen.getByText("deprecated: 1");
|
|
72
|
+
expect(deprecatedBadge).toHaveClass("bg-red-50");
|
|
73
|
+
expect(deprecatedBadge).toHaveClass("text-red-700");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("applies hover shadow transition", () => {
|
|
77
|
+
const breakdown: StatusBreakdown = { draft: 1 };
|
|
78
|
+
const { container } = render(
|
|
79
|
+
<StatusCard title="Test" total={1} breakdown={breakdown} />
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const card = container.firstChild as HTMLElement;
|
|
83
|
+
expect(card).toHaveClass("hover:shadow-md");
|
|
84
|
+
expect(card).toHaveClass("transition-shadow");
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -11,7 +11,7 @@ describe("WarningAlert", () => {
|
|
|
11
11
|
cleanup();
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
it("renders error severity with red
|
|
14
|
+
it("renders error severity with red background and rounded corners", () => {
|
|
15
15
|
const warning: Warning = {
|
|
16
16
|
type: "design_verification_error",
|
|
17
17
|
message: "Critical issue detected",
|
|
@@ -23,12 +23,12 @@ describe("WarningAlert", () => {
|
|
|
23
23
|
|
|
24
24
|
const alert = screen.getByTestId("warning-alert");
|
|
25
25
|
expect(alert).toBeInTheDocument();
|
|
26
|
-
expect(alert).toHaveClass("border-red-500");
|
|
27
26
|
expect(alert).toHaveClass("bg-red-50");
|
|
27
|
+
expect(alert).toHaveClass("rounded-lg");
|
|
28
28
|
expect(screen.getByText("Critical issue detected")).toBeInTheDocument();
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
it("renders warning severity with yellow
|
|
31
|
+
it("renders warning severity with yellow background and rounded corners", () => {
|
|
32
32
|
const warning: Warning = {
|
|
33
33
|
type: "missing_specification",
|
|
34
34
|
message: "Specification is missing",
|
|
@@ -39,12 +39,12 @@ describe("WarningAlert", () => {
|
|
|
39
39
|
render(<WarningAlert warning={warning} />);
|
|
40
40
|
|
|
41
41
|
const alert = screen.getByTestId("warning-alert");
|
|
42
|
-
expect(alert).toHaveClass("border-yellow-500");
|
|
43
42
|
expect(alert).toHaveClass("bg-yellow-50");
|
|
43
|
+
expect(alert).toHaveClass("rounded-lg");
|
|
44
44
|
expect(screen.getByText("Specification is missing")).toBeInTheDocument();
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
it("renders info severity with blue
|
|
47
|
+
it("renders info severity with blue background and rounded corners", () => {
|
|
48
48
|
const warning: Warning = {
|
|
49
49
|
type: "unapproved_dependency",
|
|
50
50
|
message: "Dependency not approved",
|
|
@@ -55,8 +55,8 @@ describe("WarningAlert", () => {
|
|
|
55
55
|
render(<WarningAlert warning={warning} />);
|
|
56
56
|
|
|
57
57
|
const alert = screen.getByTestId("warning-alert");
|
|
58
|
-
expect(alert).toHaveClass("border-blue-500");
|
|
59
58
|
expect(alert).toHaveClass("bg-blue-50");
|
|
59
|
+
expect(alert).toHaveClass("rounded-lg");
|
|
60
60
|
expect(screen.getByText("Dependency not approved")).toBeInTheDocument();
|
|
61
61
|
});
|
|
62
62
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
4
|
+
import { render, screen, cleanup } from "@testing-library/react";
|
|
5
|
+
import "@testing-library/jest-dom/vitest";
|
|
6
|
+
import { FeedbackFilters } from "../../../components/feedback/feedback-filters";
|
|
7
|
+
|
|
8
|
+
describe("FeedbackFilters - ラベル短縮", () => {
|
|
9
|
+
afterEach(() => cleanup());
|
|
10
|
+
|
|
11
|
+
it("requirement-gapタイプのラベルが'Req Gap'と短縮表示される", () => {
|
|
12
|
+
render(<FeedbackFilters activeFilters={{}} onFilterChange={vi.fn()} />);
|
|
13
|
+
|
|
14
|
+
// data-testidで要素を取得してテキストを確認
|
|
15
|
+
const reqGapButton = screen.getByTestId("filter-type-requirement-gap");
|
|
16
|
+
expect(reqGapButton).toHaveTextContent("Req Gap");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("spec-mismatchタイプのラベルが'Spec Mismatch'と短縮表示される", () => {
|
|
20
|
+
render(<FeedbackFilters activeFilters={{}} onFilterChange={vi.fn()} />);
|
|
21
|
+
|
|
22
|
+
const specMismatchButton = screen.getByTestId("filter-type-spec-mismatch");
|
|
23
|
+
expect(specMismatchButton).toHaveTextContent("Spec Mismatch");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("セグメントボタンのパディングが拡大されている(px-3 py-1.5)", () => {
|
|
27
|
+
render(<FeedbackFilters activeFilters={{}} onFilterChange={vi.fn()} />);
|
|
28
|
+
|
|
29
|
+
const allTypeButton = screen.getByTestId("filter-type-all");
|
|
30
|
+
expect(allTypeButton.className).toContain("px-3");
|
|
31
|
+
expect(allTypeButton.className).toContain("py-1.5");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -67,4 +67,16 @@ describe("DrillDownBreadcrumb", () => {
|
|
|
67
67
|
|
|
68
68
|
expect(onBack).not.toHaveBeenCalled();
|
|
69
69
|
});
|
|
70
|
+
|
|
71
|
+
it("back button uses warm hover colors", () => {
|
|
72
|
+
const onBack = vi.fn();
|
|
73
|
+
|
|
74
|
+
const { container } = render(
|
|
75
|
+
<DrillDownBreadcrumb requirementTitle="User Login" onBack={onBack} />
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const button = container.querySelector("button");
|
|
79
|
+
expect(button).toHaveClass("hover:bg-warm-200/60");
|
|
80
|
+
expect(button).toHaveClass("hover:text-warm-900");
|
|
81
|
+
});
|
|
70
82
|
});
|
|
@@ -2,24 +2,24 @@ import { describe, it, expect } from "vitest";
|
|
|
2
2
|
import { EDGE_STYLES } from "../../../components/graph/edge-styles";
|
|
3
3
|
|
|
4
4
|
describe("EDGE_STYLES", () => {
|
|
5
|
-
it("dependency has stroke #
|
|
5
|
+
it("dependency has stroke #94a3b8 (slate-400) and strokeWidth 2 (brand color system)", () => {
|
|
6
6
|
expect(EDGE_STYLES.dependency).toEqual({
|
|
7
|
-
stroke: "#
|
|
7
|
+
stroke: "#94a3b8",
|
|
8
8
|
strokeWidth: 2,
|
|
9
9
|
});
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
it("implements has stroke #
|
|
12
|
+
it("implements has stroke #dc2626 (brand red), strokeWidth 2, and strokeDasharray '5,5'", () => {
|
|
13
13
|
expect(EDGE_STYLES.implements).toEqual({
|
|
14
|
-
stroke: "#
|
|
14
|
+
stroke: "#dc2626",
|
|
15
15
|
strokeWidth: 2,
|
|
16
16
|
strokeDasharray: "5,5",
|
|
17
17
|
});
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
it("tracks has stroke #
|
|
20
|
+
it("tracks has stroke #a855f7 (purple), strokeWidth 1.5, and strokeDasharray '2,2'", () => {
|
|
21
21
|
expect(EDGE_STYLES.tracks).toEqual({
|
|
22
|
-
stroke: "#
|
|
22
|
+
stroke: "#a855f7",
|
|
23
23
|
strokeWidth: 1.5,
|
|
24
24
|
strokeDasharray: "2,2",
|
|
25
25
|
});
|
|
@@ -46,7 +46,7 @@ describe("IssueNode", () => {
|
|
|
46
46
|
expect(screen.getByText("Issue #123")).toBeInTheDocument();
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
it("renders open status with
|
|
49
|
+
it("renders open status with amber background class (brand color system)", () => {
|
|
50
50
|
const { container } = render(
|
|
51
51
|
<IssueNode
|
|
52
52
|
{...defaultNodeProps}
|
|
@@ -61,11 +61,11 @@ describe("IssueNode", () => {
|
|
|
61
61
|
/>
|
|
62
62
|
);
|
|
63
63
|
|
|
64
|
-
const nodeDiv = container.querySelector(".bg-
|
|
64
|
+
const nodeDiv = container.querySelector(".bg-amber-50");
|
|
65
65
|
expect(nodeDiv).toBeInTheDocument();
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
it("renders closed status with
|
|
68
|
+
it("renders closed status with emerald background class (brand color system)", () => {
|
|
69
69
|
const { container } = render(
|
|
70
70
|
<IssueNode
|
|
71
71
|
{...defaultNodeProps}
|
|
@@ -80,11 +80,11 @@ describe("IssueNode", () => {
|
|
|
80
80
|
/>
|
|
81
81
|
);
|
|
82
82
|
|
|
83
|
-
const nodeDiv = container.querySelector(".bg-
|
|
83
|
+
const nodeDiv = container.querySelector(".bg-emerald-50");
|
|
84
84
|
expect(nodeDiv).toBeInTheDocument();
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
it("renders in_progress status with blue background class", () => {
|
|
87
|
+
it("renders in_progress status with blue background class (brand color system)", () => {
|
|
88
88
|
const { container } = render(
|
|
89
89
|
<IssueNode
|
|
90
90
|
{...defaultNodeProps}
|
|
@@ -99,10 +99,29 @@ describe("IssueNode", () => {
|
|
|
99
99
|
/>
|
|
100
100
|
);
|
|
101
101
|
|
|
102
|
-
const nodeDiv = container.querySelector(".bg-blue-
|
|
102
|
+
const nodeDiv = container.querySelector(".bg-blue-50");
|
|
103
103
|
expect(nodeDiv).toBeInTheDocument();
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
+
it("renders status badge with semi-transparent white background", () => {
|
|
107
|
+
const { container } = render(
|
|
108
|
+
<IssueNode
|
|
109
|
+
{...defaultNodeProps}
|
|
110
|
+
id="issue-spec-000001-123"
|
|
111
|
+
data={{
|
|
112
|
+
label: "Issue #123",
|
|
113
|
+
status: "open",
|
|
114
|
+
issueNumber: 123,
|
|
115
|
+
issueUrl: "https://github.com/test/repo/issues/123",
|
|
116
|
+
}}
|
|
117
|
+
type="issue"
|
|
118
|
+
/>
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const badge = container.querySelector(".bg-white\\/80");
|
|
122
|
+
expect(badge).toBeInTheDocument();
|
|
123
|
+
});
|
|
124
|
+
|
|
106
125
|
it("renders status badge", () => {
|
|
107
126
|
render(
|
|
108
127
|
<IssueNode
|
|
@@ -148,4 +148,49 @@ describe("RequirementNode", () => {
|
|
|
148
148
|
expect(screen.getByRole("button", { name: /1 spec$/i })).toBeInTheDocument();
|
|
149
149
|
});
|
|
150
150
|
});
|
|
151
|
+
|
|
152
|
+
describe("brand color system", () => {
|
|
153
|
+
it("renders approved status with blue border and background (brand color system)", () => {
|
|
154
|
+
const { container } = render(
|
|
155
|
+
<RequirementNode
|
|
156
|
+
{...baseProps}
|
|
157
|
+
data={{ ...baseProps.data, status: "approved" }}
|
|
158
|
+
/>
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const nodeDiv = container.querySelector(".border-blue-300.bg-blue-50");
|
|
162
|
+
expect(nodeDiv).toBeInTheDocument();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("renders implemented status with emerald border and background (brand color system)", () => {
|
|
166
|
+
const { container } = render(
|
|
167
|
+
<RequirementNode
|
|
168
|
+
{...baseProps}
|
|
169
|
+
data={{ ...baseProps.data, status: "implemented" }}
|
|
170
|
+
/>
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const nodeDiv = container.querySelector(".border-emerald-300.bg-emerald-50");
|
|
174
|
+
expect(nodeDiv).toBeInTheDocument();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("renders with shadow-md for improved visibility", () => {
|
|
178
|
+
const { container } = render(
|
|
179
|
+
<RequirementNode {...baseProps} />
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const nodeDiv = container.querySelector(".shadow-md");
|
|
183
|
+
expect(nodeDiv).toBeInTheDocument();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("renders handles with softened gray color (!bg-gray-300)", () => {
|
|
187
|
+
const { container } = render(
|
|
188
|
+
<RequirementNode {...baseProps} />
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
// Check that no handle has the old !bg-gray-400 class
|
|
192
|
+
const oldHandles = container.querySelector('[class*="bg-gray-400"]');
|
|
193
|
+
expect(oldHandles).not.toBeInTheDocument();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
151
196
|
});
|
|
@@ -41,7 +41,7 @@ describe("SpecificationNode", () => {
|
|
|
41
41
|
expect(screen.getByText("spec-000001")).toBeInTheDocument();
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
it("renders draft status with
|
|
44
|
+
it("renders draft status with gray background class (brand color system)", () => {
|
|
45
45
|
const { container } = render(
|
|
46
46
|
<SpecificationNode
|
|
47
47
|
{...defaultNodeProps}
|
|
@@ -51,11 +51,11 @@ describe("SpecificationNode", () => {
|
|
|
51
51
|
/>
|
|
52
52
|
);
|
|
53
53
|
|
|
54
|
-
const nodeDiv = container.querySelector(".bg-
|
|
54
|
+
const nodeDiv = container.querySelector(".bg-gray-100");
|
|
55
55
|
expect(nodeDiv).toBeInTheDocument();
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
-
it("renders approved status with
|
|
58
|
+
it("renders approved status with blue background class (brand color system)", () => {
|
|
59
59
|
const { container } = render(
|
|
60
60
|
<SpecificationNode
|
|
61
61
|
{...defaultNodeProps}
|
|
@@ -65,11 +65,11 @@ describe("SpecificationNode", () => {
|
|
|
65
65
|
/>
|
|
66
66
|
);
|
|
67
67
|
|
|
68
|
-
const nodeDiv = container.querySelector(".bg-
|
|
68
|
+
const nodeDiv = container.querySelector(".bg-blue-100");
|
|
69
69
|
expect(nodeDiv).toBeInTheDocument();
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
-
it("renders implemented status with emerald background class", () => {
|
|
72
|
+
it("renders implemented status with emerald background class (brand color system)", () => {
|
|
73
73
|
const { container } = render(
|
|
74
74
|
<SpecificationNode
|
|
75
75
|
{...defaultNodeProps}
|
|
@@ -79,36 +79,49 @@ describe("SpecificationNode", () => {
|
|
|
79
79
|
/>
|
|
80
80
|
);
|
|
81
81
|
|
|
82
|
-
const nodeDiv = container.querySelector(".bg-emerald-
|
|
82
|
+
const nodeDiv = container.querySelector(".bg-emerald-100");
|
|
83
83
|
expect(nodeDiv).toBeInTheDocument();
|
|
84
84
|
});
|
|
85
85
|
|
|
86
|
-
it("renders
|
|
86
|
+
it("renders deprecated status with red background class (brand color system)", () => {
|
|
87
87
|
const { container } = render(
|
|
88
88
|
<SpecificationNode
|
|
89
89
|
{...defaultNodeProps}
|
|
90
90
|
id="spec-000004"
|
|
91
|
-
data={{ label: "spec-000004", status: "
|
|
91
|
+
data={{ label: "spec-000004", status: "deprecated" }}
|
|
92
92
|
type="specification"
|
|
93
93
|
/>
|
|
94
94
|
);
|
|
95
95
|
|
|
96
|
-
const nodeDiv = container.querySelector(".bg-
|
|
96
|
+
const nodeDiv = container.querySelector(".bg-red-100");
|
|
97
97
|
expect(nodeDiv).toBeInTheDocument();
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
-
it("renders
|
|
100
|
+
it("renders status badge with semi-transparent white background", () => {
|
|
101
101
|
const { container } = render(
|
|
102
102
|
<SpecificationNode
|
|
103
103
|
{...defaultNodeProps}
|
|
104
|
-
id="spec-
|
|
105
|
-
data={{ label: "
|
|
104
|
+
id="spec-000001"
|
|
105
|
+
data={{ label: "My Spec Title", status: "draft" }}
|
|
106
106
|
type="specification"
|
|
107
107
|
/>
|
|
108
108
|
);
|
|
109
109
|
|
|
110
|
-
const
|
|
111
|
-
expect(
|
|
110
|
+
const badge = container.querySelector(".bg-white\\/80");
|
|
111
|
+
expect(badge).toBeInTheDocument();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("renders label text as title", () => {
|
|
115
|
+
render(
|
|
116
|
+
<SpecificationNode
|
|
117
|
+
{...defaultNodeProps}
|
|
118
|
+
id="spec-000001"
|
|
119
|
+
data={{ label: "My Spec Title", status: "draft" }}
|
|
120
|
+
type="specification"
|
|
121
|
+
/>
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
expect(screen.getByText("My Spec Title")).toBeInTheDocument();
|
|
112
125
|
});
|
|
113
126
|
|
|
114
127
|
it("renders status badge", () => {
|