@semiont/react-ui 0.2.33-build.79 → 0.2.33-build.81

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 (213) hide show
  1. package/dist/EventBusContext-CJjL_cCf.d.mts +462 -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-EMHEHPCJ.mjs → ar-4ZEORRW2.mjs} +7 -4
  5. package/dist/ar-4ZEORRW2.mjs.map +1 -0
  6. package/dist/{bn-OVCI4F6X.mjs → bn-SEDE5BQJ.mjs} +7 -4
  7. package/dist/bn-SEDE5BQJ.mjs.map +1 -0
  8. package/dist/{chunk-LIHZTECW.mjs → chunk-D7NBW4RV.mjs} +7 -4
  9. package/dist/chunk-D7NBW4RV.mjs.map +1 -0
  10. package/dist/{chunk-JZIO2A3B.mjs → chunk-QB52Q7EQ.mjs} +206 -146
  11. package/dist/chunk-QB52Q7EQ.mjs.map +1 -0
  12. package/dist/{cs-FAN66Q2F.mjs → cs-7W4WF5WD.mjs} +7 -4
  13. package/dist/cs-7W4WF5WD.mjs.map +1 -0
  14. package/dist/{da-YBBIHI2O.mjs → da-75XGBCBK.mjs} +7 -4
  15. package/dist/da-75XGBCBK.mjs.map +1 -0
  16. package/dist/{de-MAYU33LB.mjs → de-ODJVFLHM.mjs} +7 -4
  17. package/dist/de-ODJVFLHM.mjs.map +1 -0
  18. package/dist/{el-MKGSWN4O.mjs → el-C4PM4WB3.mjs} +7 -4
  19. package/dist/el-C4PM4WB3.mjs.map +1 -0
  20. package/dist/{en-DDLIXJCU.mjs → en-KJCJQ4OO.mjs} +2 -2
  21. package/dist/{es-52LHUWJD.mjs → es-WD33R7QL.mjs} +7 -4
  22. package/dist/es-WD33R7QL.mjs.map +1 -0
  23. package/dist/{fa-FJICRANB.mjs → fa-2BP6V56P.mjs} +7 -4
  24. package/dist/fa-2BP6V56P.mjs.map +1 -0
  25. package/dist/{fi-O455XFCR.mjs → fi-USRRW24J.mjs} +7 -4
  26. package/dist/fi-USRRW24J.mjs.map +1 -0
  27. package/dist/{fr-TXIXHOOE.mjs → fr-EC5S6WVF.mjs} +7 -4
  28. package/dist/fr-EC5S6WVF.mjs.map +1 -0
  29. package/dist/{he-JBSOX5IN.mjs → he-7TBVIKAA.mjs} +7 -4
  30. package/dist/he-7TBVIKAA.mjs.map +1 -0
  31. package/dist/{hi-KGHI3XVT.mjs → hi-FO4VIZLA.mjs} +7 -4
  32. package/dist/hi-FO4VIZLA.mjs.map +1 -0
  33. package/dist/{id-5OCPPZLO.mjs → id-7U7GGVWY.mjs} +7 -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 +715 -574
  38. package/dist/index.mjs +3898 -3575
  39. package/dist/index.mjs.map +1 -1
  40. package/dist/{it-PNBBZSM2.mjs → it-Y4OPL6I2.mjs} +7 -4
  41. package/dist/it-Y4OPL6I2.mjs.map +1 -0
  42. package/dist/{ja-LDD7R3TJ.mjs → ja-PK7SQL55.mjs} +7 -4
  43. package/dist/ja-PK7SQL55.mjs.map +1 -0
  44. package/dist/{ko-F47ZDEY3.mjs → ko-L25PXMYD.mjs} +7 -4
  45. package/dist/ko-L25PXMYD.mjs.map +1 -0
  46. package/dist/{ms-Z7LMXJWL.mjs → ms-STH777QM.mjs} +7 -4
  47. package/dist/ms-STH777QM.mjs.map +1 -0
  48. package/dist/{nl-6SJFBPJ3.mjs → nl-Y7LECDDR.mjs} +7 -4
  49. package/dist/nl-Y7LECDDR.mjs.map +1 -0
  50. package/dist/{no-YXPBPSGF.mjs → no-KEKCEWU6.mjs} +7 -4
  51. package/dist/no-KEKCEWU6.mjs.map +1 -0
  52. package/dist/{pl-P4AZ2QME.mjs → pl-7A7OC75O.mjs} +7 -4
  53. package/dist/pl-7A7OC75O.mjs.map +1 -0
  54. package/dist/{pt-LHWUS6U6.mjs → pt-35HTM7RA.mjs} +7 -4
  55. package/dist/pt-35HTM7RA.mjs.map +1 -0
  56. package/dist/{ro-EA5J2ZON.mjs → ro-VAWL5KQA.mjs} +7 -4
  57. package/dist/ro-VAWL5KQA.mjs.map +1 -0
  58. package/dist/{sv-DATBS3UQ.mjs → sv-7ZK5EQEB.mjs} +7 -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-WTFJRWPT.mjs → th-UDWZ4X34.mjs} +7 -4
  64. package/dist/th-UDWZ4X34.mjs.map +1 -0
  65. package/dist/{tr-IKO3RXOX.mjs → tr-4WMPK3UX.mjs} +7 -4
  66. package/dist/tr-4WMPK3UX.mjs.map +1 -0
  67. package/dist/{uk-CF6CTTRK.mjs → uk-SSLASQYJ.mjs} +7 -4
  68. package/dist/uk-SSLASQYJ.mjs.map +1 -0
  69. package/dist/{vi-AJLTXPZQ.mjs → vi-IF42Z5PU.mjs} +7 -4
  70. package/dist/vi-IF42Z5PU.mjs.map +1 -0
  71. package/dist/{zh-U3ORHHYH.mjs → zh-HRQTNTAI.mjs} +7 -4
  72. package/dist/zh-HRQTNTAI.mjs.map +1 -0
  73. package/package.json +3 -1
  74. package/src/components/CodeMirrorRenderer.tsx +66 -93
  75. package/src/components/DetectionProgressWidget.tsx +16 -5
  76. package/src/components/ResizeHandle.tsx +10 -4
  77. package/src/components/SessionExpiryBanner.tsx +2 -3
  78. package/src/components/SessionTimer.tsx +3 -3
  79. package/src/components/Toolbar.tsx +18 -9
  80. package/src/components/__tests__/SessionTimer.test.tsx +33 -33
  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 -134
  100. package/src/components/resource/BrowseView.tsx +86 -166
  101. package/src/components/resource/HistoryEvent.tsx +13 -7
  102. package/src/components/resource/ResourceViewer.tsx +122 -264
  103. package/src/components/resource/__tests__/BrowseView.test.tsx +631 -0
  104. package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +231 -0
  105. package/src/components/resource/panels/AssessmentEntry.tsx +25 -33
  106. package/src/components/resource/panels/AssessmentPanel.tsx +106 -28
  107. package/src/components/resource/panels/CommentEntry.tsx +38 -32
  108. package/src/components/resource/panels/CommentsPanel.tsx +121 -28
  109. package/src/components/resource/panels/DetectSection.css +36 -1
  110. package/src/components/resource/panels/DetectSection.tsx +49 -15
  111. package/src/components/resource/panels/HighlightEntry.tsx +25 -33
  112. package/src/components/resource/panels/HighlightPanel.tsx +100 -25
  113. package/src/components/resource/panels/ReferenceEntry.tsx +61 -75
  114. package/src/components/resource/panels/ReferencesPanel.tsx +134 -42
  115. package/src/components/resource/panels/ResourceInfoPanel.tsx +47 -48
  116. package/src/components/resource/panels/TagEntry.tsx +25 -33
  117. package/src/components/resource/panels/TaggingPanel.tsx +118 -30
  118. package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +30 -92
  119. package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +129 -110
  120. package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +86 -78
  121. package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +144 -149
  122. package/src/components/resource/panels/__tests__/DetectSection.test.tsx +480 -0
  123. package/src/components/resource/panels/__tests__/HighlightPanel.detectionProgress.test.tsx +362 -0
  124. package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +226 -111
  125. package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +117 -61
  126. package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +128 -106
  127. package/src/components/settings/SettingsPanel.tsx +15 -12
  128. package/src/features/admin-devops/__tests__/AdminDevOpsPage.test.tsx +1 -46
  129. package/src/features/admin-devops/components/AdminDevOpsPage.tsx +0 -9
  130. package/src/features/admin-security/__tests__/AdminSecurityPage.test.tsx +0 -3
  131. package/src/features/admin-security/components/AdminSecurityPage.tsx +0 -9
  132. package/src/features/admin-users/__tests__/AdminUsersPage.test.tsx +0 -3
  133. package/src/features/admin-users/components/AdminUsersPage.tsx +0 -9
  134. package/src/features/moderate-entity-tags/__tests__/EntityTagsPage.test.tsx +0 -3
  135. package/src/features/moderate-entity-tags/components/EntityTagsPage.tsx +1 -9
  136. package/src/features/moderate-recent/__tests__/RecentDocumentsPage.test.tsx +0 -32
  137. package/src/features/moderate-recent/components/RecentDocumentsPage.tsx +1 -9
  138. package/src/features/moderate-tag-schemas/__tests__/TagSchemasPage.test.tsx +0 -32
  139. package/src/features/moderate-tag-schemas/components/TagSchemasPage.tsx +1 -9
  140. package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +51 -54
  141. package/src/features/resource-compose/components/ResourceComposePage.tsx +3 -13
  142. package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +39 -45
  143. package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +9 -13
  144. package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +234 -0
  145. package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +234 -0
  146. package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +388 -0
  147. package/src/features/resource-viewer/__tests__/DetectionProgressDismissal.test.tsx +318 -0
  148. package/src/features/resource-viewer/__tests__/GenerationFlowIntegration.test.tsx +503 -0
  149. package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +139 -93
  150. package/src/features/resource-viewer/__tests__/detection-progress-flow.test.tsx +322 -0
  151. package/src/features/resource-viewer/components/ResourceViewerPage.tsx +341 -524
  152. package/translations/ar.json +6 -3
  153. package/translations/bn.json +6 -3
  154. package/translations/cs.json +6 -3
  155. package/translations/da.json +6 -3
  156. package/translations/de.json +6 -3
  157. package/translations/el.json +6 -3
  158. package/translations/en.json +6 -3
  159. package/translations/es.json +6 -3
  160. package/translations/fa.json +6 -3
  161. package/translations/fi.json +6 -3
  162. package/translations/fr.json +6 -3
  163. package/translations/he.json +6 -3
  164. package/translations/hi.json +6 -3
  165. package/translations/id.json +6 -3
  166. package/translations/it.json +6 -3
  167. package/translations/ja.json +6 -3
  168. package/translations/ko.json +6 -3
  169. package/translations/ms.json +6 -3
  170. package/translations/nl.json +6 -3
  171. package/translations/no.json +6 -3
  172. package/translations/pl.json +6 -3
  173. package/translations/pt.json +6 -3
  174. package/translations/ro.json +6 -3
  175. package/translations/sv.json +6 -3
  176. package/translations/th.json +6 -3
  177. package/translations/tr.json +6 -3
  178. package/translations/uk.json +6 -3
  179. package/translations/vi.json +6 -3
  180. package/translations/zh.json +6 -3
  181. package/dist/PdfAnnotationCanvas.client-ADC4FFSE.mjs.map +0 -1
  182. package/dist/TranslationManager-Co_5fSxl.d.mts +0 -118
  183. package/dist/ar-EMHEHPCJ.mjs.map +0 -1
  184. package/dist/bn-OVCI4F6X.mjs.map +0 -1
  185. package/dist/chunk-JZIO2A3B.mjs.map +0 -1
  186. package/dist/chunk-LIHZTECW.mjs.map +0 -1
  187. package/dist/cs-FAN66Q2F.mjs.map +0 -1
  188. package/dist/da-YBBIHI2O.mjs.map +0 -1
  189. package/dist/de-MAYU33LB.mjs.map +0 -1
  190. package/dist/el-MKGSWN4O.mjs.map +0 -1
  191. package/dist/es-52LHUWJD.mjs.map +0 -1
  192. package/dist/fa-FJICRANB.mjs.map +0 -1
  193. package/dist/fi-O455XFCR.mjs.map +0 -1
  194. package/dist/fr-TXIXHOOE.mjs.map +0 -1
  195. package/dist/he-JBSOX5IN.mjs.map +0 -1
  196. package/dist/hi-KGHI3XVT.mjs.map +0 -1
  197. package/dist/id-5OCPPZLO.mjs.map +0 -1
  198. package/dist/it-PNBBZSM2.mjs.map +0 -1
  199. package/dist/ja-LDD7R3TJ.mjs.map +0 -1
  200. package/dist/ko-F47ZDEY3.mjs.map +0 -1
  201. package/dist/ms-Z7LMXJWL.mjs.map +0 -1
  202. package/dist/nl-6SJFBPJ3.mjs.map +0 -1
  203. package/dist/no-YXPBPSGF.mjs.map +0 -1
  204. package/dist/pl-P4AZ2QME.mjs.map +0 -1
  205. package/dist/pt-LHWUS6U6.mjs.map +0 -1
  206. package/dist/ro-EA5J2ZON.mjs.map +0 -1
  207. package/dist/sv-DATBS3UQ.mjs.map +0 -1
  208. package/dist/th-WTFJRWPT.mjs.map +0 -1
  209. package/dist/tr-IKO3RXOX.mjs.map +0 -1
  210. package/dist/uk-CF6CTTRK.mjs.map +0 -1
  211. package/dist/vi-AJLTXPZQ.mjs.map +0 -1
  212. package/dist/zh-U3ORHHYH.mjs.map +0 -1
  213. /package/dist/{en-DDLIXJCU.mjs.map → en-KJCJQ4OO.mjs.map} +0 -0
