@semiont/react-ui 0.2.33-build.79 → 0.2.33-build.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/EventBusContext-CJjL_cCf.d.mts +462 -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-QB52Q7EQ.mjs} +206 -146
- package/dist/chunk-QB52Q7EQ.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 +715 -574
- package/dist/index.mjs +3898 -3575
- 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/ResizeHandle.tsx +10 -4
- package/src/components/SessionExpiryBanner.tsx +2 -3
- package/src/components/SessionTimer.tsx +3 -3
- package/src/components/Toolbar.tsx +18 -9
- package/src/components/__tests__/SessionTimer.test.tsx +33 -33
- 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 +49 -15
- 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 +118 -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 +234 -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 +503 -0
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +139 -93
- package/src/features/resource-viewer/__tests__/detection-progress-flow.test.tsx +322 -0
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +341 -524
- package/translations/ar.json +6 -3
- package/translations/bn.json +6 -3
- package/translations/cs.json +6 -3
- package/translations/da.json +6 -3
- package/translations/de.json +6 -3
- package/translations/el.json +6 -3
- package/translations/en.json +6 -3
- package/translations/es.json +6 -3
- package/translations/fa.json +6 -3
- package/translations/fi.json +6 -3
- package/translations/fr.json +6 -3
- package/translations/he.json +6 -3
- package/translations/hi.json +6 -3
- package/translations/id.json +6 -3
- package/translations/it.json +6 -3
- package/translations/ja.json +6 -3
- package/translations/ko.json +6 -3
- package/translations/ms.json +6 -3
- package/translations/nl.json +6 -3
- package/translations/no.json +6 -3
- package/translations/pl.json +6 -3
- package/translations/pt.json +6 -3
- package/translations/ro.json +6 -3
- package/translations/sv.json +6 -3
- package/translations/th.json +6 -3
- package/translations/tr.json +6 -3
- package/translations/uk.json +6 -3
- package/translations/vi.json +6 -3
- package/translations/zh.json +6 -3
- package/dist/PdfAnnotationCanvas.client-ADC4FFSE.mjs.map +0 -1
- package/dist/TranslationManager-Co_5fSxl.d.mts +0 -118
- package/dist/ar-EMHEHPCJ.mjs.map +0 -1
- package/dist/bn-OVCI4F6X.mjs.map +0 -1
- package/dist/chunk-JZIO2A3B.mjs.map +0 -1
- package/dist/chunk-LIHZTECW.mjs.map +0 -1
- package/dist/cs-FAN66Q2F.mjs.map +0 -1
- package/dist/da-YBBIHI2O.mjs.map +0 -1
- package/dist/de-MAYU33LB.mjs.map +0 -1
- package/dist/el-MKGSWN4O.mjs.map +0 -1
- package/dist/es-52LHUWJD.mjs.map +0 -1
- package/dist/fa-FJICRANB.mjs.map +0 -1
- package/dist/fi-O455XFCR.mjs.map +0 -1
- package/dist/fr-TXIXHOOE.mjs.map +0 -1
- package/dist/he-JBSOX5IN.mjs.map +0 -1
- package/dist/hi-KGHI3XVT.mjs.map +0 -1
- package/dist/id-5OCPPZLO.mjs.map +0 -1
- package/dist/it-PNBBZSM2.mjs.map +0 -1
- package/dist/ja-LDD7R3TJ.mjs.map +0 -1
- package/dist/ko-F47ZDEY3.mjs.map +0 -1
- package/dist/ms-Z7LMXJWL.mjs.map +0 -1
- package/dist/nl-6SJFBPJ3.mjs.map +0 -1
- package/dist/no-YXPBPSGF.mjs.map +0 -1
- package/dist/pl-P4AZ2QME.mjs.map +0 -1
- package/dist/pt-LHWUS6U6.mjs.map +0 -1
- package/dist/ro-EA5J2ZON.mjs.map +0 -1
- package/dist/sv-DATBS3UQ.mjs.map +0 -1
- package/dist/th-WTFJRWPT.mjs.map +0 -1
- package/dist/tr-IKO3RXOX.mjs.map +0 -1
- package/dist/uk-CF6CTTRK.mjs.map +0 -1
- package/dist/vi-AJLTXPZQ.mjs.map +0 -1
- package/dist/zh-U3ORHHYH.mjs.map +0 -1
- /package/dist/{en-DDLIXJCU.mjs.map → en-KJCJQ4OO.mjs.map} +0 -0
|
@@ -1,22 +1,77 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
2
|
import type { MockedFunction } from 'vitest';
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
4
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
5
5
|
import userEvent from '@testing-library/user-event';
|
|
6
6
|
import '@testing-library/jest-dom';
|
|
7
7
|
import { TaggingPanel } from '../TaggingPanel';
|
|
8
|
+
import { EventBusProvider, resetEventBusForTesting, useEventBus } from '../../../../contexts/EventBusContext';
|
|
8
9
|
import type { components } from '@semiont/api-client';
|
|
9
10
|
|
|
10
11
|
type Annotation = components['schemas']['Annotation'];
|
|
11
12
|
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
// Composition-based event tracker
|
|
14
|
+
interface TrackedEvent {
|
|
15
|
+
event: string;
|
|
16
|
+
payload: any;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function createEventTracker() {
|
|
20
|
+
const events: TrackedEvent[] = [];
|
|
21
|
+
|
|
22
|
+
function EventTrackingWrapper({ children }: { children: React.ReactNode }) {
|
|
23
|
+
const eventBus = useEventBus();
|
|
24
|
+
|
|
25
|
+
React.useEffect(() => {
|
|
26
|
+
const handlers: Array<() => void> = [];
|
|
27
|
+
|
|
28
|
+
const trackEvent = (eventName: string) => (payload: any) => {
|
|
29
|
+
events.push({ event: eventName, payload });
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const panelEvents = ['annotation:create', 'detection:start'];
|
|
33
|
+
|
|
34
|
+
panelEvents.forEach(eventName => {
|
|
35
|
+
const handler = trackEvent(eventName);
|
|
36
|
+
eventBus.on(eventName, handler);
|
|
37
|
+
handlers.push(() => eventBus.off(eventName, handler));
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return () => {
|
|
41
|
+
handlers.forEach(cleanup => cleanup());
|
|
42
|
+
};
|
|
43
|
+
}, [eventBus]);
|
|
44
|
+
|
|
45
|
+
return <>{children}</>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
EventTrackingWrapper,
|
|
50
|
+
events,
|
|
51
|
+
clear: () => {
|
|
52
|
+
events.length = 0;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Helper to render with EventBusProvider
|
|
58
|
+
const renderWithEventBus = (component: React.ReactElement, tracker?: ReturnType<typeof createEventTracker>) => {
|
|
59
|
+
if (tracker) {
|
|
60
|
+
return render(
|
|
61
|
+
<EventBusProvider>
|
|
62
|
+
<tracker.EventTrackingWrapper>
|
|
63
|
+
{component}
|
|
64
|
+
</tracker.EventTrackingWrapper>
|
|
65
|
+
</EventBusProvider>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return render(
|
|
70
|
+
<EventBusProvider>
|
|
71
|
+
{component}
|
|
72
|
+
</EventBusProvider>
|
|
73
|
+
);
|
|
74
|
+
};
|
|
20
75
|
|
|
21
76
|
// Mock TranslationContext
|
|
22
77
|
vi.mock('../../../../contexts/TranslationContext', () => ({
|
|
@@ -50,6 +105,7 @@ vi.mock('../../../../contexts/TranslationContext', () => ({
|
|
|
50
105
|
}
|
|
51
106
|
return result;
|
|
52
107
|
}),
|
|
108
|
+
TranslationProvider: ({ children }: { children: React.ReactNode }) => children,
|
|
53
109
|
}));
|
|
54
110
|
|
|
55
111
|
// Mock @semiont/api-client utilities
|
|
@@ -64,17 +120,8 @@ vi.mock('@semiont/api-client', async () => {
|
|
|
64
120
|
|
|
65
121
|
// Mock TagEntry component to simplify testing
|
|
66
122
|
vi.mock('../TagEntry', () => ({
|
|
67
|
-
TagEntry: ({ tag,
|
|
68
|
-
<div
|
|
69
|
-
data-testid={`tag-${tag.id}`}
|
|
70
|
-
onClick={() => onClick()}
|
|
71
|
-
>
|
|
72
|
-
<button
|
|
73
|
-
onMouseEnter={() => onTagHover?.(tag.id)}
|
|
74
|
-
onMouseLeave={() => onTagHover?.(null)}
|
|
75
|
-
>
|
|
76
|
-
Hover
|
|
77
|
-
</button>
|
|
123
|
+
TagEntry: ({ tag, onTagRef }: any) => (
|
|
124
|
+
<div data-testid={`tag-${tag.id}`}>
|
|
78
125
|
<div>{tag.id}</div>
|
|
79
126
|
</div>
|
|
80
127
|
),
|
|
@@ -152,13 +199,11 @@ const createPendingAnnotation = (exact: string) => ({
|
|
|
152
199
|
describe('TaggingPanel Component', () => {
|
|
153
200
|
const defaultProps = {
|
|
154
201
|
annotations: mockTags.empty,
|
|
155
|
-
onAnnotationClick: vi.fn(),
|
|
156
|
-
onCreate: vi.fn(),
|
|
157
|
-
focusedAnnotationId: null,
|
|
158
202
|
pendingAnnotation: null,
|
|
159
203
|
};
|
|
160
204
|
|
|
161
205
|
beforeEach(() => {
|
|
206
|
+
resetEventBusForTesting();
|
|
162
207
|
vi.clearAllMocks();
|
|
163
208
|
|
|
164
209
|
// Mock scrollIntoView for jsdom
|
|
@@ -184,20 +229,21 @@ describe('TaggingPanel Component', () => {
|
|
|
184
229
|
|
|
185
230
|
describe('Rendering', () => {
|
|
186
231
|
it('should render panel header with title and count', () => {
|
|
187
|
-
|
|
232
|
+
renderWithEventBus(<TaggingPanel {...defaultProps} annotations={mockTags.multiple} />);
|
|
188
233
|
|
|
189
|
-
|
|
234
|
+
const headings = screen.getAllByText(/Tags/);
|
|
235
|
+
expect(headings.length).toBeGreaterThan(0);
|
|
190
236
|
expect(screen.getByText(/\(3\)/)).toBeInTheDocument();
|
|
191
237
|
});
|
|
192
238
|
|
|
193
239
|
it('should show empty state when no tags', () => {
|
|
194
|
-
|
|
240
|
+
renderWithEventBus(<TaggingPanel {...defaultProps} />);
|
|
195
241
|
|
|
196
242
|
expect(screen.getByText(/No tags yet/)).toBeInTheDocument();
|
|
197
243
|
});
|
|
198
244
|
|
|
199
245
|
it('should render all tags', () => {
|
|
200
|
-
|
|
246
|
+
renderWithEventBus(<TaggingPanel {...defaultProps} annotations={mockTags.multiple} />);
|
|
201
247
|
|
|
202
248
|
expect(screen.getByTestId('tag-1')).toBeInTheDocument();
|
|
203
249
|
expect(screen.getByTestId('tag-2')).toBeInTheDocument();
|
|
@@ -205,7 +251,7 @@ describe('TaggingPanel Component', () => {
|
|
|
205
251
|
});
|
|
206
252
|
|
|
207
253
|
it('should have proper panel structure', () => {
|
|
208
|
-
const { container } =
|
|
254
|
+
const { container } = renderWithEventBus(<TaggingPanel {...defaultProps} />);
|
|
209
255
|
|
|
210
256
|
const panel = container.firstChild as HTMLElement;
|
|
211
257
|
expect(panel).toHaveClass('semiont-panel');
|
|
@@ -214,7 +260,7 @@ describe('TaggingPanel Component', () => {
|
|
|
214
260
|
|
|
215
261
|
describe('Tag Sorting', () => {
|
|
216
262
|
it('should sort tags by position in resource', () => {
|
|
217
|
-
|
|
263
|
+
renderWithEventBus(<TaggingPanel {...defaultProps} annotations={mockTags.multiple} />);
|
|
218
264
|
|
|
219
265
|
const tags = screen.getAllByTestId(/tag-/);
|
|
220
266
|
|
|
@@ -228,14 +274,14 @@ describe('TaggingPanel Component', () => {
|
|
|
228
274
|
mockGetTextPositionSelector.mockReturnValue(null);
|
|
229
275
|
|
|
230
276
|
expect(() => {
|
|
231
|
-
|
|
277
|
+
renderWithEventBus(<TaggingPanel {...defaultProps} annotations={mockTags.multiple} />);
|
|
232
278
|
}).not.toThrow();
|
|
233
279
|
});
|
|
234
280
|
});
|
|
235
281
|
|
|
236
282
|
describe('Manual Tag Creation', () => {
|
|
237
283
|
it('should not show tag creation form by default', () => {
|
|
238
|
-
|
|
284
|
+
renderWithEventBus(<TaggingPanel {...defaultProps} />);
|
|
239
285
|
|
|
240
286
|
expect(screen.queryByText(/Create tag for selection/)).not.toBeInTheDocument();
|
|
241
287
|
});
|
|
@@ -243,7 +289,7 @@ describe('TaggingPanel Component', () => {
|
|
|
243
289
|
it('should show tag creation form when pendingAnnotation exists', () => {
|
|
244
290
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
245
291
|
|
|
246
|
-
|
|
292
|
+
renderWithEventBus(
|
|
247
293
|
<TaggingPanel
|
|
248
294
|
{...defaultProps}
|
|
249
295
|
pendingAnnotation={pendingAnnotation}
|
|
@@ -256,7 +302,7 @@ describe('TaggingPanel Component', () => {
|
|
|
256
302
|
it('should display quoted selected text in tag creation form', () => {
|
|
257
303
|
const pendingAnnotation = createPendingAnnotation('Selected text for tagging');
|
|
258
304
|
|
|
259
|
-
|
|
305
|
+
renderWithEventBus(
|
|
260
306
|
<TaggingPanel
|
|
261
307
|
{...defaultProps}
|
|
262
308
|
pendingAnnotation={pendingAnnotation}
|
|
@@ -270,7 +316,7 @@ describe('TaggingPanel Component', () => {
|
|
|
270
316
|
const longText = 'A'.repeat(150);
|
|
271
317
|
const pendingAnnotation = createPendingAnnotation(longText);
|
|
272
318
|
|
|
273
|
-
|
|
319
|
+
renderWithEventBus(
|
|
274
320
|
<TaggingPanel
|
|
275
321
|
{...defaultProps}
|
|
276
322
|
pendingAnnotation={pendingAnnotation}
|
|
@@ -284,7 +330,7 @@ describe('TaggingPanel Component', () => {
|
|
|
284
330
|
it('should show schema selector in tag creation form', () => {
|
|
285
331
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
286
332
|
|
|
287
|
-
|
|
333
|
+
renderWithEventBus(
|
|
288
334
|
<TaggingPanel
|
|
289
335
|
{...defaultProps}
|
|
290
336
|
pendingAnnotation={pendingAnnotation}
|
|
@@ -298,7 +344,7 @@ describe('TaggingPanel Component', () => {
|
|
|
298
344
|
it('should show category selector in tag creation form', () => {
|
|
299
345
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
300
346
|
|
|
301
|
-
|
|
347
|
+
renderWithEventBus(
|
|
302
348
|
<TaggingPanel
|
|
303
349
|
{...defaultProps}
|
|
304
350
|
pendingAnnotation={pendingAnnotation}
|
|
@@ -308,16 +354,16 @@ describe('TaggingPanel Component', () => {
|
|
|
308
354
|
expect(screen.getByText(/Select category/)).toBeInTheDocument();
|
|
309
355
|
});
|
|
310
356
|
|
|
311
|
-
it('should
|
|
312
|
-
const
|
|
357
|
+
it('should emit annotation:create event when category is selected', async () => {
|
|
358
|
+
const tracker = createEventTracker();
|
|
313
359
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
314
360
|
|
|
315
|
-
|
|
361
|
+
renderWithEventBus(
|
|
316
362
|
<TaggingPanel
|
|
317
363
|
{...defaultProps}
|
|
318
364
|
pendingAnnotation={pendingAnnotation}
|
|
319
|
-
|
|
320
|
-
|
|
365
|
+
/>,
|
|
366
|
+
tracker
|
|
321
367
|
);
|
|
322
368
|
|
|
323
369
|
// Find the category selector (the one in the pending annotation form)
|
|
@@ -330,13 +376,20 @@ describe('TaggingPanel Component', () => {
|
|
|
330
376
|
|
|
331
377
|
await userEvent.selectOptions(categorySelect!, 'Issue');
|
|
332
378
|
|
|
333
|
-
|
|
379
|
+
await waitFor(() => {
|
|
380
|
+
expect(tracker.events.some(e =>
|
|
381
|
+
e.event === 'annotation:create' &&
|
|
382
|
+
e.payload?.motivation === 'tagging' &&
|
|
383
|
+
e.payload?.body?.[0]?.value === 'Issue' &&
|
|
384
|
+
e.payload?.body?.[0]?.type === 'TextualBody'
|
|
385
|
+
)).toBe(true);
|
|
386
|
+
});
|
|
334
387
|
});
|
|
335
388
|
|
|
336
389
|
it('should have proper styling for tag creation form', () => {
|
|
337
390
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
338
391
|
|
|
339
|
-
const { container } =
|
|
392
|
+
const { container } = renderWithEventBus(
|
|
340
393
|
<TaggingPanel
|
|
341
394
|
{...defaultProps}
|
|
342
395
|
pendingAnnotation={pendingAnnotation}
|
|
@@ -350,43 +403,23 @@ describe('TaggingPanel Component', () => {
|
|
|
350
403
|
});
|
|
351
404
|
|
|
352
405
|
describe('Tag Interactions', () => {
|
|
353
|
-
it('should
|
|
354
|
-
|
|
355
|
-
render(
|
|
406
|
+
it('should render tag entries', () => {
|
|
407
|
+
renderWithEventBus(
|
|
356
408
|
<TaggingPanel
|
|
357
409
|
{...defaultProps}
|
|
358
410
|
annotations={mockTags.single}
|
|
359
|
-
onAnnotationClick={onAnnotationClick}
|
|
360
411
|
/>
|
|
361
412
|
);
|
|
362
413
|
|
|
363
414
|
const tag = screen.getByTestId('tag-1');
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
expect(onAnnotationClick).toHaveBeenCalledWith(mockTags.single[0]);
|
|
415
|
+
expect(tag).toBeInTheDocument();
|
|
367
416
|
});
|
|
368
417
|
});
|
|
369
418
|
|
|
370
419
|
describe('Tag Hover Behavior', () => {
|
|
371
|
-
it('should
|
|
372
|
-
const onAnnotationHover = vi.fn();
|
|
373
|
-
render(
|
|
374
|
-
<TaggingPanel
|
|
375
|
-
{...defaultProps}
|
|
376
|
-
annotations={mockTags.single}
|
|
377
|
-
onAnnotationHover={onAnnotationHover}
|
|
378
|
-
/>
|
|
379
|
-
);
|
|
380
|
-
|
|
381
|
-
const hoverButton = screen.getByText('Hover');
|
|
382
|
-
fireEvent.mouseEnter(hoverButton);
|
|
383
|
-
|
|
384
|
-
expect(onAnnotationHover).toHaveBeenCalledWith('1');
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
it('should not error when onAnnotationHover is not provided', () => {
|
|
420
|
+
it('should render without errors', () => {
|
|
388
421
|
expect(() => {
|
|
389
|
-
|
|
422
|
+
renderWithEventBus(
|
|
390
423
|
<TaggingPanel
|
|
391
424
|
{...defaultProps}
|
|
392
425
|
annotations={mockTags.single}
|
|
@@ -397,11 +430,10 @@ describe('TaggingPanel Component', () => {
|
|
|
397
430
|
});
|
|
398
431
|
|
|
399
432
|
describe('Detection Section', () => {
|
|
400
|
-
it('should render detection section when
|
|
401
|
-
|
|
433
|
+
it('should render detection section when annotateMode is true', () => {
|
|
434
|
+
renderWithEventBus(
|
|
402
435
|
<TaggingPanel
|
|
403
436
|
{...defaultProps}
|
|
404
|
-
onDetect={vi.fn()}
|
|
405
437
|
annotateMode={true}
|
|
406
438
|
/>
|
|
407
439
|
);
|
|
@@ -409,22 +441,10 @@ describe('TaggingPanel Component', () => {
|
|
|
409
441
|
expect(screen.getByText(/Detect Tags/)).toBeInTheDocument();
|
|
410
442
|
});
|
|
411
443
|
|
|
412
|
-
it('should not render detection section when onDetect is not provided', () => {
|
|
413
|
-
render(
|
|
414
|
-
<TaggingPanel
|
|
415
|
-
{...defaultProps}
|
|
416
|
-
annotateMode={true}
|
|
417
|
-
/>
|
|
418
|
-
);
|
|
419
|
-
|
|
420
|
-
expect(screen.queryByText(/Detect Tags/)).not.toBeInTheDocument();
|
|
421
|
-
});
|
|
422
|
-
|
|
423
444
|
it('should not render detection section when annotateMode is false', () => {
|
|
424
|
-
|
|
445
|
+
renderWithEventBus(
|
|
425
446
|
<TaggingPanel
|
|
426
447
|
{...defaultProps}
|
|
427
|
-
onDetect={vi.fn()}
|
|
428
448
|
annotateMode={false}
|
|
429
449
|
/>
|
|
430
450
|
);
|
|
@@ -433,10 +453,9 @@ describe('TaggingPanel Component', () => {
|
|
|
433
453
|
});
|
|
434
454
|
|
|
435
455
|
it('should show schema selector in detection section', () => {
|
|
436
|
-
|
|
456
|
+
renderWithEventBus(
|
|
437
457
|
<TaggingPanel
|
|
438
458
|
{...defaultProps}
|
|
439
|
-
onDetect={vi.fn()}
|
|
440
459
|
annotateMode={true}
|
|
441
460
|
/>
|
|
442
461
|
);
|
|
@@ -446,10 +465,9 @@ describe('TaggingPanel Component', () => {
|
|
|
446
465
|
});
|
|
447
466
|
|
|
448
467
|
it('should show Select All and Deselect All buttons', () => {
|
|
449
|
-
|
|
468
|
+
renderWithEventBus(
|
|
450
469
|
<TaggingPanel
|
|
451
470
|
{...defaultProps}
|
|
452
|
-
onDetect={vi.fn()}
|
|
453
471
|
annotateMode={true}
|
|
454
472
|
/>
|
|
455
473
|
);
|
|
@@ -459,10 +477,9 @@ describe('TaggingPanel Component', () => {
|
|
|
459
477
|
});
|
|
460
478
|
|
|
461
479
|
it('should show category checkboxes', () => {
|
|
462
|
-
|
|
480
|
+
renderWithEventBus(
|
|
463
481
|
<TaggingPanel
|
|
464
482
|
{...defaultProps}
|
|
465
|
-
onDetect={vi.fn()}
|
|
466
483
|
annotateMode={true}
|
|
467
484
|
/>
|
|
468
485
|
);
|
|
@@ -474,10 +491,9 @@ describe('TaggingPanel Component', () => {
|
|
|
474
491
|
});
|
|
475
492
|
|
|
476
493
|
it('should disable detect button when no categories selected', () => {
|
|
477
|
-
|
|
494
|
+
renderWithEventBus(
|
|
478
495
|
<TaggingPanel
|
|
479
496
|
{...defaultProps}
|
|
480
|
-
onDetect={vi.fn()}
|
|
481
497
|
annotateMode={true}
|
|
482
498
|
/>
|
|
483
499
|
);
|
|
@@ -487,10 +503,9 @@ describe('TaggingPanel Component', () => {
|
|
|
487
503
|
});
|
|
488
504
|
|
|
489
505
|
it('should enable detect button when categories are selected', async () => {
|
|
490
|
-
|
|
506
|
+
renderWithEventBus(
|
|
491
507
|
<TaggingPanel
|
|
492
508
|
{...defaultProps}
|
|
493
|
-
onDetect={vi.fn()}
|
|
494
509
|
annotateMode={true}
|
|
495
510
|
/>
|
|
496
511
|
);
|
|
@@ -502,14 +517,14 @@ describe('TaggingPanel Component', () => {
|
|
|
502
517
|
expect(detectButton).not.toBeDisabled();
|
|
503
518
|
});
|
|
504
519
|
|
|
505
|
-
it('should
|
|
506
|
-
const
|
|
507
|
-
|
|
520
|
+
it('should emit detection:start event with selected schema and categories', async () => {
|
|
521
|
+
const tracker = createEventTracker();
|
|
522
|
+
renderWithEventBus(
|
|
508
523
|
<TaggingPanel
|
|
509
524
|
{...defaultProps}
|
|
510
|
-
onDetect={onDetect}
|
|
511
525
|
annotateMode={true}
|
|
512
|
-
|
|
526
|
+
/>,
|
|
527
|
+
tracker
|
|
513
528
|
);
|
|
514
529
|
|
|
515
530
|
const issueCheckbox = screen.getByLabelText(/Issue/);
|
|
@@ -521,7 +536,15 @@ describe('TaggingPanel Component', () => {
|
|
|
521
536
|
const detectButton = screen.getByRole('button', { name: /✨ Detect/i });
|
|
522
537
|
await userEvent.click(detectButton);
|
|
523
538
|
|
|
524
|
-
|
|
539
|
+
await waitFor(() => {
|
|
540
|
+
expect(tracker.events.some(e =>
|
|
541
|
+
e.event === 'detection:start' &&
|
|
542
|
+
e.payload?.motivation === 'tagging' &&
|
|
543
|
+
e.payload?.options?.schemaId === 'legal-irac' &&
|
|
544
|
+
e.payload?.options?.categories?.includes('Issue') &&
|
|
545
|
+
e.payload?.options?.categories?.includes('Rule')
|
|
546
|
+
)).toBe(true);
|
|
547
|
+
});
|
|
525
548
|
});
|
|
526
549
|
});
|
|
527
550
|
|
|
@@ -529,7 +552,7 @@ describe('TaggingPanel Component', () => {
|
|
|
529
552
|
it('should show Cancel button when pendingAnnotation exists', () => {
|
|
530
553
|
const pendingAnnotation = createPendingAnnotation('Selected text');
|
|
531
554
|
|
|
532
|
-
|
|
555
|
+
renderWithEventBus(
|
|
533
556
|
<TaggingPanel
|
|
534
557
|
{...defaultProps}
|
|
535
558
|
pendingAnnotation={pendingAnnotation}
|
|
@@ -542,17 +565,16 @@ describe('TaggingPanel Component', () => {
|
|
|
542
565
|
|
|
543
566
|
describe('Accessibility', () => {
|
|
544
567
|
it('should have proper heading structure', () => {
|
|
545
|
-
|
|
568
|
+
renderWithEventBus(<TaggingPanel {...defaultProps} />);
|
|
546
569
|
|
|
547
|
-
const
|
|
548
|
-
expect(
|
|
570
|
+
const headings = screen.getAllByText(/Tags/);
|
|
571
|
+
expect(headings[0]).toHaveClass('semiont-panel-header__text');
|
|
549
572
|
});
|
|
550
573
|
|
|
551
574
|
it('should have proper checkbox labels', () => {
|
|
552
|
-
|
|
575
|
+
renderWithEventBus(
|
|
553
576
|
<TaggingPanel
|
|
554
577
|
{...defaultProps}
|
|
555
|
-
onDetect={vi.fn()}
|
|
556
578
|
annotateMode={true}
|
|
557
579
|
/>
|
|
558
580
|
);
|
|
@@ -4,28 +4,31 @@ import React, { useEffect } from 'react';
|
|
|
4
4
|
import { LOCALES } from '@semiont/api-client';
|
|
5
5
|
import { useTranslations } from '../../contexts/TranslationContext';
|
|
6
6
|
import { useLanguageChangeAnnouncements } from '../LiveRegion';
|
|
7
|
+
import { useEventBus } from '../../contexts/EventBusContext';
|
|
7
8
|
import './SettingsPanel.css';
|
|
8
9
|
|
|
9
10
|
interface SettingsPanelProps {
|
|
10
11
|
showLineNumbers: boolean;
|
|
11
|
-
onLineNumbersToggle: () => void;
|
|
12
12
|
theme: 'light' | 'dark' | 'system';
|
|
13
|
-
onThemeChange: (theme: 'light' | 'dark' | 'system') => void;
|
|
14
13
|
locale: string;
|
|
15
|
-
onLocaleChange: (locale: string) => void;
|
|
16
14
|
isPendingLocaleChange?: boolean;
|
|
17
15
|
}
|
|
18
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Settings panel for application preferences
|
|
19
|
+
*
|
|
20
|
+
* @emits settings:locale-changed - Locale changed by user. Payload: { locale: string }
|
|
21
|
+
* @emits settings:line-numbers-toggled - Line numbers toggled on/off. Payload: undefined
|
|
22
|
+
* @emits settings:theme-changed - Theme changed by user. Payload: { theme: 'light' | 'dark' | 'system' }
|
|
23
|
+
*/
|
|
19
24
|
export function SettingsPanel({
|
|
20
25
|
showLineNumbers,
|
|
21
|
-
onLineNumbersToggle,
|
|
22
26
|
theme,
|
|
23
|
-
onThemeChange,
|
|
24
27
|
locale,
|
|
25
|
-
onLocaleChange,
|
|
26
28
|
isPendingLocaleChange = false
|
|
27
29
|
}: SettingsPanelProps) {
|
|
28
30
|
const t = useTranslations('Settings');
|
|
31
|
+
const eventBus = useEventBus();
|
|
29
32
|
const { announceLanguageChanging, announceLanguageChanged } = useLanguageChangeAnnouncements();
|
|
30
33
|
|
|
31
34
|
// Track previous locale to detect changes
|
|
@@ -35,7 +38,7 @@ export function SettingsPanel({
|
|
|
35
38
|
const handleLocaleChange = (newLocale: string) => {
|
|
36
39
|
const localeName = LOCALES.find(l => l.code === newLocale)?.nativeName || newLocale;
|
|
37
40
|
announceLanguageChanging(localeName);
|
|
38
|
-
|
|
41
|
+
eventBus.emit('settings:locale-changed', { locale: newLocale });
|
|
39
42
|
};
|
|
40
43
|
|
|
41
44
|
// Announce when language has successfully changed
|
|
@@ -45,7 +48,7 @@ export function SettingsPanel({
|
|
|
45
48
|
announceLanguageChanged(localeName);
|
|
46
49
|
setPreviousLocale(locale);
|
|
47
50
|
}
|
|
48
|
-
}, [locale, previousLocale, isPendingLocaleChange
|
|
51
|
+
}, [locale, previousLocale, isPendingLocaleChange]);
|
|
49
52
|
|
|
50
53
|
return (
|
|
51
54
|
<div className="semiont-settings-panel">
|
|
@@ -64,7 +67,7 @@ export function SettingsPanel({
|
|
|
64
67
|
type="button"
|
|
65
68
|
role="switch"
|
|
66
69
|
aria-checked={showLineNumbers}
|
|
67
|
-
onClick={
|
|
70
|
+
onClick={() => eventBus.emit('settings:line-numbers-toggled', undefined)}
|
|
68
71
|
className={`semiont-toggle ${
|
|
69
72
|
showLineNumbers ? 'semiont-toggle--active' : ''
|
|
70
73
|
}`}
|
|
@@ -88,7 +91,7 @@ export function SettingsPanel({
|
|
|
88
91
|
</label>
|
|
89
92
|
<div className="semiont-settings-panel__button-group">
|
|
90
93
|
<button
|
|
91
|
-
onClick={() =>
|
|
94
|
+
onClick={() => eventBus.emit('settings:theme-changed', { theme: 'light' })}
|
|
92
95
|
className={`semiont-panel-button ${
|
|
93
96
|
theme === 'light' ? 'semiont-panel-button-active' : ''
|
|
94
97
|
}`}
|
|
@@ -97,7 +100,7 @@ export function SettingsPanel({
|
|
|
97
100
|
☀️ {t('themeLight')}
|
|
98
101
|
</button>
|
|
99
102
|
<button
|
|
100
|
-
onClick={() =>
|
|
103
|
+
onClick={() => eventBus.emit('settings:theme-changed', { theme: 'dark' })}
|
|
101
104
|
className={`semiont-panel-button ${
|
|
102
105
|
theme === 'dark' ? 'semiont-panel-button-active' : ''
|
|
103
106
|
}`}
|
|
@@ -106,7 +109,7 @@ export function SettingsPanel({
|
|
|
106
109
|
🌙 {t('themeDark')}
|
|
107
110
|
</button>
|
|
108
111
|
<button
|
|
109
|
-
onClick={() =>
|
|
112
|
+
onClick={() => eventBus.emit('settings:theme-changed', { theme: 'system' })}
|
|
110
113
|
className={`semiont-panel-button ${
|
|
111
114
|
theme === 'system' ? 'semiont-panel-button-active' : ''
|
|
112
115
|
}`}
|
|
@@ -34,11 +34,8 @@ const createMockProps = (overrides?: Partial<AdminDevOpsPageProps>): AdminDevOps
|
|
|
34
34
|
},
|
|
35
35
|
],
|
|
36
36
|
theme: 'light',
|
|
37
|
-
onThemeChange: vi.fn(),
|
|
38
37
|
showLineNumbers: false,
|
|
39
|
-
onLineNumbersToggle: vi.fn(),
|
|
40
38
|
activePanel: null,
|
|
41
|
-
onPanelToggle: vi.fn(),
|
|
42
39
|
translations: {
|
|
43
40
|
title: 'DevOps',
|
|
44
41
|
subtitle: 'System monitoring and management',
|
|
@@ -303,30 +300,6 @@ describe('AdminDevOpsPage', () => {
|
|
|
303
300
|
);
|
|
304
301
|
});
|
|
305
302
|
|
|
306
|
-
it('passes onThemeChange to toolbar panels', () => {
|
|
307
|
-
const onThemeChange = vi.fn();
|
|
308
|
-
const ToolbarPanels = vi.fn(() => <div data-testid="toolbar-panels" />);
|
|
309
|
-
const props = createMockProps({ onThemeChange, ToolbarPanels });
|
|
310
|
-
render(<AdminDevOpsPage {...props} />);
|
|
311
|
-
|
|
312
|
-
expect(ToolbarPanels).toHaveBeenCalledWith(
|
|
313
|
-
expect.objectContaining({ onThemeChange }),
|
|
314
|
-
expect.anything()
|
|
315
|
-
);
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
it('passes onLineNumbersToggle to toolbar panels', () => {
|
|
319
|
-
const onLineNumbersToggle = vi.fn();
|
|
320
|
-
const ToolbarPanels = vi.fn(() => <div data-testid="toolbar-panels" />);
|
|
321
|
-
const props = createMockProps({ onLineNumbersToggle, ToolbarPanels });
|
|
322
|
-
render(<AdminDevOpsPage {...props} />);
|
|
323
|
-
|
|
324
|
-
expect(ToolbarPanels).toHaveBeenCalledWith(
|
|
325
|
-
expect.objectContaining({ onLineNumbersToggle }),
|
|
326
|
-
expect.anything()
|
|
327
|
-
);
|
|
328
|
-
});
|
|
329
|
-
|
|
330
303
|
it('passes activePanel to toolbar panels', () => {
|
|
331
304
|
const ToolbarPanels = vi.fn(() => <div data-testid="toolbar-panels" />);
|
|
332
305
|
const props = createMockProps({ activePanel: 'settings', ToolbarPanels });
|
|
@@ -360,17 +333,6 @@ describe('AdminDevOpsPage', () => {
|
|
|
360
333
|
);
|
|
361
334
|
});
|
|
362
335
|
|
|
363
|
-
it('passes onPanelToggle to toolbar', () => {
|
|
364
|
-
const onPanelToggle = vi.fn();
|
|
365
|
-
const Toolbar = vi.fn(() => <div data-testid="toolbar" />);
|
|
366
|
-
const props = createMockProps({ onPanelToggle, Toolbar });
|
|
367
|
-
render(<AdminDevOpsPage {...props} />);
|
|
368
|
-
|
|
369
|
-
expect(Toolbar).toHaveBeenCalledWith(
|
|
370
|
-
expect.objectContaining({ onPanelToggle }),
|
|
371
|
-
expect.anything()
|
|
372
|
-
);
|
|
373
|
-
});
|
|
374
336
|
});
|
|
375
337
|
|
|
376
338
|
describe('Layout and Structure', () => {
|
|
@@ -430,14 +392,7 @@ describe('AdminDevOpsPage', () => {
|
|
|
430
392
|
});
|
|
431
393
|
|
|
432
394
|
it('handles all callbacks being defined', () => {
|
|
433
|
-
const
|
|
434
|
-
const onLineNumbersToggle = vi.fn();
|
|
435
|
-
const onPanelToggle = vi.fn();
|
|
436
|
-
const props = createMockProps({
|
|
437
|
-
onThemeChange,
|
|
438
|
-
onLineNumbersToggle,
|
|
439
|
-
onPanelToggle,
|
|
440
|
-
});
|
|
395
|
+
const props = createMockProps();
|
|
441
396
|
render(<AdminDevOpsPage {...props} />);
|
|
442
397
|
|
|
443
398
|
expect(screen.getByText('DevOps')).toBeInTheDocument();
|
|
@@ -23,11 +23,8 @@ export interface AdminDevOpsPageProps {
|
|
|
23
23
|
|
|
24
24
|
// UI state
|
|
25
25
|
theme: 'light' | 'dark' | 'system';
|
|
26
|
-
onThemeChange: (theme: 'light' | 'dark' | 'system') => void;
|
|
27
26
|
showLineNumbers: boolean;
|
|
28
|
-
onLineNumbersToggle: () => void;
|
|
29
27
|
activePanel: string | null;
|
|
30
|
-
onPanelToggle: (panel: string | null) => void;
|
|
31
28
|
|
|
32
29
|
// Translations
|
|
33
30
|
translations: {
|
|
@@ -49,11 +46,8 @@ export interface AdminDevOpsPageProps {
|
|
|
49
46
|
export function AdminDevOpsPage({
|
|
50
47
|
suggestedFeatures,
|
|
51
48
|
theme,
|
|
52
|
-
onThemeChange,
|
|
53
49
|
showLineNumbers,
|
|
54
|
-
onLineNumbersToggle,
|
|
55
50
|
activePanel,
|
|
56
|
-
onPanelToggle,
|
|
57
51
|
translations: t,
|
|
58
52
|
StatusDisplay,
|
|
59
53
|
ToolbarPanels,
|
|
@@ -138,15 +132,12 @@ export function AdminDevOpsPage({
|
|
|
138
132
|
<ToolbarPanels
|
|
139
133
|
activePanel={activePanel}
|
|
140
134
|
theme={theme}
|
|
141
|
-
onThemeChange={onThemeChange}
|
|
142
135
|
showLineNumbers={showLineNumbers}
|
|
143
|
-
onLineNumbersToggle={onLineNumbersToggle}
|
|
144
136
|
/>
|
|
145
137
|
|
|
146
138
|
<Toolbar
|
|
147
139
|
context="simple"
|
|
148
140
|
activePanel={activePanel}
|
|
149
|
-
onPanelToggle={onPanelToggle}
|
|
150
141
|
/>
|
|
151
142
|
</div>
|
|
152
143
|
</div>
|