@semiont/react-ui 0.5.26 → 0.5.28
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 +28 -4
- package/dist/{PdfAnnotationCanvas.client-6ZSF6ZTJ.js → PdfAnnotationCanvas.client-C2IGG4SW.js} +33 -6
- package/dist/{PdfAnnotationCanvas.client-JCJMXWWJ.css → PdfAnnotationCanvas.client-TGDPPGXJ.css} +6 -1
- package/dist/{ar-YTUUGK2O.js → ar-FABPTJVB.js} +88 -26
- package/dist/{bn-ASGNGPMD.js → bn-LKP6BDMH.js} +88 -26
- package/dist/{chunk-L3WKRWP3.js → chunk-2GOHTTHW.js} +88 -26
- package/dist/{chunk-OBQOZCJE.js → chunk-2YHEE63S.js} +31 -31
- package/dist/{cs-WMYVAOZY.js → cs-YQYNWHRG.js} +88 -26
- package/dist/{da-FSHHN4D6.js → da-QHZOYKRY.js} +88 -26
- package/dist/{de-FJFO3TPT.js → de-GKA7RD7J.js} +88 -26
- package/dist/{el-XOH4QIAG.js → el-45T257AL.js} +88 -26
- package/dist/{en-3FCBW3YD.js → en-RVW5EQ66.js} +4 -2
- package/dist/{es-HDX4QI7E.js → es-AVER7RU4.js} +88 -26
- package/dist/{fa-DJ6WTFTJ.js → fa-K3VPK3O2.js} +88 -26
- package/dist/{fi-4S7H7W7U.js → fi-5NEKF7EB.js} +88 -26
- package/dist/{fr-6UJSYHXC.js → fr-FUMNDLBZ.js} +88 -26
- package/dist/{he-ZZJOHJX2.js → he-3JGNA3PR.js} +88 -26
- package/dist/{hi-HZJTAW7U.js → hi-IJY375DF.js} +88 -26
- package/dist/{id-E3I6ILEM.js → id-LWHKJ7ZD.js} +88 -26
- package/dist/index.css +380 -524
- package/dist/index.d.ts +318 -192
- package/dist/index.js +2960 -2421
- package/dist/integrations/css-modules-helper.js +1 -3
- package/dist/{it-ULAPAD7U.js → it-EBVOV4RW.js} +88 -26
- package/dist/{ja-SAHZQ4CZ.js → ja-KWRIX2NF.js} +88 -26
- package/dist/{ko-DFU2ADQ4.js → ko-L5CJMFDS.js} +88 -26
- package/dist/{ms-NWL76XNT.js → ms-LSNMJDR2.js} +88 -26
- package/dist/{nl-ATCM76WN.js → nl-ZB32MB7U.js} +88 -26
- package/dist/{no-UIAQ5RJF.js → no-4GTNY7M5.js} +88 -26
- package/dist/{pl-NOECQDXD.js → pl-LUHBLTHB.js} +88 -26
- package/dist/{pt-SVWIIUPC.js → pt-C4H3UNQ6.js} +88 -26
- package/dist/{ro-NR2DRTZG.js → ro-GGITS5V4.js} +88 -26
- package/dist/{sv-STVJ37JP.js → sv-T56RTLBP.js} +88 -26
- package/dist/test-utils.css +1 -1
- package/dist/test-utils.d.ts +1 -1
- package/dist/test-utils.js +2 -2
- package/dist/{th-UWQR4K54.js → th-22X2IWCB.js} +88 -26
- package/dist/{tr-WNXZSD27.js → tr-77TFG62H.js} +88 -26
- package/dist/{uk-SJRMB47Y.js → uk-MIIL6NXT.js} +88 -26
- package/dist/{vi-TMRQFES2.js → vi-7JYOIGNV.js} +88 -26
- package/dist/{zh-QOL3LYEM.js → zh-ID6DRYU4.js} +88 -26
- package/package.json +11 -10
- package/src/components/AssistProgress.tsx +178 -134
- package/src/components/CodeMirrorRenderer.tsx +8 -8
- package/src/components/{EntityFoundLog.tsx → ItemFoundLog.tsx} +12 -7
- package/src/components/ProtectedErrorBoundary.css +3 -1
- package/src/components/StatusDisplay.css +1 -1
- package/src/components/Toast.css +3 -1
- package/src/components/__tests__/AssistProgress.test.tsx +268 -141
- package/src/components/annotation/annotation-entries.css +0 -4
- package/src/components/annotation/references.css +0 -4
- package/src/components/image-annotation/AnnotationOverlay.tsx +6 -6
- package/src/components/image-annotation/SvgDrawingCanvas.tsx +1 -1
- package/src/components/image-annotation/__tests__/AnnotationOverlay.click.test.tsx +7 -9
- package/src/components/image-annotation/__tests__/SvgDrawingCanvas.click.test.tsx +1 -2
- package/src/components/layout/Header.css +0 -4
- package/src/components/layout/LeftSidebar.css +0 -4
- package/src/components/modals/ComposeStep.tsx +218 -0
- package/src/components/modals/ConfigureGatherStep.tsx +10 -18
- package/src/components/modals/ConfigureGenerationStep.tsx +131 -54
- package/src/components/modals/ConfigureSearchStep.tsx +42 -40
- package/src/components/modals/ContextSummary.tsx +185 -72
- package/src/components/modals/CorpusPane.tsx +75 -0
- package/src/components/modals/DiscardPrompt.tsx +38 -0
- package/src/components/modals/GatherContextStep.tsx +237 -107
- package/src/components/modals/ReferenceWizardModal.tsx +287 -60
- package/src/components/modals/ResourceGenerateModal.tsx +233 -109
- package/src/components/modals/SearchModal.css +335 -111
- package/src/components/modals/SearchModal.tsx +1 -1
- package/src/components/modals/SearchResultsStep.tsx +63 -88
- package/src/components/modals/WizardFooter.tsx +69 -0
- package/src/components/modals/__tests__/ComposeStep.test.tsx +152 -0
- package/src/components/modals/__tests__/ConfigureGatherStep.test.tsx +14 -26
- package/src/components/modals/__tests__/ConfigureGenerationStep.test.tsx +290 -0
- package/src/components/modals/__tests__/ConfigureSearchStep.test.tsx +95 -0
- package/src/components/modals/__tests__/GatherContextStep.resource.test.tsx +463 -25
- package/src/components/modals/__tests__/ReferenceWizardModal.test.tsx +635 -0
- package/src/components/modals/__tests__/ResourceGenerateModal.test.tsx +277 -85
- package/src/components/modals/__tests__/SearchModal.search-wiring.test.tsx +6 -2
- package/src/components/modals/__tests__/SearchResultsStep.test.tsx +61 -0
- package/src/components/modals/__tests__/WizardFooter.test.tsx +344 -0
- package/src/components/modals/modals.css +16 -42
- package/src/components/navigation/NavigationTabs.css +0 -20
- package/src/components/pdf-annotation/PdfAnnotationCanvas.css +21 -2
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +36 -3
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +41 -6
- package/src/components/resource/AnnotateView.tsx +19 -8
- package/src/components/resource/AnnotationHistory.tsx +2 -0
- package/src/components/resource/BrowseView.tsx +20 -9
- package/src/components/resource/ResourceViewer.tsx +27 -28
- package/src/components/resource/__tests__/AnnotateView.embeddable.test.tsx +1 -1
- package/src/components/resource/__tests__/AnnotateView.focus.test.tsx +50 -0
- package/src/components/resource/__tests__/AnnotationHistory.test.tsx +2 -0
- package/src/components/resource/__tests__/BrowseView.embeddable.test.tsx +1 -1
- package/src/components/resource/__tests__/BrowseView.test.tsx +38 -12
- package/src/components/resource/__tests__/ResourceViewer.embeddable.test.tsx +47 -3
- package/src/components/resource/annotate-renderers.tsx +3 -3
- package/src/components/resource/event-formatting.ts +0 -3
- package/src/components/resource/panels/AssessmentEntry.tsx +1 -1
- package/src/components/resource/panels/AssessmentPanel.css +5 -6
- package/src/components/resource/panels/AssistSection.css +95 -263
- package/src/components/resource/panels/AssistSection.tsx +4 -4
- package/src/components/resource/panels/AssistShell.tsx +12 -4
- package/src/components/resource/panels/CollaborationPanel.css +47 -22
- package/src/components/resource/panels/CollaborationPanel.tsx +93 -2
- package/src/components/resource/panels/CommentEntry.tsx +1 -1
- package/src/components/resource/panels/CommentsPanel.css +0 -16
- package/src/components/resource/panels/HighlightEntry.tsx +1 -1
- package/src/components/resource/panels/HighlightPanel.css +0 -7
- package/src/components/resource/panels/JsonLdPanel.css +0 -4
- package/src/components/resource/panels/ReferenceEntry.tsx +10 -3
- package/src/components/resource/panels/ReferencesPanel.css +0 -12
- package/src/components/resource/panels/ReferencesPanel.tsx +16 -20
- package/src/components/resource/panels/ResourceInfoPanel.css +0 -32
- package/src/components/resource/panels/ResourceInfoPanel.tsx +89 -20
- package/src/components/resource/panels/StatisticsPanel.css +0 -20
- package/src/components/resource/panels/TagEntry.tsx +1 -1
- package/src/components/resource/panels/TaggingPanel.css +0 -4
- package/src/components/resource/panels/TaggingPanel.tsx +2 -10
- package/src/components/resource/panels/UnifiedAnnotationsPanel.css +0 -4
- package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +7 -2
- package/src/components/resource/panels/__tests__/AssessmentEntry.test.tsx +0 -1
- package/src/components/resource/panels/__tests__/AssistSection.test.tsx +48 -50
- package/src/components/resource/panels/__tests__/AssistShell.test.tsx +11 -6
- package/src/components/resource/panels/__tests__/CollaborationPanel.test.tsx +89 -0
- package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +1 -2
- package/src/components/resource/panels/__tests__/HighlightEntry.test.tsx +0 -1
- package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +44 -36
- package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +41 -1
- package/src/components/resource/panels/__tests__/ReferencesPanel.headless.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +67 -54
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +185 -7
- package/src/components/resource/panels/__tests__/TagEntry.test.tsx +0 -1
- package/src/components/settings/SettingsPanel.css +0 -4
- package/src/features/auth/auth.css +2 -26
- package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +0 -65
- package/src/features/resource-compose/components/ResourceComposePage.tsx +17 -130
- package/src/features/resource-compose/state/__tests__/compose-page-state-unit.test.ts +1 -76
- package/src/features/resource-compose/state/compose-page-state-unit.ts +5 -48
- package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +55 -0
- package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +29 -0
- package/src/features/resource-discovery/state/__tests__/discover-state-unit.test.ts +53 -3
- package/src/features/resource-discovery/state/discover-state-unit.ts +29 -8
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +71 -15
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +166 -71
- package/src/features/resource-viewer/hooks/__tests__/useResourceViewedReport.test.tsx +63 -0
- package/src/features/resource-viewer/hooks/useResourceViewedReport.ts +33 -0
- package/src/features/resource-viewer/state/__tests__/resource-viewer-page-state-unit.test.ts +2 -2
- package/src/features/resource-viewer/state/resource-viewer-page-state-unit.ts +1 -1
- package/src/styles/core/buttons.css +3 -1
- package/src/styles/core/forms.css +35 -29
- package/src/styles/core/indicators.css +0 -4
- package/src/styles/core/inputs.css +0 -4
- package/src/styles/core/progress.css +2 -34
- package/src/styles/core/sliders.css +0 -4
- package/src/styles/core/toggles.css +0 -4
- package/src/styles/features/resource-discovery.css +13 -0
- package/src/styles/features/resource-viewer.css +0 -42
- package/src/styles/features/resource.css +0 -4
- package/src/styles/motivations/motivation-assessment.css +2 -2
- package/src/styles/panels/history-panel.css +0 -40
- package/src/styles/panels/user-panel.css +0 -3
- package/src/styles/patterns/panel-helpers.css +0 -27
- package/src/styles/patterns/panel-sections.css +0 -59
- package/src/styles/patterns/panels-base.css +0 -32
- package/translations/ar.json +84 -24
- package/translations/bn.json +84 -24
- package/translations/cs.json +84 -24
- package/translations/da.json +84 -24
- package/translations/de.json +84 -24
- package/translations/el.json +84 -24
- package/translations/en.json +84 -24
- package/translations/es.json +84 -24
- package/translations/fa.json +84 -24
- package/translations/fi.json +84 -24
- package/translations/fr.json +84 -24
- package/translations/he.json +84 -24
- package/translations/hi.json +84 -24
- package/translations/id.json +84 -24
- package/translations/it.json +84 -24
- package/translations/ja.json +84 -24
- package/translations/ko.json +84 -24
- package/translations/ms.json +84 -24
- package/translations/nl.json +84 -24
- package/translations/no.json +84 -24
- package/translations/pl.json +84 -24
- package/translations/pt.json +84 -24
- package/translations/ro.json +84 -24
- package/translations/sv.json +84 -24
- package/translations/th.json +84 -24
- package/translations/tr.json +84 -24
- package/translations/uk.json +84 -24
- package/translations/vi.json +84 -24
- package/translations/zh.json +84 -24
- package/dist/PdfAnnotationCanvas.client-6ZSF6ZTJ.js.map +0 -1
- package/dist/PdfAnnotationCanvas.client-JCJMXWWJ.css.map +0 -1
- package/dist/ar-YTUUGK2O.js.map +0 -1
- package/dist/bn-ASGNGPMD.js.map +0 -1
- package/dist/chunk-BTUG3VJJ.js.map +0 -1
- package/dist/chunk-L3WKRWP3.js.map +0 -1
- package/dist/chunk-O23TLLXW.js.map +0 -1
- package/dist/chunk-OBQOZCJE.js.map +0 -1
- package/dist/chunk-QT7LYM72.js.map +0 -1
- package/dist/chunk-VXASXU4K.js.map +0 -1
- package/dist/chunk-XUDKYAVC.js +0 -21
- package/dist/chunk-XUDKYAVC.js.map +0 -1
- package/dist/cs-WMYVAOZY.js.map +0 -1
- package/dist/da-FSHHN4D6.js.map +0 -1
- package/dist/de-FJFO3TPT.js.map +0 -1
- package/dist/el-XOH4QIAG.js.map +0 -1
- package/dist/en-3FCBW3YD.js.map +0 -1
- package/dist/es-HDX4QI7E.js.map +0 -1
- package/dist/fa-DJ6WTFTJ.js.map +0 -1
- package/dist/fi-4S7H7W7U.js.map +0 -1
- package/dist/fr-6UJSYHXC.js.map +0 -1
- package/dist/he-ZZJOHJX2.js.map +0 -1
- package/dist/hi-HZJTAW7U.js.map +0 -1
- package/dist/id-E3I6ILEM.js.map +0 -1
- package/dist/index.css.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/integrations/css-modules-helper.js.map +0 -1
- package/dist/integrations/styled-components-theme.js.map +0 -1
- package/dist/it-ULAPAD7U.js.map +0 -1
- package/dist/ja-SAHZQ4CZ.js.map +0 -1
- package/dist/ko-DFU2ADQ4.js.map +0 -1
- package/dist/ms-NWL76XNT.js.map +0 -1
- package/dist/nl-ATCM76WN.js.map +0 -1
- package/dist/no-UIAQ5RJF.js.map +0 -1
- package/dist/pl-NOECQDXD.js.map +0 -1
- package/dist/pt-SVWIIUPC.js.map +0 -1
- package/dist/ro-NR2DRTZG.js.map +0 -1
- package/dist/sv-STVJ37JP.js.map +0 -1
- package/dist/test-utils.css.map +0 -1
- package/dist/test-utils.js.map +0 -1
- package/dist/th-UWQR4K54.js.map +0 -1
- package/dist/tr-WNXZSD27.js.map +0 -1
- package/dist/uk-SJRMB47Y.js.map +0 -1
- package/dist/vi-TMRQFES2.js.map +0 -1
- package/dist/zh-QOL3LYEM.js.map +0 -1
- package/src/components/modals/ResourceSearchModal.tsx +0 -224
- package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +0 -159
- package/src/integrations/tailwind-plugin.cjs +0 -224
|
@@ -23,200 +23,327 @@ import { AssistProgress, type AssistProgressTranslations } from '../AssistProgre
|
|
|
23
23
|
|
|
24
24
|
type JobProgress = components['schemas']['JobProgress'];
|
|
25
25
|
|
|
26
|
-
const running = (over: Partial<JobProgress> = {}): JobProgress => ({
|
|
27
|
-
stage: 'analyzing', percentage: 40, message: 'working on it', ...over,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
/** Every required key, echoing its own name; override per test. */
|
|
31
|
-
const T = (over: Partial<AssistProgressTranslations> = {}): AssistProgressTranslations => ({
|
|
32
|
-
cancel: 'tr.cancel',
|
|
33
|
-
inProgress: 'tr.inProgress',
|
|
34
|
-
complete: 'tr.complete',
|
|
35
|
-
failed: 'tr.failed',
|
|
36
|
-
close: 'tr.close',
|
|
37
|
-
paramsTitle: 'tr.paramsTitle',
|
|
38
|
-
processing: (label: string) => `tr.processing(${label})`,
|
|
39
|
-
...over,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
26
|
describe('AssistProgress', () => {
|
|
43
|
-
|
|
27
|
+
// Trimmed by P3 (2026-08-12). Five tests were DELETED rather than adapted
|
|
28
|
+
// because they pinned surfaces this phase removes, and each has a successor
|
|
29
|
+
// in the P3 block below:
|
|
30
|
+
// • paramsTitle copy → H1 (the label is gone; the line is conditional)
|
|
31
|
+
// • "title header only when given one" → A2 (the section header is the title)
|
|
32
|
+
// • "cancel in the header / hidden once complete" → A3 ×2 (one control)
|
|
33
|
+
// • "stage branching" → A8 + A3-ended (D7: no producer emits a
|
|
34
|
+
// terminal stage; terminality is a prop)
|
|
35
|
+
// • "percentage bar only when opted in" → A4 (a bar follows the data, not a flag)
|
|
36
|
+
// What survives here is what P3 does NOT change.
|
|
37
|
+
|
|
38
|
+
it('renders provider-free — no session, no context, no providers', () => {
|
|
39
|
+
// The embeddable contract: this must render standalone. If it ever reaches
|
|
40
|
+
// for a provider, this throws rather than silently degrading.
|
|
44
41
|
const { container } = render(
|
|
45
|
-
<AssistProgress
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
<AssistProgress ended={false} progress={detecting()} dataType="reference" translations={T3()} />,
|
|
43
|
+
);
|
|
44
|
+
expect(container.querySelector('.semiont-assist-progress')).toBeInTheDocument();
|
|
45
|
+
expect(container.querySelector('[data-type="reference"]')).toBeInTheDocument();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('renders the completed entity-type log when data + formatter are present', () => {
|
|
49
|
+
render(
|
|
50
|
+
<AssistProgress ended={false}
|
|
51
|
+
progress={detecting({
|
|
52
|
+
completedItems: [{ value: 'Person', foundCount: 3 }],
|
|
53
|
+
})}
|
|
54
|
+
dataType="reference"
|
|
55
|
+
translations={T3()}
|
|
49
56
|
/>,
|
|
50
57
|
);
|
|
51
|
-
expect(screen.getByText('
|
|
52
|
-
expect(screen.getByText(
|
|
53
|
-
expect(screen.getByText('5')).toBeInTheDocument();
|
|
54
|
-
const root = container.querySelector('.semiont-annotation-progress');
|
|
55
|
-
expect(root).toHaveAttribute('data-type', 'comment');
|
|
56
|
-
expect(root).toHaveAttribute('data-status', 'analyzing');
|
|
58
|
+
expect(screen.getByText('Person:')).toBeInTheDocument();
|
|
59
|
+
expect(screen.getByText('tr.found(3)')).toBeInTheDocument();
|
|
57
60
|
});
|
|
58
61
|
|
|
59
|
-
it('
|
|
62
|
+
it('omits the entity-type log when the formatter is absent (non-reference flows)', () => {
|
|
63
|
+
const tr = T3();
|
|
64
|
+
delete (tr as Partial<AssistProgressTranslations>).found;
|
|
60
65
|
render(
|
|
61
66
|
<AssistProgress
|
|
62
|
-
progress={
|
|
67
|
+
progress={detecting({ completedItems: [{ value: 'Person', foundCount: 3 }] })}
|
|
63
68
|
dataType="comment"
|
|
64
|
-
|
|
69
|
+
ended={false}
|
|
70
|
+
translations={tr}
|
|
65
71
|
/>,
|
|
66
72
|
);
|
|
67
|
-
expect(screen.
|
|
68
|
-
expect(screen.queryByText('Request Parameters:')).not.toBeInTheDocument();
|
|
73
|
+
expect(screen.queryByText('Person:')).toBeNull();
|
|
69
74
|
});
|
|
70
75
|
|
|
71
|
-
it('
|
|
76
|
+
it('the control takes its accessible name from translations, per lifecycle', () => {
|
|
72
77
|
const { rerender } = render(
|
|
73
|
-
<AssistProgress
|
|
78
|
+
<AssistProgress ended={false}
|
|
79
|
+
progress={detecting()} dataType="reference"
|
|
80
|
+
onCancel={vi.fn()} onDismiss={vi.fn()} translations={T3()}
|
|
81
|
+
/>,
|
|
74
82
|
);
|
|
75
|
-
expect(screen.
|
|
76
|
-
rerender(<AssistProgress progress={running()} dataType="comment" translations={T()} />);
|
|
77
|
-
expect(screen.queryByText('Annotating Entity References')).not.toBeInTheDocument();
|
|
78
|
-
});
|
|
83
|
+
expect(screen.getByLabelText('tr.cancel')).toBeInTheDocument();
|
|
79
84
|
|
|
80
|
-
it('shows cancel in the header while running, hides it once complete', async () => {
|
|
81
|
-
// Cancel lives in the title header — both flows that offer cancel
|
|
82
|
-
// (reference detection, generation) render the titled profile.
|
|
83
|
-
const onCancel = vi.fn();
|
|
84
|
-
const tr = T({ title: 'Generating Resource', cancel: 'Cancel Job' });
|
|
85
|
-
const { rerender } = render(
|
|
86
|
-
<AssistProgress progress={running()} dataType="generation" onCancel={onCancel} translations={tr} />,
|
|
87
|
-
);
|
|
88
|
-
await userEvent.click(screen.getByTitle('Cancel Job'));
|
|
89
|
-
expect(onCancel).toHaveBeenCalledOnce();
|
|
90
|
-
rerender(
|
|
91
|
-
<AssistProgress progress={running({ stage: 'complete' })} dataType="generation" onCancel={onCancel} translations={tr} />,
|
|
92
|
-
);
|
|
93
|
-
expect(screen.queryByTitle('Cancel Job')).not.toBeInTheDocument();
|
|
94
|
-
// Error is terminal too — offering cancel on a dead job is misleading and
|
|
95
|
-
// invites redundant cancel requests.
|
|
96
85
|
rerender(
|
|
97
|
-
<AssistProgress
|
|
86
|
+
<AssistProgress
|
|
87
|
+
progress={detecting()} dataType="reference" ended
|
|
88
|
+
onCancel={vi.fn()} onDismiss={vi.fn()} translations={T3()}
|
|
89
|
+
/>,
|
|
98
90
|
);
|
|
99
|
-
expect(screen.
|
|
91
|
+
expect(screen.getByLabelText('tr.close')).toBeInTheDocument();
|
|
100
92
|
});
|
|
101
93
|
|
|
102
|
-
it('
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
94
|
+
it('offers no control at all when the caller wires neither callback', () => {
|
|
95
|
+
render(<AssistProgress ended={false} progress={detecting()} dataType="reference" translations={T3()} />);
|
|
96
|
+
expect(screen.queryByTestId('semiont-assist-control')).toBeNull();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('falls back to the generic in-progress copy when no code has arrived', () => {
|
|
100
|
+
// `JobProgress.message` is optional: a pure liveness heartbeat carries none.
|
|
101
|
+
const noCode = { percentage: 5 } as JobProgress;
|
|
102
|
+
render(<AssistProgress ended={false} progress={noCode} dataType="comment" translations={T3()} />);
|
|
103
|
+
expect(screen.getByTestId('semiont-assist-status').textContent).toBe('tr.inProgress');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
108
|
+
// ASSIST-PROGRESS-CONSOLIDATION P3 — the RED wave (A1-A5, A8).
|
|
109
|
+
//
|
|
110
|
+
// These assert STRUCTURE, never copy: H3 set the wording ("Marking…", subject
|
|
111
|
+
// beneath) and it will be revised from use. A test pinned to a sentence rots on
|
|
112
|
+
// the first edit and teaches the next reader to weaken it.
|
|
113
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
114
|
+
const STATUS = 'semiont-assist-status';
|
|
115
|
+
const SUBJECT = 'semiont-assist-subject';
|
|
116
|
+
const CONTROL = 'semiont-assist-control';
|
|
117
|
+
const BAR = 'semiont-assist-bar';
|
|
118
|
+
const PARAMS = 'semiont-assist-params';
|
|
119
|
+
|
|
120
|
+
/** Post-P3 translations: one function for the coded copy, plus structure keys. */
|
|
121
|
+
const T3 = (over: Partial<AssistProgressTranslations> = {}): AssistProgressTranslations =>
|
|
122
|
+
({
|
|
123
|
+
cancel: 'tr.cancel',
|
|
124
|
+
close: 'tr.close',
|
|
125
|
+
inProgress: 'tr.inProgress',
|
|
126
|
+
message: (m: any) => `tr.code(${m.code})`,
|
|
127
|
+
subject: (current: { kind: string; value: string }, done?: number, total?: number) =>
|
|
128
|
+
done === undefined
|
|
129
|
+
? `tr.subject(${current.kind}:${current.value})`
|
|
130
|
+
: `tr.subject(${current.kind}:${current.value}|${done}/${total})`,
|
|
131
|
+
paramLabel: (code: string) => `tr.param(${code})`,
|
|
132
|
+
found: (n: number) => `tr.found(${n})`,
|
|
133
|
+
...over,
|
|
134
|
+
}) as AssistProgressTranslations;
|
|
135
|
+
|
|
136
|
+
const detecting = (over: Partial<JobProgress> = {}): JobProgress =>
|
|
137
|
+
({
|
|
138
|
+
percentage: 40,
|
|
139
|
+
message: { code: 'detecting-entities', entityType: 'Person' },
|
|
140
|
+
current: { kind: 'entity-type', value: 'Person' },
|
|
141
|
+
processed: 1,
|
|
142
|
+
total: 3,
|
|
143
|
+
...over,
|
|
144
|
+
}) as JobProgress;
|
|
145
|
+
|
|
146
|
+
describe('AssistProgress — P3 consolidation', () => {
|
|
147
|
+
it('A1: renders the subject exactly once for one progress event', () => {
|
|
148
|
+
// Defect 1: the status line and the detail line both called
|
|
149
|
+
// `currentLabel(currentEntityType)`. Post-P1 they produce the IDENTICAL
|
|
150
|
+
// string, which is why two tests here had to use getAllByText. Singular
|
|
151
|
+
// `getByText` throws on multiple matches — that IS the assertion.
|
|
152
|
+
render(<AssistProgress progress={detecting()} dataType="reference" ended={false} translations={T3()} />);
|
|
153
|
+
|
|
154
|
+
expect(screen.getByText(/tr\.subject\(entity-type:Person/)).toBeInTheDocument();
|
|
155
|
+
// And the status line is the CODE's copy, not a second copy of the subject.
|
|
156
|
+
expect(screen.getByTestId(STATUS).textContent).toContain('tr.code(detecting-entities)');
|
|
157
|
+
expect(screen.getByTestId(STATUS).textContent).not.toContain('tr.subject');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('A2: renders no heading of its own — the section header is the title', () => {
|
|
161
|
+
const { container } = render(
|
|
162
|
+
<AssistProgress ended={false} progress={detecting()} dataType="reference" translations={T3()} />,
|
|
112
163
|
);
|
|
113
|
-
expect(
|
|
114
|
-
|
|
115
|
-
|
|
164
|
+
expect(container.querySelector('h1,h2,h3,h4,h5,h6')).toBeNull();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('A3: offers exactly one control, and it means cancel while running', async () => {
|
|
168
|
+
const onCancel = vi.fn();
|
|
169
|
+
const onDismiss = vi.fn();
|
|
170
|
+
render(
|
|
171
|
+
<AssistProgress ended={false}
|
|
172
|
+
progress={detecting()} dataType="reference"
|
|
173
|
+
onCancel={onCancel} onDismiss={onDismiss} translations={T3()}
|
|
174
|
+
/>,
|
|
116
175
|
);
|
|
117
|
-
|
|
176
|
+
const controls = screen.getAllByTestId(CONTROL);
|
|
177
|
+
expect(controls).toHaveLength(1);
|
|
178
|
+
|
|
179
|
+
await userEvent.click(controls[0]!);
|
|
180
|
+
expect(onCancel).toHaveBeenCalledTimes(1);
|
|
181
|
+
expect(onDismiss).not.toHaveBeenCalled();
|
|
118
182
|
});
|
|
119
183
|
|
|
120
|
-
it('
|
|
184
|
+
it('A3: the same single control means dismiss once the run has ENDED', async () => {
|
|
185
|
+
// D7: terminality is the owner's fact, arriving as a prop. The component
|
|
186
|
+
// never reads `progress.stage` — no producer emits a terminal stage.
|
|
187
|
+
const onCancel = vi.fn();
|
|
188
|
+
const onDismiss = vi.fn();
|
|
121
189
|
render(
|
|
122
190
|
<AssistProgress
|
|
123
|
-
progress={
|
|
124
|
-
dataType="reference"
|
|
191
|
+
progress={detecting({ message: { code: 'complete-created', count: 7, kind: 'reference' } } as any)}
|
|
192
|
+
dataType="reference" ended
|
|
193
|
+
onCancel={onCancel} onDismiss={onDismiss} translations={T3()}
|
|
125
194
|
/>,
|
|
126
195
|
);
|
|
127
|
-
|
|
128
|
-
expect(
|
|
196
|
+
const controls = screen.getAllByTestId(CONTROL);
|
|
197
|
+
expect(controls).toHaveLength(1);
|
|
198
|
+
|
|
199
|
+
await userEvent.click(controls[0]!);
|
|
200
|
+
expect(onDismiss).toHaveBeenCalledTimes(1);
|
|
201
|
+
expect(onCancel).not.toHaveBeenCalled();
|
|
129
202
|
});
|
|
130
203
|
|
|
131
|
-
it('
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
204
|
+
it('A4: the REFERENCE flow renders a fraction and a bar — data it already receives', () => {
|
|
205
|
+
render(<AssistProgress ended={false} progress={detecting()} dataType="reference" translations={T3()} />);
|
|
206
|
+
|
|
207
|
+
expect(screen.getByTestId(SUBJECT).textContent).toBe('tr.subject(entity-type:Person|1/3)');
|
|
208
|
+
expect(screen.getByTestId(BAR)).toBeInTheDocument();
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('A4: the TAG flow gets a bar from percentage alone — it sends no fraction', () => {
|
|
212
|
+
// The REAL tag shape. `processTagJob` emits percentage and nothing else:
|
|
213
|
+
// no currentCategory, no processed, no total. An
|
|
214
|
+
// earlier version of this test invented those fields and so "passed" while
|
|
215
|
+
// the tag flow had silently lost its bar (PR #1179 review).
|
|
216
|
+
render(
|
|
217
|
+
<AssistProgress ended={false}
|
|
218
|
+
progress={{
|
|
219
|
+
percentage: 60,
|
|
220
|
+
message: { code: 'creating-tag-annotations', count: 4 },
|
|
221
|
+
} as JobProgress}
|
|
222
|
+
dataType="tag" translations={T3()}
|
|
136
223
|
/>,
|
|
137
224
|
);
|
|
138
|
-
expect(screen.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
225
|
+
expect(screen.getByTestId(BAR)).toBeInTheDocument();
|
|
226
|
+
// No fraction to show, so no subject line — and that is correct, not a gap.
|
|
227
|
+
expect(screen.queryByTestId(SUBJECT)).toBeNull();
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('A4: the bar is unconditional — percentage is required on every event', () => {
|
|
231
|
+
// Replaces a test that asserted "no bar when nothing fills it". That state
|
|
232
|
+
// is unreachable: `percentage` is a REQUIRED field on JobProgress, so there
|
|
233
|
+
// is always something to fill a bar with. Asserting an impossible case is
|
|
234
|
+
// how the tag regression hid.
|
|
235
|
+
render(
|
|
236
|
+
<AssistProgress ended={false}
|
|
237
|
+
progress={{ percentage: 10, message: { code: 'loading' } } as JobProgress}
|
|
238
|
+
dataType="comment" translations={T3()}
|
|
143
239
|
/>,
|
|
144
240
|
);
|
|
145
|
-
expect(screen.
|
|
146
|
-
expect(screen.getByText(/2\/5/)).toBeInTheDocument();
|
|
241
|
+
expect(screen.getByTestId(BAR)).toBeInTheDocument();
|
|
147
242
|
});
|
|
148
243
|
|
|
149
|
-
it('
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
// path may render a hardcoded "Processing: ".
|
|
153
|
-
const { rerender } = render(
|
|
154
|
-
<AssistProgress progress={running({ currentEntityType: 'Location' })} dataType="reference" translations={T()} />,
|
|
244
|
+
it('A5: every rendered string is traceable to translations', () => {
|
|
245
|
+
const { container } = render(
|
|
246
|
+
<AssistProgress ended={false} progress={detecting()} dataType="reference" translations={T3()} />,
|
|
155
247
|
);
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
248
|
+
// Key-echo strings mean any text NOT starting `tr.` came from the component.
|
|
249
|
+
const stray = Array.from(container.querySelectorAll('*'))
|
|
250
|
+
.filter((el) => el.children.length === 0)
|
|
251
|
+
.map((el) => el.textContent?.trim() ?? '')
|
|
252
|
+
.filter((t) => t.length > 0 && !t.startsWith('tr.') && !/^[\s✨✅✓×✕()0-9/of]+$/.test(t));
|
|
253
|
+
expect(stray).toEqual([]);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('A8: renders no failure UI of its own — job:fail owns that surface', () => {
|
|
257
|
+
// Revised by D7. `stage: 'error'` has no producer anywhere in the repo;
|
|
258
|
+
// failure reaches the user through useOutcomeToasts' job:fail handler.
|
|
259
|
+
// The widget must not resurrect a branch for a state it never sees.
|
|
260
|
+
render(
|
|
261
|
+
<AssistProgress ended={false}
|
|
262
|
+
progress={detecting({ stage: 'error' } as Partial<JobProgress>)}
|
|
263
|
+
dataType="reference" translations={T3()}
|
|
264
|
+
/>,
|
|
159
265
|
);
|
|
160
|
-
|
|
161
|
-
expect(screen.
|
|
266
|
+
// Still the ordinary running render — no special-cased error text.
|
|
267
|
+
expect(screen.getByTestId(STATUS).textContent).toContain('tr.code(detecting-entities)');
|
|
162
268
|
});
|
|
163
269
|
|
|
164
|
-
it('renders the
|
|
165
|
-
|
|
166
|
-
|
|
270
|
+
it('the outcome link renders only in the ended frame (GENERATE-FROM-RESOURCE P2, D8)', async () => {
|
|
271
|
+
// The label is the resource's name — user content, deliberately NOT a
|
|
272
|
+
// translation. While the run is live there is no outcome to offer, even if
|
|
273
|
+
// a caller wires the prop early.
|
|
274
|
+
const onOpen = vi.fn();
|
|
275
|
+
const outcome = { label: 'Summary of PB', onOpen };
|
|
276
|
+
const { rerender } = render(
|
|
277
|
+
<AssistProgress ended={false} progress={detecting()} dataType="generation"
|
|
278
|
+
outcome={outcome} translations={T3()} />,
|
|
167
279
|
);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
expect(fill).toHaveStyle({ width: '40%' });
|
|
280
|
+
expect(screen.queryByText('Summary of PB')).toBeNull();
|
|
281
|
+
|
|
171
282
|
rerender(
|
|
172
|
-
<AssistProgress progress={
|
|
283
|
+
<AssistProgress ended progress={detecting()} dataType="generation"
|
|
284
|
+
outcome={outcome} translations={T3()} />,
|
|
173
285
|
);
|
|
174
|
-
|
|
286
|
+
await userEvent.click(screen.getByRole('button', { name: 'Summary of PB' }));
|
|
287
|
+
expect(onOpen).toHaveBeenCalledTimes(1);
|
|
175
288
|
});
|
|
176
289
|
|
|
177
|
-
it('
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
290
|
+
it('H1: the params line appears only when it adds information', () => {
|
|
291
|
+
// The discriminator is the COUNT, not the copy — splitting a localized
|
|
292
|
+
// string on commas to decide whether to show it would be its own defect.
|
|
293
|
+
const oneType = detecting({
|
|
294
|
+
requestParams: [{ label: 'entity-types', value: 'Person' }],
|
|
295
|
+
total: 1,
|
|
296
|
+
});
|
|
297
|
+
const { unmount } = render(
|
|
298
|
+
<AssistProgress ended={false} progress={oneType} dataType="reference" translations={T3()} />,
|
|
185
299
|
);
|
|
186
|
-
expect(screen.
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
300
|
+
expect(screen.queryByTestId(PARAMS)).toBeNull();
|
|
301
|
+
unmount();
|
|
302
|
+
|
|
303
|
+
const many = detecting({
|
|
304
|
+
requestParams: [{ label: 'entity-types', value: 'Person, Organization, Location' }],
|
|
305
|
+
total: 3,
|
|
306
|
+
});
|
|
307
|
+
render(<AssistProgress ended={false} progress={many} dataType="reference" translations={T3()} />);
|
|
308
|
+
expect(screen.getByTestId(PARAMS)).toBeInTheDocument();
|
|
190
309
|
});
|
|
310
|
+
});
|
|
191
311
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
312
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
313
|
+
// GENERATION-ARRIVAL P1 — the honest ended frame. The producer's terminal
|
|
314
|
+
// 100% frame races job:complete and can lose (its emit is a fire-and-forget
|
|
315
|
+
// heartbeat); terminality is the OWNER's fact (D7), so the ended rendering
|
|
316
|
+
// stops trusting the last payload: full bar, and the owner's terminal
|
|
317
|
+
// sentence when it supplies one.
|
|
318
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
319
|
+
describe('AssistProgress — the honest ended frame (GENERATION-ARRIVAL P1)', () => {
|
|
320
|
+
it('A1: an ended frame renders a FULL bar whatever the last payload said', () => {
|
|
321
|
+
const { container } = render(
|
|
322
|
+
<AssistProgress ended progress={detecting({ percentage: 95 })} dataType="generation" translations={T3()} />,
|
|
198
323
|
);
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
324
|
+
const fill = container.querySelector('.semiont-progress-bar__fill') as HTMLElement;
|
|
325
|
+
expect(fill.style.width).toBe('100%');
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('a live frame keeps the payload percentage', () => {
|
|
329
|
+
const { container } = render(
|
|
330
|
+
<AssistProgress ended={false} progress={detecting({ percentage: 95 })} dataType="generation" translations={T3()} />,
|
|
202
331
|
);
|
|
203
|
-
|
|
332
|
+
const fill = container.querySelector('.semiont-progress-bar__fill') as HTMLElement;
|
|
333
|
+
expect(fill.style.width).toBe('95%');
|
|
204
334
|
});
|
|
205
335
|
|
|
206
|
-
it('
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
// "callback present → affordance rendered".
|
|
210
|
-
const onDismiss = vi.fn();
|
|
211
|
-
const { rerender } = render(
|
|
212
|
-
<AssistProgress progress={running()} dataType="highlight" translations={T({ close: 'Close' })} />,
|
|
336
|
+
it('endedMessage replaces the stale payload copy once ended', () => {
|
|
337
|
+
render(
|
|
338
|
+
<AssistProgress ended endedMessage="tr.ended" progress={detecting({ percentage: 95 })} dataType="generation" translations={T3()} />,
|
|
213
339
|
);
|
|
214
|
-
expect(screen.
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
340
|
+
expect(screen.getByTestId(STATUS).textContent).toBe('tr.ended');
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('endedMessage is inert while the run is live', () => {
|
|
344
|
+
render(
|
|
345
|
+
<AssistProgress ended={false} endedMessage="tr.ended" progress={detecting()} dataType="generation" translations={T3()} />,
|
|
218
346
|
);
|
|
219
|
-
|
|
220
|
-
expect(onDismiss).toHaveBeenCalledOnce();
|
|
347
|
+
expect(screen.getByTestId(STATUS).textContent).toBe('tr.code(detecting-entities)');
|
|
221
348
|
});
|
|
222
349
|
});
|
|
@@ -133,10 +133,6 @@
|
|
|
133
133
|
color: var(--semiont-text-tertiary);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
[data-theme="dark"] .semiont-annotation-entry__char-count {
|
|
137
|
-
color: var(--semiont-text-tertiary);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
136
|
/* Tags within entries */
|
|
141
137
|
.semiont-annotation-entry__tags {
|
|
142
138
|
display: flex;
|
|
@@ -105,10 +105,6 @@
|
|
|
105
105
|
background-color: var(--semiont-bg-tertiary);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
[data-theme="dark"] .semiont-chip--selectable[data-selected="false"]:hover {
|
|
109
|
-
background-color: var(--semiont-bg-tertiary);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
108
|
.semiont-chip--selectable[data-selected="true"] {
|
|
113
109
|
background-color: rgba(59, 130, 246, 0.1);
|
|
114
110
|
color: var(--semiont-color-blue-700);
|
|
@@ -128,7 +128,7 @@ export function AnnotationOverlay({
|
|
|
128
128
|
className="semiont-annotation-overlay__shape"
|
|
129
129
|
data-hovered={isHovered ? 'true' : 'false'}
|
|
130
130
|
data-selected={isSelected ? 'true' : 'false'}
|
|
131
|
-
onClick={(e) => session?.client.browse.click(annotation.id,
|
|
131
|
+
onClick={(e) => session?.client.browse.click(annotation.id, e.currentTarget.getBoundingClientRect())}
|
|
132
132
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
133
133
|
onMouseLeave={handleMouseLeave}
|
|
134
134
|
/>
|
|
@@ -141,7 +141,7 @@ export function AnnotationOverlay({
|
|
|
141
141
|
style={{ userSelect: 'none' }}
|
|
142
142
|
onClick={(e) => {
|
|
143
143
|
e.stopPropagation();
|
|
144
|
-
session?.client.browse.click(annotation.id,
|
|
144
|
+
session?.client.browse.click(annotation.id, e.currentTarget.getBoundingClientRect());
|
|
145
145
|
}}
|
|
146
146
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
147
147
|
onMouseLeave={handleMouseLeave}
|
|
@@ -175,7 +175,7 @@ export function AnnotationOverlay({
|
|
|
175
175
|
className="semiont-annotation-overlay__shape"
|
|
176
176
|
data-hovered={isHovered ? 'true' : 'false'}
|
|
177
177
|
data-selected={isSelected ? 'true' : 'false'}
|
|
178
|
-
onClick={(e) => session?.client.browse.click(annotation.id,
|
|
178
|
+
onClick={(e) => session?.client.browse.click(annotation.id, e.currentTarget.getBoundingClientRect())}
|
|
179
179
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
180
180
|
onMouseLeave={handleMouseLeave}
|
|
181
181
|
/>
|
|
@@ -188,7 +188,7 @@ export function AnnotationOverlay({
|
|
|
188
188
|
style={{ userSelect: 'none' }}
|
|
189
189
|
onClick={(e) => {
|
|
190
190
|
e.stopPropagation();
|
|
191
|
-
session?.client.browse.click(annotation.id,
|
|
191
|
+
session?.client.browse.click(annotation.id, e.currentTarget.getBoundingClientRect());
|
|
192
192
|
}}
|
|
193
193
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
194
194
|
onMouseLeave={handleMouseLeave}
|
|
@@ -235,7 +235,7 @@ export function AnnotationOverlay({
|
|
|
235
235
|
className="semiont-annotation-overlay__shape"
|
|
236
236
|
data-hovered={isHovered ? 'true' : 'false'}
|
|
237
237
|
data-selected={isSelected ? 'true' : 'false'}
|
|
238
|
-
onClick={(e) => session?.client.browse.click(annotation.id,
|
|
238
|
+
onClick={(e) => session?.client.browse.click(annotation.id, e.currentTarget.getBoundingClientRect())}
|
|
239
239
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
240
240
|
onMouseLeave={handleMouseLeave}
|
|
241
241
|
/>
|
|
@@ -248,7 +248,7 @@ export function AnnotationOverlay({
|
|
|
248
248
|
style={{ userSelect: 'none' }}
|
|
249
249
|
onClick={(e) => {
|
|
250
250
|
e.stopPropagation();
|
|
251
|
-
session?.client.browse.click(annotation.id,
|
|
251
|
+
session?.client.browse.click(annotation.id, e.currentTarget.getBoundingClientRect());
|
|
252
252
|
}}
|
|
253
253
|
onMouseEnter={() => handleMouseEnter(annotation.id)}
|
|
254
254
|
onMouseLeave={handleMouseLeave}
|
|
@@ -221,7 +221,7 @@ export function SvgDrawingCanvas({
|
|
|
221
221
|
});
|
|
222
222
|
|
|
223
223
|
if (clickedAnnotation) {
|
|
224
|
-
session?.client.browse.click(clickedAnnotation.id,
|
|
224
|
+
session?.client.browse.click(clickedAnnotation.id, hitRect);
|
|
225
225
|
setIsDrawing(false);
|
|
226
226
|
setStartPoint(null);
|
|
227
227
|
setCurrentPoint(null);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* A1 anchor thread (HEADLESS-ANNOTATION-PANELS Phase 3) — image overlay pin.
|
|
3
3
|
*
|
|
4
4
|
* The overlay's shape elements own their on-screen geometry: a click passes
|
|
5
|
-
* the element's viewport rect as browse.click's
|
|
5
|
+
* the element's viewport rect as browse.click's second argument so hosts can
|
|
6
6
|
* anchor popovers. Runtime-only view geometry; no schema involvement.
|
|
7
7
|
*/
|
|
8
8
|
import { describe, it, expect, vi } from 'vitest';
|
|
@@ -43,7 +43,7 @@ function sessionDouble() {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
describe('AnnotationOverlay — anchorRect on click', () => {
|
|
46
|
-
it('passes the shape element rect as browse.click
|
|
46
|
+
it('passes the shape element rect as browse.click second argument', () => {
|
|
47
47
|
const { session, click } = sessionDouble();
|
|
48
48
|
|
|
49
49
|
const { container } = render(
|
|
@@ -65,8 +65,7 @@ describe('AnnotationOverlay — anchorRect on click', () => {
|
|
|
65
65
|
|
|
66
66
|
expect(click).toHaveBeenCalledTimes(1);
|
|
67
67
|
expect(click.mock.calls[0]?.[0]).toBe('img-ann-1');
|
|
68
|
-
|
|
69
|
-
const anchorRect = click.mock.calls[0]?.[2];
|
|
68
|
+
const anchorRect = click.mock.calls[0]?.[1];
|
|
70
69
|
expect(anchorRect).toBeDefined();
|
|
71
70
|
expect(typeof anchorRect.width).toBe('number');
|
|
72
71
|
expect(typeof anchorRect.left).toBe('number');
|
|
@@ -77,7 +76,7 @@ describe('AnnotationOverlay — anchorRect on click', () => {
|
|
|
77
76
|
it.each([
|
|
78
77
|
['circle', svgAnnotation('img-circle-1', 'commenting', '<svg><circle cx="30" cy="30" r="10"/></svg>')],
|
|
79
78
|
['polygon', svgAnnotation('img-polygon-1', 'assessing', '<svg><polygon points="10,10 30,10 20,30"/></svg>')],
|
|
80
|
-
] as const)('passes the %s element rect as browse.click
|
|
79
|
+
] as const)('passes the %s element rect as browse.click second argument', (_kind, annotation) => {
|
|
81
80
|
const { session, click } = sessionDouble();
|
|
82
81
|
|
|
83
82
|
const { container } = render(
|
|
@@ -99,12 +98,12 @@ describe('AnnotationOverlay — anchorRect on click', () => {
|
|
|
99
98
|
|
|
100
99
|
expect(click).toHaveBeenCalledTimes(1);
|
|
101
100
|
expect(click.mock.calls[0]?.[0]).toBe(annotation.id);
|
|
102
|
-
const anchorRect = click.mock.calls[0]?.[
|
|
101
|
+
const anchorRect = click.mock.calls[0]?.[1];
|
|
103
102
|
expect(anchorRect).toBeDefined();
|
|
104
103
|
expect(typeof anchorRect.width).toBe('number');
|
|
105
104
|
});
|
|
106
105
|
|
|
107
|
-
it('passes the status-indicator element rect as browse.click
|
|
106
|
+
it('passes the status-indicator element rect as browse.click second argument', () => {
|
|
108
107
|
// Only references render a status indicator (🔗/❓).
|
|
109
108
|
const reference = svgAnnotation('img-ref-1', 'linking', '<svg><rect x="10" y="10" width="20" height="20"/></svg>');
|
|
110
109
|
const { session, click } = sessionDouble();
|
|
@@ -129,8 +128,7 @@ describe('AnnotationOverlay — anchorRect on click', () => {
|
|
|
129
128
|
// stopPropagation: the indicator's own handler emits, exactly once.
|
|
130
129
|
expect(click).toHaveBeenCalledTimes(1);
|
|
131
130
|
expect(click.mock.calls[0]?.[0]).toBe('img-ref-1');
|
|
132
|
-
|
|
133
|
-
const anchorRect = click.mock.calls[0]?.[2];
|
|
131
|
+
const anchorRect = click.mock.calls[0]?.[1];
|
|
134
132
|
expect(anchorRect).toBeDefined();
|
|
135
133
|
expect(typeof anchorRect.width).toBe('number');
|
|
136
134
|
});
|
|
@@ -100,8 +100,7 @@ describe('SvgDrawingCanvas — drawing-path hit-test anchorRect', () => {
|
|
|
100
100
|
|
|
101
101
|
expect(click).toHaveBeenCalledTimes(1);
|
|
102
102
|
expect(click.mock.calls[0]?.[0]).toBe('canvas-ann-1');
|
|
103
|
-
expect(click.mock.calls[0]?.[1]).
|
|
104
|
-
expect(click.mock.calls[0]?.[2]).toEqual({
|
|
103
|
+
expect(click.mock.calls[0]?.[1]).toEqual({
|
|
105
104
|
x: 20, y: 20, left: 20, top: 20, width: 40, height: 40, right: 60, bottom: 60,
|
|
106
105
|
});
|
|
107
106
|
});
|