@semiont/react-ui 0.2.33-build.78 → 0.2.33-build.80

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 (219) hide show
  1. package/dist/EventBusContext-7GvDyO0d.d.mts +414 -0
  2. package/dist/{PdfAnnotationCanvas.client-ADC4FFSE.mjs → PdfAnnotationCanvas.client-RAJRPQLU.mjs} +42 -27
  3. package/dist/PdfAnnotationCanvas.client-RAJRPQLU.mjs.map +1 -0
  4. package/dist/{ar-RNNSPLQB.mjs → ar-4ZEORRW2.mjs} +8 -4
  5. package/dist/ar-4ZEORRW2.mjs.map +1 -0
  6. package/dist/{bn-S2CDL7EC.mjs → bn-SEDE5BQJ.mjs} +8 -4
  7. package/dist/bn-SEDE5BQJ.mjs.map +1 -0
  8. package/dist/{chunk-UDX2Q35T.mjs → chunk-D7NBW4RV.mjs} +8 -4
  9. package/dist/chunk-D7NBW4RV.mjs.map +1 -0
  10. package/dist/{chunk-35LLVRFK.mjs → chunk-ZR4ZV2LY.mjs} +206 -146
  11. package/dist/chunk-ZR4ZV2LY.mjs.map +1 -0
  12. package/dist/{cs-RSV675WU.mjs → cs-7W4WF5WD.mjs} +8 -4
  13. package/dist/cs-7W4WF5WD.mjs.map +1 -0
  14. package/dist/{da-CHXNPWJC.mjs → da-75XGBCBK.mjs} +8 -4
  15. package/dist/da-75XGBCBK.mjs.map +1 -0
  16. package/dist/{de-KPEZ53D4.mjs → de-ODJVFLHM.mjs} +8 -4
  17. package/dist/de-ODJVFLHM.mjs.map +1 -0
  18. package/dist/{el-MW2BME5T.mjs → el-C4PM4WB3.mjs} +8 -4
  19. package/dist/el-C4PM4WB3.mjs.map +1 -0
  20. package/dist/{en-EVMIX24Y.mjs → en-KJCJQ4OO.mjs} +2 -2
  21. package/dist/{es-HQ24NYS3.mjs → es-WD33R7QL.mjs} +8 -4
  22. package/dist/es-WD33R7QL.mjs.map +1 -0
  23. package/dist/{fa-W34LRLHG.mjs → fa-2BP6V56P.mjs} +8 -4
  24. package/dist/fa-2BP6V56P.mjs.map +1 -0
  25. package/dist/{fi-3U44IGOA.mjs → fi-USRRW24J.mjs} +8 -4
  26. package/dist/fi-USRRW24J.mjs.map +1 -0
  27. package/dist/{fr-N7DKX6NN.mjs → fr-EC5S6WVF.mjs} +8 -4
  28. package/dist/fr-EC5S6WVF.mjs.map +1 -0
  29. package/dist/{he-CS4WRXN3.mjs → he-7TBVIKAA.mjs} +8 -4
  30. package/dist/he-7TBVIKAA.mjs.map +1 -0
  31. package/dist/{hi-GJDY46KA.mjs → hi-FO4VIZLA.mjs} +8 -4
  32. package/dist/hi-FO4VIZLA.mjs.map +1 -0
  33. package/dist/{id-WAEZJK2Y.mjs → id-7U7GGVWY.mjs} +8 -4
  34. package/dist/id-7U7GGVWY.mjs.map +1 -0
  35. package/dist/index.css +123 -85
  36. package/dist/index.css.map +1 -1
  37. package/dist/index.d.mts +699 -529
  38. package/dist/index.mjs +4291 -3491
  39. package/dist/index.mjs.map +1 -1
  40. package/dist/{it-VDNDMZPU.mjs → it-Y4OPL6I2.mjs} +8 -4
  41. package/dist/it-Y4OPL6I2.mjs.map +1 -0
  42. package/dist/{ja-5PEH56J5.mjs → ja-PK7SQL55.mjs} +8 -4
  43. package/dist/ja-PK7SQL55.mjs.map +1 -0
  44. package/dist/{ko-JYPL3WVA.mjs → ko-L25PXMYD.mjs} +8 -4
  45. package/dist/ko-L25PXMYD.mjs.map +1 -0
  46. package/dist/{ms-5PZVW76T.mjs → ms-STH777QM.mjs} +8 -4
  47. package/dist/ms-STH777QM.mjs.map +1 -0
  48. package/dist/{nl-YXES36KM.mjs → nl-Y7LECDDR.mjs} +8 -4
  49. package/dist/nl-Y7LECDDR.mjs.map +1 -0
  50. package/dist/{no-XRA2UCQD.mjs → no-KEKCEWU6.mjs} +8 -4
  51. package/dist/no-KEKCEWU6.mjs.map +1 -0
  52. package/dist/{pl-WH6LJA5G.mjs → pl-7A7OC75O.mjs} +8 -4
  53. package/dist/pl-7A7OC75O.mjs.map +1 -0
  54. package/dist/{pt-7GAG57BM.mjs → pt-35HTM7RA.mjs} +8 -4
  55. package/dist/pt-35HTM7RA.mjs.map +1 -0
  56. package/dist/{ro-BTDDRB7N.mjs → ro-VAWL5KQA.mjs} +8 -4
  57. package/dist/ro-VAWL5KQA.mjs.map +1 -0
  58. package/dist/{sv-7V5C2IT4.mjs → sv-7ZK5EQEB.mjs} +8 -4
  59. package/dist/sv-7ZK5EQEB.mjs.map +1 -0
  60. package/dist/test-utils.d.mts +18 -8
  61. package/dist/test-utils.mjs +36 -14
  62. package/dist/test-utils.mjs.map +1 -1
  63. package/dist/{th-LPKYLBX5.mjs → th-UDWZ4X34.mjs} +8 -4
  64. package/dist/th-UDWZ4X34.mjs.map +1 -0
  65. package/dist/{tr-DU4RQL4M.mjs → tr-4WMPK3UX.mjs} +8 -4
  66. package/dist/tr-4WMPK3UX.mjs.map +1 -0
  67. package/dist/{uk-36UHTDDI.mjs → uk-SSLASQYJ.mjs} +8 -4
  68. package/dist/uk-SSLASQYJ.mjs.map +1 -0
  69. package/dist/{vi-GDHOUZDH.mjs → vi-IF42Z5PU.mjs} +8 -4
  70. package/dist/vi-IF42Z5PU.mjs.map +1 -0
  71. package/dist/{zh-TYUID4XZ.mjs → zh-HRQTNTAI.mjs} +8 -4
  72. package/dist/zh-HRQTNTAI.mjs.map +1 -0
  73. package/package.json +8 -2
  74. package/src/components/CodeMirrorRenderer.tsx +66 -93
  75. package/src/components/DetectionProgressWidget.tsx +16 -5
  76. package/src/components/LiveRegion.tsx +18 -18
  77. package/src/components/ResizeHandle.tsx +10 -4
  78. package/src/components/SessionTimer.tsx +2 -2
  79. package/src/components/Toolbar.tsx +18 -9
  80. package/src/components/__tests__/SessionTimer.test.tsx +9 -9
  81. package/src/components/annotation/AnnotateToolbar.tsx +17 -15
  82. package/src/components/annotation/__tests__/AnnotateToolbar.test.tsx +165 -63
  83. package/src/components/annotation/annotation-entries.css +10 -0
  84. package/src/components/annotation-popups/JsonLdView.tsx +8 -2
  85. package/src/components/image-annotation/AnnotationOverlay.tsx +42 -22
  86. package/src/components/image-annotation/SvgDrawingCanvas.tsx +27 -30
  87. package/src/components/layout/__tests__/LeftSidebar.test.tsx +12 -33
  88. package/src/components/layout/__tests__/PageLayout.test.tsx +37 -32
  89. package/src/components/layout/__tests__/UnifiedHeader.test.tsx +21 -40
  90. package/src/components/modals/ResourceSearchModal.tsx +2 -2
  91. package/src/components/modals/SearchModal.tsx +1 -1
  92. package/src/components/navigation/CollapsibleResourceNavigation.tsx +14 -9
  93. package/src/components/navigation/NavigationTabs.css +36 -24
  94. package/src/components/navigation/ObservableLink.tsx +91 -0
  95. package/src/components/navigation/SimpleNavigation.tsx +20 -16
  96. package/src/components/navigation/SortableResourceTab.tsx +11 -5
  97. package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +51 -26
  98. package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +28 -22
  99. package/src/components/resource/AnnotateView.tsx +64 -138
  100. package/src/components/resource/AnnotationHistory.tsx +12 -13
  101. package/src/components/resource/BrowseView.tsx +89 -177
  102. package/src/components/resource/HistoryEvent.tsx +16 -11
  103. package/src/components/resource/ResourceViewer.tsx +201 -370
  104. package/src/components/resource/__tests__/BrowseView.test.tsx +631 -0
  105. package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +231 -0
  106. package/src/components/resource/event-formatting.ts +316 -0
  107. package/src/components/resource/panels/AssessmentEntry.tsx +25 -33
  108. package/src/components/resource/panels/AssessmentPanel.tsx +137 -31
  109. package/src/components/resource/panels/CollaborationPanel.tsx +20 -13
  110. package/src/components/resource/panels/CommentEntry.tsx +38 -32
  111. package/src/components/resource/panels/CommentsPanel.tsx +153 -31
  112. package/src/components/resource/panels/DetectSection.css +36 -1
  113. package/src/components/resource/panels/DetectSection.tsx +38 -10
  114. package/src/components/resource/panels/HighlightEntry.tsx +25 -33
  115. package/src/components/resource/panels/HighlightPanel.tsx +100 -25
  116. package/src/components/resource/panels/ReferenceEntry.tsx +61 -75
  117. package/src/components/resource/panels/ReferencesPanel.tsx +166 -49
  118. package/src/components/resource/panels/ResourceInfoPanel.tsx +47 -48
  119. package/src/components/resource/panels/StatisticsPanel.tsx +9 -19
  120. package/src/components/resource/panels/TagEntry.tsx +25 -33
  121. package/src/components/resource/panels/TaggingPanel.tsx +141 -25
  122. package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +46 -101
  123. package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +566 -0
  124. package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +86 -78
  125. package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +146 -141
  126. package/src/components/resource/panels/__tests__/DetectSection.test.tsx +480 -0
  127. package/src/components/resource/panels/__tests__/HighlightPanel.detectionProgress.test.tsx +362 -0
  128. package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +228 -103
  129. package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +117 -61
  130. package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +586 -0
  131. package/src/components/settings/SettingsPanel.tsx +15 -12
  132. package/src/features/admin-devops/__tests__/AdminDevOpsPage.test.tsx +1 -46
  133. package/src/features/admin-devops/components/AdminDevOpsPage.tsx +0 -9
  134. package/src/features/admin-security/__tests__/AdminSecurityPage.test.tsx +0 -3
  135. package/src/features/admin-security/components/AdminSecurityPage.tsx +0 -9
  136. package/src/features/admin-users/__tests__/AdminUsersPage.test.tsx +0 -3
  137. package/src/features/admin-users/components/AdminUsersPage.tsx +0 -9
  138. package/src/features/moderate-entity-tags/__tests__/EntityTagsPage.test.tsx +0 -3
  139. package/src/features/moderate-entity-tags/components/EntityTagsPage.tsx +1 -9
  140. package/src/features/moderate-recent/__tests__/RecentDocumentsPage.test.tsx +0 -32
  141. package/src/features/moderate-recent/components/RecentDocumentsPage.tsx +1 -9
  142. package/src/features/moderate-tag-schemas/__tests__/TagSchemasPage.test.tsx +0 -32
  143. package/src/features/moderate-tag-schemas/components/TagSchemasPage.tsx +1 -9
  144. package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +51 -54
  145. package/src/features/resource-compose/components/ResourceComposePage.tsx +3 -13
  146. package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +39 -45
  147. package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +16 -27
  148. package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +231 -0
  149. package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +234 -0
  150. package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +388 -0
  151. package/src/features/resource-viewer/__tests__/DetectionProgressDismissal.test.tsx +318 -0
  152. package/src/features/resource-viewer/__tests__/GenerationFlowIntegration.test.tsx +504 -0
  153. package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +145 -91
  154. package/src/features/resource-viewer/__tests__/detection-progress-flow.test.tsx +322 -0
  155. package/src/features/resource-viewer/components/ResourceViewerPage.tsx +325 -476
  156. package/src/styles/motivations/motivation-assessment.css +28 -0
  157. package/src/styles/patterns/panel-helpers.css +26 -0
  158. package/translations/ar.json +7 -3
  159. package/translations/bn.json +7 -3
  160. package/translations/cs.json +7 -3
  161. package/translations/da.json +7 -3
  162. package/translations/de.json +7 -3
  163. package/translations/el.json +7 -3
  164. package/translations/en.json +7 -3
  165. package/translations/es.json +7 -3
  166. package/translations/fa.json +7 -3
  167. package/translations/fi.json +7 -3
  168. package/translations/fr.json +7 -3
  169. package/translations/he.json +7 -3
  170. package/translations/hi.json +7 -3
  171. package/translations/id.json +7 -3
  172. package/translations/it.json +7 -3
  173. package/translations/ja.json +7 -3
  174. package/translations/ko.json +7 -3
  175. package/translations/ms.json +7 -3
  176. package/translations/nl.json +7 -3
  177. package/translations/no.json +7 -3
  178. package/translations/pl.json +7 -3
  179. package/translations/pt.json +7 -3
  180. package/translations/ro.json +7 -3
  181. package/translations/sv.json +7 -3
  182. package/translations/th.json +7 -3
  183. package/translations/tr.json +7 -3
  184. package/translations/uk.json +7 -3
  185. package/translations/vi.json +7 -3
  186. package/translations/zh.json +7 -3
  187. package/dist/PdfAnnotationCanvas.client-ADC4FFSE.mjs.map +0 -1
  188. package/dist/TranslationManager-Co_5fSxl.d.mts +0 -118
  189. package/dist/ar-RNNSPLQB.mjs.map +0 -1
  190. package/dist/bn-S2CDL7EC.mjs.map +0 -1
  191. package/dist/chunk-35LLVRFK.mjs.map +0 -1
  192. package/dist/chunk-UDX2Q35T.mjs.map +0 -1
  193. package/dist/cs-RSV675WU.mjs.map +0 -1
  194. package/dist/da-CHXNPWJC.mjs.map +0 -1
  195. package/dist/de-KPEZ53D4.mjs.map +0 -1
  196. package/dist/el-MW2BME5T.mjs.map +0 -1
  197. package/dist/es-HQ24NYS3.mjs.map +0 -1
  198. package/dist/fa-W34LRLHG.mjs.map +0 -1
  199. package/dist/fi-3U44IGOA.mjs.map +0 -1
  200. package/dist/fr-N7DKX6NN.mjs.map +0 -1
  201. package/dist/he-CS4WRXN3.mjs.map +0 -1
  202. package/dist/hi-GJDY46KA.mjs.map +0 -1
  203. package/dist/id-WAEZJK2Y.mjs.map +0 -1
  204. package/dist/it-VDNDMZPU.mjs.map +0 -1
  205. package/dist/ja-5PEH56J5.mjs.map +0 -1
  206. package/dist/ko-JYPL3WVA.mjs.map +0 -1
  207. package/dist/ms-5PZVW76T.mjs.map +0 -1
  208. package/dist/nl-YXES36KM.mjs.map +0 -1
  209. package/dist/no-XRA2UCQD.mjs.map +0 -1
  210. package/dist/pl-WH6LJA5G.mjs.map +0 -1
  211. package/dist/pt-7GAG57BM.mjs.map +0 -1
  212. package/dist/ro-BTDDRB7N.mjs.map +0 -1
  213. package/dist/sv-7V5C2IT4.mjs.map +0 -1
  214. package/dist/th-LPKYLBX5.mjs.map +0 -1
  215. package/dist/tr-DU4RQL4M.mjs.map +0 -1
  216. package/dist/uk-36UHTDDI.mjs.map +0 -1
  217. package/dist/vi-GDHOUZDH.mjs.map +0 -1
  218. package/dist/zh-TYUID4XZ.mjs.map +0 -1
  219. /package/dist/{en-EVMIX24Y.mjs.map → en-KJCJQ4OO.mjs.map} +0 -0
