@semiont/react-ui 0.5.6 → 0.5.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{PdfAnnotationCanvas.client-NIMALXNZ.js → PdfAnnotationCanvas.client-U4EVBZEV.js} +6 -52
- package/dist/PdfAnnotationCanvas.client-U4EVBZEV.js.map +1 -0
- package/dist/{ar-U2EXWUMQ.js → ar-WA47UUWA.js} +4 -8
- package/dist/ar-WA47UUWA.js.map +1 -0
- package/dist/{bn-DRJGV772.js → bn-5ANDRIU6.js} +4 -8
- package/dist/bn-5ANDRIU6.js.map +1 -0
- package/dist/chunk-O2MD7TGE.js +42 -0
- package/dist/chunk-O2MD7TGE.js.map +1 -0
- package/dist/chunk-QT7LYM72.js +234 -0
- package/dist/chunk-QT7LYM72.js.map +1 -0
- package/dist/{chunk-3Q3TUKWP.js → chunk-WHUGODTB.js} +31 -31
- package/dist/chunk-XUDKYAVC.js +21 -0
- package/dist/chunk-XUDKYAVC.js.map +1 -0
- package/dist/{chunk-K6BJDL2I.js → chunk-YBGN4ACY.js} +6 -2
- package/dist/{cs-PTWDM23V.js → cs-3RU7F4JX.js} +4 -8
- package/dist/cs-3RU7F4JX.js.map +1 -0
- package/dist/{da-KSNIKYSS.js → da-GSW5P5HG.js} +7 -11
- package/dist/da-GSW5P5HG.js.map +1 -0
- package/dist/{de-F2XBEWFY.js → de-JUAUYF4Z.js} +4 -8
- package/dist/de-JUAUYF4Z.js.map +1 -0
- package/dist/{el-DLD2GWAP.js → el-JNLWCKEC.js} +4 -8
- package/dist/el-JNLWCKEC.js.map +1 -0
- package/dist/{en-L45VK7BS.js → en-RBN2GUHF.js} +2 -2
- package/dist/{es-WLPYWGB5.js → es-WPOX225H.js} +12 -16
- package/dist/es-WPOX225H.js.map +1 -0
- package/dist/{fa-BAXHSDZG.js → fa-TAEP6N77.js} +4 -8
- package/dist/fa-TAEP6N77.js.map +1 -0
- package/dist/{fi-FCHSYVOT.js → fi-ZVZHANSP.js} +4 -8
- package/dist/fi-ZVZHANSP.js.map +1 -0
- package/dist/{fr-3UERBSL6.js → fr-VLZW7M4N.js} +4 -8
- package/dist/fr-VLZW7M4N.js.map +1 -0
- package/dist/{he-F6F3FV2K.js → he-QFAFYA77.js} +4 -8
- package/dist/he-QFAFYA77.js.map +1 -0
- package/dist/{hi-4BK6IK7Q.js → hi-AO3DQPCV.js} +4 -8
- package/dist/hi-AO3DQPCV.js.map +1 -0
- package/dist/{id-7ECCWP3J.js → id-GTXF42WM.js} +4 -8
- package/dist/id-GTXF42WM.js.map +1 -0
- package/dist/index.css +97 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +52 -23
- package/dist/index.js +2835 -3015
- package/dist/index.js.map +1 -1
- package/dist/integrations/css-modules-helper.d.ts +118 -0
- package/dist/integrations/css-modules-helper.js +117 -0
- package/dist/integrations/css-modules-helper.js.map +1 -0
- package/dist/integrations/styled-components-theme.d.ts +258 -0
- package/dist/integrations/styled-components-theme.js +1774 -0
- package/dist/integrations/styled-components-theme.js.map +1 -0
- package/dist/{it-234Z6XK6.js → it-AS5GM232.js} +4 -8
- package/dist/it-AS5GM232.js.map +1 -0
- package/dist/{ja-PJWQI4OQ.js → ja-GZ4HLUOF.js} +4 -8
- package/dist/ja-GZ4HLUOF.js.map +1 -0
- package/dist/{ko-APUEW2RS.js → ko-A4XUXFGJ.js} +4 -8
- package/dist/ko-A4XUXFGJ.js.map +1 -0
- package/dist/{ms-PJBZWZWD.js → ms-YBAO3S6K.js} +4 -8
- package/dist/ms-YBAO3S6K.js.map +1 -0
- package/dist/{nl-L4C3ZBCU.js → nl-3TJGIIIU.js} +5 -12
- package/dist/nl-3TJGIIIU.js.map +1 -0
- package/dist/{no-QE5N5KNG.js → no-4AXIQPRQ.js} +21 -25
- package/dist/no-4AXIQPRQ.js.map +1 -0
- package/dist/{pl-5Q2D23PD.js → pl-5GP6GKXO.js} +4 -8
- package/dist/pl-5GP6GKXO.js.map +1 -0
- package/dist/{pt-AIGUOIOC.js → pt-26Y6JFG5.js} +119 -123
- package/dist/pt-26Y6JFG5.js.map +1 -0
- package/dist/{ro-T56CSHTY.js → ro-C7UXFRWJ.js} +4 -8
- package/dist/ro-C7UXFRWJ.js.map +1 -0
- package/dist/{sv-L4TJQ2UH.js → sv-44DVD76T.js} +44 -48
- package/dist/sv-44DVD76T.js.map +1 -0
- package/dist/test-utils.js +3 -3
- package/dist/test-utils.js.map +1 -1
- package/dist/{th-6O7Y6O2Q.js → th-GIQRTBOY.js} +4 -8
- package/dist/th-GIQRTBOY.js.map +1 -0
- package/dist/{tr-D4CQCSNO.js → tr-WMQWO4D6.js} +4 -8
- package/dist/tr-WMQWO4D6.js.map +1 -0
- package/dist/{uk-2HMQG6ND.js → uk-I7ML6RGG.js} +4 -8
- package/dist/uk-I7ML6RGG.js.map +1 -0
- package/dist/{vi-XVJ4RUEJ.js → vi-FGWECASQ.js} +4 -8
- package/dist/vi-FGWECASQ.js.map +1 -0
- package/dist/{zh-K2KDPGHK.js → zh-L5FN73XC.js} +4 -8
- package/dist/zh-L5FN73XC.js.map +1 -0
- package/package.json +7 -6
- package/src/components/ProtectedErrorBoundary.css +119 -0
- package/src/components/ProtectedErrorBoundary.tsx +18 -13
- package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +1 -1
- package/src/components/modals/__tests__/SearchModal.search-wiring.test.tsx +1 -1
- 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 +4 -8
- package/src/components/resource/__tests__/HistoryEvent.test.tsx +0 -1
- 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/__tests__/AssessmentEntry.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/HighlightEntry.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/JsonLdPanel.test.tsx +95 -424
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/TagEntry.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +1 -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/moderation-linked-data/__tests__/LinkedDataPage.test.tsx +11 -9
- 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-viewer/components/ResourceViewerPage.tsx +7 -5
- package/src/features/resource-viewer/state/__tests__/resource-viewer-page-state-unit.test.ts +37 -0
- package/src/features/resource-viewer/state/resource-viewer-page-state-unit.ts +9 -5
- 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-NIMALXNZ.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-WHUGODTB.js.map} +0 -0
- /package/dist/{chunk-K6BJDL2I.js.map → chunk-YBGN4ACY.js.map} +0 -0
- /package/dist/{en-L45VK7BS.js.map → en-RBN2GUHF.js.map} +0 -0
|
@@ -56,7 +56,7 @@ vi.mock('../../../../contexts/TranslationContext', () => ({
|
|
|
56
56
|
TranslationProvider: ({ children }: { children: React.ReactNode }) => children,
|
|
57
57
|
}));
|
|
58
58
|
|
|
59
|
-
// Mock @semiont/
|
|
59
|
+
// Mock @semiont/http-transport utilities
|
|
60
60
|
vi.mock('@semiont/core', async () => {
|
|
61
61
|
const actual = await vi.importActual('@semiont/core');
|
|
62
62
|
return {
|
|
@@ -6,7 +6,7 @@ import userEvent from '@testing-library/user-event';
|
|
|
6
6
|
|
|
7
7
|
import type { Annotation, AnnotationId } from '@semiont/core';
|
|
8
8
|
|
|
9
|
-
// Mock @semiont/
|
|
9
|
+
// Mock @semiont/http-transport
|
|
10
10
|
vi.mock('@semiont/core', async () => {
|
|
11
11
|
const actual = await vi.importActual('@semiont/core');
|
|
12
12
|
return {
|
|
@@ -3,500 +3,171 @@ import { render, screen, waitFor } from '@testing-library/react';
|
|
|
3
3
|
import userEvent from '@testing-library/user-event';
|
|
4
4
|
import '@testing-library/jest-dom';
|
|
5
5
|
import { JsonLdPanel } from '../JsonLdPanel';
|
|
6
|
+
import { resourceId } from '@semiont/core';
|
|
6
7
|
import type { components } from '@semiont/core';
|
|
7
8
|
|
|
8
|
-
type
|
|
9
|
+
type GetResourceResponse = components['schemas']['GetResourceResponse'];
|
|
9
10
|
|
|
10
|
-
// Mock CodeMirror modules
|
|
11
|
+
// Mock CodeMirror modules (the panel still renders the graph in CodeMirror)
|
|
11
12
|
vi.mock('@codemirror/view', () => {
|
|
12
13
|
class MockEditorView {
|
|
13
14
|
destroy = vi.fn();
|
|
14
|
-
constructor() {
|
|
15
|
-
// Store config if needed for assertions
|
|
16
|
-
}
|
|
17
15
|
static editable = { of: vi.fn() };
|
|
18
16
|
static theme = vi.fn(() => ({}));
|
|
19
17
|
}
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
EditorView: MockEditorView,
|
|
23
|
-
lineNumbers: vi.fn(),
|
|
24
|
-
};
|
|
18
|
+
return { EditorView: MockEditorView, lineNumbers: vi.fn() };
|
|
25
19
|
});
|
|
26
|
-
|
|
27
20
|
vi.mock('@codemirror/state', () => ({
|
|
28
|
-
EditorState: {
|
|
29
|
-
create: vi.fn(() => ({})),
|
|
30
|
-
readOnly: { of: vi.fn() },
|
|
31
|
-
},
|
|
32
|
-
}));
|
|
33
|
-
|
|
34
|
-
vi.mock('@codemirror/lang-json', () => ({
|
|
35
|
-
json: vi.fn(),
|
|
21
|
+
EditorState: { create: vi.fn(() => ({})), readOnly: { of: vi.fn() } },
|
|
36
22
|
}));
|
|
37
|
-
|
|
38
|
-
vi.mock('@codemirror/theme-one-dark', () => ({
|
|
39
|
-
oneDark: {},
|
|
40
|
-
}));
|
|
41
|
-
|
|
23
|
+
vi.mock('@codemirror/lang-json', () => ({ json: vi.fn() }));
|
|
24
|
+
vi.mock('@codemirror/theme-one-dark', () => ({ oneDark: {} }));
|
|
42
25
|
vi.mock('@codemirror/language', () => ({
|
|
43
26
|
syntaxHighlighting: vi.fn(),
|
|
44
|
-
HighlightStyle: {
|
|
45
|
-
define: vi.fn(() => ({})),
|
|
46
|
-
},
|
|
27
|
+
HighlightStyle: { define: vi.fn(() => ({})) },
|
|
47
28
|
}));
|
|
48
|
-
|
|
49
29
|
vi.mock('../../../lib/codemirror-json-theme', () => ({
|
|
50
30
|
jsonLightTheme: {},
|
|
51
31
|
jsonLightHighlightStyle: {},
|
|
52
32
|
}));
|
|
53
33
|
|
|
54
|
-
// Mock
|
|
34
|
+
// Mock the hooks the panel consumes — NOT the session/client stack.
|
|
55
35
|
vi.mock('@/hooks/useLineNumbers');
|
|
56
|
-
|
|
36
|
+
vi.mock('@/hooks/useResourceGraph');
|
|
57
37
|
import { useLineNumbers } from '@/hooks/useLineNumbers';
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
38
|
+
import { useResourceGraph } from '@/hooks/useResourceGraph';
|
|
39
|
+
|
|
40
|
+
const RID = resourceId('test-resource-1');
|
|
41
|
+
|
|
42
|
+
const MOCK_GRAPH: GetResourceResponse = {
|
|
43
|
+
resource: {
|
|
44
|
+
'@context': 'http://www.w3.org/ns/anno.jsonld',
|
|
45
|
+
'@id': 'test-resource-1',
|
|
46
|
+
id: 'test-resource-1',
|
|
47
|
+
name: 'Test Resource',
|
|
48
|
+
content: 'This is test content',
|
|
49
|
+
format: 'text/plain',
|
|
50
|
+
archived: false,
|
|
51
|
+
entityTypes: ['Person'],
|
|
52
|
+
locale: 'en-US',
|
|
53
|
+
representations: [],
|
|
54
|
+
created: '2024-01-01T10:00:00Z',
|
|
55
|
+
modified: '2024-01-01T10:00:00Z',
|
|
56
|
+
},
|
|
57
|
+
annotations: [],
|
|
58
|
+
entityReferences: [],
|
|
59
|
+
};
|
|
75
60
|
|
|
76
61
|
describe('JsonLdPanel Component', () => {
|
|
77
|
-
let mockClipboard:
|
|
62
|
+
let mockClipboard: { writeText: ReturnType<typeof vi.fn> };
|
|
78
63
|
|
|
79
64
|
beforeEach(() => {
|
|
80
65
|
vi.clearAllMocks();
|
|
81
66
|
|
|
82
|
-
|
|
83
|
-
mockClipboard = {
|
|
84
|
-
writeText: vi.fn().mockResolvedValue(undefined),
|
|
85
|
-
};
|
|
67
|
+
mockClipboard = { writeText: vi.fn().mockResolvedValue(undefined) };
|
|
86
68
|
Object.defineProperty(navigator, 'clipboard', {
|
|
87
|
-
value: mockClipboard,
|
|
88
|
-
writable: true,
|
|
89
|
-
configurable: true,
|
|
69
|
+
value: mockClipboard, writable: true, configurable: true,
|
|
90
70
|
});
|
|
91
|
-
|
|
92
|
-
// Mock document.documentElement for dark mode detection
|
|
93
71
|
Object.defineProperty(document.documentElement, 'classList', {
|
|
94
|
-
value: {
|
|
95
|
-
|
|
96
|
-
},
|
|
97
|
-
writable: true,
|
|
98
|
-
configurable: true,
|
|
72
|
+
value: { contains: vi.fn().mockReturnValue(false) },
|
|
73
|
+
writable: true, configurable: true,
|
|
99
74
|
});
|
|
100
75
|
|
|
101
|
-
// Mock useLineNumbers hook implementation
|
|
102
76
|
vi.mocked(useLineNumbers).mockReturnValue({
|
|
103
|
-
showLineNumbers: true,
|
|
104
|
-
|
|
77
|
+
showLineNumbers: true, toggleLineNumbers: vi.fn(),
|
|
78
|
+
});
|
|
79
|
+
// Default: graph loaded successfully.
|
|
80
|
+
vi.mocked(useResourceGraph).mockReturnValue({
|
|
81
|
+
graph: MOCK_GRAPH, loading: false, error: null,
|
|
105
82
|
});
|
|
106
83
|
});
|
|
107
84
|
|
|
108
|
-
afterEach(() => {
|
|
109
|
-
vi.restoreAllMocks();
|
|
110
|
-
});
|
|
85
|
+
afterEach(() => { vi.restoreAllMocks(); });
|
|
111
86
|
|
|
112
87
|
describe('Rendering', () => {
|
|
113
|
-
it('
|
|
114
|
-
const
|
|
115
|
-
render(<JsonLdPanel resource={resource} />);
|
|
116
|
-
|
|
88
|
+
it('renders the header, copy button, and editor container', () => {
|
|
89
|
+
const { container } = render(<JsonLdPanel resourceId={RID} />);
|
|
117
90
|
expect(screen.getByText('JSON-LD')).toBeInTheDocument();
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('should render copy button', () => {
|
|
121
|
-
const resource = createMockResource();
|
|
122
|
-
render(<JsonLdPanel resource={resource} />);
|
|
123
|
-
|
|
124
91
|
expect(screen.getByText(/Copy/)).toBeInTheDocument();
|
|
92
|
+
expect(container.querySelector('.semiont-jsonld-panel__editor')).toBeInTheDocument();
|
|
125
93
|
});
|
|
126
94
|
|
|
127
|
-
it('
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const editorDiv = container.querySelector('.semiont-jsonld-panel__editor');
|
|
132
|
-
expect(editorDiv).toBeInTheDocument();
|
|
95
|
+
it('fetches the graph for the given resourceId', () => {
|
|
96
|
+
render(<JsonLdPanel resourceId={RID} />);
|
|
97
|
+
expect(useResourceGraph).toHaveBeenCalledWith(RID);
|
|
133
98
|
});
|
|
134
99
|
});
|
|
135
100
|
|
|
136
|
-
describe('
|
|
137
|
-
it('
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const editorDiv = container.querySelector('.semiont-jsonld-panel__editor');
|
|
143
|
-
expect(editorDiv).toBeInTheDocument();
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it('should pass resource as JSON to editor', () => {
|
|
147
|
-
const resource = createMockResource();
|
|
148
|
-
const { container } = render(<JsonLdPanel resource={resource} />);
|
|
149
|
-
|
|
150
|
-
// Verify component renders without errors
|
|
151
|
-
expect(container.firstChild).toBeInTheDocument();
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it('should format JSON with indentation', () => {
|
|
155
|
-
const resource = createMockResource();
|
|
156
|
-
const { container } = render(<JsonLdPanel resource={resource} />);
|
|
157
|
-
|
|
158
|
-
// Verify component renders without errors
|
|
159
|
-
expect(container.firstChild).toBeInTheDocument();
|
|
101
|
+
describe('Loading / error states', () => {
|
|
102
|
+
it('shows a loading state and disables Copy while loading', () => {
|
|
103
|
+
vi.mocked(useResourceGraph).mockReturnValue({ graph: null, loading: true, error: null });
|
|
104
|
+
render(<JsonLdPanel resourceId={RID} />);
|
|
105
|
+
expect(screen.getByText(/Loading JSON-LD/i)).toBeInTheDocument();
|
|
106
|
+
expect(screen.getByText(/Copy/).closest('button')).toBeDisabled();
|
|
160
107
|
});
|
|
161
108
|
|
|
162
|
-
it('
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
// Verify component renders without errors (read-only is configured internally)
|
|
167
|
-
expect(container.firstChild).toBeInTheDocument();
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('should include line numbers when enabled', () => {
|
|
171
|
-
vi.mocked(useLineNumbers).mockReturnValue({ showLineNumbers: true, toggleLineNumbers: vi.fn() });
|
|
172
|
-
|
|
173
|
-
const resource = createMockResource();
|
|
174
|
-
const { container } = render(<JsonLdPanel resource={resource} />);
|
|
175
|
-
|
|
176
|
-
// Verify component renders without errors (line numbers configured internally)
|
|
177
|
-
expect(container.firstChild).toBeInTheDocument();
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('should not include line numbers when disabled', () => {
|
|
181
|
-
vi.mocked(useLineNumbers).mockReturnValue({ showLineNumbers: false, toggleLineNumbers: vi.fn() });
|
|
182
|
-
|
|
183
|
-
const resource = createMockResource();
|
|
184
|
-
const { container } = render(<JsonLdPanel resource={resource} />);
|
|
185
|
-
|
|
186
|
-
// Verify component renders without errors (line numbers not included)
|
|
187
|
-
expect(container.firstChild).toBeInTheDocument();
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('should cleanup editor on unmount', () => {
|
|
191
|
-
const resource = createMockResource();
|
|
192
|
-
const { unmount } = render(<JsonLdPanel resource={resource} />);
|
|
193
|
-
|
|
194
|
-
// Should unmount without errors (cleanup happens internally)
|
|
195
|
-
expect(() => unmount()).not.toThrow();
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
describe('Dark Mode Support', () => {
|
|
200
|
-
it('should use dark theme when dark mode is active', () => {
|
|
201
|
-
Object.defineProperty(document.documentElement, 'classList', {
|
|
202
|
-
value: {
|
|
203
|
-
contains: vi.fn().mockReturnValue(true), // Dark mode active
|
|
204
|
-
},
|
|
205
|
-
writable: true,
|
|
206
|
-
configurable: true,
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
const resource = createMockResource();
|
|
210
|
-
const { container } = render(<JsonLdPanel resource={resource} />);
|
|
211
|
-
|
|
212
|
-
// Verify component renders without errors (dark theme configured internally)
|
|
213
|
-
expect(container.firstChild).toBeInTheDocument();
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it('should use light theme when dark mode is not active', () => {
|
|
217
|
-
Object.defineProperty(document.documentElement, 'classList', {
|
|
218
|
-
value: {
|
|
219
|
-
contains: vi.fn().mockReturnValue(false), // Light mode
|
|
220
|
-
},
|
|
221
|
-
writable: true,
|
|
222
|
-
configurable: true,
|
|
109
|
+
it('shows an error state and disables Copy on failure', () => {
|
|
110
|
+
vi.mocked(useResourceGraph).mockReturnValue({
|
|
111
|
+
graph: null, loading: false, error: new Error('boom'),
|
|
223
112
|
});
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
// Verify component renders without errors (light theme configured internally)
|
|
229
|
-
expect(container.firstChild).toBeInTheDocument();
|
|
113
|
+
render(<JsonLdPanel resourceId={RID} />);
|
|
114
|
+
expect(screen.getByText(/Failed to load JSON-LD/i)).toBeInTheDocument();
|
|
115
|
+
expect(screen.getByText(/Copy/).closest('button')).toBeDisabled();
|
|
230
116
|
});
|
|
231
117
|
});
|
|
232
118
|
|
|
233
|
-
describe('Copy to
|
|
234
|
-
it('
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const copyButton = screen.getByText(/Copy/);
|
|
239
|
-
await userEvent.click(copyButton);
|
|
119
|
+
describe('Copy to clipboard', () => {
|
|
120
|
+
it('copies the full graph — not the bare descriptor', async () => {
|
|
121
|
+
render(<JsonLdPanel resourceId={RID} />);
|
|
122
|
+
await userEvent.click(screen.getByText(/Copy/));
|
|
240
123
|
|
|
241
124
|
expect(mockClipboard.writeText).toHaveBeenCalledOnce();
|
|
125
|
+
const parsed = JSON.parse(mockClipboard.writeText.mock.calls[0][0]);
|
|
242
126
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
expect(
|
|
127
|
+
// The graph wraps the descriptor and adds annotation collections; a bare
|
|
128
|
+
// ResourceDescriptor would have `id` at the top level and no `resource`.
|
|
129
|
+
expect(parsed.resource.id).toBe('test-resource-1');
|
|
130
|
+
expect(Array.isArray(parsed.annotations)).toBe(true);
|
|
131
|
+
expect(Array.isArray(parsed.entityReferences)).toBe(true);
|
|
132
|
+
expect(parsed.id).toBeUndefined();
|
|
246
133
|
});
|
|
247
134
|
|
|
248
|
-
it('
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
// Should be formatted with indentation
|
|
258
|
-
expect(copiedText).toContain('\n');
|
|
259
|
-
expect(copiedText).toContain(' ');
|
|
135
|
+
it('copies pretty-printed, valid JSON', async () => {
|
|
136
|
+
render(<JsonLdPanel resourceId={RID} />);
|
|
137
|
+
await userEvent.click(screen.getByText(/Copy/));
|
|
138
|
+
const copied = mockClipboard.writeText.mock.calls[0][0];
|
|
139
|
+
expect(copied).toContain('\n');
|
|
140
|
+
expect(copied).toContain(' ');
|
|
141
|
+
expect(() => JSON.parse(copied)).not.toThrow();
|
|
142
|
+
});
|
|
260
143
|
|
|
261
|
-
|
|
262
|
-
|
|
144
|
+
it('does not copy when there is no graph yet', async () => {
|
|
145
|
+
vi.mocked(useResourceGraph).mockReturnValue({ graph: null, loading: true, error: null });
|
|
146
|
+
render(<JsonLdPanel resourceId={RID} />);
|
|
147
|
+
const button = screen.getByText(/Copy/).closest('button')!;
|
|
148
|
+
await userEvent.click(button);
|
|
149
|
+
expect(mockClipboard.writeText).not.toHaveBeenCalled();
|
|
263
150
|
});
|
|
264
151
|
|
|
265
|
-
it('
|
|
152
|
+
it('handles clipboard errors gracefully', async () => {
|
|
266
153
|
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
267
154
|
mockClipboard.writeText.mockRejectedValue(new Error('Clipboard error'));
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
render(<JsonLdPanel resource={resource} />);
|
|
271
|
-
|
|
272
|
-
const copyButton = screen.getByText(/Copy/);
|
|
273
|
-
|
|
274
|
-
await expect(async () => {
|
|
275
|
-
await userEvent.click(copyButton);
|
|
276
|
-
}).not.toThrow();
|
|
277
|
-
|
|
155
|
+
render(<JsonLdPanel resourceId={RID} />);
|
|
156
|
+
await userEvent.click(screen.getByText(/Copy/));
|
|
278
157
|
await waitFor(() => {
|
|
279
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
280
|
-
'Failed to copy JSON-LD:',
|
|
281
|
-
expect.any(Error)
|
|
282
|
-
);
|
|
158
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith('Failed to copy JSON-LD:', expect.any(Error));
|
|
283
159
|
});
|
|
284
|
-
|
|
285
160
|
consoleErrorSpy.mockRestore();
|
|
286
161
|
});
|
|
287
162
|
});
|
|
288
163
|
|
|
289
|
-
describe('
|
|
290
|
-
it('
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
expect(container.firstChild).toBeInTheDocument();
|
|
295
|
-
|
|
296
|
-
const resource2 = createMockResource({ id: 'resource-2', name: 'Resource 2' });
|
|
297
|
-
|
|
298
|
-
// Should rerender without errors
|
|
299
|
-
expect(() => rerender(<JsonLdPanel resource={resource2} />)).not.toThrow();
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
it('should reinitialize editor when line numbers setting changes', () => {
|
|
303
|
-
vi.mocked(useLineNumbers).mockReturnValue({ showLineNumbers: true, toggleLineNumbers: vi.fn() });
|
|
304
|
-
|
|
305
|
-
const resource = createMockResource();
|
|
306
|
-
const { rerender } = render(<JsonLdPanel resource={resource} />);
|
|
307
|
-
|
|
308
|
-
vi.mocked(useLineNumbers).mockReturnValue({ showLineNumbers: false, toggleLineNumbers: vi.fn() });
|
|
309
|
-
|
|
310
|
-
// Should rerender without errors
|
|
311
|
-
expect(() => rerender(<JsonLdPanel resource={resource} />)).not.toThrow();
|
|
312
|
-
});
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
describe('Edge Cases', () => {
|
|
316
|
-
it('should handle resource with minimal data', () => {
|
|
317
|
-
const minimalResource: SemiontResource = {
|
|
318
|
-
'@context': 'http://www.w3.org/ns/anno.jsonld',
|
|
319
|
-
'@id': 'minimal',
|
|
320
|
-
id: 'minimal',
|
|
321
|
-
name: 'Minimal',
|
|
322
|
-
content: 'Test',
|
|
323
|
-
format: 'text/plain',
|
|
324
|
-
archived: false,
|
|
325
|
-
representations: [],
|
|
326
|
-
created: '2024-01-01T00:00:00Z',
|
|
327
|
-
modified: '2024-01-01T00:00:00Z',
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
expect(() => {
|
|
331
|
-
render(<JsonLdPanel resource={minimalResource} />);
|
|
332
|
-
}).not.toThrow();
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
it('should handle resource with special characters', () => {
|
|
336
|
-
const resource = createMockResource({
|
|
337
|
-
name: 'Test "Resource" with \'quotes\' & special <chars>',
|
|
338
|
-
content: 'Content with\nnewlines\tand\ttabs',
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
expect(() => {
|
|
342
|
-
render(<JsonLdPanel resource={resource} />);
|
|
343
|
-
}).not.toThrow();
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
it('should handle very large resource data', () => {
|
|
347
|
-
const largeContent = 'A'.repeat(100000);
|
|
348
|
-
const resource = createMockResource({
|
|
349
|
-
content: largeContent,
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
expect(() => {
|
|
353
|
-
render(<JsonLdPanel resource={resource} />);
|
|
354
|
-
}).not.toThrow();
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
it('should handle resource with unicode characters', () => {
|
|
358
|
-
const resource = createMockResource({
|
|
359
|
-
name: '测试资源 テストリソース ресурс тест',
|
|
360
|
-
content: '🎉🔥💯 Unicode content',
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
expect(() => {
|
|
364
|
-
render(<JsonLdPanel resource={resource} />);
|
|
365
|
-
}).not.toThrow();
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
it('should handle undefined optional fields', () => {
|
|
369
|
-
const resource: SemiontResource = {
|
|
370
|
-
'@context': 'http://www.w3.org/ns/anno.jsonld',
|
|
371
|
-
'@id': 'test',
|
|
372
|
-
id: 'test',
|
|
373
|
-
name: 'Test',
|
|
374
|
-
content: 'Content',
|
|
375
|
-
format: 'text/plain',
|
|
376
|
-
archived: false,
|
|
377
|
-
representations: [],
|
|
378
|
-
created: '2024-01-01T00:00:00Z',
|
|
379
|
-
modified: '2024-01-01T00:00:00Z',
|
|
380
|
-
// Optional fields omitted
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
expect(() => {
|
|
384
|
-
render(<JsonLdPanel resource={resource} />);
|
|
385
|
-
}).not.toThrow();
|
|
386
|
-
});
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
describe('Styling and Appearance', () => {
|
|
390
|
-
it('should have proper panel structure', () => {
|
|
391
|
-
const resource = createMockResource();
|
|
392
|
-
const { container } = render(<JsonLdPanel resource={resource} />);
|
|
393
|
-
|
|
394
|
-
const panel = container.firstChild as HTMLElement;
|
|
395
|
-
expect(panel).toHaveClass('semiont-jsonld-panel');
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
it('should have proper header styling', () => {
|
|
399
|
-
const resource = createMockResource();
|
|
400
|
-
render(<JsonLdPanel resource={resource} />);
|
|
401
|
-
|
|
402
|
-
const header = screen.getByText('JSON-LD').parentElement;
|
|
403
|
-
expect(header).toHaveClass('semiont-jsonld-panel__header');
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
it('should have proper button styling', () => {
|
|
407
|
-
const resource = createMockResource();
|
|
408
|
-
render(<JsonLdPanel resource={resource} />);
|
|
409
|
-
|
|
410
|
-
const copyButton = screen.getByText(/Copy/).closest('button');
|
|
411
|
-
expect(copyButton).toHaveClass('semiont-button', 'semiont-button--icon');
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
it('should have proper editor container styling', () => {
|
|
415
|
-
const resource = createMockResource();
|
|
416
|
-
const { container } = render(<JsonLdPanel resource={resource} />);
|
|
417
|
-
|
|
418
|
-
const editorContainer = container.querySelector('.semiont-jsonld-panel__editor');
|
|
419
|
-
expect(editorContainer).toBeInTheDocument();
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
it('should support dark mode styling', () => {
|
|
423
|
-
const resource = createMockResource();
|
|
424
|
-
const { container } = render(<JsonLdPanel resource={resource} />);
|
|
425
|
-
|
|
426
|
-
const panel = container.firstChild as HTMLElement;
|
|
427
|
-
expect(panel).toHaveClass('semiont-jsonld-panel');
|
|
428
|
-
});
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
describe('Accessibility', () => {
|
|
432
|
-
it('should have title attribute on copy button', () => {
|
|
433
|
-
const resource = createMockResource();
|
|
434
|
-
render(<JsonLdPanel resource={resource} />);
|
|
435
|
-
|
|
164
|
+
describe('Accessibility / structure', () => {
|
|
165
|
+
it('has the panel structure and a titled copy button', () => {
|
|
166
|
+
const { container } = render(<JsonLdPanel resourceId={RID} />);
|
|
167
|
+
expect(container.firstChild).toHaveClass('semiont-jsonld-panel');
|
|
436
168
|
const copyButton = screen.getByText(/Copy/).closest('button');
|
|
437
169
|
expect(copyButton).toHaveAttribute('title', 'Copy to clipboard');
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
it('should have semantic heading', () => {
|
|
441
|
-
const resource = createMockResource();
|
|
442
|
-
render(<JsonLdPanel resource={resource} />);
|
|
443
|
-
|
|
444
|
-
const heading = screen.getByText('JSON-LD');
|
|
445
|
-
expect(heading).toHaveClass('semiont-jsonld-panel__title');
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
it('should have proper button structure', () => {
|
|
449
|
-
const resource = createMockResource();
|
|
450
|
-
render(<JsonLdPanel resource={resource} />);
|
|
451
|
-
|
|
452
|
-
const copyButton = screen.getByText(/Copy/).closest('button');
|
|
453
|
-
expect(copyButton?.tagName).toBe('BUTTON');
|
|
454
|
-
});
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
describe('JSON-LD Format', () => {
|
|
458
|
-
it('should produce valid JSON', async () => {
|
|
459
|
-
const resource = createMockResource();
|
|
460
|
-
render(<JsonLdPanel resource={resource} />);
|
|
461
|
-
|
|
462
|
-
const copyButton = screen.getByText(/Copy/);
|
|
463
|
-
await userEvent.click(copyButton);
|
|
464
|
-
|
|
465
|
-
const copiedText = mockClipboard.writeText.mock.calls[0][0];
|
|
466
|
-
|
|
467
|
-
expect(() => {
|
|
468
|
-
const parsed = JSON.parse(copiedText);
|
|
469
|
-
expect(parsed).toHaveProperty('id');
|
|
470
|
-
expect(parsed).toHaveProperty('name');
|
|
471
|
-
expect(parsed).toHaveProperty('content');
|
|
472
|
-
}).not.toThrow();
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
it('should include all resource fields', async () => {
|
|
476
|
-
const resource = createMockResource({
|
|
477
|
-
id: 'full-resource',
|
|
478
|
-
name: 'Full Resource',
|
|
479
|
-
content: 'Complete content',
|
|
480
|
-
format: 'text/markdown',
|
|
481
|
-
archived: true,
|
|
482
|
-
entityTypes: ['Person', 'Organization'],
|
|
483
|
-
locale: 'en-US',
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
render(<JsonLdPanel resource={resource} />);
|
|
487
|
-
|
|
488
|
-
const copyButton = screen.getByText(/Copy/);
|
|
489
|
-
await userEvent.click(copyButton);
|
|
490
|
-
|
|
491
|
-
const copiedText = mockClipboard.writeText.mock.calls[0][0];
|
|
492
|
-
const parsed = JSON.parse(copiedText);
|
|
493
|
-
|
|
494
|
-
expect(parsed.id).toBe('full-resource');
|
|
495
|
-
expect(parsed.name).toBe('Full Resource');
|
|
496
|
-
expect(parsed.format).toBe('text/markdown');
|
|
497
|
-
expect(parsed.archived).toBe(true);
|
|
498
|
-
expect(parsed.entityTypes).toEqual(['Person', 'Organization']);
|
|
499
|
-
expect(parsed.locale).toBe('en-US');
|
|
170
|
+
expect(screen.getByText('JSON-LD')).toHaveClass('semiont-jsonld-panel__title');
|
|
500
171
|
});
|
|
501
172
|
});
|
|
502
173
|
});
|
|
@@ -35,7 +35,7 @@ vi.mock('../../../../contexts/TranslationContext', () => ({
|
|
|
35
35
|
TranslationProvider: ({ children }: { children: React.ReactNode }) => children,
|
|
36
36
|
}));
|
|
37
37
|
|
|
38
|
-
// Mock @semiont/
|
|
38
|
+
// Mock @semiont/http-transport utilities
|
|
39
39
|
vi.mock('@semiont/core', async () => {
|
|
40
40
|
const actual = await vi.importActual('@semiont/core');
|
|
41
41
|
return {
|
|
@@ -9,7 +9,7 @@ import type { TagSchema } from '@semiont/core';
|
|
|
9
9
|
|
|
10
10
|
import type { Annotation, AnnotationId } from '@semiont/core';
|
|
11
11
|
|
|
12
|
-
// Mock @semiont/
|
|
12
|
+
// Mock @semiont/http-transport
|
|
13
13
|
vi.mock('@semiont/core', async () => {
|
|
14
14
|
const actual = await vi.importActual('@semiont/core');
|
|
15
15
|
return {
|
|
@@ -112,7 +112,7 @@ vi.mock('../../../../contexts/TranslationContext', () => ({
|
|
|
112
112
|
TranslationProvider: ({ children }: { children: React.ReactNode }) => children,
|
|
113
113
|
}));
|
|
114
114
|
|
|
115
|
-
// Mock @semiont/
|
|
115
|
+
// Mock @semiont/http-transport utilities
|
|
116
116
|
vi.mock('@semiont/core', async () => {
|
|
117
117
|
const actual = await vi.importActual('@semiont/core');
|
|
118
118
|
return {
|