From 50c7511698ce430f3c31fcd4a9d0a2e7c6eb1228 Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Sun, 18 Jan 2026 19:24:11 +0300 Subject: [PATCH 1/8] fix(storybook): add aliases from vite config to storybook --- .storybook/main.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/.storybook/main.ts b/.storybook/main.ts index 8f8d617..78cd878 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -1,4 +1,16 @@ import type { StorybookConfig } from '@storybook/svelte-vite'; +import { + dirname, + resolve, +} from 'path'; +import { fileURLToPath } from 'url'; +import { + loadConfigFromFile, + mergeConfig, +} from 'vite'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); const config: StorybookConfig = { 'stories': [ @@ -18,5 +30,17 @@ const config: StorybookConfig = { '@storybook/addon-docs', ], 'framework': '@storybook/svelte-vite', + async viteFinal(config) { + // This attempts to find your actual vite.config.ts + const { config: userConfig } = await loadConfigFromFile( + { command: 'serve', mode: 'development' }, + resolve(__dirname, '../vite.config.ts'), + ) || {}; + + return mergeConfig(config, { + // Merge only the resolve/alias parts if you want to be safe + resolve: userConfig?.resolve || {}, + }); + }, }; export default config; -- 2.49.1 From 06cb155b4710b2c6421018788f8a29d857a7145b Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Sun, 18 Jan 2026 19:25:07 +0300 Subject: [PATCH 2/8] feat(storybook): add a global decorator for stories --- .storybook/StoryStage.svelte | 16 ++++++++++++++++ .storybook/preview.ts | 18 +++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 .storybook/StoryStage.svelte diff --git a/.storybook/StoryStage.svelte b/.storybook/StoryStage.svelte new file mode 100644 index 0000000..1e8b94e --- /dev/null +++ b/.storybook/StoryStage.svelte @@ -0,0 +1,16 @@ + + +
+
+
+ {@render children()} +
+
+
diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 89f39af..c4c3ddd 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -1,9 +1,10 @@ import type { Preview } from '@storybook/svelte-vite'; - +import StoryStage from './StoryStage.svelte'; import '../src/app/styles/app.css'; const preview: Preview = { parameters: { + layout: 'fullscreen', controls: { matchers: { color: /(background|color)$/i, @@ -18,6 +19,21 @@ const preview: Preview = { test: 'todo', }, }, + decorators: [ + (storyFn, { parameters }) => { + const { Component, props } = storyFn(); + return { + Component: StoryStage, + // We pass the actual story component into the Stage via a snippet/slot + // Svelte 5 Storybook handles this mapping internally when you return this structure + props: { + children: Component, + width: parameters.stageWidth || 'max-w-3xl', + ...props, + }, + }; + }, + ], }; export default preview; -- 2.49.1 From 72cc441c6fb92cc1db790e668f4532c9fd19ffd5 Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Sun, 18 Jan 2026 19:25:34 +0300 Subject: [PATCH 3/8] chore(CheckboxFilter): add stories for CheckboxFilter --- .../CheckboxFilter.stories.svelte | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/shared/ui/CheckboxFilter/CheckboxFilter.stories.svelte diff --git a/src/shared/ui/CheckboxFilter/CheckboxFilter.stories.svelte b/src/shared/ui/CheckboxFilter/CheckboxFilter.stories.svelte new file mode 100644 index 0000000..35a8850 --- /dev/null +++ b/src/shared/ui/CheckboxFilter/CheckboxFilter.stories.svelte @@ -0,0 +1,37 @@ + + + + + + + -- 2.49.1 From 2444e05bb76a3ed27d2a5489dba56f600bab658d Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Sun, 18 Jan 2026 20:06:48 +0300 Subject: [PATCH 4/8] chore(storybook): align items inside decorator --- .storybook/StoryStage.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.storybook/StoryStage.svelte b/.storybook/StoryStage.svelte index 1e8b94e..d0510dc 100644 --- a/.storybook/StoryStage.svelte +++ b/.storybook/StoryStage.svelte @@ -9,7 +9,7 @@ let { children, width = 'max-w-3xl' }: Props = $props();
-
+
{@render children()}
-- 2.49.1 From cca69a73cee01f3304cc97d08da990249694322d Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Sun, 18 Jan 2026 20:07:37 +0300 Subject: [PATCH 5/8] fix(SearchBar): make id prop unnecessary --- src/shared/ui/SearchBar/SearchBar.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/ui/SearchBar/SearchBar.svelte b/src/shared/ui/SearchBar/SearchBar.svelte index 9cb6dac..d1e339f 100644 --- a/src/shared/ui/SearchBar/SearchBar.svelte +++ b/src/shared/ui/SearchBar/SearchBar.svelte @@ -18,7 +18,7 @@ import type { Snippet } from 'svelte'; interface Props { /** Unique identifier for the input element */ - id: string; + id?: string; /** Current search value (bindable) */ value: string; /** Additional CSS classes for the container */ -- 2.49.1 From 488857e0ec02f3c13aacf0576bd9a2f140874121 Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Sun, 18 Jan 2026 20:08:13 +0300 Subject: [PATCH 6/8] chore: basic storybook coverage for shared/ui components --- .../ComboControl/ComboControl.stories.svelte | 38 +++++++++++++++++++ .../ContentEditable.stories.svelte | 22 +++++++++++ .../ui/SearchBar/SearchBar.stories.svelte | 26 +++++++++++++ .../ui/VirtualList/VirtualList.stories.svelte | 26 +++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 src/shared/ui/ComboControl/ComboControl.stories.svelte create mode 100644 src/shared/ui/ContentEditable/ContentEditable.stories.svelte create mode 100644 src/shared/ui/SearchBar/SearchBar.stories.svelte create mode 100644 src/shared/ui/VirtualList/VirtualList.stories.svelte diff --git a/src/shared/ui/ComboControl/ComboControl.stories.svelte b/src/shared/ui/ComboControl/ComboControl.stories.svelte new file mode 100644 index 0000000..fb0d6c7 --- /dev/null +++ b/src/shared/ui/ComboControl/ComboControl.stories.svelte @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/shared/ui/ContentEditable/ContentEditable.stories.svelte b/src/shared/ui/ContentEditable/ContentEditable.stories.svelte new file mode 100644 index 0000000..6438345 --- /dev/null +++ b/src/shared/ui/ContentEditable/ContentEditable.stories.svelte @@ -0,0 +1,22 @@ + + + + + + + diff --git a/src/shared/ui/SearchBar/SearchBar.stories.svelte b/src/shared/ui/SearchBar/SearchBar.stories.svelte new file mode 100644 index 0000000..8b47710 --- /dev/null +++ b/src/shared/ui/SearchBar/SearchBar.stories.svelte @@ -0,0 +1,26 @@ + + + + + + + Here will be the search result +
+ Popover closes only when the user clicks outside the search bar or presses the Escape key. +
+
diff --git a/src/shared/ui/VirtualList/VirtualList.stories.svelte b/src/shared/ui/VirtualList/VirtualList.stories.svelte new file mode 100644 index 0000000..eb9c710 --- /dev/null +++ b/src/shared/ui/VirtualList/VirtualList.stories.svelte @@ -0,0 +1,26 @@ + + + + + + + {#snippet children({ item })} +
{item}
+ {/snippet} +
+
-- 2.49.1 From e7f430439185511b353f4bb304d88d732c78859f Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Sun, 18 Jan 2026 20:54:48 +0300 Subject: [PATCH 7/8] chore(storybook): increase height of autodoc stories window --- .storybook/preview.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.storybook/preview.ts b/.storybook/preview.ts index c4c3ddd..2734a23 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -18,6 +18,15 @@ const preview: Preview = { // 'off' - skip a11y checks entirely test: 'todo', }, + + docs: { + story: { + // This sets the default height for the iframe in Autodocs + iframeHeight: '400px', + // Ensure the story isn't forced into a tiny inline box + // inline: true, + }, + }, }, decorators: [ (storyFn, { parameters }) => { -- 2.49.1 From c0eed67618a5de49e4018ef842d62a150d9c47c9 Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Sun, 18 Jan 2026 20:55:36 +0300 Subject: [PATCH 8/8] chore(shared/ui): enhance stories with cases, controls and documentation --- .../CheckboxFilter.stories.svelte | 40 ++++++++- .../ComboControl/ComboControl.stories.svelte | 69 ++++++++++++++- .../ContentEditable.stories.svelte | 87 ++++++++++++++++++- .../ui/SearchBar/SearchBar.stories.svelte | 62 ++++++++++++- .../ui/VirtualList/VirtualList.stories.svelte | 50 ++++++++++- 5 files changed, 295 insertions(+), 13 deletions(-) diff --git a/src/shared/ui/CheckboxFilter/CheckboxFilter.stories.svelte b/src/shared/ui/CheckboxFilter/CheckboxFilter.stories.svelte index 35a8850..2d0df2f 100644 --- a/src/shared/ui/CheckboxFilter/CheckboxFilter.stories.svelte +++ b/src/shared/ui/CheckboxFilter/CheckboxFilter.stories.svelte @@ -8,14 +8,28 @@ const { Story } = defineMeta({ tags: ['autodocs'], parameters: { docs: { + description: { + component: + 'A collapsible property filter with checkboxes. Displays selected count as a badge and supports reduced motion for accessibility. Open by default for immediate visibility and interaction.', + }, story: { inline: false }, // Render stories in iframe for state isolation }, }, + argTypes: { + displayedLabel: { + control: 'text', + description: 'Label for this filter group (e.g., "Properties", "Tags")', + }, + filter: { + control: 'object', + description: 'Filter entity managing properties and selection state', + }, + }, }); - + + + + + diff --git a/src/shared/ui/ComboControl/ComboControl.stories.svelte b/src/shared/ui/ComboControl/ComboControl.stories.svelte index fb0d6c7..c697da4 100644 --- a/src/shared/ui/ComboControl/ComboControl.stories.svelte +++ b/src/shared/ui/ComboControl/ComboControl.stories.svelte @@ -5,12 +5,35 @@ import ComboControl from './ComboControl.svelte'; const { Story } = defineMeta({ title: 'Shared/ComboControl', + component: ComboControl, tags: ['autodocs'], parameters: { docs: { + description: { + component: + 'Provides multiple ways to change a numeric value via decrease/increase buttons, slider, and direct input. All three methods are synchronized, giving users flexibility based on precision needs.', + }, story: { inline: false }, // Render stories in iframe for state isolation }, }, + argTypes: { + control: { + control: 'object', + description: 'TypographyControl instance managing the value and bounds', + }, + decreaseLabel: { + control: 'text', + description: 'Accessibility label for the decrease button', + }, + increaseLabel: { + control: 'text', + description: 'Accessibility label for the increase button', + }, + controlLabel: { + control: 'text', + description: 'Accessibility label for the control button (opens popover)', + }, + }, }); @@ -19,20 +42,58 @@ const defaultControl = createTypographyControl({ value: 77, min: 0, max: 100, st const atMinimumControl = createTypographyControl({ value: 0, min: 0, max: 100, step: 1 }); const atMaximumControl = createTypographyControl({ value: 100, min: 0, max: 100, step: 1 }); const withFloatControl = createTypographyControl({ value: 77.5, min: 0, max: 100, step: 0.1 }); +const customLabelsControl = createTypographyControl({ value: 50, min: 0, max: 100, step: 1 }); - + - + - + - + + + + + diff --git a/src/shared/ui/ContentEditable/ContentEditable.stories.svelte b/src/shared/ui/ContentEditable/ContentEditable.stories.svelte index 6438345..c839f86 100644 --- a/src/shared/ui/ContentEditable/ContentEditable.stories.svelte +++ b/src/shared/ui/ContentEditable/ContentEditable.stories.svelte @@ -4,19 +4,104 @@ import ContentEditable from './ContentEditable.svelte'; const { Story } = defineMeta({ title: 'Shared/ContentEditable', + component: ContentEditable, tags: ['autodocs'], parameters: { docs: { + description: { + component: + 'A contenteditable div with custom font and text properties. Allows inline text editing with support for font size, line height, and letter spacing. The text is two-way bindable for form use cases.', + }, story: { inline: false }, // Render stories in iframe for state isolation }, }, + argTypes: { + text: { + control: 'text', + description: 'Visible text content (two-way bindable)', + }, + fontSize: { + control: { type: 'number', min: 8, max: 200 }, + description: 'Font size in pixels', + }, + lineHeight: { + control: { type: 'number', min: 0.8, max: 3, step: 0.1 }, + description: 'Line height multiplier', + }, + letterSpacing: { + control: { type: 'number', min: -0.5, max: 1, step: 0.05 }, + description: 'Letter spacing in em units', + }, + }, }); - + + + + + + + + + + + + + + + + + diff --git a/src/shared/ui/SearchBar/SearchBar.stories.svelte b/src/shared/ui/SearchBar/SearchBar.stories.svelte index 8b47710..f86dad9 100644 --- a/src/shared/ui/SearchBar/SearchBar.stories.svelte +++ b/src/shared/ui/SearchBar/SearchBar.stories.svelte @@ -4,23 +4,79 @@ import SearchBar from './SearchBar.svelte'; const { Story } = defineMeta({ title: 'Shared/SearchBar', + component: SearchBar, tags: ['autodocs'], parameters: { docs: { + description: { + component: + 'Search input with popover dropdown for results/suggestions. Features keyboard navigation (ArrowDown/Up/Enter) and auto-focus prevention on popover open. The input field serves as the popover trigger.', + }, story: { inline: false }, // Render stories in iframe for state isolation }, }, + argTypes: { + value: { + control: 'text', + description: 'Current search value (two-way bindable)', + }, + placeholder: { + control: 'text', + description: 'Placeholder text for the input', + }, + label: { + control: 'text', + description: 'Optional label displayed above the input', + }, + }, }); - - + + Here will be the search result
Popover closes only when the user clicks outside the search bar or presses the Escape key.
+ + + +
+

No results found

+
+
+
+ + + +
+ Start typing to see results +
+
+
diff --git a/src/shared/ui/VirtualList/VirtualList.stories.svelte b/src/shared/ui/VirtualList/VirtualList.stories.svelte index eb9c710..1e3064e 100644 --- a/src/shared/ui/VirtualList/VirtualList.stories.svelte +++ b/src/shared/ui/VirtualList/VirtualList.stories.svelte @@ -7,18 +7,62 @@ const { Story } = defineMeta({ tags: ['autodocs'], parameters: { docs: { + description: { + component: + 'High-performance virtualized list for large datasets. Only renders visible items plus an overscan buffer for optimal performance. Supports keyboard navigation (ArrowUp/Down, Home, End) and fixed or dynamic item heights.', + }, story: { inline: false }, // Render stories in iframe for state isolation }, }, + argTypes: { + items: { + description: 'Array of items to render', + }, + itemHeight: { + control: 'number', + description: 'Height of each item in pixels', + }, + overscan: { + control: 'number', + description: 'Number of extra items to render above and below the visible area', + }, + class: { + description: 'CSS class to apply to the root element', + }, + onVisibleItemsChange: { + description: 'Callback invoked when the visible items change', + }, + }, }); - - + + + {#snippet children({ item })} +
{item}
+ {/snippet} +
+
+ + + + {#snippet children({ item })} +
{item}
+ {/snippet} +
+
+ + + {#snippet children({ item })}
{item}
{/snippet} -- 2.49.1