@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,10 +1,12 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { screen, fireEvent, waitFor } from '@testing-library/react';
|
|
4
|
+
import { renderWithProviders, resetEventBusForTesting } from '../../../../test-utils';
|
|
4
5
|
import userEvent from '@testing-library/user-event';
|
|
5
6
|
import '@testing-library/jest-dom';
|
|
6
7
|
import { CommentEntry } from '../CommentEntry';
|
|
7
8
|
import type { components } from '@semiont/api-client';
|
|
9
|
+
import type { EventBus } from '../../../../contexts/EventBusContext';
|
|
8
10
|
|
|
9
11
|
type Annotation = components['schemas']['Annotation'];
|
|
10
12
|
|
|
@@ -18,6 +20,7 @@ vi.mock('../../../../contexts/TranslationContext', () => ({
|
|
|
18
20
|
};
|
|
19
21
|
return translations[key] || key;
|
|
20
22
|
}),
|
|
23
|
+
TranslationProvider: ({ children }: { children: React.ReactNode }) => children,
|
|
21
24
|
}));
|
|
22
25
|
|
|
23
26
|
// Mock @semiont/api-client utilities
|
|
@@ -102,6 +105,7 @@ describe('CommentEntry Component', () => {
|
|
|
102
105
|
|
|
103
106
|
beforeEach(() => {
|
|
104
107
|
vi.clearAllMocks();
|
|
108
|
+
resetEventBusForTesting(); // Reset event bus between tests
|
|
105
109
|
mockGetCommentText.mockReturnValue('This is a test comment');
|
|
106
110
|
mockGetAnnotationExactText.mockReturnValue('This is th');
|
|
107
111
|
|
|
@@ -115,14 +119,14 @@ describe('CommentEntry Component', () => {
|
|
|
115
119
|
|
|
116
120
|
describe('Rendering', () => {
|
|
117
121
|
it('should render comment with text and metadata', () => {
|
|
118
|
-
|
|
122
|
+
renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
119
123
|
|
|
120
124
|
expect(screen.getByText('This is a test comment')).toBeInTheDocument();
|
|
121
125
|
expect(screen.getByText(/user@example.com/)).toBeInTheDocument();
|
|
122
126
|
});
|
|
123
127
|
|
|
124
128
|
it('should render selected text quote', () => {
|
|
125
|
-
const { container } =
|
|
129
|
+
const { container } = renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
126
130
|
|
|
127
131
|
const quote = container.querySelector('.semiont-annotation-entry__quote');
|
|
128
132
|
expect(quote).toBeInTheDocument();
|
|
@@ -133,14 +137,14 @@ describe('CommentEntry Component', () => {
|
|
|
133
137
|
const longText = 'A'.repeat(150);
|
|
134
138
|
mockGetAnnotationExactText.mockReturnValue(longText);
|
|
135
139
|
|
|
136
|
-
|
|
140
|
+
renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
137
141
|
|
|
138
142
|
expect(screen.getByText(new RegExp(`"${'A'.repeat(100)}`))).toBeInTheDocument();
|
|
139
143
|
expect(screen.getByText(/\.\.\./)).toBeInTheDocument();
|
|
140
144
|
});
|
|
141
145
|
|
|
142
146
|
it('should render creator name from creator object', () => {
|
|
143
|
-
|
|
147
|
+
renderWithProviders(
|
|
144
148
|
<CommentEntry
|
|
145
149
|
{...defaultProps}
|
|
146
150
|
comment={mockCommentStates.withCreatorObject}
|
|
@@ -151,7 +155,7 @@ describe('CommentEntry Component', () => {
|
|
|
151
155
|
});
|
|
152
156
|
|
|
153
157
|
it('should handle creator as object with name', () => {
|
|
154
|
-
|
|
158
|
+
renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
155
159
|
|
|
156
160
|
expect(screen.getByText(/user@example.com/)).toBeInTheDocument();
|
|
157
161
|
});
|
|
@@ -160,7 +164,7 @@ describe('CommentEntry Component', () => {
|
|
|
160
164
|
const { creator, ...rest } = createMockComment();
|
|
161
165
|
const commentWithoutCreator = rest as Annotation;
|
|
162
166
|
|
|
163
|
-
|
|
167
|
+
renderWithProviders(
|
|
164
168
|
<CommentEntry
|
|
165
169
|
{...defaultProps}
|
|
166
170
|
comment={commentWithoutCreator}
|
|
@@ -171,7 +175,7 @@ describe('CommentEntry Component', () => {
|
|
|
171
175
|
});
|
|
172
176
|
|
|
173
177
|
it('should format relative time correctly for recent comments', () => {
|
|
174
|
-
|
|
178
|
+
renderWithProviders(
|
|
175
179
|
<CommentEntry
|
|
176
180
|
{...defaultProps}
|
|
177
181
|
comment={mockCommentStates.recentComment}
|
|
@@ -182,7 +186,7 @@ describe('CommentEntry Component', () => {
|
|
|
182
186
|
});
|
|
183
187
|
|
|
184
188
|
it('should format relative time correctly for old comments', () => {
|
|
185
|
-
|
|
189
|
+
renderWithProviders(
|
|
186
190
|
<CommentEntry
|
|
187
191
|
{...defaultProps}
|
|
188
192
|
comment={mockCommentStates.oldComment}
|
|
@@ -195,7 +199,7 @@ describe('CommentEntry Component', () => {
|
|
|
195
199
|
|
|
196
200
|
describe('Focus State', () => {
|
|
197
201
|
it('should apply focus styles when focused', () => {
|
|
198
|
-
const { container } =
|
|
202
|
+
const { container } = renderWithProviders(
|
|
199
203
|
<CommentEntry {...defaultProps} isFocused={true} />
|
|
200
204
|
);
|
|
201
205
|
|
|
@@ -205,7 +209,7 @@ describe('CommentEntry Component', () => {
|
|
|
205
209
|
});
|
|
206
210
|
|
|
207
211
|
it('should not apply focus styles when not focused', () => {
|
|
208
|
-
const { container } =
|
|
212
|
+
const { container } = renderWithProviders(
|
|
209
213
|
<CommentEntry {...defaultProps} isFocused={false} />
|
|
210
214
|
);
|
|
211
215
|
|
|
@@ -217,7 +221,7 @@ describe('CommentEntry Component', () => {
|
|
|
217
221
|
const mockScrollIntoView = vi.fn();
|
|
218
222
|
Element.prototype.scrollIntoView = mockScrollIntoView;
|
|
219
223
|
|
|
220
|
-
const { rerender } =
|
|
224
|
+
const { rerender } = renderWithProviders(
|
|
221
225
|
<CommentEntry {...defaultProps} isFocused={false} />
|
|
222
226
|
);
|
|
223
227
|
|
|
@@ -237,20 +241,31 @@ describe('CommentEntry Component', () => {
|
|
|
237
241
|
});
|
|
238
242
|
|
|
239
243
|
describe('Click Interactions', () => {
|
|
240
|
-
it('should
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
+
it('should emit annotation:click event when comment is clicked', async () => {
|
|
245
|
+
const clickHandler = vi.fn();
|
|
246
|
+
|
|
247
|
+
const { container, eventBus } = renderWithProviders(
|
|
248
|
+
<CommentEntry {...defaultProps} />,
|
|
249
|
+
{ returnEventBus: true }
|
|
244
250
|
);
|
|
245
251
|
|
|
252
|
+
// Subscribe to actual event on real event bus
|
|
253
|
+
eventBus!.on('annotation:click', clickHandler);
|
|
254
|
+
|
|
246
255
|
const commentDiv = container.firstChild as HTMLElement;
|
|
247
256
|
await userEvent.click(commentDiv);
|
|
248
257
|
|
|
249
|
-
expect(
|
|
258
|
+
expect(clickHandler).toHaveBeenCalledWith({
|
|
259
|
+
annotationId: 'comment-1',
|
|
260
|
+
motivation: 'commenting'
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Clean up
|
|
264
|
+
eventBus!.off('annotation:click', clickHandler);
|
|
250
265
|
});
|
|
251
266
|
|
|
252
267
|
it('should be clickable with cursor-pointer class', () => {
|
|
253
|
-
const { container } =
|
|
268
|
+
const { container } = renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
254
269
|
|
|
255
270
|
const commentDiv = container.firstChild as HTMLElement;
|
|
256
271
|
expect(commentDiv).toHaveClass('semiont-annotation-entry');
|
|
@@ -258,34 +273,49 @@ describe('CommentEntry Component', () => {
|
|
|
258
273
|
});
|
|
259
274
|
|
|
260
275
|
describe('Hover Interactions', () => {
|
|
261
|
-
it('should
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
276
|
+
it('should emit annotation:hover event with annotation id on mouse enter', () => {
|
|
277
|
+
const hoverHandler = vi.fn();
|
|
278
|
+
|
|
279
|
+
const { container, eventBus } = renderWithProviders(
|
|
280
|
+
<CommentEntry {...defaultProps} />,
|
|
281
|
+
{ returnEventBus: true }
|
|
265
282
|
);
|
|
266
283
|
|
|
284
|
+
// Subscribe to actual event
|
|
285
|
+
eventBus!.on('annotation:hover', hoverHandler);
|
|
286
|
+
|
|
267
287
|
const commentDiv = container.firstChild as HTMLElement;
|
|
268
288
|
fireEvent.mouseEnter(commentDiv);
|
|
269
289
|
|
|
270
|
-
expect(
|
|
290
|
+
expect(hoverHandler).toHaveBeenCalledWith({ annotationId: 'comment-1' });
|
|
291
|
+
|
|
292
|
+
// Clean up
|
|
293
|
+
eventBus!.off('annotation:hover', hoverHandler);
|
|
271
294
|
});
|
|
272
295
|
|
|
273
|
-
it('should
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
296
|
+
it('should emit annotation:hover event with null on mouse leave', () => {
|
|
297
|
+
const hoverHandler = vi.fn();
|
|
298
|
+
|
|
299
|
+
const { container, eventBus } = renderWithProviders(
|
|
300
|
+
<CommentEntry {...defaultProps} />,
|
|
301
|
+
{ returnEventBus: true }
|
|
277
302
|
);
|
|
278
303
|
|
|
304
|
+
// Subscribe to actual event
|
|
305
|
+
eventBus!.on('annotation:hover', hoverHandler);
|
|
306
|
+
|
|
279
307
|
const commentDiv = container.firstChild as HTMLElement;
|
|
280
308
|
fireEvent.mouseLeave(commentDiv);
|
|
281
309
|
|
|
282
|
-
expect(
|
|
310
|
+
expect(hoverHandler).toHaveBeenCalledWith({ annotationId: null });
|
|
311
|
+
|
|
312
|
+
// Clean up
|
|
313
|
+
eventBus!.off('annotation:hover', hoverHandler);
|
|
283
314
|
});
|
|
284
315
|
|
|
285
|
-
it('should
|
|
286
|
-
const {
|
|
287
|
-
|
|
288
|
-
<CommentEntry {...propsWithoutHover} />
|
|
316
|
+
it('should handle hover events without errors', () => {
|
|
317
|
+
const { container } = renderWithProviders(
|
|
318
|
+
<CommentEntry {...defaultProps} />
|
|
289
319
|
);
|
|
290
320
|
|
|
291
321
|
const commentDiv = container.firstChild as HTMLElement;
|
|
@@ -299,13 +329,13 @@ describe('CommentEntry Component', () => {
|
|
|
299
329
|
|
|
300
330
|
describe('Edit Functionality', () => {
|
|
301
331
|
it('should show edit button', () => {
|
|
302
|
-
|
|
332
|
+
renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
303
333
|
|
|
304
334
|
expect(screen.getByText('Edit')).toBeInTheDocument();
|
|
305
335
|
});
|
|
306
336
|
|
|
307
337
|
it('should enter edit mode when edit button is clicked', async () => {
|
|
308
|
-
|
|
338
|
+
renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
309
339
|
|
|
310
340
|
const editButton = screen.getByText('Edit');
|
|
311
341
|
await userEvent.click(editButton);
|
|
@@ -318,7 +348,7 @@ describe('CommentEntry Component', () => {
|
|
|
318
348
|
});
|
|
319
349
|
|
|
320
350
|
it('should show save and cancel buttons in edit mode', async () => {
|
|
321
|
-
|
|
351
|
+
renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
322
352
|
|
|
323
353
|
const editButton = screen.getByText('Edit');
|
|
324
354
|
await userEvent.click(editButton);
|
|
@@ -330,7 +360,7 @@ describe('CommentEntry Component', () => {
|
|
|
330
360
|
|
|
331
361
|
it('should stop event propagation when clicking textarea in edit mode', async () => {
|
|
332
362
|
const onClick = vi.fn();
|
|
333
|
-
|
|
363
|
+
renderWithProviders(<CommentEntry {...defaultProps} onClick={onClick} />);
|
|
334
364
|
|
|
335
365
|
await userEvent.click(screen.getByText('Edit'));
|
|
336
366
|
|
|
@@ -345,7 +375,7 @@ describe('CommentEntry Component', () => {
|
|
|
345
375
|
});
|
|
346
376
|
|
|
347
377
|
it('should update text in textarea', async () => {
|
|
348
|
-
|
|
378
|
+
renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
349
379
|
|
|
350
380
|
await userEvent.click(screen.getByText('Edit'));
|
|
351
381
|
const textarea = screen.getByRole('textbox');
|
|
@@ -357,7 +387,7 @@ describe('CommentEntry Component', () => {
|
|
|
357
387
|
});
|
|
358
388
|
|
|
359
389
|
it('should show character count in edit mode', async () => {
|
|
360
|
-
|
|
390
|
+
renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
361
391
|
|
|
362
392
|
await userEvent.click(screen.getByText('Edit'));
|
|
363
393
|
|
|
@@ -365,7 +395,7 @@ describe('CommentEntry Component', () => {
|
|
|
365
395
|
});
|
|
366
396
|
|
|
367
397
|
it('should enforce max length of 2000 characters', async () => {
|
|
368
|
-
|
|
398
|
+
renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
369
399
|
|
|
370
400
|
await userEvent.click(screen.getByText('Edit'));
|
|
371
401
|
const textarea = screen.getByRole('textbox') as HTMLTextAreaElement;
|
|
@@ -374,7 +404,7 @@ describe('CommentEntry Component', () => {
|
|
|
374
404
|
});
|
|
375
405
|
|
|
376
406
|
it('should update textarea value and exit edit mode when save is clicked', async () => {
|
|
377
|
-
|
|
407
|
+
renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
378
408
|
|
|
379
409
|
await userEvent.click(screen.getByText('Edit'));
|
|
380
410
|
const textarea = screen.getByRole('textbox');
|
|
@@ -393,7 +423,7 @@ describe('CommentEntry Component', () => {
|
|
|
393
423
|
});
|
|
394
424
|
|
|
395
425
|
it('should exit edit mode after save', async () => {
|
|
396
|
-
|
|
426
|
+
renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
397
427
|
|
|
398
428
|
await userEvent.click(screen.getByText('Edit'));
|
|
399
429
|
await userEvent.click(screen.getByText('Save'));
|
|
@@ -403,7 +433,7 @@ describe('CommentEntry Component', () => {
|
|
|
403
433
|
});
|
|
404
434
|
|
|
405
435
|
it('should discard changes when cancel is clicked', async () => {
|
|
406
|
-
|
|
436
|
+
renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
407
437
|
|
|
408
438
|
await userEvent.click(screen.getByText('Edit'));
|
|
409
439
|
const textarea = screen.getByRole('textbox');
|
|
@@ -419,7 +449,7 @@ describe('CommentEntry Component', () => {
|
|
|
419
449
|
|
|
420
450
|
it('should not call onUpdate when cancel is clicked', async () => {
|
|
421
451
|
const onUpdate = vi.fn();
|
|
422
|
-
|
|
452
|
+
renderWithProviders(<CommentEntry {...defaultProps} onUpdate={onUpdate} />);
|
|
423
453
|
|
|
424
454
|
await userEvent.click(screen.getByText('Edit'));
|
|
425
455
|
await userEvent.click(screen.getByText('Cancel'));
|
|
@@ -428,40 +458,18 @@ describe('CommentEntry Component', () => {
|
|
|
428
458
|
});
|
|
429
459
|
});
|
|
430
460
|
|
|
431
|
-
|
|
432
|
-
it('should call onCommentRef with element on mount', () => {
|
|
433
|
-
const onCommentRef = vi.fn();
|
|
434
|
-
render(<CommentEntry {...defaultProps} onCommentRef={onCommentRef} />);
|
|
435
|
-
|
|
436
|
-
expect(onCommentRef).toHaveBeenCalledWith(
|
|
437
|
-
'comment-1',
|
|
438
|
-
expect.any(HTMLDivElement)
|
|
439
|
-
);
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
it('should call onCommentRef with null on unmount', () => {
|
|
443
|
-
const onCommentRef = vi.fn();
|
|
444
|
-
const { unmount } = render(
|
|
445
|
-
<CommentEntry {...defaultProps} onCommentRef={onCommentRef} />
|
|
446
|
-
);
|
|
447
|
-
|
|
448
|
-
onCommentRef.mockClear();
|
|
449
|
-
unmount();
|
|
450
|
-
|
|
451
|
-
expect(onCommentRef).toHaveBeenCalledWith('comment-1', null);
|
|
452
|
-
});
|
|
453
|
-
});
|
|
461
|
+
// Ref Management tests removed - CommentEntry uses forwardRef but doesn't emit ref-update events
|
|
454
462
|
|
|
455
463
|
describe('Styling and Appearance', () => {
|
|
456
464
|
it('should have proper border and padding styles', () => {
|
|
457
|
-
const { container } =
|
|
465
|
+
const { container } = renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
458
466
|
|
|
459
467
|
const commentDiv = container.firstChild as HTMLElement;
|
|
460
468
|
expect(commentDiv).toHaveClass('semiont-annotation-entry');
|
|
461
469
|
});
|
|
462
470
|
|
|
463
471
|
it('should have hover styles when not focused', () => {
|
|
464
|
-
const { container } =
|
|
472
|
+
const { container } = renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
465
473
|
|
|
466
474
|
const commentDiv = container.firstChild as HTMLElement;
|
|
467
475
|
expect(commentDiv).toHaveClass('semiont-annotation-entry');
|
|
@@ -469,7 +477,7 @@ describe('CommentEntry Component', () => {
|
|
|
469
477
|
});
|
|
470
478
|
|
|
471
479
|
it('should support dark mode classes', () => {
|
|
472
|
-
const { container } =
|
|
480
|
+
const { container } = renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
473
481
|
|
|
474
482
|
const commentDiv = container.firstChild as HTMLElement;
|
|
475
483
|
expect(commentDiv).toHaveClass('semiont-annotation-entry');
|
|
@@ -477,7 +485,7 @@ describe('CommentEntry Component', () => {
|
|
|
477
485
|
|
|
478
486
|
it('should render comment text with whitespace preserved', () => {
|
|
479
487
|
mockGetCommentText.mockReturnValue('Line 1\nLine 2\nLine 3');
|
|
480
|
-
const { container } =
|
|
488
|
+
const { container } = renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
481
489
|
|
|
482
490
|
// Look for the comment text in the annotation entry
|
|
483
491
|
const commentContent = container.querySelector('.semiont-annotation-entry__body');
|
|
@@ -488,7 +496,7 @@ describe('CommentEntry Component', () => {
|
|
|
488
496
|
describe('Edge Cases', () => {
|
|
489
497
|
it('should handle empty comment text', () => {
|
|
490
498
|
mockGetCommentText.mockReturnValue('');
|
|
491
|
-
const { container } =
|
|
499
|
+
const { container } = renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
492
500
|
|
|
493
501
|
// Check that the annotation entry exists even with empty text
|
|
494
502
|
const commentDiv = container.querySelector('.semiont-annotation-entry');
|
|
@@ -499,7 +507,7 @@ describe('CommentEntry Component', () => {
|
|
|
499
507
|
mockGetAnnotationExactText.mockReturnValue('');
|
|
500
508
|
|
|
501
509
|
expect(() => {
|
|
502
|
-
|
|
510
|
+
renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
503
511
|
}).not.toThrow();
|
|
504
512
|
});
|
|
505
513
|
|
|
@@ -508,7 +516,7 @@ describe('CommentEntry Component', () => {
|
|
|
508
516
|
const commentWithoutTimestamp = rest as Annotation;
|
|
509
517
|
|
|
510
518
|
expect(() => {
|
|
511
|
-
|
|
519
|
+
renderWithProviders(
|
|
512
520
|
<CommentEntry {...defaultProps} comment={commentWithoutTimestamp} />
|
|
513
521
|
);
|
|
514
522
|
}).not.toThrow();
|
|
@@ -518,7 +526,7 @@ describe('CommentEntry Component', () => {
|
|
|
518
526
|
const longText = 'This is a very long comment that exceeds the typical length and should test text wrapping and display handling. '.repeat(5);
|
|
519
527
|
mockGetCommentText.mockReturnValue(longText);
|
|
520
528
|
|
|
521
|
-
|
|
529
|
+
renderWithProviders(
|
|
522
530
|
<CommentEntry {...defaultProps} comment={mockCommentStates.withLongText} />
|
|
523
531
|
);
|
|
524
532
|
|
|
@@ -529,7 +537,7 @@ describe('CommentEntry Component', () => {
|
|
|
529
537
|
});
|
|
530
538
|
|
|
531
539
|
it('should handle rapid edit/cancel cycles', async () => {
|
|
532
|
-
|
|
540
|
+
renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
533
541
|
|
|
534
542
|
for (let i = 0; i < 3; i++) {
|
|
535
543
|
await userEvent.click(screen.getByText('Edit'));
|
|
@@ -543,7 +551,7 @@ describe('CommentEntry Component', () => {
|
|
|
543
551
|
|
|
544
552
|
describe('Accessibility', () => {
|
|
545
553
|
it('should have proper ARIA attributes for textarea', async () => {
|
|
546
|
-
|
|
554
|
+
renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
547
555
|
|
|
548
556
|
await userEvent.click(screen.getByText('Edit'));
|
|
549
557
|
const textarea = screen.getByRole('textbox');
|
|
@@ -552,7 +560,7 @@ describe('CommentEntry Component', () => {
|
|
|
552
560
|
});
|
|
553
561
|
|
|
554
562
|
it('should be keyboard navigable', async () => {
|
|
555
|
-
|
|
563
|
+
renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
556
564
|
|
|
557
565
|
const editButton = screen.getByText('Edit');
|
|
558
566
|
editButton.focus();
|
|
@@ -561,7 +569,7 @@ describe('CommentEntry Component', () => {
|
|
|
561
569
|
});
|
|
562
570
|
|
|
563
571
|
it('should support keyboard interaction for save/cancel', async () => {
|
|
564
|
-
|
|
572
|
+
renderWithProviders(<CommentEntry {...defaultProps} />);
|
|
565
573
|
|
|
566
574
|
await userEvent.click(screen.getByText('Edit'));
|
|
567
575
|
|