@wakastellar/ui 2.3.4 → 2.4.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.
Files changed (194) hide show
  1. package/dist/blocks/dashboard/index.d.ts +4 -1
  2. package/dist/blocks/empty-states/index.d.ts +4 -1
  3. package/dist/blocks/error-pages/index.d.ts +4 -1
  4. package/dist/blocks/index.d.ts +1 -1
  5. package/dist/blocks/landing/index.d.ts +4 -1
  6. package/dist/blocks/pricing/index.d.ts +5 -1
  7. package/dist/blocks/sidebar/index.d.ts +5 -1
  8. package/dist/index.cjs.js +130 -130
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.es.js +7905 -7856
  11. package/dist/stories/Button.d.ts +14 -0
  12. package/dist/stories/Button.stories.d.ts +8 -0
  13. package/dist/stories/Header.d.ts +11 -0
  14. package/dist/stories/Header.stories.d.ts +6 -0
  15. package/dist/stories/Page.d.ts +2 -0
  16. package/dist/stories/Page.stories.d.ts +6 -0
  17. package/dist/types/index.d.ts +1 -0
  18. package/dist/types/link.d.ts +23 -0
  19. package/package.json +11 -3
  20. package/src/blocks/activity-timeline/ActivityTimeline.stories.tsx +460 -0
  21. package/src/blocks/apm-overview/APMOverview.stories.tsx +435 -0
  22. package/src/blocks/auth-2fa/Auth2FA.stories.tsx +308 -0
  23. package/src/blocks/calendar-view/CalendarView.stories.tsx +398 -0
  24. package/src/blocks/chat/Chat.stories.tsx +466 -0
  25. package/src/blocks/chat-interface/ChatInterface.stories.tsx +412 -0
  26. package/src/blocks/checkout-flow/CheckoutFlow.stories.tsx +408 -0
  27. package/src/blocks/cicd-builder/CICDBuilder.stories.tsx +499 -0
  28. package/src/blocks/cloud-cost-dashboard/CloudCostDashboard.stories.tsx +356 -0
  29. package/src/blocks/container-orchestrator/ContainerOrchestrator.stories.tsx +461 -0
  30. package/src/blocks/dashboard/Dashboard.stories.tsx +559 -0
  31. package/src/blocks/dashboard/index.tsx +68 -27
  32. package/src/blocks/dashboard-kpi/DashboardKPI.stories.tsx +422 -0
  33. package/src/blocks/database-admin/DatabaseAdmin.stories.tsx +393 -0
  34. package/src/blocks/deployment-dashboard/DeploymentDashboard.stories.tsx +457 -0
  35. package/src/blocks/empty-states/EmptyStates.stories.tsx +467 -0
  36. package/src/blocks/empty-states/index.tsx +26 -8
  37. package/src/blocks/error-pages/ErrorPages.stories.tsx +401 -0
  38. package/src/blocks/error-pages/index.tsx +26 -8
  39. package/src/blocks/faq/FAQ.stories.tsx +416 -0
  40. package/src/blocks/file-manager/FileManager.stories.tsx +413 -0
  41. package/src/blocks/footer/Footer.stories.tsx +328 -0
  42. package/src/blocks/gitops-sync-status/GitOpsSyncStatus.stories.tsx +529 -0
  43. package/src/blocks/header/WakaHeader.stories.tsx +455 -0
  44. package/src/blocks/headtab/Headtab.stories.tsx +369 -0
  45. package/src/blocks/i18n-editor/I18nEditor.stories.tsx +451 -0
  46. package/src/blocks/incident-manager/IncidentManager.stories.tsx +464 -0
  47. package/src/blocks/index.ts +1 -1
  48. package/src/blocks/infrastructure-map/InfrastructureMap.stories.tsx +533 -0
  49. package/src/blocks/kanban-board/KanbanBoard.stories.tsx +494 -0
  50. package/src/blocks/landing/WakaLanding.stories.tsx +449 -0
  51. package/src/blocks/landing/index.tsx +125 -66
  52. package/src/blocks/language-selector/LanguageSelector.stories.tsx +345 -0
  53. package/src/blocks/layout/Layout.stories.tsx +373 -0
  54. package/src/blocks/login/Login.stories.tsx +443 -0
  55. package/src/blocks/on-call-schedule/OnCallSchedule.stories.tsx +381 -0
  56. package/src/blocks/player-profile/PlayerProfile.stories.tsx +316 -0
  57. package/src/blocks/pricing/WakaPricing.stories.tsx +530 -0
  58. package/src/blocks/pricing/index.tsx +38 -4
  59. package/src/blocks/profile/Profile.stories.tsx +371 -0
  60. package/src/blocks/release-notes/ReleaseNotes.stories.tsx +431 -0
  61. package/src/blocks/settings/Settings.stories.tsx +520 -0
  62. package/src/blocks/sidebar/WakaSidebar.stories.tsx +513 -0
  63. package/src/blocks/sidebar/index.tsx +49 -20
  64. package/src/blocks/theme-creator-block/ThemeCreatorBlock.stories.tsx +370 -0
  65. package/src/blocks/user-management/UserManagement.stories.tsx +411 -0
  66. package/src/blocks/wizard/Wizard.stories.tsx +683 -0
  67. package/src/components/accordion/Accordion.stories.tsx +93 -0
  68. package/src/components/alert/Alert.stories.tsx +95 -0
  69. package/src/components/alert-dialog/AlertDialog.stories.tsx +126 -0
  70. package/src/components/aspect-ratio/AspectRatio.stories.tsx +118 -0
  71. package/src/components/avatar/Avatar.stories.tsx +104 -0
  72. package/src/components/button/Button.stories.tsx +12 -1
  73. package/src/components/calendar/Calendar.stories.tsx +102 -0
  74. package/src/components/card/Card.stories.tsx +125 -0
  75. package/src/components/checkbox/Checkbox.stories.tsx +100 -0
  76. package/src/components/code/Code.stories.tsx +402 -0
  77. package/src/components/collapsible/Collapsible.stories.tsx +123 -0
  78. package/src/components/command/Command.stories.tsx +207 -0
  79. package/src/components/context-menu/ContextMenu.stories.tsx +220 -0
  80. package/src/components/dialog/Dialog.stories.tsx +157 -0
  81. package/src/components/dropdown-menu/DropdownMenu.stories.tsx +225 -0
  82. package/src/components/form/Form.stories.tsx +413 -0
  83. package/src/components/hover-card/HoverCard.stories.tsx +148 -0
  84. package/src/components/input-otp/InputOTP.stories.tsx +255 -0
  85. package/src/components/label/Label.stories.tsx +68 -0
  86. package/src/components/menubar/Menubar.stories.tsx +278 -0
  87. package/src/components/navigation-menu/NavigationMenu.stories.tsx +202 -0
  88. package/src/components/popover/Popover.stories.tsx +199 -0
  89. package/src/components/progress/Progress.stories.tsx +104 -0
  90. package/src/components/radio-group/RadioGroup.stories.tsx +138 -0
  91. package/src/components/scroll-area/ScrollArea.stories.tsx +153 -0
  92. package/src/components/select/Select.stories.tsx +146 -0
  93. package/src/components/separator/Separator.stories.tsx +117 -0
  94. package/src/components/sheet/Sheet.stories.tsx +195 -0
  95. package/src/components/skeleton/Skeleton.stories.tsx +114 -0
  96. package/src/components/slider/Slider.stories.tsx +157 -0
  97. package/src/components/switch/Switch.stories.tsx +114 -0
  98. package/src/components/table/Table.stories.tsx +153 -0
  99. package/src/components/tabs/Tabs.stories.tsx +155 -0
  100. package/src/components/textarea/Textarea.stories.tsx +116 -0
  101. package/src/components/toast/Toast.stories.tsx +160 -0
  102. package/src/components/toggle/Toggle.stories.tsx +125 -0
  103. package/src/components/tooltip/Tooltip.stories.tsx +133 -0
  104. package/src/components/typography/Typography.stories.tsx +207 -0
  105. package/src/components/waka-3d-pie-chart/Waka3DPieChart.stories.tsx +308 -0
  106. package/src/components/waka-achievement-unlock/WakaAchievementUnlock.stories.tsx +353 -0
  107. package/src/components/waka-artifact-list/WakaArtifactList.stories.tsx +258 -0
  108. package/src/components/waka-autocomplete/WakaAutocomplete.stories.tsx +224 -0
  109. package/src/components/waka-badge-showcase/WakaBadgeShowcase.stories.tsx +269 -0
  110. package/src/components/waka-barcode/WakaBarcode.stories.tsx +227 -0
  111. package/src/components/waka-bottom-sheet/WakaBottomSheet.stories.tsx +408 -0
  112. package/src/components/waka-breadcrumb/WakaBreadcrumb.stories.tsx +237 -0
  113. package/src/components/waka-build-matrix/WakaBuildMatrix.stories.tsx +328 -0
  114. package/src/components/waka-carousel/WakaCarousel.stories.tsx +296 -0
  115. package/src/components/waka-charts/WakaCharts.stories.tsx +519 -0
  116. package/src/components/waka-color-picker/WakaColorPicker.stories.tsx +200 -0
  117. package/src/components/waka-combobox/WakaCombobox.stories.tsx +250 -0
  118. package/src/components/waka-container-list/WakaContainerList.stories.tsx +315 -0
  119. package/src/components/waka-contribution-graph/WakaContributionGraph.stories.tsx +354 -0
  120. package/src/components/waka-cost-breakdown/WakaCostBreakdown.stories.tsx +348 -0
  121. package/src/components/waka-daily-reward/WakaDailyReward.stories.tsx +365 -0
  122. package/src/components/waka-database-card/WakaDatabaseCard.stories.tsx +306 -0
  123. package/src/components/waka-date-range-picker/WakaDateRangePicker.stories.tsx +339 -0
  124. package/src/components/waka-datetime-picker/WakaDateTimePicker.stories.tsx +317 -0
  125. package/src/components/waka-deployment-lane/WakaDeploymentLane.stories.tsx +292 -0
  126. package/src/components/waka-dock/WakaDock.stories.tsx +332 -0
  127. package/src/components/waka-drawer/WakaDrawer.stories.tsx +437 -0
  128. package/src/components/waka-env-var-editor/WakaEnvVarEditor.stories.tsx +263 -0
  129. package/src/components/waka-error-shake/WakaErrorShake.stories.tsx +410 -0
  130. package/src/components/waka-file-upload/WakaFileUpload.stories.tsx +239 -0
  131. package/src/components/waka-flow-diagram/WakaFlowDiagram.stories.tsx +365 -0
  132. package/src/components/waka-funnel-chart/WakaFunnelChart.stories.tsx +281 -0
  133. package/src/components/waka-glow-card/WakaGlowCard.stories.tsx +274 -0
  134. package/src/components/waka-haptic-button/WakaHapticButton.stories.tsx +349 -0
  135. package/src/components/waka-health-pulse/WakaHealthPulse.stories.tsx +293 -0
  136. package/src/components/waka-heatmap/WakaHeatmap.stories.tsx +376 -0
  137. package/src/components/waka-image/WakaImage.stories.tsx +255 -0
  138. package/src/components/waka-incident-timeline/WakaIncidentTimeline.stories.tsx +300 -0
  139. package/src/components/waka-kanban/WakaKanban.stories.tsx +399 -0
  140. package/src/components/waka-kubernetes-overview/WakaKubernetesOverview.stories.tsx +281 -0
  141. package/src/components/waka-leaderboard/WakaLeaderboard.stories.tsx +300 -0
  142. package/src/components/waka-level-progress/WakaLevelProgress.stories.tsx +313 -0
  143. package/src/components/waka-loading-orbit/WakaLoadingOrbit.stories.tsx +413 -0
  144. package/src/components/waka-log-viewer/WakaLogViewer.stories.tsx +312 -0
  145. package/src/components/waka-loot-box/WakaLootBox.stories.tsx +374 -0
  146. package/src/components/waka-metric-sparkline/WakaMetricSparkline.stories.tsx +312 -0
  147. package/src/components/waka-migration-list/WakaMigrationList.stories.tsx +289 -0
  148. package/src/components/waka-modal/WakaModal.stories.tsx +434 -0
  149. package/src/components/waka-morph-button/WakaMorphButton.stories.tsx +405 -0
  150. package/src/components/waka-network-topology/WakaNetworkTopology.stories.tsx +364 -0
  151. package/src/components/waka-notifications/WakaNotifications.stories.tsx +290 -0
  152. package/src/components/waka-number-input/WakaNumberInput.stories.tsx +282 -0
  153. package/src/components/waka-pagination/WakaPagination.stories.tsx +328 -0
  154. package/src/components/waka-password-strength/WakaPasswordStrength.stories.tsx +318 -0
  155. package/src/components/waka-pipeline-view/WakaPipelineView.stories.tsx +386 -0
  156. package/src/components/waka-player-card/WakaPlayerCard.stories.tsx +333 -0
  157. package/src/components/waka-pod-card/WakaPodCard.stories.tsx +435 -0
  158. package/src/components/waka-qrcode/WakaQRCode.stories.tsx +232 -0
  159. package/src/components/waka-query-explain/WakaQueryExplain.stories.tsx +407 -0
  160. package/src/components/waka-quest-card/WakaQuestCard.stories.tsx +394 -0
  161. package/src/components/waka-quota-bar/WakaQuotaBar.stories.tsx +435 -0
  162. package/src/components/waka-radar-score/WakaRadarScore.stories.tsx +372 -0
  163. package/src/components/waka-resource-gauge/WakaResourceGauge.stories.tsx +366 -0
  164. package/src/components/waka-rich-text-editor/WakaRichTextEditor.stories.tsx +238 -0
  165. package/src/components/waka-sankey-diagram/WakaSankeyDiagram.stories.tsx +389 -0
  166. package/src/components/waka-scratch-card/WakaScratchCard.stories.tsx +388 -0
  167. package/src/components/waka-secret-card/WakaSecretCard.stories.tsx +314 -0
  168. package/src/components/waka-segmented-control/WakaSegmentedControl.stories.tsx +309 -0
  169. package/src/components/waka-server-rack/WakaServerRack.stories.tsx +382 -0
  170. package/src/components/waka-service-graph/WakaServiceGraph.stories.tsx +262 -0
  171. package/src/components/waka-skeleton-wave/WakaSkeletonWave.stories.tsx +321 -0
  172. package/src/components/waka-skill-tree/WakaSkillTree.stories.tsx +308 -0
  173. package/src/components/waka-spin-wheel/WakaSpinWheel.stories.tsx +368 -0
  174. package/src/components/waka-spinner/WakaSpinner.stories.tsx +156 -0
  175. package/src/components/waka-stat/WakaStat.stories.tsx +334 -0
  176. package/src/components/waka-status-matrix/WakaStatusMatrix.stories.tsx +331 -0
  177. package/src/components/waka-stepper/WakaStepper.stories.tsx +468 -0
  178. package/src/components/waka-streak-counter/WakaStreakCounter.stories.tsx +235 -0
  179. package/src/components/waka-success-explosion/WakaSuccessExplosion.stories.tsx +389 -0
  180. package/src/components/waka-tabs-morph/WakaTabsMorph.stories.tsx +471 -0
  181. package/src/components/waka-terminal-output/WakaTerminalOutput.stories.tsx +351 -0
  182. package/src/components/waka-test-report/WakaTestReport.stories.tsx +322 -0
  183. package/src/components/waka-tilt-card/WakaTiltCard.stories.tsx +300 -0
  184. package/src/components/waka-time-picker/WakaTimePicker.stories.tsx +227 -0
  185. package/src/components/waka-timeline/WakaTimeline.stories.tsx +383 -0
  186. package/src/components/waka-tournament-bracket/WakaTournamentBracket.stories.tsx +375 -0
  187. package/src/components/waka-trace-viewer/WakaTraceViewer.stories.tsx +445 -0
  188. package/src/components/waka-tree/WakaTree.stories.tsx +359 -0
  189. package/src/components/waka-treemap-chart/WakaTreemapChart.stories.tsx +378 -0
  190. package/src/components/waka-typewriter/WakaTypewriter.stories.tsx +366 -0
  191. package/src/components/waka-versus-card/WakaVersusCard.stories.tsx +530 -0
  192. package/src/components/waka-video/WakaVideo.stories.tsx +203 -0
  193. package/src/components/waka-virtual-list/WakaVirtualList.stories.tsx +273 -0
  194. package/src/components/waka-xp-bar/WakaXPBar.stories.tsx +305 -0
