@semiont/react-ui 0.5.5 → 0.5.7
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 +59 -55
- package/dist/{PdfAnnotationCanvas.client-CN3C3S55.js → PdfAnnotationCanvas.client-NIMALXNZ.js} +7 -27
- package/dist/PdfAnnotationCanvas.client-NIMALXNZ.js.map +1 -0
- package/dist/{ar-U2EXWUMQ.js → ar-SONK6MON.js} +3 -7
- package/dist/ar-SONK6MON.js.map +1 -0
- package/dist/{bn-DRJGV772.js → bn-ZKPRITNG.js} +3 -7
- package/dist/bn-ZKPRITNG.js.map +1 -0
- package/dist/{chunk-3Q3TUKWP.js → chunk-Y2EEAOMZ.js} +29 -29
- package/dist/{cs-PTWDM23V.js → cs-LPXQ7NHQ.js} +3 -7
- package/dist/cs-LPXQ7NHQ.js.map +1 -0
- package/dist/{da-KSNIKYSS.js → da-6TKY7MCY.js} +6 -10
- package/dist/da-6TKY7MCY.js.map +1 -0
- package/dist/{de-F2XBEWFY.js → de-C3GNII74.js} +3 -7
- package/dist/de-C3GNII74.js.map +1 -0
- package/dist/{el-DLD2GWAP.js → el-UBCXQDJ7.js} +3 -7
- package/dist/el-UBCXQDJ7.js.map +1 -0
- package/dist/{es-WLPYWGB5.js → es-BQ23TRI7.js} +11 -15
- package/dist/es-BQ23TRI7.js.map +1 -0
- package/dist/{fa-BAXHSDZG.js → fa-AFTBZB77.js} +3 -7
- package/dist/fa-AFTBZB77.js.map +1 -0
- package/dist/{fi-FCHSYVOT.js → fi-WOYNLZC2.js} +3 -7
- package/dist/fi-WOYNLZC2.js.map +1 -0
- package/dist/{fr-3UERBSL6.js → fr-NDSMIFJM.js} +3 -7
- package/dist/fr-NDSMIFJM.js.map +1 -0
- package/dist/{he-F6F3FV2K.js → he-VJXVRDOY.js} +3 -7
- package/dist/he-VJXVRDOY.js.map +1 -0
- package/dist/{hi-4BK6IK7Q.js → hi-BF6PHIE2.js} +3 -7
- package/dist/hi-BF6PHIE2.js.map +1 -0
- package/dist/{id-7ECCWP3J.js → id-GXG5QCZY.js} +3 -7
- package/dist/id-GXG5QCZY.js.map +1 -0
- package/dist/index.css +103 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +271 -120
- package/dist/index.js +877 -698
- package/dist/index.js.map +1 -1
- package/dist/{it-234Z6XK6.js → it-XKHHCBAF.js} +3 -7
- package/dist/it-XKHHCBAF.js.map +1 -0
- package/dist/{ja-PJWQI4OQ.js → ja-TX7VM4XD.js} +3 -7
- package/dist/ja-TX7VM4XD.js.map +1 -0
- package/dist/{ko-APUEW2RS.js → ko-DNC7EQ7J.js} +3 -7
- package/dist/ko-DNC7EQ7J.js.map +1 -0
- package/dist/{ms-PJBZWZWD.js → ms-POZGBKPH.js} +3 -7
- package/dist/ms-POZGBKPH.js.map +1 -0
- package/dist/{nl-L4C3ZBCU.js → nl-IRMTKI7Z.js} +4 -11
- package/dist/nl-IRMTKI7Z.js.map +1 -0
- package/dist/{no-QE5N5KNG.js → no-ZUDJA4S6.js} +20 -24
- package/dist/no-ZUDJA4S6.js.map +1 -0
- package/dist/{pl-5Q2D23PD.js → pl-2NGAXL5U.js} +3 -7
- package/dist/pl-2NGAXL5U.js.map +1 -0
- package/dist/{pt-AIGUOIOC.js → pt-ABMCXZUM.js} +118 -122
- package/dist/pt-ABMCXZUM.js.map +1 -0
- package/dist/{ro-T56CSHTY.js → ro-VOJP6O5X.js} +3 -7
- package/dist/ro-VOJP6O5X.js.map +1 -0
- package/dist/{sv-L4TJQ2UH.js → sv-4HVFIIE5.js} +43 -47
- package/dist/sv-4HVFIIE5.js.map +1 -0
- package/dist/test-utils.js +2 -2
- package/dist/test-utils.js.map +1 -1
- package/dist/{th-6O7Y6O2Q.js → th-IFPZP3HQ.js} +3 -7
- package/dist/th-IFPZP3HQ.js.map +1 -0
- package/dist/{tr-D4CQCSNO.js → tr-2GYEAMJ4.js} +3 -7
- package/dist/tr-2GYEAMJ4.js.map +1 -0
- package/dist/{uk-2HMQG6ND.js → uk-XCJBVLLD.js} +3 -7
- package/dist/uk-XCJBVLLD.js.map +1 -0
- package/dist/{vi-XVJ4RUEJ.js → vi-4FR7CB2F.js} +3 -7
- package/dist/vi-4FR7CB2F.js.map +1 -0
- package/dist/{zh-K2KDPGHK.js → zh-NSKFOINB.js} +3 -7
- package/dist/zh-NSKFOINB.js.map +1 -0
- package/package.json +17 -13
- package/src/components/Button/__tests__/Button.test.tsx +0 -2
- package/src/components/CodeMirrorRenderer.tsx +2 -0
- package/src/components/ErrorBoundary.tsx +0 -9
- package/src/components/ProtectedErrorBoundary.css +119 -0
- package/src/components/ProtectedErrorBoundary.tsx +24 -15
- package/src/components/__tests__/AnnotateReferencesProgressWidget.test.tsx +0 -1
- package/src/components/__tests__/ErrorBoundary.test.tsx +20 -13
- package/src/components/__tests__/LiveRegion.hooks.test.tsx +1 -1
- package/src/components/__tests__/ProtectedErrorBoundary.test.tsx +2 -1
- package/src/components/__tests__/ResizeHandle.test.tsx +0 -1
- package/src/components/__tests__/SessionExpiryBanner.test.tsx +0 -1
- package/src/components/__tests__/StatusDisplay.test.tsx +0 -1
- package/src/components/__tests__/Toast.test.tsx +2 -3
- package/src/components/__tests__/Toolbar.test.tsx +0 -1
- package/src/components/annotation/annotations.css +14 -0
- package/src/components/annotation-popups/__tests__/JsonLdView.test.tsx +3 -5
- package/src/components/annotation-popups/__tests__/SharedPopupElements.test.tsx +0 -1
- package/src/components/branding/__tests__/SemiontBranding.test.tsx +1 -2
- package/src/components/layout/__tests__/LeftSidebar.test.tsx +5 -6
- package/src/components/layout/__tests__/PageLayout.test.tsx +1 -3
- package/src/components/layout/__tests__/SkipLinks.a11y.test.tsx +8 -8
- package/src/components/layout/__tests__/UnifiedHeader.test.tsx +12 -1
- package/src/components/modals/__tests__/KeyboardShortcutsHelpModal.test.tsx +0 -1
- package/src/components/modals/__tests__/PermissionDeniedModal.test.tsx +3 -4
- package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +1 -2
- package/src/components/modals/__tests__/SearchModal.basic.test.tsx +1 -1
- package/src/components/modals/__tests__/SearchModal.keyboard.test.tsx +0 -5
- package/src/components/modals/__tests__/SearchModal.search-wiring.test.tsx +1 -2
- package/src/components/modals/__tests__/SearchModal.visual.test.tsx +2 -2
- package/src/components/modals/__tests__/SessionExpiredModal.test.tsx +0 -1
- package/src/components/navigation/NavigationMenu.tsx +1 -1
- package/src/components/navigation/__tests__/Footer.a11y.test.tsx +4 -0
- package/src/components/navigation/__tests__/Footer.test.tsx +3 -6
- package/src/components/navigation/__tests__/NavigationMenu.a11y.test.tsx +1 -1
- package/src/components/navigation/__tests__/NavigationMenu.test.tsx +7 -9
- package/src/components/navigation/__tests__/ObservableLink.test.tsx +0 -1
- package/src/components/navigation/__tests__/SimpleNavigation.test.tsx +1 -2
- package/src/components/navigation/__tests__/SortableResourceTab.test.tsx +0 -1
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +6 -4
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +10 -19
- package/src/components/resource/AnnotateView.tsx +35 -37
- package/src/components/resource/BrowseView.tsx +31 -31
- package/src/components/resource/__tests__/AnnotationHistory.test.tsx +0 -1
- package/src/components/resource/__tests__/BrowseView.test.tsx +12 -14
- package/src/components/resource/__tests__/HistoryEvent.test.tsx +0 -5
- package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +4 -6
- package/src/components/resource/__tests__/event-formatting.test.ts +1 -1
- package/src/components/resource/panels/CollaborationPanel.tsx +1 -1
- package/src/components/resource/panels/JsonLdPanel.tsx +33 -16
- package/src/components/resource/panels/ReferencesPanel.tsx +1 -1
- package/src/components/resource/panels/__tests__/AssessmentEntry.test.tsx +4 -5
- package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +8 -7
- package/src/components/resource/panels/__tests__/AssistSection.test.tsx +14 -10
- package/src/components/resource/panels/__tests__/CollaborationPanel.test.tsx +0 -1
- package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +31 -18
- package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +7 -6
- package/src/components/resource/panels/__tests__/HighlightEntry.test.tsx +5 -6
- package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +19 -13
- package/src/components/resource/panels/__tests__/JsonLdPanel.test.tsx +95 -426
- package/src/components/resource/panels/__tests__/PanelHeader.test.tsx +0 -1
- package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +5 -5
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +40 -7
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +4 -4
- package/src/components/resource/panels/__tests__/StatisticsPanel.test.tsx +30 -32
- package/src/components/resource/panels/__tests__/TagEntry.test.tsx +6 -6
- package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +7 -6
- package/src/components/settings/__tests__/SettingsPanel.test.tsx +0 -1
- package/src/components/viewers/__tests__/ImageViewer.test.tsx +0 -1
- package/src/features/admin-exchange/__tests__/AdminExchangePage.test.tsx +7 -10
- package/src/features/admin-exchange/__tests__/ImportProgress.test.tsx +38 -27
- package/src/features/admin-exchange/components/ImportProgress.tsx +28 -34
- package/src/features/auth/__tests__/SignInForm.a11y.test.tsx +2 -0
- package/src/features/auth/__tests__/SignUpForm.a11y.test.tsx +11 -12
- package/src/features/auth/__tests__/SignUpForm.test.tsx +3 -3
- package/src/features/moderate-tag-schemas/components/TagSchemasPage.tsx +1 -0
- package/src/features/moderation-linked-data/__tests__/LinkedDataPage.test.tsx +11 -9
- package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +2 -1
- package/src/features/resource-compose/components/ResourceComposePage.tsx +36 -9
- package/src/features/resource-compose/state/compose-page-state-unit.ts +5 -8
- package/src/features/resource-discovery/__tests__/ResourceCard.test.tsx +0 -1
- package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +33 -35
- package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +12 -11
- package/src/features/resource-discovery/state/__tests__/discover-state-unit.test.ts +204 -11
- package/src/features/resource-discovery/state/discover-state-unit.ts +70 -11
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +2 -2
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +10 -7
- package/src/features/resource-viewer/state/__tests__/resource-viewer-page-state-unit.test.ts +37 -1
- package/src/features/resource-viewer/state/resource-viewer-page-state-unit.ts +14 -7
- package/src/integrations/__tests__/css-modules-helper.test.tsx +2 -3
- package/src/integrations/__tests__/styled-components-theme.test.ts +1 -3
- package/src/styles/features/exchange.css +0 -30
- package/src/styles/index.css +1 -0
- package/translations/ar.json +1 -3
- package/translations/bn.json +1 -3
- package/translations/cs.json +1 -3
- package/translations/da.json +4 -6
- package/translations/de.json +1 -3
- package/translations/el.json +1 -3
- package/translations/es.json +9 -11
- package/translations/fa.json +1 -3
- package/translations/fi.json +1 -3
- package/translations/fr.json +1 -3
- package/translations/he.json +1 -3
- package/translations/hi.json +1 -3
- package/translations/id.json +1 -3
- package/translations/it.json +1 -3
- package/translations/ja.json +1 -3
- package/translations/ko.json +1 -3
- package/translations/ms.json +1 -3
- package/translations/nl.json +2 -7
- package/translations/no.json +18 -20
- package/translations/pl.json +1 -3
- package/translations/pt.json +116 -118
- package/translations/ro.json +1 -3
- package/translations/sv.json +41 -43
- package/translations/th.json +1 -3
- package/translations/tr.json +1 -3
- package/translations/uk.json +1 -3
- package/translations/vi.json +1 -3
- package/translations/zh.json +1 -3
- package/dist/PdfAnnotationCanvas.client-CN3C3S55.js.map +0 -1
- package/dist/ar-U2EXWUMQ.js.map +0 -1
- package/dist/bn-DRJGV772.js.map +0 -1
- package/dist/cs-PTWDM23V.js.map +0 -1
- package/dist/da-KSNIKYSS.js.map +0 -1
- package/dist/de-F2XBEWFY.js.map +0 -1
- package/dist/el-DLD2GWAP.js.map +0 -1
- package/dist/es-WLPYWGB5.js.map +0 -1
- package/dist/fa-BAXHSDZG.js.map +0 -1
- package/dist/fi-FCHSYVOT.js.map +0 -1
- package/dist/fr-3UERBSL6.js.map +0 -1
- package/dist/he-F6F3FV2K.js.map +0 -1
- package/dist/hi-4BK6IK7Q.js.map +0 -1
- package/dist/id-7ECCWP3J.js.map +0 -1
- package/dist/it-234Z6XK6.js.map +0 -1
- package/dist/ja-PJWQI4OQ.js.map +0 -1
- package/dist/ko-APUEW2RS.js.map +0 -1
- package/dist/ms-PJBZWZWD.js.map +0 -1
- package/dist/nl-L4C3ZBCU.js.map +0 -1
- package/dist/no-QE5N5KNG.js.map +0 -1
- package/dist/pl-5Q2D23PD.js.map +0 -1
- package/dist/pt-AIGUOIOC.js.map +0 -1
- package/dist/ro-T56CSHTY.js.map +0 -1
- package/dist/sv-L4TJQ2UH.js.map +0 -1
- package/dist/th-6O7Y6O2Q.js.map +0 -1
- package/dist/tr-D4CQCSNO.js.map +0 -1
- package/dist/uk-2HMQG6ND.js.map +0 -1
- package/dist/vi-XVJ4RUEJ.js.map +0 -1
- package/dist/zh-K2KDPGHK.js.map +0 -1
- /package/dist/{chunk-3Q3TUKWP.js.map → chunk-Y2EEAOMZ.js.map} +0 -0
|
@@ -4,7 +4,7 @@ import { useEffect, useRef, useCallback, useMemo, memo, lazy, Suspense } from 'r
|
|
|
4
4
|
import ReactMarkdown from 'react-markdown';
|
|
5
5
|
import remarkGfm from 'remark-gfm';
|
|
6
6
|
import { annotationId as toAnnotationId } from '@semiont/core';
|
|
7
|
-
import {
|
|
7
|
+
import { capabilitiesOf } from '@semiont/core';
|
|
8
8
|
import { createHoverHandlers } from '@semiont/sdk';
|
|
9
9
|
import { ANNOTATORS } from '../../lib/annotation-registry';
|
|
10
10
|
import { scrollAnnotationIntoView } from '../../lib/scroll-utils';
|
|
@@ -83,7 +83,7 @@ export const BrowseView = memo(function BrowseView({
|
|
|
83
83
|
const session = useObservable(useSemiont().activeSession$);
|
|
84
84
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
85
85
|
|
|
86
|
-
const
|
|
86
|
+
const render = capabilitiesOf(mimeType)?.render ?? 'none';
|
|
87
87
|
|
|
88
88
|
const { highlights, references, assessments, comments, tags } = annotations;
|
|
89
89
|
|
|
@@ -207,8 +207,9 @@ export const BrowseView = memo(function BrowseView({
|
|
|
207
207
|
'beckon:focus': handleAnnotationFocus,
|
|
208
208
|
});
|
|
209
209
|
|
|
210
|
-
// Route to
|
|
211
|
-
|
|
210
|
+
// Route to the viewer for this media type's render mode. The switch is
|
|
211
|
+
// exhaustive over RenderMode, so every path returns.
|
|
212
|
+
switch (render) {
|
|
212
213
|
case 'text':
|
|
213
214
|
return (
|
|
214
215
|
<div className="semiont-browse-view" data-mime-type="text">
|
|
@@ -226,34 +227,31 @@ export const BrowseView = memo(function BrowseView({
|
|
|
226
227
|
</div>
|
|
227
228
|
);
|
|
228
229
|
|
|
229
|
-
case '
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
/>
|
|
250
|
-
</Suspense>
|
|
251
|
-
</div>
|
|
230
|
+
case 'pdf':
|
|
231
|
+
return (
|
|
232
|
+
<div className="semiont-browse-view" data-mime-type="pdf">
|
|
233
|
+
<AnnotateToolbar
|
|
234
|
+
selectedMotivation={null}
|
|
235
|
+
selectedClick={selectedClick}
|
|
236
|
+
showSelectionGroup={false}
|
|
237
|
+
showDeleteButton={false}
|
|
238
|
+
annotateMode={annotateMode}
|
|
239
|
+
annotators={ANNOTATORS}
|
|
240
|
+
/>
|
|
241
|
+
<div ref={containerRef} className="semiont-browse-view__content">
|
|
242
|
+
<Suspense fallback={<div className="semiont-browse-view__loading">Loading PDF viewer...</div>}>
|
|
243
|
+
<PdfAnnotationCanvas
|
|
244
|
+
pdfUrl={content}
|
|
245
|
+
existingAnnotations={allAnnotations}
|
|
246
|
+
drawingMode={null}
|
|
247
|
+
selectedMotivation={null}
|
|
248
|
+
/>
|
|
249
|
+
</Suspense>
|
|
252
250
|
</div>
|
|
253
|
-
|
|
254
|
-
|
|
251
|
+
</div>
|
|
252
|
+
);
|
|
255
253
|
|
|
256
|
-
|
|
254
|
+
case 'image':
|
|
257
255
|
return (
|
|
258
256
|
<div className="semiont-browse-view" data-mime-type="image">
|
|
259
257
|
<AnnotateToolbar
|
|
@@ -274,7 +272,9 @@ export const BrowseView = memo(function BrowseView({
|
|
|
274
272
|
</div>
|
|
275
273
|
);
|
|
276
274
|
|
|
277
|
-
case '
|
|
275
|
+
case 'none':
|
|
276
|
+
// Catalogued type with no preview (render: 'none') or an imported
|
|
277
|
+
// foreign type the registry doesn't know — same UI: metadata + download.
|
|
278
278
|
return (
|
|
279
279
|
<div ref={containerRef} className="semiont-browse-view semiont-browse-view--unsupported" data-mime-type="unsupported">
|
|
280
280
|
<div className="semiont-browse-view__empty">
|
|
@@ -2,10 +2,10 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
4
4
|
import { BrowseView } from '../BrowseView';
|
|
5
|
-
import type {
|
|
5
|
+
import type { EventBus } from '@semiont/core';
|
|
6
6
|
import { createTestSemiontWrapper } from '../../../test-utils';
|
|
7
7
|
|
|
8
|
-
import type { Annotation } from '@semiont/core';
|
|
8
|
+
import type { Annotation, AnnotationId } from '@semiont/core';
|
|
9
9
|
|
|
10
10
|
// Mock ResourceAnnotationsContext - keep this simple
|
|
11
11
|
let mockNewAnnotationIds = new Set<string>();
|
|
@@ -15,18 +15,14 @@ vi.mock('../../../contexts/ResourceAnnotationsContext', () => ({
|
|
|
15
15
|
})),
|
|
16
16
|
}));
|
|
17
17
|
|
|
18
|
-
// Mock @semiont/core utilities
|
|
18
|
+
// Mock @semiont/core utilities. The media-type registry (`capabilitiesOf`)
|
|
19
|
+
// is NOT mocked — BrowseView dispatches on the real registry's render mode,
|
|
20
|
+
// so the tested types (text/markdown, image/png, application/octet-stream)
|
|
21
|
+
// resolve through the real source of truth.
|
|
19
22
|
vi.mock('@semiont/core', async () => {
|
|
20
23
|
const actual = await vi.importActual('@semiont/core');
|
|
21
24
|
return {
|
|
22
25
|
...actual,
|
|
23
|
-
getMimeCategory: vi.fn((mimeType: string) => {
|
|
24
|
-
if (mimeType.startsWith('text/')) return 'text';
|
|
25
|
-
if (mimeType.startsWith('image/')) return 'image';
|
|
26
|
-
if (mimeType === 'application/pdf') return 'image';
|
|
27
|
-
return 'unsupported';
|
|
28
|
-
}),
|
|
29
|
-
isPdfMimeType: vi.fn((mimeType: string) => mimeType === 'application/pdf'),
|
|
30
26
|
resourceId: vi.fn((id: string) => id),
|
|
31
27
|
getExactText: vi.fn(() => 'exact text'),
|
|
32
28
|
getTextPositionSelector: vi.fn(() => ({ start: 0, end: 10 })),
|
|
@@ -151,12 +147,12 @@ const renderWithEventTracking = (
|
|
|
151
147
|
};
|
|
152
148
|
|
|
153
149
|
// Test data fixtures
|
|
154
|
-
const createMockAnnotation = (motivation:
|
|
150
|
+
const createMockAnnotation = (motivation: Annotation['motivation'], id: string): Annotation => ({
|
|
155
151
|
'@context': 'http://www.w3.org/ns/anno.jsonld',
|
|
156
|
-
id,
|
|
152
|
+
id: id as AnnotationId,
|
|
157
153
|
type: 'Annotation',
|
|
158
154
|
motivation,
|
|
159
|
-
creator: { name: 'user@example.com' },
|
|
155
|
+
creator: { '@type': 'Person', name: 'user@example.com' },
|
|
160
156
|
created: '2024-01-01T10:00:00Z',
|
|
161
157
|
target: {
|
|
162
158
|
source: 'resource-1',
|
|
@@ -197,7 +193,9 @@ describe('BrowseView Component', () => {
|
|
|
197
193
|
// Mock querySelector and querySelectorAll
|
|
198
194
|
if (typeof document !== 'undefined') {
|
|
199
195
|
document.querySelector = vi.fn();
|
|
200
|
-
|
|
196
|
+
// Return a real (empty) NodeList so the mock matches the DOM signature.
|
|
197
|
+
const emptyNodeList = document.createDocumentFragment().querySelectorAll('*');
|
|
198
|
+
document.querySelectorAll = vi.fn(() => emptyNodeList);
|
|
201
199
|
}
|
|
202
200
|
});
|
|
203
201
|
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import React from 'react';
|
|
3
2
|
import { screen, fireEvent, act } from '@testing-library/react';
|
|
4
3
|
import '@testing-library/jest-dom';
|
|
5
4
|
import { HistoryEvent } from '../HistoryEvent';
|
|
6
5
|
import { renderWithProviders } from '../../../test-utils';
|
|
7
|
-
import type { StoredEvent } from '@semiont/core';
|
|
8
6
|
|
|
9
7
|
// Mock @semiont/core - must use importOriginal to preserve EventBus etc.
|
|
10
8
|
vi.mock('@semiont/core', async (importOriginal) => {
|
|
@@ -37,8 +35,6 @@ vi.mock('../event-formatting', () => ({
|
|
|
37
35
|
import { getAnnotationUriFromEvent } from '@semiont/core';
|
|
38
36
|
import {
|
|
39
37
|
formatEventType,
|
|
40
|
-
getEventEmoji,
|
|
41
|
-
formatRelativeTime,
|
|
42
38
|
getEventDisplayContent,
|
|
43
39
|
getEventEntityTypes,
|
|
44
40
|
getResourceCreationDetails,
|
|
@@ -62,7 +58,6 @@ function makeStoredEvent(overrides: Record<string, any> = {}): any {
|
|
|
62
58
|
...overrides,
|
|
63
59
|
metadata: {
|
|
64
60
|
sequenceNumber: 1,
|
|
65
|
-
streamPosition: 0,
|
|
66
61
|
},
|
|
67
62
|
};
|
|
68
63
|
}
|
|
@@ -11,15 +11,13 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
14
|
-
import { render, screen
|
|
14
|
+
import { render, screen } from '@testing-library/react';
|
|
15
15
|
import { BehaviorSubject } from 'rxjs';
|
|
16
16
|
import { ResourceViewer } from '../ResourceViewer';
|
|
17
17
|
import { createTestSemiontWrapper } from '../../../test-utils';
|
|
18
18
|
import { TranslationProvider } from '../../../contexts/TranslationContext';
|
|
19
19
|
import { ResourceAnnotationsProvider } from '../../../contexts/ResourceAnnotationsContext';
|
|
20
|
-
import type {
|
|
21
|
-
|
|
22
|
-
type SemiontResource = components['schemas']['ResourceDescriptor'];
|
|
20
|
+
import type { ResourceDescriptor as SemiontResource, ResourceId } from '@semiont/core';
|
|
23
21
|
|
|
24
22
|
// Mock dependencies
|
|
25
23
|
vi.mock('../../../hooks/useObservableBrowse', () => ({
|
|
@@ -61,7 +59,7 @@ vi.mock('../../../session/SemiontProvider', async () => {
|
|
|
61
59
|
|
|
62
60
|
const mockResource: SemiontResource & { content: string } = {
|
|
63
61
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
|
64
|
-
'@id': 'test-123',
|
|
62
|
+
'@id': 'test-123' as ResourceId,
|
|
65
63
|
name: 'Test Document',
|
|
66
64
|
created: '2024-01-01T00:00:00Z',
|
|
67
65
|
entityTypes: [],
|
|
@@ -84,7 +82,7 @@ const mockAnnotations = {
|
|
|
84
82
|
};
|
|
85
83
|
|
|
86
84
|
const mockTranslationManager = {
|
|
87
|
-
t: (namespace: string, key: string
|
|
85
|
+
t: (namespace: string, key: string) => {
|
|
88
86
|
return `${namespace}.${key}`;
|
|
89
87
|
},
|
|
90
88
|
};
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
getResourceCreationDetails,
|
|
9
9
|
} from '../event-formatting';
|
|
10
10
|
|
|
11
|
-
// Mock
|
|
11
|
+
// Mock http-transport functions
|
|
12
12
|
vi.mock('@semiont/core', async (importOriginal) => {
|
|
13
13
|
const actual = await importOriginal<typeof import('@semiont/core')>();
|
|
14
14
|
return {
|
|
@@ -7,7 +7,7 @@ import './CollaborationPanel.css';
|
|
|
7
7
|
interface Props {
|
|
8
8
|
/**
|
|
9
9
|
* Connection state from `client.actor.state$`. See
|
|
10
|
-
* `packages/
|
|
10
|
+
* `packages/http-transport/src/state/domain/actor-state-unit.ts`.
|
|
11
11
|
*
|
|
12
12
|
* UI mapping:
|
|
13
13
|
* `open` | `reconnecting` | `initial` | `connecting`
|
|
@@ -8,42 +8,46 @@ import { oneDark } from '@codemirror/theme-one-dark';
|
|
|
8
8
|
import { syntaxHighlighting } from '@codemirror/language';
|
|
9
9
|
import { jsonLightTheme, jsonLightHighlightStyle } from '../../../lib/codemirror-json-theme';
|
|
10
10
|
import { useLineNumbers } from '../../../hooks/useLineNumbers';
|
|
11
|
-
import
|
|
11
|
+
import { useResourceGraph } from '../../../hooks/useResourceGraph';
|
|
12
|
+
import type { ResourceId } from '@semiont/core';
|
|
12
13
|
import './JsonLdPanel.css';
|
|
13
14
|
|
|
14
|
-
type SemiontResource = components['schemas']['ResourceDescriptor'];
|
|
15
|
-
|
|
16
15
|
interface Props {
|
|
17
|
-
|
|
16
|
+
resourceId: ResourceId;
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Dereferences the resource's LD face (`GET /resources/:id/jsonld` via
|
|
21
|
+
* `browse.resourceGraph`) and pretty-prints the full graph — descriptor +
|
|
22
|
+
* annotations + inbound entity references — read-only. This is exactly what
|
|
23
|
+
* an external linked-data client gets when dereferencing the resource's
|
|
24
|
+
* `describedby` URI, so the panel doubles as a living end-to-end test of the
|
|
25
|
+
* LD face. See `.plans/SIMPLER-JSON-LD.md` §5.
|
|
26
|
+
*/
|
|
27
|
+
export function JsonLdPanel({ resourceId }: Props) {
|
|
21
28
|
const editorRef = useRef<HTMLDivElement>(null);
|
|
22
29
|
const viewRef = useRef<EditorView | null>(null);
|
|
23
30
|
const { showLineNumbers } = useLineNumbers();
|
|
31
|
+
const { graph, loading, error } = useResourceGraph(resourceId);
|
|
32
|
+
|
|
33
|
+
const documentText = graph ? JSON.stringify(graph, null, 2) : '';
|
|
24
34
|
|
|
25
|
-
// Initialize CodeMirror
|
|
35
|
+
// Initialize CodeMirror once the graph has loaded.
|
|
26
36
|
useEffect(() => {
|
|
27
|
-
if (!editorRef.current) return;
|
|
37
|
+
if (!editorRef.current || !documentText) return;
|
|
28
38
|
|
|
29
|
-
// Check if dark mode is active
|
|
30
39
|
const isDarkMode = document.documentElement?.classList.contains('dark') ?? false;
|
|
31
40
|
|
|
32
|
-
// Convert resource to JSON-LD format
|
|
33
|
-
const jsonLdContent = JSON.stringify(semiontResource, null, 2);
|
|
34
|
-
|
|
35
41
|
const extensions = [
|
|
36
42
|
json(),
|
|
37
43
|
EditorView.editable.of(false),
|
|
38
44
|
EditorState.readOnly.of(true),
|
|
39
45
|
];
|
|
40
46
|
|
|
41
|
-
// Add line numbers if enabled
|
|
42
47
|
if (showLineNumbers) {
|
|
43
48
|
extensions.push(lineNumbers());
|
|
44
49
|
}
|
|
45
50
|
|
|
46
|
-
// Add theme based on dark/light mode
|
|
47
51
|
if (isDarkMode) {
|
|
48
52
|
extensions.push(oneDark);
|
|
49
53
|
} else {
|
|
@@ -52,7 +56,7 @@ export function JsonLdPanel({ resource: semiontResource }: Props) {
|
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
const state = EditorState.create({
|
|
55
|
-
doc:
|
|
59
|
+
doc: documentText,
|
|
56
60
|
extensions,
|
|
57
61
|
});
|
|
58
62
|
|
|
@@ -67,11 +71,12 @@ export function JsonLdPanel({ resource: semiontResource }: Props) {
|
|
|
67
71
|
view.destroy();
|
|
68
72
|
viewRef.current = null;
|
|
69
73
|
};
|
|
70
|
-
}, [
|
|
74
|
+
}, [documentText, showLineNumbers]);
|
|
71
75
|
|
|
72
76
|
const handleCopyToClipboard = async () => {
|
|
77
|
+
if (!documentText) return;
|
|
73
78
|
try {
|
|
74
|
-
await navigator.clipboard.writeText(
|
|
79
|
+
await navigator.clipboard.writeText(documentText);
|
|
75
80
|
} catch (err) {
|
|
76
81
|
console.error('Failed to copy JSON-LD:', err);
|
|
77
82
|
}
|
|
@@ -88,11 +93,23 @@ export function JsonLdPanel({ resource: semiontResource }: Props) {
|
|
|
88
93
|
onClick={handleCopyToClipboard}
|
|
89
94
|
className="semiont-button semiont-button--icon"
|
|
90
95
|
title="Copy to clipboard"
|
|
96
|
+
disabled={!graph}
|
|
91
97
|
>
|
|
92
98
|
📋 Copy
|
|
93
99
|
</button>
|
|
94
100
|
</div>
|
|
95
101
|
|
|
102
|
+
{loading && (
|
|
103
|
+
<p className="semiont-jsonld-panel__status" role="status">
|
|
104
|
+
Loading JSON-LD…
|
|
105
|
+
</p>
|
|
106
|
+
)}
|
|
107
|
+
{error && !loading && (
|
|
108
|
+
<p className="semiont-jsonld-panel__status semiont-jsonld-panel__status--error" role="alert">
|
|
109
|
+
Failed to load JSON-LD.
|
|
110
|
+
</p>
|
|
111
|
+
)}
|
|
112
|
+
|
|
96
113
|
{/* JSON-LD content rendered with CodeMirror */}
|
|
97
114
|
<div
|
|
98
115
|
ref={editorRef}
|
|
@@ -236,7 +236,7 @@ export function ReferencesPanel({
|
|
|
236
236
|
}
|
|
237
237
|
|
|
238
238
|
// When annotation is complete and we haven't saved yet, save the log
|
|
239
|
-
if (!
|
|
239
|
+
if (!hasSavedLogRef.current && progress?.completedEntityTypes) {
|
|
240
240
|
hasSavedLogRef.current = true;
|
|
241
241
|
setLastDetectionLog(progress.completedEntityTypes);
|
|
242
242
|
setSelectedEntityTypes([]);
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
-
import React from 'react';
|
|
3
2
|
import { screen } from '@testing-library/react';
|
|
4
3
|
import '@testing-library/jest-dom';
|
|
5
4
|
import { renderWithProviders } from '../../../../test-utils';
|
|
6
5
|
import userEvent from '@testing-library/user-event';
|
|
7
|
-
import type { components } from '@semiont/core';
|
|
8
6
|
|
|
9
|
-
import type { Annotation } from '@semiont/core';
|
|
7
|
+
import type { Annotation, AnnotationId } from '@semiont/core';
|
|
10
8
|
|
|
11
|
-
// Mock @semiont/
|
|
9
|
+
// Mock @semiont/http-transport
|
|
12
10
|
vi.mock('@semiont/core', async () => {
|
|
13
11
|
const actual = await vi.importActual('@semiont/core');
|
|
14
12
|
return {
|
|
@@ -25,10 +23,11 @@ const mockGetAnnotationExactText = getAnnotationExactText as MockedFunction<type
|
|
|
25
23
|
|
|
26
24
|
const createMockAssessment = (overrides?: Partial<Annotation>): Annotation => ({
|
|
27
25
|
'@context': 'http://www.w3.org/ns/anno.jsonld',
|
|
28
|
-
id: 'assessment-1',
|
|
26
|
+
id: 'assessment-1' as AnnotationId,
|
|
29
27
|
type: 'Annotation',
|
|
30
28
|
motivation: 'assessing',
|
|
31
29
|
creator: {
|
|
30
|
+
'@type': 'Person',
|
|
32
31
|
name: 'reviewer@example.com',
|
|
33
32
|
},
|
|
34
33
|
created: '2024-06-15T12:00:00Z',
|
|
@@ -1,14 +1,14 @@
|
|
|
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,
|
|
4
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
5
5
|
import userEvent from '@testing-library/user-event';
|
|
6
6
|
import '@testing-library/jest-dom';
|
|
7
7
|
import { AssessmentPanel } from '../AssessmentPanel';
|
|
8
|
-
import type {
|
|
8
|
+
import type { EventBus } from '@semiont/core';
|
|
9
9
|
import { createTestSemiontWrapper } from '../../../../test-utils';
|
|
10
10
|
|
|
11
|
-
import type { Annotation } from '@semiont/core';
|
|
11
|
+
import type { Annotation, AnnotationId } from '@semiont/core';
|
|
12
12
|
|
|
13
13
|
// Composition-based event tracker
|
|
14
14
|
interface TrackedEvent {
|
|
@@ -57,7 +57,7 @@ vi.mock('../../../../contexts/TranslationContext', () => ({
|
|
|
57
57
|
TranslationProvider: ({ children }: { children: React.ReactNode }) => children,
|
|
58
58
|
}));
|
|
59
59
|
|
|
60
|
-
// Mock @semiont/
|
|
60
|
+
// Mock @semiont/http-transport utilities
|
|
61
61
|
vi.mock('@semiont/core', async () => {
|
|
62
62
|
const actual = await vi.importActual('@semiont/core');
|
|
63
63
|
return {
|
|
@@ -69,7 +69,7 @@ vi.mock('@semiont/core', async () => {
|
|
|
69
69
|
|
|
70
70
|
// Mock AssessmentEntry component to simplify testing
|
|
71
71
|
vi.mock('../AssessmentEntry', () => ({
|
|
72
|
-
AssessmentEntry: ({ assessment
|
|
72
|
+
AssessmentEntry: ({ assessment }: any) => (
|
|
73
73
|
<div data-testid={`assessment-${assessment.id}`}>
|
|
74
74
|
<div>{assessment.id}</div>
|
|
75
75
|
</div>
|
|
@@ -78,7 +78,7 @@ vi.mock('../AssessmentEntry', () => ({
|
|
|
78
78
|
|
|
79
79
|
// Mock AssistSection component — just render a simplified version.
|
|
80
80
|
vi.mock('../AssistSection', () => ({
|
|
81
|
-
AssistSection: ({
|
|
81
|
+
AssistSection: ({ isAssisting }: any) => (
|
|
82
82
|
<div data-testid="detect-section">
|
|
83
83
|
<button>Start Detection</button>
|
|
84
84
|
{isAssisting && <div>Detecting...</div>}
|
|
@@ -93,10 +93,11 @@ const mockGetTargetSelector = getTargetSelector as MockedFunction<typeof getTarg
|
|
|
93
93
|
// Test data fixtures
|
|
94
94
|
const createMockAssessment = (id: string, start: number, end: number): Annotation => ({
|
|
95
95
|
'@context': 'http://www.w3.org/ns/anno.jsonld',
|
|
96
|
-
id,
|
|
96
|
+
id: id as AnnotationId,
|
|
97
97
|
type: 'Annotation',
|
|
98
98
|
motivation: 'assessing',
|
|
99
99
|
creator: {
|
|
100
|
+
'@type': 'Person',
|
|
100
101
|
name: `user${id}@example.com`,
|
|
101
102
|
},
|
|
102
103
|
created: `2024-01-0${id.slice(-1)}T10:00:00Z`,
|
|
@@ -17,7 +17,6 @@ import { screen } from '@testing-library/react';
|
|
|
17
17
|
import { renderWithProviders } from '../../../../test-utils';
|
|
18
18
|
import userEvent from '@testing-library/user-event';
|
|
19
19
|
import { AssistSection } from '../AssistSection';
|
|
20
|
-
import type { EventBus } from "@semiont/core"
|
|
21
20
|
|
|
22
21
|
// Mock translations
|
|
23
22
|
const mockT = vi.fn((key: string) => {
|
|
@@ -68,7 +67,7 @@ describe('AssistSection', () => {
|
|
|
68
67
|
annotationType="highlight"
|
|
69
68
|
isAssisting={true}
|
|
70
69
|
progress={{
|
|
71
|
-
|
|
70
|
+
stage: 'analyzing',
|
|
72
71
|
percentage: 30,
|
|
73
72
|
message: 'Analyzing text for highlights...',
|
|
74
73
|
}}
|
|
@@ -84,7 +83,8 @@ describe('AssistSection', () => {
|
|
|
84
83
|
annotationType="highlight"
|
|
85
84
|
isAssisting={true}
|
|
86
85
|
progress={{
|
|
87
|
-
|
|
86
|
+
stage: 'analyzing',
|
|
87
|
+
percentage: 0,
|
|
88
88
|
message: 'Processing...',
|
|
89
89
|
}}
|
|
90
90
|
/>
|
|
@@ -102,7 +102,8 @@ describe('AssistSection', () => {
|
|
|
102
102
|
annotationType="highlight"
|
|
103
103
|
isAssisting={true}
|
|
104
104
|
progress={{
|
|
105
|
-
|
|
105
|
+
stage: 'analyzing',
|
|
106
|
+
percentage: 0,
|
|
106
107
|
message: 'Analyzing...',
|
|
107
108
|
requestParams: [
|
|
108
109
|
{ label: 'Instructions', value: 'Find important points' },
|
|
@@ -125,7 +126,8 @@ describe('AssistSection', () => {
|
|
|
125
126
|
annotationType="highlight"
|
|
126
127
|
isAssisting={true}
|
|
127
128
|
progress={{
|
|
128
|
-
|
|
129
|
+
stage: 'analyzing',
|
|
130
|
+
percentage: 0,
|
|
129
131
|
message: 'Analyzing...',
|
|
130
132
|
}}
|
|
131
133
|
/>
|
|
@@ -170,7 +172,7 @@ describe('AssistSection', () => {
|
|
|
170
172
|
annotationType="highlight"
|
|
171
173
|
isAssisting={false}
|
|
172
174
|
progress={{
|
|
173
|
-
|
|
175
|
+
stage: 'complete',
|
|
174
176
|
percentage: 100,
|
|
175
177
|
message: 'Complete! Created 14 highlights',
|
|
176
178
|
}}
|
|
@@ -433,7 +435,8 @@ describe('AssistSection', () => {
|
|
|
433
435
|
annotationType="highlight"
|
|
434
436
|
isAssisting={true}
|
|
435
437
|
progress={{
|
|
436
|
-
|
|
438
|
+
stage: 'analyzing',
|
|
439
|
+
percentage: 0,
|
|
437
440
|
message: '',
|
|
438
441
|
}}
|
|
439
442
|
/>
|
|
@@ -450,9 +453,9 @@ describe('AssistSection', () => {
|
|
|
450
453
|
annotationType="highlight"
|
|
451
454
|
isAssisting={true}
|
|
452
455
|
progress={{
|
|
453
|
-
|
|
456
|
+
stage: 'analyzing',
|
|
457
|
+
percentage: 0,
|
|
454
458
|
message: 'Processing...',
|
|
455
|
-
// no percentage
|
|
456
459
|
}}
|
|
457
460
|
/>
|
|
458
461
|
);
|
|
@@ -466,7 +469,8 @@ describe('AssistSection', () => {
|
|
|
466
469
|
annotationType="highlight"
|
|
467
470
|
isAssisting={true}
|
|
468
471
|
progress={{
|
|
469
|
-
|
|
472
|
+
stage: 'analyzing',
|
|
473
|
+
percentage: 0,
|
|
470
474
|
message: 'Processing...',
|
|
471
475
|
requestParams: [],
|
|
472
476
|
}}
|
|
@@ -5,10 +5,8 @@ import { renderWithProviders } from '../../../../test-utils';
|
|
|
5
5
|
import userEvent from '@testing-library/user-event';
|
|
6
6
|
import '@testing-library/jest-dom';
|
|
7
7
|
import { CommentEntry } from '../CommentEntry';
|
|
8
|
-
import type { components } from '@semiont/core';
|
|
9
|
-
import type { EventBus } from "@semiont/core"
|
|
10
8
|
|
|
11
|
-
import type { Annotation } from '@semiont/core';
|
|
9
|
+
import type { Annotation, AnnotationId } from '@semiont/core';
|
|
12
10
|
|
|
13
11
|
// Mock TranslationContext
|
|
14
12
|
vi.mock('../../../../contexts/TranslationContext', () => ({
|
|
@@ -23,7 +21,7 @@ vi.mock('../../../../contexts/TranslationContext', () => ({
|
|
|
23
21
|
TranslationProvider: ({ children }: { children: React.ReactNode }) => children,
|
|
24
22
|
}));
|
|
25
23
|
|
|
26
|
-
// Mock @semiont/
|
|
24
|
+
// Mock @semiont/http-transport utilities
|
|
27
25
|
vi.mock('@semiont/core', async () => {
|
|
28
26
|
const actual = await vi.importActual('@semiont/core');
|
|
29
27
|
return {
|
|
@@ -42,10 +40,11 @@ const mockGetAnnotationExactText = getAnnotationExactText as MockedFunction<type
|
|
|
42
40
|
// Test data fixtures
|
|
43
41
|
const createMockComment = (overrides?: Partial<Annotation>): Annotation => ({
|
|
44
42
|
'@context': 'http://www.w3.org/ns/anno.jsonld',
|
|
45
|
-
id: 'comment-1',
|
|
43
|
+
id: 'comment-1' as AnnotationId,
|
|
46
44
|
type: 'Annotation',
|
|
47
45
|
motivation: 'commenting',
|
|
48
46
|
creator: {
|
|
47
|
+
'@type': 'Person',
|
|
49
48
|
name: 'user@example.com',
|
|
50
49
|
},
|
|
51
50
|
created: '2024-01-01T10:00:00Z',
|
|
@@ -98,9 +97,6 @@ describe('CommentEntry Component', () => {
|
|
|
98
97
|
const defaultProps = {
|
|
99
98
|
comment: mockCommentStates.standard,
|
|
100
99
|
isFocused: false,
|
|
101
|
-
onClick: vi.fn(),
|
|
102
|
-
onCommentRef: vi.fn(),
|
|
103
|
-
onCommentHover: vi.fn(),
|
|
104
100
|
};
|
|
105
101
|
|
|
106
102
|
beforeEach(() => {
|
|
@@ -396,19 +392,26 @@ describe('CommentEntry Component', () => {
|
|
|
396
392
|
});
|
|
397
393
|
|
|
398
394
|
it('should stop event propagation when clicking textarea in edit mode', async () => {
|
|
399
|
-
const
|
|
400
|
-
|
|
395
|
+
const clickHandler = vi.fn();
|
|
396
|
+
const { eventBus } = renderWithProviders(
|
|
397
|
+
<CommentEntry {...defaultProps} />,
|
|
398
|
+
{ returnEventBus: true }
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
const subscription = eventBus!.get('browse:click').subscribe(clickHandler);
|
|
401
402
|
|
|
402
403
|
await userEvent.click(screen.getByText('Edit'));
|
|
403
404
|
|
|
404
|
-
// Clear
|
|
405
|
-
|
|
405
|
+
// Clear any browse:click emissions that happened during entering edit mode
|
|
406
|
+
clickHandler.mockClear();
|
|
406
407
|
|
|
407
408
|
const textarea = screen.getByRole('textbox');
|
|
408
409
|
await userEvent.click(textarea);
|
|
409
410
|
|
|
410
|
-
//
|
|
411
|
-
expect(
|
|
411
|
+
// browse:click should not be emitted for textarea click due to stopPropagation
|
|
412
|
+
expect(clickHandler).not.toHaveBeenCalled();
|
|
413
|
+
|
|
414
|
+
subscription.unsubscribe();
|
|
412
415
|
});
|
|
413
416
|
|
|
414
417
|
it('should update text in textarea', async () => {
|
|
@@ -484,14 +487,24 @@ describe('CommentEntry Component', () => {
|
|
|
484
487
|
expect(screen.getByText('This is a test comment')).toBeInTheDocument();
|
|
485
488
|
});
|
|
486
489
|
|
|
487
|
-
it('should not
|
|
488
|
-
const
|
|
489
|
-
|
|
490
|
+
it('should not emit any update when cancel is clicked', async () => {
|
|
491
|
+
const clickHandler = vi.fn();
|
|
492
|
+
const { eventBus } = renderWithProviders(
|
|
493
|
+
<CommentEntry {...defaultProps} />,
|
|
494
|
+
{ returnEventBus: true }
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
const subscription = eventBus!.get('browse:click').subscribe(clickHandler);
|
|
490
498
|
|
|
491
499
|
await userEvent.click(screen.getByText('Edit'));
|
|
500
|
+
clickHandler.mockClear();
|
|
501
|
+
|
|
492
502
|
await userEvent.click(screen.getByText('Cancel'));
|
|
493
503
|
|
|
494
|
-
|
|
504
|
+
// Cancel discards the edit without emitting any event
|
|
505
|
+
expect(clickHandler).not.toHaveBeenCalled();
|
|
506
|
+
|
|
507
|
+
subscription.unsubscribe();
|
|
495
508
|
});
|
|
496
509
|
});
|
|
497
510
|
|