@@ -0,0 +1,480 @@
1
+ /**
2
+ * Layer 1 Unit Test: DetectSection Component
3
+ *
4
+ * Tests the DetectSection component in isolation with mocked dependencies.
5
+ *
6
+ * This test verifies:
7
+ * - Detection progress rendering when detectionProgress prop is provided
8
+ * - Progress message display
9
+ * - Request parameters display
10
+ * - Form visibility toggling based on progress state
11
+ * - Event emission when detect button clicked
12
+ */
13
+
14
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
15
+ import React from 'react';
16
+ import { screen } from '@testing-library/react';
17
+ import { renderWithProviders } from '../../../../test-utils';
18
+ import userEvent from '@testing-library/user-event';
19
+ import { DetectSection } from '../DetectSection';
20
+ import { resetEventBusForTesting } from '../../../../contexts/EventBusContext';
21
+ import type { EventBus } from '../../../../contexts/EventBusContext';
22
+
23
+ // Mock translations
24
+ const mockT = vi.fn((key: string) => {
25
+ const translations: Record<string, string> = {
26
+ detectHighlights: 'Detect Highlights',
27
+ detectAssessments: 'Detect Assessments',
28
+ detectComments: 'Detect Comments',
29
+ instructions: 'Instructions',
30
+ optional: '(optional)',
31
+ instructionsPlaceholder: 'Enter custom instructions...',
32
+ toneLabel: 'Tone',
33
+ toneOptional: '(optional)',
34
+ toneScholarly: 'Scholarly',
35
+ toneExplanatory: 'Explanatory',
36
+ toneConversational: 'Conversational',
37
+ toneTechnical: 'Technical',
38
+ toneAnalytical: 'Analytical',
39
+ toneCritical: 'Critical',
40
+ toneBalanced: 'Balanced',
41
+ toneConstructive: 'Constructive',
42
+ densityLabel: 'Density',
43
+ densitySparse: 'Sparse',
44
+ densityDense: 'Dense',
45
+ detect: 'Detect',
46
+ };
47
+ return translations[key] || key;
48
+ });
49
+
50
+ vi.mock('../../../../contexts/TranslationContext', () => ({
51
+ useTranslations: () => mockT,
52
+ TranslationProvider: ({ children }: { children: React.ReactNode }) => children,
53
+ }));
54
+
55
+ describe('DetectSection', () => {
56
+ beforeEach(() => {
57
+ vi.clearAllMocks();
58
+ resetEventBusForTesting();
59
+ // Clear localStorage
60
+ if (typeof window !== 'undefined') {
61
+ localStorage.clear();
62
+ }
63
+ });
64
+
65
+ describe('Progress Display', () => {
66
+ it('should render progress message when detectionProgress prop provided', () => {
67
+ renderWithProviders(
68
+ <DetectSection
69
+ annotationType="highlight"
70
+ isDetecting={true}
71
+ detectionProgress={{
72
+ status: 'analyzing',
73
+ percentage: 30,
74
+ message: 'Analyzing text for highlights...',
75
+ }}
76
+ />
77
+ );
78
+
79
+ expect(screen.getByText('Analyzing text for highlights...')).toBeInTheDocument();
80
+ });
81
+
82
+ it('should render progress message with sparkle icon', () => {
83
+ renderWithProviders(
84
+ <DetectSection
85
+ annotationType="highlight"
86
+ isDetecting={true}
87
+ detectionProgress={{
88
+ status: 'analyzing',
89
+ message: 'Processing...',
90
+ }}
91
+ />
92
+ );
93
+
94
+ // Check for icon and message
95
+ const progressDiv = screen.getByText('Processing...').closest('.semiont-detection-progress__message');
96
+ expect(progressDiv).toBeInTheDocument();
97
+ expect(progressDiv?.querySelector('.semiont-detection-progress__icon')).toBeInTheDocument();
98
+ });
99
+
100
+ it('should render request parameters when provided', () => {
101
+ renderWithProviders(
102
+ <DetectSection
103
+ annotationType="highlight"
104
+ isDetecting={true}
105
+ detectionProgress={{
106
+ status: 'analyzing',
107
+ message: 'Analyzing...',
108
+ requestParams: [
109
+ { label: 'Instructions', value: 'Find important points' },
110
+ { label: 'Density', value: '5' },
111
+ ],
112
+ }}
113
+ />
114
+ );
115
+
116
+ expect(screen.getByText('Request Parameters:')).toBeInTheDocument();
117
+ expect(screen.getByText('Find important points')).toBeInTheDocument();
118
+ expect(screen.getByText(/Instructions:/)).toBeInTheDocument();
119
+ expect(screen.getByText('5')).toBeInTheDocument();
120
+ expect(screen.getByText(/Density:/)).toBeInTheDocument();
121
+ });
122
+
123
+ it('should hide form when detectionProgress is present', () => {
124
+ renderWithProviders(
125
+ <DetectSection
126
+ annotationType="highlight"
127
+ isDetecting={true}
128
+ detectionProgress={{
129
+ status: 'analyzing',
130
+ message: 'Analyzing...',
131
+ }}
132
+ />
133
+ );
134
+
135
+ // Form should not be visible
136
+ expect(screen.queryByPlaceholderText('Enter custom instructions...')).not.toBeInTheDocument();
137
+ expect(screen.queryByRole('button', { name: /✨ Detect/ })).not.toBeInTheDocument();
138
+ });
139
+
140
+ it('should show form when detectionProgress is null', () => {
141
+ renderWithProviders(
142
+ <DetectSection
143
+ annotationType="highlight"
144
+ isDetecting={false}
145
+ detectionProgress={null}
146
+ />
147
+ );
148
+
149
+ // Form should be visible
150
+ expect(screen.getByPlaceholderText('Enter custom instructions...')).toBeInTheDocument();
151
+ expect(screen.getByRole('button', { name: /✨ Detect/ })).toBeInTheDocument();
152
+ });
153
+
154
+ it('should show form when detectionProgress is undefined', () => {
155
+ renderWithProviders(
156
+ <DetectSection
157
+ annotationType="highlight"
158
+ isDetecting={false}
159
+ detectionProgress={undefined}
160
+ />
161
+ );
162
+
163
+ // Form should be visible
164
+ expect(screen.getByPlaceholderText('Enter custom instructions...')).toBeInTheDocument();
165
+ expect(screen.getByRole('button', { name: /✨ Detect/ })).toBeInTheDocument();
166
+ });
167
+
168
+ it('should keep progress visible after detection completes (isDetecting=false but progress exists)', () => {
169
+ renderWithProviders(
170
+ <DetectSection
171
+ annotationType="highlight"
172
+ isDetecting={false}
173
+ detectionProgress={{
174
+ status: 'complete',
175
+ percentage: 100,
176
+ message: 'Complete! Created 14 highlights',
177
+ }}
178
+ />
179
+ );
180
+
181
+ // Progress should still be visible
182
+ expect(screen.getByText('Complete! Created 14 highlights')).toBeInTheDocument();
183
+ // Form should NOT be visible
184
+ expect(screen.queryByPlaceholderText('Enter custom instructions...')).not.toBeInTheDocument();
185
+ });
186
+ });
187
+
188
+ describe('Annotation Type Variations', () => {
189
+ it('should render for highlight type', () => {
190
+ renderWithProviders(
191
+ <DetectSection
192
+ annotationType="highlight"
193
+ isDetecting={false}
194
+ detectionProgress={null}
195
+ />
196
+ );
197
+
198
+ expect(screen.getByText('Detect Highlights')).toBeInTheDocument();
199
+ });
200
+
201
+ it('should render for assessment type', () => {
202
+ renderWithProviders(
203
+ <DetectSection
204
+ annotationType="assessment"
205
+ isDetecting={false}
206
+ detectionProgress={null}
207
+ />
208
+ );
209
+
210
+ expect(screen.getByText('Detect Assessments')).toBeInTheDocument();
211
+ });
212
+
213
+ it('should render for comment type', () => {
214
+ renderWithProviders(
215
+ <DetectSection
216
+ annotationType="comment"
217
+ isDetecting={false}
218
+ detectionProgress={null}
219
+ />
220
+ );
221
+
222
+ expect(screen.getByText('Detect Comments')).toBeInTheDocument();
223
+ });
224
+
225
+ it('should show tone selector for comments', () => {
226
+ renderWithProviders(
227
+ <DetectSection
228
+ annotationType="comment"
229
+ isDetecting={false}
230
+ detectionProgress={null}
231
+ />
232
+ );
233
+
234
+ expect(screen.getByText('Scholarly')).toBeInTheDocument();
235
+ expect(screen.getByText('Explanatory')).toBeInTheDocument();
236
+ });
237
+
238
+ it('should show tone selector for assessments', () => {
239
+ renderWithProviders(
240
+ <DetectSection
241
+ annotationType="assessment"
242
+ isDetecting={false}
243
+ detectionProgress={null}
244
+ />
245
+ );
246
+
247
+ expect(screen.getByText('Analytical')).toBeInTheDocument();
248
+ expect(screen.getByText('Critical')).toBeInTheDocument();
249
+ });
250
+
251
+ it('should not show tone selector for highlights', () => {
252
+ renderWithProviders(
253
+ <DetectSection
254
+ annotationType="highlight"
255
+ isDetecting={false}
256
+ detectionProgress={null}
257
+ />
258
+ );
259
+
260
+ expect(screen.queryByText('Scholarly')).not.toBeInTheDocument();
261
+ expect(screen.queryByText('Analytical')).not.toBeInTheDocument();
262
+ });
263
+ });
264
+
265
+ describe('Event Emission', () => {
266
+ it('should emit detection:start event when detect button clicked', async () => {
267
+ const user = userEvent.setup();
268
+ const detectionHandler = vi.fn();
269
+
270
+ const { eventBus } = renderWithProviders(
271
+ <DetectSection
272
+ annotationType="highlight"
273
+ isDetecting={false}
274
+ detectionProgress={null}
275
+ />,
276
+ { returnEventBus: true }
277
+ );
278
+
279
+ eventBus!.on('detection:start', detectionHandler);
280
+
281
+ const detectButton = screen.getByRole('button', { name: /✨ Detect/ });
282
+ await user.click(detectButton);
283
+
284
+ expect(detectionHandler).toHaveBeenCalledWith({
285
+ motivation: 'highlighting',
286
+ options: expect.any(Object),
287
+ });
288
+
289
+ eventBus!.off('detection:start', detectionHandler);
290
+ });
291
+
292
+ it('should emit correct motivation for assessment type', async () => {
293
+ const user = userEvent.setup();
294
+ const detectionHandler = vi.fn();
295
+
296
+ const { eventBus } = renderWithProviders(
297
+ <DetectSection
298
+ annotationType="assessment"
299
+ isDetecting={false}
300
+ detectionProgress={null}
301
+ />,
302
+ { returnEventBus: true }
303
+ );
304
+
305
+ eventBus!.on('detection:start', detectionHandler);
306
+
307
+ const detectButton = screen.getByRole('button', { name: /✨ Detect/ });
308
+ await user.click(detectButton);
309
+
310
+ expect(detectionHandler).toHaveBeenCalledWith({
311
+ motivation: 'assessing',
312
+ options: expect.any(Object),
313
+ });
314
+
315
+ eventBus!.off('detection:start', detectionHandler);
316
+ });
317
+
318
+ it('should emit correct motivation for comment type', async () => {
319
+ const user = userEvent.setup();
320
+ const detectionHandler = vi.fn();
321
+
322
+ const { eventBus } = renderWithProviders(
323
+ <DetectSection
324
+ annotationType="comment"
325
+ isDetecting={false}
326
+ detectionProgress={null}
327
+ />,
328
+ { returnEventBus: true }
329
+ );
330
+
331
+ eventBus!.on('detection:start', detectionHandler);
332
+
333
+ const detectButton = screen.getByRole('button', { name: /✨ Detect/ });
334
+ await user.click(detectButton);
335
+
336
+ expect(detectionHandler).toHaveBeenCalledWith({
337
+ motivation: 'commenting',
338
+ options: expect.any(Object),
339
+ });
340
+
341
+ eventBus!.off('detection:start', detectionHandler);
342
+ });
343
+
344
+ it('should include instructions in event when provided', async () => {
345
+ const user = userEvent.setup();
346
+ const detectionHandler = vi.fn();
347
+
348
+ const { eventBus } = renderWithProviders(
349
+ <DetectSection
350
+ annotationType="highlight"
351
+ isDetecting={false}
352
+ detectionProgress={null}
353
+ />,
354
+ { returnEventBus: true }
355
+ );
356
+
357
+ eventBus!.on('detection:start', detectionHandler);
358
+
359
+ const textarea = screen.getByPlaceholderText('Enter custom instructions...');
360
+ await user.type(textarea, 'Find key concepts');
361
+
362
+ const detectButton = screen.getByRole('button', { name: /✨ Detect/ });
363
+ await user.click(detectButton);
364
+
365
+ expect(detectionHandler).toHaveBeenCalledWith({
366
+ motivation: 'highlighting',
367
+ options: {
368
+ instructions: 'Find key concepts',
369
+ density: expect.any(Number),
370
+ },
371
+ });
372
+
373
+ eventBus!.off('detection:start', detectionHandler);
374
+ });
375
+ });
376
+
377
+ describe('Collapsible Behavior', () => {
378
+ it('should be expanded by default', () => {
379
+ renderWithProviders(
380
+ <DetectSection
381
+ annotationType="highlight"
382
+ isDetecting={false}
383
+ detectionProgress={null}
384
+ />
385
+ );
386
+
387
+ const collapseButton = screen.getByRole('button', { name: /Detect Highlights/ });
388
+ expect(collapseButton).toHaveAttribute('aria-expanded', 'true');
389
+ expect(screen.getByPlaceholderText('Enter custom instructions...')).toBeInTheDocument();
390
+ });
391
+
392
+ it('should collapse when title clicked', async () => {
393
+ const user = userEvent.setup();
394
+
395
+ renderWithProviders(
396
+ <DetectSection
397
+ annotationType="highlight"
398
+ isDetecting={false}
399
+ detectionProgress={null}
400
+ />
401
+ );
402
+
403
+ const collapseButton = screen.getByRole('button', { name: /Detect Highlights/ });
404
+ await user.click(collapseButton);
405
+
406
+ expect(collapseButton).toHaveAttribute('aria-expanded', 'false');
407
+ expect(screen.queryByPlaceholderText('Enter custom instructions...')).not.toBeInTheDocument();
408
+ });
409
+
410
+ it('should expand when title clicked again', async () => {
411
+ const user = userEvent.setup();
412
+
413
+ renderWithProviders(
414
+ <DetectSection
415
+ annotationType="highlight"
416
+ isDetecting={false}
417
+ detectionProgress={null}
418
+ />
419
+ );
420
+
421
+ const collapseButton = screen.getByRole('button', { name: /Detect Highlights/ });
422
+ await user.click(collapseButton); // Collapse
423
+ await user.click(collapseButton); // Expand
424
+
425
+ expect(collapseButton).toHaveAttribute('aria-expanded', 'true');
426
+ expect(screen.getByPlaceholderText('Enter custom instructions...')).toBeInTheDocument();
427
+ });
428
+ });
429
+
430
+ describe('Edge Cases', () => {
431
+ it('should handle empty progress message', () => {
432
+ renderWithProviders(
433
+ <DetectSection
434
+ annotationType="highlight"
435
+ isDetecting={true}
436
+ detectionProgress={{
437
+ status: 'analyzing',
438
+ message: '',
439
+ }}
440
+ />
441
+ );
442
+
443
+ // Should render progress section even with empty message
444
+ const progressDiv = document.querySelector('.semiont-detection-progress');
445
+ expect(progressDiv).toBeInTheDocument();
446
+ });
447
+
448
+ it('should handle progress without percentage', () => {
449
+ renderWithProviders(
450
+ <DetectSection
451
+ annotationType="highlight"
452
+ isDetecting={true}
453
+ detectionProgress={{
454
+ status: 'analyzing',
455
+ message: 'Processing...',
456
+ // no percentage
457
+ }}
458
+ />
459
+ );
460
+
461
+ expect(screen.getByText('Processing...')).toBeInTheDocument();
462
+ });
463
+
464
+ it('should handle progress with empty requestParams array', () => {
465
+ renderWithProviders(
466
+ <DetectSection
467
+ annotationType="highlight"
468
+ isDetecting={true}
469
+ detectionProgress={{
470
+ status: 'analyzing',
471
+ message: 'Processing...',
472
+ requestParams: [],
473
+ }}
474
+ />
475
+ );
476
+
477
+ expect(screen.queryByText('Request Parameters:')).not.toBeInTheDocument();
478
+ });
479
+ });
480
+ });