@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
|
@@ -1,124 +1,99 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
import { WizardFooter } from './WizardFooter';
|
|
2
3
|
|
|
3
|
-
import type {
|
|
4
|
-
import { ContextSummary } from './ContextSummary';
|
|
5
|
-
import type { ContextSummaryTranslations } from './ContextSummary';
|
|
4
|
+
import type { ResourceDescriptor } from '@semiont/core';
|
|
6
5
|
|
|
7
6
|
export type ScoredResult = ResourceDescriptor & {
|
|
8
7
|
score?: number;
|
|
9
8
|
matchReason?: string;
|
|
10
9
|
};
|
|
11
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Pure RESULTS (GEP D10, amended): ranked rows with scores and Link buttons.
|
|
13
|
+
* The evidence display is the HOST's job — the wizard stacks the full
|
|
14
|
+
* display-only GatherContextStep (with the collapsed strategy band) above
|
|
15
|
+
* this step, the same grammar as the configure steps.
|
|
16
|
+
*/
|
|
12
17
|
export interface SearchResultsStepProps {
|
|
13
18
|
results: ScoredResult[];
|
|
14
|
-
context: GatheredContext;
|
|
15
19
|
onLink: (resourceId: string) => void;
|
|
16
20
|
onBack: () => void;
|
|
17
|
-
onCancel: () => void;
|
|
18
21
|
translations: {
|
|
19
22
|
noResults: string;
|
|
20
23
|
link: string;
|
|
21
24
|
back: string;
|
|
22
|
-
cancel: string;
|
|
23
25
|
score: string;
|
|
24
|
-
}
|
|
26
|
+
};
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
export function SearchResultsStep({
|
|
28
30
|
results,
|
|
29
|
-
context,
|
|
30
31
|
onLink,
|
|
31
32
|
onBack,
|
|
32
|
-
onCancel,
|
|
33
33
|
translations: t,
|
|
34
34
|
}: SearchResultsStepProps) {
|
|
35
35
|
return (
|
|
36
36
|
<>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
37
|
+
<div className="semiont-search-results">
|
|
38
|
+
{results.length === 0 ? (
|
|
39
|
+
<div className="semiont-modal__empty-state" style={{ textAlign: 'center', padding: '2rem 0' }}>
|
|
40
|
+
{t.noResults}
|
|
41
|
+
</div>
|
|
42
|
+
) : (
|
|
43
|
+
results.map((result) => {
|
|
44
|
+
const id = result['@id'];
|
|
45
|
+
return (
|
|
46
|
+
<div
|
|
47
|
+
key={id}
|
|
48
|
+
style={{
|
|
49
|
+
padding: '0.75rem',
|
|
50
|
+
borderBottom: '1px solid var(--semiont-border-primary)',
|
|
51
|
+
display: 'flex',
|
|
52
|
+
alignItems: 'center',
|
|
53
|
+
justifyContent: 'space-between',
|
|
54
|
+
gap: '0.75rem',
|
|
55
|
+
}}
|
|
56
|
+
>
|
|
57
|
+
<div style={{ flex: 1, minWidth: 0 }}>
|
|
58
|
+
<div style={{ fontWeight: 500, color: 'var(--semiont-text-primary)' }}>
|
|
59
|
+
{result.name}
|
|
60
|
+
</div>
|
|
61
|
+
{result.description && (
|
|
62
|
+
<div style={{
|
|
63
|
+
fontSize: 'var(--semiont-text-sm)',
|
|
64
|
+
color: 'var(--semiont-text-secondary)',
|
|
65
|
+
marginTop: '0.25rem',
|
|
66
|
+
overflow: 'hidden',
|
|
67
|
+
textOverflow: 'ellipsis',
|
|
68
|
+
whiteSpace: 'nowrap',
|
|
69
|
+
}}>
|
|
70
|
+
{result.description}
|
|
68
71
|
</div>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
whiteSpace: 'nowrap',
|
|
77
|
-
}}>
|
|
78
|
-
{result.description}
|
|
79
|
-
</div>
|
|
72
|
+
)}
|
|
73
|
+
<div style={{ fontSize: 'var(--semiont-text-xs)', color: 'var(--semiont-text-tertiary)', marginTop: '0.25rem', display: 'flex', gap: '0.5rem' }}>
|
|
74
|
+
{result.score !== undefined && (
|
|
75
|
+
<span>{t.score}: {result.score}</span>
|
|
76
|
+
)}
|
|
77
|
+
{result.matchReason && (
|
|
78
|
+
<span>{result.matchReason}</span>
|
|
80
79
|
)}
|
|
81
|
-
<div style={{ fontSize: 'var(--semiont-text-xs)', color: 'var(--semiont-text-tertiary)', marginTop: '0.25rem', display: 'flex', gap: '0.5rem' }}>
|
|
82
|
-
{result.score !== undefined && (
|
|
83
|
-
<span>{t.score}: {result.score}</span>
|
|
84
|
-
)}
|
|
85
|
-
{result.matchReason && (
|
|
86
|
-
<span>{result.matchReason}</span>
|
|
87
|
-
)}
|
|
88
|
-
</div>
|
|
89
80
|
</div>
|
|
90
|
-
<button
|
|
91
|
-
type="button"
|
|
92
|
-
onClick={() => onLink(id)}
|
|
93
|
-
className="semiont-button--primary"
|
|
94
|
-
style={{ flexShrink: 0 }}
|
|
95
|
-
>
|
|
96
|
-
🔗 {t.link}
|
|
97
|
-
</button>
|
|
98
81
|
</div>
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
82
|
+
<button
|
|
83
|
+
type="button"
|
|
84
|
+
onClick={() => onLink(id)}
|
|
85
|
+
className="semiont-button--primary"
|
|
86
|
+
style={{ flexShrink: 0 }}
|
|
87
|
+
>
|
|
88
|
+
🔗 {t.link}
|
|
89
|
+
</button>
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
})
|
|
93
|
+
)}
|
|
103
94
|
</div>
|
|
104
95
|
|
|
105
|
-
{
|
|
106
|
-
<div className="semiont-modal__actions" style={{ paddingTop: '0.5rem' }}>
|
|
107
|
-
<button
|
|
108
|
-
type="button"
|
|
109
|
-
onClick={onCancel}
|
|
110
|
-
className="semiont-button--secondary semiont-button--flex"
|
|
111
|
-
>
|
|
112
|
-
✕ {t.cancel}
|
|
113
|
-
</button>
|
|
114
|
-
<button
|
|
115
|
-
type="button"
|
|
116
|
-
onClick={onBack}
|
|
117
|
-
className="semiont-button--secondary semiont-button--flex"
|
|
118
|
-
>
|
|
119
|
-
◀ {t.back}
|
|
120
|
-
</button>
|
|
121
|
-
</div>
|
|
96
|
+
<WizardFooter backLabel={t.back} onBack={onBack} />
|
|
122
97
|
</>
|
|
123
98
|
);
|
|
124
99
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The one wizard footer (WIZARD-NAVIGATION D7).
|
|
5
|
+
*
|
|
6
|
+
* Four steps used to hand-build this markup, which is how four different footers
|
|
7
|
+
* happened: three carried a `✕ Cancel` that duplicated the corner control, one had no
|
|
8
|
+
* footer at all, and every button stretched to equal width so "leave" read as a peer of
|
|
9
|
+
* "do the thing".
|
|
10
|
+
*
|
|
11
|
+
* The grammar, settled in D1/D2:
|
|
12
|
+
* • Dismissal is NOT here. The corner ✕, Esc and the backdrop are the way out, and
|
|
13
|
+
* they are the same on every step including the one with no footer.
|
|
14
|
+
* • Retreat is chrome: quiet, left, no emoji.
|
|
15
|
+
* • Advance is the decision: filled, right, one verb.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export interface WizardFooterPrimary {
|
|
19
|
+
label: string;
|
|
20
|
+
/** Shown while `pending`; falls back to `label`. */
|
|
21
|
+
pendingLabel?: string;
|
|
22
|
+
pending?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* The action's PRECONDITION is not met yet (e.g. the review step waiting on
|
|
25
|
+
* gather). Unlike `pending` — the action itself running — this disables only
|
|
26
|
+
* the primary: retreat must stay live while a precondition loads.
|
|
27
|
+
*/
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
type: 'submit' | 'button';
|
|
30
|
+
onClick?: () => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface WizardFooterProps {
|
|
34
|
+
/** Omit both to render no retreat — correct on the first step. */
|
|
35
|
+
backLabel?: string;
|
|
36
|
+
onBack?: () => void;
|
|
37
|
+
/** Omit on steps whose action lives elsewhere (search results links per row). */
|
|
38
|
+
primary?: WizardFooterPrimary;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function WizardFooter({ backLabel, onBack, primary }: WizardFooterProps) {
|
|
42
|
+
const pending = primary?.pending ?? false;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div className="semiont-modal__actions semiont-modal__actions--wizard">
|
|
46
|
+
{onBack && backLabel && (
|
|
47
|
+
<button
|
|
48
|
+
type="button"
|
|
49
|
+
onClick={onBack}
|
|
50
|
+
className="semiont-button--secondary semiont-wizard-footer__back"
|
|
51
|
+
disabled={pending}
|
|
52
|
+
>
|
|
53
|
+
◀ {backLabel}
|
|
54
|
+
</button>
|
|
55
|
+
)}
|
|
56
|
+
{primary && (
|
|
57
|
+
<button
|
|
58
|
+
type={primary.type}
|
|
59
|
+
{...(primary.onClick ? { onClick: primary.onClick } : {})}
|
|
60
|
+
className="semiont-button--primary"
|
|
61
|
+
disabled={pending || (primary.disabled ?? false)}
|
|
62
|
+
data-pending={pending ? 'true' : 'false'}
|
|
63
|
+
>
|
|
64
|
+
{pending ? (primary.pendingLabel ?? primary.label) : primary.label}
|
|
65
|
+
</button>
|
|
66
|
+
)}
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COMPOSE-IN-MODAL P1 — the compose strategy's form, as a pure step.
|
|
3
|
+
*
|
|
4
|
+
* The from-reference slice of the compose page, lifted: name, save location,
|
|
5
|
+
* entity types (read-only tags when the reference fixed them, picker
|
|
6
|
+
* otherwise), language, and the editor. Deliberately NONE of the page's
|
|
7
|
+
* upload/format/encoding machinery — the fences reference mode already
|
|
8
|
+
* proved are here as absence pins. The draft is CONTROLLED by the host
|
|
9
|
+
* (WIZARD-NAVIGATION D3: stepping Back must not discard typed work), and
|
|
10
|
+
* the evidence display is the host's job (plan A3: no context redux here).
|
|
11
|
+
*/
|
|
12
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
13
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
14
|
+
import '@testing-library/jest-dom';
|
|
15
|
+
import { ComposeStep, type ComposeDraft } from '../ComposeStep';
|
|
16
|
+
|
|
17
|
+
// Mock CodeMirrorRenderer to avoid CodeMirror dependencies (same mock the
|
|
18
|
+
// compose page's own tests use).
|
|
19
|
+
vi.mock('../../CodeMirrorRenderer', () => ({
|
|
20
|
+
CodeMirrorRenderer: ({ content, onChange, editable }: any) => (
|
|
21
|
+
<textarea
|
|
22
|
+
data-testid="code-editor"
|
|
23
|
+
value={content}
|
|
24
|
+
disabled={!editable}
|
|
25
|
+
onChange={(e) => onChange?.(e.target.value)}
|
|
26
|
+
/>
|
|
27
|
+
),
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
const T = {
|
|
31
|
+
resourceTitle: 'New resource title',
|
|
32
|
+
resourceTitlePlaceholder: 'Title…',
|
|
33
|
+
saveLocation: 'Save location',
|
|
34
|
+
entityTypes: 'Entity types',
|
|
35
|
+
language: 'Language',
|
|
36
|
+
contentLabel: 'Content',
|
|
37
|
+
back: 'Back',
|
|
38
|
+
createAndLink: 'Create & Link',
|
|
39
|
+
creatingAndLinking: 'Creating…',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const DRAFT: ComposeDraft = {
|
|
43
|
+
name: 'Black Hawk',
|
|
44
|
+
storagePath: 'people/black-hawk.md',
|
|
45
|
+
content: 'Sauk leader.',
|
|
46
|
+
entityTypes: [],
|
|
47
|
+
language: 'en',
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
function renderStep(over: Partial<React.ComponentProps<typeof ComposeStep>> = {}) {
|
|
51
|
+
const onDraftChange = vi.fn();
|
|
52
|
+
const onBack = vi.fn();
|
|
53
|
+
const onCompose = vi.fn<(p: unknown) => Promise<void>>().mockResolvedValue(undefined);
|
|
54
|
+
const utils = render(
|
|
55
|
+
<ComposeStep
|
|
56
|
+
draft={DRAFT}
|
|
57
|
+
onDraftChange={onDraftChange}
|
|
58
|
+
referenceEntityTypes={['Person']}
|
|
59
|
+
entityTypeOptions={['Person', 'Topic', 'Location']}
|
|
60
|
+
showLineNumbers={false}
|
|
61
|
+
hoverDelayMs={300}
|
|
62
|
+
onBack={onBack}
|
|
63
|
+
onCompose={onCompose}
|
|
64
|
+
translations={T}
|
|
65
|
+
{...over}
|
|
66
|
+
/>,
|
|
67
|
+
);
|
|
68
|
+
return { ...utils, onDraftChange, onBack, onCompose };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
describe('ComposeStep — the from-reference slice, and nothing else', () => {
|
|
72
|
+
it('renders the draft: name, save location, content, language', () => {
|
|
73
|
+
renderStep();
|
|
74
|
+
expect(screen.getByLabelText(T.resourceTitle)).toHaveValue('Black Hawk');
|
|
75
|
+
expect(screen.getByLabelText(T.saveLocation)).toHaveValue('people/black-hawk.md');
|
|
76
|
+
expect(screen.getByTestId('code-editor')).toHaveValue('Sauk leader.');
|
|
77
|
+
expect(screen.getByLabelText(T.language)).toBeInTheDocument();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('edits flow through onDraftChange — the host owns the draft (D3)', () => {
|
|
81
|
+
const { onDraftChange } = renderStep();
|
|
82
|
+
fireEvent.change(screen.getByLabelText(T.resourceTitle), { target: { value: 'Black Hawk (Sauk)' } });
|
|
83
|
+
expect(onDraftChange).toHaveBeenCalledWith({ name: 'Black Hawk (Sauk)' });
|
|
84
|
+
fireEvent.change(screen.getByTestId('code-editor'), { target: { value: 'A Sauk leader.' } });
|
|
85
|
+
expect(onDraftChange).toHaveBeenCalledWith({ content: 'A Sauk leader.' });
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('reference-fixed entity types render as read-only tags, not a picker (D6)', () => {
|
|
89
|
+
renderStep(); // referenceEntityTypes: ['Person']
|
|
90
|
+
expect(screen.getByText('Person')).toBeInTheDocument();
|
|
91
|
+
// No toggling: the types were chosen when the reference was created.
|
|
92
|
+
expect(screen.queryByRole('button', { name: /Person/ })).not.toBeInTheDocument();
|
|
93
|
+
expect(screen.queryByRole('button', { name: /Topic/ })).not.toBeInTheDocument();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('an empty reference set falls back to the picker over the owner-supplied options (D6)', () => {
|
|
97
|
+
const { onDraftChange } = renderStep({ referenceEntityTypes: [] });
|
|
98
|
+
const topic = screen.getByRole('button', { name: /Topic/ });
|
|
99
|
+
fireEvent.click(topic);
|
|
100
|
+
expect(onDraftChange).toHaveBeenCalledWith({ entityTypes: ['Topic'] });
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('carries NONE of the page-only machinery: no upload, no format, no encoding', () => {
|
|
104
|
+
const { container } = renderStep();
|
|
105
|
+
expect(container.querySelector('.semiont-form__upload-dropzone')).toBeNull();
|
|
106
|
+
expect(container.querySelector('.semiont-form__content-source-toggle')).toBeNull();
|
|
107
|
+
expect(container.querySelector('#format-select')).toBeNull();
|
|
108
|
+
expect(container.querySelector('#charset-select')).toBeNull();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('renders no context of its own — the evidence is host-stacked (A3)', () => {
|
|
112
|
+
const { container } = renderStep();
|
|
113
|
+
expect(container.querySelector('.semiont-gather-pane')).toBeNull();
|
|
114
|
+
expect(container.querySelector('.semiont-gather__outer')).toBeNull();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('submit emits the full params with the file:// prefix applied at the seam', async () => {
|
|
118
|
+
const { onCompose } = renderStep({ referenceEntityTypes: ['Person'] });
|
|
119
|
+
fireEvent.click(screen.getByRole('button', { name: T.createAndLink }));
|
|
120
|
+
await waitFor(() => expect(onCompose).toHaveBeenCalledTimes(1));
|
|
121
|
+
expect(onCompose).toHaveBeenCalledWith({
|
|
122
|
+
name: 'Black Hawk',
|
|
123
|
+
storagePath: 'file://people/black-hawk.md',
|
|
124
|
+
content: 'Sauk leader.',
|
|
125
|
+
entityTypes: ['Person'],
|
|
126
|
+
language: 'en',
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('the footer is a wizard footer: Back + Create, pending while in flight, re-enabled on rejection (A4)', async () => {
|
|
131
|
+
let reject!: (e: Error) => void;
|
|
132
|
+
const onCompose = vi.fn(() => new Promise<void>((_, r) => { reject = r; }));
|
|
133
|
+
const { container } = renderStep({ onCompose });
|
|
134
|
+
|
|
135
|
+
expect(container.querySelector('.semiont-modal__actions--wizard')).not.toBeNull();
|
|
136
|
+
fireEvent.click(screen.getByRole('button', { name: T.createAndLink }));
|
|
137
|
+
expect(await screen.findByRole('button', { name: T.creatingAndLinking })).toBeDisabled();
|
|
138
|
+
|
|
139
|
+
reject(new Error('boom'));
|
|
140
|
+
expect(await screen.findByRole('button', { name: T.createAndLink })).toBeEnabled();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('Back is the only retreat and dismissal never lives in the footer (A4)', () => {
|
|
144
|
+
const { container, onBack } = renderStep();
|
|
145
|
+
const footerButtons = Array.from(
|
|
146
|
+
container.querySelectorAll('.semiont-modal__actions button'),
|
|
147
|
+
).map((b) => b.textContent ?? '');
|
|
148
|
+
expect(footerButtons.filter((l) => /cancel|✕/i.test(l))).toEqual([]);
|
|
149
|
+
fireEvent.click(screen.getByRole('button', { name: `◀ ${T.back}` }));
|
|
150
|
+
expect(onBack).toHaveBeenCalledTimes(1);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -16,13 +16,12 @@ const t = {
|
|
|
16
16
|
includeSummary: 'Include summary',
|
|
17
17
|
depth: 'Depth',
|
|
18
18
|
maxResources: 'Max resources',
|
|
19
|
-
cancel: 'Cancel',
|
|
20
19
|
gather: 'Gather',
|
|
21
20
|
};
|
|
22
21
|
|
|
23
22
|
describe('ConfigureGatherStep', () => {
|
|
24
23
|
it('renders the intro and the default option values', () => {
|
|
25
|
-
render(<ConfigureGatherStep onGather={vi.fn()}
|
|
24
|
+
render(<ConfigureGatherStep onGather={vi.fn()} translations={t} />);
|
|
26
25
|
expect(screen.getByText('Choose what to include.')).toBeInTheDocument();
|
|
27
26
|
expect(screen.getByLabelText('Include content')).toBeChecked();
|
|
28
27
|
expect(screen.getByLabelText('Include summary')).toBeChecked();
|
|
@@ -32,14 +31,14 @@ describe('ConfigureGatherStep', () => {
|
|
|
32
31
|
|
|
33
32
|
it('emits the default config on submit', () => {
|
|
34
33
|
const onGather = vi.fn();
|
|
35
|
-
render(<ConfigureGatherStep onGather={onGather}
|
|
34
|
+
render(<ConfigureGatherStep onGather={onGather} translations={t} />);
|
|
36
35
|
fireEvent.click(screen.getByRole('button', { name: /Gather/ }));
|
|
37
36
|
expect(onGather).toHaveBeenCalledWith({ includeContent: true, includeSummary: true, depth: 2, maxResources: 10 });
|
|
38
37
|
});
|
|
39
38
|
|
|
40
39
|
it('reflects edits in the emitted config', () => {
|
|
41
40
|
const onGather = vi.fn();
|
|
42
|
-
render(<ConfigureGatherStep onGather={onGather}
|
|
41
|
+
render(<ConfigureGatherStep onGather={onGather} translations={t} />);
|
|
43
42
|
fireEvent.click(screen.getByLabelText('Include content')); // uncheck
|
|
44
43
|
fireEvent.click(screen.getByLabelText('Include summary')); // uncheck
|
|
45
44
|
fireEvent.change(screen.getByLabelText('Depth'), { target: { value: '4' } });
|
|
@@ -48,32 +47,21 @@ describe('ConfigureGatherStep', () => {
|
|
|
48
47
|
expect(onGather).toHaveBeenCalledWith({ includeContent: false, includeSummary: false, depth: 4, maxResources: 25 });
|
|
49
48
|
});
|
|
50
49
|
|
|
51
|
-
it('
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
);
|
|
61
|
-
expect(screen.getByLabelText('Include summary')).not.toBeChecked();
|
|
62
|
-
expect(screen.getByLabelText('Depth')).toHaveValue(1);
|
|
63
|
-
fireEvent.click(screen.getByRole('button', { name: /Gather/ }));
|
|
64
|
-
expect(onGather).toHaveBeenCalledWith({ includeContent: true, includeSummary: false, depth: 1, maxResources: 5 });
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('calls onCancel from the cancel button', () => {
|
|
68
|
-
const onCancel = vi.fn();
|
|
69
|
-
render(<ConfigureGatherStep onGather={vi.fn()} onCancel={onCancel} translations={t} />);
|
|
70
|
-
fireEvent.click(screen.getByRole('button', { name: /Cancel/ }));
|
|
71
|
-
expect(onCancel).toHaveBeenCalledTimes(1);
|
|
50
|
+
it('the footer is the wizard footer: advance only, no dismissal, no flex (GFR A5)', () => {
|
|
51
|
+
// Dismissal lives on the modal's corner ✕/Esc/backdrop, never in a step
|
|
52
|
+
// footer (WIZARD-NAVIGATION D1); this is the first step, so no retreat either.
|
|
53
|
+
const { container } = render(<ConfigureGatherStep onGather={vi.fn()} translations={t} />);
|
|
54
|
+
const footer = container.querySelector('.semiont-modal__actions--wizard');
|
|
55
|
+
expect(footer).not.toBeNull();
|
|
56
|
+
const buttons = Array.from(footer!.querySelectorAll('button'));
|
|
57
|
+
expect(buttons).toHaveLength(1);
|
|
58
|
+
expect(buttons[0]!.textContent).not.toMatch(/cancel|✕/i);
|
|
59
|
+
expect(buttons[0]!.className).not.toContain('semiont-button--flex');
|
|
72
60
|
});
|
|
73
61
|
|
|
74
62
|
it('renders the children slot (exclusion picker)', () => {
|
|
75
63
|
render(
|
|
76
|
-
<ConfigureGatherStep onGather={vi.fn()}
|
|
64
|
+
<ConfigureGatherStep onGather={vi.fn()} translations={t}>
|
|
77
65
|
<div>EXCLUDE-SLOT</div>
|
|
78
66
|
</ConfigureGatherStep>,
|
|
79
67
|
);
|