@semiont/react-ui 0.2.33-build.79 → 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.
- package/dist/EventBusContext-7GvDyO0d.d.mts +414 -0
- package/dist/{PdfAnnotationCanvas.client-ADC4FFSE.mjs → PdfAnnotationCanvas.client-RAJRPQLU.mjs} +42 -27
- package/dist/PdfAnnotationCanvas.client-RAJRPQLU.mjs.map +1 -0
- package/dist/{ar-EMHEHPCJ.mjs → ar-4ZEORRW2.mjs} +7 -4
- package/dist/ar-4ZEORRW2.mjs.map +1 -0
- package/dist/{bn-OVCI4F6X.mjs → bn-SEDE5BQJ.mjs} +7 -4
- package/dist/bn-SEDE5BQJ.mjs.map +1 -0
- package/dist/{chunk-LIHZTECW.mjs → chunk-D7NBW4RV.mjs} +7 -4
- package/dist/chunk-D7NBW4RV.mjs.map +1 -0
- package/dist/{chunk-JZIO2A3B.mjs → chunk-ZR4ZV2LY.mjs} +206 -146
- package/dist/chunk-ZR4ZV2LY.mjs.map +1 -0
- package/dist/{cs-FAN66Q2F.mjs → cs-7W4WF5WD.mjs} +7 -4
- package/dist/cs-7W4WF5WD.mjs.map +1 -0
- package/dist/{da-YBBIHI2O.mjs → da-75XGBCBK.mjs} +7 -4
- package/dist/da-75XGBCBK.mjs.map +1 -0
- package/dist/{de-MAYU33LB.mjs → de-ODJVFLHM.mjs} +7 -4
- package/dist/de-ODJVFLHM.mjs.map +1 -0
- package/dist/{el-MKGSWN4O.mjs → el-C4PM4WB3.mjs} +7 -4
- package/dist/el-C4PM4WB3.mjs.map +1 -0
- package/dist/{en-DDLIXJCU.mjs → en-KJCJQ4OO.mjs} +2 -2
- package/dist/{es-52LHUWJD.mjs → es-WD33R7QL.mjs} +7 -4
- package/dist/es-WD33R7QL.mjs.map +1 -0
- package/dist/{fa-FJICRANB.mjs → fa-2BP6V56P.mjs} +7 -4
- package/dist/fa-2BP6V56P.mjs.map +1 -0
- package/dist/{fi-O455XFCR.mjs → fi-USRRW24J.mjs} +7 -4
- package/dist/fi-USRRW24J.mjs.map +1 -0
- package/dist/{fr-TXIXHOOE.mjs → fr-EC5S6WVF.mjs} +7 -4
- package/dist/fr-EC5S6WVF.mjs.map +1 -0
- package/dist/{he-JBSOX5IN.mjs → he-7TBVIKAA.mjs} +7 -4
- package/dist/he-7TBVIKAA.mjs.map +1 -0
- package/dist/{hi-KGHI3XVT.mjs → hi-FO4VIZLA.mjs} +7 -4
- package/dist/hi-FO4VIZLA.mjs.map +1 -0
- package/dist/{id-5OCPPZLO.mjs → id-7U7GGVWY.mjs} +7 -4
- package/dist/id-7U7GGVWY.mjs.map +1 -0
- package/dist/index.css +123 -85
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +645 -471
- package/dist/index.mjs +3461 -3025
- package/dist/index.mjs.map +1 -1
- package/dist/{it-PNBBZSM2.mjs → it-Y4OPL6I2.mjs} +7 -4
- package/dist/it-Y4OPL6I2.mjs.map +1 -0
- package/dist/{ja-LDD7R3TJ.mjs → ja-PK7SQL55.mjs} +7 -4
- package/dist/ja-PK7SQL55.mjs.map +1 -0
- package/dist/{ko-F47ZDEY3.mjs → ko-L25PXMYD.mjs} +7 -4
- package/dist/ko-L25PXMYD.mjs.map +1 -0
- package/dist/{ms-Z7LMXJWL.mjs → ms-STH777QM.mjs} +7 -4
- package/dist/ms-STH777QM.mjs.map +1 -0
- package/dist/{nl-6SJFBPJ3.mjs → nl-Y7LECDDR.mjs} +7 -4
- package/dist/nl-Y7LECDDR.mjs.map +1 -0
- package/dist/{no-YXPBPSGF.mjs → no-KEKCEWU6.mjs} +7 -4
- package/dist/no-KEKCEWU6.mjs.map +1 -0
- package/dist/{pl-P4AZ2QME.mjs → pl-7A7OC75O.mjs} +7 -4
- package/dist/pl-7A7OC75O.mjs.map +1 -0
- package/dist/{pt-LHWUS6U6.mjs → pt-35HTM7RA.mjs} +7 -4
- package/dist/pt-35HTM7RA.mjs.map +1 -0
- package/dist/{ro-EA5J2ZON.mjs → ro-VAWL5KQA.mjs} +7 -4
- package/dist/ro-VAWL5KQA.mjs.map +1 -0
- package/dist/{sv-DATBS3UQ.mjs → sv-7ZK5EQEB.mjs} +7 -4
- package/dist/sv-7ZK5EQEB.mjs.map +1 -0
- package/dist/test-utils.d.mts +18 -8
- package/dist/test-utils.mjs +36 -14
- package/dist/test-utils.mjs.map +1 -1
- package/dist/{th-WTFJRWPT.mjs → th-UDWZ4X34.mjs} +7 -4
- package/dist/th-UDWZ4X34.mjs.map +1 -0
- package/dist/{tr-IKO3RXOX.mjs → tr-4WMPK3UX.mjs} +7 -4
- package/dist/tr-4WMPK3UX.mjs.map +1 -0
- package/dist/{uk-CF6CTTRK.mjs → uk-SSLASQYJ.mjs} +7 -4
- package/dist/uk-SSLASQYJ.mjs.map +1 -0
- package/dist/{vi-AJLTXPZQ.mjs → vi-IF42Z5PU.mjs} +7 -4
- package/dist/vi-IF42Z5PU.mjs.map +1 -0
- package/dist/{zh-U3ORHHYH.mjs → zh-HRQTNTAI.mjs} +7 -4
- package/dist/zh-HRQTNTAI.mjs.map +1 -0
- package/package.json +3 -1
- package/src/components/CodeMirrorRenderer.tsx +66 -93
- package/src/components/DetectionProgressWidget.tsx +16 -5
- package/src/components/LiveRegion.tsx +18 -18
- package/src/components/ResizeHandle.tsx +10 -4
- package/src/components/SessionTimer.tsx +2 -2
- package/src/components/Toolbar.tsx +18 -9
- package/src/components/__tests__/SessionTimer.test.tsx +9 -9
- package/src/components/annotation/AnnotateToolbar.tsx +17 -15
- package/src/components/annotation/__tests__/AnnotateToolbar.test.tsx +165 -63
- package/src/components/annotation/annotation-entries.css +10 -0
- package/src/components/annotation-popups/JsonLdView.tsx +8 -2
- package/src/components/image-annotation/AnnotationOverlay.tsx +42 -22
- package/src/components/image-annotation/SvgDrawingCanvas.tsx +27 -30
- package/src/components/layout/__tests__/LeftSidebar.test.tsx +12 -33
- package/src/components/layout/__tests__/PageLayout.test.tsx +37 -32
- package/src/components/layout/__tests__/UnifiedHeader.test.tsx +21 -40
- package/src/components/modals/ResourceSearchModal.tsx +2 -2
- package/src/components/modals/SearchModal.tsx +1 -1
- package/src/components/navigation/CollapsibleResourceNavigation.tsx +14 -9
- package/src/components/navigation/NavigationTabs.css +36 -24
- package/src/components/navigation/ObservableLink.tsx +91 -0
- package/src/components/navigation/SimpleNavigation.tsx +20 -16
- package/src/components/navigation/SortableResourceTab.tsx +11 -5
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +51 -26
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +28 -22
- package/src/components/resource/AnnotateView.tsx +64 -134
- package/src/components/resource/BrowseView.tsx +86 -166
- package/src/components/resource/HistoryEvent.tsx +13 -7
- package/src/components/resource/ResourceViewer.tsx +122 -264
- package/src/components/resource/__tests__/BrowseView.test.tsx +631 -0
- package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +231 -0
- package/src/components/resource/panels/AssessmentEntry.tsx +25 -33
- package/src/components/resource/panels/AssessmentPanel.tsx +106 -28
- package/src/components/resource/panels/CommentEntry.tsx +38 -32
- package/src/components/resource/panels/CommentsPanel.tsx +121 -28
- package/src/components/resource/panels/DetectSection.css +36 -1
- package/src/components/resource/panels/DetectSection.tsx +38 -10
- package/src/components/resource/panels/HighlightEntry.tsx +25 -33
- package/src/components/resource/panels/HighlightPanel.tsx +100 -25
- package/src/components/resource/panels/ReferenceEntry.tsx +61 -75
- package/src/components/resource/panels/ReferencesPanel.tsx +134 -42
- package/src/components/resource/panels/ResourceInfoPanel.tsx +47 -48
- package/src/components/resource/panels/TagEntry.tsx +25 -33
- package/src/components/resource/panels/TaggingPanel.tsx +119 -30
- package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +30 -92
- package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +129 -110
- package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +86 -78
- package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +144 -149
- package/src/components/resource/panels/__tests__/DetectSection.test.tsx +480 -0
- package/src/components/resource/panels/__tests__/HighlightPanel.detectionProgress.test.tsx +362 -0
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +226 -111
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +117 -61
- package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +128 -106
- package/src/components/settings/SettingsPanel.tsx +15 -12
- package/src/features/admin-devops/__tests__/AdminDevOpsPage.test.tsx +1 -46
- package/src/features/admin-devops/components/AdminDevOpsPage.tsx +0 -9
- package/src/features/admin-security/__tests__/AdminSecurityPage.test.tsx +0 -3
- package/src/features/admin-security/components/AdminSecurityPage.tsx +0 -9
- package/src/features/admin-users/__tests__/AdminUsersPage.test.tsx +0 -3
- package/src/features/admin-users/components/AdminUsersPage.tsx +0 -9
- package/src/features/moderate-entity-tags/__tests__/EntityTagsPage.test.tsx +0 -3
- package/src/features/moderate-entity-tags/components/EntityTagsPage.tsx +1 -9
- package/src/features/moderate-recent/__tests__/RecentDocumentsPage.test.tsx +0 -32
- package/src/features/moderate-recent/components/RecentDocumentsPage.tsx +1 -9
- package/src/features/moderate-tag-schemas/__tests__/TagSchemasPage.test.tsx +0 -32
- package/src/features/moderate-tag-schemas/components/TagSchemasPage.tsx +1 -9
- package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +51 -54
- package/src/features/resource-compose/components/ResourceComposePage.tsx +3 -13
- package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +39 -45
- package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +9 -13
- package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +231 -0
- package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +234 -0
- package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +388 -0
- package/src/features/resource-viewer/__tests__/DetectionProgressDismissal.test.tsx +318 -0
- package/src/features/resource-viewer/__tests__/GenerationFlowIntegration.test.tsx +504 -0
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +135 -88
- package/src/features/resource-viewer/__tests__/detection-progress-flow.test.tsx +322 -0
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +308 -528
- package/translations/ar.json +6 -3
- package/translations/bn.json +6 -3
- package/translations/cs.json +6 -3
- package/translations/da.json +6 -3
- package/translations/de.json +6 -3
- package/translations/el.json +6 -3
- package/translations/en.json +6 -3
- package/translations/es.json +6 -3
- package/translations/fa.json +6 -3
- package/translations/fi.json +6 -3
- package/translations/fr.json +6 -3
- package/translations/he.json +6 -3
- package/translations/hi.json +6 -3
- package/translations/id.json +6 -3
- package/translations/it.json +6 -3
- package/translations/ja.json +6 -3
- package/translations/ko.json +6 -3
- package/translations/ms.json +6 -3
- package/translations/nl.json +6 -3
- package/translations/no.json +6 -3
- package/translations/pl.json +6 -3
- package/translations/pt.json +6 -3
- package/translations/ro.json +6 -3
- package/translations/sv.json +6 -3
- package/translations/th.json +6 -3
- package/translations/tr.json +6 -3
- package/translations/uk.json +6 -3
- package/translations/vi.json +6 -3
- package/translations/zh.json +6 -3
- package/dist/PdfAnnotationCanvas.client-ADC4FFSE.mjs.map +0 -1
- package/dist/TranslationManager-Co_5fSxl.d.mts +0 -118
- package/dist/ar-EMHEHPCJ.mjs.map +0 -1
- package/dist/bn-OVCI4F6X.mjs.map +0 -1
- package/dist/chunk-JZIO2A3B.mjs.map +0 -1
- package/dist/chunk-LIHZTECW.mjs.map +0 -1
- package/dist/cs-FAN66Q2F.mjs.map +0 -1
- package/dist/da-YBBIHI2O.mjs.map +0 -1
- package/dist/de-MAYU33LB.mjs.map +0 -1
- package/dist/el-MKGSWN4O.mjs.map +0 -1
- package/dist/es-52LHUWJD.mjs.map +0 -1
- package/dist/fa-FJICRANB.mjs.map +0 -1
- package/dist/fi-O455XFCR.mjs.map +0 -1
- package/dist/fr-TXIXHOOE.mjs.map +0 -1
- package/dist/he-JBSOX5IN.mjs.map +0 -1
- package/dist/hi-KGHI3XVT.mjs.map +0 -1
- package/dist/id-5OCPPZLO.mjs.map +0 -1
- package/dist/it-PNBBZSM2.mjs.map +0 -1
- package/dist/ja-LDD7R3TJ.mjs.map +0 -1
- package/dist/ko-F47ZDEY3.mjs.map +0 -1
- package/dist/ms-Z7LMXJWL.mjs.map +0 -1
- package/dist/nl-6SJFBPJ3.mjs.map +0 -1
- package/dist/no-YXPBPSGF.mjs.map +0 -1
- package/dist/pl-P4AZ2QME.mjs.map +0 -1
- package/dist/pt-LHWUS6U6.mjs.map +0 -1
- package/dist/ro-EA5J2ZON.mjs.map +0 -1
- package/dist/sv-DATBS3UQ.mjs.map +0 -1
- package/dist/th-WTFJRWPT.mjs.map +0 -1
- package/dist/tr-IKO3RXOX.mjs.map +0 -1
- package/dist/uk-CF6CTTRK.mjs.map +0 -1
- package/dist/vi-AJLTXPZQ.mjs.map +0 -1
- package/dist/zh-U3ORHHYH.mjs.map +0 -1
- /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 { CommentsPanel } from '../CommentsPanel';
|
|
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
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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', () => ({
|
|
@@ -30,6 +85,7 @@ vi.mock('../../../../contexts/TranslationContext', () => ({
|
|
|
30
85
|
};
|
|
31
86
|
return translations[key] || key;
|
|
32
87
|
}),
|
|
88
|
+
TranslationProvider: ({ children }: { children: React.ReactNode }) => children,
|
|
33
89
|
}));
|
|
34
90
|
|
|
35
91
|
// Mock @semiont/api-client utilities
|
|
@@ -118,13 +174,11 @@ const createPendingAnnotation = (exact: string) => ({
|
|
|
118
174
|
describe('CommentsPanel Component', () => {
|
|
119
175
|
const defaultProps = {
|
|
120
176
|
annotations: mockComments.empty,
|
|
121
|
-
onAnnotationClick: vi.fn(),
|
|
122
|
-
onCreate: vi.fn(),
|
|
123
|
-
focusedAnnotationId: null,
|
|
124
177
|
pendingAnnotation: null,
|
|
125
178
|
};
|
|
126
179
|
|
|
127
180
|
beforeEach(() => {
|
|
181
|
+
resetEventBusForTesting();
|
|
128
182
|
vi.clearAllMocks();
|
|
129
183
|
|
|
130
184
|
// Mock scrollIntoView for jsdom
|
|
@@ -146,20 +200,21 @@ describe('CommentsPanel Component', () => {
|
|
|
146
200
|
|
|
147
201
|
describe('Rendering', () => {
|
|
148
202
|
it('should render panel header with title and count', () => {
|
|
149
|
-
|
|
203
|
+
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />);
|
|
150
204
|
|
|
151
|
-
|
|
205
|
+
const headings = screen.getAllByText(/Comments/);
|
|
206
|
+
expect(headings.length).toBeGreaterThan(0);
|
|
152
207
|
expect(screen.getByText(/\(3\)/)).toBeInTheDocument();
|
|
153
208
|
});
|
|
154
209
|
|
|
155
210
|
it('should show empty state when no comments', () => {
|
|
156
|
-
|
|
211
|
+
renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
157
212
|
|
|
158
213
|
expect(screen.getByText(/No comments yet/)).toBeInTheDocument();
|
|
159
214
|
});
|
|
160
215
|
|
|
161
216
|
it('should render all comments', () => {
|
|
162
|
-
|
|
217
|
+
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />);
|
|
163
218
|
|
|
164
219
|
expect(screen.getByTestId('comment-1')).toBeInTheDocument();
|
|
165
220
|
expect(screen.getByTestId('comment-2')).toBeInTheDocument();
|
|
@@ -167,7 +222,7 @@ describe('CommentsPanel Component', () => {
|
|
|
167
222
|
});
|
|
168
223
|
|
|
169
224
|
it('should have proper panel structure', () => {
|
|
170
|
-
const { container } =
|
|
225
|
+
const { container } = renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
171
226
|
|
|
172
227
|
// Find the root panel div (first child of the container)
|
|
173
228
|
const panel = container.firstChild as HTMLElement;
|
|
@@ -175,7 +230,7 @@ describe('CommentsPanel Component', () => {
|
|
|
175
230
|
});
|
|
176
231
|
|
|
177
232
|
it('should have scrollable comments list', () => {
|
|
178
|
-
const { container } =
|
|
233
|
+
const { container } = renderWithEventBus(
|
|
179
234
|
<CommentsPanel {...defaultProps} annotations={mockComments.many} />
|
|
180
235
|
);
|
|
181
236
|
|
|
@@ -186,7 +241,7 @@ describe('CommentsPanel Component', () => {
|
|
|
186
241
|
|
|
187
242
|
describe('Comment Sorting', () => {
|
|
188
243
|
it('should sort comments by position in resource', () => {
|
|
189
|
-
|
|
244
|
+
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />);
|
|
190
245
|
|
|
191
246
|
const comments = screen.getAllByTestId(/comment-/);
|
|
192
247
|
|
|
@@ -200,12 +255,12 @@ describe('CommentsPanel Component', () => {
|
|
|
200
255
|
mockGetTextPositionSelector.mockReturnValue(null);
|
|
201
256
|
|
|
202
257
|
expect(() => {
|
|
203
|
-
|
|
258
|
+
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />);
|
|
204
259
|
}).not.toThrow();
|
|
205
260
|
});
|
|
206
261
|
|
|
207
262
|
it('should maintain sort order when comments update', () => {
|
|
208
|
-
const { rerender } =
|
|
263
|
+
const { rerender } = renderWithEventBus(
|
|
209
264
|
<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />
|
|
210
265
|
);
|
|
211
266
|
|
|
@@ -215,7 +270,11 @@ describe('CommentsPanel Component', () => {
|
|
|
215
270
|
createMockComment('4', 25, 35),
|
|
216
271
|
];
|
|
217
272
|
|
|
218
|
-
rerender(
|
|
273
|
+
rerender(
|
|
274
|
+
<EventBusProvider>
|
|
275
|
+
<CommentsPanel {...defaultProps} annotations={updatedComments} />
|
|
276
|
+
</EventBusProvider>
|
|
277
|
+
);
|
|
219
278
|
|
|
220
279
|
const comments = screen.getAllByTestId(/comment-/);
|
|
221
280
|
|
|
@@ -229,7 +288,7 @@ describe('CommentsPanel Component', () => {
|
|
|
229
288
|
|
|
230
289
|
describe('New Comment Creation', () => {
|
|
231
290
|
it('should not show new comment input by default', () => {
|
|
232
|
-
|
|
291
|
+
renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
233
292
|
|
|
234
293
|
expect(screen.queryByPlaceholderText(/Add your comment/)).not.toBeInTheDocument();
|
|
235
294
|
});
|
|
@@ -237,11 +296,10 @@ describe('CommentsPanel Component', () => {
|
|
|
237
296
|
it('should show new comment input when pendingAnnotation exists', () => {
|
|
238
297
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
239
298
|
|
|
240
|
-
|
|
299
|
+
renderWithEventBus(
|
|
241
300
|
<CommentsPanel
|
|
242
301
|
{...defaultProps}
|
|
243
302
|
pendingAnnotation={pendingAnnotation}
|
|
244
|
-
onCreate={vi.fn()}
|
|
245
303
|
/>
|
|
246
304
|
);
|
|
247
305
|
|
|
@@ -251,11 +309,10 @@ describe('CommentsPanel Component', () => {
|
|
|
251
309
|
it('should display quoted selected text in new comment area', () => {
|
|
252
310
|
const pendingAnnotation = createPendingAnnotation('Selected text for comment');
|
|
253
311
|
|
|
254
|
-
|
|
312
|
+
renderWithEventBus(
|
|
255
313
|
<CommentsPanel
|
|
256
314
|
{...defaultProps}
|
|
257
315
|
pendingAnnotation={pendingAnnotation}
|
|
258
|
-
onCreate={vi.fn()}
|
|
259
316
|
/>
|
|
260
317
|
);
|
|
261
318
|
|
|
@@ -266,11 +323,10 @@ describe('CommentsPanel Component', () => {
|
|
|
266
323
|
const longText = 'A'.repeat(150);
|
|
267
324
|
const pendingAnnotation = createPendingAnnotation(longText);
|
|
268
325
|
|
|
269
|
-
|
|
326
|
+
renderWithEventBus(
|
|
270
327
|
<CommentsPanel
|
|
271
328
|
{...defaultProps}
|
|
272
329
|
pendingAnnotation={pendingAnnotation}
|
|
273
|
-
onCreate={vi.fn()}
|
|
274
330
|
/>
|
|
275
331
|
);
|
|
276
332
|
|
|
@@ -281,11 +337,10 @@ describe('CommentsPanel Component', () => {
|
|
|
281
337
|
it('should allow typing in new comment textarea', async () => {
|
|
282
338
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
283
339
|
|
|
284
|
-
|
|
340
|
+
renderWithEventBus(
|
|
285
341
|
<CommentsPanel
|
|
286
342
|
{...defaultProps}
|
|
287
343
|
pendingAnnotation={pendingAnnotation}
|
|
288
|
-
onCreate={vi.fn()}
|
|
289
344
|
/>
|
|
290
345
|
);
|
|
291
346
|
|
|
@@ -298,11 +353,10 @@ describe('CommentsPanel Component', () => {
|
|
|
298
353
|
it('should show character count', async () => {
|
|
299
354
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
300
355
|
|
|
301
|
-
|
|
356
|
+
renderWithEventBus(
|
|
302
357
|
<CommentsPanel
|
|
303
358
|
{...defaultProps}
|
|
304
359
|
pendingAnnotation={pendingAnnotation}
|
|
305
|
-
onCreate={vi.fn()}
|
|
306
360
|
/>
|
|
307
361
|
);
|
|
308
362
|
|
|
@@ -317,11 +371,10 @@ describe('CommentsPanel Component', () => {
|
|
|
317
371
|
it('should enforce maxLength of 2000 characters', () => {
|
|
318
372
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
319
373
|
|
|
320
|
-
|
|
374
|
+
renderWithEventBus(
|
|
321
375
|
<CommentsPanel
|
|
322
376
|
{...defaultProps}
|
|
323
377
|
pendingAnnotation={pendingAnnotation}
|
|
324
|
-
onCreate={vi.fn()}
|
|
325
378
|
/>
|
|
326
379
|
);
|
|
327
380
|
|
|
@@ -332,11 +385,10 @@ describe('CommentsPanel Component', () => {
|
|
|
332
385
|
it('should auto-focus new comment textarea', () => {
|
|
333
386
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
334
387
|
|
|
335
|
-
|
|
388
|
+
renderWithEventBus(
|
|
336
389
|
<CommentsPanel
|
|
337
390
|
{...defaultProps}
|
|
338
391
|
pendingAnnotation={pendingAnnotation}
|
|
339
|
-
onCreate={vi.fn()}
|
|
340
392
|
/>
|
|
341
393
|
);
|
|
342
394
|
|
|
@@ -344,16 +396,16 @@ describe('CommentsPanel Component', () => {
|
|
|
344
396
|
expect(textarea).toHaveFocus();
|
|
345
397
|
});
|
|
346
398
|
|
|
347
|
-
it('should
|
|
348
|
-
const
|
|
399
|
+
it('should emit annotation:create event when save is clicked', async () => {
|
|
400
|
+
const tracker = createEventTracker();
|
|
349
401
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
350
402
|
|
|
351
|
-
|
|
403
|
+
renderWithEventBus(
|
|
352
404
|
<CommentsPanel
|
|
353
405
|
{...defaultProps}
|
|
354
406
|
pendingAnnotation={pendingAnnotation}
|
|
355
|
-
|
|
356
|
-
|
|
407
|
+
/>,
|
|
408
|
+
tracker
|
|
357
409
|
);
|
|
358
410
|
|
|
359
411
|
const textarea = screen.getByPlaceholderText(/Add your comment/);
|
|
@@ -362,17 +414,22 @@ describe('CommentsPanel Component', () => {
|
|
|
362
414
|
const saveButton = screen.getByText('Save');
|
|
363
415
|
await userEvent.click(saveButton);
|
|
364
416
|
|
|
365
|
-
|
|
417
|
+
await waitFor(() => {
|
|
418
|
+
expect(tracker.events.some(e =>
|
|
419
|
+
e.event === 'annotation:create' &&
|
|
420
|
+
e.payload?.motivation === 'commenting' &&
|
|
421
|
+
e.payload?.body?.[0]?.value === 'My new comment'
|
|
422
|
+
)).toBe(true);
|
|
423
|
+
});
|
|
366
424
|
});
|
|
367
425
|
|
|
368
|
-
it('should clear textarea after
|
|
426
|
+
it('should clear textarea after save is clicked', async () => {
|
|
369
427
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
370
428
|
|
|
371
|
-
|
|
429
|
+
renderWithEventBus(
|
|
372
430
|
<CommentsPanel
|
|
373
431
|
{...defaultProps}
|
|
374
432
|
pendingAnnotation={pendingAnnotation}
|
|
375
|
-
onCreate={vi.fn()}
|
|
376
433
|
/>
|
|
377
434
|
);
|
|
378
435
|
|
|
@@ -386,11 +443,10 @@ describe('CommentsPanel Component', () => {
|
|
|
386
443
|
it('should disable save button when textarea is empty', () => {
|
|
387
444
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
388
445
|
|
|
389
|
-
|
|
446
|
+
renderWithEventBus(
|
|
390
447
|
<CommentsPanel
|
|
391
448
|
{...defaultProps}
|
|
392
449
|
pendingAnnotation={pendingAnnotation}
|
|
393
|
-
onCreate={vi.fn()}
|
|
394
450
|
/>
|
|
395
451
|
);
|
|
396
452
|
|
|
@@ -401,11 +457,10 @@ describe('CommentsPanel Component', () => {
|
|
|
401
457
|
it('should disable save button when textarea contains only whitespace', async () => {
|
|
402
458
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
403
459
|
|
|
404
|
-
|
|
460
|
+
renderWithEventBus(
|
|
405
461
|
<CommentsPanel
|
|
406
462
|
{...defaultProps}
|
|
407
463
|
pendingAnnotation={pendingAnnotation}
|
|
408
|
-
onCreate={vi.fn()}
|
|
409
464
|
/>
|
|
410
465
|
);
|
|
411
466
|
|
|
@@ -419,11 +474,10 @@ describe('CommentsPanel Component', () => {
|
|
|
419
474
|
it('should enable save button when text is entered', async () => {
|
|
420
475
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
421
476
|
|
|
422
|
-
|
|
477
|
+
renderWithEventBus(
|
|
423
478
|
<CommentsPanel
|
|
424
479
|
{...defaultProps}
|
|
425
480
|
pendingAnnotation={pendingAnnotation}
|
|
426
|
-
onCreate={vi.fn()}
|
|
427
481
|
/>
|
|
428
482
|
);
|
|
429
483
|
|
|
@@ -437,11 +491,10 @@ describe('CommentsPanel Component', () => {
|
|
|
437
491
|
it('should have proper styling for new comment area', () => {
|
|
438
492
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
439
493
|
|
|
440
|
-
const { container } =
|
|
494
|
+
const { container } = renderWithEventBus(
|
|
441
495
|
<CommentsPanel
|
|
442
496
|
{...defaultProps}
|
|
443
497
|
pendingAnnotation={pendingAnnotation}
|
|
444
|
-
onCreate={vi.fn()}
|
|
445
498
|
/>
|
|
446
499
|
);
|
|
447
500
|
|
|
@@ -452,76 +505,24 @@ describe('CommentsPanel Component', () => {
|
|
|
452
505
|
});
|
|
453
506
|
|
|
454
507
|
describe('Comment Interactions', () => {
|
|
455
|
-
it('should
|
|
456
|
-
|
|
457
|
-
render(
|
|
508
|
+
it('should render comment entries', () => {
|
|
509
|
+
renderWithEventBus(
|
|
458
510
|
<CommentsPanel
|
|
459
511
|
{...defaultProps}
|
|
460
512
|
annotations={mockComments.single}
|
|
461
|
-
onAnnotationClick={onCommentClick}
|
|
462
513
|
/>
|
|
463
514
|
);
|
|
464
515
|
|
|
465
516
|
const comment = screen.getByTestId('comment-1');
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
expect(onCommentClick).toHaveBeenCalledWith(mockComments.single[0]);
|
|
517
|
+
expect(comment).toBeInTheDocument();
|
|
469
518
|
});
|
|
470
519
|
|
|
471
520
|
});
|
|
472
521
|
|
|
473
522
|
describe('Comment Hover Behavior', () => {
|
|
474
|
-
it('should
|
|
475
|
-
const onAnnotationHover = vi.fn();
|
|
476
|
-
render(
|
|
477
|
-
<CommentsPanel
|
|
478
|
-
{...defaultProps}
|
|
479
|
-
annotations={mockComments.single}
|
|
480
|
-
onAnnotationHover={onAnnotationHover}
|
|
481
|
-
/>
|
|
482
|
-
);
|
|
483
|
-
|
|
484
|
-
const hoverButton = screen.getByText('Hover');
|
|
485
|
-
fireEvent.mouseEnter(hoverButton);
|
|
486
|
-
|
|
487
|
-
expect(onAnnotationHover).toHaveBeenCalledWith('1');
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
it('should handle hoveredAnnotationId prop changes', () => {
|
|
491
|
-
const { rerender } = render(
|
|
492
|
-
<CommentsPanel
|
|
493
|
-
{...defaultProps}
|
|
494
|
-
annotations={mockComments.multiple}
|
|
495
|
-
hoveredAnnotationId={null}
|
|
496
|
-
/>
|
|
497
|
-
);
|
|
498
|
-
|
|
499
|
-
// Should not error when hoveredAnnotationId changes
|
|
500
|
-
expect(() => {
|
|
501
|
-
rerender(
|
|
502
|
-
<CommentsPanel
|
|
503
|
-
{...defaultProps}
|
|
504
|
-
annotations={mockComments.multiple}
|
|
505
|
-
hoveredAnnotationId="2"
|
|
506
|
-
/>
|
|
507
|
-
);
|
|
508
|
-
}).not.toThrow();
|
|
509
|
-
|
|
510
|
-
// Should handle being set back to null
|
|
523
|
+
it('should render without errors', () => {
|
|
511
524
|
expect(() => {
|
|
512
|
-
|
|
513
|
-
<CommentsPanel
|
|
514
|
-
{...defaultProps}
|
|
515
|
-
annotations={mockComments.multiple}
|
|
516
|
-
hoveredAnnotationId={null}
|
|
517
|
-
/>
|
|
518
|
-
);
|
|
519
|
-
}).not.toThrow();
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
it('should not error when onAnnotationHover is not provided', () => {
|
|
523
|
-
expect(() => {
|
|
524
|
-
render(
|
|
525
|
+
renderWithEventBus(
|
|
525
526
|
<CommentsPanel
|
|
526
527
|
{...defaultProps}
|
|
527
528
|
annotations={mockComments.single}
|
|
@@ -532,51 +533,39 @@ describe('CommentsPanel Component', () => {
|
|
|
532
533
|
});
|
|
533
534
|
|
|
534
535
|
describe('Focus Management', () => {
|
|
535
|
-
it('should
|
|
536
|
-
|
|
536
|
+
it('should render comments', () => {
|
|
537
|
+
renderWithEventBus(
|
|
537
538
|
<CommentsPanel
|
|
538
539
|
{...defaultProps}
|
|
539
540
|
annotations={mockComments.multiple}
|
|
540
|
-
focusedAnnotationId="2"
|
|
541
541
|
/>
|
|
542
542
|
);
|
|
543
543
|
|
|
544
|
-
//
|
|
544
|
+
// Comments should be rendered
|
|
545
545
|
expect(screen.getByTestId('comment-2')).toBeInTheDocument();
|
|
546
546
|
});
|
|
547
|
-
|
|
548
|
-
it('should handle null focusedAnnotationId', () => {
|
|
549
|
-
expect(() => {
|
|
550
|
-
render(
|
|
551
|
-
<CommentsPanel
|
|
552
|
-
{...defaultProps}
|
|
553
|
-
annotations={mockComments.multiple}
|
|
554
|
-
focusedAnnotationId={null}
|
|
555
|
-
/>
|
|
556
|
-
);
|
|
557
|
-
}).not.toThrow();
|
|
558
|
-
});
|
|
559
547
|
});
|
|
560
548
|
|
|
561
549
|
describe('Panel Structure and Styling', () => {
|
|
562
550
|
it('should have fixed header that does not scroll', () => {
|
|
563
|
-
|
|
551
|
+
renderWithEventBus(
|
|
564
552
|
<CommentsPanel {...defaultProps} annotations={mockComments.many} />
|
|
565
553
|
);
|
|
566
554
|
|
|
567
|
-
const
|
|
555
|
+
const headers = screen.getAllByText(/Comments/);
|
|
556
|
+
const header = headers[0].closest('div');
|
|
568
557
|
expect(header).toHaveClass('semiont-panel-header');
|
|
569
558
|
});
|
|
570
559
|
|
|
571
560
|
it('should support dark mode', () => {
|
|
572
|
-
const { container } =
|
|
561
|
+
const { container } = renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
573
562
|
|
|
574
563
|
const panel = container.firstChild as HTMLElement;
|
|
575
564
|
expect(panel).toHaveClass('semiont-panel');
|
|
576
565
|
});
|
|
577
566
|
|
|
578
567
|
it('should have proper spacing between comments', () => {
|
|
579
|
-
const { container } =
|
|
568
|
+
const { container } = renderWithEventBus(
|
|
580
569
|
<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />
|
|
581
570
|
);
|
|
582
571
|
|
|
@@ -585,9 +574,10 @@ describe('CommentsPanel Component', () => {
|
|
|
585
574
|
});
|
|
586
575
|
|
|
587
576
|
it('should have proper border styling', () => {
|
|
588
|
-
|
|
577
|
+
renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
589
578
|
|
|
590
|
-
const
|
|
579
|
+
const headers = screen.getAllByText(/Comments/);
|
|
580
|
+
const header = headers[0].closest('div');
|
|
591
581
|
expect(header).toHaveClass('semiont-panel-header');
|
|
592
582
|
});
|
|
593
583
|
});
|
|
@@ -595,14 +585,14 @@ describe('CommentsPanel Component', () => {
|
|
|
595
585
|
describe('Edge Cases', () => {
|
|
596
586
|
it('should handle empty comments array', () => {
|
|
597
587
|
expect(() => {
|
|
598
|
-
|
|
588
|
+
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={[]} />);
|
|
599
589
|
}).not.toThrow();
|
|
600
590
|
|
|
601
591
|
expect(screen.getByText(/No comments yet/)).toBeInTheDocument();
|
|
602
592
|
});
|
|
603
593
|
|
|
604
594
|
it('should handle rapid comment additions', () => {
|
|
605
|
-
const { rerender } =
|
|
595
|
+
const { rerender } = renderWithEventBus(
|
|
606
596
|
<CommentsPanel {...defaultProps} annotations={mockComments.empty} />
|
|
607
597
|
);
|
|
608
598
|
|
|
@@ -610,37 +600,43 @@ describe('CommentsPanel Component', () => {
|
|
|
610
600
|
const comments = Array.from({ length: i }, (_, j) =>
|
|
611
601
|
createMockComment(`${j + 1}`, j * 10, (j + 1) * 10)
|
|
612
602
|
);
|
|
613
|
-
rerender(
|
|
603
|
+
rerender(
|
|
604
|
+
<EventBusProvider>
|
|
605
|
+
<CommentsPanel {...defaultProps} annotations={comments} />
|
|
606
|
+
</EventBusProvider>
|
|
607
|
+
);
|
|
614
608
|
}
|
|
615
609
|
|
|
616
610
|
expect(screen.getAllByTestId(/comment-/)).toHaveLength(5);
|
|
617
611
|
});
|
|
618
612
|
|
|
619
613
|
it('should handle comment removal', () => {
|
|
620
|
-
const { rerender } =
|
|
614
|
+
const { rerender } = renderWithEventBus(
|
|
621
615
|
<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />
|
|
622
616
|
);
|
|
623
617
|
|
|
624
618
|
expect(screen.getAllByTestId(/comment-/)).toHaveLength(3);
|
|
625
619
|
|
|
626
620
|
rerender(
|
|
627
|
-
<
|
|
621
|
+
<EventBusProvider>
|
|
622
|
+
<CommentsPanel {...defaultProps} annotations={mockComments.single} />
|
|
623
|
+
</EventBusProvider>
|
|
628
624
|
);
|
|
629
625
|
|
|
630
626
|
expect(screen.getAllByTestId(/comment-/)).toHaveLength(1);
|
|
631
627
|
});
|
|
632
628
|
|
|
633
|
-
it('should show new comment input
|
|
629
|
+
it('should show new comment input when pendingAnnotation exists', () => {
|
|
634
630
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
635
631
|
|
|
636
|
-
|
|
632
|
+
renderWithEventBus(
|
|
637
633
|
<CommentsPanel
|
|
638
634
|
{...defaultProps}
|
|
639
635
|
pendingAnnotation={pendingAnnotation}
|
|
640
636
|
/>
|
|
641
637
|
);
|
|
642
638
|
|
|
643
|
-
// Component shows textarea when pendingAnnotation exists
|
|
639
|
+
// Component shows textarea when pendingAnnotation exists
|
|
644
640
|
expect(screen.getByPlaceholderText(/Add your comment/)).toBeInTheDocument();
|
|
645
641
|
});
|
|
646
642
|
|
|
@@ -650,7 +646,7 @@ describe('CommentsPanel Component', () => {
|
|
|
650
646
|
);
|
|
651
647
|
|
|
652
648
|
expect(() => {
|
|
653
|
-
|
|
649
|
+
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={manyComments} />);
|
|
654
650
|
}).not.toThrow();
|
|
655
651
|
|
|
656
652
|
expect(screen.getAllByTestId(/comment-/)).toHaveLength(100);
|
|
@@ -664,7 +660,7 @@ describe('CommentsPanel Component', () => {
|
|
|
664
660
|
];
|
|
665
661
|
|
|
666
662
|
expect(() => {
|
|
667
|
-
|
|
663
|
+
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={commentsAtSamePosition} />);
|
|
668
664
|
}).not.toThrow();
|
|
669
665
|
|
|
670
666
|
expect(screen.getAllByTestId(/comment-/)).toHaveLength(3);
|
|
@@ -673,10 +669,10 @@ describe('CommentsPanel Component', () => {
|
|
|
673
669
|
|
|
674
670
|
describe('Accessibility', () => {
|
|
675
671
|
it('should have proper heading structure', () => {
|
|
676
|
-
|
|
672
|
+
renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
677
673
|
|
|
678
|
-
const
|
|
679
|
-
expect(
|
|
674
|
+
const headings = screen.getAllByText(/Comments/);
|
|
675
|
+
expect(headings[0]).toHaveClass('semiont-panel-header__text');
|
|
680
676
|
});
|
|
681
677
|
|
|
682
678
|
it('should have proper textarea attributes for new comments', () => {
|
|
@@ -688,11 +684,10 @@ describe('CommentsPanel Component', () => {
|
|
|
688
684
|
},
|
|
689
685
|
};
|
|
690
686
|
|
|
691
|
-
|
|
687
|
+
renderWithEventBus(
|
|
692
688
|
<CommentsPanel
|
|
693
689
|
{...defaultProps}
|
|
694
690
|
pendingAnnotation={pendingAnnotation}
|
|
695
|
-
onCreate={vi.fn()}
|
|
696
691
|
/>
|
|
697
692
|
);
|
|
698
693
|
|
|
@@ -701,7 +696,7 @@ describe('CommentsPanel Component', () => {
|
|
|
701
696
|
});
|
|
702
697
|
|
|
703
698
|
it('should have semantic HTML structure', () => {
|
|
704
|
-
const { container } =
|
|
699
|
+
const { container } = renderWithEventBus(
|
|
705
700
|
<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />
|
|
706
701
|
);
|
|
707
702
|
|