@checkstack/ui 1.9.0 → 1.11.0
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/CHANGELOG.md +417 -0
- package/package.json +15 -7
- package/scripts/generate-stdlib-types.ts +2 -2
- package/src/components/ActionCard.tsx +221 -0
- package/src/components/CodeEditor/CodeEditor.tsx +51 -9
- package/src/components/CodeEditor/TypefoxEditor.tsx +868 -0
- package/src/components/CodeEditor/bracketKeyGroups.test.ts +120 -0
- package/src/components/CodeEditor/bracketKeyGroups.ts +205 -0
- package/src/components/CodeEditor/generateTypeDefinitions.ts +4 -4
- package/src/components/CodeEditor/index.ts +2 -0
- package/src/components/CodeEditor/scriptContext.test.ts +41 -0
- package/src/components/CodeEditor/scriptContext.ts +76 -1
- package/src/components/CodeEditor/templateValidation.ts +51 -0
- package/src/components/CodeEditor/types.ts +109 -0
- package/src/components/CodeEditor/validateJsonTemplate.test.ts +61 -0
- package/src/components/CodeEditor/validateJsonTemplate.ts +26 -0
- package/src/components/CodeEditor/validateXmlTemplate.test.ts +34 -0
- package/src/components/CodeEditor/validateXmlTemplate.ts +35 -0
- package/src/components/CodeEditor/validateYamlTemplate.test.ts +39 -0
- package/src/components/CodeEditor/validateYamlTemplate.ts +28 -0
- package/src/components/DynamicForm/DynamicForm.tsx +2 -0
- package/src/components/DynamicForm/FormField.tsx +29 -9
- package/src/components/DynamicForm/KeyValueEditor.tsx +2 -169
- package/src/components/DynamicForm/MultiTypeEditorField.tsx +16 -7
- package/src/components/DynamicForm/types.ts +11 -0
- package/src/components/ListEmptyState.tsx +51 -0
- package/src/components/QueryErrorState.tsx +64 -0
- package/src/components/ResponsiveTable.tsx +92 -0
- package/src/components/Skeleton.tsx +39 -0
- package/src/components/TemplateInput.tsx +104 -0
- package/src/components/TemplateInputToggle.tsx +111 -0
- package/src/components/TemplateValueInput.test.ts +98 -0
- package/src/components/TemplateValueInput.tsx +470 -0
- package/src/components/VariablePicker.tsx +271 -0
- package/src/hooks/useInitOnceForKey.test.ts +27 -0
- package/src/hooks/useInitOnceForKey.ts +21 -18
- package/src/index.ts +10 -0
- package/src/utils/toastTemplates.test.ts +82 -0
- package/src/utils/toastTemplates.ts +47 -0
- package/stories/ActionCard.stories.tsx +62 -0
- package/stories/Alert.stories.tsx +5 -5
- package/stories/ListEmptyState.stories.tsx +48 -0
- package/stories/QueryErrorState.stories.tsx +40 -0
- package/stories/ResponsiveTable.stories.tsx +93 -0
- package/stories/Skeleton.stories.tsx +53 -0
- package/stories/TemplateInputToggle.stories.tsx +77 -0
- package/stories/TemplateValueInput.stories.tsx +65 -0
- package/stories/VariablePicker.stories.tsx +109 -0
- package/stories/toastTemplates.stories.tsx +60 -0
- package/src/components/CodeEditor/MonacoEditor.tsx +0 -616
- package/src/components/CodeEditor/monacoStdlib.ts +0 -62
- package/src/components/CodeEditor/monacoWorkers.ts +0 -118
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
ChevronDown,
|
|
4
|
+
ChevronRight,
|
|
5
|
+
GripVertical,
|
|
6
|
+
AlertTriangle,
|
|
7
|
+
Trash2,
|
|
8
|
+
} from "lucide-react";
|
|
9
|
+
import { Card, CardContent, CardHeader } from "./Card";
|
|
10
|
+
import { Button } from "./Button";
|
|
11
|
+
import { Toggle } from "./Toggle";
|
|
12
|
+
import { DynamicIcon, type LucideIconName } from "./DynamicIcon";
|
|
13
|
+
import { Badge, type BadgeProps } from "./Badge";
|
|
14
|
+
import { cn } from "../utils";
|
|
15
|
+
|
|
16
|
+
export interface ActionCardProps {
|
|
17
|
+
/** Stable identifier used for drag-reorder + React key. */
|
|
18
|
+
id: string;
|
|
19
|
+
/** Bold header label, e.g. "Notify User". */
|
|
20
|
+
title: string;
|
|
21
|
+
/** Operator-supplied description (the action's `id`/`description` field). */
|
|
22
|
+
description?: string;
|
|
23
|
+
/** Plugin/category label rendered as a subdued badge. */
|
|
24
|
+
category?: string;
|
|
25
|
+
/** Lucide icon (PascalCase) shown to the left of the title. */
|
|
26
|
+
icon?: LucideIconName;
|
|
27
|
+
/** Toggle for the action's `enabled` flag. Omit to hide the toggle. */
|
|
28
|
+
enabled?: boolean;
|
|
29
|
+
onEnabledChange?: (enabled: boolean) => void;
|
|
30
|
+
/** Removes the card from its container. Omit to hide the delete button. */
|
|
31
|
+
onDelete?: () => void;
|
|
32
|
+
/** Drag handle shown on the left; integrators wire it up via `dnd-kit`. */
|
|
33
|
+
dragHandleProps?: React.HTMLAttributes<HTMLButtonElement>;
|
|
34
|
+
/** Initial expanded state when uncontrolled. */
|
|
35
|
+
defaultExpanded?: boolean;
|
|
36
|
+
/** Controlled expanded state. */
|
|
37
|
+
expanded?: boolean;
|
|
38
|
+
onExpandedChange?: (expanded: boolean) => void;
|
|
39
|
+
/** Extra badges (e.g. produces / consumes hints). */
|
|
40
|
+
badges?: Array<{
|
|
41
|
+
label: string;
|
|
42
|
+
variant?: BadgeProps["variant"];
|
|
43
|
+
className?: string;
|
|
44
|
+
}>;
|
|
45
|
+
/** Card body — the action's config form. */
|
|
46
|
+
children?: React.ReactNode;
|
|
47
|
+
/** Optional class override on the outer Card. */
|
|
48
|
+
className?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Validation messages attached to this card. When non-empty the card
|
|
51
|
+
* is marked with a destructive border + warning icon and the messages
|
|
52
|
+
* are listed in the header, so the operator sees *which* card (and
|
|
53
|
+
* field) is wrong without a separate panel.
|
|
54
|
+
*/
|
|
55
|
+
errors?: string[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Collapsible card that wraps a single automation action in the visual
|
|
60
|
+
* editor.
|
|
61
|
+
*
|
|
62
|
+
* Mirrors the visual layout of `StrategyConfigCard` but stays decoupled
|
|
63
|
+
* from `DynamicForm` so containers (`ChooseBlock`, `ParallelBlock`,
|
|
64
|
+
* `RepeatBlock`) can supply their own children — typically nested lists
|
|
65
|
+
* of `ActionCard`s rather than a flat config schema.
|
|
66
|
+
*
|
|
67
|
+
* Composition pattern (consumed by automation-frontend's editor):
|
|
68
|
+
*
|
|
69
|
+
* ```tsx
|
|
70
|
+
* <ActionCard
|
|
71
|
+
* id={action.id}
|
|
72
|
+
* title={action.action}
|
|
73
|
+
* enabled={action.enabled ?? true}
|
|
74
|
+
* onEnabledChange={(next) => onActionChange({ ...action, enabled: next })}
|
|
75
|
+
* onDelete={() => onDelete(action.id)}
|
|
76
|
+
* >
|
|
77
|
+
* <DynamicForm
|
|
78
|
+
* schema={registeredAction.configJsonSchema}
|
|
79
|
+
* value={action.config}
|
|
80
|
+
* onChange={(config) => onActionChange({ ...action, config })}
|
|
81
|
+
* />
|
|
82
|
+
* </ActionCard>
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* The toggle, delete, and drag handle each gate on their respective
|
|
86
|
+
* callback being supplied — pass `onDelete` to render the trash button,
|
|
87
|
+
* `onEnabledChange` to render the enable toggle, `dragHandleProps` to
|
|
88
|
+
* render the grip. Containers that don't want any of those (e.g. a
|
|
89
|
+
* disabled preview) just omit them.
|
|
90
|
+
*/
|
|
91
|
+
export const ActionCard: React.FC<ActionCardProps> = ({
|
|
92
|
+
id,
|
|
93
|
+
title,
|
|
94
|
+
description,
|
|
95
|
+
category,
|
|
96
|
+
icon,
|
|
97
|
+
enabled,
|
|
98
|
+
onEnabledChange,
|
|
99
|
+
onDelete,
|
|
100
|
+
dragHandleProps,
|
|
101
|
+
defaultExpanded = true,
|
|
102
|
+
expanded,
|
|
103
|
+
onExpandedChange,
|
|
104
|
+
badges,
|
|
105
|
+
children,
|
|
106
|
+
className,
|
|
107
|
+
errors,
|
|
108
|
+
}) => {
|
|
109
|
+
const [internalExpanded, setInternalExpanded] =
|
|
110
|
+
React.useState(defaultExpanded);
|
|
111
|
+
const isExpanded = expanded ?? internalExpanded;
|
|
112
|
+
const toggleExpanded = () => {
|
|
113
|
+
const next = !isExpanded;
|
|
114
|
+
if (expanded === undefined) setInternalExpanded(next);
|
|
115
|
+
onExpandedChange?.(next);
|
|
116
|
+
};
|
|
117
|
+
const hasErrors = errors !== undefined && errors.length > 0;
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<Card
|
|
121
|
+
data-action-id={id}
|
|
122
|
+
className={cn(
|
|
123
|
+
"transition-opacity",
|
|
124
|
+
enabled === false && "opacity-60",
|
|
125
|
+
hasErrors && "border-destructive/60 ring-1 ring-destructive/30",
|
|
126
|
+
className,
|
|
127
|
+
)}
|
|
128
|
+
>
|
|
129
|
+
<CardHeader className="flex flex-row items-center gap-2 p-3 space-y-0">
|
|
130
|
+
{dragHandleProps && (
|
|
131
|
+
<button
|
|
132
|
+
type="button"
|
|
133
|
+
className="cursor-grab text-muted-foreground hover:text-foreground"
|
|
134
|
+
aria-label="Drag to reorder"
|
|
135
|
+
{...dragHandleProps}
|
|
136
|
+
>
|
|
137
|
+
<GripVertical className="w-4 h-4" />
|
|
138
|
+
</button>
|
|
139
|
+
)}
|
|
140
|
+
<button
|
|
141
|
+
type="button"
|
|
142
|
+
onClick={toggleExpanded}
|
|
143
|
+
className="flex items-center flex-1 gap-2 text-left"
|
|
144
|
+
aria-expanded={isExpanded}
|
|
145
|
+
aria-controls={`${id}-body`}
|
|
146
|
+
>
|
|
147
|
+
{isExpanded ? (
|
|
148
|
+
<ChevronDown className="w-4 h-4 shrink-0 text-muted-foreground" />
|
|
149
|
+
) : (
|
|
150
|
+
<ChevronRight className="w-4 h-4 shrink-0 text-muted-foreground" />
|
|
151
|
+
)}
|
|
152
|
+
{hasErrors ? (
|
|
153
|
+
<AlertTriangle className="w-4 h-4 shrink-0 text-destructive" />
|
|
154
|
+
) : (
|
|
155
|
+
icon && <DynamicIcon name={icon} className="w-4 h-4 shrink-0" />
|
|
156
|
+
)}
|
|
157
|
+
<div className="flex-1 min-w-0">
|
|
158
|
+
<div className="flex items-center gap-2">
|
|
159
|
+
<span className="text-sm font-semibold truncate">{title}</span>
|
|
160
|
+
{category && (
|
|
161
|
+
<Badge variant="outline" className="shrink-0 text-[10px]">
|
|
162
|
+
{category}
|
|
163
|
+
</Badge>
|
|
164
|
+
)}
|
|
165
|
+
{badges?.map((badge) => (
|
|
166
|
+
<Badge
|
|
167
|
+
key={badge.label}
|
|
168
|
+
variant={badge.variant ?? "outline"}
|
|
169
|
+
className={cn("shrink-0 text-[10px]", badge.className)}
|
|
170
|
+
>
|
|
171
|
+
{badge.label}
|
|
172
|
+
</Badge>
|
|
173
|
+
))}
|
|
174
|
+
</div>
|
|
175
|
+
{description && (
|
|
176
|
+
<p className="text-xs truncate text-muted-foreground">
|
|
177
|
+
{description}
|
|
178
|
+
</p>
|
|
179
|
+
)}
|
|
180
|
+
{hasErrors && (
|
|
181
|
+
<ul className="mt-0.5 space-y-0.5">
|
|
182
|
+
{errors!.map((error, index) => (
|
|
183
|
+
<li
|
|
184
|
+
key={index}
|
|
185
|
+
className="text-[11px] font-mono text-destructive"
|
|
186
|
+
>
|
|
187
|
+
{error}
|
|
188
|
+
</li>
|
|
189
|
+
))}
|
|
190
|
+
</ul>
|
|
191
|
+
)}
|
|
192
|
+
</div>
|
|
193
|
+
</button>
|
|
194
|
+
{onEnabledChange && (
|
|
195
|
+
<Toggle
|
|
196
|
+
checked={enabled ?? true}
|
|
197
|
+
onCheckedChange={onEnabledChange}
|
|
198
|
+
aria-label={enabled ? "Disable action" : "Enable action"}
|
|
199
|
+
/>
|
|
200
|
+
)}
|
|
201
|
+
{onDelete && (
|
|
202
|
+
<Button
|
|
203
|
+
type="button"
|
|
204
|
+
variant="ghost"
|
|
205
|
+
size="icon"
|
|
206
|
+
onClick={onDelete}
|
|
207
|
+
className="w-8 h-8 text-destructive hover:text-destructive/90 hover:bg-destructive/10 shrink-0"
|
|
208
|
+
aria-label="Delete action"
|
|
209
|
+
>
|
|
210
|
+
<Trash2 className="w-4 h-4" />
|
|
211
|
+
</Button>
|
|
212
|
+
)}
|
|
213
|
+
</CardHeader>
|
|
214
|
+
{isExpanded && children && (
|
|
215
|
+
<CardContent id={`${id}-body`} className="p-3 border-t border-border">
|
|
216
|
+
{children}
|
|
217
|
+
</CardContent>
|
|
218
|
+
)}
|
|
219
|
+
</Card>
|
|
220
|
+
);
|
|
221
|
+
};
|
|
@@ -1,13 +1,55 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
1
|
+
// Public `CodeEditor` component. Adapts the stable `CodeEditorProps` API to the
|
|
2
|
+
// `@typefox/monaco-editor-react`-backed `TypefoxEditor` (real VS Code language
|
|
3
|
+
// services in the browser). Consumers (DynamicForm, automation, healthcheck)
|
|
4
|
+
// import this and are unaffected by the underlying editor implementation.
|
|
5
|
+
import { TypefoxEditor } from "./TypefoxEditor";
|
|
6
|
+
import type { CodeEditorProps } from "./types";
|
|
3
7
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Code editor with context-aware IntelliSense, template / shell completion, and
|
|
10
|
+
* structural validation. See `CodeEditorProps`.
|
|
11
|
+
*/
|
|
12
|
+
export const CodeEditor = ({
|
|
13
|
+
id,
|
|
14
|
+
value,
|
|
15
|
+
onChange,
|
|
16
|
+
language = "typescript",
|
|
17
|
+
minHeight = "100px",
|
|
18
|
+
readOnly,
|
|
19
|
+
placeholder,
|
|
20
|
+
typeDefinitions,
|
|
21
|
+
templateProperties,
|
|
22
|
+
shellEnvVars,
|
|
23
|
+
markers,
|
|
24
|
+
}: CodeEditorProps) => {
|
|
25
|
+
// CodeEditorProps.minHeight is a CSS length string ("240px"); TypefoxEditor
|
|
26
|
+
// takes a pixel number.
|
|
27
|
+
const minHeightPx = Number.parseInt(minHeight, 10) || 100;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<TypefoxEditor
|
|
31
|
+
id={id ?? "code-editor"}
|
|
32
|
+
value={value}
|
|
33
|
+
onChange={onChange}
|
|
34
|
+
language={language}
|
|
35
|
+
minHeight={minHeightPx}
|
|
36
|
+
readOnly={readOnly}
|
|
37
|
+
placeholder={placeholder}
|
|
38
|
+
typeDefinitions={typeDefinitions}
|
|
39
|
+
templateProperties={templateProperties}
|
|
40
|
+
shellEnvVars={shellEnvVars}
|
|
41
|
+
markers={markers}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type {
|
|
47
|
+
CodeEditorProps,
|
|
48
|
+
CodeEditorLanguage,
|
|
49
|
+
TemplateProperty,
|
|
50
|
+
ShellEnvVar,
|
|
51
|
+
EditorMarker,
|
|
52
|
+
} from "./types";
|
|
11
53
|
|
|
12
54
|
export {
|
|
13
55
|
generateTypeDefinitions,
|