@@ -1,22 +1,77 @@
1
1
  import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
2
  import type { MockedFunction } from 'vitest';
3
3
  import React from 'react';
4
- import { render, screen, fireEvent } from '@testing-library/react';
4
+ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
5
5
  import userEvent from '@testing-library/user-event';
6
6
  import '@testing-library/jest-dom';
7
7
  import { AssessmentPanel } from '../AssessmentPanel';
8
+ import { EventBusProvider, resetEventBusForTesting, useEventBus } from '../../../../contexts/EventBusContext';
8
9
  import type { components } from '@semiont/api-client';
9
10
 
10
11
  type Annotation = components['schemas']['Annotation'];
11
12
 
12
- // Mock MakeMeaningEventBusContext
13
- vi.mock('../../../../contexts/MakeMeaningEventBusContext', () => ({
14
- useMakeMeaningEvents: vi.fn(() => ({
15
- emit: vi.fn(),
16
- on: vi.fn(),
17
- off: vi.fn(),
18
- })),
19
- }));
13
+ // Composition-based event tracker
14
+ interface TrackedEvent {
15
+ event: string;
16
+ payload: any;
17
+ }
18
+
19
+ function createEventTracker() {
20
+ const events: TrackedEvent[] = [];
21
+
22
+ function EventTrackingWrapper({ children }: { children: React.ReactNode }) {
23
+ const eventBus = useEventBus();
24
+
25
+ React.useEffect(() => {
26
+ const handlers: Array<() => void> = [];
27
+
28
+ const trackEvent = (eventName: string) => (payload: any) => {
29
+ events.push({ event: eventName, payload });
30
+ };
31
+
32
+ const panelEvents = ['annotation:create'];
33
+
34
+ panelEvents.forEach(eventName => {
35
+ const handler = trackEvent(eventName);
36
+ eventBus.on(eventName, handler);
37
+ handlers.push(() => eventBus.off(eventName, handler));
38
+ });
39
+
40
+ return () => {
41
+ handlers.forEach(cleanup => cleanup());
42
+ };
43
+ }, [eventBus]);
44
+
45
+ return <>{children}</>;
46
+ }
47
+
48
+ return {
49
+ EventTrackingWrapper,
50
+ events,
51
+ clear: () => {
52
+ events.length = 0;
53
+ },
54
+ };
55
+ }
56
+
57
+ // Helper to render with EventBusProvider
58
+ const renderWithEventBus = (component: React.ReactElement, tracker?: ReturnType<typeof createEventTracker>) => {
59
+ if (tracker) {
60
+ return render(
61
+ <EventBusProvider>
62
+ <tracker.EventTrackingWrapper>
63
+ {component}
64
+ </tracker.EventTrackingWrapper>
65
+ </EventBusProvider>
66
+ );
67
+ }
68
+
69
+ return render(
70
+ <EventBusProvider>
71
+ {component}
72
+ </EventBusProvider>
73
+ );
74
+ };
20
75
 