@@ -0,0 +1,351 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { WakaTerminalOutput, useTerminalOutput } from './index'
3
+ import type { LogLine, LogLevel } from './index'
4
+ import * as React from 'react'
5
+
6
+ const sampleLogs: LogLine[] = [
7
+ { content: '[2024-01-15 10:30:45] INFO Starting application server...', level: 'info', timestamp: new Date('2024-01-15T10:30:45') },
8
+ { content: '[2024-01-15 10:30:46] DEBUG Loading configuration from /etc/app/config.yaml', level: 'debug', timestamp: new Date('2024-01-15T10:30:46') },
9
+ { content: '[2024-01-15 10:30:47] INFO Database connection established', level: 'info', timestamp: new Date('2024-01-15T10:30:47') },
10
+ { content: '[2024-01-15 10:30:48] WARN High memory usage detected: 85%', level: 'warn', timestamp: new Date('2024-01-15T10:30:48') },
11
+ { content: '[2024-01-15 10:30:49] INFO HTTP server listening on port 3000', level: 'info', timestamp: new Date('2024-01-15T10:30:49') },
12
+ { content: '[2024-01-15 10:30:50] DEBUG Request: GET /api/health - 200 OK (12ms)', level: 'debug', timestamp: new Date('2024-01-15T10:30:50') },
13
+ { content: '[2024-01-15 10:30:51] ERROR Connection refused to redis://localhost:6379', level: 'error', timestamp: new Date('2024-01-15T10:30:51') },
14
+ { content: '[2024-01-15 10:30:52] INFO Retrying connection in 5 seconds...', level: 'info', timestamp: new Date('2024-01-15T10:30:52') },
15
+ { content: '[2024-01-15 10:30:57] INFO Redis connection restored', level: 'info', timestamp: new Date('2024-01-15T10:30:57') },
16
+ { content: '[2024-01-15 10:30:58] DEBUG Cache hit for key: user:12345', level: 'debug', timestamp: new Date('2024-01-15T10:30:58') },
17
+ ]
18
+
19
+ const ansiLogs: LogLine[] = [
20
+ { content: '\x1b[32m✓\x1b[0m All tests passed', level: 'info' },
21
+ { content: '\x1b[34mINFO\x1b[0m Compiling TypeScript...', level: 'info' },
22
+ { content: '\x1b[33mWARN\x1b[0m Deprecated API usage detected', level: 'warn' },
23
+ { content: '\x1b[31mERROR\x1b[0m Failed to load module: \x1b[1m@app/utils\x1b[0m', level: 'error' },
24
+ { content: '\x1b[90mDEBUG\x1b[0m Memory: \x1b[36m256MB\x1b[0m / \x1b[36m512MB\x1b[0m', level: 'debug' },
25
+ { content: '\x1b[32m✓\x1b[0m Build completed in \x1b[1m\x1b[33m2.3s\x1b[0m', level: 'info' },
26
+ ]
27
+
28
+ const meta: Meta<typeof WakaTerminalOutput> = {
29
+ title: 'Components/DevOps/WakaTerminalOutput',
30
+ component: WakaTerminalOutput,
31
+ parameters: {
32
+ layout: 'centered',
33
+ docs: {
34
+ description: {
35
+ component: 'A terminal output viewer with ANSI color support, syntax highlighting, search, filtering, virtualization, and auto-scroll.',
36
+ },
37
+ },
38
+ },
39
+ tags: ['autodocs'],
40
+ argTypes: {
41
+ showLineNumbers: {
42
+ control: 'boolean',
43
+ description: 'Show line numbers',
44
+ },
45
+ showTimestamps: {
46
+ control: 'boolean',
47
+ description: 'Show timestamps',
48
+ },
49
+ autoScroll: {
50
+ control: 'boolean',
51
+ description: 'Auto-scroll to bottom on new lines',
52
+ },
53
+ searchable: {
54
+ control: 'boolean',
55
+ description: 'Enable search functionality',
56
+ },
57
+ filterable: {
58
+ control: 'boolean',
59
+ description: 'Enable log level filtering',
60
+ },
61
+ height: {
62
+ control: { type: 'number', min: 200, max: 800, step: 50 },
63
+ description: 'Height of the terminal',
64
+ },
65
+ },
66
+ }
67
+
68
+ export default meta
69
+ type Story = StoryObj<typeof WakaTerminalOutput>
70
+
71
+ export const Default: Story = {
72
+ args: {
73
+ lines: sampleLogs,
74
+ height: 400,
75
+ showLineNumbers: true,
76
+ showTimestamps: true,
77
+ autoScroll: true,
78
+ searchable: true,
79
+ filterable: true,
80
+ },
81
+ render: (args) => (
82
+ <div className="w-[700px]">
83
+ <WakaTerminalOutput {...args} />
84
+ </div>
85
+ ),
86
+ }
87
+
88
+ export const WithLineNumbers: Story = {
89
+ render: () => (
90
+ <div className="w-[700px]">
91
+ <WakaTerminalOutput
92
+ lines={sampleLogs}
93
+ showLineNumbers
94
+ height={400}
95
+ />
96
+ </div>
97
+ ),
98
+ }
99
+
100
+ export const WithTimestamps: Story = {
101
+ render: () => (
102
+ <div className="w-[700px]">
103
+ <WakaTerminalOutput
104
+ lines={sampleLogs}
105
+ showTimestamps
106
+ height={400}
107
+ />
108
+ </div>
109
+ ),
110
+ }
111
+
112
+ export const FullFeatured: Story = {
113
+ render: () => (
114
+ <div className="w-[800px]">
115
+ <WakaTerminalOutput
116
+ lines={sampleLogs}
117
+ showLineNumbers
118
+ showTimestamps
119
+ searchable
120
+ filterable
121
+ height={450}
122
+ onCopy={(content) => console.log('Copied:', content)}
123
+ onClear={() => console.log('Cleared')}
124
+ />
125
+ </div>
126
+ ),
127
+ }
128
+
129
+ export const AnsiColors: Story = {
130
+ render: () => (
131
+ <div className="w-[700px]">
132
+ <p className="text-sm text-muted-foreground mb-4">
133
+ Terminal output with ANSI escape codes for colors
134
+ </p>
135
+ <WakaTerminalOutput
136
+ lines={ansiLogs}
137
+ height={300}
138
+ />
139
+ </div>
140
+ ),
141
+ }
142
+
143
+ export const WithHook: Story = {
144
+ render: () => {
145
+ const { lines, log, warn, error, debug, clear } = useTerminalOutput()
146
+
147
+ return (
148
+ <div className="w-[700px] space-y-4">
149
+ <WakaTerminalOutput
150
+ lines={lines}
151
+ showLineNumbers
152
+ searchable
153
+ filterable
154
+ height={300}
155
+ onClear={clear}
156
+ />
157
+
158
+ <div className="flex gap-2">
159
+ <button
160
+ onClick={() => log(`[${new Date().toISOString()}] INFO Application running normally`)}
161
+ className="px-3 py-1.5 text-xs rounded bg-blue-500 text-white"
162
+ >
163
+ Add Info
164
+ </button>
165
+ <button
166
+ onClick={() => warn(`[${new Date().toISOString()}] WARN Memory usage at 75%`)}
167
+ className="px-3 py-1.5 text-xs rounded bg-yellow-500 text-black"
168
+ >
169
+ Add Warning
170
+ </button>
171
+ <button
172
+ onClick={() => error(`[${new Date().toISOString()}] ERROR Connection failed`)}
173
+ className="px-3 py-1.5 text-xs rounded bg-red-500 text-white"
174
+ >
175
+ Add Error
176
+ </button>
177
+ <button
178
+ onClick={() => debug(`[${new Date().toISOString()}] DEBUG Request processed`)}
179
+ className="px-3 py-1.5 text-xs rounded bg-gray-500 text-white"
180
+ >
181
+ Add Debug
182
+ </button>
183
+ <button
184
+ onClick={clear}
185
+ className="px-3 py-1.5 text-xs rounded border hover:bg-muted"
186
+ >
187
+ Clear
188
+ </button>
189
+ </div>
190
+ </div>
191
+ )
192
+ },
193
+ }
194
+
195
+ export const LiveStreaming: Story = {
196
+ render: () => {
197
+ const [lines, setLines] = React.useState<LogLine[]>([
198
+ { content: '[INFO] Starting log stream...', level: 'info', timestamp: new Date() },
199
+ ])
200
+
201
+ React.useEffect(() => {
202
+ const messages = [
203
+ { content: 'GET /api/users - 200 OK (23ms)', level: 'debug' as LogLevel },
204
+ { content: 'POST /api/orders - 201 Created (156ms)', level: 'info' as LogLevel },
205
+ { content: 'Connection pool exhausted, retrying...', level: 'warn' as LogLevel },
206
+ { content: 'GET /api/products - 200 OK (12ms)', level: 'debug' as LogLevel },
207
+ { content: 'Cache invalidated for key: products:*', level: 'info' as LogLevel },
208
+ { content: 'Database query timeout after 30s', level: 'error' as LogLevel },
209
+ { content: 'Reconnecting to database...', level: 'info' as LogLevel },
210
+ { content: 'DELETE /api/sessions/expired - 204 (89ms)', level: 'debug' as LogLevel },
211
+ ]
212
+
213
+ let index = 0
214
+ const interval = setInterval(() => {
215
+ const msg = messages[index % messages.length]
216
+ setLines(prev => [
217
+ ...prev,
218
+ {
219
+ content: `[${new Date().toISOString()}] ${msg.level.toUpperCase()} ${msg.content}`,
220
+ level: msg.level,
221
+ timestamp: new Date(),
222
+ },
223
+ ].slice(-100))
224
+ index++
225
+ }, 800)
226
+
227
+ return () => clearInterval(interval)
228
+ }, [])
229
+
230
+ return (
231
+ <div className="w-[700px]">
232
+ <p className="text-sm text-muted-foreground mb-4">
233
+ Live streaming logs (simulated)
234
+ </p>
235
+ <WakaTerminalOutput
236
+ lines={lines}
237
+ showLineNumbers
238
+ filterable
239
+ autoScroll
240
+ height={400}
241
+ />
242
+ </div>
243
+ )
244
+ },
245
+ }
246
+
247
+ export const LargeLogs: Story = {
248
+ render: () => {
249
+ const largeLogs: LogLine[] = Array.from({ length: 1000 }, (_, i) => ({
250
+ content: `[${new Date().toISOString()}] ${['INFO', 'DEBUG', 'WARN', 'ERROR'][Math.floor(Math.random() * 4)]} Log line ${i + 1} - ${Math.random().toString(36).substring(7)}`,
251
+ level: ['info', 'debug', 'warn', 'error'][Math.floor(Math.random() * 4)] as LogLevel,
252
+ timestamp: new Date(Date.now() - (1000 - i) * 1000),
253
+ }))
254
+
255
+ return (
256
+ <div className="w-[700px]">
257
+ <p className="text-sm text-muted-foreground mb-4">
258
+ 1000 lines with virtualization for smooth scrolling
259
+ </p>
260
+ <WakaTerminalOutput
261
+ lines={largeLogs}
262
+ showLineNumbers
263
+ searchable
264
+ filterable
265
+ height={400}
266
+ />
267
+ </div>
268
+ )
269
+ },
270
+ }
271
+
272
+ export const BuildOutput: Story = {
273
+ render: () => {
274
+ const buildLogs: LogLine[] = [
275
+ { content: '> wakastar-ui@1.0.0 build', level: 'info' },
276
+ { content: '> tsc && vite build', level: 'info' },
277
+ { content: '', level: 'info' },
278
+ { content: '\x1b[34mvite v5.0.12\x1b[0m building for production...', level: 'info' },
279
+ { content: '✓ 156 modules transformed.', level: 'info' },
280
+ { content: '\x1b[33mwarn\x1b[0m: The following dependencies are externalized:', level: 'warn' },
281
+ { content: ' - react', level: 'warn' },
282
+ { content: ' - react-dom', level: 'warn' },
283
+ { content: 'rendering chunks...', level: 'debug' },
284
+ { content: 'computing gzip size...', level: 'debug' },
285
+ { content: '', level: 'info' },
286
+ { content: 'dist/index.js 45.32 kB │ gzip: 14.21 kB', level: 'info' },
287
+ { content: 'dist/index.css 12.45 kB │ gzip: 3.12 kB', level: 'info' },
288
+ { content: '', level: 'info' },
289
+ { content: '\x1b[32m✓ built in 2.34s\x1b[0m', level: 'info' },
290
+ ]
291
+
292
+ return (
293
+ <div className="w-[700px]">
294
+ <p className="text-sm text-muted-foreground mb-4">
295
+ Build output with progress information
296
+ </p>
297
+ <WakaTerminalOutput
298
+ lines={buildLogs}
299
+ height={350}
300
+ />
301
+ </div>
302
+ )
303
+ },
304
+ }
305
+
306
+ export const DockerLogs: Story = {
307
+ render: () => {
308
+ const dockerLogs: LogLine[] = [
309
+ { content: 'Sending build context to Docker daemon 2.048kB', level: 'info' },
310
+ { content: 'Step 1/8 : FROM node:20-alpine', level: 'info' },
311
+ { content: ' ---> 3c2c7d5e3c12', level: 'debug' },
312
+ { content: 'Step 2/8 : WORKDIR /app', level: 'info' },
313
+ { content: ' ---> Using cache', level: 'debug' },
314
+ { content: ' ---> 8f9b7c6d5e4a', level: 'debug' },
315
+ { content: 'Step 3/8 : COPY package*.json ./', level: 'info' },
316
+ { content: ' ---> 1a2b3c4d5e6f', level: 'debug' },
317
+ { content: 'Step 4/8 : RUN npm ci --only=production', level: 'info' },
318
+ { content: 'npm warn deprecated @types/node@16.0.0', level: 'warn' },
319
+ { content: 'added 156 packages in 12s', level: 'info' },
320
+ { content: 'Step 5/8 : COPY . .', level: 'info' },
321
+ { content: ' ---> 7g8h9i0j1k2l', level: 'debug' },
322
+ { content: 'Step 6/8 : RUN npm run build', level: 'info' },
323
+ { content: 'Successfully built a1b2c3d4e5f6', level: 'info' },
324
+ { content: 'Successfully tagged myapp:latest', level: 'info' },
325
+ ]
326
+
327
+ return (
328
+ <div className="w-[700px]">
329
+ <p className="text-sm text-muted-foreground mb-4">
330
+ Docker build logs
331
+ </p>
332
+ <WakaTerminalOutput
333
+ lines={dockerLogs}
334
+ showLineNumbers
335
+ height={400}
336
+ />
337
+ </div>
338
+ )
339
+ },
340
+ }
341
+
342
+ export const Empty: Story = {
343
+ render: () => (
344
+ <div className="w-[700px]">
345
+ <WakaTerminalOutput
346
+ lines={[]}
347
+ height={300}
348
+ />
349
+ </div>
350
+ ),
351
+ }
@@ -0,0 +1,322 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { WakaTestReport, defaultTestSuites, defaultCoverage } from './index'
3
+ import type { TestSuite, TestCase, TestStatus, CoverageReport } from './index'
4
+ import * as React from 'react'
5
+
6
+ const allPassingSuites: TestSuite[] = [
7
+ {
8
+ id: '1',
9
+ name: 'Unit Tests',
10
+ file: 'src/__tests__/unit.test.ts',
11
+ duration: 1250,
12
+ tests: [
13
+ { id: '1-1', name: 'should format date correctly', status: 'passed', duration: 45 },
14
+ { id: '1-2', name: 'should parse JSON strings', status: 'passed', duration: 32 },
15
+ { id: '1-3', name: 'should validate email format', status: 'passed', duration: 28 },
16
+ { id: '1-4', name: 'should hash passwords securely', status: 'passed', duration: 156 },
17
+ ],
18
+ },
19
+ {
20
+ id: '2',
21
+ name: 'Integration Tests',
22
+ file: 'src/__tests__/integration.test.ts',
23
+ duration: 3500,
24
+ tests: [
25
+ { id: '2-1', name: 'should connect to database', status: 'passed', duration: 890 },
26
+ { id: '2-2', name: 'should authenticate user', status: 'passed', duration: 1200 },
27
+ { id: '2-3', name: 'should fetch user profile', status: 'passed', duration: 450 },
28
+ ],
29
+ },
30
+ ]
31
+
32
+ const allFailingSuites: TestSuite[] = [
33
+ {
34
+ id: '1',
35
+ name: 'API Tests',
36
+ file: 'src/api/__tests__/api.test.ts',
37
+ duration: 5200,
38
+ tests: [
39
+ {
40
+ id: '1-1',
41
+ name: 'should return 200 for valid request',
42
+ status: 'failed',
43
+ duration: 245,
44
+ errorMessage: 'Expected status code 200, received 500',
45
+ stackTrace: 'Error: Request failed\n at ApiClient.request (src/api/client.ts:45:15)',
46
+ },
47
+ {
48
+ id: '1-2',
49
+ name: 'should handle rate limiting',
50
+ status: 'failed',
51
+ duration: 1500,
52
+ errorMessage: 'Rate limit not applied correctly',
53
+ },
54
+ {
55
+ id: '1-3',
56
+ name: 'should validate request body',
57
+ status: 'failed',
58
+ duration: 89,
59
+ errorMessage: 'Validation error: missing required field "email"',
60
+ },
61
+ ],
62
+ },
63
+ ]
64
+
65
+ const mixedSuites: TestSuite[] = [
66
+ {
67
+ id: '1',
68
+ name: 'Component Tests',
69
+ file: 'src/components/__tests__/Button.test.tsx',
70
+ duration: 890,
71
+ tests: [
72
+ { id: '1-1', name: 'renders with default props', status: 'passed', duration: 45, file: 'src/components/__tests__/Button.test.tsx', line: 12 },
73
+ { id: '1-2', name: 'handles click events', status: 'passed', duration: 67, file: 'src/components/__tests__/Button.test.tsx', line: 28 },
74
+ { id: '1-3', name: 'applies custom className', status: 'passed', duration: 23, file: 'src/components/__tests__/Button.test.tsx', line: 45 },
75
+ {
76
+ id: '1-4',
77
+ name: 'disables when loading',
78
+ status: 'failed',
79
+ duration: 89,
80
+ errorMessage: 'Button should be disabled when loading=true',
81
+ file: 'src/components/__tests__/Button.test.tsx',
82
+ line: 62,
83
+ },
84
+ ],
85
+ },
86
+ {
87
+ id: '2',
88
+ name: 'Hook Tests',
89
+ file: 'src/hooks/__tests__/useAuth.test.ts',
90
+ duration: 1200,
91
+ tests: [
92
+ { id: '2-1', name: 'returns authenticated state', status: 'passed', duration: 234 },
93
+ { id: '2-2', name: 'handles login flow', status: 'passed', duration: 456 },
94
+ { id: '2-3', name: 'handles logout', status: 'skipped', duration: 0 },
95
+ { id: '2-4', name: 'refreshes token on expiry', status: 'pending', duration: 0 },
96
+ ],
97
+ },
98
+ {
99
+ id: '3',
100
+ name: 'E2E Tests',
101
+ file: 'e2e/checkout.test.ts',
102
+ duration: 15000,
103
+ tests: [
104
+ { id: '3-1', name: 'completes checkout flow', status: 'passed', duration: 8500 },
105
+ {
106
+ id: '3-2',
107
+ name: 'handles payment failure',
108
+ status: 'failed',
109
+ duration: 4500,
110
+ errorMessage: 'Timeout: Element not found within 5000ms',
111
+ stackTrace: 'TimeoutError: waitForSelector timed out\n at e2e/checkout.test.ts:89:5',
112
+ },
113
+ ],
114
+ },
115
+ ]
116
+
117
+ const lowCoverage: CoverageReport = {
118
+ lines: { covered: 450, total: 1500, percentage: 30.0 },
119
+ branches: { covered: 25, total: 120, percentage: 20.8 },
120
+ functions: { covered: 45, total: 180, percentage: 25.0 },
121
+ statements: { covered: 520, total: 1650, percentage: 31.5 },
122
+ }
123
+
124
+ const highCoverage: CoverageReport = {
125
+ lines: { covered: 1425, total: 1500, percentage: 95.0 },
126
+ branches: { covered: 108, total: 120, percentage: 90.0 },
127
+ functions: { covered: 171, total: 180, percentage: 95.0 },
128
+ statements: { covered: 1567, total: 1650, percentage: 95.0 },
129
+ }
130
+
131
+ const meta: Meta<typeof WakaTestReport> = {
132
+ title: 'Components/DevOps/WakaTestReport',
133
+ component: WakaTestReport,
134
+ parameters: {
135
+ layout: 'centered',
136
+ docs: {
137
+ description: {
138
+ component: 'A test report viewer with suite expansion, coverage bars, search, filtering, and detailed error messages.',
139
+ },
140
+ },
141
+ },
142
+ tags: ['autodocs'],
143
+ argTypes: {
144
+ showFailedOnly: {
145
+ control: 'boolean',
146
+ description: 'Show only failed tests by default',
147
+ },
148
+ },
149
+ }
150
+
151
+ export default meta
152
+ type Story = StoryObj<typeof WakaTestReport>
153
+
154
+ export const Default: Story = {
155
+ args: {
156
+ suites: defaultTestSuites,
157
+ coverage: defaultCoverage,
158
+ },
159
+ render: (args) => (
160
+ <div className="w-[800px] h-[600px]">
161
+ <WakaTestReport
162
+ {...args}
163
+ onTestClick={(test) => console.log('Test click:', test)}
164
+ onViewFile={(file, line) => console.log('View file:', file, line)}
165
+ />
166
+ </div>
167
+ ),
168
+ }
169
+
170
+ export const AllPassing: Story = {
171
+ render: () => (
172
+ <div className="w-[800px] h-[500px]">
173
+ <WakaTestReport
174
+ suites={allPassingSuites}
175
+ coverage={highCoverage}
176
+ title="All Tests Passing"
177
+ />
178
+ </div>
179
+ ),
180
+ }
181
+
182
+ export const AllFailing: Story = {
183
+ render: () => (
184
+ <div className="w-[800px] h-[500px]">
185
+ <WakaTestReport
186
+ suites={allFailingSuites}
187
+ coverage={lowCoverage}
188
+ title="Build Failed"
189
+ />
190
+ </div>
191
+ ),
192
+ }
193
+
194
+ export const MixedResults: Story = {
195
+ render: () => (
196
+ <div className="w-[800px] h-[600px]">
197
+ <WakaTestReport
198
+ suites={mixedSuites}
199
+ coverage={defaultCoverage}
200
+ title="Test Results"
201
+ onTestClick={(test) => console.log('Test click:', test)}
202
+ onViewFile={(file, line) => console.log('View file:', file, line)}
203
+ />
204
+ </div>
205
+ ),
206
+ }
207
+
208
+ export const FailedOnlyFilter: Story = {
209
+ render: () => (
210
+ <div className="w-[800px] h-[500px]">
211
+ <p className="text-sm text-muted-foreground mb-4">
212
+ Pre-filtered to show only failed tests
213
+ </p>
214
+ <WakaTestReport
215
+ suites={mixedSuites}
216
+ showFailedOnly
217
+ title="Failed Tests"
218
+ />
219
+ </div>
220
+ ),
221
+ }
222
+
223
+ export const NoCoverage: Story = {
224
+ render: () => (
225
+ <div className="w-[800px] h-[500px]">
226
+ <WakaTestReport
227
+ suites={defaultTestSuites}
228
+ title="Tests Without Coverage"
229
+ />
230
+ </div>
231
+ ),
232
+ }
233
+
234
+ export const LowCoverage: Story = {
235
+ render: () => (
236
+ <div className="w-[800px] h-[500px]">
237
+ <p className="text-sm text-muted-foreground mb-4">
238
+ Coverage below threshold (red/yellow bars)
239
+ </p>
240
+ <WakaTestReport
241
+ suites={allFailingSuites}
242
+ coverage={lowCoverage}
243
+ title="Low Coverage Report"
244
+ />
245
+ </div>
246
+ ),
247
+ }
248
+
249
+ export const HighCoverage: Story = {
250
+ render: () => (
251
+ <div className="w-[800px] h-[500px]">
252
+ <WakaTestReport
253
+ suites={allPassingSuites}
254
+ coverage={highCoverage}
255
+ title="Excellent Coverage"
256
+ />
257
+ </div>
258
+ ),
259
+ }
260
+
261
+ export const ManySuites: Story = {
262
+ render: () => {
263
+ const manySuites: TestSuite[] = Array.from({ length: 15 }, (_, i) => ({
264
+ id: `suite-${i}`,
265
+ name: `Test Suite ${i + 1}`,
266
+ file: `src/modules/module-${i}/__tests__/index.test.ts`,
267
+ duration: Math.floor(Math.random() * 5000) + 500,
268
+ tests: Array.from({ length: Math.floor(Math.random() * 8) + 2 }, (_, j) => ({
269
+ id: `${i}-${j}`,
270
+ name: `Test case ${j + 1} for suite ${i + 1}`,
271
+ status: ['passed', 'passed', 'passed', 'failed', 'skipped'][Math.floor(Math.random() * 5)] as TestStatus,
272
+ duration: Math.floor(Math.random() * 500) + 10,
273
+ })),
274
+ }))
275
+
276
+ return (
277
+ <div className="w-[800px] h-[600px]">
278
+ <WakaTestReport
279
+ suites={manySuites}
280
+ coverage={defaultCoverage}
281
+ title="Large Test Suite"
282
+ />
283
+ </div>
284
+ )
285
+ },
286
+ }
287
+
288
+ export const Empty: Story = {
289
+ render: () => (
290
+ <div className="w-[800px] h-[400px]">
291
+ <WakaTestReport suites={[]} title="No Tests" />
292
+ </div>
293
+ ),
294
+ }
295
+
296
+ export const CITestReport: Story = {
297
+ render: () => (
298
+ <div className="p-6 rounded-xl border bg-card">
299
+ <div className="flex items-center justify-between mb-6">
300
+ <div>
301
+ <h2 className="text-xl font-bold">CI Pipeline - Test Stage</h2>
302
+ <p className="text-sm text-muted-foreground">
303
+ Branch: feature/add-auth • Commit: abc123f
304
+ </p>
305
+ </div>
306
+ <div className="flex items-center gap-2">
307
+ <div className="w-2 h-2 rounded-full bg-yellow-500" />
308
+ <span className="text-sm text-yellow-500 font-medium">Some tests failed</span>
309
+ </div>
310
+ </div>
311
+
312
+ <div className="w-[750px]">
313
+ <WakaTestReport
314
+ suites={mixedSuites}
315
+ coverage={defaultCoverage}
316
+ onTestClick={(test) => console.log('Test click:', test)}
317
+ onViewFile={(file, line) => console.log('View file:', file, line)}
318
+ />
319
+ </div>
320
+ </div>
321
+ ),
322
+ }