@keboola/design 3.2.0 → 4.0.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/AGENTS.md +447 -0
- package/COMPONENT-API.md +378 -0
- package/README.md +20 -11
- package/dist/constants-D3gaMY-W.d.cts +39 -0
- package/dist/constants-D3gaMY-W.d.ts +39 -0
- package/dist/editors.cjs +1976 -0
- package/dist/editors.cjs.map +1 -0
- package/dist/editors.d.cts +113 -0
- package/dist/editors.d.ts +113 -0
- package/dist/editors.js +1948 -0
- package/dist/editors.js.map +1 -0
- package/dist/index.cjs +59 -1203
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +330 -407
- package/dist/index.d.ts +330 -407
- package/dist/index.js +60 -1199
- package/dist/index.js.map +1 -1
- package/dist/markdown-editor.cjs +4 -4
- package/dist/markdown-editor.cjs.map +1 -1
- package/dist/markdown-editor.d.cts +4 -3
- package/dist/markdown-editor.d.ts +4 -3
- package/dist/markdown-editor.js +4 -4
- package/dist/markdown-editor.js.map +1 -1
- package/dist/styles.css +3 -1
- package/dist/testing.cjs +23 -0
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js +23 -0
- package/dist/testing.js.map +1 -1
- package/package.json +53 -37
- package/src/styles.css +41 -1
package/COMPONENT-API.md
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
# @keboola/design — component API reference
|
|
2
|
+
|
|
3
|
+
> **Generated file — do not edit by hand.** Regenerate with `pnpm --filter @keboola/design generate:api` (extracts prop signatures from the TypeScript types via `react-docgen-typescript`).
|
|
4
|
+
|
|
5
|
+
This is the prop-signature contract for the most-used components. For _which_ component to reach for, see [`AGENTS.md`](./AGENTS.md) (the decision tree); for live worked examples, open the colocated `*.stories.tsx` in Storybook (`pnpm dev --filter=@keboola/design`).
|
|
6
|
+
|
|
7
|
+
Props listed are the ones each component **declares itself**. Components built on a native element (`Button` on `<button>`, `Link` on `<a>`, `Card` on `<div>`, …) also accept that element's standard attributes — called out in each entry's note.
|
|
8
|
+
|
|
9
|
+
## Actions
|
|
10
|
+
|
|
11
|
+
### `Button`
|
|
12
|
+
|
|
13
|
+
Source: `src/components/Button/Button.tsx`
|
|
14
|
+
|
|
15
|
+
> Icons are composed as children, not an `icon` prop. Plus all native `<button>` attributes.
|
|
16
|
+
|
|
17
|
+
| Prop | Type | Required | Default | Description |
|
|
18
|
+
| ----------- | --------------------------------------------------------------------------------------------------------------------- | -------- | --------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
19
|
+
| `argo` | `boolean` | no | `false` | @internal Transitional opt-in; removed once consumers wrap argo sections in `<ThemeProvider>`. |
|
|
20
|
+
| `asChild` | `boolean` | no | `false` | Render as a Radix `Slot` so the parent JSX (e.g. a `react-router` `<Link>`) becomes the button element. |
|
|
21
|
+
| `block` | `boolean \| null` | no | `false` | |
|
|
22
|
+
| `isLoading` | `boolean` | no | `false` | Replaces the leading content with a spinner and disables the button visually. The caller still controls `disabled`. |
|
|
23
|
+
| `size` | `"small" \| "medium" \| "large" \| "icon-lg" \| "icon" \| "icon-sm" \| "icon-xs" \| "icon-2xs" \| "icon-3xs" \| null` | no | `medium` | |
|
|
24
|
+
| `variant` | `"primary" \| "secondary" \| "danger" \| "invisible" \| "outline" \| "inline" \| null` | no | `primary` | |
|
|
25
|
+
|
|
26
|
+
### `ButtonInline`
|
|
27
|
+
|
|
28
|
+
Source: `src/components/Button/ButtonInline.tsx`
|
|
29
|
+
|
|
30
|
+
> Inline text-link-style action. Plus all native `<button>` attributes.
|
|
31
|
+
|
|
32
|
+
| Prop | Type | Required | Default | Description |
|
|
33
|
+
| --------- | ---------------------------------------------- | -------- | ------- | ----------- |
|
|
34
|
+
| `asChild` | `boolean` | no | `false` | |
|
|
35
|
+
| `variant` | `"link" \| "custom" \| "link-reverse" \| null` | no | — | |
|
|
36
|
+
|
|
37
|
+
### `Link`
|
|
38
|
+
|
|
39
|
+
Source: `src/components/Link.tsx`
|
|
40
|
+
|
|
41
|
+
> Defaults `target="_blank"` + `rel="noopener noreferrer"`. Accepts all native `<a>` attributes (`href`, `target`, …) — none are required props.
|
|
42
|
+
|
|
43
|
+
_No own props — see note above for passthrough attributes._
|
|
44
|
+
|
|
45
|
+
## Status surfaces
|
|
46
|
+
|
|
47
|
+
### `Alert`
|
|
48
|
+
|
|
49
|
+
Source: `src/components/Alert/Alert.tsx`
|
|
50
|
+
|
|
51
|
+
> Discriminated by `mode`: `"default"` renders `title`/`children`/`actions`; `"collapsible"` requires `title`+`children` and forbids `actions`. `variant` is a categorical colour contract (`info`/`success`/`warning`/`error`).
|
|
52
|
+
|
|
53
|
+
| Prop | Type | Required | Default | Description |
|
|
54
|
+
| ---------- | ---------------------------- | -------- | --------- | ---------------------------------------------------------------------------------------------- |
|
|
55
|
+
| `actions` | `ReactNode` | no | — | |
|
|
56
|
+
| `argo` | `boolean` | no | — | @internal Transitional opt-in; removed once consumers wrap argo sections in `<ThemeProvider>`. |
|
|
57
|
+
| `hideIcon` | `boolean` | no | `false` | |
|
|
58
|
+
| `icon` | `ReactNode` | no | — | |
|
|
59
|
+
| `mode` | `"collapsible" \| "default"` | no | `default` | |
|
|
60
|
+
| `variant` | `Variant` | no | `info` | |
|
|
61
|
+
|
|
62
|
+
### `Toaster`
|
|
63
|
+
|
|
64
|
+
Source: `src/components/Toast/Toaster.tsx`
|
|
65
|
+
|
|
66
|
+
> Mount ONCE at app root. Wraps `sonner`'s `<Toaster>` — accepts all of sonner's props (`position`, `theme`, `richColors`, …). Fire toasts imperatively with `toast()` / `useNotification`, not by rendering children.
|
|
67
|
+
|
|
68
|
+
_No own props — see note above for passthrough attributes._
|
|
69
|
+
|
|
70
|
+
## Inputs — bare
|
|
71
|
+
|
|
72
|
+
### `TextInput`
|
|
73
|
+
|
|
74
|
+
Source: `src/components/Input/TextInput/TextInput.tsx`
|
|
75
|
+
|
|
76
|
+
> Bare input. Plus all native `<input>` attributes.
|
|
77
|
+
|
|
78
|
+
| Prop | Type | Required | Default | Description |
|
|
79
|
+
| -------------------- | --------------------------------------------------------------- | -------- | ------- | ----------------------------------------------------------------------------------------- |
|
|
80
|
+
| `allowClear` | `boolean` | no | — | |
|
|
81
|
+
| `defaultValue` | `string \| null` | no | — | |
|
|
82
|
+
| `focusVariant` | `"off" \| "on" \| "default" \| null` | no | — | |
|
|
83
|
+
| `leftSuffix` | `ReactNode` | no | — | |
|
|
84
|
+
| `onClear` | `(() => void)` | no | — | |
|
|
85
|
+
| `onValueBlur` | `((value: string) => void)` | no | — | Called with the unwrapped string value on blur. For the raw event use `onBlur`. |
|
|
86
|
+
| `onValueChange` | `((value: string) => void)` | no | — | Called with the unwrapped string value on every change. For the raw event use `onChange`. |
|
|
87
|
+
| `prefix` | `ReactNode` | no | — | |
|
|
88
|
+
| `rightSuffix` | `ReactNode` | no | — | |
|
|
89
|
+
| `rightSuffixVariant` | `"default" \| "addon"` | no | — | |
|
|
90
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
91
|
+
| `state` | `"warning" \| "error" \| "default" \| null` | no | — | |
|
|
92
|
+
| `testId` | `string` | no | — | |
|
|
93
|
+
| `type` | `"text" \| "email" \| "date" \| "datetime-local" \| "password"` | no | `text` | |
|
|
94
|
+
| `value` | `string \| null` | no | — | |
|
|
95
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
96
|
+
|
|
97
|
+
### `PasswordInput`
|
|
98
|
+
|
|
99
|
+
Source: `src/blocks/PasswordInput/PasswordInput.tsx`
|
|
100
|
+
|
|
101
|
+
| Prop | Type | Required | Default | Description |
|
|
102
|
+
| --------------------------- | ----------------------------------------------- | -------- | -------------- | ----------- |
|
|
103
|
+
| `allowClear` | `boolean` | no | `false` | |
|
|
104
|
+
| `allowPasswordToggle` | `boolean` | no | `false` | |
|
|
105
|
+
| `autoComplete` | `"off" \| "current-password" \| "new-password"` | no | `new-password` | |
|
|
106
|
+
| `defaultPasswordVisibility` | `boolean` | no | `false` | |
|
|
107
|
+
| `defaultValue` | `string \| null` | no | — | |
|
|
108
|
+
| `focusVariant` | `"off" \| "on" \| "default" \| null` | no | — | |
|
|
109
|
+
| `onValueBlur` | `((value: string) => void)` | no | — | |
|
|
110
|
+
| `onValueChange` | `((value: string) => void)` | no | — | |
|
|
111
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
112
|
+
| `state` | `"warning" \| "error" \| "default" \| null` | no | — | |
|
|
113
|
+
| `value` | `string \| null` | no | — | |
|
|
114
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
115
|
+
|
|
116
|
+
### `Textarea`
|
|
117
|
+
|
|
118
|
+
Source: `src/blocks/Textarea/Textarea.tsx`
|
|
119
|
+
|
|
120
|
+
| Prop | Type | Required | Default | Description |
|
|
121
|
+
| -------------------- | --------------------------------------------------------- | -------- | --------- | ----------- |
|
|
122
|
+
| `containerClassName` | `string` | no | — | |
|
|
123
|
+
| `defaultValue` | `string \| null` | no | — | |
|
|
124
|
+
| `disableResize` | `boolean` | no | — | |
|
|
125
|
+
| `focusVariant` | `"off" \| "on" \| "default" \| null` | no | — | |
|
|
126
|
+
| `maxRows` | `number \| "unlimited"` | no | — | |
|
|
127
|
+
| `onValueBlur` | `(((value: string) => void) & ((value: string) => void))` | no | — | |
|
|
128
|
+
| `onValueChange` | `(((value: string) => void) & ((value: string) => void))` | no | — | |
|
|
129
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
130
|
+
| `state` | `"warning" \| "error" \| "default" \| null` | no | `default` | |
|
|
131
|
+
| `value` | `string \| null` | no | — | |
|
|
132
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
133
|
+
|
|
134
|
+
## Inputs — FormGroup (label + input + help, shared validation state)
|
|
135
|
+
|
|
136
|
+
### `FormGroup`
|
|
137
|
+
|
|
138
|
+
Source: `src/components/FormGroup/FormGroup.tsx`
|
|
139
|
+
|
|
140
|
+
> `state="error"` propagates to nested `FormGroup.TextInput` / `FormGroup.Help` via context. Subcomponents: `FormGroup.TextInput`, `FormGroup.NumberInput`, `FormGroup.ColorInput`, `FormGroup.Textarea`, `FormGroup.Help`.
|
|
141
|
+
|
|
142
|
+
| Prop | Type | Required | Default | Description |
|
|
143
|
+
| ----------- | --------------- | -------- | ------- | ----------- |
|
|
144
|
+
| `className` | `string` | no | — | |
|
|
145
|
+
| `state` | `ValidityState` | no | — | |
|
|
146
|
+
| `testId` | `string` | no | — | |
|
|
147
|
+
|
|
148
|
+
### `FormGroup.TextInput`
|
|
149
|
+
|
|
150
|
+
Source: `src/components/FormGroup/FormGroupTextInput.tsx`
|
|
151
|
+
|
|
152
|
+
> NO `error` prop — error styling comes from the parent `FormGroup state="error"` via context. `state` is omitted from the surface for the same reason. Plus native `<input>` attributes.
|
|
153
|
+
|
|
154
|
+
| Prop | Type | Required | Default | Description |
|
|
155
|
+
| -------------------- | --------------------------------------------------------------- | -------- | ------- | ----------------------------------------------------------------------------------------- |
|
|
156
|
+
| `allowClear` | `boolean` | no | — | |
|
|
157
|
+
| `defaultValue` | `string \| null` | no | — | |
|
|
158
|
+
| `focusVariant` | `"off" \| "on" \| "default" \| null` | no | — | |
|
|
159
|
+
| `leftSuffix` | `ReactNode` | no | — | |
|
|
160
|
+
| `onClear` | `(() => void)` | no | — | |
|
|
161
|
+
| `onValueBlur` | `((value: string) => void)` | no | — | Called with the unwrapped string value on blur. For the raw event use `onBlur`. |
|
|
162
|
+
| `onValueChange` | `((value: string) => void)` | no | — | Called with the unwrapped string value on every change. For the raw event use `onChange`. |
|
|
163
|
+
| `prefix` | `ReactNode` | no | — | |
|
|
164
|
+
| `rightSuffix` | `ReactNode` | no | — | |
|
|
165
|
+
| `rightSuffixVariant` | `"default" \| "addon"` | no | — | |
|
|
166
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
167
|
+
| `testId` | `string` | no | — | |
|
|
168
|
+
| `type` | `"text" \| "email" \| "date" \| "datetime-local" \| "password"` | no | — | |
|
|
169
|
+
| `value` | `string \| null` | no | — | |
|
|
170
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
171
|
+
|
|
172
|
+
### `FormGroup.Help`
|
|
173
|
+
|
|
174
|
+
Source: `src/components/FormGroup/FormGroupHelp.tsx`
|
|
175
|
+
|
|
176
|
+
| Prop | Type | Required | Default | Description |
|
|
177
|
+
| ----------- | -------- | -------- | ------- | ----------- |
|
|
178
|
+
| `className` | `string` | no | — | |
|
|
179
|
+
|
|
180
|
+
## Forms — useAppForm field connectors (accessed as `field.X` inside `form.AppField`)
|
|
181
|
+
|
|
182
|
+
### `field.TextField`
|
|
183
|
+
|
|
184
|
+
Source: `src/blocks/Form/connectors/TextField.tsx`
|
|
185
|
+
|
|
186
|
+
> Value/onChange/onBlur/id/name are wired from the field — do not pass them. Forwards remaining `FormGroup.TextInput` props.
|
|
187
|
+
|
|
188
|
+
| Prop | Type | Required | Default | Description |
|
|
189
|
+
| -------------------- | --------------------------------------------------------------- | -------- | ------- | ----------------------------------------------------------------------------------------- |
|
|
190
|
+
| `allowClear` | `boolean` | no | — | |
|
|
191
|
+
| `className` | `string` | no | — | |
|
|
192
|
+
| `defaultValue` | `string \| null` | no | — | |
|
|
193
|
+
| `focusVariant` | `"off" \| "on" \| "default" \| null` | no | — | |
|
|
194
|
+
| `help` | `ReactNode` | no | — | |
|
|
195
|
+
| `label` | `ReactNode` | no | — | |
|
|
196
|
+
| `leftSuffix` | `ReactNode` | no | — | |
|
|
197
|
+
| `onClear` | `(() => void)` | no | — | |
|
|
198
|
+
| `onValueBlur` | `((value: string) => void)` | no | — | Called with the unwrapped string value on blur. For the raw event use `onBlur`. |
|
|
199
|
+
| `onValueChange` | `((value: string) => void)` | no | — | Called with the unwrapped string value on every change. For the raw event use `onChange`. |
|
|
200
|
+
| `prefix` | `ReactNode` | no | — | |
|
|
201
|
+
| `required` | `boolean` | no | — | |
|
|
202
|
+
| `rightSuffix` | `ReactNode` | no | — | |
|
|
203
|
+
| `rightSuffixVariant` | `"default" \| "addon"` | no | — | |
|
|
204
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
205
|
+
| `testId` | `string` | no | — | |
|
|
206
|
+
| `type` | `"text" \| "email" \| "date" \| "datetime-local" \| "password"` | no | — | |
|
|
207
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
208
|
+
|
|
209
|
+
### `field.PasswordField`
|
|
210
|
+
|
|
211
|
+
Source: `src/blocks/Form/connectors/PasswordField.tsx`
|
|
212
|
+
|
|
213
|
+
| Prop | Type | Required | Default | Description |
|
|
214
|
+
| --------------------------- | ----------------------------------------------- | -------- | ------- | ----------- |
|
|
215
|
+
| `allowClear` | `boolean` | no | — | |
|
|
216
|
+
| `allowPasswordToggle` | `boolean` | no | — | |
|
|
217
|
+
| `autoComplete` | `"off" \| "current-password" \| "new-password"` | no | — | |
|
|
218
|
+
| `className` | `string` | no | — | |
|
|
219
|
+
| `defaultPasswordVisibility` | `boolean` | no | — | |
|
|
220
|
+
| `defaultValue` | `string \| null` | no | — | |
|
|
221
|
+
| `focusVariant` | `"off" \| "on" \| "default" \| null` | no | — | |
|
|
222
|
+
| `help` | `ReactNode` | no | — | |
|
|
223
|
+
| `label` | `ReactNode` | no | — | |
|
|
224
|
+
| `onValueBlur` | `((value: string) => void)` | no | — | |
|
|
225
|
+
| `required` | `boolean` | no | — | |
|
|
226
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
227
|
+
| `state` | `"warning" \| "error" \| "default" \| null` | no | — | |
|
|
228
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
229
|
+
|
|
230
|
+
### `field.NumberField`
|
|
231
|
+
|
|
232
|
+
Source: `src/blocks/Form/connectors/NumberField.tsx`
|
|
233
|
+
|
|
234
|
+
| Prop | Type | Required | Default | Description |
|
|
235
|
+
| -------------- | -------------------------------------------- | -------- | ------- | ----------- |
|
|
236
|
+
| `className` | `string` | no | — | |
|
|
237
|
+
| `defaultValue` | `number \| null` | no | — | |
|
|
238
|
+
| `focusVariant` | `"off" \| "on" \| "default" \| null` | no | — | |
|
|
239
|
+
| `help` | `ReactNode` | no | — | |
|
|
240
|
+
| `label` | `ReactNode` | no | — | |
|
|
241
|
+
| `onValueBlur` | `((value: number \| null) => void)` | no | — | |
|
|
242
|
+
| `prefix` | `ReactNode` | no | — | |
|
|
243
|
+
| `required` | `boolean` | no | — | |
|
|
244
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
245
|
+
| `suffix` | `ReactNode` | no | — | |
|
|
246
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
247
|
+
|
|
248
|
+
### `field.SelectField`
|
|
249
|
+
|
|
250
|
+
Source: `src/blocks/Form/connectors/SelectField.tsx`
|
|
251
|
+
|
|
252
|
+
> Pass `items` (canonical, matches the base `Select`) or the deprecated `options` alias — one is required. Forwards `variant === "argo"` to `Select.Content`.
|
|
253
|
+
|
|
254
|
+
| Prop | Type | Required | Default | Description |
|
|
255
|
+
| ------------------ | -------------------------------------------- | -------- | ------- | ----------------------- |
|
|
256
|
+
| `autoComplete` | `string` | no | — | |
|
|
257
|
+
| `className` | `string` | no | — | |
|
|
258
|
+
| `defaultOpen` | `boolean` | no | — | |
|
|
259
|
+
| `defaultValue` | `string` | no | — | |
|
|
260
|
+
| `dir` | `"rtl" \| "ltr"` | no | — | |
|
|
261
|
+
| `disabled` | `boolean` | no | — | |
|
|
262
|
+
| `form` | `string` | no | — | |
|
|
263
|
+
| `help` | `ReactNode` | no | — | |
|
|
264
|
+
| `items` | `readonly Option[]` | no | — | |
|
|
265
|
+
| `label` | `ReactNode` | no | — | |
|
|
266
|
+
| `name` | `string` | no | — | |
|
|
267
|
+
| `onOpenChange` | `((open: boolean) => void)` | no | — | |
|
|
268
|
+
| `open` | `boolean` | no | — | |
|
|
269
|
+
| `options` | `readonly Option[]` | no | — | @deprecated use `items` |
|
|
270
|
+
| `placeholder` | `string` | no | — | |
|
|
271
|
+
| `required` | `boolean` | no | — | |
|
|
272
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
273
|
+
| `state` | `"warning" \| "error" \| "default" \| null` | no | — | |
|
|
274
|
+
| `triggerClassName` | `string` | no | — | |
|
|
275
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
276
|
+
|
|
277
|
+
### `field.TextAreaField`
|
|
278
|
+
|
|
279
|
+
Source: `src/blocks/Form/connectors/TextAreaField.tsx`
|
|
280
|
+
|
|
281
|
+
| Prop | Type | Required | Default | Description |
|
|
282
|
+
| -------------------- | --------------------------------------------------------- | -------- | ------- | ----------- |
|
|
283
|
+
| `className` | `string` | no | — | |
|
|
284
|
+
| `containerClassName` | `string` | no | — | |
|
|
285
|
+
| `defaultValue` | `string \| null` | no | — | |
|
|
286
|
+
| `disableResize` | `boolean` | no | — | |
|
|
287
|
+
| `focusVariant` | `"off" \| "on" \| "default" \| null` | no | — | |
|
|
288
|
+
| `help` | `ReactNode` | no | — | |
|
|
289
|
+
| `label` | `ReactNode` | no | — | |
|
|
290
|
+
| `maxRows` | `number \| "unlimited"` | no | — | |
|
|
291
|
+
| `onValueBlur` | `(((value: string) => void) & ((value: string) => void))` | no | — | |
|
|
292
|
+
| `required` | `boolean` | no | — | |
|
|
293
|
+
| `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
|
|
294
|
+
| `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
|
|
295
|
+
|
|
296
|
+
### `field.CheckboxField`
|
|
297
|
+
|
|
298
|
+
Source: `src/blocks/Form/connectors/CheckboxField.tsx`
|
|
299
|
+
|
|
300
|
+
| Prop | Type | Required | Default | Description |
|
|
301
|
+
| ------------------ | ---------------------------------------- | -------- | ------- | ---------------------------------------------------------------------------------------------- |
|
|
302
|
+
| `argo` | `boolean` | no | — | @internal Transitional opt-in; removed once consumers wrap argo sections in `<ThemeProvider>`. |
|
|
303
|
+
| `asChild` | `boolean` | no | — | |
|
|
304
|
+
| `className` | `string` | no | — | |
|
|
305
|
+
| `description` | `ReactNode` | no | — | |
|
|
306
|
+
| `help` | `ReactNode` | no | — | |
|
|
307
|
+
| `label` | `ReactNode` | no | — | |
|
|
308
|
+
| `position` | `"start" \| "end"` | no | — | |
|
|
309
|
+
| `tooltip` | `ReactNode` | no | — | |
|
|
310
|
+
| `tooltipPlacement` | `"top" \| "right" \| "bottom" \| "left"` | no | — | |
|
|
311
|
+
| `variant` | `"default" \| "boxed" \| null` | no | — | |
|
|
312
|
+
|
|
313
|
+
### `field.SwitchField`
|
|
314
|
+
|
|
315
|
+
Source: `src/blocks/Form/connectors/SwitchField.tsx`
|
|
316
|
+
|
|
317
|
+
| Prop | Type | Required | Default | Description |
|
|
318
|
+
| ------------- | ---------------------------------------- | -------- | ------- | ---------------------------------------------------------------------------------------------- |
|
|
319
|
+
| `argo` | `boolean` | no | — | @internal Transitional opt-in; removed once consumers wrap argo sections in `<ThemeProvider>`. |
|
|
320
|
+
| `className` | `string` | no | — | |
|
|
321
|
+
| `description` | `ReactNode` | no | — | |
|
|
322
|
+
| `help` | `ReactNode` | no | — | |
|
|
323
|
+
| `label` | `ReactNode` | no | — | |
|
|
324
|
+
| `position` | `"start" \| "end"` | no | — | |
|
|
325
|
+
| `size` | `"small" \| "medium" \| "large" \| null` | no | — | |
|
|
326
|
+
| `variant` | `"default" \| "boxed" \| null` | no | — | |
|
|
327
|
+
|
|
328
|
+
## Data display — Card (compound)
|
|
329
|
+
|
|
330
|
+
### `Card`
|
|
331
|
+
|
|
332
|
+
Source: `src/components/Card/Card.tsx`
|
|
333
|
+
|
|
334
|
+
> Subcomponents that EXIST: `Card.Header`, `Card.Title`, `Card.Content`. There is NO `Card.Description` and NO `Card.Footer`. Plus native `<div>` attributes.
|
|
335
|
+
|
|
336
|
+
| Prop | Type | Required | Default | Description |
|
|
337
|
+
| ---------- | -------------------------------------------------- | -------- | ------- | ---------------------------------------------------------------------------------------------- |
|
|
338
|
+
| `argo` | `boolean` | no | `false` | @internal Transitional opt-in; removed once consumers wrap argo sections in `<ThemeProvider>`. |
|
|
339
|
+
| `bordered` | `boolean \| null` | no | `false` | |
|
|
340
|
+
| `padding` | `"small" \| "medium" \| "large" \| "none" \| null` | no | `small` | |
|
|
341
|
+
|
|
342
|
+
### `Card.Header`
|
|
343
|
+
|
|
344
|
+
Source: `src/components/Card/CardHeader.tsx`
|
|
345
|
+
|
|
346
|
+
> Native `<div>` attributes only.
|
|
347
|
+
|
|
348
|
+
_No own props — see note above for passthrough attributes._
|
|
349
|
+
|
|
350
|
+
### `Card.Title`
|
|
351
|
+
|
|
352
|
+
Source: `src/components/Card/CardTitle.tsx`
|
|
353
|
+
|
|
354
|
+
> Native `<div>` attributes only.
|
|
355
|
+
|
|
356
|
+
_No own props — see note above for passthrough attributes._
|
|
357
|
+
|
|
358
|
+
### `Card.Content`
|
|
359
|
+
|
|
360
|
+
Source: `src/components/Card/CardContent.tsx`
|
|
361
|
+
|
|
362
|
+
> Native `<div>` attributes only.
|
|
363
|
+
|
|
364
|
+
_No own props — see note above for passthrough attributes._
|
|
365
|
+
|
|
366
|
+
## Theming
|
|
367
|
+
|
|
368
|
+
### `ThemeProvider`
|
|
369
|
+
|
|
370
|
+
Source: `src/components/ThemeProvider/ThemeProvider.tsx`
|
|
371
|
+
|
|
372
|
+
> Wrap the app root. `brand` defaults to the canonical `keboola` brand. Pair with `useTheme()` to read/flip the brand at runtime.
|
|
373
|
+
|
|
374
|
+
| Prop | Type | Required | Default | Description |
|
|
375
|
+
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
376
|
+
| `brand` | `{ id: string; name: string; colors: { foreground: string; background: string; muted: string; mutedForeground: string; border: string; primary: string; primaryForeground: string; destructive: string; destructiveForeground: string; accent?: string; accentForeground?: string; }; ... 6 more ...; ...` | no | — | Brand whose tokens drive the chrome (`@keboola/design`'s semantic CSS variables) inside this provider's subtree. Defaults to the canonical `keboola` brand so existing apps that mount `<ThemeProvider>` without a `brand` prop continue to render as today. |
|
|
377
|
+
| `colorScheme` | `ColorScheme` | no | `light` | Light/dark mode. `'system'` follows `prefers-color-scheme` and updates reactively. Defaults to `'light'` so existing mounts don't drift. Only the ROOT provider applies this (sets `data-theme`, owns the media listener); a nested provider inherits the root's resolved mode. |
|
|
378
|
+
| `setBrand` | `((brand: { id: string; name: string; colors: { foreground: string; background: string; muted: string; mutedForeground: string; border: string; primary: string; primaryForeground: string; destructive: string; destructiveForeground: string; accent?: string; accentForeground?: string; }; ... 6 m...` | no | — | Optional setter exposed through `useTheme()` so consumers can flip brands at runtime (Storybook toolbar, a customer-impersonation switch). |
|
package/README.md
CHANGED
|
@@ -7,14 +7,23 @@ The Keboola design system — shadcn-aligned, Radix-underneath, Lucide icons.
|
|
|
7
7
|
|
|
8
8
|
📦 **Storybook:** https://design-keboola.vercel.app/
|
|
9
9
|
|
|
10
|
-
📖 **Decision tree:** [`AGENTS.md`](./AGENTS.md) — which component to use when.
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
📖 **Decision tree:** [`AGENTS.md`](./AGENTS.md) — which component to use when (ships in the tarball).
|
|
11
|
+
🔧 **Prop signatures:** [`COMPONENT-API.md`](./COMPONENT-API.md) — generated, version-pinned (ships in the tarball).
|
|
12
|
+
|
|
13
|
+
## For agents and consumers: read the installed package's AGENTS.md first
|
|
14
|
+
|
|
15
|
+
Before writing UI with this design system, read the contract that ships with the version you installed:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
node_modules/@keboola/design/AGENTS.md
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
It is version-pinned to your lockfile, so it always matches the components you actually have — read it rather than relying on the published Storybook or these docs, which track `main`. More generally, before building against any installed `@keboola/*` package, read that package's `node_modules/@keboola/<name>/AGENTS.md` when present.
|
|
13
22
|
|
|
14
23
|
## Install
|
|
15
24
|
|
|
16
25
|
```bash
|
|
17
|
-
|
|
26
|
+
pnpm add @keboola/design @keboola/tailwind-config
|
|
18
27
|
```
|
|
19
28
|
|
|
20
29
|
## Use
|
|
@@ -29,7 +38,7 @@ import '@keboola/design/styles';
|
|
|
29
38
|
### Documentation
|
|
30
39
|
|
|
31
40
|
```
|
|
32
|
-
$
|
|
41
|
+
$ pnpm dev
|
|
33
42
|
```
|
|
34
43
|
|
|
35
44
|
See [Storybook's documentation](https://storybook.js.org/docs/react/writing-stories/introduction) for an introduction to writing stories
|
|
@@ -37,13 +46,13 @@ See [Storybook's documentation](https://storybook.js.org/docs/react/writing-stor
|
|
|
37
46
|
### Testing
|
|
38
47
|
|
|
39
48
|
```
|
|
40
|
-
$
|
|
49
|
+
$ pnpm test
|
|
41
50
|
```
|
|
42
51
|
|
|
43
52
|
Or in UI mode:
|
|
44
53
|
|
|
45
54
|
```
|
|
46
|
-
$
|
|
55
|
+
$ pnpm test:ui
|
|
47
56
|
```
|
|
48
57
|
|
|
49
58
|
For more information, read [Vitest's documentation](https://vitest.dev/guide/)
|
|
@@ -56,18 +65,18 @@ Every story is also run as a test via the [Storybook Vitest addon](https://story
|
|
|
56
65
|
- **interaction** — each story's `play()` function runs as a test.
|
|
57
66
|
|
|
58
67
|
```bash
|
|
59
|
-
|
|
68
|
+
pnpm --filter @keboola/design test:stories
|
|
60
69
|
```
|
|
61
70
|
|
|
62
71
|
This is the cheap, low-flakiness base layer for testing stories. Screenshot/visual regression is a separate, complementary layer (UT-4060) — the Vitest addon does **not** do screenshot comparison.
|
|
63
72
|
|
|
64
73
|
**How it's wired:**
|
|
65
74
|
|
|
66
|
-
- `vitest.config.ts` — a separate Vitest config (browser mode, Playwright Chromium provider) so `
|
|
75
|
+
- `vitest.config.ts` — a separate Vitest config (browser mode, Playwright Chromium provider) so `pnpm test` stays jsdom-only and browser-free; story tests are opt-in via `test:stories`. It is named `vitest.config.ts` (not `vitest.storybook.config.ts`) so the Storybook "Run tests" button (`@storybook/addon-vitest`) can discover it — the addon only scans `vitest.workspace.*` / `vitest.config.*` / `vite.config.*` names; under any other name it falls back to `vite.config.ts` and crashes the dev server (UT-4243). The jsdom `test` script therefore pins `--config vite.config.ts` explicitly.
|
|
67
76
|
- `.storybook/vitest.setup.ts` — applies the Storybook `preview` annotations (decorators, globals) so tests render exactly like Storybook.
|
|
68
77
|
- `optimizeDeps.entries` crawls every story up front so Vite optimizes all story dependencies in one pass — without it, a heavy dep (mermaid, codemirror, …) discovered mid-run makes Vite re-optimize and 404 in-flight imports.
|
|
69
78
|
|
|
70
|
-
**Prerequisites:** build the workspace deps once (`
|
|
79
|
+
**Prerequisites:** build the workspace deps once (`pnpm build --filter='@keboola/design^...'`) and install the browser (`pnpm --filter @keboola/design exec playwright install chromium`).
|
|
71
80
|
|
|
72
81
|
**Adding coverage:** nothing to do — every CSF `*.stories.tsx` is picked up automatically. Add a `play()` to a story to turn it into an interaction test. (MDX docs pages — `src/**/*.mdx` — are not executed as tests; the addon only runs CSF stories.)
|
|
73
82
|
|
|
@@ -118,7 +127,7 @@ Worked examples: `src/components/Button/Button.stories.tsx` (click + disabled),
|
|
|
118
127
|
Coverage is **measured, not gated** — it gives a baseline for the story suite. The default `test:stories` run stays coverage-free and fast; coverage only loads when you opt in:
|
|
119
128
|
|
|
120
129
|
```bash
|
|
121
|
-
|
|
130
|
+
pnpm --filter @keboola/design test:stories:coverage
|
|
122
131
|
```
|
|
123
132
|
|
|
124
133
|
Same prerequisites as `test:stories` (build workspace deps, install Chromium). Reports land in `packages/design/coverage/` (gitignored): a `text` summary in the console and a browsable `html` report at `coverage/index.html`.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
declare const CHUNK_TYPE: {
|
|
2
|
+
readonly UNCHANGED: "unchanged";
|
|
3
|
+
readonly ADDED: "added";
|
|
4
|
+
readonly REMOVED: "removed";
|
|
5
|
+
};
|
|
6
|
+
type ChunkType = (typeof CHUNK_TYPE)[keyof typeof CHUNK_TYPE];
|
|
7
|
+
type DiffChunk = {
|
|
8
|
+
type: ChunkType;
|
|
9
|
+
value: string;
|
|
10
|
+
/** 1-indexed start line in the old (left) text */
|
|
11
|
+
oldStart: number;
|
|
12
|
+
/** 1-indexed end line in the old (left) text */
|
|
13
|
+
oldEnd: number;
|
|
14
|
+
/** 1-indexed start line in the new (right) text */
|
|
15
|
+
newStart: number;
|
|
16
|
+
/** 1-indexed end line in the new (right) text */
|
|
17
|
+
newEnd: number;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* A paired conflict: adjacent REMOVED + ADDED chunks from diffLines,
|
|
21
|
+
* representing a section that was modified. If only one side exists,
|
|
22
|
+
* the other is empty (pure insertion or deletion).
|
|
23
|
+
*/
|
|
24
|
+
type MergeConflict = {
|
|
25
|
+
id: string;
|
|
26
|
+
oldValue: string;
|
|
27
|
+
oldStartLine: number;
|
|
28
|
+
oldEndLine: number;
|
|
29
|
+
newValue: string;
|
|
30
|
+
newStartLine: number;
|
|
31
|
+
newEndLine: number;
|
|
32
|
+
};
|
|
33
|
+
declare const CHANGE_CLASS: {
|
|
34
|
+
readonly SAFE: "safe";
|
|
35
|
+
readonly CONFLICT: "conflict";
|
|
36
|
+
};
|
|
37
|
+
type ChangeClassification = (typeof CHANGE_CLASS)[keyof typeof CHANGE_CLASS];
|
|
38
|
+
|
|
39
|
+
export type { ChangeClassification as C, DiffChunk as D, MergeConflict as M };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
declare const CHUNK_TYPE: {
|
|
2
|
+
readonly UNCHANGED: "unchanged";
|
|
3
|
+
readonly ADDED: "added";
|
|
4
|
+
readonly REMOVED: "removed";
|
|
5
|
+
};
|
|
6
|
+
type ChunkType = (typeof CHUNK_TYPE)[keyof typeof CHUNK_TYPE];
|
|
7
|
+
type DiffChunk = {
|
|
8
|
+
type: ChunkType;
|
|
9
|
+
value: string;
|
|
10
|
+
/** 1-indexed start line in the old (left) text */
|
|
11
|
+
oldStart: number;
|
|
12
|
+
/** 1-indexed end line in the old (left) text */
|
|
13
|
+
oldEnd: number;
|
|
14
|
+
/** 1-indexed start line in the new (right) text */
|
|
15
|
+
newStart: number;
|
|
16
|
+
/** 1-indexed end line in the new (right) text */
|
|
17
|
+
newEnd: number;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* A paired conflict: adjacent REMOVED + ADDED chunks from diffLines,
|
|
21
|
+
* representing a section that was modified. If only one side exists,
|
|
22
|
+
* the other is empty (pure insertion or deletion).
|
|
23
|
+
*/
|
|
24
|
+
type MergeConflict = {
|
|
25
|
+
id: string;
|
|
26
|
+
oldValue: string;
|
|
27
|
+
oldStartLine: number;
|
|
28
|
+
oldEndLine: number;
|
|
29
|
+
newValue: string;
|
|
30
|
+
newStartLine: number;
|
|
31
|
+
newEndLine: number;
|
|
32
|
+
};
|
|
33
|
+
declare const CHANGE_CLASS: {
|
|
34
|
+
readonly SAFE: "safe";
|
|
35
|
+
readonly CONFLICT: "conflict";
|
|
36
|
+
};
|
|
37
|
+
type ChangeClassification = (typeof CHANGE_CLASS)[keyof typeof CHANGE_CLASS];
|
|
38
|
+
|
|
39
|
+
export type { ChangeClassification as C, DiffChunk as D, MergeConflict as M };
|