21
76
  // Mock TranslationContext
22
77
  vi.mock('../../../../contexts/TranslationContext', () => ({
@@ -31,6 +86,7 @@ vi.mock('../../../../contexts/TranslationContext', () => ({
31
86
  };
32
87
  return translations[key] || key;
33
88
  }),
89
+ TranslationProvider: ({ children }: { children: React.ReactNode }) => children,
34
90
  }));
35
91
 
36
92
  // Mock @semiont/api-client utilities
@@ -45,29 +101,19 @@ vi.mock('@semiont/api-client', async () => {
45
101
 
46
102
  // Mock AssessmentEntry component to simplify testing
47
103
  vi.mock('../AssessmentEntry', () => ({
48
- AssessmentEntry: ({ assessment, onClick, onAssessmentRef, onAssessmentHover }: any) => (
49
- <div
50
- data-testid={`assessment-${assessment.id}`}
51
- onClick={() => onClick()}
52
- >
53
- <button
54
- onMouseEnter={() => onAssessmentHover?.(assessment.id)}
55
- onMouseLeave={() => onAssessmentHover?.(null)}
56
- >
57
- Hover
58
- </button>
104
+ AssessmentEntry: ({ assessment, onAssessmentRef }: any) => (
105
+ <div data-testid={`assessment-${assessment.id}`}>
59
106
  <div>{assessment.id}</div>
60
107
  </div>
61
108
  ),
62
109
  }));
63
110
 
64
- // Mock DetectSection component
111
+ // Mock DetectSection component - it will internally use the mocked useEventBus
112
+ // Just render a simplified version
65
113
  vi.mock('../DetectSection', () => ({
66
- DetectSection: ({ annotationType, isDetecting, onDetect }: any) => (
114
+ DetectSection: ({ annotationType, isDetecting }: any) => (
67
115
  <div data-testid="detect-section">
68
- <button onClick={() => onDetect?.('test instructions')}>
69
- Start Detection
70
- </button>
116
+ <button>Start Detection</button>
71
117
  {isDetecting && <div>Detecting...</div>}
72
118
  </div>
73
119
  ),
@@ -128,13 +174,11 @@ const createPendingAnnotation = (exact: string) => ({
128
174
  describe('AssessmentPanel Component', () => {
129
175
  const defaultProps = {
130
176
  annotations: mockAssessments.empty,
131
- onAnnotationClick: vi.fn(),
132
- onCreate: vi.fn(),
133
- focusedAnnotationId: null,
134
177
  pendingAnnotation: null,
135
178
  };
136
179
 
137
180
  beforeEach(() => {
181
+ resetEventBusForTesting();
138
182
  vi.clearAllMocks();
139
183
 
140
184
  // Mock scrollIntoView for jsdom
@@ -156,20 +200,20 @@ describe('AssessmentPanel Component', () => {
156
200
 
157
201
  describe('Rendering', () => {
158
202
  it('should render panel header with title and count', () => {
159
- render(<AssessmentPanel {...defaultProps} annotations={mockAssessments.multiple} />);
203
+ renderWithEventBus(<AssessmentPanel {...defaultProps} annotations={mockAssessments.multiple} />);
160
204
 
161
205
  expect(screen.getByText(/Assessments/)).toBeInTheDocument();
162
206
  expect(screen.getByText(/\(3\)/)).toBeInTheDocument();
163
207
  });
164
208
 
165
209
  it('should show empty state when no assessments', () => {
166
- render(<AssessmentPanel {...defaultProps} />);
210
+ renderWithEventBus(<AssessmentPanel {...defaultProps} />);
167
211
 
168
212
  expect(screen.getByText(/No assessments yet/)).toBeInTheDocument();
169
213
  });
170
214
 
171
215
  it('should render all assessments', () => {
172
- render(<AssessmentPanel {...defaultProps} annotations={mockAssessments.multiple} />);
216
+ renderWithEventBus(<AssessmentPanel {...defaultProps} annotations={mockAssessments.multiple} />);
173
217
 
174
218
  expect(screen.getByTestId('assessment-1')).toBeInTheDocument();
175
219
  expect(screen.getByTestId('assessment-2')).toBeInTheDocument();
@@ -177,7 +221,7 @@ describe('AssessmentPanel Component', () => {
177
221
  });
178
222
 
179
223
  it('should have proper panel structure', () => {
180
- const { container } = render(<AssessmentPanel {...defaultProps} />);
224
+ const { container } = renderWithEventBus(<AssessmentPanel {...defaultProps} />);
181
225
 
182
226
  const panel = container.firstChild as HTMLElement;
183
227
  expect(panel).toHaveClass('semiont-panel');
@@ -186,7 +230,7 @@ describe('AssessmentPanel Component', () => {
186
230
 
187
231
  describe('Assessment Sorting', () => {
188
232
  it('should sort assessments by position in resource', () => {
189
- render(<AssessmentPanel {...defaultProps} annotations={mockAssessments.multiple} />);
233
+ renderWithEventBus(<AssessmentPanel {...defaultProps} annotations={mockAssessments.multiple} />);
190
234
 
191
235
  const assessments = screen.getAllByTestId(/assessment-/);
192
236
 
@@ -200,14 +244,14 @@ describe('AssessmentPanel Component', () => {
200
244
  mockGetTextPositionSelector.mockReturnValue(null);
201
245
 
202
246
  expect(() => {
203
- render(<AssessmentPanel {...defaultProps} annotations={mockAssessments.multiple} />);
247
+ renderWithEventBus(<AssessmentPanel {...defaultProps} annotations={mockAssessments.multiple} />);
204
248
  }).not.toThrow();
205
249
  });
206
250
  });
207
251
 
208
252
  describe('New Assessment Creation', () => {
209
253
  it('should not show new assessment input by default', () => {
210
- render(<AssessmentPanel {...defaultProps} />);
254
+ renderWithEventBus(<AssessmentPanel {...defaultProps} />);
211
255
 
212
256
  expect(screen.queryByPlaceholderText(/Type your assessment here/)).not.toBeInTheDocument();
213
257
  });
@@ -215,7 +259,7 @@ describe('AssessmentPanel Component', () => {
215
259
  it('should show new assessment input when pendingAnnotation exists', () => {
216
260
  const pendingAnnotation = createPendingAnnotation('Selected text');
217
261
 
218
- render(
262
+ renderWithEventBus(
219
263
  <AssessmentPanel
220
264
  {...defaultProps}
221
265
  pendingAnnotation={pendingAnnotation}
@@ -228,7 +272,7 @@ describe('AssessmentPanel Component', () => {
228
272
  it('should display quoted selected text in new assessment area', () => {
229
273
  const pendingAnnotation = createPendingAnnotation('Selected text for assessment');
230
274
 
231
- render(
275
+ renderWithEventBus(
232
276
  <AssessmentPanel
233
277
  {...defaultProps}
234
278
  pendingAnnotation={pendingAnnotation}
@@ -242,7 +286,7 @@ describe('AssessmentPanel Component', () => {
242
286
  const longText = 'A'.repeat(150);
243
287
  const pendingAnnotation = createPendingAnnotation(longText);
244
288
 
245
- render(
289
+ renderWithEventBus(
246
290
  <AssessmentPanel
247
291
  {...defaultProps}
248
292
  pendingAnnotation={pendingAnnotation}
@@ -256,7 +300,7 @@ describe('AssessmentPanel Component', () => {
256
300
  it('should allow typing in new assessment textarea', async () => {
257
301
  const pendingAnnotation = createPendingAnnotation('Selected text');
258
302
 
259
- render(
303
+ renderWithEventBus(
260
304
  <AssessmentPanel
261
305
  {...defaultProps}
262
306
  pendingAnnotation={pendingAnnotation}
@@ -272,7 +316,7 @@ describe('AssessmentPanel Component', () => {
272
316
  it('should show character count', async () => {
273
317
  const pendingAnnotation = createPendingAnnotation('Selected text');
274
318
 
275
- render(
319
+ renderWithEventBus(
276
320
  <AssessmentPanel
277
321
  {...defaultProps}
278
322
  pendingAnnotation={pendingAnnotation}
@@ -290,7 +334,7 @@ describe('AssessmentPanel Component', () => {
290
334
  it('should enforce maxLength of 2000 characters', () => {
291
335
  const pendingAnnotation = createPendingAnnotation('Selected text');
292
336
 
293
- render(
337
+ renderWithEventBus(
294
338
  <AssessmentPanel
295
339
  {...defaultProps}
296
340
  pendingAnnotation={pendingAnnotation}
@@ -304,7 +348,7 @@ describe('AssessmentPanel Component', () => {
304
348
  it('should auto-focus new assessment textarea', () => {
305
349
  const pendingAnnotation = createPendingAnnotation('Selected text');
306
350
 
307
- render(
351
+ renderWithEventBus(
308
352
  <AssessmentPanel
309
353
  {...defaultProps}
310
354
  pendingAnnotation={pendingAnnotation}
@@ -315,16 +359,16 @@ describe('AssessmentPanel Component', () => {
315
359
  expect(textarea).toHaveFocus();
316
360
  });
317
361
 
318
- it('should call onCreate when save is clicked', async () => {
319
- const onCreate = vi.fn();
362
+ it('should emit annotation:create event when save is clicked', async () => {
363
+ const tracker = createEventTracker();
320
364
  const pendingAnnotation = createPendingAnnotation('Selected text');
321
365
 
322
- render(
366
+ renderWithEventBus(
323
367
  <AssessmentPanel
324
368
  {...defaultProps}
325
369
  pendingAnnotation={pendingAnnotation}
326
- onCreate={onCreate}
327
- />
370
+ />,
371
+ tracker
328
372
  );
329
373
 
330
374
  const textarea = screen.getByPlaceholderText(/Type your assessment here/);
@@ -333,13 +377,19 @@ describe('AssessmentPanel Component', () => {
333
377
  const saveButton = screen.getByText('Save');
334
378
  await userEvent.click(saveButton);
335
379
 
336
- expect(onCreate).toHaveBeenCalledWith('My assessment');
380
+ await waitFor(() => {
381
+ expect(tracker.events.some(e =>
382
+ e.event === 'annotation:create' &&
383
+ e.payload?.motivation === 'assessing' &&
384
+ e.payload?.body?.[0]?.value === 'My assessment'
385
+ )).toBe(true);
386
+ });
337
387
  });
338
388
 
339
389
  it('should clear textarea after successful save', async () => {
340
390
  const pendingAnnotation = createPendingAnnotation('Selected text');
341
391
 
342
- render(
392
+ renderWithEventBus(
343
393
  <AssessmentPanel
344
394
  {...defaultProps}
345
395
  pendingAnnotation={pendingAnnotation}
@@ -353,29 +403,35 @@ describe('AssessmentPanel Component', () => {
353
403
  expect(textarea).toHaveValue('');
354
404
  });
355
405
 
356
- it('should allow saving with empty text (assessment text is optional)', async () => {
357
- const onCreate = vi.fn();
406
+ it('should emit event when saving with empty text (text is optional for assessments)', async () => {
407
+ const tracker = createEventTracker();
358
408
  const pendingAnnotation = createPendingAnnotation('Selected text');
359
409
 
360
- render(
410
+ renderWithEventBus(
361
411
  <AssessmentPanel
362
412
  {...defaultProps}
363
413
  pendingAnnotation={pendingAnnotation}
364
- onCreate={onCreate}
365
- />
414
+ />,
415
+ tracker
366
416
  );
367
417
 
368
418
  const saveButton = screen.getByText('Save');
369
419
  await userEvent.click(saveButton);
370
420
 
371
- // Should NOT call onCreate with empty text (handleSaveNewAssessment checks trim())
372
- expect(onCreate).not.toHaveBeenCalled();
421
+ await waitFor(() => {
422
+ expect(tracker.events.some(e =>
423
+ e.event === 'annotation:create' &&
424
+ e.payload?.motivation === 'assessing' &&
425
+ Array.isArray(e.payload?.body) &&
426
+ e.payload.body.length === 0
427
+ )).toBe(true);
428
+ });
373
429
  });
374
430
 
375
431
  it('should have proper styling for new assessment area', () => {
376
432
  const pendingAnnotation = createPendingAnnotation('Selected text');
377
433
 
378
- const { container } = render(
434
+ const { container } = renderWithEventBus(
379
435
  <AssessmentPanel
380
436
  {...defaultProps}
381
437
  pendingAnnotation={pendingAnnotation}
@@ -389,43 +445,23 @@ describe('AssessmentPanel Component', () => {
389
445
  });
390
446
 
391
447
  describe('Assessment Interactions', () => {
392
- it('should call onAnnotationClick when assessment is clicked', () => {
393
- const onAnnotationClick = vi.fn();
394
- render(
448
+ it('should render assessment entries', () => {
449
+ renderWithEventBus(
395
450
  <AssessmentPanel
396
451
  {...defaultProps}
397
452
  annotations={mockAssessments.single}
398
- onAnnotationClick={onAnnotationClick}
399
453
  />
400
454
  );
401
455
 
402
456
  const assessment = screen.getByTestId('assessment-1');
403
- fireEvent.click(assessment);
404
-
405
- expect(onAnnotationClick).toHaveBeenCalledWith(mockAssessments.single[0]);
457
+ expect(assessment).toBeInTheDocument();
406
458
  });
407
459
  });
408
460
 
409
461
  describe('Assessment Hover Behavior', () => {
410
- it('should call onAnnotationHover when provided', () => {
411
- const onAnnotationHover = vi.fn();
412
- render(
413
- <AssessmentPanel
414
- {...defaultProps}
415
- annotations={mockAssessments.single}
416
- onAnnotationHover={onAnnotationHover}
417
- />
418
- );
419
-
420
- const hoverButton = screen.getByText('Hover');
421
- fireEvent.mouseEnter(hoverButton);
422
-
423
- expect(onAnnotationHover).toHaveBeenCalledWith('1');
424
- });
425
-
426
- it('should not error when onAnnotationHover is not provided', () => {
462
+ it('should render without errors', () => {
427
463
  expect(() => {
428
- render(
464
+ renderWithEventBus(
429
465
  <AssessmentPanel
430
466
  {...defaultProps}
431
467
  annotations={mockAssessments.single}
@@ -436,11 +472,10 @@ describe('AssessmentPanel Component', () => {
436
472
  });
437
473
 
438
474
  describe('Detection Section', () => {
439
- it('should render DetectSection when onDetect is provided and annotateMode is true', () => {
440
- render(
475
+ it('should render DetectSection when annotateMode is true', () => {
476
+ renderWithEventBus(
441
477
  <AssessmentPanel
442
478
  {...defaultProps}
443
- onDetect={vi.fn()}
444
479
  annotateMode={true}
445
480
  />
446
481
  );
@@ -448,22 +483,10 @@ describe('AssessmentPanel Component', () => {
448
483
  expect(screen.getByTestId('detect-section')).toBeInTheDocument();
449
484
  });
450
485
 
451
- it('should not render DetectSection when onDetect is not provided', () => {
452
- render(
453
- <AssessmentPanel
454
- {...defaultProps}
455
- annotateMode={true}
456
- />
457
- );
458
-
459
- expect(screen.queryByTestId('detect-section')).not.toBeInTheDocument();
460
- });
461
-
462
486
  it('should not render DetectSection when annotateMode is false', () => {
463
- render(
487
+ renderWithEventBus(
464
488
  <AssessmentPanel
465
489
  {...defaultProps}
466
- onDetect={vi.fn()}
467
490
  annotateMode={false}
468
491
  />
469
492
  );
@@ -471,20 +494,16 @@ describe('AssessmentPanel Component', () => {
471
494
  expect(screen.queryByTestId('detect-section')).not.toBeInTheDocument();
472
495
  });
473
496
 
474
- it('should call onDetect when detection is started', async () => {
475
- const onDetect = vi.fn();
476
- render(
497
+ it('should render DetectSection with correct annotationType', () => {
498
+ renderWithEventBus(
477
499
  <AssessmentPanel
478
500
  {...defaultProps}
479
- onDetect={onDetect}
480
501
  annotateMode={true}
481
502
  />
482
503
  );
483
504
 
484
- const detectButton = screen.getByText('Start Detection');
485
- await userEvent.click(detectButton);
486
-
487
- expect(onDetect).toHaveBeenCalledWith('test instructions');
505
+ // DetectSection is rendered (mocked component renders the button)
506
+ expect(screen.getByText('Start Detection')).toBeInTheDocument();
488
507
  });
489
508
  });
490
509
 
@@ -492,7 +511,7 @@ describe('AssessmentPanel Component', () => {
492
511
  it('should show Cancel button when pendingAnnotation exists', () => {
493
512
  const pendingAnnotation = createPendingAnnotation('Selected text');
494
513
 
495
- render(
514
+ renderWithEventBus(
496
515
  <AssessmentPanel
497
516
  {...defaultProps}
498
517
  pendingAnnotation={pendingAnnotation}
@@ -505,7 +524,7 @@ describe('AssessmentPanel Component', () => {
505
524
  it('should clear textarea when Cancel button is clicked', async () => {
506
525
  const pendingAnnotation = createPendingAnnotation('Selected text');
507
526
 
508
- render(
527
+ renderWithEventBus(
509
528
  <AssessmentPanel
510
529
  {...defaultProps}
511
530
  pendingAnnotation={pendingAnnotation}
@@ -524,7 +543,7 @@ describe('AssessmentPanel Component', () => {
524
543
 
525
544
  describe('Accessibility', () => {
526
545
  it('should have proper heading structure', () => {
527
- render(<AssessmentPanel {...defaultProps} />);
546
+ renderWithEventBus(<AssessmentPanel {...defaultProps} />);
528
547
 
529
548
  const heading = screen.getByText(/Assessments/);
530
549
  expect(heading).toHaveClass('semiont-panel-header__text');
@@ -533,7 +552,7 @@ describe('AssessmentPanel Component', () => {
533
552
  it('should have proper textarea attributes for new assessments', () => {
534
553
  const pendingAnnotation = createPendingAnnotation('Selected text');
535
554
 
536
- render(
555
+ renderWithEventBus(
537
556
  <AssessmentPanel
538
557
  {...defaultProps}
539
558
  pendingAnnotation={pendingAnnotation}