@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,18 +1,73 @@
1
1
  import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
2
  import React from 'react';
3
- import { render, screen, fireEvent } from '@testing-library/react';
3
+ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
4
4
  import userEvent from '@testing-library/user-event';
5
5
  import '@testing-library/jest-dom';
6
6
  import { ReferencesPanel } from '../ReferencesPanel';
7
+ import { EventBusProvider, resetEventBusForTesting, useEventBus } from '../../../../contexts/EventBusContext';
7
8
 
8
- // Mock MakeMeaningEventBusContext
9
- vi.mock('../../../../contexts/MakeMeaningEventBusContext', () => ({
10
- useMakeMeaningEvents: vi.fn(() => ({
11
- emit: vi.fn(),
12
- on: vi.fn(),
13
- off: vi.fn(),
14
- })),
15
- }));
9
+ // Composition-based event tracker
10
+ interface TrackedEvent {
11
+ event: string;
12
+ payload: any;
13
+ }
14
+
15
+ function createEventTracker() {
16
+ const events: TrackedEvent[] = [];
17
+
18
+ function EventTrackingWrapper({ children }: { children: React.ReactNode }) {
19
+ const eventBus = useEventBus();
20
+
21
+ React.useEffect(() => {
22
+ const handlers: Array<() => void> = [];
23
+
24
+ const trackEvent = (eventName: string) => (payload: any) => {
25
+ events.push({ event: eventName, payload });
26
+ };
27
+
28
+ const panelEvents = ['detection:start'];
29
+
30
+ panelEvents.forEach(eventName => {
31
+ const handler = trackEvent(eventName);
32
+ eventBus.on(eventName, handler);
33
+ handlers.push(() => eventBus.off(eventName, handler));
34
+ });
35
+
36
+ return () => {
37
+ handlers.forEach(cleanup => cleanup());
38
+ };
39
+ }, [eventBus]);
40
+
41
+ return <>{children}</>;
42
+ }
43
+
44
+ return {
45
+ EventTrackingWrapper,
46
+ events,
47
+ clear: () => {
48
+ events.length = 0;
49
+ },
50
+ };
51
+ }
52
+
53
+ // Helper to render with EventBusProvider
54
+ const renderWithEventBus = (component: React.ReactElement, tracker?: ReturnType<typeof createEventTracker>) => {
55
+ if (tracker) {
56
+ return render(
57
+ <EventBusProvider>
58
+ <tracker.EventTrackingWrapper>
59
+ {component}
60
+ </tracker.EventTrackingWrapper>
61
+ </EventBusProvider>
62
+ );
63
+ }
64
+
65
+ return render(
66
+ <EventBusProvider>
67
+ {component}
68
+ </EventBusProvider>
69
+ );
70
+ };
16
71
 
17
72
  // Mock TranslationContext
18
73
  vi.mock('../../../../contexts/TranslationContext', () => ({
@@ -38,30 +93,42 @@ vi.mock('../../../../contexts/TranslationContext', () => ({
38
93
  }
39
94
  return result;
40
95
  }),
96
+ TranslationProvider: ({ children }: { children: React.ReactNode }) => children,
41
97
  }));
42
98
 
