@semiont/react-ui 0.5.10 → 0.5.12
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/README.md +1 -1
- package/dist/{PdfAnnotationCanvas.client-P37SXBWQ.js → PdfAnnotationCanvas.client-75GY2EDR.js} +15 -6
- package/dist/PdfAnnotationCanvas.client-75GY2EDR.js.map +1 -0
- package/dist/{chunk-5LFFAVDI.js → chunk-PFUXQPWJ.js} +96 -96
- package/dist/{chunk-5LFFAVDI.js.map → chunk-PFUXQPWJ.js.map} +1 -1
- package/dist/{chunk-O2MD7TGE.js → chunk-VXASXU4K.js} +10 -2
- package/dist/chunk-VXASXU4K.js.map +1 -0
- package/dist/index.css +150 -112
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +334 -48
- package/dist/index.js +2494 -2201
- package/dist/index.js.map +1 -1
- package/dist/test-utils.d.ts +5 -1
- package/dist/test-utils.js +10 -4
- package/dist/test-utils.js.map +1 -1
- package/package.json +7 -7
- package/src/components/ResourceTagsInline.tsx +94 -11
- package/src/components/StatusDisplay.css +36 -0
- package/src/components/__tests__/ResourceTagsInline.test.tsx +101 -3
- package/src/components/annotation/AnnotateToolbar.tsx +53 -28
- package/src/components/annotation/__tests__/AnnotateToolbar.display.test.tsx +76 -0
- package/src/components/annotation/__tests__/AnnotateToolbar.test.tsx +124 -360
- package/src/components/image-annotation/AnnotationOverlay.tsx +6 -6
- package/src/components/image-annotation/SvgDrawingCanvas.tsx +17 -5
- package/src/components/image-annotation/__tests__/AnnotationOverlay.click.test.tsx +137 -0
- package/src/components/image-annotation/__tests__/SvgDrawingCanvas.click.test.tsx +108 -0
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +17 -4
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +143 -7
- package/src/components/resource/AnnotateView.tsx +59 -38
- package/src/components/resource/BrowseView.tsx +172 -138
- package/src/components/resource/ResourceViewer.tsx +123 -130
- package/src/components/resource/__tests__/AnnotateView.embeddable.test.tsx +55 -0
- package/src/components/resource/__tests__/BrowseView.embeddable.test.tsx +49 -0
- package/src/components/resource/__tests__/BrowseView.links.test.tsx +60 -0
- package/src/components/resource/__tests__/BrowseView.overlay-async.test.tsx +113 -0
- package/src/components/resource/__tests__/BrowseView.reference-hover.test.tsx +161 -0
- package/src/components/resource/__tests__/BrowseView.registry.test.tsx +43 -0
- package/src/components/resource/__tests__/BrowseView.test.tsx +133 -53
- package/src/components/resource/__tests__/ResourceViewer.embeddable.test.tsx +102 -0
- package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +12 -7
- package/src/components/resource/__tests__/ResourceViewer.toolbar-opt-out.test.tsx +135 -0
- package/src/components/resource/__tests__/ResourceViewer.toolbar-prefs.test.tsx +200 -0
- package/src/components/resource/__tests__/browse-renderers.dispatch.test.tsx +113 -0
- package/src/components/resource/__tests__/browse-renderers.test.tsx +34 -0
- package/src/components/resource/__tests__/inline-embedding.test.tsx +81 -0
- package/src/components/resource/__tests__/media-completeness.test.tsx +88 -0
- package/src/components/resource/browse-renderers.tsx +85 -0
- package/src/components/resource/panels/AssessmentEntry.tsx +5 -4
- package/src/components/resource/panels/AssessmentPanel.tsx +12 -5
- package/src/components/resource/panels/AssistSection.tsx +4 -3
- package/src/components/resource/panels/CommentEntry.tsx +5 -4
- package/src/components/resource/panels/CommentsPanel.tsx +12 -5
- package/src/components/resource/panels/HighlightEntry.tsx +5 -4
- package/src/components/resource/panels/HighlightPanel.tsx +12 -5
- package/src/components/resource/panels/ReferenceEntry.tsx +9 -10
- package/src/components/resource/panels/ReferencesPanel.tsx +15 -6
- package/src/components/resource/panels/ResourceInfoPanel.tsx +4 -3
- package/src/components/resource/panels/TagEntry.tsx +5 -3
- package/src/components/resource/panels/TaggingPanel.tsx +11 -4
- package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +15 -2
- package/src/components/resource/panels/__tests__/AssessmentEntry.test.tsx +28 -20
- package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +39 -28
- package/src/components/resource/panels/__tests__/AssistSection.test.tsx +46 -17
- package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +65 -57
- package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +54 -24
- package/src/components/resource/panels/__tests__/HighlightEntry.test.tsx +27 -17
- package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +23 -17
- package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +48 -77
- package/src/components/resource/panels/__tests__/ReferencesPanel.headless.test.tsx +87 -0
- package/src/components/resource/panels/__tests__/ReferencesPanel.observable-flow.test.tsx +15 -9
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +78 -59
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +11 -4
- package/src/components/resource/panels/__tests__/TagEntry.test.tsx +33 -19
- package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +16 -7
- package/src/components/toolbar/Toolbar.css +14 -0
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +7 -17
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +43 -17
- package/src/features/resource-viewer/state/resource-viewer-page-state-unit.ts +2 -14
- package/src/styles/features/resource-viewer.css +19 -0
- package/dist/PdfAnnotationCanvas.client-P37SXBWQ.js.map +0 -1
- package/dist/chunk-O2MD7TGE.js.map +0 -1
|
@@ -6,6 +6,7 @@ import userEvent from '@testing-library/user-event';
|
|
|
6
6
|
import '@testing-library/jest-dom';
|
|
7
7
|
import { CommentsPanel } from '../CommentsPanel';
|
|
8
8
|
import type { EventBus } from '@semiont/core';
|
|
9
|
+
import type { SemiontSession } from '@semiont/sdk';
|
|
9
10
|
import { createTestSemiontWrapper } from '../../../../test-utils';
|
|
10
11
|
|
|
11
12
|
import type { Annotation, AnnotationId } from '@semiont/core';
|
|
@@ -32,8 +33,18 @@ function createEventTracker() {
|
|
|
32
33
|
};
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
// Per-test session/bus/wrapper — created in beforeEach because test-utils
|
|
37
|
+
// disposes every created client after each test (a module-scope wrapper
|
|
38
|
+
// would hand later tests a disposed client).
|
|
39
|
+
let session: SemiontSession;
|
|
40
|
+
let eventBus: EventBus;
|
|
41
|
+
let SemiontWrapper: React.ComponentType<{ children: React.ReactNode }>;
|
|
42
|
+
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
({ session, eventBus, SemiontWrapper } = createTestSemiontWrapper());
|
|
45
|
+
});
|
|
46
|
+
|
|
35
47
|
const renderWithEventBus = (component: React.ReactElement, tracker?: ReturnType<typeof createEventTracker>) => {
|
|
36
|
-
const { SemiontWrapper, eventBus } = createTestSemiontWrapper();
|
|
37
48
|
if (tracker) tracker._attach(eventBus);
|
|
38
49
|
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
39
50
|
<SemiontWrapper>{children}</SemiontWrapper>
|
|
@@ -141,6 +152,7 @@ const createPendingAnnotation = (exact: string) => ({
|
|
|
141
152
|
|
|
142
153
|
describe('CommentsPanel Component', () => {
|
|
143
154
|
const defaultProps = {
|
|
155
|
+
resourceId: 'res-1',
|
|
144
156
|
annotations: mockComments.empty,
|
|
145
157
|
pendingAnnotation: null,
|
|
146
158
|
};
|
|
@@ -167,7 +179,7 @@ describe('CommentsPanel Component', () => {
|
|
|
167
179
|
|
|
168
180
|
describe('Rendering', () => {
|
|
169
181
|
it('should render panel header with title and count', () => {
|
|
170
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />);
|
|
182
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} annotations={mockComments.multiple} />);
|
|
171
183
|
|
|
172
184
|
const headings = screen.getAllByText(/Comments/);
|
|
173
185
|
expect(headings.length).toBeGreaterThan(0);
|
|
@@ -175,13 +187,13 @@ describe('CommentsPanel Component', () => {
|
|
|
175
187
|
});
|
|
176
188
|
|
|
177
189
|
it('should show empty state when no comments', () => {
|
|
178
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
190
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} />);
|
|
179
191
|
|
|
180
192
|
expect(screen.getByText(/No comments yet/)).toBeInTheDocument();
|
|
181
193
|
});
|
|
182
194
|
|
|
183
195
|
it('should render all comments', () => {
|
|
184
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />);
|
|
196
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} annotations={mockComments.multiple} />);
|
|
185
197
|
|
|
186
198
|
expect(screen.getByTestId('comment-1')).toBeInTheDocument();
|
|
187
199
|
expect(screen.getByTestId('comment-2')).toBeInTheDocument();
|
|
@@ -189,7 +201,7 @@ describe('CommentsPanel Component', () => {
|
|
|
189
201
|
});
|
|
190
202
|
|
|
191
203
|
it('should have proper panel structure', () => {
|
|
192
|
-
const { container } = renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
204
|
+
const { container } = renderWithEventBus(<CommentsPanel session={session} {...defaultProps} />);
|
|
193
205
|
|
|
194
206
|
// Find the root panel div (first child of the container)
|
|
195
207
|
const panel = container.firstChild as HTMLElement;
|
|
@@ -198,7 +210,7 @@ describe('CommentsPanel Component', () => {
|
|
|
198
210
|
|
|
199
211
|
it('should have scrollable comments list', () => {
|
|
200
212
|
const { container } = renderWithEventBus(
|
|
201
|
-
<CommentsPanel {...defaultProps} annotations={mockComments.many} />
|
|
213
|
+
<CommentsPanel session={session} {...defaultProps} annotations={mockComments.many} />
|
|
202
214
|
);
|
|
203
215
|
|
|
204
216
|
const commentsList = container.querySelector('.semiont-panel__list');
|
|
@@ -208,7 +220,7 @@ describe('CommentsPanel Component', () => {
|
|
|
208
220
|
|
|
209
221
|
describe('Comment Sorting', () => {
|
|
210
222
|
it('should sort comments by position in resource', () => {
|
|
211
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />);
|
|
223
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} annotations={mockComments.multiple} />);
|
|
212
224
|
|
|
213
225
|
const comments = screen.getAllByTestId(/comment-/);
|
|
214
226
|
|
|
@@ -222,13 +234,13 @@ describe('CommentsPanel Component', () => {
|
|
|
222
234
|
mockGetTextPositionSelector.mockReturnValue(null);
|
|
223
235
|
|
|
224
236
|
expect(() => {
|
|
225
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />);
|
|
237
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} annotations={mockComments.multiple} />);
|
|
226
238
|
}).not.toThrow();
|
|
227
239
|
});
|
|
228
240
|
|
|
229
241
|
it('should maintain sort order when comments update', () => {
|
|
230
242
|
const { rerender } = renderWithEventBus(
|
|
231
|
-
<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />
|
|
243
|
+
<CommentsPanel session={session} {...defaultProps} annotations={mockComments.multiple} />
|
|
232
244
|
);
|
|
233
245
|
|
|
234
246
|
// Add a new comment at position 25
|
|
@@ -238,7 +250,7 @@ describe('CommentsPanel Component', () => {
|
|
|
238
250
|
];
|
|
239
251
|
|
|
240
252
|
rerender(
|
|
241
|
-
<CommentsPanel {...defaultProps} annotations={updatedComments} />
|
|
253
|
+
<CommentsPanel session={session} {...defaultProps} annotations={updatedComments} />
|
|
242
254
|
);
|
|
243
255
|
|
|
244
256
|
const comments = screen.getAllByTestId(/comment-/);
|
|
@@ -253,7 +265,7 @@ describe('CommentsPanel Component', () => {
|
|
|
253
265
|
|
|
254
266
|
describe('New Comment Creation', () => {
|
|
255
267
|
it('should not show new comment input by default', () => {
|
|
256
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
268
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} />);
|
|
257
269
|
|
|
258
270
|
expect(screen.queryByPlaceholderText(/Add your comment/)).not.toBeInTheDocument();
|
|
259
271
|
});
|
|
@@ -263,6 +275,7 @@ describe('CommentsPanel Component', () => {
|
|
|
263
275
|
|
|
264
276
|
renderWithEventBus(
|
|
265
277
|
<CommentsPanel
|
|
278
|
+
session={session}
|
|
266
279
|
{...defaultProps}
|
|
267
280
|
pendingAnnotation={pendingAnnotation}
|
|
268
281
|
/>
|
|
@@ -276,6 +289,7 @@ describe('CommentsPanel Component', () => {
|
|
|
276
289
|
|
|
277
290
|
renderWithEventBus(
|
|
278
291
|
<CommentsPanel
|
|
292
|
+
session={session}
|
|
279
293
|
{...defaultProps}
|
|
280
294
|
pendingAnnotation={pendingAnnotation}
|
|
281
295
|
/>
|
|
@@ -290,6 +304,7 @@ describe('CommentsPanel Component', () => {
|
|
|
290
304
|
|
|
291
305
|
renderWithEventBus(
|
|
292
306
|
<CommentsPanel
|
|
307
|
+
session={session}
|
|
293
308
|
{...defaultProps}
|
|
294
309
|
pendingAnnotation={pendingAnnotation}
|
|
295
310
|
/>
|
|
@@ -304,6 +319,7 @@ describe('CommentsPanel Component', () => {
|
|
|
304
319
|
|
|
305
320
|
renderWithEventBus(
|
|
306
321
|
<CommentsPanel
|
|
322
|
+
session={session}
|
|
307
323
|
{...defaultProps}
|
|
308
324
|
pendingAnnotation={pendingAnnotation}
|
|
309
325
|
/>
|
|
@@ -320,6 +336,7 @@ describe('CommentsPanel Component', () => {
|
|
|
320
336
|
|
|
321
337
|
renderWithEventBus(
|
|
322
338
|
<CommentsPanel
|
|
339
|
+
session={session}
|
|
323
340
|
{...defaultProps}
|
|
324
341
|
pendingAnnotation={pendingAnnotation}
|
|
325
342
|
/>
|
|
@@ -338,6 +355,7 @@ describe('CommentsPanel Component', () => {
|
|
|
338
355
|
|
|
339
356
|
renderWithEventBus(
|
|
340
357
|
<CommentsPanel
|
|
358
|
+
session={session}
|
|
341
359
|
{...defaultProps}
|
|
342
360
|
pendingAnnotation={pendingAnnotation}
|
|
343
361
|
/>
|
|
@@ -352,6 +370,7 @@ describe('CommentsPanel Component', () => {
|
|
|
352
370
|
|
|
353
371
|
renderWithEventBus(
|
|
354
372
|
<CommentsPanel
|
|
373
|
+
session={session}
|
|
355
374
|
{...defaultProps}
|
|
356
375
|
pendingAnnotation={pendingAnnotation}
|
|
357
376
|
/>
|
|
@@ -367,6 +386,7 @@ describe('CommentsPanel Component', () => {
|
|
|
367
386
|
|
|
368
387
|
renderWithEventBus(
|
|
369
388
|
<CommentsPanel
|
|
389
|
+
session={session}
|
|
370
390
|
{...defaultProps}
|
|
371
391
|
pendingAnnotation={pendingAnnotation}
|
|
372
392
|
/>,
|
|
@@ -393,6 +413,7 @@ describe('CommentsPanel Component', () => {
|
|
|
393
413
|
|
|
394
414
|
renderWithEventBus(
|
|
395
415
|
<CommentsPanel
|
|
416
|
+
session={session}
|
|
396
417
|
{...defaultProps}
|
|
397
418
|
pendingAnnotation={pendingAnnotation}
|
|
398
419
|
/>
|
|
@@ -410,6 +431,7 @@ describe('CommentsPanel Component', () => {
|
|
|
410
431
|
|
|
411
432
|
renderWithEventBus(
|
|
412
433
|
<CommentsPanel
|
|
434
|
+
session={session}
|
|
413
435
|
{...defaultProps}
|
|
414
436
|
pendingAnnotation={pendingAnnotation}
|
|
415
437
|
/>
|
|
@@ -424,6 +446,7 @@ describe('CommentsPanel Component', () => {
|
|
|
424
446
|
|
|
425
447
|
renderWithEventBus(
|
|
426
448
|
<CommentsPanel
|
|
449
|
+
session={session}
|
|
427
450
|
{...defaultProps}
|
|
428
451
|
pendingAnnotation={pendingAnnotation}
|
|
429
452
|
/>
|
|
@@ -441,6 +464,7 @@ describe('CommentsPanel Component', () => {
|
|
|
441
464
|
|
|
442
465
|
renderWithEventBus(
|
|
443
466
|
<CommentsPanel
|
|
467
|
+
session={session}
|
|
444
468
|
{...defaultProps}
|
|
445
469
|
pendingAnnotation={pendingAnnotation}
|
|
446
470
|
/>
|
|
@@ -458,6 +482,7 @@ describe('CommentsPanel Component', () => {
|
|
|
458
482
|
|
|
459
483
|
const { container } = renderWithEventBus(
|
|
460
484
|
<CommentsPanel
|
|
485
|
+
session={session}
|
|
461
486
|
{...defaultProps}
|
|
462
487
|
pendingAnnotation={pendingAnnotation}
|
|
463
488
|
/>
|
|
@@ -473,6 +498,7 @@ describe('CommentsPanel Component', () => {
|
|
|
473
498
|
it('should render comment entries', () => {
|
|
474
499
|
renderWithEventBus(
|
|
475
500
|
<CommentsPanel
|
|
501
|
+
session={session}
|
|
476
502
|
{...defaultProps}
|
|
477
503
|
annotations={mockComments.single}
|
|
478
504
|
/>
|
|
@@ -489,6 +515,7 @@ describe('CommentsPanel Component', () => {
|
|
|
489
515
|
expect(() => {
|
|
490
516
|
renderWithEventBus(
|
|
491
517
|
<CommentsPanel
|
|
518
|
+
session={session}
|
|
492
519
|
{...defaultProps}
|
|
493
520
|
annotations={mockComments.single}
|
|
494
521
|
/>
|
|
@@ -501,6 +528,7 @@ describe('CommentsPanel Component', () => {
|
|
|
501
528
|
it('should render comments', () => {
|
|
502
529
|
renderWithEventBus(
|
|
503
530
|
<CommentsPanel
|
|
531
|
+
session={session}
|
|
504
532
|
{...defaultProps}
|
|
505
533
|
annotations={mockComments.multiple}
|
|
506
534
|
/>
|
|
@@ -514,7 +542,7 @@ describe('CommentsPanel Component', () => {
|
|
|
514
542
|
describe('Panel Structure and Styling', () => {
|
|
515
543
|
it('should have fixed header that does not scroll', () => {
|
|
516
544
|
renderWithEventBus(
|
|
517
|
-
<CommentsPanel {...defaultProps} annotations={mockComments.many} />
|
|
545
|
+
<CommentsPanel session={session} {...defaultProps} annotations={mockComments.many} />
|
|
518
546
|
);
|
|
519
547
|
|
|
520
548
|
const headers = screen.getAllByText(/Comments/);
|
|
@@ -523,7 +551,7 @@ describe('CommentsPanel Component', () => {
|
|
|
523
551
|
});
|
|
524
552
|
|
|
525
553
|
it('should support dark mode', () => {
|
|
526
|
-
const { container } = renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
554
|
+
const { container } = renderWithEventBus(<CommentsPanel session={session} {...defaultProps} />);
|
|
527
555
|
|
|
528
556
|
const panel = container.firstChild as HTMLElement;
|
|
529
557
|
expect(panel).toHaveClass('semiont-panel');
|
|
@@ -531,7 +559,7 @@ describe('CommentsPanel Component', () => {
|
|
|
531
559
|
|
|
532
560
|
it('should have proper spacing between comments', () => {
|
|
533
561
|
const { container } = renderWithEventBus(
|
|
534
|
-
<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />
|
|
562
|
+
<CommentsPanel session={session} {...defaultProps} annotations={mockComments.multiple} />
|
|
535
563
|
);
|
|
536
564
|
|
|
537
565
|
const commentsList = container.querySelector('.semiont-panel__list');
|
|
@@ -539,7 +567,7 @@ describe('CommentsPanel Component', () => {
|
|
|
539
567
|
});
|
|
540
568
|
|
|
541
569
|
it('should have proper border styling', () => {
|
|
542
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
570
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} />);
|
|
543
571
|
|
|
544
572
|
const headers = screen.getAllByText(/Comments/);
|
|
545
573
|
const header = headers[0].closest('div');
|
|
@@ -550,7 +578,7 @@ describe('CommentsPanel Component', () => {
|
|
|
550
578
|
describe('Edge Cases', () => {
|
|
551
579
|
it('should handle empty comments array', () => {
|
|
552
580
|
expect(() => {
|
|
553
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={[]} />);
|
|
581
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} annotations={[]} />);
|
|
554
582
|
}).not.toThrow();
|
|
555
583
|
|
|
556
584
|
expect(screen.getByText(/No comments yet/)).toBeInTheDocument();
|
|
@@ -558,7 +586,7 @@ describe('CommentsPanel Component', () => {
|
|
|
558
586
|
|
|
559
587
|
it('should handle rapid comment additions', () => {
|
|
560
588
|
const { rerender } = renderWithEventBus(
|
|
561
|
-
<CommentsPanel {...defaultProps} annotations={mockComments.empty} />
|
|
589
|
+
<CommentsPanel session={session} {...defaultProps} annotations={mockComments.empty} />
|
|
562
590
|
);
|
|
563
591
|
|
|
564
592
|
for (let i = 1; i <= 5; i++) {
|
|
@@ -566,7 +594,7 @@ describe('CommentsPanel Component', () => {
|
|
|
566
594
|
createMockComment(`${j + 1}`, j * 10, (j + 1) * 10)
|
|
567
595
|
);
|
|
568
596
|
rerender(
|
|
569
|
-
<CommentsPanel {...defaultProps} annotations={comments} />
|
|
597
|
+
<CommentsPanel session={session} {...defaultProps} annotations={comments} />
|
|
570
598
|
);
|
|
571
599
|
}
|
|
572
600
|
|
|
@@ -575,13 +603,13 @@ describe('CommentsPanel Component', () => {
|
|
|
575
603
|
|
|
576
604
|
it('should handle comment removal', () => {
|
|
577
605
|
const { rerender } = renderWithEventBus(
|
|
578
|
-
<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />
|
|
606
|
+
<CommentsPanel session={session} {...defaultProps} annotations={mockComments.multiple} />
|
|
579
607
|
);
|
|
580
608
|
|
|
581
609
|
expect(screen.getAllByTestId(/comment-/)).toHaveLength(3);
|
|
582
610
|
|
|
583
611
|
rerender(
|
|
584
|
-
<CommentsPanel {...defaultProps} annotations={mockComments.single} />
|
|
612
|
+
<CommentsPanel session={session} {...defaultProps} annotations={mockComments.single} />
|
|
585
613
|
);
|
|
586
614
|
|
|
587
615
|
expect(screen.getAllByTestId(/comment-/)).toHaveLength(1);
|
|
@@ -592,6 +620,7 @@ describe('CommentsPanel Component', () => {
|
|
|
592
620
|
|
|
593
621
|
renderWithEventBus(
|
|
594
622
|
<CommentsPanel
|
|
623
|
+
session={session}
|
|
595
624
|
{...defaultProps}
|
|
596
625
|
pendingAnnotation={pendingAnnotation}
|
|
597
626
|
/>
|
|
@@ -607,7 +636,7 @@ describe('CommentsPanel Component', () => {
|
|
|
607
636
|
);
|
|
608
637
|
|
|
609
638
|
expect(() => {
|
|
610
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={manyComments} />);
|
|
639
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} annotations={manyComments} />);
|
|
611
640
|
}).not.toThrow();
|
|
612
641
|
|
|
613
642
|
expect(screen.getAllByTestId(/comment-/)).toHaveLength(100);
|
|
@@ -621,7 +650,7 @@ describe('CommentsPanel Component', () => {
|
|
|
621
650
|
];
|
|
622
651
|
|
|
623
652
|
expect(() => {
|
|
624
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={commentsAtSamePosition} />);
|
|
653
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} annotations={commentsAtSamePosition} />);
|
|
625
654
|
}).not.toThrow();
|
|
626
655
|
|
|
627
656
|
expect(screen.getAllByTestId(/comment-/)).toHaveLength(3);
|
|
@@ -630,7 +659,7 @@ describe('CommentsPanel Component', () => {
|
|
|
630
659
|
|
|
631
660
|
describe('Accessibility', () => {
|
|
632
661
|
it('should have proper heading structure', () => {
|
|
633
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
662
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} />);
|
|
634
663
|
|
|
635
664
|
const headings = screen.getAllByText(/Comments/);
|
|
636
665
|
expect(headings[0]).toHaveClass('semiont-panel-header__text');
|
|
@@ -647,6 +676,7 @@ describe('CommentsPanel Component', () => {
|
|
|
647
676
|
|
|
648
677
|
renderWithEventBus(
|
|
649
678
|
<CommentsPanel
|
|
679
|
+
session={session}
|
|
650
680
|
{...defaultProps}
|
|
651
681
|
pendingAnnotation={pendingAnnotation}
|
|
652
682
|
/>
|
|
@@ -658,7 +688,7 @@ describe('CommentsPanel Component', () => {
|
|
|
658
688
|
|
|
659
689
|
it('should have semantic HTML structure', () => {
|
|
660
690
|
const { container } = renderWithEventBus(
|
|
661
|
-
<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />
|
|
691
|
+
<CommentsPanel session={session} {...defaultProps} annotations={mockComments.multiple} />
|
|
662
692
|
);
|
|
663
693
|
|
|
664
694
|
// Panel should be a properly structured div hierarchy
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
2
|
import { screen } from '@testing-library/react';
|
|
3
3
|
import '@testing-library/jest-dom';
|
|
4
|
-
import { renderWithProviders } from '../../../../test-utils';
|
|
4
|
+
import { renderWithProviders, createTestSemiontWrapper } from '../../../../test-utils';
|
|
5
5
|
import userEvent from '@testing-library/user-event';
|
|
6
|
+
import type { SemiontSession } from '@semiont/sdk';
|
|
6
7
|
|
|
7
|
-
import type { Annotation, AnnotationId } from '@semiont/core';
|
|
8
|
+
import type { Annotation, AnnotationId, EventBus } from '@semiont/core';
|
|
8
9
|
|
|
9
10
|
// Mock @semiont/http-transport
|
|
10
11
|
vi.mock('@semiont/core', async () => {
|
|
@@ -49,14 +50,21 @@ describe('HighlightEntry', () => {
|
|
|
49
50
|
isFocused: false,
|
|
50
51
|
};
|
|
51
52
|
|
|
53
|
+
// Created per-test: test-utils disposes every created client in a
|
|
54
|
+
// module-scope afterEach, so a module-scope session would be dead
|
|
55
|
+
// after the first test.
|
|
56
|
+
let session: SemiontSession;
|
|
57
|
+
let eventBus: EventBus;
|
|
58
|
+
|
|
52
59
|
beforeEach(() => {
|
|
53
60
|
vi.clearAllMocks();
|
|
54
61
|
mockGetAnnotationExactText.mockReturnValue('This is the highlighted text');
|
|
62
|
+
({ session, eventBus } = createTestSemiontWrapper());
|
|
55
63
|
});
|
|
56
64
|
|
|
57
65
|
describe('Rendering', () => {
|
|
58
66
|
it('should render the selected text in quotes', () => {
|
|
59
|
-
renderWithProviders(<HighlightEntry {...defaultProps} />);
|
|
67
|
+
renderWithProviders(<HighlightEntry {...defaultProps} session={session} />);
|
|
60
68
|
|
|
61
69
|
expect(screen.getByText(/This is the highlighted text/)).toBeInTheDocument();
|
|
62
70
|
});
|
|
@@ -65,7 +73,7 @@ describe('HighlightEntry', () => {
|
|
|
65
73
|
const longText = 'A'.repeat(250);
|
|
66
74
|
mockGetAnnotationExactText.mockReturnValue(longText);
|
|
67
75
|
|
|
68
|
-
renderWithProviders(<HighlightEntry {...defaultProps} />);
|
|
76
|
+
renderWithProviders(<HighlightEntry {...defaultProps} session={session} />);
|
|
69
77
|
|
|
70
78
|
// Should show first 200 chars followed by ellipsis
|
|
71
79
|
expect(screen.getByText(new RegExp(`"${'A'.repeat(200)}`))).toBeInTheDocument();
|
|
@@ -76,7 +84,7 @@ describe('HighlightEntry', () => {
|
|
|
76
84
|
const exactText = 'B'.repeat(200);
|
|
77
85
|
mockGetAnnotationExactText.mockReturnValue(exactText);
|
|
78
86
|
|
|
79
|
-
const { container } = renderWithProviders(<HighlightEntry {...defaultProps} />);
|
|
87
|
+
const { container } = renderWithProviders(<HighlightEntry {...defaultProps} session={session} />);
|
|
80
88
|
|
|
81
89
|
const quote = container.querySelector('.semiont-annotation-entry__quote');
|
|
82
90
|
expect(quote).toBeInTheDocument();
|
|
@@ -84,7 +92,7 @@ describe('HighlightEntry', () => {
|
|
|
84
92
|
});
|
|
85
93
|
|
|
86
94
|
it('should show creator name', () => {
|
|
87
|
-
renderWithProviders(<HighlightEntry {...defaultProps} />);
|
|
95
|
+
renderWithProviders(<HighlightEntry {...defaultProps} session={session} />);
|
|
88
96
|
|
|
89
97
|
expect(screen.getByText(/alice@example.com/)).toBeInTheDocument();
|
|
90
98
|
});
|
|
@@ -94,7 +102,7 @@ describe('HighlightEntry', () => {
|
|
|
94
102
|
delete (highlight as Record<string, unknown>).creator;
|
|
95
103
|
|
|
96
104
|
renderWithProviders(
|
|
97
|
-
<HighlightEntry highlight={highlight} isFocused={false} />
|
|
105
|
+
<HighlightEntry highlight={highlight} isFocused={false} session={session} />
|
|
98
106
|
);
|
|
99
107
|
|
|
100
108
|
expect(screen.getByText(/Unknown/)).toBeInTheDocument();
|
|
@@ -106,7 +114,7 @@ describe('HighlightEntry', () => {
|
|
|
106
114
|
});
|
|
107
115
|
|
|
108
116
|
renderWithProviders(
|
|
109
|
-
<HighlightEntry highlight={recentHighlight} isFocused={false} />
|
|
117
|
+
<HighlightEntry highlight={recentHighlight} isFocused={false} session={session} />
|
|
110
118
|
);
|
|
111
119
|
|
|
112
120
|
expect(screen.getByText(/just now/)).toBeInTheDocument();
|
|
@@ -115,7 +123,7 @@ describe('HighlightEntry', () => {
|
|
|
115
123
|
it('should not render quote section when selectedText is empty', () => {
|
|
116
124
|
mockGetAnnotationExactText.mockReturnValue('');
|
|
117
125
|
|
|
118
|
-
const { container } = renderWithProviders(<HighlightEntry {...defaultProps} />);
|
|
126
|
+
const { container } = renderWithProviders(<HighlightEntry {...defaultProps} session={session} />);
|
|
119
127
|
|
|
120
128
|
expect(container.querySelector('.semiont-annotation-entry__quote')).not.toBeInTheDocument();
|
|
121
129
|
});
|
|
@@ -125,12 +133,14 @@ describe('HighlightEntry', () => {
|
|
|
125
133
|
it('should emit browse:click on click', async () => {
|
|
126
134
|
const clickHandler = vi.fn();
|
|
127
135
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
// The component only sees the `session` prop now, so the assertion
|
|
137
|
+
// must subscribe on that same session's bus — not the bus of the
|
|
138
|
+
// wrapper renderWithProviders creates internally.
|
|
139
|
+
const { container } = renderWithProviders(
|
|
140
|
+
<HighlightEntry {...defaultProps} session={session} />
|
|
131
141
|
);
|
|
132
142
|
|
|
133
|
-
const subscription = eventBus
|
|
143
|
+
const subscription = eventBus.get('browse:click').subscribe(clickHandler);
|
|
134
144
|
|
|
135
145
|
const entry = container.firstChild as HTMLElement;
|
|
136
146
|
await userEvent.click(entry);
|
|
@@ -147,7 +157,7 @@ describe('HighlightEntry', () => {
|
|
|
147
157
|
describe('Hover state', () => {
|
|
148
158
|
it('should apply pulse class when isHovered is true', () => {
|
|
149
159
|
const { container } = renderWithProviders(
|
|
150
|
-
<HighlightEntry {...defaultProps} isHovered={true} />
|
|
160
|
+
<HighlightEntry {...defaultProps} isHovered={true} session={session} />
|
|
151
161
|
);
|
|
152
162
|
|
|
153
163
|
const entry = container.firstChild as HTMLElement;
|
|
@@ -156,7 +166,7 @@ describe('HighlightEntry', () => {
|
|
|
156
166
|
|
|
157
167
|
it('should not apply pulse class when isHovered is false', () => {
|
|
158
168
|
const { container } = renderWithProviders(
|
|
159
|
-
<HighlightEntry {...defaultProps} isHovered={false} />
|
|
169
|
+
<HighlightEntry {...defaultProps} isHovered={false} session={session} />
|
|
160
170
|
);
|
|
161
171
|
|
|
162
172
|
const entry = container.firstChild as HTMLElement;
|
|
@@ -167,7 +177,7 @@ describe('HighlightEntry', () => {
|
|
|
167
177
|
describe('Focus state', () => {
|
|
168
178
|
it('should set data-focused to true when focused', () => {
|
|
169
179
|
const { container } = renderWithProviders(
|
|
170
|
-
<HighlightEntry {...defaultProps} isFocused={true} />
|
|
180
|
+
<HighlightEntry {...defaultProps} isFocused={true} session={session} />
|
|
171
181
|
);
|
|
172
182
|
|
|
173
183
|
const entry = container.firstChild as HTMLElement;
|
|
@@ -176,7 +186,7 @@ describe('HighlightEntry', () => {
|
|
|
176
186
|
|
|
177
187
|
it('should set data-focused to false when not focused', () => {
|
|
178
188
|
const { container } = renderWithProviders(
|
|
179
|
-
<HighlightEntry {...defaultProps} isFocused={false} />
|
|
189
|
+
<HighlightEntry {...defaultProps} isFocused={false} session={session} />
|
|
180
190
|
);
|
|
181
191
|
|
|
182
192
|
const entry = container.firstChild as HTMLElement;
|
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
15
15
|
import React from 'react';
|
|
16
16
|
import { screen } from '@testing-library/react';
|
|
17
|
-
import { renderWithProviders } from '../../../../test-utils';
|
|
17
|
+
import { renderWithProviders, createTestSemiontWrapper } from '../../../../test-utils';
|
|
18
18
|
import { HighlightPanel } from '../HighlightPanel';
|
|
19
|
+
import type { SemiontSession } from '@semiont/sdk';
|
|
19
20
|
|
|
20
21
|
import type { Annotation, AnnotationId } from '@semiont/core';
|
|
21
22
|
|
|
@@ -48,9 +49,14 @@ vi.mock('../../../../contexts/useEventSubscription', () => ({
|
|
|
48
49
|
|
|
49
50
|
describe('HighlightPanel + AssistSection Integration', () => {
|
|
50
51
|
let mockAnnotations: Annotation[];
|
|
52
|
+
// Created per-test: test-utils disposes every created client in a
|
|
53
|
+
// module-scope afterEach, so a module-scope session would be dead
|
|
54
|
+
// after the first test.
|
|
55
|
+
let session: SemiontSession;
|
|
51
56
|
|
|
52
57
|
beforeEach(() => {
|
|
53
58
|
vi.clearAllMocks();
|
|
59
|
+
({ session } = createTestSemiontWrapper());
|
|
54
60
|
|
|
55
61
|
mockAnnotations = [
|
|
56
62
|
{
|
|
@@ -74,7 +80,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
74
80
|
describe('Detection Progress Prop Passing', () => {
|
|
75
81
|
it('should pass progress to AssistSection when provided', () => {
|
|
76
82
|
renderWithProviders(
|
|
77
|
-
<HighlightPanel
|
|
83
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
78
84
|
annotations={mockAnnotations}
|
|
79
85
|
pendingAnnotation={null}
|
|
80
86
|
isAssisting={true}
|
|
@@ -93,7 +99,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
93
99
|
|
|
94
100
|
it('should pass null progress to AssistSection', () => {
|
|
95
101
|
renderWithProviders(
|
|
96
|
-
<HighlightPanel
|
|
102
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
97
103
|
annotations={mockAnnotations}
|
|
98
104
|
pendingAnnotation={null}
|
|
99
105
|
isAssisting={false}
|
|
@@ -109,7 +115,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
109
115
|
|
|
110
116
|
it('should pass undefined progress to AssistSection', () => {
|
|
111
117
|
renderWithProviders(
|
|
112
|
-
<HighlightPanel
|
|
118
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
113
119
|
annotations={mockAnnotations}
|
|
114
120
|
pendingAnnotation={null}
|
|
115
121
|
isAssisting={false}
|
|
@@ -125,7 +131,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
125
131
|
|
|
126
132
|
it('should keep progress visible after detection completes (isAssisting=false)', () => {
|
|
127
133
|
renderWithProviders(
|
|
128
|
-
<HighlightPanel
|
|
134
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
129
135
|
annotations={mockAnnotations}
|
|
130
136
|
pendingAnnotation={null}
|
|
131
137
|
isAssisting={false}
|
|
@@ -146,7 +152,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
146
152
|
|
|
147
153
|
it('should pass progress with request parameters to AssistSection', () => {
|
|
148
154
|
renderWithProviders(
|
|
149
|
-
<HighlightPanel
|
|
155
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
150
156
|
annotations={mockAnnotations}
|
|
151
157
|
pendingAnnotation={null}
|
|
152
158
|
isAssisting={true}
|
|
@@ -172,7 +178,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
172
178
|
describe('Annotate Mode Toggling', () => {
|
|
173
179
|
it('should render AssistSection when annotateMode is true', () => {
|
|
174
180
|
renderWithProviders(
|
|
175
|
-
<HighlightPanel
|
|
181
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
176
182
|
annotations={mockAnnotations}
|
|
177
183
|
pendingAnnotation={null}
|
|
178
184
|
isAssisting={false}
|
|
@@ -186,7 +192,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
186
192
|
|
|
187
193
|
it('should NOT render AssistSection when annotateMode is false', () => {
|
|
188
194
|
renderWithProviders(
|
|
189
|
-
<HighlightPanel
|
|
195
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
190
196
|
annotations={mockAnnotations}
|
|
191
197
|
pendingAnnotation={null}
|
|
192
198
|
isAssisting={false}
|
|
@@ -200,7 +206,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
200
206
|
|
|
201
207
|
it('should hide progress when switching to browse mode (annotateMode=false)', () => {
|
|
202
208
|
const { rerender } = renderWithProviders(
|
|
203
|
-
<HighlightPanel
|
|
209
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
204
210
|
annotations={mockAnnotations}
|
|
205
211
|
pendingAnnotation={null}
|
|
206
212
|
isAssisting={true}
|
|
@@ -217,7 +223,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
217
223
|
|
|
218
224
|
// Switch to browse mode
|
|
219
225
|
rerender(
|
|
220
|
-
<HighlightPanel
|
|
226
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
221
227
|
annotations={mockAnnotations}
|
|
222
228
|
pendingAnnotation={null}
|
|
223
229
|
isAssisting={true}
|
|
@@ -238,7 +244,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
238
244
|
describe('State Combinations', () => {
|
|
239
245
|
it('should handle isAssisting=true with no progress (starting state)', () => {
|
|
240
246
|
renderWithProviders(
|
|
241
|
-
<HighlightPanel
|
|
247
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
242
248
|
annotations={mockAnnotations}
|
|
243
249
|
pendingAnnotation={null}
|
|
244
250
|
isAssisting={true}
|
|
@@ -253,7 +259,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
253
259
|
|
|
254
260
|
it('should handle isAssisting=false with progress (final state)', () => {
|
|
255
261
|
renderWithProviders(
|
|
256
|
-
<HighlightPanel
|
|
262
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
257
263
|
annotations={mockAnnotations}
|
|
258
264
|
pendingAnnotation={null}
|
|
259
265
|
isAssisting={false}
|
|
@@ -274,7 +280,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
274
280
|
|
|
275
281
|
it('should handle multiple progress updates', () => {
|
|
276
282
|
const { rerender } = renderWithProviders(
|
|
277
|
-
<HighlightPanel
|
|
283
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
278
284
|
annotations={mockAnnotations}
|
|
279
285
|
pendingAnnotation={null}
|
|
280
286
|
isAssisting={true}
|
|
@@ -291,7 +297,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
291
297
|
|
|
292
298
|
// Update to analyzing
|
|
293
299
|
rerender(
|
|
294
|
-
<HighlightPanel
|
|
300
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
295
301
|
annotations={mockAnnotations}
|
|
296
302
|
pendingAnnotation={null}
|
|
297
303
|
isAssisting={true}
|
|
@@ -309,7 +315,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
309
315
|
|
|
310
316
|
// Update to complete
|
|
311
317
|
rerender(
|
|
312
|
-
<HighlightPanel
|
|
318
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
313
319
|
annotations={mockAnnotations}
|
|
314
320
|
pendingAnnotation={null}
|
|
315
321
|
isAssisting={false}
|
|
@@ -330,7 +336,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
330
336
|
describe('Highlights List Rendering', () => {
|
|
331
337
|
it('should render highlights list alongside detection progress', () => {
|
|
332
338
|
renderWithProviders(
|
|
333
|
-
<HighlightPanel
|
|
339
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
334
340
|
annotations={mockAnnotations}
|
|
335
341
|
pendingAnnotation={null}
|
|
336
342
|
isAssisting={true}
|
|
@@ -350,7 +356,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
350
356
|
|
|
351
357
|
it('should show empty state when no highlights', () => {
|
|
352
358
|
renderWithProviders(
|
|
353
|
-
<HighlightPanel
|
|
359
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
354
360
|
annotations={[]}
|
|
355
361
|
pendingAnnotation={null}
|
|
356
362
|
isAssisting={false}
|