@keboola/design 3.2.3 → 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 CHANGED
@@ -13,6 +13,7 @@ Working **on** the package (commands, layer boundary, token surface, the FA→Lu
13
13
  - `import '@keboola/design/styles'` — Tailwind-extending consumers. Your PostCSS+Tailwind processes `@tailwind` directives plus design's `dist/` in your content scan. Avoids duplicate preflights/utilities. **Default for any Keboola app.**
14
14
  - `import '@keboola/design/styles/compiled'` — drop-in pre-compiled CSS (~176 KB minified). No Tailwind setup required. Use only when you genuinely cannot run Tailwind.
15
15
  - Icons: `import { ChevronDown } from '@keboola/design/icons'`.
16
+ - Code editors: `import { CodeEditor, SqlEditor, MergeEditor } from '@keboola/design/editors'`. These pull the full CodeMirror bundle (~1.3 MB), so they ship from a dedicated subpath — keeping the root barrel CodeMirror-free for apps that don't edit code.
16
17
 
17
18
  ## Decision tree
18
19
 
@@ -112,6 +113,14 @@ This file answers **which** component to use; [`COMPONENT-API.md`](./COMPONENT-A
112
113
 
113
114
  Vertical rhythm: prefer `tw-gap-*` on a flex column parent over `tw-mt-*` on children — avoids margin-collapse. Page-level max width: use `PageLayout`'s `maxWidth` prop (`narrow` / `default` / `wide` / `full`), not `tw-max-w-[…]` literals.
114
115
 
116
+ **Page gutter is consumer-owned.** `AppShell.Main` and `PageLayout` impose **no** page gutter by design — content renders flush against the sidebar/header unless you add one. Put the padding on the content region yourself (the `AppShell` story wraps Main content in `tw-p-6`):
117
+
118
+ ```tsx
119
+ <AppShell.Main>
120
+ <div className="tw-flex tw-flex-col tw-gap-4 tw-px-6 tw-py-6">{/* page content */}</div>
121
+ </AppShell.Main>
122
+ ```
123
+
115
124
  ### Navigation / flow
116
125
 
117
126
  | Use | When |
@@ -148,7 +157,7 @@ The `label` prop implies `orientation="horizontal"` — labels are not rendered
148
157
  | `CodeEditor` | Generic code editing (JSON, YAML, shell) |
149
158
  | `SqlEditor` | SQL editing (syntax + autocomplete) |
150
159
 
151
- `Select` is wrong above ~10 items — use `Combobox`. `Combobox` is wrong for free-form values — use `Tagger`. `CodeEditor`/`SqlEditor` load the full CodeMirror bundle; reserve for real code editing.
160
+ `Select` is wrong above ~10 items — use `Combobox`. `Combobox` is wrong for free-form values — use `Tagger`. `CodeEditor`/`SqlEditor` load the full CodeMirror bundle and ship from the `@keboola/design/editors` subpath (not the root); reserve for real code editing.
152
161
 
153
162
  ## Composition recipes
154
163
 
@@ -357,6 +366,17 @@ const form = useAppForm({
357
366
  />
358
367
  ```
359
368
 
369
+ **Sheet** — use `Sheet.Header title="…" description="…"` for a styled title + close icon; do **not** hand-roll with the raw `Sheet.Title`, which renders an unstyled `<h2>`. `Sheet.Body` provides horizontal padding only (`tw-px-4`) — add your own vertical rhythm/gap:
370
+
371
+ ```tsx
372
+ <Sheet open={open} onOpenChange={setOpen}>
373
+ <Sheet.Content>
374
+ <Sheet.Header title="Edit table" description="Adjust the table metadata." />
375
+ <Sheet.Body className="tw-flex tw-flex-col tw-gap-4 tw-py-4">{/* fields */}</Sheet.Body>
376
+ </Sheet.Content>
377
+ </Sheet>
378
+ ```
379
+
360
380
  ## Icons
361
381
 
362
382
  Canonical icon surface is `@keboola/design/icons` — a wildcard re-export of [lucide-react](https://lucide.dev) plus Keboola-custom inline-SVG icons. Follow **shadcn convention**: import named icons directly, no wrapper component. Every Lucide icon is available without any package edit.
package/COMPONENT-API.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @keboola/design — component API reference
2
2
 
3
- > **Generated file — do not edit by hand.** Regenerate with `yarn workspace @keboola/design generate:api` (extracts prop signatures from the TypeScript types via `react-docgen-typescript`).
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
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 (`yarn dev --filter=@keboola/design`).
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
6
 
7
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
8
 
@@ -249,29 +249,30 @@ Source: `src/blocks/Form/connectors/NumberField.tsx`
249
249
 
250
250
  Source: `src/blocks/Form/connectors/SelectField.tsx`
251
251
 
252
- > `options` is required. Forwards `variant === "argo"` to `Select.Content`.
253
-
254
- | Prop | Type | Required | Default | Description |
255
- | ------------------ | -------------------------------------------- | -------- | ------- | ----------- |
256
- | `options` | `readonly Option[]` | yes | — | |
257
- | `autoComplete` | `string` | no | — | |
258
- | `className` | `string` | no | — | |
259
- | `defaultOpen` | `boolean` | no | — | |
260
- | `defaultValue` | `string` | no | — | |
261
- | `dir` | `"rtl" \| "ltr"` | no | — | |
262
- | `disabled` | `boolean` | no | — | |
263
- | `form` | `string` | no | — | |
264
- | `help` | `ReactNode` | no | — | |
265
- | `label` | `ReactNode` | no | — | |
266
- | `name` | `string` | no | — | |
267
- | `onOpenChange` | `((open: boolean) => void)` | no | — | |
268
- | `open` | `boolean` | no | — | |
269
- | `placeholder` | `string` | no | — | |
270
- | `required` | `boolean` | no | — | |
271
- | `sizing` | `"small" \| "medium" \| "default" \| null` | no | — | |
272
- | `state` | `"warning" \| "error" \| "default" \| null` | no | — | |
273
- | `triggerClassName` | `string` | no | — | |
274
- | `variant` | `"primary" \| "secondary" \| "argo" \| null` | no | — | |
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 | — | |
275
276
 
276
277
  ### `field.TextAreaField`
277
278
 
package/README.md CHANGED
@@ -23,7 +23,7 @@ It is version-pinned to your lockfile, so it always matches the components you a
23
23
  ## Install
24
24
 
25
25
  ```bash
26
- yarn add @keboola/design @keboola/tailwind-config
26
+ pnpm add @keboola/design @keboola/tailwind-config
27
27
  ```
28
28
 
29
29
  ## Use
@@ -38,7 +38,7 @@ import '@keboola/design/styles';
38
38
  ### Documentation
39
39
 
40
40
  ```
41
- $ yarn dev
41
+ $ pnpm dev
42
42
  ```
43
43
 
44
44
  See [Storybook's documentation](https://storybook.js.org/docs/react/writing-stories/introduction) for an introduction to writing stories
@@ -46,13 +46,13 @@ See [Storybook's documentation](https://storybook.js.org/docs/react/writing-stor
46
46
  ### Testing
47
47
 
48
48
  ```
49
- $ yarn test
49
+ $ pnpm test
50
50
  ```
51
51
 
52
52
  Or in UI mode:
53
53
 
54
54
  ```
55
- $ yarn test:ui
55
+ $ pnpm test:ui
56
56
  ```
57
57
 
58
58
  For more information, read [Vitest's documentation](https://vitest.dev/guide/)
@@ -65,18 +65,18 @@ Every story is also run as a test via the [Storybook Vitest addon](https://story
65
65
  - **interaction** — each story's `play()` function runs as a test.
66
66
 
67
67
  ```bash
68
- yarn workspace @keboola/design test:stories
68
+ pnpm --filter @keboola/design test:stories
69
69
  ```
70
70
 
71
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.
72
72
 
73
73
  **How it's wired:**
74
74
 
75
- - `vitest.config.ts` — a separate Vitest config (browser mode, Playwright Chromium provider) so `yarn 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.
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.
76
76
  - `.storybook/vitest.setup.ts` — applies the Storybook `preview` annotations (decorators, globals) so tests render exactly like Storybook.
77
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.
78
78
 
79
- **Prerequisites:** build the workspace deps once (`yarn build --filter='@keboola/design^...'`) and install the browser (`yarn workspace @keboola/design exec playwright install chromium`).
79
+ **Prerequisites:** build the workspace deps once (`pnpm build --filter='@keboola/design^...'`) and install the browser (`pnpm --filter @keboola/design exec playwright install chromium`).
80
80
 
81
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.)
82
82
 
@@ -127,7 +127,7 @@ Worked examples: `src/components/Button/Button.stories.tsx` (click + disabled),
127
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:
128
128
 
129
129
  ```bash
130
- yarn workspace @keboola/design test:stories:coverage
130
+ pnpm --filter @keboola/design test:stories:coverage
131
131
  ```
132
132
 
133
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 };