43
- // Mock DetectionProgressWidget
99
+ // Mock DetectionProgressWidget - simplified to avoid module import issues
44
100
  vi.mock('@/components/DetectionProgressWidget', () => ({
45
- DetectionProgressWidget: ({ progress, onCancel }: any) => (
101
+ DetectionProgressWidget: ({ progress }: any) => (
46
102
  <div data-testid="detection-progress-widget">
47
103
  <div data-testid="progress-data">{JSON.stringify(progress)}</div>
48
- <button onClick={onCancel}>Cancel Detection</button>
104
+ <button title="Cancel Detection">Cancel</button>
49
105
  </div>
50
106
  ),
51
107
  }));
52
108
 
53
109
  describe('ReferencesPanel Component', () => {
110
+ // Mock Link component
111
+ const MockLink = ({ href, children, ...props }: any) => (
112
+ <a href={href} {...props}>{children}</a>
113
+ );
114
+
115
+ // Mock routes
116
+ const mockRoutes = {
117
+ resourceDetail: (id: string) => `/resources/${id}`,
118
+ } as any;
119
+
54
120
  const defaultProps = {
55
121
  allEntityTypes: ['Person', 'Organization', 'Location', 'Date'],
56
122
  isDetecting: false,
57
123
  detectionProgress: null,
58
- onDetect: vi.fn(),
59
- onCancelDetection: vi.fn(),
60
- mediaType: 'text/plain',
61
124
  annotateMode: true,
125
+ Link: MockLink,
126
+ routes: mockRoutes,
127
+ pendingAnnotation: null,
62
128
  };
63
129
 
64
130
  beforeEach(() => {
131
+ resetEventBusForTesting();
65
132
  vi.clearAllMocks();
66
133
  });
67
134
 
@@ -71,13 +138,13 @@ describe('ReferencesPanel Component', () => {
71
138
 
72
139
  describe('Rendering', () => {
73
140
  it('should render panel with title', () => {
74
- render(<ReferencesPanel {...defaultProps} />);
141
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
75
142
 
76
143
  expect(screen.getByText('Detect Entities')).toBeInTheDocument();
77
144
  });
78
145
 
79
146
  it('should render all entity type buttons', () => {
80
- render(<ReferencesPanel {...defaultProps} />);
147
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
81
148
 
82
149
  expect(screen.getByText('Person')).toBeInTheDocument();
83
150
  expect(screen.getByText('Organization')).toBeInTheDocument();
@@ -86,13 +153,13 @@ describe('ReferencesPanel Component', () => {
86
153
  });
87
154
 
88
155
  it('should show message when no entity types available', () => {
89
- render(<ReferencesPanel {...defaultProps} allEntityTypes={[]} />);
156
+ renderWithEventBus(<ReferencesPanel {...defaultProps} allEntityTypes={[]} />);
90
157
 
91
158
  expect(screen.getByText('No entity types available')).toBeInTheDocument();
92
159
  });
93
160
 
94
161
  it('should render start detection button', () => {
95
- render(<ReferencesPanel {...defaultProps} />);
162
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
96
163
 
97
164
  expect(screen.getByTitle('Start Detection')).toBeInTheDocument();
98
165
  });
@@ -100,7 +167,7 @@ describe('ReferencesPanel Component', () => {
100
167
 
101
168
  describe('Entity Type Selection', () => {
102
169
  it('should toggle entity type selection on click', async () => {
103
- render(<ReferencesPanel {...defaultProps} />);
170
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
104
171
 
105
172
  const personButton = screen.getByText('Person');
106
173
 
@@ -119,7 +186,7 @@ describe('ReferencesPanel Component', () => {
119
186
  });
120
187
 
121
188
  it('should allow multiple selections', async () => {
122
- render(<ReferencesPanel {...defaultProps} />);
189
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
123
190
 
124
191
  const personButton = screen.getByText('Person');
125
192
  const orgButton = screen.getByText('Organization');
@@ -135,7 +202,7 @@ describe('ReferencesPanel Component', () => {
135
202
  });
136
203
 
137
204
  it('should deselect when clicking selected type', async () => {
138
- render(<ReferencesPanel {...defaultProps} />);
205
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
139
206
 
140
207
  const personButton = screen.getByText('Person');
141
208
 
@@ -147,7 +214,7 @@ describe('ReferencesPanel Component', () => {
147
214
  });
148
215
 
149
216
  it('should show selected count', async () => {
150
- render(<ReferencesPanel {...defaultProps} />);
217
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
151
218
 
152
219
  const personButton = screen.getByText('Person');
153
220
  const orgButton = screen.getByText('Organization');
@@ -164,7 +231,7 @@ describe('ReferencesPanel Component', () => {
164
231
  });
165
232
 
166
233
  it('should not show selected count when none selected', () => {
167
- render(<ReferencesPanel {...defaultProps} />);
234
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
168
235
 
169
236
  expect(screen.queryByText(/selected/i)).not.toBeInTheDocument();
170
237
  });
@@ -172,7 +239,7 @@ describe('ReferencesPanel Component', () => {
172
239
 
173
240
  describe('Button Styling', () => {
174
241
  it('should style selected buttons differently', async () => {
175
- render(<ReferencesPanel {...defaultProps} />);
242
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
176
243
 
177
244
  const personButton = screen.getByText('Person');
178
245
 
@@ -187,7 +254,7 @@ describe('ReferencesPanel Component', () => {
187
254
  });
188
255
 
189
256
  it('should have proper ARIA attributes', () => {
190
- render(<ReferencesPanel {...defaultProps} />);
257
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
191
258
 
192
259
  const personButton = screen.getByText('Person');
193
260
 
@@ -196,7 +263,7 @@ describe('ReferencesPanel Component', () => {
196
263
  });
197
264
 
198
265
  it('should have focus styles', () => {
199
- render(<ReferencesPanel {...defaultProps} />);
266
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
200
267
 
201
268
  const personButton = screen.getByText('Person');
202
269
 
@@ -206,7 +273,7 @@ describe('ReferencesPanel Component', () => {
206
273
 
207
274
  describe('Start Detection Button', () => {
208
275
  it('should be disabled when no types selected', () => {
209
- render(<ReferencesPanel {...defaultProps} />);
276
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
210
277
 
211
278
  const startButton = screen.getByTitle('Start Detection');
212
279
 
@@ -214,7 +281,7 @@ describe('ReferencesPanel Component', () => {
214
281
  });
215
282
 
216
283
  it('should be enabled when types are selected', async () => {
217
- render(<ReferencesPanel {...defaultProps} />);
284
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
218
285
 
219
286
  const personButton = screen.getByText('Person');
220
287
  await userEvent.click(personButton);
@@ -224,9 +291,9 @@ describe('ReferencesPanel Component', () => {
224
291
  expect(startButton).not.toBeDisabled();
225
292
  });
226
293
 
227
- it('should call onDetect with selected types and includeDescriptiveReferences', async () => {
228
- const onDetect = vi.fn();
229
- render(<ReferencesPanel {...defaultProps} onDetect={onDetect} />);
294
+ it('should emit detection:start event with selected types and includeDescriptiveReferences', async () => {
295
+ const tracker = createEventTracker();
296
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />, tracker);
230
297
 
231
298
  await userEvent.click(screen.getByText('Person'));
232
299
  await userEvent.click(screen.getByText('Organization'));
@@ -234,12 +301,20 @@ describe('ReferencesPanel Component', () => {
234
301
  const startButton = screen.getByTitle('Start Detection');
235
302
  await userEvent.click(startButton);
236
303
 
237
- expect(onDetect).toHaveBeenCalledWith(['Person', 'Organization'], false);
304
+ await waitFor(() => {
305
+ expect(tracker.events.some(e =>
306
+ e.event === 'detection:start' &&
307
+ e.payload?.motivation === 'linking' &&
308
+ e.payload?.options?.entityTypes?.includes('Person') &&
309
+ e.payload?.options?.entityTypes?.includes('Organization') &&
310
+ e.payload?.options?.includeDescriptiveReferences === false
311
+ )).toBe(true);
312
+ });
238
313
  });
239
314
 
240
- it('should call onDetect with includeDescriptiveReferences when checkbox is checked', async () => {
241
- const onDetect = vi.fn();
242
- render(<ReferencesPanel {...defaultProps} onDetect={onDetect} />);
315
+ it('should emit detection:start event with includeDescriptiveReferences when checkbox is checked', async () => {
316
+ const tracker = createEventTracker();
317
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />, tracker);
243
318
 
244
319
  await userEvent.click(screen.getByText('Person'));
245
320
 
@@ -251,11 +326,18 @@ describe('ReferencesPanel Component', () => {
251
326
  const startButton = screen.getByTitle('Start Detection');
252
327
  await userEvent.click(startButton);
253
328
 
254
- expect(onDetect).toHaveBeenCalledWith(['Person'], true);
329
+ await waitFor(() => {
330
+ expect(tracker.events.some(e =>
331
+ e.event === 'detection:start' &&
332
+ e.payload?.motivation === 'linking' &&
333
+ e.payload?.options?.entityTypes?.includes('Person') &&
334
+ e.payload?.options?.includeDescriptiveReferences === true
335
+ )).toBe(true);
336
+ });
255
337
  });
256
338
 
257
339
  it('should clear selected types after detection starts', async () => {
258
- const { rerender } = render(<ReferencesPanel {...defaultProps} />);
340
+ const { rerender } = renderWithEventBus(<ReferencesPanel {...defaultProps} />);
259
341
 
260
342
  await userEvent.click(screen.getByText('Person'));
261
343
 
@@ -264,22 +346,26 @@ describe('ReferencesPanel Component', () => {
264
346
 
265
347
  // Simulate detection starting
266
348
  rerender(
267
- <ReferencesPanel
268
- {...defaultProps}
269
- isDetecting={true}
270
- detectionProgress={{ completedEntityTypes: [] }}
271
- />
349
+ <EventBusProvider>
350
+ <ReferencesPanel
351
+ {...defaultProps}
352
+ isDetecting={true}
353
+ detectionProgress={{ completedEntityTypes: [] }}
354
+ />
355
+ </EventBusProvider>
272
356
  );
273
357
 
274
358
  // Simulate detection completing
275
359
  rerender(
276
- <ReferencesPanel
277
- {...defaultProps}
278
- isDetecting={false}
279
- detectionProgress={{
280
- completedEntityTypes: [{ entityType: 'Person', foundCount: 5 }],
281
- }}
282
- />
360
+ <EventBusProvider>
361
+ <ReferencesPanel
362
+ {...defaultProps}
363
+ isDetecting={false}
364
+ detectionProgress={{
365
+ completedEntityTypes: [{ entityType: 'Person', foundCount: 5 }],
366
+ }}
367
+ />
368
+ </EventBusProvider>
283
369
  );
284
370
 
285
371
  // UI should reset but we can't directly test internal state
@@ -287,7 +373,7 @@ describe('ReferencesPanel Component', () => {
287
373
  });
288
374
 
289
375
  it('should have proper styling when disabled', () => {
290
- render(<ReferencesPanel {...defaultProps} />);
376
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
291
377
 
292
378
  const startButton = screen.getByTitle('Start Detection');
293
379
 
@@ -298,7 +384,7 @@ describe('ReferencesPanel Component', () => {
298
384
  });
299
385
 
300
386
  it('should have proper styling when enabled', async () => {
301
- render(<ReferencesPanel {...defaultProps} />);
387
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
302
388
 
303
389
  await userEvent.click(screen.getByText('Person'));
304
390
 
@@ -313,7 +399,7 @@ describe('ReferencesPanel Component', () => {
313
399
 
314
400
  describe('Detection Progress', () => {
315
401
  it('should show progress widget when detecting', () => {
316
- render(
402
+ renderWithEventBus(
317
403
  <ReferencesPanel
318
404
  {...defaultProps}
319
405
  isDetecting={true}
@@ -332,7 +418,7 @@ describe('ReferencesPanel Component', () => {
332
418
  ],
333
419
  };
334
420
 
335
- render(
421
+ renderWithEventBus(
336
422
  <ReferencesPanel
337
423
  {...defaultProps}
338
424
  isDetecting={true}
@@ -346,7 +432,7 @@ describe('ReferencesPanel Component', () => {
346
432
  });
347
433
 
348
434
  it('should hide entity type selection during detection', () => {
349
- render(
435
+ renderWithEventBus(
350
436
  <ReferencesPanel
351
437
  {...defaultProps}
352
438
  isDetecting={true}
@@ -358,28 +444,23 @@ describe('ReferencesPanel Component', () => {
358
444
  expect(screen.queryByText('Person')).not.toBeInTheDocument();
359
445
  });
360
446
 
361
- it('should allow canceling detection', async () => {
362
- const onCancelDetection = vi.fn();
363
-
364
- render(
447
+ it('should render cancel button when detecting', async () => {
448
+ renderWithEventBus(
365
449
  <ReferencesPanel
366
450
  {...defaultProps}
367
451
  isDetecting={true}
368
452
  detectionProgress={{ completedEntityTypes: [] }}
369
- onCancelDetection={onCancelDetection}
370
453
  />
371
454
  );
372
455
 
373
- const cancelButton = screen.getByText('Cancel Detection');
374
- await userEvent.click(cancelButton);
375
-
376
- expect(onCancelDetection).toHaveBeenCalledOnce();
456
+ const cancelButton = screen.getByTitle('Cancel Detection');
457
+ expect(cancelButton).toBeInTheDocument();
377
458
  });
378
459
  });
379
460
 
380
461
  describe('Detection Complete Log', () => {
381
462
  it('should show completed log after detection finishes', () => {
382
- const { rerender } = render(
463
+ const { rerender } = renderWithEventBus(
383
464
  <ReferencesPanel
384
465
  {...defaultProps}
385
466
  isDetecting={false}
@@ -394,11 +475,13 @@ describe('ReferencesPanel Component', () => {
394
475
 
395
476
  // Parent clears detectionProgress after completion
396
477
  rerender(
397
- <ReferencesPanel
398
- {...defaultProps}
399
- isDetecting={false}
400
- detectionProgress={null}
401
- />
478
+ <EventBusProvider>
479
+ <ReferencesPanel
480
+ {...defaultProps}
481
+ isDetecting={false}
482
+ detectionProgress={null}
483
+ />
484
+ </EventBusProvider>
402
485
  );
403
486
 
404
487
  expect(screen.getByText('Person:')).toBeInTheDocument();
@@ -406,7 +489,7 @@ describe('ReferencesPanel Component', () => {
406
489
  });
407
490
 
408
491
  it('should show found counts in log', () => {
409
- const { rerender } = render(
492
+ const { rerender } = renderWithEventBus(
410
493
  <ReferencesPanel
411
494
  {...defaultProps}
412
495
  isDetecting={false}
@@ -416,12 +499,16 @@ describe('ReferencesPanel Component', () => {
416
499
  />
417
500
  );
418
501
 
419
- rerender(<ReferencesPanel {...defaultProps} isDetecting={false} detectionProgress={null} />);
502
+ rerender(
503
+ <EventBusProvider>
504
+ <ReferencesPanel {...defaultProps} isDetecting={false} detectionProgress={null} />
505
+ </EventBusProvider>
506
+ );
420
507
  expect(screen.getByText(/Found.*5/i)).toBeInTheDocument();
421
508
  });
422
509
 
423
510
  it('should show checkmarks for completed types', () => {
424
- const { rerender } = render(
511
+ const { rerender } = renderWithEventBus(
425
512
  <ReferencesPanel
426
513
  {...defaultProps}
427
514
  isDetecting={false}
@@ -431,12 +518,16 @@ describe('ReferencesPanel Component', () => {
431
518
  />
432
519
  );
433
520
 
434
- rerender(<ReferencesPanel {...defaultProps} isDetecting={false} detectionProgress={null} />);
521
+ rerender(
522
+ <EventBusProvider>
523
+ <ReferencesPanel {...defaultProps} isDetecting={false} detectionProgress={null} />
524
+ </EventBusProvider>
525
+ );
435
526
  expect(screen.getByText('✓')).toBeInTheDocument();
436
527
  });
437
528
 
438
529
  it('should show "Detect More" button after completion', () => {
439
- const { rerender } = render(
530
+ const { rerender } = renderWithEventBus(
440
531
  <ReferencesPanel
441
532
  {...defaultProps}
442
533
  isDetecting={false}
@@ -446,12 +537,16 @@ describe('ReferencesPanel Component', () => {
446
537
  />
447
538
  );
448
539
 
449
- rerender(<ReferencesPanel {...defaultProps} isDetecting={false} detectionProgress={null} />);
540
+ rerender(
541
+ <EventBusProvider>
542
+ <ReferencesPanel {...defaultProps} isDetecting={false} detectionProgress={null} />
543
+ </EventBusProvider>
544
+ );
450
545
  expect(screen.getByText('Detect More')).toBeInTheDocument();
451
546
  });
452
547
 
453
548
  it('should clear log and show selection UI when clicking "Detect More"', async () => {
454
- const { rerender } = render(
549
+ const { rerender } = renderWithEventBus(
455
550
  <ReferencesPanel
456
551
  {...defaultProps}
457
552
  isDetecting={false}
@@ -461,7 +556,11 @@ describe('ReferencesPanel Component', () => {
461
556
  />
462
557
  );
463
558
 
464
- rerender(<ReferencesPanel {...defaultProps} isDetecting={false} detectionProgress={null} />);
559
+ rerender(
560
+ <EventBusProvider>
561
+ <ReferencesPanel {...defaultProps} isDetecting={false} detectionProgress={null} />
562
+ </EventBusProvider>
563
+ );
465
564
 
466
565
  const detectMoreButton = screen.getByText('Detect More');
467
566
  await userEvent.click(detectMoreButton);
@@ -472,7 +571,7 @@ describe('ReferencesPanel Component', () => {
472
571
  });
473
572
 
474
573
  it('should not show log when empty', () => {
475
- render(
574
+ renderWithEventBus(
476
575
  <ReferencesPanel
477
576
  {...defaultProps}
478
577
  isDetecting={false}
@@ -488,18 +587,20 @@ describe('ReferencesPanel Component', () => {
488
587
 
489
588
  describe('State Transitions', () => {
490
589
  it('should transition from idle to detecting', () => {
491
- const { rerender } = render(<ReferencesPanel {...defaultProps} />);
590
+ const { rerender } = renderWithEventBus(<ReferencesPanel {...defaultProps} />);
492
591
 
493
592
  // Idle state
494
593
  expect(screen.getByText('Select entity types')).toBeInTheDocument();
495
594
 
496
595
  // Start detecting
497
596
  rerender(
498
- <ReferencesPanel
499
- {...defaultProps}
500
- isDetecting={true}
501
- detectionProgress={{ completedEntityTypes: [] }}
502
- />
597
+ <EventBusProvider>
598
+ <ReferencesPanel
599
+ {...defaultProps}
600
+ isDetecting={true}
601
+ detectionProgress={{ completedEntityTypes: [] }}
602
+ />
603
+ </EventBusProvider>
503
604
  );
504
605
 
505
606
  // Detecting state
@@ -508,7 +609,7 @@ describe('ReferencesPanel Component', () => {
508
609
  });
509
610
 
510
611
  it('should transition from detecting to complete', () => {
511
- const { rerender } = render(
612
+ const { rerender } = renderWithEventBus(
512
613
  <ReferencesPanel
513
614
  {...defaultProps}
514
615
  isDetecting={true}
@@ -521,24 +622,30 @@ describe('ReferencesPanel Component', () => {
521
622
 
522
623
  // Complete - first trigger useEffect to copy to lastDetectionLog
523
624
  rerender(
524
- <ReferencesPanel
525
- {...defaultProps}
526
- isDetecting={false}
527
- detectionProgress={{
528
- completedEntityTypes: [{ entityType: 'Person', foundCount: 5 }],
529
- }}
530
- />
625
+ <EventBusProvider>
626
+ <ReferencesPanel
627
+ {...defaultProps}
628
+ isDetecting={false}
629
+ detectionProgress={{
630
+ completedEntityTypes: [{ entityType: 'Person', foundCount: 5 }],
631
+ }}
632
+ />
633
+ </EventBusProvider>
531
634
  );
532
635
 
533
636
  // Then clear detectionProgress to show the log
534
- rerender(<ReferencesPanel {...defaultProps} isDetecting={false} detectionProgress={null} />);
637
+ rerender(
638
+ <EventBusProvider>
639
+ <ReferencesPanel {...defaultProps} isDetecting={false} detectionProgress={null} />
640
+ </EventBusProvider>
641
+ );
535
642
 
536
643
  expect(screen.queryByTestId('detection-progress-widget')).not.toBeInTheDocument();
537
644
  expect(screen.getByText('Detect More')).toBeInTheDocument();
538
645
  });
539
646
 
540
647
  it('should transition from complete to idle', async () => {
541
- const { rerender } = render(
648
+ const { rerender } = renderWithEventBus(
542
649
  <ReferencesPanel
543
650
  {...defaultProps}
544
651
  isDetecting={false}
@@ -549,12 +656,20 @@ describe('ReferencesPanel Component', () => {
549
656
  );
550
657
 
551
658
  // Clear detectionProgress to show the log
552
- rerender(<ReferencesPanel {...defaultProps} isDetecting={false} detectionProgress={null} />);
659
+ rerender(
660
+ <EventBusProvider>
661
+ <ReferencesPanel {...defaultProps} isDetecting={false} detectionProgress={null} />
662
+ </EventBusProvider>
663
+ );
553
664
 
554
665
  const detectMoreButton = screen.getByText('Detect More');
555
666
  await userEvent.click(detectMoreButton);
556
667
 
557
- rerender(<ReferencesPanel {...defaultProps} />);
668
+ rerender(
669
+ <EventBusProvider>
670
+ <ReferencesPanel {...defaultProps} />
671
+ </EventBusProvider>
672
+ );
558
673
 
559
674
  expect(screen.getByText('Select entity types')).toBeInTheDocument();
560
675
  });
@@ -563,7 +678,7 @@ describe('ReferencesPanel Component', () => {
563
678
  describe('Edge Cases', () => {
564
679
  it('should handle empty entity types array', () => {
565
680
  expect(() => {
566
- render(<ReferencesPanel {...defaultProps} allEntityTypes={[]} />);
681
+ renderWithEventBus(<ReferencesPanel {...defaultProps} allEntityTypes={[]} />);
567
682
  }).not.toThrow();
568
683
  });
569
684
 
@@ -571,7 +686,7 @@ describe('ReferencesPanel Component', () => {
571
686
  const manyTypes = Array.from({ length: 50 }, (_, i) => `Type${i}`);
572
687
 
573
688
  expect(() => {
574
- render(<ReferencesPanel {...defaultProps} allEntityTypes={manyTypes} />);
689
+ renderWithEventBus(<ReferencesPanel {...defaultProps} allEntityTypes={manyTypes} />);
575
690
  }).not.toThrow();
576
691
 
577
692
  expect(screen.getByText('Type0')).toBeInTheDocument();
@@ -581,7 +696,7 @@ describe('ReferencesPanel Component', () => {
581
696
  it('should handle entity types with special characters', () => {
582
697
  const specialTypes = ['Type-A', 'Type_B', 'Type.C', 'Type/D'];
583
698
 
584
- render(<ReferencesPanel {...defaultProps} allEntityTypes={specialTypes} />);
699
+ renderWithEventBus(<ReferencesPanel {...defaultProps} allEntityTypes={specialTypes} />);
585
700
 
586
701
  specialTypes.forEach(type => {
587
702
  expect(screen.getByText(type)).toBeInTheDocument();
@@ -589,7 +704,7 @@ describe('ReferencesPanel Component', () => {
589
704
  });
590
705
 
591
706
  it('should handle selecting and deselecting all types', async () => {
592
- render(<ReferencesPanel {...defaultProps} />);
707
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
593
708
 
594
709
  // Select all
595
710
  for (const type of defaultProps.allEntityTypes) {
@@ -611,7 +726,7 @@ describe('ReferencesPanel Component', () => {
611
726
  });
612
727
 
613
728
  it('should handle rapid selection changes', async () => {
614
- render(<ReferencesPanel {...defaultProps} />);
729
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
615
730
 
616
731
  const personButton = screen.getByText('Person');
617
732
 
@@ -625,7 +740,7 @@ describe('ReferencesPanel Component', () => {
625
740
  });
626
741
 
627
742
  it('should handle zero found count in results', () => {
628
- render(
743
+ renderWithEventBus(
629
744
  <ReferencesPanel
630
745
  {...defaultProps}
631
746
  isDetecting={false}
@@ -640,7 +755,7 @@ describe('ReferencesPanel Component', () => {
640
755
 
641
756
  it('should handle undefined detectionProgress', () => {
642
757
  expect(() => {
643
- render(
758
+ renderWithEventBus(
644
759
  <ReferencesPanel
645
760
  {...defaultProps}
646
761
  isDetecting={false}
@@ -653,21 +768,21 @@ describe('ReferencesPanel Component', () => {
653
768
 
654
769
  describe('Styling and Appearance', () => {
655
770
  it('should have proper panel structure', () => {
656
- const { container } = render(<ReferencesPanel {...defaultProps} />);
771
+ const { container } = renderWithEventBus(<ReferencesPanel {...defaultProps} />);
657
772
 
658
773
  const panel = container.firstChild as HTMLElement;
659
774
  expect(panel).toHaveClass('semiont-panel');
660
775
  });
661
776
 
662
777
  it('should support dark mode', () => {
663
- const { container } = render(<ReferencesPanel {...defaultProps} />);
778
+ const { container } = renderWithEventBus(<ReferencesPanel {...defaultProps} />);
664
779
 
665
780
  const panel = container.firstChild as HTMLElement;
666
781
  expect(panel).toHaveClass('semiont-panel');
667
782
  });
668
783
 
669
784
  it('should have title without emoji', () => {
670
- render(<ReferencesPanel {...defaultProps} />);
785
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
671
786
 
672
787
  // The emoji is no longer in the title (it's only in the tab now)
673
788
  const title = screen.getByRole('heading', { level: 2 });
@@ -676,7 +791,7 @@ describe('ReferencesPanel Component', () => {
676
791
  });
677
792
 
678
793
  it('should have proper button layout', () => {
679
- render(<ReferencesPanel {...defaultProps} />);
794
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
680
795
 
681
796
  const buttonContainer = screen.getByText('Person').parentElement;
682
797
  expect(buttonContainer).toHaveClass('semiont-detect-widget__chips');
@@ -685,7 +800,7 @@ describe('ReferencesPanel Component', () => {
685
800
 
686
801
  describe('Accessibility', () => {
687
802
  it('should have proper ARIA labels for selection', async () => {
688
- render(<ReferencesPanel {...defaultProps} />);
803
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
689
804
 
690
805
  const personButton = screen.getByText('Person');
691
806
 
@@ -699,7 +814,7 @@ describe('ReferencesPanel Component', () => {
699
814
  });
700
815
 
701
816
  it('should have proper ARIA pressed states', async () => {
702
- render(<ReferencesPanel {...defaultProps} />);
817
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
703
818
 
704
819
  const personButton = screen.getByText('Person');
705
820
 
@@ -711,7 +826,7 @@ describe('ReferencesPanel Component', () => {
711
826
  });
712
827
 
713
828
  it('should be keyboard navigable', () => {
714
- render(<ReferencesPanel {...defaultProps} />);
829
+ renderWithEventBus(<ReferencesPanel {...defaultProps} />);
715
830
 
716
831
  const personButton = screen.getByText('Person');
717
832
  personButton.focus();